diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000000..d084b0217db --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,18 @@ +# Documentation: https://github.com/codecov/support/wiki/codecov.yml + +codecov: + bot: dlang-bot + +coverage: + precision: 3 + round: down + range: "80...100" + + status: + + # Learn more at https://codecov.io/docs#yaml_default_commit_status + project: true + patch: true + changes: false + +comment: false diff --git a/.dscanner.ini b/.dscanner.ini new file mode 100644 index 00000000000..a2b0999c75a --- /dev/null +++ b/.dscanner.ini @@ -0,0 +1,80 @@ +; Configure which static analysis checks are enabled +[analysis.config.StaticAnalysisConfig] +; Check variable, class, struct, interface, union, and function names against +; the Phobos style guide +style_check="disabled" +; Check for array literals that cause unnecessary allocation +enum_array_literal_check="enabled" +; Check for poor exception handling practices +exception_check="disabled" ; FIXME +; Check for use of the deprecated 'delete' keyword +delete_check="enabled" +; Check for use of the deprecated floating point operators +float_operator_check="enabled" +; Check number literals for readability +number_style_check="skip-unittest"; +; Checks that opEquals, opCmp, toHash, and toString are either const, immutable +; , or inout. +object_const_check="disabled" +; Checks for .. expressions where the left side is larger than the right. +backwards_range_check="enabled" +; Checks for if statements whose 'then' block is the same as the 'else' block +if_else_same_check="enabled" +; Checks for some problems with constructors +constructor_check="enabled" +; Checks for unused variables and function parameters +unused_variable_check="disabled" +; Checks for unused labels +unused_label_check="disabled" ; FIXME +; Checks for duplicate attributes +duplicate_attribute="enabled" +; Checks that opEquals and toHash are both defined or neither are defined +opequals_tohash_check="disabled" +; Checks for subtraction from .length properties +length_subtraction_check="disabled" +; Checks for methods or properties whose names conflict with built-in properties +builtin_property_names_check="enabled"; FIXME +; Checks for confusing code in inline asm statements +asm_style_check="disabled"; FIXME +; Checks for confusing logical operator precedence +logical_precedence_check="disabled" +; Checks for undocumented public declarations +undocumented_declaration_check="disabled"; FIXME +; Checks for poor placement of function attributes +function_attribute_check="disabled" +; Checks for use of the comma operator +comma_expression_check="enabled" +; Checks for local imports that are too broad +local_import_check="skip-unittest" +; Checks for variables that could be declared immutable +could_be_immutable_check="disabled" +; Checks for redundant expressions in if statements +redundant_if_check="enabled" +; Checks for redundant parenthesis +redundant_parens_check="enabled" +; Checks for mismatched argument and parameter names +mismatched_args_check="disabled" +; Checks for labels with the same name as variables +label_var_same_name_check="disabled" +; Checks for lines longer than 120 characters +long_line_check="enabled" +; Checks for assignment to auto-ref function parameters +auto_ref_assignment_check="disabled" ; FIXME +; Checks for incorrect infinite range definitions +incorrect_infinite_range_check="enabled" +; Checks for asserts that are always true +useless_assert_check="enabled" +; Check for uses of the old-style alias syntax +alias_syntax_check="enabled" +; Checks for else if that should be else static if +static_if_else_check="enabled" +; Check for unclear lambda syntax +lambda_return_check="enabled" +; Check for auto function without return statement +auto_function_check = "enabled" +; Check for explicitly annotated unittests +explicitly_annotated_unittests = "enabled" +; Check for sortedness of imports +imports_sortedness = "disabled" +; Check for useless usage of the final attribute +final_attribute_check = "enabled" diff --git a/.editorconfig b/.editorconfig index f0ea6fbc127..1111d794394 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ root = true -[*.{c,h,d,di,dd}] +[*.{c,h,d,di,dd,sh}] end_of_line = lf insert_final_newline = true indent_style = space diff --git a/.gitignore b/.gitignore index 47c70ae439c..1c5757d80f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,23 @@ /etc/c/zlib/*.obj -/etc/c/zlib/zlib.lib +*.lib /phobos.json -/phobos.lib generated GNUmakefile .DS_Store .*.sw* Makefile +*.lst + +temp/ +tmp/ + +# Rules for Xamarin Studio project data/build output +*.dproj +bin/ +obj/ + +# Rule for VS Code config folder +.vscode + +*.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2f5a1decb05..bcfcf463eba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,28 +2,15 @@ Guidelines for Contributing =========================== Welcome to the D community and thanks for your interest in contributing! - -## [How to contribute pull requests?](http://wiki.dlang.org/Pull_Requests) -## [How to build dmd/druntime/phobos/docs?](http://wiki.dlang.org/Building_DMD) +If this is your first pull request, please read [Contributing to Phobos](https://wiki.dlang.org/Contributing_to_Phobos). +Have a look at [Starting as a Contributor](http://wiki.dlang.org/Starting_as_a_Contributor#Building_D) for build instructions. More Links ---------- -* Fork [on Github](https://github.com/D-Programming-Language/phobos) -* Use our [Bugzilla bug tracker](http://d.puremagic.com/issues/) -* Follow the [Styleguide](http://dlang.org/dstyle.html) +* Fork [on Github](https://github.com/dlang/phobos) +* Use our [Bugzilla bug tracker](https://issues.dlang.org) +* Follow the [D style](http://dlang.org/dstyle.html) * Participate in [our forum](http://forum.dlang.org/) -* [Review Queue](http://wiki.dlang.org/Review_Queue). - - -Tips for Development --------------------- - -1. Build and test (e.g. the regex module) - - rdmd -unittest -main std/regex.d - -2. Do not forget to test your changes. - Aim for a [high code coverage of your tests](http://forum.dlang.org/thread/ki5ovr$17m1$1@digitalmars.com). - -3. Do not forget the [API Documentation](http://dlang.org/phobos/). +* Ask questions on our `#d` IRC channel on freenode.org ([web interface](https://kiwiirc.com/client/irc.freenode.net/d)) +* Review Phobos additions in the [Review Queue](http://wiki.dlang.org/Review_Queue). diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000000..e0f3e3288ff --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,22 @@ +#!/bin/env groovy + +def clone (repo_url, git_ref = "master") { + checkout( + poll: false, + scm: [ + $class: 'GitSCM', + branches: [[name: git_ref]], + extensions: [[$class: 'CleanBeforeCheckout']], + userRemoteConfigs: [[url: repo_url]] + ] + ) +} + +def pipeline +node { + dir('dlang/ci') { + clone 'https://github.com/dlang/ci.git', 'master' + } + pipeline = load 'dlang/ci/pipeline.groovy' +} +pipeline.runPipeline() diff --git a/README.md b/README.md index 291f6b1d268..7210f20d585 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ ![D Logo](http://dlang.org/images/dlogo.png) Phobos Standard Library =================================================================== +[![GitHub tag](https://img.shields.io/github/tag/dlang/phobos.svg?maxAge=86400)](#) +[![Build status](https://img.shields.io/circleci/project/dlang/phobos/master.svg?maxAge=86400)](https://circleci.com/gh/dlang/phobos) +[![Code coverage](https://img.shields.io/codecov/c/github/dlang/phobos.svg?maxAge=86400)](https://codecov.io/gh/dlang/phobos) +[![Issue Stats](https://img.shields.io/issuestats/p/github/dlang/phobos.svg?maxAge=2592000)](http://www.issuestats.com/github/dlang/phobos) + Phobos is the standard library that comes with the [D Programming Language](http://dlang.org) Compiler. @@ -28,4 +33,3 @@ I Want to Contribute Great! See the [CONTRIBUTING.md file](CONTRIBUTING.md). - diff --git a/changelog.dd b/changelog.dd deleted file mode 100644 index fd39361019d..00000000000 --- a/changelog.dd +++ /dev/null @@ -1,466 +0,0 @@ -$(VERSION 061, ddd mm, 2012, =================================================, - $(WHATSNEW - $(LI std.string: $(RED The implementations of std.string.format and string.sformat have - been replaced with improved implementations which conform to writef. In some, - rare cases, this will break code. Please see the documentation for std.string.format - and std.string.sformat for details.)) - $(LI std.string.format now works in CTFE.) - $(LI std.range.hasSlicing has been made stricter in an effort to make it more reliable. - opSlice for infinite ranges must now return the result of std.range.take, and any - range with slicing which supports $(D $) must now support it with the same semantics - as arrays (including supporting subtraction for finite ranges).) - $(LI std.range.isRandomAccessRange now requires hasLength for finite ranges, as it makes no - sense for such ranges not to define length and many random-access algorithms rely - on length.) - $(LI std.digest: Added new package for digests. This replaces std.md5 and the internal crc32 - implementation.) - $(LI std.digest.sha: Added SHA1 digest implementation, including a fast SSSE3 version.) - $(LI std.digest.ripemd: Added RIPEMD-160 digest implementation.) - $(LI std.uuid: Support SHA1 UUIDs.) - $(LI std.uuid: md5UUID and sha1UUID can now be used in pure code.) - $(LI std.net.curl: Added operationTimeout.) - $(LI std.md5 has been scheduled for deprecation (Use std.digest.md instead).) - $(LI crc32 has been scheduled for deprecation (Use std.digest.crc instead).) - $(LI std.string.xformat and std.string.xsformat have been scheduled for deprecation. Please - use std.string.format and std.string.xsformat instead (which now use the same - implementation as xformat and xsformat).) - ) - - $(LIBBUGSFIXED - $(LI Unlisted Bug: std.net.curl dataTimeout fixed. (Used to set operation timeout - instead of inactivity timeout)) - ) -) - -$(VERSION 060, ddd mm, 2012, =================================================, - - $(WHATSNEW - $(LI std.string: $(RED The current implementations of std.string.format and string.sformat are - scheduled to be replaced in November 2012 with improved implementations - which conform to writef. In some, rare cases, this will break code. - Please see the documentation for std.string.format and std.string.sformat for details.)) - $(LI std.bitmanip: Added peek, read, write, and append for converting - ranges of bytes to and from integral types.) - $(LI std.container: Added DList, which is an implementation of a doubly-linked list.) - $(LI Added std.file.tempDir which returns the path to a directory - where a program can put temporary files.) - $(LI std.process: Added escapeShellCommand, escapeShellFileName, and - escapeWindowsArgument. Documented browse function.) - $(LI std.range: Added RefRange, which effectively makes it possible to - pass a range by reference.) - $(LI std.traits: Added KeyType, ValueType, isScalarType, isBasicType, and - SetFunctionAttributes templates.) - $(LI std.traits: areAllSafe has been scheduled for deprecation. Please use - allSatisfy(isSafe, ...) instead.) - $(LI std.utf: Added overload of codeLength which operates on a string.) - $(LI Capitalized std.traits.pointerTarget to PointerTarget. Old one is - scheduled for deprecation.) - $(LI std.algorithm.indexOf - which was scheduled for deprecation - has been deprecated (it was - easily confused with std.string.indexOf). Please use countUntil instead.) - $(LI std.cpuid - which was scheduled for deprecation - has been deprecated. - Please use core.cpuid instead.) - $(LI std.conv.ConvError and ConvOverflowException - which were scheduled for deprecation - have - been deprecated. Please catch ConvException and ConvOverflowException instead.) - $(LI The overloads of std.conv.to which were scheduled for deprecation - because formattedWrite replaced them have now been deprecated. - Please use std.format.formattedWrite instead.) - $(LI The overload of std.exception.enforce which takes the file and line number as template - arguments has been scheduled for deprecation (as it causes unnecessary template bloat). - Please use the overload which takes them as function arguments instead. This will have no - effect on any calls to enforce which do not explicitly pass the file or line number.) - $(LI std.format.FormatError - which was scheduled for deprecation - has been deprecated. - Please catch FormatException instead.) - $(LI std.file.listDir has been deprecated. Please use std.file.dirEntries instead.) - $(LI std.range.replicate - which was scheduled for deprecation - has been deprecated. - Please use repeat instead.) - $(LI std.range.SortedRange.canFind - which was scheduled for deprecation - has been deprecated. - Please use SortedRange.contains instead.) - $(LI std.socket: timeval and linger - which were scheduled for deprecation - have been deprecated. - Please use TimeVal and Linger.) - $(LI std.stdio.isStreamingDevice has been scheduled for deprecation. Please use - isFileHandle instead.) - $(LI The deprecated std.typecons.defineEnum has been removed.) - $(LI UtfException - which was scheduled for deprecation - has been deprecated. - Please use UTFException instead.) - $(LI The deprecated overloads of std.array.insert and std.array.replace - have been removed. Please use insertInPlace and replaceInPlace instead.) - $(LI The deprecated toISOExtendedString and fromISOExtendedString - functions in std.datetime have been removed. Please use - toISOExtString and fromISOExtString instead.) - $(LI The deprecated std.file.getTimesPosix has been removed. - Please use std.file.getTimes instead.) - $(LI The deprecated overloads of isFile, isDir, and isSymLink in - std.file which took uint have been removed. Please use - attrIsFile, attrIsDir, and attrIsSymlink instead.) - $(LI The deprecated std.file.DirEntry.timeStatusChanged has been removed. - Please use std.file.DirEntry.attributes to get at that information - if you need it.) - $(LI The deprecated std.contracts module has been removed. Please use - std.exception instead.) - $(LI The deprecated std.arg, std.bind, and std.loader modules have been - removed.) - ) - - $(LIBBUGSFIXED - $(LI $(BUGZILLA 2328): setTypeInfo in gc.d backwards.) - $(LI $(BUGZILLA 2588): std.signals should not use 'length' stealth keyword in indexing) - $(LI $(BUGZILLA 4405): all function - returns whether predicate is true for all elements in a range) - $(LI $(BUGZILLA 4603): array(iota(1, 0)) error) - $(LI $(BUGZILLA 4605): Wrong print of an int[string] aa) - $(LI $(BUGZILLA 4629): BufferedFile.printf() wants char[] as first argument) - $(LI $(BUGZILLA 4695): std.range.zip is broken) - $(LI $(BUGZILLA 4744): std.conv: string->enum doesn't look for longer match) - $(LI $(BUGZILLA 4822): Problem with std.stdio.File.writef("%c")) - $(LI $(BUGZILLA 5011): std.container: SList linearRemove produces wrong results) - $(LI $(BUGZILLA 5089): feqrel does not compile for floats) - $(LI $(BUGZILLA 5260): std.math.feqrel() returns negative number) - $(LI $(BUGZILLA 5346): instantiation of std.conv.toImpl and std.format.formatValue fails for unions) - $(LI $(BUGZILLA 5354): formatValue: range templates introduce 3 bugs related to class & struct cases) - $(LI $(BUGZILLA 5786): std.algorithm.sort does not work with std.container.Array: Range violation) - $(LI $(BUGZILLA 5843): Unable to convert a struct with an alias-this to long/ulong to int, using std.conv.to!int.) - $(LI $(BUGZILLA 5970): fix BigInt.toString) - $(LI $(BUGZILLA 6027): bigint to!string conversion and its implications) - $(LI $(BUGZILLA 6175): String corruption when passing static char arrays to std.conv) - $(LI $(BUGZILLA 6191): removechars doesn't accept a const string) - $(LI $(BUGZILLA 6197): std.traits.isImplicitlyConvertible returns some wrong results.) - $(LI $(BUGZILLA 6222): A problem with iota() using size_t) - $(LI $(BUGZILLA 6231): [patch] std.conv.to/std.format.: Structs with toString and isInputRange match multiple templates.) - $(LI $(BUGZILLA 6273): Tuple [] operator in pure function) - $(LI $(BUGZILLA 6379): std.container.SList fails to compile) - $(LI $(BUGZILLA 6437): Refcounted calls dtor before ctor, never calls dtor for globals) - $(LI $(BUGZILLA 6547): Call to std.algorithm.remove causes compile error) - $(LI $(BUGZILLA 6580): scoped classes are aligned incorrectly) - $(LI $(BUGZILLA 6597): to!SomeString should use std.format.formatValue) - $(LI $(BUGZILLA 6642): SysTime should not be hasUnsharedAliasing) - $(LI $(BUGZILLA 6892): Formatted write with specified length of enum member) - $(LI $(BUGZILLA 6926): std.process.system return wrong exit code) - $(LI $(BUGZILLA 7022): File.byLine doesn't release file handle) - $(LI $(BUGZILLA 7138): Can't call array() on dirEntries) - $(LI $(BUGZILLA 7317): writeln cannot handle alias this of array type) - $(LI $(BUGZILLA 7326): write interprets enum with byte backing type as a character) - $(LI $(BUGZILLA 7348): to!string(null) matches more than one template declaration) - $(LI $(BUGZILLA 7356): Implement KeyType, ValueType for hashes in std.traits) - $(LI $(BUGZILLA 7360): Predicate templates in std.traits should consider alias this) - $(LI $(BUGZILLA 7515): The new std.string.translate is slow for ASCII text) - $(LI $(BUGZILLA 7537): `File.tmpfile` requires administrator rights on Windows) - $(LI $(BUGZILLA 7561): std.net.curl broken) - $(LI $(BUGZILLA 7660): toImpl conflict in std.conv) - $(LI $(BUGZILLA 7796): std.typecons.Unique is using writeln without importing std.stdio) - $(LI $(BUGZILLA 7824): isInputRange fails to recognize inout(T)[]) - $(LI $(BUGZILLA 7831): Unlisted @@@BUG in File.detach causes FILE* leaks when reopening) - $(LI $(BUGZILLA 7878): A problem with purity and general templated algorithms) - $(LI $(BUGZILLA 7898): [CTFE] std.algorithm:copy fails when used with two arrays) - $(LI $(BUGZILLA 7909): to!(enum)(string) and to!(string)(enum) break when enum is integral) - $(LI $(BUGZILLA 7919): Sample code works on GDC but fails with DMD) - $(LI $(BUGZILLA 7936): std.random.randomSample always returns the same first value when passed a random number generator) - $(LI $(BUGZILLA 7937): Range iota.Result should be const where possible) - $(LI $(BUGZILLA 7944): std.range.iota.popFront() cycles when the range is empty) - $(LI $(BUGZILLA 7948): std.range.zip broken with requireSameLength) - $(LI $(BUGZILLA 7962): std.regex: Captures.length() returns incorrect value) - $(LI $(BUGZILLA 7973): BigInt %= long/ulong gives wrong value) - $(LI $(BUGZILLA 7975): Incorrect quotes escaping in std.format) - $(LI $(BUGZILLA 7982): iota broken when start and end are unsigned and step is negative.) - $(LI $(BUGZILLA 7993): BigInt divide-by-1 error) - $(LI $(BUGZILLA 8003): Phobos uses deprecated std.path sep symbol) - $(LI $(BUGZILLA 8011): BigInt ++ and -- do wrong thing on negative numbers) - $(LI $(BUGZILLA 8015): std.typecons.Tuple does not support struct with alias method this) - $(LI $(BUGZILLA 8022): BigInt division bug (2)) - $(LI $(BUGZILLA 8026): Fix or disallow randomShuffle() on fixed-sized arrays) - $(LI $(BUGZILLA 8031): If a class have some signals it's impossible for a derived class to have any signals) - $(LI $(BUGZILLA 8037): hasElaborateDestructor is false for non-zero-length static array of structs with elaborate destructor) - $(LI $(BUGZILLA 8039): `scoped` doesn't call any elaborate destructors for struct fields) - $(LI $(BUGZILLA 8040): writeln(null) too) - $(LI $(BUGZILLA 8055): [Regression 2.059] std.algorithm.move corrupts moved object field) - $(LI $(BUGZILLA 8057): std.algorithm.move cannot use for nested struct) - $(LI $(BUGZILLA 8080): 'alias this' causes toString to be shadowed by aliased object) - $(LI $(BUGZILLA 8112): std.algorithm.fill must accept InputRange) - $(LI $(BUGZILLA 8158): std.algorithm.min fails to compile with user-defined types) - $(LI $(BUGZILLA 8164): BigInt from char[] too) - $(LI $(BUGZILLA 8165): BigInt opAssign return value) - $(LI $(BUGZILLA 8171): [Regression 2.060head] Broken std.algorithm.move for nested struct has no member) - $(LI $(BUGZILLA 8186): Formatting class object has an alias this to int* field is broken.) - $(LI $(BUGZILLA 8187): replaceFirst doesn't work for string[] haystack) - $(LI $(BUGZILLA 8191): cstream.printf is completely unusable on x86_64) - $(LI $(BUGZILLA 8195): Segfault when comparing a VariantN to a non-variant type which it holds) - $(LI $(BUGZILLA 8203): Use of std.regex.match() generates "not enough preallocated memory" error) - $(LI $(BUGZILLA 8214): blocking option for TaskPool.finish()) - $(LI $(BUGZILLA 8233): std.array.array fails to compile with ranges of immutable elements which have a length property) - $(LI $(BUGZILLA 8240): std.algorithm.joiner and empty inputRangeObject) - $(LI $(BUGZILLA 8264): [std.conv.to] constructing conversion doesn't work with alias this) - $(LI $(BUGZILLA 8310): writeln of Range of fixed size array) - $(LI $(BUGZILLA 8323): std.string.chompPrefix does not handle differing string types properly) - $(LI $(BUGZILLA 8362): std.traits.isSafe doesn't work with unsafe lamdba functions) - $(LI $(BUGZILLA 8386): writeln stopped working with wstring) - $(LI $(BUGZILLA 8398): enforceEx cannot be used with OutOfMemoryError) - $(LI $(BUGZILLA 8450): measureTime doesn't work with unsafe template functions) - $(LI $(BUGZILLA 8459): std.traits.isSafe behavior silently changed) - ) - ) - -$(VERSION 059, ddd mm, 2012, =================================================, - $(WHATSNEW - $(LI std.array: replaceInto, an alternative for replace that outputs result directly - to an output range, avoiding extra allocation.) - $(LI The deprecated std.date, std.dateparse, and std.gregorian modules - have been removed. Please use std.datetime instead.) - $(LI Several deprecated functions in std.file have been removed.) - $(LI The old functions in std.path which were scheduled for deprecation - have now been deprecated. Please use the new ones which were - introduced in 2.055. However, note that curdir and pardir do not - have replacements, because they're "." and ".." respectively on all - OSes so variables for them were seen as unnecessary. Also, one major - change to note about the new std.path functions is that when operating - on extensions, they expect "." to be part of the extension whereas the - old ones did not (e.g. "file.txt".extension == ".txt" whereas - "file.txt".getExt() == "txt").) - $(LI The version of std.exception.enforceEx which was scheduled for - deprecation has been deprecated. Please use the version which - takes exceptions which can be constructed with new E(msg, file, line) - (rather than just new E(msg) as the old version did). That way, - exceptions constructed with enforceEx will give the file and line - number where enforceEx was called.) - $(LI Get rid of Win9x support.) - $(LI std.typecons: Added Proxy mixin template.) - $(LI std.format: Added documentation about compound format specifier.) - ) - - $(LIBBUGSFIXED - $(LI $(BUGZILLA 4604): A stack overflow with writeln) - $(LI $(BUGZILLA 5523): std.regex handles "\s" and "\W" (etc.) inside square brackets improperly) - $(LI $(BUGZILLA 5652): Add \p and \P unicode properties to std.regex) - $(LI $(BUGZILLA 5674): AssertError in std.regex) - $(LI $(BUGZILLA 5964): std.stdio.readln can throw a UnicodeException) - $(LI $(BUGZILLA 6217): [GSOC] result of std.algorithm.map is not movable) - $(LI $(BUGZILLA 6403): Upgrade std.regex to Unicode UTS #18 Level 1 support) - $(LI $(BUGZILLA 6892): Formatted write with specified length of enum member) - $(LI $(BUGZILLA 7111): New regex engine cannot match beginning of empty string) - $(LI $(BUGZILLA 7138): Can't call array() on dirEntries) - $(LI $(BUGZILLA 7264): Can't iterate result from 4-arg dirEntries as string) - $(LI $(BUGZILLA 7299): std.uni missing doc comments) - $(LI $(BUGZILLA 7300): std.regex.ShiftOr!dchar.search is broken) - $(LI $(BUGZILLA 7374): stdin.byLine() throws AssertError on empty input) - $(LI $(BUGZILLA 7460): std.windows.registry reports a false exception message) - $(LI $(BUGZILLA 7476): Write(ln) functions no longer accept retro range) - $(LI $(BUGZILLA 7628): std.format formatValue incorrect overload) - $(LI $(BUGZILLA 7674): regex replace requires escaped format) - $(LI $(BUGZILLA 7679): std.regex.split and splitter don't work w/ ctRegex) - $(LI $(BUGZILLA 7718): regex and ctRegex produce different results) - $(LI $(BUGZILLA 6116): May not join spawn()'ed threads) - ) - ) - -$(VERSION 058, ddd mm, 2012, =================================================, - - $(WHATSNEW - $(LI Added std.csv for reading CSV files.) - $(LI Added std.net.curl as D-ified wrapper over etc.c.curl and libcurl.) - $(LI Added templates PackageName, ModuleName and FullyQualifiedName into std.traits.) - $(LI The overload of std.concurrency.receiveTimeout which takes a long has - been deprecated. Please use the overload which takes a core.time.Duration.) - $(LI Moved std.datetime.abs to core.time.) - $(LI The overload of std.conv.to which used a member function named to on the - type being converted has been deprecated. Please define opCast on the type - instead, and std.conv.to will use that.) - $(LI std.datetime's endOfMonthDay functions have been deprecated. Please use - daysInMonth instead.) - $(LI std.ctype has been deprecated. Please use std.ascii instead.) - $(LI std.string's hexdigits, digits, octdigits, lowercase, letters, uppercase, - whitespace, and newline have been deprecated. Please use the - corresponding symbols in std.ascii instead.) - $(LI std.string's LS and PS have been deprecated. Please use the corresponding - symbols in std.uni instead.) - $(LI std.string's iswhite has been deprecated. Please use either - std.ascii.isWhite or std.uni.isWhite instead.) - $(LI std.string's tolower, tolowerInPlace, toupper, toupperInPlace, splitlines, - stripl, stripr, ljustify, rjustify, and expandtabs have been deprecated. - Please use the new versions of these functions with properly camelcased names.) - $(LI std.string's zfill has been deprecated. Please use rightJustify instead.) - $(LI std.string's capwords has been deprecated.) - $(LI The overloads of std.string's isNumeric which takes anything and - which takes a va_list have been deprecated. The other overloads remain.) - $(LI std.uni's isUniLower, isUniUpper, toUniLower, toUniUpper, and isUniAlpha - have been deprecated. Please use the versions of these functions which do - not have Uni in their name.) - $(LI Get rid of Windows 3.x and Windows 9x support. Affected modules: std.file, - std.mmfile, std.stream, and std.windows.registry.) - ) - - $(LIBBUGSFIXED - $(LI $(BUGZILLA 4295): IID_IUnknown symbol undefined in phobos.lib) - $(LI $(BUGZILLA 5614): version(Win32) vs version(Windows) in Phobos) - $(LI $(BUGZILLA 5718): Can't demangle symbol defined inside unittest block) - $(LI $(BUGZILLA 6255): Add support for different base conversions in std.conv) - $(LI $(BUGZILLA 6472): RedBlackTree.removeKey) - $(LI $(BUGZILLA 6642): SysTime should not be hasUnsharedAliasing) - $(LI $(BUGZILLA 6874): heap corruption caused by std.array.insertInPlaceImpl or gc.gcx) - $(LI $(BUGZILLA 6944): stdio.File.byLine can't handle an empty file) - $(LI $(BUGZILLA 7092): std.concurrency.receive does not accept free functions) - $(LI $(BUGZILLA 7141): std.regex - escaped characters can form operators in character classes) - $(LI $(BUGZILLA 7230): Crash during printing anonymous union with writeln family functions.) - $(LI $(BUGZILLA 7241): std.format can't read into array of dchar) - $(LI $(BUGZILLA 7302): std.conv.parse with radix doesn't work on ranges) - $(LI $(BUGZILLA 7397): [Regression] std.path.buildPath can't be used with string[]) - $(LI $(BUGZILLA 7480): Unhelpful formatting specifier mismatch exception message for pointers) - $(LI $(BUGZILLA 7484): std.algorithm.copy overlapping array copy) - ) - ) - -$(VERSION 057, ddd mm, 2011, =================================================, - - $(WHATSNEW - $(LI Major overhaul of std.regex module's implementation. - $(RED Breaking change) in std.regex.replace with delegate, - use Captures!string instead of RegexMatch!string as delegate parameter.) - $(LI As typedef has been deprecated, overloads of std.conv.to which use - typedef have now been deprecated.) - $(LI std.array.insert has been deprecated. Please use std.array.insertInPlace instead.) - $(LI The overload of std.array.replace which replaces in place has been deprecated. - Please use std.array.replaceInPlace instead.) - $(LI The toISOExtendedString and fromISOExtendedString functions on SysTime, Date, - TimeOfDay, and DateTime in std.datetime have been deprecated. Please use - toISOExtString and fromISOExtString instead.) - $(LI std.file.getTimesPosix has been deprecated. Please use std.file.getTimes instead.) - $(LI The overloads for isDir, isFile, and isSymlink in std.file which take a uint - have been deprecated. Please use attrIsDir, attrIsFile, and attrIsSymlink instead.) - ) - - $(LIBBUGSFIXED - $(LI Unlisted bug: std.conv: Fix to!float("-0")) - $(LI Unlisted bug: std.file broken on OS X x86_64 due to wrong stat64 declaration.) - $(LI $(BUGZILLA 2936): std.regex.match() short string optimization) - $(LI $(BUGZILLA 4765): std.math.modf always returns 0) - $(LI $(BUGZILLA 5193): SList cannot have struct elements that have immutable members.) - $(LI $(BUGZILLA 5620): Implicit conversion of RegexMatch to bool.) - $(LI $(BUGZILLA 5712): [patch] std.regex.replace disallows w/dstring) - $(LI $(BUGZILLA 6204): emplace() for classes accepts larger chunk but fails in array assignment) - $(LI $(BUGZILLA 6887): Regression of getopt) - $(LI $(BUGZILLA 6888): std.getopt.getopt: one-letter hash option causes range violation) - $(LI $(BUGZILLA 6935): struct with @disable this cannot make range) - $(LI $(BUGZILLA 6973): static assert(isOutputRange!(OutputRange!int, int)) is false) - $(LI $(BUGZILLA 6976): GetLastError called as property) - $(LI $(BUGZILLA 6977): getErrno called as property in std.stdio) - $(LI $(BUGZILLA 6979): hasUnsharedAliasing cannot accept plural parameters) - $(LI $(BUGZILLA 6990): std.string.splitlines deprecation doc missing a word) - $(LI $(BUGZILLA 7000): missing import of std.stdio in std.regex?) - $(LI $(BUGZILLA 7039): Posix 2.057 Makefile error breaking 64bit build) - $(LI $(BUGZILLA 7040): Phobos must use "version/else version" blocks for proper - documentation generation.) - $(LI $(BUGZILLA 7045): AssertError in std.regex on line 1573) - $(LI $(BUGZILLA 7055): to!float("INF2") == 2) - ) - ) - -$(VERSION 056, ddd mm, 2011, =================================================, - - $(WHATSNEW - $(LI std.exception: enforce/enforceEx now can use in @safe pure function.) - $(LI Added optional KeepTerminator param to std.string.splitLines.) - $(LI Added std.string.outdent.) - $(LI std.utf: More @safe and pure.) - $(LI std.windows.registry now use *W functions in order to deal properly with Unicode.) - ) - - $(LIBBUGSFIXED - $(LI $(BUGZILLA 5522): std.range.zip fails on arrays of Object.) - $(LI $(BUGZILLA 6009): std/container disabled on freebsd/64) - $(LI $(BUGZILLA 6160): std.conv.to: Ignore _ to match the rest of D) - $(LI $(BUGZILLA 6181): assert fails in datetime.d while runining Phobos unittest) - $(LI $(BUGZILLA 6258): std.conv.to!real("-") fetches the front of an empty array.) - $(LI $(BUGZILLA 6275): Const values in tuples) - $(LI $(BUGZILLA 6288): std.conv.to removes const/immutable when converting a class) - $(LI $(BUGZILLA 6609): std.conv.parse!Integer should consider sign when radix == 10) - $(LI $(BUGZILLA 6634): std.path.globMatch throws wrong assertion) - $(LI $(BUGZILLA 6640): More formatting consistency between string and range of char) - $(LI $(BUGZILLA 6761): Strange behavior of RedBlackTree causing a dangling pointer) - ) - ) - -$(VERSION 055, ddd mm, 2011, =================================================, - - $(WHATSNEW - $(LI std.algorithm.copy now specializes on arrays for 10-80x improved - performance.) - $(LI std.path has been rewritten from scratch and has a completely new API.) - $(LI std.utf.toUTFz allows you to get a zero-terminated string of any - character type and of any type of mutability.) - $(LI Added symlink and readLink to std.file for Posix systems.) - $(LI Values for GDC and LDC were added to std.compiler.Vendor.) - $(LI Added functions to std.bitswap for generically handling swapping - endianness.) - $(LI Added std.parallelism.TaskPool.workerIndex.) - $(LI Added buffer recycling overload of std.parallelism.asyncBuf) - $(LI std.math.tgamma, lgamma, erf, and erfc are now deprecated. The - equivalent functions in std.mathspecial should be used instead.) - $(LI The names of the values of std.mmfile.Mode, std.system.Endian, - std.traits.FunctionAttributes, std.traits.ParameterStorageClass, - and std.traits.Variadic were changed to match Phobos' naming conventions.) - $(LI std.range: Added indexed and chunks) - $(LI std.string.translate has been updated to work with unicode. As a - result, its signature has been changed. The old version and - std.string.maketrans have been scheduled for deprecation.) - $(LI std.string.tr has been updated so that it works with any string type.) - $(LI std.conv.parse works for associative array and static array) - $(LI std.format: Improvement of formatValue and unformatValue. - They now works for associative array, consider element escaping, - and treat range format spec more properly.) - ) - - $(LIBBUGSFIXED - $(LI Unlisted bug: std.range.transversal should have length) - $(LI $(BUGZILLA 3890): Bad writeln of a nested struct) - $(LI $(BUGZILLA 4977): cannot use nothrow or pure with Rebindable) - $(LI $(BUGZILLA 5237): writefln doesn't respect Complex.toString) - $(LI $(BUGZILLA 5645): std.range.drop()) - $(LI $(BUGZILLA 5825): write is calling a deprecated function) - $(LI $(BUGZILLA 6064): std.array.join is unnecssarily slow for strings) - $(LI $(BUGZILLA 6194): [GSoC] Destructor gets called on object before it is copied when calling writeln()) - $(LI $(BUGZILLA 6261): [2.054 beta regression] Regex cannot take a char[]) - $(LI $(BUGZILLA 6377): std.conv.to should check range when changing signedness) - $(LI $(BUGZILLA 6587): std.parallelism's Task cannot handle immutable values) - $(LI $(BUGZILLA 6606): RefCounted doesn't work with unions due to use of format) - $(LI $(BUGZILLA 6608): Tuple field is not escaped) - ) - ) - -$(VERSION 054, ddd mm, 2011, =================================================, - - $(WHATSNEW - $(LI std.array.insertInPlace supports inserting of multiple ranges/elements in one go) - $(LI Added std.array.uninitializedArray and std.array.minimallyInitializedArray) - $(LI Various functions in std.string were renamed to match Phobos' - naming conventions and be properly camelcased. The old names - are still there but have been scheduled for deprecation.) - $(LI The deprecated std.string.toString and std.string.atoi functions - were removed. If you were still using them for any reason, - replace them with calls to std.conv.to.) - $(LI Various functions in std.uni were renamed so that they don't have - "Uni" in their name, since it was decided that it was not desirable to - repeat a module's name in its functions' names. The old names - are still there but have been scheduled for deprecation.) - $(LI std.ctype has been scheduled for deprecation. std.ascii has been - added to replace it.) - $(LI Major performance improvements for std.algorithm.sort) - ) - - $(LIBBUGSFIXED - $(LI $(BUGZILLA 876): std.intrinsic.bswap overloads) - $(LI $(BUGZILLA 2108): regexp.d: The greedy dotstar isn't so greedy) - $(LI $(BUGZILLA 2117): Please add more byteswapping support) - $(LI $(BUGZILLA 3136): Incorrect and strange behavior of std.regexp.RegExp if using a pattern with optional prefix and suffix longer than 1 char) - $(LI $(BUGZILLA 3457): rdmd fails silently in a particular setup where the compiler is not the expected) - $(LI $(BUGZILLA 3479): writef/writefln: positional precision not working) - $(LI $(BUGZILLA 3564): Rdmd failing to link external C libraries) - $(LI $(BUGZILLA 3752): File.byLine fetches lines in a confusing manner) - $(LI $(BUGZILLA 4367): std.regex: Captures is not a random access range) - $(LI $(BUGZILLA 4574): std.regex: breaks with empy string regex) - $(LI $(BUGZILLA 4608): std.string.chomp documentation mismatch implementation) - $(LI $(BUGZILLA 5019): In std.regex, empty capture at end of string causes error) - $(LI $(BUGZILLA 5511): std.regex optional capture with no-match cause error) - $(LI $(BUGZILLA 5673): Add lookahead and forgetful matching support std.regex) - $(LI $(BUGZILLA 5857): std.regex (...){n,m} is bogus when (...) contains repetitions) - $(LI $(BUGZILLA 6076): regression, std.regex: "c.*|d" matches "mm") - $(LI $(BUGZILLA 6113): singletons in std.datetime are not created early enough) - $(LI $(BUGZILLA 5705): Swapping identical struct with hasElaborateAssign causes "overlapping array copy" exception) - $(LI $(BUGZILLA 6193): Appender.clear() functionality or documentation) - ) -) diff --git a/changelog/README.md b/changelog/README.md new file mode 100644 index 00000000000..b79acf7e725 --- /dev/null +++ b/changelog/README.md @@ -0,0 +1,42 @@ +This directory will get copied to dlang.org and cleared when master gets +merged into stable prior to a new release. + +How to add a new changelog entry to the pending changelog? +========================================================== + +Create a new file in the `changelog` folder. It should end with `.dd` and look +similar to a git commit message. The first line represents the title of the change. +After an empty line follows the long description: + +``` +My fancy title of the new feature + +A long description of the new feature in `std.range`. +It can be followed by an example: +------- +import std.range : padLeft, padRight; +import std.algorithm.comparison : equal; + +assert([1, 2, 3, 4, 5].padLeft(0, 7).equal([0, 0, 1, 2, 3, 4, 5])); + +assert("Hello World!".padRight('!', 15).equal("Hello World!!!!")); +------- +and links to the documentation, e.g. $(REF drop, std, range) or +$(REF_ALTTEXT a custom name for the function, drop, std, range). + +Links to the spec can look like this $(LINK2 $(ROOT_DIR)spec/module.html, this) +and of course you can link to other $(LINK2 https://forum.dlang.org/, external resources). +``` + +The title can't contain links (it's already one). +For more infos, see the [Ddoc spec](https://dlang.org/spec/ddoc.html). + +Preview changes +--------------- + +If you have cloned the [tools](https://github.com/dlang/tools) and [dlang.org](https://github.com/dlang/dlang.org) repo), +you can preview the changelog with: + +``` +make -C ../dlang.org -f posix.mak pending_changelog +``` diff --git a/changelog/pattern-deprecate.dd b/changelog/pattern-deprecate.dd new file mode 100644 index 00000000000..2fe5214fe66 --- /dev/null +++ b/changelog/pattern-deprecate.dd @@ -0,0 +1,10 @@ +Several functions in `std.string` have been deprecated + +The functions $(REF inPattern, std, string), $(REF countchars, std, string), +$(REF removechars, std, string), $(REF squeeze, std, string), and +$(REF munch, std, string), have all been deprecated. These functions are +obsolete, as their functionality is better covered by the functions in +$(MREF std, regex) and $(MREF std, algorithm). They will be removed from +$(MREF std, string) on May 2018. + +If you still need to use these, please see $(LINK2 https://github.com/dlang/undeaD, undeaD). diff --git a/changelog/split-std-datetime.dd b/changelog/split-std-datetime.dd new file mode 100644 index 00000000000..656979a6cac --- /dev/null +++ b/changelog/split-std-datetime.dd @@ -0,0 +1,54 @@ +std.datetime has been split into a package. + +std.datetime is now a package containing the following modules: + +$(UL + $(LI $(MREF std,datetime,date)) + $(LI $(MREF std,datetime,interval)) + $(LI $(MREF std,datetime,stopwatch)) + $(LI $(MREF std,datetime,systime)) + $(LI $(MREF std,datetime,timezone)) +) + +$(MREF std,datetime,package) publicly imports all of those modules. So, it +should be the case that no existing code will break, as everything in +std.datetime will still be imported by importing std.datetime. New code can +choose to import the modules individually or to import the entire package. + +$(MREF std,datetime,date) contains Date, TimeOfDay, DateTime, and the related +free functions. It also contains DateTimeException. + +$(MREF std,datetime,interval) contains the *Interval and *IntervalRange types +as well as the related free functions. + +$(MREF std,datetime,systime) contains SysTime and the related free functions. + +$(MREF std,datetime,timezone) contains the time zone types. + +$(MREF std,datetime,package) contains StopWatch and the benchmarking functions +(so, they can only be imported via std.datetime and not via a submodule). As +those functions use $(REF TickDuration,core,time) (which is being replaced by +$(REF MonoTime,core,time), they are slated for deprecation. + +$(MREF std,datetime,stopwatch) has been added. It contains versions of +StopWatch and benchmark which have almost the same API as the existing symbols, +but they use $(REF MonoTime,core,time) and $(REF Duration,core,time) instead of +$(REF TickDuration,core,time). In the next major release, the old functions in +std.datetime.package will be deprecated, so code which uses the old +benchmarking functions should be updated to use std.datetime.stopwatch. + +However, note that in order to avoid irreconcilable symbol conflicts between +the new and old versions, std.datetime.stopwatch will not be publicly imported +by std.datetime.package until the old symbols have been removed. So, for the +time being, code using $(REF StopWatch,std,datetime,stopwatch) or +$(REF StopWatch,std,datetime,benchmark) will need to import +std.datetime.stopwatch directly. Code which imports both std.datetime and +std.datetime.stopwatch will need to either use selective imports or fully +qualified symbols to reconcile the symbol conflicts, but no code will be +affected by the changes until it's updated to import std.datetime.stopwatch, +and when the old symbols are finally removed, the selective imports and fully +qualified paths to the new symbols will continue to work and won't break +(though at that point, simply importing std.datetime will work, since +std.datetime.package will have been updated to publicly import +std.datetime.stopwatch). Code that simply imporst std.datetime.stopwatch without +importing std.datetime will not have to worry about symbol conflicts. diff --git a/changelog/std-digest-digest-secureCompare.dd b/changelog/std-digest-digest-secureCompare.dd new file mode 100644 index 00000000000..03d4026f3c6 --- /dev/null +++ b/changelog/std-digest-digest-secureCompare.dd @@ -0,0 +1,29 @@ +Added a constant time comparison function for cryptographic hashes + +Added a new function to $(REF secureEqual, std, digest, digest) that compares +two ranges that represent hashes in a secure manner. The comparison is done in +constant time regardless of the equality of the two ranges in order to protect +against timing attacks. For more information on the attack, please refer to +the docs on $(REF secureEqual, std, digest, digest). + +``` +import std.digest.digest : secureEqual, toHexString; +import std.digest.hmac : hmac; +import std.digest.sha : SHA1; +import std.string : representation; + +void main() +{ + // a typical HMAC data integrity verification + auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation; + auto data = "data".representation; + + string hex1 = data.hmac!SHA1(secret).toHexString; + string hex2 = data.hmac!SHA1(secret).toHexString; + + string hex3 = "data1".representation.hmac!SHA1(secret).toHexString; + + assert( secureEqual(hex1, hex2)); + assert(!secureEqual(hex1, hex3)); +} +``` \ No newline at end of file diff --git a/changelog/std-range-slides.dd b/changelog/std-range-slides.dd new file mode 100644 index 00000000000..1da68532597 --- /dev/null +++ b/changelog/std-range-slides.dd @@ -0,0 +1,33 @@ +`std.range.slide` (a fixed-size sliding window range) was added + +$(REF slide, std, range) allows to iterate a range in sliding windows: + +--- +import std.array : array; +import std.algorithm.comparison : equal; + +assert([0, 1, 2, 3].slide(2).equal!equal( + [[0, 1], [1, 2], [2, 3]] +)); +assert(5.iota.slide(3).equal!equal( + [[0, 1, 2], [1, 2, 3], [2, 3, 4]] +)); + +assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); +assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + +// set a custom stepsize (default 1) +assert(6.iota.slide(1, 2).equal!equal( + [[0], [2], [4]] +)); + +assert(6.iota.slide(2, 4).equal!equal( + [[0, 1], [4, 5]] +)); + +// allow slide with less elements than the window size +assert(3.iota.slide!(No.withFewerElements)(4).empty); +assert(3.iota.slide!(Yes.withFewerElements)(4).equal!equal( + [[0, 1, 2]] +)); +--- diff --git a/circle.yml b/circle.yml new file mode 100644 index 00000000000..72801310c99 --- /dev/null +++ b/circle.yml @@ -0,0 +1,18 @@ +dependencies: + pre: + - ./circleci.sh install-deps + cache_directories: + - "~/dlang" + +test: + override: + - ./circleci.sh setup-repos + - ./circleci.sh style_lint + - ./circleci.sh has_public_example + - ./circleci.sh publictests + - ./circleci.sh coverage: + parallel: true + timeout: 1200 + + post: + - bash <(curl -s https://codecov.io/bash) diff --git a/circleci.sh b/circleci.sh new file mode 100755 index 00000000000..218958e4f66 --- /dev/null +++ b/circleci.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +set -uexo pipefail + +HOST_DMD_VER=2.068.2 # same as in dmd/src/posix.mak +DSCANNER_DMD_VER=2.071.2 # dscanner needs a more up-to-date version +CURL_USER_AGENT="CirleCI $(curl --version | head -n 1)" +DUB=${DUB:-$HOME/dlang/dub/dub} +N=2 +CIRCLE_NODE_INDEX=${CIRCLE_NODE_INDEX:-0} + +case $CIRCLE_NODE_INDEX in + 0) MODEL=64 ;; + 1) MODEL=32 ;; +esac + +install_deps() { + if [ $MODEL -eq 32 ]; then + sudo apt-get update + sudo apt-get install g++-multilib + fi + + for i in {0..4}; do + if curl -fsS -A "$CURL_USER_AGENT" --max-time 5 https://dlang.org/install.sh -O || + curl -fsS -A "$CURL_USER_AGENT" --max-time 5 https://nightlies.dlang.org/install.sh -O ; then + break + elif [ $i -ge 4 ]; then + sleep $((1 << $i)) + else + echo 'Failed to download install script' 1>&2 + exit 1 + fi + done + + source "$(CURL_USER_AGENT=\"$CURL_USER_AGENT\" bash install.sh dmd-$HOST_DMD_VER --activate)" + $DC --version + env +} + +# clone dmd and druntime +clone() { + local url="$1" + local path="$2" + local branch="$3" + for i in {0..4}; do + if git clone --branch "$branch" "$url" "$path" "${@:4}"; then + break + elif [ $i -lt 4 ]; then + sleep $((1 << $i)) + else + echo "Failed to clone: ${url}" + exit 1 + fi + done +} + +setup_repos() +{ + # set a default in case we run into rate limit restrictions + local base_branch="" + if [ -n "${CIRCLE_PR_NUMBER:-}" ]; then + base_branch=$((curl -fsSL https://api.github.com/repos/dlang/phobos/pulls/$CIRCLE_PR_NUMBER || echo) | jq -r '.base.ref') + else + base_branch=$CIRCLE_BRANCH + fi + base_branch=${base_branch:-"master"} + + # merge upstream branch with changes, s.t. we check with the latest changes + if [ -n "${CIRCLE_PR_NUMBER:-}" ]; then + local current_branch=$(git rev-parse --abbrev-ref HEAD) + git config user.name dummyuser + git config user.email dummyuser@dummyserver.com + git remote add upstream https://github.com/dlang/phobos.git + git fetch upstream + git checkout -f upstream/$base_branch + git merge -m "Automatic merge" $current_branch + fi + + for proj in dmd druntime ; do + if [ $base_branch != master ] && [ $base_branch != stable ] && + ! git ls-remote --exit-code --heads https://github.com/dlang/$proj.git $base_branch > /dev/null; then + # use master as fallback for other repos to test feature branches + clone https://github.com/dlang/$proj.git ../$proj master --depth 1 + else + clone https://github.com/dlang/$proj.git ../$proj $base_branch --depth 1 + fi + done + + # load environment for bootstrap compiler + source "$(CURL_USER_AGENT=\"$CURL_USER_AGENT\" bash ~/dlang/install.sh dmd-$HOST_DMD_VER --activate)" + + # build dmd and druntime + make -j$N -C ../dmd/src -f posix.mak MODEL=$MODEL HOST_DMD=$DMD all + make -j$N -C ../druntime -f posix.mak MODEL=$MODEL HOST_DMD=$DMD +} + +# verify style guide +style() +{ + # dscanner needs a more up-to-date DMD version + source "$(CURL_USER_AGENT=\"$CURL_USER_AGENT\" bash ~/dlang/install.sh dmd-$DSCANNER_DMD_VER --activate)" + + # some style tools are at the tools repo + clone https://github.com/dlang/tools.git ../tools master + # fix to a specific version of https://github.com/dlang/tools/tree/master/styles + git -C ../tools checkout 60583c8363ff25d00017dffdb18c7ee7e7d9a343 + + make -f posix.mak style DUB=$DUB +} + +# run unittest with coverage +coverage() +{ + make -f posix.mak clean + # remove all existing coverage files (just in case) + rm -rf $(find -name '*.lst') + + # currently using the test_runner yields wrong code coverage results + # see https://github.com/dlang/phobos/pull/4719 for details + ENABLE_COVERAGE="1" make -f posix.mak MODEL=$MODEL unittest-debug + + # instead we run all tests individually + make -f posix.mak $(find std etc -name "*.d" | sed "s/[.]d$/.test") +} + +case $1 in + install-deps) install_deps ;; + setup-repos) setup_repos ;; + coverage) coverage ;; + style) style ;; +esac diff --git a/etc/c/curl.d b/etc/c/curl.d index 71afc34263a..7ac597c9fc0 100644 --- a/etc/c/curl.d +++ b/etc/c/curl.d @@ -35,8 +35,6 @@ module etc.c.curl; -version (Windows) pragma(lib, "curl"); - import core.stdc.time; import core.stdc.config; import std.socket; @@ -92,10 +90,12 @@ enum LIBCURL_VERSION_NUM = 0x071504; */ enum LIBCURL_TIMESTAMP = "Thu Feb 17 12:19:40 UTC 2011"; -/** Data type definition of curl_off_t. */ -/// jdrewsen - Always 64bit signed and that is what long is in D. -/// Comment below is from curlbuild.h: -/** +/** Data type definition of curl_off_t. + * + * jdrewsen - Always 64bit signed and that is what long is in D. + * + * Comment below is from curlbuild.h: + * * NOTE 2: * * For any given platform/compiler curl_off_t must be typedef'ed to a @@ -106,16 +106,17 @@ enum LIBCURL_TIMESTAMP = "Thu Feb 17 12:19:40 UTC 2011"; * As an exception to the above, curl_off_t shall be typedef'ed to a * 32-bit wide signed integral data type if there is no 64-bit type. */ -alias long curl_off_t; +alias curl_off_t = long; /// -alias void CURL; +alias CURL = void; /// jdrewsen - Get socket alias from std.socket -alias socket_t curl_socket_t; +alias curl_socket_t = socket_t; /// jdrewsen - Would like to get socket error constant from std.socket by it is private atm. -version(Windows) { +version(Windows) +{ private import core.sys.windows.windows, core.sys.windows.winsock2; enum CURL_SOCKET_BAD = SOCKET_ERROR; } @@ -158,7 +159,7 @@ enum HTTPPOST_CALLBACK = 64; /** upload file contents by using the pointer */ /// -alias int function(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) curl_progress_callback; +alias curl_progress_callback = int function(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); /** Tests have proven that 20K is a very bad buffer size for uploads on Windows, while 16K for some odd reason performed a lot better. @@ -166,7 +167,7 @@ alias int function(void *clientp, double dltotal, double dlnow, double ultotal, time for those who feel adventurous. The practical minimum is about 400 bytes since libcurl uses a buffer of this size as a scratch area (unrelated to network send operations). */ -enum CURL_MAX_WRITE_SIZE = 16384; +enum CURL_MAX_WRITE_SIZE = 16_384; /** The only reason to have a max limit for this is to avoid the risk of a bad server feeding libcurl with a never-ending header that will cause reallocs @@ -179,7 +180,7 @@ enum CURL_MAX_HTTP_HEADER = (100*1024); enum CURL_WRITEFUNC_PAUSE = 0x10000001; /// -alias size_t function(char *buffer, size_t size, size_t nitems, void *outstream)curl_write_callback; +alias curl_write_callback = size_t function(char *buffer, size_t size, size_t nitems, void *outstream); /** enumeration of file types */ enum CurlFileType { @@ -195,7 +196,7 @@ enum CurlFileType { } /// -alias int curlfiletype; +alias curlfiletype = int; /// enum CurlFInfoFlagKnown { @@ -256,7 +257,7 @@ enum CurlChunkBgnFunc { /** if splitting of data transfer is enabled, this callback is called before download of an individual chunk started. Note that parameter "remains" works only for FTP wildcard downloading (for now), otherwise is not used */ -alias c_long function(void *transfer_info, void *ptr, int remains)curl_chunk_bgn_callback; +alias curl_chunk_bgn_callback = c_long function(void *transfer_info, void *ptr, int remains); /** return codes for CURLOPT_CHUNK_END_FUNCTION */ enum CurlChunkEndFunc { @@ -269,7 +270,7 @@ enum CurlChunkEndFunc { Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. This is the reason why we don't need "transfer_info" parameter in this callback and we are not interested in "remains" parameter too. */ -alias c_long function(void *ptr)curl_chunk_end_callback; +alias curl_chunk_end_callback = c_long function(void *ptr); /** return codes for FNMATCHFUNCTION */ enum CurlFnMAtchFunc { @@ -280,7 +281,7 @@ enum CurlFnMAtchFunc { /** callback type for wildcard downloading pattern matching. If the string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */ -alias int function(void *ptr, char *pattern, char *string)curl_fnmatch_callback; +alias curl_fnmatch_callback = int function(void *ptr, in char *pattern, in char *string); /// seek whence... enum CurlSeekPos { @@ -298,7 +299,7 @@ enum CurlSeek { } /// -alias int function(void *instream, curl_off_t offset, int origin)curl_seek_callback; +alias curl_seek_callback = int function(void *instream, curl_off_t offset, int origin); /// enum CurlReadFunc { @@ -313,7 +314,7 @@ enum CurlReadFunc { } /// -alias size_t function(char *buffer, size_t size, size_t nitems, void *instream)curl_read_callback; +alias curl_read_callback = size_t function(char *buffer, size_t size, size_t nitems, void *instream); /// enum CurlSockType { @@ -321,10 +322,10 @@ enum CurlSockType { last /** never use */ } /// -alias int curlsocktype; +alias curlsocktype = int; /// -alias int function(void *clientp, curl_socket_t curlfd, curlsocktype purpose)curl_sockopt_callback; +alias curl_sockopt_callback = int function(void *clientp, curl_socket_t curlfd, curlsocktype purpose); /** addrlen was a socklen_t type before 7.18.0 but it turned really ugly and painful on the systems that lack this type */ @@ -340,7 +341,7 @@ extern (C) struct curl_sockaddr } /// -alias curl_socket_t function(void *clientp, curlsocktype purpose, curl_sockaddr *address)curl_opensocket_callback; +alias curl_opensocket_callback = curl_socket_t function(void *clientp, curlsocktype purpose, curl_sockaddr *address); /// enum CurlIoError @@ -351,7 +352,7 @@ enum CurlIoError last /** never use */ } /// -alias int curlioerr; +alias curlioerr = int; /// enum CurlIoCmd { @@ -360,10 +361,10 @@ enum CurlIoCmd { last, /** never use */ } /// -alias int curliocmd; +alias curliocmd = int; /// -alias curlioerr function(CURL *handle, int cmd, void *clientp)curl_ioctl_callback; +alias curl_ioctl_callback = curlioerr function(CURL *handle, int cmd, void *clientp); /** * The following typedef's are signatures of malloc, free, realloc, strdup and @@ -371,15 +372,15 @@ alias curlioerr function(CURL *handle, int cmd, void *clientp)curl_ioctl_callba * curl_global_init_mem() function to set user defined memory management * callback routines. */ -alias void * function(size_t size)curl_malloc_callback; +alias curl_malloc_callback = void* function(size_t size); /// ditto -alias void function(void *ptr)curl_free_callback; +alias curl_free_callback = void function(void *ptr); /// ditto -alias void * function(void *ptr, size_t size)curl_realloc_callback; +alias curl_realloc_callback = void* function(void *ptr, size_t size); /// ditto -alias char * function(char *str)curl_strdup_callback; +alias curl_strdup_callback = char * function(in char *str); /// ditto -alias void * function(size_t nmemb, size_t size)curl_calloc_callback; +alias curl_calloc_callback = void* function(size_t nmemb, size_t size); /** the kind of data that is passed to information_callback*/ enum CurlCallbackInfo { @@ -393,15 +394,16 @@ enum CurlCallbackInfo { end /// } /// -alias int curl_infotype; +alias curl_infotype = int; /// -alias int function(CURL *handle, /** the handle/transfer this concerns */ - curl_infotype type, /** what kind of data */ - char *data, /** points to the data */ - size_t size, /** size of the data pointed to */ - void *userptr /** whatever the user please */ - )curl_debug_callback; +alias curl_debug_callback = + int function(CURL *handle, /** the handle/transfer this concerns */ + curl_infotype type, /** what kind of data */ + char *data, /** points to the data */ + size_t size, /** size of the data pointed to */ + void *userptr /** whatever the user please */ + ); /** All possible error codes from all sorts of curl functions. Future versions may return other values, stay prepared. @@ -415,7 +417,8 @@ enum CurlError unsupported_protocol, /** 1 */ failed_init, /** 2 */ url_malformat, /** 3 */ - obsolete4, /** 4 - NOT USED */ + not_built_in, /** 4 - [was obsoleted in August 2007 for + 7.17.0, reused in April 2011 for 7.21.5] */ couldnt_resolve_proxy, /** 5 */ couldnt_resolve_host, /** 6 */ couldnt_connect, /** 7 */ @@ -465,7 +468,7 @@ enum CurlError interface_failed, /** 45 - CURLOPT_INTERFACE failed */ obsolete46, /** 46 - NOT USED */ too_many_redirects, /** 47 - catch endless re-direct loops */ - unknown_telnet_option, /** 48 - User specified an unknown option */ + unknown_option, /** 48 - User specified an unknown option */ telnet_option_syntax, /** 49 - Malformed telnet option */ obsolete50, /** 50 - NOT USED */ peer_failed_verification, /** 51 - peer's certificate or fingerprint @@ -518,17 +521,17 @@ enum CurlError curl_last /** never use! */ } /// -alias int CURLcode; +alias CURLcode = int; /** This prototype applies to all conversion callbacks */ -alias CURLcode function(char *buffer, size_t length)curl_conv_callback; +alias curl_conv_callback = CURLcode function(char *buffer, size_t length); /** actually an OpenSSL SSL_CTX */ -alias CURLcode function(CURL *curl, /** easy handle */ - void *ssl_ctx, /** actually an - OpenSSL SSL_CTX */ - void *userptr - )curl_ssl_ctx_callback; +alias curl_ssl_ctx_callback = + CURLcode function(CURL *curl, /** easy handle */ + void *ssl_ctx, /** actually an OpenSSL SSL_CTX */ + void *userptr + ); /// enum CurlProxy { @@ -542,7 +545,7 @@ enum CurlProxy { in 7.18.0 */ } /// -alias int curl_proxytype; +alias curl_proxytype = int; /// enum CurlAuth : long { @@ -552,7 +555,7 @@ enum CurlAuth : long { gssnegotiate = 4, /** GSS-Negotiate */ ntlm = 8, /** NTLM */ digest_ie = 16, /** Digest with IE flavour */ - only = 2147483648, /** used together with a single other + only = 2_147_483_648, /** used together with a single other type to force no auth or just that single type */ any = -17, /* (~CURLAUTH_DIGEST_IE) */ /** all fine types set */ @@ -583,8 +586,8 @@ enum CurlKHType /// extern (C) struct curl_khkey { - char *key; /** points to a zero-terminated string encoded with base64 - if len is zero, otherwise to the "raw" data */ + const(char) *key; /** points to a zero-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ size_t len; /// CurlKHType keytype; /// } @@ -610,12 +613,13 @@ enum CurlKHMatch { } /// -alias int function(CURL *easy, /** easy handle */ - curl_khkey *knownkey, /** known */ - curl_khkey *foundkey, /** found */ - CurlKHMatch m, /** libcurl's view on the keys */ - void *clientp /** custom pointer passed from app */ - )curl_sshkeycallback; +alias curl_sshkeycallback = + int function(CURL *easy, /** easy handle */ + curl_khkey *knownkey, /** known */ + curl_khkey *foundkey, /** found */ + CurlKHMatch m, /** libcurl's view on the keys */ + void *clientp /** custom pointer passed from app */ + ); /** parameter for the CURLOPT_USE_SSL option */ enum CurlUseSSL { @@ -626,7 +630,7 @@ enum CurlUseSSL { last /** not an option, never use */ } /// -alias int curl_usessl; +alias curl_usessl = int; /** parameter for the CURLOPT_FTP_SSL_CCC option */ enum CurlFtpSSL { @@ -636,7 +640,7 @@ enum CurlFtpSSL { ccc_last /** not an option, never use */ } /// -alias int curl_ftpccc; +alias curl_ftpccc = int; /** parameter for the CURLOPT_FTPSSLAUTH option */ enum CurlFtpAuth { @@ -646,7 +650,7 @@ enum CurlFtpAuth { last /** not an option, never use */ } /// -alias int curl_ftpauth; +alias curl_ftpauth = int; /** parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ enum CurlFtp { @@ -658,7 +662,7 @@ enum CurlFtp { create_dir_last /** not an option, never use */ } /// -alias int curl_ftpcreatedir; +alias curl_ftpcreatedir = int; /** parameter for the CURLOPT_FTP_FILEMETHOD option */ enum CurlFtpMethod { @@ -669,7 +673,7 @@ enum CurlFtpMethod { last /** not an option, never use */ } /// -alias int curl_ftpmethod; +alias curl_ftpmethod = int; /** CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ enum CurlProto { @@ -687,18 +691,18 @@ enum CurlProto { tftp = 2048, /// imap = 4096, /// imaps = 8192, /// - pop3 = 16384, /// - pop3s = 32768, /// - smtp = 65536, /// - smtps = 131072, /// - rtsp = 262144, /// - rtmp = 524288, /// - rtmpt = 1048576, /// - rtmpe = 2097152, /// - rtmpte = 4194304, /// - rtmps = 8388608, /// - rtmpts = 16777216, /// - gopher = 33554432, /// + pop3 = 16_384, /// + pop3s = 32_768, /// + smtp = 65_536, /// + smtps = 131_072, /// + rtsp = 262_144, /// + rtmp = 524_288, /// + rtmpt = 1_048_576, /// + rtmpe = 2_097_152, /// + rtmpte = 4_194_304, /// + rtmps = 8_388_608, /// + rtmpts = 16_777_216, /// + gopher = 33_554_432, /// all = -1 /** enable everything */ } @@ -706,36 +710,36 @@ enum CurlProto { but 32 */ enum CURLOPTTYPE_LONG = 0; /// ditto -enum CURLOPTTYPE_OBJECTPOINT = 10000; +enum CURLOPTTYPE_OBJECTPOINT = 10_000; /// ditto -enum CURLOPTTYPE_FUNCTIONPOINT = 20000; +enum CURLOPTTYPE_FUNCTIONPOINT = 20_000; /// ditto -enum CURLOPTTYPE_OFF_T = 30000; -/** name is uppercase CURLOPT_, - type is one of the defined CURLOPTTYPE_ +enum CURLOPTTYPE_OFF_T = 30_000; +/** name is uppercase CURLOPT_$(LT)name$(GT), + type is one of the defined CURLOPTTYPE_$(LT)type$(GT) number is unique identifier */ /** The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -alias CURLOPTTYPE_LONG LONG; +alias LONG = CURLOPTTYPE_LONG; /// ditto -alias CURLOPTTYPE_OBJECTPOINT OBJECTPOINT; +alias OBJECTPOINT = CURLOPTTYPE_OBJECTPOINT; /// ditto -alias CURLOPTTYPE_FUNCTIONPOINT FUNCTIONPOINT; +alias FUNCTIONPOINT = CURLOPTTYPE_FUNCTIONPOINT; /// ditto -alias CURLOPTTYPE_OFF_T OFF_T; +alias OFF_T = CURLOPTTYPE_OFF_T; /// enum CurlOption { /** This is the FILE * or void * the regular output should be written to. */ - file = 10001, + file = 10_001, /** The full URL to get/put */ url, /** Port number to connect to, if other than default. */ port = 3, /** Name of proxy to use. */ - proxy = 10004, + proxy = 10_004, /** "name:password" to use when fetching. */ userpwd, /** "name:password" to use with proxy. */ @@ -745,13 +749,13 @@ enum CurlOption { /** not used */ /** Specified file stream to upload from (use as input): */ - infile = 10009, + infile = 10_009, /** Buffer to receive error messages in, must be at least CURL_ERROR_SIZE * bytes big. If this is not used, error messages go to stderr instead: */ errorbuffer, /** Function that will be called to store the output (instead of fwrite). The * parameters will use fwrite() syntax, make sure to follow them. */ - writefunction = 20011, + writefunction = 20_011, /** Function that will be called to read the input (instead of fread). The * parameters will use fread() syntax, make sure to follow them. */ readfunction, @@ -768,7 +772,7 @@ enum CurlOption { */ infilesize, /** POST static input fields. */ - postfields = 10015, + postfields = 10_015, /** Set the referrer page (needed by some CGIs) */ referer, /** Set the FTP PORT string (interface name, named or numerical IP address) @@ -794,7 +798,7 @@ enum CurlOption { */ resume_from, /** Set cookie in request: */ - cookie = 10022, + cookie = 10_022, /** This points to a linked list of headers, struct curl_slist kind */ httpheader, /** This points to a linked list of post entries, struct curl_httppost */ @@ -806,13 +810,13 @@ enum CurlOption { /** send TYPE parameter? */ crlf = 27, /** send linked-list of QUOTE commands */ - quote = 10028, + quote = 10_028, /** send FILE * or void * to store headers to, if you use a callback it is simply passed to the callback unmodified */ writeheader, /** point to a file to read the initial cookies from, also enables "cookie awareness" */ - cookiefile = 10031, + cookiefile = 10_031, /** What version to specifically try to use. See CURL_SSLVERSION defines below. */ sslversion = 32, @@ -821,19 +825,19 @@ enum CurlOption { /** Time to use with the above condition. Specified in number of seconds since 1 Jan 1970 */ timevalue, - /** 35 = OBSOLETE */ + /* 35 = OBSOLETE */ /** Custom request, for customizing the get command like HTTP: DELETE, TRACE and others FTP: to use a different list command */ - customrequest = 10036, + customrequest = 10_036, /** HTTP request, for odd commands like DELETE, TRACE and others */ stderr, - /** 38 is not used */ + /* 38 is not used */ /** send linked-list of post-transfer QUOTE commands */ - postquote = 10039, + postquote = 10_039, /** Pass a pointer to string of the output using full variable-replacement as described elsewhere. */ writeinfo, @@ -852,14 +856,14 @@ enum CurlOption { followlocation, /** use Location: Luke! */ transfertext, /** transfer data in text/ASCII format */ put, /** HTTP PUT */ - /** 55 = OBSOLETE */ + /* 55 = OBSOLETE */ /** Function that will be called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ - progressfunction = 20056, + progressfunction = 20_056, /** Data passed to the progress callback */ - progressdata = 10057, + progressdata = 10_057, /** We want the referrer field set automatically when following locations */ autoreferer = 58, /** Port of the proxy, can be set in the proxy string as well with: @@ -870,7 +874,7 @@ enum CurlOption { /** tunnel non-http operations through a HTTP proxy */ httpproxytunnel, /** Set the interface string to use as outgoing network interface */ - intrface = 10062, + intrface = 10_062, /** Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string * is set but doesn't match one of these, 'private' will be used. */ @@ -879,9 +883,9 @@ enum CurlOption { ssl_verifypeer = 64, /** The CApath or CAfile used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - cainfo = 10065, - /** 66 = OBSOLETE */ - /** 67 = OBSOLETE */ + cainfo = 10_065, + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ /** Maximum number of http redirects to follow */ maxredirs = 68, @@ -889,13 +893,13 @@ enum CurlOption { possible)! Pass a zero to shut it off. */ filetime, /** This points to a linked list of telnet options */ - telnetoptions = 10070, + telnetoptions = 10_070, /** Max amount of cached alive connections */ maxconnects = 71, /** What policy to use when closing connections when the cache is filled up */ closepolicy, - /** 73 = OBSOLETE */ + /* 73 = OBSOLETE */ /** Set to explicitly use a new connection for the upcoming transfer. Do not use this unless you're absolutely sure of this, as it makes the @@ -907,7 +911,7 @@ enum CurlOption { forbid_reuse, /** Set to a file name that contains random data for libcurl to use to seed the random engine when doing SSL connects. */ - random_file = 10076, + random_file = 10_076, /** Set to the Entropy Gathering Daemon socket pathname */ egdsocket, /** Time-out connect operations after this amount of seconds, if connects @@ -916,7 +920,7 @@ enum CurlOption { connecttimeout = 78, /** Function that will be called to store headers (instead of fwrite). The * parameters will use fwrite() syntax, make sure to follow them. */ - headerfunction = 20079, + headerfunction = 20_079, /** Set this to force the HTTP request to get back to GET. Only really usable if POST, PUT or a custom request have been used first. */ @@ -927,7 +931,7 @@ enum CurlOption { ssl_verifyhost, /** Specify which file name to write all known cookies in after completed operation. Set file name to "-" (dash) to make it go to stdout. */ - cookiejar = 10082, + cookiejar = 10_082, /** Specify which SSL ciphers to use */ ssl_cipher_list, /** Specify which HTTP version to use! This must be set to one of the @@ -938,7 +942,7 @@ enum CurlOption { PASV command. */ ftp_use_epsv, /** type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ - sslcerttype = 10086, + sslcerttype = 10_086, /** name of the file keeping your private SSL-key */ sslkey, /** type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ @@ -954,16 +958,16 @@ enum CurlOption { /** DNS cache timeout */ dns_cache_timeout, /** send linked-list of pre-transfer QUOTE commands */ - prequote = 10093, + prequote = 10_093, /** set the debug function */ - debugfunction = 20094, + debugfunction = 20_094, /** set the data for the debug function */ - debugdata = 10095, + debugdata = 10_095, /** mark this as start of a cookie session */ cookiesession = 96, /** The CApath directory used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - capath = 10097, + capath = 10_097, /** Instruct libcurl to use a smaller receive buffer */ buffersize = 98, /** Instruct libcurl to not use any signal/alarm handlers, even when using @@ -971,13 +975,13 @@ enum CurlOption { See libcurl-the-guide for more background information. */ nosignal, /** Provide a CURLShare for mutexing non-ts data */ - share = 10100, + share = 10_100, /** indicates type of proxy. accepted values are CURLPROXY_HTTP (default), CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ proxytype = 101, /** Set the Accept-Encoding string. Use this to tell a server you would like the response to be compressed. */ - encoding = 10102, + encoding = 10_102, /** Set pointer to private data */ private_opt, /** Set aliases for HTTP 200 in the HTTP Response header */ @@ -997,10 +1001,10 @@ enum CurlOption { /** Set the ssl context callback function, currently only for OpenSSL ssl_ctx in second argument. The function must be matching the curl_ssl_ctx_callback proto. */ - ssl_ctx_function = 20108, + ssl_ctx_function = 20_108, /** Set the userdata for the ssl context callback function's third argument */ - ssl_ctx_data = 10109, + ssl_ctx_data = 10_109, /** FTP Option that causes missing dirs to be created on the remote server. In 7.19.4 we introduced the convenience enums for this option using the CURLFTP_CREATE_DIR prefix. @@ -1028,7 +1032,7 @@ enum CurlOption { /** See the comment for INFILESIZE above, but in short, specifies * the size of the file being uploaded. -1 means unknown. */ - infilesize_large = 30115, + infilesize_large = 30_115, /** Sets the continuation offset. There is also a LONG version of this; * look above for RESUME_FROM. */ @@ -1041,7 +1045,7 @@ enum CurlOption { to parse (using the CURLOPT_NETRC option). If not set, libcurl will do a poor attempt to find the user's home directory and check for a .netrc file in there. */ - netrc_file = 10118, + netrc_file = 10_118, /** Enable SSL/TLS for FTP, pick one of: CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise CURLFTPSSL_CONTROL - SSL for the control connection or fail @@ -1049,16 +1053,16 @@ enum CurlOption { */ use_ssl = 119, /** The _LARGE version of the standard POSTFIELDSIZE option */ - postfieldsize_large = 30120, + postfieldsize_large = 30_120, /** Enable/disable the TCP Nagle algorithm */ tcp_nodelay = 121, - /** 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /** 123 OBSOLETE. Gone in 7.16.0 */ - /** 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /** 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /** 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ - /** 127 OBSOLETE. Gone in 7.16.0 */ - /** 128 OBSOLETE. Gone in 7.16.0 */ + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ /** When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option can be used to change libcurl's default action which is to first try @@ -1071,14 +1075,14 @@ enum CurlOption { CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL */ ftpsslauth = 129, - ioctlfunction = 20130, /// - ioctldata = 10131, /// - /** 132 OBSOLETE. Gone in 7.16.0 */ - /** 133 OBSOLETE. Gone in 7.16.0 */ + ioctlfunction = 20_130, /// + ioctldata = 10_131, /// + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ /** zero terminated string for pass on to the FTP server when asked for "account" info */ - ftp_account = 10134, + ftp_account = 10_134, /** feed cookies into cookie engine */ cookielist, /** ignore Content-Length */ @@ -1102,7 +1106,7 @@ enum CurlOption { connect_only, /** Function that will be called to convert from the network encoding (instead of using the iconv calls in libcurl) */ - conv_from_network_function = 20142, + conv_from_network_function = 20_142, /** Function that will be called to convert to the network encoding (instead of using the iconv calls in libcurl) */ conv_to_network_function, @@ -1110,22 +1114,23 @@ enum CurlOption { (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ conv_from_utf8_function, - /** if the connection proceeds too quickly then need to slow it down */ + /** If the connection proceeds too quickly then need to slow it down */ + /** */ /** limit-rate: maximum number of bytes per second to send or receive */ - max_send_speed_large = 30145, + max_send_speed_large = 30_145, max_recv_speed_large, /// ditto /** Pointer to command string to send if USER/PASS fails. */ - ftp_alternative_to_user = 10147, + ftp_alternative_to_user = 10_147, /** callback function for setting socket options */ - sockoptfunction = 20148, - sockoptdata = 10149, + sockoptfunction = 20_148, + sockoptdata = 10_149, /** set to 0 to disable session ID re-use for this transfer, default is enabled (== 1) */ ssl_sessionid_cache = 150, /** allowed SSH authentication methods */ ssh_auth_types, /** Used by scp/sftp to do public/private key authentication */ - ssh_public_keyfile = 10152, + ssh_public_keyfile = 10_152, ssh_private_keyfile, /** Send CCC (Clear Command Channel) after authentication */ ftp_ssl_ccc = 154, @@ -1144,20 +1149,20 @@ enum CurlOption { of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ postredir, /** used by scp/sftp to verify the host's public key */ - ssh_host_public_key_md5 = 10162, + ssh_host_public_key_md5 = 10_162, /** Callback function for opening socket (instead of socket(2)). Optionally, callback is able change the address or refuse to connect returning CURL_SOCKET_BAD. The callback should have type curl_opensocket_callback */ - opensocketfunction = 20163, - opensocketdata = 10164, /// ditto + opensocketfunction = 20_163, + opensocketdata = 10_164, /// ditto /** POST volatile input fields. */ copypostfields, - /** set transfer mode (;type=) when doing FTP via an HTTP proxy */ + /** set transfer mode (;type=$(LT)a|i$(GT)) when doing FTP via an HTTP proxy */ proxy_transfer_mode = 166, /** Callback function for seeking in the input stream */ - seekfunction = 20167, - seekdata = 10168, /// ditto + seekfunction = 20_167, + seekdata = 10_168, /// ditto /** CRL file */ crlfile, /** Issuer certificate */ @@ -1169,7 +1174,7 @@ enum CurlOption { working with OpenSSL-powered builds. */ certinfo, /** "name" and "pwd" to use when fetching. */ - username = 10173, + username = 10_173, password, /// ditto /** "name" and "pwd" to use with Proxy when fetching. */ proxyusername, @@ -1185,7 +1190,7 @@ enum CurlOption { /** block size for TFTP transfers */ tftp_blksize = 178, /** Socks Service */ - socks5_gssapi_service = 10179, + socks5_gssapi_service = 10_179, /** Socks Service */ socks5_gssapi_nec = 180, /** set the bitmask for the protocols that are allowed to be used for the @@ -1199,12 +1204,12 @@ enum CurlOption { to all protocols except FILE and SCP. */ redir_protocols, /** set the SSH knownhost file name to use */ - ssh_knownhosts = 10183, + ssh_knownhosts = 10_183, /** set the SSH host key callback, must point to a curl_sshkeycallback function */ - ssh_keyfunction = 20184, + ssh_keyfunction = 20_184, /** set the SSH host key callback custom pointer */ - ssh_keydata = 10185, + ssh_keydata = 10_185, /** set the SMTP mail originator */ mail_from, /** set the SMTP mail receiver(s) */ @@ -1214,7 +1219,7 @@ enum CurlOption { /** RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ rtsp_request, /** The RTSP session identifier */ - rtsp_session_id = 10190, + rtsp_session_id = 10_190, /** The RTSP stream URI */ rtsp_stream_uri, /** The Transport: header to use in RTSP requests */ @@ -1224,21 +1229,21 @@ enum CurlOption { /** Manually initialize the server RTSP CSeq for this handle */ rtsp_server_cseq, /** The stream to pass to INTERLEAVEFUNCTION. */ - interleavedata = 10195, + interleavedata = 10_195, /** Let the application define a custom write method for RTP data */ - interleavefunction = 20196, + interleavefunction = 20_196, /** Turn on wildcard matching */ wildcardmatch = 197, /** Directory matching callback called before downloading of an individual file (chunk) started */ - chunk_bgn_function = 20198, + chunk_bgn_function = 20_198, /** Directory matching callback called after the file (chunk) was downloaded, or skipped */ chunk_end_function, /** Change match (fnmatch-like) callback for wildcard matching */ fnmatch_function, /** Let the application define custom chunk data pointer */ - chunk_data = 10201, + chunk_data = 10_201, /** FNMATCH_FUNCTION user pointer */ fnmatch_data, /** send linked-list of name:port:address sets */ @@ -1258,7 +1263,7 @@ enum CurlOption { rtspheader = httpheader, /// ditto } /// -alias int CURLoption; +alias CURLoption = int; /// enum CURLOPT_SERVER_RESPONSE_TIMEOUT = CurlOption.ftp_response_timeout; @@ -1354,15 +1359,15 @@ enum CurlTimeCond { last /// } /// -alias int curl_TimeCond; +alias curl_TimeCond = int; /** curl_strequal() and curl_strnequal() are subject for removal in a future libcurl, see lib/README.curlx for details */ extern (C) { -int curl_strequal(char *s1, char *s2); +int curl_strequal(in char *s1, in char *s2); /// ditto -int curl_strnequal(char *s1, char *s2, size_t n); +int curl_strnequal(in char *s1, in char *s2, size_t n); } enum CurlForm { nothing, /********** the first one is unused ************/ @@ -1387,30 +1392,34 @@ enum CurlForm { stream, lastentry /** the last unused */ } -alias int CURLformoption; +alias CURLformoption = int; /** structure to be used as parameter for CURLFORM_ARRAY */ extern (C) struct curl_forms { CURLformoption option; /// - char *value; /// + const(char) *value; /// } -/** use this for multipart formpost building */ -/** Returns code for curl_formadd() +/** Use this for multipart formpost building + * + * Returns code for curl_formadd() * * Returns: - * CURL_FORMADD_OK on success - * CURL_FORMADD_MEMORY if the FormInfo allocation fails - * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form - * CURL_FORMADD_NULL if a null pointer was given for a char - * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed - * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used - * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) - * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated - * CURL_FORMADD_MEMORY if some allocation for string copying failed. - * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + * $(UL + * $(LI CURL_FORMADD_OK on success ) + * $(LI CURL_FORMADD_MEMORY if the FormInfo allocation fails ) + * $(LI CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form ) + * $(LI CURL_FORMADD_NULL if a null pointer was given for a char ) + * $(LI CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed ) + * $(LI CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used ) + * $(LI CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) ) + * $(LI CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated ) + * $(LI CURL_FORMADD_MEMORY if some allocation for string copying failed. ) + * $(LI CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array ) + * ) * ***************************************************************************/ enum CurlFormAdd { @@ -1425,7 +1434,7 @@ enum CurlFormAdd { last /// } /// -alias int CURLFORMcode; +alias CURLFORMcode = int; extern (C) { @@ -1448,7 +1457,7 @@ CURLFORMcode curl_formadd(curl_httppost **httppost, curl_httppost **last_post,. * Should return the buffer length passed to it as the argument "len" on * success. */ -alias size_t function(void *arg, char *buf, size_t len)curl_formget_callback; +alias curl_formget_callback = size_t function(void *arg, in char *buf, size_t len); /** * Name: curl_formget() @@ -1498,10 +1507,10 @@ char * curl_version(); * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -char * curl_easy_escape(CURL *handle, char *string, int length); +char * curl_easy_escape(CURL *handle, in char *string, int length); /** the previous version: */ -char * curl_escape(char *string, int length); +char * curl_escape(in char *string, int length); /** @@ -1515,10 +1524,10 @@ char * curl_escape(char *string, int length); * Conversion Note: On non-ASCII platforms the ASCII %XX codes are * converted into the host encoding. */ -char * curl_easy_unescape(CURL *handle, char *string, int length, int *outlength); +char * curl_easy_unescape(CURL *handle, in char *string, int length, int *outlength); /** the previous version */ -char * curl_unescape(char *string, int length); +char * curl_unescape(in char *string, int length); /** * Name: curl_free() @@ -1555,7 +1564,14 @@ CURLcode curl_global_init(c_long flags); * callback routines with be invoked by this library instead of the system * memory management routines like malloc, free etc. */ -CURLcode curl_global_init_mem(c_long flags, curl_malloc_callback m, curl_free_callback f, curl_realloc_callback r, curl_strdup_callback s, curl_calloc_callback c); +CURLcode curl_global_init_mem( + c_long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c +); /** * Name: curl_global_cleanup() @@ -1636,9 +1652,9 @@ enum CURLINFO_TYPEMASK = 0xf00000; /// enum CurlInfo { none, /// - effective_url = 1048577, /// - response_code = 2097154, /// - total_time = 3145731, /// + effective_url = 1_048_577, /// + response_code = 2_097_154, /// + total_time = 3_145_731, /// namelookup_time, /// connect_time, /// pretransfer_time, /// @@ -1646,43 +1662,43 @@ enum CurlInfo { size_download, /// speed_download, /// speed_upload, /// - header_size = 2097163, /// + header_size = 2_097_163, /// request_size, /// ssl_verifyresult, /// filetime, /// - content_length_download = 3145743, /// + content_length_download = 3_145_743, /// content_length_upload, /// starttransfer_time, /// - content_type = 1048594, /// - redirect_time = 3145747, /// - redirect_count = 2097172, /// - private_info = 1048597, /// - http_connectcode = 2097174, /// + content_type = 1_048_594, /// + redirect_time = 3_145_747, /// + redirect_count = 2_097_172, /// + private_info = 1_048_597, /// + http_connectcode = 2_097_174, /// httpauth_avail, /// proxyauth_avail, /// os_errno, /// num_connects, /// - ssl_engines = 4194331, /// + ssl_engines = 4_194_331, /// cookielist, /// - lastsocket = 2097181, /// - ftp_entry_path = 1048606, /// + lastsocket = 2_097_181, /// + ftp_entry_path = 1_048_606, /// redirect_url, /// primary_ip, /// - appconnect_time = 3145761, /// - certinfo = 4194338, /// - condition_unmet = 2097187, /// - rtsp_session_id = 1048612, /// - rtsp_client_cseq = 2097189, /// + appconnect_time = 3_145_761, /// + certinfo = 4_194_338, /// + condition_unmet = 2_097_187, /// + rtsp_session_id = 1_048_612, /// + rtsp_client_cseq = 2_097_189, /// rtsp_server_cseq, /// rtsp_cseq_recv, /// primary_port, /// - local_ip = 1048617, /// - local_port = 2097194, /// + local_ip = 1_048_617, /// + local_port = 2_097_194, /// /** Fill in new entries below here! */ lastone = 42 } /// -alias int CURLINFO; +alias CURLINFO = int; /** CURLINFO_RESPONSE_CODE is the new name for the option previously known as CURLINFO_HTTP_CODE */ @@ -1699,7 +1715,7 @@ enum CurlClosePolicy { last /// } /// -alias int curl_closepolicy; +alias curl_closepolicy = int; /// enum CurlGlobal { @@ -1730,7 +1746,7 @@ enum CurlLockData { last /// } /// -alias int curl_lock_data; +alias curl_lock_data = int; /** Different lock access types */ enum CurlLockAccess { @@ -1740,15 +1756,15 @@ enum CurlLockAccess { last /** never use */ } /// -alias int curl_lock_access; +alias curl_lock_access = int; /// -alias void function(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr)curl_lock_function; +alias curl_lock_function = void function(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr); /// -alias void function(CURL *handle, curl_lock_data data, void *userptr)curl_unlock_function; +alias curl_unlock_function = void function(CURL *handle, curl_lock_data data, void *userptr); /// -alias void CURLSH; +alias CURLSH = void; /// enum CurlShError { @@ -1760,7 +1776,7 @@ enum CurlShError { last /** never use */ } /// -alias int CURLSHcode; +alias CURLSHcode = int; /** pass in a user data pointer used in the lock/unlock callback functions */ @@ -1775,7 +1791,7 @@ enum CurlShOption { last /** never use */ } /// -alias int CURLSHoption; +alias CURLSHoption = int; extern (C) { /// @@ -1799,7 +1815,7 @@ enum CurlVer { last /// } /// -alias int CURLversion; +alias CURLversion = int; /** The 'CURLVERSION_NOW' is the symbolic name meant to be used by basically all programs ever that want to get version information. It is @@ -1812,27 +1828,27 @@ enum CURLVERSION_NOW = CurlVer.fourth; extern (C) struct _N28 { CURLversion age; /** age of the returned struct */ - char *version_; /** LIBCURL_VERSION */ + const(char) *version_; /** LIBCURL_VERSION */ uint version_num; /** LIBCURL_VERSION_NUM */ - char *host; /** OS/host/cpu/machine when configured */ + const(char) *host; /** OS/host/cpu/machine when configured */ int features; /** bitmask, see defines below */ - char *ssl_version; /** human readable string */ + const(char) *ssl_version; /** human readable string */ c_long ssl_version_num; /** not used anymore, always 0 */ - char *libz_version; /** human readable string */ + const(char) *libz_version; /** human readable string */ /** protocols is terminated by an entry with a NULL protoname */ - char **protocols; + const(char) **protocols; /** The fields below this were added in CURLVERSION_SECOND */ - char *ares; + const(char) *ares; int ares_num; /** This field was added in CURLVERSION_THIRD */ - char *libidn; - /** These field were added in CURLVERSION_FOURTH */ + const(char) *libidn; + /** These field were added in CURLVERSION_FOURTH. */ /** Same as '_libiconv_version' if built with HAVE_ICONV */ int iconv_ver_num; - char *libssh_version; /** human readable string */ + const(char) *libssh_version; /** human readable string */ } /// -alias _N28 curl_version_info_data; +alias curl_version_info_data = _N28; /// // CURL_VERSION_* @@ -1851,7 +1867,7 @@ enum CurlVersion { sspi = 2048, /** SSPI is supported */ conv = 4096, /** character conversions supported */ curldebug = 8192, /** debug memory tracking supported */ - tlsauth_srp = 16384 /** TLS-SRP auth is supported */ + tlsauth_srp = 16_384 /** TLS-SRP auth is supported */ } extern (C) { @@ -2043,7 +2059,7 @@ extern (C) CURLcode curl_easy_send(CURL *curl, void *buffer, size_t buflen, siz ***************************************************************************/ /// -alias void CURLM; +alias CURLM = void; /// enum CurlM { @@ -2058,7 +2074,7 @@ enum CurlM { last, /// } /// -alias int CURLMcode; +alias CURLMcode = int; /** just to make code nicer when using curl_multi_socket() you can now check for CURLM_CALL_MULTI_SOCKET too in the same style it works for @@ -2074,7 +2090,7 @@ enum CurlMsg last, /** no used */ } /// -alias int CURLMSG; +alias CURLMSG = int; /// extern (C) union _N31 @@ -2129,9 +2145,15 @@ extern (C) CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_h */ /** tmp decl */ -alias int fd_set; +alias fd_set = int; /// -extern (C) CURLMcode curl_multi_fdset(CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd); +extern (C) CURLMcode curl_multi_fdset( + CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd +); /** * Name: curl_multi_perform() @@ -2222,7 +2244,7 @@ enum CurlPoll { } /// -alias CURL_SOCKET_BAD CURL_SOCKET_TIMEOUT; +alias CURL_SOCKET_TIMEOUT = CURL_SOCKET_BAD; /// enum CurlCSelect { @@ -2233,11 +2255,12 @@ enum CurlCSelect { extern (C) { /// - alias int function(CURL *easy, /** easy handle */ - curl_socket_t s, /** socket */ - int what, /** see above */ - void *userp, /** private callback pointer */ - void *socketp)curl_socket_callback; /** private socket pointer */ + alias curl_socket_callback = + int function(CURL *easy, /** easy handle */ + curl_socket_t s, /** socket */ + int what, /** see above */ + void *userp, /** private callback pointer */ + void *socketp); /** private socket pointer */ } /** @@ -2250,12 +2273,12 @@ extern (C) { * * Returns: The callback should return zero. */ -/** private callback pointer */ extern (C) { - alias int function(CURLM *multi, /** multi handle */ - c_long timeout_ms, /** see above */ - void *userp) curl_multi_timer_callback; /** private callback pointer */ + alias curl_multi_timer_callback = + int function(CURLM *multi, /** multi handle */ + c_long timeout_ms, /** see above */ + void *userp); /** private callback pointer */ /// ditto CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); /// ditto @@ -2281,17 +2304,16 @@ extern (C) CURLMcode curl_multi_timeout(CURLM *multi_handle, c_long *millisecon /// enum CurlMOption { - socketfunction = 20001, /** This is the socket callback function pointer */ - socketdata = 10002, /** This is the argument passed to the socket callback */ + socketfunction = 20_001, /** This is the socket callback function pointer */ + socketdata = 10_002, /** This is the argument passed to the socket callback */ pipelining = 3, /** set to 1 to enable pipelining for this multi handle */ - timerfunction = 20004, /** This is the timer callback function pointer */ - timerdata = 10005, /** This is the argument passed to the timer callback */ + timerfunction = 20_004, /** This is the timer callback function pointer */ + timerdata = 10_005, /** This is the argument passed to the timer callback */ maxconnects = 6, /** maximum number of entries in the connection cache */ lastentry /// } /// -alias int CURLMoption; - +alias CURLMoption = int; /** * Name: curl_multi_setopt() @@ -2302,7 +2324,6 @@ alias int CURLMoption; */ extern (C) CURLMcode curl_multi_setopt(CURLM *multi_handle, CURLMoption option,...); - /** * Name: curl_multi_assign() * diff --git a/etc/c/odbc/sql.d b/etc/c/odbc/sql.d new file mode 100644 index 00000000000..da403a6aa47 --- /dev/null +++ b/etc/c/odbc/sql.d @@ -0,0 +1,1423 @@ +/** +Declarations for interfacing with the ODBC library. + +Adapted with minimal changes from the work of David L. Davis +(refer to the $(HTTP +forum.dlang.org/post/cfk7ql$(DOLLAR)1p4n$(DOLLAR)1@digitaldaemon.com, +original announcement)). + +`etc.c.odbc.sql` is the the main include for ODBC v3.0+ Core functions, +corresponding to the `sql.h` C header file. It `import`s `public`ly +`etc.c.odbc.sqltypes` for conformity with the C header. + +Note: The ODBC library itself not a part of the `dmd` distribution (and +typically not a part of the distribution packages of other compilers +such as `gdc` and `ldc`). To use ODBC, install it per the vendor- and +platform-specific instructions and then use the appropriate command-line +flags (e.g. for dmd, `-L-lodbc` on Posix and `-Lodbc32.lib` on Windows) to link +with the ODBC library. On Windows, using $(D pragma(lib, "odbc32")) in D +code at top level is also appropriate. + +See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, + ODBC API Reference on MSN Online) +*/ + +module etc.c.odbc.sql; + +public import etc.c.odbc.sqltypes; + +extern (Windows): + +// * special length/indicator values * +enum int SQL_NULL_DATA = (-1); +enum int SQL_DATA_AT_EXEC = (-2); + +// * return values from functions * +enum +{ + SQL_SUCCESS = 0, + SQL_SUCCESS_WITH_INFO = 1, + SQL_NO_DATA = 100, + SQL_ERROR = (-1), + SQL_INVALID_HANDLE = (-2), + SQL_STILL_EXECUTING = 2, + SQL_NEED_DATA = 99 +} + +/* test for SQL_SUCCESS or SQL_SUCCESS_WITH_INFO */ +bool SQL_SUCCEEDED()(uint rc) { return (rc & ~1U) == 0; } + +enum +{ + // * flags for null-terminated string * + SQL_NTS = (-3), + SQL_NTSL = (-3L), + + // * maximum message length * + SQL_MAX_MESSAGE_LENGTH = 512, + + // * date/time length constants * + SQL_DATE_LEN = 10, + SQL_TIME_LEN = 8, /* add P+1 if precision is nonzero */ + SQL_TIMESTAMP_LEN = 19, /* add P+1 if precision is nonzero */ + + // * handle type identifiers * + SQL_HANDLE_ENV = 1, + SQL_HANDLE_DBC = 2, + SQL_HANDLE_STMT = 3, + SQL_HANDLE_DESC = 4, + + // * environment attribute * + SQL_ATTR_OUTPUT_NTS = 10_001, + + // * connection attributes * + SQL_ATTR_AUTO_IPD = 10_001, + SQL_ATTR_METADATA_ID = 10_014, + + // * statement attributes * + SQL_ATTR_APP_ROW_DESC = 10_010, + SQL_ATTR_APP_PARAM_DESC = 10_011, + SQL_ATTR_IMP_ROW_DESC = 10_012, + SQL_ATTR_IMP_PARAM_DESC = 10_013, + SQL_ATTR_CURSOR_SCROLLABLE = (-1), + SQL_ATTR_CURSOR_SENSITIVITY = (-2), + + // * SQL_ATTR_CURSOR_SCROLLABLE values * + SQL_NONSCROLLABLE = 0, + SQL_SCROLLABLE = 1, + + // * identifiers of fields in the SQL descriptor * + SQL_DESC_COUNT = 1001, + SQL_DESC_TYPE = 1002, + SQL_DESC_LENGTH = 1003, + SQL_DESC_OCTET_LENGTH_PTR = 1004, + SQL_DESC_PRECISION = 1005, + SQL_DESC_SCALE = 1006, + SQL_DESC_DATETIME_INTERVAL_CODE = 1007, + SQL_DESC_NULLABLE = 1008, + SQL_DESC_INDICATOR_PTR = 1009, + SQL_DESC_DATA_PTR = 1010, + SQL_DESC_NAME = 1011, + SQL_DESC_UNNAMED = 1012, + SQL_DESC_OCTET_LENGTH = 1013, + SQL_DESC_ALLOC_TYPE = 1099 +} + +// * identifiers of fields in the diagnostics area * +enum +{ + SQL_DIAG_RETURNCODE = 1, + SQL_DIAG_NUMBER = 2, + SQL_DIAG_ROW_COUNT = 3, + SQL_DIAG_SQLSTATE = 4, + SQL_DIAG_NATIVE = 5, + SQL_DIAG_MESSAGE_TEXT = 6, + SQL_DIAG_DYNAMIC_FUNCTION = 7, + SQL_DIAG_CLASS_ORIGIN = 8, + SQL_DIAG_SUBCLASS_ORIGIN = 9, + SQL_DIAG_CONNECTION_NAME = 10, + SQL_DIAG_SERVER_NAME = 11, + SQL_DIAG_DYNAMIC_FUNCTION_CODE = 12 +} + +// * dynamic function codes * +enum +{ + SQL_DIAG_ALTER_DOMAIN = 3, + SQL_DIAG_ALTER_TABLE = 4, + SQL_DIAG_CALL = 7, + SQL_DIAG_CREATE_ASSERTION = 6, + SQL_DIAG_CREATE_CHARACTER_SET = 8, + SQL_DIAG_CREATE_COLLATION = 10, + SQL_DIAG_CREATE_DOMAIN = 23, + SQL_DIAG_CREATE_INDEX = (-1), + SQL_DIAG_CREATE_SCHEMA = 64, + SQL_DIAG_CREATE_TABLE = 77, + SQL_DIAG_CREATE_TRANSLATION = 79, + SQL_DIAG_CREATE_VIEW = 84, + SQL_DIAG_DELETE_WHERE = 19, + SQL_DIAG_DROP_ASSERTION = 24, + SQL_DIAG_DROP_CHARACTER_SET = 25, + SQL_DIAG_DROP_COLLATION = 26, + SQL_DIAG_DROP_DOMAIN = 27, + SQL_DIAG_DROP_INDEX = (-2), + SQL_DIAG_DROP_SCHEMA = 31, + SQL_DIAG_DROP_TABLE = 32, + SQL_DIAG_DROP_TRANSLATION = 33, + SQL_DIAG_DROP_VIEW = 36, + SQL_DIAG_DYNAMIC_DELETE_CURSOR = 38, + SQL_DIAG_DYNAMIC_UPDATE_CURSOR = 81, + SQL_DIAG_GRANT = 48, + SQL_DIAG_INSERT = 50, + SQL_DIAG_REVOKE = 59, + SQL_DIAG_SELECT_CURSOR = 85, + SQL_DIAG_UNKNOWN_STATEMENT = 0, + SQL_DIAG_UPDATE_WHERE = 82 +} + +enum +{ + // * SQL data type codes * + SQL_UNKNOWN_TYPE = 0, + SQL_CHAR = 1, + SQL_NUMERIC = 2, + SQL_DECIMAL = 3, + SQL_INTEGER = 4, + SQL_SMALLINT = 5, + SQL_FLOAT = 6, + SQL_REAL = 7, + SQL_DOUBLE = 8, + SQL_DATETIME = 9, + SQL_VARCHAR = 12, + + // * One-parameter shortcuts for date/time data types * + SQL_TYPE_DATE = 91, + SQL_TYPE_TIME = 92, + SQL_TYPE_TIMESTAMP = 93 +} + +// * Statement attribute values for cursor sensitivity * +enum +{ + SQL_UNSPECIFIED = 0, + SQL_INSENSITIVE = 1, + SQL_SENSITIVE = 2 +} + +// * GetTypeInfo() request for all data types * +enum +{ + SQL_ALL_TYPES = 0 +} + +// * Default conversion code for SQLBindCol(), SQLBindParam() and SQLGetData() * +enum { SQL_DEFAULT = 99 } + +/+ SQLGetData() code indicating that the application row descriptor + ' specifies the data type + +/ +enum +{ + SQL_ARD_TYPE = (-99) +} + +// * SQL date/time type subcodes * +enum +{ + SQL_CODE_DATE = 1, + SQL_CODE_TIME = 2, + SQL_CODE_TIMESTAMP = 3 +} + +// * CLI option values * +enum +{ + SQL_FALSE = 0, + SQL_TRUE = 1 +} + +// * values of NULLABLE field in descriptor * +enum +{ + SQL_NO_NULLS = 0, + SQL_NULLABLE = 1 +} + +/+ Value returned by SQLGetTypeInfo() to denote that it is + ' not known whether or not a data type supports null values. + +/ +enum { SQL_NULLABLE_UNKNOWN = 2 } + +/+ Values returned by SQLGetTypeInfo() to show WHERE clause + ' supported + +/ +enum +{ + SQL_PRED_NONE = 0, + SQL_PRED_CHAR = 1, + SQL_PRED_BASIC = 2 +} + +// * values of UNNAMED field in descriptor * +enum +{ + SQL_NAMED = 0, + SQL_UNNAMED = 1 +} + +// * values of ALLOC_TYPE field in descriptor * +enum +{ + SQL_DESC_ALLOC_AUTO = 1, + SQL_DESC_ALLOC_USER = 2 +} + +// * FreeStmt() options * +enum +{ + SQL_CLOSE = 0, + SQL_DROP = 1, + SQL_UNBIND = 2, + SQL_RESET_PARAMS = 3 +} + +// * Codes used for FetchOrientation in SQLFetchScroll(), and in SQLDataSources() * +enum +{ + SQL_FETCH_NEXT = 1, + SQL_FETCH_FIRST = 2 +} + +// * Other codes used for FetchOrientation in SQLFetchScroll() * +enum +{ + SQL_FETCH_LAST = 3, + SQL_FETCH_PRIOR = 4, + SQL_FETCH_ABSOLUTE = 5, + SQL_FETCH_RELATIVE = 6 +} + +// * SQLEndTran() options * +enum +{ + SQL_COMMIT = 0, + SQL_ROLLBACK = 1 +} + +// * null handles returned by SQLAllocHandle() * +enum SQLHANDLE SQL_NULL_HENV = cast(SQLHANDLE) 0; +enum SQLHANDLE SQL_NULL_HDBC = cast(SQLHANDLE) 0; +enum SQLHANDLE SQL_NULL_HSTMT = cast(SQLHANDLE) 0; +enum SQLHANDLE SQL_NULL_HDESC = cast(SQLHANDLE) 0; + +// * null handle used in place of parent handle when allocating HENV * +enum SQLHANDLE SQL_NULL_HANDLE = cast(SQLHANDLE) 0L; + +// * Values that may appear in the result set of SQLSpecialColumns() * +enum +{ + SQL_SCOPE_CURROW = 0, + SQL_SCOPE_TRANSACTION = 1, + SQL_SCOPE_SESSION = 2 +} + +enum +{ + SQL_PC_UNKNOWN = 0, + SQL_PC_NON_PSEUDO = 1, + SQL_PC_PSEUDO = 2 +} + +// * Reserved value for the IdentifierType argument of SQLSpecialColumns() * +enum +{ + SQL_ROW_IDENTIFIER = 1 +} + +// * Reserved values for UNIQUE argument of SQLStatistics() * +enum +{ + SQL_INDEX_UNIQUE = 0, + SQL_INDEX_ALL = 1, + + // * Values that may appear in the result set of SQLStatistics() * + SQL_INDEX_CLUSTERED = 1, + SQL_INDEX_HASHED = 2, + SQL_INDEX_OTHER = 3 +} + +// * SQLGetFunctions() values to identify ODBC APIs * +enum +{ + SQL_API_SQLALLOCCONNECT = 1, + SQL_API_SQLALLOCENV = 2, + SQL_API_SQLALLOCHANDLE = 1001, + SQL_API_SQLALLOCSTMT = 3, + SQL_API_SQLBINDCOL = 4, + SQL_API_SQLBINDPARAM = 1002, + SQL_API_SQLCANCEL = 5, + SQL_API_SQLCLOSECURSOR = 1003, + SQL_API_SQLCOLATTRIBUTE = 6, + SQL_API_SQLCOLUMNS = 40, + SQL_API_SQLCONNECT = 7, + SQL_API_SQLCOPYDESC = 1004, + SQL_API_SQLDATASOURCES = 57, + SQL_API_SQLDESCRIBECOL = 8, + SQL_API_SQLDISCONNECT = 9, + SQL_API_SQLENDTRAN = 1005, + SQL_API_SQLERROR = 10, + SQL_API_SQLEXECDIRECT = 11, + SQL_API_SQLEXECUTE = 12, + SQL_API_SQLFETCH = 13, + SQL_API_SQLFETCHSCROLL = 1021, + SQL_API_SQLFREECONNECT = 14, + SQL_API_SQLFREEENV = 15, + SQL_API_SQLFREEHANDLE = 1006, + SQL_API_SQLFREESTMT = 16, + SQL_API_SQLGETCONNECTATTR = 1007, + SQL_API_SQLGETCONNECTOPTION = 42, + SQL_API_SQLGETCURSORNAME = 17, + SQL_API_SQLGETDATA = 43, + SQL_API_SQLGETDESCFIELD = 1008, + SQL_API_SQLGETDESCREC = 1009, + SQL_API_SQLGETDIAGFIELD = 1010, + SQL_API_SQLGETDIAGREC = 1011, + SQL_API_SQLGETENVATTR = 1012, + SQL_API_SQLGETFUNCTIONS = 44, + SQL_API_SQLGETINFO = 45, + SQL_API_SQLGETSTMTATTR = 1014, + SQL_API_SQLGETSTMTOPTION = 46, + SQL_API_SQLGETTYPEINFO = 47, + SQL_API_SQLNUMRESULTCOLS = 18, + SQL_API_SQLPARAMDATA = 48, + SQL_API_SQLPREPARE = 19, + SQL_API_SQLPUTDATA = 49, + SQL_API_SQLROWCOUNT = 20, + SQL_API_SQLSETCONNECTATTR = 1016, + SQL_API_SQLSETCONNECTOPTION = 50, + SQL_API_SQLSETCURSORNAME = 21, + SQL_API_SQLSETDESCFIELD = 1017, + SQL_API_SQLSETDESCREC = 1018, + SQL_API_SQLSETENVATTR = 1019, + SQL_API_SQLSETPARAM = 22, + SQL_API_SQLSETSTMTATTR = 1020, + SQL_API_SQLSETSTMTOPTION = 51, + SQL_API_SQLSPECIALCOLUMNS = 52, + SQL_API_SQLSTATISTICS = 53, + SQL_API_SQLTABLES = 54, + SQL_API_SQLTRANSACT = 23 +} + +// * Information requested by SQLGetInfo() * +enum +{ + SQL_MAX_DRIVER_CONNECTIONS = 0, + SQL_MAXIMUM_DRIVER_CONNECTIONS = SQL_MAX_DRIVER_CONNECTIONS, + SQL_MAX_CONCURRENT_ACTIVITIES = 1, + SQL_MAXIMUM_CONCURRENT_ACTIVITIES = SQL_MAX_CONCURRENT_ACTIVITIES, + SQL_DATA_SOURCE_NAME = 2, + SQL_FETCH_DIRECTION = 8, + SQL_SERVER_NAME = 13, + SQL_SEARCH_PATTERN_ESCAPE = 14, + SQL_DBMS_NAME = 17, + SQL_DBMS_VER = 18, + SQL_ACCESSIBLE_TABLES = 19, + SQL_ACCESSIBLE_PROCEDURES = 20, + SQL_CURSOR_COMMIT_BEHAVIOR = 23, + SQL_DATA_SOURCE_READ_ONLY = 25, + SQL_DEFAULT_TXN_ISOLATION = 26, + SQL_IDENTIFIER_CASE = 28, + SQL_IDENTIFIER_QUOTE_CHAR = 29, + SQL_MAX_COLUMN_NAME_LEN = 30, + SQL_MAXIMUM_COLUMN_NAME_LENGTH = SQL_MAX_COLUMN_NAME_LEN, + SQL_MAX_CURSOR_NAME_LEN = 31, + SQL_MAXIMUM_CURSOR_NAME_LENGTH = SQL_MAX_CURSOR_NAME_LEN, + SQL_MAX_SCHEMA_NAME_LEN = 32, + SQL_MAXIMUM_SCHEMA_NAME_LENGTH = SQL_MAX_SCHEMA_NAME_LEN, + SQL_MAX_CATALOG_NAME_LEN = 34, + SQL_MAXIMUM_CATALOG_NAME_LENGTH = SQL_MAX_CATALOG_NAME_LEN, + SQL_MAX_TABLE_NAME_LEN = 35, + SQL_SCROLL_CONCURRENCY = 43, + SQL_TXN_CAPABLE = 46, + SQL_TRANSACTION_CAPABLE = SQL_TXN_CAPABLE, + SQL_USER_NAME = 47, + SQL_TXN_ISOLATION_OPTION = 72, + SQL_TRANSACTION_ISOLATION_OPTION = SQL_TXN_ISOLATION_OPTION, + SQL_INTEGRITY = 73, + SQL_GETDATA_EXTENSIONS = 81, + SQL_NULL_COLLATION = 85, + SQL_ALTER_TABLE = 86, + SQL_ORDER_BY_COLUMNS_IN_SELECT = 90, + SQL_SPECIAL_CHARACTERS = 94, + SQL_MAX_COLUMNS_IN_GROUP_BY = 97, + SQL_MAXIMUM_COLUMNS_IN_GROUP_BY = SQL_MAX_COLUMNS_IN_GROUP_BY, + SQL_MAX_COLUMNS_IN_INDEX = 98, + SQL_MAXIMUM_COLUMNS_IN_INDEX = SQL_MAX_COLUMNS_IN_INDEX, + SQL_MAX_COLUMNS_IN_ORDER_BY = 99, + SQL_MAXIMUM_COLUMNS_IN_ORDER_BY = SQL_MAX_COLUMNS_IN_ORDER_BY, + SQL_MAX_COLUMNS_IN_SELECT = 100, + SQL_MAXIMUM_COLUMNS_IN_SELECT = SQL_MAX_COLUMNS_IN_SELECT, + SQL_MAX_COLUMNS_IN_TABLE = 101, + SQL_MAX_INDEX_SIZE = 102, + SQL_MAXIMUM_INDEX_SIZE = SQL_MAX_INDEX_SIZE, + SQL_MAX_ROW_SIZE = 104, + SQL_MAXIMUM_ROW_SIZE = SQL_MAX_ROW_SIZE, + SQL_MAX_STATEMENT_LEN = 105, + SQL_MAXIMUM_STATEMENT_LENGTH = SQL_MAX_STATEMENT_LEN, + SQL_MAX_TABLES_IN_SELECT = 106, + SQL_MAXIMUM_TABLES_IN_SELECT = SQL_MAX_TABLES_IN_SELECT, + SQL_MAX_USER_NAME_LEN = 107, + SQL_MAXIMUM_USER_NAME_LENGTH = SQL_MAX_USER_NAME_LEN, + SQL_OJ_CAPABILITIES = 115, + SQL_OUTER_JOIN_CAPABILITIES = SQL_OJ_CAPABILITIES +} + +enum +{ + SQL_XOPEN_CLI_YEAR = 10_000, + SQL_CURSOR_SENSITIVITY = 10_001, + SQL_DESCRIBE_PARAMETER = 10_002, + SQL_CATALOG_NAME = 10_003, + SQL_COLLATION_SEQ = 10_004, + SQL_MAX_IDENTIFIER_LEN = 10_005, + SQL_MAXIMUM_IDENTIFIER_LENGTH = SQL_MAX_IDENTIFIER_LEN +} + +// * SQL_ALTER_TABLE bitmasks * +enum +{ + SQL_AT_ADD_COLUMN = 0x00000001L, + SQL_AT_DROP_COLUMN = 0x00000002L, + SQL_AT_ADD_CONSTRAINT = 0x00000008L +} + +/+ The following bitmasks are ODBC extensions and defined in sqlext.d +enum : ulong +{ + SQL_AT_COLUMN_SINGLE = 0x00000020L, + SQL_AT_ADD_COLUMN_DEFAULT = 0x00000040L, + SQL_AT_ADD_COLUMN_COLLATION = 0x00000080L, + SQL_AT_SET_COLUMN_DEFAULT = 0x00000100L, + SQL_AT_DROP_COLUMN_DEFAULT = 0x00000200L, + SQL_AT_DROP_COLUMN_CASCADE = 0x00000400L, + SQL_AT_DROP_COLUMN_RESTRICT = 0x00000800L, + SQL_AT_ADD_TABLE_CONSTRAINT = 0x00001000L, + SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE = 0x00002000L, + SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT = 0x00004000L, + SQL_AT_CONSTRAINT_NAME_DEFINITION = 0x00008000L, + SQL_AT_CONSTRAINT_INITIALLY_DEFERRED = 0x00010000L, + SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00020000L, + SQL_AT_CONSTRAINT_DEFERRABLE = 0x00040000L, + SQL_AT_CONSTRAINT_NON_DEFERRABLE = 0x00080000L +} ++/ + +// * SQL_ASYNC_MODE values * +enum +{ + SQL_AM_NONE = 0, + SQL_AM_CONNECTION = 1, + SQL_AM_STATEMENT = 2 +} + +// * SQL_CURSOR_COMMIT_BEHAVIOR values * +enum +{ + SQL_CB_DELETE = 0, + SQL_CB_CLOSE = 1, + SQL_CB_PRESERVE = 2 +} + +// * SQL_FETCH_DIRECTION bitmasks * +enum +{ + SQL_FD_FETCH_NEXT = 0x00000001L, + SQL_FD_FETCH_FIRST = 0x00000002L, + SQL_FD_FETCH_LAST = 0x00000004L, + SQL_FD_FETCH_PRIOR = 0x00000008L, + SQL_FD_FETCH_ABSOLUTE = 0x00000010L, + SQL_FD_FETCH_RELATIVE = 0x00000020L +} + +// * SQL_GETDATA_EXTENSIONS bitmasks * +enum +{ + SQL_GD_ANY_COLUMN = 0x00000001L, + SQL_GD_ANY_ORDER = 0x00000002L +} + +// * SQL_IDENTIFIER_CASE values * +enum +{ + SQL_IC_UPPER = 1, + SQL_IC_LOWER = 2, + SQL_IC_SENSITIVE = 3, + SQL_IC_MIXED = 4 +} + +// * SQL_OJ_CAPABILITIES bitmasks * +// * OJ means 'outer join' * +enum +{ + SQL_OJ_LEFT = 0x00000001L, + SQL_OJ_RIGHT = 0x00000002L, + SQL_OJ_FULL = 0x00000004L, + SQL_OJ_NESTED = 0x00000008L, + SQL_OJ_NOT_ORDERED = 0x00000010L, + SQL_OJ_INNER = 0x00000020L, + SQL_OJ_ALL_COMPARISON_OPS = 0x00000040L +} + +// * SQL_SCROLL_CONCURRENCY bitmasks * +enum +{ + SQL_SCCO_READ_ONLY = 0x00000001L, + SQL_SCCO_LOCK = 0x00000002L, + SQL_SCCO_OPT_ROWVER = 0x00000004L, + SQL_SCCO_OPT_VALUES = 0x00000008L +} + +// * SQL_TXN_CAPABLE values * +enum +{ + SQL_TC_NONE = 0, + SQL_TC_DML = 1, + SQL_TC_ALL = 2, + SQL_TC_DDL_COMMIT = 3, + SQL_TC_DDL_IGNORE = 4 +} + +// * SQL_TXN_ISOLATION_OPTION bitmasks * +enum +{ + SQL_TXN_READ_UNCOMMITTED = 0x00000001L, + SQL_TRANSACTION_READ_UNCOMMITTED = SQL_TXN_READ_UNCOMMITTED, + SQL_TXN_READ_COMMITTED = 0x00000002L, + SQL_TRANSACTION_READ_COMMITTED = SQL_TXN_READ_COMMITTED, + SQL_TXN_REPEATABLE_READ = 0x00000004L, + SQL_TRANSACTION_REPEATABLE_READ = SQL_TXN_REPEATABLE_READ, + SQL_TXN_SERIALIZABLE = 0x00000008L, + SQL_TRANSACTION_SERIALIZABLE = SQL_TXN_SERIALIZABLE +} + +// * SQL_NULL_COLLATION values * +enum +{ + SQL_NC_HIGH = 0, + SQL_NC_LOW = 1 +} + +/+ + ' ODBC v3.0+ ISO 92 + ' Allocates an environment, connection, statement, or descriptor handle. + ' + ' -- HandleTypes -- + ' SQL_HANDLE_ENV + ' SQL_HANDLE_DBC + ' SQL_HANDLE_DESC + ' SQL_HANDLE_STMT + ' + ' -- InputHandle -- + ' The input handle in whose context the new handle is to be allocated. + ' If HandleType is SQL_HANDLE_ENV, this is SQL_NULL_HANDLE. If HandleType + ' is SQL_HANDLE_DBC, this must be an environment handle, and if it is + ' SQL_HANDLE_STMT or SQL_HANDLE_DESC, it must be a connection handle. + ' + +/ +SQLRETURN SQLAllocHandle +( + /+ IN +/ SQLSMALLINT HandleType, + /+ IN +/ SQLHANDLE InputHandle, + /+ OUT +/ SQLHANDLE *OutputHandle +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Binds application data buffers to columns in the result set. + ' + +/ +SQLRETURN SQLBindCol +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLUSMALLINT ColumnNumber, + /+ IN +/ SQLSMALLINT TargetType, + /+ INOUT +/ SQLPOINTER TargetValue, + /+ IN +/ SQLINTEGER BufferLength, + /+ INOUT +/ SQLINTEGER *StrLen_or_Ind +); + +SQLRETURN SQLBindParam +( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLUINTEGER LengthPrecision, + SQLSMALLINT ParameterScale, + SQLPOINTER ParameterValue, + SQLINTEGER *StrLen_or_Ind +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Cancels the processing on a statement. + ' + +/ +SQLRETURN SQLCancel +( + /+ IN +/ SQLHSTMT StatementHandle +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Closes a cursor that has been opened on a statement and discards pending results. + ' + +/ +SQLRETURN SQLCloseCursor +( + SQLHSTMT StatementHandle +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns descriptor information for a column in a result set. + ' Descriptor information is returned as a character string, a 32-bit + ' descriptor-dependent value, or an integer value. + ' + +/ +SQLRETURN SQLColAttribute +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLUSMALLINT ColumnNumber, + /+ IN +/ SQLUSMALLINT FieldIdentifier, + /+ OUT +/ SQLPOINTER CharacterAttribute, + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *StringLength, + /+ OUT +/ SQLPOINTER NumericAttribute +); + +/+ + ' ODBC v1.0+ X/Open + ' Returns the list of column names in specified tables. The driver + ' returns this information as a result set on the specified StatementHandle. + ' + +/ +SQLRETURN SQLColumns +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLCHAR *CatalogName, + /+ IN +/ SQLSMALLINT NameLength1, + /+ IN +/ SQLCHAR *SchemaName, + /+ IN +/ SQLSMALLINT NameLength2, + /+ IN +/ SQLCHAR *TableName, + /+ IN +/ SQLSMALLINT NameLength3, + /+ IN +/ SQLCHAR *ColumnName, + /+ IN +/ SQLSMALLINT NameLength4 +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Establishes connections to a driver and a data source. The connection + ' handle references storage of all information about the connection to + ' the data source, including status, transaction state, and error information. + ' + +/ +SQLRETURN SQLConnect +( + /+ IN +/ SQLHDBC ConnectionHandle, + /+ IN +/ SQLCHAR *ServerName, + /+ IN +/ SQLSMALLINT NameLength1, + /+ IN +/ SQLCHAR *UserName, + /+ IN +/ SQLSMALLINT NameLength2, + /+ IN +/ SQLCHAR *Authentication, + /+ IN +/ SQLSMALLINT NameLength3 +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Copies descriptor information from one descriptor handle to another. + ' + +/ +SQLRETURN SQLCopyDesc +( + /+ IN +/ SQLHDESC SourceDescHandle, + /+ IN +/ SQLHDESC TargetDescHandle +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Returns information about a data source. This function is implemented + ' solely by the Driver Manager. + ' + +/ +SQLRETURN SQLDataSources +( + /+ IN +/ SQLHENV EnvironmentHandle, + /+ IN +/ SQLUSMALLINT Direction, + /+ OUT +/ SQLCHAR *ServerName, + /+ IN +/ SQLSMALLINT BufferLength1, + /+ OUT +/ SQLSMALLINT *NameLength1, + /+ OUT +/ SQLCHAR *Description, + /+ IN +/ SQLSMALLINT BufferLength2, + /+ OUT +/ SQLSMALLINT *NameLength2 +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Returns the result descriptor column name, type, column size, + ' decimal digits, and nullability for one column in the result set. + ' This information also is available in the fields of the IRD. + ' + +/ +SQLRETURN SQLDescribeCol +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLUSMALLINT ColumnNumber, + /+ OUT +/ SQLCHAR *ColumnName, + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *NameLength, + /+ OUT +/ SQLSMALLINT *DataType, + /+ OUT +/ SQLUINTEGER *ColumnSize, + /+ OUT +/ SQLSMALLINT *DecimalDigits, + /+ OUT +/ SQLSMALLINT *Nullable +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Closes the connection associated with a specific connection handle. + ' + +/ +SQLRETURN SQLDisconnect +( + /+ IN +/ SQLHDBC ConnectionHandle +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Requests a commit or rollback operation for all active operations on all + ' statements associated with a connection. SQLEndTran can also request that + ' a commit or rollback operation be performed for all connections associated + ' with an environment. + ' + ' -- HandleType -- + ' Contains either SQL_HANDLE_ENV (if Handle is an environment handle) + ' or SQL_HANDLE_DBC (if Handle is a connection handle). + ' + ' -- Handle -- + ' The handle, of the type indicated by HandleType, indicating the scope of the transaction. + ' + ' -- CompletionType -- + ' One of the following two values: + ' SQL_COMMIT + ' SQL_ROLLBACK + ' + +/ +SQLRETURN SQLEndTran +( + /+ IN +/ SQLSMALLINT HandleType, + /+ IN +/ SQLHANDLE Handle, + /+ IN +/ SQLSMALLINT CompletionType +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Executes a preparable statement, using the current values of the + ' parameter marker variables if any parameters exist in the statement. + ' SQLExecDirect is the fastest way to submit an SQL statement for + ' one-time execution. + ' + +/ +SQLRETURN SQLExecDirect +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLCHAR *StatementText, + /+ IN +/ SQLINTEGER TextLength +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Executes a prepared statement, using the current values of the parameter + ' marker variables if any parameter markers exist in the statement. + ' + +/ +SQLRETURN SQLExecute +( + /+ IN +/ SQLHSTMT StatementHandle +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Fetches the next rowset of data from the result set and returns + ' data for all bound columns. + ' + +/ +SQLRETURN SQLFetch +( + /+ IN +/ SQLHSTMT StatementHandle +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Fetches the specified rowset of data from the result set and + ' returns data for all bound columns. Rowsets can be specified + ' at an absolute or relative position or by bookmark. + ' + ' -- FetchOrientation -- + ' Type of fetch: + ' SQL_FETCH_NEXT + ' SQL_FETCH_PRIOR + ' SQL_FETCH_FIRST + ' SQL_FETCH_LAST + ' SQL_FETCH_ABSOLUTE + ' SQL_FETCH_RELATIVE + ' SQL_FETCH_BOOKMARK + ' + ' -- FetchOffset -- + ' Number of the row to fetch based on the type above. + ' + +/ +SQLRETURN SQLFetchScroll +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLSMALLINT FetchOrientation, + /+ IN +/ SQLINTEGER FetchOffset +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Frees resources associated with a specific environment, connection, + ' statement, or descriptor handle. + ' + ' -- HandleType -- + ' Must be one of the following values: + ' SQL_HANDLE_ENV + ' SQL_HANDLE_DBC + ' SQL_HANDLE_STMT + ' SQL_HANDLE_DESC + ' + +/ +SQLRETURN SQLFreeHandle +( + /+ IN +/ SQLSMALLINT HandleType, + /+ IN +/ SQLHANDLE Handle +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Stops processing associated with a specific statement, + ' closes any open cursors associated with the statement, + ' discards pending results, or, optionally, frees all + ' resources associated with the statement handle. + ' + +/ +SQLRETURN SQLFreeStmt +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLUSMALLINT Option +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current setting of a connection attribute. + ' + +/ +SQLRETURN SQLGetConnectAttr +( + /+ IN +/ SQLHDBC ConnectionHandle, + /+ IN +/ SQLINTEGER Attribute, + /+ OUT +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER BufferLength, + /+ OUT +/ SQLINTEGER *StringLength +); + +/+ + ' ODBC v1.+ ISO 92 + ' Returns the cursor name associated with a specified statement. + ' + +/ +SQLRETURN SQLGetCursorName +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ OUT +/ SQLCHAR *CursorName, + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *NameLength +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Retrieves data for a single column in the result set. It can be called + ' multiple times to retrieve variable-length data in parts. + ' + +/ +SQLRETURN SQLGetData +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLUSMALLINT ColumnNumber, + /+ IN +/ SQLSMALLINT TargetType, + /+ OUT +/ SQLPOINTER TargetValue, + /+ IN +/ SQLINTEGER BufferLength, + /+ OUT +/ SQLINTEGER *StrLen_or_Ind +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current setting or value of a single field of a descriptor record. + ' + +/ +SQLRETURN SQLGetDescField +( + /+ IN +/ SQLHDESC DescriptorHandle, + /+ IN +/ SQLSMALLINT RecNumber, + /+ IN +/ SQLSMALLINT FieldIdentifier, + /+ OUT +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER BufferLength, + /+ OUT +/ SQLINTEGER *StringLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current settings or values of multiple fields of a descriptor + ' record. The fields returned describe the name, data type, and storage of + ' column or parameter data. + ' + +/ +SQLRETURN SQLGetDescRec +( + /+ IN +/ SQLHDESC DescriptorHandle, + /+ IN +/ SQLSMALLINT RecNumber, + /+ OUT +/ SQLCHAR *Name, // SQLGetDescField( DescriptorHandle = SQL_DESC_NAME ) + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *StringLength, + /+ OUT +/ SQLSMALLINT *Type, // SQLGetDescField( DescriptorHandle = SQL_DESC_TYPE ) + /+ OUT +/ SQLSMALLINT *SubType, // SQLGetDescField( DescriptorHandle = SQL_DESC_DATETIME_INTERVAL_CODE ) + /+ OUT +/ SQLINTEGER *Length, // SQLGetDescField( DescriptorHandle = SQL_DESC_OCTET_LENGTH ) + /+ OUT +/ SQLSMALLINT *Precision, // SQLGetDescField( DescriptorHandle = SQL_DESC_PRECISION ) + /+ OUT +/ SQLSMALLINT *Scale, // SQLGetDescField( DescriptorHandle = SQL_DESC_SCALE ) + /+ OUT +/ SQLSMALLINT *Nullable // SQLGetDescField( DescriptorHandle = SQL_DESC_NULLABLE ) +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current value of a field of a record of the diagnostic + ' data structure (associated with a specified handle) that contains + ' error, warning, and status information. + ' + ' -- HandleType -- + ' Must be one of the following: + ' SQL_HANDLE_ENV + ' SQL_HANDLE_DBC + ' SQL_HANDLE_STMT + ' SQL_HANDLE_DESC + ' + +/ +SQLRETURN SQLGetDiagField +( + /+ IN +/ SQLSMALLINT HandleType, + /+ IN +/ SQLHANDLE Handle, + /+ IN +/ SQLSMALLINT RecNumber, + /+ IN +/ SQLSMALLINT DiagIdentifier, + /+ OUT +/ SQLPOINTER DiagInfo, + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *StringLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current values of multiple fields of a diagnostic record that + ' contains error, warning, and status information. Unlike SQLGetDiagField, + ' which returns one diagnostic field per call, SQLGetDiagRec returns several + ' commonly used fields of a diagnostic record, including the SQLSTATE, the + ' native error code, and the diagnostic message text. + ' + ' -- HandleType -- + ' Must be one of the following: + ' SQL_HANDLE_ENV + ' SQL_HANDLE_DBC + ' SQL_HANDLE_STMT + ' SQL_HANDLE_DESC + ' + +/ +SQLRETURN SQLGetDiagRec +( + /+ IN +/ SQLSMALLINT HandleType, + /+ IN +/ SQLHANDLE Handle, + /+ IN +/ SQLSMALLINT RecNumber, + /+ OUT +/ SQLCHAR *Sqlstate, + /+ OUT +/ SQLINTEGER *NativeError, + /+ OUT +/ SQLCHAR *MessageText, + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *TextLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current setting of an environment attribute. + ' + +/ +SQLRETURN SQLGetEnvAttr +( + /+ IN +/ SQLHENV EnvironmentHandle, + /+ IN +/ SQLINTEGER Attribute, + /+ OUT +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER BufferLength, + /+ OUT +/ SQLINTEGER *StringLength +); + +/+ + ' ODBC v1.0+ ISO 92 + ' returns information about whether a driver supports a specific ODBC + ' function. This function is implemented in the Driver Manager; it can + ' also be implemented in drivers. If a driver implements SQLGetFunctions, + ' the Driver Manager calls the function in the driver. Otherwise, + ' it executes the function itself. + ' + +/ +SQLRETURN SQLGetFunctions +( + /+ IN +/ SQLHDBC ConnectionHandle, + /+ IN +/ SQLUSMALLINT FunctionId, + /+ OUT +/ SQLUSMALLINT *Supported +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Returns general information about the driver and data + ' source associated with a connection. + ' + +/ +SQLRETURN SQLGetInfo +( + /+ IN +/ SQLHDBC ConnectionHandle, + /+ IN +/ SQLUSMALLINT InfoType, + /+ OUT +/ SQLPOINTER InfoValue, + /+ IN +/ SQLSMALLINT BufferLength, + /+ OUT +/ SQLSMALLINT *StringLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Returns the current setting of a statement attribute. + ' + +/ +SQLRETURN SQLGetStmtAttr +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLINTEGER Attribute, + /+ OUT +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER BufferLength, + /+ OUT +/ SQLINTEGER *StringLength +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Returns information about data types supported by the data source. + ' The driver returns the information in the form of an SQL result set. + ' The data types are intended for use in Data Definition Language (DDL) statements. + ' + +/ +SQLRETURN SQLGetTypeInfo +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLSMALLINT DataType +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Returns the number of columns in a result set. + ' + +/ +SQLRETURN SQLNumResultCols +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ OUT +/ SQLSMALLINT *ColumnCount +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Is used in conjunction with SQLPutData to supply parameter data at statement execution time. + ' + +/ +SQLRETURN SQLParamData +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ OUT +/ SQLPOINTER *Value +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Prepares an SQL string for execution. + ' + +/ +SQLRETURN SQLPrepare +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLCHAR *StatementText, + /+ IN +/ SQLINTEGER TextLength +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Allows an application to send data for a parameter or column to the driver + ' at statement execution time. This function can be used to send character or + ' binary data values in parts to a column with a character, binary, or data + ' source specific data type (for example, parameters of the SQL_LONGVARBINARY + ' or SQL_LONGVARCHAR types). SQLPutData supports binding to a Unicode C data + ' type, even if the underlying driver does not support Unicode data. + ' + +/ +SQLRETURN SQLPutData +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLPOINTER Data, + /+ IN +/ SQLINTEGER StrLen_or_Ind +); + +/+ + ' ODBC v1.+ ISO 92 + ' Returns the number of rows affected by an UPDATE, INSERT, or DELETE statement; + ' an SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK operation in + ' SQLBulkOperations; or an SQL_UPDATE or SQL_DELETE operation in SQLSetPos. + ' + +/ +SQLRETURN SQLRowCount +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ OUT +/ SQLINTEGER *RowCount +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Sets attributes that govern aspects of connections. + ' + +/ +SQLRETURN SQLSetConnectAttr +( + /+ IN +/ SQLHDBC ConnectionHandle, + /+ IN +/ SQLINTEGER Attribute, + /+ IN +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER StringLength +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Associates a cursor name with an active statement. If an application + ' does not call SQLSetCursorName, the driver generates cursor names as + ' needed for SQL statement processing. + ' + +/ +SQLRETURN SQLSetCursorName +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLCHAR *CursorName, + /+ IN +/ SQLSMALLINT NameLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Sets the value of a single field of a descriptor record. + ' + +/ +SQLRETURN SQLSetDescField +( + /+ IN +/ SQLHDESC DescriptorHandle, + /+ IN +/ SQLSMALLINT RecNumber, + /+ IN +/ SQLSMALLINT FieldIdentifier, + /+ IN +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER BufferLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Function sets multiple descriptor fields that affect the data + ' type and buffer bound to a column or parameter data. + ' + +/ +SQLRETURN SQLSetDescRec +( + /+ IN +/ SQLHDESC DescriptorHandle, + /+ IN +/ SQLSMALLINT RecNumber, + /+ IN +/ SQLSMALLINT Type, + /+ IN +/ SQLSMALLINT SubType, + /+ IN +/ SQLINTEGER Length, + /+ IN +/ SQLSMALLINT Precision, + /+ IN +/ SQLSMALLINT Scale, + /+ INOUT +/ SQLPOINTER Data, + /+ INOUT +/ SQLINTEGER *StringLength, + /+ INOUT +/ SQLINTEGER *Indicator +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Sets attributes that govern aspects of environments. + ' + +/ +SQLRETURN SQLSetEnvAttr +( + /+ IN +/ SQLHENV EnvironmentHandle, + /+ IN +/ SQLINTEGER Attribute, + /+ IN +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER StringLength +); + +/+ + ' ODBC v3.0+ ISO 92 + ' Sets attributes related to a statement. + ' + +/ +SQLRETURN SQLSetStmtAttr +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLINTEGER Attribute, + /+ IN +/ SQLPOINTER Value, + /+ IN +/ SQLINTEGER StringLength +); + +/+ + ' ODBC v1.0+ X/Open + ' Retrieves the following information about columns within a specified table: + ' + ' 1) The optimal set of columns that uniquely identifies a row in the table. + ' 2) Columns that are automatically updated when any value in the row is updated by a transaction. + ' + ' + +/ +SQLRETURN SQLSpecialColumns +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLUSMALLINT IdentifierType, + /+ IN +/ SQLCHAR *CatalogName, + /+ IN +/ SQLSMALLINT NameLength1, + /+ IN +/ SQLCHAR *SchemaName, + /+ IN +/ SQLSMALLINT NameLength2, + /+ IN +/ SQLCHAR *TableName, + /+ IN +/ SQLSMALLINT NameLength3, + /+ IN +/ SQLUSMALLINT Scope, + /+ IN +/ SQLUSMALLINT Nullable +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Retrieves a list of statistics about a single table and the + ' indexes associated with the table. The driver returns the + ' information as a result set. + ' + ' -- Unique -- + ' Type of index: SQL_INDEX_UNIQUE or SQL_INDEX_ALL. + ' + +/ +SQLRETURN SQLStatistics +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLCHAR *CatalogName, + /+ IN +/ SQLSMALLINT NameLength1, + /+ IN +/ SQLCHAR *SchemaName, + /+ IN +/ SQLSMALLINT NameLength2, + /+ IN +/ SQLCHAR *TableName, + /+ IN +/ SQLSMALLINT NameLength3, + /+ IN +/ SQLUSMALLINT Unique, + /+ IN +/ SQLUSMALLINT Reserved +); + +/+ + ' OBDC v1.0+ X/Open + ' Returns the list of table, catalog, or schema names, and table + ' types, stored in a specific data source. The driver returns the + ' information as a result set. + ' + +/ +SQLRETURN SQLTables +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLCHAR *CatalogName, + /+ IN +/ SQLSMALLINT NameLength1, + /+ IN +/ SQLCHAR *SchemaName, + /+ IN +/ SQLSMALLINT NameLength2, + /+ IN +/ SQLCHAR *TableName, + /+ IN +/ SQLSMALLINT NameLength3, + /+ IN +/ SQLCHAR *TableType, + /+ IN +/ SQLSMALLINT NameLength4 +); + +/+---------------------------+ + | * Deprecated Functions * | + +---------------------------+/ +/+ + ' In ODBC 3.x, the ODBC 2.x function SQLAllocConnect has been + ' replaced by SQLAllocHandle. + ' + +/ +SQLRETURN SQLAllocConnect +( + SQLHENV EnvironmentHandle, + SQLHDBC *ConnectionHandle +); + +/+ + ' In ODBC 3.x, the ODBC 2.x function SQLAllocEnv has been replaced by SQLAllocHandle. + ' + +/ +SQLRETURN SQLAllocEnv +( + SQLHENV *EnvironmentHandle +); + +/+ + ' In ODBC 3.x, the ODBC 2.x function SQLAllocStmt has been replaced by SQLAllocHandle. + ' + +/ +SQLRETURN SQLAllocStmt +( + SQLHDBC ConnectionHandle, + SQLHSTMT *StatementHandle +); + +SQLRETURN SQLError +( + SQLHENV EnvironmentHandle, + SQLHDBC ConnectionHandle, + SQLHSTMT StatementHandle, + SQLCHAR *Sqlstate, + SQLINTEGER *NativeError, + SQLCHAR *MessageText, + SQLSMALLINT BufferLength, + SQLSMALLINT *TextLength +); + +SQLRETURN SQLFreeConnect +( + SQLHDBC ConnectionHandle +); + +SQLRETURN SQLFreeEnv +( + SQLHENV EnvironmentHandle +); + +SQLRETURN SQLGetConnectOption +( + SQLHDBC ConnectionHandle, + SQLUSMALLINT Option, + SQLPOINTER Value +); + +SQLRETURN SQLGetStmtOption +( + SQLHSTMT StatementHandle, + SQLUSMALLINT Option, + SQLPOINTER Value +); + +SQLRETURN SQLSetConnectOption +( + SQLHDBC ConnectionHandle, + SQLUSMALLINT Option, + SQLUINTEGER Value +); + +SQLRETURN SQLSetParam +( + SQLHSTMT StatementHandle, + SQLUSMALLINT ParameterNumber, + SQLSMALLINT ValueType, + SQLSMALLINT ParameterType, + SQLUINTEGER LengthPrecision, + SQLSMALLINT ParameterScale, + SQLPOINTER ParameterValue, + SQLINTEGER *StrLen_or_Ind +); + +SQLRETURN SQLSetStmtOption +( + SQLHSTMT StatementHandle, + SQLUSMALLINT Option, + SQLUINTEGER Value +); + +SQLRETURN SQLTransact +( + SQLHENV EnvironmentHandle, + SQLHDBC ConnectionHandle, + SQLUSMALLINT CompletionType +); + +// end Deprecated Functions diff --git a/etc/c/odbc/sqlext.d b/etc/c/odbc/sqlext.d new file mode 100644 index 00000000000..c54ce29de1a --- /dev/null +++ b/etc/c/odbc/sqlext.d @@ -0,0 +1,2155 @@ +/** +Declarations for interfacing with the ODBC library. + +Adapted with minimal changes from the work of David L. Davis +(refer to the $(HTTP +forum.dlang.org/post/cfk7ql$(DOLLAR)1p4n$(DOLLAR)1@digitaldaemon.com, +original announcement)). + +`etc.c.odbc.sqlext` corresponds to the `sqlext.h` C header file. + +See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, + ODBC API Reference on MSN Online) +*/ + +module etc.c.odbc.sqlext; + +private import etc.c.odbc.sql; +private import etc.c.odbc.sqltypes; + +extern (Windows): + +// * generally useful constants * +enum int SQL_SPEC_MAJOR = 3; /* Major version of specification */ +enum int SQL_SPEC_MINOR = 51; /* Minor version of specification */ +immutable char[] SQL_SPEC_STRING = "03.51"; /* String constant for version */ + +enum int SQL_SQLSTATE_SIZE = 5; /* size of SQLSTATE */ +enum int SQL_MAX_DSN_LENGTH = 32; /* maximum data source name size */ + +enum int SQL_MAX_OPTION_STRING_LENGTH = 256; + +// * return code SQL_NO_DATA_FOUND is the same as SQL_NO_DATA * +//enum int SQL_NO_DATA_FOUND = 100; +enum int SQL_NO_DATA_FOUND = SQL_NO_DATA; + +// * an end handle type * +enum int SQL_HANDLE_SENV = 5; + +// * env attribute * +enum : uint +{ + SQL_ATTR_ODBC_VERSION = 200, + SQL_ATTR_CONNECTION_POOLING = 201, + SQL_ATTR_CP_MATCH = 202, + + // * values for SQL_ATTR_CONNECTION_POOLING * + SQL_CP_OFF = 0UL, + SQL_CP_ONE_PER_DRIVER = 1UL, + SQL_CP_ONE_PER_HENV = 2UL, + SQL_CP_DEFAULT = SQL_CP_OFF, + + // * values for SQL_ATTR_CP_MATCH * + SQL_CP_STRICT_MATCH = 0UL, + SQL_CP_RELAXED_MATCH = 1UL, + SQL_CP_MATCH_DEFAULT = SQL_CP_STRICT_MATCH, + + // * values for SQL_ATTR_ODBC_VERSION * + SQL_OV_ODBC2 = 2UL, + SQL_OV_ODBC3 = 3UL +} + +// * connection attributes * +enum +{ + SQL_ACCESS_MODE = 101, + SQL_AUTOCOMMIT = 102, + SQL_LOGIN_TIMEOUT = 103, + SQL_OPT_TRACE = 104, + SQL_OPT_TRACEFILE = 105, + SQL_TRANSLATE_DLL = 106, + SQL_TRANSLATE_OPTION = 107, + SQL_TXN_ISOLATION = 108, + SQL_CURRENT_QUALIFIER = 109, + SQL_ODBC_CURSORS = 110, + SQL_QUIET_MODE = 111, + SQL_PACKET_SIZE = 112 +} + +// * connection attributes with new names * +enum +{ + SQL_ATTR_ACCESS_MODE = SQL_ACCESS_MODE, + SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT, + SQL_ATTR_CONNECTION_TIMEOUT = 113, + SQL_ATTR_CURRENT_CATALOG = SQL_CURRENT_QUALIFIER, + SQL_ATTR_DISCONNECT_BEHAVIOR = 114, + SQL_ATTR_ENLIST_IN_DTC = 1207, + SQL_ATTR_ENLIST_IN_XA = 1208, + SQL_ATTR_LOGIN_TIMEOUT = SQL_LOGIN_TIMEOUT, + SQL_ATTR_ODBC_CURSORS = SQL_ODBC_CURSORS, + SQL_ATTR_PACKET_SIZE = SQL_PACKET_SIZE, + SQL_ATTR_QUIET_MODE = SQL_QUIET_MODE, + SQL_ATTR_TRACE = SQL_OPT_TRACE, + SQL_ATTR_TRACEFILE = SQL_OPT_TRACEFILE, + SQL_ATTR_TRANSLATE_LIB = SQL_TRANSLATE_DLL, + SQL_ATTR_TRANSLATE_OPTION = SQL_TRANSLATE_OPTION, + SQL_ATTR_TXN_ISOLATION = SQL_TXN_ISOLATION +} + +// * GetConnectAttr only * +enum int SQL_ATTR_CONNECTION_DEAD = 1209; + +/+ + ' ODBC Driver Manager sets this connection attribute to a unicode driver + ' (which supports SQLConnectW) when the application is an ANSI application + ' (which calls SQLConnect, SQLDriverConnect, or SQLBrowseConnect). + ' This is SetConnectAttr only and application does not set this attribute + ' This attribute was introduced because some unicode driver's some APIs may + ' need to behave differently on ANSI or Unicode applications. A unicode + ' driver, which has same behavior for both ANSI or Unicode applications, + ' should return SQL_ERROR when the driver manager sets this connection + ' attribute. When a unicode driver returns SQL_SUCCESS on this attribute, + ' the driver manager treates ANSI and Unicode connections differently in + ' connection pooling. ++/ +enum int SQL_ATTR_ANSI_APP = 115; + + +// * SQL_ACCESS_MODE options * +enum : uint +{ + SQL_MODE_READ_WRITE = 0UL, + SQL_MODE_READ_ONLY = 1UL, + SQL_MODE_DEFAULT = SQL_MODE_READ_WRITE +} + +// * SQL_AUTOCOMMIT options * +enum : uint +{ + SQL_AUTOCOMMIT_OFF = 0UL, + SQL_AUTOCOMMIT_ON = 1UL, + SQL_AUTOCOMMIT_DEFAULT = SQL_AUTOCOMMIT_ON +} + +// * SQL_LOGIN_TIMEOUT options * +enum uint SQL_LOGIN_TIMEOUT_DEFAULT = 15UL; + +// * SQL_OPT_TRACE options * +enum : uint +{ + SQL_OPT_TRACE_OFF = 0UL, + SQL_OPT_TRACE_ON = 1UL, + SQL_OPT_TRACE_DEFAULT = SQL_OPT_TRACE_OFF +} + +immutable char[] SQL_OPT_TRACE_FILE_DEFAULT = r"\SQL.LOG"; + +// * SQL_ODBC_CURSORS options * +enum : uint +{ + SQL_CUR_USE_IF_NEEDED = 0UL, + SQL_CUR_USE_ODBC = 1UL, + SQL_CUR_USE_DRIVER = 2UL, + SQL_CUR_DEFAULT = SQL_CUR_USE_DRIVER +} + +enum +{ + // * values for SQL_ATTR_DISCONNECT_BEHAVIOR * + SQL_DB_RETURN_TO_POOL = 0UL, + SQL_DB_DISCONNECT = 1UL, + SQL_DB_DEFAULT = SQL_DB_RETURN_TO_POOL, + + // * values for SQL_ATTR_ENLIST_IN_DTC * + SQL_DTC_DONE = 0L + +} + +// * values for SQL_ATTR_CONNECTION_DEAD * +enum int SQL_CD_TRUE = 1L; // * Connection is closed/dead * +enum int SQL_CD_FALSE = 0L; // * Connection is open/available * + +// * values for SQL_ATTR_ANSI_APP ( ODBC v3.51 ) * +enum int SQL_AA_TRUE = 1L; // * the application is an ANSI app * +enum int SQL_AA_FALSE = 0L; // * the application is a Unicode app * + +// * statement attributes * +enum +{ + SQL_QUERY_TIMEOUT = 0, + SQL_MAX_ROWS = 1, + SQL_NOSCAN = 2, + SQL_MAX_LENGTH = 3, + SQL_ASYNC_ENABLE = 4, // * same as SQL_ATTR_ASYNC_ENABLE * + SQL_BIND_TYPE = 5, + SQL_CURSOR_TYPE = 6, + SQL_CONCURRENCY = 7, + SQL_KEYSET_SIZE = 8, + SQL_ROWSET_SIZE = 9, + SQL_SIMULATE_CURSOR = 10, + SQL_RETRIEVE_DATA = 11, + SQL_USE_BOOKMARKS = 12, + SQL_GET_BOOKMARK = 13, // * GetStmtOption Only * + SQL_ROW_NUMBER = 14 // * GetStmtOption Only * +} + +// * statement attributes for ODBC 3.0 * +enum +{ + SQL_ATTR_ASYNC_ENABLE = 4, + SQL_ATTR_CONCURRENCY = SQL_CONCURRENCY, + SQL_ATTR_CURSOR_TYPE = SQL_CURSOR_TYPE, + SQL_ATTR_ENABLE_AUTO_IPD = 15, + SQL_ATTR_FETCH_BOOKMARK_PTR = 16, + SQL_ATTR_KEYSET_SIZE = SQL_KEYSET_SIZE, + SQL_ATTR_MAX_LENGTH = SQL_MAX_LENGTH, + SQL_ATTR_MAX_ROWS = SQL_MAX_ROWS, + SQL_ATTR_NOSCAN = SQL_NOSCAN, + SQL_ATTR_PARAM_BIND_OFFSET_PTR = 17, + SQL_ATTR_PARAM_BIND_TYPE = 18, + SQL_ATTR_PARAM_OPERATION_PTR = 19, + SQL_ATTR_PARAM_STATUS_PTR = 20, + SQL_ATTR_PARAMS_PROCESSED_PTR = 21, + SQL_ATTR_PARAMSET_SIZE = 22, + SQL_ATTR_QUERY_TIMEOUT = SQL_QUERY_TIMEOUT, + SQL_ATTR_RETRIEVE_DATA = SQL_RETRIEVE_DATA, + SQL_ATTR_ROW_BIND_OFFSET_PTR = 23, + SQL_ATTR_ROW_BIND_TYPE = SQL_BIND_TYPE, + SQL_ATTR_ROW_NUMBER = SQL_ROW_NUMBER, // * GetStmtAttr * + SQL_ATTR_ROW_OPERATION_PTR = 24, + SQL_ATTR_ROW_STATUS_PTR = 25, + SQL_ATTR_ROWS_FETCHED_PTR = 26, + SQL_ATTR_ROW_ARRAY_SIZE = 27, + SQL_ATTR_SIMULATE_CURSOR = SQL_SIMULATE_CURSOR, + SQL_ATTR_USE_BOOKMARKS = SQL_USE_BOOKMARKS +} + +// * whether an attribute is a pointer or not * +enum +{ + SQL_IS_POINTER = (-4), + SQL_IS_UINTEGER = (-5), + SQL_IS_INTEGER = (-6), + SQL_IS_USMALLINT = (-7), + SQL_IS_SMALLINT = (-8) +} + +// * the value of SQL_ATTR_PARAM_BIND_TYPE * +enum : uint +{ + SQL_PARAM_BIND_BY_COLUMN = 0UL, + SQL_PARAM_BIND_TYPE_DEFAULT = SQL_PARAM_BIND_BY_COLUMN +} + +// * SQL_QUERY_TIMEOUT options * +enum uint SQL_QUERY_TIMEOUT_DEFAULT = 0UL; + +// * SQL_MAX_ROWS options * +enum uint SQL_MAX_ROWS_DEFAULT = 0UL; + +// * SQL_NOSCAN options * +enum : uint +{ + SQL_NOSCAN_OFF = 0UL, /* 1.0 FALSE */ + SQL_NOSCAN_ON = 1UL, /* 1.0 TRUE */ + SQL_NOSCAN_DEFAULT = SQL_NOSCAN_OFF +} + +// * SQL_MAX_LENGTH options * +enum uint SQL_MAX_LENGTH_DEFAULT = 0UL; + +// * values for SQL_ATTR_ASYNC_ENABLE * +enum : uint +{ + SQL_ASYNC_ENABLE_OFF = 0UL, + SQL_ASYNC_ENABLE_ON = 1UL, + SQL_ASYNC_ENABLE_DEFAULT = SQL_ASYNC_ENABLE_OFF +} + +// * SQL_BIND_TYPE options * +enum : uint +{ + SQL_BIND_BY_COLUMN = 0UL, + SQL_BIND_TYPE_DEFAULT = SQL_BIND_BY_COLUMN /* Default value */ +} + +// * SQL_CONCURRENCY options * +enum +{ + SQL_CONCUR_READ_ONLY = 1, + SQL_CONCUR_LOCK = 2, + SQL_CONCUR_ROWVER = 3, + SQL_CONCUR_VALUES = 4, + SQL_CONCUR_DEFAULT = SQL_CONCUR_READ_ONLY /* Default value */ +} + +// * SQL_CURSOR_TYPE options * +enum : uint +{ + SQL_CURSOR_FORWARD_ONLY = 0UL, + SQL_CURSOR_KEYSET_DRIVEN = 1UL, + SQL_CURSOR_DYNAMIC = 2UL, + SQL_CURSOR_STATIC = 3UL, + SQL_CURSOR_TYPE_DEFAULT = SQL_CURSOR_FORWARD_ONLY /* Default value */ +} + +// * SQL_ROWSET_SIZE options * +enum uint SQL_ROWSET_SIZE_DEFAULT = 1UL; + +// * SQL_KEYSET_SIZE options * +enum uint SQL_KEYSET_SIZE_DEFAULT = 0UL; + +// * SQL_SIMULATE_CURSOR options * +enum : uint +{ + SQL_SC_NON_UNIQUE = 0UL, + SQL_SC_TRY_UNIQUE = 1UL, + SQL_SC_UNIQUE = 2UL +} + +// * SQL_RETRIEVE_DATA options * +enum : uint +{ + SQL_RD_OFF = 0UL, + SQL_RD_ON = 1UL, + SQL_RD_DEFAULT = SQL_RD_ON +} + +// * SQL_USE_BOOKMARKS options * +enum : uint +{ + SQL_UB_OFF = 0UL, + SQL_UB_ON = 01UL, + SQL_UB_DEFAULT = SQL_UB_OFF +} + +// * New values for SQL_USE_BOOKMARKS attribute * +enum : uint +{ + SQL_UB_FIXED = SQL_UB_ON, + SQL_UB_VARIABLE = 2UL +} + +/* SQLColAttributes defines */ +enum +{ + SQL_COLUMN_COUNT = 0, + SQL_COLUMN_NAME = 1, + SQL_COLUMN_TYPE = 2, + SQL_COLUMN_LENGTH = 3, + SQL_COLUMN_PRECISION = 4, + SQL_COLUMN_SCALE = 5, + SQL_COLUMN_DISPLAY_SIZE = 6, + SQL_COLUMN_NULLABLE = 7, + SQL_COLUMN_UNSIGNED = 8, + SQL_COLUMN_MONEY = 9, + SQL_COLUMN_UPDATABLE = 10, + SQL_COLUMN_AUTO_INCREMENT = 11, + SQL_COLUMN_CASE_SENSITIVE = 12, + SQL_COLUMN_SEARCHABLE = 13, + SQL_COLUMN_TYPE_NAME = 14, + SQL_COLUMN_TABLE_NAME = 15, + SQL_COLUMN_OWNER_NAME = 16, + SQL_COLUMN_QUALIFIER_NAME = 17, + SQL_COLUMN_LABEL = 18, + SQL_COLATT_OPT_MAX = SQL_COLUMN_LABEL +} + +// * extended descriptor field * +enum +{ + SQL_DESC_ARRAY_SIZE = 20, + SQL_DESC_ARRAY_STATUS_PTR = 21, + SQL_DESC_AUTO_UNIQUE_VALUE = SQL_COLUMN_AUTO_INCREMENT, + SQL_DESC_BASE_COLUMN_NAME = 22, + SQL_DESC_BASE_TABLE_NAME = 23, + SQL_DESC_BIND_OFFSET_PTR = 24, + SQL_DESC_BIND_TYPE = 25, + SQL_DESC_CASE_SENSITIVE = SQL_COLUMN_CASE_SENSITIVE, + SQL_DESC_CATALOG_NAME = SQL_COLUMN_QUALIFIER_NAME, + SQL_DESC_CONCISE_TYPE = SQL_COLUMN_TYPE, + SQL_DESC_DATETIME_INTERVAL_PRECISION = 26, + SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE, + SQL_DESC_FIXED_PREC_SCALE = SQL_COLUMN_MONEY, + SQL_DESC_LABEL = SQL_COLUMN_LABEL, + SQL_DESC_LITERAL_PREFIX = 27, + SQL_DESC_LITERAL_SUFFIX = 28, + SQL_DESC_LOCAL_TYPE_NAME = 29, + SQL_DESC_MAXIMUM_SCALE = 30, + SQL_DESC_MINIMUM_SCALE = 31, + SQL_DESC_NUM_PREC_RADIX = 32, + SQL_DESC_PARAMETER_TYPE = 33, + SQL_DESC_ROWS_PROCESSED_PTR = 34, + SQL_DESC_SCHEMA_NAME = SQL_COLUMN_OWNER_NAME, + SQL_DESC_SEARCHABLE = SQL_COLUMN_SEARCHABLE, + SQL_DESC_TYPE_NAME = SQL_COLUMN_TYPE_NAME, + SQL_DESC_TABLE_NAME = SQL_COLUMN_TABLE_NAME, + SQL_DESC_UNSIGNED = SQL_COLUMN_UNSIGNED, + SQL_DESC_UPDATABLE = SQL_COLUMN_UPDATABLE +} + +// ODBCVER >= 0x0350 +enum int SQL_DESC_ROWVER = 35; + +// * defines for diagnostics fields * +enum +{ + SQL_DIAG_CURSOR_ROW_COUNT = (-1249), + SQL_DIAG_ROW_NUMBER = (-1248), + SQL_DIAG_COLUMN_NUMBER = (-1247) +} + +// * SQL extended datatypes * +enum +{ + SQL_DATE = 9, + SQL_INTERVAL = 10, + SQL_TIME = 10, + SQL_TIMESTAMP = 11, + SQL_LONGVARCHAR = (-1), + SQL_BINARY = (-2), + SQL_VARBINARY = (-3), + SQL_LONGVARBINARY = (-4), + SQL_BIGINT = (-5), + SQL_TINYINT = (-6), + SQL_BIT = (-7), + + // ODBCVER >= 0x0350 + SQL_GUID = (-11) +} + +enum +{ + // * interval code * + SQL_CODE_YEAR = 1, + SQL_CODE_MONTH = 2, + SQL_CODE_DAY = 3, + SQL_CODE_HOUR = 4, + SQL_CODE_MINUTE = 5, + SQL_CODE_SECOND = 6, + SQL_CODE_YEAR_TO_MONTH = 7, + SQL_CODE_DAY_TO_HOUR = 8, + SQL_CODE_DAY_TO_MINUTE = 9, + SQL_CODE_DAY_TO_SECOND = 10, + SQL_CODE_HOUR_TO_MINUTE = 11, + SQL_CODE_HOUR_TO_SECOND = 12, + SQL_CODE_MINUTE_TO_SECOND = 13, + + SQL_INTERVAL_YEAR = (100 + SQL_CODE_YEAR), + SQL_INTERVAL_MONTH = (100 + SQL_CODE_MONTH), + SQL_INTERVAL_DAY = (100 + SQL_CODE_DAY), + SQL_INTERVAL_HOUR = (100 + SQL_CODE_HOUR), + SQL_INTERVAL_MINUTE = (100 + SQL_CODE_MINUTE), + SQL_INTERVAL_SECOND = (100 + SQL_CODE_SECOND), + SQL_INTERVAL_YEAR_TO_MONTH = (100 + SQL_CODE_YEAR_TO_MONTH), + SQL_INTERVAL_DAY_TO_HOUR = (100 + SQL_CODE_DAY_TO_HOUR), + SQL_INTERVAL_DAY_TO_MINUTE = (100 + SQL_CODE_DAY_TO_MINUTE), + SQL_INTERVAL_DAY_TO_SECOND = (100 + SQL_CODE_DAY_TO_SECOND), + SQL_INTERVAL_HOUR_TO_MINUTE = (100 + SQL_CODE_HOUR_TO_MINUTE), + SQL_INTERVAL_HOUR_TO_SECOND = (100 + SQL_CODE_HOUR_TO_SECOND), + SQL_INTERVAL_MINUTE_TO_SECOND = (100 + SQL_CODE_MINUTE_TO_SECOND), +} + +// * The previous definitions for SQL_UNICODE_ are historical and obsolete * +enum +{ + SQL_WCHAR = (-8), + SQL_WVARCHAR = (-9), + SQL_WLONGVARCHAR = (-10), + SQL_C_WCHAR = SQL_WCHAR, + SQL_UNICODE = SQL_WCHAR, + SQL_UNICODE_VARCHAR = SQL_WVARCHAR, + SQL_UNICODE_LONGVARCHAR = SQL_WLONGVARCHAR, + SQL_UNICODE_CHAR = SQL_WCHAR +} + +// * C datatype to SQL datatype mapping SQL types * +enum +{ + /* ------------------------------- */ + SQL_C_CHAR = SQL_CHAR, /* CHAR, VARCHAR, DECIMAL, NUMERIC */ + SQL_C_LONG = SQL_INTEGER, /* INTEGER */ + SQL_C_SHORT = SQL_SMALLINT, /* SMALLINT */ + SQL_C_FLOAT = SQL_REAL, /* REAL */ + SQL_C_DOUBLE = SQL_DOUBLE, /* FLOAT, DOUBLE */ + SQL_C_NUMERIC = SQL_NUMERIC, + SQL_C_DEFAULT = 99, + SQL_SIGNED_OFFSET = (-20), + SQL_UNSIGNED_OFFSET = (-22) +} + +// * C datatype to SQL datatype mapping * +enum +{ + SQL_C_DATE = SQL_DATE, + SQL_C_TIME = SQL_TIME, + SQL_C_TIMESTAMP = SQL_TIMESTAMP +} + +enum +{ + SQL_C_TYPE_DATE = SQL_TYPE_DATE, + SQL_C_TYPE_TIME = SQL_TYPE_TIME, + SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP, + SQL_C_INTERVAL_YEAR = SQL_INTERVAL_YEAR, + SQL_C_INTERVAL_MONTH = SQL_INTERVAL_MONTH, + SQL_C_INTERVAL_DAY = SQL_INTERVAL_DAY, + SQL_C_INTERVAL_HOUR = SQL_INTERVAL_HOUR, + SQL_C_INTERVAL_MINUTE = SQL_INTERVAL_MINUTE, + SQL_C_INTERVAL_SECOND = SQL_INTERVAL_SECOND, + SQL_C_INTERVAL_YEAR_TO_MONTH = SQL_INTERVAL_YEAR_TO_MONTH, + SQL_C_INTERVAL_DAY_TO_HOUR = SQL_INTERVAL_DAY_TO_HOUR, + SQL_C_INTERVAL_DAY_TO_MINUTE = SQL_INTERVAL_DAY_TO_MINUTE, + SQL_C_INTERVAL_DAY_TO_SECOND = SQL_INTERVAL_DAY_TO_SECOND, + SQL_C_INTERVAL_HOUR_TO_MINUTE = SQL_INTERVAL_HOUR_TO_MINUTE, + SQL_C_INTERVAL_HOUR_TO_SECOND = SQL_INTERVAL_HOUR_TO_SECOND, + SQL_C_INTERVAL_MINUTE_TO_SECOND = SQL_INTERVAL_MINUTE_TO_SECOND +} + +enum +{ + SQL_C_BINARY = SQL_BINARY, + SQL_C_BIT = SQL_BIT, + SQL_C_SBIGINT = (SQL_BIGINT+SQL_SIGNED_OFFSET), /* SIGNED BIGINT */ + SQL_C_UBIGINT = (SQL_BIGINT+SQL_UNSIGNED_OFFSET), /* UNSIGNED BIGINT */ + SQL_C_TINYINT = SQL_TINYINT, + SQL_C_SLONG = (SQL_C_LONG + SQL_SIGNED_OFFSET), /* SIGNED INTEGER */ + SQL_C_SSHORT = (SQL_C_SHORT + SQL_SIGNED_OFFSET), /* SIGNED SMALLINT */ + SQL_C_STINYINT = (SQL_TINYINT + SQL_SIGNED_OFFSET), /* SIGNED TINYINT */ + SQL_C_ULONG = (SQL_C_LONG + SQL_UNSIGNED_OFFSET), /* UNSIGNED INTEGER */ + SQL_C_USHORT = (SQL_C_SHORT + SQL_UNSIGNED_OFFSET), /* UNSIGNED SMALLINT */ + SQL_C_UTINYINT = (SQL_TINYINT + SQL_UNSIGNED_OFFSET), /* UNSIGNED TINYINT */ + SQL_C_BOOKMARK = SQL_C_ULONG, /* BOOKMARK */ + SQL_C_VARBOOKMARK = SQL_C_BINARY, + + // ODBCVER >= 0x0350 + SQL_C_GUID = SQL_GUID /* GUID */ +} + +enum int SQL_TYPE_NULL = 0; + +// * define for SQL_DIAG_ROW_NUMBER and SQL_DIAG_COLUMN_NUMBER * +enum : uint +{ + SQL_NO_ROW_NUMBER = (-1), + SQL_NO_COLUMN_NUMBER = (-1), + SQL_ROW_NUMBER_UNKNOWN = (-2), + SQL_COLUMN_NUMBER_UNKNOWN = (-2) +} + +// * SQLBindParameter extensions * +enum uint SQL_DEFAULT_PARAM = (-5); +enum uint SQL_IGNORE = (-6); +enum uint SQL_COLUMN_IGNORE = SQL_IGNORE; + +enum : uint +{ + SQL_LEN_DATA_AT_EXEC_OFFSET = (-100), +} + +uint SQL_LEN_DATA_AT_EXEC() +( + uint length +) +{ + return ( ( -1 * length ) + cast(uint) SQL_LEN_DATA_AT_EXEC_OFFSET ); +} + +// * binary length for driver specific attributes * +enum uint SQL_LEN_BINARY_ATTR_OFFSET = (-100); + +uint SQL_LEN_BINARY_ATTR() +( + uint length +) +{ + return ( ( -1 * length ) + cast(uint) SQL_LEN_BINARY_ATTR_OFFSET ); +} + +// * Defines used by Driver Manager when mapping SQLSetParam to SQLBindParameter * +enum int SQL_PARAM_TYPE_DEFAULT = SQL_PARAM_INPUT_OUTPUT; +enum int SQL_SETPARAM_VALUE_MAX = (-1L); + +// ODBCVER < 0x0300 +enum int SQL_COLUMN_DRIVER_START = 1000; + +enum int SQL_COLATT_OPT_MIN = SQL_COLUMN_COUNT; + +// * SQLColAttributes subdefines for SQL_COLUMN_UPDATABLE * +enum +{ + SQL_ATTR_READONLY = 0, + SQL_ATTR_WRITE = 1, + SQL_ATTR_READWRITE_UNKNOWN = 2 +} + +// * SQLColAttributes subdefines for SQL_COLUMN_SEARCHABLE * +// * These are also used by SQLGetInfo * +enum +{ + SQL_UNSEARCHABLE = 0, + SQL_LIKE_ONLY = 1, + SQL_ALL_EXCEPT_LIKE = 2, + SQL_SEARCHABLE = 3, + SQL_PRED_SEARCHABLE = SQL_SEARCHABLE +} + +// * New defines for SEARCHABLE column in SQLGetTypeInfo * +enum +{ + SQL_COL_PRED_CHAR = SQL_LIKE_ONLY, + SQL_COL_PRED_BASIC = SQL_ALL_EXCEPT_LIKE +} + +// * Special return values for SQLGetData * +enum uint SQL_NO_TOTAL = (-4); + +/********************************************/ +/* SQLGetFunctions: additional values for */ +/* fFunction to represent functions that */ +/* are not in the X/Open spec. */ +/********************************************/ +enum +{ + SQL_API_SQLALLOCHANDLESTD = 73, + SQL_API_SQLBULKOPERATIONS = 24, + SQL_API_SQLBINDPARAMETER = 72, + SQL_API_SQLBROWSECONNECT = 55, + SQL_API_SQLCOLATTRIBUTES = 6, + SQL_API_SQLCOLUMNPRIVILEGES = 56, + SQL_API_SQLDESCRIBEPARAM = 58, + SQL_API_SQLDRIVERCONNECT = 41, + SQL_API_SQLDRIVERS = 71, + SQL_API_SQLEXTENDEDFETCH = 59, + SQL_API_SQLFOREIGNKEYS = 60, + SQL_API_SQLMORERESULTS = 61, + SQL_API_SQLNATIVESQL = 62, + SQL_API_SQLNUMPARAMS = 63, + SQL_API_SQLPARAMOPTIONS = 64, + SQL_API_SQLPRIMARYKEYS = 65, + SQL_API_SQLPROCEDURECOLUMNS = 66, + SQL_API_SQLPROCEDURES = 67, + SQL_API_SQLSETPOS = 68, + SQL_API_SQLSETSCROLLOPTIONS = 69, + SQL_API_SQLTABLEPRIVILEGES = 70 +} + +/+----------------------------------------------+ + ' SQL_API_ODBC3_ALL_FUNCTIONS ' + ' This returns a bitmap, which allows us to ' + ' handle the higher-valued function numbers. ' + ' Use SQL_FUNC_EXISTS(bitmap,function_number) ' + ' to determine if the function exists. ' + +----------------------------------------------+/ +enum +{ + SQL_API_ODBC3_ALL_FUNCTIONS = 999, + SQL_API_ODBC3_ALL_FUNCTIONS_SIZE = 250, /* array of 250 words */ +} + +//SQL_FUNC_EXISTS(pfExists, uwAPI) ((*(((UWORD*) (pfExists)) + ((uwAPI) >> 4)) +// & (1 << ((uwAPI) & 0x000F)) ) ? SQL_TRUE : SQL_FALSE ) + +/+-----------------------------------------------+ + ' ODBC 3.0 SQLGetInfo values that are not part ' + ' of the X/Open standard at this time. X/Open ' + ' standard values are in sql.h. ' + +-----------------------------------------------+/ +enum +{ + SQL_ACTIVE_ENVIRONMENTS = 116, + SQL_ALTER_DOMAIN = 117, + SQL_SQL_CONFORMANCE = 118, + SQL_DATETIME_LITERALS = 119, + SQL_ASYNC_MODE = 10_021, /* new X/Open spec */ + SQL_BATCH_ROW_COUNT = 120, + SQL_BATCH_SUPPORT = 121, + SQL_QUALIFIER_LOCATION = 114, + SQL_QUALIFIER_NAME_SEPARATOR = 41, + SQL_QUALIFIER_TERM = 42, + SQL_QUALIFIER_USAGE = 92, + SQL_CATALOG_LOCATION = SQL_QUALIFIER_LOCATION, + SQL_CATALOG_NAME_SEPARATOR = SQL_QUALIFIER_NAME_SEPARATOR, + SQL_CATALOG_TERM = SQL_QUALIFIER_TERM, + SQL_CATALOG_USAGE = SQL_QUALIFIER_USAGE, + SQL_CONVERT_WCHAR = 122, + SQL_CONVERT_INTERVAL_DAY_TIME = 123, + SQL_CONVERT_INTERVAL_YEAR_MONTH = 124, + SQL_CONVERT_WLONGVARCHAR = 125, + SQL_CONVERT_WVARCHAR = 126, + SQL_CREATE_ASSERTION = 127, + SQL_CREATE_CHARACTER_SET = 128, + SQL_CREATE_COLLATION = 129, + SQL_CREATE_DOMAIN = 130, + SQL_CREATE_SCHEMA = 131, + SQL_CREATE_TABLE = 132, + SQL_CREATE_TRANSLATION = 133, + SQL_CREATE_VIEW = 134, + SQL_DRIVER_HDESC = 135, + SQL_DROP_ASSERTION = 136, + SQL_DROP_CHARACTER_SET = 137, + SQL_DROP_COLLATION = 138, + SQL_DROP_DOMAIN = 139, + SQL_DROP_SCHEMA = 140, + SQL_DROP_TABLE = 141, + SQL_DROP_TRANSLATION = 142, + SQL_DROP_VIEW = 143, + SQL_DYNAMIC_CURSOR_ATTRIBUTES1 = 144, + SQL_DYNAMIC_CURSOR_ATTRIBUTES2 = 145, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 = 146, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 = 147, + SQL_INDEX_KEYWORDS = 148, + SQL_INFO_SCHEMA_VIEWS = 149, + SQL_KEYSET_CURSOR_ATTRIBUTES1 = 150, + SQL_KEYSET_CURSOR_ATTRIBUTES2 = 151, + SQL_MAX_ASYNC_CONCURRENT_STATEMENTS = 10_022, /* new X/Open spec */ + SQL_ODBC_INTERFACE_CONFORMANCE = 152, + SQL_PARAM_ARRAY_ROW_COUNTS = 153, + SQL_PARAM_ARRAY_SELECTS = 154, + SQL_OWNER_TERM = 39, + SQL_OWNER_USAGE = 91, + SQL_SCHEMA_TERM = SQL_OWNER_TERM, + SQL_SCHEMA_USAGE = SQL_OWNER_USAGE, + SQL_SQL92_DATETIME_FUNCTIONS = 155, + SQL_SQL92_FOREIGN_KEY_DELETE_RULE = 156, + SQL_SQL92_FOREIGN_KEY_UPDATE_RULE = 157, + SQL_SQL92_GRANT = 158, + SQL_SQL92_NUMERIC_VALUE_FUNCTIONS = 159, + SQL_SQL92_PREDICATES = 160, + SQL_SQL92_RELATIONAL_JOIN_OPERATORS = 161, + SQL_SQL92_REVOKE = 162, + SQL_SQL92_ROW_VALUE_enumRUCTOR = 163, + SQL_SQL92_STRING_FUNCTIONS = 164, + SQL_SQL92_VALUE_EXPRESSIONS = 165, + SQL_STANDARD_CLI_CONFORMANCE = 166, + SQL_STATIC_CURSOR_ATTRIBUTES1 = 167, + SQL_STATIC_CURSOR_ATTRIBUTES2 = 168, + SQL_AGGREGATE_FUNCTIONS = 169, + SQL_DDL_INDEX = 170, + SQL_DM_VER = 171, + SQL_INSERT_STATEMENT = 172, + SQL_UNION = 96, + SQL_UNION_STATEMENT = SQL_UNION +} + +enum int SQL_DTC_TRANSITION_COST = 1750; + +/+ + ' -- SQL_ALTER_TABLE bitmasks -- + ' the following 5 bitmasks are defined in sql.d + ' enum int SQL_AT_ADD_COLUMN = 0x00000001L + ' enum int SQL_AT_DROP_COLUMN = 0x00000002L + ' enum int SQL_AT_ADD_CONSTRAINT = 0x00000008L + ' + +/ +enum +{ + SQL_AT_ADD_COLUMN_SINGLE = 0x00000020L, + SQL_AT_ADD_COLUMN_DEFAULT = 0x00000040L, + SQL_AT_ADD_COLUMN_COLLATION = 0x00000080L, + SQL_AT_SET_COLUMN_DEFAULT = 0x00000100L, + SQL_AT_DROP_COLUMN_DEFAULT = 0x00000200L, + SQL_AT_DROP_COLUMN_CASCADE = 0x00000400L, + SQL_AT_DROP_COLUMN_RESTRICT = 0x00000800L, + SQL_AT_ADD_TABLE_CONSTRAINT = 0x00001000L, + SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE = 0x00002000L, + SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT = 0x00004000L, + SQL_AT_CONSTRAINT_NAME_DEFINITION = 0x00008000L, + SQL_AT_CONSTRAINT_INITIALLY_DEFERRED = 0x00010000L, + SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00020000L, + SQL_AT_CONSTRAINT_DEFERRABLE = 0x00040000L, + SQL_AT_CONSTRAINT_NON_DEFERRABLE = 0x00080000L +} + +// * SQL_CONVERT_* return value bitmasks * +enum +{ + SQL_CVT_CHAR = 0x00000001L, + SQL_CVT_NUMERIC = 0x00000002L, + SQL_CVT_DECIMAL = 0x00000004L, + SQL_CVT_INTEGER = 0x00000008L, + SQL_CVT_SMALLINT = 0x00000010L, + SQL_CVT_FLOAT = 0x00000020L, + SQL_CVT_REAL = 0x00000040L, + SQL_CVT_DOUBLE = 0x00000080L, + SQL_CVT_VARCHAR = 0x00000100L, + SQL_CVT_LONGVARCHAR = 0x00000200L, + SQL_CVT_BINARY = 0x00000400L, + SQL_CVT_VARBINARY = 0x00000800L, + SQL_CVT_BIT = 0x00001000L, + SQL_CVT_TINYINT = 0x00002000L, + SQL_CVT_BIGINT = 0x00004000L, + SQL_CVT_DATE = 0x00008000L, + SQL_CVT_TIME = 0x00010000L, + SQL_CVT_TIMESTAMP = 0x00020000L, + SQL_CVT_LONGVARBINARY = 0x00040000L +} + +enum +{ + SQL_CVT_INTERVAL_YEAR_MONTH = 0x00080000L, + SQL_CVT_INTERVAL_DAY_TIME = 0x00100000L, + SQL_CVT_WCHAR = 0x00200000L, + SQL_CVT_WLONGVARCHAR = 0x00400000L, + SQL_CVT_WVARCHAR = 0x00800000L +} + +// * SQL_CONVERT_FUNCTIONS functions * +enum +{ + SQL_FN_CVT_CONVERT = 0x00000001L, + SQL_FN_CVT_CAST = 0x00000002L +} + +// * SQL_STRING_FUNCTIONS functions * +enum +{ + SQL_FN_STR_CONCAT = 0x00000001L, + SQL_FN_STR_INSERT = 0x00000002L, + SQL_FN_STR_LEFT = 0x00000004L, + SQL_FN_STR_LTRIM = 0x00000008L, + SQL_FN_STR_LENGTH = 0x00000010L, + SQL_FN_STR_LOCATE = 0x00000020L, + SQL_FN_STR_LCASE = 0x00000040L, + SQL_FN_STR_REPEAT = 0x00000080L, + SQL_FN_STR_REPLACE = 0x00000100L, + SQL_FN_STR_RIGHT = 0x00000200L, + SQL_FN_STR_RTRIM = 0x00000400L, + SQL_FN_STR_SUBSTRING = 0x00000800L, + SQL_FN_STR_UCASE = 0x00001000L, + SQL_FN_STR_ASCII = 0x00002000L, + SQL_FN_STR_CHAR = 0x00004000L, + SQL_FN_STR_DIFFERENCE = 0x00008000L, + SQL_FN_STR_LOCATE_2 = 0x00010000L, + SQL_FN_STR_SOUNDEX = 0x00020000L, + SQL_FN_STR_SPACE = 0x00040000L +} + +enum +{ + SQL_FN_STR_BIT_LENGTH = 0x00080000L, + SQL_FN_STR_CHAR_LENGTH = 0x00100000L, + SQL_FN_STR_CHARACTER_LENGTH = 0x00200000L, + SQL_FN_STR_OCTET_LENGTH = 0x00400000L, + SQL_FN_STR_POSITION = 0x00800000L +} + +// * SQL_SQL92_STRING_FUNCTIONS * +enum +{ + SQL_SSF_CONVERT = 0x00000001L, + SQL_SSF_LOWER = 0x00000002L, + SQL_SSF_UPPER = 0x00000004L, + SQL_SSF_SUBSTRING = 0x00000008L, + SQL_SSF_TRANSLATE = 0x00000010L, + SQL_SSF_TRIM_BOTH = 0x00000020L, + SQL_SSF_TRIM_LEADING = 0x00000040L, + SQL_SSF_TRIM_TRAILING = 0x00000080L +} + +// * SQL_NUMERIC_FUNCTIONS functions * +enum +{ + SQL_FN_NUM_ABS = 0x00000001L, + SQL_FN_NUM_ACOS = 0x00000002L, + SQL_FN_NUM_ASIN = 0x00000004L, + SQL_FN_NUM_ATAN = 0x00000008L, + SQL_FN_NUM_ATAN2 = 0x00000010L, + SQL_FN_NUM_CEILING = 0x00000020L, + SQL_FN_NUM_COS = 0x00000040L, + SQL_FN_NUM_COT = 0x00000080L, + SQL_FN_NUM_EXP = 0x00000100L, + SQL_FN_NUM_FLOOR = 0x00000200L, + SQL_FN_NUM_LOG = 0x00000400L, + SQL_FN_NUM_MOD = 0x00000800L, + SQL_FN_NUM_SIGN = 0x00001000L, + SQL_FN_NUM_SIN = 0x00002000L, + SQL_FN_NUM_SQRT = 0x00004000L, + SQL_FN_NUM_TAN = 0x00008000L, + SQL_FN_NUM_PI = 0x00010000L, + SQL_FN_NUM_RAND = 0x00020000L, + SQL_FN_NUM_DEGREES = 0x00040000L, + SQL_FN_NUM_LOG10 = 0x00080000L, + SQL_FN_NUM_POWER = 0x00100000L, + SQL_FN_NUM_RADIANS = 0x00200000L, + SQL_FN_NUM_ROUND = 0x00400000L, + SQL_FN_NUM_TRUNCATE = 0x00800000L +} + +// * SQL_SQL92_NUMERIC_VALUE_FUNCTIONS * +enum +{ + SQL_SNVF_BIT_LENGTH = 0x00000001L, + SQL_SNVF_CHAR_LENGTH = 0x00000002L, + SQL_SNVF_CHARACTER_LENGTH = 0x00000004L, + SQL_SNVF_EXTRACT = 0x00000008L, + SQL_SNVF_OCTET_LENGTH = 0x00000010L, + SQL_SNVF_POSITION = 0x00000020L +} + +// * SQL_TIMEDATE_FUNCTIONS functions * +enum +{ + SQL_FN_TD_NOW = 0x00000001L, + SQL_FN_TD_CURDATE = 0x00000002L, + SQL_FN_TD_DAYOFMONTH = 0x00000004L, + SQL_FN_TD_DAYOFWEEK = 0x00000008L, + SQL_FN_TD_DAYOFYEAR = 0x00000010L, + SQL_FN_TD_MONTH = 0x00000020L, + SQL_FN_TD_QUARTER = 0x00000040L, + SQL_FN_TD_WEEK = 0x00000080L, + SQL_FN_TD_YEAR = 0x00000100L, + SQL_FN_TD_CURTIME = 0x00000200L, + SQL_FN_TD_HOUR = 0x00000400L, + SQL_FN_TD_MINUTE = 0x00000800L, + SQL_FN_TD_SECOND = 0x00001000L, + SQL_FN_TD_TIMESTAMPADD = 0x00002000L, + SQL_FN_TD_TIMESTAMPDIFF = 0x00004000L, + SQL_FN_TD_DAYNAME = 0x00008000L, + SQL_FN_TD_MONTHNAME = 0x00010000L +} + +enum +{ + SQL_FN_TD_CURRENT_DATE = 0x00020000L, + SQL_FN_TD_CURRENT_TIME = 0x00040000L, + SQL_FN_TD_CURRENT_TIMESTAMP = 0x00080000L, + SQL_FN_TD_EXTRACT = 0x00100000L +} + +// * SQL_SQL92_DATETIME_FUNCTIONS * +enum +{ + SQL_SDF_CURRENT_DATE = 0x00000001L, + SQL_SDF_CURRENT_TIME = 0x00000002L, + SQL_SDF_CURRENT_TIMESTAMP = 0x00000004L +} + +// * SQL_SYSTEM_FUNCTIONS functions * +enum +{ + SQL_FN_SYS_USERNAME = 0x00000001L, + SQL_FN_SYS_DBNAME = 0x00000002L, + SQL_FN_SYS_IFNULL = 0x00000004L +} + +// * SQL_TIMEDATE_ADD_INTERVALS and SQL_TIMEDATE_DIFF_INTERVALS functions * +enum +{ + SQL_FN_TSI_FRAC_SECOND = 0x00000001L, + SQL_FN_TSI_SECOND = 0x00000002L, + SQL_FN_TSI_MINUTE = 0x00000004L, + SQL_FN_TSI_HOUR = 0x00000008L, + SQL_FN_TSI_DAY = 0x00000010L, + SQL_FN_TSI_WEEK = 0x00000020L, + SQL_FN_TSI_MONTH = 0x00000040L, + SQL_FN_TSI_QUARTER = 0x00000080L, + SQL_FN_TSI_YEAR = 0x00000100L +} + +/+ + ' bitmasks for SQL_DYNAMIC_CURSOR_ATTRIBUTES1, + ' SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, + ' SQL_KEYSET_CURSOR_ATTRIBUTES1, and SQL_STATIC_CURSOR_ATTRIBUTES1 + ' + +/ +enum +{ + // * supported SQLFetchScroll FetchOrientation's * + SQL_CA1_NEXT = 0x00000001L, + SQL_CA1_ABSOLUTE = 0x00000002L, + SQL_CA1_RELATIVE = 0x00000004L, + SQL_CA1_BOOKMARK = 0x00000008L, + + // * supported SQLSetPos LockType's * + SQL_CA1_LOCK_NO_CHANGE = 0x00000040L, + SQL_CA1_LOCK_EXCLUSIVE = 0x00000080L, + SQL_CA1_LOCK_UNLOCK = 0x00000100L, + + // * supported SQLSetPos Operations * + SQL_CA1_POS_POSITION = 0x00000200L, + SQL_CA1_POS_UPDATE = 0x00000400L, + SQL_CA1_POS_DELETE = 0x00000800L, + SQL_CA1_POS_REFRESH = 0x00001000L, + + // * positioned updates and deletes * + SQL_CA1_POSITIONED_UPDATE = 0x00002000L, + SQL_CA1_POSITIONED_DELETE = 0x00004000L, + SQL_CA1_SELECT_FOR_UPDATE = 0x00008000L, + + // * supported SQLBulkOperations operations * + SQL_CA1_BULK_ADD = 0x00010000L, + SQL_CA1_BULK_UPDATE_BY_BOOKMARK = 0x00020000L, + SQL_CA1_BULK_DELETE_BY_BOOKMARK = 0x00040000L, + SQL_CA1_BULK_FETCH_BY_BOOKMARK = 0x00080000L +} + +/+ + ' bitmasks for SQL_DYNAMIC_CURSOR_ATTRIBUTES2, + ' SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, + ' SQL_KEYSET_CURSOR_ATTRIBUTES2, and SQL_STATIC_CURSOR_ATTRIBUTES2 + ' + +/ +enum +{ + // * supported values for SQL_ATTR_SCROLL_CONCURRENCY * + SQL_CA2_READ_ONLY_CONCURRENCY = 0x00000001L, + SQL_CA2_LOCK_CONCURRENCY = 0x00000002L, + SQL_CA2_OPT_ROWVER_CONCURRENCY = 0x00000004L, + SQL_CA2_OPT_VALUES_CONCURRENCY = 0x00000008L, + + // * sensitivity of the cursor to its own inserts, deletes, and updates * + SQL_CA2_SENSITIVITY_ADDITIONS = 0x00000010L, + SQL_CA2_SENSITIVITY_DELETIONS = 0x00000020L, + SQL_CA2_SENSITIVITY_UPDATES = 0x00000040L, + + // * semantics of SQL_ATTR_MAX_ROWS * + SQL_CA2_MAX_ROWS_SELECT = 0x00000080L, + SQL_CA2_MAX_ROWS_INSERT = 0x00000100L, + SQL_CA2_MAX_ROWS_DELETE = 0x00000200L, + SQL_CA2_MAX_ROWS_UPDATE = 0x00000400L, + SQL_CA2_MAX_ROWS_CATALOG = 0x00000800L, + SQL_CA2_MAX_ROWS_AFFECTS_ALL = (SQL_CA2_MAX_ROWS_SELECT | + SQL_CA2_MAX_ROWS_INSERT | SQL_CA2_MAX_ROWS_DELETE | + SQL_CA2_MAX_ROWS_UPDATE | SQL_CA2_MAX_ROWS_CATALOG), + + // * semantics of SQL_DIAG_CURSOR_ROW_COUNT * + SQL_CA2_CRC_EXACT = 0x00001000L, + SQL_CA2_CRC_APPROXIMATE = 0x00002000L, + + // * the kinds of positioned statements that can be simulated * + SQL_CA2_SIMULATE_NON_UNIQUE = 0x00004000L, + SQL_CA2_SIMULATE_TRY_UNIQUE = 0x00008000L, + SQL_CA2_SIMULATE_UNIQUE = 0x00010000L +} + +// * SQL_ODBC_API_CONFORMANCE values * +enum +{ + SQL_OAC_NONE = 0x0000, + SQL_OAC_LEVEL1 = 0x0001, + SQL_OAC_LEVEL2 = 0x0002 +} + +// * SQL_ODBC_SAG_CLI_CONFORMANCE values * +enum +{ + SQL_OSCC_NOT_COMPLIANT = 0x0000, + SQL_OSCC_COMPLIANT = 0x0001 +} + +// * SQL_ODBC_SQL_CONFORMANCE values * +enum +{ + SQL_OSC_MINIMUM = 0x0000, + SQL_OSC_CORE = 0x0001, + SQL_OSC_EXTENDED = 0x0002 +} + +// * SQL_CONCAT_NULL_BEHAVIOR values * +enum +{ + SQL_CB_NULL = 0x0000, + SQL_CB_NON_NULL = 0x0001 +} + +// * SQL_SCROLL_OPTIONS masks * +enum +{ + SQL_SO_FORWARD_ONLY = 0x00000001L, + SQL_SO_KEYSET_DRIVEN = 0x00000002L, + SQL_SO_DYNAMIC = 0x00000004L, + SQL_SO_MIXED = 0x00000008L, + SQL_SO_STATIC = 0x00000010L +} + +// * SQL_FETCH_DIRECTION masks * +enum +{ + SQL_FD_FETCH_BOOKMARK = 0x00000080L +} + +// * SQL_CORRELATION_NAME values * +enum +{ + SQL_CN_NONE = 0x0000, + SQL_CN_DIFFERENT = 0x0001, + SQL_CN_ANY = 0x0002 +} + +enum +{ + // * SQL_NON_NULLABLE_COLUMNS values * + SQL_NNC_NULL = 0x0000, + SQL_NNC_NON_NULL = 0x0001, + + // * SQL_NULL_COLLATION values * + SQL_NC_START = 0x0002, + SQL_NC_END = 0x0004 +} + +// * SQL_FILE_USAGE values * +enum +{ + SQL_FILE_NOT_SUPPORTED = 0x0000, + SQL_FILE_TABLE = 0x0001, + SQL_FILE_QUALIFIER = 0x0002, + SQL_FILE_CATALOG = SQL_FILE_QUALIFIER +} + +// * SQL_GETDATA_EXTENSIONS values * +enum +{ + SQL_GD_BLOCK = 0x00000004L, + SQL_GD_BOUND = 0x00000008L +} + +// * SQL_POSITIONED_STATEMENTS masks * +enum +{ + SQL_PS_POSITIONED_DELETE = 0x00000001L, + SQL_PS_POSITIONED_UPDATE = 0x00000002L, + SQL_PS_SELECT_FOR_UPDATE = 0x00000004L +} + +// * SQL_GROUP_BY values * +enum +{ + SQL_GB_NOT_SUPPORTED = 0x0000, + SQL_GB_GROUP_BY_EQUALS_SELECT = 0x0001, + SQL_GB_GROUP_BY_CONTAINS_SELECT = 0x0002, + SQL_GB_NO_RELATION = 0x0003, + SQL_GB_COLLATE = 0x0004 +} + +// * SQL_OWNER_USAGE masks * +enum +{ + SQL_OU_DML_STATEMENTS = 0x00000001L, + SQL_OU_PROCEDURE_INVOCATION = 0x00000002L, + SQL_OU_TABLE_DEFINITION = 0x00000004L, + SQL_OU_INDEX_DEFINITION = 0x00000008L, + SQL_OU_PRIVILEGE_DEFINITION = 0x00000010L +} + +// * SQL_SCHEMA_USAGE masks * +enum +{ + SQL_SU_DML_STATEMENTS = SQL_OU_DML_STATEMENTS, + SQL_SU_PROCEDURE_INVOCATION = SQL_OU_PROCEDURE_INVOCATION, + SQL_SU_TABLE_DEFINITION = SQL_OU_TABLE_DEFINITION, + SQL_SU_INDEX_DEFINITION = SQL_OU_INDEX_DEFINITION, + SQL_SU_PRIVILEGE_DEFINITION = SQL_OU_PRIVILEGE_DEFINITION +} + +// * SQL_QUALIFIER_USAGE masks * +enum +{ + SQL_QU_DML_STATEMENTS = 0x00000001L, + SQL_QU_PROCEDURE_INVOCATION = 0x00000002L, + SQL_QU_TABLE_DEFINITION = 0x00000004L, + SQL_QU_INDEX_DEFINITION = 0x00000008L, + SQL_QU_PRIVILEGE_DEFINITION = 0x00000010L +} + +enum +{ + // * SQL_CATALOG_USAGE masks * + SQL_CU_DML_STATEMENTS = SQL_QU_DML_STATEMENTS, + SQL_CU_PROCEDURE_INVOCATION = SQL_QU_PROCEDURE_INVOCATION, + SQL_CU_TABLE_DEFINITION = SQL_QU_TABLE_DEFINITION, + SQL_CU_INDEX_DEFINITION = SQL_QU_INDEX_DEFINITION, + SQL_CU_PRIVILEGE_DEFINITION = SQL_QU_PRIVILEGE_DEFINITION +} + +enum +{ + // * SQL_SUBQUERIES masks * + SQL_SQ_COMPARISON = 0x00000001L, + SQL_SQ_EXISTS = 0x00000002L, + SQL_SQ_IN = 0x00000004L, + SQL_SQ_QUANTIFIED = 0x00000008L, + SQL_SQ_CORRELATED_SUBQUERIES = 0x00000010L, + + // * SQL_UNION masks * + SQL_U_UNION = 0x00000001L, + SQL_U_UNION_ALL = 0x00000002L, + + // * SQL_BOOKMARK_PERSISTENCE values * + SQL_BP_CLOSE = 0x00000001L, + SQL_BP_DELETE = 0x00000002L, + SQL_BP_DROP = 0x00000004L, + SQL_BP_TRANSACTION = 0x00000008L, + SQL_BP_UPDATE = 0x00000010L, + SQL_BP_OTHER_HSTMT = 0x00000020L, + SQL_BP_SCROLL = 0x00000040L, + + // * SQL_STATIC_SENSITIVITY values * + SQL_SS_ADDITIONS = 0x00000001L, + SQL_SS_DELETIONS = 0x00000002L, + SQL_SS_UPDATES = 0x00000004L, + + // * SQL_VIEW values * + SQL_CV_CREATE_VIEW = 0x00000001L, + SQL_CV_CHECK_OPTION = 0x00000002L, + SQL_CV_CASCADED = 0x00000004L, + SQL_CV_LOCAL = 0x00000008L, + + // * SQL_LOCK_TYPES masks * + SQL_LCK_NO_CHANGE = 0x00000001L, + SQL_LCK_EXCLUSIVE = 0x00000002L, + SQL_LCK_UNLOCK = 0x00000004L, + + // * SQL_POS_OPERATIONS masks * + SQL_POS_POSITION = 0x00000001L, + SQL_POS_REFRESH = 0x00000002L, + SQL_POS_UPDATE = 0x00000004L, + SQL_POS_DELETE = 0x00000008L, + SQL_POS_ADD = 0x00000010L, + + // * SQL_QUALIFIER_LOCATION values * + SQL_QL_START = 0x0001, + SQL_QL_END = 0x0002 +} + +// * Here start return values for ODBC 3.0 SQLGetInfo * +enum +{ + // * SQL_AGGREGATE_FUNCTIONS bitmasks * + SQL_AF_AVG = 0x00000001L, + SQL_AF_COUNT = 0x00000002L, + SQL_AF_MAX = 0x00000004L, + SQL_AF_MIN = 0x00000008L, + SQL_AF_SUM = 0x00000010L, + SQL_AF_DISTINCT = 0x00000020L, + SQL_AF_ALL = 0x00000040L, + + // * SQL_SQL_CONFORMANCE bit masks * + SQL_SC_SQL92_ENTRY = 0x00000001L, + SQL_SC_FIPS127_2_TRANSITIONAL = 0x00000002L, + SQL_SC_SQL92_INTERMEDIATE = 0x00000004L, + SQL_SC_SQL92_FULL = 0x00000008L, + + // * SQL_DATETIME_LITERALS masks * + SQL_DL_SQL92_DATE = 0x00000001L, + SQL_DL_SQL92_TIME = 0x00000002L, + SQL_DL_SQL92_TIMESTAMP = 0x00000004L, + SQL_DL_SQL92_INTERVAL_YEAR = 0x00000008L, + SQL_DL_SQL92_INTERVAL_MONTH = 0x00000010L, + SQL_DL_SQL92_INTERVAL_DAY = 0x00000020L, + SQL_DL_SQL92_INTERVAL_HOUR = 0x00000040L, + SQL_DL_SQL92_INTERVAL_MINUTE = 0x00000080L, + SQL_DL_SQL92_INTERVAL_SECOND = 0x00000100L, + SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH = 0x00000200L, + SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR = 0x00000400L, + SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE = 0x00000800L, + SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND = 0x00001000L, + SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE = 0x00002000L, + SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND = 0x00004000L, + SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND = 0x00008000L, + + // * SQL_CATALOG_LOCATION values * + SQL_CL_START = SQL_QL_START, + SQL_CL_END = SQL_QL_END, + + // * values for SQL_BATCH_ROW_COUNT * + SQL_BRC_PROCEDURES = 0x0000001, + SQL_BRC_EXPLICIT = 0x0000002, + SQL_BRC_ROLLED_UP = 0x0000004, + + // * bitmasks for SQL_BATCH_SUPPORT * + SQL_BS_SELECT_EXPLICIT = 0x00000001L, + SQL_BS_ROW_COUNT_EXPLICIT = 0x00000002L, + SQL_BS_SELECT_PROC = 0x00000004L, + SQL_BS_ROW_COUNT_PROC = 0x00000008L, + + // * Values for SQL_PARAM_ARRAY_ROW_COUNTS getinfo */ + SQL_PARC_BATCH = 1, + SQL_PARC_NO_BATCH = 2, + + // * values for SQL_PARAM_ARRAY_SELECTS * + SQL_PAS_BATCH = 1, + SQL_PAS_NO_BATCH = 2, + SQL_PAS_NO_SELECT = 3, + + // * Bitmasks for SQL_INDEX_KEYWORDS * + SQL_IK_NONE = 0x00000000L, + SQL_IK_ASC = 0x00000001L, + SQL_IK_DESC = 0x00000002L, + SQL_IK_ALL = (SQL_IK_ASC | SQL_IK_DESC), + + // * Bitmasks for SQL_INFO_SCHEMA_VIEWS * + SQL_ISV_ASSERTIONS = 0x00000001L, + SQL_ISV_CHARACTER_SETS = 0x00000002L, + SQL_ISV_CHECK_CONSTRAINTS = 0x00000004L, + SQL_ISV_COLLATIONS = 0x00000008L, + SQL_ISV_COLUMN_DOMAIN_USAGE = 0x00000010L, + SQL_ISV_COLUMN_PRIVILEGES = 0x00000020L, + SQL_ISV_COLUMNS = 0x00000040L, + SQL_ISV_CONSTRAINT_COLUMN_USAGE = 0x00000080L, + SQL_ISV_CONSTRAINT_TABLE_USAGE = 0x00000100L, + SQL_ISV_DOMAIN_CONSTRAINTS = 0x00000200L, + SQL_ISV_DOMAINS = 0x00000400L, + SQL_ISV_KEY_COLUMN_USAGE = 0x00000800L, + SQL_ISV_REFERENTIAL_CONSTRAINTS = 0x00001000L, + SQL_ISV_SCHEMATA = 0x00002000L, + SQL_ISV_SQL_LANGUAGES = 0x00004000L, + SQL_ISV_TABLE_CONSTRAINTS = 0x00008000L, + SQL_ISV_TABLE_PRIVILEGES = 0x00010000L, + SQL_ISV_TABLES = 0x00020000L, + SQL_ISV_TRANSLATIONS = 0x00040000L, + SQL_ISV_USAGE_PRIVILEGES = 0x00080000L, + SQL_ISV_VIEW_COLUMN_USAGE = 0x00100000L, + SQL_ISV_VIEW_TABLE_USAGE = 0x00200000L, + SQL_ISV_VIEWS = 0x00400000L, + + // * Bitmasks for SQL_ASYNC_MODE * + SQL_AM_NONE = 0, + SQL_AM_CONNECTION = 1, + SQL_AM_STATEMENT = 2, + + // * Bitmasks for SQL_ALTER_DOMAIN * + SQL_AD_CONSTRAINT_NAME_DEFINITION = 0x00000001L, + SQL_AD_ADD_DOMAIN_CONSTRAINT = 0x00000002L, + SQL_AD_DROP_DOMAIN_CONSTRAINT = 0x00000004L, + SQL_AD_ADD_DOMAIN_DEFAULT = 0x00000008L, + SQL_AD_DROP_DOMAIN_DEFAULT = 0x00000010L, + SQL_AD_ADD_CONSTRAINT_INITIALLY_DEFERRED = 0x00000020L, + SQL_AD_ADD_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000040L, + SQL_AD_ADD_CONSTRAINT_DEFERRABLE = 0x00000080L, + SQL_AD_ADD_CONSTRAINT_NON_DEFERRABLE = 0x00000100L, + + // * SQL_CREATE_SCHEMA bitmasks * + SQL_CS_CREATE_SCHEMA = 0x00000001L, + SQL_CS_AUTHORIZATION = 0x00000002L, + SQL_CS_DEFAULT_CHARACTER_SET = 0x00000004L, + + // * SQL_CREATE_TRANSLATION bitmasks * + SQL_CTR_CREATE_TRANSLATION = 0x00000001L, + + // * SQL_CREATE_ASSERTION bitmasks * + SQL_CA_CREATE_ASSERTION = 0x00000001L, + SQL_CA_CONSTRAINT_INITIALLY_DEFERRED = 0x00000010L, + SQL_CA_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000020L, + SQL_CA_CONSTRAINT_DEFERRABLE = 0x00000040L, + SQL_CA_CONSTRAINT_NON_DEFERRABLE = 0x00000080L, + + // * SQL_CREATE_CHARACTER_SET bitmasks * + SQL_CCS_CREATE_CHARACTER_SET = 0x00000001L, + SQL_CCS_COLLATE_CLAUSE = 0x00000002L, + SQL_CCS_LIMITED_COLLATION = 0x00000004L, + + // * SQL_CREATE_COLLATION bitmasks * + SQL_CCOL_CREATE_COLLATION = 0x00000001L, + + // * SQL_CREATE_DOMAIN bitmasks * + SQL_CDO_CREATE_DOMAIN = 0x00000001L, + SQL_CDO_DEFAULT = 0x00000002L, + SQL_CDO_CONSTRAINT = 0x00000004L, + SQL_CDO_COLLATION = 0x00000008L, + SQL_CDO_CONSTRAINT_NAME_DEFINITION = 0x00000010L, + SQL_CDO_CONSTRAINT_INITIALLY_DEFERRED = 0x00000020L, + SQL_CDO_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000040L, + SQL_CDO_CONSTRAINT_DEFERRABLE = 0x00000080L, + SQL_CDO_CONSTRAINT_NON_DEFERRABLE = 0x00000100L, + + // * SQL_CREATE_TABLE bitmasks * + SQL_CT_CREATE_TABLE = 0x00000001L, + SQL_CT_COMMIT_PRESERVE = 0x00000002L, + SQL_CT_COMMIT_DELETE = 0x00000004L, + SQL_CT_GLOBAL_TEMPORARY = 0x00000008L, + SQL_CT_LOCAL_TEMPORARY = 0x00000010L, + SQL_CT_CONSTRAINT_INITIALLY_DEFERRED = 0x00000020L, + SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE = 0x00000040L, + SQL_CT_CONSTRAINT_DEFERRABLE = 0x00000080L, + SQL_CT_CONSTRAINT_NON_DEFERRABLE = 0x00000100L, + SQL_CT_COLUMN_CONSTRAINT = 0x00000200L, + SQL_CT_COLUMN_DEFAULT = 0x00000400L, + SQL_CT_COLUMN_COLLATION = 0x00000800L, + SQL_CT_TABLE_CONSTRAINT = 0x00001000L, + SQL_CT_CONSTRAINT_NAME_DEFINITION = 0x00002000L, + + // * SQL_DDL_INDEX bitmasks * + SQL_DI_CREATE_INDEX = 0x00000001L, + SQL_DI_DROP_INDEX = 0x00000002L, + + // * SQL_DROP_COLLATION bitmasks * + SQL_DC_DROP_COLLATION = 0x00000001L, + + // * SQL_DROP_DOMAIN bitmasks * + SQL_DD_DROP_DOMAIN = 0x00000001L, + SQL_DD_RESTRICT = 0x00000002L, + SQL_DD_CASCADE = 0x00000004L, + + // * SQL_DROP_SCHEMA bitmasks * + SQL_DS_DROP_SCHEMA = 0x00000001L, + SQL_DS_RESTRICT = 0x00000002L, + SQL_DS_CASCADE = 0x00000004L, + + // * SQL_DROP_CHARACTER_SET bitmasks * + SQL_DCS_DROP_CHARACTER_SET = 0x00000001L, + + // * SQL_DROP_ASSERTION bitmasks * + SQL_DA_DROP_ASSERTION = 0x00000001L, + + // * SQL_DROP_TABLE bitmasks * + SQL_DT_DROP_TABLE = 0x00000001L, + SQL_DT_RESTRICT = 0x00000002L, + SQL_DT_CASCADE = 0x00000004L, + + // * SQL_DROP_TRANSLATION bitmasks * + SQL_DTR_DROP_TRANSLATION = 0x00000001L, + + // * SQL_DROP_VIEW bitmasks * + SQL_DV_DROP_VIEW = 0x00000001L, + SQL_DV_RESTRICT = 0x00000002L, + SQL_DV_CASCADE = 0x00000004L, + + // * SQL_INSERT_STATEMENT bitmasks * + SQL_IS_INSERT_LITERALS = 0x00000001L, + SQL_IS_INSERT_SEARCHED = 0x00000002L, + SQL_IS_SELECT_INTO = 0x00000004L, + + // * SQL_ODBC_INTERFACE_CONFORMANCE values * + SQL_OIC_CORE = 1UL, + SQL_OIC_LEVEL1 = 2UL, + SQL_OIC_LEVEL2 = 3UL, + + // * SQL_SQL92_FOREIGN_KEY_DELETE_RULE bitmasks * + SQL_SFKD_CASCADE = 0x00000001L, + SQL_SFKD_NO_ACTION = 0x00000002L, + SQL_SFKD_SET_DEFAULT = 0x00000004L, + SQL_SFKD_SET_NULL = 0x00000008L, + + // * SQL_SQL92_FOREIGN_KEY_UPDATE_RULE bitmasks * + SQL_SFKU_CASCADE = 0x00000001L, + SQL_SFKU_NO_ACTION = 0x00000002L, + SQL_SFKU_SET_DEFAULT = 0x00000004L, + SQL_SFKU_SET_NULL = 0x00000008L, + + // * SQL_SQL92_GRANT bitmasks * + SQL_SG_USAGE_ON_DOMAIN = 0x00000001L, + SQL_SG_USAGE_ON_CHARACTER_SET = 0x00000002L, + SQL_SG_USAGE_ON_COLLATION = 0x00000004L, + SQL_SG_USAGE_ON_TRANSLATION = 0x00000008L, + SQL_SG_WITH_GRANT_OPTION = 0x00000010L, + SQL_SG_DELETE_TABLE = 0x00000020L, + SQL_SG_INSERT_TABLE = 0x00000040L, + SQL_SG_INSERT_COLUMN = 0x00000080L, + SQL_SG_REFERENCES_TABLE = 0x00000100L, + SQL_SG_REFERENCES_COLUMN = 0x00000200L, + SQL_SG_SELECT_TABLE = 0x00000400L, + SQL_SG_UPDATE_TABLE = 0x00000800L, + SQL_SG_UPDATE_COLUMN = 0x00001000L, + + // * SQL_SQL92_PREDICATES bitmasks * + SQL_SP_EXISTS = 0x00000001L, + SQL_SP_ISNOTNULL = 0x00000002L, + SQL_SP_ISNULL = 0x00000004L, + SQL_SP_MATCH_FULL = 0x00000008L, + SQL_SP_MATCH_PARTIAL = 0x00000010L, + SQL_SP_MATCH_UNIQUE_FULL = 0x00000020L, + SQL_SP_MATCH_UNIQUE_PARTIAL = 0x00000040L, + SQL_SP_OVERLAPS = 0x00000080L, + SQL_SP_UNIQUE = 0x00000100L, + SQL_SP_LIKE = 0x00000200L, + SQL_SP_IN = 0x00000400L, + SQL_SP_BETWEEN = 0x00000800L, + SQL_SP_COMPARISON = 0x00001000L, + SQL_SP_QUANTIFIED_COMPARISON = 0x00002000L, + + // * SQL_SQL92_RELATIONAL_JOIN_OPERATORS bitmasks * + SQL_SRJO_CORRESPONDING_CLAUSE = 0x00000001L, + SQL_SRJO_CROSS_JOIN = 0x00000002L, + SQL_SRJO_EXCEPT_JOIN = 0x00000004L, + SQL_SRJO_FULL_OUTER_JOIN = 0x00000008L, + SQL_SRJO_INNER_JOIN = 0x00000010L, + SQL_SRJO_INTERSECT_JOIN = 0x00000020L, + SQL_SRJO_LEFT_OUTER_JOIN = 0x00000040L, + SQL_SRJO_NATURAL_JOIN = 0x00000080L, + SQL_SRJO_RIGHT_OUTER_JOIN = 0x00000100L, + SQL_SRJO_UNION_JOIN = 0x00000200L, + + // * SQL_SQL92_REVOKE bitmasks * + SQL_SR_USAGE_ON_DOMAIN = 0x00000001L, + SQL_SR_USAGE_ON_CHARACTER_SET = 0x00000002L, + SQL_SR_USAGE_ON_COLLATION = 0x00000004L, + SQL_SR_USAGE_ON_TRANSLATION = 0x00000008L, + SQL_SR_GRANT_OPTION_FOR = 0x00000010L, + SQL_SR_CASCADE = 0x00000020L, + SQL_SR_RESTRICT = 0x00000040L, + SQL_SR_DELETE_TABLE = 0x00000080L, + SQL_SR_INSERT_TABLE = 0x00000100L, + SQL_SR_INSERT_COLUMN = 0x00000200L, + SQL_SR_REFERENCES_TABLE = 0x00000400L, + SQL_SR_REFERENCES_COLUMN = 0x00000800L, + SQL_SR_SELECT_TABLE = 0x00001000L, + SQL_SR_UPDATE_TABLE = 0x00002000L, + SQL_SR_UPDATE_COLUMN = 0x00004000L, + + // * SQL_SQL92_ROW_VALUE_CONSTRUCTOR bitmasks * + SQL_SRVC_VALUE_EXPRESSION = 0x00000001L, + SQL_SRVC_NULL = 0x00000002L, + SQL_SRVC_DEFAULT = 0x00000004L, + SQL_SRVC_ROW_SUBQUERY = 0x00000008L, + + // * SQL_SQL92_VALUE_EXPRESSIONS bitmasks * + SQL_SVE_CASE = 0x00000001L, + SQL_SVE_CAST = 0x00000002L, + SQL_SVE_COALESCE = 0x00000004L, + SQL_SVE_NULLIF = 0x00000008L, + + // * SQL_STANDARD_CLI_CONFORMANCE bitmasks * + SQL_SCC_XOPEN_CLI_VERSION1 = 0x00000001L, + SQL_SCC_ISO92_CLI = 0x00000002L, + + // * SQL_UNION_STATEMENT bitmasks * + SQL_US_UNION = SQL_U_UNION, + SQL_US_UNION_ALL = SQL_U_UNION_ALL +} + +// * SQL_DTC_TRANSITION_COST bitmasks * +enum +{ + SQL_DTC_ENLIST_EXPENSIVE = 0x00000001L, + SQL_DTC_UNENLIST_EXPENSIVE = 0x00000002L +} + +// * additional SQLDataSources fetch directions * +enum +{ + SQL_FETCH_FIRST_USER = 31, + SQL_FETCH_FIRST_SYSTEM = 32 + +} + +enum +{ + // * Defines for SQLSetPos * + SQL_ENTIRE_ROWSET = 0, + + // * Operations in SQLSetPos * + SQL_POSITION = 0, /* 1.0 FALSE */ + SQL_REFRESH = 1, /* 1.0 TRUE */ + SQL_UPDATE = 2, + SQL_DELETE = 3, + + // * Operations in SQLBulkOperations * + SQL_ADD = 4, + SQL_SETPOS_MAX_OPTION_VALUE = SQL_ADD +} + +enum +{ + SQL_UPDATE_BY_BOOKMARK = 5, + SQL_DELETE_BY_BOOKMARK = 6, + SQL_FETCH_BY_BOOKMARK = 7 +} + +// * Lock options in SQLSetPos * +enum +{ + SQL_LOCK_NO_CHANGE = 0, /* 1.0 FALSE */ + SQL_LOCK_EXCLUSIVE = 1, /* 1.0 TRUE */ + SQL_LOCK_UNLOCK = 2, + SQL_SETPOS_MAX_LOCK_VALUE = SQL_LOCK_UNLOCK +} + +//************************ +/+ Macros for SQLSetPos. They're templates so they don't link in. +/ +//************************ +int SQL_POSITION_TO() +( + SQLHSTMT hstmt, + ushort irow +) +{ + return SQLSetPos( hstmt, irow, SQL_POSITION, SQL_LOCK_NO_CHANGE ); +} + +int SQL_LOCK_RECORD() +( + SQLHSTMT hstmt, + ushort irow, + bool fLock +) +{ + return SQLSetPos( hstmt, irow, SQL_POSITION, fLock ); +} + +int SQL_REFRESH_RECORD() +( + SQLHSTMT hstmt, + ushort irow, + bool fLock +) +{ + return SQLSetPos( hstmt, irow, SQL_REFRESH, fLock ); +} + +int SQL_UPDATE_RECORD() +( + SQLHSTMT hstmt, + ushort irow +) +{ + return SQLSetPos( hstmt, irow, SQL_UPDATE, SQL_LOCK_NO_CHANGE ); +} + +int SQL_DELETE_RECORD() +( + SQLHSTMT hstmt, + ushort irow +) +{ + return SQLSetPos( hstmt, irow, SQL_DELETE, SQL_LOCK_NO_CHANGE ); +} + +int SQL_ADD_RECORD() +( + SQLHSTMT hstmt, + ushort irow +) +{ + return SQLSetPos( hstmt, irow, SQL_ADD,SQL_LOCK_NO_CHANGE ); +} + +// * Column types and scopes in SQLSpecialColumns. * +enum +{ + SQL_BEST_ROWID = 1, + SQL_ROWVER = 2 +} + +/+ + ' Defines for SQLSpecialColumns (returned in the result set) + ' SQL_PC_UNKNOWN and SQL_PC_PSEUDO are defined in sql.d + ' + +/ +enum int SQL_PC_NOT_PSEUDO = 1; + +// * Defines for SQLStatistics * +enum +{ + SQL_QUICK = 0, + SQL_ENSURE = 1 +} + +/+ + ' Defines for SQLStatistics (returned in the result set) + ' SQL_INDEX_CLUSTERED, SQL_INDEX_HASHED, and SQL_INDEX_OTHER are + ' defined in sql.d + ' + +/ +enum int SQL_TABLE_STAT = 0; + +// * Defines for SQLTables * +immutable char[] SQL_ALL_CATALOGS = "%"; +immutable char[] SQL_ALL_SCHEMAS = "%"; +immutable char[] SQL_ALL_TABLE_TYPES = "%"; + +// * Options for SQLDriverConnect - fDriverCompletion * +enum +{ + SQL_DRIVER_NOPROMPT = 0, + SQL_DRIVER_COMPLETE = 1, + SQL_DRIVER_PROMPT = 2, + SQL_DRIVER_COMPLETE_REQUIRED = 3 +} + +/+ + ' ODBC v1.0+ ODBC + ' Is an alternative to SQLConnect. It supports data sources that require more + ' connection information than the three arguments in SQLConnect, dialog boxes + ' to prompt the user for all connection information, and data sources that are + ' not defined in the system information. + ' + ' SQLDriverConnect provides the following connection attributes: + ' + ' 1) Establish a connection using a connection string that contains the data + ' source name, one or more user IDs, one or more passwords, and other information + ' required by the data source. + ' + ' 2) Establish a connection using a partial connection string or no additional + ' information; in this case, the Driver Manager and the driver can each prompt + ' the user for connection information. + ' + ' 3) Establish a connection to a data source that is not defined in the system + ' information. If the application supplies a partial connection string, the + ' driver can prompt the user for connection information. + ' + ' 4) Establish a connection to a data source using a connection string + ' constructed from the information in a .dsn file. + ' + +/ +SQLRETURN SQLDriverConnect +( + /+ IN +/ SQLHDBC hdbc, + /+ IN +/ SQLHWND hwnd, + /+ IN +/ SQLCHAR *szConnStrIn, + /+ IN +/ SQLSMALLINT cbConnStrIn, + /+ OUT +/ SQLCHAR *szConnStrOut, + /+ IN +/ SQLSMALLINT cbConnStrOutMax, + /+ OUT +/ SQLSMALLINT *pcbConnStrOut, + /+ IN +/ SQLUSMALLINT fDriverCompletion +); + +// * Level 2 Functions * +// * SQLExtendedFetch "fFetchType" values * +enum int SQL_FETCH_BOOKMARK = 8; + +// * SQLExtendedFetch "rgfRowStatus" element values * +enum +{ + SQL_ROW_SUCCESS = 0, + SQL_ROW_DELETED = 1, + SQL_ROW_UPDATED = 2, + SQL_ROW_NOROW = 3, + SQL_ROW_ADDED = 4, + SQL_ROW_ERROR = 5 +} + +enum +{ + SQL_ROW_SUCCESS_WITH_INFO = 6, + SQL_ROW_PROCEED = 0, + SQL_ROW_IGNORE = 1 +} + +// * value for SQL_DESC_ARRAY_STATUS_PTR * +enum +{ + SQL_PARAM_SUCCESS = 0, + SQL_PARAM_SUCCESS_WITH_INFO = 6, + SQL_PARAM_ERROR = 5, + SQL_PARAM_UNUSED = 7, + SQL_PARAM_DIAG_UNAVAILABLE = 1, + + SQL_PARAM_PROCEED = 0, + SQL_PARAM_IGNORE = 1 +} + +// * Defines for SQLForeignKeys (UPDATE_RULE and DELETE_RULE) * +enum +{ + SQL_CASCADE = 0, + SQL_RESTRICT = 1, + SQL_SET_NULL = 2, + SQL_NO_ACTION = 3, + SQL_SET_DEFAULT = 4 +} + +/+ + ' Note that the following are in a different column of SQLForeignKeys than + ' the previous ones above. These are for DEFERRABILITY. + ' + +/ +enum +{ + SQL_INITIALLY_DEFERRED = 5, + SQL_INITIALLY_IMMEDIATE = 6, + SQL_NOT_DEFERRABLE = 7 +} + +/* Defines for SQLBindParameter and SQLProcedureColumns (returned in the result set) */ +enum +{ + SQL_PARAM_TYPE_UNKNOWN = 0, + SQL_PARAM_INPUT = 1, + SQL_PARAM_INPUT_OUTPUT = 2, + SQL_RESULT_COL = 3, + SQL_PARAM_OUTPUT = 4, + SQL_RETURN_VALUE = 5, + + /* Defines for SQLProcedures (returned in the result set) */ + SQL_PT_UNKNOWN = 0, + SQL_PT_PROCEDURE = 1, + SQL_PT_FUNCTION = 2 +} + +// * This define is too large for RC * +static immutable char[] SQL_ODBC_KEYWORDS = +"ABSOLUTE,ACTION,ADA,ADD,ALL,ALLOCATE,ALTER,AND,ANY,ARE,AS," ~ +"ASC,ASSERTION,AT,AUTHORIZATION,AVG," ~ +"BEGIN,BETWEEN,BIT,BIT_LENGTH,BOTH,BY,CASCADE,CASCADED,CASE,CAST,CATALOG," ~ +"CHAR,CHAR_LENGTH,CHARACTER,CHARACTER_LENGTH,CHECK,CLOSE,COALESCE," ~ +"COLLATE,COLLATION,COLUMN,COMMIT,CONNECT,CONNECTION,CONSTRAINT," ~ +"CONSTRAINTS,CONTINUE,CONVERT,CORRESPONDING,COUNT,CREATE,CROSS,CURRENT," ~ +"CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,CURSOR," ~ +"DATE,DAY,DEALLOCATE,DEC,DECIMAL,DECLARE,DEFAULT,DEFERRABLE," ~ +"DEFERRED,DELETE,DESC,DESCRIBE,DESCRIPTOR,DIAGNOSTICS,DISCONNECT," ~ +"DISTINCT,DOMAIN,DOUBLE,DROP," ~ +"ELSE,END,END-EXEC,ESCAPE,EXCEPT,EXCEPTION,EXEC,EXECUTE," ~ +"EXISTS,EXTERNAL,EXTRACT," ~ +"FALSE,FETCH,FIRST,FLOAT,FOR,FOREIGN,FORTRAN,FOUND,FROM,FULL," ~ +"GET,GLOBAL,GO,GOTO,GRANT,GROUP,HAVING,HOUR," ~ +"IDENTITY,IMMEDIATE,IN,INCLUDE,INDEX,INDICATOR,INITIALLY,INNER," ~ +"INPUT,INSENSITIVE,INSERT,INT,INTEGER,INTERSECT,INTERVAL,INTO,IS,ISOLATION," ~ +"JOIN,KEY,LANGUAGE,LAST,LEADING,LEFT,LEVEL,LIKE,LOCAL,LOWER," ~ +"MATCH,MAX,MIN,MINUTE,MODULE,MONTH," ~ +"NAMES,NATIONAL,NATURAL,NCHAR,NEXT,NO,NONE,NOT,NULL,NULLIF,NUMERIC," ~ +"OCTET_LENGTH,OF,ON,ONLY,OPEN,OPTION,OR,ORDER,OUTER,OUTPUT,OVERLAPS," ~ +"PAD,PARTIAL,PASCAL,PLI,POSITION,PRECISION,PREPARE,PRESERVE," ~ +"PRIMARY,PRIOR,PRIVILEGES,PROCEDURE,PUBLIC," ~ +"READ,REAL,REFERENCES,RELATIVE,RESTRICT,REVOKE,RIGHT,ROLLBACK,ROWS" ~ +"SCHEMA,SCROLL,SECOND,SECTION,SELECT,SESSION,SESSION_USER,SET,SIZE," ~ +"SMALLINT,SOME,SPACE,SQL,SQLCA,SQLCODE,SQLERROR,SQLSTATE,SQLWARNING," ~ +"SUBSTRING,SUM,SYSTEM_USER," ~ +"TABLE,TEMPORARY,THEN,TIME,TIMESTAMP,TIMEZONE_HOUR,TIMEZONE_MINUTE," ~ +"TO,TRAILING,TRANSACTION,TRANSLATE,TRANSLATION,TRIM,TRUE," ~ +"UNION,UNIQUE,UNKNOWN,UPDATE,UPPER,USAGE,USER,USING," ~ +"VALUE,VALUES,VARCHAR,VARYING,VIEW,WHEN,WHENEVER,WHERE,WITH,WORK,WRITE," ~ +"YEAR,ZONE"; + +/+ + ' ODBC v1.0+ ODBC + ' Supports an iterative method of discovering and enumerating the attributes + ' and attribute values required to connect to a data source. Each call to + ' SQLBrowseConnect returns successive levels of attributes and attribute values. + ' When all levels have been enumerated, a connection to the data source is + ' completed and a complete connection string is returned by SQLBrowseConnect. + ' A return code of SQL_SUCCESS or SQL_SUCCESS_WITH_INFO indicates that all + ' connection information has been specified and the application is now connected + ' to the data source. + ' + +/ +SQLRETURN SQLBrowseConnect +( + /+ IN +/ SQLHDBC hdbc, + /+ IN +/ SQLCHAR *szConnStrIn, + /+ IN +/ SQLSMALLINT cbConnStrIn, + /+ OUT +/ SQLCHAR *szConnStrOut, + /+ IN +/ SQLSMALLINT cbConnStrOutMax, + /+ OUT +/ SQLSMALLINT *pcbConnStrOut +); + +/+ + ' ODBC v3.0+ ODBC + ' Performs bulk insertions and bulk bookmark operations, + ' including update, delete, and fetch by bookmark. + ' + ' -- Operation -- + ' Operation to perform: + ' SQL_ADD + ' SQL_UPDATE_BY_BOOKMARK + ' SQL_DELETE_BY_BOOKMARK + ' SQL_FETCH_BY_BOOKMARK + ' + +/ +SQLRETURN SQLBulkOperations +( + /+ IN +/ SQLHSTMT StatementHandle, + /+ IN +/ SQLSMALLINT Operation +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns a list of columns and associated privileges for the + ' specified table. The driver returns the information as a + ' result set on the specified StatementHandle. + ' + +/ +SQLRETURN SQLColumnPrivileges +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLCHAR *szCatalogName, + /+ IN +/ SQLSMALLINT cbCatalogName, + /+ IN +/ SQLCHAR *szSchemaName, + /+ IN +/ SQLSMALLINT cbSchemaName, + /+ IN +/ SQLCHAR *szTableName, + /+ IN +/ SQLSMALLINT cbTableName, + /+ IN +/ SQLCHAR *szColumnName, + /+ IN +/ SQLSMALLINT cbColumnName +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns the description of a parameter marker associated + ' with a prepared SQL statement. This information is also + ' available in the fields of the IPD. + ' + ' -- pfNullable -- + ' Pointer to a buffer in which to return a value that indicates + ' whether the parameter allows NULL values. This value is read + ' from the SQL_DESC_NULLABLE field of the IPD. One of the following: + ' + ' SQL_NO_NULLS: The parameter does not allow NULL values (this is the default value). + ' SQL_NULLABLE: The parameter allows NULL values. + ' SQL_NULLABLE_UNKNOWN: The driver cannot determine if the parameter allows NULL values. + ' + +/ +SQLRETURN SQLDescribeParam +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLUSMALLINT ipar, + /+ OUT +/ SQLSMALLINT *pfSqlType, + /+ OUT +/ SQLUINTEGER *pcbParamDef, + /+ OUT +/ SQLSMALLINT *pibScale, + /+ OUT +/ SQLSMALLINT *pfNullable +); + +/+ + ' ODBC v1.0+ ODBC + ' SQLForeignKeys can return: + ' A list of foreign keys in the specified table (columns in the + ' specified table that refer to primary keys in other tables). + ' A list of foreign keys in other tables that refer to the primary + ' key in the specified table. The driver returns each list as a + ' result set on the specified statement. + ' + +/ +SQLRETURN SQLForeignKeys +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLCHAR *szPkCatalogName, + /+ IN +/ SQLSMALLINT cbPkCatalogName, + /+ IN +/ SQLCHAR *szPkSchemaName, + /+ IN +/ SQLSMALLINT cbPkSchemaName, + /+ IN +/ SQLCHAR *szPkTableName, + /+ IN +/ SQLSMALLINT cbPkTableName, + /+ IN +/ SQLCHAR *szFkCatalogName, + /+ IN +/ SQLSMALLINT cbFkCatalogName, + /+ IN +/ SQLCHAR *szFkSchemaName, + /+ IN +/ SQLSMALLINT cbFkSchemaName, + /+ IN +/ SQLCHAR *szFkTableName, + /+ IN +/ SQLSMALLINT cbFkTableName +); + +/+ + ' ODBC v1.0+ ODBC + ' Determines whether more results are available on a statement + ' containing SELECT, UPDATE, INSERT, or DELETE statements and, + ' if so, initializes processing for those results. + ' + +/ +SQLRETURN SQLMoreResults +( + /+ IN +/ SQLHSTMT hstmt +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns the SQL string as modified by the driver. + ' SQLNativeSql does not execute the SQL statement. + ' + +/ +SQLRETURN SQLNativeSql +( + /+ IN +/ SQLHDBC hdbc, + /+ IN +/ SQLCHAR *szSqlStrIn, + /+ IN +/ SQLINTEGER cbSqlStrIn, + /+ OUT +/ SQLCHAR *szSqlStr, + /+ IN +/ SQLINTEGER cbSqlStrMax, + /+ OUT +/ SQLINTEGER *pcbSqlStr +); + +/+ + ' ODBC v1.0+ ISO 92 + ' Returns the number of parameters in an SQL statement. + ' + +/ +SQLRETURN SQLNumParams +( + /+ IN +/ SQLHSTMT hstmt, + /+ OUT +/ SQLSMALLINT *pcpar +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns the column names that make up the primary key + ' for a table. The driver returns the information as a + ' result set. This function does not support returning + ' primary keys from multiple tables in a single call. + ' + +/ +SQLRETURN SQLPrimaryKeys +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLCHAR *szCatalogName, + /+ IN +/ SQLSMALLINT cbCatalogName, + /+ IN +/ SQLCHAR *szSchemaName, + /+ IN +/ SQLSMALLINT cbSchemaName, + /+ IN +/ SQLCHAR *szTableName, + /+ IN +/ SQLSMALLINT cbTableName +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns the list of input and output parameters, as + ' well as the columns that make up the result set for + ' the specified procedures. The driver returns the + ' information as a result set on the specified statement. + ' + +/ +SQLRETURN SQLProcedureColumns +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLCHAR *szCatalogName, + /+ IN +/ SQLSMALLINT cbCatalogName, + /+ IN +/ SQLCHAR *szSchemaName, + /+ IN +/ SQLSMALLINT cbSchemaName, + /+ IN +/ SQLCHAR *szProcName, + /+ IN +/ SQLSMALLINT cbProcName, + /+ IN +/ SQLCHAR *szColumnName, + /+ IN +/ SQLSMALLINT cbColumnName +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns the list of procedure names stored in a specific + ' data source. Procedure is a generic term used to describe + ' an executable object, or a named entity that can be invoked + ' using input and output parameters. + ' + +/ +SQLRETURN SQLProcedures +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLCHAR *szCatalogName, + /+ IN +/ SQLSMALLINT cbCatalogName, + /+ IN +/ SQLCHAR *szSchemaName, + /+ IN +/ SQLSMALLINT cbSchemaName, + /+ IN +/ SQLCHAR *szProcName, + /+ IN +/ SQLSMALLINT cbProcName +); + +/+ + ' ODBC v1.0+ ODBC + ' Sets the cursor position in a rowset and allows an + ' application to refresh data in the rowset or to + ' update or delete data in the result set. + ' + ' -- fOperation -- + ' Operation to perform: + ' SQL_POSITION + ' SQL_REFRESH + ' SQL_UPDATE + ' SQL_DELETE + ' + ' -- fLockType -- + ' Specifies how to lock the row after performing the + ' operation specified in the Operation argument. + ' SQL_LOCK_NO_CHANGE + ' SQL_LOCK_EXCLUSIVE + ' SQL_LOCK_UNLOCK + ' + +/ +SQLRETURN SQLSetPos +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLUSMALLINT irow, + /+ IN +/ SQLUSMALLINT fOperation, + /+ IN +/ SQLUSMALLINT fLockType +); + +/+ + ' ODBC v1.0+ ODBC + ' Returns a list of tables and the privileges associated + ' with each table. The driver returns the information as + ' a result set on the specified statement. + ' + +/ +SQLRETURN SQLTablePrivileges +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLCHAR *szCatalogName, + /+ IN +/ SQLSMALLINT cbCatalogName, + /+ IN +/ SQLCHAR *szSchemaName, + /+ IN +/ SQLSMALLINT cbSchemaName, + /+ IN +/ SQLCHAR *szTableName, + /+ IN +/ SQLSMALLINT cbTableName +); + +/+ + ' ODBC v2.0 ODBC + ' Lists driver descriptions and driver attribute keywords. + ' This function is implemented solely by the Driver Manager. + ' + ' -- fDirection -- + ' Determines whether the Driver Manager fetches the next driver + ' description in the list (SQL_FETCH_NEXT) or whether the search + ' starts from the beginning of the list (SQL_FETCH_FIRST). + ' + +/ +SQLRETURN SQLDrivers +( + /+ IN +/ SQLHENV henv, + /+ IN +/ SQLUSMALLINT fDirection, + /+ OUT +/ SQLCHAR *szDriverDesc, + /+ IN +/ SQLSMALLINT cbDriverDescMax, + /+ OUT +/ SQLSMALLINT *pcbDriverDesc, + /+ OUT +/ SQLCHAR *szDriverAttributes, + /+ IN +/ SQLSMALLINT cbDrvrAttrMax, + /+ OUT +/ SQLSMALLINT *pcbDrvrAttr +); + +/+ + ' ODBC v2.0+ ODBC + ' Binds a buffer to a parameter marker in an SQL statement. + ' SQLBindParameter supports binding to a Unicode C data type, + ' even if the underlying driver does not support Unicode data. + ' + +/ +SQLRETURN SQLBindParameter +( + /+ IN +/ SQLHSTMT hstmt, + /+ IN +/ SQLUSMALLINT ipar, + /+ IN +/ SQLSMALLINT fParamType, + /+ IN +/ SQLSMALLINT fCType, + /+ IN +/ SQLSMALLINT fSqlType, + /+ IN +/ SQLUINTEGER cbColDef, + /+ IN +/ SQLSMALLINT ibScale, + /+ IN +/ SQLPOINTER rgbValue, + /+ INOUT +/ SQLINTEGER cbValueMax, + /+ IN +/ SQLINTEGER *pcbValue +); + +/+----------------------+ + | Deprecated Functions | + +----------------------+/ + +/+ + ' In ODBC 3.x, the ODBC 2.0 function SQLColAttributes has + ' been replaced by SQLColAttribute. + ' + +/ +SQLRETURN SQLColAttributes +( + SQLHSTMT hstmt, + SQLUSMALLINT icol, + SQLUSMALLINT fDescType, + SQLPOINTER rgbDesc, + SQLSMALLINT cbDescMax, + SQLSMALLINT *pcbDesc, + SQLINTEGER *pfDesc +); + +/+ + ' In ODBC 3.x, SQLExtendedFetch has been replaced by + ' SQLFetchScroll. ODBC 3.x applications should not + ' call SQLExtendedFetch; instead they should call + ' SQLFetchScroll. + ' + +/ +SQLRETURN SQLExtendedFetch +( + SQLHSTMT hstmt, + SQLUSMALLINT fFetchType, + SQLINTEGER irow, + SQLUINTEGER *pcrow, + SQLUSMALLINT *rgfRowStatus +); + +/+ + ' SQLParamOptions has been replaced in ODBC 3.x by calls to SQLSetStmtAttr. + ' + +/ +SQLRETURN SQLParamOptions +( + SQLHSTMT hstmt, + SQLUINTEGER crow, + SQLUINTEGER *pirow +); + +// end Deprecated Functions diff --git a/etc/c/odbc/sqltypes.d b/etc/c/odbc/sqltypes.d new file mode 100644 index 00000000000..4c7e7ecad5d --- /dev/null +++ b/etc/c/odbc/sqltypes.d @@ -0,0 +1,185 @@ +/** +Declarations for interfacing with the ODBC library. + +Adapted with minimal changes from the work of David L. Davis +(refer to the $(HTTP +forum.dlang.org/post/cfk7ql$(DOLLAR)1p4n$(DOLLAR)1@digitaldaemon.com, +original announcement)). + +`etc.c.odbc.sqlext.d` corresponds to the `sqlext.h` C header file. + +See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, + ODBC API Reference on MSN Online) +*/ +module etc.c.odbc.sqltypes; + +extern (Windows): + +// * API declaration data types * +//alias void *HANDLE; + +//alias ubyte SQLCHAR; +alias SQLCHAR = char; +alias SQLSCHAR = byte; +alias SQLDATE = ubyte; +alias SQLDECIMAL = ubyte; +alias SQLDOUBLE = double; +alias SQLFLOAT = double; +alias SQLINTEGER = int; +alias SQLUINTEGER = ushort; +alias SQLNUMERIC = ubyte; +alias SQLREAL = float; +alias SQLTIME = ubyte; +alias SQLTIMESTAMP = ubyte; +alias SQLVARCHAR = ubyte; +alias SQLPOINTER = void*; +alias SQLSMALLINT = short; +alias SQLUSMALLINT = ushort; + +// * function return type * +alias SQLRETURN = SQLSMALLINT; + +// * generic data structures * +alias SQLHANDLE = void*; +alias SQLHENV = SQLHANDLE; +alias SQLHDBC = SQLHANDLE; +alias SQLHSTMT = SQLHANDLE; +alias SQLHDESC = SQLHANDLE; + +// * SQL portable types for C * +//alias ubyte UCHAR; // std.c.windows.windows has this alias +//alias char UCHAR; +alias SCHAR = byte; +//alias SCHAR SQLSCHAR; +alias DWORD = uint; +alias SDWORD = int; +alias SWORD = short; +alias UDWORD = uint ; +alias UWORD = ushort; +alias WORD = short; +//alias UDWORD SQLUINTEGER; +alias SLONG = long; +alias SSHORT = short; +alias ULONG = ulong; +alias USHORT = ushort; +alias SDOUBLE = double; +alias LDOUBLE = double; +alias SFLOAT = float; +alias PTR = void*; +alias HENV = void*; +alias HDBC = void*; +alias HSTMT = void*; +alias RETCODE = short; +alias HWND = SQLPOINTER; +alias SQLHWND = HWND; + +// * transfer types for DATE, TIME, TIMESTAMP * +struct DATE_STRUCT +{ + SQLSMALLINT year; + SQLUSMALLINT month; + SQLUSMALLINT day; +} + +alias SQL_DATE_STRUCT = DATE_STRUCT; + +struct TIME_STRUCT +{ + SQLUSMALLINT hour; + SQLUSMALLINT minute; + SQLUSMALLINT second; +} + +alias SQL_TIME_STRUCT = TIME_STRUCT; + +struct TIMESTAMP_STRUCT +{ + SQLSMALLINT year; + SQLUSMALLINT month; + SQLUSMALLINT day; + SQLUSMALLINT hour; + SQLUSMALLINT minute; + SQLUSMALLINT second; + SQLUINTEGER fraction; +} + +alias SQL_TIMESTAMP_STRUCT = TIMESTAMP_STRUCT; + +/+ + ' enumerations for DATETIME_INTERVAL_SUBCODE values for interval data types + ' these values are from SQL-92 + +/ +enum SQLINTERVAL +{ + SQL_IS_YEAR = 1, + SQL_IS_MONTH = 2, + SQL_IS_DAY = 3, + SQL_IS_HOUR = 4, + SQL_IS_MINUTE = 5, + SQL_IS_SECOND = 6, + SQL_IS_YEAR_TO_MONTH = 7, + SQL_IS_DAY_TO_HOUR = 8, + SQL_IS_DAY_TO_MINUTE = 9, + SQL_IS_DAY_TO_SECOND = 10, + SQL_IS_HOUR_TO_MINUTE = 11, + SQL_IS_HOUR_TO_SECOND = 12, + SQL_IS_MINUTE_TO_SECOND = 13 +} + +struct SQL_YEAR_MONTH_STRUCT +{ + SQLUINTEGER year; + SQLUINTEGER month; +} + +struct SQL_DAY_SECOND_STRUCT +{ + SQLUINTEGER day; + SQLUINTEGER hour; + SQLUINTEGER minute; + SQLUINTEGER second; + SQLUINTEGER fraction; +} + +struct SQL_INTERVAL_STRUCT +{ + SQLINTERVAL interval_type; + SQLSMALLINT interval_sign; + + union intval { + SQL_YEAR_MONTH_STRUCT year_month; + SQL_DAY_SECOND_STRUCT day_second; + } +} + +// * internal representation of numeric data type * +const int SQL_MAX_NUMERIC_LEN = 16; +struct SQL_NUMERIC_STRUCT +{ + SQLCHAR precision; + SQLSCHAR scale; + SQLCHAR sign; /* 1 if positive, 0 if negative */ + SQLCHAR[ SQL_MAX_NUMERIC_LEN ] val; +} + +/* size is 16 */ +struct SQLGUID +{ + DWORD Data1; + WORD Data2; + WORD Data3; + ubyte[ 8 ] Data4; +} + +alias GUID = SQLGUID; +alias BOOKMARK = uint; +alias SQLWCHAR = ushort; + +version( UNICODE ) +{ +alias SQLTCHAR = SQLWCHAR; +} +else +{ +alias SQLTCHAR = SQLCHAR; +} // end version( UNICODE ) diff --git a/etc/c/odbc/sqlucode.d b/etc/c/odbc/sqlucode.d new file mode 100644 index 00000000000..8262caebd1d --- /dev/null +++ b/etc/c/odbc/sqlucode.d @@ -0,0 +1,473 @@ +/** +Declarations for interfacing with the ODBC library. + +Adapted with minimal changes from the work of David L. Davis +(refer to the $(HTTP +forum.dlang.org/post/cfk7ql$(DOLLAR)1p4n$(DOLLAR)1@digitaldaemon.com, +original announcement)). + +`etc.c.odbc.sqlucode` corresponds to the `sqlucode.h` C include file. + +See_Also: $(LINK2 https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/odbc-api-reference, + ODBC API Reference on MSN Online) +*/ + +/+ +sqlucode.d - This is the the unicode include for ODBC v3.0+ Core functions. + ++/ + +module etc.c.odbc.sqlucode; + +import etc.c.odbc.sqlext; +import etc.c.odbc.sqltypes; + +extern (Windows): + +enum +{ + SQL_WCHAR = (-8), + SQL_WVARCHAR = (-9), + SQL_WLONGVARCHAR = (-10), + SQL_C_WCHAR = SQL_WCHAR, + SQL_C_TCHAR = SQL_C_WCHAR +} + +enum int SQL_SQLSTATE_SIZEW = 10; /* size of SQLSTATE for unicode */ + +// UNICODE versions + +SQLRETURN SQLColAttributeW +( + SQLHSTMT hstmt, + SQLUSMALLINT iCol, + SQLUSMALLINT iField, + SQLPOINTER pCharAttr, + SQLSMALLINT cbCharAttrMax, + SQLSMALLINT *pcbCharAttr, + SQLPOINTER pNumAttr +); + +SQLRETURN SQLColAttributesW +( + SQLHSTMT hstmt, + SQLUSMALLINT icol, + SQLUSMALLINT fDescType, + SQLPOINTER rgbDesc, + SQLSMALLINT cbDescMax, + SQLSMALLINT *pcbDesc, + SQLINTEGER *pfDesc +); + +SQLRETURN SQLConnectW +( + SQLHDBC hdbc, + SQLWCHAR *szDSN, + SQLSMALLINT cbDSN, + SQLWCHAR *szUID, + SQLSMALLINT cbUID, + SQLWCHAR *szAuthStr, + SQLSMALLINT cbAuthStr +); + +SQLRETURN SQLDescribeColW +( + SQLHSTMT hstmt, + SQLUSMALLINT icol, + SQLWCHAR *szColName, + SQLSMALLINT cbColNameMax, + SQLSMALLINT *pcbColName, + SQLSMALLINT *pfSqlType, + SQLUINTEGER *pcbColDef, + SQLSMALLINT *pibScale, + SQLSMALLINT *pfNullable +); + +SQLRETURN SQLErrorW +( + SQLHENV henv, + SQLHDBC hdbc, + SQLHSTMT hstmt, + SQLWCHAR *szSqlState, + SQLINTEGER *pfNativeError, + SQLWCHAR *szErrorMsg, + SQLSMALLINT cbErrorMsgMax, + SQLSMALLINT *pcbErrorMsg +); + +SQLRETURN SQLExecDirectW +( + SQLHSTMT hstmt, + SQLWCHAR *szSqlStr, + SQLINTEGER cbSqlStr +); + +SQLRETURN SQLGetConnectAttrW +( + SQLHDBC hdbc, + SQLINTEGER fAttribute, + SQLPOINTER rgbValue, + SQLINTEGER cbValueMax, + SQLINTEGER *pcbValue +); + +SQLRETURN SQLGetCursorNameW +( + SQLHSTMT hstmt, + SQLWCHAR *szCursor, + SQLSMALLINT cbCursorMax, + SQLSMALLINT *pcbCursor +); + +SQLRETURN SQLSetDescFieldW +( + SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, + SQLSMALLINT FieldIdentifier, + SQLPOINTER Value, + SQLINTEGER BufferLength +); + +SQLRETURN SQLGetDescFieldW +( + SQLHDESC hdesc, + SQLSMALLINT iRecord, + SQLSMALLINT iField, + SQLPOINTER rgbValue, + SQLINTEGER cbValueMax, + SQLINTEGER *pcbValue +); + +SQLRETURN SQLGetDescRecW +( + SQLHDESC hdesc, + SQLSMALLINT iRecord, + SQLWCHAR *szName, + SQLSMALLINT cbNameMax, + SQLSMALLINT *pcbName, + SQLSMALLINT *pfType, + SQLSMALLINT *pfSubType, + SQLINTEGER *pLength, + SQLSMALLINT *pPrecision, + SQLSMALLINT *pScale, + SQLSMALLINT *pNullable +); + +SQLRETURN SQLGetDiagFieldW +( + SQLSMALLINT fHandleType, + SQLHANDLE handle, + SQLSMALLINT iRecord, + SQLSMALLINT fDiagField, + SQLPOINTER rgbDiagInfo, + SQLSMALLINT cbDiagInfoMax, + SQLSMALLINT *pcbDiagInfo +); + +SQLRETURN SQLGetDiagRecW +( + SQLSMALLINT fHandleType, + SQLHANDLE handle, + SQLSMALLINT iRecord, + SQLWCHAR *szSqlState, + SQLINTEGER *pfNativeError, + SQLWCHAR *szErrorMsg, + SQLSMALLINT cbErrorMsgMax, + SQLSMALLINT *pcbErrorMsg +); + +SQLRETURN SQLPrepareW +( + SQLHSTMT hstmt, + SQLWCHAR *szSqlStr, + SQLINTEGER cbSqlStr +); + +SQLRETURN SQLSetConnectAttrW +( + SQLHDBC hdbc, + SQLINTEGER fAttribute, + SQLPOINTER rgbValue, + SQLINTEGER cbValue +); + +SQLRETURN SQLSetCursorNameW +( + SQLHSTMT hstmt, + SQLWCHAR *szCursor, + SQLSMALLINT cbCursor +); + +SQLRETURN SQLColumnsW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName, + SQLWCHAR *szColumnName, + SQLSMALLINT cbColumnName +); + +SQLRETURN SQLGetConnectOptionW +( + SQLHDBC hdbc, + SQLUSMALLINT fOption, + SQLPOINTER pvParam +); + +SQLRETURN SQLGetInfoW +( + SQLHDBC hdbc, + SQLUSMALLINT fInfoType, + SQLPOINTER rgbInfoValue, + SQLSMALLINT cbInfoValueMax, + SQLSMALLINT *pcbInfoValue +); + +SQLRETURN SQLGetTypeInfoW +( + SQLHSTMT StatementHandle, + SQLSMALLINT DataType +); + + +SQLRETURN SQLSetConnectOptionW +( + SQLHDBC hdbc, + SQLUSMALLINT fOption, + SQLUINTEGER vParam +); + +SQLRETURN SQLSpecialColumnsW +( + SQLHSTMT hstmt, + SQLUSMALLINT fColType, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName, + SQLUSMALLINT fScope, + SQLUSMALLINT fNullable +); + +SQLRETURN SQLStatisticsW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName, + SQLUSMALLINT fUnique, + SQLUSMALLINT fAccuracy +); + +SQLRETURN SQLTablesW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName, + SQLWCHAR *szTableType, + SQLSMALLINT cbTableType +); + +SQLRETURN SQLDataSourcesW +( + SQLHENV henv, + SQLUSMALLINT fDirection, + SQLWCHAR *szDSN, + SQLSMALLINT cbDSNMax, + SQLSMALLINT *pcbDSN, + SQLWCHAR *szDescription, + SQLSMALLINT cbDescriptionMax, + SQLSMALLINT *pcbDescription +); + +SQLRETURN SQLDriverConnectW +( + SQLHDBC hdbc, + SQLHWND hwnd, + SQLWCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLWCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut, + SQLUSMALLINT fDriverCompletion +); + +SQLRETURN SQLBrowseConnectW +( + SQLHDBC hdbc, + SQLWCHAR *szConnStrIn, + SQLSMALLINT cbConnStrIn, + SQLWCHAR *szConnStrOut, + SQLSMALLINT cbConnStrOutMax, + SQLSMALLINT *pcbConnStrOut +); + +SQLRETURN SQLColumnPrivilegesW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName, + SQLWCHAR *szColumnName, + SQLSMALLINT cbColumnName +); + +SQLRETURN SQLGetStmtAttrW +( + SQLHSTMT hstmt, + SQLINTEGER fAttribute, + SQLPOINTER rgbValue, + SQLINTEGER cbValueMax, + SQLINTEGER *pcbValue +); + +SQLRETURN SQLSetStmtAttrW +( + SQLHSTMT hstmt, + SQLINTEGER fAttribute, + SQLPOINTER rgbValue, + SQLINTEGER cbValueMax +); + +SQLRETURN SQLForeignKeysW +( + SQLHSTMT hstmt, + SQLWCHAR *szPkCatalogName, + SQLSMALLINT cbPkCatalogName, + SQLWCHAR *szPkSchemaName, + SQLSMALLINT cbPkSchemaName, + SQLWCHAR *szPkTableName, + SQLSMALLINT cbPkTableName, + SQLWCHAR *szFkCatalogName, + SQLSMALLINT cbFkCatalogName, + SQLWCHAR *szFkSchemaName, + SQLSMALLINT cbFkSchemaName, + SQLWCHAR *szFkTableName, + SQLSMALLINT cbFkTableName +); + +SQLRETURN SQLNativeSqlW +( + SQLHDBC hdbc, + SQLWCHAR *szSqlStrIn, + SQLINTEGER cbSqlStrIn, + SQLWCHAR *szSqlStr, + SQLINTEGER cbSqlStrMax, + SQLINTEGER *pcbSqlStr +); + +SQLRETURN SQLPrimaryKeysW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName +); + +SQLRETURN SQLProcedureColumnsW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szProcName, + SQLSMALLINT cbProcName, + SQLWCHAR *szColumnName, + SQLSMALLINT cbColumnName +); + +SQLRETURN SQLProceduresW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szProcName, + SQLSMALLINT cbProcName +); + +SQLRETURN SQLTablePrivilegesW +( + SQLHSTMT hstmt, + SQLWCHAR *szCatalogName, + SQLSMALLINT cbCatalogName, + SQLWCHAR *szSchemaName, + SQLSMALLINT cbSchemaName, + SQLWCHAR *szTableName, + SQLSMALLINT cbTableName +); + +SQLRETURN SQLDriversW +( + SQLHENV henv, + SQLUSMALLINT fDirection, + SQLWCHAR *szDriverDesc, + SQLSMALLINT cbDriverDescMax, + SQLSMALLINT *pcbDriverDesc, + SQLWCHAR *szDriverAttributes, + SQLSMALLINT cbDrvrAttrMax, + SQLSMALLINT *pcbDrvrAttr +); + +//--------------------------------------------- +// Mapping Unicode Functions +//--------------------------------------------- +/+ +alias SQLColAttributeW SQLColAttribute; +alias SQLColAttributesW SQLColAttributes; +alias SQLConnectW SQLConnect; +alias SQLDescribeColW SQLDescribeCol; +alias SQLErrorW SQLError; +alias SQLExecDirectW SQLExecDirect; +alias SQLGetConnectAttrW SQLGetConnectAttr; +alias SQLGetCursorNameW SQLGetCursorName; +alias SQLGetDescFieldW SQLGetDescField; +alias SQLGetDescRecW SQLGetDescRec; +alias SQLGetDiagFieldW SQLGetDiagField; +alias SQLGetDiagRecW SQLGetDiagRec; +alias SQLPrepareW SQLPrepare; +alias SQLSetConnectAttrW SQLSetConnectAttr; +alias SQLSetCursorNameW SQLSetCursorName; +alias SQLSetDescFieldW SQLSetDescField; +alias SQLSetStmtAttrW SQLSetStmtAttr; +alias SQLColumnsW SQLColumns; +alias SQLGetConnectOptionW SQLGetConnectOption; +alias SQLGetInfoW SQLGetInfo; +alias SQLGetTypeInfoW SQLGetTypeInfo; +alias SQLSetConnectOptionW SQLSetConnectOption; +alias SQLSpecialColumnsW SQLSpecialColumns; +alias SQLStatisticsW SQLStatistics; +alias SQLTablesW SQLTables; +alias SQLDataSourcesW SQLDataSources; +alias SQLDriverConnectW SQLDriverConnect; +alias SQLBrowseConnectW SQLBrowseConnect; +alias SQLColumnPrivilegesW SQLColumnPrivileges; +alias SQLForeignKeysW SQLForeignKeys; +alias SQLNativeSqlW SQLNativeSql; +alias SQLPrimaryKeysW SQLPrimaryKeys; +alias SQLProcedureColumnsW SQLProcedureColumns; +alias SQLProceduresW SQLProcedures; +alias SQLTablePrivilegesW SQLTablePrivileges; +alias SQLDriversW SQLDrivers; ++/ diff --git a/etc/c/sqlite3.d b/etc/c/sqlite3.d index a26ecde2367..43a72f52887 100644 --- a/etc/c/sqlite3.d +++ b/etc/c/sqlite3.d @@ -32,17 +32,18 @@ module etc.c.sqlite3; ** part of the build process. */ -extern (C) { -__gshared: +import core.stdc.stdarg : va_list; + +extern (C) __gshared nothrow: /** ** CAPI3REF: Compile-Time Library Version Numbers */ -enum SQLITE_VERSION = "3.8.6"; +enum SQLITE_VERSION = "3.10.2"; /// Ditto -enum SQLITE_VERSION_NUMBER = 3008006; +enum SQLITE_VERSION_NUMBER = 3_010_002; /// Ditto -enum SQLITE_SOURCE_ID = "2014-08-15 11:46:33 9491ba7d738528f168657adb43a198238abde19e"; +enum SQLITE_SOURCE_ID = "2016-01-20 15:27:19 17efb4209f97fb4971656086b138599a91a75ff9"; /** ** CAPI3REF: Run-Time Library Version Numbers @@ -73,9 +74,9 @@ int sqlite3_threadsafe(); struct sqlite3; /// -alias long sqlite3_int64; +alias sqlite3_int64 = long; /// -alias ulong sqlite3_uint64; +alias sqlite3_uint64 = ulong; /** ** CAPI3REF: Closing A Database Connection @@ -89,7 +90,7 @@ int sqlite3_close_v2(sqlite3*); ** This is legacy and deprecated. It is included for historical ** compatibility and is not documented. */ -alias int function (void*,int,char**, char**) sqlite3_callback; +alias sqlite3_callback = int function (void*,int,char**, char**); /** ** CAPI3REF: One-Step Query Execution Interface @@ -107,18 +108,18 @@ int sqlite3_exec( */ enum { - SQLITE_OK = 0, /** Successful result */ + SQLITE_OK = 0, /** Successful result */ /* beginning-of-error-codes */ /// Ditto - SQLITE_ERROR = 1, /** SQL error or missing database */ - SQLITE_INTERNAL = 2, /** Internal logic error in SQLite */ - SQLITE_PERM = 3, /** Access permission denied */ - SQLITE_ABORT = 4, /** Callback routine requested an abort */ - SQLITE_BUSY = 5, /** The database file is locked */ - SQLITE_LOCKED = 6, /** A table in the database is locked */ - SQLITE_NOMEM = 7, /** A malloc() failed */ - SQLITE_READONLY = 8, /** Attempt to write a readonly database */ - SQLITE_INTERRUPT = 9, /** Operation terminated by sqlite3_interrupt()*/ + SQLITE_ERROR = 1, /** SQL error or missing database */ + SQLITE_INTERNAL = 2, /** Internal logic error in SQLite */ + SQLITE_PERM = 3, /** Access permission denied */ + SQLITE_ABORT = 4, /** Callback routine requested an abort */ + SQLITE_BUSY = 5, /** The database file is locked */ + SQLITE_LOCKED = 6, /** A table in the database is locked */ + SQLITE_NOMEM = 7, /** A malloc() failed */ + SQLITE_READONLY = 8, /** Attempt to write a readonly database */ + SQLITE_INTERRUPT = 9, /** Operation terminated by sqlite3_interrupt()*/ SQLITE_IOERR = 10, /** Some kind of disk I/O error occurred */ SQLITE_CORRUPT = 11, /** The database disk image is malformed */ SQLITE_NOTFOUND = 12, /** Unknown opcode in sqlite3_file_control() */ @@ -148,54 +149,59 @@ enum */ enum { - SQLITE_IOERR_READ = (SQLITE_IOERR | (1<<8)), - SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2<<8)), - SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3<<8)), - SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4<<8)), - SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5<<8)), - SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6<<8)), - SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7<<8)), - SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8<<8)), - SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9<<8)), - SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10<<8)), - SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11<<8)), - SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12<<8)), - SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13<<8)), - SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14<<8)), - SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15<<8)), - SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16<<8)), - SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17<<8)), - SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18<<8)), - SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19<<8)), - SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20<<8)), - SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1<<8)), - SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1<<8)), - SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1<<8)), - SQLITE_IOERR_GETTEMPPATH = (SQLITE_IOERR | (25<<8)), - SQLITE_IOERR_CONVPATH = (SQLITE_IOERR | (26<<8)), - SQLITE_BUSY_SNAPSHOT = (SQLITE_BUSY | (2<<8)), - SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2<<8)), - SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3<<8)), - SQLITE_CANTOPEN_CONVPATH = (SQLITE_CANTOPEN | (4<<8)), - SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1<<8)), - SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1<<8)), - SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2<<8)), - SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3<<8)), - SQLITE_READONLY_DBMOVED = (SQLITE_READONLY | (4<<8)), - SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2<<8)), - SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1<<8)), - SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2<<8)), - SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3<<8)), - SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4<<8)), - SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5<<8)), - SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6<<8)), - SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7<<8)), - SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8<<8)), - SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9<<8)), - SQLITE_CONSTRAINT_ROWID = (SQLITE_CONSTRAINT |(10<<8)), - SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1<<8)), - SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2<<8)), - SQLITE_WARNING_AUTOINDEX = (SQLITE_WARNING | (1<<8)) + SQLITE_IOERR_READ = (SQLITE_IOERR | (1 << 8)), + SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2 << 8)), + SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3 << 8)), + SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4 << 8)), + SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5 << 8)), + SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6 << 8)), + SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7 << 8)), + SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8 << 8)), + SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9 << 8)), + SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10 << 8)), + SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11 << 8)), + SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12 << 8)), + SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13 << 8)), + SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14 << 8)), + SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15 << 8)), + SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16 << 8)), + SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17 << 8)), + SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18 << 8)), + SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19 << 8)), + SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20 << 8)), + SQLITE_IOERR_SHMMAP = (SQLITE_IOERR | (21 << 8)), + SQLITE_IOERR_SEEK = (SQLITE_IOERR | (22 << 8)), + SQLITE_IOERR_DELETE_NOENT = (SQLITE_IOERR | (23 << 8)), + SQLITE_IOERR_MMAP = (SQLITE_IOERR | (24 << 8)), + SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1 << 8)), + SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1 << 8)), + SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1 << 8)), + SQLITE_IOERR_GETTEMPPATH = (SQLITE_IOERR | (25 << 8)), + SQLITE_IOERR_CONVPATH = (SQLITE_IOERR | (26 << 8)), + SQLITE_BUSY_SNAPSHOT = (SQLITE_BUSY | (2 << 8)), + SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2 << 8)), + SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3 << 8)), + SQLITE_CANTOPEN_CONVPATH = (SQLITE_CANTOPEN | (4 << 8)), + SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1 << 8)), + SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1 << 8)), + SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2 << 8)), + SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3 << 8)), + SQLITE_READONLY_DBMOVED = (SQLITE_READONLY | (4 << 8)), + SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2 << 8)), + SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1 << 8)), + SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2 << 8)), + SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3 << 8)), + SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4 << 8)), + SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5 << 8)), + SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6 << 8)), + SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7 << 8)), + SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8 << 8)), + SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9 << 8)), + SQLITE_CONSTRAINT_ROWID = (SQLITE_CONSTRAINT |(10 << 8)), + SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1 << 8)), + SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2 << 8)), + SQLITE_WARNING_AUTOINDEX = (SQLITE_WARNING | (1 << 8)), + SQLITE_AUTH_USER = (SQLITE_AUTH | (1 << 8)) } /** @@ -209,6 +215,8 @@ enum SQLITE_OPEN_DELETEONCLOSE = 0x00000008, /** VFS only */ SQLITE_OPEN_EXCLUSIVE = 0x00000010, /** VFS only */ SQLITE_OPEN_AUTOPROXY = 0x00000020, /** VFS only */ + SQLITE_OPEN_URI = 0x00000040, /** Ok for sqlite3_open_v2() */ + SQLITE_OPEN_MEMORY = 0x00000080, /** Ok for sqlite3_open_v2() */ SQLITE_OPEN_MAIN_DB = 0x00000100, /** VFS only */ SQLITE_OPEN_TEMP_DB = 0x00000200, /** VFS only */ SQLITE_OPEN_TRANSIENT_DB = 0x00000400, /** VFS only */ @@ -300,6 +308,8 @@ struct sqlite3_io_methods int function (sqlite3_file*, int deleteFlag) xShmUnmap; /* Methods above are valid for version 2 */ /* Additional methods may be added in future releases */ + int function (sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp) xFetch; + int function (sqlite3_file*, sqlite3_int64 iOfst, void *p) xUnfetch; } /** @@ -315,7 +325,7 @@ enum SQLITE_FCNTL_CHUNK_SIZE = 6, SQLITE_FCNTL_FILE_POINTER = 7, SQLITE_FCNTL_SYNC_OMITTED = 8, - SQLITE_FCNTL_WIN_AV_RETRY = 32, + SQLITE_FCNTL_WIN32_AV_RETRY = 9, SQLITE_FCNTL_PERSIST_WAL = 10, SQLITE_FCNTL_OVERWRITE = 11, SQLITE_FCNTL_VFSNAME = 12, @@ -328,7 +338,11 @@ enum SQLITE_FCNTL_HAS_MOVED = 20, SQLITE_FCNTL_SYNC = 21, SQLITE_FCNTL_COMMIT_PHASETWO = 22, - SQLITE_FCNTL_WIN_SET_HANDLE = 32 + SQLITE_FCNTL_WIN32_SET_HANDLE = 23, + SQLITE_FCNTL_WAL_BLOCK = 24, + SQLITE_FCNTL_ZIPVFS = 25, + SQLITE_FCNTL_RBU = 26, + SQLITE_FCNTL_VFS_POINTER = 27, } /** @@ -340,9 +354,9 @@ struct sqlite3_mutex; ** CAPI3REF: OS Interface Object */ -alias void * function() xDlSymReturn; +alias xDlSymReturn = void * function(); /// Ditto -alias void function() sqlite3_syscall_ptr; +alias sqlite3_syscall_ptr = void function(); struct sqlite3_vfs { @@ -472,10 +486,12 @@ enum SQLITE_CONFIG_URI = 17, SQLITE_CONFIG_PCACHE2 = 18, SQLITE_CONFIG_GETPCACHE2 = 19, - SQLITE_CONFIG_COVERING_INDEX_SCAN2 = 20, - SQLITE_CONFIG_SQLLOG2 = 21, - SQLITE_CONFIG_MMAP_SIZE2 = 22, - SQLITE_CONFIG_WIN32_HEAPSIZE = 23 + SQLITE_CONFIG_COVERING_INDEX_SCAN = 20, + SQLITE_CONFIG_SQLLOG = 21, + SQLITE_CONFIG_MMAP_SIZE = 22, + SQLITE_CONFIG_WIN32_HEAPSIZE = 23, + SQLITE_CONFIG_PCACHE_HDRSZ = 24, + SQLITE_CONFIG_PMASZ = 25, } /** @@ -545,9 +561,6 @@ int sqlite3_get_table( /// void sqlite3_free_table(char **result); -/// -alias char *va_list; - /** ** CAPI3REF: Formatted String Printing Functions */ @@ -561,9 +574,15 @@ char *sqlite3_vsnprintf(int,char*,const char*, va_list); */ void *sqlite3_malloc(int); /// Ditto +void *sqlite3_malloc64(sqlite3_uint64); +/// Ditto void *sqlite3_realloc(void*, int); /// Ditto +void *sqlite3_realloc64(void*, sqlite3_uint64); +/// Ditto void sqlite3_free(void*); +/// Ditto +sqlite3_uint64 sqlite3_msize(void*); /** ** CAPI3REF: Memory Allocator Statistics @@ -641,8 +660,7 @@ enum */ void *sqlite3_trace(sqlite3*, void function (void*,const char*) xTrace, void*); /// Ditto -void *sqlite3_profile(sqlite3*, - void function (void*,const char*,sqlite3_uint64) xProfile, void*); +void *sqlite3_profile(sqlite3*, void function (void*,const char*,sqlite3_uint64) xProfile, void*); /** ** CAPI3REF: Query Progress Callbacks @@ -685,9 +703,11 @@ int sqlite3_errcode(sqlite3 *db); /// Ditto int sqlite3_extended_errcode(sqlite3 *db); /// Ditto -immutable(char)* sqlite3_errmsg(sqlite3*); +const(char)* sqlite3_errmsg(sqlite3*); /// Ditto -immutable(void)* sqlite3_errmsg16(sqlite3*); +const(void)* sqlite3_errmsg16(sqlite3*); +/// Ditto +const(char)* sqlite3_errstr(int); /** ** CAPI3REF: SQL Statement Object @@ -714,7 +734,8 @@ enum SQLITE_LIMIT_ATTACHED = 7, SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8, SQLITE_LIMIT_VARIABLE_NUMBER = 9, - SQLITE_LIMIT_TRIGGER_DEPTH = 10 + SQLITE_LIMIT_TRIGGER_DEPTH = 10, + SQLITE_LIMIT_WORKER_THREADS = 11, } /** @@ -757,11 +778,17 @@ int sqlite3_prepare16_v2( */ const(char)* sqlite3_sql(sqlite3_stmt *pStmt); -/** +/* ** CAPI3REF: Determine If An SQL Statement Writes The Database */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +/** +** CAPI3REF: Determine If A Prepared Statement Has Been Reset +*/ +int sqlite3_stmt_busy(sqlite3_stmt*); + + /** ** CAPI3REF: Dynamically Typed Value Object */ @@ -777,6 +804,8 @@ struct sqlite3_context; */ int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void function (void*)); /// Ditto +int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,void function (void*)); +/// Ditto int sqlite3_bind_double(sqlite3_stmt*, int, double); /// Ditto int sqlite3_bind_int(sqlite3_stmt*, int, int); @@ -789,9 +818,13 @@ int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void function (voi /// Ditto int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void function (void*)); /// Ditto +int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,void function (void*), ubyte encoding); +/// Ditto int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); /// Ditto int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); +/// Ditto +int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64 n); /** ** CAPI3REF: Number Of SQL Parameters @@ -972,8 +1005,7 @@ deprecated int sqlite3_expired(sqlite3_stmt*); deprecated int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); deprecated int sqlite3_global_recover(); deprecated void sqlite3_thread_cleanup(); -deprecated int sqlite3_memory_alarm(void function(void*,sqlite3_int64,int), - void*,sqlite3_int64); +deprecated int sqlite3_memory_alarm(void function(void*,sqlite3_int64,int),void*,sqlite3_int64); /** ** CAPI3REF: Obtaining SQL Function Parameter Values @@ -1002,6 +1034,17 @@ int sqlite3_value_type(sqlite3_value*); /// Ditto int sqlite3_value_numeric_type(sqlite3_value*); +/* +** CAPI3REF: Finding The Subtype Of SQL Values +*/ +uint sqlite3_value_subtype(sqlite3_value*); + +/* +** CAPI3REF: Copy And Free SQL Values +*/ +sqlite3_value* sqlite3_value_dup(const sqlite3_value*); +void sqlite3_value_free(sqlite3_value*); + /** ** CAPI3REF: Obtain Aggregate Function Context */ @@ -1028,7 +1071,7 @@ void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void function (void*)); /** ** CAPI3REF: Constants Defining Special Destructor Behavior */ -alias void function (void*) sqlite3_destructor_type; +alias sqlite3_destructor_type = void function (void*); /// Ditto enum { @@ -1041,6 +1084,8 @@ enum */ void sqlite3_result_blob(sqlite3_context*, const void*, int, void function(void*)); /// Ditto +void sqlite3_result_blob64(sqlite3_context*,const void*,sqlite3_uint64,void function(void*)); +/// Ditto void sqlite3_result_double(sqlite3_context*, double); /// Ditto void sqlite3_result_error(sqlite3_context*, const char*, int); @@ -1061,6 +1106,8 @@ void sqlite3_result_null(sqlite3_context*); /// Ditto void sqlite3_result_text(sqlite3_context*, const char*, int, void function(void*)); /// Ditto +void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,void function(void*), ubyte encoding); +/// Ditto void sqlite3_result_text16(sqlite3_context*, const void*, int, void function(void*)); /// Ditto void sqlite3_result_text16le(sqlite3_context*, const void*, int, void function(void*)); @@ -1070,6 +1117,13 @@ void sqlite3_result_text16be(sqlite3_context*, const void*, int, void function(v void sqlite3_result_value(sqlite3_context*, sqlite3_value*); /// Ditto void sqlite3_result_zeroblob(sqlite3_context*, int n); +/// Ditto +int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); + +/* +** CAPI3REF: Setting The Subtype Of An SQL Function +*/ +void sqlite3_result_subtype(sqlite3_context*,uint); /** ** CAPI3REF: Define New Collating Sequences @@ -1128,7 +1182,7 @@ int sqlite3_key_v2( /** ** Change the key on an open database. If the current database is not -** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** encrypted, this routine will encrypt it. If pNew == 0 or nNew == 0, the ** database is decrypted. ** ** The code to implement this API is not available in the public release @@ -1170,6 +1224,11 @@ int sqlite3_sleep(int); */ extern char *sqlite3_temp_directory; +/** +** CAPI3REF: Name Of The Folder Holding Database Files +*/ +extern char *sqlite3_data_directory; + /** ** CAPI3REF: Test For Auto-Commit Mode */ @@ -1294,7 +1353,7 @@ void sqlite3_reset_auto_extension(); ** CAPI3REF: Virtual Table Object */ -alias void function (sqlite3_context*,int,sqlite3_value**) mapFunction; +alias mapFunction = void function (sqlite3_context*,int,sqlite3_value**); /// Ditto struct sqlite3_module @@ -1326,6 +1385,9 @@ struct sqlite3_module mapFunction*, void **ppArg) xFindFunction; int function (sqlite3_vtab *pVtab, const char *zNew) xRename; + int function (sqlite3_vtab *pVTab, int) xSavepoint; + int function (sqlite3_vtab *pVTab, int) xRelease; + int function (sqlite3_vtab *pVTab, int) xRollbackTo; } /** @@ -1335,20 +1397,20 @@ struct sqlite3_index_info { struct sqlite3_index_constraint { - int iColumn; /** Column on left-hand side of constraint */ - char op; /** Constraint operator */ - char usable; /** True if this constraint is usable */ - int iTermOffset; /** Used internally - xBestIndex should ignore */ + int iColumn; /** Column on left-hand side of constraint */ + char op; /** Constraint operator */ + char usable; /** True if this constraint is usable */ + int iTermOffset; /** Used internally - xBestIndex should ignore */ } struct sqlite3_index_orderby { - int iColumn; /** Column number */ - char desc; /** True for DESC. False for ASC. */ + int iColumn; /** Column number */ + char desc; /** True for DESC. False for ASC. */ } struct sqlite3_index_constraint_usage { - int argvIndex; /** if >0, constraint is part of argv to xFilter */ - char omit; /** Do not code a test for this constraint */ + int argvIndex; /** if >0, constraint is part of argv to xFilter */ + char omit; /** Do not code a test for this constraint */ } /* Inputs */ int nConstraint; /** Number of entries in aConstraint */ @@ -1362,6 +1424,9 @@ struct sqlite3_index_info int needToFreeIdxStr; /** Free idxStr using sqlite3_free() if true */ int orderByConsumed; /** True if output is already ordered */ double estimatedCost; /** Estimated cost of using this index */ + sqlite3_int64 estimatedRows; + int idxFlags; + sqlite3_uint64 colUsed; } /** @@ -1369,12 +1434,16 @@ struct sqlite3_index_info */ enum { - SQLITE_INDEX_CONSTRAINT_EQ = 2, - SQLITE_INDEX_CONSTRAINT_GT = 4, - SQLITE_INDEX_CONSTRAINT_LE = 8, - SQLITE_INDEX_CONSTRAINT_LT = 16, - SQLITE_INDEX_CONSTRAINT_GE = 32, - SQLITE_INDEX_CONSTRAINT_MATCH = 64 + SQLITE_INDEX_SCAN_UNIQUE = 1, + SQLITE_INDEX_CONSTRAINT_EQ = 2, + SQLITE_INDEX_CONSTRAINT_GT = 4, + SQLITE_INDEX_CONSTRAINT_LE = 8, + SQLITE_INDEX_CONSTRAINT_LT = 16, + SQLITE_INDEX_CONSTRAINT_GE = 32, + SQLITE_INDEX_CONSTRAINT_MATCH = 64, + SQLITE_INDEX_CONSTRAINT_LIKE = 65, + SQLITE_INDEX_CONSTRAINT_GLOB = 66, + SQLITE_INDEX_CONSTRAINT_REGEXP = 67, } /** @@ -1533,21 +1602,20 @@ enum { SQLITE_MUTEX_FAST = 0, SQLITE_MUTEX_RECURSIVE = 1, - SQLITE_MUTEX_STATIC_MASTER = 2 -} -/// Ditto -enum -{ + SQLITE_MUTEX_STATIC_MASTER = 2, SQLITE_MUTEX_STATIC_MEM = 3, /** sqlite3_malloc() */ SQLITE_MUTEX_STATIC_MEM2 = 4, /** NOT USED */ SQLITE_MUTEX_STATIC_OPEN = 4, /** sqlite3BtreeOpen() */ SQLITE_MUTEX_STATIC_PRNG = 5, /** sqlite3_random() */ SQLITE_MUTEX_STATIC_LRU = 6, /** lru page list */ SQLITE_MUTEX_STATIC_LRU2 = 7, /** NOT USED */ - SQLITE_MUTEX_STATIC_PMEM = 7, - SQLITE_MUTEX_STATIC_APP1 = 8, - SQLITE_MUTEX_STATIC_APP2 = 9, - SQLITE_MUTEX_STATIC_APP3 = 10 + SQLITE_MUTEX_STATIC_PMEM = 7, /** sqlite3PageMalloc() */ + SQLITE_MUTEX_STATIC_APP1 = 8, /** For use by application */ + SQLITE_MUTEX_STATIC_APP2 = 9, /** For use by application */ + SQLITE_MUTEX_STATIC_APP3 = 10, /** For use by application */ + SQLITE_MUTEX_STATIC_VFS1 = 11, /** For use by built-in VFS */ + SQLITE_MUTEX_STATIC_VFS2 = 12, /** For use by extension VFS */ + SQLITE_MUTEX_STATIC_VFS3 = 13, /** For use by application VFS */ } /** @@ -1570,12 +1638,12 @@ int sqlite3_test_control(int op, ...); */ enum { - SQLITE_TESTCTRL_FIRST = 5, - SQLITE_TESTCTRL_PRNG_SAVE = 5, - SQLITE_TESTCTRL_PRNG_RESTORE = 6, - SQLITE_TESTCTRL_PRNG_RESET = 7, - SQLITE_TESTCTRL_BITVEC_TEST = 8, - SQLITE_TESTCTRL_FAULT_INSTALL = 9, + SQLITE_TESTCTRL_FIRST = 5, + SQLITE_TESTCTRL_PRNG_SAVE = 5, + SQLITE_TESTCTRL_PRNG_RESTORE = 6, + SQLITE_TESTCTRL_PRNG_RESET = 7, + SQLITE_TESTCTRL_BITVEC_TEST = 8, + SQLITE_TESTCTRL_FAULT_INSTALL = 9, SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS = 10, SQLITE_TESTCTRL_PENDING_BYTE = 11, SQLITE_TESTCTRL_ASSERT = 12, @@ -1583,21 +1651,24 @@ enum SQLITE_TESTCTRL_RESERVE = 14, SQLITE_TESTCTRL_OPTIMIZATIONS = 15, SQLITE_TESTCTRL_ISKEYWORD = 16, - SQLITE_TESTCTRL_PGHDRSZ = 17, - SQLITE_TESTCTRL_SCRATCHMALLOC = 18, + SQLITE_TESTCTRL_SCRATCHMALLOC = 17, + SQLITE_TESTCTRL_LOCALTIME_FAULT = 18, SQLITE_TESTCTRL_EXPLAIN_STMT = 19, SQLITE_TESTCTRL_NEVER_CORRUPT = 20, SQLITE_TESTCTRL_VDBE_COVERAGE = 21, SQLITE_TESTCTRL_BYTEORDER = 22, SQLITE_TESTCTRL_ISINIT = 23, - SQLITE_TESTCTRL_LAST = 23 + SQLITE_TESTCTRL_SORTER_MMAP = 24, + SQLITE_TESTCTRL_IMPOSTER = 25, + SQLITE_TESTCTRL_LAST = 25, } /** ** CAPI3REF: SQLite Runtime Status */ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); - +/// Ditto +int sqlite3_status64(int op, long *pCurrent, long *pHighwater, int resetFlag); /** ** CAPI3REF: Status Parameters @@ -1704,7 +1775,7 @@ struct sqlite3_pcache_methods void function (sqlite3_pcache*, void*, uint oldKey, uint newKey) xRekey; void function (sqlite3_pcache*, uint iLimit) xTruncate; void function (sqlite3_pcache*) xDestroy; -}; +} /** ** CAPI3REF: Online Backup Object @@ -1750,6 +1821,11 @@ int sqlite3_strnicmp(const char * , const char * , int); */ int sqlite3_strglob(const(char)* zGlob, const(char)* zStr); +/* +** CAPI3REF: String LIKE Matching +*/ +int sqlite3_strlike(const(char)* zGlob, const(char)* zStr, uint cEsc); + /** ** CAPI3REF: Error Logging Interface */ @@ -1790,9 +1866,10 @@ int sqlite3_wal_checkpoint_v2( */ enum { - SQLITE_CHECKPOINT_PASSIVE = 0, - SQLITE_CHECKPOINT_FULL = 1, - SQLITE_CHECKPOINT_RESTART = 2 + SQLITE_CHECKPOINT_PASSIVE = 0, + SQLITE_CHECKPOINT_FULL = 1, + SQLITE_CHECKPOINT_RESTART = 2, + SQLITE_CHECKPOINT_TRUNCATE = 3, } /* @@ -1800,7 +1877,10 @@ enum */ int sqlite3_vtab_config(sqlite3*, int op, ...); -} /* End of the 'extern (C) block */ +/** +** CAPI3REF: Virtual Table Configuration Options +*/ +enum SQLITE_VTAB_CONSTRAINT_SUPPORT = 1; /* ** 2010 August 30 @@ -1819,7 +1899,6 @@ int sqlite3_vtab_config(sqlite3*, int op, ...); //#define _SQLITE3RTREE_H_ -extern (C) { /* ** CAPI3REF: Determine The Virtual Table Conflict Policy */ @@ -1835,11 +1914,56 @@ enum SQLITE_REPLACE = 5 } +/* +** CAPI3REF: Prepared Statement Scan Status Opcodes +*/ +enum +{ + SQLITE_SCANSTAT_NLOOP = 0, + SQLITE_SCANSTAT_NVISIT = 1, + SQLITE_SCANSTAT_EST = 2, + SQLITE_SCANSTAT_NAME = 3, + SQLITE_SCANSTAT_EXPLAIN = 4, + SQLITE_SCANSTAT_SELECTID = 5, +} + +/* +** CAPI3REF: Prepared Statement Scan Status +*/ +int sqlite3_stmt_scanstatus(sqlite3_stmt *pStmt, int idx, int iScanStatusOp, void *pOut); + +/* +** CAPI3REF: Zero Scan-Status Counters +*/ +void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *); + +/* +** CAPI3REF: Flush caches to disk mid-transaction +*/ +int sqlite3_db_cacheflush(sqlite3 *); + +struct sqlite3_snapshot; + +/* +** CAPI3REF: Record A Database Snapshot +*/ +int sqlite3_snapshot_get(sqlite3 *db, char *zSchema, sqlite3_snapshot **ppSnapshot); + +/* +** CAPI3REF: Start a read transaction on an historical snapshot +*/ +int sqlite3_snapshot_open(sqlite3 *db, char *zSchema, sqlite3_snapshot *pSnapshot); + +/* +** CAPI3REF: Destroy a snapshot +*/ +void sqlite3_snapshot_free(sqlite3_snapshot *); + /** ** Register a geometry callback named zGeom that can be used as part of an ** R-Tree geometry query as follows: ** -** SELECT ... FROM WHERE MATCH $zGeom(... params ...) +** SELECT ... FROM $(LT)rtree$(GT) WHERE $(LT)rtree col$(GT) MATCH $zGeom(... params ...) */ int sqlite3_rtree_geometry_callback( sqlite3 *db, @@ -1886,6 +2010,7 @@ struct sqlite3_rtree_query_info int eParentWithin; /* Visibility of parent node */ int eWithin; /* OUT: Visiblity */ double rScore; /* OUT: Write the score here */ + sqlite3_value **apSqlParam; /* Original SQL values of parameters */ } enum @@ -1894,4 +2019,108 @@ enum PARTLY_WITHIN = 1, FULLY_WITHIN = 2 } -} /* end of the 'extern (C) block */ + +/****************************************************************************** +** Interfaces to extend FTS5. +*/ +struct Fts5Context; +/// Ditto +alias fts5_extension_function = void function( + const Fts5ExtensionApi *pApi, + Fts5Context *pFts, + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +); +/// Ditto +struct Fts5PhraseIter +{ + const(ubyte) *a; + const(ubyte) *b; +} +/// Ditto +struct Fts5ExtensionApi +{ + int iVersion; + void* function(Fts5Context*) xUserData; + int function(Fts5Context*) xColumnCount; + int function(Fts5Context*, sqlite3_int64 *pnRow) xRowCount; + int function(Fts5Context*, int iCol, sqlite3_int64 *pnToken) xColumnTotalSize; + int function(Fts5Context*, + const char *pText, int nText, + void *pCtx, + int function(void*, int, const char*, int, int, int) xToken + ) xTokenize; + int function(Fts5Context*) xPhraseCount; + int function(Fts5Context*, int iPhrase) xPhraseSize; + int function(Fts5Context*, int *pnInst) xInstCount; + int function(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff) xInst; + sqlite3_int64 function(Fts5Context*) xRowid; + int function(Fts5Context*, int iCol, const char **pz, int *pn) xColumnText; + int function(Fts5Context*, int iCol, int *pnToken) xColumnSize; + int function(Fts5Context*, int iPhrase, void *pUserData, + int function(const Fts5ExtensionApi*,Fts5Context*,void*) + ) xQueryPhrase; + int function(Fts5Context*, void *pAux, void function(void*) xDelete) xSetAuxdata; + void* function(Fts5Context*, int bClear) xGetAuxdata; + void function(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*) xPhraseFirst; + void function(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff) xPhraseNext; +} +/// Ditto +struct Fts5Tokenizer; +struct fts5_tokenizer +{ + int function(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut) xCreate; + void function(Fts5Tokenizer*) xDelete; + int function(Fts5Tokenizer*, + void *pCtx, + int flags, + const char *pText, int nText, + int function( + void *pCtx, + int tflags, + const char *pToken, + int nToken, + int iStart, + int iEnd + ) xToken + ) xTokenize; +} +/// Ditto +enum FTS5_TOKENIZE_QUERY = 0x0001; +/// Ditto +enum FTS5_TOKENIZE_PREFIX = 0x0002; +/// Ditto +enum FTS5_TOKENIZE_DOCUMENT = 0x0004; +/// Ditto +enum FTS5_TOKENIZE_AUX = 0x0008; +/// Ditto +enum FTS5_TOKEN_COLOCATED = 0x0001; +/// Ditto +struct fts5_api +{ + int iVersion; + + int function( + fts5_api *pApi, + const char *zName, + void *pContext, + fts5_tokenizer *pTokenizer, + void function(void*) xDestroy + ) xCreateTokenizer; + + int function( + fts5_api *pApi, + const char *zName, + void **ppContext, + fts5_tokenizer *pTokenizer + ) xFindTokenizer; + + int function( + fts5_api *pApi, + const char *zName, + void *pContext, + fts5_extension_function xFunction, + void function(void*) xDestroy + ) xCreateFunction; +} diff --git a/etc/c/zlib.d b/etc/c/zlib.d index 68cc0bef950..b3e9783389c 100644 --- a/etc/c/zlib.d +++ b/etc/c/zlib.d @@ -1,15 +1,16 @@ /* zlib.d: modified from zlib.h by Walter Bright */ /* updated from 1.2.1 to 1.2.3 by Thomas Kuehne */ /* updated from 1.2.3 to 1.2.8 by Dmitry Atamanov */ +/* updated from 1.2.8 to 1.2.11 by Iain Buclaw */ module etc.c.zlib; import core.stdc.config; /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.3, July 18th, 2005 + version 1.2.11, January 15th, 2017 - Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -32,75 +33,77 @@ import core.stdc.config; The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt - (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ +nothrow: extern (C): -const char[] ZLIB_VERSION = "1.2.8"; -const ZLIB_VERNUM = 0x1280; +const char[] ZLIB_VERSION = "1.2.11"; +const ZLIB_VERNUM = 0x12b0; /* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed - data. This version of the library supports only one compression method - (deflation) but other algorithms will be added later and will have the same - stream interface. - - Compression can be done in a single step if the buffers are large - enough (for example if an input file is mmap'ed), or can be done by - repeated calls of the compression function. In the latter case, the - application must provide more input and/or consume the output + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output (providing more output space) before each call. - The compressed data format used by default by the in-memory functions is + The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. - The library also supports reading and writing files in gzip (.gz) format + The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - This library can optionally read and write gzip streams in memory as well. + This library can optionally read and write gzip and raw deflate streams in + memory as well. - The zlib format was designed to be compact and fast for use in memory + The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never - crash even in case of corrupted input. + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. */ -alias void* function (void* opaque, uint items, uint size) alloc_func; -alias void function (void* opaque, void* address) free_func; +alias alloc_func = void* function (void* opaque, uint items, uint size); +alias free_func = void function (void* opaque, void* address); struct z_stream { - ubyte* next_in; /* next input byte */ + const(ubyte)* next_in; /* next input byte */ uint avail_in; /* number of bytes available at next_in */ c_ulong total_in; /* total nb of input bytes read so far */ - ubyte* next_out; /* next output byte should be put there */ + ubyte* next_out; /* next output byte will go here */ uint avail_out; /* remaining free space at next_out */ c_ulong total_out; /* total nb of bytes output so far */ - char* msg; /* last error message, NULL if no error */ + const(char)* msg; /* last error message, NULL if no error */ void* state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ void* opaque; /* private data object passed to zalloc and zfree */ - int data_type; /* best guess about the data type: binary or text */ - c_ulong adler; /* adler32 value of the uncompressed data */ + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + c_ulong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ c_ulong reserved; /* reserved for future use */ } -alias z_stream* z_streamp; +alias z_streamp = z_stream*; /* gzip header information passed to and from zlib routines. See RFC 1952 @@ -124,38 +127,39 @@ struct gz_header when writing a gzip file) */ } -alias gz_header* gz_headerp; +alias gz_headerp = gz_header*; /* - The application must update next_in and avail_in when avail_in has - dropped to zero. It must update next_out and avail_out when avail_out - has dropped to zero. The application must initialize zalloc, zfree and - opaque before calling the init function. All other fields are set by the - compression library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the opaque value. - zalloc must return Z_NULL if there is not enough memory for the object. + zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this - if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, - pointers returned by zalloc for objects of exactly 65536 bytes *must* - have their offset normalized to zero. The default allocation function - provided by this library ensures this (see zutil.c). To reduce memory - requirements and avoid any allocation of 64K objects, at the expense of - compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or - progress reports. After compression, total_in holds the total size of - the uncompressed data and may be saved for use in the decompressor - (particularly if the decompressor wants to decompress everything in - a single step). + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). */ /* constants */ @@ -215,7 +219,7 @@ enum Z_ASCII = Z_TEXT } -/* Possible values of the data_type field (though see inflate()) */ +/* Possible values of the data_type field for deflate() */ enum { @@ -229,9 +233,9 @@ const int Z_NULL = 0; /* for initializing zalloc, zfree, opaque */ const(char)* zlibVersion(); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is - not compatible with the zlib.h header file used by the application. - This check is automatically made by deflateInit and inflateInit. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. */ int deflateInit(z_streamp strm, int level) @@ -239,122 +243,149 @@ int deflateInit(z_streamp strm, int level) return deflateInit_(strm, level, ZLIB_VERSION.ptr, z_stream.sizeof); } /* - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. - If zalloc and zfree are set to Z_NULL, deflateInit updates them to - use default allocation functions. + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at - all (the input data is simply copied a block at a time). - Z_DEFAULT_COMPRESSION requests a default compromise between speed and - compression (currently equivalent to level 6). + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if level is not a valid compression level, + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). - msg is set to null if there is no error message. deflateInit does not - perform any compression: this will be done by deflate(). + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). */ int deflate(z_streamp strm, int flush); /* deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce some - output latency (reading input without producing any output) except when + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when forced to flush. - The detailed semantics are as follows. deflate performs one or both of the + The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not + accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - - Provide more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). - Some output may be provided even if flush is not set. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating avail_in or avail_out accordingly; avail_out - should never be zero before the call. The application can consume the - compressed output when it wants, for example when the output buffer is full - (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK - and with zero avail_out, it must be called again after making room in the - output buffer because there might be more output pending. + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumualte before producing output, in order to + decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In particular - avail_in is zero after the call if enough output space has been provided - before the call.) Flushing may degrade compression for some compression - algorithms and so it should be used only when necessary. + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there - was enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the - stream are deflateReset or deflateEnd. - - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least - the value returned by deflateBound (see below). If deflate does not return - Z_STREAM_END, then it must be called again as described above. - - deflate() sets strm->adler to the adler32 checksum of all input read - so far (that is, total_in bytes). + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect - the compression algorithm in any manner. + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. */ int deflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. + This function discards any unprocessed input and does not flush any pending + output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, - msg may be set but then points to a static string (which must not be + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be deallocated). */ @@ -364,132 +395,154 @@ int inflateInit(z_streamp strm) return inflateInit_(strm, ZLIB_VERSION.ptr, z_stream.sizeof); } /* - Initializes the internal stream state for decompression. The fields + Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the exact - value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller. msg is set to null if there is no error - message. inflateInit does not perform any decompression apart from reading - the zlib header if present: this will be done by inflate(). (So next_in and - avail_in may be modified, but next_out and avail_out are unchanged.) + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. */ int inflate(z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce + buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. - The detailed semantics are as follows. inflate performs one or both of the + The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing - will resume at this point for the next call of inflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there - is no more input data or no more space in the output buffer (see below - about the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating the next_* and avail_* values accordingly. - The application can consume the uncompressed output when it wants, for - example when the output buffer is full (avail_out == 0), or after each - call of inflate(). If inflate returns Z_OK and with zero avail_out, it - must be called again after making room in the output buffer because there - might be more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, - Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() stop - if and when it gets to the next deflate block boundary. When decoding the - zlib or gzip format, this will cause inflate() to return immediately after - the header and before the first block. When doing a raw inflate, inflate() - will go ahead and process the first block, and will return when it gets to - the end of that block, or when it runs out of data. + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 - if inflate() is currently decoding the last block in the deflate stream, - plus 128 if inflate() returned immediately after decoding an end-of-block - code or decoding the complete header up to just before the first byte of the - deflate stream. The end-of-block will not be indicated until all of the - uncompressed data from that block has been written to strm->next_out. The - number of unused bits may in general be greater than seven, except when - bit 7 of data_type is set, in which case the number of unused bits will be - less than eight. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step - (a single call of inflate), the parameter flush should be set to - Z_FINISH. In this case all pending input is processed and all pending - output is flushed; avail_out must be large enough to hold all the - uncompressed data. (The size of the uncompressed data may have been saved - by the compressor for this purpose.) The next operation on this stream must - be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster approach - may be used for the single inflate() call. + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the - first call. So the only effect of the flush parameter in this implementation - is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK is used. + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the adler32 checksum of the dictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the adler32 checksum of all output produced so far (that is, + strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 + below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. - inflate() will decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically. Any information - contained in the gzip header is not retained, so applications that need that - information should instead use raw inflate, see inflateInit2() below, or - inflateBack() and perform their own processing of the gzip header and - trailer. + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may then - call inflateSync() to look for a good compression block if a partial recovery - of the data is desired. + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. */ int inflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. + This function discards any unprocessed input and does not flush any pending + output. - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. */ /* Advanced functions */ @@ -509,92 +562,132 @@ int deflateInit2(z_streamp strm, strategy, ZLIB_VERSION.ptr, z_stream.sizeof); } /* - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by - the caller. + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. - The method parameter is the compression method. It must be Z_DEFLATED in + The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if + (the size of the history buffer). It should be in the range 8 .. 15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. - windowBits can also be greater than 15 for optional gzip encoding. Add + windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), - no header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but - is slow and reduces compression ratio; memLevel=9 uses maximum memory - for optimal speed. The default value is 8. See zconf.h for total memory - usage as a function of windowBits and memLevel. + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. - The strategy parameter is used to tune the compression algorithm. Use the + The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between - Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as - Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy - parameter only affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately. Z_FIXED prevents the - use of dynamic Huffman codes, allowing for a simpler decoder for special - applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid - method). msg is set to null if there is no error message. deflateInit2 does - not perform any compression: this will be done by deflate(). + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). */ int deflateSetDictionary(z_streamp strm, const(ubyte)* dictionary, uint dictLength); /* Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any - call of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a + used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. In addition, the - current implementation of deflate will use at most the window size minus - 262 bytes of the provided dictionary. + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. - Upon return of this function, strm->adler is set to the adler32 value + Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value + which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. + Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +int deflateGetDictionary(z_streamp strm, ubyte *dictionary, uint dictLength); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. */ int deflateCopy(z_streamp dest, z_streamp source); @@ -603,98 +696,61 @@ int deflateCopy(z_streamp dest, z_streamp source); This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed + data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and - can consume lots of memory. + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and + (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ int deflateReset(z_streamp strm); /* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. - The stream will keep the same compression level and any other attributes - that may have been set by deflateInit2. + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -int inflatePrime(z_streamp strm, int bits, int value); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -int inflateGetHeader(z_streamp strm, gz_headerp head); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK can be used to - force inflate() to return immediately after header processing is complete - and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When - any of extra, name, or comment are not Z_NULL and the respective field is - not present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). */ int deflateParams(z_streamp strm, int level, int strategy); /* Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be + interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different - strategy. If the compression level is changed, the input available so far - is compressed with the old level (and may be flushed); the new level will - take effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to - be compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR - if strm->avail_out was zero. + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1 .. 3, and 4 .. 9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. */ int deflateTune(z_streamp strm, int good_length, int max_lazy, int nice_length, @@ -711,31 +767,51 @@ int deflateTune(z_streamp strm, int good_length, int max_lazy, int nice_length, returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ -int deflateBound(z_streamp strm, size_t sourceLen); +size_t deflateBound(z_streamp strm, size_t sourceLen); /* deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() - or deflateInit2(). This would be used to allocate an output buffer - for deflation in a single pass, and so would be called before deflate(). + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. */ +int deflatePending(z_streamp strm, uint* pending, int* bits); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + int deflatePrime(z_streamp strm, int bits, int value); /* deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the - bits leftover from a previous deflate stream when appending to it. As such, - this function can only be used for raw deflate, and must be used before the - first deflate() call after a deflateInit2() or deflateReset(). bits must be - less than or equal to 16, and that many of the least significant bits of - value will be inserted in the output. - - deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. */ int deflateSetHeader(z_streamp strm, gz_headerp head); /* - deflateSetHeader() provides gzip header information for when a gzip + deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information @@ -748,11 +824,11 @@ int deflateSetHeader(z_streamp strm, gz_headerp head); 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. - If deflateSetHeader is not used, the default gzip header has text false, + If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ @@ -761,81 +837,109 @@ int inflateInit2(z_streamp strm, int windowBits) return inflateInit2_(strm, windowBits, ZLIB_VERSION.ptr, z_stream.sizeof); } /* - This is another version of inflateInit with an extra parameter. The + This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value + size (the size of the history buffer). It should be in the range 8 .. 15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window + deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This + looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom + such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to + recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments + most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. - windowBits can also be greater than 15 for optional gzip decoding. Add + windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is - a crc32 instead of an adler32. + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg - is set to null if there is no error message. inflateInit2 does not perform - any decompression apart from reading the zlib header if present: this will - be done by inflate(). (So next_in and avail_in may be modified, but next_out - and avail_out are unchanged.) -/ + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ int inflateSetDictionary(z_streamp strm, const(ubyte)* dictionary, uint dictLength); /* Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called - immediately after inflateInit2() or inflateReset() and before any call of - inflate() to set the dictionary. The application must insure that the - dictionary that was used for compression is provided. + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not + expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ -int inflateSync(z_streamp strm); +int inflateGetDictionary(z_streamp strm, ubyte* dictionary, uint* dictLength); /* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been found, - or Z_STREAM_ERROR if the stream structure was inconsistent. In the success - case, the application may save the current current value of total_in which - indicates where valid compressed data was found. In the error case, the - application may repeatedly call inflateSync, providing more input each time, - until success or end of the input data. +int inflateSync(z_streamp strm); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. */ -int inflateCopy (z_streamp dest, z_streamp source); +int inflateCopy(z_streamp dest, z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -846,18 +950,117 @@ int inflateCopy (z_streamp dest, z_streamp source); inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and + (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ int inflateReset(z_streamp strm); /* This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. - The stream will keep attributes that may have been set by inflateInit2. + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). +int inflateReset2(z_streamp strm, int windowBits); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +int inflatePrime(z_streamp strm, int bits, int value); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +c_long inflateMark(z_streamp strm); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +int inflateGetHeader(z_streamp strm, gz_headerp head); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. */ @@ -870,7 +1073,7 @@ int inflateBackInit(z_stream* strm, int windowBits, ubyte* window) calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8..15. window is a caller + logarithm of the window size, in the range 8 .. 15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general @@ -879,13 +1082,13 @@ int inflateBackInit(z_stream* strm, int windowBits, ubyte* window) See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the paramaters are invalid, Z_MEM_ERROR if the internal state could not - be allocated, or Z_VERSION_ERROR if the version of the library does not - match the version of the header file. + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. */ -alias uint function(void*, ubyte**) in_func; -alias int function(void*, ubyte*, uint) out_func; +alias in_func = uint function(void*, ubyte**); +alias out_func = int function(void*, ubyte*, uint); int inflateBack(z_stream* strm, in_func f_in, @@ -894,25 +1097,26 @@ int inflateBack(z_stream* strm, void* out_desc); /* inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is more efficient than inflate() for - file i/o applications in that it avoids copying between the output and the - sliding window by simply making the window itself the output buffer. This - function trusts the application to not change the output buffer passed by - the output function, at least until inflateBack() returns. + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free - the allocated state. + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects - only the raw deflate stream to decompress. This is different from the - normal behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those @@ -921,12 +1125,12 @@ int inflateBack(z_stream* strm, parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0 .. len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). @@ -937,7 +1141,7 @@ int inflateBack(z_stream* strm, calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These @@ -947,15 +1151,15 @@ int inflateBack(z_stream* strm, On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format - error in the deflate stream (in which case strm->msg is set to indicate the - nature of the error), or Z_STREAM_ERROR if the stream was not properly - initialized. In the case of Z_BUF_ERROR, an input or output error can be - distinguished using strm->next_in which will be Z_NULL only if in() returned - an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to - out() returning non-zero. (in() will always be called before out(), so - strm->next_in is assured to be defined if out() returns non-zero.) Note - that inflateBack() cannot return Z_OK. + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. */ int inflateBackEnd(z_stream* strm); @@ -976,7 +1180,7 @@ uint zlibCompileFlags(); 7.6: size of z_off_t Compiler, assembler, and debug options: - 8: DEBUG + 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) @@ -1010,11 +1214,11 @@ uint zlibCompileFlags(); /* utility functions */ /* - The following utility functions are implemented on top of the - basic stream-oriented functions. To simplify the interface, some - default options are assumed (compression level and memory usage, - standard memory allocation functions). The source code of these - utility functions can easily be modified if you need special options. + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. */ int compress(ubyte* dest, @@ -1023,12 +1227,12 @@ int compress(ubyte* dest, size_t sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least the value returned - by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - This function can be used to compress a whole file at once if the - input file is mmap'ed. + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. @@ -1040,12 +1244,12 @@ int compress2(ubyte* dest, size_t sourceLen, int level); /* - Compresses the source buffer into the destination buffer. The level + Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the + length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, @@ -1055,8 +1259,8 @@ int compress2(ubyte* dest, size_t compressBound(size_t sourceLen); /* compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before - a compress() or compress2() call to allocate the destination buffer. + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. */ int uncompress(ubyte* dest, @@ -1065,150 +1269,296 @@ int uncompress(ubyte* dest, size_t sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. */ +int uncompress2(ubyte* dest, + size_t* destLen, + const(ubyte)* source, + size_t* sourceLen); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ -alias void* gzFile; -alias int z_off_t; // file offset +alias gzFile = void*; +alias z_off_t = int; // file offset +alias z_size_t = size_t; gzFile gzopen(const(char)* path, const(char)* mode); /* - Opens a gzip (.gz) file for reading or writing. The mode parameter - is as in fopen ("rb" or "wb") but can also include a compression level - ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for - Huffman only compression as in "wb1h", or 'R' for run-length encoding - as in "wb1R". (See the description of deflateInit2 for more information - about the strategy parameter.) + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. - - gzopen returns NULL if the file could not be opened or if there was - insufficient memory to allocate the (de)compression state; errno - can be checked to distinguish the two cases (if errno is zero, the - zlib error is Z_MEM_ERROR). */ + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ gzFile gzdopen(int fd, const(char)* mode); /* - gzdopen() associates a gzFile with the file descriptor fd. File - descriptors are obtained from calls like open, dup, creat, pipe or - fileno (in the file has been previously opened with fopen). - The mode parameter is as in gzopen. - The next call of gzclose on the returned gzFile will also close the - file descriptor fd, just like fclose(fdopen(fd), mode) closes the file - descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). - gzdopen returns NULL if there was insufficient memory to allocate - the (de)compression state. + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +int gzbuffer(gzFile file, uint size); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. */ int gzsetparams(gzFile file, int level, int strategy); /* - Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. */ int gzread(gzFile file, void* buf, uint len); /* - Reads the given number of uncompressed bytes from the compressed file. - If the input file was not in gzip format, gzread copies the given number - of bytes into the buffer. - gzread returns the number of uncompressed bytes actually read (0 for - end of file, -1 for error). */ + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +z_size_t gzfread(void* buf, z_size_t size, z_size_t nitems, gzFile file); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ int gzwrite(gzFile file, void* buf, uint len); /* Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes actually written - (0 in case of error). + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +z_size_t gzfwrite(void* buf, z_size_t size, z_size_t nitems, gzFile file); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. */ int gzprintf(gzFile file, const(char)* format, ...); /* - Converts, formats, and writes the args to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written (0 in case of error). The number of - uncompressed bytes written is limited to 4095. The caller should assure that - this limit is not exceeded. If it is exceeded, then gzprintf() will return - return an error (0) with nothing written. In this case, there may also be a + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf() because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). */ int gzputs(gzFile file, const(char)* s); /* - Writes the given null-terminated string to the compressed file, excluding + Writes the given null-terminated string to the compressed file, excluding the terminating null character. - gzputs returns the number of characters written, or -1 in case of error. + + gzputs returns the number of characters written, or -1 in case of error. */ const(char)* gzgets(gzFile file, const(char)* buf, int len); /* - Reads bytes from the compressed file until len-1 characters are read, or - a newline character is read and transferred to buf, or an end-of-file - condition is encountered. The string is then terminated with a null - character. - gzgets returns buf, or Z_NULL in case of error. + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. */ int gzputc(gzFile file, int c); /* - Writes c, converted to an unsigned char, into the compressed file. - gzputc returns the value that was written, or -1 in case of error. + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. */ -int gzgetc(gzFile file); +int gzgetc(gzFile file); /* - Reads one byte from the compressed file. gzgetc returns this byte - or -1 in case of end of file or error. + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. */ int gzungetc(int c, gzFile file); /* - Push one character back onto the stream to be read again later. - Only one character of push-back is allowed. gzungetc() returns the - character pushed, or -1 on failure. gzungetc() will fail if a - character has been pushed but not read yet, or if c is -1. The pushed - character will be discarded if the stream is repositioned with gzseek() - or gzrewind(). + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). */ int gzflush(gzFile file, int flush); /* - Flushes all pending output into the compressed file. The parameter - flush is as in the deflate() function. The return value is the zlib - error number (see function gzerror below). gzflush returns Z_OK if - the flush parameter is Z_FINISH and all output could be flushed. - gzflush should be called only when strictly necessary because it can - degrade compression. + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. */ z_off_t gzseek(gzFile file, z_off_t offset, int whence); /* - Sets the starting position for the next gzread or gzwrite on the - given compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are + extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. - gzseek returns the resulting offset location as measured in bytes from + gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. @@ -1218,50 +1568,109 @@ int gzrewind(gzFile file); /* Rewinds the given file. This function is supported only for reading. - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ -z_off_t gztell(gzFile file); +z_off_t gztell(gzFile file); /* - Returns the starting position for the next gzread or gzwrite on the - given compressed file. This position represents a number of bytes in the - uncompressed data stream. + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +z_off_t gzoffset(gzFile file); +/* + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. */ int gzeof(gzFile file); /* - Returns 1 when EOF has previously been detected reading the given - input stream, otherwise zero. + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. */ int gzdirect(gzFile file); /* - Returns 1 if file is being read directly without decompression, otherwise - zero. + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) */ int gzclose(gzFile file); /* - Flushes all pending output if necessary, closes the compressed file - and deallocates all the (de)compression state. The return value is the zlib - error number (see function gzerror below). + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +int gzclose_r(gzFile file); +int gzclose_w(gzFile file); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. */ const(char)* gzerror(gzFile file, int* errnum); /* - Returns the error message for the last error which occurred on the - given compressed file. errnum is set to zlib error number. If an - error occurred in the file system and not in the compression library, - errnum is set to Z_ERRNO and the application may consult errno - to get the exact error code. + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. */ -void gzclearerr (gzFile file); +void gzclearerr(gzFile file); /* - Clears the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ @@ -1269,52 +1678,67 @@ void gzclearerr (gzFile file); /* These functions are not related to compression but are exported - anyway because they might be useful in applications using the - compression library. + anyway because they might be useful in applications using the compression + library. */ - uint adler32 (uint adler, ubyte* buf, uint len); - +uint adler32(uint adler, const(ubyte)* buf, uint len); /* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns - the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. Usage example: + Update a running Adler-32 checksum with the bytes buf[0 .. len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: - uint adler = adler32(0L, Z_NULL, 0); + uLong adler = adler32(0L, Z_NULL, 0); - while (read_buffer(buffer, length) != EOF) { + while (read_buffer(buffer, length) != EOF) adler = adler32(adler, buffer, length); - } + if (adler != original_adler) error(); */ +uint adler32_z (uint adler, const(ubyte)* buf, z_size_t len); +/* + Same as adler32(), but with a size_t length. +*/ + uint adler32_combine(uint adler1, uint adler2, z_off_t len2); /* Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. */ -uint crc32(uint crc, ubyte* buf, uint len); +uint crc32(uint crc, const(ubyte)* buf, uint len); /* - Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is NULL, this function returns the required initial - value for the for the crc. Pre- and post-conditioning (one's complement) is + Update a running CRC-32 with the bytes buf[0 .. len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. + Usage example: - uint crc = crc32(0L, Z_NULL, 0); + uLong crc = crc32(0L, Z_NULL, 0); - while (read_buffer(buffer, length) != EOF) { + while (read_buffer(buffer, length) != EOF) crc = crc32(crc, buffer, length); - } + if (crc != original_crc) error(); */ -uint crc32_combine (uint crc1, uint crc2, z_off_t len2); +uint crc32_z(uint adler, const(ubyte)* buf, z_size_t len); +/* + Same as crc32(), but with a size_t length. +*/ + +uint crc32_combine(uint crc1, uint crc2, z_off_t len2); /* Combine two CRC-32 check values into one. For two sequences of bytes, diff --git a/etc/c/zlib/ChangeLog b/etc/c/zlib/ChangeLog index f22aabaef53..30199a65a03 100644 --- a/etc/c/zlib/ChangeLog +++ b/etc/c/zlib/ChangeLog @@ -1,10 +1,53 @@ ChangeLog file for zlib +Changes in 1.2.11 (15 Jan 2017) +- Fix deflate stored bug when pulling last block from window +- Permit immediate deflateParams changes before any deflate input + +Changes in 1.2.10 (2 Jan 2017) +- Avoid warnings on snprintf() return value +- Fix bug in deflate_stored() for zero-length input +- Fix bug in gzwrite.c that produced corrupt gzip files +- Remove files to be installed before copying them in Makefile.in +- Add warnings when compiling with assembler code + +Changes in 1.2.9 (31 Dec 2016) +- Fix contrib/minizip to permit unzipping with desktop API [Zouzou] +- Improve contrib/blast to return unused bytes +- Assure that gzoffset() is correct when appending +- Improve compress() and uncompress() to support large lengths +- Fix bug in test/example.c where error code not saved +- Remedy Coverity warning [Randers-Pehrson] +- Improve speed of gzprintf() in transparent mode +- Fix inflateInit2() bug when windowBits is 16 or 32 +- Change DEBUG macro to ZLIB_DEBUG +- Avoid uninitialized access by gzclose_w() +- Allow building zlib outside of the source directory +- Fix bug that accepted invalid zlib header when windowBits is zero +- Fix gzseek() problem on MinGW due to buggy _lseeki64 there +- Loop on write() calls in gzwrite.c in case of non-blocking I/O +- Add --warn (-w) option to ./configure for more compiler warnings +- Reject a window size of 256 bytes if not using the zlib wrapper +- Fix bug when level 0 used with Z_HUFFMAN or Z_RLE +- Add --debug (-d) option to ./configure to define ZLIB_DEBUG +- Fix bugs in creating a very large gzip header +- Add uncompress2() function, which returns the input size used +- Assure that deflateParams() will not switch functions mid-block +- Dramatically speed up deflation for level 0 (storing) +- Add gzfread(), duplicating the interface of fread() +- Add gzfwrite(), duplicating the interface of fwrite() +- Add deflateGetDictionary() function +- Use snprintf() for later versions of Microsoft C +- Fix *Init macros to use z_ prefix when requested +- Replace as400 with os400 for OS/400 support [Monnerat] +- Add crc32_z() and adler32_z() functions with size_t lengths +- Update Visual Studio project files [AraHaan] + Changes in 1.2.8 (28 Apr 2013) - Update contrib/minizip/iowin32.c for Windows RT [Vollant] - Do not force Z_CONST for C++ -- Clean up contrib/vstudio [Ro§] +- Clean up contrib/vstudio [Roß] - Correct spelling error in zlib.h - Fix mixed line endings in contrib/vstudio @@ -34,7 +77,7 @@ Changes in 1.2.7.1 (24 Mar 2013) - Clean up the usage of z_const and respect const usage within zlib - Clean up examples/gzlog.[ch] comparisons of different types - Avoid shift equal to bits in type (caused endless loop) -- Fix unintialized value bug in gzputc() introduced by const patches +- Fix uninitialized value bug in gzputc() introduced by const patches - Fix memory allocation error in examples/zran.c [Nor] - Fix bug where gzopen(), gzclose() would write an empty file - Fix bug in gzclose() when gzwrite() runs out of memory @@ -194,7 +237,7 @@ Changes in 1.2.5.2 (17 Dec 2011) - Add a transparent write mode to gzopen() when 'T' is in the mode - Update python link in zlib man page - Get inffixed.h and MAKEFIXED result to match -- Add a ./config --solo option to make zlib subset with no libary use +- Add a ./config --solo option to make zlib subset with no library use - Add undocumented inflateResetKeep() function for CAB file decoding - Add --cover option to ./configure for gcc coverage testing - Add #define ZLIB_CONST option to use const in the z_stream interface @@ -564,7 +607,7 @@ Changes in 1.2.3.1 (16 August 2006) - Update make_vms.com [Zinser] - Use -fPIC for shared build in configure [Teredesai, Nicholson] - Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] -- Use fdopen() (not _fdopen()) for Interix in zutil.h [BŠck] +- Use fdopen() (not _fdopen()) for Interix in zutil.h [Bäck] - Add some FAQ entries about the contrib directory - Update the MVS question in the FAQ - Avoid extraneous reads after EOF in gzio.c [Brown] @@ -1178,7 +1221,7 @@ Changes in 1.0.6 (19 Jan 1998) 386 asm code replacing longest_match(). contrib/iostream/ by Kevin Ruland A C++ I/O streams interface to the zlib gz* functions - contrib/iostream2/ by Tyge Løvset + contrib/iostream2/ by Tyge Løvset Another C++ I/O streams interface contrib/untgz/ by "Pedro A. Aranda Guti\irrez" A very simple tar.gz file extractor using zlib @@ -1267,7 +1310,7 @@ Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] - fix array overlay in deflate.c which sometimes caused bad compressed data - fix inflate bug with empty stored block - fix MSDOS medium model which was broken in 0.99 -- fix deflateParams() which could generated bad compressed data. +- fix deflateParams() which could generate bad compressed data. - Bytef is define'd instead of typedef'ed (work around Borland bug) - added an INDEX file - new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), diff --git a/etc/c/zlib/README b/etc/c/zlib/README index 5ca9d127eda..51106de4753 100644 --- a/etc/c/zlib/README +++ b/etc/c/zlib/README @@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.8 is a general purpose data compression library. All the code is +zlib 1.2.11 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and @@ -31,7 +31,7 @@ Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at http://marknelson.us/1997/01/01/zlib-engine/ . -The changes made in version 1.2.8 are documented in the file ChangeLog. +The changes made in version 1.2.11 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . @@ -84,7 +84,7 @@ Acknowledgments: Copyright notice: - (C) 1995-2013 Jean-loup Gailly and Mark Adler + (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/etc/c/zlib/adler32.c b/etc/c/zlib/adler32.c index a868f073d8a..d0be4380a39 100644 --- a/etc/c/zlib/adler32.c +++ b/etc/c/zlib/adler32.c @@ -1,5 +1,5 @@ /* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2011 Mark Adler + * Copyright (C) 1995-2011, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -7,11 +7,9 @@ #include "zutil.h" -#define local static - local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); -#define BASE 65521 /* largest prime smaller than 65536 */ +#define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -62,10 +60,10 @@ local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); #endif /* ========================================================================= */ -uLong ZEXPORT adler32(adler, buf, len) +uLong ZEXPORT adler32_z(adler, buf, len) uLong adler; const Bytef *buf; - uInt len; + z_size_t len; { unsigned long sum2; unsigned n; @@ -132,6 +130,15 @@ uLong ZEXPORT adler32(adler, buf, len) return adler | (sum2 << 16); } +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + return adler32_z(adler, buf, len); +} + /* ========================================================================= */ local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; @@ -156,7 +163,7 @@ local uLong adler32_combine_(adler1, adler2, len2) sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; - if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } diff --git a/etc/c/zlib/compress.c b/etc/c/zlib/compress.c index 6e9762676a0..e2db404abf8 100644 --- a/etc/c/zlib/compress.c +++ b/etc/c/zlib/compress.c @@ -1,5 +1,5 @@ /* compress.c -- compress a memory buffer - * Copyright (C) 1995-2005 Jean-loup Gailly. + * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -28,16 +28,11 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) { z_stream stream; int err; + const uInt max = (uInt)-1; + uLong left; - stream.next_in = (z_const Bytef *)source; - stream.avail_in = (uInt)sourceLen; -#ifdef MAXSEG_64K - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; -#endif - stream.next_out = dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + left = *destLen; + *destLen = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; @@ -46,15 +41,26 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) err = deflateInit(&stream, level); if (err != Z_OK) return err; - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - deflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out; + stream.next_out = dest; + stream.avail_out = 0; + stream.next_in = (z_const Bytef *)source; + stream.avail_in = 0; + + do { + if (stream.avail_out == 0) { + stream.avail_out = left > (uLong)max ? max : (uInt)left; + left -= stream.avail_out; + } + if (stream.avail_in == 0) { + stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen; + sourceLen -= stream.avail_in; + } + err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH); + } while (err == Z_OK); - err = deflateEnd(&stream); - return err; + *destLen = stream.total_out; + deflateEnd(&stream); + return err == Z_STREAM_END ? Z_OK : err; } /* =========================================================================== diff --git a/etc/c/zlib/crc32.c b/etc/c/zlib/crc32.c index 979a7190a3c..9580440c0e6 100644 --- a/etc/c/zlib/crc32.c +++ b/etc/c/zlib/crc32.c @@ -1,5 +1,5 @@ /* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler + * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster @@ -30,17 +30,15 @@ #include "zutil.h" /* for STDC and FAR definitions */ -#define local static - /* Definitions for doing the crc four data bytes at a time. */ #if !defined(NOBYFOUR) && defined(Z_U4) # define BYFOUR #endif #ifdef BYFOUR local unsigned long crc32_little OF((unsigned long, - const unsigned char FAR *, unsigned)); + const unsigned char FAR *, z_size_t)); local unsigned long crc32_big OF((unsigned long, - const unsigned char FAR *, unsigned)); + const unsigned char FAR *, z_size_t)); # define TBLS 8 #else # define TBLS 1 @@ -201,10 +199,10 @@ const z_crc_t FAR * ZEXPORT get_crc_table() #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 /* ========================================================================= */ -unsigned long ZEXPORT crc32(crc, buf, len) +unsigned long ZEXPORT crc32_z(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - uInt len; + z_size_t len; { if (buf == Z_NULL) return 0UL; @@ -235,8 +233,29 @@ unsigned long ZEXPORT crc32(crc, buf, len) return crc ^ 0xffffffffUL; } +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + return crc32_z(crc, buf, len); +} + #ifdef BYFOUR +/* + This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit + integer pointer type. This violates the strict aliasing rule, where a + compiler can assume, for optimization purposes, that two pointers to + fundamentally different types won't ever point to the same memory. This can + manifest as a problem only if one of the pointers is written to. This code + only reads from those pointers. So long as this code remains isolated in + this compilation unit, there won't be a problem. For this reason, this code + should not be copied and pasted into a compilation unit in which other code + writes to the buffer that is passed to these routines. + */ + /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ @@ -247,7 +266,7 @@ unsigned long ZEXPORT crc32(crc, buf, len) local unsigned long crc32_little(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - unsigned len; + z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; @@ -278,7 +297,7 @@ local unsigned long crc32_little(crc, buf, len) } /* ========================================================================= */ -#define DOBIG4 c ^= *++buf4; \ +#define DOBIG4 c ^= *buf4++; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 @@ -287,7 +306,7 @@ local unsigned long crc32_little(crc, buf, len) local unsigned long crc32_big(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; - unsigned len; + z_size_t len; { register z_crc_t c; register const z_crc_t FAR *buf4; @@ -300,7 +319,6 @@ local unsigned long crc32_big(crc, buf, len) } buf4 = (const z_crc_t FAR *)(const void FAR *)buf; - buf4--; while (len >= 32) { DOBIG32; len -= 32; @@ -309,7 +327,6 @@ local unsigned long crc32_big(crc, buf, len) DOBIG4; len -= 4; } - buf4++; buf = (const unsigned char FAR *)buf4; if (len) do { diff --git a/etc/c/zlib/deflate.c b/etc/c/zlib/deflate.c index 696957705b7..1ec761448de 100644 --- a/etc/c/zlib/deflate.c +++ b/etc/c/zlib/deflate.c @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; + " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -73,6 +73,8 @@ typedef enum { typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ +local int deflateStateCheck OF((z_streamp strm)); +local void slide_hash OF((deflate_state *s)); local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); @@ -84,15 +86,16 @@ local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); -local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV +# pragma message("Assembler code may have bugs -- use at your own risk") void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif -#ifdef DEBUG +#ifdef ZLIB_DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif @@ -148,21 +151,14 @@ local const config configuration_table[10] = { * meaning. */ -#define EQUAL 0 -/* result of memcmp for equal strings */ - -#ifndef NO_DUMMY_DECL -struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ -#endif - /* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ -#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) +#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) /* =========================================================================== * Update a hash value with the given input byte - * IN assertion: all calls to to UPDATE_HASH are made with consecutive - * input characters, so that a running hash key can be computed from the - * previous key instead of complete recalculation each time. + * IN assertion: all calls to UPDATE_HASH are made with consecutive input + * characters, so that a running hash key can be computed from the previous + * key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) @@ -173,9 +169,9 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. - * IN assertion: all calls to to INSERT_STRING are made with consecutive - * input characters and the first MIN_MATCH bytes of str are valid - * (except for the last MIN_MATCH-1 bytes of the input file). + * IN assertion: all calls to INSERT_STRING are made with consecutive input + * characters and the first MIN_MATCH bytes of str are valid (except for + * the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ @@ -197,6 +193,37 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ s->head[s->hash_size-1] = NIL; \ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); +/* =========================================================================== + * Slide the hash table when sliding the window down (could be avoided with 32 + * bit values at the expense of memory usage). We slide even when level == 0 to + * keep the hash table consistent if we switch back to level > 0 later. + */ +local void slide_hash(s) + deflate_state *s; +{ + unsigned n, m; + Posf *p; + uInt wsize = s->w_size; + + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m - wsize : NIL); + } while (--n); + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m - wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif +} + /* ========================================================================= */ int ZEXPORT deflateInit_(strm, level, version, stream_size) z_streamp strm; @@ -270,7 +297,7 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { + strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ @@ -278,14 +305,15 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; + s->status = INIT_STATE; /* to pass state test in deflateReset() */ s->wrap = wrap; s->gzhead = Z_NULL; - s->w_bits = windowBits; + s->w_bits = (uInt)windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; - s->hash_bits = memLevel + 7; + s->hash_bits = (uInt)memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); @@ -319,6 +347,31 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, return deflateReset(strm); } +/* ========================================================================= + * Check for a valid deflate stream state. Return 0 if ok, 1 if not. + */ +local int deflateStateCheck (strm) + z_streamp strm; +{ + deflate_state *s; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + s = strm->state; + if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && +#ifdef GZIP + s->status != GZIP_STATE && +#endif + s->status != EXTRA_STATE && + s->status != NAME_STATE && + s->status != COMMENT_STATE && + s->status != HCRC_STATE && + s->status != BUSY_STATE && + s->status != FINISH_STATE)) + return 1; + return 0; +} + /* ========================================================================= */ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; @@ -331,7 +384,7 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) unsigned avail; z_const unsigned char *next; - if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + if (deflateStateCheck(strm) || dictionary == Z_NULL) return Z_STREAM_ERROR; s = strm->state; wrap = s->wrap; @@ -388,14 +441,35 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) return Z_OK; } +/* ========================================================================= */ +int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength) + z_streamp strm; + Bytef *dictionary; + uInt *dictLength; +{ + deflate_state *s; + uInt len; + + if (deflateStateCheck(strm)) + return Z_STREAM_ERROR; + s = strm->state; + len = s->strstart + s->lookahead; + if (len > s->w_size) + len = s->w_size; + if (dictionary != Z_NULL && len) + zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); + if (dictLength != Z_NULL) + *dictLength = len; + return Z_OK; +} + /* ========================================================================= */ int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; - if (strm == Z_NULL || strm->state == Z_NULL || - strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + if (deflateStateCheck(strm)) { return Z_STREAM_ERROR; } @@ -410,7 +484,11 @@ int ZEXPORT deflateResetKeep (strm) if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } - s->status = s->wrap ? INIT_STATE : BUSY_STATE; + s->status = +#ifdef GZIP + s->wrap == 2 ? GZIP_STATE : +#endif + s->wrap ? INIT_STATE : BUSY_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : @@ -440,8 +518,8 @@ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; { - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - if (strm->state->wrap != 2) return Z_STREAM_ERROR; + if (deflateStateCheck(strm) || strm->state->wrap != 2) + return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } @@ -452,7 +530,7 @@ int ZEXPORT deflatePending (strm, pending, bits) int *bits; z_streamp strm; { - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; if (bits != Z_NULL) @@ -469,7 +547,7 @@ int ZEXPORT deflatePrime (strm, bits, value) deflate_state *s; int put; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; @@ -494,9 +572,8 @@ int ZEXPORT deflateParams(strm, level, strategy) { deflate_state *s; compress_func func; - int err = Z_OK; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST @@ -510,13 +587,22 @@ int ZEXPORT deflateParams(strm, level, strategy) func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && - strm->total_in != 0) { + s->high_water) { /* Flush the last buffer: */ - err = deflate(strm, Z_BLOCK); - if (err == Z_BUF_ERROR && s->pending == 0) - err = Z_OK; + int err = deflate(strm, Z_BLOCK); + if (err == Z_STREAM_ERROR) + return err; + if (strm->avail_out == 0) + return Z_BUF_ERROR; } if (s->level != level) { + if (s->level == 0 && s->matches != 0) { + if (s->matches == 1) + slide_hash(s); + else + CLEAR_HASH(s); + s->matches = 0; + } s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; @@ -524,7 +610,7 @@ int ZEXPORT deflateParams(strm, level, strategy) s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; - return err; + return Z_OK; } /* ========================================================================= */ @@ -537,12 +623,12 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) { deflate_state *s; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; - s->good_match = good_length; - s->max_lazy_match = max_lazy; + s->good_match = (uInt)good_length; + s->max_lazy_match = (uInt)max_lazy; s->nice_match = nice_length; - s->max_chain_length = max_chain; + s->max_chain_length = (uInt)max_chain; return Z_OK; } @@ -569,14 +655,13 @@ uLong ZEXPORT deflateBound(strm, sourceLen) { deflate_state *s; uLong complen, wraplen; - Bytef *str; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; /* if can't get parameters, return conservative bound plus zlib wrapper */ - if (strm == Z_NULL || strm->state == Z_NULL) + if (deflateStateCheck(strm)) return complen + 6; /* compute wrapper length */ @@ -588,9 +673,11 @@ uLong ZEXPORT deflateBound(strm, sourceLen) case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; +#ifdef GZIP case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + Bytef *str; if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; @@ -607,6 +694,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen) wraplen += 2; } break; +#endif default: /* for compiler happiness */ wraplen = 6; } @@ -634,10 +722,10 @@ local void putShortMSB (s, b) } /* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->next_out buffer and copying into it. - * (See also read_buf()). + * Flush as much pending output as possible. All deflate() output, except for + * some deflate_stored() output, goes through this function so some + * applications may wish to modify it to avoid allocating a large + * strm->next_out buffer and copying into it. (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; @@ -654,13 +742,23 @@ local void flush_pending(strm) strm->next_out += len; s->pending_out += len; strm->total_out += len; - strm->avail_out -= len; - s->pending -= len; + strm->avail_out -= len; + s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } +/* =========================================================================== + * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. + */ +#define HCRC_UPDATE(beg) \ + do { \ + if (s->gzhead->hcrc && s->pending > (beg)) \ + strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ + s->pending - (beg)); \ + } while (0) + /* ========================================================================= */ int ZEXPORT deflate (strm, flush) z_streamp strm; @@ -669,230 +767,229 @@ int ZEXPORT deflate (strm, flush) int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; - if (strm == Z_NULL || strm->state == Z_NULL || - flush > Z_BLOCK || flush < 0) { + if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || - (strm->next_in == Z_NULL && strm->avail_in != 0) || + (strm->avail_in != 0 && strm->next_in == Z_NULL) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); - s->strm = strm; /* just in case */ old_flush = s->last_flush; s->last_flush = flush; + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + /* Write the header */ if (s->status == INIT_STATE) { -#ifdef GZIP - if (s->wrap == 2) { - strm->adler = crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (s->gzhead == Z_NULL) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s->status = BUSY_STATE; - } - else { - put_byte(s, (s->gzhead->text ? 1 : 0) + - (s->gzhead->hcrc ? 2 : 0) + - (s->gzhead->extra == Z_NULL ? 0 : 4) + - (s->gzhead->name == Z_NULL ? 0 : 8) + - (s->gzhead->comment == Z_NULL ? 0 : 16) - ); - put_byte(s, (Byte)(s->gzhead->time & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != Z_NULL) { - put_byte(s, s->gzhead->extra_len & 0xff); - put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); - } - if (s->gzhead->hcrc) - strm->adler = crc32(strm->adler, s->pending_buf, - s->pending); - s->gzindex = 0; - s->status = EXTRA_STATE; - } - } + /* zlib header */ + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; else -#endif - { - uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; - uInt level_flags; - - if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) - level_flags = 0; - else if (s->level < 6) - level_flags = 1; - else if (s->level == 6) - level_flags = 2; - else - level_flags = 3; - header |= (level_flags << 6); - if (s->strstart != 0) header |= PRESET_DICT; - header += 31 - (header % 31); + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + putShortMSB(s, header); + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } +#ifdef GZIP + if (s->status == GZIP_STATE) { + /* gzip header */ + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); s->status = BUSY_STATE; - putShortMSB(s, header); - /* Save the adler32 of the preset dictionary: */ - if (s->strstart != 0) { - putShortMSB(s, (uInt)(strm->adler >> 16)); - putShortMSB(s, (uInt)(strm->adler & 0xffff)); + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } - strm->adler = adler32(0L, Z_NULL, 0); + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; } } -#ifdef GZIP if (s->status == EXTRA_STATE) { if (s->gzhead->extra != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - - while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) - break; + ulg beg = s->pending; /* start of bytes to update crc */ + uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; + while (s->pending + left > s->pending_buf_size) { + uInt copy = s->pending_buf_size - s->pending; + zmemcpy(s->pending_buf + s->pending, + s->gzhead->extra + s->gzindex, copy); + s->pending = s->pending_buf_size; + HCRC_UPDATE(beg); + s->gzindex += copy; + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; } - put_byte(s, s->gzhead->extra[s->gzindex]); - s->gzindex++; - } - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (s->gzindex == s->gzhead->extra_len) { - s->gzindex = 0; - s->status = NAME_STATE; + beg = 0; + left -= copy; } + zmemcpy(s->pending_buf + s->pending, + s->gzhead->extra + s->gzindex, left); + s->pending += left; + HCRC_UPDATE(beg); + s->gzindex = 0; } - else - s->status = NAME_STATE; + s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ + ulg beg = s->pending; /* start of bytes to update crc */ int val; - do { if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); + HCRC_UPDATE(beg); flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; } + beg = 0; } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) { - s->gzindex = 0; - s->status = COMMENT_STATE; - } + HCRC_UPDATE(beg); + s->gzindex = 0; } - else - s->status = COMMENT_STATE; + s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ + ulg beg = s->pending; /* start of bytes to update crc */ int val; - do { if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); + HCRC_UPDATE(beg); flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; } + beg = 0; } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) - s->status = HCRC_STATE; + HCRC_UPDATE(beg); } - else - s->status = HCRC_STATE; + s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { - if (s->pending + 2 > s->pending_buf_size) + if (s->pending + 2 > s->pending_buf_size) { flush_pending(strm); - if (s->pending + 2 <= s->pending_buf_size) { - put_byte(s, (Byte)(strm->adler & 0xff)); - put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); - strm->adler = crc32(0L, Z_NULL, 0); - s->status = BUSY_STATE; + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } } + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); } - else - s->status = BUSY_STATE; - } -#endif + s->status = BUSY_STATE; - /* Flush as much pending output as possible */ - if (s->pending != 0) { + /* Compression must start with an empty pending buffer */ flush_pending(strm); - if (strm->avail_out == 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ + if (s->pending != 0) { s->last_flush = -1; return Z_OK; } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && - flush != Z_FINISH) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s->status == FINISH_STATE && strm->avail_in != 0) { - ERR_RETURN(strm, Z_BUF_ERROR); } +#endif /* Start a new block or continue the current one. */ @@ -900,9 +997,10 @@ int ZEXPORT deflate (strm, flush) (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; - bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : - (s->strategy == Z_RLE ? deflate_rle(s, flush) : - (*(configuration_table[s->level].func))(s, flush)); + bstate = s->level == 0 ? deflate_stored(s, flush) : + s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; @@ -944,7 +1042,6 @@ int ZEXPORT deflate (strm, flush) } } } - Assert(strm->avail_out > 0, "bug2"); if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; @@ -981,18 +1078,9 @@ int ZEXPORT deflateEnd (strm) { int status; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (deflateStateCheck(strm)) return Z_STREAM_ERROR; status = strm->state->status; - if (status != INIT_STATE && - status != EXTRA_STATE && - status != NAME_STATE && - status != COMMENT_STATE && - status != HCRC_STATE && - status != BUSY_STATE && - status != FINISH_STATE) { - return Z_STREAM_ERROR; - } /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); @@ -1023,7 +1111,7 @@ int ZEXPORT deflateCopy (dest, source) ushf *overlay; - if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + if (deflateStateCheck(source) || dest == Z_NULL) { return Z_STREAM_ERROR; } @@ -1073,7 +1161,7 @@ int ZEXPORT deflateCopy (dest, source) * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ -local int read_buf(strm, buf, size) +local unsigned read_buf(strm, buf, size) z_streamp strm; Bytef *buf; unsigned size; @@ -1097,7 +1185,7 @@ local int read_buf(strm, buf, size) strm->next_in += len; strm->total_in += len; - return (int)len; + return len; } /* =========================================================================== @@ -1151,9 +1239,9 @@ local uInt longest_match(s, cur_match) { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ - register Bytef *match; /* matched string */ + register Bytef *match; /* matched string */ register int len; /* length of current match */ - int best_len = s->prev_length; /* best match length so far */ + int best_len = (int)s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; @@ -1188,7 +1276,7 @@ local uInt longest_match(s, cur_match) /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ - if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); @@ -1349,7 +1437,11 @@ local uInt longest_match(s, cur_match) #endif /* FASTEST */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG + +#define EQUAL 0 +/* result of memcmp for equal strings */ + /* =========================================================================== * Check that the match at match_start is indeed a match. */ @@ -1375,7 +1467,7 @@ local void check_match(s, start, match, length) } #else # define check_match(s, start, match, length) -#endif /* DEBUG */ +#endif /* ZLIB_DEBUG */ /* =========================================================================== * Fill the window when the lookahead becomes insufficient. @@ -1390,8 +1482,7 @@ local void check_match(s, start, match, length) local void fill_window(s) deflate_state *s; { - register unsigned n, m; - register Posf *p; + unsigned n; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; @@ -1418,35 +1509,11 @@ local void fill_window(s) */ if (s->strstart >= wsize+MAX_DIST(s)) { - zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - n = s->hash_size; - p = &s->head[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - } while (--n); - - n = wsize; -#ifndef FASTEST - p = &s->prev[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); -#endif + slide_hash(s); more += wsize; } if (s->strm->avail_in == 0) break; @@ -1552,70 +1619,199 @@ local void fill_window(s) if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } +/* Maximum stored block length in deflate format (not including header). */ +#define MAX_STORED 65535 + +/* Minimum of a and b. */ +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. + * + * In case deflateParams() is used to later switch to a non-zero compression + * level, s->matches (otherwise unused when storing) keeps track of the number + * of hash table slides to perform. If s->matches is 1, then one hash table + * slide will be done when switching. If s->matches is 2, the maximum value + * allowed here, then the hash table will be cleared, since two or more slides + * is the same as a clear. + * + * deflate_stored() is written to minimize the number of times an input byte is + * copied. It is most efficient with large input and output buffers, which + * maximizes the opportunites to have a single copy from next_in to next_out. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: + /* Smallest worthy block size when not flushing or finishing. By default + * this is 32K. This can be as small as 507 bytes for memLevel == 1. For + * large input and output buffers, the stored block size will be larger. */ - ulg max_block_size = 0xffff; - ulg max_start; - - if (max_block_size > s->pending_buf_size - 5) { - max_block_size = s->pending_buf_size - 5; - } + unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s->lookahead <= 1) { - - Assert(s->strstart < s->w_size+MAX_DIST(s) || - s->block_start >= (long)s->w_size, "slide too late"); + /* Copy as many min_block or larger stored blocks directly to next_out as + * possible. If flushing, copy the remaining available input to next_out as + * stored blocks, if there is enough space. + */ + unsigned len, left, have, last = 0; + unsigned used = s->strm->avail_in; + do { + /* Set len to the maximum size block that we can copy directly with the + * available input data and output space. Set left to how much of that + * would be copied from what's left in the window. + */ + len = MAX_STORED; /* maximum deflate stored block length */ + have = (s->bi_valid + 42) >> 3; /* number of header bytes */ + if (s->strm->avail_out < have) /* need room for header */ + break; + /* maximum stored block length that will fit in avail_out: */ + have = s->strm->avail_out - have; + left = s->strstart - s->block_start; /* bytes left in window */ + if (len > (ulg)left + s->strm->avail_in) + len = left + s->strm->avail_in; /* limit len to the input */ + if (len > have) + len = have; /* limit len to the output */ + + /* If the stored block would be less than min_block in length, or if + * unable to copy all of the available input when flushing, then try + * copying to the window and the pending buffer instead. Also don't + * write an empty block when flushing -- deflate() does that. + */ + if (len < min_block && ((len == 0 && flush != Z_FINISH) || + flush == Z_NO_FLUSH || + len != left + s->strm->avail_in)) + break; - fill_window(s); - if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + /* Make a dummy stored block in pending to get the header bytes, + * including any pending bits. This also updates the debugging counts. + */ + last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; + _tr_stored_block(s, (char *)0, 0L, last); + + /* Replace the lengths in the dummy stored block with len. */ + s->pending_buf[s->pending - 4] = len; + s->pending_buf[s->pending - 3] = len >> 8; + s->pending_buf[s->pending - 2] = ~len; + s->pending_buf[s->pending - 1] = ~len >> 8; + + /* Write the stored block header bytes. */ + flush_pending(s->strm); + +#ifdef ZLIB_DEBUG + /* Update debugging counts for the data about to be copied. */ + s->compressed_len += len << 3; + s->bits_sent += len << 3; +#endif - if (s->lookahead == 0) break; /* flush the current block */ + /* Copy uncompressed bytes from the window to next_out. */ + if (left) { + if (left > len) + left = len; + zmemcpy(s->strm->next_out, s->window + s->block_start, left); + s->strm->next_out += left; + s->strm->avail_out -= left; + s->strm->total_out += left; + s->block_start += left; + len -= left; } - Assert(s->block_start >= 0L, "block gone"); - - s->strstart += s->lookahead; - s->lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - max_start = s->block_start + max_block_size; - if (s->strstart == 0 || (ulg)s->strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s->lookahead = (uInt)(s->strstart - max_start); - s->strstart = (uInt)max_start; - FLUSH_BLOCK(s, 0); + + /* Copy uncompressed bytes directly from next_in to next_out, updating + * the check value. + */ + if (len) { + read_buf(s->strm, s->strm->next_out, len); + s->strm->next_out += len; + s->strm->avail_out -= len; + s->strm->total_out += len; } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: + } while (last == 0); + + /* Update the sliding window with the last s->w_size bytes of the copied + * data, or append all of the copied data to the existing window if less + * than s->w_size bytes were copied. Also update the number of bytes to + * insert in the hash tables, in the event that deflateParams() switches to + * a non-zero compression level. + */ + used -= s->strm->avail_in; /* number of input bytes directly copied */ + if (used) { + /* If any input was used, then no unused input remains in the window, + * therefore s->block_start == s->strstart. */ - if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { - FLUSH_BLOCK(s, 0); + if (used >= s->w_size) { /* supplant the previous history */ + s->matches = 2; /* clear hash */ + zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); + s->strstart = s->w_size; } + else { + if (s->window_size - s->strstart <= used) { + /* Slide the window down. */ + s->strstart -= s->w_size; + zmemcpy(s->window, s->window + s->w_size, s->strstart); + if (s->matches < 2) + s->matches++; /* add a pending slide_hash() */ + } + zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); + s->strstart += used; + } + s->block_start = s->strstart; + s->insert += MIN(used, s->w_size - s->insert); } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); + if (s->high_water < s->strstart) + s->high_water = s->strstart; + + /* If the last block was written to next_out, then done. */ + if (last) return finish_done; + + /* If flushing and all input has been consumed, then done. */ + if (flush != Z_NO_FLUSH && flush != Z_FINISH && + s->strm->avail_in == 0 && (long)s->strstart == s->block_start) + return block_done; + + /* Fill the window with any remaining input. */ + have = s->window_size - s->strstart - 1; + if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { + /* Slide the window down. */ + s->block_start -= s->w_size; + s->strstart -= s->w_size; + zmemcpy(s->window, s->window + s->w_size, s->strstart); + if (s->matches < 2) + s->matches++; /* add a pending slide_hash() */ + have += s->w_size; /* more space now */ } - if ((long)s->strstart > s->block_start) - FLUSH_BLOCK(s, 0); - return block_done; + if (have > s->strm->avail_in) + have = s->strm->avail_in; + if (have) { + read_buf(s->strm, s->window + s->strstart, have); + s->strstart += have; + } + if (s->high_water < s->strstart) + s->high_water = s->strstart; + + /* There was not enough avail_out to write a complete worthy or flushed + * stored block to next_out. Write a stored block to pending instead, if we + * have enough input for a worthy block, or if flushing and there is enough + * room for the remaining input as a stored block in the pending buffer. + */ + have = (s->bi_valid + 42) >> 3; /* number of header bytes */ + /* maximum stored block length that will fit in pending: */ + have = MIN(s->pending_buf_size - have, MAX_STORED); + min_block = MIN(have, s->w_size); + left = s->strstart - s->block_start; + if (left >= min_block || + ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && + s->strm->avail_in == 0 && left <= have)) { + len = MIN(left, have); + last = flush == Z_FINISH && s->strm->avail_in == 0 && + len == left ? 1 : 0; + _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); + s->block_start += len; + flush_pending(s->strm); + } + + /* We've done all we can with the available input and output. */ + return last ? finish_started : need_more; } /* =========================================================================== @@ -1892,7 +2088,7 @@ local block_state deflate_rle(s, flush) prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); - s->match_length = MAX_MATCH - (int)(strend - scan); + s->match_length = MAX_MATCH - (uInt)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } diff --git a/etc/c/zlib/deflate.h b/etc/c/zlib/deflate.h index ce0299edd19..23ecdd312bc 100644 --- a/etc/c/zlib/deflate.h +++ b/etc/c/zlib/deflate.h @@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2012 Jean-loup Gailly + * Copyright (C) 1995-2016 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -51,13 +51,16 @@ #define Buf_size 16 /* size of bit buffer in bi_buf */ -#define INIT_STATE 42 -#define EXTRA_STATE 69 -#define NAME_STATE 73 -#define COMMENT_STATE 91 -#define HCRC_STATE 103 -#define BUSY_STATE 113 -#define FINISH_STATE 666 +#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ +#ifdef GZIP +# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +#endif +#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ +#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ +#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ +#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ +#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ +#define FINISH_STATE 666 /* stream complete */ /* Stream status */ @@ -83,7 +86,7 @@ typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ - static_tree_desc *stat_desc; /* the corresponding static tree */ + const static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; @@ -100,10 +103,10 @@ typedef struct internal_state { Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ - uInt pending; /* nb of bytes in the pending buffer */ + ulg pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ - uInt gzindex; /* where in extra, name, or comment */ + ulg gzindex; /* where in extra, name, or comment */ Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ @@ -249,7 +252,7 @@ typedef struct internal_state { uInt matches; /* number of string matches in current block */ uInt insert; /* bytes at end of window left to insert */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif @@ -275,7 +278,7 @@ typedef struct internal_state { /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ -#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} +#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) @@ -309,7 +312,7 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, * used. */ -#ifndef DEBUG +#ifndef ZLIB_DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) @@ -328,8 +331,8 @@ void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, flush = (s->last_lit == s->lit_bufsize-1); \ } # define _tr_tally_dist(s, distance, length, flush) \ - { uch len = (length); \ - ush dist = (distance); \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ s->d_buf[s->last_lit] = dist; \ s->l_buf[s->last_lit++] = len; \ dist--; \ diff --git a/etc/c/zlib/gzguts.h b/etc/c/zlib/gzguts.h index d87659d0319..990a4d25149 100644 --- a/etc/c/zlib/gzguts.h +++ b/etc/c/zlib/gzguts.h @@ -1,5 +1,5 @@ /* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -25,6 +25,10 @@ # include # include #endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif #include #ifdef _WIN32 @@ -35,6 +39,10 @@ # include #endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define WIDECHAR +#endif + #ifdef WINAPI_FAMILY # define open _open # define read _read @@ -95,18 +103,19 @@ # endif #endif -/* unlike snprintf (which is required in C99, yet still not supported by - Microsoft more than a decade later!), _snprintf does not guarantee null - termination of the result -- however this is only used in gzlib.c where +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where the result is assured to fit in the space provided */ -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf #endif #ifndef local # define local static #endif -/* compile with -Dlocal if your debugger can't find static symbols */ +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ /* gz* functions always use library allocation functions */ #ifndef STDC @@ -170,7 +179,7 @@ typedef struct { char *path; /* path or fd for error messages */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ - unsigned char *in; /* input buffer */ + unsigned char *in; /* input buffer (double-sized when writing) */ unsigned char *out; /* output buffer (double-sized when reading) */ int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ diff --git a/etc/c/zlib/gzlib.c b/etc/c/zlib/gzlib.c index fae202ef890..34ad7297259 100644 --- a/etc/c/zlib/gzlib.c +++ b/etc/c/zlib/gzlib.c @@ -1,11 +1,11 @@ /* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" -#if defined(_WIN32) && !defined(__BORLANDC__) +#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__) && !defined(__DMC__) # define LSEEK _lseeki64 #else #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 @@ -94,7 +94,7 @@ local gzFile gz_open(path, fd, mode) const char *mode; { gz_statep state; - size_t len; + z_size_t len; int oflag; #ifdef O_CLOEXEC int cloexec = 0; @@ -188,10 +188,10 @@ local gzFile gz_open(path, fd, mode) } /* save the path name for error messages */ -#ifdef _WIN32 +#ifdef WIDECHAR if (fd == -2) { len = wcstombs(NULL, path, 0); - if (len == (size_t)-1) + if (len == (z_size_t)-1) len = 0; } else @@ -202,7 +202,7 @@ local gzFile gz_open(path, fd, mode) free(state); return NULL; } -#ifdef _WIN32 +#ifdef WIDECHAR if (fd == -2) if (len) wcstombs(state->path, path, len + 1); @@ -211,7 +211,7 @@ local gzFile gz_open(path, fd, mode) else #endif #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state->path, len + 1, "%s", (const char *)path); + (void)snprintf(state->path, len + 1, "%s", (const char *)path); #else strcpy(state->path, path); #endif @@ -239,7 +239,7 @@ local gzFile gz_open(path, fd, mode) /* open the file with the appropriate flags (or just use fd) */ state->fd = fd > -1 ? fd : ( -#ifdef _WIN32 +#ifdef WIDECHAR fd == -2 ? _wopen(path, oflag, 0666) : #endif open((const char *)path, oflag, 0666)); @@ -248,8 +248,10 @@ local gzFile gz_open(path, fd, mode) free(state); return NULL; } - if (state->mode == GZ_APPEND) + if (state->mode == GZ_APPEND) { + LSEEK(state->fd, 0, SEEK_END); /* so gzoffset() is correct */ state->mode = GZ_WRITE; /* simplify later checks */ + } /* save the current position for rewinding (only if reading) */ if (state->mode == GZ_READ) { @@ -291,7 +293,7 @@ gzFile ZEXPORT gzdopen(fd, mode) if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) return NULL; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ + (void)snprintf(path, 7 + 3 * sizeof(int), "", fd); #else sprintf(path, "", fd); /* for debugging */ #endif @@ -301,7 +303,7 @@ gzFile ZEXPORT gzdopen(fd, mode) } /* -- see zlib.h -- */ -#ifdef _WIN32 +#ifdef WIDECHAR gzFile ZEXPORT gzopen_w(path, mode) const wchar_t *path; const char *mode; @@ -329,6 +331,8 @@ int ZEXPORT gzbuffer(file, size) return -1; /* check and set requested size */ + if ((size << 1) < size) + return -1; /* need to be able to double it */ if (size < 2) size = 2; /* need two bytes to check magic header */ state->want = size; @@ -604,14 +608,13 @@ void ZLIB_INTERNAL gz_error(state, err, msg) return; } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, - "%s%s%s", state->path, ": ", msg); + (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); #else strcpy(state->msg, state->path); strcat(state->msg, ": "); strcat(state->msg, msg); #endif - return; } #ifndef INT_MAX diff --git a/etc/c/zlib/gzread.c b/etc/c/zlib/gzread.c index bf4538eb274..956b91ea7d9 100644 --- a/etc/c/zlib/gzread.c +++ b/etc/c/zlib/gzread.c @@ -1,5 +1,5 @@ /* gzread.c -- zlib functions for reading gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -12,6 +12,7 @@ local int gz_look OF((gz_statep)); local int gz_decomp OF((gz_statep)); local int gz_fetch OF((gz_statep)); local int gz_skip OF((gz_statep, z_off64_t)); +local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. @@ -24,13 +25,17 @@ local int gz_load(state, buf, len, have) unsigned *have; { int ret; + unsigned get, max = ((unsigned)-1 >> 2) + 1; *have = 0; do { - ret = read(state->fd, buf + *have, len - *have); + get = len - *have; + if (get > max) + get = max; + ret = read(state->fd, buf + *have, get); if (ret <= 0) break; - *have += ret; + *have += (unsigned)ret; } while (*have < len); if (ret < 0) { gz_error(state, Z_ERRNO, zstrerror()); @@ -94,10 +99,8 @@ local int gz_look(state) state->in = (unsigned char *)malloc(state->want); state->out = (unsigned char *)malloc(state->want << 1); if (state->in == NULL || state->out == NULL) { - if (state->out != NULL) - free(state->out); - if (state->in != NULL) - free(state->in); + free(state->out); + free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } @@ -284,33 +287,17 @@ local int gz_skip(state, len) return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; +/* Read len bytes into buf from file, or less than len up to the end of the + input. Return the number of bytes read. If zero is returned, either the + end of file was reached, or there was an error. state->err must be + consulted in that case to determine which. */ +local z_size_t gz_read(state, buf, len) + gz_statep state; voidp buf; - unsigned len; + z_size_t len; { - unsigned got, n; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return -1; - } + z_size_t got; + unsigned n; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -320,32 +307,38 @@ int ZEXPORT gzread(file, buf, len) if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) - return -1; + return 0; } /* get len bytes to buf, or less than len if at the end */ got = 0; do { + /* set n to the maximum amount of len that fits in an unsigned int */ + n = -1; + if (n > len) + n = len; + /* first just try copying data from the output buffer */ if (state->x.have) { - n = state->x.have > len ? len : state->x.have; + if (state->x.have < n) + n = state->x.have; memcpy(buf, state->x.next, n); state->x.next += n; state->x.have -= n; } /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && strm->avail_in == 0) { + else if (state->eof && state->strm.avail_in == 0) { state->past = 1; /* tried to read past end */ break; } /* need output data -- for small len or new stream load up our output buffer */ - else if (state->how == LOOK || len < (state->size << 1)) { + else if (state->how == LOOK || n < (state->size << 1)) { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) - return -1; + return 0; continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ @@ -353,16 +346,16 @@ int ZEXPORT gzread(file, buf, len) /* large len -- read directly into user buffer */ else if (state->how == COPY) { /* read directly */ - if (gz_load(state, (unsigned char *)buf, len, &n) == -1) - return -1; + if (gz_load(state, (unsigned char *)buf, n, &n) == -1) + return 0; } /* large len -- decompress directly into user buffer */ else { /* state->how == GZIP */ - strm->avail_out = len; - strm->next_out = (unsigned char *)buf; + state->strm.avail_out = n; + state->strm.next_out = (unsigned char *)buf; if (gz_decomp(state) == -1) - return -1; + return 0; n = state->x.have; state->x.have = 0; } @@ -374,8 +367,75 @@ int ZEXPORT gzread(file, buf, len) state->x.pos += n; } while (len); - /* return number of bytes read into user buffer (will fit in int) */ - return (int)got; + /* return number of bytes read into user buffer */ + return got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); + return -1; + } + + /* read len or fewer bytes to buf */ + len = gz_read(state, buf, len); + + /* check for an error */ + if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* return the number of bytes read (this is assured to fit in an int) */ + return (int)len; +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfread(buf, size, nitems, file) + voidp buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* read len or fewer bytes to buf, return the number of full items read */ + return len ? gz_read(state, buf, len) / size : 0; } /* -- see zlib.h -- */ @@ -408,8 +468,8 @@ int ZEXPORT gzgetc(file) return *(state->x.next)++; } - /* nothing there -- try gzread() */ - ret = gzread(file, buf, 1); + /* nothing there -- try gz_read() */ + ret = gz_read(state, buf, 1); return ret < 1 ? -1 : buf[0]; } @@ -451,7 +511,7 @@ int ZEXPORT gzungetc(c, file) if (state->x.have == 0) { state->x.have = 1; state->x.next = state->out + (state->size << 1) - 1; - state->x.next[0] = c; + state->x.next[0] = (unsigned char)c; state->x.pos--; state->past = 0; return c; @@ -473,7 +533,7 @@ int ZEXPORT gzungetc(c, file) } state->x.have++; state->x.next--; - state->x.next[0] = c; + state->x.next[0] = (unsigned char)c; state->x.pos--; state->past = 0; return c; diff --git a/etc/c/zlib/gzwrite.c b/etc/c/zlib/gzwrite.c index aa767fbf63e..c7b5651d70b 100644 --- a/etc/c/zlib/gzwrite.c +++ b/etc/c/zlib/gzwrite.c @@ -1,5 +1,5 @@ /* gzwrite.c -- zlib functions for writing gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,17 +9,19 @@ local int gz_init OF((gz_statep)); local int gz_comp OF((gz_statep, int)); local int gz_zero OF((gz_statep, z_off64_t)); +local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); /* Initialize state for writing a gzip file. Mark initialization by setting - state->size to non-zero. Return -1 on failure or 0 on success. */ + state->size to non-zero. Return -1 on a memory allocation failure, or 0 on + success. */ local int gz_init(state) gz_statep state; { int ret; z_streamp strm = &(state->strm); - /* allocate input buffer */ - state->in = (unsigned char *)malloc(state->want); + /* allocate input buffer (double size for gzprintf) */ + state->in = (unsigned char *)malloc(state->want << 1); if (state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; @@ -47,6 +49,7 @@ local int gz_init(state) gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } + strm->next_in = NULL; } /* mark state as initialized */ @@ -62,17 +65,17 @@ local int gz_init(state) } /* Compress whatever is at avail_in and next_in and write to the output file. - Return -1 if there is an error writing to the output file, otherwise 0. - flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, - then the deflate() state is reset to start a new gzip stream. If gz->direct - is true, then simply write to the output file without compressing, and - ignore flush. */ + Return -1 if there is an error writing to the output file or if gz_init() + fails to allocate memory, otherwise 0. flush is assumed to be a valid + deflate() flush value. If flush is Z_FINISH, then the deflate() state is + reset to start a new gzip stream. If gz->direct is true, then simply write + to the output file without compressing, and ignore flush. */ local int gz_comp(state, flush) gz_statep state; int flush; { - int ret, got; - unsigned have; + int ret, writ; + unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state->strm); /* allocate memory if this is the first time through */ @@ -81,12 +84,16 @@ local int gz_comp(state, flush) /* write directly if requested */ if (state->direct) { - got = write(state->fd, strm->next_in, strm->avail_in); - if (got < 0 || (unsigned)got != strm->avail_in) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; + while (strm->avail_in) { + put = strm->avail_in > max ? max : strm->avail_in; + writ = write(state->fd, strm->next_in, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in -= (unsigned)writ; + strm->next_in += writ; } - strm->avail_in = 0; return 0; } @@ -97,17 +104,21 @@ local int gz_comp(state, flush) doing Z_FINISH then don't write until we get to Z_STREAM_END */ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { - have = (unsigned)(strm->next_out - state->x.next); - if (have && ((got = write(state->fd, state->x.next, have)) < 0 || - (unsigned)got != have)) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; + while (strm->next_out > state->x.next) { + put = strm->next_out - state->x.next > (int)max ? max : + (unsigned)(strm->next_out - state->x.next); + writ = write(state->fd, state->x.next, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + state->x.next += writ; } if (strm->avail_out == 0) { strm->avail_out = state->size; strm->next_out = state->out; + state->x.next = state->out; } - state->x.next = strm->next_out; } /* compress */ @@ -129,7 +140,8 @@ local int gz_comp(state, flush) return 0; } -/* Compress len zeros to output. Return -1 on error, 0 on success. */ +/* Compress len zeros to output. Return -1 on a write error or memory + allocation failure by gz_comp(), or 0 on success. */ local int gz_zero(state, len) gz_statep state; z_off64_t len; @@ -161,32 +173,14 @@ local int gz_zero(state, len) return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; +/* Write len bytes from buf to file. Return the number of bytes written. If + the returned value is less than len, then there was an error. */ +local z_size_t gz_write(state, buf, len) + gz_statep state; voidpc buf; - unsigned len; + z_size_t len; { - unsigned put = len; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return 0; - } + z_size_t put = len; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -209,14 +203,15 @@ int ZEXPORT gzwrite(file, buf, len) do { unsigned have, copy; - if (strm->avail_in == 0) - strm->next_in = state->in; - have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (state->strm.avail_in == 0) + state->strm.next_in = state->in; + have = (unsigned)((state->strm.next_in + state->strm.avail_in) - + state->in); copy = state->size - have; if (copy > len) copy = len; memcpy(state->in + have, buf, copy); - strm->avail_in += copy; + state->strm.avail_in += copy; state->x.pos += copy; buf = (const char *)buf + copy; len -= copy; @@ -226,19 +221,83 @@ int ZEXPORT gzwrite(file, buf, len) } else { /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* directly compress user buffer to file */ - strm->avail_in = len; - strm->next_in = (z_const Bytef *)buf; - state->x.pos += len; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return 0; + state->strm.next_in = (z_const Bytef *)buf; + do { + unsigned n = (unsigned)-1; + if (n > len) + n = len; + state->strm.avail_in = n; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + len -= n; + } while (len); + } + + /* input was all buffered or compressed */ + return put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* write len bytes from buf (the return value will fit in an int) */ + return (int)gz_write(state, buf, len); +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) + voidpc buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; } - /* input was all buffered or compressed (put will fit in int) */ - return (int)put; + /* write len bytes to buf, return the number of full items written */ + return len ? gz_write(state, buf, len) / size : 0; } /* -- see zlib.h -- */ @@ -275,7 +334,7 @@ int ZEXPORT gzputc(file, c) strm->next_in = state->in; have = (unsigned)((strm->next_in + strm->avail_in) - state->in); if (have < state->size) { - state->in[have] = c; + state->in[have] = (unsigned char)c; strm->avail_in++; state->x.pos++; return c & 0xff; @@ -283,8 +342,8 @@ int ZEXPORT gzputc(file, c) } /* no room in buffer or not initialized, use gz_write() */ - buf[0] = c; - if (gzwrite(file, buf, 1) != 1) + buf[0] = (unsigned char)c; + if (gz_write(state, buf, 1) != 1) return -1; return c & 0xff; } @@ -295,11 +354,21 @@ int ZEXPORT gzputs(file, str) const char *str; { int ret; - unsigned len; + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; /* write string */ - len = (unsigned)strlen(str); - ret = gzwrite(file, str, len); + len = strlen(str); + ret = gz_write(state, str, len); return ret == 0 && len != 0 ? -1 : ret; } @@ -309,63 +378,73 @@ int ZEXPORT gzputs(file, str) /* -- see zlib.h -- */ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { - int size, len; + int len; + unsigned left; + char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; + return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) - return 0; + return state->err; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return 0; + return state->err; } - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in); + next[state->size - 1] = 0; #ifdef NO_vsnprintf # ifdef HAS_vsprintf_void - (void)vsprintf((char *)(state->in), format, va); - for (len = 0; len < size; len++) - if (state->in[len] == 0) break; + (void)vsprintf(next, format, va); + for (len = 0; len < state->size; len++) + if (next[len] == 0) break; # else - len = vsprintf((char *)(state->in), format, va); + len = vsprintf(next, format, va); # endif #else # ifdef HAS_vsnprintf_void - (void)vsnprintf((char *)(state->in), size, format, va); - len = strlen((char *)(state->in)); + (void)vsnprintf(next, state->size, format, va); + len = strlen(next); # else - len = vsnprintf((char *)(state->in), size, format, va); + len = vsnprintf(next, state->size, format, va); # endif #endif /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0) return 0; - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; + /* update buffer and position, compress first half if past that */ + strm->avail_in += (unsigned)len; state->x.pos += len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } return len; } @@ -390,73 +469,82 @@ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; { - int size, len; + unsigned len, left; + char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that can really pass pointer in ints */ if (sizeof(int) != sizeof(void *)) - return 0; + return Z_STREAM_ERROR; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; + return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) - return 0; + return state->error; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return 0; + return state->error; } - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state->in; + next = (char *)(strm->next_in + strm->avail_in); + next[state->size - 1] = 0; #ifdef NO_snprintf # ifdef HAS_sprintf_void - sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20); for (len = 0; len < size; len++) - if (state->in[len] == 0) break; + if (next[len] == 0) + break; # else - len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #else # ifdef HAS_snprintf_void - snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - len = strlen((char *)(state->in)); + snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(next); # else - len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, - a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, - a19, a20); + len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #endif /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + if (len == 0 || len >= state->size || next[state->size - 1] != 0) return 0; - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; + /* update buffer and position, compress first half if past that */ + strm->avail_in += len; state->x.pos += len; - return len; + if (strm->avail_in >= state->size) { + left = strm->avail_in - state->size; + strm->avail_in = state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state->err; + memcpy(state->in, state->in + state->size, left); + strm->next_in = state->in; + strm->avail_in = left; + } + return (int)len; } #endif @@ -470,7 +558,7 @@ int ZEXPORT gzflush(file, flush) /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing and that there's no error */ @@ -485,11 +573,11 @@ int ZEXPORT gzflush(file, flush) if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return -1; + return state->err; } /* compress remaining data with requested flush */ - gz_comp(state, flush); + (void)gz_comp(state, flush); return state->err; } @@ -520,13 +608,13 @@ int ZEXPORT gzsetparams(file, level, strategy) if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) - return -1; + return state->err; } /* change compression parameters for subsequent input */ if (state->size) { /* flush previous input with previous parameters before changing */ - if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) return state->err; deflateParams(strm, level, strategy); } diff --git a/etc/c/zlib/infback.c b/etc/c/zlib/infback.c index f3833c2e434..59679ecbfc5 100644 --- a/etc/c/zlib/infback.c +++ b/etc/c/zlib/infback.c @@ -1,5 +1,5 @@ /* infback.c -- inflate using a call-back interface - * Copyright (C) 1995-2011 Mark Adler + * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -61,7 +61,7 @@ int stream_size; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->dmax = 32768U; - state->wbits = windowBits; + state->wbits = (uInt)windowBits; state->wsize = 1U << windowBits; state->window = window; state->wnext = 0; diff --git a/etc/c/zlib/inffast.c b/etc/c/zlib/inffast.c index bda59ceb6a1..0dbd1dbc09f 100644 --- a/etc/c/zlib/inffast.c +++ b/etc/c/zlib/inffast.c @@ -1,5 +1,5 @@ /* inffast.c -- fast decoding - * Copyright (C) 1995-2008, 2010, 2013 Mark Adler + * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -8,26 +8,9 @@ #include "inflate.h" #include "inffast.h" -#ifndef ASMINF - -/* Allow machine dependent optimization for post-increment or pre-increment. - Based on testing to date, - Pre-increment preferred for: - - PowerPC G3 (Adler) - - MIPS R5000 (Randers-Pehrson) - Post-increment preferred for: - - none - No measurable difference: - - Pentium III (Anderson) - - M68060 (Nikl) - */ -#ifdef POSTINC -# define OFF 0 -# define PUP(a) *(a)++ +#ifdef ASMINF +# pragma message("Assembler code may have bugs -- use at your own risk") #else -# define OFF 1 -# define PUP(a) *++(a) -#endif /* Decode literal, length, and distance codes and write out the resulting @@ -96,9 +79,9 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; - in = strm->next_in - OFF; + in = strm->next_in; last = in + (strm->avail_in - 5); - out = strm->next_out - OFF; + out = strm->next_out; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT @@ -119,9 +102,9 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ input data or output space */ do { if (bits < 15) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } here = lcode[hold & lmask]; @@ -134,14 +117,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); - PUP(out) = (unsigned char)(here.val); + *out++ = (unsigned char)(here.val); } else if (op & 16) { /* length base */ len = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); @@ -150,9 +133,9 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } here = dcode[hold & dmask]; @@ -165,10 +148,10 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ dist = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (bits < op) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; if (bits < op) { - hold += (unsigned long)(PUP(in)) << bits; + hold += (unsigned long)(*in++) << bits; bits += 8; } } @@ -196,30 +179,30 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { - PUP(out) = 0; + *out++ = 0; } while (--len); continue; } len -= op - whave; do { - PUP(out) = 0; + *out++ = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--len); continue; } #endif } - from = window - OFF; + from = window; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } @@ -230,14 +213,14 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ if (op < len) { /* some from end of window */ len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); - from = window - OFF; + from = window; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } @@ -248,35 +231,35 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ if (op < len) { /* some from window */ len -= op; do { - PUP(out) = PUP(from); + *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { - PUP(out) = PUP(from); - PUP(out) = PUP(from); - PUP(out) = PUP(from); + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; len -= 3; } if (len) { - PUP(out) = PUP(from); + *out++ = *from++; if (len > 1) - PUP(out) = PUP(from); + *out++ = *from++; } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ - PUP(out) = PUP(from); - PUP(out) = PUP(from); - PUP(out) = PUP(from); + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; len -= 3; } while (len > 2); if (len) { - PUP(out) = PUP(from); + *out++ = *from++; if (len > 1) - PUP(out) = PUP(from); + *out++ = *from++; } } } @@ -313,8 +296,8 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ hold &= (1U << bits) - 1; /* update state and return */ - strm->next_in = in + OFF; - strm->next_out = out + OFF; + strm->next_in = in; + strm->next_out = out; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); diff --git a/etc/c/zlib/inflate.c b/etc/c/zlib/inflate.c index 870f89bb4d3..ac333e8c2ed 100644 --- a/etc/c/zlib/inflate.c +++ b/etc/c/zlib/inflate.c @@ -1,5 +1,5 @@ /* inflate.c -- zlib decompression - * Copyright (C) 1995-2012 Mark Adler + * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -92,6 +92,7 @@ #endif /* function prototypes */ +local int inflateStateCheck OF((z_streamp strm)); local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, unsigned copy)); @@ -101,12 +102,26 @@ local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); +local int inflateStateCheck(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + state = (struct inflate_state FAR *)strm->state; + if (state == Z_NULL || state->strm != strm || + state->mode < HEAD || state->mode > SYNC) + return 1; + return 0; +} + int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; @@ -131,7 +146,7 @@ z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->wsize = 0; state->whave = 0; @@ -147,7 +162,7 @@ int windowBits; struct inflate_state FAR *state; /* get the state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ @@ -156,7 +171,7 @@ int windowBits; windowBits = -windowBits; } else { - wrap = (windowBits >> 4) + 1; + wrap = (windowBits >> 4) + 5; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; @@ -210,7 +225,9 @@ int stream_size; if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; + state->strm = strm; state->window = Z_NULL; + state->mode = HEAD; /* to pass state test in inflateReset2() */ ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); @@ -234,17 +251,17 @@ int value; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } - if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; - state->hold += value << state->bits; - state->bits += bits; + state->hold += (unsigned)value << state->bits; + state->bits += (uInt)bits; return Z_OK; } @@ -625,7 +642,7 @@ int flush; static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + if (inflateStateCheck(strm) || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; @@ -645,6 +662,8 @@ int flush; NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + if (state->wbits == 0) + state->wbits = 15; state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); @@ -672,7 +691,7 @@ int flush; len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; - else if (len > state->wbits) { + if (len > 15 || len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; @@ -699,14 +718,16 @@ int flush; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); - if (state->flags & 0x0200) CRC2(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); INITBITS(); state->mode = TIME; case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; - if (state->flags & 0x0200) CRC4(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC4(state->check, hold); INITBITS(); state->mode = OS; case OS: @@ -715,7 +736,8 @@ int flush; state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } - if (state->flags & 0x0200) CRC2(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; case EXLEN: @@ -724,7 +746,8 @@ int flush; state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; - if (state->flags & 0x0200) CRC2(state->check, hold); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) @@ -742,7 +765,7 @@ int flush; len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } - if (state->flags & 0x0200) + if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; @@ -761,9 +784,9 @@ int flush; if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) - state->head->name[state->length++] = len; + state->head->name[state->length++] = (Bytef)len; } while (len && copy < have); - if (state->flags & 0x0200) + if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; @@ -782,9 +805,9 @@ int flush; if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) - state->head->comment[state->length++] = len; + state->head->comment[state->length++] = (Bytef)len; } while (len && copy < have); - if (state->flags & 0x0200) + if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; @@ -796,7 +819,7 @@ int flush; case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); - if (hold != (state->check & 0xffff)) { + if ((state->wrap & 4) && hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; @@ -1177,11 +1200,11 @@ int flush; out -= left; strm->total_out += out; state->total += out; - if (out) + if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; - if (( + if ((state->wrap & 4) && ( #ifdef GUNZIP state->flags ? hold : #endif @@ -1240,10 +1263,10 @@ int flush; strm->total_in += in; strm->total_out += out; state->total += out; - if (state->wrap && out) + if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); - strm->data_type = state->bits + (state->last ? 64 : 0) + + strm->data_type = (int)state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) @@ -1255,7 +1278,7 @@ int ZEXPORT inflateEnd(strm) z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); @@ -1273,7 +1296,7 @@ uInt *dictLength; struct inflate_state FAR *state; /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* copy dictionary */ @@ -1298,7 +1321,7 @@ uInt dictLength; int ret; /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; @@ -1330,7 +1353,7 @@ gz_headerp head; struct inflate_state FAR *state; /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; @@ -1383,7 +1406,7 @@ z_streamp strm; struct inflate_state FAR *state; /* check parameters */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; @@ -1430,7 +1453,7 @@ z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } @@ -1445,8 +1468,7 @@ z_streamp source; unsigned wsize; /* check input */ - if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || - source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + if (inflateStateCheck(source) || dest == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; @@ -1467,6 +1489,7 @@ z_streamp source; /* copy state */ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + copy->strm = dest; if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); @@ -1488,25 +1511,51 @@ int subvert; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - state->sane = !subvert; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + state->sane = !subvert; return Z_OK; #else + (void)subvert; state->sane = 1; return Z_DATA_ERROR; #endif } +int ZEXPORT inflateValidate(strm, check) +z_streamp strm; +int check; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (check) + state->wrap |= 4; + else + state->wrap &= ~4; + return Z_OK; +} + long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + if (inflateStateCheck(strm)) + return -(1L << 16); state = (struct inflate_state FAR *)strm->state; - return ((long)(state->back) << 16) + + return (long)(((unsigned long)((long)state->back)) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } + +unsigned long ZEXPORT inflateCodesUsed(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (inflateStateCheck(strm)) return (unsigned long)-1; + state = (struct inflate_state FAR *)strm->state; + return (unsigned long)(state->next - state->codes); +} diff --git a/etc/c/zlib/inflate.h b/etc/c/zlib/inflate.h index 95f4986d400..a46cce6b6d0 100644 --- a/etc/c/zlib/inflate.h +++ b/etc/c/zlib/inflate.h @@ -1,5 +1,5 @@ /* inflate.h -- internal inflate state definition - * Copyright (C) 1995-2009 Mark Adler + * Copyright (C) 1995-2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -18,7 +18,7 @@ /* Possible inflate modes between inflate() calls */ typedef enum { - HEAD, /* i: waiting for magic header */ + HEAD = 16180, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ @@ -77,11 +77,14 @@ typedef enum { CHECK -> LENGTH -> DONE */ -/* state maintained between inflate() calls. Approximately 10K bytes. */ +/* State maintained between inflate() calls -- approximately 7K bytes, not + including the allocated sliding window, which is up to 32K bytes. */ struct inflate_state { + z_streamp strm; /* pointer back to this zlib stream */ inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip, + bit 2 true to validate check value */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ diff --git a/etc/c/zlib/inftrees.c b/etc/c/zlib/inftrees.c index 44d89cf24e1..2ea08fc13ea 100644 --- a/etc/c/zlib/inftrees.c +++ b/etc/c/zlib/inftrees.c @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2013 Mark Adler + * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; + " inflate 1.2.11 Copyright 1995-2017 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -54,7 +54,7 @@ unsigned short FAR *work; code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ - int end; /* use base and extra for symbol > end */ + unsigned match; /* use base and extra for symbol >= match */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ @@ -62,7 +62,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, @@ -181,19 +181,17 @@ unsigned short FAR *work; switch (type) { case CODES: base = extra = work; /* dummy value--not used */ - end = 19; + match = 20; break; case LENS: base = lbase; - base -= 257; extra = lext; - extra -= 257; - end = 256; + match = 257; break; - default: /* DISTS */ + default: /* DISTS */ base = dbase; extra = dext; - end = -1; + match = 0; } /* initialize state for loop */ @@ -216,13 +214,13 @@ unsigned short FAR *work; for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); - if ((int)(work[sym]) < end) { + if (work[sym] + 1U < match) { here.op = (unsigned char)0; here.val = work[sym]; } - else if ((int)(work[sym]) > end) { - here.op = (unsigned char)(extra[work[sym]]); - here.val = base[work[sym]]; + else if (work[sym] >= match) { + here.op = (unsigned char)(extra[work[sym] - match]); + here.val = base[work[sym] - match]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ diff --git a/etc/c/zlib/linux.mak b/etc/c/zlib/linux.mak index 6faafaa1836..604f70027c6 100644 --- a/etc/c/zlib/linux.mak +++ b/etc/c/zlib/linux.mak @@ -1,95 +1,95 @@ -# Makefile for zlib - -MODEL=32 -CC=gcc -LD=link -CFLAGS=-O -m$(MODEL) -LDFLAGS= -O=.o - -.c.o: - $(CC) -c $(CFLAGS) $* - -.d.o: - $(DMD) -c $(DFLAGS) $* - -# variables -OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ - gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) - -all: zlib.a example minigzip - -adler32.o: zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -zutil.o: zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -gzclose.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzlib.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzread.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzwrite.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -compress.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -example.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -minigzip.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -uncompr.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -crc32.o: zutil.h zlib.h zconf.h crc32.h - $(CC) -c $(CFLAGS) $*.c - -deflate.o: deflate.h zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) -c $(CFLAGS) $*.c - -inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) -c $(CFLAGS) $*.c - -inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h - $(CC) -c $(CFLAGS) $*.c - -inftrees.o: zutil.h zlib.h zconf.h inftrees.h - $(CC) -c $(CFLAGS) $*.c - -trees.o: deflate.h zutil.h zlib.h zconf.h trees.h - $(CC) -c $(CFLAGS) $*.c - - -example.o: example.c zlib.h zconf.h - $(CC) -c $(cvarsdll) $(CFLAGS) $*.c - -minigzip.o: minigzip.c zlib.h zconf.h - $(CC) -c $(cvarsdll) $(CFLAGS) $*.c - -zlib.a: $(OBJS) - ar -r $@ $(OBJS) - -example: example.o zlib.a - $(CC) $(CFLAGS) -o $@ example.o zlib.a -g - -minigzip: minigzip.o zlib.a - $(CC) $(CFLAGS) -o $@ minigzip.o zlib.a -g - -test: example minigzip - ./example - echo hello world | minigzip | minigzip -d - -clean: - $(RM) $(OBJS) zlib.a example.o example minigzip minigzip.o test foo.gz - +# Makefile for zlib + +MODEL=32 +CC=gcc +LD=link +CFLAGS=-O -m$(MODEL) +LDFLAGS= +O=.o + +.c.o: + $(CC) -c $(CFLAGS) $* + +.d.o: + $(DMD) -c $(DFLAGS) $* + +# variables +OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ + gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) + +all: zlib.a example minigzip + +adler32.o: zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +zutil.o: zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +gzclose.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzlib.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzread.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzwrite.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +compress.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +example.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +minigzip.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +uncompr.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +crc32.o: zutil.h zlib.h zconf.h crc32.h + $(CC) -c $(CFLAGS) $*.c + +deflate.o: deflate.h zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) -c $(CFLAGS) $*.c + +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) -c $(CFLAGS) $*.c + +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h + $(CC) -c $(CFLAGS) $*.c + +inftrees.o: zutil.h zlib.h zconf.h inftrees.h + $(CC) -c $(CFLAGS) $*.c + +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h + $(CC) -c $(CFLAGS) $*.c + + +example.o: example.c zlib.h zconf.h + $(CC) -c $(cvarsdll) $(CFLAGS) $*.c + +minigzip.o: minigzip.c zlib.h zconf.h + $(CC) -c $(cvarsdll) $(CFLAGS) $*.c + +zlib.a: $(OBJS) + ar -r $@ $(OBJS) + +example: example.o zlib.a + $(CC) $(CFLAGS) -o $@ example.o zlib.a -g + +minigzip: minigzip.o zlib.a + $(CC) $(CFLAGS) -o $@ minigzip.o zlib.a -g + +test: example minigzip + ./example + echo hello world | minigzip | minigzip -d + +clean: + $(RM) $(OBJS) zlib.a example.o example minigzip minigzip.o test foo.gz + diff --git a/etc/c/zlib/osx.mak b/etc/c/zlib/osx.mak index 6faafaa1836..604f70027c6 100644 --- a/etc/c/zlib/osx.mak +++ b/etc/c/zlib/osx.mak @@ -1,95 +1,95 @@ -# Makefile for zlib - -MODEL=32 -CC=gcc -LD=link -CFLAGS=-O -m$(MODEL) -LDFLAGS= -O=.o - -.c.o: - $(CC) -c $(CFLAGS) $* - -.d.o: - $(DMD) -c $(DFLAGS) $* - -# variables -OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ - gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) - -all: zlib.a example minigzip - -adler32.o: zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -zutil.o: zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -gzclose.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzlib.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzread.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzwrite.o: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -compress.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -example.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -minigzip.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -uncompr.o: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -crc32.o: zutil.h zlib.h zconf.h crc32.h - $(CC) -c $(CFLAGS) $*.c - -deflate.o: deflate.h zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) -c $(CFLAGS) $*.c - -inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) -c $(CFLAGS) $*.c - -inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h - $(CC) -c $(CFLAGS) $*.c - -inftrees.o: zutil.h zlib.h zconf.h inftrees.h - $(CC) -c $(CFLAGS) $*.c - -trees.o: deflate.h zutil.h zlib.h zconf.h trees.h - $(CC) -c $(CFLAGS) $*.c - - -example.o: example.c zlib.h zconf.h - $(CC) -c $(cvarsdll) $(CFLAGS) $*.c - -minigzip.o: minigzip.c zlib.h zconf.h - $(CC) -c $(cvarsdll) $(CFLAGS) $*.c - -zlib.a: $(OBJS) - ar -r $@ $(OBJS) - -example: example.o zlib.a - $(CC) $(CFLAGS) -o $@ example.o zlib.a -g - -minigzip: minigzip.o zlib.a - $(CC) $(CFLAGS) -o $@ minigzip.o zlib.a -g - -test: example minigzip - ./example - echo hello world | minigzip | minigzip -d - -clean: - $(RM) $(OBJS) zlib.a example.o example minigzip minigzip.o test foo.gz - +# Makefile for zlib + +MODEL=32 +CC=gcc +LD=link +CFLAGS=-O -m$(MODEL) +LDFLAGS= +O=.o + +.c.o: + $(CC) -c $(CFLAGS) $* + +.d.o: + $(DMD) -c $(DFLAGS) $* + +# variables +OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ + gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) + +all: zlib.a example minigzip + +adler32.o: zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +zutil.o: zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +gzclose.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzlib.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzread.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzwrite.o: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +compress.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +example.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +minigzip.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +uncompr.o: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +crc32.o: zutil.h zlib.h zconf.h crc32.h + $(CC) -c $(CFLAGS) $*.c + +deflate.o: deflate.h zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) -c $(CFLAGS) $*.c + +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) -c $(CFLAGS) $*.c + +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h + $(CC) -c $(CFLAGS) $*.c + +inftrees.o: zutil.h zlib.h zconf.h inftrees.h + $(CC) -c $(CFLAGS) $*.c + +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h + $(CC) -c $(CFLAGS) $*.c + + +example.o: example.c zlib.h zconf.h + $(CC) -c $(cvarsdll) $(CFLAGS) $*.c + +minigzip.o: minigzip.c zlib.h zconf.h + $(CC) -c $(cvarsdll) $(CFLAGS) $*.c + +zlib.a: $(OBJS) + ar -r $@ $(OBJS) + +example: example.o zlib.a + $(CC) $(CFLAGS) -o $@ example.o zlib.a -g + +minigzip: minigzip.o zlib.a + $(CC) $(CFLAGS) -o $@ minigzip.o zlib.a -g + +test: example minigzip + ./example + echo hello world | minigzip | minigzip -d + +clean: + $(RM) $(OBJS) zlib.a example.o example minigzip minigzip.o test foo.gz + diff --git a/etc/c/zlib/trees.c b/etc/c/zlib/trees.c index 1fd7759ef00..50cf4b4571c 100644 --- a/etc/c/zlib/trees.c +++ b/etc/c/zlib/trees.c @@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2012 Jean-loup Gailly + * Copyright (C) 1995-2017 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -36,7 +36,7 @@ #include "deflate.h" -#ifdef DEBUG +#ifdef ZLIB_DEBUG # include #endif @@ -122,13 +122,13 @@ struct static_tree_desc_s { int max_length; /* max bit length for the codes */ }; -local static_tree_desc static_l_desc = +local const static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; -local static_tree_desc static_d_desc = +local const static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; -local static_tree_desc static_bl_desc = +local const static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== @@ -152,18 +152,16 @@ local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); -local void copy_block OF((deflate_state *s, charf *buf, unsigned len, - int header)); #ifdef GEN_TREES_H local void gen_trees_header OF((void)); #endif -#ifndef DEBUG +#ifndef ZLIB_DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ -#else /* DEBUG */ +#else /* !ZLIB_DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } @@ -182,7 +180,7 @@ local void gen_trees_header OF((void)); * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) @@ -208,12 +206,12 @@ local void send_bits(s, value, length) s->bi_valid += length; } } -#else /* !DEBUG */ +#else /* !ZLIB_DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ - int val = value;\ + int val = (int)value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ @@ -223,7 +221,7 @@ local void send_bits(s, value, length) s->bi_valid += len;\ }\ } -#endif /* DEBUG */ +#endif /* ZLIB_DEBUG */ /* the arguments must not have side effects */ @@ -317,7 +315,7 @@ local void tr_static_init() * Genererate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H -# ifndef DEBUG +# ifndef ZLIB_DEBUG # include # endif @@ -394,7 +392,7 @@ void ZLIB_INTERNAL _tr_init(s) s->bi_buf = 0; s->bi_valid = 0; -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif @@ -522,12 +520,12 @@ local void gen_bitlen(s, desc) xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; - s->opt_len += (ulg)f * (bits + xbits); - if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + s->opt_len += (ulg)f * (unsigned)(bits + xbits); + if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); } if (overflow == 0) return; - Trace((stderr,"\nbit length overflow\n")); + Tracev((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ @@ -554,9 +552,8 @@ local void gen_bitlen(s, desc) m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { - Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s->opt_len += ((long)bits - (long)tree[m].Len) - *(long)tree[m].Freq; + Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; tree[m].Len = (ush)bits; } n--; @@ -578,7 +575,7 @@ local void gen_codes (tree, max_code, bl_count) ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - ush code = 0; /* running code value */ + unsigned code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ @@ -586,7 +583,8 @@ local void gen_codes (tree, max_code, bl_count) * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits-1]) << 1; + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = (ush)code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. @@ -599,7 +597,7 @@ local void gen_codes (tree, max_code, bl_count) int len = tree[n].Len; if (len == 0) continue; /* Now reverse the bits */ - tree[n].Code = bi_reverse(next_code[len]++, len); + tree[n].Code = (ush)bi_reverse(next_code[len]++, len); Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); @@ -821,7 +819,7 @@ local int build_bl_tree(s) if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ - s->opt_len += 3*(max_blindex+1) + 5+5+4; + s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); @@ -869,11 +867,17 @@ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ -#ifdef DEBUG + bi_windup(s); /* align on byte boundary */ + put_short(s, (ush)stored_len); + put_short(s, (ush)~stored_len); + zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); + s->pending += stored_len; +#ifdef ZLIB_DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; + s->bits_sent += 2*16; + s->bits_sent += stored_len<<3; #endif - copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } /* =========================================================================== @@ -894,7 +898,7 @@ void ZLIB_INTERNAL _tr_align(s) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); @@ -902,7 +906,7 @@ void ZLIB_INTERNAL _tr_align(s) /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. + * trees or store, and write out the encoded block. */ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; @@ -974,7 +978,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) send_bits(s, (STATIC_TREES<<1)+last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 3 + s->static_len; #endif } else { @@ -983,7 +987,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) max_blindex+1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 3 + s->opt_len; #endif } @@ -995,7 +999,7 @@ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) if (last) { bi_windup(s); -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } @@ -1090,7 +1094,7 @@ local void compress_block(s, ltree, dtree) send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { - dist -= base_dist[code]; + dist -= (unsigned)base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ @@ -1193,34 +1197,7 @@ local void bi_windup(s) } s->bi_buf = 0; s->bi_valid = 0; -#ifdef DEBUG +#ifdef ZLIB_DEBUG s->bits_sent = (s->bits_sent+7) & ~7; #endif } - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -local void copy_block(s, buf, len, header) - deflate_state *s; - charf *buf; /* the input data */ - unsigned len; /* its length */ - int header; /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, (ush)len); - put_short(s, (ush)~len); -#ifdef DEBUG - s->bits_sent += 2*16; -#endif - } -#ifdef DEBUG - s->bits_sent += (ulg)len<<3; -#endif - while (len--) { - put_byte(s, *buf++); - } -} diff --git a/etc/c/zlib/uncompr.c b/etc/c/zlib/uncompr.c index 242e9493dff..f03a1a865e3 100644 --- a/etc/c/zlib/uncompr.c +++ b/etc/c/zlib/uncompr.c @@ -1,5 +1,5 @@ /* uncompr.c -- decompress a memory buffer - * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,51 +9,85 @@ #include "zlib.h" /* =========================================================================== - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted. + Decompresses the source buffer into the destination buffer. *sourceLen is + the byte length of the source buffer. Upon entry, *destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, + *destLen is the size of the decompressed data and *sourceLen is the number + of source bytes consumed. Upon return, source + *sourceLen points to the + first unused input byte. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, or + Z_DATA_ERROR if the input data was corrupted, including if the input data is + an incomplete zlib stream. */ -int ZEXPORT uncompress (dest, destLen, source, sourceLen) +int ZEXPORT uncompress2 (dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; - uLong sourceLen; + uLong *sourceLen; { z_stream stream; int err; + const uInt max = (uInt)-1; + uLong len, left; + Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ - stream.next_in = (z_const Bytef *)source; - stream.avail_in = (uInt)sourceLen; - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; - - stream.next_out = dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + len = *sourceLen; + if (*destLen) { + left = *destLen; + *destLen = 0; + } + else { + left = 1; + dest = buf; + } + stream.next_in = (z_const Bytef *)source; + stream.avail_in = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; err = inflateInit(&stream); if (err != Z_OK) return err; - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) - return Z_DATA_ERROR; - return err; - } - *destLen = stream.total_out; + stream.next_out = dest; + stream.avail_out = 0; - err = inflateEnd(&stream); - return err; + do { + if (stream.avail_out == 0) { + stream.avail_out = left > (uLong)max ? max : (uInt)left; + left -= stream.avail_out; + } + if (stream.avail_in == 0) { + stream.avail_in = len > (uLong)max ? max : (uInt)len; + len -= stream.avail_in; + } + err = inflate(&stream, Z_NO_FLUSH); + } while (err == Z_OK); + + *sourceLen -= len + stream.avail_in; + if (dest != buf) + *destLen = stream.total_out; + else if (stream.total_out && err == Z_BUF_ERROR) + left = 1; + + inflateEnd(&stream); + return err == Z_STREAM_END ? Z_OK : + err == Z_NEED_DICT ? Z_DATA_ERROR : + err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR : + err; +} + +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return uncompress2(dest, destLen, source, &sourceLen); } diff --git a/etc/c/zlib/win32.mak b/etc/c/zlib/win32.mak index f6077b7dfc7..838fff9bc4d 100644 --- a/etc/c/zlib/win32.mak +++ b/etc/c/zlib/win32.mak @@ -1,96 +1,96 @@ -# Makefile for zlib - -CC=dmc -LD=link -LIB=lib -CFLAGS=-o -DNO_snprintf -LDFLAGS= -O=.obj - -# variables - -OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ - gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) - - -all: zlib.lib example.exe minigzip.exe - -adler32.obj: zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -zutil.obj: zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -gzclose.obj: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzlib.obj: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzread.obj: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -gzwrite.obj: zlib.h zconf.h gzguts.h - $(CC) -c $(CFLAGS) $*.c - -compress.obj: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -example.obj: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -minigzip.obj: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -uncompr.obj: zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -crc32.obj: zutil.h zlib.h zconf.h crc32.h - $(CC) -c $(CFLAGS) $*.c - -deflate.obj: deflate.h zutil.h zlib.h zconf.h - $(CC) -c $(CFLAGS) $*.c - -infback.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) -c $(CFLAGS) $*.c - -inflate.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) -c $(CFLAGS) $*.c - -inffast.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h - $(CC) -c $(CFLAGS) $*.c - -inftrees.obj: zutil.h zlib.h zconf.h inftrees.h - $(CC) -c $(CFLAGS) $*.c - -trees.obj: deflate.h zutil.h zlib.h zconf.h trees.h - $(CC) -c $(CFLAGS) $*.c - - - -example.obj: example.c zlib.h zconf.h - $(CC) -c $(cvarsdll) $(CFLAGS) $*.c - -minigzip.obj: minigzip.c zlib.h zconf.h - $(CC) -c $(cvarsdll) $(CFLAGS) $*.c - -zlib.lib: $(OBJS) - $(LIB) -c zlib.lib $(OBJS) - -example.exe: example.obj zlib.lib - $(LD) $(LDFLAGS) example.obj zlib.lib - -minigzip.exe: minigzip.obj zlib.lib - $(LD) $(LDFLAGS) minigzip.obj zlib.lib - -test: example.exe minigzip.exe - example - echo hello world | minigzip | minigzip -d - -clean: - del *.obj - del *.exe - del *.dll - del *.lib - del *.lst - del foo.gz +# Makefile for zlib + +CC=dmc +LD=link +LIB=lib +CFLAGS=-o -DNO_snprintf +LDFLAGS= +O=.obj + +# variables + +OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ + gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) + + +all: zlib.lib example.exe minigzip.exe + +adler32.obj: zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +zutil.obj: zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +gzclose.obj: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzlib.obj: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzread.obj: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +gzwrite.obj: zlib.h zconf.h gzguts.h + $(CC) -c $(CFLAGS) $*.c + +compress.obj: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +example.obj: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +minigzip.obj: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +uncompr.obj: zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +crc32.obj: zutil.h zlib.h zconf.h crc32.h + $(CC) -c $(CFLAGS) $*.c + +deflate.obj: deflate.h zutil.h zlib.h zconf.h + $(CC) -c $(CFLAGS) $*.c + +infback.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) -c $(CFLAGS) $*.c + +inflate.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) -c $(CFLAGS) $*.c + +inffast.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h + $(CC) -c $(CFLAGS) $*.c + +inftrees.obj: zutil.h zlib.h zconf.h inftrees.h + $(CC) -c $(CFLAGS) $*.c + +trees.obj: deflate.h zutil.h zlib.h zconf.h trees.h + $(CC) -c $(CFLAGS) $*.c + + + +example.obj: example.c zlib.h zconf.h + $(CC) -c $(cvarsdll) $(CFLAGS) $*.c + +minigzip.obj: minigzip.c zlib.h zconf.h + $(CC) -c $(cvarsdll) $(CFLAGS) $*.c + +zlib.lib: $(OBJS) + $(LIB) -c zlib.lib $(OBJS) + +example.exe: example.obj zlib.lib + $(LD) $(LDFLAGS) example.obj zlib.lib + +minigzip.exe: minigzip.obj zlib.lib + $(LD) $(LDFLAGS) minigzip.obj zlib.lib + +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +clean: + del *.obj + del *.exe + del *.dll + del *.lib + del *.lst + del foo.gz diff --git a/etc/c/zlib/win64.mak b/etc/c/zlib/win64.mak index 164c0459666..339c5eac80c 100644 --- a/etc/c/zlib/win64.mak +++ b/etc/c/zlib/win64.mak @@ -1,101 +1,104 @@ -# Makefile for zlib64 - -MODEL=64 -VCDIR=\Program Files (x86)\Microsoft Visual Studio 10.0\VC - -CC="$(VCDIR)\bin\amd64\cl" -LD="$(VCDIR)\bin\amd64\link" -LIB="$(VCDIR)\bin\amd64\lib" - -CFLAGS=/O2 /nologo /I"$(VCDIR)\INCLUDE" -LIBFLAGS=/nologo -LDFLAGS=/nologo -O=.obj - -# variables - -OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ - gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) - - -all: zlib64.lib example.exe minigzip.exe - -adler32.obj: zutil.h zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -zutil.obj: zutil.h zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -gzclose.obj: zlib.h zconf.h gzguts.h - $(CC) /c $(CFLAGS) $*.c - -gzlib.obj: zlib.h zconf.h gzguts.h - $(CC) /c $(CFLAGS) $*.c - -gzread.obj: zlib.h zconf.h gzguts.h - $(CC) /c $(CFLAGS) $*.c - -gzwrite.obj: zlib.h zconf.h gzguts.h - $(CC) /c $(CFLAGS) $*.c - -compress.obj: zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -example.obj: zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -minigzip.obj: zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -uncompr.obj: zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -crc32.obj: zutil.h zlib.h zconf.h crc32.h - $(CC) /c $(CFLAGS) $*.c - -deflate.obj: deflate.h zutil.h zlib.h zconf.h - $(CC) /c $(CFLAGS) $*.c - -infback.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) /c $(CFLAGS) $*.c - -inflate.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h - $(CC) /c $(CFLAGS) $*.c - -inffast.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h - $(CC) /c $(CFLAGS) $*.c - -inftrees.obj: zutil.h zlib.h zconf.h inftrees.h - $(CC) /c $(CFLAGS) $*.c - -trees.obj: deflate.h zutil.h zlib.h zconf.h trees.h - $(CC) /c $(CFLAGS) $*.c - - - -example.obj: example.c zlib.h zconf.h - $(CC) /c $(cvarsdll) $(CFLAGS) $*.c - -minigzip.obj: minigzip.c zlib.h zconf.h - $(CC) /c $(cvarsdll) $(CFLAGS) $*.c - -zlib$(MODEL).lib: $(OBJS) - $(LIB) $(LIBFLAGS) /OUT:zlib$(MODEL).lib $(OBJS) - -example.exe: example.obj zlib$(MODEL).lib - $(LD) $(LDFLAGS) example.obj zlib$(MODEL).lib - -minigzip.exe: minigzip.obj zlib$(MODEL).lib - $(LD) $(LDFLAGS) minigzip.obj zlib$(MODEL).lib - -test: example.exe minigzip.exe - example - echo hello world | minigzip | minigzip -d - -clean: - del *.obj - del *.exe - del *.dll - del *.lib - del *.lst - del foo.gz +# Makefile for zlib64 + +MODEL=64 +VCDIR=\Program Files (x86)\Microsoft Visual Studio 10.0\VC + +CC="$(VCDIR)\bin\amd64\cl" +LD="$(VCDIR)\bin\amd64\link" +LIB="$(VCDIR)\bin\amd64\lib" + +CFLAGS=/O2 /nologo /I"$(VCDIR)\INCLUDE" +LIBFLAGS=/nologo +LDFLAGS=/nologo +O=.obj + +# do not preselect a C runtime (extracted from the line above to make the auto tester happy) +CFLAGS=$(CFLAGS) /Zl + +# variables + +OBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzclose$(O) gzlib$(O) gzread$(O) \ + gzwrite$(O) infback$(O) inffast$(O) inflate$(O) inftrees$(O) trees$(O) uncompr$(O) zutil$(O) + + +all: zlib64.lib example.exe minigzip.exe + +adler32.obj: zutil.h zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +zutil.obj: zutil.h zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +gzclose.obj: zlib.h zconf.h gzguts.h + $(CC) /c $(CFLAGS) $*.c + +gzlib.obj: zlib.h zconf.h gzguts.h + $(CC) /c $(CFLAGS) $*.c + +gzread.obj: zlib.h zconf.h gzguts.h + $(CC) /c $(CFLAGS) $*.c + +gzwrite.obj: zlib.h zconf.h gzguts.h + $(CC) /c $(CFLAGS) $*.c + +compress.obj: zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +example.obj: zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +minigzip.obj: zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +uncompr.obj: zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +crc32.obj: zutil.h zlib.h zconf.h crc32.h + $(CC) /c $(CFLAGS) $*.c + +deflate.obj: deflate.h zutil.h zlib.h zconf.h + $(CC) /c $(CFLAGS) $*.c + +infback.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) /c $(CFLAGS) $*.c + +inflate.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inffixed.h + $(CC) /c $(CFLAGS) $*.c + +inffast.obj: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h + $(CC) /c $(CFLAGS) $*.c + +inftrees.obj: zutil.h zlib.h zconf.h inftrees.h + $(CC) /c $(CFLAGS) $*.c + +trees.obj: deflate.h zutil.h zlib.h zconf.h trees.h + $(CC) /c $(CFLAGS) $*.c + + + +example.obj: example.c zlib.h zconf.h + $(CC) /c $(cvarsdll) $(CFLAGS) $*.c + +minigzip.obj: minigzip.c zlib.h zconf.h + $(CC) /c $(cvarsdll) $(CFLAGS) $*.c + +zlib$(MODEL).lib: $(OBJS) + $(LIB) $(LIBFLAGS) /OUT:zlib$(MODEL).lib $(OBJS) + +example.exe: example.obj zlib$(MODEL).lib + $(LD) $(LDFLAGS) example.obj zlib$(MODEL).lib + +minigzip.exe: minigzip.obj zlib$(MODEL).lib + $(LD) $(LDFLAGS) minigzip.obj zlib$(MODEL).lib + +test: example.exe minigzip.exe + example + echo hello world | minigzip | minigzip -d + +clean: + del *.obj + del *.exe + del *.dll + del *.lib + del *.lst + del foo.gz diff --git a/etc/c/zlib/zconf.h b/etc/c/zlib/zconf.h index 9987a775530..5e1d68a004e 100644 --- a/etc/c/zlib/zconf.h +++ b/etc/c/zlib/zconf.h @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -17,7 +17,7 @@ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET -/* all linked symbols */ +/* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align @@ -29,6 +29,7 @@ # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 @@ -37,10 +38,14 @@ # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams @@ -67,6 +72,8 @@ # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets @@ -78,7 +85,6 @@ # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf -# define gzvprintf z_gzvprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread @@ -89,32 +95,39 @@ # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary -# define inflateGetDictionary z_inflateGetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine -# define inflateResetKeep z_inflateResetKeep +# define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress +# define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO @@ -224,9 +237,19 @@ # define z_const #endif -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ @@ -256,7 +279,7 @@ Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ diff --git a/etc/c/zlib/zlib.3 b/etc/c/zlib/zlib.3 index 0160e62b69f..bda4eb07370 100644 --- a/etc/c/zlib/zlib.3 +++ b/etc/c/zlib/zlib.3 @@ -1,4 +1,4 @@ -.TH ZLIB 3 "28 Apr 2013" +.TH ZLIB 3 "15 Jan 2017" .SH NAME zlib \- compression/decompression library .SH SYNOPSIS @@ -48,32 +48,10 @@ Changes to this version are documented in the file that accompanies the source. .LP .I zlib -is available in Java using the java.util.zip package: -.IP -http://java.sun.com/developer/technicalArticles/Programming/compression/ -.LP -A Perl interface to -.IR zlib , -written by Paul Marquess (pmqs@cpan.org), -is available at CPAN (Comprehensive Perl Archive Network) sites, -including: -.IP -http://search.cpan.org/~pmqs/IO-Compress-Zlib/ -.LP -A Python interface to -.IR zlib , -written by A.M. Kuchling (amk@magnet.com), -is available in Python 1.5 and later versions: -.IP -http://docs.python.org/library/zlib.html -.LP -.I zlib -is built into -.IR tcl: -.IP -http://wiki.tcl.tk/4610 +is built in to many languages and operating systems, including but not limited to +Java, Python, .NET, PHP, Perl, Ruby, Swift, and Go. .LP -An experimental package to read and write files in .zip format, +An experimental package to read and write files in the .zip format, written on top of .I zlib by Gilles Vollant (info@winimage.com), @@ -92,7 +70,9 @@ web site can be found at: .IP http://zlib.net/ .LP -The data format used by the zlib library is described by RFC +The data format used by the +.I zlib +library is described by RFC (Request for Comments) 1950 to 1952 in the files: .IP http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format) @@ -124,17 +104,35 @@ http://zlib.net/zlib_faq.html before asking for help. Send questions and/or comments to zlib@gzip.org, or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). -.SH AUTHORS -Version 1.2.8 -Copyright (C) 1995-2013 Jean-loup Gailly (jloup@gzip.org) -and Mark Adler (madler@alumni.caltech.edu). -.LP -This software is provided "as-is," -without any express or implied warranty. -In no event will the authors be held liable for any damages +.SH AUTHORS AND LICENSE +Version 1.2.11 +.LP +Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler +.LP +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages arising from the use of this software. -See the distribution directory with respect to requirements -governing redistribution. +.LP +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: +.LP +.nr step 1 1 +.IP \n[step]. 3 +The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +.IP \n+[step]. +Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +.IP \n+[step]. +This notice may not be removed or altered from any source distribution. +.LP +Jean-loup Gailly Mark Adler +.br +jloup@gzip.org madler@alumni.caltech.edu +.LP The deflate format used by .I zlib was defined by Phil Katz. diff --git a/etc/c/zlib/zlib.h b/etc/c/zlib/zlib.h index 3e0c7672ac5..f09cdaf1e05 100644 --- a/etc/c/zlib/zlib.h +++ b/etc/c/zlib/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.8, April 28th, 2013 + version 1.2.11, January 15th, 2017 - Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.8" -#define ZLIB_VERNUM 0x1280 +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_REVISION 11 #define ZLIB_VER_SUBREVISION 0 /* @@ -65,7 +65,8 @@ extern "C" { with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - This library can optionally read and write gzip streams in memory as well. + This library can optionally read and write gzip and raw deflate streams in + memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- @@ -74,7 +75,7 @@ extern "C" { The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash - even in case of corrupted input. + even in the case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); @@ -87,7 +88,7 @@ typedef struct z_stream_s { uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ - Bytef *next_out; /* next output byte should be put there */ + Bytef *next_out; /* next output byte will go here */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ @@ -98,8 +99,9 @@ typedef struct z_stream_s { free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ - int data_type; /* best guess about the data type: binary or text */ - uLong adler; /* adler32 value of the uncompressed data */ + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; @@ -142,7 +144,9 @@ typedef gz_header FAR *gz_headerp; zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if @@ -155,7 +159,7 @@ typedef gz_header FAR *gz_headerp; The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the - uncompressed data and may be saved for use in the decompressor (particularly + uncompressed data and may be saved for use by the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ @@ -200,7 +204,7 @@ typedef gz_header FAR *gz_headerp; #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 -/* Possible values of the data_type field (though see inflate()) */ +/* Possible values of the data_type field for deflate() */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ @@ -258,11 +262,11 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - - Provide more output starting at next_out and update next_out and avail_out + - Generate more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). Some - output may be provided even if flush is not set. + should be set only when necessary. Some output may be provided even if + flush is zero. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more @@ -271,7 +275,9 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output - buffer because there might be more output pending. + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to @@ -292,8 +298,8 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output - in order for the decompressor to finish the block before the empty fixed code - block. + in order for the decompressor to finish the block before the empty fixed + codes block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to @@ -319,34 +325,38 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was - enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the stream - are deflateReset or deflateEnd. - - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least the - value returned by deflateBound (see below). Then deflate is guaranteed to - return Z_STREAM_END. If not enough output space is provided, deflate will - not return Z_STREAM_END, and it must be called again as described above. - - deflate() sets strm->adler to the adler32 checksum of all input read - so far (that is, total_in bytes). + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect the - compression algorithm in any manner. + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. */ @@ -369,23 +379,21 @@ ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the - exact value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit does not perform any decompression - apart from possibly reading the zlib header if present: actual decompression - will be done by inflate(). (So next_in and avail_in may be modified, but - next_out and avail_out are unused and unchanged.) The current implementation - of inflateInit() does not process any header information -- that is deferred - until inflate() is called. + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. */ @@ -401,17 +409,20 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing will - resume at this point for the next call of inflate(). + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). - - Provide more output starting at next_out and update next_out and avail_out + - Generate more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more - output, and updating the next_* and avail_* values accordingly. The + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be @@ -428,7 +439,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the + To assist in this, on return inflate() always sets strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or @@ -454,7 +465,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been - saved by the compressor for this purpose.) The use of Z_FINISH is not + saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the @@ -476,32 +487,33 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 + below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip - header is not retained, so applications that need that information should - instead use raw inflate, see inflateInit2() below, or inflateBack() and - perform their own processing of the gzip header and trailer. When processing + header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output - producted so far. The CRC-32 is checked against the gzip trailer. + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial - recovery of the data is desired. + recovery of the data is to be attempted. */ @@ -511,9 +523,8 @@ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); This function discards any unprocessed input and does not flush any pending output. - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. */ @@ -544,16 +555,29 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. + with no zlib header or trailer, and will not compute a check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no - header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is @@ -614,12 +638,12 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. - Upon return of this function, strm->adler is set to the adler32 value + Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value + which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. + Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is @@ -628,6 +652,28 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, not perform any compression: this will be done by deflate(). */ +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* @@ -648,10 +694,10 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. The - stream will keep the same compression level and any other attributes that - may have been set by deflateInit2. + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). @@ -662,20 +708,36 @@ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int strategy)); /* Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be + interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. - If the compression level is changed, the input available so far is - compressed with the old level (and may be flushed); the new level will take - effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to be - compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if - strm->avail_out was zero. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, @@ -793,7 +855,7 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to + recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. @@ -802,7 +864,10 @@ ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a - crc32 instead of an adler32. + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the @@ -823,7 +888,7 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. + can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the @@ -834,7 +899,7 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not + expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ @@ -892,7 +957,7 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. The + but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source @@ -904,7 +969,9 @@ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted - the same as it is for inflateInit2. + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if @@ -956,7 +1023,7 @@ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. - inflateMark returns the value noted above or -1 << 16 if the provided + inflateMark returns the value noted above, or -65536 if the provided source stream state was inconsistent. */ @@ -1048,9 +1115,9 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only - the raw deflate stream to decompress. This is different from the normal - behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those @@ -1059,12 +1126,12 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). @@ -1092,7 +1159,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is - assured to be defined if out() returns non-zero.) Note that inflateBack() + assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ @@ -1114,7 +1181,7 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); 7.6: size of z_off_t Compiler, assembler, and debug options: - 8: DEBUG + 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) @@ -1164,7 +1231,8 @@ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output @@ -1180,7 +1248,7 @@ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. + compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, @@ -1203,7 +1271,7 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen - is the actual size of the uncompressed buffer. + is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output @@ -1212,6 +1280,14 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, buffer with the uncompressed data up to that point. */ +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + /* gzip file access functions */ /* @@ -1290,10 +1366,9 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or - write. Two buffers are allocated, either both of the specified size when - writing, or one of the specified size and the other twice that size when - reading. A larger buffer size of, for example, 64K or 128K bytes will - noticeably increase the speed of decompression (reading). + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). @@ -1304,10 +1379,12 @@ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); @@ -1335,7 +1412,35 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); case. gzread returns the number of uncompressed bytes actually read, less than - len for end of file, or -1 for error. + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, @@ -1346,19 +1451,33 @@ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, error. */ +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written, or 0 in case of error. The number of - uncompressed bytes written is limited to 8191, or one less than the buffer - size given to gzbuffer(). The caller should assure that this limit is not - exceeded. If it is exceeded, then gzprintf() will return an error (0) with - nothing written. In this case, there may also be a buffer overflow with - unpredictable consequences, which is possible only if zlib was compiled with - the insecure functions sprintf() or vsprintf() because the secure snprintf() - or vsnprintf() functions were not available. This can be determined using - zlibCompileFlags(). + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); @@ -1418,7 +1537,7 @@ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such - concatented gzip streams. + concatenated gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. @@ -1572,7 +1691,7 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed much faster. Usage example: @@ -1585,6 +1704,12 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); if (adler != original_adler) error(); */ +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); @@ -1614,6 +1739,12 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); @@ -1644,19 +1775,35 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ - (int)sizeof(z_stream)) -#define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, (int)sizeof(z_stream)) +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif #ifndef Z_SOLO @@ -1676,10 +1823,10 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #else # define gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or @@ -1737,19 +1884,16 @@ ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #endif /* !Z_SOLO */ -/* hack for buggy compilers */ -#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; -#endif - /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); -#if defined(_WIN32) && !defined(Z_SOLO) +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif diff --git a/etc/c/zlib/zutil.c b/etc/c/zlib/zutil.c index 23d2ebef008..a76c6b0c7e5 100644 --- a/etc/c/zlib/zutil.c +++ b/etc/c/zlib/zutil.c @@ -1,5 +1,5 @@ /* zutil.c -- target dependent utility functions for the compression library - * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. + * Copyright (C) 1995-2017 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -10,21 +10,18 @@ # include "gzguts.h" #endif -#ifndef NO_DUMMY_DECL -struct internal_state {int dummy;}; /* for buggy compilers */ -#endif - z_const char * const z_errmsg[10] = { -"need dictionary", /* Z_NEED_DICT 2 */ -"stream end", /* Z_STREAM_END 1 */ -"", /* Z_OK 0 */ -"file error", /* Z_ERRNO (-1) */ -"stream error", /* Z_STREAM_ERROR (-2) */ -"data error", /* Z_DATA_ERROR (-3) */ -"insufficient memory", /* Z_MEM_ERROR (-4) */ -"buffer error", /* Z_BUF_ERROR (-5) */ -"incompatible version",/* Z_VERSION_ERROR (-6) */ -""}; + (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ + (z_const char *)"stream end", /* Z_STREAM_END 1 */ + (z_const char *)"", /* Z_OK 0 */ + (z_const char *)"file error", /* Z_ERRNO (-1) */ + (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ + (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ + (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ + (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ + (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ + (z_const char *)"" +}; const char * ZEXPORT zlibVersion() @@ -61,7 +58,7 @@ uLong ZEXPORT zlibCompileFlags() case 8: flags += 2 << 6; break; default: flags += 3 << 6; } -#ifdef DEBUG +#ifdef ZLIB_DEBUG flags += 1 << 8; #endif #if defined(ASMV) || defined(ASMINF) @@ -115,8 +112,8 @@ uLong ZEXPORT zlibCompileFlags() return flags; } -#ifdef DEBUG - +#ifdef ZLIB_DEBUG +#include # ifndef verbose # define verbose 0 # endif @@ -219,9 +216,11 @@ local ptr_table table[MAX_PTR]; voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { - voidpf buf = opaque; /* just to make some compilers happy */ + voidpf buf; ulg bsize = (ulg)items*size; + (void)opaque; + /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ @@ -244,6 +243,9 @@ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; + + (void)opaque; + if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; @@ -259,7 +261,6 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) next_ptr--; return; } - ptr = opaque; /* just to make some compilers happy */ Assert(0, "zcfree: ptr not found"); } @@ -278,13 +279,13 @@ void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { - if (opaque) opaque = 0; /* to make compiler happy */ + (void)opaque; return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { - if (opaque) opaque = 0; /* to make compiler happy */ + (void)opaque; _hfree(ptr); } @@ -306,7 +307,7 @@ voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) unsigned items; unsigned size; { - if (opaque) items += size - size; /* make compiler happy */ + (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } @@ -315,8 +316,8 @@ void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { + (void)opaque; free(ptr); - if (opaque) return; /* make compiler happy */ } #endif /* MY_ZCALLOC */ diff --git a/etc/c/zlib/zutil.h b/etc/c/zlib/zutil.h index 24ab06b1cf6..b079ea6a80f 100644 --- a/etc/c/zlib/zutil.h +++ b/etc/c/zlib/zutil.h @@ -1,5 +1,5 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -36,7 +36,9 @@ #ifndef local # define local static #endif -/* compile with -Dlocal if your debugger can't find static symbols */ +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ typedef unsigned char uch; typedef uch FAR uchf; @@ -98,28 +100,38 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif #ifdef AMIGA -# define OS_CODE 0x01 +# define OS_CODE 1 #endif #if defined(VAXC) || defined(VMS) -# define OS_CODE 0x02 +# define OS_CODE 2 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif +#ifdef __370__ +# if __TARGET_LIB__ < 0x20000000 +# define OS_CODE 4 +# elif __TARGET_LIB__ < 0x40000000 +# define OS_CODE 11 +# else +# define OS_CODE 8 +# endif +#endif + #if defined(ATARI) || defined(atarist) -# define OS_CODE 0x05 +# define OS_CODE 5 #endif #ifdef OS2 -# define OS_CODE 0x06 +# define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) -# define OS_CODE 0x07 +# define OS_CODE 7 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include /* for fdopen */ @@ -131,18 +143,24 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif -#ifdef TOPS20 -# define OS_CODE 0x0a +#ifdef __acorn +# define OS_CODE 13 #endif -#ifdef WIN32 -# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ -# define OS_CODE 0x0b -# endif +#if defined(WIN32) && !defined(__CYGWIN__) +# define OS_CODE 10 +#endif + +#ifdef _BEOS_ +# define OS_CODE 16 +#endif + +#ifdef __TOS_OS400__ +# define OS_CODE 18 #endif -#ifdef __50SERIES /* Prime/PRIMOS */ -# define OS_CODE 0x0f +#ifdef __APPLE__ +# define OS_CODE 19 #endif #if defined(_BEOS_) || defined(RISCOS) @@ -177,7 +195,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* common defaults */ #ifndef OS_CODE -# define OS_CODE 0x03 /* assume Unix */ +# define OS_CODE 3 /* assume Unix */ #endif #ifndef F_OPEN @@ -216,7 +234,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #endif /* Diagnostic functions */ -#ifdef DEBUG +#ifdef ZLIB_DEBUG # include extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error OF((char *m)); diff --git a/index.d b/index.d index 99adf0b912d..b7f5341ebd5 100644 --- a/index.d +++ b/index.d @@ -20,11 +20,11 @@ $(BOOKTABLE , ) $(LEADINGROW Algorithms & ranges) $(TR - $(TD + $(TDNW $(LINK2 std_algorithm.html, std.algorithm)$(BR) $(LINK2 std_range.html, std.range)$(BR) $(LINK2 std_range_primitives.html, std.range.primitives)$(BR) - $(LINK2 std_range_interfaces.html, std.range.interfaces) + $(LINK2 std_range_interfaces.html, std.range.interfaces)$(BR) ) $(TD Generic algorithms that work with $(LINK2 std_range.html, ranges) of any type, including strings, arrays, and other kinds of @@ -34,9 +34,9 @@ $(BOOKTABLE , ) $(LEADINGROW Array manipulation) $(TR - $(TD + $(TDNW $(LINK2 std_array.html, std.array)$(BR) - $(LINK2 std_algorithm.html, std.algorithm) + $(LINK2 std_algorithm.html, std.algorithm)$(BR) ) $(TD Convenient operations commonly used with built-in arrays. Note that many common array operations are subsets of more generic @@ -46,12 +46,12 @@ $(BOOKTABLE , ) $(LEADINGROW Containers) $(TR - $(TD + $(TDNW $(LINK2 std_container_array.html, std.container.array)$(BR) $(LINK2 std_container_binaryheap.html, std.container.binaryheap)$(BR) $(LINK2 std_container_dlist.html, std.container.dlist)$(BR) $(LINK2 std_container_rbtree.html, std.container.rbtree)$(BR) - $(LINK2 std_container_slist.html, std.container.slist) + $(LINK2 std_container_slist.html, std.container.slist)$(BR) ) $(TD See $(LINK2 std_container.html, std.container.*) for an overview. @@ -59,275 +59,470 @@ $(BOOKTABLE , ) $(LEADINGROW Data formats) $(TR - $(TD - $(LINK2 std_base64.html, std.base64)$(BR) - $(LINK2 std_csv.html, std.csv)$(BR) - $(LINK2 std_json.html, std.json)$(BR) - $(LINK2 std_xml.html, std.xml)$(BR) - $(LINK2 std_zip.html, std.zip)$(BR) - $(LINK2 std_zlib.html, std.zlib) - ) - $(TD Modules for reading/writing different data formats. - ) + $(TDNW $(LINK2 std_base64.html, std.base64)) + $(TD Encoding / decoding Base64 format.) + ) + $(TR + $(TDNW $(LINK2 std_csv.html, std.csv)) + $(TD Read Comma Separated Values and its variants from an input range of $(CODE dchar).) + ) + $(TR + $(TDNW $(LINK2 std_json.html, std.json)) + $(TD Read/write data in JSON format.) + ) + $(TR + $(TDNW $(LINK2 std_xml.html, std.xml)) + $(TD Read/write data in XML format.) + ) + $(TR + $(TDNW $(LINK2 std_zip.html, std.zip)) + $(TD Read/write data in the ZIP archive format.) + ) + $(TR + $(TDNW $(LINK2 std_zlib.html, std.zlib)) + $(TD Compress/decompress data using the zlib library.) ) $(LEADINGROW Data integrity) $(TR - $(TD - $(LINK2 std_digest_crc.html, std.digest.crc)$(BR) - $(LINK2 std_digest_digest.html, std.digest.digest)$(BR) - $(LINK2 std_digest_md.html, std.digest.md)$(BR) - $(LINK2 std_digest_ripemd.html, std.digest.ripemd)$(BR) - $(LINK2 std_digest_sha.html, std.digest.sha)$(BR) - ) - $(TD Hash algorithms for verifying data integrity. - ) + $(TDNW $(LINK2 std_experimental_checkedint.html, std.experimental.checkedint)) + $(TD Checked integral types.) + ) + $(TR + $(TDNW $(LINK2 std_digest_crc.html, std.digest.crc)) + $(TD Cyclic Redundancy Check (32-bit) implementation.) + ) + $(TR + $(TDNW $(LINK2 std_digest_digest.html, std.digest.digest)) + $(TD Compute digests such as md5, sha1 and crc32.) + ) + $(TR + $(TDNW $(LINK2 std_digest_hmac.html, std.digest.hmac)) + $(TD Compute HMAC digests of arbitrary data.) + ) + $(TR + $(TDNW $(LINK2 std_digest_md.html, std.digest.md)) + $(TD Compute MD5 hash of arbitrary data.) + ) + $(TR + $(TDNW $(LINK2 std_digest_murmurhash.html, std.digest.murmurhash)) + $(TD Compute MurmurHash of arbitrary data.) + ) + $(TR + $(TDNW $(LINK2 std_digest_ripemd.html, std.digest.ripemd)) + $(TD Compute RIPEMD-160 hash of arbitrary data.) + ) + $(TR + $(TDNW $(LINK2 std_digest_sha.html, std.digest.sha)) + $(TD Compute SHA1 and SHA2 hashes of arbitrary data.) ) $(LEADINGROW Date & time) $(TR - $(TD - $(LINK2 std_datetime.html, std.datetime)$(BR) - $(LINK2 core_time.html, core.time) - ) - $(TD $(D std.datetime) provides convenient access to date and time - representations.$(BR) - $(D core.time) implements low-level time primitives. - ) + $(TDNW $(LINK2 std_datetime.html, std.datetime)) + $(TD Provides convenient access to date and time representations.) + ) + $(TR + $(TDNW $(LINK2 core_time.html, core.time)) + $(TD Implements low-level time primitives.) ) $(LEADINGROW Exception handling) $(TR - $(TD - $(LINK2 std_exception.html, std.exception)$(BR) - $(LINK2 core_exception.html, core.exception) - ) - $(TD $(D std.exception) implements routines related to exceptions. - $(D core.exception) defines built-in exception types and low-level - language hooks required by the compiler. - ) + $(TDNW $(LINK2 std_exception.html, std.exception)) + $(TD Implements routines related to exceptions.) + ) + $(TR + $(TDNW $(LINK2 core_exception.html, core.exception)) + $(TD Defines built-in exception types and low-level + language hooks required by the compiler.) ) $(LEADINGROW External library bindings) $(TR - $(TD - $(LINK2 etc_c_curl.html, etc.c.curl)$(BR) - $(LINK2 etc_c_sqlite3.html, etc.c.sqlite3)$(BR) - $(LINK2 etc_c_zlib.html, etc.c.zlib) - ) - $(TD Various bindings to external C libraries. - ) + $(TDNW $(LINK2 etc_c_curl.html, etc.c.curl)) + $(TD Interface to libcurl C library.) + ) + $(TR + $(TDNW $(LINK2 etc_c_odbc_sql.html, etc.c.odbc.sql)) + $(TD Interface to ODBC C library.) + ) + $(TR + $(TDNW $(LINK2 etc_c_odbc_sqlext.html, etc.c.odbc.sqlext)) + ) + $(TR + $(TDNW $(LINK2 etc_c_odbc_sqltypes.html, etc.c.odbc.sqltypes)) + ) + $(TR + $(TDNW $(LINK2 etc_c_odbc_sqlucode.html, etc.c.odbc.sqlucode)) + ) + $(TR + $(TDNW $(LINK2 etc_c_sqlite3.html, etc.c.sqlite3)) + $(TD Interface to SQLite C library.) + ) + $(TR + $(TDNW $(LINK2 etc_c_zlib.html, etc.c.zlib)) + $(TD Interface to zlib C library.) ) $(LEADINGROW I/O & File system) $(TR - $(TD - $(LINK2 std_file.html, std.file)$(BR) - $(LINK2 std_path.html, std.path)$(BR) - $(LINK2 std_stdio.html, std.stdio) + $(TDNW $(LINK2 std_file.html, std.file)) + $(TD Manipulate files and directories.) + ) + $(TR + $(TDNW $(LINK2 std_path.html, std.path)) + $(TD Manipulate strings that represent filesystem paths.) + ) + $(TR + $(TDNW $(LINK2 std_stdio.html, std.stdio)) + $(TD Perform buffered I/O.) + ) + $(LEADINGROW Interoperability) + $(TR + $(TDNW + $(LINK2 core_stdc_complex.html, core.stdc.complex)$(BR) + $(LINK2 core_stdc_ctype.html, core.stdc.ctype)$(BR) + $(LINK2 core_stdc_errno.html, core.stdc.errno)$(BR) + $(LINK2 core_stdc_fenv.html, core.stdc.fenv)$(BR) + $(LINK2 core_stdc_float_.html, core.stdc.float_)$(BR) + $(LINK2 core_stdc_inttypes.html, core.stdc.inttypes)$(BR) + $(LINK2 core_stdc_limits.html, core.stdc.limits)$(BR) + $(LINK2 core_stdc_locale.html, core.stdc.locale)$(BR) + $(LINK2 core_stdc_math.html, core.stdc.math)$(BR) + $(LINK2 core_stdc_signal.html, core.stdc.signal)$(BR) + $(LINK2 core_stdc_stdarg.html, core.stdc.stdarg)$(BR) + $(LINK2 core_stdc_stddef.html, core.stdc.stddef)$(BR) + $(LINK2 core_stdc_stdint.html, core.stdc.stdint)$(BR) + $(LINK2 core_stdc_stdio.html, core.stdc.stdio)$(BR) + $(LINK2 core_stdc_stdlib.html, core.stdc.stdlib)$(BR) + $(LINK2 core_stdc_string.html, core.stdc.string)$(BR) + $(LINK2 core_stdc_tgmath.html, core.stdc.tgmath)$(BR) + $(LINK2 core_stdc_time.html, core.stdc.time)$(BR) + $(LINK2 core_stdc_wchar_.html, core.stdc.wchar_)$(BR) + $(LINK2 core_stdc_wctype.html, core.stdc.wctype)$(BR) ) $(TD - $(D std.stdio) is the main module for I/O.$(BR) - $(D std.file) is for accessing the operating system's filesystem, - and $(D std.path) is for manipulating filesystem pathnames in a - platform-independent way.$(BR) - Note that $(D std.stream) and $(D std.cstream) are older, - deprecated modules scheduled to be replaced in the future; new - client code should avoid relying on them. + D bindings for standard C headers.$(BR)$(BR) + These are mostly undocumented, as documentation + for the functions these declarations provide + bindings to can be found on external resources. ) ) $(LEADINGROW Memory management) $(TR - $(TD - $(LINK2 core_memory.html, core.memory)$(BR) - $(LINK2 std_typecons.html, std.typecons)$(BR) - ) - $(TD - $(D core.memory) provides an API for user code to control the - built-in garbage collector.$(BR) - $(D std.typecons) contains primitives for building scoped variables - and reference-counted types. - ) + $(TDNW $(LINK2 core_memory.html, core.memory)) + $(TD Control the built-in garbage collector.) + ) + $(TR + $(TDNW $(LINK2 std_typecons.html, std.typecons)) + $(TD Build scoped variables and reference-counted types.) ) $(LEADINGROW Metaprogramming) $(TR - $(TD - $(LINK2 std_traits.html, std.traits)$(BR) - $(LINK2 std_typecons.html, std.typecons)$(BR) - $(LINK2 std_typetuple.html, std.typetuple)$(BR) - $(LINK2 core_demangle.html, core.demangle) - ) - $(TD - These modules provide the primitives for compile-time introspection - and metaprogramming. - ) + $(TDNW $(LINK2 core_attribute.html, core.attribute)) + $(TD Definitions of special attributes recognized by the compiler.) + ) + $(TR + $(TDNW $(LINK2 core_demangle.html, core.demangle)) + $(TD Convert $(I mangled) D symbol identifiers to source representation.) + ) + $(TR + $(TDNW $(LINK2 std_demangle.html, std.demangle)) + $(TD A simple wrapper around core.demangle.) + ) + $(TR + $(TDNW $(LINK2 std_meta.html, std.meta)) + $(TD Construct and manipulate template argument lists (aka type lists).) + ) + $(TR + $(TDNW $(LINK2 std_traits.html, std.traits)) + $(TD Extract information about types and symbols at compile time.) + ) + $(TR + $(TDNW $(LINK2 std_typecons.html, std.typecons)) + $(TD Construct new, useful general purpose types.) ) $(LEADINGROW Multitasking) $(TR - $(TD - $(LINK2 std_concurrency.html, std.concurrency)$(BR) - $(LINK2 std_parallelism.html, std.parallelism)$(BR) - $(LINK2 std_process.html, std.process)$(BR) - $(LINK2 core_atomic.html, core.atomic)$(BR) - $(LINK2 core_sync_barrier.html, core.sync.barrier)$(BR) - $(LINK2 core_sync_condition.html, core.sync.condition)$(BR) - $(LINK2 core_sync_exception.html, core.sync.exception)$(BR) - $(LINK2 core_sync_mutex.html, core.sync.mutex)$(BR) - $(LINK2 core_sync_rwmutex.html, core.sync.rwmutex)$(BR) - $(LINK2 core_sync_semaphore.html, core.sync.semaphore)$(BR) - $(LINK2 core_thread.html, core.thread) - ) - $(TD These modules provide primitives for concurrent processing, - multithreading, synchronization, and interacting with operating - system processes.$(BR) - - $(D core.atomic) provides primitives for lock-free concurrent - programming.$(BR) - - $(D core.sync.*) modules provide low-level concurrent - programming building blocks.$(BR) - - $(D core.thread) implements multithreading primitives. - ) + $(TDNW $(LINK2 std_concurrency.html, std.concurrency)) + $(TD Low level messaging API for threads.) + ) + $(TR + $(TDNW $(LINK2 std_parallelism.html, std.parallelism)) + $(TD High level primitives for SMP parallelism.) + ) + $(TR + $(TDNW $(LINK2 std_process.html, std.process)) + $(TD Starting and manipulating processes.) + ) + $(TR + $(TDNW $(LINK2 core_atomic.html, core.atomic)) + $(TD Basic support for lock-free concurrent programming.) + ) + $(TR + $(TDNW $(LINK2 core_sync_barrier.html, core.sync.barrier)) + $(TD Synchronize the progress of a group of threads.) + ) + $(TR + $(TDNW $(LINK2 core_sync_condition.html, core.sync.condition)) + $(TD Synchronized condition checking.) + ) + $(TR + $(TDNW $(LINK2 core_sync_exception.html, core.sync.exception)) + $(TD Base class for synchronization exceptions.) + ) + $(TR + $(TDNW $(LINK2 core_sync_mutex.html, core.sync.mutex)) + $(TD Mutex for mutually exclusive access.) + ) + $(TR + $(TDNW $(LINK2 core_sync_rwmutex.html, core.sync.rwmutex)) + $(TD Shared read access and mutually exclusive write access.) + ) + $(TR + $(TDNW $(LINK2 core_sync_semaphore.html, core.sync.semaphore)) + $(TD General use synchronization semaphore.) + ) + $(TR + $(TDNW $(LINK2 core_thread.html, core.thread)) + $(TD Thread creation and management.) ) $(LEADINGROW Networking) $(TR - $(TD - $(LINK2 std_socket.html, std.socket)$(BR) - $(LINK2 std_socketstream.html, std.socketstream)$(BR) - $(LINK2 std_net_curl.html, std.net.curl)$(BR) - $(LINK2 std_net_isemail.html, std.net.isemail) - ) - $(TD Utilities for networking. - ) + $(TDNW $(LINK2 std_socket.html, std.socket)) + $(TD Socket primitives.) + ) + $(TR + $(TDNW $(LINK2 std_net_curl.html, std.net.curl)) + $(TD Networking client functionality as provided by libcurl.) + ) + $(TR + $(TDNW $(LINK2 std_net_isemail.html, std.net.isemail)) + $(TD Validates an email address according to RFCs 5321, 5322 and others.) + ) + $(TR + $(TDNW $(LINK2 std_uri.html, std.uri)) + $(TD Encode and decode Uniform Resource Identifiers (URIs).) + ) + $(TR + $(TDNW $(LINK2 std_uuid.html, std.uuid)) + $(TD Universally-unique identifiers for resources in distributed + systems.) ) $(LEADINGROW Numeric) $(TR - $(TD - $(LINK2 std_bigint.html, std.bigint)$(BR) - $(LINK2 std_complex.html, std.complex)$(BR) - $(LINK2 std_math.html, std.math)$(BR) - $(LINK2 std_mathspecial.html, std.mathspecial)$(BR) - $(LINK2 std_numeric.html, std.numeric)$(BR) - $(LINK2 std_random.html, std.random) - ) - $(TD These modules provide the standard mathematical functions and - numerical algorithms.$(BR) - $(D std.bigint) provides an arbitrary-precision integer type.$(BR) - $(D std.complex) provides a complex number type.$(BR) - $(D std.random) provides pseudo-random number generators. - ) + $(TDNW $(LINK2 std_bigint.html, std.bigint)) + $(TD An arbitrary-precision integer type.) + ) + $(TR + $(TDNW $(LINK2 std_complex.html, std.complex)) + $(TD A complex number type.) + ) + $(TR + $(TDNW $(LINK2 std_math.html, std.math)) + $(TD Elementary mathematical functions (powers, roots, trigonometry).) + ) + $(TR + $(TDNW $(LINK2 std_mathspecial.html, std.mathspecial)) + $(TD Families of transcendental functions.) + ) + $(TR + $(TDNW $(LINK2 std_numeric.html, std.numeric)) + $(TD Floating point numerics functions.) + ) + $(TR + $(TDNW $(LINK2 std_random.html, std.random)) + $(TD Pseudo-random number generators.) + ) + $(TR + $(TDNW $(LINK2 core_checkedint.html, core.checkedint)) + $(TD Range-checking integral arithmetic primitives.) + ) + $(TR + $(TDNW $(LINK2 core_math.html, core.math)) + $(TD Built-in mathematical intrinsics.) ) $(LEADINGROW Paradigms) $(TR - $(TD - $(LINK2 std_functional.html, std.functional)$(BR) - $(LINK2 std_algorithm.html, std.algorithm)$(BR) - $(LINK2 std_signals.html, std.signals) - ) - $(TD $(D std.functional), along with the lazy algorithms of - $(D std.algorithm), provides utilities for writing functional-style - code in D.$(BR) - - $(D std.signals) provides a signal-and-slots framework for - event-driven programming. - ) + $(TDNW $(LINK2 std_functional.html, std.functional)) + $(TD Functions that manipulate other functions.) + ) + $(TR + $(TDNW $(LINK2 std_algorithm.html, std.algorithm)) + $(TD Generic algorithms for processing sequences.) + ) + $(TR + $(TDNW $(LINK2 std_signals.html, std.signals)) + $(TD Signal-and-slots framework for event-driven programming.) ) $(LEADINGROW Runtime utilities) $(TR - $(TD - $(LINK2 std_getopt.html, std.getopt)$(BR) - $(LINK2 std_compiler.html, std.compiler)$(BR) - $(LINK2 std_system.html, std.system)$(BR) - $(LINK2 core_cpuid.html, core.cpuid)$(BR) - $(LINK2 core_memory.html, core.memory)$(BR) - ) - $(TD Various modules for interacting with the execution environment and - compiler.$(BR) - $(D std.getopt) implements parsing of command-line arguments.$(BR) - $(D std.compiler) provides compiler information, mainly the - compiler vendor string and language version.$(BR) - $(D std.system) provides information about the runtime environment, - such as OS type and endianness.$(BR) - $(D core.cpuid) provides information on the capabilities of the - CPU the program is running on.$(BR) - $(D core.memory) allows user code to control the built-in garbage - collector. - ) + $(TDNW $(LINK2 object.html, object)) + $(TD Core language definitions. Automatically imported.) + ) + $(TR + $(TDNW $(LINK2 std_getopt.html, std.getopt)) + $(TD Parsing of command-line arguments.) + ) + $(TR + $(TDNW $(LINK2 std_compiler.html, std.compiler)) + $(TD Host compiler vendor string and language version.) + ) + $(TR + $(TDNW $(LINK2 std_system.html, std.system)) + $(TD Runtime environment, such as OS type and endianness.) + ) + $(TR + $(TDNW $(LINK2 core_cpuid.html, core.cpuid)) + $(TD Capabilities of the CPU the program is running on.) + ) + $(TR + $(TDNW $(LINK2 core_memory.html, core.memory)) + $(TD Control the built-in garbage collector.) + ) + $(TR + $(TDNW $(LINK2 core_runtime.html, core.runtime)) + $(TD Control and configure the D runtime.) ) $(LEADINGROW String manipulation) $(TR + $(TDNW $(LINK2 std_string.html, std.string)) + $(TD Algorithms that work specifically with strings.) + ) + $(TR + $(TDNW $(LINK2 std_array.html, std.array)) + $(TD Manipulate builtin arrays.) + ) + $(TR + $(TDNW $(LINK2 std_algorithm.html, std.algorithm)) + $(TD Generic algorithms for processing sequences.) + ) + $(TR + $(TDNW $(LINK2 std_uni.html, std.uni)) + $(TD Fundamental Unicode algorithms and data structures.) + ) + $(TR + $(TDNW $(LINK2 std_utf.html, std.utf)) + $(TD Encode and decode UTF-8, UTF-16 and UTF-32 strings.) + ) + $(TR + $(TDNW $(LINK2 std_format.html, std.format)) + $(TD Format data into strings.) + ) + $(TR + $(TDNW $(LINK2 std_path.html, std.path)) + $(TD Manipulate strings that represent filesystem paths.) + ) + $(TR + $(TDNW $(LINK2 std_regex.html, std.regex)) + $(TD Regular expressions.) + ) + $(TR + $(TDNW $(LINK2 std_ascii.html, std.ascii)) + $(TD Routines specific to the ASCII subset of Unicode.) + ) + $(TR + $(TDNW $(LINK2 std_encoding.html, std.encoding)) + $(TD Handle and transcode between various text encodings.) + ) + $(TR + $(TDNW $(LINK2 std_windows_charset.html, std.windows.charset)) + $(TD Windows specific character set support.) + ) + $(TR + $(TDNW $(LINK2 std_outbuffer.html, std.outbuffer)) + $(TD Serialize data to $(CODE ubyte) arrays.) + ) + $(LEADINGROW Type manipulations) + $(TR + $(TDNW $(LINK2 std_conv.html, std.conv)) + $(TD Convert types from one type to another.) + ) + $(TR + $(TDNW $(LINK2 std_typecons.html, std.typecons)) + $(TD Type constructors for scoped variables, ref counted types, etc.) + ) + $(TR + $(TDNW $(LINK2 std_bitmanip.html, std.bitmanip)) + $(TD High level bit level manipulation, bit arrays, bit fields.) + ) + $(TR + $(TDNW $(LINK2 std_variant.html, std.variant)) + $(TD Discriminated unions and algebraic types.) + ) + $(TR + $(TDNW $(LINK2 core_bitop.html, core.bitop)) + $(TD Low level bit manipulation.) + ) + $(LEADINGROW Vector programming) + $(TR + $(TDNW $(LINK2 core_simd.html, core.simd)) + $(TD SIMD intrinsics) + ) + +$(COMMENT + $(LEADINGROW Undocumented modules (intentionally omitted).) + $(TR + $(TDNW + $(LINK2 core_sync_config.html, core.sync.config)$(BR) + $(LINK2 std_concurrencybase.html, std.concurrencybase)$(BR) + $(LINK2 std_container_util.html, std.container.util)$(BR) + $(LINK2 std_regex_internal_backtracking.html, std.regex.internal.backtracking)$(BR) + $(LINK2 std_regex_internal_generator.html, std.regex.internal.generator)$(BR) + $(LINK2 std_regex_internal_ir.html, std.regex.internal.ir)$(BR) + $(LINK2 std_regex_internal_kickstart.html, std.regex.internal.kickstart)$(BR) + $(LINK2 std_regex_internal_parser.html, std.regex.internal.parser)$(BR) + $(LINK2 std_regex_internal_tests.html, std.regex.internal.tests)$(BR) + $(LINK2 std_regex_internal_thompson.html, std.regex.internal.thompson)$(BR) + $(LINK2 std_stdiobase.html, std.stdiobase)$(BR) + ) $(TD - $(LINK2 std_string.html, std.string)$(BR) - $(LINK2 std_array.html, std.array)$(BR) - $(LINK2 std_algorithm.html, std.algorithm)$(BR) - $(LINK2 std_uni.html, std.uni)$(BR) - $(LINK2 std_utf.html, std.utf)$(BR) - $(LINK2 std_format.html, std.format)$(BR) - $(LINK2 std_path.html, std.path)$(BR) - $(LINK2 std_regex.html, std.regex)$(BR) - $(LINK2 std_ascii.html, std.ascii)$(BR) - $(LINK2 std_encoding.html, std.encoding)$(BR) - $(LINK2 std_windows_charset.html, std.windows.charset) + Internal modules. ) - $(TD $(D std.string) contains functions that work specifically with - strings.$(BR) - - Many string manipulations are special cases of more generic - algorithms that work with general arrays, or generic ranges; these - are found in $(D std.array) and $(D std.algorithm).$(BR) - - D strings are encoded in Unicode; $(D std.uni) provides operations - that work with Unicode strings in general, while $(D std.utf) deals - with specific Unicode encodings and conversions between them.$(BR) - - $(D std.format) provides $(D printf)-style format string - formatting, with D's own improvements and extensions.$(BR) - - For manipulating filesystem pathnames, $(D std.path) is - provided.$(BR) - - $(D std.regex) is a very fast library for string matching and - substitution using regular expressions.$(BR) - - $(D std.ascii) provides routines specific to the ASCII subset of - Unicode. - - Windows-specific character set support is provided by - $(D std.windows.charset). - - Rudimentary support for other string encodings is provided by - $(D std.encoding). + ) + $(TR + $(TDNW + $(LINK2 core_vararg.html, core.vararg)$(BR) + $(LINK2 std_c_fenv.html, std.c.fenv)$(BR) + $(LINK2 std_c_linux_linux.html, std.c.linux_linux)$(BR) + $(LINK2 std_c_linux_socket.html, std.c.linux_socket)$(BR) + $(LINK2 std_c_locale.html, std.c.locale)$(BR) + $(LINK2 std_c_math.html, std.c.math)$(BR) + $(LINK2 std_c_process.html, std.c.process)$(BR) + $(LINK2 std_c_stdarg.html, std.c.stdarg)$(BR) + $(LINK2 std_c_stddef.html, std.c.stddef)$(BR) + $(LINK2 std_c_stdio.html, std.c.stdio)$(BR) + $(LINK2 std_c_stdlib.html, std.c.stdlib)$(BR) + $(LINK2 std_c_string.html, std.c.string)$(BR) + $(LINK2 std_c_time.html, std.c.time)$(BR) + $(LINK2 std_c_wcharh.html, std.c.wcharh)$(BR) + $(LINK2 std_stdint.html, std.stdint)$(BR) + ) + $(TDN + Redirect modules. ) ) - $(LEADINGROW Type manipulations) $(TR - $(TD - $(LINK2 std_conv.html, std.conv)$(BR) - $(LINK2 std_typecons.html, std.typecons)$(BR) - $(LINK2 std_bitmanip.html, std.bitmanip)$(BR) - $(LINK2 core_bitop.html, core.bitop)$(BR) + $(TDNW + $(LINK2 std_mmfile.html, std.mmfile)$(BR) + $(LINK2 std_typetuple.html, std.typetuple)$(BR) ) - $(TD $(D std.conv) provides powerful automatic conversions between - built-in types as well as user-defined types that implement - standard conversion primitives.$(BR) - - $(D std.typecons) provides various utilities for type construction - and compile-time type introspection. It provides facilities for - constructing scoped variables and reference-counted types, as well - as miscellaneous useful generic types such as tuples and - flags.$(BR) - - $(D std.bitmanip) provides various bit-level operations, bit - arrays, and bit fields. $(D core.bitop) provides low-level bit - manipulation primitives.$(BR) + $(TD + Deprecated modules. ) ) - $(LEADINGROW Vector programming) $(TR + $(TDNW + $(LINK2 std_experimental_logger.html, std.experimental.logger)$(BR) + $(LINK2 std_experimental_logger_core.html, std.experimental.logger.core)$(BR) + $(LINK2 std_experimental_logger_filelogger.html, std.experimental.logger.filelogger)$(BR) + $(LINK2 std_experimental_logger_multilogger.html, std.experimental.logger.multilogger)$(BR) + $(LINK2 std_experimental_logger_nulllogger.html, std.experimental.logger.nulllogger)$(BR) + ) $(TD - $(LINK2 core_simd.html, core.simd)$(BR) + Experimental modules. ) - $(TD The $(D core.simd) module provides access to SIMD intrinsics in - the compiler.) ) ) +) Macros: - TITLE=Phobos Runtime Library - WIKI=Phobos + TITLE=Phobos Runtime Library + DDOC_BLANKLINE= + _= diff --git a/osmodel.mak b/osmodel.mak new file mode 100644 index 00000000000..d0f160ae374 --- /dev/null +++ b/osmodel.mak @@ -0,0 +1,55 @@ +# This Makefile snippet detects the OS and the architecture MODEL +# Keep this file in sync between druntime, phobos, and dmd repositories! + +ifeq (,$(OS)) + uname_S:=$(shell uname -s) + ifeq (Darwin,$(uname_S)) + OS:=osx + endif + ifeq (Linux,$(uname_S)) + OS:=linux + endif + ifeq (FreeBSD,$(uname_S)) + OS:=freebsd + endif + ifeq (NetBSD,$(uname_S)) + OS:=netbsd + endif + ifeq (OpenBSD,$(uname_S)) + OS:=openbsd + endif + ifeq (Solaris,$(uname_S)) + OS:=solaris + endif + ifeq (SunOS,$(uname_S)) + OS:=solaris + endif + ifeq (,$(OS)) + $(error Unrecognized or unsupported OS for uname: $(uname_S)) + endif +endif + +# When running make from XCode it may set environment var OS=MACOS. +# Adjust it here: +ifeq (MACOS,$(OS)) + OS:=osx +endif + +ifeq (,$(MODEL)) + ifeq ($(OS), solaris) + uname_M:=$(shell isainfo -n) + else + uname_M:=$(shell uname -m) + endif + ifneq (,$(findstring $(uname_M),x86_64 amd64)) + MODEL:=64 + endif + ifneq (,$(findstring $(uname_M),i386 i586 i686)) + MODEL:=32 + endif + ifeq (,$(MODEL)) + $(error Cannot figure 32/64 model from uname -m: $(uname_M)) + endif +endif + +MODEL_FLAG:=-m$(MODEL) diff --git a/posix.mak b/posix.mak index 2909b31d702..2080712aa9c 100644 --- a/posix.mak +++ b/posix.mak @@ -13,6 +13,8 @@ # # make BUILD=debug unittest => builds all unittests (for debug) and runs them # +# make DEBUGGER=ddd std/XXXXX.debug => builds the module XXXXX and executes it in the debugger ddd +# # make html => makes html documentation # # make install => copies library to /usr/lib @@ -26,47 +28,14 @@ # OS can be linux, win32, win32wine, osx, or freebsd. The system will be # determined by using uname -QUIET:=@ +QUIET:= -OS:= -uname_S:=$(shell uname -s) -ifeq (Darwin,$(uname_S)) - OS:=osx -endif -ifeq (Linux,$(uname_S)) - OS:=linux -endif -ifeq (FreeBSD,$(uname_S)) - OS:=freebsd -endif -ifeq (OpenBSD,$(uname_S)) - OS:=openbsd -endif -ifeq (Solaris,$(uname_S)) - OS:=solaris -endif -ifeq (SunOS,$(uname_S)) - OS:=solaris -endif -ifeq (,$(OS)) - $(error Unrecognized or unsupported OS for uname: $(uname_S)) -endif +DEBUGGER=gdb -ifeq (,$(MODEL)) - ifeq ($(OS),solaris) - uname_M:=$(shell isainfo -n) - else - uname_M:=$(shell uname -m) - endif - ifneq (,$(findstring $(uname_M),x86_64 amd64)) - MODEL:=64 - endif - ifneq (,$(findstring $(uname_M),i386 i586 i686)) - MODEL:=32 - endif - ifeq (,$(MODEL)) - $(error Cannot figure 32/64 model from uname -m: $(uname_M)) - endif +include osmodel.mak + +ifeq (osx,$(OS)) + export MACOSX_DEPLOYMENT_TARGET=10.7 endif # Default to a release built, override with BUILD=debug @@ -91,6 +60,10 @@ DRUNTIME_PATH = ../druntime ZIPFILE = phobos.zip ROOT_OF_THEM_ALL = generated ROOT = $(ROOT_OF_THEM_ALL)/$(OS)/$(BUILD)/$(MODEL) +DUB=dub +GIT_HOME=https://github.com/dlang +TOOLS_DIR=../tools + # Documentation-related stuff DOCSRC = ../dlang.org WEBSITE_DIR = ../web @@ -101,7 +74,7 @@ SRC_DOCUMENTABLES = index.d $(addsuffix .d,$(STD_MODULES) \ STDDOC = $(DOCSRC)/html.ddoc $(DOCSRC)/dlang.org.ddoc $(DOCSRC)/std_navbar-prerelease.ddoc $(DOCSRC)/std.ddoc $(DOCSRC)/macros.ddoc $(DOCSRC)/.generated/modlist-prerelease.ddoc BIGSTDDOC = $(DOCSRC)/std_consolidated.ddoc $(DOCSRC)/macros.ddoc # Set DDOC, the documentation generator -DDOC=$(DMD) -conf= -m$(MODEL) -w -c -o- -version=StdDdoc \ +DDOC=$(DMD) -conf= $(MODEL_FLAG) -w -c -o- -version=StdDdoc \ -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS) # Set DRUNTIME name and full path @@ -109,8 +82,8 @@ ifneq (,$(DRUNTIME)) CUSTOM_DRUNTIME=1 endif ifeq (,$(findstring win,$(OS))) - DRUNTIME = $(DRUNTIME_PATH)/lib/libdruntime-$(OS)$(MODEL).a - DRUNTIMESO = $(basename $(DRUNTIME))so.a + DRUNTIME = $(DRUNTIME_PATH)/generated/$(OS)/$(BUILD)/$(MODEL)/libdruntime.a + DRUNTIMESO = $(basename $(DRUNTIME)).so.a else DRUNTIME = $(DRUNTIME_PATH)/lib/druntime.lib endif @@ -121,7 +94,7 @@ ifeq ($(OS),win32wine) DMD = wine dmd.exe RUN = wine else - DMD = ../dmd/src/dmd + DMD = ../dmd/generated/$(OS)/release/$(MODEL)/dmd ifeq ($(OS),win32) CC = dmc else @@ -131,24 +104,25 @@ else endif # Set CFLAGS -CFLAGS= -ifneq (,$(filter cc% gcc% clang% icc% egcc%, $(CC))) - CFLAGS += -m$(MODEL) -fPIC - ifeq ($(BUILD),debug) - CFLAGS += -g - else - CFLAGS += -O3 - endif +CFLAGS=$(MODEL_FLAG) -fPIC -DHAVE_UNISTD_H +ifeq ($(BUILD),debug) + CFLAGS += -g +else + CFLAGS += -O3 endif # Set DFLAGS -DFLAGS=-conf= -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS) -w -dip25 -m$(MODEL) $(PIC) +DFLAGS=-conf= -I$(DRUNTIME_PATH)/import $(DMDEXTRAFLAGS) -w -dip25 $(MODEL_FLAG) $(PIC) ifeq ($(BUILD),debug) DFLAGS += -g -debug else DFLAGS += -O -release endif +ifdef ENABLE_COVERAGE +DFLAGS += -cov +endif + # Set DOTOBJ and DOTEXE ifeq (,$(findstring win,$(OS))) DOTOBJ:=.o @@ -162,6 +136,9 @@ endif LINKDL:=$(if $(findstring $(OS),linux),-L-ldl,) +# use timelimit to avoid deadlocks if available +TIMELIMIT:=$(if $(shell which timelimit 2>/dev/null || true),timelimit -t 90 ,) + # Set VERSION, where the file is that contains the version string VERSION=../dmd/VERSION @@ -181,35 +158,51 @@ else LIB:=$(ROOT)/phobos.lib endif -LIBCURL_STUB:=$(if $(findstring $(OS),linux),$(ROOT)/libcurl_stub.so,) -LINKCURL:=$(if $(LIBCURL_STUB),-L$(LIBCURL_STUB),-L-lcurl) - ################################################################################ MAIN = $(ROOT)/emptymain.d -# Packages in std. Just mention the package name here and the actual files in -# the package in STD_MODULES. -STD_PACKAGES = $(addprefix std/, algorithm container experimental/logger \ - range regex) - -# Modules in std (including those in packages), in alphabetical order. -STD_MODULES = $(addprefix std/, \ - array ascii base64 bigint bitmanip compiler complex concurrency \ - $(addprefix container/, array binaryheap dlist rbtree slist util) \ - conv cstream csv datetime demangle \ - $(addprefix digest/, digest crc md ripemd sha) \ - encoding exception \ - $(addprefix experimental/logger/, core filelogger nulllogger multilogger) \ - file format functional getopt json math mathspecial \ - metastrings mmfile net/isemail net/curl numeric outbuffer parallelism path \ - process random \ - $(addprefix range/, primitives interfaces) \ - $(addprefix regex/, $(addprefix internal/,generator ir parser backtracking \ - kickstart tests thompson)) \ - signals socket socketstream stdint stdio stdiobase stream \ - string syserror system traits typecons typetuple uni uri utf uuid variant \ - xml zip zlib $(addprefix algorithm/,comparison iteration \ - mutation searching setops sorting)) +# Given one or more packages, returns the modules they contain +P2MODULES=$(foreach P,$1,$(addprefix $P/,$(PACKAGE_$(subst /,_,$P)))) + +# Packages in std. Just mention the package name here. The contents of package +# xy/zz is in variable PACKAGE_xy_zz. This allows automation in iterating +# packages and their modules. +STD_PACKAGES = std $(addprefix std/,\ + algorithm container datetime digest experimental/allocator \ + experimental/allocator/building_blocks experimental/logger \ + net \ + experimental range regex) + +# Modules broken down per package + +PACKAGE_std = array ascii base64 bigint bitmanip compiler complex concurrency \ + conv csv demangle encoding exception file format \ + functional getopt json math mathspecial meta mmfile numeric \ + outbuffer parallelism path process random signals socket stdint \ + stdio string system traits typecons typetuple uni \ + uri utf uuid variant xml zip zlib +PACKAGE_std_experimental = checkedint typecons +PACKAGE_std_algorithm = comparison iteration mutation package searching setops \ + sorting +PACKAGE_std_container = array binaryheap dlist package rbtree slist util +PACKAGE_std_datetime = date interval package stopwatch systime timezone +PACKAGE_std_digest = crc digest hmac md murmurhash ripemd sha +PACKAGE_std_experimental_logger = core filelogger \ + nulllogger multilogger package +PACKAGE_std_experimental_allocator = \ + common gc_allocator mallocator mmap_allocator package showcase typed +PACKAGE_std_experimental_allocator_building_blocks = \ + affix_allocator allocator_list bucketizer \ + fallback_allocator free_list free_tree bitmapped_block \ + kernighan_ritchie null_allocator package quantizer \ + region scoped_allocator segregator stats_collector +PACKAGE_std_net = curl isemail +PACKAGE_std_range = interfaces package primitives +PACKAGE_std_regex = package $(addprefix internal/,generator ir parser \ + backtracking tests thompson kickstart) + +# Modules in std (including those in packages) +STD_MODULES=$(call P2MODULES,$(STD_PACKAGES)) # OS-specific D modules EXTRA_MODULES_LINUX := $(addprefix std/c/linux/, linux socket) @@ -217,61 +210,70 @@ EXTRA_MODULES_OSX := $(addprefix std/c/osx/, socket) EXTRA_MODULES_FREEBSD := $(addprefix std/c/freebsd/, socket) EXTRA_MODULES_WIN32 := $(addprefix std/c/windows/, com stat windows \ winsock) $(addprefix std/windows/, charset iunknown syserror) -ifeq (,$(findstring win,$(OS))) - EXTRA_DOCUMENTABLES:=$(EXTRA_MODULES_LINUX) -else - EXTRA_DOCUMENTABLES:=$(EXTRA_MODULES_WIN32) -endif # Other D modules that aren't under std/ -EXTRA_DOCUMENTABLES += $(addprefix etc/c/,curl sqlite3 zlib) $(addprefix \ -std/c/, fenv locale math process stdarg stddef stdio stdlib string \ -time wcharh) -EXTRA_MODULES += $(EXTRA_DOCUMENTABLES) $(addprefix \ - std/internal/digest/, sha_SSSE3 ) $(addprefix \ - std/internal/math/, biguintcore biguintnoasm biguintx86 \ - gammafunction errorfunction) $(addprefix std/internal/, \ - cstring processinit unicode_tables scopebuffer\ - unicode_comp unicode_decomp unicode_grapheme unicode_norm) \ - $(addprefix std/internal/test/, dummyrange) \ - $(addprefix std/algorithm/, internal) +EXTRA_MODULES_COMMON := $(addprefix etc/c/,curl odbc/sql odbc/sqlext \ + odbc/sqltypes odbc/sqlucode sqlite3 zlib) $(addprefix std/c/,fenv locale \ + math process stdarg stddef stdio stdlib string time wcharh) + +EXTRA_DOCUMENTABLES := $(EXTRA_MODULES_LINUX) $(EXTRA_MODULES_WIN32) $(EXTRA_MODULES_COMMON) + +EXTRA_MODULES_INTERNAL := $(addprefix std/, \ + algorithm/internal concurrencybase \ + $(addprefix internal/, \ + cstring digest/sha_SSSE3 encodinginit \ + $(addprefix math/, biguintcore biguintnoasm biguintx86 \ + errorfunction gammafunction ) \ + processinit scopebuffer test/dummyrange \ + $(addprefix unicode_, comp decomp grapheme norm tables) \ + ) \ + stdiobase \ +) + +EXTRA_MODULES += $(EXTRA_DOCUMENTABLES) $(EXTRA_MODULES_INTERNAL) # Aggregate all D modules relevant to this build -D_MODULES = $(STD_MODULES) $(EXTRA_MODULES) \ - $(addsuffix /package,$(STD_PACKAGES)) +D_MODULES = $(STD_MODULES) $(EXTRA_MODULES) # Add the .d suffix to the module names D_FILES = $(addsuffix .d,$(D_MODULES)) # Aggregate all D modules over all OSs (this is for the zip file) -ALL_D_FILES = $(addsuffix .d, $(D_MODULES) \ +ALL_D_FILES = $(addsuffix .d, $(STD_MODULES) $(EXTRA_MODULES_COMMON) \ $(EXTRA_MODULES_LINUX) $(EXTRA_MODULES_OSX) $(EXTRA_MODULES_FREEBSD) \ - $(EXTRA_MODULES_WIN32)) std/internal/windows/advapi32.d \ + $(EXTRA_MODULES_WIN32) $(EXTRA_MODULES_INTERNAL)) \ + std/internal/windows/advapi32.d \ std/windows/registry.d std/c/linux/pthread.d std/c/linux/termios.d \ std/c/linux/tipc.d # C files to be part of the build C_MODULES = $(addprefix etc/c/zlib/, adler32 compress crc32 deflate \ gzclose gzlib gzread gzwrite infback inffast inflate inftrees trees uncompr zutil) -C_FILES = $(addsuffix .c,$(C_MODULES)) -# C files that are not compiled (right now only zlib-related) -C_EXTRAS = $(addprefix etc/c/zlib/, algorithm.txt ChangeLog crc32.h \ -deflate.h example.c inffast.h inffixed.h inflate.h inftrees.h \ -linux.mak minigzip.c osx.mak README trees.h win32.mak zconf.h \ -win64.mak \ -gzguts.h zlib.3 zlib.h zutil.h) -# Aggregate all C files over all OSs (this is for the zip file) -ALL_C_FILES = $(C_FILES) $(C_EXTRAS) OBJS = $(addsuffix $(DOTOBJ),$(addprefix $(ROOT)/,$(C_MODULES))) MAKEFILE = $(firstword $(MAKEFILE_LIST)) +# build with shared library support (defaults to true on supported platforms) +SHARED=$(if $(findstring $(OS),linux freebsd),1,) + +# Check for missing imports in public unittest examples. +# A blacklist of ignored module is provided as not all public unittest in +# Phobos are independently runnable yet +IGNORED_PUBLICTESTS= $(addprefix std/, \ + base64 $(addprefix experimental/allocator/, \ + building_blocks/free_list building_blocks/quantizer \ + ) digest/hmac \ + file math stdio traits typecons uuid) +PUBLICTESTS= $(addsuffix .publictests,$(filter-out $(IGNORED_PUBLICTESTS), $(D_MODULES))) +TEST_EXTRACTOR=$(TOOLS_DIR)/styles/test_extractor +PUBLICTESTS_DIR=$(ROOT)/publictests + ################################################################################ # Rules begin here ################################################################################ # Main target (builds the dll on linux, too) -ifeq (linux,$(OS)) +ifeq (1,$(SHARED)) all : lib dll else all : lib @@ -290,10 +292,6 @@ unittest-%: $(MAKE) -f $(MAKEFILE) unittest OS=$(OS) MODEL=$(MODEL) DMD=$(DMD) BUILD=$* endif -depend: $(addprefix $(ROOT)/unittest/,$(addsuffix .deps,$(D_MODULES))) - --include $(addprefix $(ROOT)/unittest/,$(addsuffix .deps,$(D_MODULES))) - ################################################################################ # Patterns begin here ################################################################################ @@ -316,13 +314,8 @@ $(ROOT)/$(SONAME): $(LIBSO) ln -sf $(notdir $(LIBSO)) $@ $(LIBSO): override PIC:=-fPIC -$(LIBSO): $(OBJS) $(ALL_D_FILES) $(DRUNTIMESO) $(LIBCURL_STUB) - $(DMD) $(DFLAGS) -shared -debuglib= -defaultlib= -of$@ -L-soname=$(SONAME) $(DRUNTIMESO) $(LINKDL) $(LINKCURL) $(D_FILES) $(OBJS) - -# stub library with soname of the real libcurl.so (Bugzilla 10710) -$(LIBCURL_STUB): - @echo "void curl_global_init() {}" > $(ROOT)/libcurl_stub.c - $(CC) -shared $(CFLAGS) $(ROOT)/libcurl_stub.c -o $@ -Wl,-soname=libcurl.so.4 +$(LIBSO): $(OBJS) $(ALL_D_FILES) $(DRUNTIMESO) + $(DMD) $(DFLAGS) -shared -debuglib= -defaultlib= -of$@ -L-soname=$(SONAME) $(DRUNTIMESO) $(LINKDL) $(D_FILES) $(OBJS) ifeq (osx,$(OS)) # Build fat library that combines the 32 bit and the 64 bit libraries @@ -343,20 +336,18 @@ $(addprefix $(ROOT)/unittest/,$(DISABLED_TESTS)) : @echo Testing $@ - disabled UT_D_OBJS:=$(addprefix $(ROOT)/unittest/,$(addsuffix .o,$(D_MODULES))) +# need to recompile all unittest objects whenever sth. changes +$(UT_D_OBJS): $(ALL_D_FILES) $(UT_D_OBJS): $(ROOT)/unittest/%.o: %.d @mkdir -p $(dir $@) - $(DMD) $(DFLAGS) -unittest -c -of$@ -deps=$(@:.o=.deps.tmp) $< - @echo $@: `sed 's|.*(\(.*\)).*|\1|' $(@:.o=.deps.tmp) | sort | uniq` \ - >$(@:.o=.deps) - @rm $(@:.o=.deps.tmp) -# $(DMD) $(DFLAGS) -unittest -c -of$@ $*.d + $(DMD) $(DFLAGS) -unittest -c -of$@ $< -ifneq (linux,$(OS)) +ifneq (1,$(SHARED)) $(UT_D_OBJS): $(DRUNTIME) $(ROOT)/unittest/test_runner: $(DRUNTIME_PATH)/src/test_runner.d $(UT_D_OBJS) $(OBJS) $(DRUNTIME) - $(DMD) $(DFLAGS) -unittest -of$@ $(DRUNTIME_PATH)/src/test_runner.d $(UT_D_OBJS) $(OBJS) $(DRUNTIME) $(LINKCURL) -defaultlib= -debuglib= + $(DMD) $(DFLAGS) -unittest -of$@ $(DRUNTIME_PATH)/src/test_runner.d $(UT_D_OBJS) $(OBJS) $(DRUNTIME) $(LINKDL) -defaultlib= -debuglib= else @@ -365,8 +356,8 @@ UT_LIBSO:=$(ROOT)/unittest/libphobos2-ut.so $(UT_D_OBJS): $(DRUNTIMESO) $(UT_LIBSO): override PIC:=-fPIC -$(UT_LIBSO): $(UT_D_OBJS) $(OBJS) $(DRUNTIMESO) $(LIBCURL_STUB) - $(DMD) $(DFLAGS) -shared -unittest -of$@ $(UT_D_OBJS) $(OBJS) $(DRUNTIMESO) $(LINKDL) $(LINKCURL) -defaultlib= -debuglib= +$(UT_LIBSO): $(UT_D_OBJS) $(OBJS) $(DRUNTIMESO) + $(DMD) $(DFLAGS) -shared -unittest -of$@ $(UT_D_OBJS) $(OBJS) $(DRUNTIMESO) $(LINKDL) -defaultlib= -debuglib= $(ROOT)/unittest/test_runner: $(DRUNTIME_PATH)/src/test_runner.d $(UT_LIBSO) $(DMD) $(DFLAGS) -of$@ $< -L$(UT_LIBSO) -defaultlib= -debuglib= @@ -378,11 +369,38 @@ moduleName=$(subst /,.,$(1)) # target for batch unittests (using shared phobos library and test_runner) unittest/%.run : $(ROOT)/unittest/test_runner - $(QUIET)$(RUN) $< $(call moduleName,$*) + $(QUIET)$(TIMELIMIT)$(RUN) $< $(call moduleName,$*) -# target for quickly running a single unittest (using static phobos library) +# Target for quickly running a single unittest (using static phobos library). +# For example: "make std/algorithm/mutation.test" +# The mktemp business is needed so .o files don't clash in concurrent unittesting. %.test : %.d $(LIB) - $(DMD) $(DFLAGS) -main -unittest $(LIB) -defaultlib= -debuglib= -L-lcurl -run $< + T=`mktemp -d /tmp/.dmd-run-test.XXXXXX` && \ + ( \ + $(DMD) -od$$T $(DFLAGS) -main -unittest $(LIB) -defaultlib= -debuglib= $(LINKDL) -cov -run $< ; \ + RET=$$? ; rm -rf $$T ; exit $$RET \ + ) + +# Target for quickly unittesting all modules and packages within a package, +# transitively. For example: "make std/algorithm.test" +%.test : $(LIB) + $(MAKE) -f $(MAKEFILE) $(addsuffix .test,$(patsubst %.d,%,$(wildcard $*/*))) + +# Recursive target for %.debug +# It has to be recursive as %.debug depends on $(LIB) and we don't want to +# force the user to call make with BUILD=debug. +# Therefore we call %.debug_with_debugger and pass BUILD=debug from %.debug +# This forces all of phobos to have debug symbols, which we need as we don't +# know where debugging is leading us. +%.debug_with_debugger : %.d $(LIB) + $(DMD) $(DFLAGS) -main -unittest $(LIB) -defaultlib= -debuglib= $(LINKDL) $< + $(DEBUGGER) ./$(basename $(notdir $<)) + +# Target for quickly debugging a single module +# For example: make -f posix.mak DEBUGGER=ddd std/format.debug +# ddd in this case is a graphical frontend to gdb +%.debug : %.d + BUILD=debug $(MAKE) -f $(MAKEFILE) $(basename $<).debug_with_debugger ################################################################################ # More stuff @@ -398,14 +416,18 @@ unittest/%.run : $(ROOT)/unittest/test_runner clean : rm -rf $(ROOT_OF_THEM_ALL) $(ZIPFILE) $(DOC_OUTPUT_DIR) +gitzip: + git archive --format=zip HEAD > $(ZIPFILE) + zip : - zip $(ZIPFILE) $(MAKEFILE) $(ALL_D_FILES) $(ALL_C_FILES) win32.mak win64.mak + -rm -f $(ZIPFILE) + zip -r $(ZIPFILE) . -x .git\* -x generated\* install2 : all $(eval lib_dir=$(if $(filter $(OS),osx), lib, lib$(MODEL))) mkdir -p $(INSTALL_DIR)/$(OS)/$(lib_dir) cp $(LIB) $(INSTALL_DIR)/$(OS)/$(lib_dir)/ -ifneq (,$(findstring $(OS),linux)) +ifeq (1,$(SHARED)) cp -P $(LIBSO) $(INSTALL_DIR)/$(OS)/$(lib_dir)/ ln -sf $(notdir $(LIBSO)) $(INSTALL_DIR)/$(OS)/$(lib_dir)/libphobos2.so endif @@ -422,7 +444,7 @@ else # to always invoke druntime's make. Use FORCE instead of .PHONY to # avoid rebuilding phobos when $(DRUNTIME) didn't change. $(DRUNTIME): FORCE - $(MAKE) -C $(DRUNTIME_PATH) -f posix.mak MODEL=$(MODEL) DMD=$(DMD) OS=$(OS) + $(MAKE) -C $(DRUNTIME_PATH) -f posix.mak MODEL=$(MODEL) DMD=$(DMD) OS=$(OS) BUILD=$(BUILD) ifeq (,$(findstring win,$(OS))) $(DRUNTIMESO): $(DRUNTIME) @@ -432,17 +454,20 @@ FORCE: endif +JSON = phobos.json +json : $(JSON) +$(JSON) : $(ALL_D_FILES) + $(DMD) $(DFLAGS) -o- -Xf$@ $^ + ########################################################### # html documentation -# Package to html, e.g. std/algorithm -> std_algorithm.html -P2HTML=$(addsuffix .html,$(subst /,_,$1)) # D file to html, e.g. std/conv.d -> std_conv.html -D2HTML=$(subst /,_,$(subst .d,.html,$1)) +# But "package.d" is special cased: std/range/package.d -> std_range.html +D2HTML=$(foreach p,$1,$(if $(subst package.d,,$(notdir $p)),$(subst /,_,$(subst .d,.html,$p)),$(subst /,_,$(subst /package.d,.html,$p)))) HTMLS=$(addprefix $(DOC_OUTPUT_DIR)/, \ - $(call D2HTML, $(SRC_DOCUMENTABLES)) \ - $(call P2HTML, $(STD_PACKAGES))) + $(call D2HTML, $(SRC_DOCUMENTABLES))) BIGHTMLS=$(addprefix $(BIGDOC_OUTPUT_DIR)/, \ $(call D2HTML, $(SRC_DOCUMENTABLES))) @@ -455,16 +480,10 @@ $(foreach p,$(SRC_DOCUMENTABLES),$(eval \ $(DOC_OUTPUT_DIR)/$(call D2HTML,$p) : $p $(STDDOC) ;\ $(DDOC) project.ddoc $(STDDOC) -Df$$@ $$<)) -# For each package, define a rule e.g.: -# ../web/phobos/std_algorithm.html : std/algorithm/package.d $(STDDOC) ; ... -$(foreach p,$(STD_PACKAGES),$(eval \ -$(DOC_OUTPUT_DIR)/$(call P2HTML,$p) : $p/package.d $(STDDOC) ;\ - $(DDOC) project.ddoc $(STDDOC) -Df$$@ $$<)) - html : $(DOC_OUTPUT_DIR)/. $(HTMLS) $(STYLECSS_TGT) allmod : - @echo $(SRC_DOCUMENTABLES) $(addsuffix /package.d,$(STD_PACKAGES)) + @echo $(SRC_DOCUMENTABLES) rsync-prerelease : html rsync -avz $(DOC_OUTPUT_DIR)/ d-programming@digitalmars.com:data/phobos-prerelease/ @@ -473,8 +492,123 @@ rsync-prerelease : html html_consolidated : $(DDOC) -Df$(DOCSRC)/std_consolidated_header.html $(DOCSRC)/std_consolidated_header.dd $(DDOC) -Df$(DOCSRC)/std_consolidated_footer.html $(DOCSRC)/std_consolidated_footer.dd - $(MAKE) DOC_OUTPUT_DIR=$(BIGDOC_OUTPUT_DIR) STDDOC=$(BIGSTDDOC) html -j 8 + $(MAKE) -f $(MAKEFILE) DOC_OUTPUT_DIR=$(BIGDOC_OUTPUT_DIR) STDDOC=$(BIGSTDDOC) html -j 8 cat $(DOCSRC)/std_consolidated_header.html $(BIGHTMLS) \ $(DOCSRC)/std_consolidated_footer.html > $(DOC_OUTPUT_DIR)/std_consolidated.html +changelog.html: changelog.dd + $(DMD) -Df$@ $< + +################################################################################ +# Automatically create dlang/tools repository if non-existent +################################################################################ + +${TOOLS_DIR}: + git clone --depth=1 ${GIT_HOME}/$(@F) $@ +$(TOOLS_DIR)/checkwhitespace.d: | $(TOOLS_DIR) +$(TOOLS_DIR)/styles/tests_extractor.d: | $(TOOLS_DIR) +$(TOOLS_DIR)/styles/has_public_example.d: | $(TOOLS_DIR) + +#################### test for undesired white spaces ########################## +CWS_TOCHECK = posix.mak win32.mak win64.mak osmodel.mak +CWS_TOCHECK += $(ALL_D_FILES) index.d + +checkwhitespace: $(LIB) $(TOOLS_DIR)/checkwhitespace.d + $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -run $(TOOLS_DIR)/checkwhitespace.d $(CWS_TOCHECK) + ############################# +# Submission to Phobos are required to conform to the DStyle +# The tests below automate some, but not all parts of the DStyle guidelines. +# See also: http://dlang.org/dstyle.html +############################# + +../dscanner: + git clone https://github.com/Hackerpilot/Dscanner ../dscanner + git -C ../dscanner checkout tags/v0.4.0 + git -C ../dscanner submodule update --init --recursive + +../dscanner/dsc: ../dscanner + # debug build is faster, but disable 'missing import' messages (missing core from druntime) + sed 's/dparse_verbose/StdLoggerDisableWarning/' ../dscanner/makefile > dscanner_makefile_tmp + mv dscanner_makefile_tmp ../dscanner/makefile + make -C ../dscanner githash debug + +style: has_public_example publictests style_lint + +style_lint: ../dscanner/dsc $(LIB) + @echo "Check for trailing whitespace" + grep -nr '[[:blank:]]$$' etc std ; test $$? -eq 1 + + @echo "Enforce whitespace before opening parenthesis" + grep -nrE "(for|foreach|foreach_reverse|if|while|switch|catch)\(" $$(find etc std -name '*.d') ; test $$? -eq 1 + + @echo "Enforce whitespace between colon(:) for import statements (doesn't catch everything)" + grep -nr 'import [^/,=]*:.*;' $$(find etc std -name '*.d') | grep -vE "import ([^ ]+) :\s"; test $$? -eq 1 + + @echo "Check for package wide std.algorithm imports" + grep -nr 'import std.algorithm : ' $$(find etc std -name '*.d') ; test $$? -eq 1 + + @echo "Enforce Allman style" + grep -nrE '(if|for|foreach|foreach_reverse|while|unittest|switch|else|version) .*{$$' $$(find etc std -name '*.d'); test $$? -eq 1 + + @echo "Enforce do { to be in Allman style" + grep -nr 'do *{$$' $$(find etc std -name '*.d') ; test $$? -eq 1 + + @echo "Enforce no space between assert and the opening brace, i.e. assert(" + grep -nrE 'assert +\(' $$(find etc std -name '*.d') ; test $$? -eq 1 + + @echo "Enforce space after cast(...)" + grep -nrE '[^"]cast\([^)]*?\)[[:alnum:]]' $$(find etc std -name '*.d') ; test $$? -eq 1 + + @echo "Enforce space between a .. b" + grep -nrE '[[:alnum:]][.][.][[:alnum:]]|[[:alnum:]] [.][.][[:alnum:]]|[[:alnum:]][.][.] [[:alnum:]]' $$(find etc std -name '*.d' | grep -vE 'std/string.d|std/uni.d') ; test $$? -eq 1 + + @echo "Enforce space between binary operators" + grep -nrE "[[:alnum:]](==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]] (==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]](==|!=|<=|<<|>>|>>>|^^) [[:alnum:]]" $$(find etc std -name '*.d'); test $$? -eq 1 + + @echo "Validate changelog files (Do _not_ use REF in the title!)" + @for file in $$(find changelog -name '*.dd') ; do \ + cat $$file | head -n1 | grep -nqE '\$$\((REF|LINK2|HTTP|MREF)' && \ + { echo "$$file: The title line can't contain links - it's already a link" && exit 1; } ;\ + cat $$file | head -n2 | tail -n1 | grep -q '^$$' || \ + { echo "$$file: After the title line an empty, separating line is expected" && exit 1; } ;\ + cat $$file | head -n3 | tail -n1 | grep -nqE '^.{1,}$$' || \ + { echo "$$file: The title is supposed to be followed by a long description" && exit 1; } ;\ + done + + @echo "Check that Ddoc runs without errors" + $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -w -D -Df/dev/null -main -c -o- $$(find etc std -type f -name '*.d') 2>&1 | grep -v "Deprecation:"; test $$? -eq 1 + + # at the moment libdparse has problems to parse some modules (->excludes) + @echo "Running DScanner" + ../dscanner/dsc --config .dscanner.ini --styleCheck $$(find etc std -type f -name '*.d' | grep -vE 'std/traits.d|std/typecons.d') -I. + +################################################################################ +# Check for missing imports in public unittest examples. +################################################################################ +publictests: $(PUBLICTESTS) + +$(TEST_EXTRACTOR): $(TOOLS_DIR)/styles/tests_extractor.d + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) build --compiler=$${PWD}/$(DMD) --root=$(TOOLS_DIR)/styles -c tests_extractor + +################################################################################ +# Extract public tests of a module and test them in an separate file (i.e. without its module) +# This is done to check for potentially missing imports in the examples, e.g. +# make -f posix.mak std/format.publictests +################################################################################ +%.publictests: %.d $(LIB) $(TEST_EXTRACTOR) + @$(TEST_EXTRACTOR) --inputdir $< --outputdir $(PUBLICTESTS_DIR) + @$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -main -unittest -run $(PUBLICTESTS_DIR)/$(subst /,_,$<) + +has_public_example: $(LIB) + # checks whether public function have public examples (for now some modules are excluded) + rm -rf ./out + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/stopwatch.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" + +.PHONY : auto-tester-build +auto-tester-build: all checkwhitespace + +.PHONY : auto-tester-test +auto-tester-test: unittest + +.DELETE_ON_ERROR: # GNU Make directive (delete output files on error) diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index e4a8c86c773..25da1c94d76 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -1,12 +1,11 @@ // Written in the D programming language. /** -This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). +This is a submodule of $(MREF std, algorithm). It contains generic _comparison algorithms. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE Cheat Sheet, - $(TR $(TH Function Name) $(TH Description)) - $(T2 among, Checks if a value is among a set of values, e.g. $(D if (v.among(1, 2, 3)) // `v` is 1, 2 or 3)) @@ -17,16 +16,25 @@ $(T2 clamp, $(T2 cmp, $(D cmp("abc", "abcd")) is $(D -1), $(D cmp("abc", "aba")) is $(D 1), and $(D cmp("abc", "abc")) is $(D 0).) +$(T2 either, + Return first parameter $(D p) that passes an $(D if (p)) test, e.g. + $(D either(0, 42, 43)) returns $(D 42).) $(T2 equal, Compares ranges for element-by-element equality, e.g. $(D equal([1, 2, 3], [1.0, 2.0, 3.0])) returns $(D true).) +$(T2 isPermutation, + $(D isPermutation([1, 2], [2, 1])) returns $(D true).) +$(T2 isSameLength, + $(D isSameLength([1, 2, 3], [4, 5, 6])) returns $(D true).) $(T2 levenshteinDistance, $(D levenshteinDistance("kitten", "sitting")) returns $(D 3) by using - the $(LUCKY Levenshtein distance _algorithm).) + the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, + Levenshtein distance _algorithm).) $(T2 levenshteinDistanceAndPath, $(D levenshteinDistanceAndPath("kitten", "sitting")) returns - $(D tuple(3, "snnnsni")) by using the $(LUCKY Levenshtein distance - _algorithm).) + $(D tuple(3, "snnnsni")) by using the + $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, + Levenshtein distance _algorithm).) $(T2 max, $(D max(3, 4, 2)) returns $(D 4).) $(T2 min, @@ -39,9 +47,9 @@ $(T2 predSwitch, Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_comparison.d) @@ -55,7 +63,8 @@ import std.functional; // : unaryFun, binaryFun; import std.range.primitives; import std.traits; // FIXME -import std.typecons; // : tuple, Tuple; +import std.typecons; // : tuple, Tuple, Flag, Yes; +import std.meta : allSatisfy; /** Find $(D value) _among $(D values), returning the 1-based index @@ -73,12 +82,12 @@ Returns: found value plus one is returned. See_Also: -$(LREF find) and $(LREF canFind) for finding a value in a +$(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a range. */ uint among(alias pred = (a, b) => a == b, Value, Values...) (Value value, Values values) - if (Values.length != 0) +if (Values.length != 0) { foreach (uint i, ref v; values) { @@ -90,7 +99,7 @@ uint among(alias pred = (a, b) => a == b, Value, Values...) /// Ditto template among(values...) - if (isExpressionTuple!values) +if (isExpressionTuple!values) { uint among(Value)(Value value) if (!is(CommonType!(Value, values) == void)) @@ -132,7 +141,7 @@ efficient search, but one that only supports matching on equality: @safe unittest { - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; if (auto pos = 3.among(1, 2, 3)) assert(pos == 3); @@ -144,7 +153,7 @@ efficient search, but one that only supports matching on equality: assert(position); assert(position == 1); - alias values = TypeTuple!("foo", "bar", "baz"); + alias values = AliasSeq!("foo", "bar", "baz"); auto arr = [values]; assert(arr[0 .. "foo".among(values)] == ["foo"]); assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]); @@ -169,8 +178,8 @@ efficient search, but one that only supports matching on equality: // in a tuple. private template indexOfFirstOvershadowingChoiceOnLast(choices...) { - alias firstParameterTypes = ParameterTypeTuple!(choices[0]); - alias lastParameterTypes = ParameterTypeTuple!(choices[$ - 1]); + alias firstParameterTypes = Parameters!(choices[0]); + alias lastParameterTypes = Parameters!(choices[$ - 1]); static if (lastParameterTypes.length == 0) { @@ -221,6 +230,7 @@ Params: handlers that accept one argument. There can also be a choice that accepts zero arguments. That choice will be invoked if the $(D switchObject) is null. + switchObject = the object against which the tests are being made. Returns: The value of the selected choice. @@ -230,30 +240,29 @@ Note: $(D castSwitch) can only be used with object types. auto castSwitch(choices...)(Object switchObject) { import core.exception : SwitchError; + import std.format : format; // Check to see if all handlers return void. - enum areAllHandlersVoidResult={ - foreach(index, choice; choices) + enum areAllHandlersVoidResult = { + bool result = true; + foreach (index, choice; choices) { - if(!is(ReturnType!choice == void)) - { - return false; - } + result &= is(ReturnType!choice == void); } - return true; + return result; }(); if (switchObject !is null) { // Checking for exact matches: - ClassInfo classInfo = typeid(switchObject); + const classInfo = typeid(switchObject); foreach (index, choice; choices) { static assert(isCallable!choice, "A choice handler must be callable"); - alias choiceParameterTypes = ParameterTypeTuple!choice; + alias choiceParameterTypes = Parameters!choice; static assert(choiceParameterTypes.length <= 1, "A choice handler can not have more than one argument."); @@ -265,15 +274,15 @@ auto castSwitch(choices...)(Object switchObject) // Check for overshadowing: immutable indexOfOvershadowingChoice = - indexOfFirstOvershadowingChoiceOnLast!(choices[0..index + 1]); + indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); static assert(indexOfOvershadowingChoice == index, "choice number %d(type %s) is overshadowed by choice number %d(type %s)".format( index + 1, CastClass.stringof, indexOfOvershadowingChoice + 1, - ParameterTypeTuple!(choices[indexOfOvershadowingChoice])[0].stringof)); + Parameters!(choices[indexOfOvershadowingChoice])[0].stringof)); if (classInfo == typeid(CastClass)) { - static if(is(ReturnType!(choice) == void)) + static if (is(ReturnType!(choice) == void)) { choice(cast(CastClass) switchObject); static if (areAllHandlersVoidResult) @@ -296,12 +305,12 @@ auto castSwitch(choices...)(Object switchObject) // Checking for derived matches: foreach (choice; choices) { - alias choiceParameterTypes = ParameterTypeTuple!choice; + alias choiceParameterTypes = Parameters!choice; static if (choiceParameterTypes.length == 1) { if (auto castedObject = cast(choiceParameterTypes[0]) switchObject) { - static if(is(ReturnType!(choice) == void)) + static if (is(ReturnType!(choice) == void)) { choice(castedObject); static if (areAllHandlersVoidResult) @@ -326,10 +335,10 @@ auto castSwitch(choices...)(Object switchObject) // Checking for null matches: foreach (index, choice; choices) { - static if (ParameterTypeTuple!(choice).length == 0) + static if (Parameters!(choice).length == 0) { immutable indexOfOvershadowingChoice = - indexOfFirstOvershadowingChoiceOnLast!(choices[0..index + 1]); + indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); // Check for overshadowing: static assert(indexOfOvershadowingChoice == index, @@ -338,7 +347,7 @@ auto castSwitch(choices...)(Object switchObject) if (switchObject is null) { - static if(is(ReturnType!(choice) == void)) + static if (is(ReturnType!(choice) == void)) { choice(); static if (areAllHandlersVoidResult) @@ -364,7 +373,7 @@ auto castSwitch(choices...)(Object switchObject) } /// -unittest +@system unittest { import std.algorithm.iteration : map; import std.format : format; @@ -395,7 +404,7 @@ unittest } /// Using with void handlers: -unittest +@system unittest { import std.exception : assertThrown; @@ -411,12 +420,12 @@ unittest // Void handlers are also allowed if all the handlers are void: new A().castSwitch!( - (A a) { assert(true); }, + (A a) { }, (B b) { assert(false); }, )(); } -unittest +@system unittest { import core.exception : SwitchError; import std.exception : assertThrown; @@ -479,7 +488,7 @@ unittest // All-void handlers work for the null case: null.castSwitch!( (Object o) { assert(false); }, - () { assert(true); }, + () { }, )(); // Throwing void handlers work for the null case: @@ -489,6 +498,23 @@ unittest )()); } +@system unittest +{ + interface I { } + class B : I { } + class C : I { } + + assert((new B()).castSwitch!( + (B b) => "class B", + (I i) => "derived from I", + ) == "class B"); + + assert((new C()).castSwitch!( + (B b) => "class B", + (I i) => "derived from I", + ) == "derived from I"); +} + /** Clamps a value into the given bounds. This functions is equivalent to $(D max(lower, min(upper,val))). @@ -500,7 +526,7 @@ Params: Returns: Returns $(D val), if it is between $(D lower) and $(D upper). - Otherwise returns the nearest of the two. + Otherwise returns the nearest of the two. */ auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) @@ -556,7 +582,8 @@ body // cmp /********************************** -Performs three-way lexicographical comparison on two input ranges +Performs three-way lexicographical comparison on two +$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) according to predicate $(D pred). Iterating $(D r1) and $(D r2) in lockstep, $(D cmp) compares each element $(D e1) of $(D r1) with the corresponding element $(D e2) in $(D r2). If one of the ranges has been @@ -594,7 +621,8 @@ if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2)) } /// ditto -int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) if (isSomeString!R1 && isSomeString!R2) +int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) +if (isSomeString!R1 && isSomeString!R2) { import core.stdc.string : memcmp; import std.utf : decode; @@ -656,8 +684,8 @@ int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) if (isSomeString!R1 && isSom { if (i1 == r1.length) return threeWay(i2, r2.length); if (i2 == r2.length) return threeWay(r1.length, i1); - immutable c1 = std.utf.decode(r1, i1), - c2 = std.utf.decode(r2, i2); + immutable c1 = decode(r1, i1), + c2 = decode(r2, i2); if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2); } } @@ -705,41 +733,77 @@ Compares two ranges for equality, as defined by predicate $(D pred) */ template equal(alias pred = "a == b") { + enum isEmptyRange(R) = + isInputRange!R && __traits(compiles, {static assert(R.empty);}); + + enum hasFixedLength(T) = hasLength!T || isNarrowString!T; + /++ - This function compares to ranges for equality. The ranges may have - different element types, as long as $(D pred(a, b)) evaluates to $(D bool) - for $(D a) in $(D r1) and $(D b) in $(D r2). + Compares two ranges for equality. The ranges may have + different element types, as long as $(D pred(r1.front, r2.front)) + evaluates to $(D bool). Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred). Params: - r1 = The first range to be compared. + r1 = The first range to be compared. r2 = The second range to be compared. - Returns: - $(D true) if and only if the two ranges compare equal element + Returns: + $(D true) if and only if the two ranges compare _equal element for element, according to binary predicate $(D pred). See_Also: - $(WEB sgi.com/tech/stl/_equal.html, STL's _equal) + $(HTTP sgi.com/tech/stl/_equal.html, STL's _equal) +/ bool equal(Range1, Range2)(Range1 r1, Range2 r2) - if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(binaryFun!pred(r1.front, r2.front)))) + if (isInputRange!Range1 && isInputRange!Range2 && + is(typeof(binaryFun!pred(r1.front, r2.front)))) { - //Start by detecting default pred and compatible dynamicarray. - static if (is(typeof(pred) == string) && pred == "a == b" && + static assert(!(isInfinite!Range1 && isInfinite!Range2), + "Both ranges are known to be infinite"); + + //No pred calls necessary + static if (isEmptyRange!Range1 || isEmptyRange!Range2) + { + return r1.empty && r2.empty; + } + else static if ((isInfinite!Range1 && hasFixedLength!Range2) || + (hasFixedLength!Range1 && isInfinite!Range2)) + { + return false; + } + //Detect default pred and compatible dynamic array + else static if (is(typeof(pred) == string) && pred == "a == b" && isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2))) { return r1 == r2; } + // if one of the arguments is a string and the other isn't, then auto-decoding + // can be avoided if they have the same ElementEncodingType + else static if (is(typeof(pred) == string) && pred == "a == b" && + isAutodecodableString!Range1 != isAutodecodableString!Range2 && + is(ElementEncodingType!Range1 == ElementEncodingType!Range2)) + { + import std.utf : byCodeUnit; + + static if (isAutodecodableString!Range1) + { + return equal(r1.byCodeUnit, r2); + } + else + { + return equal(r2.byCodeUnit, r1); + } + } //Try a fast implementation when the ranges have comparable lengths else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length))) { - auto len1 = r1.length; - auto len2 = r2.length; + immutable len1 = r1.length; + immutable len2 = r2.length; if (len1 != len2) return false; //Short circuit return //Lengths are the same, so we need to do an actual comparison - //Good news is we can sqeeze out a bit of performance by not checking if r2 is empty + //Good news is we can squeeze out a bit of performance by not checking if r2 is empty for (; !r1.empty; r1.popFront(), r2.popFront()) { if (!binaryFun!(pred)(r1.front, r2.front)) return false; @@ -754,7 +818,8 @@ template equal(alias pred = "a == b") if (r2.empty) return false; if (!binaryFun!(pred)(r1.front, r2.front)) return false; } - return r2.empty; + static if (!isInfinite!Range1) + return r2.empty; } } } @@ -763,11 +828,12 @@ template equal(alias pred = "a == b") @safe unittest { import std.math : approxEqual; - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] a = [ 1, 2, 4, 3 ]; assert(!equal(a, a[1..$])); assert(equal(a, a)); + assert(equal!((a, b) => a == b)(a, a)); // different types double[] b = [ 1.0, 2, 4, 3]; @@ -788,7 +854,7 @@ range of range (of range...) comparisons. @safe unittest { import std.range : iota, chunks; - import std.algorithm : equal; + import std.algorithm.comparison : equal; assert(equal!(equal!equal)( [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], iota(0, 8).chunks(2).chunks(2) @@ -799,7 +865,7 @@ range of range (of range...) comparisons. { import std.algorithm.iteration : map; import std.math : approxEqual; - import std.internal.test.dummyrange : ReferenceForwardRange, + import std.internal.test.dummyrange : ReferenceForwardRange, ReferenceInputRange, ReferenceInfiniteForwardRange; debug(std_algorithm) scope(success) @@ -853,14 +919,51 @@ range of range (of range...) comparisons. cir = new ReferenceInputRange!int([1, 2, 8, 1]); assert(!equal(cir, cfr)); - //Test with an infinte range - ReferenceInfiniteForwardRange!int ifr = new ReferenceInfiniteForwardRange!int; + //Test with an infinite range + auto ifr = new ReferenceInfiniteForwardRange!int; assert(!equal(a, ifr)); + assert(!equal(ifr, a)); + //Test InputRange without length + assert(!equal(ifr, cir)); + assert(!equal(cir, ifr)); +} + +@safe pure unittest +{ + import std.utf : byChar, byWchar, byDchar; + + assert(equal("æøå".byChar, "æøå")); + assert(equal("æøå", "æøå".byChar)); + assert(equal("æøå".byWchar, "æøå"w)); + assert(equal("æøå"w, "æøå".byWchar)); + assert(equal("æøå".byDchar, "æøå"d)); + assert(equal("æøå"d, "æøå".byDchar)); +} + +@safe pure unittest +{ + struct R(bool _empty) { + enum empty = _empty; + @property char front(){assert(0);} + void popFront(){assert(0);} + } + alias I = R!false; + static assert(!__traits(compiles, I().equal(I()))); + // strings have fixed length so don't need to compare elements + assert(!I().equal("foo")); + assert(!"bar".equal(I())); + + alias E = R!true; + assert(E().equal(E())); + assert(E().equal("")); + assert("".equal(E())); + assert(!E().equal("foo")); + assert(!"bar".equal(E())); } // MaxType private template MaxType(T...) - if (T.length >= 1) +if (T.length >= 1) { static if (T.length == 1) { @@ -883,7 +986,7 @@ private template MaxType(T...) // levenshteinDistance /** -Encodes $(WEB realityinteractive.com/rgrzywinski/archives/000249.html, +Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html, edit operations) necessary to transform one sequence into another. Given sequences $(D s) (source) and $(D t) (target), a sequence of $(D EditOp) encodes the steps that need to be taken to @@ -911,57 +1014,6 @@ enum EditOp : char private struct Levenshtein(Range, alias equals, CostType = size_t) { - void deletionIncrement(CostType n) - { - _deletionIncrement = n; - InitMatrix(); - } - - void insertionIncrement(CostType n) - { - _insertionIncrement = n; - InitMatrix(); - } - - CostType distance(Range s, Range t) - { - auto slen = walkLength(s.save), tlen = walkLength(t.save); - AllocMatrix(slen + 1, tlen + 1); - foreach (i; 1 .. rows) - { - auto sfront = s.front; - s.popFront(); - auto tt = t; - foreach (j; 1 .. cols) - { - auto cSub = matrix(i - 1,j - 1) - + (equals(sfront, tt.front) ? 0 : _substitutionIncrement); - tt.popFront(); - auto cIns = matrix(i,j - 1) + _insertionIncrement; - auto cDel = matrix(i - 1,j) + _deletionIncrement; - switch (min_index(cSub, cIns, cDel)) - { - case 0: - matrix(i,j) = cSub; - break; - case 1: - matrix(i,j) = cIns; - break; - default: - matrix(i,j) = cDel; - break; - } - } - } - return matrix(slen,tlen); - } - - EditOp[] path(Range s, Range t) - { - distanceWithPath(s, t); - return path(); - } - EditOp[] path() { import std.algorithm.mutation : reverse; @@ -969,13 +1021,15 @@ private struct Levenshtein(Range, alias equals, CostType = size_t) EditOp[] result; size_t i = rows - 1, j = cols - 1; // restore the path - while (i || j) { + while (i || j) + { auto cIns = j == 0 ? CostType.max : matrix(i,j - 1); auto cDel = i == 0 ? CostType.max : matrix(i - 1,j); auto cSub = i == 0 || j == 0 ? CostType.max : matrix(i - 1,j - 1); - switch (min_index(cSub, cIns, cDel)) { + switch (min_index(cSub, cIns, cDel)) + { case 0: result ~= matrix(i - 1,j - 1) == matrix(i,j) ? EditOp.none @@ -997,6 +1051,10 @@ private struct Levenshtein(Range, alias equals, CostType = size_t) return result; } + ~this() { + FreeMatrix(); + } + private: CostType _deletionIncrement = 1, _insertionIncrement = 1, @@ -1007,16 +1065,34 @@ private: // Treat _matrix as a rectangular array ref CostType matrix(size_t row, size_t col) { return _matrix[row * cols + col]; } - void AllocMatrix(size_t r, size_t c) { + void AllocMatrix(size_t r, size_t c) @trusted { + import core.checkedint : mulu; + bool overflow; + const rc = mulu(r, c, overflow); + if (overflow) assert(0); rows = r; cols = c; - if (_matrix.length < r * c) { - delete _matrix; - _matrix = new CostType[r * c]; + if (_matrix.length < rc) + { + import core.stdc.stdlib : realloc; + import core.exception : onOutOfMemoryError; + const nbytes = mulu(rc, _matrix[0].sizeof, overflow); + if (overflow) assert(0); + auto m = cast(CostType *) realloc(_matrix.ptr, nbytes); + if (!m) + onOutOfMemoryError(); + _matrix = m[0 .. r * c]; InitMatrix(); } } + void FreeMatrix() @trusted { + import core.stdc.stdlib : free; + + free(_matrix.ptr); + _matrix = null; + } + void InitMatrix() { foreach (r; 0 .. rows) matrix(r,0) = r * _deletionIncrement; @@ -1111,7 +1187,7 @@ private: } /** -Returns the $(WEB wikipedia.org/wiki/Levenshtein_distance, Levenshtein +Returns the $(HTTP wikipedia.org/wiki/Levenshtein_distance, Levenshtein distance) between $(D s) and $(D t). The Levenshtein distance computes the minimal amount of edit operations necessary to transform $(D s) into $(D t). Performs $(BIGOH s.length * t.length) evaluations of $(D @@ -1125,11 +1201,11 @@ Params: Returns: The minimal number of edits to transform s into t. -Allocates GC memory. +Does not allocate GC memory. */ -size_t levenshteinDistance(alias equals = "a == b", Range1, Range2) +size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) (Range1 s, Range2 t) - if (isForwardRange!(Range1) && isForwardRange!(Range2)) +if (isForwardRange!(Range1) && isForwardRange!(Range2)) { alias eq = binaryFun!(equals); @@ -1186,12 +1262,43 @@ size_t levenshteinDistance(alias equals = "a == b", Range1, Range2) assert(levenshteinDistance("abcde", "abcde") == 0); assert(levenshteinDistance("abcde", "abCde") == 1); assert(levenshteinDistance("kitten", "sitting") == 3); - assert(levenshteinDistance!((a, b) => std.uni.toUpper(a) == std.uni.toUpper(b)) + assert(levenshteinDistance!((a, b) => toUpper(a) == toUpper(b)) ("parks", "SPARK") == 2); assert(levenshteinDistance("parks".filter!"true", "spark".filter!"true") == 2); assert(levenshteinDistance("ID", "I♥D") == 1); } +@safe @nogc nothrow unittest +{ + assert(levenshteinDistance("cat"d, "rat"d) == 1); +} + +/// ditto +size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) + (auto ref Range1 s, auto ref Range2 t) +if (isConvertibleToString!Range1 || isConvertibleToString!Range2) +{ + import std.meta : staticMap; + alias Types = staticMap!(convertToString, Range1, Range2); + return levenshteinDistance!(equals, Types)(s, t); +} + +@safe unittest +{ + static struct S { string s; alias s this; } + assert(levenshteinDistance(S("cat"), S("rat")) == 1); + assert(levenshteinDistance("cat", S("rat")) == 1); + assert(levenshteinDistance(S("cat"), "rat") == 1); +} + +@safe @nogc nothrow unittest +{ + static struct S { dstring s; alias s this; } + assert(levenshteinDistance(S("cat"d), S("rat"d)) == 1); + assert(levenshteinDistance("cat"d, S("rat"d)) == 1); + assert(levenshteinDistance(S("cat"d), "rat"d) == 1); +} + /** Returns the Levenshtein distance and the edit path between $(D s) and $(D t). @@ -1202,15 +1309,15 @@ Params: t = The transformation target Returns: - The minimal amount of edits to transform s into t and the sequence of - edits to effect this transformation. + Tuple with the first element being the minimal amount of edits to transform s into t and + the second element being the sequence of edits to effect this transformation. -Allocates GC memory. +Allocates GC memory for the returned EditOp[] array. */ Tuple!(size_t, EditOp[]) -levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2) +levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) (Range1 s, Range2 t) - if (isForwardRange!(Range1) && isForwardRange!(Range2)) +if (isForwardRange!(Range1) && isForwardRange!(Range2)) { Levenshtein!(Range1, binaryFun!(equals)) lev; auto d = lev.distanceWithPath(s, t); @@ -1220,10 +1327,10 @@ levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2) /// @safe unittest { - string a = "Saturday", b = "Sunday"; + string a = "Saturday", b = "Sundays"; auto p = levenshteinDistanceAndPath(a, b); - assert(p[0] == 3); - assert(equal(p[1], "nrrnsnnn")); + assert(p[0] == 4); + assert(equal(p[1], "nrrnsnnni")); } @safe unittest @@ -1238,6 +1345,25 @@ levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2) assert(levenshteinDistance("kitten", "sitting") == 3); } +/// ditto +Tuple!(size_t, EditOp[]) +levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) + (auto ref Range1 s, auto ref Range2 t) +if (isConvertibleToString!Range1 || isConvertibleToString!Range2) +{ + import std.meta : staticMap; + alias Types = staticMap!(convertToString, Range1, Range2); + return levenshteinDistanceAndPath!(equals, Types)(s, t); +} + +@safe unittest +{ + static struct S { string s; alias s this; } + assert(levenshteinDistanceAndPath(S("cat"), S("rat"))[0] == 1); + assert(levenshteinDistanceAndPath("cat", S("rat"))[0] == 1); + assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1); +} + // max /** Iterates the passed arguments and return the maximum value. @@ -1249,26 +1375,29 @@ Params: Returns: The maximum of the passed-in args. The type of the returned value is the type among the passed arguments that is able to store the largest value. + +See_Also: + $(REF maxElement, std,algorithm,searching) */ MaxType!T max(T...)(T args) - if (T.length >= 2) +if (T.length >= 2) { //Get "a" static if (T.length <= 2) - alias args[0] a; + alias a = args[0]; else auto a = max(args[0 .. ($+1)/2]); - alias typeof(a) T0; + alias T0 = typeof(a); //Get "b" static if (T.length <= 3) - alias args[$-1] b; + alias b = args[$-1]; else auto b = max(args[($+1)/2 .. $]); - alias typeof(b) T1; + alias T1 = typeof(b); import std.algorithm.internal : algoFormat; - static assert (is(typeof(a < b)), + static assert(is(typeof(a < b)), algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); //Do the "max" proper with a and b @@ -1324,7 +1453,7 @@ MaxType!T max(T...)(T args) // MinType private template MinType(T...) - if (T.length >= 1) +if (T.length >= 1) { static if (T.length == 1) { @@ -1356,27 +1485,33 @@ private template MinType(T...) // min /** -Returns the minimum of the passed-in values. +Iterates the passed arguments and returns the minimum value. + +Params: args = The values to select the minimum from. At least two arguments + must be passed, and they must be comparable with `<`. +Returns: The minimum of the passed-in values. +See_Also: + $(REF minElement, std,algorithm,searching) */ MinType!T min(T...)(T args) - if (T.length >= 2) +if (T.length >= 2) { //Get "a" static if (T.length <= 2) - alias args[0] a; + alias a = args[0]; else auto a = min(args[0 .. ($+1)/2]); - alias typeof(a) T0; + alias T0 = typeof(a); //Get "b" static if (T.length <= 3) - alias args[$-1] b; + alias b = args[$-1]; else auto b = min(args[($+1)/2 .. $]); - alias typeof(b) T1; + alias T1 = typeof(b); import std.algorithm.internal : algoFormat; - static assert (is(typeof(a < b)), + static assert(is(typeof(a < b)), algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); //Do the "min" proper with a and b @@ -1385,10 +1520,9 @@ MinType!T min(T...)(T args) return cast(typeof(return)) (chooseA ? a : b); } +/// @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int a = 5; short b = 6; double c = 2; @@ -1398,13 +1532,15 @@ MinType!T min(T...)(T args) auto e = min(a, b, c); static assert(is(typeof(e) == double)); assert(e == 2); - // mixed signedness test + + // With arguments of mixed signedness, the return type is the one that can + // store the lowest values. a = -10; uint f = 10; static assert(is(typeof(min(a, f)) == int)); assert(min(a, f) == -10); - //Test user-defined types + // User-defined types that support comparison with < are supported. import std.datetime; assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4)); assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4)); @@ -1425,11 +1561,11 @@ two mismatched values. Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred). See_Also: - $(WEB sgi.com/tech/stl/_mismatch.html, STL's _mismatch) + $(HTTP sgi.com/tech/stl/_mismatch.html, STL's _mismatch) */ Tuple!(Range1, Range2) mismatch(alias pred = "a == b", Range1, Range2)(Range1 r1, Range2 r2) - if (isInputRange!(Range1) && isInputRange!(Range2)) +if (isInputRange!(Range1) && isInputRange!(Range2)) { for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) { @@ -1564,7 +1700,7 @@ auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choi assertThrown!Exception(factorial(-9)); } -unittest +@system unittest { import core.exception : SwitchError; import std.exception : assertThrown; @@ -1597,3 +1733,429 @@ unittest 2, "two", )); } + +/** +Checks if the two ranges have the same number of elements. This function is +optimized to always take advantage of the $(D length) member of either range +if it exists. + +If both ranges have a length member, this function is $(BIGOH 1). Otherwise, +this function is $(BIGOH min(r1.length, r2.length)). + +Params: + r1 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + r2 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + +Returns: + $(D true) if both ranges have the same length, $(D false) otherwise. +*/ +bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2) +if (isInputRange!Range1 && + isInputRange!Range2 && + !isInfinite!Range1 && + !isInfinite!Range2) +{ + static if (hasLength!(Range1) && hasLength!(Range2)) + { + return r1.length == r2.length; + } + else static if (hasLength!(Range1) && !hasLength!(Range2)) + { + size_t length; + + while (!r2.empty) + { + r2.popFront; + + if (++length > r1.length) + { + return false; + } + } + + return !(length < r1.length); + } + else static if (!hasLength!(Range1) && hasLength!(Range2)) + { + size_t length; + + while (!r1.empty) + { + r1.popFront; + + if (++length > r2.length) + { + return false; + } + } + + return !(length < r2.length); + } + else + { + while (!r1.empty) + { + if (r2.empty) + { + return false; + } + + r1.popFront; + r2.popFront; + } + + return r2.empty; + } +} + +/// +@safe nothrow pure unittest +{ + assert(isSameLength([1, 2, 3], [4, 5, 6])); + assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3])); + assert(isSameLength("abc", "xyz")); + + int[] a; + int[] b; + assert(isSameLength(a, b)); + + assert(!isSameLength([1, 2, 3], [4, 5])); + assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); + assert(!isSameLength("abcd", "xyz")); +} + +// Test CTFE +@safe pure unittest +{ + enum result1 = isSameLength([1, 2, 3], [4, 5, 6]); + static assert(result1); + + enum result2 = isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3]); + static assert(!result2); +} + +@safe nothrow pure unittest +{ + import std.internal.test.dummyrange; + + auto r1 = new ReferenceInputRange!int([1, 2, 3]); + auto r2 = new ReferenceInputRange!int([4, 5, 6]); + assert(isSameLength(r1, r2)); + + auto r3 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r4; + assert(isSameLength(r3, r4)); + + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r5; + auto r6 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert(isSameLength(r5, r6)); + + auto r7 = new ReferenceInputRange!int([1, 2]); + auto r8 = new ReferenceInputRange!int([4, 5, 6]); + assert(!isSameLength(r7, r8)); + + auto r9 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r10; + assert(!isSameLength(r9, r10)); + + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11; + auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); + assert(!isSameLength(r11, r12)); +} + +/// For convenience +alias AllocateGC = Flag!"allocateGC"; + +/** +Checks if both ranges are permutations of each other. + +This function can allocate if the $(D Yes.allocateGC) flag is passed. This has +the benefit of have better complexity than the $(D Yes.allocateGC) option. However, +this option is only available for ranges whose equality can be determined via each +element's $(D toHash) method. If customized equality is needed, then the $(D pred) +template parameter can be passed, and the function will automatically switch to +the non-allocating algorithm. See $(REF binaryFun, std,functional) for more details on +how to define $(D pred). + +Non-allocating forward range option: $(BIGOH n^2) +Non-allocating forward range option with custom $(D pred): $(BIGOH n^2) +Allocating forward range option: amortized $(BIGOH r1.length) + $(BIGOH r2.length) + +Params: + pred = an optional parameter to change how equality is defined + allocate_gc = $(D Yes.allocateGC)/$(D No.allocateGC) + r1 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + r2 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + +Returns: + $(D true) if all of the elements in $(D r1) appear the same number of times in $(D r2). + Otherwise, returns $(D false). +*/ + +bool isPermutation(AllocateGC allocate_gc, Range1, Range2) +(Range1 r1, Range2 r2) +if (allocate_gc == Yes.allocateGC && + isForwardRange!Range1 && + isForwardRange!Range2 && + !isInfinite!Range1 && + !isInfinite!Range2) +{ + alias E1 = Unqual!(ElementType!Range1); + alias E2 = Unqual!(ElementType!Range2); + + if (!isSameLength(r1.save, r2.save)) + { + return false; + } + + // Skip the elements at the beginning where r1.front == r2.front, + // they are in the same order and don't need to be counted. + while (!r1.empty && !r2.empty && r1.front == r2.front) + { + r1.popFront(); + r2.popFront(); + } + + if (r1.empty && r2.empty) + { + return true; + } + + int[CommonType!(E1, E2)] counts; + + foreach (item; r1) + { + ++counts[item]; + } + + foreach (item; r2) + { + if (--counts[item] < 0) + { + return false; + } + } + + return true; +} + +/// ditto +bool isPermutation(alias pred = "a == b", Range1, Range2) +(Range1 r1, Range2 r2) +if (is(typeof(binaryFun!(pred))) && + isForwardRange!Range1 && + isForwardRange!Range2 && + !isInfinite!Range1 && + !isInfinite!Range2) +{ + import std.algorithm.searching : count; + + alias predEquals = binaryFun!(pred); + alias E1 = Unqual!(ElementType!Range1); + alias E2 = Unqual!(ElementType!Range2); + + if (!isSameLength(r1.save, r2.save)) + { + return false; + } + + // Skip the elements at the beginning where r1.front == r2.front, + // they are in the same order and don't need to be counted. + while (!r1.empty && !r2.empty && predEquals(r1.front, r2.front)) + { + r1.popFront(); + r2.popFront(); + } + + if (r1.empty && r2.empty) + { + return true; + } + + size_t r1_count; + size_t r2_count; + + // At each element item, when computing the count of item, scan it while + // also keeping track of the scanning index. If the first occurrence + // of item in the scanning loop has an index smaller than the current index, + // then you know that the element has been seen before + size_t index; + outloop: for (auto r1s1 = r1.save; !r1s1.empty; r1s1.popFront, index++) + { + auto item = r1s1.front; + r1_count = 0; + r2_count = 0; + + size_t i; + for (auto r1s2 = r1.save; !r1s2.empty; r1s2.popFront, i++) + { + auto e = r1s2.front; + if (predEquals(e, item) && i < index) + { + continue outloop; + } + else if (predEquals(e, item)) + { + ++r1_count; + } + } + + r2_count = r2.save.count!pred(item); + + if (r1_count != r2_count) + { + return false; + } + } + + return true; +} + +/// +@safe pure unittest +{ + import std.typecons : Yes; + + assert(isPermutation([1, 2, 3], [3, 2, 1])); + assert(isPermutation([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); + assert(isPermutation("abc", "bca")); + + assert(!isPermutation([1, 2], [3, 4])); + assert(!isPermutation([1, 1, 2, 3], [1, 2, 2, 3])); + assert(!isPermutation([1, 1], [1, 1, 1])); + + // Faster, but allocates GC handled memory + assert(isPermutation!(Yes.allocateGC)([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); + assert(!isPermutation!(Yes.allocateGC)([1, 2], [3, 4])); +} + +// Test @nogc inference +@safe @nogc pure unittest +{ + static immutable arr1 = [1, 2, 3]; + static immutable arr2 = [3, 2, 1]; + assert(isPermutation(arr1, arr2)); + + static immutable arr3 = [1, 1, 2, 3]; + static immutable arr4 = [1, 2, 2, 3]; + assert(!isPermutation(arr3, arr4)); +} + +@safe pure unittest +{ + import std.internal.test.dummyrange; + + auto r1 = new ReferenceForwardRange!int([1, 2, 3, 4]); + auto r2 = new ReferenceForwardRange!int([1, 2, 4, 3]); + assert(isPermutation(r1, r2)); + + auto r3 = new ReferenceForwardRange!int([1, 2, 3, 4]); + auto r4 = new ReferenceForwardRange!int([4, 2, 1, 3]); + assert(isPermutation!(Yes.allocateGC)(r3, r4)); + + auto r5 = new ReferenceForwardRange!int([1, 2, 3]); + auto r6 = new ReferenceForwardRange!int([4, 2, 1, 3]); + assert(!isPermutation(r5, r6)); + + auto r7 = new ReferenceForwardRange!int([4, 2, 1, 3]); + auto r8 = new ReferenceForwardRange!int([1, 2, 3]); + assert(!isPermutation!(Yes.allocateGC)(r7, r8)); + + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r9; + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r10; + assert(isPermutation(r9, r10)); + + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r11; + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r12; + assert(isPermutation!(Yes.allocateGC)(r11, r12)); + + alias mytuple = Tuple!(int, int); + + assert(isPermutation!"a[0] == b[0]"( + [mytuple(1, 4), mytuple(2, 5)], + [mytuple(2, 3), mytuple(1, 2)] + )); +} + +/** +Get the _first argument `a` that passes an `if (unaryFun!pred(a))` test. If +no argument passes the test, return the last argument. + +Similar to behaviour of the `or` operator in dynamic languages such as Lisp's +`(or ...)` and Python's `a or b or ...` except that the last argument is +returned upon no match. + +Simplifies logic, for instance, in parsing rules where a set of alternative +matchers are tried. The _first one that matches returns it match result, +typically as an abstract syntax tree (AST). + +Bugs: +Lazy parameters are currently, too restrictively, inferred by DMD to +always throw even though they don't need to be. This makes it impossible to +currently mark `either` as `nothrow`. See issue at $(BUGZILLA 12647). + +Returns: + The _first argument that passes the test `pred`. +*/ +CommonType!(T, Ts) either(alias pred = a => a, T, Ts...)(T first, lazy Ts alternatives) +if (alternatives.length >= 1 && + !is(CommonType!(T, Ts) == void) && + allSatisfy!(ifTestable, T, Ts)) +{ + alias predFun = unaryFun!pred; + + if (predFun(first)) return first; + + foreach (e; alternatives[0 .. $ - 1]) + if (predFun(e)) return e; + + return alternatives[$ - 1]; +} + +/// +@safe pure unittest +{ + const a = 1; + const b = 2; + auto ab = either(a, b); + static assert(is(typeof(ab) == const(int))); + assert(ab == a); + + auto c = 2; + const d = 3; + auto cd = either!(a => a == 3)(c, d); // use predicate + static assert(is(typeof(cd) == int)); + assert(cd == d); + + auto e = 0; + const f = 2; + auto ef = either(e, f); + static assert(is(typeof(ef) == int)); + assert(ef == f); + + immutable p = 1; + immutable q = 2; + auto pq = either(p, q); + static assert(is(typeof(pq) == immutable(int))); + assert(pq == p); + + assert(either(3, 4) == 3); + assert(either(0, 4) == 4); + assert(either(0, 0) == 0); + assert(either("", "a") == ""); + + string r = null; + assert(either(r, "a") == "a"); + assert(either("a", "") == "a"); + + immutable s = [1, 2]; + assert(either(s, s) == s); + + assert(either([0, 1], [1, 2]) == [0, 1]); + assert(either([0, 1], [1]) == [0, 1]); + assert(either("a", "b") == "a"); + + static assert(!__traits(compiles, either(1, "a"))); + static assert(!__traits(compiles, either(1.0, "a"))); + static assert(!__traits(compiles, either('a', "a"))); +} diff --git a/std/algorithm/internal.d b/std/algorithm/internal.d index 3e0b4111dc5..35ba503e3cc 100644 --- a/std/algorithm/internal.d +++ b/std/algorithm/internal.d @@ -74,5 +74,4 @@ version(unittest) } } -package T* addressOf(T)(ref T val) { return &val; } - +package(std) T* addressOf(T)(ref T val) { return &val; } diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index 6cd044257f1..c731799af75 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -1,30 +1,35 @@ // Written in the D programming language. /** -This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). +This is a submodule of $(MREF std, algorithm). It contains generic _iteration algorithms. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE Cheat Sheet, - $(TR $(TH Function Name) $(TH Description)) - $(T2 cache, Eagerly evaluates and caches another range's $(D front).) $(T2 cacheBidirectional, As above, but also provides $(D back) and $(D popBack).) -$(T2 chunkyBy, - $(D chunkyBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])) +$(T2 chunkBy, + $(D chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])) returns a range containing 3 subranges: the first with just $(D [1, 1]); the second with the elements $(D [1, 2]) and $(D [2, 2]); and the third with just $(D [2, 1]).) +$(T2 cumulativeFold, + $(D cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])) returns a + lazily-evaluated range containing the successive reduced values `1`, + `3`, `6`, `10`.) $(T2 each, $(D each!writeln([1, 2, 3])) eagerly prints the numbers $(D 1), $(D 2) and $(D 3) on their own lines.) $(T2 filter, - $(D filter!"a > 0"([1, -1, 2, 0, -3])) iterates over elements $(D 1) + $(D filter!(a => a > 0)([1, -1, 2, 0, -3])) iterates over elements $(D 1) and $(D 2).) $(T2 filterBidirectional, Similar to $(D filter), but also provides $(D back) and $(D popBack) at a small increase in cost.) +$(T2 fold, + $(D fold!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).) $(T2 group, $(D group([5, 2, 2, 3, 3])) returns a range containing the tuples $(D tuple(5, 1)), $(D tuple(2, 2)), and $(D tuple(3, 2)).) @@ -33,23 +38,26 @@ $(T2 joiner, over the characters $(D "hello; world!"). No new string is created - the existing inputs are iterated.) $(T2 map, - $(D map!"2 * a"([1, 2, 3])) lazily returns a range with the numbers + $(D map!(a => a * 2)([1, 2, 3])) lazily returns a range with the numbers $(D 2), $(D 4), $(D 6).) +$(T2 permutations, + Lazily computes all permutations using Heap's algorithm.) $(T2 reduce, - $(D reduce!"a + b"([1, 2, 3, 4])) returns $(D 10).) + $(D reduce!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10). + This is the old implementation of `fold`.) $(T2 splitter, Lazily splits a range by a separator.) $(T2 sum, - Same as $(D reduce), but specialized for accurate summation.) + Same as $(D fold), but specialized for accurate summation.) $(T2 uniq, Iterates over the unique elements in a range, which is assumed sorted.) ) Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_iteration.d) @@ -63,7 +71,8 @@ import std.functional; // : unaryFun, binaryFun; import std.range.primitives; import std.traits; -template aggregate(fun...) if (fun.length >= 1) +private template aggregate(fun...) +if (fun.length >= 1) { /* --Intentionally not ddoc-- * Aggregates elements in each subrange of the given range of ranges using @@ -86,7 +95,7 @@ template aggregate(fun...) if (fun.length >= 1) return ror.map!(reduce!fun); } - unittest + @safe unittest { import std.algorithm.comparison : equal, max, min; @@ -115,12 +124,13 @@ The result is then directly returned when $(D front) is called, rather than re-evaluated. This can be a useful function to place in a chain, after functions -that have expensive evaluation, as a lazy alternative to $(XREF array,array). +that have expensive evaluation, as a lazy alternative to $(REF array, std,array). In particular, it can be placed after a call to $(D map), or before a call to $(D filter). -$(D cache) may provide bidirectional iteration if needed, but since -this comes at an increased cost, it must be explicitly requested via the +$(D cache) may provide +$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) +iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the call to $(D cacheBidirectional). Furthermore, a bidirectional cache will evaluate the "center" element twice, when there is only one element left in the range. @@ -129,8 +139,14 @@ $(D cache) does not provide random access primitives, as $(D cache) would be unable to cache the random accesses. If $(D Range) provides slicing primitives, then $(D cache) will provide the same slicing primitives, -but $(D hasSlicing!Cache) will not yield true (as the $(XREF range,hasSlicing) +but $(D hasSlicing!Cache) will not yield true (as the $(REF hasSlicing, std,_range,primitives) trait also checks for random access). + +Params: + range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + +Returns: + an input range with the cached values of range +/ auto cache(Range)(Range range) if (isInputRange!Range) @@ -161,8 +177,8 @@ if (isBidirectionalRange!Range) } // Without cache, with array (greedy) auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() - .filter!"a[1]<0"() - .map!"a[0]"() + .filter!(a => a[1] < 0)() + .map!(a => a[0])() .array(); // the values of x that have a negative y are: @@ -176,8 +192,8 @@ if (isBidirectionalRange!Range) // Without array, with cache (lazy) auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() .cache() - .filter!"a[1]<0"() - .map!"a[0]"(); + .filter!(a => a[1] < 0)() + .map!(a => a[0])(); // the values of x that have a negative y are: assert(equal(result2, [-3, -2, 2])); @@ -189,14 +205,14 @@ if (isBidirectionalRange!Range) /++ Tip: $(D cache) is eager when evaluating elements. If calling front on the -underlying range has a side effect, it will be observeable before calling -front on the actual cached range. +underlying _range has a side effect, it will be observable before calling +front on the actual cached _range. -Furtermore, care should be taken composing $(D cache) with $(XREF range,take). +Furthermore, care should be taken composing $(D cache) with $(REF take, std,_range). By placing $(D take) before $(D cache), then $(D cache) will be "aware" -of when the range ends, and correctly stop caching elements when needed. +of when the _range ends, and correctly stop caching elements when needed. If calling front has no side effect though, placing $(D take) after $(D cache) -may yield a faster range. +may yield a faster _range. Either way, the resulting ranges will be equivalent, but maybe not at the same cost or side effects. @@ -223,8 +239,8 @@ same cost or side effects. import std.algorithm.comparison : equal; import std.range; auto a = [1, 2, 3, 4]; - assert(equal(a.map!"(a - 1)*a"().cache(), [ 0, 2, 6, 12])); - assert(equal(a.map!"(a - 1)*a"().cacheBidirectional().retro(), [12, 6, 2, 0])); + assert(equal(a.map!(a => (a - 1) * a)().cache(), [ 0, 2, 6, 12])); + assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2, 0])); auto r1 = [1, 2, 3, 4].cache() [1 .. $]; auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $]; assert(equal(r1, [2, 3, 4])); @@ -294,19 +310,25 @@ private struct _Cache(R, bool bidir) private { import std.algorithm.internal : algoFormat; - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; alias E = ElementType!R; alias UE = Unqual!E; R source; - static if (bidir) alias CacheTypes = TypeTuple!(UE, UE); - else alias CacheTypes = TypeTuple!UE; + static if (bidir) alias CacheTypes = AliasSeq!(UE, UE); + else alias CacheTypes = AliasSeq!UE; CacheTypes caches; static assert(isAssignable!(UE, E) && is(UE : E), - algoFormat("Cannot instantiate range with %s because %s elements are not assignable to %s.", R.stringof, E.stringof, UE.stringof)); + algoFormat( + "Cannot instantiate range with %s because %s elements are not assignable to %s.", + R.stringof, + E.stringof, + UE.stringof + ) + ); } this(R range) @@ -403,12 +425,12 @@ private struct _Cache(R, bool bidir) auto opSlice(size_t low, size_t high) in { - assert(low <= high); + assert(low <= high, "Bounds error when slicing cache."); } body { - import std.range : take; - return this[low .. $].take(high - low); + import std.range : takeExactly; + return this[low .. $].takeExactly(high - low); } } } @@ -423,35 +445,48 @@ returns a range of which elements are obtained by applying $(D fun(a)) left to right for all elements $(D a) in $(D range). The original ranges are not changed. Evaluation is done lazily. +Params: + fun = one or more transformation functions + r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + +Returns: + a range with each fun applied to all the elements. If there is more than one + fun, the element type will be $(D Tuple) containing one element for each fun. + See_Also: - $(WEB en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function)) + $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function)) */ -template map(fun...) if (fun.length >= 1) +template map(fun...) +if (fun.length >= 1) { auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) { - import std.typetuple : staticMap; - - alias AppliedReturnType(alias f) = typeof(f(r.front)); + import std.meta : AliasSeq, staticMap; + alias RE = ElementType!(Range); static if (fun.length > 1) { import std.functional : adjoin; - import std.typetuple : staticIndexOf; + import std.meta : staticIndexOf; alias _funs = staticMap!(unaryFun, fun); alias _fun = adjoin!_funs; - alias ReturnTypes = staticMap!(AppliedReturnType, _funs); - static assert(staticIndexOf!(void, ReturnTypes) == -1, - "All mapping functions must not return void."); + // Once DMD issue #5710 is fixed, this validation loop can be moved into a template. + foreach (f; _funs) + { + static assert(!is(typeof(f(RE.init)) == void), + "Mapping function(s) must not return void: " ~ _funs.stringof); + } } else { alias _fun = unaryFun!fun; + alias _funs = AliasSeq!(_fun); - static assert(!is(AppliedReturnType!_fun == void), - "Mapping function must not return void."); + // Do the validation separately for single parameters due to DMD issue #15777. + static assert(!is(typeof(_fun(RE.init)) == void), + "Mapping function(s) must not return void: " ~ _funs.stringof); } return MapResult!(_fun, Range)(r); @@ -501,6 +536,17 @@ it separately: assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); } +@safe unittest +{ + // Verify workaround for DMD #15777 + + import std.algorithm.mutation, std.string; + auto foo(string[] args) + { + return args.map!strip; + } +} + private struct MapResult(alias fun, Range) { alias R = Unqual!Range; @@ -510,11 +556,13 @@ private struct MapResult(alias fun, Range) { @property auto ref back()() { + assert(!empty, "Attempting to fetch the back of an empty map."); return fun(_input.back); } void popBack()() { + assert(!empty, "Attempting to popBack an empty map."); _input.popBack(); } } @@ -539,11 +587,13 @@ private struct MapResult(alias fun, Range) void popFront() { + assert(!empty, "Attempting to popFront an empty map."); _input.popFront(); } @property auto ref front() { + assert(!empty, "Attempting to fetch the front of an empty map."); return fun(_input.front); } @@ -595,8 +645,8 @@ private struct MapResult(alias fun, Range) auto opSlice(opSlice_t low, opSlice_t high) { - import std.range : take; - return this[low .. $].take(high - low); + import std.range : takeExactly; + return this[low .. $].takeExactly(high - low); } } } @@ -616,9 +666,6 @@ private struct MapResult(alias fun, Range) import std.conv : to; import std.functional : adjoin; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - alias stringize = map!(to!string); assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); @@ -632,15 +679,14 @@ private struct MapResult(alias fun, Range) //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ])); } -unittest +@safe unittest { import std.algorithm.comparison : equal; import std.internal.test.dummyrange; import std.ascii : toUpper; import std.range; + import std.typecons : tuple; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] arr1 = [ 1, 2, 3, 4 ]; const int[] arr1Const = arr1; int[] arr2 = [ 5, 6 ]; @@ -681,7 +727,7 @@ unittest assert(squares[3] == 16); // Test slicing. - auto squareSlice = squares[1..squares.length - 1]; + auto squareSlice = squares[1 .. squares.length - 1]; assert(equal(squareSlice, [4, 9][])); assert(squareSlice.back == 9); assert(squareSlice[1] == 9); @@ -721,9 +767,9 @@ unittest static assert(!is(ms1[0])); //narrow strings can't be indexed assert(ms2[0] == 'æ—¥'); assert(ms3[0] == 'H'); - static assert(!is(ms1[0..1])); //narrow strings can't be sliced - assert(equal(ms2[0..2], "日本"w)); - assert(equal(ms3[0..2], "HE")); + static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced + assert(equal(ms2[0 .. 2], "日本"w)); + assert(equal(ms3[0 .. 2], "HE")); // Issue 5753 static void voidFun(int) {} @@ -732,6 +778,15 @@ unittest static assert(!__traits(compiles, map!(voidFun, voidFun)([1]))); static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1]))); static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1]))); + static assert(!__traits(compiles, map!(a => voidFun(a))([1]))); + + // Phobos issue #15480 + auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]); + assert(dd[0] == tuple(1, 1)); + assert(dd[1] == tuple(4, 8)); + assert(dd[2] == tuple(9, 27)); + assert(dd[3] == tuple(16, 64)); + assert(dd.length == 4); } @safe unittest @@ -749,14 +804,14 @@ unittest // Issue #10130 - map of iota with const step. const step = 2; - static assert(__traits(compiles, map!(i => i)(iota(0, 10, step)))); + assert(map!(i => i)(iota(0, 10, step)).walkLength == 5); // Need these to all by const to repro the float case, due to the // CommonType template used in the float specialization of iota. const floatBegin = 0.0; const floatEnd = 1.0; const floatStep = 0.02; - static assert(__traits(compiles, map!(i => i)(iota(floatBegin, floatEnd, floatStep)))); + assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50); } @safe unittest @@ -782,43 +837,28 @@ unittest /** Eagerly iterates over $(D r) and calls $(D pred) over _each element. -Params: - pred = predicate to apply to each element of the range - r = range or iterable over which each iterates - -Example: ---- -void deleteOldBackups() -{ - import std.algorithm, std.datetime, std.file; - auto cutoff = Clock.currTime() - 7.days; - dirEntries("", "*~", SpanMode.depth) - .filter!(de => de.timeLastModified < cutoff) - .each!remove(); -} ---- - -If the range supports it, the value can be mutated in place. Examples: ---- -arr.each!((ref a) => a++); -arr.each!"a++"; ---- - If no predicate is specified, $(D each) will default to doing nothing but consuming the entire range. $(D .front) will be evaluated, but this can be avoided by explicitly specifying a predicate lambda with a $(D lazy) parameter. $(D each) also supports $(D opApply)-based iterators, so it will work -with e.g. $(XREF parallelism, parallel). +with e.g. $(REF parallel, std,parallelism). + +Params: + pred = predicate to apply to each element of the range + r = range or iterable over which each iterates -See_Also: $(XREF range,tee) +See_Also: $(REF tee, std,range) */ template each(alias pred = "a") { - import std.typetuple : TypeTuple; - alias BinaryArgs = TypeTuple!(pred, "i", "a"); + import std.meta : AliasSeq; + import std.traits : Parameters; + +private: + alias BinaryArgs = AliasSeq!(pred, "i", "a"); enum isRangeUnaryIterable(R) = is(typeof(unaryFun!pred(R.init.front))); @@ -833,99 +873,210 @@ template each(alias pred = "a") enum isForeachUnaryIterable(R) = is(typeof((R r) { foreach (ref a; r) - cast(void)unaryFun!pred(a); + cast(void) unaryFun!pred(a); })); enum isForeachBinaryIterable(R) = is(typeof((R r) { - foreach (i, ref a; r) - cast(void)binaryFun!BinaryArgs(i, a); + foreach (ref i, ref a; r) + cast(void) binaryFun!BinaryArgs(i, a); })); enum isForeachIterable(R) = (!isForwardRange!R || isDynamicArray!R) && (isForeachUnaryIterable!R || isForeachBinaryIterable!R); +public: void each(Range)(Range r) - if (isRangeIterable!Range && !isForeachIterable!Range) + if (!isForeachIterable!Range && ( + isRangeIterable!Range || + __traits(compiles, typeof(r.front).length))) { - debug(each) pragma(msg, "Using while for ", Range.stringof); - static if (isRangeUnaryIterable!Range) + static if (isRangeIterable!Range) { - while (!r.empty) + debug(each) pragma(msg, "Using while for ", Range.stringof); + static if (isRangeUnaryIterable!Range) { - cast(void)unaryFun!pred(r.front); - r.popFront(); + while (!r.empty) + { + cast(void) unaryFun!pred(r.front); + r.popFront(); + } } - } - else // if (isRangeBinaryIterable!Range) - { - size_t i = 0; - while (!r.empty) + else // if (isRangeBinaryIterable!Range) { - cast(void)binaryFun!BinaryArgs(i, r.front); - r.popFront(); - i++; + size_t i = 0; + while (!r.empty) + { + cast(void) binaryFun!BinaryArgs(i, r.front); + r.popFront(); + i++; + } } } + else + { + // range interface with >2 parameters. + for (auto range = r; !range.empty; range.popFront()) + pred(range.front.expand); + } } - void each(Iterable)(Iterable r) - if (isForeachIterable!Iterable) + void each(Iterable)(auto ref Iterable r) + if (isForeachIterable!Iterable || + __traits(compiles, Parameters!(Parameters!(r.opApply)))) { - debug(each) pragma(msg, "Using foreach for ", Iterable.stringof); - static if (isForeachUnaryIterable!Iterable) + static if (isForeachIterable!Iterable) { - foreach (ref e; r) - cast(void)unaryFun!pred(e); + debug(each) pragma(msg, "Using foreach for ", Iterable.stringof); + static if (isForeachUnaryIterable!Iterable) + { + foreach (ref e; r) + cast(void) unaryFun!pred(e); + } + else // if (isForeachBinaryIterable!Iterable) + { + foreach (ref i, ref e; r) + cast(void) binaryFun!BinaryArgs(i, e); + } } - else // if (isForeachBinaryIterable!Iterable) + else { - foreach (i, ref e; r) - cast(void)binaryFun!BinaryArgs(i, e); + // opApply with >2 parameters. count the delegate args. + // only works if it is not templated (otherwise we cannot count the args) + auto dg(Parameters!(Parameters!(r.opApply)) params) { + pred(params); + return 0; // tells opApply to continue iteration + } + r.opApply(&dg); } } } -unittest +/// +@system unittest { import std.range : iota; long[] arr; - // Note: each over arrays should resolve to the - // foreach variant, but as this is a performance - // improvement it is not unit-testable. iota(5).each!(n => arr ~= n); assert(arr == [0, 1, 2, 3, 4]); - // in-place mutation + // If the range supports it, the value can be mutated in place arr.each!((ref n) => n++); assert(arr == [1, 2, 3, 4, 5]); - // by-ref lambdas should not be allowed for non-ref ranges + arr.each!"a++"; + assert(arr == [2, 3, 4, 5, 6]); + + // by-ref lambdas are not allowed for non-ref ranges static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++)))); - // default predicate (walk / consume) + // The default predicate consumes the range auto m = arr.map!(n => n); (&m).each(); assert(m.empty); - // in-place mutation with index + // Indexes are also available for in-place mutations arr[] = 0; arr.each!"a=i"(); assert(arr == [0, 1, 2, 3, 4]); - // opApply iterators - static assert(is(typeof({ - import std.parallelism; - arr.parallel.each!"a++"; - }))); + // opApply iterators work as well + static class S + { + int x; + int opApply(scope int delegate(ref int _x) dg) { return dg(x); } + } + + auto s = new S; + s.each!"a++"; + assert(s.x == 1); +} + +// binary foreach with two ref args +@system unittest +{ + import std.range : lockstep; + + auto a = [ 1, 2, 3 ]; + auto b = [ 2, 3, 4 ]; + + a.lockstep(b).each!((ref x, ref y) { ++x; ++y; }); + + assert(a == [ 2, 3, 4 ]); + assert(b == [ 3, 4, 5 ]); +} + +// #15358: application of `each` with >2 args (opApply) +@system unittest +{ + import std.range : lockstep; + auto a = [0,1,2]; + auto b = [3,4,5]; + auto c = [6,7,8]; + + lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; }); + + assert(a == [1,2,3]); + assert(b == [4,5,6]); + assert(c == [7,8,9]); +} + +// #15358: application of `each` with >2 args (range interface) +@safe unittest +{ + import std.range : zip; + auto a = [0,1,2]; + auto b = [3,4,5]; + auto c = [6,7,8]; + + int[] res; + + zip(a, b, c).each!((x, y, z) { res ~= x + y + z; }); + + assert(res == [9, 12, 15]); +} + +// #16255: `each` on opApply doesn't support ref +@safe unittest +{ + int[] dynamicArray = [1, 2, 3, 4, 5]; + int[5] staticArray = [1, 2, 3, 4, 5]; + + dynamicArray.each!((ref x) => x++); + assert(dynamicArray == [2, 3, 4, 5, 6]); + + staticArray.each!((ref x) => x++); + assert(staticArray == [2, 3, 4, 5, 6]); + + staticArray[].each!((ref x) => x++); + assert(staticArray == [3, 4, 5, 6, 7]); +} + +// #16255: `each` on opApply doesn't support ref +@system unittest +{ + struct S + { + int x; + int opApply(int delegate(ref int _x) dg) { return dg(x); } + } + + S s; + foreach (ref a; s) ++a; + assert(s.x == 1); + s.each!"++a"; + assert(s.x == 2); } +// filter /** $(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));) -Implements the higher order _filter function. +Implements the higher order _filter function. The predicate is passed to +$(REF unaryFun, std,functional), and can either accept a string, or any callable +that can be executed via $(D pred(element)). Params: predicate = Function to apply to each element of range @@ -936,9 +1087,10 @@ Returns: which $(D predicate(x)) returns $(D true). See_Also: - $(WEB en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)) + $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)) */ -template filter(alias predicate) if (is(typeof(unaryFun!predicate))) +template filter(alias predicate) +if (is(typeof(unaryFun!predicate))) { auto filter(Range)(Range range) if (isInputRange!(Unqual!Range)) { @@ -979,14 +1131,27 @@ private struct FilterResult(alias pred, Range) { alias R = Unqual!Range; R _input; + private bool _primed; - this(R r) + private void prime() { - _input = r; + if (_primed) return; while (!_input.empty && !pred(_input.front)) { _input.popFront(); } + _primed = true; + } + + this(R r) + { + _input = r; + } + + private this(R r, bool primed) + { + _input = r; + _primed = primed; } auto opSlice() { return this; } @@ -997,7 +1162,7 @@ private struct FilterResult(alias pred, Range) } else { - @property bool empty() { return _input.empty; } + @property bool empty() { prime; return _input.empty; } } void popFront() @@ -1006,10 +1171,13 @@ private struct FilterResult(alias pred, Range) { _input.popFront(); } while (!_input.empty && !pred(_input.front)); + _primed = true; } @property auto ref front() { + prime; + assert(!empty, "Attempting to fetch the front of an empty filter."); return _input.front; } @@ -1017,7 +1185,7 @@ private struct FilterResult(alias pred, Range) { @property auto save() { - return typeof(this)(_input.save); + return typeof(this)(_input.save, _primed); } } } @@ -1028,8 +1196,8 @@ private struct FilterResult(alias pred, Range) import std.internal.test.dummyrange; import std.range; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0); + int[] a = [ 3, 4, 2 ]; auto r = filter!("a > 3")(a); static assert(isForwardRange!(typeof(r))); @@ -1049,12 +1217,14 @@ private struct FilterResult(alias pred, Range) static assert(isInfinite!(typeof(infinite))); static assert(isForwardRange!(typeof(infinite))); - foreach (DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { DummyType d; auto f = filter!"a & 1"(d); assert(equal(f, [1,3,5,7,9])); - static if (isForwardRange!DummyType) { + static if (isForwardRange!DummyType) + { static assert(isForwardRange!(typeof(f))); } } @@ -1122,16 +1292,23 @@ private struct FilterResult(alias pred, Range) /** * $(D auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));) * - * Similar to $(D filter), except it defines a bidirectional - * range. There is a speed disadvantage - the constructor spends time + * Similar to $(D filter), except it defines a + * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). + * There is a speed disadvantage - the constructor spends time * finding the last element in the range that satisfies the filtering * condition (in addition to finding the first one). The advantage is * that the filtered range can be spanned from both directions. Also, - * $(XREF range, retro) can be applied against the filtered range. + * $(REF retro, std,range) can be applied against the filtered range. + * + * The predicate is passed to $(REF unaryFun, std,functional), and can either + * accept a string, or any callable that can be executed via $(D pred(element)). * * Params: * pred = Function to apply to each element of range * r = Bidirectional range of elements + * + * Returns: + * a new range containing only the elements in r for which pred returns $(D true). */ template filterBidirectional(alias pred) { @@ -1184,6 +1361,7 @@ private struct FilterBidiResult(alias pred, Range) @property auto ref front() { + assert(!empty, "Attempting to fetch the front of an empty filterBidirectional."); return _input.front; } @@ -1197,6 +1375,7 @@ private struct FilterBidiResult(alias pred, Range) @property auto ref back() { + assert(!empty, "Attempting to fetch the back of an empty filterBidirectional."); return _input.back; } @@ -1206,8 +1385,39 @@ private struct FilterBidiResult(alias pred, Range) } } -// group -struct Group(alias pred, R) if (isInputRange!R) +/** +Groups consecutively equivalent elements into a single tuple of the element and +the number of its repetitions. + +Similarly to $(D uniq), $(D group) produces a range that iterates over unique +consecutive elements of the given range. Each element of this range is a tuple +of the element and the number of times it is repeated in the original range. +Equivalence of elements is assessed by using the predicate $(D pred), which +defaults to $(D "a == b"). The predicate is passed to $(REF binaryFun, std,functional), +and can either accept a string, or any callable that can be executed via +$(D pred(element, element)). + +Params: + pred = Binary predicate for determining equivalence of two elements. + r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to + iterate over. + +Returns: A range of elements of type $(D Tuple!(ElementType!R, uint)), +representing each consecutively unique element and its respective number of +occurrences in that run. This will be an input range if $(D R) is an input +range, and a forward range in all other cases. + +See_Also: $(LREF chunkBy), which chunks an input range into subranges + of equivalent adjacent elements. +*/ +Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) +{ + return typeof(return)(r); +} + +/// ditto +struct Group(alias pred, R) +if (isInputRange!R) { import std.typecons : Rebindable, tuple, Tuple; @@ -1231,12 +1441,14 @@ struct Group(alias pred, R) if (isInputRange!R) private R _input; private Tuple!(MutableE, uint) _current; + /// this(R input) { _input = input; if (!_input.empty) popFront(); } + /// void popFront() { if (_input.empty) @@ -1257,23 +1469,28 @@ struct Group(alias pred, R) if (isInputRange!R) static if (isInfinite!R) { + /// enum bool empty = false; // Propagate infiniteness. } else { + /// @property bool empty() { return _current[1] == 0; } } + /// @property auto ref front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty Group."); return _current; } - static if (isForwardRange!R) { + static if (isForwardRange!R) + { + /// @property typeof(this) save() { typeof(this) ret = this; ret._input = this._input.save; @@ -1283,30 +1500,6 @@ struct Group(alias pred, R) if (isInputRange!R) } } -/** -Groups consecutively equivalent elements into a single tuple of the element and -the number of its repetitions. - -Similarly to $(D uniq), $(D group) produces a range that iterates over unique -consecutive elements of the given range. Each element of this range is a tuple -of the element and the number of times it is repeated in the original range. -Equivalence of elements is assessed by using the predicate $(D pred), which -defaults to $(D "a == b"). - -Params: - pred = Binary predicate for determining equivalence of two elements. - r = The $(XREF2 range, isInputRange, input range) to iterate over. - -Returns: A range of elements of type $(D Tuple!(ElementType!R, uint)), -representing each consecutively unique element and its respective number of -occurrences in that run. This will be an input range if $(D R) is an input -range, and a forward range in all other cases. -*/ -Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) -{ - return typeof(return)(r); -} - /// @safe unittest { @@ -1318,20 +1511,37 @@ Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) tuple(4, 3u), tuple(5, 1u) ][])); } +/** + * Using group, an associative array can be easily generated with the count of each + * unique element in the range. + */ +@safe unittest +{ + import std.algorithm.sorting : sort; + import std.array : assocArray; + + uint[string] result; + auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"]; + result = range.sort!((a, b) => a < b) + .group + .assocArray; + + assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]); +} + @safe unittest { import std.algorithm.comparison : equal; import std.internal.test.dummyrange; import std.typecons : tuple, Tuple; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ][])); static assert(isForwardRange!(typeof(group(arr)))); - foreach (DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { DummyType d; auto g = group(d); @@ -1343,7 +1553,7 @@ Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) } } -unittest +@safe unittest { // Issue 13857 immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9]; @@ -1371,7 +1581,7 @@ unittest // Used by implementation of chunkBy for non-forward input ranges. private struct ChunkByChunkImpl(alias pred, Range) - if (isInputRange!Range && !isForwardRange!Range) +if (isInputRange!Range && !isForwardRange!Range) { alias fun = binaryFun!pred; @@ -1410,7 +1620,7 @@ private template ChunkByImplIsUnary(alias pred, Range) // Implementation of chunkBy for non-forward input ranges. private struct ChunkByImpl(alias pred, Range) - if (isInputRange!Range && !isForwardRange!Range) +if (isInputRange!Range && !isForwardRange!Range) { enum bool isUnary = ChunkByImplIsUnary!(pred, Range); @@ -1474,7 +1684,7 @@ private struct ChunkByImpl(alias pred, Range) // Single-pass implementation of chunkBy for forward ranges. private struct ChunkByImpl(alias pred, Range) - if (isForwardRange!Range) +if (isForwardRange!Range) { import std.typecons : RefCounted; @@ -1595,7 +1805,7 @@ private struct ChunkByImpl(alias pred, Range) static assert(isForwardRange!(typeof(this))); } -unittest +@system unittest { import std.algorithm.comparison : equal; @@ -1604,6 +1814,8 @@ unittest { int[] impl; + @safe nothrow: + this(int[] data) { impl = data; } @property bool empty() { return impl.empty; } @property auto ref front() { return impl.front; } @@ -1650,11 +1862,15 @@ unittest /** * Chunks an input range into subranges of equivalent adjacent elements. + * In other languages this is often called `partitionBy`, `groupBy` + * or `sliceWhen`. * * Equivalence is defined by the predicate $(D pred), which can be either - * binary or unary. In the binary form, two _range elements $(D a) and $(D b) - * are considered equivalent if $(D pred(a,b)) is true. In unary form, two - * elements are considered equivalent if $(D pred(a) == pred(b)) is true. + * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is + * passed to $(REF unaryFun, std,functional). In the binary form, two _range elements + * $(D a) and $(D b) are considered equivalent if $(D pred(a,b)) is true. In + * unary form, two elements are considered equivalent if $(D pred(a) == pred(b)) + * is true. * * This predicate must be an equivalence relation, that is, it must be * reflexive ($(D pred(x,x)) is always true), symmetric @@ -1664,7 +1880,7 @@ unittest * * Params: * pred = Predicate for determining equivalence. - * r = The range to be chunked. + * r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked. * * Returns: With a binary predicate, a range of ranges is returned in which * all elements in a given subrange are equivalent under the given predicate. @@ -1680,17 +1896,17 @@ unittest * they appear in the original range. * * See_also: - * $(XREF algorithm,group), which collapses adjacent equivalent elements into a - * single element. + * $(LREF group), which collapses adjacent equivalent elements into a single + * element. */ auto chunkBy(alias pred, Range)(Range r) - if (isInputRange!Range) +if (isInputRange!Range) { return ChunkByImpl!(pred, Range)(r); } /// Showing usage with binary predicate: -/*FIXME: @safe*/ unittest +/*FIXME: @safe*/ @system unittest { import std.algorithm.comparison : equal; @@ -1717,7 +1933,7 @@ auto chunkBy(alias pred, Range)(Range r) } version(none) // this example requires support for non-equivalence relations -unittest +@safe unittest { auto data = [ [1, 1], @@ -1740,10 +1956,11 @@ unittest } /// Showing usage with unary predicate: -/* FIXME: pure @safe nothrow*/ unittest +/* FIXME: pure @safe nothrow*/ @system unittest { import std.algorithm.comparison : equal; import std.typecons : tuple; + import std.range.primitives; // Grouping by particular attribute of each element: auto range = @@ -1788,7 +2005,7 @@ unittest } } -/*FIXME: pure @safe nothrow*/ unittest +/*FIXME: pure @safe nothrow*/ @system unittest { import std.algorithm.comparison : equal; import std.typecons : tuple; @@ -1866,7 +2083,7 @@ unittest // Issue 13595 version(none) // This requires support for non-equivalence relations -unittest +@system unittest { import std.algorithm.comparison : equal; auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].chunkBy!((x, y) => ((x*y) % 3) == 0); @@ -1879,7 +2096,7 @@ unittest } // Issue 13805 -unittest +@system unittest { [""].map!((s) => s).chunkBy!((x, y) => true); } @@ -1887,22 +2104,23 @@ unittest // joiner /** Lazily joins a range of ranges with a separator. The separator itself -is a range. If you do not provide a separator, then the ranges are -joined directly without anything in between them. +is a range. If a separator is not provided, then the ranges are +joined directly without anything in between them (often called `flatten` +in other languages). Params: - r = An $(XREF2 range, isInputRange, input range) of input ranges to be - joined. - sep = A $(XREF2 range, isForwardRange, forward range) of element(s) to - serve as separators in the joined range. + r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input + ranges to be joined. + sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of + element(s) to serve as separators in the joined range. Returns: -An input range of elements in the joined range. This will be a forward range if +A range of elements in the joined range. This will be a forward range if both outer and inner ranges of $(D RoR) are forward ranges; otherwise it will be only an input range. See_also: -$(XREF range,chain), which chains a sequence of ranges with compatible elements +$(REF chain, std,range), which chains a sequence of ranges with compatible elements into a single range. */ auto joiner(RoR, Separator)(RoR r, Separator sep) @@ -2021,13 +2239,13 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR) @property ElementType!(ElementType!RoR) front() { if (!_currentSep.empty) return _currentSep.front; - assert(!_current.empty); + assert(!_current.empty, "Attempting to fetch the front of an empty joiner."); return _current.front; } void popFront() { - assert(!_items.empty); + assert(!_items.empty, "Attempting to popFront an empty joiner."); // Using separator? if (!_currentSep.empty) { @@ -2066,32 +2284,26 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR) import std.algorithm.comparison : equal; import std.conv : text; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - - static assert(isInputRange!(typeof(joiner([""], "")))); - static assert(isForwardRange!(typeof(joiner([""], "")))); - assert(equal(joiner([""], "xyz"), ""), text(joiner([""], "xyz"))); - assert(equal(joiner(["", ""], "xyz"), "xyz"), text(joiner(["", ""], "xyz"))); - assert(equal(joiner(["", "abc"], "xyz"), "xyzabc")); - assert(equal(joiner(["abc", ""], "xyz"), "abcxyz")); - assert(equal(joiner(["abc", "def"], "xyz"), "abcxyzdef")); - assert(equal(joiner(["Mary", "has", "a", "little", "lamb"], "..."), - "Mary...has...a...little...lamb")); - assert(equal(joiner(["abc", "def"]), "abcdef")); + assert(["abc", "def"].joiner.equal("abcdef")); + assert(["Mary", "has", "a", "little", "lamb"] + .joiner("...") + .equal("Mary...has...a...little...lamb")); + assert(["", "abc"].joiner("xyz").equal("xyzabc")); + assert([""].joiner("xyz").equal("")); + assert(["", ""].joiner("xyz").equal("xyz")); } -unittest +@system unittest { import std.algorithm.comparison : equal; import std.range.primitives; import std.range.interfaces; // joiner() should work for non-forward ranges too. auto r = inputRangeObject(["abc", "def"]); - assert (equal(joiner(r, "xyz"), "abcxyzdef")); + assert(equal(joiner(r, "xyz"), "abcxyzdef")); } -unittest +@system unittest { import std.algorithm.comparison : equal; import std.range; @@ -2187,6 +2399,12 @@ unittest assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1])); } +@safe unittest +{ + static assert(isInputRange!(typeof(joiner([""], "")))); + static assert(isForwardRange!(typeof(joiner([""], "")))); +} + /// Ditto auto joiner(RoR)(RoR r) if (isInputRange!RoR && isInputRange!(ElementType!RoR)) @@ -2213,6 +2431,11 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) else _current = _items.front; }; + this(RoR items, ElementType!RoR current) + { + _items = items; + _current = current; + } public: this(RoR r) { @@ -2249,12 +2472,12 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) } @property auto ref front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty joiner."); return _current.front; } void popFront() { - assert(!_current.empty); + assert(!_current.empty, "Attempting to popFront an empty joiner."); _current.popFront(); if (_current.empty) { @@ -2267,24 +2490,34 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) { @property auto save() { - Result copy = this; - copy._items = _items.save; - copy._current = _current.save; - return copy; + return Result(_items.save, _current.save); + } + } + + static if (hasAssignableElements!(ElementType!RoR)) + { + @property void front(ElementType!(ElementType!RoR) element) + { + assert(!empty, "Attempting to assign to front of an empty joiner."); + _current.front = element; + } + + @property void front(ref ElementType!(ElementType!RoR) element) + { + assert(!empty, "Attempting to assign to front of an empty joiner."); + _current.front = element; } } } return Result(r); } -unittest +@safe unittest { import std.algorithm.comparison : equal; - import std.range.interfaces; + import std.range.interfaces : inputRangeObject; import std.range : repeat; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); static assert(isInputRange!(typeof(joiner([""])))); static assert(isForwardRange!(typeof(joiner([""])))); assert(equal(joiner([""]), "")); @@ -2294,13 +2527,20 @@ unittest assert(equal(joiner(["abc", "def"]), "abcdef")); assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]), "Maryhasalittlelamb")); - assert(equal(joiner(std.range.repeat("abc", 3)), "abcabcabc")); + assert(equal(joiner(repeat("abc", 3)), "abcabcabc")); // joiner allows in-place mutation! auto a = [ [1, 2, 3], [42, 43] ]; auto j = joiner(a); j.front = 44; assert(a == [ [44, 2, 3], [42, 43] ]); +} + + +@system unittest +{ + import std.algorithm.comparison : equal; + import std.range.interfaces : inputRangeObject; // bugzilla 8240 assert(equal(joiner([inputRangeObject("")]), "")); @@ -2357,7 +2597,8 @@ unittest // Can't use array() or equal() directly because they fail with transient // .front. int[] result; - foreach (c; rr.joiner()) { + foreach (c; rr.joiner()) + { result ~= c; } @@ -2402,16 +2643,19 @@ unittest // Can't use array() or equal() directly because they fail with transient // .front. dchar[] result; - foreach (c; rr.joiner()) { + foreach (c; rr.joiner()) + { result ~= c; } + import std.conv : to; assert(equal(result, "abc12def34"d), - "Unexpected result: '%s'"d.algoFormat(result)); + //Convert to string for assert's message + to!string("Unexpected result: '%s'"d.algoFormat(result))); } // Issue 8061 -unittest +@system unittest { import std.range.interfaces; import std.conv : to; @@ -2423,29 +2667,84 @@ unittest assert(str == "abcd"); } +@safe unittest +{ + import std.range : repeat; + + class AssignableRange + { + @safe: + int element; + @property int front() + { + return element; + } + + enum empty = false; + + void popFront() + { + } + + @property void front(int newValue) + { + element = newValue; + } + } + + static assert(isInputRange!AssignableRange); + static assert(is(ElementType!AssignableRange == int)); + static assert(hasAssignableElements!AssignableRange); + static assert(!hasLvalueElements!AssignableRange); + + auto range = new AssignableRange(); + assert(range.element == 0); + + auto joined = joiner(repeat(range)); + joined.front = 5; + assert(range.element == 5); + assert(joined.front == 5); + + joined.popFront; + int byRef = 7; + joined.front = byRef; + assert(range.element == byRef); + assert(joined.front == byRef); +} + /++ Implements the homonym function (also known as $(D accumulate), $(D compress), $(D inject), or $(D foldl)) present in various programming -languages of functional flavor. The call $(D reduce!(fun)(seed, -range)) first assigns $(D seed) to an internal variable $(D result), -also called the accumulator. Then, for each element $(D x) in $(D -range), $(D result = fun(result, x)) gets evaluated. Finally, $(D -result) is returned. The one-argument version $(D reduce!(fun)(range)) +languages of functional flavor. There is also $(LREF fold) which does +the same thing but with the opposite parameter order. +The call $(D reduce!(fun)(seed, range)) first assigns $(D seed) to +an internal variable $(D result), also called the accumulator. +Then, for each element $(D x) in $(D range), $(D result = fun(result, x)) +gets evaluated. Finally, $(D result) is returned. +The one-argument version $(D reduce!(fun)(range)) works similarly, but it uses the first element of the range as the seed (the range must be non-empty). Returns: the accumulated $(D result) +Params: + fun = one or more functions + See_Also: - $(WEB en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) + $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) + + $(LREF fold) is functionally equivalent to $(LREF reduce) with the argument order reversed, + and without the need to use $(LREF tuple) for multiple seeds. This makes it easier + to use in UFCS chains. $(LREF sum) is similar to $(D reduce!((a, b) => a + b)) that offers - precise summing of floating point numbers. + pairwise summing of floating point numbers. +/ -template reduce(fun...) if (fun.length >= 1) +template reduce(fun...) +if (fun.length >= 1) { - import std.typetuple : staticMap; + import std.meta : staticMap; alias binfuns = staticMap!(binaryFun, fun); static if (fun.length > 1) @@ -2463,6 +2762,12 @@ template reduce(fun...) if (fun.length >= 1) must both be legal. If $(D r) is empty, an $(D Exception) is thrown. + + Params: + r = an iterable value as defined by $(D isIterable) + + Returns: + the final result of the accumulator applied to the iterable +/ auto reduce(R)(R r) if (isIterable!R) @@ -2473,7 +2778,7 @@ template reduce(fun...) if (fun.length >= 1) static if (isInputRange!R) { - enforce(!r.empty); + enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value."); Args result = r.front; r.popFront(); return reduceImpl!false(r, result); @@ -2488,11 +2793,20 @@ template reduce(fun...) if (fun.length >= 1) /++ Seed version. The seed should be a single value if $(D fun) is a single function. If $(D fun) is multiple functions, then $(D seed) - should be a $(XREF typecons,Tuple), with one field per function in $(D f). + should be a $(REF Tuple, std,typecons), with one field per function in $(D f). For convenience, if the seed is const, or has qualified fields, then $(D reduce) will operate on an unqualified copy. If this happens then the returned type will not perfectly match $(D S). + + Use $(D fold) instead of $(D reduce) to use the seed version in a UFCS chain. + + Params: + seed = the initial value of the accumulator + r = an iterable value as defined by $(D isIterable) + + Returns: + the final result of the accumulator applied to the iterable +/ auto reduce(S, R)(S seed, R r) if (isIterable!R) @@ -2529,8 +2843,16 @@ template reduce(fun...) if (fun.length >= 1) foreach (/+auto ref+/ E e; r) // @@@4707@@@ { foreach (i, f; binfuns) - static assert(is(typeof(args[i] = f(args[i], e))), - algoFormat("Incompatible function/seed/element: %s/%s/%s", fullyQualifiedName!f, Args[i].stringof, E.stringof)); + { + static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))), + algoFormat( + "Incompatible function/seed/element: %s/%s/%s", + fullyQualifiedName!f, + Args[i].stringof, + E.stringof + ) + ); + } static if (mustInitialize) if (initialized == false) { @@ -2544,7 +2866,9 @@ template reduce(fun...) if (fun.length >= 1) foreach (i, f; binfuns) args[i] = f(args[i], e); } - static if (mustInitialize) if (!initialized) throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value."); + static if (mustInitialize) + if (!initialized) + throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value."); static if (Args.length == 1) return args[0]; @@ -2553,23 +2877,6 @@ template reduce(fun...) if (fun.length >= 1) } } -//Helper for Reduce -private template ReduceSeedType(E) -{ - static template ReduceSeedType(alias fun) - { - import std.algorithm.internal : algoFormat; - - E e = E.init; - static alias ReduceSeedType = Unqual!(typeof(fun(e, e))); - - //Check the Seed type is useable. - ReduceSeedType s = ReduceSeedType.init; - static assert(is(typeof({ReduceSeedType s = e;})) && is(typeof(s = fun(s, e))), - algoFormat("Unable to deduce an acceptable seed type for %s with element type %s.", fullyQualifiedName!fun, E.stringof)); - } -} - /** Many aggregate range operations turn out to be solved with $(D reduce) quickly and easily. The example below illustrates $(D reduce)'s @@ -2627,7 +2934,7 @@ Sometimes it is very useful to compute multiple aggregates in one pass. One advantage is that the computation is faster because the looping overhead is shared. That's why $(D reduce) accepts multiple functions. If two or more functions are passed, $(D reduce) returns a -$(XREF typecons, Tuple) object with one member per passed-in function. +$(REF Tuple, std,typecons) object with one member per passed-in function. The number of seeds must be correspondingly increased. */ @safe unittest @@ -2652,11 +2959,10 @@ The number of seeds must be correspondingly increased. auto stdev = sqrt(r[1] / a.length - avg * avg); } -unittest +@safe unittest { import std.algorithm.comparison : max, min; - import std.exception : assertThrown; - import std.range; + import std.range : chain; import std.typecons : tuple, Tuple; double[] a = [ 3, 4 ]; @@ -2680,18 +2986,26 @@ unittest // Stringize with commas string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a); assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]"); +} + +@system unittest +{ + import std.algorithm.comparison : max, min; + import std.exception : assertThrown; + import std.range : iota; + import std.typecons : tuple, Tuple; // Test the opApply case. static struct OpApply { bool actEmpty; - int opApply(int delegate(ref int) dg) + int opApply(scope int delegate(ref int) dg) { int res; if (actEmpty) return res; - foreach (i; 0..100) + foreach (i; 0 .. 100) { res = dg(i); if (res) break; @@ -2708,7 +3022,7 @@ unittest assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99)); // Test for throwing on empty range plus no seed. - assertThrown(reduce!"a + b"([1, 2][0..0])); + assertThrown(reduce!"a + b"([1, 2][0 .. 0])); oa.actEmpty = true; assertThrown(reduce!"a + b"(oa)); @@ -2716,8 +3030,6 @@ unittest @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); const float a = 0.0; const float[] b = [ 1.2, 3, 3.3 ]; float[] c = [ 1.2, 3, 3.3 ]; @@ -2751,7 +3063,7 @@ unittest assert(r2 == tuple(3, 3)); } -unittest +@system unittest { int i = 0; static struct OpApply @@ -2770,7 +3082,7 @@ unittest } } //test CTFE and functions with context - int fun(int a, int b){return a + b + 1;} + int fun(int a, int b) @safe {return a + b + 1;} auto foo() { import std.algorithm.comparison : max; @@ -2794,10 +3106,11 @@ unittest import std.algorithm.comparison : max, min; import std.typecons : tuple, Tuple; - //http://forum.dlang.org/thread/oghtttkopzjshsuflelk@forum.dlang.org + //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org //Seed is tuple of const. static auto minmaxElement(alias F = min, alias G = max, R)(in R range) - @safe pure nothrow if (isInputRange!R) + @safe pure nothrow + if (isInputRange!R) { return reduce!(F, G)(tuple(ElementType!R.max, ElementType!R.min), range); @@ -2808,7 +3121,7 @@ unittest @safe unittest //12569 { import std.algorithm.comparison : max, min; - import std.typecons: tuple; + import std.typecons : tuple; dchar c = 'a'; reduce!(min, max)(tuple(c, c), "hello"); // OK static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); @@ -2832,6 +3145,492 @@ unittest static assert(is(typeof(reduce!((a, b)=>a+b)(data)))); } +//Helper for Reduce +private template ReduceSeedType(E) +{ + static template ReduceSeedType(alias fun) + { + import std.algorithm.internal : algoFormat; + + alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E))); + + //Check the Seed type is useable. + ReduceSeedType s = ReduceSeedType.init; + static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) && + is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))), + algoFormat( + "Unable to deduce an acceptable seed type for %s with element type %s.", + fullyQualifiedName!fun, + E.stringof + ) + ); + } +} + + +/++ +Implements the homonym function (also known as $(D accumulate), $(D +compress), $(D inject), or $(D foldl)) present in various programming +languages of functional flavor. The call $(D fold!(fun)(range, seed)) +first assigns $(D seed) to an internal variable $(D result), +also called the accumulator. Then, for each element $(D x) in $(D +range), $(D result = fun(result, x)) gets evaluated. Finally, $(D +result) is returned. The one-argument version $(D fold!(fun)(range)) +works similarly, but it uses the first element of the range as the +seed (the range must be non-empty). + +Returns: + the accumulated $(D result) + +See_Also: + $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) + + $(LREF sum) is similar to $(D fold!((a, b) => a + b)) that offers + precise summing of floating point numbers. + + This is functionally equivalent to $(LREF reduce) with the argument order reversed, + and without the need to use $(LREF tuple) for multiple seeds. ++/ +template fold(fun...) +if (fun.length >= 1) +{ + auto fold(R, S...)(R r, S seed) + { + static if (S.length < 2) + { + return reduce!fun(seed, r); + } + else + { + import std.typecons : tuple; + return reduce!fun(tuple(seed), r); + } + } +} + +/// +@safe pure unittest +{ + immutable arr = [1, 2, 3, 4, 5]; + + // Sum all elements + assert(arr.fold!((a, b) => a + b) == 15); + + // Sum all elements with explicit seed + assert(arr.fold!((a, b) => a + b)(6) == 21); + + import std.algorithm.comparison : min, max; + import std.typecons : tuple; + + // Compute minimum and maximum at the same time + assert(arr.fold!(min, max) == tuple(1, 5)); + + // Compute minimum and maximum at the same time with seeds + assert(arr.fold!(min, max)(0, 7) == tuple(0, 7)); + + // Can be used in a UFCS chain + assert(arr.map!(a => a + 1).fold!((a, b) => a + b) == 20); + + // Return the last element of any range + assert(arr.fold!((a, b) => b) == 5); +} + +@safe @nogc pure nothrow unittest +{ + int[1] arr; + static assert(!is(typeof(arr.fold!()))); + static assert(!is(typeof(arr.fold!(a => a)))); + static assert(is(typeof(arr.fold!((a, b) => a)))); + static assert(is(typeof(arr.fold!((a, b) => a)(1)))); +} + +/++ +Similar to `fold`, but returns a range containing the successive reduced values. +The call $(D cumulativeFold!(fun)(range, seed)) first assigns `seed` to an +internal variable `result`, also called the accumulator. +The returned range contains the values $(D result = fun(result, x)) lazily +evaluated for each element `x` in `range`. Finally, the last element has the +same value as $(D fold!(fun)(seed, range)). +The one-argument version $(D cumulativeFold!(fun)(range)) works similarly, but +it returns the first element unchanged and uses it as seed for the next +elements. +This function is also known as + $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum), + $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate), + $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan), + $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum). + +Params: + fun = one or more functions to use as fold operation + +Returns: + The function returns a range containing the consecutive reduced values. If + there is more than one `fun`, the element type will be $(REF Tuple, + std,typecons) containing one element for each `fun`. + +See_Also: + $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum) ++/ +template cumulativeFold(fun...) +if (fun.length >= 1) +{ + import std.meta : staticMap; + private alias binfuns = staticMap!(binaryFun, fun); + + /++ + No-seed version. The first element of `r` is used as the seed's value. + For each function `f` in `fun`, the corresponding seed type `S` is + $(D Unqual!(typeof(f(e, e)))), where `e` is an element of `r`: + `ElementType!R`. + Once `S` has been determined, then $(D S s = e;) and $(D s = f(s, e);) must + both be legal. + + Params: + range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + Returns: + a range containing the consecutive reduced values. + +/ + auto cumulativeFold(R)(R range) + if (isInputRange!(Unqual!R)) + { + return cumulativeFoldImpl(range); + } + + /++ + Seed version. The seed should be a single value if `fun` is a single + function. If `fun` is multiple functions, then `seed` should be a + $(REF Tuple, std,typecons), with one field per function in `f`. + For convenience, if the seed is `const`, or has qualified fields, then + `cumulativeFold` will operate on an unqualified copy. If this happens + then the returned type will not perfectly match `S`. + + Params: + range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + seed = the initial value of the accumulator + Returns: + a range containing the consecutive reduced values. + +/ + auto cumulativeFold(R, S)(R range, S seed) + if (isInputRange!(Unqual!R)) + { + static if (fun.length == 1) + return cumulativeFoldImpl(range, seed); + else + return cumulativeFoldImpl(range, seed.expand); + } + + private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args) + { + import std.algorithm.internal : algoFormat; + + static assert(Args.length == 0 || Args.length == fun.length, + algoFormat("Seed %s does not have the correct amount of fields (should be %s)", + Args.stringof, fun.length)); + + static if (args.length) + alias State = staticMap!(Unqual, Args); + else + alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns); + + foreach (i, f; binfuns) + { + static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles, + { args[i] = f(args[i], e); }()), + algoFormat("Incompatible function/seed/element: %s/%s/%s", + fullyQualifiedName!f, Args[i].stringof, E.stringof)); + } + + static struct Result + { + private: + R source; + State state; + + this(R range, ref Args args) + { + source = range; + if (source.empty) + return; + + foreach (i, f; binfuns) + { + static if (args.length) + state[i] = f(args[i], source.front); + else + state[i] = source.front; + } + } + + public: + @property bool empty() + { + return source.empty; + } + + @property auto front() + { + assert(!empty, "Attempting to fetch the front of an empty cumulativeFold."); + static if (fun.length > 1) + { + import std.typecons : tuple; + return tuple(state); + } + else + { + return state[0]; + } + } + + void popFront() + { + assert(!empty, "Attempting to popFront an empty cumulativeFold."); + source.popFront; + + if (source.empty) + return; + + foreach (i, f; binfuns) + state[i] = f(state[i], source.front); + } + + static if (isForwardRange!R) + { + @property auto save() + { + auto result = this; + result.source = source.save; + return result; + } + } + + static if (hasLength!R) + { + @property size_t length() + { + return source.length; + } + } + } + + return Result(range, args); + } +} + +/// +@safe unittest +{ + import std.algorithm.comparison : max, min; + import std.array : array; + import std.math : approxEqual; + import std.range : chain; + + int[] arr = [1, 2, 3, 4, 5]; + // Partial sum of all elements + auto sum = cumulativeFold!((a, b) => a + b)(arr, 0); + assert(sum.array == [1, 3, 6, 10, 15]); + + // Partial sum again, using a string predicate with "a" and "b" + auto sum2 = cumulativeFold!"a + b"(arr, 0); + assert(sum2.array == [1, 3, 6, 10, 15]); + + // Compute the partial maximum of all elements + auto largest = cumulativeFold!max(arr); + assert(largest.array == [1, 2, 3, 4, 5]); + + // Partial max again, but with Uniform Function Call Syntax (UFCS) + largest = arr.cumulativeFold!max; + assert(largest.array == [1, 2, 3, 4, 5]); + + // Partial count of odd elements + auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0); + assert(odds.array == [1, 1, 2, 2, 3]); + + // Compute the partial sum of squares + auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0); + assert(ssquares.array == [1, 5, 14, 30, 55]); + + // Chain multiple ranges into seed + int[] a = [3, 4]; + int[] b = [100]; + auto r = cumulativeFold!"a + b"(chain(a, b)); + assert(r.array == [3, 7, 107]); + + // Mixing convertible types is fair game, too + double[] c = [2.5, 3.0]; + auto r1 = cumulativeFold!"a + b"(chain(a, b, c)); + assert(approxEqual(r1, [3, 7, 107, 109.5, 112.5])); + + // To minimize nesting of parentheses, Uniform Function Call Syntax can be used + auto r2 = chain(a, b, c).cumulativeFold!"a + b"; + assert(approxEqual(r2, [3, 7, 107, 109.5, 112.5])); +} + +/** +Sometimes it is very useful to compute multiple aggregates in one pass. +One advantage is that the computation is faster because the looping overhead +is shared. That's why `cumulativeFold` accepts multiple functions. +If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple, +std,typecons) object with one member per passed-in function. +The number of seeds must be correspondingly increased. +*/ +@safe unittest +{ + import std.algorithm.comparison : max, min; + import std.algorithm.iteration : map; + import std.math : approxEqual; + import std.typecons : tuple; + + double[] a = [3.0, 4, 7, 11, 3, 2, 5]; + // Compute minimum and maximum in one pass + auto r = a.cumulativeFold!(min, max); + // The type of r is Tuple!(int, int) + assert(approxEqual(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum + assert(approxEqual(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum + + // Compute sum and sum of squares in one pass + auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0)); + assert(approxEqual(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum + assert(approxEqual(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares +} + +@safe unittest +{ + import std.algorithm.comparison : equal, max, min; + import std.conv : to; + import std.range : chain; + import std.typecons : tuple; + + double[] a = [3, 4]; + auto r = a.cumulativeFold!("a + b")(0.0); + assert(r.equal([3, 7])); + auto r2 = cumulativeFold!("a + b")(a); + assert(r2.equal([3, 7])); + auto r3 = cumulativeFold!(min)(a); + assert(r3.equal([3, 3])); + double[] b = [100]; + auto r4 = cumulativeFold!("a + b")(chain(a, b)); + assert(r4.equal([3, 7, 107])); + + // two funs + auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0)); + assert(r5.equal([tuple(3, -3), tuple(7, -7)])); + auto r6 = cumulativeFold!("a + b", "a - b")(a); + assert(r6.equal([tuple(3, 3), tuple(7, -1)])); + + a = [1, 2, 3, 4, 5]; + // Stringize with commas + auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, ""); + assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"])); + + // Test for empty range + a = []; + assert(a.cumulativeFold!"a + b".empty); + assert(a.cumulativeFold!"a + b"(2.0).empty); +} + +@safe unittest +{ + import std.algorithm.comparison : max, min; + import std.array : array; + import std.math : approxEqual; + import std.typecons : tuple; + + const float a = 0.0; + const float[] b = [1.2, 3, 3.3]; + float[] c = [1.2, 3, 3.3]; + + auto r = cumulativeFold!"a + b"(b, a); + assert(approxEqual(r, [1.2, 4.2, 7.5])); + + auto r2 = cumulativeFold!"a + b"(c, a); + assert(approxEqual(r2, [1.2, 4.2, 7.5])); + + const numbers = [10, 30, 20]; + enum m = numbers.cumulativeFold!(min).array; + assert(m == [10, 10, 10]); + enum minmax = numbers.cumulativeFold!(min, max).array; + assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]); +} + +@safe unittest +{ + import std.math : approxEqual; + import std.typecons : tuple; + + enum foo = "a + 0.5 * b"; + auto r = [0, 1, 2, 3]; + auto r1 = r.cumulativeFold!foo; + auto r2 = r.cumulativeFold!(foo, foo); + assert(approxEqual(r1, [0, 0.5, 1.5, 3])); + assert(approxEqual(r2.map!"a[0]", [0, 0.5, 1.5, 3])); + assert(approxEqual(r2.map!"a[1]", [0, 0.5, 1.5, 3])); +} + +@safe unittest +{ + import std.algorithm.comparison : equal, max, min; + import std.array : array; + import std.typecons : tuple; + + //Seed is tuple of const. + static auto minmaxElement(alias F = min, alias G = max, R)(in R range) + @safe pure nothrow + if (isInputRange!R) + { + return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min)); + } + + assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)])); +} + +@safe unittest //12569 +{ + import std.algorithm.comparison : equal, max, min; + import std.typecons : tuple; + + dchar c = 'a'; + + assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'), + tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')])); + static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); + static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); + + //"Seed dchar should be a Tuple" + static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c))); + //"Seed (dchar) does not have the correct amount of fields (should be 2)" + static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); + //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" + static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); + //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar" + static assert(!__traits(compiles, cumulativeFold!all("hello", 1))); + static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1)))); +} + +@safe unittest //13304 +{ + int[] data; + assert(data.cumulativeFold!((a, b) => a + b).empty); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, + propagatesRangeType, RangeType; + + foreach (DummyType; AllDummyRanges) + { + DummyType d; + auto m = d.cumulativeFold!"a * b"; + + static assert(propagatesLength!(typeof(m), DummyType)); + static if (DummyType.rt <= RangeType.Forward) + static assert(propagatesRangeType!(typeof(m), DummyType)); + + assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800])); + } +} + // splitter /** Lazily splits a range using an element as a separator. This can be used with @@ -2842,6 +3641,9 @@ Two adjacent separators are considered to surround an empty element in the split range. Use $(D filter!(a => !a.empty)) on the result to compress empty elements. +The predicate is passed to $(REF binaryFun, std,functional), and can either accept +a string, or any callable that can be executed via $(D pred(element, s)). + If the empty range is given, the result is a range with one empty element. If a range with one separator is given, the result is a range with two empty elements. @@ -2853,8 +3655,8 @@ below). Params: pred = The predicate for comparing each element with the separator, defaulting to $(D "a == b"). - r = The $(XREF2 range, isInputRange, input range) to be split. Must support - slicing and $(D .length). + r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be + split. Must support slicing and $(D .length). s = The element to be treated as the separator between range segments to be split. @@ -2864,11 +3666,12 @@ Constraints: Returns: An input range of the subranges of elements between separators. If $(D r) - is a forward range or bidirectional range, the returned range will be - likewise. + is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), + the returned range will be likewise. See_Also: - $(XREF regex, _splitter) for a version that splits using a regular + $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator. */ auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s) @@ -2884,10 +3687,9 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) Range _input; Separator _separator; // Do we need hasLength!Range? popFront uses _input.length... - alias IndexType = typeof(unsigned(_input.length)); - enum IndexType _unComputed = IndexType.max - 1, _atEnd = IndexType.max; - IndexType _frontLength = _unComputed; - IndexType _backLength = _unComputed; + enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max; + size_t _frontLength = _unComputed; + size_t _backLength = _unComputed; static if (isNarrowString!Range) { @@ -2900,7 +3702,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) static if (isBidirectionalRange!Range) { - static IndexType lastIndexOf(Range haystack, Separator needle) + static size_t lastIndexOf(Range haystack, Separator needle) { import std.range : retro; auto r = haystack.retro().find!pred(needle); @@ -2938,7 +3740,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) @property Range front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty splitter."); if (_frontLength == _unComputed) { auto r = _input.find!pred(_separator); @@ -2949,7 +3751,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront an empty splitter."); if (_frontLength == _unComputed) { front; @@ -2984,7 +3786,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) { @property Range back() { - assert(!empty); + assert(!empty, "Attempting to fetch the back of an empty splitter."); if (_backLength == _unComputed) { immutable lastIndex = lastIndexOf(_input, _separator); @@ -3002,7 +3804,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) void popBack() { - assert(!empty); + assert(!empty, "Attempting to popBack an empty splitter."); if (_backLength == _unComputed) { // evaluate back to make sure it's computed @@ -3051,17 +3853,12 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) import std.array : array; import std.range : retro; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); assert(equal(splitter("žlutouÄkýřkůň", 'Å™'), [ "žlutouÄký", "kůň" ])); int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; static assert(isForwardRange!(typeof(splitter(a, 0)))); - // foreach (x; splitter(a, 0)) { - // writeln("[", x, "]"); - // } assert(equal(splitter(a, 0), w)); a = null; assert(equal(splitter(a, 0), (int[][]).init)); @@ -3097,7 +3894,8 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) assert(split.back == "r "); foreach (DummyType; AllDummyRanges) { // Bug 4408 - static if (isRandomAccessRange!DummyType) { + static if (isRandomAccessRange!DummyType) + { static assert(isBidirectionalRange!DummyType); DummyType d; auto s = splitter(d, 5); @@ -3125,7 +3923,9 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) /** Similar to the previous overload of $(D splitter), except this one uses another range as a separator. This can be used with any narrow string type or sliceable -range type, but is most popular with string types. +range type, but is most popular with string types. The predicate is passed to +$(REF binaryFun, std,functional), and can either accept a string, or any callable +that can be executed via $(D pred(r.front, s.front)). Two adjacent separators are considered to surround an empty element in the split range. Use $(D filter!(a => !a.empty)) on the result to compress @@ -3134,9 +3934,10 @@ empty elements. Params: pred = The predicate for comparing each element with the separator, defaulting to $(D "a == b"). - r = The $(XREF2 range, isInputRange, input range) to be split. - s = The $(XREF2 range, isForwardRange, forward range) to be treated as the - separator between segments of $(D r) to be split. + r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be + split. + s = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to + be treated as the separator between segments of $(D r) to be split. Constraints: The predicate $(D pred) needs to accept an element of $(D r) and an @@ -3144,10 +3945,10 @@ Constraints: Returns: An input range of the subranges of elements between separators. If $(D r) - is a forward range or bidirectional range, the returned range will be - likewise. + is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), + the returned range will be likewise. -See_Also: $(XREF regex, _splitter) for a version that splits using a regular +See_Also: $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator. */ auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s) @@ -3164,11 +3965,10 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) private: Range _input; Separator _separator; - alias RIndexType = typeof(unsigned(_input.length)); // _frontLength == size_t.max means empty - RIndexType _frontLength = RIndexType.max; + size_t _frontLength = size_t.max; static if (isBidirectionalRange!Range) - RIndexType _backLength = RIndexType.max; + size_t _backLength = size_t.max; @property auto separatorLength() { return _separator.length; } @@ -3206,7 +4006,7 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) @property Range front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty splitter."); ensureFrontLength(); return _input[0 .. _frontLength]; } @@ -3219,13 +4019,13 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) { @property bool empty() { - return _frontLength == RIndexType.max && _input.empty; + return _frontLength == size_t.max && _input.empty; } } void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront an empty splitter."); ensureFrontLength(); if (_frontLength == _input.length) { @@ -3262,45 +4062,6 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) return ret; } } - - // Bidirectional functionality as suggested by Brad Roberts. - static if (isBidirectionalRange!Range && isBidirectionalRange!Separator) - { - //Deprecated. It will be removed in December 2015 - deprecated("splitter!(Range, Range) cannot be iterated backwards (due to separator overlap).") - @property Range back() - { - ensureBackLength(); - return _input[_input.length - _backLength .. _input.length]; - } - - //Deprecated. It will be removed in December 2015 - deprecated("splitter!(Range, Range) cannot be iterated backwards (due to separator overlap).") - void popBack() - { - ensureBackLength(); - if (_backLength == _input.length) - { - // done - _input = _input[0 .. 0]; - _frontLength = _frontLength.max; - _backLength = _backLength.max; - return; - } - if (_backLength + separatorLength == _input.length) - { - // Special case: popping the first-to-first item; there is - // an empty item right before this. Leave the separator in. - _input = _input[0 .. 0]; - _frontLength = 0; - _backLength = 0; - return; - } - // Normal case - _input = _input[0 .. _input.length - _backLength - separatorLength]; - _backLength = _backLength.max; - } - } } return Result(r, s); @@ -3337,16 +4098,12 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) import std.conv : text; import std.array : split; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto s = ",abc, de, fg,hi,"; auto sp0 = splitter(s, ','); - // //foreach (e; sp0) writeln("[", e, "]"); assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][])); auto s1 = ", abc, de, fg, hi, "; auto sp1 = splitter(s1, ", "); - //foreach (e; sp1) writeln("[", e, "]"); assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][])); static assert(isForwardRange!(typeof(sp1))); @@ -3390,14 +4147,9 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) @safe unittest { import std.algorithm.comparison : equal; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto s6 = ","; auto sp6 = splitter(s6, ','); - foreach (e; sp6) - { - //writeln("{", e, "}"); - } + foreach (e; sp6) {} assert(equal(sp6, ["", ""][])); } @@ -3435,6 +4187,8 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) Similar to the previous overload of $(D splitter), except this one does not use a separator. Instead, the predicate is an unary function on the input range's element type. +The $(D isTerminator) predicate is passed to $(REF unaryFun, std,functional) and can +either accept a string, or any callable that can be executed via $(D pred(element, s)). Two adjacent separators are considered to surround an empty element in the split range. Use $(D filter!(a => !a.empty)) on the result to compress @@ -3442,17 +4196,18 @@ empty elements. Params: isTerminator = The predicate for deciding where to split the range. - input = The $(XREF2 range, isInputRange, input range) to be split. + input = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to + be split. Constraints: The predicate $(D isTerminator) needs to accept an element of $(D input). Returns: An input range of the subranges of elements between separators. If $(D input) - is a forward range or bidirectional range, the returned range will be - likewise. + is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), + the returned range will be likewise. -See_Also: $(XREF regex, _splitter) for a version that splits using a regular +See_Also: $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator. */ auto splitter(alias isTerminator, Range)(Range input) @@ -3465,17 +4220,18 @@ if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front)))) @safe unittest { import std.algorithm.comparison : equal; + import std.range.primitives : front; - assert(equal(splitter!"a == ' '"("hello world"), [ "hello", "", "world" ])); + assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ])); int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; - assert(equal(splitter!"a == 0"(a), w)); + assert(equal(splitter!(a => a == 0)(a), w)); a = [ 0 ]; - assert(equal(splitter!"a == 0"(a), [ (int[]).init, (int[]).init ])); + assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ])); a = [ 0, 1 ]; - assert(equal(splitter!"a == 0"(a), [ [], [1] ])); + assert(equal(splitter!(a => a == 0)(a), [ [], [1] ])); w = [ [0], [1], [2] ]; - assert(equal(splitter!"a.front == 1"(w), [ [[0]], [[2]] ])); + assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ])); } private struct SplitterResult(alias isTerminator, Range) @@ -3485,7 +4241,7 @@ private struct SplitterResult(alias isTerminator, Range) private Range _input; private size_t _end = 0; - static if(!fullSlicing) + static if (!fullSlicing) private Range _next; private void findTerminator() @@ -3507,7 +4263,7 @@ private struct SplitterResult(alias isTerminator, Range) this(Range input) { _input = input; - static if(!fullSlicing) + static if (!fullSlicing) _next = _input.save; if (!_input.empty) @@ -3608,8 +4364,6 @@ private struct SplitterResult(alias isTerminator, Range) import std.algorithm.comparison : equal; import std.internal.test.dummyrange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); void compare(string sentence, string[] witness) { auto r = splitter!"a == ' '"(sentence); @@ -3672,8 +4426,14 @@ private struct SplitterResult(alias isTerminator, Range) import std.uni : isWhite; //@@@6791@@@ - assert(equal(splitter("là dove terminava quella valle"), ["là", "dove", "terminava", "quella", "valle"])); - assert(equal(splitter!(std.uni.isWhite)("là dove terminava quella valle"), ["là", "dove", "terminava", "quella", "valle"])); + assert(equal( + splitter("là dove terminava quella valle"), + ["là", "dove", "terminava", "quella", "valle"] + )); + assert(equal( + splitter!(std.uni.isWhite)("là dove terminava quella valle"), + ["là", "dove", "terminava", "quella", "valle"] + )); assert(equal(splitter!"a=='本'"("日本語"), ["æ—¥", "語"])); } @@ -3688,8 +4448,8 @@ Params: s = The string to be split. Returns: - An $(XREF2 range, isInputRange, input range) of slices of the original - string split by whitespace. + An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of + the original string split by whitespace. +/ auto splitter(C)(C[] s) if (isSomeChar!C) @@ -3698,7 +4458,7 @@ if (isSomeChar!C) static struct Result { private: - import core.exception; + import core.exception : RangeError; C[] _s; size_t _frontLength; @@ -3756,8 +4516,8 @@ if (isSomeChar!C) @safe pure unittest { import std.algorithm.comparison : equal; - import std.typetuple : TypeTuple; - foreach(S; TypeTuple!(string, wstring, dstring)) + import std.meta : AliasSeq; + foreach (S; AliasSeq!(string, wstring, dstring)) { import std.conv : to; S a = " a bcd ef gh "; @@ -3781,11 +4541,13 @@ if (isSomeChar!C) lines[0] = "line one".dup; lines[1] = "line \ttwo".dup; lines[2] = "yah last line\ryah".dup; - foreach (line; lines) { - foreach (word; splitter(strip(line))) { + foreach (line; lines) + { + foreach (word; splitter(strip(line))) + { if (word in dictionary) continue; // Nothing to do auto newID = dictionary.length; - dictionary[to!string(word)] = cast(uint)newID; + dictionary[to!string(word)] = cast(uint) newID; } } assert(dictionary.length == 5); @@ -3870,40 +4632,49 @@ if (isSomeChar!C) // sum /** -Sums elements of $(D r), which must be a finite $(XREF2 range, isInputRange, input range). Although -conceptually $(D sum(r)) is equivalent to $(LREF reduce)!((a, b) => a + -b)(0, r), $(D sum) uses specialized algorithms to maximize accuracy, +Sums elements of $(D r), which must be a finite +$(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although +conceptually $(D sum(r)) is equivalent to $(LREF fold)!((a, b) => a + +b)(r, 0), $(D sum) uses specialized algorithms to maximize accuracy, as follows. $(UL -$(LI If $(D $(XREF range, ElementType)!R) is a floating-point type and $(D R) is a -$(XREF2 range, isRandomAccessRange, random-access range) with length and slicing, then $(D sum) uses the -$(WEB en.wikipedia.org/wiki/Pairwise_summation, pairwise summation) +$(LI If $(D $(REF ElementType, std,range,primitives)!R) is a floating-point +type and $(D R) is a +$(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with +length and slicing, then $(D sum) uses the +$(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation) algorithm.) $(LI If $(D ElementType!R) is a floating-point type and $(D R) is a finite input range (but not a random-access range with slicing), then -$(D sum) uses the $(WEB en.wikipedia.org/wiki/Kahan_summation, +$(D sum) uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation, Kahan summation) algorithm.) $(LI In all other cases, a simple element by element addition is done.) ) -For floating point inputs, calculations are made in $(LINK2 ../type.html, $(D real)) +For floating point inputs, calculations are made in +$(DDLINK spec/type, Types, $(D real)) precision for $(D real) inputs and in $(D double) precision otherwise -(Note this is a special case that deviates from $(D reduce)'s behavior, +(Note this is a special case that deviates from $(D fold)'s behavior, which would have kept $(D float) precision for a $(D float) range). For all other types, the calculations are done in the same type obtained from from adding two elements of the range, which may be a different -type from the elements themselves (for example, in case of $(LINK2 ../type.html#integer-promotions, integral promotion)). +type from the elements themselves (for example, in case of +$(DDSUBLINK spec/type,integer-promotions, integral promotion)). A seed may be passed to $(D sum). Not only will this seed be used as an initial value, but its type will override all the above, and determine the algorithm -and precision used for sumation. +and precision used for summation. Note that these specialized summing algorithms execute more primitive operations than vanilla summation. Therefore, if in certain cases maximum speed is required -at expense of precision, one can use $(D reduce!((a, b) => a + b)(0, r)), which +at expense of precision, one can use $(D fold!((a, b) => a + b)(r, 0)), which is not specialized for summation. +Params: + seed = the initial value of the summation + r = a finite input range + Returns: The sum of all the elements in the range r. */ @@ -3924,7 +4695,10 @@ if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front))) static if (isFloatingPoint!E) { static if (hasLength!R && hasSlicing!R) + { + if (r.empty) return seed; return seed + sumPairwise!E(r); + } else return sumKahan!E(seed, r); } @@ -3935,27 +4709,109 @@ if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front))) } // Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation -private auto sumPairwise(Result, R)(R r) +private auto sumPairwise(F, R)(R data) +if (isInputRange!R && !isInfinite!R) { - static assert (isFloatingPoint!Result); - switch (r.length) + import core.bitop : bsf; + // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t + // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes + // from the manual unrolling in sumPairWise16 + F[64] store = void; + size_t idx = 0; + + void collapseStore(T)(T k) { - case 0: return cast(Result) 0; - case 1: return cast(Result) r.front; - case 2: return cast(Result) r[0] + cast(Result) r[1]; - default: return sumPairwise!Result(r[0 .. $ / 2]) + sumPairwise!Result(r[$ / 2 .. $]); + auto lastToKeep = idx - cast(uint) bsf(k+1); + while (idx > lastToKeep) + { + store[idx - 1] += store[idx]; + --idx; + } } + + static if (hasLength!R) + { + foreach (k; 0 .. data.length / 16) + { + static if (isRandomAccessRange!R && hasSlicing!R) + { + store[idx] = sumPairwise16!F(data); + data = data[16 .. data.length]; + } + else store[idx] = sumPairwiseN!(16, false, F)(data); + + collapseStore(k); + ++idx; + } + + size_t i = 0; + foreach (el; data) + { + store[idx] = el; + collapseStore(i); + ++idx; + ++i; + } + } + else + { + size_t k = 0; + while (!data.empty) + { + store[idx] = sumPairwiseN!(16, true, F)(data); + collapseStore(k); + ++idx; + ++k; + } + } + + F s = store[idx - 1]; + foreach_reverse (j; 0 .. idx - 1) + s += store[j]; + + return s; +} + +private auto sumPairwise16(F, R)(R r) +if (isRandomAccessRange!R) +{ + return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3])) + + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7]))) + + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11])) + + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15]))); +} + +private auto sumPair(bool needEmptyChecks, F, R)(ref R r) +if (isForwardRange!R && !isRandomAccessRange!R) +{ + static if (needEmptyChecks) if (r.empty) return F(0); + F s0 = r.front; + r.popFront(); + static if (needEmptyChecks) if (r.empty) return s0; + s0 += r.front; + r.popFront(); + return s0; +} + +private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r) +if (isForwardRange!R && !isRandomAccessRange!R) +{ + import std.math : isPowerOf2; + static assert(isPowerOf2(N)); + static if (N == 2) return sumPair!(needEmptyChecks, F)(r); + else return sumPairwiseN!(N/2, needEmptyChecks, F)(r) + + sumPairwiseN!(N/2, needEmptyChecks, F)(r); } // Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm private auto sumKahan(Result, R)(Result result, R r) { - static assert (isFloatingPoint!Result && isMutable!Result); + static assert(isFloatingPoint!Result && isMutable!Result); Result c = 0; for (; !r.empty; r.popFront()) { - auto y = r.front - c; - auto t = result + y; + immutable y = r.front - c; + immutable t = result + y; c = (t - result) - y; result = t; } @@ -3994,8 +4850,8 @@ private auto sumKahan(Result, R)(Result result, R r) @safe pure nothrow unittest { - static assert(is(typeof(sum([cast( byte)1])) == int)); - static assert(is(typeof(sum([cast(ubyte)1])) == int)); + static assert(is(typeof(sum([cast( byte) 1])) == int)); + static assert(is(typeof(sum([cast(ubyte) 1])) == int)); static assert(is(typeof(sum([ 1, 2, 3, 4])) == int)); static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint)); static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long)); @@ -4047,7 +4903,7 @@ private auto sumKahan(Result, R)(Result result, R r) auto s2 = a.map!(x => x).sum; // Error } -unittest +@system unittest { import std.bigint; import std.range; @@ -4060,22 +4916,33 @@ unittest assert(sb == (BigInt(ulong.max/2) * 10)); } +@safe pure nothrow @nogc unittest +{ + import std.range; + foreach (n; iota(50)) + assert(repeat(1.0, n).sum == n); +} + // uniq /** Lazily iterates unique consecutive elements of the given range (functionality -akin to the $(WEB wikipedia.org/wiki/_Uniq, _uniq) system +akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system utility). Equivalence of elements is assessed by using the predicate -$(D pred), by default $(D "a == b"). If the given range is -bidirectional, $(D uniq) also yields a bidirectional range. +$(D pred), by default $(D "a == b"). The predicate is passed to +$(REF binaryFun, std,functional), and can either accept a string, or any callable +that can be executed via $(D pred(element, element)). If the given range is +bidirectional, $(D uniq) also yields a +$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). Params: pred = Predicate for determining equivalence between range elements. - r = An $(XREF2 range, isInputRange, input range) of elements to filter. + r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of + elements to filter. Returns: - An $(XREF2 range, isInputRange, input range) of consecutively unique - elements in the original range. If $(D r) is also a forward range or - bidirectional range, the returned range will be likewise. + An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of + consecutively unique elements in the original range. If $(D r) is also a + forward range or bidirectional range, the returned range will be likewise. */ auto uniq(alias pred = "a == b", Range)(Range r) if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool)) @@ -4102,9 +4969,48 @@ if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool)) assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1])); } +/// +@system unittest +{ + import std.algorithm.comparison : equal; + + struct S + { + int i; + string s; + } + + auto arr = [ S(1, "a"), S(1, "b"), S(2, "c"), S(2, "d") ]; + + // Let's consider just the 'i' member for equality + auto r = arr.uniq!((a, b) => a.i == b.i); + assert(r.equal([S(1, "a"), S(2, "c")])); + assert(r.front == S(1, "a")); + assert(r.back == S(2, "c")); + + r.popBack; + assert(r.back == S(1, "a")); + assert(r.front == r.back); + + r.popBack; + assert(r.empty); + + import std.exception : assertThrown; + + assertThrown!Error(r.front); + assertThrown!Error(r.back); + assertThrown!Error(r.popFront); + assertThrown!Error(r.popBack); +} + private struct UniqResult(alias pred, Range) { - Range _input; + private Range _input; + static if (isBidirectionalRange!Range) + { + private ElementType!Range _back; + private bool _isInBack; + } this(Range input) { @@ -4118,29 +5024,70 @@ private struct UniqResult(alias pred, Range) void popFront() { + assert(!empty, "Attempting to popFront an empty uniq."); + static if (isBidirectionalRange!Range) + { + if (_input.empty) + { + _isInBack = false; + return; + } + } + auto last = _input.front; do { - _input.popFront(); + _input.popFront; } while (!_input.empty && pred(last, _input.front)); } - @property ElementType!Range front() { return _input.front; } + @property ElementType!Range front() + { + assert(!empty, "Attempting to fetch the front of an empty uniq."); + static if (isBidirectionalRange!Range) + { + if (_input.empty && _isInBack) + { + return _back; + } + } + return _input.front; + } static if (isBidirectionalRange!Range) { void popBack() { - auto last = _input.back; - do + assert(!empty, "Attempting to popBack an empty uniq."); + if (_input.empty) + { + _isInBack = false; + } + else { - _input.popBack(); + _isInBack = true; + _back = _input.back; + ElementType!Range last; + do + { + last = _input.back; + _input.popBack; + } + while (!_input.empty && pred(_back, _input.back)); + _back = last; } - while (!_input.empty && pred(last, _input.back)); } - @property ElementType!Range back() { return _input.back; } + @property ElementType!Range back() + { + assert(!empty, "Attempting to fetch the back of an empty uniq."); + if (!_isInBack) + { + popBack; + } + return _back; + } } static if (isInfinite!Range) @@ -4149,10 +5096,21 @@ private struct UniqResult(alias pred, Range) } else { - @property bool empty() { return _input.empty; } + @property bool empty() + { + static if (isBidirectionalRange!Range) + { + return _input.empty && !_isInBack; + } + else + { + return _input.empty; + } + } } - static if (isForwardRange!Range) { + static if (isForwardRange!Range) + { @property typeof(this) save() { return typeof(this)(_input.save); } @@ -4165,8 +5123,6 @@ private struct UniqResult(alias pred, Range) import std.internal.test.dummyrange; import std.range; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; auto r = uniq(arr); static assert(isForwardRange!(typeof(r))); @@ -4174,15 +5130,134 @@ private struct UniqResult(alias pred, Range) assert(equal(r, [ 1, 2, 3, 4, 5 ][])); assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][]))); - foreach (DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { DummyType d; auto u = uniq(d); assert(equal(u, [1,2,3,4,5,6,7,8,9,10])); static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u))); - static if (d.rt >= RangeType.Bidirectional) { + static if (d.rt >= RangeType.Bidirectional) + { assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1])); } } } + +/** +Lazily computes all _permutations of $(D r) using $(HTTP +en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm). + +Returns: +A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) +the elements of which are an $(REF indexed, std,range) view into $(D r). + +See_Also: +$(REF nextPermutation, std,algorithm,sorting). +*/ +Permutations!Range permutations(Range)(Range r) +if (isRandomAccessRange!Range && hasLength!Range) +{ + return typeof(return)(r); +} + +/// ditto +struct Permutations(Range) +if (isRandomAccessRange!Range && hasLength!Range) +{ + private size_t[] _indices, _state; + private Range _r; + private bool _empty; + + // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@ + deprecated("Private variable. Use front()") + @property size_t[] indices() pure nothrow @nogc @safe { return _indices; } + + // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@ + deprecated("Private variable. Don't set it manually") + @property void indices(size_t[] indices) pure nothrow @nogc @safe { _indices = indices; } + + // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@ + deprecated("Private variable. Use front()") + @property size_t[] state() pure nothrow @nogc @safe { return _state; } + + // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@ + deprecated("Private variable. Don't set it manually") + @property void state(size_t[] state) pure nothrow @nogc @safe { state = state; } + + // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@ + deprecated("Private variable. Access will be forbidden.") + @property Range r() pure nothrow @nogc @safe { return _r; } + + // Explicitly undocumented. It will be removed in June 2017. @@@DEPRECATED_2017-06@@@ + deprecated("Private variable. Don't set it manually") + @property void r(Range r) pure nothrow @nogc @safe { _r = r; } + + /// + this(Range r) + { + import std.range : iota; + import std.array : array; + + this._r = r; + _state = r.length ? new size_t[r.length-1] : null; + _indices = iota(size_t(r.length)).array; + _empty = r.length == 0; + } + + /// + @property bool empty() const pure nothrow @safe @nogc + { + return _empty; + } + + /// + @property auto front() + { + import std.range : indexed; + return _r.indexed(_indices); + } + + /// + void popFront() + { + void next(int n) + { + import std.algorithm.mutation : swap; + + if (n > _indices.length) + { + _empty = true; + return; + } + + if (n % 2 == 1) + swap(_indices[0], _indices[n-1]); + else + swap(_indices[_state[n-2]], _indices[n-1]); + + if (++_state[n-2] == n) + { + _state[n-2] = 0; + next(n+1); + } + } + + next(2); + } +} + +/// +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.range : iota; + assert(equal!equal(iota(3).permutations, + [[0, 1, 2], + [1, 0, 2], + [2, 0, 1], + [0, 2, 1], + [1, 2, 0], + [2, 1, 0]])); +} diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index 616f1bba764..cf2b40bb7ea 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -1,12 +1,11 @@ // Written in the D programming language. /** -This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). +This is a submodule of $(MREF std, algorithm). It contains generic _mutation algorithms. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE Cheat Sheet, - $(TR $(TH Function Name) $(TH Description)) - $(T2 bringToFront, If $(D a = [1, 2, 3]) and $(D b = [4, 5, 6, 7]), $(D bringToFront(a, b)) leaves $(D a = [4, 5, 6]) and @@ -25,11 +24,17 @@ $(T2 initializeAll, $(D a = [double.init, double.init]).) $(T2 move, $(D move(a, b)) moves $(D a) into $(D b). $(D move(a)) reads $(D a) - destructively.) + destructively when necessary.) +$(T2 moveEmplace, + Similar to $(D move) but assumes `target` is uninitialized.) $(T2 moveAll, Moves all elements from one range to another.) +$(T2 moveEmplaceAll, + Similar to $(D moveAll) but assumes all elements in `target` are uninitialized.) $(T2 moveSome, Moves as many elements as possible from one range to another.) +$(T2 moveEmplaceSome, + Similar to $(D moveSome) but assumes all elements in `target` are uninitialized.) $(T2 remove, Removes elements from a range in-place, and returns the shortened range.) @@ -51,6 +56,8 @@ $(T2 stripRight, $(D stripRight!(e => e == 1)(a)) returns $(D [1, 1, 0]).) $(T2 swap, Swaps two values.) +$(T2 swapAt, + Swaps two values by indices.) $(T2 swapRanges, Swaps all elements of two ranges.) $(T2 uninitializedFill, @@ -59,9 +66,9 @@ $(T2 uninitializedFill, Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_mutation.d) @@ -71,13 +78,10 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) module std.algorithm.mutation; import std.range.primitives; -import std.traits : isBlitAssignable, isNarrowString; +import std.traits : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar; // FIXME import std.typecons; // : tuple, Tuple; -// FIXME: somehow deleting this breaks the bringToFront() unittests. -import std.range; - // bringToFront /** The $(D bringToFront) function has considerable flexibility and @@ -92,6 +96,11 @@ range such that all elements in $(D back) are brought to the beginning of the unified range. The relative ordering of elements in $(D front) and $(D back), respectively, remains unchanged. +The $(D bringToFront) function treats strings at the code unit +level and it is not concerned with Unicode character integrity. +$(D bringToFront) is designed as a function for moving elements +in ranges, not as a string function. + Performs $(BIGOH max(front.length, back.length)) evaluations of $(D swap). @@ -101,20 +110,49 @@ Either $(D front) and $(D back) are disjoint, or $(D back) is reachable from $(D front) and $(D front) is not reachable from $(D back). -Returns: +Params: + front = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + back = a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) -The number of elements brought to the front, i.e., the length of $(D -back). +Returns: + The number of elements brought to the front, i.e., the length of $(D back). See_Also: - $(WEB sgi.com/tech/stl/_rotate.html, STL's rotate) + $(HTTP sgi.com/tech/stl/_rotate.html, STL's rotate) */ -size_t bringToFront(Range1, Range2)(Range1 front, Range2 back) - if (isInputRange!Range1 && isForwardRange!Range2) +size_t bringToFront(InputRange, ForwardRange)(InputRange front, ForwardRange back) +if (isInputRange!InputRange && isForwardRange!ForwardRange) +{ + import std.string : representation; + + static if (isNarrowString!InputRange) + { + auto frontW = representation(front); + } + else + { + alias frontW = front; + } + static if (isNarrowString!ForwardRange) + { + auto backW = representation(back); + } + else + { + alias backW = back; + } + + return bringToFrontImpl(frontW, backW); +} + +private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back) +if (isInputRange!InputRange && isForwardRange!ForwardRange) { - import std.range: Take, take; + import std.range : take, Take; + import std.array : sameHead; enum bool sameHeadExists = is(typeof(front.sameHead(back))); size_t result; + for (bool semidone; !front.empty && !back.empty; ) { static if (sameHeadExists) @@ -154,7 +192,7 @@ size_t bringToFront(Range1, Range2)(Range1 front, Range2 back) { assert(front.empty); // Left side was shorter. Let's step into the back. - static if (is(Range1 == Take!Range2)) + static if (is(InputRange == Take!ForwardRange)) { front = take(back0, nswaps); } @@ -192,6 +230,7 @@ the example below, $(D r2) is a right subrange of $(D r1). { import std.algorithm.comparison : equal; import std.container : SList; + import std.range.primitives : popFrontN; auto list = SList!(int)(4, 5, 6, 7, 1, 2, 3); auto r1 = list[]; @@ -201,7 +240,6 @@ the example below, $(D r2) is a right subrange of $(D r1). assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ])); } - /** Elements can be swapped across ranges of different types: */ @@ -217,15 +255,32 @@ Elements can be swapped across ranges of different types: assert(equal(vec, [ 5, 6, 7 ])); } +/** +Unicode integrity is not preserved: +*/ +@safe unittest +{ + import std.string : representation; + auto ar = representation("a".dup); + auto br = representation("ç".dup); + + bringToFront(ar, br); + + auto a = cast(char[]) ar; + auto b = cast(char[]) br; + + // Illegal UTF-8 + assert(a == "\303"); + // Illegal UTF-8 + assert(b == "\247a"); +} + @safe unittest { import std.algorithm.comparison : equal; import std.conv : text; import std.random : Random, unpredictableSeed, uniform; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - // a more elaborate test { auto rnd = Random(unpredictableSeed); @@ -281,72 +336,93 @@ Elements can be swapped across ranges of different types: bringToFront(r1, r2) == 4 || assert(0); assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); } + + // Bugzilla 16959 + auto arr = ['4', '5', '6', '7', '1', '2', '3']; + auto p = bringToFront(arr[0 .. 4], arr[4 .. $]); + + assert(p == arr.length - 4); + assert(arr == ['1', '2', '3', '4', '5', '6', '7']); } +// Tests if types are arrays and support slice assign. +private enum bool areCopyCompatibleArrays(T1, T2) = + isArray!T1 && isArray!T2 && is(typeof(T2.init[] = T1.init[])); + // copy /** Copies the content of $(D source) into $(D target) and returns the remaining (unfilled) part of $(D target). -Preconditions: $(D target) shall have enough room to accomodate +Preconditions: $(D target) shall have enough room to accommodate the entirety of $(D source). +Params: + source = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + target = an output range + +Returns: + The unfilled part of target + See_Also: - $(WEB sgi.com/tech/stl/_copy.html, STL's _copy) + $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy) */ TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) -if (isInputRange!SourceRange && isOutputRange!(TargetRange, ElementType!SourceRange)) +if (isInputRange!SourceRange) { - static TargetRange genericImpl(SourceRange source, TargetRange target) + static if (areCopyCompatibleArrays!(SourceRange, TargetRange)) { - // Specialize for 2 random access ranges. - // Typically 2 random access ranges are faster iterated by common - // index then by x.popFront(), y.popFront() pair - static if (isRandomAccessRange!SourceRange && hasLength!SourceRange - && hasSlicing!TargetRange && isRandomAccessRange!TargetRange && hasLength!TargetRange) - { - assert(target.length >= source.length, + const tlen = target.length; + const slen = source.length; + assert(tlen >= slen, "Cannot copy a source range into a smaller target range."); - auto len = source.length; - foreach (idx; 0 .. len) + immutable overlaps = __ctfe || () @trusted { + return source.ptr < target.ptr + tlen && + target.ptr < source.ptr + slen; }(); + + if (overlaps) + { + foreach (idx; 0 .. slen) target[idx] = source[idx]; - return target[len .. target.length]; + return target[slen .. tlen]; } else { - put(target, source); - return target; + // Array specialization. This uses optimized memory copying + // routines under the hood and is about 10-20x faster than the + // generic implementation. + target[0 .. slen] = source[]; + return target[slen .. $]; } } - - import std.traits : isArray; - static if (isArray!SourceRange && isArray!TargetRange && - is(Unqual!(typeof(source[0])) == Unqual!(typeof(target[0])))) + else static if (isOutputRange!(TargetRange, ElementType!SourceRange)) { - immutable overlaps = () @trusted { - return source.ptr < target.ptr + target.length && - target.ptr < source.ptr + source.length; }(); - - if (overlaps) + // Specialize for 2 random access ranges. + // Typically 2 random access ranges are faster iterated by common + // index than by x.popFront(), y.popFront() pair + static if (isRandomAccessRange!SourceRange && + hasLength!SourceRange && + hasSlicing!TargetRange && + isRandomAccessRange!TargetRange && + hasLength!TargetRange) { - return genericImpl(source, target); + auto len = source.length; + foreach (idx; 0 .. len) + target[idx] = source[idx]; + return target[len .. target.length]; } else { - // Array specialization. This uses optimized memory copying - // routines under the hood and is about 10-20x faster than the - // generic implementation. - assert(target.length >= source.length, - "Cannot copy a source array into a smaller target array."); - target[0 .. source.length] = source[]; - - return target[source.length .. $]; + put(target, source); + return target; } } else { - return genericImpl(source, target); + enum msg = "TargetRange is neither copy-compatible with the SourceRange" ~ + "nor an OutputRange with the same ElementType."; + static assert(0, msg); } } @@ -375,7 +451,7 @@ range elements, different types of ranges are accepted: /** To _copy at most $(D n) elements from a range, you may want to use -$(XREF range, take): +$(REF take, std,range): */ @safe unittest { @@ -402,8 +478,8 @@ use $(LREF filter): } /** -$(XREF range, retro) can be used to achieve behavior similar to -$(WEB sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): +$(REF retro, std,range) can be used to achieve behavior similar to +$(HTTP sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): */ @safe unittest { @@ -414,12 +490,18 @@ $(WEB sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): assert(dest == [0, 0, 1, 2, 4]); } +// Test CTFE copy. +@safe unittest +{ + enum c = copy([1,2,3], [4,5,6,7]); + assert(c == [7]); +} + + @safe unittest { import std.algorithm.iteration : filter; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); { int[] a = [ 1, 5 ]; int[] b = [ 9, 8 ]; @@ -429,8 +511,8 @@ $(WEB sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): { int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - copy(a[5..10], a[4..9]); - assert(a[4..9] == [6, 7, 8, 9, 10]); + copy(a[5 .. 10], a[4 .. 9]); + assert(a[4 .. 9] == [6, 7, 8, 9, 10]); } { // Test for bug 7898 @@ -445,20 +527,37 @@ $(WEB sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): } } +@safe unittest +{ + // Issue 13650 + import std.meta : AliasSeq; + foreach (Char; AliasSeq!(char, wchar, dchar)) + { + Char[3] a1 = "123"; + Char[6] a2 = "456789"; + assert(copy(a1[], a2[]) is a2[3..$]); + assert(a1[] == "123"); + assert(a2[] == "123789"); + } +} + /** -Assigns $(D value) to each element of input range $(D range). +Assigns $(D value) to each element of input _range $(D range). Params: - range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements - and has assignable elements + range = An + $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + that exposes references to its elements and has assignable + elements value = Assigned to each element of range See_Also: $(LREF uninitializedFill) $(LREF initializeAll) */ -void fill(Range, Value)(Range range, Value value) - if (isInputRange!Range && is(typeof(range.front = value))) +void fill(Range, Value)(auto ref Range range, auto ref Value value) +if ((isInputRange!Range && is(typeof(range.front = value)) || + isSomeChar!Value && is(typeof(range[] = value)))) { alias T = ElementType!Range; @@ -487,14 +586,74 @@ void fill(Range, Value)(Range range, Value value) assert(a == [ 5, 5, 5, 5 ]); } +// issue 16342, test fallback on mutable narrow strings +@safe unittest +{ + char[] chars = ['a', 'b']; + fill(chars, 'c'); + assert(chars == "cc"); + + char[2] chars2 = ['a', 'b']; + fill(chars2, 'c'); + assert(chars2 == "cc"); + + wchar[] wchars = ['a', 'b']; + fill(wchars, wchar('c')); + assert(wchars == "cc"w); + + dchar[] dchars = ['a', 'b']; + fill(dchars, dchar('c')); + assert(dchars == "cc"d); +} + +@nogc @safe unittest +{ + const(char)[] chars; + static assert(!__traits(compiles, fill(chars, 'c'))); + wstring wchars; + static assert(!__traits(compiles, fill(wchars, wchar('c')))); +} + +@nogc @safe unittest +{ + char[] chars; + fill(chars, 'c'); + assert(chars == ""c); +} + +@safe unittest +{ + shared(char)[] chrs = ['r']; + fill(chrs, 'c'); + assert(chrs == [shared(char)('c')]); +} + +@nogc @safe unittest +{ + struct Str(size_t len) + { + private char[len] _data; + void opIndexAssign(char value) @safe @nogc + {_data[] = value;} + } + Str!2 str; + str.fill(':'); + assert(str._data == "::"); +} + +@safe unittest +{ + char[] chars = ['a','b','c','d']; + chars[1 .. 3].fill(':'); + assert(chars == "a::d"); +} +// end issue 16342 + @safe unittest { import std.conv : text; import std.internal.test.dummyrange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[] a = [ 1, 2, 3 ]; fill(a, 6); assert(a == [ 6, 6, 6 ], text(a)); @@ -507,8 +666,6 @@ void fill(Range, Value)(Range range, Value value) } } void fun1() { foreach (i; 0 .. 1000) fill(a, 6); } - //void fun2() { foreach (i; 0 .. 1000) fill2(a, 6); } - //writeln(benchmark!(fun0, fun1, fun2)(10000)); // fill should accept InputRange alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); @@ -545,12 +702,14 @@ void fill(Range, Value)(Range range, Value value) { int[] a = [1, 2, 3]; immutable(int) b = 0; - static assert(__traits(compiles, a.fill(b))); + a.fill(b); + assert(a == [0, 0, 0]); } { double[] a = [1, 2, 3]; immutable(int) b = 0; - static assert(__traits(compiles, a.fill(b))); + a.fill(b); + assert(a == [0, 0, 0]); } } @@ -560,21 +719,22 @@ $(D range) does not have to be a multiple of the length of $(D filler). If $(D filler) is empty, an exception is thrown. Params: - range = An $(XREF2 range, isInputRange, input range) that exposes - references to its elements and has assignable elements. - filler = The $(XREF2 range, isForwardRange, forward range) representing the - _fill pattern. + range = An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + that exposes references to its elements and has assignable elements. + filler = The + $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives) + representing the _fill pattern. */ -void fill(Range1, Range2)(Range1 range, Range2 filler) - if (isInputRange!Range1 - && (isForwardRange!Range2 - || (isInputRange!Range2 && isInfinite!Range2)) - && is(typeof(Range1.init.front = Range2.init.front))) +void fill(InputRange, ForwardRange)(InputRange range, ForwardRange filler) +if (isInputRange!InputRange + && (isForwardRange!ForwardRange + || (isInputRange!ForwardRange && isInfinite!ForwardRange)) + && is(typeof(InputRange.init.front = ForwardRange.init.front))) { - static if (isInfinite!Range2) + static if (isInfinite!ForwardRange) { - //Range2 is infinite, no need for bounds checking or saving - static if (hasSlicing!Range2 && hasLength!Range1 + //ForwardRange is infinite, no need for bounds checking or saving + static if (hasSlicing!ForwardRange && hasLength!InputRange && is(typeof(filler[0 .. range.length]))) { copy(filler[0 .. range.length], range); @@ -594,11 +754,11 @@ void fill(Range1, Range2)(Range1 range, Range2 filler) enforce(!filler.empty, "Cannot fill range with an empty filler"); - static if (hasLength!Range1 && hasLength!Range2 + static if (hasLength!InputRange && hasLength!ForwardRange && is(typeof(range.length > filler.length))) { //Case we have access to length - auto len = filler.length; + immutable len = filler.length; //Start by bulk copies while (range.length > len) { @@ -606,7 +766,7 @@ void fill(Range1, Range2)(Range1 range, Range2 filler) } //and finally fill the partial range. No need to save here. - static if (hasSlicing!Range2 && is(typeof(filler[0 .. range.length]))) + static if (hasSlicing!ForwardRange && is(typeof(filler[0 .. range.length]))) { //use a quick copy auto len2 = range.length; @@ -648,9 +808,6 @@ void fill(Range1, Range2)(Range1 range, Range2 filler) import std.exception : assertThrown; import std.internal.test.dummyrange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[] a = [ 1, 2, 3, 4, 5 ]; int[] b = [1, 2]; fill(a, b); @@ -661,7 +818,7 @@ void fill(Range1, Range2)(Range1 range, Range2 filler) InputRange range; fill(range,[1,2]); foreach (i,value;range.arr) - assert(value == (i%2==0?1:2)); + assert(value == (i%2 == 0?1:2)); //test with a input being a "reference forward" range fill(a, new ReferenceForwardRange!int([8, 9])); @@ -680,66 +837,94 @@ Initializes all elements of $(D range) with their $(D .init) value. Assumes that the elements of the range are uninitialized. Params: - range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements - and has assignable elements + range = An + $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + that exposes references to its elements and has assignable + elements See_Also: $(LREF fill) $(LREF uninitializeFill) - -Example: ----- -struct S { ... } -S[] s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; -initializeAll(s); -assert(s == [ 0, 0, 0, 0, 0 ]); ----- */ void initializeAll(Range)(Range range) - if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) +if (isInputRange!Range) { - import core.stdc.string : memset, memcpy; - import std.traits : hasElaborateAssign, isDynamicArray; - - alias T = ElementType!Range; - static if (hasElaborateAssign!T) + static if (hasLvalueElements!Range && hasAssignableElements!Range) { - import std.algorithm.internal : addressOf; - //Elaborate opAssign. Must go the memcpy road. - //We avoid calling emplace here, because our goal is to initialize to - //the static state of T.init, - //So we want to avoid any un-necassarilly CC'ing of T.init - auto p = typeid(T).init().ptr; - if (p) - for ( ; !range.empty ; range.popFront() ) - memcpy(addressOf(range.front), p, T.sizeof); - else - static if (isDynamicArray!Range) - memset(range.ptr, 0, range.length * T.sizeof); - else + import core.stdc.string : memset, memcpy; + import std.traits : hasElaborateAssign, isDynamicArray; + + alias T = ElementType!Range; + static if (hasElaborateAssign!T) + { + import std.algorithm.internal : addressOf; + //Elaborate opAssign. Must go the memcpy road. + //We avoid calling emplace here, because our goal is to initialize to + //the static state of T.init, + //So we want to avoid any un-necassarilly CC'ing of T.init + auto p = typeid(T).initializer(); + if (p.ptr) + { for ( ; !range.empty ; range.popFront() ) - memset(addressOf(range.front), 0, T.sizeof); + { + static if (__traits(isStaticArray, T)) + { + // static array initializer only contains initialization + // for one element of the static array. + auto elemp = cast(void *) addressOf(range.front); + auto endp = elemp + T.sizeof; + while (elemp < endp) + { + memcpy(elemp, p.ptr, p.length); + elemp += p.length; + } + } + else + { + memcpy(addressOf(range.front), p.ptr, T.sizeof); + } + } + } + else + static if (isDynamicArray!Range) + memset(range.ptr, 0, range.length * T.sizeof); + else + for ( ; !range.empty ; range.popFront() ) + memset(addressOf(range.front), 0, T.sizeof); + } + else + fill(range, T.init); } - else - fill(range, T.init); + else static if (is(Range == char[]) || is(Range == wchar[])) + { + alias T = ElementEncodingType!Range; + range[] = T.init; + } + else static assert(0, "Range doesn't have assignable elements."); } -// ditto -void initializeAll(Range)(Range range) - if (is(Range == char[]) || is(Range == wchar[])) +/// +@system unittest { - alias T = ElementEncodingType!Range; - range[] = T.init; + import core.stdc.stdlib : malloc, free; + + struct S + { + int a = 10; + } + + auto s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; + initializeAll(s); + assert(s == [S(10), S(10), S(10), S(10), S(10)]); + + scope(exit) free(s.ptr); } -unittest +@system unittest { import std.algorithm.iteration : filter; + import std.meta : AliasSeq; import std.traits : hasElaborateAssign; - import std.typetuple : TypeTuple; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); //Test strings: //Must work on narrow strings. @@ -778,16 +963,16 @@ unittest int i = 1; this(this){} } - static assert (!hasElaborateAssign!S1); - static assert (!hasElaborateAssign!S2); - static assert ( hasElaborateAssign!S3); - static assert ( hasElaborateAssign!S4); - assert (!typeid(S1).init().ptr); - assert ( typeid(S2).init().ptr); - assert (!typeid(S3).init().ptr); - assert ( typeid(S4).init().ptr); + static assert(!hasElaborateAssign!S1); + static assert(!hasElaborateAssign!S2); + static assert( hasElaborateAssign!S3); + static assert( hasElaborateAssign!S4); + assert(!typeid(S1).initializer().ptr); + assert( typeid(S2).initializer().ptr); + assert(!typeid(S3).initializer().ptr); + assert( typeid(S4).initializer().ptr); - foreach(S; TypeTuple!(S1, S2, S3, S4)) + foreach (S; AliasSeq!(S1, S2, S3, S4)) { //initializeAll { @@ -819,72 +1004,54 @@ unittest } } +// test that initializeAll works for arrays of static arrays of structs with +// elaborate assigns. +@system unittest +{ + struct Int { + ~this() {} + int x = 3; + } + Int[2] xs = [Int(1), Int(2)]; + struct R { + bool done; + bool empty() { return done; } + ref Int[2] front() { return xs; } + void popFront() { done = true; } + } + initializeAll(R()); + assert(xs[0].x == 3); + assert(xs[1].x == 3); +} + // move /** -Moves $(D source) into $(D target) via a destructive copy. +Moves `source` into `target`, via a destructive copy when necessary. + +If `T` is a struct with a destructor or postblit defined, source is reset +to its `.init` value after it is moved into target, otherwise it is +left unchanged. + +Preconditions: +If source has internal pointers that point to itself, it cannot be moved, and +will trigger an assertion failure. Params: - source = Data to copy. If a destructor or postblit is defined, it is reset - to its $(D .init) value after it is moved into target. Note that data - with internal pointers that point to itself cannot be moved, and will - trigger an assertion failure. + source = Data to copy. target = Where to copy into. The destructor, if any, is invoked before the copy is performed. */ void move(T)(ref T source, ref T target) { - import core.stdc.string : memcpy; - import std.traits : hasAliasing, hasElaborateAssign, - hasElaborateCopyConstructor, hasElaborateDestructor, - isAssignable; - - static if (!is( T == class) && hasAliasing!T) if (!__ctfe) - { - import std.exception : doesPointTo; - assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); - } - - static if (is(T == struct)) - { - if (&source == &target) return; - // Most complicated case. Destroy whatever target had in it - // and bitblast source over it - static if (hasElaborateDestructor!T) typeid(T).destroy(&target); - - static if (hasElaborateAssign!T || !isAssignable!T) - memcpy(&target, &source, T.sizeof); - else - target = source; - - // If the source defines a destructor or a postblit hook, we must obliterate the - // object in order to avoid double freeing and undue aliasing - static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) - { - import std.algorithm.searching : endsWith; - - static T empty; - static if (T.tupleof.length > 0 && - T.tupleof[$-1].stringof.endsWith("this")) - { - // If T is nested struct, keep original context pointer - memcpy(&source, &empty, T.sizeof - (void*).sizeof); - } - else - { - memcpy(&source, &empty, T.sizeof); - } - } - } + // test @safe destructible + static if (__traits(compiles, (T t) @safe {})) + trustedMoveImpl(source, target); else - { - // Primitive data (including pointers and arrays) or class - - // assignment works great - target = source; - } + moveImpl(source, target); } -/// -unittest +/// For non-struct types, `move` just performs `target = source`: +@safe unittest { Object obj1 = new Object; Object obj2 = obj1; @@ -892,10 +1059,12 @@ unittest move(obj2, obj3); assert(obj3 is obj1); + // obj2 unchanged + assert(obj2 is obj1); } /// -unittest +pure nothrow @safe @nogc unittest { // Structs without destructors are simply copied struct S1 @@ -908,8 +1077,8 @@ unittest move(s11, s12); - assert(s11.a == 10 && s11.b == 11 && - s12.a == 10 && s12.b == 11); + assert(s12 == S1(10, 11)); + assert(s11 == s12); // But structs with destructors or postblits are reset to their .init value // after copying to the target. @@ -918,23 +1087,22 @@ unittest int a = 1; int b = 2; - ~this() { } + ~this() pure nothrow @safe @nogc { } } S2 s21 = { 3, 4 }; S2 s22; move(s21, s22); - assert(s21.a == 1 && s21.b == 2 && - s22.a == 3 && s22.b == 4); + assert(s21 == S2(1, 2)); + assert(s22 == S2(3, 4)); } -unittest +@safe unittest { import std.traits; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); import std.exception : assertCTFEable; + assertCTFEable!((){ Object obj1 = new Object; Object obj2 = obj1; @@ -985,67 +1153,70 @@ unittest class S5; S5 s51; - static assert(__traits(compiles, move(s51, s51)), - "issue 13990, cannot move opaque class reference"); + S5 s52 = s51; + S5 s53; + move(s52, s53); + assert(s53 is s51); } /// Ditto T move(T)(ref T source) { - import core.stdc.string : memcpy; - import std.traits : hasAliasing, hasElaborateAssign, - hasElaborateCopyConstructor, hasElaborateDestructor, - isAssignable; + // test @safe destructible + static if (__traits(compiles, (T t) @safe {})) + return trustedMoveImpl(source); + else + return moveImpl(source); +} - static if (!is(T == class) && hasAliasing!T) if (!__ctfe) +/// Non-copyable structs can still be moved: +pure nothrow @safe @nogc unittest +{ + struct S { - import std.exception : doesPointTo; - assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); + @disable this(this); + ~this() pure nothrow @safe @nogc {} } + S s1; + S s2 = move(s1); +} - T result = void; - static if (is(T == struct)) - { - // Can avoid destructing result. - static if (hasElaborateAssign!T || !isAssignable!T) - memcpy(&result, &source, T.sizeof); - else - result = source; +private void trustedMoveImpl(T)(ref T source, ref T target) @trusted +{ + moveImpl(source, target); +} - // If the source defines a destructor or a postblit hook, we must obliterate the - // object in order to avoid double freeing and undue aliasing - static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) - { - import std.algorithm.searching : endsWith; +private void moveImpl(T)(ref T source, ref T target) +{ + import std.traits : hasElaborateDestructor; - static T empty; - static if (T.tupleof.length > 0 && - T.tupleof[$-1].stringof.endsWith("this")) - { - // If T is nested struct, keep original context pointer - memcpy(&source, &empty, T.sizeof - (void*).sizeof); - } - else - { - memcpy(&source, &empty, T.sizeof); - } - } - } - else + static if (is(T == struct)) { - // Primitive data (including pointers and arrays) or class - - // assignment works great - result = source; + if (&source == &target) return; + // Destroy target before overwriting it + static if (hasElaborateDestructor!T) target.__xdtor(); } + // move and emplace source into target + moveEmplace(source, target); +} + +private T trustedMoveImpl(T)(ref T source) @trusted +{ + return moveImpl(source); +} + +private T moveImpl(T)(ref T source) +{ + T result = void; + moveEmplace(source, result); return result; } -unittest +@safe unittest { import std.traits; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); import std.exception : assertCTFEable; + assertCTFEable!((){ Object obj1 = new Object; Object obj2 = obj1; @@ -1094,18 +1265,34 @@ unittest class S5; S5 s51; - static assert(__traits(compiles, s51 = move(s51)), - "issue 13990, cannot move opaque class reference"); + S5 s52 = s51; + S5 s53; + s53 = move(s52); + assert(s53 is s51); +} + +@system unittest +{ + static struct S { int n = 0; ~this() @system { n = 0; } } + S a, b; + static assert(!__traits(compiles, () @safe { move(a, b); })); + static assert(!__traits(compiles, () @safe { move(a); })); + a.n = 1; + () @trusted { move(a, b); }(); + assert(a.n == 0); + a.n = 1; + () @trusted { move(a); }(); + assert(a.n == 0); } -unittest//Issue 6217 +@safe unittest//Issue 6217 { import std.algorithm.iteration : map; auto x = map!"a"([1,2,3]); x = move(x); } -unittest// Issue 8055 +@safe unittest// Issue 8055 { static struct S { @@ -1125,7 +1312,7 @@ unittest// Issue 8055 assert(b.x == 0); } -unittest// Issue 8057 +@system unittest// Issue 8057 { int n = 10; struct S @@ -1157,102 +1344,275 @@ unittest// Issue 8057 } } Array!int.Payload x = void; - static assert(__traits(compiles, move(x) )); - static assert(__traits(compiles, move(x, x) )); + move(x); + move(x, x); } -// moveAll /** -For each element $(D a) in $(D src) and each element $(D b) in $(D -tgt) in lockstep in increasing order, calls $(D move(a, b)). - -Preconditions: -$(D walkLength(src) <= walkLength(tgt)). -An exception will be thrown if this condition does not hold, i.e., there is not -enough room in $(D tgt) to accommodate all of $(D src). - -Params: - src = An $(XREF2 range, isInputRange, input range) with movable elements. - tgt = An $(XREF2 range, isInputRange, input range) with elements that - elements from $(D src) can be moved into. - -Returns: The leftover portion of $(D tgt) after all elements from $(D src) have -been moved. + * Similar to $(LREF move) but assumes `target` is uninitialized. This + * is more efficient because `source` can be blitted over `target` + * without destroying or initializing it first. + * + * Params: + * source = value to be moved into target + * target = uninitialized value to be filled by source */ -Range2 moveAll(Range1, Range2)(Range1 src, Range2 tgt) -if (isInputRange!Range1 && isInputRange!Range2 - && is(typeof(move(src.front, tgt.front)))) +void moveEmplace(T)(ref T source, ref T target) @system { - import std.exception : enforce; + import core.stdc.string : memcpy, memset; + import std.traits : hasAliasing, hasElaborateAssign, + hasElaborateCopyConstructor, hasElaborateDestructor, + isAssignable; - static if (isRandomAccessRange!Range1 && hasLength!Range1 && hasLength!Range2 - && hasSlicing!Range2 && isRandomAccessRange!Range2) + static if (!is(T == class) && hasAliasing!T) if (!__ctfe) { - auto toMove = src.length; - enforce(toMove <= tgt.length); // shouldn't this be an assert? - foreach (idx; 0 .. toMove) - move(src[idx], tgt[idx]); - return tgt[toMove .. tgt.length]; + import std.exception : doesPointTo; + assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); } - else + + static if (is(T == struct)) { - for (; !src.empty; src.popFront(), tgt.popFront()) + assert(&source !is &target, "source and target must not be identical"); + + static if (hasElaborateAssign!T || !isAssignable!T) + memcpy(&target, &source, T.sizeof); + else + target = source; + + // If the source defines a destructor or a postblit hook, we must obliterate the + // object in order to avoid double freeing and undue aliasing + static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) { - enforce(!tgt.empty); //ditto? - move(src.front, tgt.front); + // If T is nested struct, keep original context pointer + static if (__traits(isNested, T)) + enum sz = T.sizeof - (void*).sizeof; + else + enum sz = T.sizeof; + + auto init = typeid(T).initializer(); + if (init.ptr is null) // null ptr means initialize to 0s + memset(&source, 0, sz); + else + memcpy(&source, init.ptr, sz); } - return tgt; + } + else + { + // Primitive data (including pointers and arrays) or class - + // assignment works great + target = source; } } /// -unittest +pure nothrow @nogc @system unittest { - int[] a = [ 1, 2, 3 ]; - int[] b = new int[5]; - assert(moveAll(a, b) is b[3 .. $]); - assert(a == b[0 .. 3]); - assert(a == [ 1, 2, 3 ]); + static struct Foo + { + pure nothrow @nogc: + this(int* ptr) { _ptr = ptr; } + ~this() { if (_ptr) ++*_ptr; } + int* _ptr; + } + + int val; + Foo foo1 = void; // uninitialized + auto foo2 = Foo(&val); // initialized + assert(foo2._ptr is &val); + + // Using `move(foo2, foo1)` would have an undefined effect because it would destroy + // the uninitialized foo1. + // moveEmplace directly overwrites foo1 without destroying or initializing it first. + moveEmplace(foo2, foo1); + assert(foo1._ptr is &val); + assert(foo2._ptr is null); + assert(val == 0); } -// moveSome +// moveAll /** -For each element $(D a) in $(D src) and each element $(D b) in $(D -tgt) in lockstep in increasing order, calls $(D move(a, b)). Stops -when either $(D src) or $(D tgt) have been exhausted. +Calls `move(a, b)` for each element `a` in `src` and the corresponding +element `b` in `tgt`, in increasing order. + +Preconditions: +`walkLength(src) <= walkLength(tgt)`. +This precondition will be asserted. If you cannot ensure there is enough room in +`tgt` to accommodate all of `src` use $(LREF moveSome) instead. Params: - src = An $(XREF2 range, isInputRange, input range) with movable elements. - tgt = An $(XREF2 range, isInputRange, input range) with elements that - elements from $(D src) can be moved into. + src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with + movable elements. + tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with + elements that elements from $(D src) can be moved into. -Returns: The leftover portions of the two ranges after one or the other of the -ranges have been exhausted. +Returns: The leftover portion of $(D tgt) after all elements from $(D src) have +been moved. */ -Tuple!(Range1, Range2) moveSome(Range1, Range2)(Range1 src, Range2 tgt) -if (isInputRange!Range1 && isInputRange!Range2 +InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) +if (isInputRange!InputRange1 && isInputRange!InputRange2 && is(typeof(move(src.front, tgt.front)))) +{ + return moveAllImpl!move(src, tgt); +} + +/// +pure nothrow @safe @nogc unittest +{ + int[3] a = [ 1, 2, 3 ]; + int[5] b; + assert(moveAll(a[], b[]) is b[3 .. $]); + assert(a[] == b[0 .. 3]); + int[3] cmp = [ 1, 2, 3 ]; + assert(a[] == cmp[]); +} + +/** + * Similar to $(LREF moveAll) but assumes all elements in `tgt` are + * uninitialized. Uses $(LREF moveEmplace) to move elements from + * `src` over elements from `tgt`. + */ +InputRange2 moveEmplaceAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system +if (isInputRange!InputRange1 && isInputRange!InputRange2 + && is(typeof(moveEmplace(src.front, tgt.front)))) +{ + return moveAllImpl!moveEmplace(src, tgt); +} + +/// +pure nothrow @nogc @system unittest +{ + static struct Foo + { + ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; } + int* _ptr; + } + int[3] refs = [0, 1, 2]; + Foo[3] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2])]; + Foo[5] dst = void; + + auto tail = moveEmplaceAll(src[], dst[]); // move 3 value from src over dst + assert(tail.length == 2); // returns remaining uninitialized values + initializeAll(tail); + + import std.algorithm.searching : all; + assert(src[].all!(e => e._ptr is null)); + assert(dst[0 .. 3].all!(e => e._ptr !is null)); +} + +@system unittest +{ + struct InputRange + { + ref int front() { return data[0]; } + void popFront() { data.popFront; } + bool empty() { return data.empty; } + int[] data; + } + auto a = InputRange([ 1, 2, 3 ]); + auto b = InputRange(new int[5]); + moveAll(a, b); + assert(a.data == b.data[0 .. 3]); + assert(a.data == [ 1, 2, 3 ]); +} + +private InputRange2 moveAllImpl(alias moveOp, InputRange1, InputRange2)( + ref InputRange1 src, ref InputRange2 tgt) { import std.exception : enforce; - for (; !src.empty && !tgt.empty; src.popFront(), tgt.popFront()) + static if (isRandomAccessRange!InputRange1 && hasLength!InputRange1 && hasLength!InputRange2 + && hasSlicing!InputRange2 && isRandomAccessRange!InputRange2) { - enforce(!tgt.empty); - move(src.front, tgt.front); + auto toMove = src.length; + assert(toMove <= tgt.length); + foreach (idx; 0 .. toMove) + moveOp(src[idx], tgt[idx]); + return tgt[toMove .. tgt.length]; } - return tuple(src, tgt); + else + { + for (; !src.empty; src.popFront(), tgt.popFront()) + { + assert(!tgt.empty); + moveOp(src.front, tgt.front); + } + return tgt; + } +} + +// moveSome +/** +Calls `move(a, b)` for each element `a` in `src` and the corresponding +element `b` in `tgt`, in increasing order, stopping when either range has been +exhausted. + +Params: + src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with + movable elements. + tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with + elements that elements from $(D src) can be moved into. + +Returns: The leftover portions of the two ranges after one or the other of the +ranges have been exhausted. + */ +Tuple!(InputRange1, InputRange2) moveSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) +if (isInputRange!InputRange1 && isInputRange!InputRange2 + && is(typeof(move(src.front, tgt.front)))) +{ + return moveSomeImpl!move(src, tgt); } /// -unittest +pure nothrow @safe @nogc unittest { - int[] a = [ 1, 2, 3, 4, 5 ]; - int[] b = new int[3]; - assert(moveSome(a, b)[0] is a[3 .. $]); + int[5] a = [ 1, 2, 3, 4, 5 ]; + int[3] b; + assert(moveSome(a[], b[])[0] is a[3 .. $]); assert(a[0 .. 3] == b); assert(a == [ 1, 2, 3, 4, 5 ]); } +/** + * Same as $(LREF moveSome) but assumes all elements in `tgt` are + * uninitialized. Uses $(LREF moveEmplace) to move elements from + * `src` over elements from `tgt`. + */ +Tuple!(InputRange1, InputRange2) moveEmplaceSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system +if (isInputRange!InputRange1 && isInputRange!InputRange2 + && is(typeof(move(src.front, tgt.front)))) +{ + return moveSomeImpl!moveEmplace(src, tgt); +} + +/// +pure nothrow @nogc @system unittest +{ + static struct Foo + { + ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; } + int* _ptr; + } + int[4] refs = [0, 1, 2, 3]; + Foo[4] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2]), Foo(&refs[3])]; + Foo[3] dst = void; + + auto res = moveEmplaceSome(src[], dst[]); + + import std.algorithm.searching : all; + assert(src[0 .. 3].all!(e => e._ptr is null)); + assert(src[3]._ptr !is null); + assert(dst[].all!(e => e._ptr !is null)); +} + +private Tuple!(InputRange1, InputRange2) moveSomeImpl(alias moveOp, InputRange1, InputRange2)( + ref InputRange1 src, ref InputRange2 tgt) +{ + for (; !src.empty && !tgt.empty; src.popFront(), tgt.popFront()) + moveOp(src.front, tgt.front); + return tuple(src, tgt); + } + + // SwapStrategy /** Defines the swapping strategy for algorithms that need to swap @@ -1318,8 +1678,8 @@ In the case above the element at offset $(D 1) is removed and $(D remove) returns the range smaller by one element. The original array has remained of the same length because all functions in $(D std.algorithm) only change $(I content), not $(I topology). The value -$(D 8) is repeated because $(XREF algorithm, move) was invoked to move -elements around and on integers $(D move) simply copies the source to +$(D 8) is repeated because $(LREF move) was invoked to +move elements around and on integers $(D move) simply copies the source to the destination. To replace $(D a) with the effect of the removal, simply assign $(D a = remove(a, 1)). The slice will be rebound to the shorter array and the operation completes with maximal efficiency. @@ -1341,7 +1701,7 @@ offsets can be passed in. ---- int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; -assert(remove(a, 1, tuple(3, 5), 9) == [ 0, 2, 6, 7, 8, 10 ]); +assert(remove(a, 1, tuple(3, 5), 9) == [ 0, 2, 5, 6, 7, 8, 10 ]); ---- In this case, the slots at positions 1, 3, 4, and 9 are removed from @@ -1364,11 +1724,11 @@ of the stability requirement, $(D remove) moved elements from the end of the array over the slots to be removed. This way there is less data movement to be done which improves the execution time of the function. -The function $(D remove) works on any forward range. The moving -strategy is (listed from fastest to slowest): $(UL $(LI If $(D s == -SwapStrategy.unstable && isRandomAccessRange!Range && hasLength!Range -&& hasLvalueElements!Range), then elements are moved from the end -of the range into the slots to be filled. In this case, the absolute +The function $(D remove) works on bidirectional ranges that have assignable +lvalue elements. The moving strategy is (listed from fastest to slowest): +$(UL $(LI If $(D s == SwapStrategy.unstable && isRandomAccessRange!Range && +hasLength!Range && hasLvalueElements!Range), then elements are moved from the +end of the range into the slots to be filled. In this case, the absolute minimum of moves is performed.) $(LI Otherwise, if $(D s == SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range && hasLvalueElements!Range), then elements are still moved from the @@ -1377,138 +1737,164 @@ calls to $(D range.popFront).) $(LI Otherwise, elements are moved incrementally towards the front of $(D range); a given element is never moved several times, but more elements are moved than in the previous cases.)) + +Params: + s = a SwapStrategy to determine if the original order needs to be preserved + range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) + with a length member + offset = which element(s) to remove + +Returns: + a range containing all of the elements of range with offset removed */ Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) -if (s != SwapStrategy.stable - && isBidirectionalRange!Range +if (isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1) { - import std.algorithm.comparison : min; - - Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; - foreach (i, v; offset) + static if (s == SwapStrategy.unstable) { - static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) - { - blackouts[i].pos = v[0]; - blackouts[i].len = v[1] - v[0]; - } - else + Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; + foreach (i, v; offset) { - static assert(is(typeof(v) : size_t), typeof(v).stringof); - blackouts[i].pos = v; - blackouts[i].len = 1; - } - static if (i > 0) - { - import std.exception : enforce; + static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) + { + blackouts[i].pos = v[0]; + blackouts[i].len = v[1] - v[0]; + } + else + { + static assert(is(typeof(v) : size_t), typeof(v).stringof); + blackouts[i].pos = v; + blackouts[i].len = 1; + } + static if (i > 0) + { + import std.exception : enforce; - enforce(blackouts[i - 1].pos + blackouts[i - 1].len - <= blackouts[i].pos, - "remove(): incorrect ordering of elements to remove"); + enforce(blackouts[i - 1].pos + blackouts[i - 1].len + <= blackouts[i].pos, + "remove(): incorrect ordering of elements to remove"); + } } - } - size_t left = 0, right = offset.length - 1; - auto tgt = range.save; - size_t steps = 0; + size_t left = 0, right = offset.length - 1; + auto tgt = range.save; + size_t tgtPos = 0; - while (left <= right) - { - // Look for a blackout on the right - if (blackouts[right].pos + blackouts[right].len >= range.length) + while (left <= right) { - range.popBackExactly(blackouts[right].len); + // Look for a blackout on the right + if (blackouts[right].pos + blackouts[right].len >= range.length) + { + range.popBackExactly(blackouts[right].len); - // Since right is unsigned, we must check for this case, otherwise - // we might turn it into size_t.max and the loop condition will not - // fail when it should. - if (right > 0) + // Since right is unsigned, we must check for this case, otherwise + // we might turn it into size_t.max and the loop condition will not + // fail when it should. + if (right > 0) + { + --right; + continue; + } + else + break; + } + // Advance to next blackout on the left + assert(blackouts[left].pos >= tgtPos); + tgt.popFrontExactly(blackouts[left].pos - tgtPos); + tgtPos = blackouts[left].pos; + + // Number of elements to the right of blackouts[right] + immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); + size_t toMove = void; + if (tailLen < blackouts[left].len) { - --right; - continue; + toMove = tailLen; + blackouts[left].pos += toMove; + blackouts[left].len -= toMove; } else - break; - } - // Advance to next blackout on the left - assert(blackouts[left].pos >= steps); - tgt.popFrontExactly(blackouts[left].pos - steps); - steps = blackouts[left].pos; - auto toMove = min( - blackouts[left].len, - range.length - (blackouts[right].pos + blackouts[right].len)); - foreach (i; 0 .. toMove) - { - move(range.back, tgt.front); - range.popBack(); - tgt.popFront(); - } - steps += toMove; - if (toMove == blackouts[left].len) - { - // Filled the entire left hole - ++left; - continue; + { + toMove = blackouts[left].len; + ++left; + } + tgtPos += toMove; + foreach (i; 0 .. toMove) + { + move(range.back, tgt.front); + range.popBack(); + tgt.popFront(); + } } - } - - return range; -} -// Ditto -Range remove -(SwapStrategy s = SwapStrategy.stable, Range, Offset...) -(Range range, Offset offset) -if (s == SwapStrategy.stable - && isBidirectionalRange!Range - && hasLvalueElements!Range - && Offset.length >= 1) -{ - auto result = range; - auto src = range, tgt = range; - size_t pos; - foreach (pass, i; offset) + return range; + } + else static if (s == SwapStrategy.stable) { - static if (is(typeof(i[0])) && is(typeof(i[1]))) - { - auto from = i[0], delta = i[1] - i[0]; - } - else + auto result = range; + auto src = range, tgt = range; + size_t pos; + foreach (pass, i; offset) { - auto from = i; - enum delta = 1; - } + static if (is(typeof(i[0])) && is(typeof(i[1]))) + { + auto from = i[0], delta = i[1] - i[0]; + } + else + { + auto from = i; + enum delta = 1; + } - static if (pass > 0) - { - import std.exception : enforce; - enforce(pos <= from, - "remove(): incorrect ordering of elements to remove"); + static if (pass > 0) + { + import std.exception : enforce; + enforce(pos <= from, + "remove(): incorrect ordering of elements to remove"); - for (; pos < from; ++pos, src.popFront(), tgt.popFront()) + for (; pos < from; ++pos, src.popFront(), tgt.popFront()) + { + move(src.front, tgt.front); + } + } + else { - move(src.front, tgt.front); + src.popFrontExactly(from); + tgt.popFrontExactly(from); + pos = from; } + // now skip source to the "to" position + src.popFrontExactly(delta); + result.popBackExactly(delta); + pos += delta; } - else - { - src.popFrontExactly(from); - tgt.popFrontExactly(from); - pos = from; - } - // now skip source to the "to" position - src.popFrontExactly(delta); - result.popBackExactly(delta); - pos += delta; + // leftover move + moveAll(src, tgt); + return result; } - // leftover move - moveAll(src, tgt); - return result; + else static assert(0, "SwapStrategy.semistable is not supported."); +} + +/// +@safe pure unittest +{ + import std.typecons : tuple; + + auto a = [ 0, 1, 2, 3, 4, 5 ]; + assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]); + a = [ 0, 1, 2, 3, 4, 5 ]; + assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] ); + a = [ 0, 1, 2, 3, 4, 5 ]; + assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]); + + a = [ 0, 1, 2, 3, 4, 5 ]; + assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]); + a = [ 0, 1, 2, 3, 4, 5 ]; + assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]); } @safe unittest @@ -1527,10 +1913,7 @@ if (s == SwapStrategy.stable @safe unittest { import std.range; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - //writeln(remove!(SwapStrategy.stable)(a, 1)); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]); @@ -1547,20 +1930,14 @@ if (s == SwapStrategy.stable assert(remove!(SwapStrategy.unstable)(a, 2) == [ 1, 2, 4 ]); - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - //writeln(remove!(SwapStrategy.stable)(a, 1, 5)); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1, 5) == [ 0, 2, 3, 4, 6, 7, 8, 9, 10 ]); - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - //writeln(remove!(SwapStrategy.stable)(a, 1, 3, 5)); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1, 3, 5) == [ 0, 2, 4, 6, 7, 8, 9, 10]); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; - //writeln(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5))); - a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5)) == [ 0, 2, 5, 6, 7, 8, 9, 10]); @@ -1592,12 +1969,20 @@ if (s == SwapStrategy.stable } /** -Reduces the length of the bidirectional range $(D range) by removing +Reduces the length of the +$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) $(D range) by removing elements that satisfy $(D pred). If $(D s = SwapStrategy.unstable), elements are moved from the right end of the range over the elements to eliminate. If $(D s = SwapStrategy.stable) (the default), elements are moved progressively to front such that their relative order is preserved. Returns the filtered range. + +Params: + range = a bidirectional ranges with lvalue elements + +Returns: + the range with all of the elements where $(D pred) is $(D true) + removed */ Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range) (Range range) @@ -1660,36 +2045,154 @@ if (isBidirectionalRange!Range @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; assert(remove!("a == 2", SwapStrategy.unstable)(a) == [ 1, 6, 3, 5, 3, 4, 5 ]); a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; - //writeln(remove!("a != 2", SwapStrategy.stable)(a)); assert(remove!("a == 2", SwapStrategy.stable)(a) == [ 1, 3, 3, 4, 5, 5, 6 ]); } +@nogc @system unittest +{ + // @nogc test + int[10] arr = [0,1,2,3,4,5,6,7,8,9]; + alias pred = e => e < 5; + + auto r = arr[].remove!(SwapStrategy.unstable)(0); + r = r.remove!(SwapStrategy.stable)(0); + r = r.remove!(pred, SwapStrategy.unstable); + r = r.remove!(pred, SwapStrategy.stable); +} + +@safe unittest +{ + import std.algorithm.comparison : min; + import std.algorithm.searching : all, any; + import std.algorithm.sorting : isStrictlyMonotonic; + import std.array : array; + import std.meta : AliasSeq; + import std.range : iota, only; + import std.typecons : Tuple; + alias S = Tuple!(int[2]); + S[] soffsets; + foreach (start; 0 .. 5) + foreach (end; min(start+1,5) .. 5) + soffsets ~= S([start,end]); + alias D = Tuple!(int[2],int[2]); + D[] doffsets; + foreach (start1; 0 .. 10) + foreach (end1; min(start1+1,10) .. 10) + foreach (start2; end1 .. 10) + foreach (end2; min(start2+1,10) .. 10) + doffsets ~= D([start1,end1],[start2,end2]); + alias T = Tuple!(int[2],int[2],int[2]); + T[] toffsets; + foreach (start1; 0 .. 15) + foreach (end1; min(start1+1,15) .. 15) + foreach (start2; end1 .. 15) + foreach (end2; min(start2+1,15) .. 15) + foreach (start3; end2 .. 15) + foreach (end3; min(start3+1,15) .. 15) + toffsets ~= T([start1,end1],[start2,end2],[start3,end3]); + + static void verify(O...)(int[] r, int len, int removed, bool stable, O offsets) + { + assert(r.length == len - removed); + assert(!stable || r.isStrictlyMonotonic); + assert(r.all!(e => all!(o => e < o[0] || e >= o[1])(offsets.only))); + } + + foreach (offsets; AliasSeq!(soffsets,doffsets,toffsets)) + foreach (os; offsets) + { + int len = 5*os.length; + auto w = iota(0, len).array; + auto x = w.dup; + auto y = w.dup; + auto z = w.dup; + alias pred = e => any!(o => o[0] <= e && e < o[1])(only(os.expand)); + w = w.remove!(SwapStrategy.unstable)(os.expand); + x = x.remove!(SwapStrategy.stable)(os.expand); + y = y.remove!(pred, SwapStrategy.unstable); + z = z.remove!(pred, SwapStrategy.stable); + int removed; + foreach (o; os) + removed += o[1] - o[0]; + verify(w, len, removed, false, os[]); + verify(x, len, removed, true, os[]); + verify(y, len, removed, false, os[]); + verify(z, len, removed, true, os[]); + assert(w == y); + assert(x == z); + } +} + // reverse /** Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D swap). +UTF sequences consisting of multiple code units are preserved properly. + +Params: + r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) + with swappable elements, a random access range with a length member or a + narrow string. See_Also: - $(WEB sgi.com/tech/stl/_reverse.html, STL's _reverse) + $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view + +Bugs: + When passing a sting with unicode modifiers on characters, such as $(D \u0301), + this function will not properly keep the position of the modifier. For example, + reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("dÌab") instead of + $(D da\u0301b) ("dáb"). */ void reverse(Range)(Range r) -if (isBidirectionalRange!Range && !isRandomAccessRange!Range - && hasSwappableElements!Range) +if (isBidirectionalRange!Range) { - while (!r.empty) + static if (isRandomAccessRange!Range) + { + //swapAt is in fact the only way to swap non lvalue ranges + immutable last = r.length-1; + immutable steps = r.length/2; + for (size_t i = 0; i < steps; i++) + { + r.swapAt(i, last-i); + } + } + else static if (hasSwappableElements!Range) { - swap(r.front, r.back); - r.popFront(); - if (r.empty) break; - r.popBack(); + while (!r.empty) + { + swap(r.front, r.back); + r.popFront(); + if (r.empty) break; + r.popBack(); + } } + else static if (isNarrowString!Range && !is(ElementType!Range == const) && !is(ElementType!Range == immutable)) + { + import std.string : representation; + import std.utf : stride; + + auto repr = representation(r); + for (size_t i = 0; i < r.length; ) + { + immutable step = stride(r, i); + if (step > 1) + { + .reverse(repr[i .. i + step]); + i += step; + } + else + { + ++i; + } + } + reverse(repr); + } + else static assert(0, "Range is neither RandomAccess, has swappable elements nor a narrow string."); } /// @@ -1700,23 +2203,16 @@ if (isBidirectionalRange!Range && !isRandomAccessRange!Range assert(arr == [ 3, 2, 1 ]); } -///ditto -void reverse(Range)(Range r) -if (isRandomAccessRange!Range && hasLength!Range) +/// +@safe unittest { - //swapAt is in fact the only way to swap non lvalue ranges - immutable last = r.length-1; - immutable steps = r.length/2; - for (size_t i = 0; i < steps; i++) - { - swapAt(r, i, last-i); - } + char[] arr = "hello\U00010143\u0100\U00010143".dup; + reverse(arr); + assert(arr == "\U00010143\u0100\U00010143olleh"); } @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] range = null; reverse(range); range = [ 1 ]; @@ -1730,42 +2226,6 @@ if (isRandomAccessRange!Range && hasLength!Range) assert(range == [3, 2, 1]); } -/** -Reverses $(D r) in-place, where $(D r) is a narrow string (having -elements of type $(D char) or $(D wchar)). UTF sequences consisting of -multiple code units are preserved properly. -*/ -void reverse(Char)(Char[] s) -if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) -{ - import std.string : representation; - import std.utf : stride; - - auto r = representation(s); - for (size_t i = 0; i < s.length; ) - { - immutable step = std.utf.stride(s, i); - if (step > 1) - { - .reverse(r[i .. i + step]); - i += step; - } - else - { - ++i; - } - } - reverse(r); -} - -/// -@safe unittest -{ - char[] arr = "hello\U00010143\u0100\U00010143".dup; - reverse(arr); - assert(arr == "\U00010143\u0100\U00010143olleh"); -} - @safe unittest { void test(string a, string b) @@ -1785,23 +2245,6 @@ if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) test("hello\U00010143\u0100\U00010143", "\U00010143\u0100\U00010143olleh"); } -//private -void swapAt(R)(R r, size_t i1, size_t i2) -{ - static if (is(typeof(&r[i1]))) - { - swap(r[i1], r[i2]); - } - else - { - if (i1 == i2) return; - auto t1 = moveAt(r, i1); - auto t2 = moveAt(r, i2); - r[i2] = t1; - r[i1] = t2; - } -} - /** The strip group of functions allow stripping of either leading, trailing, or both leading and trailing elements. @@ -1818,23 +2261,31 @@ void swapAt(R)(R r, size_t i1, size_t i2) where the range will be stripped as long as this element can be found. The other takes a lambda predicate, where the range will be stripped as long as the predicate returns true. + + Params: + range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) + or $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + element = the elements to remove + + Returns: + a Range with all of range except element at the start and end */ Range strip(Range, E)(Range range, E element) - if (isBidirectionalRange!Range && is(typeof(range.front == element) : bool)) +if (isBidirectionalRange!Range && is(typeof(range.front == element) : bool)) { return range.stripLeft(element).stripRight(element); } /// ditto Range strip(alias pred, Range)(Range range) - if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) +if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) { return range.stripLeft!pred().stripRight!pred(); } /// ditto Range stripLeft(Range, E)(Range range, E element) - if (isInputRange!Range && is(typeof(range.front == element) : bool)) +if (isInputRange!Range && is(typeof(range.front == element) : bool)) { import std.algorithm.searching : find; return find!((auto ref a) => a != element)(range); @@ -1842,7 +2293,7 @@ Range stripLeft(Range, E)(Range range, E element) /// ditto Range stripLeft(alias pred, Range)(Range range) - if (isInputRange!Range && is(typeof(pred(range.front)) : bool)) +if (isInputRange!Range && is(typeof(pred(range.front)) : bool)) { import std.algorithm.searching : find; import std.functional : not; @@ -1852,7 +2303,7 @@ Range stripLeft(alias pred, Range)(Range range) /// ditto Range stripRight(Range, E)(Range range, E element) - if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool)) +if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool)) { for (; !range.empty; range.popBack()) { @@ -1864,7 +2315,7 @@ Range stripRight(Range, E)(Range range, E element) /// ditto Range stripRight(alias pred, Range)(Range range) - if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) +if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) { for (; !range.empty; range.popBack()) { @@ -2074,6 +2525,10 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) //Bug# 4789 int[1] s = [1]; swap(s, s); + + int[3] a = [1, 2, 3]; + swap(a[1], a[2]); + assert(a == [1, 3, 2]); } @safe unittest @@ -2115,7 +2570,7 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) SysTime a, b; } -unittest // 9975 +@system unittest // 9975 { import std.exception : doesPointTo, mayPointTo; static struct S2 @@ -2141,7 +2596,7 @@ unittest // 9975 assertThrown!Error(swap(p, pp)); } -unittest +@system unittest { static struct A { @@ -2160,14 +2615,125 @@ unittest swap(b1, b2); } -// Not yet documented -void swap(T)(ref T lhs, ref T rhs) if (is(typeof(lhs.proxySwap(rhs)))) +/// ditto +void swap(T)(ref T lhs, ref T rhs) +if (is(typeof(lhs.proxySwap(rhs)))) { lhs.proxySwap(rhs); } -void swapFront(R1, R2)(R1 r1, R2 r2) - if (isInputRange!R1 && isInputRange!R2) +/** +Swaps two elements in-place of a range `r`, +specified by their indices `i1` and `i2`. + +Params: + r = a range with swappable elements + i1 = first index + i2 = second index +*/ +void swapAt(R)(auto ref R r, size_t i1, size_t i2) +{ + static if (is(typeof(&r.swapAt))) + { + r.swapAt(i1, i2); + } + else static if (is(typeof(&r[i1]))) + { + swap(r[i1], r[i2]); + } + else + { + if (i1 == i2) return; + auto t1 = r.moveAt(i1); + auto t2 = r.moveAt(i2); + r[i2] = t1; + r[i1] = t2; + } +} + +/// +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + auto a = [1, 2, 3]; + a.swapAt(1, 2); + assert(a.equal([1, 3, 2])); +} + +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + auto a = [4, 5, 6]; + a.swapAt(1, 1); + assert(a.equal([4, 5, 6])); +} + +pure @safe nothrow unittest +{ + // test non random access ranges + import std.algorithm.comparison : equal; + import std.array : array; + + char[] b = ['a', 'b', 'c']; + b.swapAt(1, 2); + assert(b.equal(['a', 'c', 'b'])); + + int[3] c = [1, 2, 3]; + c.swapAt(1, 2); + assert(c.array.equal([1, 3, 2])); + + // opIndex returns lvalue + struct RandomIndexType(T) + { + T payload; + + @property ref auto opIndex(size_t i) + { + return payload[i]; + } + + } + auto d = RandomIndexType!(int[])([4, 5, 6]); + d.swapAt(1, 2); + assert(d.payload.equal([4, 6, 5])); + + // custom moveAt and opIndexAssign + struct RandomMoveAtType(T) + { + T payload; + + ElementType!T moveAt(size_t i) + { + return payload.moveAt(i); + } + + void opIndexAssign(ElementType!T val, size_t idx) + { + payload[idx] = val; + } + } + auto e = RandomMoveAtType!(int[])([7, 8, 9]); + e.swapAt(1, 2); + assert(e.payload.equal([7, 9, 8])); + + + // custom swapAt + struct RandomSwapAtType(T) + { + T payload; + + void swapAt(size_t i) + { + return payload.swapAt(i); + } + } + auto f = RandomMoveAtType!(int[])([10, 11, 12]); + swapAt(f, 1, 2); + assert(f.payload.equal([10, 12, 11])); +} + +private void swapFront(R1, R2)(R1 r1, R2 r2) +if (isInputRange!R1 && isInputRange!R2) { static if (is(typeof(swap(r1.front, r2.front)))) { @@ -2188,12 +2754,20 @@ Returns a tuple containing the remainder portions of $(D r1) and $(D r2) that were not swapped (one of them will be empty). The ranges may be of different types but must have the same element type and support swapping. + +Params: + r1 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + with swappable elements + r2 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + with swappable elements + +Returns: + Tuple containing the remainder portions of r1 and r2 that were not swapped */ -Tuple!(Range1, Range2) -swapRanges(Range1, Range2)(Range1 r1, Range2 r2) - if (isInputRange!(Range1) && isInputRange!(Range2) - && hasSwappableElements!(Range1) && hasSwappableElements!(Range2) - && is(ElementType!(Range1) == ElementType!(Range2))) +Tuple!(InputRange1, InputRange2) +swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) +if (hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2 + && is(ElementType!InputRange1 == ElementType!InputRange2)) { for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) { @@ -2205,6 +2779,7 @@ swapRanges(Range1, Range2)(Range1 r1, Range2 r2) /// @safe unittest { + import std.range : empty; int[] a = [ 100, 101, 102, 103 ]; int[] b = [ 0, 1, 2, 3 ]; auto c = swapRanges(a[1 .. 3], b[2 .. 4]); @@ -2221,24 +2796,18 @@ define copy constructors (for all other types, $(LREF fill) and uninitializedFill are equivalent). Params: - range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements - and has assignable elements + range = An + $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + that exposes references to its elements and has assignable + elements value = Assigned to each element of range See_Also: $(LREF fill) $(LREF initializeAll) - -Example: ----- -struct S { ... } -S[] s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; -uninitializedFill(s, 42); -assert(s == [ 42, 42, 42, 42, 42 ]); ----- */ void uninitializedFill(Range, Value)(Range range, Value value) - if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value))) +if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value))) { import std.traits : hasElaborateAssign; @@ -2256,3 +2825,14 @@ void uninitializedFill(Range, Value)(Range range, Value value) return fill(range, value); } +/// +nothrow @system unittest +{ + import core.stdc.stdlib : malloc, free; + + auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5]; + uninitializedFill(s, 42); + assert(s == [ 42, 42, 42, 42, 42 ]); + + scope(exit) free(s.ptr); +} diff --git a/std/algorithm/package.d b/std/algorithm/package.d index 55e142f07ab..fab759773df 100644 --- a/std/algorithm/package.d +++ b/std/algorithm/package.d @@ -3,8 +3,8 @@ /** This package implements generic algorithms oriented towards the processing of sequences. Sequences processed by these functions define range-based -interfaces. See also $(LINK2 std_range.html, Reference on ranges) and -$(WEB ddili.org/ders/d.en/ranges.html, tutorial on ranges). +interfaces. See also $(MREF_ALTTEF Reference on ranges, std, range) and +$(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges). $(SCRIPT inhibitQuickIndex = 1;) @@ -12,10 +12,10 @@ Algorithms are categorized into the following submodules: $(DIVC quickindex, $(BOOKTABLE , -$(TR $(TH Category) $(TH Submodule) $(TH Functions) +$(TR $(TH Submodule) $(TH Functions) ) -$(TR $(TDNW Searching) - $(TDNW $(SUBMODULE searching)) +$(TR + $(TDNW $(SUBMODULE Searching, searching)) $(TD $(SUBREF searching, all) $(SUBREF searching, any) @@ -34,20 +34,29 @@ $(TR $(TDNW Searching) $(SUBREF searching, findSplitAfter) $(SUBREF searching, findSplitBefore) $(SUBREF searching, minCount) + $(SUBREF searching, maxCount) + $(SUBREF searching, minElement) + $(SUBREF searching, maxElement) + $(SUBREF searching, minIndex) + $(SUBREF searching, maxIndex) $(SUBREF searching, minPos) + $(SUBREF searching, maxPos) $(SUBREF searching, skipOver) $(SUBREF searching, startsWith) $(SUBREF searching, until) ) ) -$(TR $(TDNW Comparison) - $(TDNW $(SUBMODULE comparison)) +$(TR + $(TDNW $(SUBMODULE Comparison, comparison)) $(TD $(SUBREF comparison, among) $(SUBREF comparison, castSwitch) $(SUBREF comparison, clamp) $(SUBREF comparison, cmp) + $(SUBREF comparison, either) $(SUBREF comparison, equal) + $(SUBREF comparison, isPermutation) + $(SUBREF comparison, isSameLength) $(SUBREF comparison, levenshteinDistance) $(SUBREF comparison, levenshteinDistanceAndPath) $(SUBREF comparison, max) @@ -56,31 +65,38 @@ $(TR $(TDNW Comparison) $(SUBREF comparison, predSwitch) ) ) -$(TR $(TDNW Iteration) - $(TDNW $(SUBMODULE iteration)) +$(TR + $(TDNW $(SUBMODULE Iteration, iteration)) $(TD $(SUBREF iteration, cache) $(SUBREF iteration, cacheBidirectional) $(SUBREF iteration, chunkBy) + $(SUBREF iteration, cumulativeFold) $(SUBREF iteration, each) $(SUBREF iteration, filter) $(SUBREF iteration, filterBidirectional) + $(SUBREF iteration, fold) $(SUBREF iteration, group) $(SUBREF iteration, joiner) $(SUBREF iteration, map) + $(SUBREF iteration, permutations) $(SUBREF iteration, reduce) $(SUBREF iteration, splitter) $(SUBREF iteration, sum) $(SUBREF iteration, uniq) ) ) -$(TR $(TDNW Sorting) - $(TDNW $(SUBMODULE sorting)) +$(TR + $(TDNW $(SUBMODULE Sorting, sorting)) $(TD $(SUBREF sorting, completeSort) $(SUBREF sorting, isPartitioned) $(SUBREF sorting, isSorted) + $(SUBREF sorting, isStrictlyMonotonic) + $(SUBREF sorting, ordered) + $(SUBREF sorting, strictlyOrdered) $(SUBREF sorting, makeIndex) + $(SUBREF sorting, merge) $(SUBREF sorting, multiSort) $(SUBREF sorting, nextEvenPermutation) $(SUBREF sorting, nextPermutation) @@ -94,8 +110,8 @@ $(TR $(TDNW Sorting) $(SUBREF sorting, topNIndex) ) ) -$(TR $(TDNW Set operations) - $(TDNW $(SUBMODULE setops)) +$(TR + $(TDNW Set operations $(BR)($(SUBMODULE setops, setops))) $(TD $(SUBREF setops, cartesianProduct) $(SUBREF setops, largestPartialIntersection) @@ -104,11 +120,10 @@ $(TR $(TDNW Set operations) $(SUBREF setops, setDifference) $(SUBREF setops, setIntersection) $(SUBREF setops, setSymmetricDifference) - $(SUBREF setops, setUnion) ) ) -$(TR $(TDNW Mutation) - $(TDNW $(SUBMODULE mutation)) +$(TR + $(TDNW $(SUBMODULE Mutation, mutation)) $(TD $(SUBREF mutation, bringToFront) $(SUBREF mutation, copy) @@ -117,6 +132,9 @@ $(TR $(TDNW Mutation) $(SUBREF mutation, move) $(SUBREF mutation, moveAll) $(SUBREF mutation, moveSome) + $(SUBREF mutation, moveEmplace) + $(SUBREF mutation, moveEmplaceAll) + $(SUBREF mutation, moveEmplaceSome) $(SUBREF mutation, remove) $(SUBREF mutation, reverse) $(SUBREF mutation, strip) @@ -129,9 +147,9 @@ $(TR $(TDNW Mutation) ) )) -Many functions in this package are parameterized with a function or a -$(GLOSSARY predicate). The predicate may be passed either as a -function name, a delegate name, a $(GLOSSARY functor) name, or a +Many functions in this package are parameterized with a $(GLOSSARY predicate). +The predicate may be any suitable callable type +(a function, a delegate, a $(GLOSSARY functor), or a lambda), or a compile-time string. The string may consist of $(B any) legal D expression that uses the symbol $(D a) (for unary functions) or the symbols $(D a) and $(D b) (for binary functions). These names will NOT @@ -148,27 +166,26 @@ static bool greater(int a, int b) { return a > b; } -sort!(greater)(a); // predicate as alias -sort!("a > b")(a); // predicate as string - // (no ambiguity with array name) -sort(a); // no predicate, "a < b" is implicit +sort!greater(a); // predicate as alias +sort!((a, b) => a > b)(a); // predicate as a lambda. +sort!"a > b"(a); // predicate as string + // (no ambiguity with array name) +sort(a); // no predicate, "a < b" is implicit ---- Macros: -WIKI = Phobos/StdAlgorithm -SUBMODULE = $(LINK2 std_algorithm_$1.html, std.algorithm.$1) -SUBREF = $(LINK2 std_algorithm_$1.html#.$2, $(TT $2))$(NBSP) +SUBMODULE = $(MREF_ALTTEXT $1, std, algorithm, $2) +SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, algorithm, $1)$(NBSP) Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/_algorithm/package.d) */ module std.algorithm; -//debug = std_algorithm; public import std.algorithm.comparison; public import std.algorithm.iteration; @@ -178,5 +195,3 @@ public import std.algorithm.searching; public import std.algorithm.sorting; static import std.functional; -deprecated("Please use std.functional.forward instead.") -alias forward = std.functional.forward; diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 1637eadafc6..85fe4e2f5c0 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -1,12 +1,11 @@ // Written in the D programming language. /** -This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). +This is a submodule of $(MREF std, algorithm). It contains generic _searching algorithms. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE Cheat Sheet, - $(TR $(TH Function Name) $(TH Description)) - $(T2 all, $(D all!"a > 0"([1, 2, 3, 4])) returns $(D true) because all elements are positive) @@ -18,7 +17,8 @@ $(T2 balancedParens, string has balanced parentheses.) $(T2 boyerMooreFinder, $(D find("hello world", boyerMooreFinder("or"))) returns $(D "orld") - using the $(LUCKY Boyer-Moore _algorithm).) + using the $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm, + Boyer-Moore _algorithm).) $(T2 canFind, $(D canFind("hello world", "or")) returns $(D true).) $(T2 count, @@ -35,7 +35,7 @@ $(T2 endsWith, $(D endsWith("rocks", "ks")) returns $(D true).) $(T2 find, $(D find("hello world", "or")) returns $(D "orld") using linear search. - (For binary search refer to $(XREF range,sortedRange).)) + (For binary search refer to $(REF sortedRange, std,range).)) $(T2 findAdjacent, $(D findAdjacent([1, 2, 3, 3, 4])) returns the subrange starting with two equal adjacent elements, i.e. $(D [3, 3, 4]).) @@ -44,8 +44,8 @@ $(T2 findAmong, among $(D "qcx").) $(T2 findSkip, If $(D a = "abcde"), then $(D findSkip(a, "x")) returns $(D false) and - leaves $(D a) unchanged, whereas $(D findSkip(a, 'c')) advances $(D a) - to $(D "cde") and returns $(D true).) + leaves $(D a) unchanged, whereas $(D findSkip(a, "c")) advances $(D a) + to $(D "de") and returns $(D true).) $(T2 findSplit, $(D findSplit("abcdefg", "de")) returns the three ranges $(D "abc"), $(D "de"), and $(D "fg").) @@ -57,10 +57,28 @@ $(T2 findSplitBefore, and $(D "defg").) $(T2 minCount, $(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).) +$(T2 maxCount, + $(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).) +$(T2 minElement, + Selects the minimal element of a range. + `minElement([3, 4, 1, 2])` returns `1`.) +$(T2 maxElement, + Selects the maximal element of a range. + `maxElement([3, 4, 1, 2])` returns `4`.) +$(T2 minIndex, + Index of the minimal element of a range. + `minElement([3, 4, 1, 2])` returns `2`.) +$(T2 maxIndex, + Index of the maximal element of a range. + `maxElement([3, 4, 1, 2])` returns `1`.) $(T2 minPos, $(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]), i.e., positions the range at the first occurrence of its minimal element.) +$(T2 maxPos, + $(D maxPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [4, 1]), + i.e., positions the range at the first occurrence of its maximal + element.) $(T2 mismatch, $(D mismatch("parakeet", "parachute")) returns the two ranges $(D "keet") and $(D "chute").) @@ -76,9 +94,9 @@ $(T2 until, Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_searching.d) @@ -92,7 +110,7 @@ import std.functional; // : unaryFun, binaryFun; import std.range.primitives; import std.traits; // FIXME -import std.typecons; // : Tuple; +import std.typecons; // : Tuple, Flag, Yes, No; /++ Checks if $(I _all) of the elements verify $(D pred). @@ -101,7 +119,7 @@ template all(alias pred = "a") { /++ Returns $(D true) if and only if $(I _all) values $(D v) found in the - input range $(D range) satisfy the predicate $(D pred). + input _range $(D range) satisfy the predicate $(D pred). Performs (at most) $(BIGOH range.length) evaluations of $(D pred). +/ bool all(Range)(Range range) @@ -142,12 +160,13 @@ are true. Checks if $(I _any) of the elements verifies $(D pred). $(D !any) can be used to verify that $(I none) of the elements verify $(D pred). +This is sometimes called `exists` in other languages. +/ template any(alias pred = "a") { /++ Returns $(D true) if and only if $(I _any) value $(D v) found in the - input range $(D range) satisfies the predicate $(D pred). + input _range $(D range) satisfies the predicate $(D pred). Performs (at most) $(BIGOH range.length) evaluations of $(D pred). +/ bool any(Range)(Range range) @@ -187,8 +206,6 @@ evaluate to true. @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto a = [ 1, 2, 0, 4 ]; assert(any!"a == 2"(a)); } @@ -200,6 +217,16 @@ of $(D lPar) are closed by corresponding instances of $(D rPar). The parameter $(D maxNestingLevel) controls the nesting level allowed. The most common uses are the default or $(D 0). In the latter case, no nesting is allowed. + +Params: + r = The range to check. + lPar = The element corresponding with a left (opening) parenthesis. + rPar = The element corresponding with a right (closing) parenthesis. + maxNestingLevel = The maximum allowed nesting level. + +Returns: + true if the given range has balanced parenthesis within the given maximum + nesting level; false otherwise. */ bool balancedParens(Range, E)(Range r, E lPar, E rPar, size_t maxNestingLevel = size_t.max) @@ -250,14 +277,6 @@ if (isInputRange!(Range) && is(typeof(r.front == lPar))) * invoke the Boyer-Moore matching algorithm for finding of $(D needle) in a * given haystack. */ -BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder -(alias pred = "a == b", Range) -(Range needle) if (isRandomAccessRange!(Range) || isSomeString!Range) -{ - return typeof(return)(needle); -} - -/// Ditto struct BoyerMooreFinder(alias pred, Range) { private: @@ -285,7 +304,8 @@ is ignored. import std.algorithm.comparison : equal; ptrdiff_t virtual_begin = needle.length - offset - portion; ptrdiff_t ignore = 0; - if (virtual_begin < 0) { + if (virtual_begin < 0) + { ignore = -virtual_begin; virtual_begin = 0; } @@ -299,6 +319,7 @@ is ignored. } public: + /// this(Range needle) { if (!needle.length) return; @@ -325,6 +346,7 @@ public: } } + /// Range beFound(Range haystack) { import std.algorithm.comparison : max; @@ -332,7 +354,7 @@ public: if (!needle.length) return haystack; if (needle.length > haystack.length) return haystack[$ .. $]; /* Search: */ - auto limit = haystack.length - needle.length; + immutable limit = haystack.length - needle.length; for (size_t hpos = 0; hpos <= limit; ) { size_t npos = needle.length - 1; @@ -346,14 +368,40 @@ public: return haystack[$ .. $]; } + /// @property size_t length() { return needle.length; } + /// alias opDollar = length; } +/// Ditto +BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder +(alias pred = "a == b", Range) +(Range needle) +if ((isRandomAccessRange!(Range) && hasSlicing!Range) || isSomeString!Range) +{ + return typeof(return)(needle); +} + +/// +@safe pure nothrow unittest +{ + auto bmFinder = boyerMooreFinder("TG"); + + string r = "TAGTGCCTGA"; + // search for the first match in the haystack r + r = bmFinder.beFound(r); + assert(r == "TGCCTGA"); + + // continue search in haystack + r = bmFinder.beFound(r[2 .. $]); + assert(r == "TGA"); +} + /** Returns the common prefix of two ranges. @@ -361,9 +409,11 @@ Params: pred = The predicate to use in comparing elements for commonality. Defaults to equality $(D "a == b"). - r1 = A $(XREF2 range, isForwardRange, forward range) of elements. + r1 = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of + elements. - r2 = An $(XREF2 range, isInputRange, input range) of elements. + r2 = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of + elements. Returns: A slice of $(D r1) which contains the characters that both ranges start with, @@ -372,7 +422,7 @@ $(D takeExactly(r1, n)), where $(D n) is the number of elements in the common prefix of both ranges. See_Also: - $(XREF range, takeExactly) + $(REF takeExactly, std,range) */ auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2) if (isForwardRange!R1 && isInputRange!R2 && @@ -455,7 +505,7 @@ if (isNarrowString!R1 && isNarrowString!R2) immutable limit = min(r1.length, r2.length); for (size_t i = 0; i < limit;) { - immutable codeLen = std.utf.stride(r1, i); + immutable codeLen = stride(r1, i); size_t j = 0; for (; j < codeLen && i < limit; ++i, ++j) @@ -479,24 +529,24 @@ if (isNarrowString!R1 && isNarrowString!R2) import std.algorithm.iteration : filter; import std.conv : to; import std.exception : assertThrown; - import std.utf : UTFException; + import std.meta : AliasSeq; import std.range; - import std.typetuple : TypeTuple; + import std.utf : UTFException; assert(commonPrefix([1, 2, 3], [1, 2, 3, 4, 5]) == [1, 2, 3]); assert(commonPrefix([1, 2, 3, 4, 5], [1, 2, 3]) == [1, 2, 3]); assert(commonPrefix([1, 2, 3, 4], [1, 2, 3, 4]) == [1, 2, 3, 4]); assert(commonPrefix([1, 2, 3], [7, 2, 3, 4, 5]).empty); assert(commonPrefix([7, 2, 3, 4, 5], [1, 2, 3]).empty); - assert(commonPrefix([1, 2, 3], cast(int[])null).empty); - assert(commonPrefix(cast(int[])null, [1, 2, 3]).empty); - assert(commonPrefix(cast(int[])null, cast(int[])null).empty); + assert(commonPrefix([1, 2, 3], cast(int[]) null).empty); + assert(commonPrefix(cast(int[]) null, [1, 2, 3]).empty); + assert(commonPrefix(cast(int[]) null, cast(int[]) null).empty); - foreach (S; TypeTuple!(char[], const(char)[], string, - wchar[], const(wchar)[], wstring, - dchar[], const(dchar)[], dstring)) + foreach (S; AliasSeq!(char[], const(char)[], string, + wchar[], const(wchar)[], wstring, + dchar[], const(dchar)[], dstring)) { - foreach(T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 assert(commonPrefix(to!S(""), to!T("")).empty); assert(commonPrefix(to!S(""), to!T("hello")).empty); @@ -556,10 +606,18 @@ true). Performs $(BIGOH haystack.length) evaluations of $(D pred). Note: Regardless of the overload, $(D count) will not accept infinite ranges for $(D haystack). + +Params: + pred = The predicate to evaluate. + haystack = The range to _count. + needle = The element or sub-range to _count in the `haystack`. + +Returns: + The number of positions in the `haystack` for which `pred` returned true. */ size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) - if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +if (isInputRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) { bool pred2(ElementType!Range a) { return binaryFun!pred(a, needle); } return count!pred2(haystack); @@ -588,9 +646,6 @@ size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) { import std.conv : text; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; assert(count(a, 2) == 3, text(count(a, 2))); assert(count!("a > b")(a, 2) == 5, text(count!("a > b")(a, 2))); @@ -607,7 +662,6 @@ size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) @safe unittest { - debug(std_algorithm) printf("algorithm.count.unittest\n"); string s = "This is a fofofof list"; string sub = "fof"; assert(count(s, sub) == 2); @@ -615,9 +669,9 @@ size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) /// Ditto size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) - if (isForwardRange!R1 && !isInfinite!R1 && - isForwardRange!R2 && - is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) +if (isForwardRange!R1 && !isInfinite!R1 && + isForwardRange!R2 && + is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { assert(!needle.empty, "Cannot count occurrences of an empty range"); @@ -639,8 +693,8 @@ size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) /// Ditto size_t count(alias pred = "true", R)(R haystack) - if (isInputRange!R && !isInfinite!R && - is(typeof(unaryFun!pred(haystack.front)) : bool)) +if (isInputRange!R && !isInfinite!R && + is(typeof(unaryFun!pred(haystack.front)) : bool)) { size_t result; alias T = ElementType!R; //For narrow strings forces dchar iteration @@ -651,8 +705,6 @@ size_t count(alias pred = "true", R)(R haystack) @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; assert(count!("a == 3")(a) == 2); assert(count("日本語") == 3); @@ -665,14 +717,18 @@ size_t count(alias pred = "true", R)(R haystack) } /++ - Counts elements in the given $(XREF2 range, isForwardRange, forward range) + Counts elements in the given + $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) until the given predicate is true for one of the given $(D needles). Params: pred = The predicate for determining when to stop counting. - haystack = The $(XREF2 range, isInputRange, input range) to be counted. - needles = Either a single element, or a $(XREF2 range, isForwardRange, - forward range) of elements, to be evaluated in turn against each + haystack = The + $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be + counted. + needles = Either a single element, or a + $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + of elements, to be evaluated in turn against each element in $(D haystack) under the given predicate. Returns: The number of elements which must be popped from the front of @@ -680,14 +736,16 @@ size_t count(alias pred = "true", R)(R haystack) $(D startsWith!pred(haystack, needles)) is $(D true). If $(D startsWith!pred(haystack, needles)) is not $(D true) for any element in $(D haystack), then $(D -1) is returned. + + See_Also: $(REF indexOf, std,string) +/ ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) - if (isForwardRange!R - && Rs.length > 0 - && isForwardRange!(Rs[0]) == isInputRange!(Rs[0]) - && is(typeof(startsWith!pred(haystack, needles[0]))) - && (Rs.length == 1 - || is(typeof(countUntil!pred(haystack, needles[1 .. $]))))) +if (isForwardRange!R + && Rs.length > 0 + && isForwardRange!(Rs[0]) == isInputRange!(Rs[0]) + && is(typeof(startsWith!pred(haystack, needles[0]))) + && (Rs.length == 1 + || is(typeof(countUntil!pred(haystack, needles[1 .. $]))))) { typeof(return) result; @@ -770,8 +828,8 @@ ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) /// ditto ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) - if (isInputRange!R && - is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +if (isInputRange!R && + is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) { bool pred2(ElementType!R a) { return binaryFun!pred(a, needle); } return countUntil!pred2(haystack); @@ -833,14 +891,15 @@ ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) Params: pred = Predicate to when to stop counting. - haystack = An $(XREF2 range, isInputRange, input range) of elements - to be counted. + haystack = An + $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of + elements to be counted. Returns: The number of elements which must be popped from $(D haystack) before $(D pred(haystack.front)) is $(D true). +/ ptrdiff_t countUntil(alias pred, R)(R haystack) - if (isInputRange!R && - is(typeof(unaryFun!pred(haystack.front)) : bool)) +if (isInputRange!R && + is(typeof(unaryFun!pred(haystack.front)) : bool)) { typeof(return) i; static if (isRandomAccessRange!R) @@ -929,7 +988,8 @@ Params: pred = The predicate to use for comparing elements between the range and the needle(s). - doesThisEnd = The $(XREF2 range, isBidirectionalRange, bidirectional range) + doesThisEnd = The + $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) to check. withOneOfThese = The needles to check against, which may be single @@ -942,7 +1002,10 @@ Returns: otherwise the position of the matching needle, that is, 1 if the range ends with $(D withOneOfThese[0]), 2 if it ends with $(D withOneOfThese[1]), and so on. - */ + +In the case when no needle parameters are given, return $(D true) iff back of +$(D doesThisStart) fulfils predicate $(D pred). +*/ uint endsWith(alias pred = "a == b", Range, Needles...)(Range doesThisEnd, Needles withOneOfThese) if (isBidirectionalRange!Range && Needles.length > 1 && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) && @@ -1047,9 +1110,30 @@ if (isBidirectionalRange!R && : binaryFun!pred(doesThisEnd.back, withThis); } +/// Ditto +bool endsWith(alias pred, R)(R doesThisEnd) +if (isInputRange!R && + ifTestable!(typeof(doesThisEnd.front), unaryFun!pred)) +{ + return !doesThisEnd.empty && unaryFun!pred(doesThisEnd.back); +} + /// @safe unittest { + import std.ascii : isAlpha; + assert("abc".endsWith!(a => a.isAlpha)); + assert("abc".endsWith!isAlpha); + + assert(!"ab1".endsWith!(a => a.isAlpha)); + + assert(!"ab1".endsWith!isAlpha); + assert(!"".endsWith!(a => a.isAlpha)); + + import std.algorithm.comparison : among; + assert("abc".endsWith!(a => a.among('c', 'd') != 0)); + assert(!"abc".endsWith!(a => a.among('a', 'b') != 0)); + assert(endsWith("abc", "")); assert(!endsWith("abc", "b")); assert(endsWith("abc", "a", 'c') == 2); @@ -1065,13 +1149,10 @@ if (isBidirectionalRange!R && @safe unittest { import std.algorithm.iteration : filterBidirectional; - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; import std.conv : to; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { assert(!endsWith(to!S("abc"), 'a')); assert(endsWith(to!S("abc"), 'a', 'c') == 2); @@ -1079,7 +1160,7 @@ if (isBidirectionalRange!R && assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3); assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2); - foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 //Lots of strings assert(endsWith(to!S("abc"), to!T(""))); @@ -1113,12 +1194,12 @@ if (isBidirectionalRange!R && }(); } - foreach (T; TypeTuple!(int, short)) + foreach (T; AliasSeq!(int, short)) { immutable arr = cast(T[])[0, 1, 2, 3, 4, 5]; //RA range - assert(endsWith(arr, cast(int[])null)); + assert(endsWith(arr, cast(int[]) null)); assert(!endsWith(arr, 0)); assert(!endsWith(arr, 4)); assert(endsWith(arr, 5)); @@ -1148,6 +1229,204 @@ if (isBidirectionalRange!R && } } +/** +Iterates the passed range and selects the extreme element with `less`. +If the extreme element occurs multiple time, the first occurrence will be +returned. + +Params: + map = custom accessor for the comparison key + selector = custom mapping for the extrema selection + seed = custom seed to use as initial element + r = Range from which the extreme value will be selected + +Returns: + The extreme value according to `map` and `selector` of the passed-in values. +*/ +private auto extremum(alias map, alias selector = "a < b", Range)(Range r) +if (isInputRange!Range && !isInfinite!Range && + is(typeof(unaryFun!map(ElementType!(Range).init)))) +in +{ + assert(!r.empty, "r is an empty range"); +} +body +{ + alias Element = ElementType!Range; + Unqual!Element seed = r.front; + r.popFront(); + return extremum!(map, selector)(r, seed); +} + +private auto extremum(alias map, alias selector = "a < b", Range, + RangeElementType = ElementType!Range) + (Range r, RangeElementType seedElement) +if (isInputRange!Range && !isInfinite!Range && + !is(CommonType!(ElementType!Range, RangeElementType) == void) && + is(typeof(unaryFun!map(ElementType!(Range).init)))) +{ + alias mapFun = unaryFun!map; + alias selectorFun = binaryFun!selector; + + alias Element = ElementType!Range; + alias CommonElement = CommonType!(Element, RangeElementType); + Unqual!CommonElement extremeElement = seedElement; + + alias MapType = Unqual!(typeof(mapFun(CommonElement.init))); + MapType extremeElementMapped = mapFun(extremeElement); + + // direct access via a random access range is faster + static if (isRandomAccessRange!Range) + { + foreach (const i; 0 .. r.length) + { + MapType mapElement = mapFun(r[i]); + if (selectorFun(mapElement, extremeElementMapped)) + { + extremeElement = r[i]; + extremeElementMapped = mapElement; + } + } + } + else + { + while (!r.empty) + { + MapType mapElement = mapFun(r.front); + if (selectorFun(mapElement, extremeElementMapped)) + { + extremeElement = r.front; + extremeElementMapped = mapElement; + } + r.popFront(); + } + } + return extremeElement; +} + +private auto extremum(alias selector = "a < b", Range)(Range r) + if (isInputRange!Range && !isInfinite!Range && + !is(typeof(unaryFun!selector(ElementType!(Range).init)))) +{ + alias Element = ElementType!Range; + Unqual!Element seed = r.front; + r.popFront(); + return extremum!selector(r, seed); +} + +// if we only have one statement in the loop it can be optimized a lot better +private auto extremum(alias selector = "a < b", Range, + RangeElementType = ElementType!Range) + (Range r, RangeElementType seedElement) + if (isInputRange!Range && !isInfinite!Range && + !is(CommonType!(ElementType!Range, RangeElementType) == void) && + !is(typeof(unaryFun!selector(ElementType!(Range).init)))) +{ + alias Element = ElementType!Range; + alias CommonElement = CommonType!(Element, RangeElementType); + Unqual!CommonElement extremeElement = seedElement; + alias selectorFun = binaryFun!selector; + + // direct access via a random access range is faster + static if (isRandomAccessRange!Range) + { + foreach (const i; 0 .. r.length) + { + if (selectorFun(r[i], extremeElement)) + { + extremeElement = r[i]; + } + } + } + else + { + while (!r.empty) + { + if (selectorFun(r.front, extremeElement)) + { + extremeElement = r.front; + } + r.popFront(); + } + } + return extremeElement; +} + +@safe pure unittest +{ + // allows a custom map to select the extremum + assert([[0, 4], [1, 2]].extremum!"a[0]" == [0, 4]); + assert([[0, 4], [1, 2]].extremum!"a[1]" == [1, 2]); + + // allows a custom selector for comparison + assert([[0, 4], [1, 2]].extremum!("a[0]", "a > b") == [1, 2]); + assert([[0, 4], [1, 2]].extremum!("a[1]", "a > b") == [0, 4]); + + // use a custom comparator + import std.math : cmp; + assert([-2., 0, 5].extremum!cmp == 5.0); + assert([-2., 0, 2].extremum!`cmp(a, b) < 0` == -2.0); + + // combine with map + import std.range : enumerate; + assert([-3., 0, 5].enumerate.extremum!(`a.value`, cmp) == tuple(2, 5.0)); + assert([-2., 0, 2].enumerate.extremum!(`a.value`, `cmp(a, b) < 0`) == tuple(0, -2.0)); + + // seed with a custom value + int[] arr; + assert(arr.extremum(1) == 1); +} + +@safe pure nothrow unittest +{ + // 2d seeds + int[][] arr2d; + assert(arr2d.extremum([1]) == [1]); + + // allow seeds of different types (implicit casting) + assert(extremum([2, 3, 4], 1.5) == 1.5); +} + +@safe pure unittest +{ + import std.range : enumerate, iota; + + // forward ranges + assert(iota(1, 5).extremum() == 1); + assert(iota(2, 5).enumerate.extremum!"a.value" == tuple(0, 2)); + + // should work with const + const(int)[] immArr = [2, 1, 3]; + assert(immArr.extremum == 1); + + // should work with immutable + immutable(int)[] immArr2 = [2, 1, 3]; + assert(immArr2.extremum == 1); + + // with strings + assert(["b", "a", "c"].extremum == "a"); + + // with all dummy ranges + import std.internal.test.dummyrange; + foreach (DummyType; AllDummyRanges) + { + DummyType d; + assert(d.extremum == 1); + assert(d.extremum!(a => a) == 1); + assert(d.extremum!`a > b` == 10); + assert(d.extremum!(a => a, `a > b`) == 10); + } +} + +@nogc @safe nothrow pure unittest +{ + static immutable arr = [7, 3, 4, 2, 1, 8]; + assert(arr.extremum == 1); + + static immutable arr2d = [[1, 9], [3, 1], [4, 2]]; + assert(arr2d.extremum!"a[1]" == arr2d[1]); +} + // find /** Finds an individual element in an input range. Elements of $(D @@ -1156,7 +1435,7 @@ pred). Performs $(BIGOH walkLength(haystack)) evaluations of $(D pred). To _find the last occurrence of $(D needle) in $(D haystack), call $(D -find(retro(haystack), needle)). See $(XREF range, retro). +find(retro(haystack), needle)). See $(REF retro, std,range). Params: @@ -1165,7 +1444,8 @@ $(D "a == b"). The negated predicate $(D "a != b") can be used to search instead for the first element $(I not) matching the needle. -haystack = The $(XREF2 range, isInputRange, input range) searched in. +haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) +searched in. needle = The element searched for. @@ -1181,9 +1461,9 @@ that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no such position exists, returns an empty $(D haystack). See_Also: - $(WEB sgi.com/tech/stl/_find.html, STL's _find) + $(HTTP sgi.com/tech/stl/_find.html, STL's _find) */ -InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, Element needle) +InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, scope Element needle) if (isInputRange!InputRange && is (typeof(binaryFun!pred(haystack.front, needle)) : bool)) { @@ -1198,7 +1478,19 @@ if (isInputRange!InputRange && alias EType = ElementType!R; - static if (isNarrowString!R) + // If the haystack is a SortedRange we can use binary search to find the needle. + // Works only for the default find predicate and any SortedRange predicate. + // 8829 enhancement + import std.range : SortedRange; + static if (is(InputRange : SortedRange!TT, TT) && isDefaultPred) + { + auto lb = haystack.lowerBound(needle); + if (lb.length == haystack.length || haystack[lb.length] != needle) + return haystack[$ .. $]; + + return haystack[lb.length .. $]; + } + else static if (isNarrowString!R) { alias EEType = ElementEncodingType!R; alias UEEType = Unqual!EEType; @@ -1220,7 +1512,7 @@ if (isInputRange!InputRange && import core.stdc.string : memchr; auto ptr = memchr(haystack.ptr, needle, haystack.length); return ptr ? - haystack[ptr - haystack.ptr .. $] : + haystack[cast(char*) ptr - haystack.ptr .. $] : haystack[$ .. $]; } return trustedMemchr(haystack, needle); @@ -1324,6 +1616,23 @@ if (isInputRange!InputRange && { import std.algorithm.comparison : equal; import std.container : SList; + import std.range.primitives : empty; + import std.range; + + auto arr = assumeSorted!"a < b"([1, 2, 4, 4, 4, 4, 5, 6, 9]); + assert(find(arr, 4) == assumeSorted!"a < b"([4, 4, 4, 4, 5, 6, 9])); + assert(find(arr, 1) == arr); + assert(find(arr, 9) == assumeSorted!"a < b"([9])); + assert(find!"a > b"(arr, 4) == assumeSorted!"a < b"([5, 6, 9])); + assert(find!"a < b"(arr, 4) == arr); + assert(find(arr, 0).empty()); + assert(find(arr, 10).empty()); + assert(find(arr, 8).empty()); + + auto r = assumeSorted!"a > b"([10, 7, 3, 1, 0, 0]); + assert(find(r, 3) == assumeSorted!"a > b"([3, 1, 0, 0])); + assert(find!"a > b"(r, 8) == r); + assert(find!"a < b"(r, 5) == assumeSorted!"a > b"([3, 1, 0, 0])); assert(find("hello, world", ',') == ", world"); assert(find([1, 2, 3, 5], 4) == []); @@ -1344,8 +1653,6 @@ if (isInputRange!InputRange && import std.algorithm.comparison : equal; import std.container : SList; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto lst = SList!int(1, 2, 5, 7, 3); assert(lst.front == 1); auto r = find(lst[], 5); @@ -1358,34 +1665,37 @@ if (isInputRange!InputRange && { int[] a1 = [1, 2, 3]; assert(!find ([1, 2, 3], 2).empty); - assert(!find!((a,b)=>a==b)([1, 2, 3], 2).empty); + assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty); ubyte[] a2 = [1, 2, 3]; ubyte b2 = 2; assert(!find ([1, 2, 3], 2).empty); - assert(!find!((a,b)=>a==b)([1, 2, 3], 2).empty); + assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty); } @safe pure unittest { - import std.typetuple : TypeTuple; - foreach(R; TypeTuple!(string, wstring, dstring)) + import std.meta : AliasSeq; + foreach (R; AliasSeq!(string, wstring, dstring)) { - foreach(E; TypeTuple!(char, wchar, dchar)) + foreach (E; AliasSeq!(char, wchar, dchar)) { R r1 = "hello world"; E e1 = 'w'; assert(find ("hello world", 'w') == "world"); - assert(find!((a,b)=>a==b)("hello world", 'w') == "world"); + assert(find!((a,b)=>a == b)("hello world", 'w') == "world"); R r2 = "æ—¥c語"; E e2 = 'c'; assert(find ("æ—¥c語", 'c') == "c語"); - assert(find!((a,b)=>a==b)("æ—¥c語", 'c') == "c語"); + assert(find!((a,b)=>a == b)("æ—¥c語", 'c') == "c語"); + R r3 = "0123456789"; + E e3 = 'A'; + assert(find ("0123456789", 'A').empty); static if (E.sizeof >= 2) { - R r3 = "hello world"; - E e3 = 'w'; + R r4 = "hello world"; + E e4 = 'w'; assert(find ("日本語", '本') == "本語"); - assert(find!((a,b)=>a==b)("日本語", '本') == "本語"); + assert(find!((a,b)=>a == b)("日本語", '本') == "本語"); } } } @@ -1394,32 +1704,32 @@ if (isInputRange!InputRange && @safe unittest { //CTFE - static assert (find("abc", 'b') == "bc"); - static assert (find("æ—¥b語", 'b') == "b語"); - static assert (find("日本語", '本') == "本語"); - static assert (find([1, 2, 3], 2) == [2, 3]); + static assert(find("abc", 'b') == "bc"); + static assert(find("æ—¥b語", 'b') == "b語"); + static assert(find("日本語", '本') == "本語"); + static assert(find([1, 2, 3], 2) == [2, 3]); int[] a1 = [1, 2, 3]; static assert(find ([1, 2, 3], 2)); - static assert(find!((a,b)=>a==b)([1, 2, 3], 2)); + static assert(find!((a,b)=>a == b)([1, 2, 3], 2)); ubyte[] a2 = [1, 2, 3]; ubyte b2 = 2; static assert(find ([1, 2, 3], 2)); - static assert(find!((a,b)=>a==b)([1, 2, 3], 2)); + static assert(find!((a,b)=>a == b)([1, 2, 3], 2)); } @safe unittest { import std.exception : assertCTFEable; - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; void dg() @safe pure nothrow { byte[] sarr = [1, 2, 3, 4]; ubyte[] uarr = [1, 2, 3, 4]; - foreach(arr; TypeTuple!(sarr, uarr)) + foreach (arr; AliasSeq!(sarr, uarr)) { - foreach(T; TypeTuple!(byte, ubyte, int, uint)) + foreach (T; AliasSeq!(byte, ubyte, int, uint)) { assert(find(arr, cast(T) 3) == arr[2 .. $]); assert(find(arr, cast(T) 9) == arr[$ .. $]); @@ -1447,16 +1757,19 @@ until either $(D pred(haystack.front)), or $(D haystack.empty). Performs $(BIGOH haystack.length) evaluations of $(D pred). -To _find the last element of a bidirectional $(D haystack) satisfying -$(D pred), call $(D find!(pred)(retro(haystack))). See $(XREF -range, retro). +To _find the last element of a +$(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) $(D haystack) satisfying +$(D pred), call $(D find!(pred)(retro(haystack))). See $(REF retro, std,range). + +`find` behaves similar to `dropWhile` in other languages. Params: pred = The predicate for determining if a given element is the one being searched for. -haystack = The $(XREF2 range, isInputRange, input range) to search in. +haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to +search in. Returns: @@ -1465,7 +1778,7 @@ that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no such position exists, returns an empty $(D haystack). See_Also: - $(WEB sgi.com/tech/stl/find_if.html, STL's find_if) + $(HTTP sgi.com/tech/stl/find_if.html, STL's find_if) */ InputRange find(alias pred, InputRange)(InputRange haystack) if (isInputRange!InputRange) @@ -1486,17 +1799,6 @@ if (isInputRange!InputRange) } return haystack[$ .. $]; } - else static if (!isInfinite!R && hasSlicing!R && is(typeof(haystack[cast(size_t)0 .. $]))) - { - size_t i = 0; - for (auto h = haystack.save; !h.empty; h.popFront()) - { - if (predFun(h.front)) - return haystack[i .. $]; - ++i; - } - return haystack[$ .. $]; - } else { //standard range @@ -1522,7 +1824,6 @@ if (isInputRange!InputRange) @safe pure unittest { - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] r = [ 1, 2, 3 ]; assert(find!(a=>a > 2)(r) == [3]); bool pred(int x) { return x + 1 > 1.5; } @@ -1537,24 +1838,27 @@ Finds the first occurrence of a forward range in another forward range. Performs $(BIGOH walkLength(haystack) * walkLength(needle)) comparisons in the worst case. There are specializations that improve performance by taking -advantage of bidirectional or random access in the given ranges (where -possible), depending on the statistics of the two ranges' content. +advantage of $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) +or random access in the given ranges (where possible), depending on the statistics +of the two ranges' content. Params: pred = The predicate to use for comparing respective elements from the haystack and the needle. Defaults to simple equality $(D "a == b"). -haystack = The $(XREF2 range, isForwardRange, forward range) searched in. +haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) +searched in. -needle = The $(XREF2 range, isForwardRange, forward range) searched for. +needle = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) +searched for. Returns: $(D haystack) advanced such that $(D needle) is a prefix of it (if no such position exists, returns $(D haystack) advanced to termination). */ -R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) +R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) if (isForwardRange!R1 && isForwardRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) && !isRandomAccessRange!R1) @@ -1562,13 +1866,13 @@ if (isForwardRange!R1 && isForwardRange!R2 static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2 && haystack[0].sizeof == needle[0].sizeof) { - //return cast(R1) find(representation(haystack), representation(needle)); + // return cast(R1) find(representation(haystack), representation(needle)); // Specialization for simple string search alias Representation = Select!(haystack[0].sizeof == 1, ubyte[], Select!(haystack[0].sizeof == 2, ushort[], uint[])); // Will use the array specialization - static TO force(TO, T)(T r) @trusted { return cast(TO)r; } + static TO force(TO, T)(T r) @trusted { return cast(TO) r; } return force!R1(.find!(pred, Representation, Representation) (force!Representation(haystack), force!Representation(needle))); } @@ -1582,6 +1886,8 @@ if (isForwardRange!R1 && isForwardRange!R2 @safe unittest { import std.container : SList; + import std.range.primitives : empty; + import std.typecons : Tuple; assert(find("hello, world", "World").empty); assert(find("hello, world", "wo") == "world"); @@ -1604,8 +1910,6 @@ if (isForwardRange!R1 && isForwardRange!R2 import std.algorithm.comparison : equal; import std.container : SList; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto lst = SList!int(1, 2, 5, 7, 3); static assert(isForwardRange!(int[])); static assert(isForwardRange!(typeof(lst[]))); @@ -1613,58 +1917,149 @@ if (isForwardRange!R1 && isForwardRange!R2 assert(equal(r, SList!int(2, 5, 7, 3)[])); } -// Specialization for searching a random-access range for a -// bidirectional range -R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) -if (isRandomAccessRange!R1 && isBidirectionalRange!R2 +/// ditto +R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) +if (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && isBidirectionalRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { if (needle.empty) return haystack; - const needleLength = walkLength(needle.save); + + static if (hasLength!R2) + { + immutable needleLength = needle.length; + } + else + { + immutable needleLength = walkLength(needle.save); + } if (needleLength > haystack.length) { - // @@@BUG@@@ - //return haystack[$ .. $]; return haystack[haystack.length .. haystack.length]; } - // @@@BUG@@@ - // auto needleBack = moveBack(needle); - // Stage 1: find the step - size_t step = 1; - auto needleBack = needle.back; - needle.popBack(); - for (auto i = needle.save; !i.empty && i.back != needleBack; - i.popBack(), ++step) + // Optimization in case the ranges are both SortedRanges. + // Binary search can be used to find the first occurence + // of the first element of the needle in haystack. + // When it is found O(walklength(needle)) steps are performed. + // 8829 enhancement + import std.range : SortedRange; + import std.algorithm.comparison : mismatch; + static if (is(R1 == R2) + && is(R1 : SortedRange!TT, TT) + && pred == "a == b") { + auto needleFirstElem = needle[0]; + auto partitions = haystack.trisect(needleFirstElem); + auto firstElemLen = partitions[1].length; + size_t count = 0; + + if (firstElemLen == 0) + return haystack[$ .. $]; + + while (needle.front() == needleFirstElem) + { + needle.popFront(); + ++count; + + if (count > firstElemLen) + return haystack[$ .. $]; + } + + auto m = mismatch(partitions[2], needle); + + if (m[1].empty) + return haystack[partitions[0].length + partitions[1].length - count .. $]; } - // Stage 2: linear find - size_t scout = needleLength - 1; - for (;;) + else static if (isRandomAccessRange!R2) { - if (scout >= haystack.length) + immutable lastIndex = needleLength - 1; + auto last = needle[lastIndex]; + size_t j = lastIndex, skip = 0; + for (; j < haystack.length;) { - return haystack[haystack.length .. haystack.length]; + if (!binaryFun!pred(haystack[j], last)) + { + ++j; + continue; + } + immutable k = j - lastIndex; + // last elements match + for (size_t i = 0;; ++i) + { + if (i == lastIndex) + return haystack[k .. haystack.length]; + if (!binaryFun!pred(haystack[k + i], needle[i])) + break; + } + if (skip == 0) + { + skip = 1; + while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1]) + { + ++skip; + } + } + j += skip; } - if (!binaryFun!pred(haystack[scout], needleBack)) + } + else + { + // @@@BUG@@@ + // auto needleBack = moveBack(needle); + // Stage 1: find the step + size_t step = 1; + auto needleBack = needle.back; + needle.popBack(); + for (auto i = needle.save; !i.empty && i.back != needleBack; + i.popBack(), ++step) { - ++scout; - continue; } - // Found a match with the last element in the needle - auto cand = haystack[scout + 1 - needleLength .. haystack.length]; - if (startsWith!pred(cand, needle)) + // Stage 2: linear find + size_t scout = needleLength - 1; + for (;;) { - // found - return cand; + if (scout >= haystack.length) + break; + if (!binaryFun!pred(haystack[scout], needleBack)) + { + ++scout; + continue; + } + // Found a match with the last element in the needle + auto cand = haystack[scout + 1 - needleLength .. haystack.length]; + if (startsWith!pred(cand, needle)) + { + // found + return cand; + } + scout += step; } - // Continue with the stride - scout += step; } + return haystack[haystack.length .. haystack.length]; +} + +@safe unittest +{ + import std.range; + import std.stdio; + + auto r1 = assumeSorted([1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 8, 10]); + auto r2 = assumeSorted([3, 3, 4, 5, 6, 7, 8, 8]); + auto r3 = assumeSorted([3, 4, 5, 6, 7, 8]); + auto r4 = assumeSorted([4, 5, 6]); + auto r5 = assumeSorted([12, 13]); + auto r6 = assumeSorted([8, 8, 10, 11]); + auto r7 = assumeSorted([3, 3, 3, 3, 3, 3, 3]); + + assert(find(r1, r2) == assumeSorted([3, 3, 4, 5, 6, 7, 8, 8, 8, 10])); + assert(find(r1, r3) == assumeSorted([3, 4, 5, 6, 7, 8, 8, 8, 10])); + assert(find(r1, r4) == assumeSorted([4, 5, 6, 7, 8, 8, 8, 10])); + assert(find(r1, r5).empty()); + assert(find(r1, r6).empty()); + assert(find(r1, r7).empty()); } @safe unittest { - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); // @@@BUG@@@ removing static below makes unittest fail static struct BiRange { @@ -1676,18 +2071,11 @@ if (isRandomAccessRange!R1 && isBidirectionalRange!R2 void popFront() { return payload.popFront(); } void popBack() { return payload.popBack(); } } - //static assert(isBidirectionalRange!BiRange); auto r = BiRange([1, 2, 3, 10, 11, 4]); - //assert(equal(find(r, [3, 10]), BiRange([3, 10, 11, 4]))); - //assert(find("abc", "bc").length == 2); - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - //assert(find!"a == b"("abc", "bc").length == 2); } -// Leftover specialization: searching a random-access range for a -// non-bidirectional forward range -R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) +/// ditto +R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { @@ -1773,9 +2161,8 @@ if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2 && assert(find(haystack, filter!"true"(needle)).empty); } -// Internally used by some find() overloads above. Can't make it -// private due to bugs in the compiler. -/*private*/ R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, R2 needle) +// Internally used by some find() overloads above +private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle) { enum estimateNeedleLength = hasLength!R1 && !hasLength!R2; @@ -1847,9 +2234,6 @@ if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2 && { // Test simpleMindedFind for the case where both haystack and needle have // length. - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - struct CustomString { @safe: @@ -1884,7 +2268,8 @@ pred = The predicate to use for comparing elements. haystack = The target of the search. Must be an input range. If any of $(D needles) is a range with elements comparable to -elements in $(D haystack), then $(D haystack) must be a forward range +elements in $(D haystack), then $(D haystack) must be a +$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) such that the search can backtrack. needles = One or more items to search for. Each of $(D needles) must @@ -1936,6 +2321,7 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) /// @safe unittest { + import std.typecons : tuple; int[] a = [ 1, 4, 2, 3 ]; assert(find(a, 4) == [ 4, 2, 3 ]); assert(find(a, [ 1, 4 ]) == [ 1, 4, 2, 3 ]); @@ -1946,10 +2332,7 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto s1 = "Mary has a little lamb"; - //writeln(find(s1, "has a", "has an")); assert(find(s1, "has a", "has an") == tuple("has a little lamb", 1)); assert(find(s1, 't', "has a", "has an") == tuple("has a little lamb", 2)); assert(find(s1, 't', "has a", 'y', "has an") == tuple("y has a little lamb", 3)); @@ -1959,17 +2342,14 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) @safe unittest { import std.algorithm.internal : rndstuff; - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; import std.uni : toUpper; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[] a = [ 1, 2, 3 ]; assert(find(a, 5).empty); assert(find(a, 2) == [2, 3]); - foreach (T; TypeTuple!(int, double)) + foreach (T; AliasSeq!(int, double)) { auto b = rndstuff!(T)(); if (!b.length) continue; @@ -1980,7 +2360,6 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) // Case-insensitive find of a string string[] s = [ "Hello", "world", "!" ]; - //writeln(find!("toUpper(a) == toUpper(b)")(s, "hello")); assert(find!("toUpper(a) == toUpper(b)")(s, "hello").length == 3); static bool f(string a, string b) { return toUpper(a) == toUpper(b); } @@ -1991,17 +2370,14 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) { import std.algorithm.internal : rndstuff; import std.algorithm.comparison : equal; + import std.meta : AliasSeq; import std.range : retro; - import std.typetuple : TypeTuple; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 3, 2, 6 ]; assert(find(retro(a), 5).empty); assert(equal(find(retro(a), 2), [ 2, 3, 2, 1 ][])); - foreach (T; TypeTuple!(int, double)) + foreach (T; AliasSeq!(int, double)) { auto b = rndstuff!(T)(); if (!b.length) continue; @@ -2017,14 +2393,13 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) import std.algorithm.comparison : equal; import std.internal.test.dummyrange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; int[] b = [ 1, 2, 3 ]; assert(find(a, b) == [ 1, 2, 3, 4, 5 ]); assert(find(b, a).empty); - foreach (DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { DummyType d; auto findRes = find(d, 5); assert(equal(findRes, [5,6,7,8,9,10])); @@ -2033,7 +2408,8 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) /** * Finds $(D needle) in $(D haystack) efficiently using the - * $(LUCKY Boyer-Moore) method. + * $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm, + * Boyer-Moore) method. * * Params: * haystack = A random-access range with length and slicing. @@ -2043,21 +2419,20 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) * $(D haystack) advanced such that $(D needle) is a prefix of it (if no * such position exists, returns $(D haystack) advanced to termination). */ -Range1 find(Range1, alias pred, Range2)( - Range1 haystack, BoyerMooreFinder!(pred, Range2) needle) +RandomAccessRange find(RandomAccessRange, alias pred, InputRange)( + RandomAccessRange haystack, scope BoyerMooreFinder!(pred, InputRange) needle) { return needle.beFound(haystack); } @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); string h = "/homes/aalexand/d/dmd/bin/../lib/libphobos.a(dmain2.o)"~ "(.gnu.linkonce.tmain+0x74): In function `main' undefined reference"~ " to `_Dmain':"; string[] ns = ["libphobos", "function", " undefined", "`", ":"]; - foreach (n ; ns) { + foreach (n ; ns) + { auto p = find(h, boyerMooreFinder(n)); assert(!p.empty); } @@ -2066,6 +2441,7 @@ Range1 find(Range1, alias pred, Range2)( /// @safe unittest { + import std.range.primitives : empty; int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; int[] b = [ 1, 2, 3 ]; @@ -2090,7 +2466,7 @@ $(LREF among) for checking a value against multiple possibilities. +/ template canFind(alias pred="a == b") { - import std.typetuple : allSatisfy; + import std.meta : allSatisfy; /++ Returns $(D true) if and only if any value $(D v) found in the @@ -2107,7 +2483,7 @@ template canFind(alias pred="a == b") Returns $(D true) if and only if $(D needle) can be found in $(D range). Performs $(BIGOH haystack.length) evaluations of $(D pred). +/ - bool canFind(Range, Element)(Range haystack, Element needle) + bool canFind(Range, Element)(Range haystack, scope Element needle) if (is(typeof(find!pred(haystack, needle)))) { return !find!pred(haystack, needle).empty; @@ -2124,7 +2500,7 @@ template canFind(alias pred="a == b") without having to deal with the tuple that $(D LREF find) returns for the same operation. +/ - size_t canFind(Range, Ranges...)(Range haystack, Ranges needles) + size_t canFind(Range, Ranges...)(Range haystack, scope Ranges needles) if (Ranges.length > 1 && allSatisfy!(isForwardRange, Ranges) && is(typeof(find!pred(haystack, needles)))) @@ -2147,11 +2523,25 @@ template canFind(alias pred="a == b") assert(canFind([0, 1, 2, 3], [1, 3], [2, 4]) == 0); } +/** + * Example using a custom predicate. + * Note that the needle appears as the second argument of the predicate. + */ +@safe unittest +{ + auto words = [ + "apple", + "beeswax", + "cardboard" + ]; + assert(!canFind(words, "bees")); + assert( canFind!((string a, string b) => a.startsWith(b))(words, "bees")); +} + @safe unittest { import std.algorithm.internal : rndstuff; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + auto a = rndstuff!(int)(); if (a.length) { @@ -2174,7 +2564,8 @@ evaluations of $(D pred). Params: pred = The predicate to satisfy. - r = A $(XREF2 range, isForwardRange, forward range) to search in. + r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to + search in. Returns: $(D r) advanced to the first occurrence of two adjacent elements that satisfy @@ -2182,10 +2573,10 @@ the given predicate. If there are no such two elements, returns $(D r) advanced until empty. See_Also: - $(WEB sgi.com/tech/stl/adjacent_find.html, STL's adjacent_find) + $(HTTP sgi.com/tech/stl/adjacent_find.html, STL's adjacent_find) */ Range findAdjacent(alias pred = "a == b", Range)(Range r) - if (isForwardRange!(Range)) +if (isForwardRange!(Range)) { auto ahead = r.save; if (!ahead.empty) @@ -2216,7 +2607,6 @@ Range findAdjacent(alias pred = "a == b", Range)(Range r) import std.internal.test.dummyrange; import std.range; - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ]; auto p = findAdjacent(a); assert(p == [10, 10, 9, 8, 8, 7, 8, 9 ]); @@ -2249,20 +2639,21 @@ Performs $(BIGOH seq.length * choices.length) evaluations of $(D pred). Params: pred = The predicate to use for determining a match. - seq = The $(XREF2 range, isInputRange, input range) to search. - choices = A $(XREF2 range, isForwardRange, forward range) of possible - choices. + seq = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to + search. + choices = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + of possible choices. Returns: $(D seq) advanced to the first matching element, or until empty if there are no matching elements. See_Also: - $(WEB sgi.com/tech/stl/find_first_of.html, STL's find_first_of) + $(HTTP sgi.com/tech/stl/find_first_of.html, STL's find_first_of) */ -Range1 findAmong(alias pred = "a == b", Range1, Range2)( - Range1 seq, Range2 choices) - if (isInputRange!Range1 && isForwardRange!Range2) +InputRange findAmong(alias pred = "a == b", InputRange, ForwardRange)( + InputRange seq, ForwardRange choices) +if (isInputRange!InputRange && isForwardRange!ForwardRange) { for (; !seq.empty && find!pred(choices, seq.front).empty; seq.popFront()) { @@ -2280,7 +2671,6 @@ Range1 findAmong(alias pred = "a == b", Range1, Range2)( @safe unittest { - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ -1, 0, 2, 1, 2, 3, 4, 5 ]; int[] b = [ 1, 2, 3 ]; assert(findAmong(a, b) == [2, 1, 2, 3, 4, 5 ]); @@ -2295,8 +2685,12 @@ Range1 findAmong(alias pred = "a == b", Range1, Range2)( * right after the first occurrence of $(D needle). * * Params: - * haystack = The $(XREF2 range, isForwardRange, forward range) to search in. - * needle = The $(XREF2 range, isForwardRange, forward range) to search for. + * haystack = The + * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search + * in. + * needle = The + * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search + * for. * * Returns: $(D true) if the needle was found, in which case $(D haystack) is * positioned after the end of the first occurrence of $(D needle); otherwise @@ -2316,6 +2710,7 @@ if (isForwardRange!R1 && isForwardRange!R2 /// @safe unittest { + import std.range.primitives : empty; // Needle is found; s is replaced by the substring following the first // occurrence of the needle. string s = "abcdef"; @@ -2331,49 +2726,88 @@ if (isForwardRange!R1 && isForwardRange!R2 } /** -These functions find the first occurrence of $(D needle) in $(D -haystack) and then split $(D haystack) as follows. - -$(D findSplit) returns a tuple $(D result) containing $(I three) -ranges. $(D result[0]) is the portion of $(D haystack) before $(D -needle), $(D result[1]) is the portion of $(D haystack) that matches -$(D needle), and $(D result[2]) is the portion of $(D haystack) after -the match. If $(D needle) was not found, $(D result[0]) -comprehends $(D haystack) entirely and $(D result[1]) and $(D result[2]) -are empty. - -$(D findSplitBefore) returns a tuple $(D result) containing two -ranges. $(D result[0]) is the portion of $(D haystack) before $(D -needle), and $(D result[1]) is the balance of $(D haystack) starting -with the match. If $(D needle) was not found, $(D result[0]) -comprehends $(D haystack) entirely and $(D result[1]) is empty. - -$(D findSplitAfter) returns a tuple $(D result) containing two ranges. -$(D result[0]) is the portion of $(D haystack) up to and including the -match, and $(D result[1]) is the balance of $(D haystack) starting -after the match. If $(D needle) was not found, $(D result[0]) is empty -and $(D result[1]) is $(D haystack). +These functions find the first occurrence of `needle` in `haystack` and then +split `haystack` as follows. + +`findSplit` returns a tuple `result` containing $(I three) ranges. `result[0]` +is the portion of `haystack` before `needle`, `result[1]` is the portion of +`haystack` that matches `needle`, and `result[2]` is the portion of `haystack` +after the match. If `needle` was not found, `result[0]` comprehends `haystack` +entirely and `result[1]` and `result[2]` are empty. + +`findSplitBefore` returns a tuple `result` containing two ranges. `result[0]` is +the portion of `haystack` before `needle`, and `result[1]` is the balance of +`haystack` starting with the match. If `needle` was not found, `result[0]` +comprehends `haystack` entirely and `result[1]` is empty. + +`findSplitAfter` returns a tuple `result` containing two ranges. +`result[0]` is the portion of `haystack` up to and including the +match, and `result[1]` is the balance of `haystack` starting +after the match. If `needle` was not found, `result[0]` is empty +and `result[1]` is `haystack`. In all cases, the concatenation of the returned ranges spans the -entire $(D haystack). +entire `haystack`. + +If `haystack` is a random-access range, all three components of the tuple have +the same type as `haystack`. Otherwise, `haystack` must be a +$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and +the type of `result[0]` and `result[1]` is the same as $(REF takeExactly, +std,range). -If $(D haystack) is a random-access range, all three components of the -tuple have the same type as $(D haystack). Otherwise, $(D haystack) -must be a forward range and the type of $(D result[0]) and $(D -result[1]) is the same as $(XREF range,takeExactly). +Params: + pred = Predicate to use for comparing needle against haystack. + haystack = The range to search. + needle = What to look for. + +Returns: + +A sub-type of `Tuple!()` of the split portions of `haystack` (see above for +details). This sub-type of `Tuple!()` has `opCast` defined for `bool`. This +`opCast` returns `true` when the separating `needle` was found +(`!result[1].empty`) and `false` otherwise. This enables the convenient idiom +shown in the following example. + +Example: +--- +if (const split = haystack.findSplit(needle)) +{ + doSomethingWithSplit(split); +} +--- */ auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2) { + static struct Result(S1, S2) if (isForwardRange!S1 && + isForwardRange!S2) + { + this(S1 pre, S1 separator, S2 post) + { + asTuple = typeof(asTuple)(pre, separator, post); + } + void opAssign(typeof(asTuple) rhs) + { + asTuple = rhs; + } + Tuple!(S1, S1, S2) asTuple; + bool opCast(T : bool)() + { + return !asTuple[1].empty; + } + alias asTuple this; + } + static if (isSomeString!R1 && isSomeString!R2 - || isRandomAccessRange!R1 && hasLength!R2) + || (isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && hasLength!R2)) { auto balance = find!pred(haystack, needle); immutable pos1 = haystack.length - balance.length; immutable pos2 = balance.empty ? pos1 : pos1 + needle.length; - return tuple(haystack[0 .. pos1], - haystack[pos1 .. pos2], - haystack[pos2 .. haystack.length]); + return Result!(typeof(haystack[0 .. pos1]), + typeof(haystack[pos2 .. haystack.length]))(haystack[0 .. pos1], + haystack[pos1 .. pos2], + haystack[pos2 .. haystack.length]); } else { @@ -2398,9 +2832,10 @@ if (isForwardRange!R1 && isForwardRange!R2) pos2 = ++pos1; } } - return tuple(takeExactly(original, pos1), - takeExactly(haystack, pos2 - pos1), - h); + return Result!(typeof(takeExactly(original, pos1)), + typeof(h))(takeExactly(original, pos1), + takeExactly(haystack, pos2 - pos1), + h); } } @@ -2408,12 +2843,33 @@ if (isForwardRange!R1 && isForwardRange!R2) auto findSplitBefore(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2) { + static struct Result(S1, S2) if (isForwardRange!S1 && + isForwardRange!S2) + { + this(S1 pre, S2 post) + { + asTuple = typeof(asTuple)(pre, post); + } + void opAssign(typeof(asTuple) rhs) + { + asTuple = rhs; + } + Tuple!(S1, S2) asTuple; + bool opCast(T : bool)() + { + return !asTuple[0].empty; + } + alias asTuple this; + } + static if (isSomeString!R1 && isSomeString!R2 - || isRandomAccessRange!R1 && hasLength!R2) + || (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2)) { auto balance = find!pred(haystack, needle); immutable pos = haystack.length - balance.length; - return tuple(haystack[0 .. pos], haystack[pos .. haystack.length]); + return Result!(typeof(haystack[0 .. pos]), + typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos], + haystack[pos .. haystack.length]); } else { @@ -2437,7 +2893,9 @@ if (isForwardRange!R1 && isForwardRange!R2) ++pos; } } - return tuple(takeExactly(original, pos), haystack); + return Result!(typeof(takeExactly(original, pos)), + typeof(haystack))(takeExactly(original, pos), + haystack); } } @@ -2445,12 +2903,33 @@ if (isForwardRange!R1 && isForwardRange!R2) auto findSplitAfter(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2) { + static struct Result(S1, S2) if (isForwardRange!S1 && + isForwardRange!S2) + { + this(S1 pre, S2 post) + { + asTuple = typeof(asTuple)(pre, post); + } + void opAssign(typeof(asTuple) rhs) + { + asTuple = rhs; + } + Tuple!(S1, S2) asTuple; + bool opCast(T : bool)() + { + return !asTuple[1].empty; + } + alias asTuple this; + } + static if (isSomeString!R1 && isSomeString!R2 - || isRandomAccessRange!R1 && hasLength!R2) + || isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2) { auto balance = find!pred(haystack, needle); immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length; - return tuple(haystack[0 .. pos], haystack[pos .. haystack.length]); + return Result!(typeof(haystack[0 .. pos]), + typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos], + haystack[pos .. haystack.length]); } else { @@ -2464,7 +2943,9 @@ if (isForwardRange!R1 && isForwardRange!R2) if (h.empty) { // Failed search - return tuple(takeExactly(original, 0), original); + return Result!(typeof(takeExactly(original, 0)), + typeof(original))(takeExactly(original, 0), + original); } if (binaryFun!pred(h.front, n.front)) { @@ -2480,15 +2961,23 @@ if (isForwardRange!R1 && isForwardRange!R2) pos2 = ++pos1; } } - return tuple(takeExactly(original, pos2), h); + return Result!(typeof(takeExactly(original, pos2)), + typeof(h))(takeExactly(original, pos2), + h); } } /// -@safe unittest +@safe pure nothrow unittest { + import std.range.primitives : empty; + auto a = "Carl Sagan Memorial Station"; auto r = findSplit(a, "Velikovsky"); + import std.typecons : isTuple; + static assert(isTuple!(typeof(r.asTuple))); + static assert(isTuple!(typeof(r))); + assert(!r); assert(r[0] == a); assert(r[1].empty); assert(r[2].empty); @@ -2497,41 +2986,58 @@ if (isForwardRange!R1 && isForwardRange!R2) assert(r[1] == " "); assert(r[2] == "Sagan Memorial Station"); auto r1 = findSplitBefore(a, "Sagan"); - assert(r1[0] == "Carl ", r1[0]); + assert(r1); + assert(r1[0] == "Carl "); assert(r1[1] == "Sagan Memorial Station"); auto r2 = findSplitAfter(a, "Sagan"); + assert(r2); assert(r2[0] == "Carl Sagan"); assert(r2[1] == " Memorial Station"); } -@safe unittest +/// Use $(REF only, std,range) to find single elements: +@safe pure nothrow unittest +{ + import std.range : only; + assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); +} + +@safe pure nothrow unittest { + import std.range.primitives : empty; + auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; auto r = findSplit(a, [9, 1]); + assert(!r); assert(r[0] == a); assert(r[1].empty); assert(r[2].empty); r = findSplit(a, [3]); + assert(r); assert(r[0] == a[0 .. 2]); assert(r[1] == a[2 .. 3]); assert(r[2] == a[3 .. $]); auto r1 = findSplitBefore(a, [9, 1]); + assert(r1); assert(r1[0] == a); assert(r1[1].empty); r1 = findSplitBefore(a, [3, 4]); + assert(r1); assert(r1[0] == a[0 .. 2]); assert(r1[1] == a[2 .. $]); - r1 = findSplitAfter(a, [9, 1]); - assert(r1[0].empty); - assert(r1[1] == a); - r1 = findSplitAfter(a, [3, 4]); - assert(r1[0] == a[0 .. 4]); - assert(r1[1] == a[4 .. $]); + auto r2 = findSplitAfter(a, [9, 1]); + assert(r2); + assert(r2[0].empty); + assert(r2[1] == a); + r2 = findSplitAfter(a, [3, 4]); + assert(r2); + assert(r2[0] == a[0 .. 4]); + assert(r2[1] == a[4 .. $]); } -@safe unittest +@safe pure nothrow unittest { import std.algorithm.comparison : equal; import std.algorithm.iteration : filter; @@ -2539,39 +3045,97 @@ if (isForwardRange!R1 && isForwardRange!R2) auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; auto fwd = filter!"a > 0"(a); auto r = findSplit(fwd, [9, 1]); + assert(!r); assert(equal(r[0], a)); assert(r[1].empty); assert(r[2].empty); r = findSplit(fwd, [3]); + assert(r); assert(equal(r[0], a[0 .. 2])); assert(equal(r[1], a[2 .. 3])); assert(equal(r[2], a[3 .. $])); auto r1 = findSplitBefore(fwd, [9, 1]); + assert(r1); assert(equal(r1[0], a)); assert(r1[1].empty); r1 = findSplitBefore(fwd, [3, 4]); + assert(r1); assert(equal(r1[0], a[0 .. 2])); assert(equal(r1[1], a[2 .. $])); - r1 = findSplitAfter(fwd, [9, 1]); - assert(r1[0].empty); - assert(equal(r1[1], a)); - r1 = findSplitAfter(fwd, [3, 4]); - assert(equal(r1[0], a[0 .. 4])); - assert(equal(r1[1], a[4 .. $])); + auto r2 = findSplitAfter(fwd, [9, 1]); + assert(r2); + assert(r2[0].empty); + assert(equal(r2[1], a)); + r2 = findSplitAfter(fwd, [3, 4]); + assert(r2); + assert(equal(r2[0], a[0 .. 4])); + assert(equal(r2[1], a[4 .. $])); } -/** -Returns the minimum element of a range together with the number of -occurrences. The function can actually be used for counting the -maximum or any other ordering predicate (that's why $(D maxCount) is -not provided). - */ +@safe pure nothrow @nogc unittest +{ + auto str = "sep,one,sep,two"; + + auto split = str.findSplitAfter(","); + assert(split[0] == "sep,"); + + split = split[1].findSplitAfter(","); + assert(split[0] == "one,"); + + split = split[1].findSplitBefore(","); + assert(split[0] == "sep"); +} + +@safe pure nothrow @nogc unittest +{ + auto str = "sep,one,sep,two"; + + auto split = str.findSplitBefore(",two"); + assert(split[0] == "sep,one,sep"); + assert(split[1] == ",two"); + + split = split[0].findSplitBefore(",sep"); + assert(split[0] == "sep,one"); + assert(split[1] == ",sep"); + + split = split[0].findSplitAfter(","); + assert(split[0] == "sep,"); + assert(split[1] == "one"); +} + +// minCount +/** + +Computes the minimum (respectively maximum) of `range` along with its number of +occurrences. Formally, the minimum is a value `x` in `range` such that $(D +pred(a, x)) is `false` for all values `a` in `range`. Conversely, the maximum is +a value `x` in `range` such that $(D pred(x, a)) is `false` for all values `a` +in `range` (note the swapped arguments to `pred`). + +These functions may be used for computing arbitrary extrema by choosing `pred` +appropriately. For corrrect functioning, `pred` must be a strict partial order, +i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and +irreflexive ($(D pred(a, a)) is `false`). The $(LUCKY trichotomy property of +inequality) is not required: these algoritms consider elements `a` and `b` equal +(for the purpose of counting) if `pred` puts them in the same equivalence class, +i.e. $(D !pred(a, b) && !pred(b, a)). + +Params: + pred = The ordering predicate to use to determine the extremum (minimum + or maximum). + range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to count. + +Returns: The minimum, respectively maximum element of a range together with the +number it occurs in the range. + +Throws: `Exception` if `range.empty`. + */ Tuple!(ElementType!Range, size_t) minCount(alias pred = "a < b", Range)(Range range) - if (isInputRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) +if (isInputRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(range.front, range.front)))) { import std.algorithm.internal : algoFormat; import std.exception : enforce; @@ -2580,7 +3144,7 @@ minCount(alias pred = "a < b", Range)(Range range) alias UT = Unqual!T; alias RetType = Tuple!(T, size_t); - static assert (is(typeof(RetType(range.front, 1))), + static assert(is(typeof(RetType(range.front, 1))), algoFormat("Error: Cannot call minCount on a %s, because it is not possible "~ "to copy the result value (a %s) into a Tuple.", Range.stringof, T.stringof)); @@ -2592,7 +3156,12 @@ minCount(alias pred = "a < b", Range)(Range range) Range least = range.save; for (range.popFront(); !range.empty; range.popFront()) { - if (binaryFun!pred(least.front, range.front)) continue; + if (binaryFun!pred(least.front, range.front)) + { + assert(!binaryFun!pred(range.front, least.front), + "min/maxPos: predicate must be a strict partial order."); + continue; + } if (binaryFun!pred(range.front, least.front)) { // change the min @@ -2608,7 +3177,7 @@ minCount(alias pred = "a < b", Range)(Range range) { UT v = UT.init; static if (isAssignable!(UT, T)) v = range.front; - else v = cast(UT)range.front; + else v = cast(UT) range.front; for (range.popFront(); !range.empty; range.popFront()) { @@ -2617,7 +3186,7 @@ minCount(alias pred = "a < b", Range)(Range range) { // change the min static if (isAssignable!(UT, T)) v = range.front; - else v = cast(UT)range.front; //Safe because !hasElaborateAssign!UT + else v = cast(UT) range.front; //Safe because !hasElaborateAssign!UT occurrences = 1; } else @@ -2649,30 +3218,34 @@ minCount(alias pred = "a < b", Range)(Range range) "to keep track of the smallest %s element.", Range.stringof, T.stringof)); } +/// Ditto +Tuple!(ElementType!Range, size_t) +maxCount(alias pred = "a < b", Range)(Range range) +if (isInputRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(range.front, range.front)))) +{ + return range.minCount!((a, b) => binaryFun!pred(b, a)); +} + /// -unittest +@safe unittest { import std.conv : text; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + import std.typecons : tuple; int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; // Minimum is 1 and occurs 3 times - assert(minCount(a) == tuple(1, 3)); + assert(a.minCount == tuple(1, 3)); // Maximum is 4 and occurs 2 times - assert(minCount!("a > b")(a) == tuple(4, 2)); + assert(a.maxCount == tuple(4, 2)); } -unittest +@system unittest { import std.conv : text; import std.exception : assertThrown; import std.internal.test.dummyrange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[][] b = [ [4], [2, 4], [4], [4] ]; auto c = minCount!("a[0] < b[0]")(b); assert(c == tuple([2, 4], 1), text(c[0])); @@ -2685,13 +3258,10 @@ unittest assert(minCount(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2)); } -unittest +@system unittest { import std.conv : text; - import std.typetuple : TypeTuple; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + import std.meta : AliasSeq; static struct R(T) //input range { @@ -2738,7 +3308,7 @@ unittest } static assert(!isAssignable!S3); - foreach (Type; TypeTuple!(S1, IS1, S2, IS2, S3)) + foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3)) { static if (is(Type == immutable)) alias V = immutable int; else alias V = int; @@ -2751,31 +3321,303 @@ unittest } } +/** +Iterates the passed range and returns the minimal element. +A custom mapping function can be passed to `map`. +In other languages this is sometimes called `argmin`. + +Complexity: O(n) + Exactly `n - 1` comparisons are needed. + +Params: + map = custom accessor for the comparison key + r = range from which the minimal element will be selected + seed = custom seed to use as initial element + +Returns: The minimal element of the passed-in range. + +See_Also: + $(REF min, std,algorithm,comparison) +*/ +auto minElement(alias map, Range)(Range r) +if (isInputRange!Range && !isInfinite!Range) +{ + return extremum!map(r); +} + +/// ditto +auto minElement(Range)(Range r) + if (isInputRange!Range && !isInfinite!Range) +{ + return extremum(r); +} + +/// ditto +auto minElement(alias map, Range, RangeElementType = ElementType!Range) + (Range r, RangeElementType seed) +if (isInputRange!Range && !isInfinite!Range && + !is(CommonType!(ElementType!Range, RangeElementType) == void)) +{ + return extremum!map(r, seed); +} + +/// ditto +auto minElement(Range, RangeElementType = ElementType!Range) + (Range r, RangeElementType seed) + if (isInputRange!Range && !isInfinite!Range && + !is(CommonType!(ElementType!Range, RangeElementType) == void)) +{ + return extremum(r, seed); +} + +/// +@safe pure unittest +{ + import std.range : enumerate; + import std.typecons : tuple; + + assert([2, 1, 4, 3].minElement == 1); + + // allows to get the index of an element too + assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3)); + + // any custom accessor can be passed + assert([[0, 4], [1, 2]].minElement!"a[1]" == [1, 2]); + + // can be seeded + int[] arr; + assert(arr.minElement(1) == 1); +} + +@safe pure unittest +{ + import std.range : enumerate, iota; + // supports mapping + assert([3, 4, 5, 1, 2].enumerate.minElement!"a.value" == tuple(3, 1)); + assert([5, 2, 4].enumerate.minElement!"a.value" == tuple(1, 2)); + + // forward ranges + assert(iota(1, 5).minElement() == 1); + assert(iota(2, 5).enumerate.minElement!"a.value" == tuple(0, 2)); + + // should work with const + const(int)[] immArr = [2, 1, 3]; + assert(immArr.minElement == 1); + + // should work with immutable + immutable(int)[] immArr2 = [2, 1, 3]; + assert(immArr2.minElement == 1); + + // with strings + assert(["b", "a", "c"].minElement == "a"); + + // with all dummy ranges + import std.internal.test.dummyrange; + foreach (DummyType; AllDummyRanges) + { + DummyType d; + assert(d.minElement == 1); + assert(d.minElement!(a => a) == 1); + } + + // with empty, but seeded ranges + int[] arr; + assert(arr.minElement(42) == 42); + assert(arr.minElement!(a => a)(42) == 42); +} + +@nogc @safe nothrow pure unittest +{ + static immutable arr = [7, 3, 4, 2, 1, 8]; + assert(arr.minElement == 1); + + static immutable arr2d = [[1, 9], [3, 1], [4, 2]]; + assert(arr2d.minElement!"a[1]" == arr2d[1]); +} + +/** +Iterates the passed range and returns the maximal element. +A custom mapping function can be passed to `map`. +In other languages this is sometimes called `argmax`. + +Complexity: + Exactly `n - 1` comparisons are needed. + +Params: + map = custom accessor for the comparison key + r = range from which the maximum will be selected + seed = custom seed to use as initial element + +Returns: The maximal element of the passed-in range. + +See_Also: + $(REF max, std,algorithm,comparison) +*/ +auto maxElement(alias map, Range)(Range r) +if (isInputRange!Range && !isInfinite!Range) +{ + return extremum!(map, "a > b")(r); +} + +/// ditto +auto maxElement(Range)(Range r) +if (isInputRange!Range && !isInfinite!Range) +{ + return extremum!`a > b`(r); +} + +/// ditto +auto maxElement(alias map, Range, RangeElementType = ElementType!Range) + (Range r, RangeElementType seed) +if (isInputRange!Range && !isInfinite!Range && + !is(CommonType!(ElementType!Range, RangeElementType) == void)) +{ + return extremum!(map, "a > b")(r, seed); +} + +/// ditto +auto maxElement(Range, RangeElementType = ElementType!Range) + (Range r, RangeElementType seed) +if (isInputRange!Range && !isInfinite!Range && + !is(CommonType!(ElementType!Range, RangeElementType) == void)) +{ + return extremum!`a > b`(r, seed); +} + +/// +@safe pure unittest +{ + import std.range : enumerate; + import std.typecons : tuple; + assert([2, 1, 4, 3].maxElement == 4); + + // allows to get the index of an element too + assert([2, 1, 4, 3].enumerate.maxElement!"a.value" == tuple(2, 4)); + + // any custom accessor can be passed + assert([[0, 4], [1, 2]].maxElement!"a[1]" == [0, 4]); + + // can be seeded + int[] arr; + assert(arr.minElement(1) == 1); +} + +@safe pure unittest +{ + import std.range : enumerate, iota; + + // supports mapping + assert([3, 4, 5, 1, 2].enumerate.maxElement!"a.value" == tuple(2, 5)); + assert([5, 2, 4].enumerate.maxElement!"a.value" == tuple(0, 5)); + + // forward ranges + assert(iota(1, 5).maxElement() == 4); + assert(iota(2, 5).enumerate.maxElement!"a.value" == tuple(2, 4)); + assert(iota(4, 14).enumerate.maxElement!"a.value" == tuple(9, 13)); + + // should work with const + const(int)[] immArr = [2, 3, 1]; + assert(immArr.maxElement == 3); + + // should work with immutable + immutable(int)[] immArr2 = [2, 3, 1]; + assert(immArr2.maxElement == 3); + + // with strings + assert(["a", "c", "b"].maxElement == "c"); + + // with all dummy ranges + import std.internal.test.dummyrange; + foreach (DummyType; AllDummyRanges) + { + DummyType d; + assert(d.maxElement == 10); + assert(d.maxElement!(a => a) == 10); + } + + // with empty, but seeded ranges + int[] arr; + assert(arr.maxElement(42) == 42); + assert(arr.maxElement!(a => a)(42) == 42); + +} + +@nogc @safe nothrow pure unittest +{ + static immutable arr = [7, 3, 8, 2, 1, 4]; + assert(arr.maxElement == 8); + + static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; + assert(arr2d.maxElement!"a[1]" == arr2d[1]); +} + // minPos /** -Returns the position of the minimum element of forward range $(D -range), i.e. a subrange of $(D range) starting at the position of its -smallest element and with the same ending as $(D range). The function -can actually be used for finding the maximum or any other ordering -predicate (that's why $(D maxPos) is not provided). - */ +Computes a subrange of `range` starting at the first occurrence of `range`'s +minimum (respectively maximum) and with the same ending as `range`, or the +empty range if `range` itself is empty. + +Formally, the minimum is a value `x` in `range` such that $(D pred(a, x)) is +`false` for all values `a` in `range`. Conversely, the maximum is a value `x` in +`range` such that $(D pred(x, a)) is `false` for all values `a` in `range` (note +the swapped arguments to `pred`). + +These functions may be used for computing arbitrary extrema by choosing `pred` +appropriately. For corrrect functioning, `pred` must be a strict partial order, +i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and +irreflexive ($(D pred(a, a)) is `false`). + +Params: + pred = The ordering predicate to use to determine the extremum (minimum or + maximum) element. + range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search. + +Returns: The position of the minimum (respectively maximum) element of forward +range `range`, i.e. a subrange of `range` starting at the position of its +smallest (respectively largest) element and with the same ending as `range`. + +*/ Range minPos(alias pred = "a < b", Range)(Range range) - if (isForwardRange!Range && !isInfinite!Range && - is(typeof(binaryFun!pred(range.front, range.front)))) +if (isForwardRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(range.front, range.front)))) { - if (range.empty) return range; - auto result = range.save; - - for (range.popFront(); !range.empty; range.popFront()) + static if (hasSlicing!Range && isRandomAccessRange!Range && hasLength!Range) { - //Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough - if (binaryFun!pred(range.front, result.front)) + // Prefer index-based access + size_t pos = 0; + foreach (i; 1 .. range.length) { - // change the min - result = range.save; + if (binaryFun!pred(range[i], range[pos])) + { + pos = i; + } } + return range[pos .. range.length]; } - return result; + else + { + auto result = range.save; + if (range.empty) return result; + for (range.popFront(); !range.empty; range.popFront()) + { + // Note: Unlike minCount, we do not care to find equivalence, so a + // single pred call is enough. + if (binaryFun!pred(range.front, result.front)) + { + // change the min + result = range.save; + } + } + return result; + } +} + +/// Ditto +Range maxPos(alias pred = "a < b", Range)(Range range) +if (isForwardRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(range.front, range.front)))) +{ + return range.minPos!((a, b) => binaryFun!pred(b, a)); } /// @@ -2783,9 +3625,9 @@ Range minPos(alias pred = "a < b", Range)(Range range) { int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; // Minimum is 1 and first occurs in position 3 - assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]); + assert(a.minPos == [ 1, 2, 4, 1, 1, 2 ]); // Maximum is 4 and first occurs in position 2 - assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]); + assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]); } @safe unittest @@ -2793,8 +3635,6 @@ Range minPos(alias pred = "a < b", Range)(Range range) import std.algorithm.comparison : equal; import std.internal.test.dummyrange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; //Test that an empty range works int[] b = a[$..$]; @@ -2804,15 +3644,12 @@ Range minPos(alias pred = "a < b", Range)(Range range) assert( equal( minPos(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])), [0, 2, 0] ) ); } -unittest +@system unittest { //Rvalue range import std.algorithm.comparison : equal; import std.container : Array; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - assert(Array!int(2, 3, 4, 1, 2, 4, 1, 1, 2) [] .minPos() @@ -2822,8 +3659,6 @@ unittest @safe unittest { //BUG 9299 - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; // Minimum is 1 and first occurs in position 3 assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]); @@ -2834,6 +3669,214 @@ unittest assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]); } +/** +Computes the index of the first occurrence of `range`'s minimum element. + +Params: + pred = The ordering predicate to use to determine the minimum element. + range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + to search. + +Complexity: O(n) + Exactly `n - 1` comparisons are needed. + +Returns: + The index of the first encounter of the minimum element in `range`. If the + `range` is empty, -1 is returned. + +See_Also: + $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos) + */ +sizediff_t minIndex(alias pred = "a < b", Range)(Range range) +if (isForwardRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(range.front, range.front)))) +{ + if (range.empty) return -1; + + sizediff_t minPos = 0; + + static if (isRandomAccessRange!Range && hasLength!Range) + { + foreach (i; 1 .. range.length) + { + if (binaryFun!pred(range[i], range[minPos])) + { + minPos = i; + } + } + } + else + { + sizediff_t curPos = 0; + Unqual!(typeof(range.front)) min = range.front; + for (range.popFront(); !range.empty; range.popFront()) + { + ++curPos; + if (binaryFun!pred(range.front, min)) + { + min = range.front; + minPos = curPos; + } + } + } + return minPos; +} + +/// +@safe pure nothrow unittest +{ + int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; + + // Minimum is 1 and first occurs in position 3 + assert(a.minIndex == 3); + // Get maximum index with minIndex + assert(a.minIndex!"a > b" == 2); + + // Range is empty, so return value is -1 + int[] b; + assert(b.minIndex == -1); + + // Works with more custom types + struct Dog { int age; } + Dog[] dogs = [Dog(10), Dog(5), Dog(15)]; + assert(dogs.minIndex!"a.age < b.age" == 1); +} + +@safe pure unittest +{ + // should work with const + const(int)[] immArr = [2, 1, 3]; + assert(immArr.minIndex == 1); + + // Works for const ranges too + const int[] c = [2, 5, 4, 1, 2, 3]; + assert(c.minIndex == 3); + + // should work with immutable + immutable(int)[] immArr2 = [2, 1, 3]; + assert(immArr2.minIndex == 1); + + // with strings + assert(["b", "a", "c"].minIndex == 1); + + // infinite range + import std.range : cycle; + static assert(!__traits(compiles, cycle([1]).minIndex)); + + // with all dummy ranges + import std.internal.test.dummyrange : AllDummyRanges; + foreach (DummyType; AllDummyRanges) + { + static if (isForwardRange!DummyType && !isInfinite!DummyType) + { + DummyType d; + d.arr = [5, 3, 7, 2, 1, 4]; + assert(d.minIndex == 4); + + d.arr = []; + assert(d.minIndex == -1); + } + } +} + +@nogc @safe nothrow pure unittest +{ + static immutable arr = [7, 3, 8, 2, 1, 4]; + assert(arr.minIndex == 4); + + static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; + assert(arr2d.minIndex!"a[1] < b[1]" == 2); +} + +/** +Computes the index of the first occurrence of `range`'s maximum element. + +Complexity: O(n) + Exactly `n - 1` comparisons are needed. + +Params: + pred = The ordering predicate to use to determine the maximum element. + range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search. + +Returns: + The index of the first encounter of the maximum in `range`. If the + `range` is empty, -1 is returned. + +See_Also: + $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos) + */ +sizediff_t maxIndex(alias pred = "a < b", Range)(Range range) +if (isInputRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(range.front, range.front)))) +{ + return range.minIndex!((a, b) => binaryFun!pred(b, a)); +} + +/// +@safe pure nothrow unittest +{ + // Maximum is 4 and first occurs in position 2 + int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; + assert(a.maxIndex == 2); + + // Empty range + int[] b; + assert(b.maxIndex == -1); + + // Works with more custom types + struct Dog { int age; } + Dog[] dogs = [Dog(10), Dog(15), Dog(5)]; + assert(dogs.maxIndex!"a.age < b.age" == 1); +} + +@safe pure unittest +{ + // should work with const + const(int)[] immArr = [5, 1, 3]; + assert(immArr.maxIndex == 0); + + // Works for const ranges too + const int[] c = [2, 5, 4, 1, 2, 3]; + assert(c.maxIndex == 1); + + + // should work with immutable + immutable(int)[] immArr2 = [2, 1, 3]; + assert(immArr2.maxIndex == 2); + + // with strings + assert(["b", "a", "c"].maxIndex == 2); + + // infinite range + import std.range : cycle; + static assert(!__traits(compiles, cycle([1]).maxIndex)); + + // with all dummy ranges + import std.internal.test.dummyrange : AllDummyRanges; + foreach (DummyType; AllDummyRanges) + { + static if (isForwardRange!DummyType && !isInfinite!DummyType) + { + DummyType d; + + d.arr = [5, 3, 7, 2, 1, 4]; + assert(d.maxIndex == 2); + + d.arr = []; + assert(d.maxIndex == -1); + } + } +} + +@nogc @safe nothrow pure unittest +{ + static immutable arr = [7, 3, 8, 2, 1, 4]; + assert(arr.maxIndex == 2); + + static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; + assert(arr2d.maxIndex!"a[1] < b[1]" == 1); +} + /** Skip over the initial portion of the first given range that matches the second range, or do nothing if there is no match. @@ -2841,9 +3884,10 @@ range, or do nothing if there is no match. Params: pred = The predicate that determines whether elements from each respective range match. Defaults to equality $(D "a == b"). - r1 = The $(XREF2 range, isForwardRange, forward range) to move forward. - r2 = The $(XREF2 range, isInputRange, input range) representing the initial - segment of $(D r1) to skip over. + r1 = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to + move forward. + r2 = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + representing the initial segment of $(D r1) to skip over. Returns: true if the initial segment of $(D r1) matches $(D r2), and $(D r1) has been @@ -2851,8 +3895,8 @@ advanced to the point past this segment; otherwise false, and $(D r1) is left in its original position. */ bool skipOver(R1, R2)(ref R1 r1, R2 r2) - if (isForwardRange!R1 && isInputRange!R2 - && is(typeof(r1.front == r2.front))) +if (isForwardRange!R1 && isInputRange!R2 + && is(typeof(r1.front == r2.front))) { static if (is(typeof(r1[0 .. $] == r2) : bool) && is(typeof(r2.length > r1.length) : bool) @@ -2873,9 +3917,9 @@ bool skipOver(R1, R2)(ref R1 r1, R2 r2) /// Ditto bool skipOver(alias pred, R1, R2)(ref R1 r1, R2 r2) - if (is(typeof(binaryFun!pred(r1.front, r2.front))) && - isForwardRange!R1 && - isInputRange!R2) +if (is(typeof(binaryFun!pred(r1.front, r2.front))) && + isForwardRange!R1 && + isInputRange!R2) { static if (hasLength!R1 && hasLength!R2) { @@ -2920,7 +3964,8 @@ Params: pred = The predicate that determines whether an element from the range matches the given element. - r = The $(XREF range, isInputRange, input range) to skip over. + r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to skip + over. e = The element to match. @@ -2930,14 +3975,14 @@ predicate, and the range has been advanced by one element; otherwise false, and the range is left untouched. */ bool skipOver(R, E)(ref R r, E e) - if (isInputRange!R && is(typeof(r.front == e) : bool)) +if (isInputRange!R && is(typeof(r.front == e) : bool)) { return skipOver!((a, b) => a == b)(r, e); } /// Ditto bool skipOver(alias pred, R, E)(ref R r, E e) - if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R) +if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R) { if (r.empty || !binaryFun!pred(r.front, e)) return false; @@ -2967,13 +4012,15 @@ bool skipOver(alias pred, R, E)(ref R r, E e) } /** -Checks whether the given $(XREF2 range, isInputRange, input range) starts with -(one of) the given needle(s). +Checks whether the given +$(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one +of) the given needle(s) or, if no needles are given, +if its front element fulfils predicate $(D pred). Params: pred = Predicate to use in comparing the elements of the haystack and the - needle(s). + needle(s). Mandatory if no needles are given. doesThisStart = The input range to check. @@ -2995,6 +4042,9 @@ elements in $(D withOneOfThese), then the shortest one matches (if there are two which match which are of the same length (e.g. $(D "a") and $(D 'a')), then the left-most of them in the argument list matches). + +In the case when no needle parameters are given, return $(D true) iff front of +$(D doesThisStart) fulfils predicate $(D pred). */ uint startsWith(alias pred = "a == b", Range, Needles...)(Range doesThisStart, Needles withOneOfThese) if (isInputRange!Range && Needles.length > 1 && @@ -3147,9 +4197,28 @@ if (isInputRange!R && : binaryFun!pred(doesThisStart.front, withThis); } +/// Ditto +bool startsWith(alias pred, R)(R doesThisStart) +if (isInputRange!R && + ifTestable!(typeof(doesThisStart.front), unaryFun!pred)) +{ + return !doesThisStart.empty && unaryFun!pred(doesThisStart.front); +} + /// @safe unittest { + import std.ascii : isAlpha; + + assert("abc".startsWith!(a => a.isAlpha)); + assert("abc".startsWith!isAlpha); + assert(!"1ab".startsWith!(a => a.isAlpha)); + assert(!"".startsWith!(a => a.isAlpha)); + + import std.algorithm.comparison : among; + assert("abc".startsWith!(a => a.among('a', 'b') != 0)); + assert(!"abc".startsWith!(a => a.among('b', 'c') != 0)); + assert(startsWith("abc", "")); assert(startsWith("abc", "a")); assert(!startsWith("abc", "b")); @@ -3161,6 +4230,8 @@ if (isInputRange!R && assert(startsWith("abc", "x", "aa", "ab") == 3); assert(startsWith("abc", "x", "aaa", "sab") == 0); assert(startsWith("abc", "x", "aaa", "a", "sab") == 3); + + import std.typecons : Tuple; alias C = Tuple!(int, "x", int, "y"); assert(startsWith!"a.x == b"([ C(1,1), C(1,2), C(2,2) ], [1, 1])); assert(startsWith!"a.x == b"([ C(1,1), C(2,1), C(2,2) ], [1, 1], [1, 2], [1, 3]) == 2); @@ -3170,13 +4241,10 @@ if (isInputRange!R && { import std.algorithm.iteration : filter; import std.conv : to; + import std.meta : AliasSeq; import std.range; - import std.typetuple : TypeTuple; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { assert(!startsWith(to!S("abc"), 'c')); assert(startsWith(to!S("abc"), 'a', 'c') == 1); @@ -3184,7 +4252,7 @@ if (isInputRange!R && assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3); assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2); - foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 //Lots of strings assert(startsWith(to!S("abc"), to!T(""))); @@ -3226,12 +4294,12 @@ if (isInputRange!R && assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(3))); assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(1))); - foreach (T; TypeTuple!(int, short)) + foreach (T; AliasSeq!(int, short)) { immutable arr = cast(T[])[0, 1, 2, 3, 4, 5]; //RA range - assert(startsWith(arr, cast(int[])null)); + assert(startsWith(arr, cast(int[]) null)); assert(!startsWith(arr, 5)); assert(!startsWith(arr, 1)); assert(startsWith(arr, 0)); @@ -3265,7 +4333,7 @@ if (isInputRange!R && Consume all elements from $(D r) that are equal to one of the elements $(D es). */ -void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es) +private void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es) //if (is(typeof(binaryFun!pred(r1.front, es[0])))) { loop: @@ -3284,38 +4352,74 @@ void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es) @safe unittest { - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto s1 = "Hello world"; skipAll(s1, 'H', 'e'); assert(s1 == "llo world"); } /** -Interval option specifier for $(D until) (below) and others. +Interval option specifier for `until` (below) and others. + +If set to $(D OpenRight.yes), then the interval is open to the right +(last element is not included). + +Otherwise if set to $(D OpenRight.no), then the interval is closed to the right +(last element included). + */ +alias OpenRight = Flag!"openRight"; + +/** +Lazily iterates $(D range) _until the element $(D e) for which +$(D pred(e, sentinel)) is true. + +This is similar to `takeWhile` in other languages. + +Params: + pred = Predicate to determine when to stop. + range = The $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) + to iterate over. + sentinel = The element to stop at. + openRight = Determines whether the element for which the given predicate is + true should be included in the resulting range ($(D No.openRight)), or + not ($(D Yes.openRight)). + +Returns: + An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) that + iterates over the original range's elements, but ends when the specified + predicate becomes true. If the original range is a + $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives) or + higher, this range will be a forward range. */ -enum OpenRight +Until!(pred, Range, Sentinel) +until(alias pred = "a == b", Range, Sentinel) +(Range range, Sentinel sentinel, OpenRight openRight = Yes.openRight) +if (!is(Sentinel == OpenRight)) +{ + return typeof(return)(range, sentinel, openRight); +} + +/// Ditto +Until!(pred, Range, void) +until(alias pred, Range) +(Range range, OpenRight openRight = Yes.openRight) { - no, /// Interval is closed to the right (last element included) - yes /// Interval is open to the right (last element is not included) + return typeof(return)(range, openRight); } -struct Until(alias pred, Range, Sentinel) if (isInputRange!Range) +/// ditto +struct Until(alias pred, Range, Sentinel) +if (isInputRange!Range) { private Range _input; static if (!is(Sentinel == void)) private Sentinel _sentinel; - // mixin(bitfields!( - // OpenRight, "_openRight", 1, - // bool, "_done", 1, - // uint, "", 6)); - // OpenRight, "_openRight", 1, - // bool, "_done", 1, - OpenRight _openRight; - bool _done; + private OpenRight _openRight; + private bool _done; static if (!is(Sentinel == void)) + /// this(Range input, Sentinel sentinel, - OpenRight openRight = OpenRight.yes) + OpenRight openRight = Yes.openRight) { _input = input; _sentinel = sentinel; @@ -3323,18 +4427,21 @@ struct Until(alias pred, Range, Sentinel) if (isInputRange!Range) _done = _input.empty || openRight && predSatisfied(); } else - this(Range input, OpenRight openRight = OpenRight.yes) + /// + this(Range input, OpenRight openRight = Yes.openRight) { _input = input; _openRight = openRight; _done = _input.empty || openRight && predSatisfied(); } + /// @property bool empty() { return _done; } + /// @property auto ref front() { assert(!empty); @@ -3349,6 +4456,7 @@ struct Until(alias pred, Range, Sentinel) if (isInputRange!Range) return cast(bool) startsWith!pred(_input, _sentinel); } + /// void popFront() { assert(!empty); @@ -3368,6 +4476,7 @@ struct Until(alias pred, Range, Sentinel) if (isInputRange!Range) static if (isForwardRange!Range) { static if (!is(Sentinel == void)) + /// @property Until save() { Until result = this; @@ -3378,6 +4487,7 @@ struct Until(alias pred, Range, Sentinel) if (isInputRange!Range) return result; } else + /// @property Until save() { Until result = this; @@ -3389,70 +4499,36 @@ struct Until(alias pred, Range, Sentinel) if (isInputRange!Range) } } -/** -Lazily iterates $(D range) _until the element $(D e) for which -$(D pred(e, sentinel)) is true. - -Params: - pred = Predicate to determine when to stop. - range = The $(XREF2 range, isInputRange, input range) to iterate over. - sentinel = The element to stop at. - openRight = Determines whether the element for which the given predicate is - true should be included in the resulting range ($(D OpenRight.no)), or - not ($(D OpenRight.yes)). - -Returns: - An $(XREF2 range, isInputRange, input range) that iterates over the - original range's elements, but ends when the specified predicate becomes - true. If the original range is a $(XREF2 range, isForwardRange, forward - range) or higher, this range will be a forward range. - */ -Until!(pred, Range, Sentinel) -until(alias pred = "a == b", Range, Sentinel) -(Range range, Sentinel sentinel, OpenRight openRight = OpenRight.yes) -if (!is(Sentinel == OpenRight)) -{ - return typeof(return)(range, sentinel, openRight); -} - -/// Ditto -Until!(pred, Range, void) -until(alias pred, Range) -(Range range, OpenRight openRight = OpenRight.yes) -{ - return typeof(return)(range, openRight); -} - /// @safe unittest { import std.algorithm.comparison : equal; + import std.typecons : No; int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; - assert(equal(a.until(7), [1, 2, 4][])); - assert(equal(a.until(7, OpenRight.no), [1, 2, 4, 7][])); + assert(equal(a.until(7), [1, 2, 4])); + assert(equal(a.until(7, No.openRight), [1, 2, 4, 7])); } @safe unittest { import std.algorithm.comparison : equal; - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; static assert(isForwardRange!(typeof(a.until(7)))); - static assert(isForwardRange!(typeof(until!"a == 2"(a, OpenRight.no)))); + static assert(isForwardRange!(typeof(until!"a == 2"(a, No.openRight)))); - assert(equal(a.until(7), [1, 2, 4][])); - assert(equal(a.until([7, 2]), [1, 2, 4, 7][])); - assert(equal(a.until(7, OpenRight.no), [1, 2, 4, 7][])); - assert(equal(until!"a == 2"(a, OpenRight.no), [1, 2][])); + assert(equal(a.until(7), [1, 2, 4])); + assert(equal(a.until([7, 2]), [1, 2, 4, 7])); + assert(equal(a.until(7, No.openRight), [1, 2, 4, 7])); + assert(equal(until!"a == 2"(a, No.openRight), [1, 2])); } -unittest // bugzilla 13171 +@system unittest // bugzilla 13171 { import std.algorithm.comparison : equal; import std.range; auto a = [1, 2, 3, 4]; - assert(equal(refRange(&a).until(3, OpenRight.no), [1, 2, 3])); + assert(equal(refRange(&a).until(3, No.openRight), [1, 2, 3])); assert(a == [4]); } @@ -3465,10 +4541,9 @@ unittest // bugzilla 13171 assert(equal(a, [0, 0, 3, 4])); } -unittest // Issue 13124 +@safe unittest // Issue 13124 { import std.algorithm.comparison : among; auto s = "hello how\nare you"; s.until!(c => c.among!('\n', '\r')); } - diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index b82945179d5..4b23446d4e3 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -1,12 +1,11 @@ // Written in the D programming language. /** -This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). +This is a submodule of $(MREF std, algorithm). It contains generic algorithms that implement set operations. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE Cheat Sheet, - $(TR $(TH Function Name) $(TH Description)) - $(T2 cartesianProduct, Computes Cartesian product of two ranges.) $(T2 largestPartialIntersection, @@ -24,15 +23,13 @@ $(T2 setIntersection, $(T2 setSymmetricDifference, Lazily computes the symmetric set difference of two or more sorted ranges.) -$(T2 setUnion, - Lazily computes the set union of two or more sorted ranges.) ) Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_setops.d) @@ -47,7 +44,10 @@ import std.range.primitives; import std.functional; // : unaryFun, binaryFun; import std.traits; // FIXME -import std.typetuple; // : TypeTuple, staticMap, allSatisfy, anySatisfy; +import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy; + +import std.algorithm.sorting; // : Merge; +import std.typecons : No; // cartesianProduct /** @@ -56,8 +56,9 @@ _range of tuples of elements from each respective range. The conditions for the two-range case are as follows: -If both ranges are finite, then one must be (at least) a forward range and the -other an input range. +If both ranges are finite, then one must be (at least) a +$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the +other an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). If one _range is infinite and the other finite, then the finite _range must be a forward _range, and the infinite range can be an input _range. @@ -66,10 +67,20 @@ If both ranges are infinite, then both must be forward ranges. When there are more than two ranges, the above conditions apply to each adjacent pair of ranges. + +Params: + range1 = The first range + range2 = The second range + ranges = Two or more non-infinite forward ranges + otherRanges = Zero or more non-infinite forward ranges + +Returns: + A forward range of $(REF Tuple, std,typecons) representing elements of the + cartesian product of the given ranges. */ auto cartesianProduct(R1, R2)(R1 range1, R2 range2) - if (!allSatisfy!(isForwardRange, R1, R2) || - anySatisfy!(isInfinite, R1, R2)) +if (!allSatisfy!(isForwardRange, R1, R2) || + anySatisfy!(isInfinite, R1, R2)) { import std.algorithm.iteration : map, joiner; @@ -83,7 +94,7 @@ auto cartesianProduct(R1, R2)(R1 range1, R2 range2) // covering the right and bottom edges of an increasing square area // over the infinite table of combinations. This schedule allows us // to require only forward ranges. - return zip(sequence!"n"(cast(size_t)0), range1.save, range2.save, + return zip(sequence!"n"(cast(size_t) 0), range1.save, range2.save, repeat(range1), repeat(range2)) .map!(function(a) => chain( zip(repeat(a[1]), take(a[4].save, a[0])), @@ -331,16 +342,15 @@ auto cartesianProduct(R1, R2)(R1 range1, R2 range2) // Issue 13091 pure nothrow @safe @nogc unittest { - import std.algorithm: cartesianProduct; int[1] a = [1]; foreach (t; cartesianProduct(a[], a[])) {} } /// ditto auto cartesianProduct(RR...)(RR ranges) - if (ranges.length >= 2 && - allSatisfy!(isForwardRange, RR) && - !anySatisfy!(isInfinite, RR)) +if (ranges.length >= 2 && + allSatisfy!(isForwardRange, RR) && + !anySatisfy!(isInfinite, RR)) { // This overload uses a much less template-heavy implementation when // all ranges are finite forward ranges, which is the most common use @@ -381,7 +391,7 @@ auto cartesianProduct(RR...)(RR ranges) r.popFront(); if (!r.empty) break; - static if (i==0) + static if (i == 0) empty = true; else r = ranges[i].save; // rollover @@ -435,8 +445,8 @@ auto cartesianProduct(RR...)(RR ranges) /// ditto auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) - if (!allSatisfy!(isForwardRange, R1, R2, RR) || - anySatisfy!(isInfinite, R1, R2, RR)) +if (!allSatisfy!(isForwardRange, R1, R2, RR) || + anySatisfy!(isInfinite, R1, R2, RR)) { /* We implement the n-ary cartesian product by recursively invoking the * binary cartesian product. To make the resulting range nicer, we denest @@ -515,6 +525,7 @@ auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) pure @safe nothrow @nogc unittest { + import std.range.primitives : isForwardRange; int[2] A = [1,2]; auto C = cartesianProduct(A[], A[], A[]); assert(isForwardRange!(typeof(C))); @@ -527,7 +538,7 @@ pure @safe nothrow @nogc unittest } // Issue 13935 -unittest +@safe unittest { import std.algorithm.iteration : map; auto seq = [1, 2].map!(x => x); @@ -536,38 +547,20 @@ unittest // largestPartialIntersection /** -Given a range of sorted forward ranges $(D ror), copies to $(D tgt) -the elements that are common to most ranges, along with their number +Given a range of sorted $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) +$(D ror), copies to $(D tgt) the elements that are common to most ranges, along with their number of occurrences. All ranges in $(D ror) are assumed to be sorted by $(D less). Only the most frequent $(D tgt.length) elements are returned. -Example: ----- -// Figure which number can be found in most arrays of the set of -// arrays below. -double[][] a = -[ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], -]; -auto b = new Tuple!(double, uint)[1]; -largestPartialIntersection(a, b); -// First member is the item, second is the occurrence count -assert(b[0] == tuple(7.0, 4u)); ----- - -$(D 7.0) is the correct answer because it occurs in $(D 4) out of the -$(D 5) inputs, more than any other number. The second member of the -resulting tuple is indeed $(D 4) (recording the number of occurrences -of $(D 7.0)). If more of the top-frequent numbers are needed, just -create a larger $(D tgt) range. In the example above, creating $(D b) -with length $(D 2) yields $(D tuple(1.0, 3u)) in the second position. +Params: + less = The predicate the ranges are sorted by. + ror = A range of forward ranges sorted by `less`. + tgt = The target range to copy common elements to. + sorted = Whether the elements copied should be in sorted order. The function $(D largestPartialIntersection) is useful for -e.g. searching an $(LUCKY inverted index) for the documents most +e.g. searching an $(LINK2 https://en.wikipedia.org/wiki/Inverted_index, +inverted index) for the documents most likely to contain some terms of interest. The complexity of the search is $(BIGOH n * log(tgt.length)), where $(D n) is the sum of lengths of all input ranges. This approach is faster than keeping an associative @@ -585,7 +578,7 @@ duplicate in between calls). */ void largestPartialIntersection (alias less = "a < b", RangeOfRanges, Range) -(RangeOfRanges ror, Range tgt, SortOutput sorted = SortOutput.no) +(RangeOfRanges ror, Range tgt, SortOutput sorted = No.sortOutput) { struct UnitWeights { @@ -595,6 +588,36 @@ void largestPartialIntersection sorted); } +/// +@system unittest +{ + import std.typecons : tuple, Tuple; + + // Figure which number can be found in most arrays of the set of + // arrays below. + double[][] a = + [ + [ 1, 4, 7, 8 ], + [ 1, 7 ], + [ 1, 7, 8], + [ 4 ], + [ 7 ], + ]; + auto b = new Tuple!(double, uint)[1]; + // it will modify the input range, hence we need to create a duplicate + largestPartialIntersection(a.dup, b); + // First member is the item, second is the occurrence count + assert(b[0] == tuple(7.0, 4u)); + // 7.0 occurs in 4 out of 5 inputs, more than any other number + + // If more of the top-frequent numbers are needed, just create a larger + // tgt range + auto c = new Tuple!(double, uint)[2]; + largestPartialIntersection(a, c); + assert(c[0] == tuple(1.0, 3u)); + // 1.0 occurs in 3 inputs +} + import std.algorithm.sorting : SortOutput; // FIXME // largestPartialIntersectionWeighted @@ -602,33 +625,18 @@ import std.algorithm.sorting : SortOutput; // FIXME Similar to $(D largestPartialIntersection), but associates a weight with each distinct element in the intersection. -Example: ----- -// Figure which number can be found in most arrays of the set of -// arrays below, with specific per-element weights -double[][] a = -[ - [ 1, 4, 7, 8 ], - [ 1, 7 ], - [ 1, 7, 8], - [ 4 ], - [ 7 ], -]; -auto b = new Tuple!(double, uint)[1]; -double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; -largestPartialIntersectionWeighted(a, b, weights); -// First member is the item, second is the occurrence count -assert(b[0] == tuple(4.0, 2u)); ----- - -The correct answer in this case is $(D 4.0), which, although only -appears two times, has a total weight $(D 4.6) (three times its weight -$(D 2.3)). The value $(D 7) is weighted with $(D 1.1) and occurs four -times for a total weight $(D 4.4). - */ +Params: + less = The predicate the ranges are sorted by. + ror = A range of $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) + sorted by `less`. + tgt = The target range to copy common elements to. + weights = An associative array mapping elements to weights. + sorted = Whether the elements copied should be in sorted order. + +*/ void largestPartialIntersectionWeighted (alias less = "a < b", RangeOfRanges, Range, WeightsAA) -(RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = SortOutput.no) +(RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = No.sortOutput) { import std.algorithm.iteration : group; import std.algorithm.sorting : topNCopy; @@ -642,13 +650,34 @@ void largestPartialIntersectionWeighted topNCopy!heapComp(group(nWayUnion!less(ror)), tgt, sorted); } -unittest +/// +@system unittest { - import std.conv : text; import std.typecons : tuple, Tuple; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + // Figure which number can be found in most arrays of the set of + // arrays below, with specific per-element weights + double[][] a = + [ + [ 1, 4, 7, 8 ], + [ 1, 7 ], + [ 1, 7, 8], + [ 4 ], + [ 7 ], + ]; + auto b = new Tuple!(double, uint)[1]; + double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; + largestPartialIntersectionWeighted(a, b, weights); + // First member is the item, second is the occurrence count + assert(b[0] == tuple(4.0, 2u)); + // 4.0 occurs 2 times -> 4.6 (2 * 2.3) + // 7.0 occurs 3 times -> 4.4 (3 * 1.1) +} + +@system unittest +{ + import std.conv : text; + import std.typecons : tuple, Tuple, Yes; double[][] a = [ @@ -659,20 +688,15 @@ unittest [ 7 ], ]; auto b = new Tuple!(double, uint)[2]; - largestPartialIntersection(a, b, SortOutput.yes); - //sort(b); - //writeln(b); + largestPartialIntersection(a, b, Yes.sortOutput); assert(b == [ tuple(7.0, 4u), tuple(1.0, 3u) ][], text(b)); assert(a[0].empty); } -unittest +@system unittest { import std.conv : text; - import std.typecons : tuple, Tuple; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + import std.typecons : tuple, Tuple, Yes; string[][] a = [ @@ -683,18 +707,16 @@ unittest [ "7" ], ]; auto b = new Tuple!(string, uint)[2]; - largestPartialIntersection(a, b, SortOutput.yes); - //writeln(b); + largestPartialIntersection(a, b, Yes.sortOutput); assert(b == [ tuple("7", 4u), tuple("1", 3u) ][], text(b)); } -unittest +@system unittest { import std.typecons : tuple, Tuple; - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); -// Figure which number can be found in most arrays of the set of -// arrays below, with specific per-element weights + // Figure which number can be found in most arrays of the set of + // arrays below, with specific per-element weights double[][] a = [ [ 1, 4, 7, 8 ], @@ -706,12 +728,11 @@ unittest auto b = new Tuple!(double, uint)[1]; double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; largestPartialIntersectionWeighted(a, b, weights); -// First member is the item, second is the occurrence count - //writeln(b[0]); + // First member is the item, second is the occurrence count assert(b[0] == tuple(4.0, 2u)); } -unittest +@system unittest { import std.container : Array; import std.typecons : Tuple; @@ -738,6 +759,13 @@ NWayUnion) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D log(ror.length)) times worse than just spanning all ranges in turn. The output comes sorted (unstably) by $(D less). +Params: + less = Predicate the given ranges are sorted by. + ror = A range of ranges sorted by `less` to compute the union for. + +Returns: + A range of the union of the ranges in `ror`. + Warning: Because $(D NWayUnion) does not allocate extra memory, it will leave $(D ror) modified. Namely, $(D NWayUnion) assumes ownership of $(D ror) and discretionarily swaps and advances elements of it. If @@ -752,14 +780,17 @@ struct NWayUnion(alias less, RangeOfRanges) private alias ElementType = .ElementType!(.ElementType!RangeOfRanges); private alias comp = binaryFun!less; private RangeOfRanges _ror; + + /// static bool compFront(.ElementType!RangeOfRanges a, .ElementType!RangeOfRanges b) { // revert comparison order so we get the smallest elements first return comp(b.front, a.front); } - BinaryHeap!(RangeOfRanges, compFront) _heap; + private BinaryHeap!(RangeOfRanges, compFront) _heap; + /// this(RangeOfRanges ror) { import std.algorithm.mutation : remove, SwapStrategy; @@ -771,13 +802,16 @@ struct NWayUnion(alias less, RangeOfRanges) _heap.acquire(_ror); } + /// @property bool empty() { return _ror.empty; } + /// @property auto ref front() { return _heap.front.front; } + /// void popFront() { _heap.removeFront(); @@ -804,7 +838,7 @@ NWayUnion!(less, RangeOfRanges) nWayUnion } /// -unittest +@system unittest { import std.algorithm.comparison : equal; @@ -826,9 +860,19 @@ unittest Lazily computes the difference of $(D r1) and $(D r2). The two ranges are assumed to be sorted by $(D less). The element types of the two ranges must have a common type. + +Params: + less = Predicate the given ranges are sorted by. + r1 = The first range. + r2 = The range to subtract from `r1`. + +Returns: + A range of the difference of `r1` and `r2`. + +See_also: $(LREF setSymmetricDifference) */ struct SetDifference(alias less = "a < b", R1, R2) - if (isInputRange!(R1) && isInputRange!(R2)) +if (isInputRange!(R1) && isInputRange!(R2)) { private: R1 r1; @@ -854,6 +898,7 @@ private: } public: + /// this(R1 r1, R2 r2) { this.r1 = r1; @@ -862,12 +907,14 @@ public: adjustPosition(); } + /// void popFront() { r1.popFront(); adjustPosition(); } + /// @property auto ref front() { assert(!empty); @@ -876,6 +923,7 @@ public: static if (isForwardRange!R1 && isForwardRange!R2) { + /// @property typeof(this) save() { auto ret = this; @@ -885,6 +933,7 @@ public: } } + /// @property bool empty() { return r1.empty; } } @@ -899,6 +948,7 @@ SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2) @safe unittest { import std.algorithm.comparison : equal; + import std.range.primitives : isForwardRange; int[] a = [ 1, 2, 4, 5, 7, 9 ]; int[] b = [ 0, 1, 2, 4, 7, 8 ]; @@ -921,10 +971,17 @@ SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2) Lazily computes the intersection of two or more input ranges $(D ranges). The ranges are assumed to be sorted by $(D less). The element types of the ranges must have a common type. + +Params: + less = Predicate the given ranges are sorted by. + ranges = The ranges to compute the intersection for. + +Returns: + A range containing the intersection of the given ranges. */ struct SetIntersection(alias less = "a < b", Rs...) - if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && - !is(CommonType!(staticMap!(ElementType, Rs)) == void)) +if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && + !is(CommonType!(staticMap!(ElementType, Rs)) == void)) { private: Rs _input; @@ -945,10 +1002,11 @@ private: if (comp(next.front, r.front)) { - do { + do + { next.popFront(); if (next.empty) return; - } while(comp(next.front, r.front)); + } while (comp(next.front, r.front)); done = Rs.length; } if (--done == 0) return; @@ -957,6 +1015,7 @@ private: } public: + /// this(Rs input) { this._input = input; @@ -964,6 +1023,7 @@ public: adjustPosition(); } + /// @property bool empty() { foreach (ref r; _input) @@ -973,6 +1033,7 @@ public: return false; } + /// void popFront() { assert(!empty); @@ -989,6 +1050,7 @@ public: adjustPosition(); } + /// @property ElementType front() { assert(!empty); @@ -997,6 +1059,7 @@ public: static if (allSatisfy!(isForwardRange, Rs)) { + /// @property SetIntersection save() { auto ret = this; @@ -1011,8 +1074,8 @@ public: /// Ditto SetIntersection!(less, Rs) setIntersection(alias less = "a < b", Rs...)(Rs ranges) - if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && - !is(CommonType!(staticMap!(ElementType, Rs)) == void)) +if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && + !is(CommonType!(staticMap!(ElementType, Rs)) == void)) { return typeof(return)(ranges); } @@ -1070,9 +1133,19 @@ ranges must have a common type. If both arguments are ranges of L-values of the same type then $(D SetSymmetricDifference) will also be a range of L-values of that type. + +Params: + less = Predicate the given ranges are sorted by. + r1 = The first range. + r2 = The second range. + +Returns: + A range of the symmetric difference between `r1` and `r2`. + +See_also: $(LREF setDifference) */ struct SetSymmetricDifference(alias less = "a < b", R1, R2) - if (isInputRange!(R1) && isInputRange!(R2)) +if (isInputRange!(R1) && isInputRange!(R2)) { private: R1 r1; @@ -1095,6 +1168,7 @@ private: } public: + /// this(R1 r1, R2 r2) { this.r1 = r1; @@ -1103,6 +1177,7 @@ public: adjustPosition(); } + /// void popFront() { assert(!empty); @@ -1124,16 +1199,18 @@ public: adjustPosition(); } + /// @property auto ref front() { assert(!empty); - bool chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front); + immutable chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front); assert(chooseR1 || r1.empty || comp(r2.front, r1.front)); return chooseR1 ? r1.front : r2.front; } static if (isForwardRange!R1 && isForwardRange!R2) { + /// @property typeof(this) save() { auto ret = this; @@ -1143,8 +1220,10 @@ public: } } + /// ref auto opSlice() { return this; } + /// @property bool empty() { return r1.empty && r2.empty; } } @@ -1160,6 +1239,7 @@ setSymmetricDifference(alias less = "a < b", R1, R2) @safe unittest { import std.algorithm.comparison : equal; + import std.range.primitives : isForwardRange; int[] a = [ 1, 2, 4, 5, 7, 9 ]; int[] b = [ 0, 1, 2, 4, 7, 8 ]; @@ -1182,132 +1262,45 @@ setSymmetricDifference(alias less = "a < b", R1, R2) static assert(hasLvalueElements!R2); } +/++ +TODO: once SetUnion got deprecated we can provide the usual definition +(= merge + filter after uniqs) +See: https://github.com/dlang/phobos/pull/4249 /** Lazily computes the union of two or more ranges $(D rs). The ranges -are assumed to be sorted by $(D less). Elements in the output are not -unique; the length of the output is the sum of the lengths of the -inputs. (The $(D length) member is offered if all ranges also have -length.) The element types of all ranges must have a common type. - */ -struct SetUnion(alias less = "a < b", Rs...) if (allSatisfy!(isInputRange, Rs)) -{ -private: - Rs _r; - alias comp = binaryFun!(less); - uint _crt; +are assumed to be sorted by $(D less). Elements in the output are +unique. The element types of all ranges must have a common type. - void adjustPosition(uint candidate = 0)() - { - static if (candidate == Rs.length) - { - _crt = _crt.max; - } - else - { - if (_r[candidate].empty) - { - adjustPosition!(candidate + 1)(); - return; - } - foreach (i, U; Rs[candidate + 1 .. $]) - { - enum j = candidate + i + 1; - if (_r[j].empty) continue; - if (comp(_r[j].front, _r[candidate].front)) - { - // a new candidate was found - adjustPosition!(j)(); - return; - } - } - // Found a successful candidate - _crt = candidate; - } - } - -public: - alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); - static assert(!is(CommonType!(staticMap!(.ElementType, Rs)) == void), - typeof(this).stringof ~ ": incompatible element types."); - - this(Rs rs) - { - this._r = rs; - adjustPosition(); - } +Params: + less = Predicate the given ranges are sorted by. + rs = The ranges to compute the union for. - @property bool empty() - { - return _crt == _crt.max; - } +Returns: + A range containing the unique union of the given ranges. - void popFront() - { - // Assumes _crt is correct - assert(!empty); - foreach (i, U; Rs) - { - if (i < _crt) continue; - // found _crt - assert(!_r[i].empty); - _r[i].popFront(); - adjustPosition(); - return; - } - assert(false); - } - - @property auto ref ElementType front() - { - assert(!empty); - // Assume _crt is correct - foreach (i, U; Rs) - { - if (i < _crt) continue; - assert(!_r[i].empty); - return _r[i].front; - } - assert(false); - } - - static if (allSatisfy!(isForwardRange, Rs)) - { - @property auto save() - { - auto ret = this; - foreach (ti, elem; _r) - { - ret._r[ti] = elem.save; - } - return ret; - } - } - - static if (allSatisfy!(hasLength, Rs)) - { - @property size_t length() - { - size_t result; - foreach (i, U; Rs) - { - result += _r[i].length; - } - return result; - } - - alias opDollar = length; - } -} - -/// Ditto -SetUnion!(less, Rs) setUnion(alias less = "a < b", Rs...) +See_Also: + $(REF merge, std,algorithm,sorting) + */ +auto setUnion(alias less = "a < b", Rs...) (Rs rs) { - return typeof(return)(rs); + import std.algorithm.iteration : uniq; + import std.algorithm.sorting : merge; + return merge!(less, Rs)(rs).uniq; } /// -@safe unittest +@safe pure nothrow unittest + /// +{ + import std.algorithm.comparison : equal; + + int[] a = [1, 3, 5]; + int[] b = [2, 3, 4]; + assert(a.setUnion(b).equal([1, 2, 3, 4, 5])); +} + +@safe pure nothrow unittest { import std.algorithm.comparison : equal; @@ -1315,13 +1308,46 @@ SetUnion!(less, Rs) setUnion(alias less = "a < b", Rs...) int[] b = [ 0, 1, 2, 4, 7, 8 ]; double[] c = [ 10.5 ]; - static assert(isForwardRange!(typeof(setUnion(a, b)))); - assert(setUnion(a, b).length == a.length + b.length); - assert(equal(setUnion(a, b), [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); + assert(equal(setUnion(a, b), [0, 1, 2, 4, 5, 7, 8, 9][])); assert(equal(setUnion(a, c, b), - [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9, 10.5][])); - auto u = setUnion(a, b); - u.front--; - assert(equal(u, [-1, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); + [0, 1, 2, 4, 5, 7, 8, 9, 10.5][])); +} + +@safe unittest +{ + // save + import std.range : dropOne; + int[] a = [0, 1, 2]; + int[] b = [0, 3]; + auto arr = a.setUnion(b); + assert(arr.front == 0); + assert(arr.save.dropOne.front == 1); + assert(arr.front == 0); } +@nogc @safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + static immutable a = [1, 3, 5]; + static immutable b = [2, 4]; + static immutable r = [1, 2, 3, 4, 5]; + assert(a.setUnion(b).equal(r)); +} + +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange; + import std.range : iota; + + auto dummyResult1 = [1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; + auto dummyResult2 = iota(1, 11); + foreach (DummyType; AllDummyRanges) + { + DummyType d; + assert(d.setUnion([1, 1.5, 5.5]).equal(dummyResult1)); + assert(d.setUnion(d).equal(dummyResult2)); + } +} +++/ diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 9e7f2ce474d..384a9c00ec8 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -1,26 +1,35 @@ // Written in the D programming language. /** -This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). +This is a submodule of $(MREF std, algorithm). It contains generic _sorting algorithms. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE Cheat Sheet, - $(TR $(TH Function Name) $(TH Description)) - $(T2 completeSort, If $(D a = [10, 20, 30]) and $(D b = [40, 6, 15]), then $(D completeSort(a, b)) leaves $(D a = [6, 10, 15]) and $(D b = [20, 30, 40]). The range $(D a) must be sorted prior to the call, and as a result the - combination $(D $(XREF range,chain)(a, b)) is sorted.) + combination $(D $(REF chain, std,range)(a, b)) is sorted.) $(T2 isPartitioned, $(D isPartitioned!"a < 0"([-1, -2, 1, 0, 2])) returns $(D true) because the predicate is $(D true) for a portion of the range and $(D false) afterwards.) $(T2 isSorted, $(D isSorted([1, 1, 2, 3])) returns $(D true).) +$(T2 isStrictlyMonotonic, + $(D isStrictlyMonotonic([1, 1, 2, 3])) returns $(D false).) +$(T2 ordered, + $(D ordered(1, 1, 2, 3)) returns $(D true).) +$(T2 strictlyOrdered, + $(D strictlyOrdered(1, 1, 2, 3)) returns $(D false).) $(T2 makeIndex, Creates a separate index for a range.) +$(T2 merge, + Lazily merges two or more sorted ranges.) +$(T2 multiSort, + Sorts by multiple keys.) $(T2 nextEvenPermutation, Computes the next lexicographically greater even permutation of a range in-place.) @@ -32,12 +41,17 @@ $(T2 partialSort, $(D a[0 .. 3] = [1, 2, 3]). The other elements of $(D a) are left in an unspecified order.) $(T2 partition, - Partitions a range according to a predicate.) + Partitions a range according to a unary predicate.) $(T2 partition3, - Partitions a range in three parts (less than, equal, greater than the - given pivot).) + Partitions a range according to a binary predicate in three parts (less + than, equal, greater than the given pivot). Pivot is not given as an + index, but instead as an element independent from the range's content.) +$(T2 pivotPartition, + Partitions a range according to a binary predicate in two parts: less + than or equal, and greater than or equal to the given pivot, passed as + an index in the range.) $(T2 schwartzSort, - Sorts with the help of the $(LUCKY Schwartzian transform).) + Sorts with the help of the $(LINK2 https://en.wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform).) $(T2 sort, Sorts.) $(T2 topN, @@ -50,9 +64,9 @@ $(T2 topNIndex, Copyright: Andrei Alexandrescu 2008-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_sorting.d) @@ -61,22 +75,24 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) */ module std.algorithm.sorting; +import std.typecons : Flag; import std.algorithm.mutation : SwapStrategy; import std.functional; // : unaryFun, binaryFun; import std.range.primitives; // FIXME import std.range; // : SortedRange; import std.traits; +import std.meta; // : allSatisfy; /** Specifies whether the output of certain algorithm is desired in sorted format. + +If set to $(D SortOutput.no), the output should not be sorted. + +Otherwise if set to $(D SortOutput.yes), the output should be sorted. */ -enum SortOutput -{ - no, /// Don't sort output - yes, /// Sort output -} +alias SortOutput = Flag!"sortOutput"; // completeSort /** @@ -87,12 +103,19 @@ exact strategy chosen depends on the relative sizes of $(D lhs) and $(D rhs). Performs $(BIGOH lhs.length + rhs.length * log(rhs.length)) (best case) to $(BIGOH (lhs.length + rhs.length) * log(lhs.length + rhs.length)) (worst-case) evaluations of $(D swap). + +Params: + less = The predicate to sort by. + ss = The swapping strategy to use. + lhs = The sorted, left-hand side of the random access range to be sorted. + rhs = The unsorted, right-hand side of the random access range to be + sorted. */ void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range1, Range2)(SortedRange!(Range1, less) lhs, Range2 rhs) -if (hasLength!(Range2) && hasSlicing!(Range2)) + RandomAccessRange1, RandomAccessRange2)(SortedRange!(RandomAccessRange1, less) lhs, RandomAccessRange2 rhs) +if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2)) { - import std.algorithm : bringToFront; // FIXME + import std.algorithm.mutation : bringToFront; import std.range : chain, assumeSorted; // Probably this algorithm can be optimized by using in-place // merge @@ -107,7 +130,7 @@ if (hasLength!(Range2) && hasSlicing!(Range2)) } /// -unittest +@safe unittest { import std.range : assumeSorted; int[] a = [ 1, 2, 3 ]; @@ -119,11 +142,27 @@ unittest // isSorted /** -Checks whether a forward range is sorted according to the comparison -operation $(D less). Performs $(BIGOH r.length) evaluations of $(D -less). +Checks whether a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) +is sorted according to the comparison operation $(D less). Performs $(BIGOH r.length) +evaluations of $(D less). + +Unlike `isSorted`, `isStrictlyMonotonic` does not allow for equal values, +i.e. values for which both `less(a, b)` and `less(b, a)` are false. + +With either function, the predicate must be a strict ordering just like with +`isSorted`. For example, using `"a <= b"` instead of `"a < b"` is +incorrect and will cause failed assertions. + +Params: + less = Predicate the range should be sorted by. + r = Forward range to check for sortedness. + +Returns: + `true` if the range is sorted, false otherwise. `isSorted` allows + duplicates, `isStrictlyMonotonic` not. */ -bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) +bool isSorted(alias less = "a < b", Range)(Range r) +if (isForwardRange!(Range)) { if (r.empty) return true; @@ -142,7 +181,7 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) } else { - auto ahead = r; + auto ahead = r.save; ahead.popFront(); size_t i; @@ -163,12 +202,20 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) /// @safe unittest { + assert([1, 1, 2].isSorted); + // strictly monotonic doesn't allow duplicates + assert(![1, 1, 2].isStrictlyMonotonic); + int[] arr = [4, 3, 2, 1]; assert(!isSorted(arr)); + assert(!isStrictlyMonotonic(arr)); + + assert(isSorted!"a > b"(arr)); + assert(isStrictlyMonotonic!"a > b"(arr)); + sort(arr); assert(isSorted(arr)); - sort!("a > b")(arr); - assert(isSorted!("a > b")(arr)); + assert(isStrictlyMonotonic(arr)); } @safe unittest @@ -186,6 +233,10 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) int[] b = [1, 3, 2]; assert(!isSorted(b)); + // ignores duplicates + int[] c = [1, 1, 2]; + assert(isSorted(c)); + dchar[] ds = "コーヒーãŒå¥½ãã§ã™"d.dup; sort(ds); string s = to!string(ds); @@ -193,13 +244,132 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) assert(isSorted(s)); // bidirectional } +@nogc @safe nothrow pure unittest +{ + static immutable a = [1, 2, 3]; + assert(a.isSorted); +} + +/// ditto +bool isStrictlyMonotonic(alias less = "a < b", Range)(Range r) +if (isForwardRange!Range) +{ + import std.algorithm.searching : findAdjacent; + return findAdjacent!((a,b) => !binaryFun!less(a,b))(r).empty; +} + +@safe unittest +{ + import std.conv : to; + + assert("abcd".isStrictlyMonotonic); + assert(!"aacd".isStrictlyMonotonic); + assert(!"acb".isStrictlyMonotonic); + + assert([1, 2, 3].isStrictlyMonotonic); + assert(![1, 3, 2].isStrictlyMonotonic); + assert(![1, 1, 2].isStrictlyMonotonic); + + // ー occurs twice -> can't be strict + dchar[] ds = "コーヒーãŒå¥½ãã§ã™"d.dup; + sort(ds); + string s = to!string(ds); + assert(!isStrictlyMonotonic(ds)); // random-access + assert(!isStrictlyMonotonic(s)); // bidirectional + + dchar[] ds2 = "コーヒãŒå¥½ãã§ã™"d.dup; + sort(ds2); + string s2 = to!string(ds2); + assert(isStrictlyMonotonic(ds2)); // random-access + assert(isStrictlyMonotonic(s2)); // bidirectional +} + +@nogc @safe nothrow pure unittest +{ + static immutable a = [1, 2, 3]; + assert(a.isStrictlyMonotonic); +} + +/** +Like $(D isSorted), returns $(D true) if the given $(D values) are ordered +according to the comparison operation $(D less). Unlike $(D isSorted), takes values +directly instead of structured in a range. + +$(D ordered) allows repeated values, e.g. $(D ordered(1, 1, 2)) is $(D true). To verify +that the values are ordered strictly monotonically, use $(D strictlyOrdered); +$(D strictlyOrdered(1, 1, 2)) is $(D false). + +With either function, the predicate must be a strict ordering. For example, +using $(D "a <= b") instead of $(D "a < b") is incorrect and will cause failed +assertions. + +Params: + values = The tested value + less = The comparison predicate + +Returns: + $(D true) if the values are ordered; $(D ordered) allows for duplicates, + $(D strictlyOrdered) does not. +*/ + +bool ordered(alias less = "a < b", T...)(T values) +if ((T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool)) + || + (T.length > 2 && is(typeof(ordered!less(values[0 .. 1 + $ / 2]))) + && is(typeof(ordered!less(values[$ / 2 .. $])))) + ) +{ + foreach (i, _; T[0 .. $ - 1]) + { + if (binaryFun!less(values[i + 1], values[i])) + { + assert(!binaryFun!less(values[i], values[i + 1]), + __FUNCTION__ ~ ": incorrect non-strict predicate."); + return false; + } + } + return true; +} + +/// ditto +bool strictlyOrdered(alias less = "a < b", T...)(T values) +if (is(typeof(ordered!less(values)))) +{ + foreach (i, _; T[0 .. $ - 1]) + { + if (!binaryFun!less(values[i], values[i + 1])) + { + return false; + } + assert(!binaryFun!less(values[i + 1], values[i]), + __FUNCTION__ ~ ": incorrect non-strict predicate."); + } + return true; +} + +/// +@safe unittest +{ + assert(ordered(42, 42, 43)); + assert(!strictlyOrdered(43, 42, 45)); + assert(ordered(42, 42, 43)); + assert(!strictlyOrdered(42, 42, 43)); + assert(!ordered(43, 42, 45)); + // Ordered lexicographically + assert(ordered("Jane", "Jim", "Joe")); + assert(strictlyOrdered("Jane", "Jim", "Joe")); + // Incidentally also ordered by length decreasing + assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); + // ... but not strictly so: "Jim" and "Joe" have the same length + assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); +} + // partition /** -Partitions a range in two using $(D pred) as a -predicate. Specifically, reorders the range $(D r = [left, -right$(RPAREN)) using $(D swap) such that all elements $(D i) for -which $(D pred(i)) is $(D true) come before all elements $(D j) for -which $(D pred(j)) returns $(D false). +Partitions a range in two using the given $(D predicate). +Specifically, reorders the range $(D r = [left, right$(RPAREN)) using $(D swap) +such that all elements $(D i) for which $(D predicate(i)) is $(D true) come +before all elements $(D j) for which $(D predicate(j)) returns $(D false). Performs $(BIGOH r.length) (if unstable or semistable) or $(BIGOH r.length * log(r.length)) (if stable) evaluations of $(D less) and $(D @@ -207,44 +377,57 @@ swap). The unstable version computes the minimum possible evaluations of $(D swap) (roughly half of those performed by the semistable version). +Params: + predicate = The predicate to partition by. + ss = The swapping strategy to employ. + r = The random-access range to partition. + Returns: The right part of $(D r) after partitioning. -If $(D ss == SwapStrategy.stable), $(D partition) preserves the -relative ordering of all elements $(D a), $(D b) in $(D r) for which -$(D pred(a) == pred(b)). If $(D ss == SwapStrategy.semistable), $(D -partition) preserves the relative ordering of all elements $(D a), $(D -b) in the left part of $(D r) for which $(D pred(a) == pred(b)). +If $(D ss == SwapStrategy.stable), $(D partition) preserves the relative +ordering of all elements $(D a), $(D b) in $(D r) for which $(D predicate(a) == +predicate(b)). If $(D ss == SwapStrategy.semistable), $(D partition) preserves +the relative ordering of all elements $(D a), $(D b) in the left part of $(D r) +for which $(D predicate(a) == predicate(b)). See_Also: - STL's $(WEB sgi.com/tech/stl/_partition.html, _partition)$(BR) - STL's $(WEB sgi.com/tech/stl/stable_partition.html, stable_partition) + STL's $(HTTP sgi.com/tech/stl/_partition.html, _partition)$(BR) + STL's $(HTTP sgi.com/tech/stl/stable_partition.html, stable_partition) */ -Range partition(alias predicate, - SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) - if ((ss == SwapStrategy.stable && isRandomAccessRange!(Range)) - || (ss != SwapStrategy.stable && isForwardRange!(Range))) +Range partition(alias predicate, SwapStrategy ss, Range)(Range r) +if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range) { - import std.algorithm : bringToFront, swap; // FIXME; + import std.algorithm.mutation : bringToFront; + alias pred = unaryFun!(predicate); if (r.empty) return r; - static if (ss == SwapStrategy.stable) + + if (r.length == 1) { - if (r.length == 1) - { - if (pred(r.front)) r.popFront(); - return r; - } - const middle = r.length / 2; - alias recurse = .partition!(pred, ss, Range); - auto lower = recurse(r[0 .. middle]); - auto upper = recurse(r[middle .. $]); - bringToFront(lower, r[middle .. r.length - upper.length]); - return r[r.length - lower.length - upper.length .. r.length]; + if (pred(r.front)) r.popFront(); + return r; } - else static if (ss == SwapStrategy.semistable) + const middle = r.length / 2; + alias recurse = .partition!(pred, ss, Range); + auto lower = recurse(r[0 .. middle]); + auto upper = recurse(r[middle .. r.length]); + bringToFront(lower, r[middle .. r.length - upper.length]); + return r[r.length - lower.length - upper.length .. r.length]; +} + +///ditto +Range partition(alias predicate, SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) +if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Range) +{ + import std.algorithm.mutation : swap; + alias pred = unaryFun!(predicate); + + static if (ss == SwapStrategy.semistable) { + if (r.empty) return r; + for (; !r.empty; r.popFront()) { // skip the initial portion of "correct" elements @@ -259,44 +442,75 @@ Range partition(alias predicate, } return result; } + return r; } - else // ss == SwapStrategy.unstable + else { // Inspired from www.stepanovpapers.com/PAM3-partition_notes.pdf, // section "Bidirectional Partition Algorithm (Hoare)" - auto result = r; - for (;;) + static if (isDynamicArray!Range) { + import std.algorithm.mutation : swapAt; + // For dynamic arrays prefer index-based manipulation + if (!r.length) return r; + size_t lo = 0, hi = r.length - 1; for (;;) { - if (r.empty) return result; - if (!pred(r.front)) break; - r.popFront(); - result.popFront(); + for (;;) + { + if (lo > hi) return r[lo .. r.length]; + if (!pred(r[lo])) break; + ++lo; + } + // found the left bound + assert(lo <= hi); + for (;;) + { + if (lo == hi) return r[lo .. r.length]; + if (pred(r[hi])) break; + --hi; + } + // found the right bound, swap & make progress + r.swapAt(lo++, hi--); } - // found the left bound - assert(!r.empty); + } + else + { + import std.algorithm.mutation : swap; + auto result = r; for (;;) { - if (pred(r.back)) break; + for (;;) + { + if (r.empty) return result; + if (!pred(r.front)) break; + r.popFront(); + result.popFront(); + } + // found the left bound + assert(!r.empty); + for (;;) + { + if (pred(r.back)) break; + r.popBack(); + if (r.empty) return result; + } + // found the right bound, swap & make progress + static if (is(typeof(swap(r.front, r.back)))) + { + swap(r.front, r.back); + } + else + { + auto t1 = r.moveFront(), t2 = r.moveBack(); + r.front = t2; + r.back = t1; + } + r.popFront(); + result.popFront(); r.popBack(); - if (r.empty) return result; - } - // found the right bound, swap & make progress - static if (is(typeof(swap(r.front, r.back)))) - { - swap(r.front, r.back); } - else - { - auto t1 = moveFront(r), t2 = moveBack(r); - r.front = t2; - r.back = t1; - } - r.popFront(); - result.popFront(); - r.popBack(); } } } @@ -304,8 +518,10 @@ Range partition(alias predicate, /// @safe unittest { - import std.algorithm : count, find; // FIXME + import std.algorithm.searching : count, find; import std.conv : text; + import std.range.primitives : empty; + import std.algorithm.mutation : SwapStrategy; auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; auto arr = Arr.dup; @@ -354,12 +570,213 @@ Range partition(alias predicate, assert(isPartitioned!`a.length < 5`(b)); } +// pivotPartition +/** + +Partitions `r` around `pivot` using comparison function `less`, algorithm akin +to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme, +Hoare partition). Specifically, permutes elements of `r` and returns +an index $(D k < r.length) such that: + +$(UL + +$(LI `r[pivot]` is swapped to `r[k]`) + +$(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(r[k], e)) +(i.e. `r[k]` is greater than or equal to each element to its left according to +predicate `less`)) + +$(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(e, +r[k])) (i.e. `r[k]` is less than or equal to each element to its right +according to predicate `less`))) + +If `r` contains equivalent elements, multiple permutations of `r` satisfy these +constraints. In such cases, `pivotPartition` attempts to distribute equivalent +elements fairly to the left and right of `k` such that `k` stays close to $(D +r.length / 2). + +Params: +less = The predicate used for comparison, modeled as a + $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, + strict weak ordering) (irreflexive, antisymmetric, transitive, and implying a transitive + equivalence) +r = The range being partitioned +pivot = The index of the pivot for partitioning, must be less than `r.length` or +`0` is `r.length` is `0` + +Returns: +The new position of the pivot + +See_Also: +$(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort +Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer +Science, February 2011. $(HTTPS youtube.com/watch?v=AxnotgLql0k, ACCU 2016 +Keynote), Andrei Alexandrescu. +*/ +size_t pivotPartition(alias less = "a < b", Range) +(Range r, size_t pivot) +if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range) +{ + assert(pivot < r.length || r.length == 0 && pivot == 0); + if (r.length <= 1) return 0; + import std.algorithm.mutation : swapAt, move; + alias lt = binaryFun!less; + + // Pivot at the front + r.swapAt(pivot, 0); + + // Fork implementation depending on nothrow copy, assignment, and + // comparison. If all of these are nothrow, use the specialized + // implementation discussed at https://youtube.com/watch?v=AxnotgLql0k. + static if (is(typeof( + () nothrow { auto x = r.front; x = r.front; return lt(x, x); } + ))) + { + auto p = r[0]; + // Plant the pivot in the end as well as a sentinel + size_t lo = 0, hi = r.length - 1; + auto save = move(r[hi]); + r[hi] = p; // Vacancy is in r[$ - 1] now + // Start process + for (;;) + { + // Loop invariant + version(unittest) + { + import std.algorithm.searching : all; + assert(r[0 .. lo].all!(x => !lt(p, x))); + assert(r[hi + 1 .. r.length].all!(x => !lt(x, p))); + } + do ++lo; while (lt(r[lo], p)); + r[hi] = r[lo]; + // Vacancy is now in r[lo] + do --hi; while (lt(p, r[hi])); + if (lo >= hi) break; + r[lo] = r[hi]; + // Vacancy is not in r[hi] + } + // Fixup + assert(lo - hi <= 2); + assert(!lt(p, r[hi])); + if (lo == hi + 2) + { + assert(!lt(r[hi + 1], p)); + r[lo] = r[hi + 1]; + --lo; + } + r[lo] = save; + if (lt(p, save)) --lo; + assert(!lt(p, r[lo])); + } + else + { + size_t lo = 1, hi = r.length - 1; + loop: for (;; lo++, hi--) + { + for (;; ++lo) + { + if (lo > hi) break loop; + if (!lt(r[lo], r[0])) break; + } + // found the left bound: r[lo] >= r[0] + assert(lo <= hi); + for (;; --hi) + { + if (lo >= hi) break loop; + if (!lt(r[0], r[hi])) break; + } + // found the right bound: r[hi] <= r[0], swap & make progress + assert(!lt(r[lo], r[hi])); + r.swapAt(lo, hi); + } + --lo; + } + r.swapAt(lo, 0); + return lo; +} + +/// +@safe nothrow unittest +{ + int[] a = [5, 3, 2, 6, 4, 1, 3, 7]; + size_t pivot = pivotPartition(a, a.length / 2); + import std.algorithm.searching : all; + assert(a[0 .. pivot].all!(x => x <= a[pivot])); + assert(a[pivot .. $].all!(x => x >= a[pivot])); +} + +@safe unittest +{ + void test(alias less)() + { + int[] a; + size_t pivot; + + a = [-9, -4, -2, -2, 9]; + pivot = pivotPartition!less(a, a.length / 2); + import std.algorithm.searching : all; + assert(a[0 .. pivot].all!(x => x <= a[pivot])); + assert(a[pivot .. $].all!(x => x >= a[pivot])); + + a = [9, 2, 8, -5, 5, 4, -8, -4, 9]; + pivot = pivotPartition!less(a, a.length / 2); + assert(a[0 .. pivot].all!(x => x <= a[pivot])); + assert(a[pivot .. $].all!(x => x >= a[pivot])); + + a = [ 42 ]; + pivot = pivotPartition!less(a, a.length / 2); + assert(pivot == 0); + assert(a == [ 42 ]); + + a = [ 43, 42 ]; + pivot = pivotPartition!less(a, 0); + assert(pivot == 1); + assert(a == [ 42, 43 ]); + + a = [ 43, 42 ]; + pivot = pivotPartition!less(a, 1); + assert(pivot == 0); + assert(a == [ 42, 43 ]); + + a = [ 42, 42 ]; + pivot = pivotPartition!less(a, 0); + assert(pivot == 0 || pivot == 1); + assert(a == [ 42, 42 ]); + pivot = pivotPartition!less(a, 1); + assert(pivot == 0 || pivot == 1); + assert(a == [ 42, 42 ]); + + import std.random; + import std.algorithm.iteration : map; + import std.stdio; + auto s = unpredictableSeed; + auto g = Random(s); + a = iota(0, uniform(1, 1000, g)) + .map!(_ => uniform(-1000, 1000, g)) + .array; + scope(failure) writeln("RNG seed was ", s); + pivot = pivotPartition!less(a, a.length / 2); + assert(a[0 .. pivot].all!(x => x <= a[pivot])); + assert(a[pivot .. $].all!(x => x >= a[pivot])); + } + test!"a < b"; + static bool myLess(int a, int b) + { + static bool bogus; + if (bogus) throw new Exception(""); // just to make it no-nothrow + return a < b; + } + test!myLess; +} + /** -Returns $(D true) if $(D r) is partitioned according to predicate $(D -pred). +Params: + pred = The predicate that the range should be partitioned by. + r = The range to check. +Returns: $(D true) if $(D r) is partitioned according to predicate $(D pred). */ bool isPartitioned(alias pred, Range)(Range r) - if (isForwardRange!(Range)) +if (isForwardRange!(Range)) { for (; !r.empty; r.popFront()) { @@ -390,12 +807,22 @@ and rightmost range only contains elements in $(D r) that are greater than $(D pivot). The less-than test is defined by the binary function $(D less). +Params: + less = The predicate to use for the rearrangement. + ss = The swapping strategy to use. + r = The random-access range to rearrange. + pivot = The pivot element. + +Returns: + A $(REF Tuple, std,typecons) of the three resulting ranges. These ranges are + slices of the original range. + BUGS: stable $(D partition3) has not been implemented yet. */ auto partition3(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, E) (Range r, E pivot) if (ss == SwapStrategy.unstable && isRandomAccessRange!Range - && hasSwappableElements!Range && hasLength!Range + && hasSwappableElements!Range && hasLength!Range && hasSlicing!Range && is(typeof(binaryFun!less(r.front, pivot)) == bool) && is(typeof(binaryFun!less(pivot, r.front)) == bool) && is(typeof(binaryFun!less(r.front, r.front)) == bool)) @@ -403,7 +830,7 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range // The algorithm is described in "Engineering a sort function" by // Jon Bentley et al, pp 1257. - import std.algorithm : swap, swapRanges; // FIXME + import std.algorithm.mutation : swap, swapAt, swapRanges; import std.algorithm.comparison : min; import std.typecons : tuple; @@ -419,7 +846,7 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range assert(j < r.length); if (lessFun(r[j], pivot)) continue; if (lessFun(pivot, r[j])) break; - swap(r[i++], r[j]); + r.swapAt(i++, j); } assert(j < k); for (;;) @@ -428,12 +855,12 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range if (!lessFun(pivot, r[--k])) { if (lessFun(r[k], pivot)) break; - swap(r[k], r[--l]); + r.swapAt(k, --l); } if (j == k) break bigloop; } // Here we know r[j] > pivot && r[k] < pivot - swap(r[j++], r[k]); + r.swapAt(j++, k); } // Swap the equal ranges from the extremes into the middle @@ -459,26 +886,31 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range @safe unittest { - import std.random : uniform; + import std.random : Random, uniform, unpredictableSeed; - auto a = new int[](uniform(0, 100)); - foreach (ref e; a) + immutable uint[] seeds = [3923355730, 1927035882, unpredictableSeed]; + foreach (s; seeds) { - e = uniform(0, 50); - } - auto pieces = partition3(a, 25); - assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length); - foreach (e; pieces[0]) - { - assert(e < 25); - } - foreach (e; pieces[1]) - { - assert(e == 25); - } - foreach (e; pieces[2]) - { - assert(e > 25); + auto r = Random(s); + auto a = new int[](uniform(0, 100, r)); + foreach (ref e; a) + { + e = uniform(0, 50); + } + auto pieces = partition3(a, 25); + assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length); + foreach (e; pieces[0]) + { + assert(e < 25); + } + foreach (e; pieces[1]) + { + assert(e == 25); + } + foreach (e; pieces[2]) + { + assert(e > 25); + } } } @@ -498,12 +930,19 @@ collection. The complexity is the same as $(D sort)'s. The first overload of $(D makeIndex) writes to a range containing pointers, and the second writes to a range containing offsets. The -first overload requires $(D Range) to be a forward range, and the +first overload requires $(D Range) to be a +$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), and the latter requires it to be a random-access range. $(D makeIndex) overwrites its second argument with the result, but never reallocates it. +Params: + less = The comparison to use. + ss = The swapping strategy. + r = The range to index. + index = The resulting index. + Returns: The pointer-based version returns a $(D SortedRange) wrapper over index, of type $(D SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b))) thus reflecting the ordering of the @@ -520,8 +959,8 @@ makeIndex( Range, RangeIndex) (Range r, RangeIndex index) - if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex) - && is(ElementType!(RangeIndex) : ElementType!(Range)*)) +if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex) + && is(ElementType!(RangeIndex) : ElementType!(Range)*)) { import std.algorithm.internal : addressOf; import std.exception : enforce; @@ -571,7 +1010,7 @@ if (isRandomAccessRange!Range && !isInfinite!Range && } /// -unittest +@system unittest { immutable(int[]) arr = [ 2, 3, 1, 5, 0 ]; // index using pointers @@ -586,10 +1025,8 @@ unittest (index2)); } -unittest +@system unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); immutable(int)[] arr = [ 2, 3, 1, 5, 0 ]; // index using pointers auto index1 = new immutable(int)*[arr.length]; @@ -619,6 +1056,361 @@ unittest (index3)); } +struct Merge(alias less = "a < b", Rs...) +if (Rs.length >= 2 && + allSatisfy!(isInputRange, Rs) && + !is(CommonType!(staticMap!(ElementType, Rs)) == void)) +{ + public Rs source; + private size_t _lastFrontIndex = size_t.max; + static if (isBidirectional) + { + private size_t _lastBackIndex = size_t.max; // `size_t.max` means uninitialized, + } + + import std.functional : binaryFun; + import std.traits : isCopyable; + import std.typetuple : anySatisfy; + + private alias comp = binaryFun!less; + private alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); + private enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs)); + + debug private enum canCheckSortedness = isCopyable!ElementType && !hasAliasing!ElementType; + + this(Rs source) + { + this.source = source; + this._lastFrontIndex = frontIndex; + } + + static if (anySatisfy!(isInfinite, Rs)) + { + enum bool empty = false; // propagate infiniteness + } + else + { + @property bool empty() + { + return _lastFrontIndex == size_t.max; + } + } + + @property auto ref front() + { + final switch (_lastFrontIndex) + { + foreach (i, _; Rs) + { + case i: + assert(!source[i].empty); + return source[i].front; + } + } + } + + private size_t frontIndex() + { + size_t bestIndex = size_t.max; // indicate undefined + Unqual!ElementType bestElement; + foreach (i, _; Rs) + { + if (source[i].empty) continue; + if (bestIndex == size_t.max || // either this is the first or + comp(source[i].front, bestElement)) + { + bestIndex = i; + bestElement = source[i].front; + } + } + return bestIndex; + } + + void popFront() + { + sw: final switch (_lastFrontIndex) + { + foreach (i, R; Rs) + { + case i: + debug static if (canCheckSortedness) + { + ElementType previousFront = source[i].front(); + } + source[i].popFront(); + debug static if (canCheckSortedness) + { + if (!source[i].empty) + { + assert(previousFront == source[i].front || + comp(previousFront, source[i].front), + "Input " ~ i.stringof ~ " is unsorted"); // @nogc + } + } + break sw; + } + } + _lastFrontIndex = frontIndex; + } + + static if (isBidirectional) + { + @property auto ref back() + { + if (_lastBackIndex == size_t.max) + { + this._lastBackIndex = backIndex; // lazy initialization + } + final switch (_lastBackIndex) + { + foreach (i, _; Rs) + { + case i: + assert(!source[i].empty); + return source[i].back; + } + } + } + + private size_t backIndex() + { + size_t bestIndex = size_t.max; // indicate undefined + Unqual!ElementType bestElement; + foreach (i, _; Rs) + { + if (source[i].empty) continue; + if (bestIndex == size_t.max || // either this is the first or + comp(bestElement, source[i].back)) + { + bestIndex = i; + bestElement = source[i].back; + } + } + return bestIndex; + } + + void popBack() + { + if (_lastBackIndex == size_t.max) + { + this._lastBackIndex = backIndex; // lazy initialization + } + sw: final switch (_lastBackIndex) + { + foreach (i, R; Rs) + { + case i: + debug static if (canCheckSortedness) + { + ElementType previousBack = source[i].back(); + } + source[i].popBack(); + debug static if (canCheckSortedness) + { + if (!source[i].empty) + { + assert(previousBack == source[i].back || + comp(source[i].back, previousBack), + "Input " ~ i.stringof ~ " is unsorted"); // @nogc + } + } + break sw; + } + } + _lastBackIndex = backIndex; + if (_lastBackIndex == size_t.max) // if emptied + { + _lastFrontIndex = size_t.max; + } + } + } + + static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) + { + @property auto save() + { + auto result = this; + foreach (i, _; Rs) + { + result.source[i] = result.source[i].save; + } + return result; + } + } + + static if (allSatisfy!(hasLength, Rs)) + { + @property size_t length() + { + size_t result; + foreach (i, _; Rs) + { + result += source[i].length; + } + return result; + } + + alias opDollar = length; + } +} + +/** + Merge multiple sorted ranges `rs` with less-than predicate function `pred` + into one single sorted output range containing the sorted union of the + elements of inputs. Duplicates are not eliminated, meaning that the total + number of elements in the output is the sum of all elements in the ranges + passed to it; the `length` member is offered if all inputs also have + `length`. The element types of all the inputs must have a common type + `CommonType`. + +Params: + less = Predicate the given ranges are sorted by. + rs = The ranges to compute the union for. + +Returns: + A range containing the union of the given ranges. + +Details: + +All of its inputs are assumed to be sorted. This can mean that inputs are + instances of $(REF SortedRange, std,range). Use the result of $(REF sort, + std,algorithm,sorting), or $(REF assumeSorted, std,range) to merge ranges + known to be sorted (show in the example below). Note that there is currently + no way of ensuring that two or more instances of $(REF SortedRange, + std,range) are sorted using a specific comparison function `pred`. Therefore + no checking is done here to assure that all inputs `rs` are instances of + $(REF SortedRange, std,range). + + This algorithm is lazy, doing work progressively as elements are pulled off + the result. + + Time complexity is proportional to the sum of element counts over all inputs. + + If all inputs have the same element type and offer it by `ref`, output + becomes a range with mutable `front` (and `back` where appropriate) that + reflects in the original inputs. + + If any of the inputs `rs` is infinite so is the result (`empty` being always + `false`). +*/ +Merge!(less, Rs) merge(alias less = "a < b", Rs...)(Rs rs) +if (Rs.length >= 2 && + allSatisfy!(isInputRange, Rs) && + !is(CommonType!(staticMap!(ElementType, Rs)) == void)) +{ + return typeof(return)(rs); +} + +/// +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.range : retro; + + int[] a = [1, 3, 5]; + int[] b = [2, 3, 4]; + + assert(a.merge(b).equal([1, 2, 3, 3, 4, 5])); + assert(a.merge(b).retro.equal([5, 4, 3, 3, 2, 1])); +} + +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + int[] a = [ 1, 2, 4, 5, 7, 9 ]; + int[] b = [ 0, 1, 2, 4, 7, 8 ]; + double[] c = [ 10.5 ]; + + assert(merge(a, b).length == a.length + b.length); + assert(equal(merge(a, b), [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); + assert(equal(merge(a, c, b), + [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9, 10.5][])); + auto u = merge(a, b); + u.front--; + assert(equal(u, [-1, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); +} + +@safe pure nothrow unittest +{ + // save + import std.range : dropOne; + int[] a = [1, 2]; + int[] b = [0, 3]; + auto arr = a.merge(b); + assert(arr.front == 0); + assert(arr.save.dropOne.front == 1); + assert(arr.front == 0); +} + +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange; + + auto dummyResult1 = [1, 1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; + auto dummyResult2 = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]; + foreach (DummyType; AllDummyRanges) + { + DummyType d; + assert(d.merge([1, 1.5, 5.5]).equal(dummyResult1)); + assert(d.merge(d).equal(dummyResult2)); + } +} + +@nogc @safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + static immutable a = [1, 3, 5]; + static immutable b = [2, 3, 4]; + static immutable r = [1, 2, 3, 3, 4, 5]; + assert(a.merge(b).equal(r)); +} + +/// test bi-directional access and common type +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.range : retro; + import std.traits : CommonType; + + alias S = short; + alias I = int; + alias D = double; + + S[] a = [1, 2, 3]; + I[] b = [50, 60]; + D[] c = [10, 20, 30, 40]; + + auto m = merge(a, b, c); + + static assert(is(typeof(m.front) == CommonType!(S, I, D))); + + assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60])); + assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1])); + + m.popFront(); + assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60])); + m.popBack(); + assert(equal(m, [2, 3, 10, 20, 30, 40, 50])); + m.popFront(); + assert(equal(m, [3, 10, 20, 30, 40, 50])); + m.popBack(); + assert(equal(m, [3, 10, 20, 30, 40])); + m.popFront(); + assert(equal(m, [10, 20, 30, 40])); + m.popBack(); + assert(equal(m, [10, 20, 30])); + m.popFront(); + assert(equal(m, [20, 30])); + m.popBack(); + assert(equal(m, [20])); + m.popFront(); + assert(m.empty); +} + private template validPredicates(E, less...) { static if (less.length == 0) @@ -632,7 +1424,7 @@ private template validPredicates(E, less...) } /** -$(D void multiSort(Range)(Range r) +$(D auto multiSort(Range)(Range r) if (validPredicates!(ElementType!Range, less));) Sorts a range by multiple keys. The call $(D multiSort!("a.id < b.id", @@ -641,12 +1433,18 @@ and sorts elements that have the same $(D id) by $(D date) descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id < b.id : a.date > b.date"(r)), but $(D multiSort) is faster because it does fewer comparisons (in addition to being more convenient). + +Returns: + The initial range wrapped as a $(D SortedRange) with its predicates + converted to an equivalent single predicate. */ template multiSort(less...) //if (less.length > 1) { - void multiSort(Range)(Range r) + auto multiSort(Range)(Range r) if (validPredicates!(ElementType!Range, less)) { + import std.range : assumeSorted; + import std.meta : AliasSeq; static if (is(typeof(less[$ - 1]) == SwapStrategy)) { enum ss = less[$ - 1]; @@ -654,41 +1452,68 @@ template multiSort(less...) //if (less.length > 1) } else { - alias ss = SwapStrategy.unstable; + enum ss = SwapStrategy.unstable; alias funs = less; } - alias lessFun = binaryFun!(funs[0]); - static if (funs.length > 1) + static if (funs.length == 0) + static assert(false, "No sorting predicate provided for multiSort"); + else + static if (funs.length == 1) + return sort!(funs[0], ss, Range)(r); + else { - while (r.length > 1) - { - auto p = getPivot!lessFun(r); - auto t = partition3!(less[0], ss)(r, r[p]); - if (t[0].length <= t[2].length) - { - .multiSort!less(t[0]); - .multiSort!(less[1 .. $])(t[1]); - r = t[2]; - } - else - { - .multiSort!(less[1 .. $])(t[1]); - .multiSort!less(t[2]); - r = t[0]; - } - } + multiSortImpl!(Range, ss, funs)(r); + return assumeSorted!(multiSortPredFun!(Range, funs))(r); } - else + } +} + +private bool multiSortPredFun(Range, funs...)(ElementType!Range a, ElementType!Range b) +{ + foreach (f; funs) + { + alias lessFun = binaryFun!(f); + if (lessFun(a, b)) return true; + if (lessFun(b, a)) return false; + } + return false; +} + +private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r) +{ + alias lessFun = binaryFun!(funs[0]); + + static if (funs.length > 1) + { + while (r.length > 1) { - sort!(lessFun, ss)(r); + auto p = getPivot!lessFun(r); + auto t = partition3!(funs[0], ss)(r, r[p]); + if (t[0].length <= t[2].length) + { + multiSortImpl!(Range, ss, funs)(t[0]); + multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); + r = t[2]; + } + else + { + multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); + multiSortImpl!(Range, ss, funs)(t[2]); + r = t[0]; + } } } + else + { + sort!(lessFun, ss)(r); + } } /// @safe unittest { + import std.algorithm.mutation : SwapStrategy; static struct Point { int x, y; } auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ]; auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ]; @@ -709,8 +1534,10 @@ template multiSort(less...) //if (less.length > 1) assert(pts1 == pts2); auto pts3 = indexed(pts1, iota(pts1.length)); - multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts3); - assert(equal(pts3, pts2)); + assert(pts3.multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable).release.equal(pts2)); + + auto pts4 = iota(10).array; + assert(pts4.multiSort!("a > b").release.equal(iota(10).retro)); } @safe unittest //issue 9160 (L-value only comparators) @@ -737,81 +1564,137 @@ template multiSort(less...) //if (less.length > 1) assert(points[1] == A(4, 1)); } -private size_t getPivot(alias less, Range)(Range r) +@safe unittest // issue 16179 (cannot access frame of function) { - import std.algorithm.mutation : swapAt; + auto arr = [[1, 2], [2, 0], [1, 0], [1, 1]]; + int c = 3; + + arr.multiSort!( + (a, b) => a[0] < b[0], + (a, b) => c*a[1] < c*b[1] + ); + assert(arr == [[1, 0], [1, 1], [1, 2], [2, 0]]); +} - // This algorithm sorts the first, middle and last elements of r, - // then returns the index of the middle element. In effect, it uses the - // median-of-three heuristic. +@safe unittest //Issue 16413 - @system comparison function +{ + bool lt(int a, int b) { return a < b; } static @system + auto a = [2, 1]; + a.multiSort!(lt, lt); + assert(a == [1, 2]); +} - alias pred = binaryFun!(less); - immutable len = r.length; - immutable size_t mid = len / 2; - immutable uint result = ((cast(uint) (pred(r[0], r[mid]))) << 2) | - ((cast(uint) (pred(r[0], r[len - 1]))) << 1) | - (cast(uint) (pred(r[mid], r[len - 1]))); - - switch(result) { - case 0b001: - swapAt(r, 0, len - 1); - swapAt(r, 0, mid); - break; - case 0b110: - swapAt(r, mid, len - 1); - break; - case 0b011: - swapAt(r, 0, mid); - break; - case 0b100: - swapAt(r, mid, len - 1); - swapAt(r, 0, mid); - break; - case 0b000: - swapAt(r, 0, len - 1); - break; - case 0b111: - break; - default: - assert(0); +private size_t getPivot(alias less, Range)(Range r) +{ + auto mid = r.length / 2; + if (r.length < 512) + { + if (r.length >= 32) + medianOf!less(r, size_t(0), mid, r.length - 1); + return mid; } + // The plan here is to take the median of five by taking five elements in + // the array, segregate around their median, and return the position of the + // third. We choose first, mid, last, and two more in between those. + + auto quarter = r.length / 4; + medianOf!less(r, + size_t(0), mid - quarter, mid, mid + quarter, r.length - 1); return mid; } -private void optimisticInsertionSort(alias less, Range)(Range r) +/* +Sorting routine that is optimized for short ranges. Note: uses insertion sort +going downward. Benchmarked a similar routine that goes upward, for some reason +it's slower. +*/ +private void shortSort(alias less, Range)(Range r) { import std.algorithm.mutation : swapAt; - alias pred = binaryFun!(less); - if (r.length < 2) + + switch (r.length) { - return; + case 0: case 1: + return; + case 2: + if (pred(r[1], r[0])) r.swapAt(0, 1); + return; + case 3: + if (pred(r[2], r[0])) + { + if (pred(r[0], r[1])) + { + r.swapAt(0, 1); + r.swapAt(0, 2); + } + else + { + r.swapAt(0, 2); + if (pred(r[1], r[0])) r.swapAt(0, 1); + } + } + else + { + if (pred(r[1], r[0])) + { + r.swapAt(0, 1); + } + else + { + if (pred(r[2], r[1])) r.swapAt(1, 2); + } + } + return; + case 4: + if (pred(r[1], r[0])) r.swapAt(0, 1); + if (pred(r[3], r[2])) r.swapAt(2, 3); + if (pred(r[2], r[0])) r.swapAt(0, 2); + if (pred(r[3], r[1])) r.swapAt(1, 3); + if (pred(r[2], r[1])) r.swapAt(1, 2); + return; + default: + sort5!pred(r[r.length - 5 .. r.length]); + if (r.length == 5) return; + break; } - immutable maxJ = r.length - 1; - for (size_t i = r.length - 2; i != size_t.max; --i) + assert(r.length >= 6); + /* The last 5 elements of the range are sorted. Proceed with expanding the + sorted portion downward. */ + immutable maxJ = r.length - 2; + for (size_t i = r.length - 6; ; --i) { - size_t j = i; - - static if (hasAssignableElements!Range) + static if (is(typeof(() nothrow + { + auto t = r[0]; if (pred(t, r[0])) r[0] = r[0]; + }))) // Can we afford to temporarily invalidate the array? { + size_t j = i + 1; auto temp = r[i]; - - for (; j < maxJ && pred(r[j + 1], temp); ++j) + if (pred(r[j], temp)) { - r[j] = r[j + 1]; + do + { + r[j - 1] = r[j]; + ++j; + } + while (j < r.length && pred(r[j], temp)); + r[j - 1] = temp; } - - r[j] = temp; } else { - for (; j < maxJ && pred(r[j + 1], r[j]); ++j) + size_t j = i; + while (pred(r[j + 1], r[j])) { - swapAt(r, j, j + 1); + r.swapAt(j, j + 1); + if (j == maxJ) break; + ++j; } } + if (i == 0) break; } } @@ -819,31 +1702,102 @@ private void optimisticInsertionSort(alias less, Range)(Range r) { import std.random : Random, uniform; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - auto rnd = Random(1); auto a = new int[uniform(100, 200, rnd)]; - foreach (ref e; a) { + foreach (ref e; a) + { e = uniform(-100, 100, rnd); } - optimisticInsertionSort!(binaryFun!("a < b"), int[])(a); + shortSort!(binaryFun!("a < b"), int[])(a); assert(isSorted(a)); } +/* +Sorts the first 5 elements exactly of range r. +*/ +private void sort5(alias lt, Range)(Range r) +{ + assert(r.length >= 5); + + import std.algorithm.mutation : swapAt; + + // 1. Sort first two pairs + if (lt(r[1], r[0])) r.swapAt(0, 1); + if (lt(r[3], r[2])) r.swapAt(2, 3); + + // 2. Arrange first two pairs by the largest element + if (lt(r[3], r[1])) + { + r.swapAt(0, 2); + r.swapAt(1, 3); + } + assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2])); + + // 3. Insert 4 into [0, 1, 3] + if (lt(r[4], r[1])) + { + r.swapAt(3, 4); + r.swapAt(1, 3); + if (lt(r[1], r[0])) + { + r.swapAt(0, 1); + } + } + else if (lt(r[4], r[3])) + { + r.swapAt(3, 4); + } + assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3])); + + // 4. Insert 2 into [0, 1, 3, 4] (note: we already know the last is greater) + assert(!lt(r[4], r[2])); + if (lt(r[2], r[1])) + { + r.swapAt(1, 2); + if (lt(r[1], r[0])) + { + r.swapAt(0, 1); + } + } + else if (lt(r[3], r[2])) + { + r.swapAt(2, 3); + } + // 7 comparisons, 0-9 swaps +} + +@safe unittest +{ + import std.algorithm.iteration : permutations; + import std.algorithm.mutation : copy; + + int[5] buf; + foreach (per; iota(5).permutations) + { + per.copy(buf[]); + sort5!((a, b) => a < b)(buf[]); + assert(buf[].isSorted); + } +} + // sort /** Sorts a random-access range according to the predicate $(D less). Performs -$(BIGOH r.length * log(r.length)) evaluations of $(D less). Stable sorting -requires $(D hasAssignableElements!Range) to be true. +$(BIGOH r.length * log(r.length)) evaluations of $(D less). If `less` involves +expensive computations on the _sort key, it may be worthwhile to use +$(LREF schwartzSort) instead. + +Stable sorting requires $(D hasAssignableElements!Range) to be true. + +$(D sort) returns a $(REF SortedRange, std,range) over the original range, +allowing functions that can take advantage of sorted data to know that the +range is sorted and adjust accordingly. The $(REF SortedRange, std,range) is a +wrapper around the original range, so both it and the original range are sorted. +Other functions can't know that the original range has been sorted, but +they $(I can) know that $(REF SortedRange, std,range) has been sorted. -$(D sort) returns a $(XREF range, SortedRange) over the original range, which -functions that can take advantage of sorted data can then use to know that the -range is sorted and adjust accordingly. The $(XREF range, SortedRange) is a -wrapper around the original range, so both it and the original range are sorted, -but other functions won't know that the original range has been sorted, whereas -they $(I can) know that $(XREF range, SortedRange) has been sorted. +Preconditions: The predicate is expected to satisfy certain rules in order for $(D sort) to behave as expected - otherwise, the program may fail on certain inputs (but not @@ -853,12 +1807,18 @@ $(D less(a,c)) (transitivity), and, conversely, $(D !less(a,b) && !less(b,c)) to imply $(D !less(a,c)). Note that the default predicate ($(D "a < b")) does not always satisfy these conditions for floating point types, because the expression will always be $(D false) when either $(D a) or $(D b) is NaN. +Use $(REF cmp, std,math) instead. + +Params: + less = The predicate to sort by. + ss = The swapping strategy to use. + r = The range to sort. Returns: The initial range wrapped as a $(D SortedRange) with the predicate $(D binaryFun!less). -Algorithms: $(WEB en.wikipedia.org/wiki/Introsort) is used for unstable sorting and -$(WEB en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting. +Algorithms: $(HTTP en.wikipedia.org/wiki/Introsort, Introsort) is used for unstable sorting and +$(HTTP en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting. Each algorithm has benefits beyond stability. Introsort is generally faster but Timsort may achieve greater speeds on data with low entropy or if predicate calls are expensive. Introsort performs no allocations whereas Timsort will perform one @@ -866,20 +1826,20 @@ or more allocations per call. Both algorithms have $(BIGOH n log n) worst-case time complexity. See_Also: - $(XREF range, assumeSorted)$(BR) - $(XREF range, SortedRange)$(BR) - $(XREF algorithm, SwapStrategy)$(BR) - $(XREF functional, binaryFun) + $(REF assumeSorted, std,range)$(BR) + $(REF SortedRange, std,range)$(BR) + $(REF SwapStrategy, std,algorithm,mutation)$(BR) + $(REF binaryFun, std,functional) */ SortedRange!(Range, less) sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) - if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || - hasAssignableElements!Range)) || - (ss != SwapStrategy.unstable && hasAssignableElements!Range)) && - isRandomAccessRange!Range && - hasSlicing!Range && - hasLength!Range) +if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || + hasAssignableElements!Range)) || + (ss != SwapStrategy.unstable && hasAssignableElements!Range)) && + isRandomAccessRange!Range && + hasSlicing!Range && + hasLength!Range) /+ Unstable sorting uses the quicksort algorithm, which uses swapAt, which either uses swap(...), requiring swappable elements, or just swaps using assignment. @@ -896,7 +1856,6 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, else //use Tim Sort for semistable & stable TimSortImpl!(lessFun, Range).sort(r, null); - enum maxLen = 8; assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); } else @@ -910,46 +1869,90 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, @safe pure nothrow unittest { int[] array = [ 1, 2, 3, 4 ]; + // sort in descending order - sort!("a > b")(array); + array.sort!("a > b"); assert(array == [ 4, 3, 2, 1 ]); + // sort in ascending order - sort(array); + array.sort(); assert(array == [ 1, 2, 3, 4 ]); - // sort with a delegate - bool myComp(int x, int y) @safe pure nothrow { return x > y; } - sort!(myComp)(array); - assert(array == [ 4, 3, 2, 1 ]); + + // sort with reusable comparator and chain + alias myComp = (x, y) => x > y; + assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]); } + /// -unittest +@safe unittest { // Showcase stable sorting + import std.algorithm.mutation : SwapStrategy; string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words); assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); } -unittest +/// +@safe unittest +{ + // Sorting floating-point numbers in presence of NaN + double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan]; + + import std.math : cmp, isIdentical; + import std.algorithm.comparison : equal; + + sort!((a, b) => cmp(a, b) < 0)(numbers); + + double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan]; + assert(numbers.equal!isIdentical(sorted)); +} + +@safe unittest +{ + // Simple regression benchmark + import std.random, std.algorithm.iteration, std.algorithm.mutation; + Random rng; + int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array; + static uint comps; + static bool less(int a, int b) { ++comps; return a < b; } + sort!less(a); // random numbers + sort!less(a); // sorted ascending + a.reverse(); + sort!less(a); // sorted descending + a[] = 0; + sort!less(a); // all equal + + // This should get smaller with time. On occasion it may go larger, but only + // if there's thorough justification. + debug enum uint watermark = 1676280; + else enum uint watermark = 1676220; + + import std.conv; + assert(comps <= watermark, text("You seem to have pessimized sort! ", + watermark, " < ", comps)); + assert(comps >= watermark, text("You seem to have improved sort!", + " Please update watermark from ", watermark, " to ", comps)); +} + +@safe unittest { import std.algorithm.internal : rndstuff; - import std.algorithm : swapRanges; // FIXME + import std.algorithm.mutation : swapRanges; import std.random : Random, unpredictableSeed, uniform; import std.uni : toUpper; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - // sort using delegate auto a = new int[100]; auto rnd = Random(unpredictableSeed); - foreach (ref e; a) { + foreach (ref e; a) + { e = uniform(-100, 100, rnd); } int i = 0; - bool greater2(int a, int b) { return a + i > b + i; } - bool delegate(int, int) greater = &greater2; + bool greater2(int a, int b) @safe { return a + i > b + i; } + auto greater = &greater2; sort!(greater)(a); assert(isSorted!(greater)(a)); @@ -958,7 +1961,8 @@ unittest assert(isSorted!("a < b")(a)); // sort using function; all elements equal - foreach (ref e; a) { + foreach (ref e; a) + { e = 5; } static bool less(int a, int b) { return a < b; } @@ -1004,7 +2008,7 @@ unittest size_t[] arr; arr.length = 1024; - foreach(k; 0..arr.length) arr[k] = k; + foreach (k; 0 .. arr.length) arr[k] = k; swapRanges(arr[0..$/2], arr[$/2..$]); sort!(pred, SwapStrategy.unstable)(arr); @@ -1012,7 +2016,7 @@ unittest } { - import std.algorithm : swap; // FIXME + import std.algorithm.mutation : swap; bool proxySwapCalled; struct S @@ -1034,20 +2038,19 @@ unittest private void quickSortImpl(alias less, Range)(Range r, size_t depth) { - import std.algorithm : swap; // FIXME - import std.algorithm.mutation : swapAt; - import std.algorithm.comparison : min; + import std.algorithm.mutation : swap, swapAt; + import std.algorithm.comparison : min, max; alias Elem = ElementType!(Range); - enum size_t optimisticInsertionSortGetsBetter = 25; - static assert(optimisticInsertionSortGetsBetter >= 1); + enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof); + static assert(shortSortGetsBetter >= 1); // partition - while (r.length > optimisticInsertionSortGetsBetter) + while (r.length > shortSortGetsBetter) { if (depth == 0) { - HeapSortImpl!(less, Range).heapSort(r); + HeapOps!(less, Range).heapSort(r); return; } depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3; @@ -1055,28 +2058,27 @@ private void quickSortImpl(alias less, Range)(Range r, size_t depth) const pivotIdx = getPivot!(less)(r); auto pivot = r[pivotIdx]; - alias pred = binaryFun!(less); - // partition - swapAt(r, pivotIdx, r.length - 1); + r.swapAt(pivotIdx, r.length - 1); size_t lessI = size_t.max, greaterI = r.length - 1; - while (true) + outer: for (;;) { + alias pred = binaryFun!less; while (pred(r[++lessI], pivot)) {} - while (greaterI > 0 && pred(pivot, r[--greaterI])) {} - - if (lessI >= greaterI) + assert(lessI <= greaterI, "sort: invalid comparison function."); + for (;;) { - break; + if (greaterI == lessI) break outer; + if (!pred(pivot, r[--greaterI])) break; } - swapAt(r, lessI, greaterI); + assert(lessI <= greaterI, "sort: invalid comparison function."); + if (lessI == greaterI) break; + r.swapAt(lessI, greaterI); } - swapAt(r, r.length - 1, lessI); - auto right = r[lessI + 1 .. r.length]; - - auto left = r[0 .. min(lessI, greaterI + 1)]; + r.swapAt(r.length - 1, lessI); + auto left = r[0 .. lessI], right = r[lessI + 1 .. r.length]; if (right.length > left.length) { swap(left, right); @@ -1085,14 +2087,14 @@ private void quickSortImpl(alias less, Range)(Range r, size_t depth) r = left; } // residual sort - static if (optimisticInsertionSortGetsBetter > 1) + static if (shortSortGetsBetter > 1) { - optimisticInsertionSort!(less, Range)(r); + shortSort!(less, Range)(r); } } -// Bottom-Up Heap-Sort Implementation -private template HeapSortImpl(alias less, Range) +// Heap operations for random-access ranges +package(std) template HeapOps(alias less, Range) { import std.algorithm.mutation : swapAt; @@ -1106,53 +2108,104 @@ private template HeapSortImpl(alias less, Range) void heapSort()(Range r) { // If true, there is nothing to do - if(r.length < 2) return; - + if (r.length < 2) return; // Build Heap - size_t i = r.length / 2; - while(i > 0) sift(r, --i, r.length); - + buildHeap(r); // Sort - i = r.length - 1; - while(i > 0) + for (size_t i = r.length - 1; i > 0; --i) + { + r.swapAt(0, i); + percolate(r, 0, i); + } + } + + //template because of @@@12410@@@ + void buildHeap()(Range r) + { + immutable n = r.length; + for (size_t i = n / 2; i-- > 0; ) + { + siftDown(r, i, n); + } + assert(isHeap(r)); + } + + bool isHeap()(Range r) + { + size_t parent = 0; + foreach (child; 1 .. r.length) + { + if (lessFun(r[parent], r[child])) return false; + // Increment parent every other pass + parent += !(child & 1); + } + return true; + } + + // Sifts down r[parent] (which is initially assumed to be messed up) so the + // heap property is restored for r[parent .. end]. + // template because of @@@12410@@@ + void siftDown()(Range r, size_t parent, immutable size_t end) + { + for (;;) { - swapAt(r, 0, i); - sift(r, 0, i); - --i; + auto child = (parent + 1) * 2; + if (child >= end) + { + // Leftover left child? + if (child == end && lessFun(r[parent], r[--child])) + r.swapAt(parent, child); + break; + } + auto leftChild = child - 1; + if (lessFun(r[child], r[leftChild])) child = leftChild; + if (!lessFun(r[parent], r[child])) break; + r.swapAt(parent, child); + parent = child; } } + // Alternate version of siftDown that performs fewer comparisons, see + // https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort. The percolate + // process first sifts the parent all the way down (without comparing it + // against the leaves), and then a bit up until the heap property is + // restored. So there are more swaps but fewer comparisons. Gains are made + // when the final position is likely to end toward the bottom of the heap, + // so not a lot of sifts back are performed. //template because of @@@12410@@@ - void sift()(Range r, size_t parent, immutable size_t end) + void percolate()(Range r, size_t parent, immutable size_t end) { immutable root = parent; - size_t child = void; // Sift down - while(true) + for (;;) { - child = parent * 2 + 1; + auto child = (parent + 1) * 2; - if(child >= end) break; - - if(child + 1 < end && lessFun(r[child], r[child + 1])) child += 1; + if (child >= end) + { + if (child == end) + { + // Leftover left node. + --child; + r.swapAt(parent, child); + parent = child; + } + break; + } - swapAt(r, parent, child); + auto leftChild = child - 1; + if (lessFun(r[child], r[leftChild])) child = leftChild; + r.swapAt(parent, child); parent = child; } - child = parent; - // Sift up - while(child > root) + for (auto child = parent; child > root; child = parent) { parent = (child - 1) / 2; - if(lessFun(r[parent], r[child])) - { - swapAt(r, parent, child); - child = parent; - } - else break; + if (!lessFun(r[parent], r[child])) break; + r.swapAt(parent, child); } } } @@ -1171,9 +2224,9 @@ private template TimSortImpl(alias pred, R) alias T = ElementType!R; alias less = binaryFun!pred; - bool greater(T a, T b){ return less(b, a); } - bool greaterEqual(T a, T b){ return !less(a, b); } - bool lessEqual(T a, T b){ return !less(b, a); } + alias greater = (a, b) => less(b, a); + alias greaterEqual = (a, b) => !less(a, b); + alias lessEqual = (a, b) => !less(b, a); enum minimalMerge = 128; enum minimalGallop = 7; @@ -1183,7 +2236,7 @@ private template TimSortImpl(alias pred, R) struct Slice{ size_t base, length; } // Entry point for tim sort - void sort(R range, T[] temp) + void sort()(R range, T[] temp) { import std.algorithm.comparison : min; @@ -1201,7 +2254,7 @@ private template TimSortImpl(alias pred, R) size_t stackLen = 0; // Allocate temporary memory if not provided by user - if (temp.length < minTemp) temp = uninitializedArray!(T[])(minTemp); + if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }(); for (size_t i = 0; i < range.length; ) { @@ -1229,8 +2282,8 @@ private template TimSortImpl(alias pred, R) immutable run3 = stackLen - 2; immutable run2 = stackLen - 3; immutable run1 = stackLen - 4; - - if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || + + if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) ) { immutable at = stack[run2].length < stack[run4].length ? run2 : run3; @@ -1238,17 +2291,17 @@ private template TimSortImpl(alias pred, R) } else if (stack[run3].length > stack[run4].length) break; else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp); - + stackLen -= 1; } - + // Assert that the code above established the invariant correctly version (assert) { if (stackLen == 2) assert(stack[0].length > stack[1].length); else if (stackLen > 2) { - foreach(k; 2 .. stackLen) + foreach (k; 2 .. stackLen) { assert(stack[k - 2].length > stack[k - 1].length + stack[k].length); assert(stack[k - 1].length > stack[k].length); @@ -1272,22 +2325,22 @@ private template TimSortImpl(alias pred, R) // Calculates optimal value for minRun: // take first 6 bits of n and add 1 if any lower bits are set - pure size_t minRunLength(size_t n) + size_t minRunLength()(size_t n) { immutable shift = bsr(n)-5; - auto result = (n>>shift) + !!(n & ~((1<> shift) + !!(n & ~((1 << shift)-1)); return result; } // Returns length of first run in range - size_t firstRun(R range) + size_t firstRun()(R range) out(ret) { assert(ret <= range.length); } body { - import std.algorithm : reverse; // FIXME + import std.algorithm.mutation : reverse; if (range.length < 2) return range.length; @@ -1305,18 +2358,18 @@ private template TimSortImpl(alias pred, R) } // A binary insertion sort for building runs up to minRun length - void binaryInsertionSort(R range, size_t sortedLen = 1) + void binaryInsertionSort()(R range, size_t sortedLen = 1) out { if (!__ctfe) assert(isSorted!pred(range)); } body { - import std.algorithm : move; // FIXME + import std.algorithm.mutation : move; for (; sortedLen < range.length; ++sortedLen) { - T item = moveAt(range, sortedLen); + T item = range.moveAt(sortedLen); size_t lower = 0; size_t upper = sortedLen; while (upper != lower) @@ -1330,14 +2383,14 @@ private template TimSortImpl(alias pred, R) //11 instructions vs 7 in the innermost loop [checked on Win32] //moveAll(retro(range[lower .. sortedLen]), // retro(range[lower+1 .. sortedLen+1])); - for(upper=sortedLen; upper>lower; upper--) - range[upper] = moveAt(range, upper-1); + for (upper=sortedLen; upper > lower; upper--) + range[upper] = range.moveAt(upper - 1); range[lower] = move(item); } } // Merge two runs in stack (at, at + 1) - void mergeAt(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp) + void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp) in { assert(stack.length >= 2); @@ -1359,7 +2412,7 @@ private template TimSortImpl(alias pred, R) // Merge two runs in a range. Mid is the starting index of the second run. // minGallop and temp are references; The calling function must receive the updated values. - void merge(R range, size_t mid, ref size_t minGallop, ref T[] temp) + void merge()(R range, size_t mid, ref size_t minGallop, ref T[] temp) in { if (!__ctfe) @@ -1394,7 +2447,7 @@ private template TimSortImpl(alias pred, R) } // Enlarge size of temporary memory if needed - T[] ensureCapacity(size_t minCapacity, T[] temp) + T[] ensureCapacity()(size_t minCapacity, T[] temp) out(ret) { assert(ret.length >= minCapacity); @@ -1408,21 +2461,21 @@ private template TimSortImpl(alias pred, R) if (newSize < minCapacity) newSize = minCapacity; if (__ctfe) temp.length = newSize; - else temp = uninitializedArray!(T[])(newSize); + else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); } return temp; } // Merge front to back. Returns new value of minGallop. // temp must be large enough to store range[0 .. mid] - size_t mergeLo(R range, immutable size_t mid, size_t minGallop, T[] temp) + size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp) out { if (!__ctfe) assert(isSorted!pred(range)); } body { - import std.algorithm : copy; // FIXME + import std.algorithm.mutation : copy; assert(mid <= range.length); assert(temp.length >= mid); @@ -1439,7 +2492,7 @@ private template TimSortImpl(alias pred, R) immutable lef_end = temp.length - 1; if (lef < lef_end && rig < range.length) - outer: while(true) + outer: while (true) { count_lef = 0; count_rig = 0; @@ -1450,14 +2503,14 @@ private template TimSortImpl(alias pred, R) if (lessEqual(temp[lef], range[rig])) { range[i++] = temp[lef++]; - if(lef >= lef_end) break outer; + if (lef >= lef_end) break outer; ++count_lef; count_rig = 0; } else { range[i++] = range[rig++]; - if(rig >= range.length) break outer; + if (rig >= range.length) break outer; count_lef = 0; ++count_rig; } @@ -1468,14 +2521,14 @@ private template TimSortImpl(alias pred, R) { count_lef = gallopForwardUpper(temp[lef .. $], range[rig]); foreach (j; 0 .. count_lef) range[i++] = temp[lef++]; - if(lef >= temp.length) break outer; + if (lef >= temp.length) break outer; count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]); foreach (j; 0 .. count_rig) range[i++] = range[rig++]; - if (rig >= range.length) while(true) + if (rig >= range.length) while (true) { range[i++] = temp[lef++]; - if(lef >= temp.length) break outer; + if (lef >= temp.length) break outer; } if (minGallop > 0) --minGallop; @@ -1498,14 +2551,14 @@ private template TimSortImpl(alias pred, R) // Merge back to front. Returns new value of minGallop. // temp must be large enough to store range[mid .. range.length] - size_t mergeHi(R range, immutable size_t mid, size_t minGallop, T[] temp) + size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp) out { if (!__ctfe) assert(isSorted!pred(range)); } body { - import std.algorithm : copy; // FIXME + import std.algorithm.mutation : copy; assert(mid <= range.length); assert(temp.length >= range.length - mid); @@ -1521,24 +2574,24 @@ private template TimSortImpl(alias pred, R) size_t count_lef, count_rig; outer: - while(true) + while (true) { count_lef = 0; count_rig = 0; // Linear merge - while((count_lef | count_rig) < minGallop) + while ((count_lef | count_rig) < minGallop) { - if(greaterEqual(temp[rig], range[lef])) + if (greaterEqual(temp[rig], range[lef])) { range[i--] = temp[rig]; - if(rig == 1) + if (rig == 1) { // Move remaining elements from left - while(true) + while (true) { range[i--] = range[lef]; - if(lef == 0) break; + if (lef == 0) break; --lef; } @@ -1554,10 +2607,10 @@ private template TimSortImpl(alias pred, R) else { range[i--] = range[lef]; - if(lef == 0) while(true) + if (lef == 0) while (true) { range[i--] = temp[rig]; - if(rig == 0) break outer; + if (rig == 0) break outer; --rig; } --lef; @@ -1570,29 +2623,29 @@ private template TimSortImpl(alias pred, R) do { count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]); - foreach(j; 0 .. count_rig) + foreach (j; 0 .. count_rig) { range[i--] = temp[rig]; - if(rig == 0) break outer; + if (rig == 0) break outer; --rig; } count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]); - foreach(j; 0 .. count_lef) + foreach (j; 0 .. count_lef) { range[i--] = range[lef]; - if(lef == 0) while(true) + if (lef == 0) while (true) { range[i--] = temp[rig]; - if(rig == 0) break outer; + if (rig == 0) break outer; --rig; } --lef; } - if(minGallop > 0) --minGallop; + if (minGallop > 0) --minGallop; } - while(count_lef >= minimalGallop || count_rig >= minimalGallop); + while (count_lef >= minimalGallop || count_rig >= minimalGallop); minGallop += 2; } @@ -1681,7 +2734,7 @@ private template TimSortImpl(alias pred, R) alias gallopReverseUpper = gallopSearch!( true, true); } -unittest +@safe unittest { import std.random : Random, uniform, randomShuffle; @@ -1692,9 +2745,9 @@ unittest } // Generates data especially for testing sorting with Timsort - static E[] genSampleData(uint seed) + static E[] genSampleData(uint seed) @safe { - import std.algorithm : swap, swapRanges; // FIXME + import std.algorithm.mutation : swap, swapRanges; auto rnd = Random(seed); @@ -1702,7 +2755,7 @@ unittest arr.length = 64 * 64; // We want duplicate values for testing stability - foreach(i, ref v; arr) v.value = i / 64; + foreach (i, ref v; arr) v.value = i / 64; // Swap ranges at random middle point (test large merge operation) immutable mid = uniform(arr.length / 4, arr.length / 4 * 3, rnd); @@ -1712,7 +2765,7 @@ unittest randomShuffle(arr[$ / 8 * 7 .. $], rnd); // Swap few random elements (test galloping mode) - foreach(i; 0 .. arr.length / 64) + foreach (i; 0 .. arr.length / 64) { immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd); swap(arr[a], arr[b]); @@ -1720,7 +2773,7 @@ unittest // Now that our test array is prepped, store original index value // This will allow us to confirm the array was sorted stably - foreach(i, ref v; arr) v.index = i; + foreach (i, ref v; arr) v.index = i; return arr; } @@ -1742,9 +2795,9 @@ unittest assert(isSorted!comp(arr)); // Test that the array was sorted stably - foreach(i; 0 .. arr.length - 1) + foreach (i; 0 .. arr.length - 1) { - if(arr[i].value == arr[i + 1].value) assert(arr[i].index < arr[i + 1].index); + if (arr[i].value == arr[i + 1].value) assert(arr[i].index < arr[i + 1].index); } return true; @@ -1753,11 +2806,10 @@ unittest enum seed = 310614065; testSort(seed); - //@@BUG: Timsort fails with CTFE as of DMD 2.060 - // enum result = testSort(seed); + enum result = testSort(seed); } -unittest +@safe unittest {//bugzilla 4584 assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)( [83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6, @@ -1766,7 +2818,7 @@ unittest } -unittest +@safe unittest { //test stable sort + zip import std.range; @@ -1778,7 +2830,7 @@ unittest assert(y == "aebcd"d); } -unittest +@safe unittest { // Issue 14223 import std.range, std.array; @@ -1788,17 +2840,22 @@ unittest // schwartzSort /** -Sorts a range using an algorithm akin to the $(WEB -wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also -known as the decorate-sort-undecorate pattern in Python and Lisp. (Not -to be confused with $(WEB youtube.com/watch?v=UHw6KXbvazs, the other -Schwartz).) This function is helpful when the sort comparison includes -an expensive computation. The complexity is the same as that of the -corresponding $(D sort), but $(D schwartzSort) evaluates $(D -transform) only $(D r.length) times (less than half when compared to -regular sorting). The usage can be best illustrated with an example. - -Examples: +Alternative sorting method that should be used when comparing keys involves an +expensive computation. Instead of using `less(a, b)` for comparing elements, +`schwartzSort` uses `less(transform(a), transform(b))`. The values of the +`transform` function are precomputed in a temporary array, thus saving on +repeatedly computing it. Conversely, if the cost of `transform` is small +compared to the cost of allocating and filling the precomputed array, `sort` +may be faster and therefore preferable. + +This approach to sorting is akin to the $(HTTP +wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also known as +the decorate-sort-undecorate pattern in Python and Lisp. The complexity is the +same as that of the corresponding `sort`, but `schwartzSort` evaluates +`transform` only `r.length` times (less than half when compared to regular +sorting). The usage can be best illustrated with an example. + +Example: ---- uint hashFun(string) { ... expensive computation ... } string[] array = ...; @@ -1819,6 +2876,12 @@ Schwartz sorting, a function $(D schwartzIsSorted) is not provided because the effect can be achieved by calling $(D isSorted!less(map!transform(r))). +Params: + transform = The transformation to apply. + less = The predicate to sort by. + ss = The swapping strategy to use. + r = The range to sort. + Returns: The initial range wrapped as a $(D SortedRange) with the predicate $(D (a, b) => binaryFun!less(transform(a), transform(b))). @@ -1827,15 +2890,24 @@ SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a), unaryFun!transform(b)))) schwartzSort(alias transform, alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, R)(R r) - if (isRandomAccessRange!R && hasLength!R) +if (isRandomAccessRange!R && hasLength!R) { - import core.stdc.stdlib : malloc, free; import std.conv : emplace; import std.string : representation; import std.range : zip, SortedRange; alias T = typeof(unaryFun!transform(r.front)); - auto xform1 = (cast(T*) malloc(r.length * T.sizeof))[0 .. r.length]; + static trustedMalloc(size_t len) @trusted + { + import core.stdc.stdlib : malloc; + import core.checkedint : mulu; + bool overflow; + const nbytes = mulu(len, T.sizeof, overflow); + if (overflow) assert(0); + return (cast(T*) malloc(nbytes))[0 .. len]; + } + auto xform1 = trustedMalloc(r.length); + size_t length; scope(exit) { @@ -1843,11 +2915,16 @@ schwartzSort(alias transform, alias less = "a < b", { foreach (i; 0 .. length) collectException(destroy(xform1[i])); } - free(xform1.ptr); + static void trustedFree(T[] p) @trusted + { + import core.stdc.stdlib : free; + free(p.ptr); + } + trustedFree(xform1); } for (; length != r.length; ++length) { - emplace(xform1.ptr + length, unaryFun!transform(r[length])); + emplace(&xform1[length], unaryFun!transform(r[length])); } // Make sure we use ubyte[] and ushort[], not char[] and wchar[] // for the intermediate array, lest zip gets confused. @@ -1863,39 +2940,11 @@ schwartzSort(alias transform, alias less = "a < b", return typeof(return)(r); } -unittest -{ - // issue 4909 - import std.typecons : Tuple; - Tuple!(char)[] chars; - schwartzSort!"a[0]"(chars); -} - -unittest -{ - // issue 5924 - import std.typecons : Tuple; - Tuple!(char)[] chars; - schwartzSort!((Tuple!(char) c){ return c[0]; })(chars); -} - -unittest +/// +@safe unittest { import std.algorithm.iteration : map; - import std.math : log2; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - - static double entropy(double[] probs) { - double result = 0; - foreach (p; probs) { - if (!p) continue; - //enforce(p > 0 && p <= 1, "Wrong probability passed to entropy"); - result -= p * log2(p); - } - return result; - } + import std.numeric : entropy; auto lowEnt = [ 1.0, 0, 0 ], midEnt = [ 0.1, 0.1, 0.8 ], @@ -1905,30 +2954,18 @@ unittest arr[1] = lowEnt; arr[2] = highEnt; - schwartzSort!(entropy, q{a > b})(arr); + schwartzSort!(entropy, "a > b")(arr); + assert(arr[0] == highEnt); assert(arr[1] == midEnt); assert(arr[2] == lowEnt); assert(isSorted!("a > b")(map!(entropy)(arr))); } -unittest +@safe unittest { import std.algorithm.iteration : map; - import std.math : log2; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - - static double entropy(double[] probs) { - double result = 0; - foreach (p; probs) { - if (!p) continue; - //enforce(p > 0 && p <= 1, "Wrong probability passed to entropy"); - result -= p * log2(p); - } - return result; - } + import std.numeric : entropy; auto lowEnt = [ 1.0, 0, 0 ], midEnt = [ 0.1, 0.1, 0.8 ], @@ -1938,13 +2975,30 @@ unittest arr[1] = lowEnt; arr[2] = highEnt; - schwartzSort!(entropy, q{a < b})(arr); + schwartzSort!(entropy, "a < b")(arr); + assert(arr[0] == lowEnt); assert(arr[1] == midEnt); assert(arr[2] == highEnt); assert(isSorted!("a < b")(map!(entropy)(arr))); } +@safe unittest +{ + // issue 4909 + import std.typecons : Tuple; + Tuple!(char)[] chars; + schwartzSort!"a[0]"(chars); +} + +@safe unittest +{ + // issue 5924 + import std.typecons : Tuple; + Tuple!(char)[] chars; + schwartzSort!((Tuple!(char) c){ return c[0]; })(chars); +} + // partialSort /** Reorders the random-access range $(D r) such that the range $(D r[0 @@ -1953,23 +3007,57 @@ the range $(D r[mid .. r.length]) in no particular order. Performs $(BIGOH r.length * log(mid)) evaluations of $(D pred). The implementation simply calls $(D topN!(less, ss)(r, n)) and then $(D sort!(less, ss)(r[0 .. n])). + +Params: + less = The predicate to sort by. + ss = The swapping strategy to use. + r = The random-access range to reorder. + n = The length of the initial segment of `r` to sort. */ void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range)(Range r, size_t n) - if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range)) +if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range)) { - topN!(less, ss)(r, n); - sort!(less, ss)(r[0 .. n]); + partialSort!(less, ss)(r[0 .. n], r[n .. $]); } /// -@safe unittest +@system unittest { int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]; partialSort(a, 5); assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]); } +/** +Stores the smallest elements of the two ranges in the left-hand range in sorted order. + +Params: + less = The predicate to sort by. + ss = The swapping strategy to use. + r1 = The first range. + r2 = The second range. + */ + +void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, + Range1, Range2)(Range1 r1, Range2 r2) +if (isRandomAccessRange!(Range1) && hasLength!Range1 && + isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && + hasLvalueElements!Range1 && hasLvalueElements!Range2) +{ + topN!(less, ss)(r1, r2); + sort!(less, ss)(r1); +} +/// +@system unittest +{ + int[] a = [5, 7, 2, 6, 7]; + int[] b = [2, 1, 5, 6, 7, 3, 0]; + + partialSort(a, b); + assert(a == [0, 1, 2, 2, 3]); +} + // topN /** Reorders the range $(D r) using $(D swap) such that $(D r[nth]) refers @@ -1982,50 +3070,147 @@ $(D !less(e2, r[nth])). Effectively, it finds the nth smallest $(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length)) (if stable) evaluations of $(D less) and $(D swap). -If $(D n >= r.length), the algorithm has no effect. +If $(D n >= r.length), the algorithm has no effect and returns +`r[0 .. r.length]`. + +Params: + less = The predicate to sort by. + ss = The swapping strategy to use. + r = The random-access range to reorder. + nth = The index of the element that should be in sorted position after the + function is done. See_Also: $(LREF topNIndex), - $(WEB sgi.com/tech/stl/nth_element.html, STL's nth_element) + $(HTTP sgi.com/tech/stl/nth_element.html, STL's nth_element) BUGS: Stable topN has not been implemented yet. */ -void topN(alias less = "a < b", +auto topN(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range)(Range r, size_t nth) - if (isRandomAccessRange!(Range) && hasLength!Range) +if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range) { - import std.algorithm : swap; // FIXME - import std.random : uniform; - static assert(ss == SwapStrategy.unstable, "Stable topN not yet implemented"); - while (r.length > nth) + if (nth >= r.length) return r[0 .. r.length]; + auto ret = r[0 .. nth]; + if (false) + { + // Workaround for https://issues.dlang.org/show_bug.cgi?id=16528 + // Safety checks: enumerate all potentially unsafe generic primitives + // then use a @trusted implementation. + auto b = binaryFun!less(r[0], r[r.length - 1]); + import std.algorithm.mutation : swapAt; + r.swapAt(size_t(0), size_t(0)); + auto len = r.length; + static assert(is(typeof(len) == size_t)); + pivotPartition!less(r, 0); + } + bool useSampling = true; + topNImpl!(binaryFun!less)(r, nth, useSampling); + return ret; +} + +private @trusted +void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling) +{ + for (;;) { - auto pivot = uniform(0, r.length); - swap(r[pivot], r.back); - assert(!binaryFun!(less)(r.back, r.back)); - auto right = partition!((a) => binaryFun!less(a, r.back), ss)(r); - assert(right.length >= 1); - swap(right.front, r.back); - pivot = r.length - right.length; - if (pivot == nth) + import std.algorithm.mutation : swapAt; + assert(n < r.length); + size_t pivot = void; + + // Decide strategy for partitioning + if (n == 0) { + pivot = 0; + foreach (i; 1 .. r.length) + if (less(r[i], r[pivot])) pivot = i; + r.swapAt(n, pivot); return; } - if (pivot < nth) + if (n + 1 == r.length) { - ++pivot; - r = r[pivot .. $]; - nth -= pivot; + pivot = 0; + foreach (i; 1 .. r.length) + if (less(r[pivot], r[i])) pivot = i; + r.swapAt(n, pivot); + return; + } + if (r.length <= 12) + { + pivot = pivotPartition!less(r, r.length / 2); + } + else if (n * 16 <= (r.length - 1) * 7) + { + pivot = topNPartitionOffMedian!(less, No.leanRight) + (r, n, useSampling); + // Quality check + if (useSampling) + { + if (pivot < n) + { + if (pivot * 4 < r.length) + { + useSampling = false; + } + } + else if ((r.length - pivot) * 8 < r.length * 3) + { + useSampling = false; + } + } + } + else if (n * 16 >= (r.length - 1) * 9) + { + pivot = topNPartitionOffMedian!(less, Yes.leanRight) + (r, n, useSampling); + // Quality check + if (useSampling) + { + if (pivot < n) + { + if (pivot * 8 < r.length * 3) + { + useSampling = false; + } + } + else if ((r.length - pivot) * 4 < r.length) + { + useSampling = false; + } + } } else { - assert(pivot < r.length); + pivot = topNPartition!less(r, n, useSampling); + // Quality check + if (useSampling && + (pivot * 9 < r.length * 2 || pivot * 9 > r.length * 7)) + { + // Failed - abort sampling going forward + useSampling = false; + } + } + + assert(pivot != size_t.max); + // See how the pivot fares + if (pivot == n) + { + return; + } + if (pivot > n) + { r = r[0 .. pivot]; } + else + { + n -= pivot + 1; + r = r[pivot + 1 .. r.length]; + } } } @@ -2033,20 +3218,235 @@ void topN(alias less = "a < b", @safe unittest { int[] v = [ 25, 7, 9, 2, 0, 5, 21 ]; + topN!"a < b"(v, 100); + assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]); auto n = 4; topN!"a < b"(v, n); assert(v[n] == 9); } +private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling) +{ + assert(r.length >= 9 && n < r.length); + immutable ninth = r.length / 9; + auto pivot = ninth / 2; + // Position subrange r[lo .. hi] to have length equal to ninth and its upper + // median r[lo .. hi][$ / 2] in exactly the same place as the upper median + // of the entire range r[$ / 2]. This is to improve behavior for searching + // the median in already sorted ranges. + immutable lo = r.length / 2 - pivot, hi = lo + ninth; + // We have either one straggler on the left, one on the right, or none. + assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1); + assert(lo >= ninth * 4); + assert(r.length - hi >= ninth * 4); + + // Partition in groups of 3, and the mid tertile again in groups of 3 + if (!useSampling) + p3!lp(r, lo - ninth, hi + ninth); + p3!lp(r, lo, hi); + + // Get the median of medians of medians + // Map the full interval of n to the full interval of the ninth + pivot = (n * (ninth - 1)) / (r.length - 1); + topNImpl!lp(r[lo .. hi], pivot, useSampling); + return expandPartition!lp(r, lo, pivot + lo, hi); +} + +private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi) +{ + assert(lo <= hi && hi < r.length); + immutable ln = hi - lo; + for (; lo < hi; ++lo) + { + assert(lo >= ln); + assert(lo + ln < r.length); + medianOf!less(r, lo - ln, lo, lo + ln); + } +} + +private void p4(alias less, Flag!"leanRight" f, Range) + (Range r, size_t lo, immutable size_t hi) +{ + assert(lo <= hi && hi < r.length); + immutable ln = hi - lo, _2ln = ln * 2; + for (; lo < hi; ++lo) + { + assert(lo >= ln); + assert(lo + ln < r.length); + static if (f == Yes.leanRight) + medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln); + else + medianOf!(less, f)(r, lo - ln, lo, lo + ln, lo + _2ln); + } +} + +private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R) + (R r, size_t n, bool useSampling) +{ + assert(r.length >= 12); + assert(n < r.length); + immutable _4 = r.length / 4; + static if (f == Yes.leanRight) + immutable leftLimit = 2 * _4; + else + immutable leftLimit = _4; + // Partition in groups of 4, and the left quartile again in groups of 3 + if (!useSampling) + { + p4!(lp, f)(r, leftLimit, leftLimit + _4); + } + immutable _12 = _4 / 3; + immutable lo = leftLimit + _12, hi = lo + _12; + p3!lp(r, lo, hi); + + // Get the median of medians of medians + // Map the full interval of n to the full interval of the ninth + immutable pivot = (n * (_12 - 1)) / (r.length - 1); + topNImpl!lp(r[lo .. hi], pivot, useSampling); + return expandPartition!lp(r, lo, pivot + lo, hi); +} + +/* +Params: +less = predicate +r = range to partition +pivot = pivot to partition around +lo = value such that r[lo .. pivot] already less than r[pivot] +hi = value such that r[pivot .. hi] already greater than r[pivot] + +Returns: new position of pivot +*/ +private +size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi) +in +{ + import std.algorithm.searching : all; + assert(lo <= pivot); + assert(pivot < hi); + assert(hi <= r.length); + assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x))); + assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot]))); + } +out +{ + import std.algorithm.searching : all; + assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x))); + assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot]))); +} +body +{ + import std.algorithm.mutation : swapAt; + import std.algorithm.searching : all; + // We work with closed intervals! + --hi; + + size_t left = 0, rite = r.length - 1; + loop: for (;; ++left, --rite) + { + for (;; ++left) + { + if (left == lo) break loop; + if (!lp(r[left], r[pivot])) break; + } + for (;; --rite) + { + if (rite == hi) break loop; + if (!lp(r[pivot], r[rite])) break; + } + r.swapAt(left, rite); + } + + assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x))); + assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot]))); + assert(r[0 .. left].all!(x => !lp(r[pivot], x))); + assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot]))); + + immutable oldPivot = pivot; + + if (left < lo) + { + // First loop: spend r[lo .. pivot] + for (; lo < pivot; ++left) + { + if (left == lo) goto done; + if (!lp(r[oldPivot], r[left])) continue; + --pivot; + assert(!lp(r[oldPivot], r[pivot])); + r.swapAt(left, pivot); + } + // Second loop: make left and pivot meet + for (;; ++left) + { + if (left == pivot) goto done; + if (!lp(r[oldPivot], r[left])) continue; + for (;;) + { + if (left == pivot) goto done; + --pivot; + if (lp(r[pivot], r[oldPivot])) + { + r.swapAt(left, pivot); + break; + } + } + } + } + + // First loop: spend r[lo .. pivot] + for (; hi != pivot; --rite) + { + if (rite == hi) goto done; + if (!lp(r[rite], r[oldPivot])) continue; + ++pivot; + assert(!lp(r[pivot], r[oldPivot])); + r.swapAt(rite, pivot); + } + // Second loop: make left and pivot meet + outer: for (; rite > pivot; --rite) + { + if (!lp(r[rite], r[oldPivot])) continue; + while (rite > pivot) + { + ++pivot; + if (lp(r[oldPivot], r[pivot])) + { + r.swapAt(rite, pivot); + break; + } + } + } + +done: + r.swapAt(oldPivot, pivot); + return pivot; +} + +@safe unittest +{ + auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ]; + assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9); + a = randomArray; + if (a.length == 0) return; + expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2, + a.length / 2 + 1); +} + +version(unittest) +private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)( + size_t maxSize = 1000, + T minValue = 0, T maxValue = 255) +{ + import std.random : unpredictableSeed, Random, uniform; + import std.algorithm.iteration : map; + auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize); + return iota(0, size).map!(_ => uniform(minValue, maxValue)).array; +} + @safe unittest { import std.algorithm.comparison : max, min; import std.algorithm.iteration : reduce; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - //scope(failure) writeln(stderr, "Failure testing algorithm"); - //auto v = [ 25, 7, 9, 2, 0, 5, 21 ]; int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ]; ptrdiff_t n = 3; topN!("a < b")(v, n); @@ -2093,49 +3493,73 @@ void topN(alias less = "a < b", { import std.algorithm.comparison : max, min; import std.algorithm.iteration : reduce; - import std.random : uniform; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + import std.random : Random, uniform, unpredictableSeed; - int[] a = new int[uniform(1, 10000)]; - foreach (ref e; a) e = uniform(-1000, 1000); - auto k = uniform(0, a.length); - topN(a, k); - if (k > 0) + immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953, unpredictableSeed]; + foreach (s; seeds) { - auto left = reduce!max(a[0 .. k]); - assert(left <= a[k]); - } - if (k + 1 < a.length) - { - auto right = reduce!min(a[k + 1 .. $]); - assert(right >= a[k]); + auto r = Random(s); + + int[] a = new int[uniform(1, 10000, r)]; + foreach (ref e; a) e = uniform(-1000, 1000, r); + + auto k = uniform(0, a.length, r); + topN(a, k); + if (k > 0) + { + auto left = reduce!max(a[0 .. k]); + assert(left <= a[k]); + } + if (k + 1 < a.length) + { + auto right = reduce!min(a[k + 1 .. $]); + assert(right >= a[k]); + } } } +// bug 12987 +@safe unittest +{ + int[] a = [ 25, 7, 9, 2, 0, 5, 21 ]; + auto n = 4; + auto t = topN(a, n); + sort(t); + assert(t == [0, 2, 5, 7]); +} + /** Stores the smallest elements of the two ranges in the left-hand range. + +Params: + less = The predicate to sort by. + ss = The swapping strategy to use. + r1 = The first range. + r2 = The second range. */ -void topN(alias less = "a < b", +auto topN(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range1, Range2)(Range1 r1, Range2 r2) - if (isRandomAccessRange!(Range1) && hasLength!Range1 && - isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2)) +if (isRandomAccessRange!(Range1) && hasLength!Range1 && + isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && + hasLvalueElements!Range1 && hasLvalueElements!Range2) { import std.container : BinaryHeap; static assert(ss == SwapStrategy.unstable, "Stable topN not yet implemented"); - auto heap = BinaryHeap!Range1(r1); - for (; !r2.empty; r2.popFront()) + + auto heap = BinaryHeap!(Range1, less)(r1); + foreach (ref e; r2) { - heap.conditionalInsert(r2.front); + heap.conditionalSwap(e); } + + return r1; } /// -unittest +@system unittest { int[] a = [ 5, 7, 2, 6, 7 ]; int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; @@ -2144,17 +3568,109 @@ unittest assert(a == [0, 1, 2, 2, 3]); } +// bug 15421 +@system unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange; + import std.meta : AliasSeq; + + alias RandomRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) + ); + + alias ReferenceRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional)); + + foreach (T1; RandomRanges) + { + foreach (T2; ReferenceRanges) + { + import std.array; + + T1 A; + T2 B; + + A.reinit(); + B.reinit(); + + topN(A, B); + + // BUG(?): sort doesn't accept DummyRanges (needs Slicing and Length) + auto a = array(A); + auto b = array(B); + sort(a); + sort(b); + + assert(equal(a, [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ])); + assert(equal(b, [ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10 ])); + } + } +} + +// bug 15421 +@system unittest +{ + auto a = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ]; + auto b = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ]; + + topN(a, 4); + topN(b[0 .. 4], b[4 .. $]); + + sort(a[0 .. 4]); + sort(a[4 .. $]); + sort(b[0 .. 4]); + sort(b[4 .. $]); + + assert(a[0 .. 4] == b[0 .. 4]); + assert(a[4 .. $] == b[4 .. $]); + assert(a == b); +} + +// bug 12987 +@system unittest +{ + int[] a = [ 5, 7, 2, 6, 7 ]; + int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; + auto t = topN(a, b); + sort(t); + assert(t == [ 0, 1, 2, 2, 3 ]); +} + +// bug 15420 +@system unittest +{ + int[] a = [ 5, 7, 2, 6, 7 ]; + int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; + topN!"a > b"(a, b); + sort!"a > b"(a); + assert(a == [ 7, 7, 7, 6, 6 ]); +} + /** -Copies the top $(D n) elements of the input range $(D source) into the +Copies the top $(D n) elements of the +$(REF_ALTTEXT input range, isInputRange, std,range,primitives) $(D source) into the random-access range $(D target), where $(D n = target.length). Elements of $(D source) are not touched. If $(D sorted) is $(D true), the target is sorted. Otherwise, the target -respects the $(WEB en.wikipedia.org/wiki/Binary_heap, heap property). +respects the $(HTTP en.wikipedia.org/wiki/Binary_heap, heap property). + +Params: + less = The predicate to sort by. + source = The source range. + target = The target range. + sorted = Whether to sort the elements copied into `target`. + +Returns: The slice of `target` containing the copied elements. */ TRange topNCopy(alias less = "a < b", SRange, TRange) - (SRange source, TRange target, SortOutput sorted = SortOutput.no) - if (isInputRange!(SRange) && isRandomAccessRange!(TRange) - && hasLength!(TRange) && hasSlicing!(TRange)) + (SRange source, TRange target, SortOutput sorted = No.sortOutput) +if (isInputRange!(SRange) && isRandomAccessRange!(TRange) + && hasLength!(TRange) && hasSlicing!(TRange)) { import std.container : BinaryHeap; @@ -2162,7 +3678,7 @@ TRange topNCopy(alias less = "a < b", SRange, TRange) auto heap = BinaryHeap!(TRange, less)(target, 0); foreach (e; source) heap.conditionalInsert(e); auto result = target[0 .. heap.length]; - if (sorted == SortOutput.yes) + if (sorted == Yes.sortOutput) { while (!heap.empty) heap.removeFront(); } @@ -2170,20 +3686,20 @@ TRange topNCopy(alias less = "a < b", SRange, TRange) } /// -unittest +@system unittest { + import std.typecons : Yes; + int[] a = [ 10, 16, 2, 3, 1, 5, 0 ]; int[] b = new int[3]; - topNCopy(a, b, SortOutput.yes); + topNCopy(a, b, Yes.sortOutput); assert(b == [ 0, 1, 2 ]); } -unittest +@system unittest { import std.random : Random, unpredictableSeed, uniform, randomShuffle; - - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); + import std.typecons : Yes; auto r = Random(unpredictableSeed); ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)]; @@ -2191,7 +3707,7 @@ unittest randomShuffle(a, r); auto n = uniform(0, a.length, r); ptrdiff_t[] b = new ptrdiff_t[n]; - topNCopy!(binaryFun!("a < b"))(a, b, SortOutput.yes); + topNCopy!(binaryFun!("a < b"))(a, b, Yes.sortOutput); assert(isSorted!(binaryFun!("a < b"))(b)); } @@ -2205,10 +3721,12 @@ Params: less = A binary predicate that defines the ordering of range elements. Defaults to $(D a < b). ss = $(RED (Not implemented yet.)) Specify the swapping strategy. - r = A $(XREF2 range, isRandomAccessRange, random-access range) of elements - to make an index for. - index = A $(XREF2 range, isRandomAccessRange, random-access range) with - assignable elements to build the index in. The length of this range + r = A + $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) + of elements to make an index for. + index = A + $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) + with assignable elements to build the index in. The length of this range determines how many top elements to index in $(D r). This index range can either have integral elements, in which case the @@ -2227,11 +3745,11 @@ ignored. */ void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, RangeIndex) - (Range r, RangeIndex index, SortOutput sorted = SortOutput.no) - if (isRandomAccessRange!Range && - isRandomAccessRange!RangeIndex && - hasAssignableElements!RangeIndex && - isIntegral!(ElementType!(RangeIndex))) + (Range r, RangeIndex index, SortOutput sorted = No.sortOutput) +if (isRandomAccessRange!Range && + isRandomAccessRange!RangeIndex && + hasAssignableElements!RangeIndex && + isIntegral!(ElementType!(RangeIndex))) { static assert(ss == SwapStrategy.unstable, "Stable swap strategy not implemented yet."); @@ -2251,7 +3769,7 @@ void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, { heap.conditionalInsert(cast(ElementType!RangeIndex) i); } - if (sorted == SortOutput.yes) + if (sorted == Yes.sortOutput) { while (!heap.empty) heap.removeFront(); } @@ -2260,11 +3778,11 @@ void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, /// ditto void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, RangeIndex) - (Range r, RangeIndex index, SortOutput sorted = SortOutput.no) - if (isRandomAccessRange!Range && - isRandomAccessRange!RangeIndex && - hasAssignableElements!RangeIndex && - is(ElementType!(RangeIndex) == ElementType!(Range)*)) + (Range r, RangeIndex index, SortOutput sorted = No.sortOutput) +if (isRandomAccessRange!Range && + isRandomAccessRange!RangeIndex && + hasAssignableElements!RangeIndex && + is(ElementType!(RangeIndex) == ElementType!(Range)*)) { static assert(ss == SwapStrategy.unstable, "Stable swap strategy not implemented yet."); @@ -2282,50 +3800,225 @@ void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, { heap.conditionalInsert(&r[i]); } - if (sorted == SortOutput.yes) + if (sorted == Yes.sortOutput) { while (!heap.empty) heap.removeFront(); } } /// -unittest +@system unittest { + import std.typecons : Yes; + // Construct index to top 3 elements using numerical indices: int[] a = [ 10, 2, 7, 5, 8, 1 ]; int[] index = new int[3]; - topNIndex(a, index, SortOutput.yes); + topNIndex(a, index, Yes.sortOutput); assert(index == [5, 1, 3]); // because a[5]==1, a[1]==2, a[3]==5 // Construct index to top 3 elements using pointer indices: int*[] ptrIndex = new int*[3]; - topNIndex(a, ptrIndex, SortOutput.yes); + topNIndex(a, ptrIndex, Yes.sortOutput); assert(ptrIndex == [ &a[5], &a[1], &a[3] ]); } -unittest +@system unittest { import std.conv : text; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - { int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; int*[] b = new int*[5]; - topNIndex!("a > b")(a, b, SortOutput.yes); - //foreach (e; b) writeln(*e); + topNIndex!("a > b")(a, b, Yes.sortOutput); assert(b == [ &a[0], &a[2], &a[1], &a[6], &a[5]]); } { int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; auto b = new ubyte[5]; - topNIndex!("a > b")(a, b, SortOutput.yes); - //foreach (e; b) writeln(e, ":", a[e]); - assert(b == [ cast(ubyte) 0, cast(ubyte)2, cast(ubyte)1, cast(ubyte)6, cast(ubyte)5], text(b)); + topNIndex!("a > b")(a, b, Yes.sortOutput); + assert(b == [ cast(ubyte) 0, cast(ubyte) 2, cast(ubyte) 1, cast(ubyte) 6, cast(ubyte) 5], text(b)); } } +// medianOf +/* +Private for the time being. + +Computes the median of 2 to 5 arbitrary indexes in random-access range `r` +using hand-written specialized algorithms. The indexes must be distinct (if not, +behavior is implementation-defined). The function also partitions the elements +involved around the median, e.g. $(D medianOf(r, a, b, c)) not only fills `r[b]` +with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in +`r[a]` and the maximum in `r[c]`. + +Params: +less = The comparison predicate used, modeled as a + $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, strict weak ordering) + (irreflexive, antisymmetric, transitive, and implying a transitive equivalence). +flag = Used only for even values of `T.length`. If `No.leanRight`, the median +"leans left", meaning $(D medianOf(r, a, b, c, d)) puts the lower median of the +four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`. +Conversely, $(D median!("a < b", Yes.leanRight)(r, a, b, c, d)) puts the upper +median of the four in `r[c]`, the maximum in `r[d]`, and the two others in +`r[a]` and `r[b]`. +r = The range containing the indexes. +i = Two to five indexes inside `r`. +*/ +private void medianOf( + alias less = "a < b", + Flag!"leanRight" flag = No.leanRight, + Range, + Indexes...) + (Range r, Indexes i) +if (isRandomAccessRange!Range && hasLength!Range && + Indexes.length >= 2 && Indexes.length <= 5 && + allSatisfy!(isUnsigned, Indexes)) +{ + assert(r.length >= Indexes.length); + import std.functional : binaryFun; + alias lt = binaryFun!less; + enum k = Indexes.length; + import std.algorithm.mutation : swapAt; + + alias a = i[0]; + static assert(is(typeof(a) == size_t)); + static if (k >= 2) + { + alias b = i[1]; + static assert(is(typeof(b) == size_t)); + assert(a != b); + } + static if (k >= 3) + { + alias c = i[2]; + static assert(is(typeof(c) == size_t)); + assert(a != c && b != c); + } + static if (k >= 4) + { + alias d = i[3]; + static assert(is(typeof(d) == size_t)); + assert(a != d && b != d && c != d); + } + static if (k >= 5) + { + alias e = i[4]; + static assert(is(typeof(e) == size_t)); + assert(a != e && b != e && c != e && d != e); + } + + static if (k == 2) + { + if (lt(r[b], r[a])) r.swapAt(a, b); + } + else static if (k == 3) + { + if (lt(r[c], r[a])) // c < a + { + if (lt(r[a], r[b])) // c < a < b + { + r.swapAt(a, b); + r.swapAt(a, c); + } + else // c < a, b <= a + { + r.swapAt(a, c); + if (lt(r[b], r[a])) r.swapAt(a, b); + } + } + else // a <= c + { + if (lt(r[b], r[a])) // b < a <= c + { + r.swapAt(a, b); + } + else // a <= c, a <= b + { + if (lt(r[c], r[b])) r.swapAt(b, c); + } + } + assert(!lt(r[b], r[a])); + assert(!lt(r[c], r[b])); + } + else static if (k == 4) + { + static if (flag == No.leanRight) + { + // Eliminate the rightmost from the competition + if (lt(r[d], r[c])) r.swapAt(c, d); // c <= d + if (lt(r[d], r[b])) r.swapAt(b, d); // b <= d + medianOf!lt(r, a, b, c); + } + else + { + // Eliminate the leftmost from the competition + if (lt(r[b], r[a])) r.swapAt(a, b); // a <= b + if (lt(r[c], r[a])) r.swapAt(a, c); // a <= c + medianOf!lt(r, b, c, d); + } + } + else static if (k == 5) + { + // Credit: Teppo Niinimäki + version(unittest) scope(success) + { + assert(!lt(r[c], r[a])); + assert(!lt(r[c], r[b])); + assert(!lt(r[d], r[c])); + assert(!lt(r[e], r[c])); + } + + if (lt(r[c], r[a])) r.swapAt(a, c); + if (lt(r[d], r[b])) r.swapAt(b, d); + if (lt(r[d], r[c])) + { + r.swapAt(c, d); + r.swapAt(a, b); + } + if (lt(r[e], r[b])) r.swapAt(b, e); + if (lt(r[e], r[c])) + { + r.swapAt(c, e); + if (lt(r[c], r[a])) r.swapAt(a, c); + } + else + { + if (lt(r[c], r[b])) r.swapAt(b, c); + } + } +} + +@safe unittest +{ + // Verify medianOf for all permutations of [1, 2, 2, 3, 4]. + int[5] data = [1, 2, 2, 3, 4]; + do + { + int[5] a = data; + medianOf(a[], size_t(0), size_t(1)); + assert(a[0] <= a[1]); + + a[] = data[]; + medianOf(a[], size_t(0), size_t(1), size_t(2)); + assert(ordered(a[0], a[1], a[2])); + + a[] = data[]; + medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3)); + assert(a[0] <= a[1] && a[1] <= a[2] && a[1] <= a[3]); + + a[] = data[]; + medianOf!("a < b", Yes.leanRight)(a[], size_t(0), size_t(1), + size_t(2), size_t(3)); + assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3]); + + a[] = data[]; + medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3), size_t(4)); + assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3] && a[2] <= a[4]); + } + while (nextPermutation(data[])); +} + // nextPermutation /** * Permutes $(D range) in-place to the next lexicographically greater @@ -2352,16 +4045,24 @@ do // proceed to the next permutation of the array. } while (nextPermutation(a)); ---- + * Params: + * less = The ordering to be used to determine lexicographical ordering of the + * permutations. + * range = The range to permute. + * * Returns: false if the range was lexicographically the greatest, in which * case the range is reversed back to the lexicographically smallest * permutation; otherwise returns true. + * See_Also: + * $(REF permutations, std,algorithm,iteration). */ bool nextPermutation(alias less="a < b", BidirectionalRange) (BidirectionalRange range) - if (isBidirectionalRange!BidirectionalRange && - hasSwappableElements!BidirectionalRange) +if (isBidirectionalRange!BidirectionalRange && + hasSwappableElements!BidirectionalRange) { - import std.algorithm : find, reverse, swap; // FIXME + import std.algorithm.mutation : reverse, swap; + import std.algorithm.searching : find; import std.range : retro, takeExactly; // Ranges of 0 or 1 element have no distinct permutations. if (range.empty) return false; @@ -2377,7 +4078,8 @@ bool nextPermutation(alias less="a < b", BidirectionalRange) break; } - if (i.empty) { + if (i.empty) + { // Entire range is decreasing: it's lexicographically the greatest. So // wrap it around. range.reverse(); @@ -2602,16 +4304,22 @@ do * permutations may fail to be generated. When the range has non-unique * elements, you should use $(MYREF nextPermutation) instead. * + * Params: + * less = The ordering to be used to determine lexicographical ordering of the + * permutations. + * range = The range to permute. + * * Returns: false if the range was lexicographically the greatest, in which * case the range is reversed back to the lexicographically smallest * permutation; otherwise returns true. */ bool nextEvenPermutation(alias less="a < b", BidirectionalRange) (BidirectionalRange range) - if (isBidirectionalRange!BidirectionalRange && - hasSwappableElements!BidirectionalRange) +if (isBidirectionalRange!BidirectionalRange && + hasSwappableElements!BidirectionalRange) { - import std.algorithm : find, reverse, swap; // FIXME + import std.algorithm.mutation : reverse, swap; + import std.algorithm.searching : find; import std.range : retro, takeExactly; // Ranges of 0 or 1 element have no distinct permutations. if (range.empty) return false; @@ -2654,7 +4362,7 @@ bool nextEvenPermutation(alias less="a < b", BidirectionalRange) reverse(takeExactly(retro(range), n)); if ((n / 2) % 2 == 1) oddParity = !oddParity; - } while(oddParity); + } while (oddParity); return ret; } @@ -2754,4 +4462,3 @@ shapes. Here's a non-trivial example: } assert(n == 60); } - diff --git a/std/array.d b/std/array.d index 95d926f8efb..4d923776be4 100644 --- a/std/array.d +++ b/std/array.d @@ -4,73 +4,82 @@ Functions and types that manipulate built-in arrays and associative arrays. This module provides all kinds of functions to create, manipulate or convert arrays: +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , $(TR $(TH Function Name) $(TH Description) ) - $(TR $(TD $(D $(LREF _array))) + $(TR $(TD $(LREF _array)) $(TD Returns a copy of the input in a newly allocated dynamic _array. )) - $(TR $(TD $(D $(LREF appender))) - $(TD Returns a new Appender initialized with a given _array. + $(TR $(TD $(LREF appender)) + $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given _array. )) - $(TR $(TD $(D $(LREF assocArray))) + $(TR $(TD $(LREF assocArray)) $(TD Returns a newly allocated associative _array from a range of key/value tuples. )) - $(TR $(TD $(D $(LREF byPair))) + $(TR $(TD $(LREF byPair)) $(TD Construct a range iterating over an associative _array by key/value tuples. )) - $(TR $(TD $(D $(LREF insertInPlace))) + $(TR $(TD $(LREF insertInPlace)) $(TD Inserts into an existing _array at a given position. )) - $(TR $(TD $(D $(LREF join))) + $(TR $(TD $(LREF join)) $(TD Concatenates a range of ranges into one _array. )) - $(TR $(TD $(D $(LREF minimallyInitializedArray))) + $(TR $(TD $(LREF minimallyInitializedArray)) $(TD Returns a new _array of type $(D T). )) - $(TR $(TD $(D $(LREF replace))) + $(TR $(TD $(LREF replace)) $(TD Returns a new _array with all occurrences of a certain subrange replaced. )) - $(TR $(TD $(D $(LREF replaceFirst))) + $(TR $(TD $(LREF replaceFirst)) $(TD Returns a new _array with the first occurrence of a certain subrange replaced. )) - $(TR $(TD $(D $(LREF replaceInPlace))) + $(TR $(TD $(LREF replaceInPlace)) $(TD Replaces all occurrences of a certain subrange and puts the result into a given _array. )) - $(TR $(TD $(D $(LREF replaceInto))) + $(TR $(TD $(LREF replaceInto)) $(TD Replaces all occurrences of a certain subrange and puts the result into an output range. )) - $(TR $(TD $(D $(LREF replaceLast))) + $(TR $(TD $(LREF replaceLast)) $(TD Returns a new _array with the last occurrence of a certain subrange replaced. )) - $(TR $(TD $(D $(LREF replaceSlice))) + $(TR $(TD $(LREF replaceSlice)) $(TD Returns a new _array with a given slice replaced. )) - $(TR $(TD $(D $(LREF replicate))) + $(TR $(TD $(LREF replicate)) $(TD Creates a new _array out of several copies of an input _array or range. )) - $(TR $(TD $(D $(LREF split))) + $(TR $(TD $(LREF sameHead)) + $(TD Checks if the initial segments of two arrays refer to the same + place in memory. + )) + $(TR $(TD $(LREF sameTail)) + $(TD Checks if the final segments of two arrays refer to the same place + in memory. + )) + $(TR $(TD $(LREF split)) $(TD Eagerly split a range or string into an _array. )) - $(TR $(TD $(D $(LREF uninitializedArray))) + $(TR $(TD $(LREF uninitializedArray)) $(TD Returns a new _array of type $(D T) without initializing its elements. )) ) Copyright: Copyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.org, Andrei Alexandrescu) and Jonathan M Davis +Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis Source: $(PHOBOSSRC std/_array.d) */ module std.array; +import std.meta; import std.traits; -import std.typetuple; import std.functional; -static import std.algorithm; // FIXME, remove with alias of splitter +static import std.algorithm.iteration; // FIXME, remove with alias of splitter import std.range.primitives; public import std.range.primitives : save, empty, popFront, popBack, front, back; @@ -117,7 +126,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) emplaceRef!E(result[i], e); ++i; } - return (() @trusted => cast(E[])result)(); + return (() @trusted => cast(E[]) result)(); } else { @@ -139,7 +148,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) @safe pure nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; struct Foo { int a; @@ -150,11 +159,11 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; struct Foo { int a; - auto opAssign(Foo foo) + void opAssign(Foo foo) { assert(0); } @@ -167,7 +176,7 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)])); } -unittest +@safe unittest { // Issue 12315 static struct Bug12315 { immutable int i; } @@ -175,7 +184,7 @@ unittest static assert(bug12315[0].i == 123456789); } -unittest +@safe unittest { import std.range; static struct S{int* p;} @@ -184,21 +193,31 @@ unittest /** Convert a narrow string to an array type that fully supports random access. -This is handled as a special case and always returns a $(D dchar[]), -$(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of -the input. +This is handled as a special case and always returns an array of `dchar` + +Params: + str = `isNarrowString` to be converted to an array of `dchar` +Returns: + a $(D dchar[]), $(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of + the input. */ -ElementType!String[] array(String)(String str) if (isNarrowString!String) +@trusted ElementType!String[] array(String)(scope String str) +if (isNarrowString!String) { import std.utf : toUTF32; + /* This function is @trusted because the following cast + * is unsafe - converting a dstring[] to a dchar[], for example. + * Our special knowledge of toUTF32 is needed to know the cast is safe. + */ return cast(typeof(return)) str.toUTF32; } -unittest +@system unittest { + // @system due to array!string import std.conv : to; - static struct TestArray { int x; string toString() { return to!string(x); } } + static struct TestArray { int x; string toString() @safe { return to!string(x); } } static struct OpAssign { @@ -212,13 +231,13 @@ unittest static struct OpApply { - int opApply(int delegate(ref int) dg) + int opApply(scope int delegate(ref int) dg) { int res; - foreach(i; 0..10) + foreach (i; 0 .. 10) { res = dg(i); - if(res) break; + if (res) break; } return res; @@ -236,7 +255,7 @@ unittest { int x; this(int y) { x = y; } - override string toString() const { return to!string(x); } + override string toString() const @safe { return to!string(x); } } auto c = array([new C(1), new C(2)][]); //writeln(c); @@ -255,7 +274,7 @@ unittest } //Bug# 8233 -unittest +@safe unittest { assert(array("hello world"d) == "hello world"d); immutable a = [1, 2, 3, 4, 5]; @@ -275,14 +294,14 @@ unittest int i; } - foreach(T; TypeTuple!(S, const S, immutable S)) + foreach (T; AliasSeq!(S, const S, immutable S)) { auto arr = [T(1), T(2), T(3), T(4)]; assert(array(arr) == arr); } } -unittest +@safe unittest { //9824 static struct S @@ -295,10 +314,10 @@ unittest } // Bugzilla 10220 -unittest +@safe unittest { import std.exception; - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : repeat; static struct S @@ -315,7 +334,7 @@ unittest }); } -unittest +@safe unittest { //Turn down infinity: static assert(!is(typeof( @@ -325,14 +344,21 @@ unittest /** Returns a newly allocated associative _array from a range of key/value tuples. -Params: r = An input range of tuples of keys and values. + +Params: + r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + of tuples of keys and values. Returns: A newly allocated associative array out of elements of the input -range, which must be a range of tuples (Key, Value). -See_Also: $(XREF typecons, Tuple) +range, which must be a range of tuples (Key, Value). Returns a null associative +array reference when given an empty range. +Duplicates: Associative arrays have unique keys. If r contains duplicate keys, +then the result will contain the value of the last pair for that key in r. + +See_Also: $(REF Tuple, std,typecons), $(REF zip, std,range) */ auto assocArray(Range)(Range r) - if (isInputRange!Range) +if (isInputRange!Range) { import std.typecons : isTuple; @@ -350,11 +376,11 @@ auto assocArray(Range)(Range r) } /// -/*@safe*/ pure /*nothrow*/ unittest +@safe pure /*nothrow*/ unittest { import std.range; import std.typecons; - auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); + auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap assert(is(typeof(a) == string[int])); assert(a == [0:"a", 1:"b", 2:"c"]); @@ -364,21 +390,21 @@ auto assocArray(Range)(Range r) } // @@@11053@@@ - Cannot be version(unittest) - recursive instantiation error -unittest +@safe unittest { import std.typecons; static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray())); static assert(!__traits(compiles, [ tuple("foo") ].assocArray())); - static assert( __traits(compiles, [ tuple("foo", "bar") ].assocArray())); + assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]); } // Issue 13909 -unittest +@safe unittest { import std.typecons; auto a = [tuple!(const string, string)("foo", "bar")]; auto b = [tuple!(string, const string)("foo", "bar")]; - static assert( __traits(compiles, assocArray(a))); + assert(assocArray(a) == [cast(const(string)) "foo": "bar"]); static assert(!__traits(compiles, assocArray(b))); } @@ -387,22 +413,22 @@ Construct a range iterating over an associative array by key/value tuples. Params: aa = The associative array to iterate over. -Returns: A forward range of Tuple's of key and value pairs from the given -associative array. +Returns: A $(REF_ALTTEXT forward range, isForwardRange, std,_range,primitives) +of Tuple's of key and value pairs from the given associative array. */ auto byPair(Key, Value)(Value[Key] aa) { import std.typecons : tuple; - import std.algorithm : map; + import std.algorithm.iteration : map; return aa.byKeyValue.map!(pair => tuple(pair.key, pair.value)); } /// -unittest +@system unittest { import std.typecons : tuple, Tuple; - import std.algorithm : sort; + import std.algorithm.sorting : sort; auto aa = ["a": 1, "b": 2, "c": 3]; Tuple!(string, int)[] pairs; @@ -423,7 +449,7 @@ unittest ]); } -unittest +@system unittest { import std.typecons : tuple, Tuple; @@ -465,7 +491,7 @@ version(unittest) // Returns the number of dimensions in an array T. private template nDimensions(T) { - static if(isArray!T) + static if (isArray!T) { enum nDimensions = 1 + nDimensions!(typeof(T.init[0])); } @@ -564,13 +590,13 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I)) @safe pure nothrow unittest { - cast(void)minimallyInitializedArray!(int[][][][][])(); + cast(void) minimallyInitializedArray!(int[][][][][])(); double[] arr = minimallyInitializedArray!(double[])(100); assert(arr.length == 100); double[][] matrix = minimallyInitializedArray!(double[][])(42); assert(matrix.length == 42); - foreach(elem; matrix) + foreach (elem; matrix) { assert(elem.ptr is null); } @@ -587,7 +613,7 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow static if (I.length != 0) { - static assert (is(I[0] == size_t)); + static assert(is(I[0] == size_t)); alias size = sizes[0]; } @@ -616,24 +642,30 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow else { import core.stdc.string : memset; - import core.memory; - auto ptr = cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E); + import core.memory : GC; + + import core.checkedint : mulu; + bool overflow; + const nbytes = mulu(size, E.sizeof, overflow); + if (overflow) assert(0); + + auto ptr = cast(E*) GC.malloc(nbytes, blockAttribute!E); static if (minimallyInitialized && hasIndirections!E) - memset(ptr, 0, size * E.sizeof); + memset(ptr, 0, nbytes); ret = ptr[0 .. size]; } } else static if (I.length > 1) { ret = arrayAllocImpl!(false, E[])(size); - foreach(ref elem; ret) + foreach (ref elem; ret) elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1..$]); } return ret; } -nothrow pure unittest +@safe nothrow pure unittest { auto s1 = uninitializedArray!(int[])(); auto s2 = minimallyInitializedArray!(int[])(); @@ -641,7 +673,7 @@ nothrow pure unittest assert(s2.length == 0); } -nothrow pure unittest //@@@9803@@@ +@safe nothrow pure unittest //@@@9803@@@ { auto a = minimallyInitializedArray!(int*[])(1); assert(a[0] == null); @@ -651,7 +683,7 @@ nothrow pure unittest //@@@9803@@@ assert(c[0][0] == null); } -unittest //@@@10637@@@ +@safe unittest //@@@10637@@@ { static struct S { @@ -676,7 +708,7 @@ unittest //@@@10637@@@ enum b = minimallyInitializedArray!(S[])(1); } -nothrow unittest +@safe nothrow unittest { static struct S1 { @@ -709,12 +741,10 @@ equal), $(D overlap) only compares the pointers in the ranges, not the values referred by them. If $(D r1) and $(D r2) have an overlapping slice, returns that slice. Otherwise, returns the null slice. */ -inout(T)[] overlap(T)(inout(T)[] r1, inout(T)[] r2) @trusted pure nothrow +auto overlap(T, U)(T[] r1, U[] r2) @trusted pure nothrow +if (is(typeof(r1.ptr < r2.ptr) == bool)) { - alias U = inout(T); - static U* max(U* a, U* b) nothrow { return a > b ? a : b; } - static U* min(U* a, U* b) nothrow { return a < b ? a : b; } - + import std.algorithm.comparison : min, max; auto b = max(r1.ptr, r2.ptr); auto e = min(r1.ptr + r1.length, r2.ptr + r2.length); return b < e ? b[0 .. e - b] : null; @@ -731,7 +761,7 @@ inout(T)[] overlap(T)(inout(T)[] r1, inout(T)[] r2) @trusted pure nothrow assert(overlap(a, b).empty); } -/*@safe nothrow*/ unittest +@safe /*nothrow*/ unittest { static void test(L, R)(L l, R r) { @@ -778,124 +808,9 @@ inout(T)[] overlap(T)(inout(T)[] r1, inout(T)[] r2) @trusted pure nothrow static assert(isRandomAccessRange!Wrapper); } -/+ -Commented out until the insert which has been deprecated has been removed. -I'd love to just remove it in favor of insertInPlace, but then code would then -use this version of insert and silently break. So, it's here so that it can -be used once insert has not only been deprecated but removed, but until then, -it's commented out. - -/++ - Creates a new array which is a copy of $(D array) with $(D stuff) (which - must be an input range or a single item) inserted at position $(D pos). - - Examples: - -------------------- - int[] a = [ 1, 2, 3, 4 ]; - auto b = a.insert(2, [ 1, 2 ]); - assert(a == [ 1, 2, 3, 4 ]); - assert(b == [ 1, 2, 1, 2, 3, 4 ]); - -------------------- - +/ -T[] insert(T, Range)(T[] array, size_t pos, Range stuff) - if(isInputRange!Range && - (is(ElementType!Range : T) || - isSomeString!(T[]) && is(ElementType!Range : dchar))) -{ - static if(hasLength!Range && is(ElementEncodingType!Range : T)) - { - import std.algorithm : copy; - auto retval = new Unqual!(T)[](array.length + stuff.length); - retval[0 .. pos] = array[0 .. pos]; - copy(stuff, retval[pos .. pos + stuff.length]); - retval[pos + stuff.length .. $] = array[pos .. $]; - return cast(T[])retval; - } - else - { - auto app = appender!(T[])(); - app.put(array[0 .. pos]); - app.put(stuff); - app.put(array[pos .. $]); - return app.data; - } -} - -/++ Ditto +/ -T[] insert(T)(T[] array, size_t pos, T stuff) -{ - auto retval = new T[](array.length + 1); - retval[0 .. pos] = array[0 .. pos]; - retval[pos] = stuff; - retval[pos + 1 .. $] = array[pos .. $]; - return retval; -} - -//Verify Example. -unittest -{ - int[] a = [ 1, 2, 3, 4 ]; - auto b = a.insert(2, [ 1, 2 ]); - assert(a == [ 1, 2, 3, 4 ]); - assert(b == [ 1, 2, 1, 2, 3, 4 ]); -} - -unittest -{ - import core.exception; - import std.conv : to; - import std.exception; - import std.algorithm; - - auto a = [1, 2, 3, 4]; - assert(a.insert(0, [6, 7]) == [6, 7, 1, 2, 3, 4]); - assert(a.insert(2, [6, 7]) == [1, 2, 6, 7, 3, 4]); - assert(a.insert(a.length, [6, 7]) == [1, 2, 3, 4, 6, 7]); - - assert(a.insert(0, filter!"true"([6, 7])) == [6, 7, 1, 2, 3, 4]); - assert(a.insert(2, filter!"true"([6, 7])) == [1, 2, 6, 7, 3, 4]); - assert(a.insert(a.length, filter!"true"([6, 7])) == [1, 2, 3, 4, 6, 7]); - - assert(a.insert(0, 22) == [22, 1, 2, 3, 4]); - assert(a.insert(2, 22) == [1, 2, 22, 3, 4]); - assert(a.insert(a.length, 22) == [1, 2, 3, 4, 22]); - assert(a == [1, 2, 3, 4]); - - auto testStr(T, U)(string file = __FILE__, size_t line = __LINE__) - { - - auto l = to!T("hello"); - auto r = to!U(" world"); - - enforce(insert(l, 0, r) == " worldhello", - new AssertError("testStr failure 1", file, line)); - enforce(insert(l, 3, r) == "hel worldlo", - new AssertError("testStr failure 2", file, line)); - enforce(insert(l, l.length, r) == "hello world", - new AssertError("testStr failure 3", file, line)); - enforce(insert(l, 0, filter!"true"(r)) == " worldhello", - new AssertError("testStr failure 4", file, line)); - enforce(insert(l, 3, filter!"true"(r)) == "hel worldlo", - new AssertError("testStr failure 5", file, line)); - enforce(insert(l, l.length, filter!"true"(r)) == "hello world", - new AssertError("testStr failure 6", file, line)); - } - - testStr!(string, string)(); - testStr!(string, wstring)(); - testStr!(string, dstring)(); - testStr!(wstring, string)(); - testStr!(wstring, wstring)(); - testStr!(wstring, dstring)(); - testStr!(dstring, string)(); - testStr!(dstring, wstring)(); - testStr!(dstring, dstring)(); -} -+/ - private void copyBackwards(T)(T[] src, T[] dest) { - import core.stdc.string; + import core.stdc.string : memmove; assert(src.length == dest.length); @@ -921,12 +836,17 @@ private void copyBackwards(T)(T[] src, T[] dest) Inserts $(D stuff) (which must be an input range or any number of implicitly convertible items) in $(D array) at position $(D pos). + Params: + array = The array that $(D stuff) will be inserted into. + pos = The position in $(D array) to insert the $(D stuff). + stuff = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives), + or any number of implicitly convertible items to insert into $(D array). +/ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) - if(!isSomeString!(T[]) - && allSatisfy!(isInputRangeOrConvertible!T, U) && U.length > 0) +if (!isSomeString!(T[]) + && allSatisfy!(isInputRangeOrConvertible!T, U) && U.length > 0) { - static if(allSatisfy!(isInputRangeWithLengthOrConvertible!T, U)) + static if (allSatisfy!(isInputRangeWithLengthOrConvertible!T, U)) { import std.conv : emplaceRef; @@ -946,7 +866,7 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) // Takes arguments array, pos, stuff // Spread apart array[] at pos by moving elements - (() @trusted { copyBackwards(array[pos..oldLen], array[pos+to_insert..$]); })(); + (() @trusted { copyBackwards(array[pos .. oldLen], array[pos+to_insert..$]); })(); // Initialize array[pos .. pos+to_insert] with stuff[] auto j = 0; @@ -982,9 +902,9 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) /// Ditto void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) - if(isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U)) +if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U)) { - static if(is(Unqual!T == T) + static if (is(Unqual!T == T) && allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U)) { import std.utf : codeLength; @@ -992,7 +912,7 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) //helper function: re-encode dchar to Ts and store at *ptr static T* putDChar(T* ptr, dchar ch) { - static if(is(T == dchar)) + static if (is(T == dchar)) { *ptr++ = ch; return ptr; @@ -1001,10 +921,10 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) { import std.utf : encode; T[dchar.sizeof/T.sizeof] buf; - size_t len = encode(buf, ch); - final switch(len) + immutable len = encode(buf, ch); + final switch (len) { - static if(T.sizeof == char.sizeof) + static if (T.sizeof == char.sizeof) { case 4: ptr[3] = buf[3]; @@ -1023,7 +943,6 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) return ptr; } } - immutable oldLen = array.length; size_t to_insert = 0; //count up the number of *codeunits* to insert foreach (i, E; U) @@ -1033,7 +952,7 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) @trusted static void moveToRight(T[] arr, size_t gap) { static assert(!hasElaborateCopyConstructor!T); - import core.stdc.string; + import core.stdc.string : memmove; if (__ctfe) { for (size_t i = arr.length - gap; i; --i) @@ -1046,7 +965,7 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) auto ptr = array.ptr + pos; foreach (i, E; U) { - static if(is(E : dchar)) + static if (is(E : dchar)) { ptr = putDChar(ptr, stuff[i]); } @@ -1062,7 +981,7 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) { // immutable/const, just construct a new array auto app = appender!(T[])(); - app.put(array[0..pos]); + app.put(array[0 .. pos]); foreach (i, E; U) app.put(stuff[i]); app.put(array[pos..$]); @@ -1109,32 +1028,34 @@ private template isInputRangeOrConvertible(E) } } -unittest +@system unittest { + // @system due to insertInPlace + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; import core.exception; import std.conv : to; import std.exception; - import std.algorithm; bool test(T, U, V)(T orig, size_t pos, U toInsert, V result, string file = __FILE__, size_t line = __LINE__) { { - static if(is(T == typeof(T.init.dup))) + static if (is(T == typeof(T.init.dup))) auto a = orig.dup; else auto a = orig.idup; a.insertInPlace(pos, toInsert); - if(!std.algorithm.equal(a, result)) + if (!equal(a, result)) return false; } - static if(isInputRange!U) + static if (isInputRange!U) { orig.insertInPlace(pos, filter!"true"(toInsert)); - return std.algorithm.equal(orig, result); + return equal(orig, result); } else return true; @@ -1149,7 +1070,7 @@ unittest assert(test([1, 2, 3, 4], 2, 23, [1, 2, 23, 3, 4])); assert(test([1, 2, 3, 4], 4, 24, [1, 2, 3, 4, 24])); - auto testStr(T, U)(string file = __FILE__, size_t line = __LINE__) + void testStr(T, U)(string file = __FILE__, size_t line = __LINE__) { auto l = to!T("hello"); @@ -1163,10 +1084,10 @@ unittest new AssertError("testStr failure 3", file, line)); } - foreach (T; TypeTuple!(char, wchar, dchar, + foreach (T; AliasSeq!(char, wchar, dchar, immutable(char), immutable(wchar), immutable(dchar))) { - foreach (U; TypeTuple!(char, wchar, dchar, + foreach (U; AliasSeq!(char, wchar, dchar, immutable(char), immutable(wchar), immutable(dchar))) { testStr!(T[], U[])(); @@ -1177,14 +1098,14 @@ unittest // variadic version bool testVar(T, U...)(T orig, size_t pos, U args) { - static if(is(T == typeof(T.init.dup))) + static if (is(T == typeof(T.init.dup))) auto a = orig.dup; else auto a = orig.idup; auto result = args[$-1]; a.insertInPlace(pos, args[0..$-1]); - if (!std.algorithm.equal(a, result)) + if (!equal(a, result)) return false; return true; } @@ -1203,9 +1124,9 @@ unittest "flip_xyz\U00010143_abc__flop")); } -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; // insertInPlace interop with postblit static struct Int { @@ -1260,7 +1181,7 @@ unittest }); } -unittest // bugzilla 6874 +@system unittest // bugzilla 6874 { import core.memory; // allocate some space @@ -1290,6 +1211,15 @@ pure nothrow bool sameHead(T)(in T[] lhs, in T[] rhs) return lhs.ptr == rhs.ptr; } +/// +@safe pure nothrow unittest +{ + auto a = [1, 2, 3, 4, 5]; + auto b = a[0 .. 2]; + + assert(a.sameHead(b)); +} + /++ Returns whether the $(D back)s of $(D lhs) and $(D rhs) both refer to the @@ -1302,9 +1232,18 @@ pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs) return lhs.ptr + lhs.length == rhs.ptr + rhs.length; } +/// @safe pure nothrow unittest { - foreach(T; TypeTuple!(int[], const(int)[], immutable(int)[], const int[], immutable int[])) + auto a = [1, 2, 3, 4, 5]; + auto b = a[3..$]; + + assert(a.sameTail(b)); +} + +@safe pure nothrow unittest +{ + foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[])) { T a = [1, 2, 3, 4, 5]; T b = a; @@ -1330,12 +1269,21 @@ pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs) } } -/******************************************** -Returns an array that consists of $(D s) (which must be an input -range) repeated $(D n) times. This function allocates, fills, and -returns a new array. For a lazy version, refer to $(XREF range, repeat). +/** +Params: + s = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + or a dynamic array + n = number of times to repeat `s` + +Returns: + An array that consists of `s` repeated `n` times. This function allocates, fills, and + returns a new array. + +See_Also: + For a lazy version, refer to $(REF repeat, std,range). */ -ElementEncodingType!S[] replicate(S)(S s, size_t n) if (isDynamicArray!S) +ElementEncodingType!S[] replicate(S)(S s, size_t n) +if (isDynamicArray!S) { alias RetType = ElementEncodingType!S[]; @@ -1355,7 +1303,7 @@ ElementEncodingType!S[] replicate(S)(S s, size_t n) if (isDynamicArray!S) r[i .. i + len] = s[]; } } - return cast(RetType) r; + return r; } /// ditto @@ -1366,13 +1314,32 @@ if (isInputRange!S && !isDynamicArray!S) return join(std.range.repeat(s, n)); } -unittest + +/// +@safe unittest +{ + auto a = "abc"; + auto s = replicate(a, 3); + + assert(s == "abcabcabc"); + + auto b = [1, 2, 3]; + auto c = replicate(b, 3); + + assert(c == [1, 2, 3, 1, 2, 3, 1, 2, 3]); + + auto d = replicate(b, 0); + + assert(d == []); +} + +@safe unittest { import std.conv : to; debug(std_array) printf("array.replicate.unittest\n"); - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) { S s; immutable S t = "abc"; @@ -1393,10 +1360,14 @@ delimiter. Runs of whitespace are merged together (no empty words are produced). $(D @safe), $(D pure) and $(D CTFE)-able. +Params: + s = the string to split + See_Also: -$(XREF algorithm, splitter) for a version that splits using any separator. +$(REF splitter, std,algorithm,iteration) for a version that splits using any +separator. -$(XREF regex, splitter) for a version that splits using a regular +$(REF splitter, std,regex) for a version that splits using a regular expression defined separator. +/ S[] split(S)(S s) @safe pure @@ -1431,7 +1402,7 @@ if (isSomeString!S) return result; } -unittest +@safe unittest { import std.conv : to; import std.format; @@ -1440,7 +1411,7 @@ unittest static auto makeEntry(S)(string l, string[] r) {return tuple(l.to!S(), r.to!(S[])());} - foreach (S; TypeTuple!(string, wstring, dstring,)) + foreach (S; AliasSeq!(string, wstring, dstring,)) { auto entries = [ @@ -1463,7 +1434,7 @@ unittest assert(split(s) == ["peter", "paul", "jerry"]); } -unittest //safety, purity, ctfe ... +@safe unittest //purity, ctfe ... { import std.exception; void dg() @safe pure { @@ -1475,27 +1446,36 @@ unittest //safety, purity, ctfe ... assertCTFEable!dg; } -/++ -Alias for $(XREF algorithm, _splitter). - +/ +/// +@safe unittest +{ + assert(split("hello world") == ["hello","world"]); + assert(split("192.168.0.1", ".") == ["192", "168", "0", "1"]); + + auto a = split([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [2, 3]); + assert(a == [[1], [4, 5, 1], [4, 5]]); +} + +// Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ deprecated("Please use std.algorithm.iteration.splitter instead.") alias splitter = std.algorithm.iteration.splitter; /++ Eagerly splits $(D range) into an array, using $(D sep) as the delimiter. - The range must be a $(XREF2 range, isForwardRange, forward range). - The separator can be a value of the same type as the elements in $(D range) or - it can be another forward range. + The _range must be a + $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives). + The separator can be a value of the same type as the elements in $(D range) + or it can be another forward _range. - Examples: + Example: If $(D range) is a $(D string), $(D sep) can be a $(D char) or another $(D string). The return type will be an array of strings. If $(D range) is an $(D int) array, $(D sep) can be an $(D int) or another $(D int) array. The return type will be an array of $(D int) arrays. Params: - range = a forward range. + range = a forward _range. sep = a value of the same type as the elements of $(D range) or another forward range. @@ -1503,36 +1483,39 @@ alias splitter = std.algorithm.iteration.splitter; An array containing the divided parts of $(D range). See_Also: - $(XREF algorithm, splitter) for the lazy version of this function. + $(REF splitter, std,algorithm,iteration) for the lazy version of this + function. +/ auto split(Range, Separator)(Range range, Separator sep) if (isForwardRange!Range && is(typeof(ElementType!Range.init == Separator.init))) { - import std.algorithm : splitter; + import std.algorithm.iteration : splitter; return range.splitter(sep).array; } ///ditto auto split(Range, Separator)(Range range, Separator sep) -if (isForwardRange!Range && isForwardRange!Separator && is(typeof(ElementType!Range.init == ElementType!Separator.init))) +if ( + isForwardRange!Range && isForwardRange!Separator + && is(typeof(ElementType!Range.init == ElementType!Separator.init))) { - import std.algorithm : splitter; + import std.algorithm.iteration : splitter; return range.splitter(sep).array; } ///ditto auto split(alias isTerminator, Range)(Range range) if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front)))) { - import std.algorithm : splitter; + import std.algorithm.iteration : splitter; return range.splitter!isTerminator.array; } -unittest +@safe unittest { import std.conv; - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; debug(std_array) printf("array.split\n"); - foreach (S; TypeTuple!(string, wstring, dstring, + foreach (S; AliasSeq!(string, wstring, dstring, immutable(string), immutable(wstring), immutable(dstring), char[], wchar[], dchar[], const(char)[], const(wchar)[], const(dchar)[], @@ -1591,24 +1574,25 @@ unittest private enum bool hasCheapIteration(R) = isArray!R; /++ - Concatenates all of the ranges in $(D ror) together into one array using - $(D sep) as the separator if present. + Eagerly concatenates all of the ranges in `ror` together (with the GC) + into one array using `sep` as the separator if present. Params: - ror = Range of Ranges of Elements - sep = Range of Elements + ror = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + of input ranges + sep = An input range, or a single element, to join the ranges on Returns: - an allocated array of Elements + An array of elements See_Also: - $(XREF algorithm, joiner) + For a lazy version, see $(REF joiner, std,algorithm,iteration) +/ -ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) - if(isInputRange!RoR && - isInputRange!(Unqual!(ElementType!RoR)) && - isInputRange!R && - is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R))) +ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, scope R sep) +if (isInputRange!RoR && + isInputRange!(Unqual!(ElementType!RoR)) && + isInputRange!R && + is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R))) { alias RetType = typeof(return); alias RetTypeElement = Unqual!(ElementEncodingType!RetType); @@ -1631,12 +1615,12 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) else alias sepArr = sep; - static if(hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) + static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) { import std.conv : emplaceRef; size_t length; // length of result array size_t rorLength; // length of range ror - foreach(r; ror.save) + foreach (r; ror.save) { length += r.length; ++rorLength; @@ -1647,14 +1631,14 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))(); size_t len; - foreach(e; ror.front) + foreach (e; ror.front) emplaceRef(result[len++], e); ror.popFront(); - foreach(r; ror) + foreach (r; ror) { - foreach(e; sepArr) + foreach (e; sepArr) emplaceRef(result[len++], e); - foreach(e; r) + foreach (e; r) emplaceRef(result[len++], e); } assert(len == result.length); @@ -1674,17 +1658,17 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) } } -unittest // Issue 14230 +@safe unittest // Issue 14230 { string[] ary = ["","aa","bb","cc"]; // leaded by _empty_ element assert(ary.join(" @") == " @aa @bb @cc"); // OK in 2.067b1 and olders } /// Ditto -ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, E sep) - if(isInputRange!RoR && - isInputRange!(Unqual!(ElementType!RoR)) && - is(E : ElementType!(ElementType!RoR))) +ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, scope E sep) +if (isInputRange!RoR && + isInputRange!(Unqual!(ElementType!RoR)) && + is(E : ElementType!(ElementType!RoR))) { alias RetType = typeof(return); alias RetTypeElement = Unqual!(ElementEncodingType!RetType); @@ -1693,21 +1677,21 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, E sep) if (ror.empty) return RetType.init; - static if(hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) + static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) { static if (isSomeChar!E && isSomeChar!RetTypeElement && E.sizeof > RetTypeElement.sizeof) { import std.utf : encode; RetTypeElement[4 / RetTypeElement.sizeof] encodeSpace; immutable size_t sepArrLength = encode(encodeSpace, sep); - return join(ror, encodeSpace[0..sepArrLength]); + return join(ror, encodeSpace[0 .. sepArrLength]); } else { import std.conv : emplaceRef; size_t length; size_t rorLength; - foreach(r; ror.save) + foreach (r; ror.save) { length += r.length; ++rorLength; @@ -1719,13 +1703,13 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, E sep) size_t len; - foreach(e; ror.front) + foreach (e; ror.front) emplaceRef(result[len++], e); ror.popFront(); - foreach(r; ror) + foreach (r; ror) { emplaceRef(result[len++], sep); - foreach(e; r) + foreach (e; r) emplaceRef(result[len++], e); } assert(len == result.length); @@ -1746,7 +1730,21 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, E)(RoR ror, E sep) } } -unittest // Issue 14230 +@safe unittest // Issue 10895 +{ + class A + { + string name; + alias name this; + this(string name) { this.name = name; } + } + auto a = [new A(`foo`)]; + assert(a[0].length == 3); + auto temp = join(a, " "); + assert(a[0].length == 3); +} + +@safe unittest // Issue 14230 { string[] ary = ["","aa","bb","cc"]; assert(ary.join('@') == "@aa@bb@cc"); @@ -1754,8 +1752,8 @@ unittest // Issue 14230 /// Ditto ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) - if(isInputRange!RoR && - isInputRange!(Unqual!(ElementType!RoR))) +if (isInputRange!RoR && + isInputRange!(Unqual!(ElementType!RoR))) { alias RetType = typeof(return); alias RetTypeElement = Unqual!(ElementEncodingType!RetType); @@ -1764,20 +1762,20 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) if (ror.empty) return RetType.init; - static if(hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) + static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem)) { import std.conv : emplaceRef; size_t length; - foreach(r; ror.save) + foreach (r; ror.save) length += r.length; auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))(); size_t len; - foreach(r; ror) - foreach(e; r) + foreach (r; ror) + foreach (e; r) emplaceRef(result[len++], e); assert(len == result.length); - return (() @trusted => cast(RetType)result)(); + return (() @trusted => cast(RetType) result)(); } else { @@ -1806,18 +1804,18 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) { import std.conv : to; - foreach (T; TypeTuple!(string,wstring,dstring)) + foreach (T; AliasSeq!(string,wstring,dstring)) { auto arr2 = "ЗдравÑтвуй Мир Unicode".to!(T); auto arr = ["ЗдравÑтвуй", "Мир", "Unicode"].to!(T[]); assert(join(arr) == "ЗдравÑтвуйМирUnicode"); - foreach (S; TypeTuple!(char,wchar,dchar)) + foreach (S; AliasSeq!(char,wchar,dchar)) { auto jarr = arr.join(to!S(' ')); static assert(is(typeof(jarr) == T)); assert(jarr == arr2); } - foreach (S; TypeTuple!(string,wstring,dstring)) + foreach (S; AliasSeq!(string,wstring,dstring)) { auto jarr = arr.join(to!S(" ")); static assert(is(typeof(jarr) == T)); @@ -1825,11 +1823,11 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) } } - foreach (T; TypeTuple!(string,wstring,dstring)) + foreach (T; AliasSeq!(string,wstring,dstring)) { auto arr2 = "ЗдравÑтвуй\u047CМир\u047CUnicode".to!(T); auto arr = ["ЗдравÑтвуй", "Мир", "Unicode"].to!(T[]); - foreach (S; TypeTuple!(wchar,dchar)) + foreach (S; AliasSeq!(wchar,dchar)) { auto jarr = arr.join(to!S('\u047C')); static assert(is(typeof(jarr) == T)); @@ -1841,7 +1839,7 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) assert(arr.join(',') == "apple,banana"); } -unittest +@system unittest { import std.conv : to; import std.algorithm; @@ -1849,7 +1847,7 @@ unittest debug(std_array) printf("array.join.unittest\n"); - foreach(R; TypeTuple!(string, wstring, dstring)) + foreach (R; AliasSeq!(string, wstring, dstring)) { R word1 = "日本語"; R word2 = "paul"; @@ -1866,7 +1864,7 @@ unittest auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3]; auto filteredWords = filter!"true"(filteredWordsArr); - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 assert(join(filteredWords, to!S(", ")) == "日本語, paul, jerry"); assert(join(filteredWords, to!(ElementType!S)(',')) == "日本語,paul,jerry"); @@ -1949,7 +1947,7 @@ unittest } // Issue 10683 -unittest +@safe unittest { import std.range : join; import std.typecons : tuple; @@ -1958,10 +1956,10 @@ unittest } // Issue 13877 -unittest +@safe unittest { // Test that the range is iterated only once. - import std.algorithm : map; + import std.algorithm.iteration : map; int c = 0; auto j1 = [1, 2, 3].map!(_ => [c++]).join; assert(c == 3); @@ -1980,17 +1978,33 @@ unittest /++ - Replace occurrences of $(D from) with $(D to) in $(D subject). Returns a new - array without changing the contents of $(D subject), or the original array - if no match is found. + Replace occurrences of `from` with `to` in `subject` in a new + array. If `sink` is defined, then output the new array into + `sink`. + + Params: + sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) + subject = the array to scan + from = the item to replace + to = the item to replace all instances of `from` with + + Returns: + If `sink` isn't defined, a new array without changing the + contents of `subject`, or the original array if no match + is found. + + See_Also: + $(REF map, std,algorithm,iteration) which can act as a lazy replace +/ E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to) if (isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) { + import std.algorithm.searching : find; + if (from.empty) return subject; - auto balance = std.algorithm.find(subject, from.save); + auto balance = find(subject, from.save); if (balance.empty) return subject; @@ -2003,21 +2017,20 @@ if (isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2 } /// -unittest +@safe unittest { assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World"); assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd"); } -/++ - Same as above, but outputs the result via OutputRange $(D sink). - If no match is found the original array is transferred to $(D sink) as is. -+/ +/// ditto void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to) if (isOutputRange!(Sink, E) && isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) { + import std.algorithm.searching : find; + if (from.empty) { sink.put(subject); @@ -2025,7 +2038,7 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[]) } for (;;) { - auto balance = std.algorithm.find(subject, from.save); + auto balance = find(subject, from.save); if (balance.empty) { sink.put(subject); @@ -2037,16 +2050,29 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[]) } } -unittest +/// +@safe unittest +{ + auto arr = [1, 2, 3, 4, 5]; + auto from = [2, 3]; + auto to = [4, 6]; + auto sink = appender!(int[])(); + + replaceInto(sink, arr, from, to); + + assert(sink.data == [1, 4, 6, 4, 5]); +} + +@safe unittest { import std.conv : to; - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; debug(std_array) printf("array.replace.unittest\n"); - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) { - foreach (T; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) + foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 auto s = to!S("This is a foo foo list"); auto from = to!T("foo"); @@ -2070,10 +2096,10 @@ unittest assert(replace(s, "foo", "silly") == "This is a silly silly list"); } -unittest +@safe unittest { import std.conv : to; - import std.algorithm : skipOver; + import std.algorithm.searching : skipOver; struct CheckOutput(C) { @@ -2081,7 +2107,7 @@ unittest this(C[] arr){ desired = arr; } void put(C[] part){ assert(skipOver(desired, part)); } } - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) { alias Char = ElementEncodingType!S; S s = to!S("yet another dummy text, yet another ..."); @@ -2093,28 +2119,36 @@ unittest } /++ - Replaces elements from $(D array) with indices ranging from $(D from) - (inclusive) to $(D to) (exclusive) with the range $(D stuff). Returns a new - array without changing the contents of $(D subject). + Replaces elements from `array` with indices ranging from `from` + (inclusive) to `to` (exclusive) with the range `stuff`. + + Params: + subject = the array to scan + from = the starting index + to = the ending index + stuff = the items to replace in-between `from` and `to` + + Returns: + A new array without changing the contents of `subject`. +/ T[] replace(T, Range)(T[] subject, size_t from, size_t to, Range stuff) - if(isInputRange!Range && - (is(ElementType!Range : T) || - isSomeString!(T[]) && is(ElementType!Range : dchar))) +if (isInputRange!Range && + (is(ElementType!Range : T) || + isSomeString!(T[]) && is(ElementType!Range : dchar))) { - static if(hasLength!Range && is(ElementEncodingType!Range : T)) + static if (hasLength!Range && is(ElementEncodingType!Range : T)) { - import std.algorithm : copy; + import std.algorithm.mutation : copy; assert(from <= to); immutable sliceLen = to - from; auto retval = new Unqual!(T)[](subject.length - sliceLen + stuff.length); retval[0 .. from] = subject[0 .. from]; - if(!stuff.empty) + if (!stuff.empty) copy(stuff, retval[from .. from + stuff.length]); retval[from + stuff.length .. $] = subject[to .. $]; - return cast(T[])retval; + return cast(T[]) retval; } else { @@ -2127,7 +2161,7 @@ T[] replace(T, Range)(T[] subject, size_t from, size_t to, Range stuff) } /// -unittest +@safe unittest { auto a = [ 1, 2, 3, 4 ]; auto b = a.replace(1, 3, [ 9, 9, 9 ]); @@ -2135,12 +2169,12 @@ unittest assert(b == [ 1, 9, 9, 9, 4 ]); } -unittest +@system unittest { import core.exception; import std.conv : to; import std.exception; - import std.algorithm; + import std.algorithm.iteration : filter; auto a = [ 1, 2, 3, 4 ]; @@ -2157,7 +2191,7 @@ unittest assert(replace(a, 2, 4, filter!"true"([5, 6, 7])) == [1, 2, 5, 6, 7]); assert(a == [ 1, 2, 3, 4 ]); - auto testStr(T, U)(string file = __FILE__, size_t line = __LINE__) + void testStr(T, U)(string file = __FILE__, size_t line = __LINE__) { auto l = to!T("hello"); @@ -2209,55 +2243,58 @@ unittest } /++ - Replaces elements from $(D array) with indices ranging from $(D from) - (inclusive) to $(D to) (exclusive) with the range $(D stuff). Expands or + Replaces elements from `array` with indices ranging from `from` + (inclusive) to `to` (exclusive) with the range `stuff`. Expands or shrinks the array as needed. + + Params: + array = the _array to scan + from = the starting index + to = the ending index + stuff = the items to replace in-between `from` and `to` +/ void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) - if(isDynamicArray!Range && - is(ElementEncodingType!Range : T) && - !is(T == const T) && - !is(T == immutable T)) +if (is(typeof(replace(array, from, to, stuff)))) { - import std.algorithm : remove; - import std.typecons : tuple; - - if (overlap(array, stuff).length) + static if (isDynamicArray!Range && + is(Unqual!(ElementEncodingType!Range) == T) && + !isNarrowString!(T[])) { - // use slower/conservative method - array = array[0 .. from] ~ stuff ~ array[to .. $]; - } - else if (stuff.length <= to - from) - { - // replacement reduces length - immutable stuffEnd = from + stuff.length; - array[from .. stuffEnd] = stuff[]; - if (stuffEnd < to) - array = remove(array, tuple(stuffEnd, to)); + // optimized for homogeneous arrays that can be overwritten. + import std.algorithm.mutation : remove; + import std.typecons : tuple; + + if (overlap(array, stuff).length) + { + // use slower/conservative method + array = array[0 .. from] ~ stuff ~ array[to .. $]; + } + else if (stuff.length <= to - from) + { + // replacement reduces length + immutable stuffEnd = from + stuff.length; + array[from .. stuffEnd] = stuff[]; + if (stuffEnd < to) + array = remove(array, tuple(stuffEnd, to)); + } + else + { + // replacement increases length + // @@@TODO@@@: optimize this + immutable replaceLen = to - from; + array[from .. to] = stuff[0 .. replaceLen]; + insertInPlace(array, to, stuff[replaceLen .. $]); + } } else { - // replacement increases length - // @@@TODO@@@: optimize this - immutable replaceLen = to - from; - array[from .. to] = stuff[0 .. replaceLen]; - insertInPlace(array, to, stuff[replaceLen .. $]); + // default implementation, just do what replace does. + array = replace(array, from, to, stuff); } } -/// Ditto -void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) - if(isInputRange!Range && - ((!isDynamicArray!Range && is(ElementType!Range : T)) || - (isDynamicArray!Range && is(ElementType!Range : T) && - (is(T == const T) || is(T == immutable T))) || - isSomeString!(T[]) && is(ElementType!Range : dchar))) -{ - array = replace(array, from, to, stuff); -} - /// -unittest +@safe unittest { int[] a = [1, 4, 5]; replaceInPlace(a, 1u, 2u, [2, 3, 4]); @@ -2268,7 +2305,7 @@ unittest assert(a == [1, 4, 5, 5]); } -unittest +@safe unittest { // Bug# 12889 int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; @@ -2277,32 +2314,93 @@ unittest assert(arr == [[0], [1], [2], [3], [0], [1], [6]]); } -unittest +@system unittest +{ + // Bug# 14925 + char[] a = "mon texte 1".dup; + char[] b = "abc".dup; + replaceInPlace(a, 4, 9, b); + assert(a == "mon abc 1"); + + // ensure we can replace in place with different encodings + string unicoded = "\U00010437"; + string unicodedLong = "\U00010437aaaaa"; + string base = "abcXXXxyz"; + string result = "abc\U00010437xyz"; + string resultLong = "abc\U00010437aaaaaxyz"; + size_t repstart = 3; + size_t repend = 3 + 3; + + void testStringReplaceInPlace(T, U)() + { + import std.conv; + import std.algorithm.comparison : equal; + auto a = unicoded.to!(U[]); + auto b = unicodedLong.to!(U[]); + + auto test = base.to!(T[]); + + test.replaceInPlace(repstart, repend, a); + assert(equal(test, result), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof); + + test = base.to!(T[]); + + test.replaceInPlace(repstart, repend, b); + assert(equal(test, resultLong), "Failed for types " ~ T.stringof ~ " and " ~ U.stringof); + } + + import std.meta : AliasSeq; + alias allChars = AliasSeq!(char, immutable(char), const(char), + wchar, immutable(wchar), const(wchar), + dchar, immutable(dchar), const(dchar)); + foreach (T; allChars) + foreach (U; allChars) + testStringReplaceInPlace!(T, U)(); + + void testInout(inout(int)[] a) + { + // will be transferred to the 'replace' function + replaceInPlace(a, 1, 2, [1,2,3]); + } +} + +@safe unittest +{ + // the constraint for the first overload used to match this, which wouldn't compile. + import std.algorithm.comparison : equal; + long[] a = [1L, 2, 3]; + int[] b = [4, 5, 6]; + a.replaceInPlace(1, 2, b); + assert(equal(a, [1L, 4, 5, 6, 3])); +} + +@system unittest { + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; import core.exception; import std.conv : to; import std.exception; - import std.algorithm; bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result, string file = __FILE__, size_t line = __LINE__) { { - static if(is(T == typeof(T.init.dup))) + static if (is(T == typeof(T.init.dup))) auto a = orig.dup; else auto a = orig.idup; a.replaceInPlace(from, to, toReplace); - if(!std.algorithm.equal(a, result)) + if (!equal(a, result)) return false; } - static if(isInputRange!U) + static if (isInputRange!U) { orig.replaceInPlace(from, to, filter!"true"(toReplace)); - return std.algorithm.equal(orig, result); + return equal(orig, result); } else return true; @@ -2320,7 +2418,7 @@ unittest assert(test([1, 2, 3, 4], 0, 2, filter!"true"([5, 6, 7]), [5, 6, 7, 3, 4])); assert(test([1, 2, 3, 4], 2, 4, filter!"true"([5, 6, 7]), [1, 2, 5, 6, 7])); - auto testStr(T, U)(string file = __FILE__, size_t line = __LINE__) + void testStr(T, U)(string file = __FILE__, size_t line = __LINE__) { auto l = to!T("hello"); @@ -2350,16 +2448,22 @@ unittest } /++ - Replaces the first occurrence of $(D from) with $(D to) in $(D a). Returns a - new array without changing the contents of $(D subject), or the original - array if no match is found. + Replaces the first occurrence of `from` with `to` in `subject`. + + Params: + subject = the array to scan + from = the item to replace + to = the item to replace `from` with + + Returns: + A new array without changing the contents of $(D subject), or the original + array if no match is found. +/ E[] replaceFirst(E, R1, R2)(E[] subject, R1 from, R2 to) if (isDynamicArray!(E[]) && isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) && isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1])))) { - import std.algorithm : countUntil; if (from.empty) return subject; static if (isSomeString!(E[])) { @@ -2368,7 +2472,7 @@ if (isDynamicArray!(E[]) && } else { - import std.algorithm : countUntil; + import std.algorithm.searching : countUntil; immutable idx = subject.countUntil(from); } if (idx == -1) @@ -2392,7 +2496,7 @@ if (isDynamicArray!(E[]) && } /// -unittest +@safe unittest { auto a = [1, 2, 2, 3, 4, 5]; auto b = a.replaceFirst([2], [1337]); @@ -2403,17 +2507,17 @@ unittest assert(r == "This is a silly foo list"); } -unittest +@safe unittest { import std.conv : to; - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; debug(std_array) printf("array.replaceFirst.unittest\n"); - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[], + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], const(char[]), immutable(char[]))) { - foreach (T; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[], + foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], const(char[]), immutable(char[]))) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 auto s = to!S("This is a foo foo list"); @@ -2442,7 +2546,7 @@ unittest } //Bug# 8187 -unittest +@safe unittest { auto res = ["a", "a"]; assert(replace(res, "a", "b") == ["b", "b"]); @@ -2450,9 +2554,16 @@ unittest } /++ - Replaces the last occurrence of $(D from) with $(D to) in $(D a). Returns a - new array without changing the contents of $(D subject), or the original - array if no match is found. + Replaces the last occurrence of `from` with `to` in `subject`. + + Params: + subject = the array to scan + from = the item to replace + to = the item to replace `from` with + + Returns: + A new array without changing the contents of $(D subject), or the original + array if no match is found. +/ E[] replaceLast(E, R1, R2)(E[] subject, R1 from , R2 to) if (isDynamicArray!(E[]) && @@ -2468,7 +2579,7 @@ if (isDynamicArray!(E[]) && } else { - import std.algorithm : countUntil; + import std.algorithm.searching : countUntil; auto idx = retro(subject).countUntil(retro(from)); } @@ -2500,7 +2611,7 @@ if (isDynamicArray!(E[]) && } /// -unittest +@safe unittest { auto a = [1, 2, 2, 3, 4, 5]; auto b = a.replaceLast([2], [1337]); @@ -2511,17 +2622,17 @@ unittest assert(r == "This is a foo silly list", r); } -unittest +@safe unittest { import std.conv : to; - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; debug(std_array) printf("array.replaceLast.unittest\n"); - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[], + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], const(char[]), immutable(char[]))) { - foreach (T; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[], + foreach (T; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[], const(char[]), immutable(char[]))) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 auto s = to!S("This is a foo foo list"); @@ -2550,8 +2661,18 @@ unittest } /++ - Returns a new array that is $(D s) with $(D slice) replaced by - $(D replacement[]). + Creates a new array such that the items in `slice` are replaced with the + items in `replacement`. `slice` and `replacement` do not need to be the + same length. The result will grow or shrink based on the items given. + + Params: + s = the base of the new array + slice = the slice of `s` to be replaced + replacement = the items to replace `slice` with + + Returns: + A new array that is `s` with `slice` replaced by + `replacement[]`. +/ inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement) in @@ -2571,9 +2692,18 @@ body return cast(inout(T)[]) result; } -unittest +/// +@system unittest +{ + auto a = [1, 2, 3, 4, 5]; + auto b = replaceSlice(a, a[1 .. 4], [0, 0, 0]); + + assert(b == [1, 0, 0, 0, 5]); +} + +@system unittest { - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; debug(std_array) printf("array.replaceSlice.unittest\n"); string s = "hello"; @@ -2587,15 +2717,18 @@ unittest /** Implements an output range that appends data to an array. This is -recommended over $(D a ~= data) when appending many elements because it is more -efficient. +recommended over $(D array ~= data) when appending many elements because it is more +efficient. `Appender` maintains its own array metadata locally, so it can avoid +global locking for each append where $(LREF capacity) is non-zero. +See_Also: $(LREF appender) */ struct Appender(A) if (isDynamicArray!A) { - import core.memory; + import core.memory : GC; private alias T = ElementEncodingType!A; + private struct Data { size_t capacity; @@ -2606,16 +2739,16 @@ if (isDynamicArray!A) private Data* _data; /** - * Construct an appender with a given array. Note that this does not copy the - * data. If the array has a larger capacity as determined by arr.capacity, + * Constructs an `Appender` with a given array. Note that this does not copy the + * data. If the array has a larger capacity as determined by `arr.capacity`, * it will be used by the appender. After initializing an appender on an array, * appending to the original array will reallocate. */ - this(T[] arr) @trusted pure nothrow + this(A arr) @trusted pure nothrow { // initialize to a given array. _data = new Data; - _data.arr = cast(Unqual!T[])arr; //trusted + _data.arr = cast(Unqual!T[]) arr; //trusted if (__ctfe) return; @@ -2626,7 +2759,7 @@ if (isDynamicArray!A) // We only do this for mutable types that can be extended. static if (isMutable!T && is(typeof(arr.length = size_t.max))) { - auto cap = arr.capacity; //trusted + immutable cap = arr.capacity; //trusted // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed) if (cap > arr.length) arr.length = cap; @@ -2634,25 +2767,9 @@ if (isDynamicArray!A) _data.capacity = arr.length; } - //Broken function. To be removed. - static if (is(T == immutable)) - { - deprecated ("Using this constructor will break the type system. Please fix your code to use `Appender!(T[]).this(T[] arr)' directly.") - this(Unqual!T[] arr) pure nothrow - { - this(cast(T[]) arr); - } - - //temporary: For resolving ambiguity: - this(typeof(null)) - { - this(cast(T[]) null); - } - } - /** * Reserve at least newCapacity elements for appending. Note that more elements - * may be reserved than requested. If newCapacity <= capacity, then nothing is + * may be reserved than requested. If `newCapacity <= capacity`, then nothing is * done. */ void reserve(size_t newCapacity) @safe pure nothrow @@ -2669,9 +2786,9 @@ if (isDynamicArray!A) } /** - * Returns the capacity of the array (the maximum number of elements the - * managed array can accommodate before triggering a reallocation). If any - * appending will reallocate, $(D capacity) returns $(D 0). + * Returns: the capacity of the array (the maximum number of elements the + * managed array can accommodate before triggering a reallocation). If any + * appending will reallocate, `0` will be returned. */ @property size_t capacity() const @safe pure nothrow { @@ -2679,9 +2796,9 @@ if (isDynamicArray!A) } /** - * Returns the managed array. + * Returns: The managed array. */ - @property inout(T)[] data() inout @trusted pure nothrow + @property inout(ElementEncodingType!A)[] data() inout @trusted pure nothrow { /* @trusted operation: * casting Unqual!T[] to inout(T)[] @@ -2726,7 +2843,7 @@ if (isDynamicArray!A) // first, try extending the current block if (_data.canExtend) { - auto u = GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof); + immutable u = GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof); if (u) { // extend worked, update the capacity @@ -2735,13 +2852,19 @@ if (isDynamicArray!A) } } + // didn't work, must reallocate - auto bi = GC.qalloc(newlen * T.sizeof, blockAttribute!T); + import core.checkedint : mulu; + bool overflow; + const nbytes = mulu(newlen, T.sizeof, overflow); + if (overflow) assert(0); + + auto bi = GC.qalloc(nbytes, blockAttribute!T); _data.capacity = bi.size / T.sizeof; import core.stdc.string : memcpy; if (len) memcpy(bi.base, _data.arr.ptr, len * T.sizeof); - _data.arr = (cast(Unqual!T*)bi.base)[0 .. len]; + _data.arr = (cast(Unqual!T*) bi.base)[0 .. len]; _data.canExtend = true; // leave the old data, for safety reasons } @@ -2757,7 +2880,8 @@ if (isDynamicArray!A) { enum bool canPutConstRange = isInputRange!(Unqual!Range) && - !isInputRange!Range; + !isInputRange!Range && + is(typeof(Appender.init.put(Range.init.front))); } private template canPutRange(Range) { @@ -2767,7 +2891,7 @@ if (isDynamicArray!A) } /** - * Appends one item to the managed array. + * Appends `item` to the managed array. */ void put(U)(U item) if (canPutItem!U) { @@ -2790,7 +2914,7 @@ if (isDynamicArray!A) immutable len = _data.arr.length; auto bigData = (() @trusted => _data.arr.ptr[0 .. len + 1])(); - emplaceRef!(Unqual!T)(bigData[len], cast(Unqual!T)item); + emplaceRef!(Unqual!T)(bigData[len], cast(Unqual!T) item); //We do this at the end, in case of exceptions _data.arr = bigData; } @@ -2840,7 +2964,7 @@ if (isDynamicArray!A) alias UT = Unqual!T; static if (is(typeof(_data.arr[] = items[])) && - !hasElaborateAssign!(Unqual!T) && isAssignable!(UT, ElementEncodingType!Range)) + !hasElaborateAssign!UT && isAssignable!(UT, ElementEncodingType!Range)) { bigData[len .. newlen] = items[]; } @@ -2869,25 +2993,14 @@ if (isDynamicArray!A) } /** - * Appends one item to the managed array. - */ - void opOpAssign(string op : "~", U)(U item) if (canPutItem!U) - { - put(item); - } - - // Const fixing hack. - void opOpAssign(string op : "~", Range)(Range items) if (canPutConstRange!Range) - { - put(items); - } - - /** - * Appends an entire range to the managed array. + * Appends `rhs` to the managed array. + * Params: + * rhs = Element or range. */ - void opOpAssign(string op : "~", Range)(Range items) if (canPutRange!Range) + void opOpAssign(string op : "~", U)(U rhs) + if (__traits(compiles, put(rhs))) { - put(items); + put(rhs); } // only allow overwriting data on non-immutable and non-const data @@ -2897,7 +3010,7 @@ if (isDynamicArray!A) * Clears the managed array. This allows the elements of the array to be reused * for appending. * - * Note that clear is disabled for immutable or const element types, due to the + * Note: clear is disabled for immutable or const element types, due to the * possibility that $(D Appender) might overwrite immutable data. */ void clear() @trusted pure nothrow @@ -2912,29 +3025,31 @@ if (isDynamicArray!A) * Shrinks the managed array to the given length. * * Throws: $(D Exception) if newlength is greater than the current array length. + * Note: shrinkTo is disabled for immutable or const element types. */ void shrinkTo(size_t newlength) @trusted pure { import std.exception : enforce; if (_data) { - enforce(newlength <= _data.arr.length); + enforce(newlength <= _data.arr.length, "Attempting to shrink Appender with newlength > length"); _data.arr = _data.arr.ptr[0 .. newlength]; } else - enforce(newlength == 0); + enforce(newlength == 0, "Attempting to shrink empty Appender with non-zero newlength"); } } - void toString()(scope void delegate(const(char)[]) sink) + void toString(Writer)(scope Writer w) { import std.format : formattedWrite; - sink.formattedWrite(typeof(this).stringof ~ "(%s)", data); + w.formattedWrite(typeof(this).stringof ~ "(%s)", data); } } /// -unittest{ +@safe unittest +{ auto app = appender!string(); string b = "abcdefg"; foreach (char c; b) @@ -2948,7 +3063,7 @@ unittest{ assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]); } -unittest +@safe unittest { import std.format : format; auto app = appender!(int[])(); @@ -2958,6 +3073,21 @@ unittest assert("%s".format(app) == "Appender!(int[])(%s)".format([1,2,3])); } +@safe unittest // issue 17251 +{ + static struct R + { + int front() const { return 0; } + bool empty() const { return true; } + void popFront() {} + } + + auto app = appender!(R[]); + const(R)[1] r; + app.put(r[0]); + app.put(r[]); +} + //Calculates an efficient growth scheme based on the old capacity //of data, and the minimum requested capacity. //arg curLen: The current length @@ -2966,80 +3096,75 @@ unittest private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen) @safe pure nothrow { import core.bitop : bsr; - import std.algorithm : max; - if(curLen == 0) + import std.algorithm.comparison : max; + if (curLen == 0) return max(reqLen,8); ulong mult = 100 + (1000UL) / (bsr(curLen * TSizeOf) + 1); // limit to doubling the length, we don't want to grow too much - if(mult > 200) + if (mult > 200) mult = 200; auto sugLen = cast(size_t)((curLen * mult + 99) / 100); return max(reqLen, sugLen); } /** - * An appender that can update an array in-place. It forwards all calls to an - * underlying appender implementation. Any calls made to the appender also update - * the pointer to the original array passed in. + * A version of $(LREF Appender) that can update an array in-place. + * It forwards all calls to an underlying appender implementation. + * Any calls made to the appender also update the pointer to the + * original array passed in. + * + * Tip: Use the `arrayPtr` overload of $(LREF appender) for construction with type-inference. */ struct RefAppender(A) if (isDynamicArray!A) { private { - alias T = ElementEncodingType!A; Appender!A impl; - T[] *arr; + A* arr; } /** - * Construct a ref appender with a given array reference. This does not copy the - * data. If the array has a larger capacity as determined by arr.capacity, it - * will be used by the appender. $(D RefAppender) assumes that arr is a non-null - * value. + * Constructs a `RefAppender` with a given array reference. This does not copy the + * data. If the array has a larger capacity as determined by `arr.capacity`, it + * will be used by the appender. + * + * Note: Do not use built-in appending (i.e. `~=`) on the original array + * until you are done with the appender, because subsequent calls to the appender + * will reallocate the array data without those appends. * - * Note, do not use builtin appending (i.e. ~=) on the original array passed in - * until you are done with the appender, because calls to the appender override - * those appends. + * Params: + * arr = Pointer to an array. Must not be _null. */ - this(T[] *arr) + this(A* arr) { impl = Appender!A(*arr); this.arr = arr; } - auto opDispatch(string fn, Args...)(Args args) if (is(typeof(mixin("impl." ~ fn ~ "(args)")))) + /** Wraps remaining `Appender` methods such as $(LREF put). + * Params: + * fn = Method name to call. + * args = Arguments to pass to the method. + */ + void opDispatch(string fn, Args...)(Args args) + if (__traits(compiles, (Appender!A a) => mixin("a." ~ fn ~ "(args)"))) { // we do it this way because we can't cache a void return scope(exit) *this.arr = impl.data; mixin("return impl." ~ fn ~ "(args);"); } - private alias AppenderType = Appender!A; - /** - * Appends one item to the managed array. + * Appends `rhs` to the managed array. + * Params: + * rhs = Element or range. */ - void opOpAssign(string op : "~", U)(U item) if (AppenderType.canPutItem!U) + void opOpAssign(string op : "~", U)(U rhs) + if (__traits(compiles, (Appender!A a){ a.put(rhs); })) { scope(exit) *this.arr = impl.data; - impl.put(item); - } - - // Const fixing hack. - void opOpAssign(string op : "~", Range)(Range items) if (AppenderType.canPutConstRange!Range) - { - scope(exit) *this.arr = impl.data; - impl.put(items); - } - - /** - * Appends an entire range to the managed array. - */ - void opOpAssign(string op : "~", Range)(Range items) if (AppenderType.canPutRange!Range) - { - scope(exit) *this.arr = impl.data; - impl.put(items); + impl.put(rhs); } /** @@ -3055,15 +3180,15 @@ if (isDynamicArray!A) /** * Returns the managed array. */ - @property inout(T)[] data() inout + @property inout(ElementEncodingType!A)[] data() inout { return impl.data; } } /++ - Convenience function that returns an $(D Appender!A) object initialized - with $(D array). + Convenience function that returns an $(LREF Appender) instance, + optionally initialized with $(D array). +/ Appender!A appender(A)() if (isDynamicArray!A) @@ -3073,7 +3198,7 @@ if (isDynamicArray!A) /// ditto Appender!(E[]) appender(A : E[], E)(auto ref A array) { - static assert (!isStaticArray!A || __traits(isRef, array), + static assert(!isStaticArray!A || __traits(isRef, array), "Cannot create Appender from an rvalue static array"); return Appender!(E[])(array); @@ -3137,7 +3262,7 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array) catch (Exception) assert(0); // Issue 5663 & 9725 tests - foreach (S; TypeTuple!(char[], const(char)[], string)) + foreach (S; AliasSeq!(char[], const(char)[], string)) { { Appender!S app5663i; @@ -3188,8 +3313,8 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array) { auto w = appender!string(); w.reserve(4); - cast(void)w.capacity; - cast(void)w.data; + cast(void) w.capacity; + cast(void) w.data; try { wchar wc = 'a'; @@ -3202,8 +3327,8 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array) { auto w = appender!(int[])(); w.reserve(4); - cast(void)w.capacity; - cast(void)w.data; + cast(void) w.capacity; + cast(void) w.data; w.put(10); w.put([10]); w.clear(); @@ -3232,7 +3357,7 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array) } } -unittest +@safe unittest { import std.typecons; import std.algorithm; @@ -3241,7 +3366,7 @@ unittest [tuple("A")].filter!(t => true).array; // error } -unittest +@system unittest { import std.range; //Coverage for put(Range) @@ -3264,7 +3389,7 @@ unittest au2.put(sc2.repeat().take(10)); } -unittest +@system unittest { struct S { @@ -3297,7 +3422,7 @@ unittest a2.put([s2]); } -unittest +@safe unittest { //9528 const(E)[] fastCopy(E)(E[] src) { @@ -3314,9 +3439,9 @@ unittest auto t = fastCopy(s); // Does not compile } -unittest +@safe unittest { - import std.algorithm : map; + import std.algorithm.iteration : map; //10753 struct Foo { immutable dchar d; @@ -3328,7 +3453,7 @@ unittest [1, 2].map!Bar.array; } -unittest +@safe unittest { //New appender signature tests alias mutARR = int[]; @@ -3362,18 +3487,18 @@ unittest {auto app = appender!(char[])(null);} } -unittest //Test large allocations (for GC.extend) +@safe unittest //Test large allocations (for GC.extend) { import std.range; - import std.algorithm : equal; + import std.algorithm.comparison : equal; Appender!(char[]) app; app.reserve(1); //cover reserve on non-initialized - foreach(_; 0 .. 100_000) + foreach (_; 0 .. 100_000) app.put('a'); assert(equal(app.data, 'a'.repeat(100_000))); } -unittest +@safe unittest { auto reference = new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends) auto arr = reference.dup; @@ -3383,13 +3508,13 @@ unittest assert(reference[] == arr[]); } -unittest // check against .clear UFCS hijacking +@safe unittest // clear method is supported only for mutable element types { Appender!string app; static assert(!__traits(compiles, app.clear())); } -unittest +@safe unittest { static struct D//dynamic { @@ -3418,7 +3543,7 @@ unittest static assert(!is(typeof(appender(foo())))); } -unittest +@system unittest { // Issue 13077 static class A {} @@ -3437,16 +3562,16 @@ unittest } /++ - Convenience function that returns a $(D RefAppender!A) object initialized - with $(D array). Don't use null for the $(D array) pointer, use the other + Convenience function that returns a $(LREF RefAppender) instance initialized + with `arrayPtr`. Don't use null for the array pointer, use the other version of $(D appender) instead. +/ -RefAppender!(E[]) appender(A : E[]*, E)(A array) +RefAppender!(E[]) appender(P : E[]*, E)(P arrayPtr) { - return RefAppender!(E[])(array); + return RefAppender!(E[])(arrayPtr); } -unittest +@system unittest { import std.exception; { @@ -3501,7 +3626,13 @@ unittest assert(app3.data == [1, 2, 3]); } -unittest +@safe unittest // issue 14605 +{ + static assert(isOutputRange!(Appender!(int[]), int)); + static assert(isOutputRange!(RefAppender!(int[]), int)); +} + +@safe unittest { Appender!(int[]) app; short[] range = [1, 2, 3]; @@ -3509,7 +3640,7 @@ unittest assert(app.data == [1, 2, 3]); } -unittest +@safe unittest { string s = "hello".idup; char[] a = "hello".dup; diff --git a/std/ascii.d b/std/ascii.d index ba057e6d75c..e5fa3e33d1a 100644 --- a/std/ascii.d +++ b/std/ascii.d @@ -9,18 +9,52 @@ to non-ASCII characters. For functions which operate on Unicode characters, see - $(LINK2 std_uni.html, std.uni). - + $(MREF std, uni). + +$(SCRIPT inhibitQuickIndex = 1;) +$(DIVC quickindex, +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Validation) $(TD + $(LREF isAlpha) + $(LREF isAlphaNum) + $(LREF isASCII) + $(LREF isControl) + $(LREF isDigit) + $(LREF isGraphical) + $(LREF isHexDigit) + $(LREF isOctalDigit) + $(LREF isPrintable) + $(LREF isPunctuation) + $(LREF isUpper) + $(LREF isWhite) +)) +$(TR $(TD Conversions) $(TD + $(LREF toLower) + $(LREF toUpper) +)) +$(TR $(TD Constants) $(TD + $(LREF digits) + $(LREF fullHexDigits) + $(LREF hexDigits) + $(LREF letters) + $(LREF lowercase) + $(LREF lowerHexDigits) + $(LREF newline) + $(LREF octalDigits) + $(LREF uppercase) + $(LREF whitespace) +)) +$(TR $(TD Enums) $(TD + $(LREF LetterCase) +)) +)) References: $(LINK2 http://www.digitalmars.com/d/ascii-table.html, ASCII Table), - $(WEB en.wikipedia.org/wiki/Ascii, Wikipedia) - - Macros: - WIKI=Phobos/StdASCII + $(HTTP en.wikipedia.org/wiki/Ascii, Wikipedia) - Copyright: Copyright 2000 - 2013 - License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(WEB digitalmars.com, Walter Bright) and Jonathan M Davis + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: $(HTTP digitalmars.com, Walter Bright) and Jonathan M Davis Source: $(PHOBOSSRC std/_ascii.d) +/ module std.ascii; @@ -28,20 +62,20 @@ module std.ascii; version (unittest) { // FIXME: When dmd bug #314 is fixed, make these selective. + import std.meta; // : AliasSeq; import std.range; // : chain; import std.traits; // : functionAttributes, FunctionAttribute, isSafe; - import std.typetuple; // : TypeTuple; } -immutable fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f -immutable hexDigits = fullHexDigits[0..16]; /// 0..9A..F -immutable lowerHexDigits = "0123456789abcdef"; /// 0..9a..f -immutable digits = hexDigits[0..10]; /// 0..9 -immutable octalDigits = digits[0..8]; /// 0..7 -immutable letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /// A..Za..z -immutable uppercase = letters[0..26]; /// A..Z -immutable lowercase = letters[26..52]; /// a..z +immutable fullHexDigits = "0123456789ABCDEFabcdef"; /// 0 .. 9A .. Fa .. f +immutable hexDigits = fullHexDigits[0 .. 16]; /// 0 .. 9A .. F +immutable lowerHexDigits = "0123456789abcdef"; /// 0 .. 9a .. f +immutable digits = hexDigits[0 .. 10]; /// 0 .. 9 +immutable octalDigits = digits[0 .. 8]; /// 0 .. 7 +immutable letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /// A .. Za .. z +immutable uppercase = letters[0 .. 26]; /// A .. Z +immutable lowercase = letters[26 .. 52]; /// a .. z immutable whitespace = " \t\v\r\n\f"; /// ASCII _whitespace /++ @@ -64,7 +98,7 @@ else /++ Params: c = The character to test. - Returns: Whether $(D c) is a letter or a number (0..9, a..z, A..Z). + Returns: Whether $(D c) is a letter or a number (0 .. 9, a .. z, A .. Z). +/ bool isAlphaNum(dchar c) @safe pure nothrow @nogc { @@ -82,19 +116,19 @@ bool isAlphaNum(dchar c) @safe pure nothrow @nogc assert(!isAlphaNum('á')); } -unittest +@safe unittest { - foreach(c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase)) + foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase)) assert(isAlphaNum(c)); - foreach(c; whitespace) + foreach (c; whitespace) assert(!isAlphaNum(c)); } /++ Params: c = The character to test. - Returns: Whether $(D c) is an ASCII letter (A..Z, a..z). + Returns: Whether $(D c) is an ASCII letter (A .. Z, a .. z). +/ bool isAlpha(dchar c) @safe pure nothrow @nogc { @@ -113,19 +147,19 @@ bool isAlpha(dchar c) @safe pure nothrow @nogc assert(!isAlpha('á')); } -unittest +@safe unittest { - foreach(c; chain(letters, lowercase, uppercase)) + foreach (c; chain(letters, lowercase, uppercase)) assert(isAlpha(c)); - foreach(c; chain(digits, octalDigits, whitespace)) + foreach (c; chain(digits, octalDigits, whitespace)) assert(!isAlpha(c)); } /++ Params: c = The character to test. - Returns: Whether $(D c) is a lowercase ASCII letter (a..z). + Returns: Whether $(D c) is a lowercase ASCII letter (a .. z). +/ bool isLower(dchar c) @safe pure nothrow @nogc { @@ -144,19 +178,19 @@ bool isLower(dchar c) @safe pure nothrow @nogc assert(!isLower('Ã')); } -unittest +@safe unittest { - foreach(c; lowercase) + foreach (c; lowercase) assert(isLower(c)); - foreach(c; chain(digits, uppercase, whitespace)) + foreach (c; chain(digits, uppercase, whitespace)) assert(!isLower(c)); } /++ Params: c = The character to test. - Returns: Whether $(D c) is an uppercase ASCII letter (A..Z). + Returns: Whether $(D c) is an uppercase ASCII letter (A .. Z). +/ bool isUpper(dchar c) @safe pure nothrow @nogc { @@ -175,19 +209,19 @@ bool isUpper(dchar c) @safe pure nothrow @nogc assert(!isUpper('Ã')); } -unittest +@safe unittest { - foreach(c; uppercase) + foreach (c; uppercase) assert(isUpper(c)); - foreach(c; chain(digits, lowercase, whitespace)) + foreach (c; chain(digits, lowercase, whitespace)) assert(!isUpper(c)); } /++ Params: c = The character to test. - Returns: Whether $(D c) is a digit (0..9). + Returns: Whether $(D c) is a digit (0 .. 9). +/ bool isDigit(dchar c) @safe pure nothrow @nogc { @@ -207,19 +241,19 @@ bool isDigit(dchar c) @safe pure nothrow @nogc assert(!isDigit('ï¼”')); // full-width digit four (U+FF14) } -unittest +@safe unittest { - foreach(c; digits) + foreach (c; digits) assert(isDigit(c)); - foreach(c; chain(letters, whitespace)) + foreach (c; chain(letters, whitespace)) assert(!isDigit(c)); } /++ Params: c = The character to test. - Returns: Whether $(D c) is a digit in base 8 (0..7). + Returns: Whether $(D c) is a digit in base 8 (0 .. 7). +/ bool isOctalDigit(dchar c) @safe pure nothrow @nogc { @@ -236,19 +270,19 @@ bool isOctalDigit(dchar c) @safe pure nothrow @nogc assert(!isOctalDigit('#')); } -unittest +@safe unittest { - foreach(c; octalDigits) + foreach (c; octalDigits) assert(isOctalDigit(c)); - foreach(c; chain(letters, ['8', '9'], whitespace)) + foreach (c; chain(letters, ['8', '9'], whitespace)) assert(!isOctalDigit(c)); } /++ Params: c = The character to test. - Returns: Whether $(D c) is a digit in base 16 (0..9, A..F, a..f). + Returns: Whether $(D c) is a digit in base 16 (0 .. 9, A .. F, a .. f). +/ bool isHexDigit(dchar c) @safe pure nothrow @nogc { @@ -266,12 +300,12 @@ bool isHexDigit(dchar c) @safe pure nothrow @nogc assert(!isHexDigit('#')); } -unittest +@safe unittest { - foreach(c; fullHexDigits) + foreach (c; fullHexDigits) assert(isHexDigit(c)); - foreach(c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace)) + foreach (c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace)) assert(!isHexDigit(c)); } @@ -303,12 +337,12 @@ bool isWhite(dchar c) @safe pure nothrow @nogc assert(!isWhite('\u00A0')); // std.ascii.isWhite } -unittest +@safe unittest { - foreach(c; whitespace) + foreach (c; whitespace) assert(isWhite(c)); - foreach(c; chain(digits, letters)) + foreach (c; chain(digits, letters)) assert(!isWhite(c)); } @@ -339,13 +373,13 @@ bool isControl(dchar c) @safe pure nothrow @nogc assert(!isControl('\u2029')); } -unittest +@safe unittest { - foreach(dchar c; 0 .. 32) + foreach (dchar c; 0 .. 32) assert(isControl(c)); assert(isControl(127)); - foreach(c; chain(digits, letters, [' '])) + foreach (c; chain(digits, letters, [' '])) assert(!isControl(c)); } @@ -383,11 +417,11 @@ bool isPunctuation(dchar c) @safe pure nothrow @nogc assert(!isPunctuation('\u2012')); // (U+2012 = en-dash) } -unittest +@safe unittest { - foreach(dchar c; 0 .. 128) + foreach (dchar c; 0 .. 128) { - if(isControl(c) || isAlphaNum(c) || c == ' ') + if (isControl(c) || isAlphaNum(c) || c == ' ') assert(!isPunctuation(c)); else assert(isPunctuation(c)); @@ -419,11 +453,11 @@ bool isGraphical(dchar c) @safe pure nothrow @nogc assert(!isGraphical('á')); } -unittest +@safe unittest { - foreach(dchar c; 0 .. 128) + foreach (dchar c; 0 .. 128) { - if(isControl(c) || c == ' ') + if (isControl(c) || c == ' ') assert(!isGraphical(c)); else assert(isGraphical(c)); @@ -454,11 +488,11 @@ bool isPrintable(dchar c) @safe pure nothrow @nogc assert(!isPrintable('á')); } -unittest +@safe unittest { - foreach(dchar c; 0 .. 128) + foreach (dchar c; 0 .. 128) { - if(isControl(c)) + if (isControl(c)) assert(!isPrintable(c)); else assert(isPrintable(c)); @@ -469,8 +503,9 @@ unittest /++ Params: c = The character to test. Returns: Whether or not $(D c) is in the ASCII character set - i.e. in the - range 0..0x7F. + range 0 .. 0x7F. +/ +pragma(inline, true) bool isASCII(dchar c) @safe pure nothrow @nogc { return c <= 0x7F; @@ -483,9 +518,9 @@ bool isASCII(dchar c) @safe pure nothrow @nogc assert(!isASCII('á')); } -unittest +@safe unittest { - foreach(dchar c; 0 .. 128) + foreach (dchar c; 0 .. 128) assert(isASCII(c)); assert(!isASCII(128)); @@ -504,7 +539,7 @@ unittest ASCII character, otherwise $(D c) itself. +/ auto toLower(C)(C c) - if(is(C : dchar)) +if (is(C : dchar)) { import std.traits : isAggregateType, OriginalType, Unqual; @@ -514,7 +549,7 @@ auto toLower(C)(C c) else alias R = Unqual!OC; - return isUpper(c) ? cast(R)(cast(R)c + 'a' - 'A') : cast(R)c; + return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c; } /// @@ -531,20 +566,20 @@ auto toLower(C)(C c) @safe pure nothrow unittest { - foreach(C; TypeTuple!(char, wchar, dchar, immutable char, ubyte)) + foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte)) { - foreach(i, c; uppercase) - assert(toLower(cast(C)c) == lowercase[i]); + foreach (i, c; uppercase) + assert(toLower(cast(C) c) == lowercase[i]); - foreach(C c; 0 .. 128) + foreach (C c; 0 .. 128) { - if(c < 'A' || c > 'Z') + if (c < 'A' || c > 'Z') assert(toLower(c) == c); else assert(toLower(c) != c); } - foreach(C c; 128 .. C.max) + foreach (C c; 128 .. C.max) assert(toLower(c) == c); //CTFE @@ -566,7 +601,7 @@ auto toLower(C)(C c) character, otherwise $(D c) itself. +/ auto toUpper(C)(C c) - if(is(C : dchar)) +if (is(C : dchar)) { import std.traits : isAggregateType, OriginalType, Unqual; @@ -576,7 +611,7 @@ auto toUpper(C)(C c) else alias R = Unqual!OC; - return isLower(c) ? cast(R)(cast(R)c - ('a' - 'A')) : cast(R)c; + return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c; } /// @@ -592,20 +627,20 @@ auto toUpper(C)(C c) @safe pure nothrow unittest { - foreach(C; TypeTuple!(char, wchar, dchar, immutable char, ubyte)) + foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte)) { - foreach(i, c; lowercase) - assert(toUpper(cast(C)c) == uppercase[i]); + foreach (i, c; lowercase) + assert(toUpper(cast(C) c) == uppercase[i]); - foreach(C c; 0 .. 128) + foreach (C c; 0 .. 128) { - if(c < 'a' || c > 'z') + if (c < 'a' || c > 'z') assert(toUpper(c) == c); else assert(toUpper(c) != c); } - foreach(C c; 128 .. C.max) + foreach (C c; 128 .. C.max) assert(toUpper(c) == c); //CTFE @@ -615,7 +650,7 @@ auto toUpper(C)(C c) } -unittest //Test both toUpper and toLower with non-builtin +@safe unittest //Test both toUpper and toLower with non-builtin { //User Defined [Char|Wchar|Dchar] static struct UDC { char c; alias c this; } @@ -631,7 +666,7 @@ unittest //Test both toUpper and toLower with non-builtin enum UDDE : UDD {a = UDD('a'), A = UDD('A')} //User defined types with implicit cast to dchar test. - foreach (Char; TypeTuple!(UDC, UDW, UDD)) + foreach (Char; AliasSeq!(UDC, UDW, UDD)) { assert(toLower(Char('a')) == 'a'); assert(toLower(Char('A')) == 'a'); @@ -642,7 +677,7 @@ unittest //Test both toUpper and toLower with non-builtin } //Various enum tests. - foreach (Enum; TypeTuple!(CE, WE, DE, UDCE, UDWE, UDDE)) + foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE)) { assert(toLower(Enum.a) == 'a'); assert(toLower(Enum.A) == 'a'); @@ -655,7 +690,7 @@ unittest //Test both toUpper and toLower with non-builtin } //Return value type tests for enum of non-UDT. These should be the original type. - foreach (T; TypeTuple!(CE, WE, DE)) + foreach (T; AliasSeq!(CE, WE, DE)) { alias C = OriginalType!T; static assert(is(typeof(toLower(T.init)) == C)); @@ -663,7 +698,7 @@ unittest //Test both toUpper and toLower with non-builtin } //Return value tests for UDT and enum of UDT. These should be dchar - foreach (T; TypeTuple!(UDC, UDW, UDD, UDCE, UDWE, UDDE)) + foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE)) { static assert(is(typeof(toLower(T.init)) == dchar)); static assert(is(typeof(toUpper(T.init)) == dchar)); diff --git a/std/base64.d b/std/base64.d index f4ff9a90b1b..63c00a6ed09 100644 --- a/std/base64.d +++ b/std/base64.d @@ -1,10 +1,18 @@ // Written in the D programming language. /** - * Encoding / Decoding Base64 format. + * Support for Base64 encoding and decoding. * - * Implemented according to $(WEB tools.ietf.org/html/rfc4648, - * RFC 4648 - The Base16, Base32, and Base64 Data Encodings). + * This module provides two default implementations of Base64 encoding, + * $(LREF Base64) with a standard encoding alphabet, and a variant + * $(LREF Base64URL) that has a modified encoding alphabet designed to be + * safe for embedding in URLs and filenames. + * + * Both variants are implemented as instantiations of the template + * $(LREF Base64Impl). Most users will not need to use this template + * directly; however, it can be used to create customized Base64 encodings, + * such as one that omits padding characters, or one that is safe to embed + * inside a regular expression. * * Example: * ----- @@ -17,7 +25,7 @@ * assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); * ----- * - * Support Range interface using Encoder / Decoder. + * The range API is supported for both encoding and decoding: * * Example: * ----- @@ -36,10 +44,16 @@ * writeln(mime64.data); * ----- * + * References: + * $(LINK2 https://tools.ietf.org/html/rfc4648, RFC 4648 - The Base16, Base32, and Base64 + * Data Encodings) + * * Copyright: Masahiro Nakagawa 2010-. - * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Masahiro Nakagawa, Daniel Murphy (Single value Encoder and Decoder) * Source: $(PHOBOSSRC std/_base64.d) + * Macros: + * LREF2=$(D $2) */ module std.base64; @@ -47,30 +61,69 @@ import std.exception; // enforce import std.range.primitives; // isInputRange, isOutputRange, isForwardRange, ElementType, hasLength import std.traits; // isArray +// Make sure module header code examples work correctly. +@safe unittest +{ + ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]; + + const(char)[] encoded = Base64.encode(data); + assert(encoded == "FPucA9l+"); + + ubyte[] decoded = Base64.decode("FPucA9l+"); + assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); +} /** - * The Base64 + * Implementation of standard _Base64 encoding. + * + * See $(LREF Base64Impl) for a description of available methods. */ alias Base64 = Base64Impl!('+', '/'); +/// +@safe unittest +{ + ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f]; + assert(Base64.encode(data) == "g9cwegE/"); + assert(Base64.decode("g9cwegE/") == data); +} + /** - * The "URL and Filename safe" Base64 + * Variation of Base64 encoding that is safe for use in URLs and filenames. + * + * See $(LREF Base64Impl) for a description of available methods. */ alias Base64URL = Base64Impl!('-', '_'); +/// +@safe unittest +{ + ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f]; + assert(Base64URL.encode(data) == "g9cwegE_"); + assert(Base64URL.decode("g9cwegE_") == data); +} + /** - * Core implementation for Base64 format. + * Template for implementing Base64 encoding and decoding. + * + * For most purposes, direct usage of this template is not necessary; instead, + * this module provides two default implementations: $(LREF Base64) and + * $(LREF Base64URL), that implement basic Base64 encoding and a variant + * intended for use in URLs and filenames, respectively. + * + * Customized Base64 encoding schemes can be implemented by instantiating this + * template with the appropriate arguments. For example: * - * Example: * ----- - * alias Base64 = Base64Impl!('+', '/'); // The Base64 format(Already defined). - * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); // non-standard Base64 format for Regular expression + * // Non-standard Base64 format for embedding in regular expressions. + * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); * ----- * * NOTE: - * encoded-string doesn't have padding character if set Padding parameter to NoPadding. + * Encoded strings will not have any padding if the $(D Padding) parameter is + * set to $(D NoPadding). */ template Base64Impl(char Map62th, char Map63th, char Padding = '=') { @@ -101,13 +154,14 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Calculates the minimum length for encoding. + * Calculates the length needed to store the encoded string corresponding + * to an input of the given length. * * Params: - * sourceLength = the length of source array. + * sourceLength = Length of the source array. * * Returns: - * the calculated length using $(D_PARAM sourceLength). + * The length of a Base64 encoding of an array of the given length. */ @safe pure nothrow size_t encodeLength(in size_t sourceLength) @@ -118,19 +172,33 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') return (sourceLength / 3 + (sourceLength % 3 ? 1 : 0)) * 4; } + /// + @safe unittest + { + ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; + + // Allocate a buffer large enough to hold the encoded string. + auto buf = new char[encodeLength(data.length)]; + + Base64.encode(data, buf); + assert(buf == "Gis8TV1u"); + } + // ubyte[] to char[] /** - * Encodes $(D_PARAM source) into $(D_PARAM buffer). + * Encode $(D_PARAM source) into a $(D char[]) buffer using Base64 + * encoding. * * Params: - * source = an $(D InputRange) to encode. - * buffer = a buffer to store encoded result. + * source = The $(LINK2 std_range_primitives.html#isInputRange, input + * range) to _encode. + * buffer = The $(D char[]) buffer to store the encoded result. * * Returns: - * the encoded string that slices buffer. + * The slice of $(D_PARAM buffer) that contains the encoded string. */ @trusted pure char[] encode(R1, R2)(in R1 source, R2 buffer) if (isArray!R1 && is(ElementType!R1 : ubyte) && @@ -154,7 +222,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') auto bufptr = buffer.ptr; auto srcptr = source.ptr; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2]; *bufptr++ = EncodeMap[val >> 18 ]; *bufptr++ = EncodeMap[val >> 12 & 0x3f]; @@ -163,19 +232,22 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') srcptr += 3; } - if (remain) { + if (remain) + { immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0); *bufptr++ = EncodeMap[val >> 18 ]; *bufptr++ = EncodeMap[val >> 12 & 0x3f]; - final switch (remain) { + final switch (remain) + { case 2: *bufptr++ = EncodeMap[val >> 6 & 0x3f]; static if (Padding != NoPadding) *bufptr++ = Padding; break; case 1: - static if (Padding != NoPadding) { + static if (Padding != NoPadding) + { *bufptr++ = Padding; *bufptr++ = Padding; } @@ -184,7 +256,23 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } // encode method can't assume buffer length. So, slice needed. - return buffer[0..bufptr - buffer.ptr]; + return buffer[0 .. bufptr - buffer.ptr]; + } + + /// + @safe unittest + { + ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f]; + char[32] buffer; // much bigger than necessary + + // Just to be sure... + auto encodedLength = Base64.encodeLength(data.length); + assert(buffer.length >= encodedLength); + + // encode() returns a slice to the provided buffer. + auto encoded = Base64.encode(data, buffer[]); + assert(encoded is buffer[0 .. encodedLength]); + assert(encoded == "g9cwegE/"); } @@ -216,7 +304,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable remain = srcLen % 3; auto bufptr = buffer.ptr; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable v1 = source.front; source.popFront(); immutable v2 = source.front; source.popFront(); immutable v3 = source.front; source.popFront(); @@ -227,9 +316,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') *bufptr++ = EncodeMap[val & 0x3f]; } - if (remain) { + if (remain) + { size_t val = source.front << 16; - if (remain == 2) { + if (remain == 2) + { source.popFront(); val |= source.front << 8; } @@ -237,14 +328,16 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') *bufptr++ = EncodeMap[val >> 18 ]; *bufptr++ = EncodeMap[val >> 12 & 0x3f]; - final switch (remain) { + final switch (remain) + { case 2: *bufptr++ = EncodeMap[val >> 6 & 0x3f]; static if (Padding != NoPadding) *bufptr++ = Padding; break; case 1: - static if (Padding != NoPadding) { + static if (Padding != NoPadding) + { *bufptr++ = Padding; *bufptr++ = Padding; } @@ -253,10 +346,14 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } // @@@BUG@@@ Workaround for DbC problem. See comment on 'out'. - version (unittest) assert(bufptr - buffer.ptr == encodeLength(srcLen), "The length of result is different from Base64"); + version (unittest) + assert( + bufptr - buffer.ptr == encodeLength(srcLen), + "The length of result is different from Base64" + ); // encode method can't assume buffer length. So, slice needed. - return buffer[0..bufptr - buffer.ptr]; + return buffer[0 .. bufptr - buffer.ptr]; } @@ -264,17 +361,22 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Encodes $(D_PARAM source) into $(D_PARAM range). + * Encodes $(D_PARAM source) into an + * $(LINK2 std_range_primitives.html#isOutputRange, output range) using + * Base64 encoding. * * Params: - * source = an $(D InputRange) to encode. - * range = an $(D OutputRange) to put encoded result. + * source = The $(LINK2 std_range_primitives.html#isInputRange, input + * range) to _encode. + * range = The $(LINK2 std_range_primitives.html#isOutputRange, output + * range) to store the encoded result. * * Returns: - * the number of calling put. + * The number of times the output range's $(D put) method was invoked. */ - size_t encode(R1, R2)(in R1 source, R2 range) if (isArray!R1 && is(ElementType!R1 : ubyte) && - !is(R2 == char[])) + size_t encode(R1, R2)(in R1 source, auto ref R2 range) + if (isArray!R1 && is(ElementType!R1 : ubyte) && + !is(R2 == char[]) && isOutputRange!(R2, char)) out(result) { assert(result == encodeLength(source.length), "The number of put is different from the length of Base64"); @@ -290,7 +392,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') auto srcptr = source.ptr; size_t pcount; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2]; put(range, EncodeMap[val >> 18 ]); put(range, EncodeMap[val >> 12 & 0x3f]); @@ -300,24 +403,28 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') pcount += 4; } - if (remain) { + if (remain) + { immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0); put(range, EncodeMap[val >> 18 ]); put(range, EncodeMap[val >> 12 & 0x3f]); pcount += 2; - final switch (remain) { + final switch (remain) + { case 2: put(range, EncodeMap[val >> 6 & 0x3f]); pcount++; - static if (Padding != NoPadding) { + static if (Padding != NoPadding) + { put(range, Padding); pcount++; } break; case 1: - static if (Padding != NoPadding) { + static if (Padding != NoPadding) + { put(range, Padding); put(range, Padding); pcount += 2; @@ -329,6 +436,25 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') return pcount; } + /// + @system unittest + { + // @system because encode for OutputRange is @system + struct OutputRange + { + char[] result; + void put(const(char) ch) @safe { result ~= ch; } + } + + ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; + + // This overload of encode() returns the number of calls to the output + // range's put method. + OutputRange output; + assert(Base64.encode(data, output) == 8); + assert(output.result == "Gis8TV1u"); + } + // InputRange to OutputRange @@ -336,9 +462,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - size_t encode(R1, R2)(R1 source, R2 range) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : ubyte) && hasLength!R1 && - !is(R2 == char[]) && isOutputRange!(R2, char)) + size_t encode(R1, R2)(R1 source, auto ref R2 range) + if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) && + hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char)) out(result) { // @@@BUG@@@ Workaround for DbC problem. @@ -354,7 +480,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable remain = srcLen % 3; size_t pcount; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable v1 = source.front; source.popFront(); immutable v2 = source.front; source.popFront(); immutable v3 = source.front; source.popFront(); @@ -366,9 +493,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') pcount += 4; } - if (remain) { + if (remain) + { size_t val = source.front << 16; - if (remain == 2) { + if (remain == 2) + { source.popFront(); val |= source.front << 8; } @@ -377,18 +506,21 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') put(range, EncodeMap[val >> 12 & 0x3f]); pcount += 2; - final switch (remain) { + final switch (remain) + { case 2: put(range, EncodeMap[val >> 6 & 0x3f]); pcount++; - static if (Padding != NoPadding) { + static if (Padding != NoPadding) + { put(range, Padding); pcount++; } break; case 1: - static if (Padding != NoPadding) { + static if (Padding != NoPadding) + { put(range, Padding); put(range, Padding); pcount += 2; @@ -398,16 +530,28 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } // @@@BUG@@@ Workaround for DbC problem. - version (unittest) assert(pcount == encodeLength(srcLen), "The number of put is different from the length of Base64"); + version (unittest) + assert( + pcount == encodeLength(srcLen), + "The number of put is different from the length of Base64" + ); return pcount; } /** - * Encodes $(D_PARAM source) to new buffer. + * Encodes $(D_PARAM source) to newly-allocated buffer. + * + * This convenience method alleviates the need to manually manage output + * buffers. + * + * Params: + * source = The $(LINK2 std_range_primitives.html#isInputRange, input + * range) to _encode. * - * Shortcut to encode(source, buffer) function. + * Returns: + * A newly-allocated $(D char[]) buffer containing the encoded string. */ @safe pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte)) @@ -415,6 +559,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') return encode(source, new char[encodeLength(source.length)]); } + /// + @safe unittest + { + ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; + assert(Base64.encode(data) == "Gis8TV1u"); + } + /** * ditto @@ -427,7 +578,15 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range that encodes chunk data at a time. + * An $(LINK2 std_range_primitives.html#isInputRange, input range) that + * iterates over the respective Base64 encodings of a range of data items. + * + * This range will be a $(LINK2 std_range_primitives.html#isForwardRange, + * forward range) if the underlying data source is at least a forward + * range. + * + * Note: This struct is not intended to be created in user code directly; + * use the $(LREF encoder) function instead. */ struct Encoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) || is(ElementType!Range : const(char)[]))) @@ -446,10 +605,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that checks iteration state. - * * Returns: - * true if there are no more elements to be iterated. + * true if there is no more encoded data left. */ @property @trusted bool empty() @@ -459,10 +616,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that returns the currently iterated element. - * - * Returns: - * the encoded string. + * Returns: The current chunk of encoded data. */ @property @safe nothrow char[] front() @@ -472,10 +626,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that advances the range to its next element. + * Advance the range to the next chunk of encoded data. * * Throws: - * an Exception when you try to call popFront on empty range. + * $(D Base64Exception) If invoked when + * $(LREF2 .Base64Impl.Encoder.empty, empty) returns $(D true). */ void popFront() { @@ -494,12 +649,17 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } - static if (isForwardRange!Range) { + static if (isForwardRange!Range) + { /** - * Captures a Range state. + * Save the current iteration state of the range. + * + * This method is only available if the underlying range is a + * $(LINK2 std_range_primitives.html#isForwardRange, forward + * range). * * Returns: - * a copy of $(D this). + * A copy of $(D this). */ @property typeof(this) save() @@ -508,7 +668,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') encoder.range_ = range_.save; encoder.buffer_ = buffer_.dup; - encoder.encoded_ = encoder.buffer_[0..encoded_.length]; + encoder.encoded_ = encoder.buffer_[0 .. encoded_.length]; return encoder; } @@ -529,7 +689,14 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range that encodes single character at a time. + * An $(LINK2 std_range_primitives.html#isInputRange, input range) that + * iterates over the encoded bytes of the given source data. + * + * It will be a $(LINK2 std_range_primitives.html#isForwardRange, forward + * range) if the underlying data source is at least a forward range. + * + * Note: This struct is not intended to be created in user code directly; + * use the $(LREF encoder) function instead. */ struct Encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte)) { @@ -554,10 +721,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that checks iteration state. - * * Returns: - * true if there are no more elements to be iterated. + * true if there are no more encoded characters to be iterated. */ @property @safe nothrow bool empty() const @@ -570,10 +735,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that returns the currently iterated element. - * - * Returns: - * the encoded character. + * Returns: The current encoded character. */ @property @safe nothrow ubyte front() @@ -583,29 +745,33 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that advances the range to its next element. + * Advance to the next encoded character. * * Throws: - * an Exception when you try to call popFront on empty range. + * $(D Base64Exception) If invoked when $(LREF2 .Base64Impl.Encoder.empty.2, + * empty) returns $(D true). */ void popFront() { enforce(!empty, new Base64Exception("Cannot call popFront on Encoder with no data remaining")); static if (Padding != NoPadding) - if (padding) { + if (padding) + { first = Padding; pos = -1; padding--; return; } - if (range_.empty) { + if (range_.empty) + { pos = -1; return; } - final switch (pos) { + final switch (pos) + { case 0: first = EncodeMap[range_.front >> 2]; break; @@ -613,10 +779,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable t = (range_.front & 0b11) << 4; range_.popFront(); - if (range_.empty) { + if (range_.empty) + { first = EncodeMap[t]; padding = 3; - } else { + } + else + { first = EncodeMap[t | (range_.front >> 4)]; } break; @@ -624,10 +793,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable t = (range_.front & 0b1111) << 2; range_.popFront(); - if (range_.empty) { + if (range_.empty) + { first = EncodeMap[t]; padding = 2; - } else { + } + else + { first = EncodeMap[t | (range_.front >> 6)]; } break; @@ -641,12 +813,17 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } - static if (isForwardRange!Range) { + static if (isForwardRange!Range) + { /** - * Captures a Range state. + * Save the current iteration state of the range. + * + * This method is only available if the underlying range is a + * $(LINK2 std_range_primitives.html#isForwardRange, forward + * range). * * Returns: - * a copy of $(D this). + * A copy of $(D this). */ @property typeof(this) save() @@ -660,11 +837,27 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Iterates through an $(D InputRange) at a time by using $(D Encoder). + * Construct an $(D Encoder) that iterates over the Base64 encoding of the + * given $(LINK2 std_range_primitives.html#isInputRange, input range). * - * Default $(D Encoder) encodes chunk data. + * Params: + * range = An $(LINK2 std_range_primitives.html#isInputRange, input + * range) over the data to be encoded. + * + * Returns: + * If $(D_PARAM range) is a range of bytes, an $(D Encoder) that iterates + * over the bytes of the corresponding Base64 encoding. + * + * If $(D_PARAM range) is a range of ranges of bytes, an $(D Encoder) that + * iterates over the Base64 encoded strings of each element of the range. + * + * In both cases, the returned $(D Encoder) will be a + * $(LINK2 std_range_primitives.html#isForwardRange, forward range) if the + * given $(D range) is at least a forward range, otherwise it will be only + * an input range. * * Example: + * This example encodes the input one line at a time. * ----- * File f = File("text.txt", "r"); * scope(exit) f.close(); @@ -676,10 +869,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * } * ----- * - * In addition, You can use $(D Encoder) that returns encoded single character. - * This $(D Encoder) performs Range-based and lazy encoding. - * * Example: + * This example encodes the input data one byte at a time. * ----- * ubyte[] data = cast(ubyte[]) "0123456789"; * @@ -689,12 +880,6 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * writeln(encoded); * } * ----- - * - * Params: - * range = an $(D InputRange) to iterate. - * - * Returns: - * a $(D Encoder) object instantiated and initialized according to the arguments. */ Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range) { @@ -723,13 +908,15 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Calculates the minimum length for decoding. + * Given a Base64 encoded string, calculates the length of the decoded + * string. * * Params: - * sourceLength = the length of source array. + * sourceLength = The length of the Base64 encoding. * * Returns: - * calculated length using $(D_PARAM sourceLength). + * The length of the decoded string corresponding to a Base64 encoding of + * length $(D_PARAM sourceLength). */ @safe pure nothrow size_t decodeLength(in size_t sourceLength) @@ -740,6 +927,18 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') return (sourceLength / 4) * 3; } + /// + @safe unittest + { + auto encoded = "Gis8TV1u"; + + // Allocate a sufficiently large buffer to hold to decoded result. + auto buffer = new ubyte[Base64.decodeLength(encoded.length)]; + + Base64.decode(encoded, buffer); + assert(buffer == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); + } + // Used in decode contracts. Calculates the actual size the decoded // result should have, taking into account trailing padding. @@ -764,24 +963,26 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Decodes $(D_PARAM source) into $(D_PARAM buffer). + * Decodes $(D_PARAM source) into the given buffer. * * Params: - * source = an $(D InputRange) to decode. - * buffer = a buffer to store decoded result. + * source = The $(LINK2 std_range_primitives.html#isInputRange, input + * range) to _decode. + * buffer = The buffer to store decoded result. * * Returns: - * the decoded string that slices buffer. + * The slice of $(D_PARAM buffer) containing the decoded result. * * Throws: - * an Exception if $(D_PARAM source) has character outside base-alphabet. + * $(D Base64Exception) if $(D_PARAM source) contains characters outside the + * base alphabet of the current Base64 encoding scheme. */ @trusted pure ubyte[] decode(R1, R2)(in R1 source, R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) && is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) in { - assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding"); + assert(buffer.length >= realDecodeLength(source), "Insufficient buffer for decoding"); } out(result) { @@ -800,7 +1001,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') auto srcptr = source.ptr; auto bufptr = buffer.ptr; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable v1 = decodeChar(*srcptr++); immutable v2 = decodeChar(*srcptr++); @@ -819,10 +1021,12 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff); } - static if (Padding == NoPadding) { + static if (Padding == NoPadding) + { immutable remain = srcLen % 4; - if (remain) { + if (remain) + { immutable v1 = decodeChar(*srcptr++); immutable v2 = decodeChar(*srcptr++); @@ -833,9 +1037,24 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } } - return buffer[0..bufptr - buffer.ptr]; + return buffer[0 .. bufptr - buffer.ptr]; } + /// + @safe unittest + { + auto encoded = "Gis8TV1u"; + ubyte[32] buffer; // much bigger than necessary + + // Just to be sure... + auto decodedLength = Base64.decodeLength(encoded.length); + assert(buffer.length >= decodedLength); + + // decode() returns a slice of the given buffer. + auto decoded = Base64.decode(encoded, buffer[]); + assert(decoded is buffer[0 .. decodedLength]); + assert(decoded == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); + } // InputRange to ubyte[] @@ -867,7 +1086,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable blocks = srcLen / 4; auto bufptr = buffer.ptr; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable v1 = decodeChar(source.front); source.popFront(); immutable v2 = decodeChar(source.front); source.popFront(); @@ -888,16 +1108,19 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') source.popFront(); } - static if (Padding == NoPadding) { + static if (Padding == NoPadding) + { immutable remain = srcLen % 4; - if (remain) { + if (remain) + { immutable v1 = decodeChar(source.front); source.popFront(); immutable v2 = decodeChar(source.front); *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4); - if (remain == 3) { + if (remain == 3) + { source.popFront(); *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff); } @@ -905,9 +1128,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } // @@@BUG@@@ Workaround for DbC problem. - version (unittest) assert((bufptr - buffer.ptr) >= (decodeLength(srcLen) - 2), "The length of result is smaller than expected length"); + version (unittest) + assert( + (bufptr - buffer.ptr) >= (decodeLength(srcLen) - 2), + "The length of result is smaller than expected length" + ); - return buffer[0..bufptr - buffer.ptr]; + return buffer[0 .. bufptr - buffer.ptr]; } @@ -915,20 +1142,25 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Decodes $(D_PARAM source) into $(D_PARAM range). + * Decodes $(D_PARAM source) into a given + * $(LINK2 std_range_primitives.html#isOutputRange, output range). * * Params: - * source = an $(D InputRange) to decode. - * range = an $(D OutputRange) to put decoded result + * source = The $(LINK2 std_range_primitives.html#isInputRange, input + * range) to _decode. + * range = The $(LINK2 std_range_primitives.html#isOutputRange, output + * range) to store the decoded result. * * Returns: - * the number of calling put. + * The number of times the output range's $(D put) method was invoked. * * Throws: - * an Exception if $(D_PARAM source) has character outside base-alphabet. + * $(D Base64Exception) if $(D_PARAM source) contains characters outside the + * base alphabet of the current Base64 encoding scheme. */ - size_t decode(R1, R2)(in R1 source, R2 range) if (isArray!R1 && is(ElementType!R1 : dchar) && - !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + size_t decode(R1, R2)(in R1 source, auto ref R2 range) + if (isArray!R1 && is(ElementType!R1 : dchar) && + !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) out(result) { immutable expect = realDecodeLength(source); @@ -946,7 +1178,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') auto srcptr = source.ptr; size_t pcount; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable v1 = decodeChar(*srcptr++); immutable v2 = decodeChar(*srcptr++); @@ -968,17 +1201,20 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') pcount++; } - static if (Padding == NoPadding) { + static if (Padding == NoPadding) + { immutable remain = srcLen % 4; - if (remain) { + if (remain) + { immutable v1 = decodeChar(*srcptr++); immutable v2 = decodeChar(*srcptr++); put(range, cast(ubyte)(v1 << 2 | v2 >> 4)); pcount++; - if (remain == 3) { + if (remain == 3) + { put(range, cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff)); pcount++; } @@ -988,6 +1224,21 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') return pcount; } + /// + @system unittest + { + struct OutputRange + { + ubyte[] result; + void put(ubyte b) { result ~= b; } + } + OutputRange output; + + // This overload of decode() returns the number of calls to put(). + assert(Base64.decode("Gis8TV1u", output) == 6); + assert(output.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); + } + // InputRange to OutputRange @@ -995,9 +1246,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - size_t decode(R1, R2)(R1 source, R2 range) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : dchar) && hasLength!R1 && - !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + size_t decode(R1, R2)(R1 source, auto ref R2 range) + if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) && + hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) out(result) { // @@@BUG@@@ Workaround for DbC problem. @@ -1015,7 +1266,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable blocks = srcLen / 4; size_t pcount; - foreach (Unused; 0..blocks) { + foreach (Unused; 0 .. blocks) + { immutable v1 = decodeChar(source.front); source.popFront(); immutable v2 = decodeChar(source.front); source.popFront(); @@ -1039,17 +1291,20 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') pcount++; } - static if (Padding == NoPadding) { + static if (Padding == NoPadding) + { immutable remain = srcLen % 4; - if (remain) { + if (remain) + { immutable v1 = decodeChar(source.front); source.popFront(); immutable v2 = decodeChar(source.front); put(range, cast(ubyte)(v1 << 2 | v2 >> 4)); pcount++; - if (remain == 3) { + if (remain == 3) + { source.popFront(); put(range, cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff)); pcount++; @@ -1058,16 +1313,28 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } // @@@BUG@@@ Workaround for DbC problem. - version (unittest) assert(pcount >= (decodeLength(srcLen) - 2), "The length of result is smaller than expected length"); + version (unittest) + assert( + pcount >= (decodeLength(srcLen) - 2), + "The length of result is smaller than expected length" + ); return pcount; } /** - * Decodes $(D_PARAM source) into new buffer. + * Decodes $(D_PARAM source) into newly-allocated buffer. + * + * This convenience method alleviates the need to manually manage decoding + * buffers. + * + * Params: + * source = The $(LINK2 std_range_primitives.html#isInputRange, input + * range) to _decode. * - * Shortcut to decode(source, buffer) function. + * Returns: + * A newly-allocated $(D ubyte[]) buffer containing the decoded string. */ @safe pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar)) @@ -1075,6 +1342,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') return decode(source, new ubyte[decodeLength(source.length)]); } + /// + @safe unittest + { + auto data = "Gis8TV1u"; + assert(Base64.decode(data) == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); + } + /** * ditto @@ -1087,7 +1361,15 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range that decodes chunk data at a time. + * An $(LINK2 std_range_primitives.html#isInputRange, input range) that + * iterates over the decoded data of a range of Base64 encodings. + * + * This range will be a $(LINK2 std_range_primitives.html#isForwardRange, + * forward range) if the underlying data source is at least a forward + * range. + * + * Note: This struct is not intended to be created in user code directly; + * use the $(LREF decoder) function instead. */ struct Decoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(char)[]) || is(ElementType!Range : const(ubyte)[]))) @@ -1106,8 +1388,6 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that checks iteration state. - * * Returns: * true if there are no more elements to be iterated. */ @@ -1119,10 +1399,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that returns the currently iterated element. - * - * Returns: - * the decoded result. + * Returns: The decoding of the current element in the input. */ @property @safe nothrow ubyte[] front() @@ -1132,10 +1409,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that advances the range to its next element. + * Advance to the next element in the input to be decoded. * * Throws: - * an Exception when you try to call popFront on empty range. + * $(D Base64Exception) if invoked when $(LREF2 .Base64Impl.Decoder.empty, + * empty) returns $(D true). */ void popFront() { @@ -1151,12 +1429,16 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } - static if (isForwardRange!Range) { + static if (isForwardRange!Range) + { /** - * Captures a Range state. + * Saves the current iteration state. * - * Returns: - * a copy of $(D this). + * This method is only available if the underlying range is a + * $(LINK2 std_range_primitives.html#isForwardRange, forward + * range). + * + * Returns: A copy of $(D this). */ @property typeof(this) save() @@ -1165,7 +1447,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') decoder.range_ = range_.save; decoder.buffer_ = buffer_.dup; - decoder.decoded_ = decoder.buffer_[0..decoded_.length]; + decoder.decoded_ = decoder.buffer_[0 .. decoded_.length]; return decoder; } @@ -1177,13 +1459,18 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') { auto data = cast(const(char)[])range_.front; - static if (Padding == NoPadding) { - while (data.length % 4 == 1) { + static if (Padding == NoPadding) + { + while (data.length % 4 == 1) + { range_.popFront(); data ~= cast(const(char)[])range_.front; } - } else { - while (data.length % 4 != 0) { + } + else + { + while (data.length % 4 != 0) + { range_.popFront(); data ~= cast(const(char)[])range_.front; } @@ -1199,7 +1486,15 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range that decodes single character at a time. + * An $(LINK2 std_range_primitives.html#isInputRange, input range) that + * iterates over the bytes of data decoded from a Base64 encoded string. + * + * This range will be a $(LINK2 std_range_primitives.html#isForwardRange, + * forward range) if the underlying data source is at least a forward + * range. + * + * Note: This struct is not intended to be created in user code directly; + * use the $(LREF decoder) function instead. */ struct Decoder(Range) if (isInputRange!Range && is(ElementType!Range : char)) { @@ -1227,8 +1522,6 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that checks iteration state. - * * Returns: * true if there are no more elements to be iterated. */ @@ -1240,10 +1533,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that returns the currently iterated element. - * - * Returns: - * the decoded result. + * Returns: The current decoded byte. */ @property @safe nothrow ubyte front() @@ -1253,21 +1543,25 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Range primitive operation that advances the range to its next element. + * Advance to the next decoded byte. * * Throws: - * an Exception when you try to call popFront on empty range. + * $(D Base64Exception) if invoked when $(LREF2 .Base64Impl.Decoder.empty, + * empty) returns $(D true). */ void popFront() { enforce(!empty, new Base64Exception("Cannot call popFront on Decoder with no data remaining")); - static if (Padding == NoPadding) { + static if (Padding == NoPadding) + { bool endCondition() { return range_.empty; } - } else { + } + else + { bool endCondition() { enforce(!range_.empty, new Base64Exception("Missing padding")); @@ -1275,12 +1569,14 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } } - if (range_.empty || range_.front == Padding) { + if (range_.empty || range_.front == Padding) + { pos = -1; return; } - final switch (pos) { + final switch (pos) + { case 0: enforce(!endCondition(), new Base64Exception("Premature end of data found")); @@ -1294,10 +1590,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable t = (DecodeMap[range_.front] & 0b1111) << 4; range_.popFront(); - if (endCondition()) { + if (endCondition()) + { pos = -1; return; - } else { + } + else + { first = cast(ubyte)(t | (DecodeMap[range_.front] >> 2)); } break; @@ -1305,10 +1604,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') immutable t = (DecodeMap[range_.front] & 0b11) << 6; range_.popFront(); - if (endCondition()) { + if (endCondition()) + { pos = -1; return; - } else { + } + else + { first = cast(ubyte)(t | DecodeMap[range_.front]); } @@ -1320,12 +1622,16 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') } - static if (isForwardRange!Range) { + static if (isForwardRange!Range) + { /** - * Captures a Range state. + * Saves the current iteration state. * - * Returns: - * a copy of $(D this). + * This method is only available if the underlying range is a + * $(LINK2 std_range_primitives.html#isForwardRange, forward + * range). + * + * Returns: A copy of $(D this). */ @property typeof(this) save() @@ -1339,11 +1645,34 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** - * Iterates through an $(D InputRange) at a time by using $(D Decoder). + * Construct a $(D Decoder) that iterates over the decoding of the given + * Base64 encoded data. + * + * Params: + * range = An $(LINK2 std_range_primitives.html#isInputRange, input + * range) over the data to be decoded. + * + * Returns: + * If $(D_PARAM range) is a range of characters, a $(D Decoder) that + * iterates over the bytes of the corresponding Base64 decoding. + * + * If $(D_PARAM range) is a range of ranges of characters, a $(D Decoder) + * that iterates over the decoded strings corresponding to each element of + * the range. In this case, the length of each subrange must be a multiple + * of 4; the returned _decoder does not keep track of Base64 decoding + * state across subrange boundaries. + * + * In both cases, the returned $(D Decoder) will be a + * $(LINK2 std_range_primitives.html#isForwardRange, forward range) if the + * given $(D range) is at least a forward range, otherwise it will be only + * an input range. * - * Default $(D Decoder) decodes chunk data. + * If the input data contains characters not found in the base alphabet of + * the current Base64 encoding scheme, the returned range may throw a + * $(D Base64Exception). * * Example: + * This example shows decoding over a range of input data lines. * ----- * foreach (decoded; Base64.decoder(stdin.byLine())) * { @@ -1351,10 +1680,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * } * ----- * - * In addition, You can use $(D Decoder) that returns decoded single character. - * This $(D Decoder) performs Range-based and lazy decoding. - * * Example: + * This example shows decoding one byte at a time. * ----- * auto encoded = Base64.encoder(cast(ubyte[])"0123456789"); * foreach (n; map!q{a - '0'}(Base64.decoder(encoded))) @@ -1362,16 +1689,6 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * writeln(n); * } * ----- - * - * NOTE: - * If you use $(D ByChunk), chunk-size should be the multiple of 4. - * $(D Decoder) can't judge a encode-boundary. - * - * Params: - * range = an $(D InputRange) to iterate. - * - * Returns: - * a $(D Decoder) object instantiated and initialized according to the arguments. */ Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range) { @@ -1400,13 +1717,13 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') if (chr > 0x7f) throw new Base64Exception("Base64-encoded character must be a single byte"); - return decodeChar(cast(char)chr); + return decodeChar(cast(char) chr); } } /** - * Exception thrown on Base64 errors. + * Exception thrown upon encountering Base64 encoding or decoding errors. */ class Base64Exception : Exception { @@ -1418,9 +1735,10 @@ class Base64Exception : Exception } -unittest +@system unittest { - import std.algorithm : sort, equal; + import std.algorithm.sorting : sort; + import std.algorithm.comparison : equal; import std.conv; import std.file; import std.stdio; @@ -1665,18 +1983,79 @@ unittest "foobar" : ["Zm9vYmFy", "Zm9vYmFy", "Zm9vYmFy", "Zm9vYmFy"], ]; - foreach (u, e; tests) { - assert(equal(Base64.encoder(cast(ubyte[])u), e[0])); - assert(equal(Base64.decoder(Base64.encoder(cast(ubyte[])u)), u)); + foreach (u, e; tests) + { + assert(equal(Base64.encoder(cast(ubyte[]) u), e[0])); + assert(equal(Base64.decoder(Base64.encoder(cast(ubyte[]) u)), u)); - assert(equal(Base64URL.encoder(cast(ubyte[])u), e[1])); - assert(equal(Base64URL.decoder(Base64URL.encoder(cast(ubyte[])u)), u)); + assert(equal(Base64URL.encoder(cast(ubyte[]) u), e[1])); + assert(equal(Base64URL.decoder(Base64URL.encoder(cast(ubyte[]) u)), u)); - assert(equal(Base64NoPadding.encoder(cast(ubyte[])u), e[2])); - assert(equal(Base64NoPadding.decoder(Base64NoPadding.encoder(cast(ubyte[])u)), u)); + assert(equal(Base64NoPadding.encoder(cast(ubyte[]) u), e[2])); + assert(equal(Base64NoPadding.decoder(Base64NoPadding.encoder(cast(ubyte[]) u)), u)); - assert(equal(Base64Re.encoder(cast(ubyte[])u), e[3])); - assert(equal(Base64Re.decoder(Base64Re.encoder(cast(ubyte[])u)), u)); + assert(equal(Base64Re.encoder(cast(ubyte[]) u), e[3])); + assert(equal(Base64Re.decoder(Base64Re.encoder(cast(ubyte[]) u)), u)); } } } + +// Regression control for the output range ref bug in encode. +@system unittest +{ + struct InputRange + { + ubyte[] impl = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]; + @property bool empty() { return impl.length == 0; } + @property ubyte front() { return impl[0]; } + void popFront() { impl = impl[1 .. $]; } + @property size_t length() { return impl.length; } + } + + struct OutputRange + { + char[] result; + void put(char b) { result ~= b; } + } + + InputRange ir; + OutputRange or; + assert(Base64.encode(ir, or) == 8); + assert(or.result == "Gis8TV1u"); + + // Verify that any existing workaround that uses & still works. + InputRange ir2; + OutputRange or2; + assert(Base64.encode(ir2, &or2) == 8); + assert(or2.result == "Gis8TV1u"); +} + +// Regression control for the output range ref bug in decode. +@system unittest +{ + struct InputRange + { + const(char)[] impl = "Gis8TV1u"; + @property bool empty() { return impl.length == 0; } + @property dchar front() { return impl[0]; } + void popFront() { impl = impl[1 .. $]; } + @property size_t length() { return impl.length; } + } + + struct OutputRange + { + ubyte[] result; + void put(ubyte b) { result ~= b; } + } + + InputRange ir; + OutputRange or; + assert(Base64.decode(ir, or) == 6); + assert(or.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); + + // Verify that any existing workaround that uses & still works. + InputRange ir2; + OutputRange or2; + assert(Base64.decode(ir2, &or2) == 6); + assert(or2.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]); +} diff --git a/std/bigint.d b/std/bigint.d index 7b5173cbcaf..72a7978dc40 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -11,9 +11,9 @@ * $(LI Binary exponentiation) * ) * - * For very large numbers, consider using the $(WEB gmplib.org, GMP library) instead. + * For very large numbers, consider using the $(HTTP gmplib.org, GMP library) instead. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Don Clugston * Source: $(PHOBOSSRC std/_bigint.d) */ @@ -25,9 +25,12 @@ module std.bigint; +import std.conv : ConvException; + private import std.internal.math.biguintcore; private import std.format : FormatSpec, FormatException; private import std.traits; +private import std.range.primitives; /** A struct representing an arbitrary precision integer. * @@ -46,37 +49,99 @@ private: BigUint data; // BigInt adds signed arithmetic to BigUint. bool sign = false; public: - /// Construct a BigInt from a decimal or hexadecimal string. - /// The number must be in the form of a D decimal or hex literal: - /// It may have a leading + or - sign; followed by "0x" if hexadecimal. - /// Underscores are permitted. - /// BUG: Should throw a IllegalArgumentException/ConvError if invalid character found - this(T : const(char)[] )(T s) pure + /** + * Construct a BigInt from a decimal or hexadecimal string. The number must + * be in the form of a decimal or hex literal. It may have a leading `+` + * or `-` sign, followed by `0x` or `0X` if hexadecimal. Underscores are + * permitted in any location after the `0x` and/or the sign of the number. + * + * Params: + * s = a finite bidirectional range of any character type + * + * Throws: + * $(REF ConvException, std,conv) if the string doesn't represent a valid number + */ + this(Range)(Range s) if ( + isBidirectionalRange!Range && + isSomeChar!(ElementType!Range) && + !isInfinite!Range) { + import std.algorithm.iteration : filterBidirectional; + import std.algorithm.searching : startsWith; + import std.utf : byCodeUnit, byChar; + import std.exception : enforce; + import std.conv : ConvException; + + enforce!ConvException(!s.empty, "Can't initialize BigInt with an empty range"); + bool neg = false; - if (s[0] == '-') { + bool ok; + + data = 0UL; + + // auto decoding special case + static if (isNarrowString!Range) + auto codeUnits = s.byCodeUnit(); + else + alias codeUnits = s; + + // check for signs and if the string is a hex value + if (codeUnits.front == '+') + { + codeUnits.popFront(); // skip '+' + } + else if (codeUnits.front == '-') + { neg = true; - s = s[1..$]; - } else if (s[0] == '+') { - s = s[1..$]; + codeUnits.popFront(); } - data = 0UL; - auto q = 0X3; - bool ok; - assert(isZero()); - if (s.length > 2 && (s[0..2] == "0x" || s[0..2] == "0X")) + + if (codeUnits.save.startsWith("0x".byChar) || + codeUnits.save.startsWith("0X".byChar)) + { + codeUnits.popFront; + codeUnits.popFront; + + if (!codeUnits.empty) + ok = data.fromHexString(codeUnits.filterBidirectional!(a => a != '_')); + else + ok = false; + } + else { - ok = data.fromHexString(s[2..$]); - } else { - ok = data.fromDecimalString(s); + ok = data.fromDecimalString(codeUnits.filterBidirectional!(a => a != '_')); } - assert(ok); + + enforce!ConvException(ok, "Not a valid numerical string"); if (isZero()) neg = false; + sign = neg; } + @system unittest + { + // system because of the dummy ranges eventually call std.array!string + import std.internal.test.dummyrange; + import std.exception : assertThrown; + + auto r1 = new ReferenceBidirectionalRange!dchar("101"); + auto big1 = BigInt(r1); + assert(big1 == BigInt(101)); + + auto r2 = new ReferenceBidirectionalRange!dchar("1_000"); + auto big2 = BigInt(r2); + assert(big2 == BigInt(1000)); + + auto r3 = new ReferenceBidirectionalRange!dchar("0x0"); + auto big3 = BigInt(r3); + assert(big3 == BigInt(0)); + + auto r4 = new ReferenceBidirectionalRange!dchar("0x"); + assertThrown!ConvException(BigInt(r4)); + } + /// Construct a BigInt from a built-in integral type. this(T)(T x) pure nothrow if (isIntegral!T) { @@ -85,8 +150,9 @@ public: } /// - unittest + @system unittest { + // @system due to failure in FreeBSD32 ulong data = 1_000_000_000_000; auto bigData = BigInt(data); assert(data == BigInt("1_000_000_000_000")); @@ -99,7 +165,7 @@ public: } /// - unittest + @system unittest { const(BigInt) b1 = BigInt("1_234_567_890"); BigInt b2 = BigInt(b1); @@ -109,13 +175,13 @@ public: /// Assignment from built-in integer types. BigInt opAssign(T)(T x) pure nothrow if (isIntegral!T) { - data = cast(ulong)absUnsign(x); + data = cast(ulong) absUnsign(x); sign = (x < 0); return this; } /// - unittest + @system unittest { auto b = BigInt("123"); b = 456; @@ -131,7 +197,7 @@ public: } /// - unittest + @system unittest { auto b1 = BigInt("123"); auto b2 = BigInt("456"); @@ -159,20 +225,23 @@ public: } else static if (op=="*") { - if (y == 0) { + if (y == 0) + { sign = false; data = 0UL; - } else { + } + else + { sign = ( sign != (y<0) ); data = BigUint.mulInt(data, u); } } else static if (op=="/") { - assert(y!=0, "Division by zero"); + assert(y != 0, "Division by zero"); static if (T.sizeof <= uint.sizeof) { - data = BigUint.divInt(data, cast(uint)u); + data = BigUint.divInt(data, cast(uint) u); } else { @@ -182,14 +251,14 @@ public: } else static if (op=="%") { - assert(y!=0, "Division by zero"); + assert(y != 0, "Division by zero"); static if (is(immutable(T) == immutable(long)) || is( immutable(T) == immutable(ulong) )) { this %= BigInt(y); } else { - data = cast(ulong)BigUint.modInt(data, cast(uint)u); + data = cast(ulong) BigUint.modInt(data, cast(uint) u); if (data.isZero()) sign = false; } @@ -228,8 +297,9 @@ public: } /// - unittest + @system unittest { + //@system because opOpAssign is @system auto b = BigInt("1_000_000_000"); b += 12345; @@ -289,8 +359,9 @@ public: } /// - unittest + @system unittest { + // @system because opOpAssign is @system auto x = BigInt("123"); auto y = BigInt("321"); x += y; @@ -310,7 +381,7 @@ public: } /// - unittest + @system unittest { auto x = BigInt("123"); auto y = BigInt("456"); @@ -331,7 +402,7 @@ public: } /// - unittest + @system unittest { auto x = BigInt("123"); x *= 300; @@ -353,7 +424,7 @@ public: auto opBinary(string op, T)(T y) pure nothrow const if (op == "%" && isIntegral!T) { - assert(y!=0); + assert(y != 0); // BigInt % long => long // BigInt % ulong => BigInt @@ -374,7 +445,7 @@ public: } else { - uint u = absUnsign(y); + immutable uint u = absUnsign(y); int rem = BigUint.modInt(data, u); // x%y always has the same sign as x. // This is not the same as mathematical mod. @@ -383,7 +454,7 @@ public: } /// - unittest + @system unittest { auto x = BigInt("1_000_000_500"); long l = 1_000_000L; @@ -408,7 +479,7 @@ public: } /// - unittest + @system unittest { auto x = BigInt("100"); BigInt y = 123 + x; @@ -452,8 +523,8 @@ public: // x%y always has the same sign as x. if (data.ulongLength > 1) return x; - ulong u = absUnsign(x); - ulong rem = u % data.peekUlong(0); + immutable u = absUnsign(x); + immutable rem = u % data.peekUlong(0); // x%y always has the same sign as x. return cast(T)((x<0) ? -rem : rem); } @@ -502,7 +573,7 @@ public: } /// - unittest + @system unittest { auto x = BigInt("1234"); assert(-x == BigInt("-1234")); @@ -525,11 +596,11 @@ public: { if (sign != (y<0)) return 0; - return data.opEquals(cast(ulong)absUnsign(y)); + return data.opEquals(cast(ulong) absUnsign(y)); } /// - unittest + @system unittest { auto x = BigInt("12345"); auto y = BigInt("12340"); @@ -552,7 +623,7 @@ public: } /// - unittest + @system unittest { // Non-zero values are regarded as true auto x = BigInt("1"); @@ -565,18 +636,101 @@ public: assert(!z); } + /** + Implements casting to integer types. + + Throws: $(REF ConvOverflowException, std,conv) if the number exceeds + the target type's range. + */ + T opCast(T:ulong)() /*pure*/ const + { + if (isUnsigned!T && sign) + { /* throw */ } + else + if (data.ulongLength == 1) + { + ulong l = data.peekUlong(0); + if (isUnsigned!T || !sign) + { + if (l <= T.max) + return cast(T) l; + } + else + { + if (l <= ulong(T.max)+1) + return cast(T)-long(l); // -long.min == long.min + } + } + + import std.conv : ConvOverflowException; + import std.string : format; + throw new ConvOverflowException( + "BigInt(%d) cannot be represented as a %s" + .format(this, T.stringof)); + } + + /// + @system unittest + { + import std.conv : to, ConvOverflowException; + import std.exception : assertThrown; + + assert(BigInt("0").to!int == 0); + + assert(BigInt("0").to!ubyte == 0); + assert(BigInt("255").to!ubyte == 255); + assertThrown!ConvOverflowException(BigInt("256").to!ubyte); + assertThrown!ConvOverflowException(BigInt("-1").to!ubyte); + } + + @system unittest + { + import std.conv : to, ConvOverflowException; + import std.exception : assertThrown; + + assert(BigInt("-1").to!byte == -1); + assert(BigInt("-128").to!byte == -128); + assert(BigInt("127").to!byte == 127); + assertThrown!ConvOverflowException(BigInt("-129").to!byte); + assertThrown!ConvOverflowException(BigInt("128").to!byte); + + assert(BigInt("0").to!uint == 0); + assert(BigInt("4294967295").to!uint == uint.max); + assertThrown!ConvOverflowException(BigInt("4294967296").to!uint); + assertThrown!ConvOverflowException(BigInt("-1").to!uint); + + assert(BigInt("-1").to!int == -1); + assert(BigInt("-2147483648").to!int == int.min); + assert(BigInt("2147483647").to!int == int.max); + assertThrown!ConvOverflowException(BigInt("-2147483649").to!int); + assertThrown!ConvOverflowException(BigInt("2147483648").to!int); + + assert(BigInt("0").to!ulong == 0); + assert(BigInt("18446744073709551615").to!ulong == ulong.max); + assertThrown!ConvOverflowException(BigInt("18446744073709551616").to!ulong); + assertThrown!ConvOverflowException(BigInt("-1").to!ulong); + + assert(BigInt("-1").to!long == -1); + assert(BigInt("-9223372036854775808").to!long == long.min); + assert(BigInt("9223372036854775807").to!long == long.max); + assertThrown!ConvOverflowException(BigInt("-9223372036854775809").to!long); + assertThrown!ConvOverflowException(BigInt("9223372036854775808").to!long); + } + /** Implements casting to/from qualified BigInt's. Warning: Casting to/from $(D const) or $(D immutable) may break type system guarantees. Use with care. */ - T opCast(T)() pure nothrow @nogc const if (is(Unqual!T == BigInt)) { + T opCast(T)() pure nothrow @nogc const + if (is(Unqual!T == BigInt)) + { return this; } /// - unittest + @system unittest { const(BigInt) x = BigInt("123"); BigInt y = cast() x; // cast away const @@ -601,20 +755,20 @@ public: { if (sign != (y<0) ) return sign ? -1 : 1; - int cmp = data.opCmp(cast(ulong)absUnsign(y)); + int cmp = data.opCmp(cast(ulong) absUnsign(y)); return sign? -cmp: cmp; } /// ditto int opCmp(T:BigInt)(const T y) pure nothrow @nogc const { - if (sign!=y.sign) + if (sign != y.sign) return sign ? -1 : 1; - int cmp = data.opCmp(y.data); + immutable cmp = data.opCmp(y.data); return sign? -cmp: cmp; } /// - unittest + @system unittest { auto x = BigInt("100"); auto y = BigInt("10"); @@ -640,7 +794,7 @@ public: } /// - unittest + @system unittest { auto b = BigInt("12345"); long l = b.toLong(); @@ -660,7 +814,7 @@ public: } /// - unittest + @system unittest { auto big = BigInt("5_000_000"); auto i = big.toInt(); @@ -673,14 +827,14 @@ public: } /// Number of significant uints which are used in storing this number. - /// The absolute value of this BigInt is always < 2^^(32*uintLength) + /// The absolute value of this BigInt is always < 2$(SUPERSCRIPT 32*uintLength) @property size_t uintLength() @safe pure nothrow @nogc const { return data.uintLength; } /// Number of significant ulongs which are used in storing this number. - /// The absolute value of this BigInt is always < 2^^(64*ulongLength) + /// The absolute value of this BigInt is always < 2$(SUPERSCRIPT 64*ulongLength) @property size_t ulongLength() @safe pure nothrow @nogc const { return data.ulongLength; @@ -695,6 +849,7 @@ public: * * $(TABLE Available output formats:, * $(TR $(TD "d") $(TD Decimal)) + * $(TR $(TD "o") $(TD Octal)) * $(TR $(TD "x") $(TD Hexadecimal, lower case)) * $(TR $(TD "X") $(TD Hexadecimal, upper case)) * $(TR $(TD "s") $(TD Default formatting (same as "d") )) @@ -711,13 +866,27 @@ public: /// ditto void toString(scope void delegate(const(char)[]) sink, ref FormatSpec!char f) const { - auto hex = (f.spec == 'x' || f.spec == 'X'); - if (!(f.spec == 's' || f.spec == 'd' || hex)) + immutable hex = (f.spec == 'x' || f.spec == 'X'); + if (!(f.spec == 's' || f.spec == 'd' || f.spec =='o' || hex)) throw new FormatException("Format specifier not understood: %" ~ f.spec); - char[] buff = - hex ? data.toHexString(0, '_', 0, f.flZero ? '0' : ' ') - : data.toDecimalString(0); + char[] buff; + if (f.spec == 'X') + { + buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.upper); + } + else if (f.spec == 'x') + { + buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.lower); + } + else if (f.spec == 'o') + { + buff = data.toOctalString(); + } + else + { + buff = data.toDecimalString(0); + } assert(buff.length > 0); char signChar = isNegative() ? '-' : 0; @@ -737,15 +906,15 @@ public: } } - auto maxw = minw < f.width ? f.width : minw; - auto difw = maxw - minw; + immutable maxw = minw < f.width ? f.width : minw; + immutable difw = maxw - minw; if (!f.flDash && !f.flZero) foreach (i; 0 .. difw) sink(" "); if (signChar) - sink((&signChar)[0..1]); + sink((&signChar)[0 .. 1]); if (!f.flDash && f.flZero) foreach (i; 0 .. difw) @@ -760,9 +929,9 @@ public: /** $(D toString) is rarely directly invoked; the usual way of using it is via - $(LINK2 std_format.html#format, std.format.format): + $(REF format, std, format): */ - unittest + @system unittest { import std.format : format; @@ -770,7 +939,9 @@ public: x *= 12345; assert(format("%d", x) == "12345000000"); + assert(format("%x", x) == "2_dfd1c040"); assert(format("%X", x) == "2_DFD1C040"); + assert(format("%o", x) == "133764340100"); } // Implement toHash so that BigInt works properly as an AA key. @@ -787,7 +958,7 @@ public: $(D toHash) is rarely directly invoked; it is implicitly used when BigInt is used as the key of an associative array. */ - unittest + @safe unittest { string[BigInt] aa; aa[BigInt(123)] = "abc"; @@ -821,7 +992,7 @@ private: } /// -unittest +@system unittest { BigInt a = "9588669891916142"; BigInt b = "7452469135154800"; @@ -867,7 +1038,7 @@ string toDecimalString(const(BigInt) x) } /// -unittest +@system unittest { auto x = BigInt("123"); x *= 1000; @@ -883,19 +1054,19 @@ Params: Returns: A $(D string) that represents the $(D BigInt) as a hexadecimal (base 16) - number. + number in upper case. */ string toHex(const(BigInt) x) { string outbuff=""; void sink(const(char)[] s) { outbuff ~= s; } - x.toString(&sink, "%x"); + x.toString(&sink, "%X"); return outbuff; } /// -unittest +@system unittest { auto x = BigInt("123"); x *= 1000; @@ -915,11 +1086,12 @@ Returns: The absolute value of x. */ -Unsigned!T absUnsign(T)(T x) if (isIntegral!T) +Unsigned!T absUnsign(T)(T x) +if (isIntegral!T) { static if (isSigned!T) { - import std.conv; + import std.conv : unsigned; /* This returns the correct result even when x = T.min * on two's complement machines because unsigned(T.min) = |T.min| * even though -T.min = T.min. @@ -932,24 +1104,35 @@ Unsigned!T absUnsign(T)(T x) if (isIntegral!T) } } -nothrow pure -unittest { +/// +nothrow pure @system +unittest +{ + assert((-1).absUnsign == 1); + assert(1.absUnsign == 1); +} + +nothrow pure @system +unittest +{ BigInt a, b; a = 1; b = 2; auto c = a + b; } -nothrow pure -unittest { +nothrow pure @system +unittest +{ long a; BigInt b; auto c = a + b; auto d = b + a; } -nothrow pure -unittest { +nothrow pure @system +unittest +{ BigInt x = 1, y = 2; assert(x < y); assert(x <= y); @@ -972,7 +1155,8 @@ unittest { assert(incr == BigInt(1)); } -unittest { +@system unittest +{ // Radix conversion assert( toDecimalString(BigInt("-1_234_567_890_123_456_789")) == "-1234567890123456789"); @@ -1004,9 +1188,12 @@ unittest { assert(BigInt(-4) % BigInt(5) == -4); assert(BigInt(2)/BigInt(-3) == BigInt(0)); // bug 8022 assert(BigInt("-1") > long.min); // bug 9548 + + assert(toDecimalString(BigInt("0000000000000000000000000000000000000000001234567")) + == "1234567"); } -unittest // Minimum signed value bug tests. +@system unittest // Minimum signed value bug tests. { assert(BigInt("-0x8000000000000000") == BigInt(long.min)); assert(BigInt("-0x8000000000000000")+1 > BigInt(long.min)); @@ -1027,7 +1214,7 @@ unittest // Minimum signed value bug tests. assert((BigInt(int.min)-1)%int.min == -1); } -unittest // Recursive division, bug 5568 +@system unittest // Recursive division, bug 5568 { enum Z = 4843; BigInt m = (BigInt(1) << (Z*8) ) - 1; @@ -1073,7 +1260,7 @@ unittest // Recursive division, bug 5568 a8165[0] = a8165[1] = 1; } -unittest +@system unittest { import std.array; import std.format; @@ -1124,7 +1311,58 @@ unittest } } -unittest +@system unittest +{ + import std.array; + import std.format; + + immutable string[][] table = [ + /* fmt, +10 -10 */ + ["%x", "a", "-a"], + ["%+x", "a", "-a"], + ["%-x", "a", "-a"], + ["%+-x", "a", "-a"], + + ["%4x", " a", " -a"], + ["%+4x", " a", " -a"], + ["%-4x", "a ", "-a "], + ["%+-4x", "a ", "-a "], + + ["%04x", "000a", "-00a"], + ["%+04x", "000a", "-00a"], + ["%-04x", "a ", "-a "], + ["%+-04x", "a ", "-a "], + + ["% 04x", "000a", "-00a"], + ["%+ 04x", "000a", "-00a"], + ["%- 04x", "a ", "-a "], + ["%+- 04x", "a ", "-a "], + ]; + + auto w1 = appender!(char[])(); + auto w2 = appender!(char[])(); + + foreach (entry; table) + { + immutable fmt = entry[0]; + + formattedWrite(w1, fmt, BigInt(10)); + formattedWrite(w2, fmt, 10); + assert(w1.data == w2.data); // Equal only positive BigInt + assert(w1.data == entry[1]); + w1.clear(); + w2.clear(); + + formattedWrite(w1, fmt, BigInt(-10)); + //formattedWrite(w2, fmt, -10); + //assert(w1.data == w2.data); + assert(w1.data == entry[2]); + w1.clear(); + //w2.clear(); + } +} + +@system unittest { import std.array; import std.format; @@ -1176,7 +1414,7 @@ unittest } // 6448 -unittest +@system unittest { import std.array; import std.format; @@ -1202,9 +1440,9 @@ unittest assert(y.toLong() == -2); } -unittest +@safe unittest { - import std.math:abs; + import std.math : abs; auto r = abs(BigInt(-1000)); // 6486 assert(r == 1000); @@ -1218,7 +1456,7 @@ unittest assert(one && !zero); } -unittest // 6850 +@system unittest // 6850 { pure long pureTest() { BigInt a = 1; @@ -1230,7 +1468,7 @@ unittest // 6850 assert(pureTest() == 1337); } -unittest // 8435 & 10118 +@system unittest // 8435 & 10118 { auto i = BigInt(100); auto j = BigInt(100); @@ -1254,7 +1492,7 @@ unittest // 8435 & 10118 assert(keys.empty); } -unittest // 11148 +@system unittest // 11148 { void foo(BigInt) {} const BigInt cbi = 3; @@ -1263,18 +1501,18 @@ unittest // 11148 assert(__traits(compiles, foo(cbi))); assert(__traits(compiles, foo(ibi))); - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; import std.conv : to; - foreach (T1; TypeTuple!(BigInt, const(BigInt), immutable(BigInt))) + foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt))) { - foreach (T2; TypeTuple!(BigInt, const(BigInt), immutable(BigInt))) + foreach (T2; AliasSeq!(BigInt, const(BigInt), immutable(BigInt))) { T1 t1 = 2; T2 t2 = t1; T2 t2_1 = to!T2(t1); - T2 t2_2 = cast(T2)t1; + T2 t2_2 = cast(T2) t1; assert(t2 == t1); assert(t2 == 2); @@ -1291,13 +1529,13 @@ unittest // 11148 n *= 2; } -unittest // 8167 +@safe unittest // 8167 { BigInt a = BigInt(3); BigInt b = BigInt(a); } -unittest // 9061 +@safe unittest // 9061 { long l1 = 0x12345678_90ABCDEF; long l2 = 0xFEDCBA09_87654321; @@ -1316,7 +1554,7 @@ unittest // 9061 assert(l5 == b5); } -unittest // 11600 +@system unittest // 11600 { import std.conv; import std.exception : assertThrown; @@ -1331,20 +1569,20 @@ unittest // 11600 assertThrown!ConvException(to!BigInt("-123four")); } -unittest // 11583 +@safe unittest // 11583 { BigInt x = 0; assert((x > 0) == false); } -unittest // 13391 +@system unittest // 13391 { BigInt x1 = "123456789"; BigInt x2 = "123456789123456789"; BigInt x3 = "123456789123456789123456789"; - import std.typetuple : TypeTuple; - foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + import std.meta : AliasSeq; + foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { assert((x1 * T.max) / T.max == x1); assert((x2 * T.max) / T.max == x2); @@ -1368,11 +1606,11 @@ unittest // 13391 assert(x2 == 1); } -unittest // 13963 +@system unittest // 13963 { BigInt x = 1; - import std.typetuple : TypeTuple; - foreach(Int; TypeTuple!(byte, ubyte, short, ushort, int, uint)) + import std.meta : AliasSeq; + foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint)) { assert(is(typeof(x % Int(1)) == int)); } @@ -1410,7 +1648,7 @@ unittest // 13963 assert(-x2 % ulong.max == -x2); } -unittest // 14124 +@system unittest // 14124 { auto x = BigInt(-3); x %= 3; @@ -1418,7 +1656,7 @@ unittest // 14124 assert(x.isZero()); x = BigInt(-3); - x %= cast(ushort)3; + x %= cast(ushort) 3; assert(!x.isNegative()); assert(x.isZero()); @@ -1432,3 +1670,29 @@ unittest // 14124 assert(!x.isNegative()); assert(x.isZero()); } + +// issue 15678 +@system unittest +{ + import std.exception : assertThrown; + assertThrown!ConvException(BigInt("")); + assertThrown!ConvException(BigInt("0x1234BARF")); + assertThrown!ConvException(BigInt("1234PUKE")); +} + +// Issue 6447 +@system unittest +{ + import std.algorithm.comparison : equal; + import std.range : iota; + + auto s = BigInt(1_000_000_000_000); + auto e = BigInt(1_000_000_000_003); + auto r = iota(s, e); + assert(r.equal([ + BigInt(1_000_000_000_000), + BigInt(1_000_000_000_001), + BigInt(1_000_000_000_002) + ])); +} + diff --git a/std/bitmanip.d b/std/bitmanip.d index 9f3f23d5edd..b2e4be33b8a 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -3,17 +3,44 @@ /** Bit-level manipulation facilities. -Macros: - -WIKI = StdBitarray +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Bit constructs) $(TD + $(LREF BitArray) + $(LREF bitfields) + $(LREF bitsSet) +)) +$(TR $(TD Endianness conversion) $(TD + $(LREF bigEndianToNative) + $(LREF littleEndianToNative) + $(LREF nativeToBigEndian) + $(LREF nativeToLittleEndian) + $(LREF swapEndian) +)) +$(TR $(TD Integral ranges) $(TD + $(LREF append) + $(LREF peek) + $(LREF read) + $(LREF write) +)) +$(TR $(TD Floating-Point manipulation) $(TD + $(LREF DoubleRep) + $(LREF FloatRep) +)) +$(TR $(TD Tagging) $(TD + $(LREF taggedClassRef) + $(LREF taggedPointer) +)) +) Copyright: Copyright Digital Mars 2007 - 2011. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB digitalmars.com, Walter Bright), - $(WEB erdani.org, Andrei Alexandrescu), +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP digitalmars.com, Walter Bright), + $(HTTP erdani.org, Andrei Alexandrescu), Jonathan M Davis, Alex Rønne Petersen, - Damian Ziemba + Damian Ziemba, Amaury SECHET Source: $(PHOBOSSRC std/_bitmanip.d) */ @@ -37,18 +64,12 @@ version(unittest) } -private string myToStringx(ulong n) -{ - enum s = "0123456789"; - if (n < 10) - return s[cast(size_t)n..cast(size_t)n+1]; - else - return myToStringx(n / 10) ~ myToStringx(n % 10); -} - private string myToString(ulong n) { - return myToStringx(n) ~ (n > uint.max ? "UL" : "U"); + import core.internal.string : UnsignedStringBuf, unsignedToTempString; + UnsignedStringBuf buf; + auto s = unsignedToTempString(n, buf); + return cast(string) s ~ (n > uint.max ? "UL" : "U"); } private template createAccessors( @@ -94,7 +115,7 @@ private template createAccessors( // setter ~"@property void " ~ name ~ "(bool v) @safe pure nothrow @nogc { " ~"if (v) "~store~" |= "~myToString(maskAllElse)~";" - ~"else "~store~" &= ~"~myToString(maskAllElse)~";}\n"; + ~"else "~store~" &= ~cast(typeof("~store~"))"~myToString(maskAllElse)~";}\n"; } else { @@ -133,31 +154,36 @@ private template createStoreName(Ts...) enum createStoreName = "_" ~ Ts[1] ~ createStoreName!(Ts[3 .. $]); } -private template createFields(string store, size_t offset, Ts...) +private template createStorageAndFields(Ts...) { - static if (!Ts.length) - { - static if (offset == ubyte.sizeof * 8) - alias StoreType = ubyte; - else static if (offset == ushort.sizeof * 8) - alias StoreType = ushort; - else static if (offset == uint.sizeof * 8) - alias StoreType = uint; - else static if (offset == ulong.sizeof * 8) - alias StoreType = ulong; - else - { - static assert(false, "Field widths must sum to 8, 16, 32, or 64"); - alias StoreType = ulong; // just to avoid another error msg - } - enum result = "private " ~ StoreType.stringof ~ " " ~ store ~ ";"; - } + enum Name = createStoreName!Ts; + enum Size = sizeOfBitField!Ts; + static if (Size == ubyte.sizeof * 8) + alias StoreType = ubyte; + else static if (Size == ushort.sizeof * 8) + alias StoreType = ushort; + else static if (Size == uint.sizeof * 8) + alias StoreType = uint; + else static if (Size == ulong.sizeof * 8) + alias StoreType = ulong; else { + static assert(false, "Field widths must sum to 8, 16, 32, or 64"); + alias StoreType = ulong; // just to avoid another error msg + } + enum result + = "private " ~ StoreType.stringof ~ " " ~ Name ~ ";" + ~ createFields!(Name, 0, Ts).result; +} + +private template createFields(string store, size_t offset, Ts...) +{ + static if (Ts.length > 0) enum result = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result ~ createFields!(store, offset + Ts[2], Ts[3 .. $]).result; - } + else + enum result = ""; } private ulong getBitsForAlign(ulong a) @@ -175,33 +201,46 @@ private ulong getBitsForAlign(ulong a) private template createReferenceAccessor(string store, T, ulong bits, string name) { + enum storage = "private void* " ~ store ~ "_ptr;\n"; + enum storage_accessor = "@property ref size_t " ~ store ~ "() return @trusted pure nothrow @nogc const { " + ~ "return *cast(size_t*) &" ~ store ~ "_ptr;}\n" + ~ "@property void " ~ store ~ "(size_t v) @trusted pure nothrow @nogc { " + ~ "" ~ store ~ "_ptr = cast(void*) v;}\n"; + enum mask = (1UL << bits) - 1; // getter - enum result = "@property "~T.stringof~" "~name~"() @trusted pure nothrow @nogc const { auto result = " - ~ "("~store~" & "~myToString(~mask)~");" - ~ " return cast("~T.stringof~") cast(void*) result;}\n" + enum ref_accessor = "@property "~T.stringof~" "~name~"() @trusted pure nothrow @nogc const { auto result = " + ~ "("~store~" & "~myToString(~mask)~"); " + ~ "return cast("~T.stringof~") cast(void*) result;}\n" // setter ~"@property void "~name~"("~T.stringof~" v) @trusted pure nothrow @nogc { " - ~"assert(((cast(typeof("~store~")) cast(void*) v) & "~myToString(mask)~`) == 0, "Value not properly aligned for '`~name~`'"); ` + ~"assert(((cast(typeof("~store~")) cast(void*) v) & "~myToString(mask) + ~`) == 0, "Value not properly aligned for '`~name~`'"); ` ~store~" = cast(typeof("~store~"))" ~" (("~store~" & (cast(typeof("~store~")) "~myToString(mask)~"))" ~" | ((cast(typeof("~store~")) cast(void*) v) & (cast(typeof("~store~")) "~myToString(~mask)~")));}\n"; + + enum result = storage ~ storage_accessor ~ ref_accessor; } private template sizeOfBitField(T...) { - static if(T.length < 2) + static if (T.length < 2) enum sizeOfBitField = 0; else enum sizeOfBitField = T[2] + sizeOfBitField!(T[3 .. $]); } -private template createTaggedReference(string store, T, ulong a, string name, Ts...) +private template createTaggedReference(T, ulong a, string name, Ts...) { - static assert(sizeOfBitField!Ts <= getBitsForAlign(a), "Fields must fit in the bits know to be zero because of alignment."); + static assert( + sizeOfBitField!Ts <= getBitsForAlign(a), + "Fields must fit in the bits know to be zero because of alignment." + ); + enum StoreName = createStoreName!(T, name, 0, Ts); enum result - = createReferenceAccessor!(store, T, sizeOfBitField!Ts, name).result - ~ createFields!(store, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result; + = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name).result + ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result; } /** @@ -253,7 +292,7 @@ bool), followed by unsigned types, followed by signed types. template bitfields(T...) { - enum { bitfields = createFields!(createStoreName!(T), 0, T).result } + enum { bitfields = createStorageAndFields!T.result } } /** @@ -268,15 +307,15 @@ $(D uint*) as specified by the first argument, and is named x, as specified by t argument. Following arguments works the same way as $(D bitfield)'s. The bitfield must fit into the -bits known to be zero because of the pointer alignement. +bits known to be zero because of the pointer alignment. */ template taggedPointer(T : T*, string name, Ts...) { - enum taggedPointer = createTaggedReference!(createStoreName!(T, name, 0, Ts), T*, T.alignof, name, Ts).result; + enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts).result; } /// -unittest +@safe unittest { struct A { @@ -303,12 +342,14 @@ The example above creates a tagged reference to an Object in the struct A. This as $(D taggedPointer), except the first argument which must be a class type instead of a pointer type. */ -template taggedClassRef(T, string name, Ts...) if(is(T == class)) { - enum taggedClassRef = createTaggedReference!(createStoreName!(T, name, 0, Ts), T, 8, name, Ts).result; +template taggedClassRef(T, string name, Ts...) +if (is(T == class)) +{ + enum taggedClassRef = createTaggedReference!(T, 8, name, Ts).result; } /// -unittest +@safe unittest { struct A { @@ -389,7 +430,7 @@ unittest t4b.a = -5; assert(t4b.a == -5L); } -unittest +@system unittest { struct Test5 { @@ -429,7 +470,7 @@ unittest assert(t6.b == true); } -unittest +@safe unittest { static assert(!__traits(compiles, taggedPointer!( @@ -440,9 +481,20 @@ unittest taggedClassRef!( Object, "a", uint, "b", 4))); + + struct S { + mixin(taggedClassRef!( + Object, "a", + bool, "b", 1)); + } + + const S s; + void bar(S s) {} + + static assert(!__traits(compiles, bar(s))); } -unittest +@safe unittest { // Bug #6686 union S { @@ -459,7 +511,7 @@ unittest assert(num.bits == 0xFFFF_FFFF_8000_0001uL); } -unittest +@safe unittest { // Bug #5942 struct S @@ -476,7 +528,7 @@ unittest assert(data.b == 42); } -unittest +@safe unittest { struct Test { @@ -501,7 +553,7 @@ unittest test(); } -unittest +@safe unittest { { static struct Integrals { @@ -578,8 +630,9 @@ unittest } // Issue 12477 -unittest +@system unittest { + import std.algorithm.searching : canFind; import std.bitmanip : bitfields; import core.exception : AssertError; @@ -594,11 +647,11 @@ unittest try { s.a = uint.max; assert(0); } catch (AssertError ae) - { assert(ae.msg == "Value is greater than the maximum value of bitfield 'a'", ae.msg); } + { assert(ae.msg.canFind("Value is greater than the maximum value of bitfield 'a'"), ae.msg); } try { s.b = int.min; assert(0); } catch (AssertError ae) - { assert(ae.msg == "Value is smaller than the minimum value of bitfield 'b'", ae.msg); } + { assert(ae.msg.canFind("Value is smaller than the minimum value of bitfield 'b'"), ae.msg); } } /** @@ -667,7 +720,7 @@ struct DoubleRep enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11; } -unittest +@safe unittest { // test reading DoubleRep x; @@ -695,35 +748,52 @@ unittest } } +@safe unittest +{ + // Issue #15305 + struct S { + mixin(bitfields!( + bool, "alice", 1, + ulong, "bob", 63, + )); + } + + S s; + s.bob = long.max - 1; + s.alice = false; + assert(s.bob == long.max - 1); +} + /** * An array of bits. */ struct BitArray { +private: + import std.format : FormatSpec; - import core.bitop: bts, btr, bsf, bt; + import core.bitop : bts, btr, bsf, bt; - size_t len; - size_t* ptr; + size_t _len; + size_t* _ptr; enum bitsPerSizeT = size_t.sizeof * 8; -private: @property size_t fullWords() const @nogc pure nothrow { - return len / bitsPerSizeT; + return _len / bitsPerSizeT; } // Number of bits after the last full word @property size_t endBits() const @nogc pure nothrow { - return len % bitsPerSizeT; + return _len % bitsPerSizeT; } // Bit mask to extract the bits after the last full word @property size_t endMask() const @nogc pure nothrow { return (size_t(1) << endBits) - 1; } - static size_t lenToDim(size_t len) @nogc pure nothrow + static size_t lenToDim(size_t len) @nogc pure nothrow @safe { return (len + (bitsPerSizeT-1)) / bitsPerSizeT; } @@ -732,17 +802,17 @@ public: /********************************************** * Gets the amount of native words backing this $(D BitArray). */ - @property size_t dim() const @nogc pure nothrow + @property size_t dim() const @nogc pure nothrow @safe { - return lenToDim(len); + return lenToDim(_len); } /********************************************** * Gets the amount of bits in the $(D BitArray). */ - @property size_t length() const @nogc pure nothrow + @property size_t length() const @nogc pure nothrow @safe { - return len; + return _len; } /********************************************** @@ -751,24 +821,24 @@ public: * final word up to the next word boundary. i.e. D dynamic * array extension semantics are not followed.) */ - @property size_t length(size_t newlen) pure nothrow + @property size_t length(size_t newlen) pure nothrow @system { - if (newlen != len) + if (newlen != _len) { size_t olddim = dim; - size_t newdim = lenToDim(newlen); + immutable newdim = lenToDim(newlen); if (newdim != olddim) { // Create a fake array so we can use D's realloc machinery - auto b = ptr[0 .. olddim]; + auto b = _ptr[0 .. olddim]; b.length = newdim; // realloc - ptr = b.ptr; + _ptr = b.ptr; } - len = newlen; + _len = newlen; } - return len; + return _len; } /********************************************** @@ -777,14 +847,14 @@ public: bool opIndex(size_t i) const @nogc pure nothrow in { - assert(i < len); + assert(i < _len); } body { - return cast(bool) bt(ptr, i); + return cast(bool) bt(_ptr, i); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opIndex.unittest\n"); @@ -805,14 +875,14 @@ public: bool opIndexAssign(bool b, size_t i) @nogc pure nothrow in { - assert(i < len); + assert(i < _len); } body { if (b) - bts(ptr, i); + bts(_ptr, i); else - btr(ptr, i); + btr(_ptr, i); return b; } @@ -823,13 +893,13 @@ public: { BitArray ba; - auto b = ptr[0 .. dim].dup; - ba.len = len; - ba.ptr = b.ptr; + auto b = _ptr[0 .. dim].dup; + ba._len = _len; + ba._ptr = b.ptr; return ba; } - unittest + @system unittest { BitArray a; BitArray b; @@ -854,7 +924,7 @@ public: { int result; - foreach (i; 0 .. len) + foreach (i; 0 .. _len) { bool b = opIndex(i); result = dg(b); @@ -870,9 +940,9 @@ public: { int result; - foreach (i; 0 .. len) + foreach (i; 0 .. _len) { - bool b = opIndex(i); + immutable b = opIndex(i); result = dg(b); if (result) break; @@ -885,7 +955,7 @@ public: { int result; - foreach (i; 0 .. len) + foreach (i; 0 .. _len) { bool b = opIndex(i); result = dg(i, b); @@ -901,9 +971,9 @@ public: { int result; - foreach (i; 0 .. len) + foreach (i; 0 .. _len) { - bool b = opIndex(i); + immutable b = opIndex(i); result = dg(i, b); if (result) break; @@ -911,7 +981,7 @@ public: return result; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opApply unittest\n"); @@ -955,13 +1025,13 @@ public: } body { - if (len >= 2) + if (_len >= 2) { bool t; size_t lo, hi; lo = 0; - hi = len - 1; + hi = _len - 1; for (; lo < hi; lo++, hi--) { t = this[lo]; @@ -972,7 +1042,7 @@ public: return this; } - unittest + @system unittest { debug(bitarray) printf("BitArray.reverse.unittest\n"); @@ -999,12 +1069,12 @@ public: } body { - if (len >= 2) + if (_len >= 2) { size_t lo, hi; lo = 0; - hi = len - 1; + hi = _len - 1; while (1) { while (1) @@ -1036,7 +1106,7 @@ public: return this; } - unittest + @system unittest { debug(bitarray) printf("BitArray.sort.unittest\n"); @@ -1057,10 +1127,10 @@ public: { if (this.length != a2.length) return false; - auto p1 = this.ptr; - auto p2 = a2.ptr; + auto p1 = this._ptr; + auto p2 = a2._ptr; - if (p1[0..fullWords] != p2[0..fullWords]) + if (p1[0 .. fullWords] != p2[0 .. fullWords]) return false; if (!endBits) @@ -1070,7 +1140,7 @@ public: return (p1[i] & endMask) == (p2[i] & endMask); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opEquals unittest\n"); @@ -1102,11 +1172,11 @@ public: */ int opCmp(BitArray a2) const @nogc pure nothrow { - auto lesser = this.length < a2.length ? &this : &a2; - size_t fullWords = lesser.fullWords; - size_t endBits = lesser.endBits; - auto p1 = this.ptr; - auto p2 = a2.ptr; + const lesser = this.length < a2.length ? &this : &a2; + immutable fullWords = lesser.fullWords; + immutable endBits = lesser.endBits; + auto p1 = this._ptr; + auto p2 = a2._ptr; foreach (i; 0 .. fullWords) { @@ -1136,7 +1206,7 @@ public: return (this.length > a2.length) - (this.length < a2.length); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opCmp unittest\n"); @@ -1204,13 +1274,13 @@ public: size_t toHash() const @nogc pure nothrow { size_t hash = 3557; - auto fullBytes = len / 8; + auto fullBytes = _len / 8; foreach (i; 0 .. fullBytes) { hash *= 3559; - hash += (cast(byte*)this.ptr)[i]; + hash += (cast(byte*) this._ptr)[i]; } - foreach (i; 8*fullBytes .. len) + foreach (i; 8*fullBytes .. _len) { hash *= 3571; hash += this[i]; @@ -1218,26 +1288,10 @@ public: return hash; } - deprecated("Please use the constructor instead. This function will be removed in Jan 2016.") - /*************************************** - * $(RED Will be deprecated in 2.068. Please use the constructor instead.) - */ - void init(bool[] ba) pure nothrow - { - this = BitArray(ba); - } - - deprecated("Please use the constructor instead. This function will be removed in Jan 2016.") - /// ditto - void init(void[] v, size_t numbits) pure nothrow - { - this = BitArray(v, numbits); - } - /*************************************** * Set this $(D BitArray) to the contents of $(D ba). */ - this(bool[] ba) pure nothrow + this(bool[] ba) pure nothrow @system { length = ba.length; foreach (i, b; ba) @@ -1247,10 +1301,10 @@ public: } // Deliberately undocumented: raw initialization of bit array. - this(size_t _len, size_t* _ptr) + this(size_t len, size_t* ptr) { - len = _len; - ptr = _ptr; + _len = len; + _ptr = ptr; } /*************************************** @@ -1269,16 +1323,16 @@ public: } body { - ptr = cast(size_t*)v.ptr; - len = numbits; + _ptr = cast(size_t*) v.ptr; + _len = numbits; if (endBits) { // Need to mask away extraneous bits from v. - ptr[dim - 1] &= endMask; + _ptr[dim - 1] &= endMask; } } - unittest + @system unittest { debug(bitarray) printf("BitArray.init unittest\n"); @@ -1287,7 +1341,7 @@ public: auto a = BitArray(ba); void[] v; - v = cast(void[])a; + v = cast(void[]) a; auto b = BitArray(v, a.length); assert(b[0] == 1); @@ -1307,7 +1361,7 @@ public: */ void[] opCast(T : void[])() @nogc pure nothrow { - return cast(void[])ptr[0 .. dim]; + return cast(void[])_ptr[0 .. dim]; } /*************************************** @@ -1315,17 +1369,17 @@ public: */ size_t[] opCast(T : size_t[])() @nogc pure nothrow { - return ptr[0 .. dim]; + return _ptr[0 .. dim]; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opCast unittest\n"); static bool[] ba = [1,0,1,0,1]; auto a = BitArray(ba); - void[] v = cast(void[])a; + void[] v = cast(void[]) a; assert(v.length == a.dim * size_t.sizeof); } @@ -1338,19 +1392,19 @@ public: auto dim = this.dim; BitArray result; - result.length = len; + result.length = _len; - result.ptr[0..dim] = ~this.ptr[0..dim]; + result._ptr[0 .. dim] = ~this._ptr[0 .. dim]; // Avoid putting garbage in extra bits // Remove once we zero on length extension if (endBits) - result.ptr[dim - 1] &= endMask; + result._ptr[dim - 1] &= endMask; return result; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opCom unittest\n"); @@ -1374,29 +1428,29 @@ public: if (op == "-" || op == "&" || op == "|" || op == "^") in { - assert(len == e2.length); + assert(_len == e2.length); } body { auto dim = this.dim; BitArray result; - result.length = len; + result.length = _len; static if (op == "-") - result.ptr[0..dim] = this.ptr[0..dim] & ~e2.ptr[0..dim]; + result._ptr[0 .. dim] = this._ptr[0 .. dim] & ~e2._ptr[0 .. dim]; else - mixin("result.ptr[0..dim] = this.ptr[0..dim]"~op~" e2.ptr[0..dim];"); + mixin("result._ptr[0 .. dim] = this._ptr[0 .. dim]"~op~" e2._ptr[0 .. dim];"); // Avoid putting garbage in extra bits // Remove once we zero on length extension if (endBits) - result.ptr[dim - 1] &= endMask; + result._ptr[dim - 1] &= endMask; return result; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opAnd unittest\n"); @@ -1415,7 +1469,7 @@ public: assert(c[4] == 0); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opOr unittest\n"); @@ -1434,7 +1488,7 @@ public: assert(c[4] == 1); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opXor unittest\n"); @@ -1453,7 +1507,7 @@ public: assert(c[4] == 1); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opSub unittest\n"); @@ -1480,32 +1534,32 @@ public: if (op == "-" || op == "&" || op == "|" || op == "^") in { - assert(len == e2.length); + assert(_len == e2.length); } body { foreach (i; 0 .. fullWords) { static if (op == "-") - ptr[i] &= ~e2.ptr[i]; + _ptr[i] &= ~e2._ptr[i]; else - mixin("ptr[i] "~op~"= e2.ptr[i];"); + mixin("_ptr[i] "~op~"= e2._ptr[i];"); } if (!endBits) return this; size_t i = fullWords; - size_t endWord = ptr[i]; + size_t endWord = _ptr[i]; static if (op == "-") - endWord &= ~e2.ptr[i]; + endWord &= ~e2._ptr[i]; else - mixin("endWord "~op~"= e2.ptr[i];"); - ptr[i] = (ptr[i] & ~endMask) | (endWord & endMask); + mixin("endWord "~op~"= e2._ptr[i];"); + _ptr[i] = (_ptr[i] & ~endMask) | (endWord & endMask); return this; } - unittest + @system unittest { static bool[] ba = [1,0,1,0,1,1,0,1,0,1]; static bool[] bb = [1,0,1,1,0]; @@ -1521,7 +1575,7 @@ public: assert(a[9] == 1); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opAndAssign unittest\n"); @@ -1539,7 +1593,7 @@ public: assert(a[4] == 0); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opOrAssign unittest\n"); @@ -1557,7 +1611,7 @@ public: assert(a[4] == 1); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opXorAssign unittest\n"); @@ -1575,7 +1629,7 @@ public: assert(a[4] == 1); } - unittest + @system unittest { debug(bitarray) printf("BitArray.opSubAssign unittest\n"); @@ -1603,12 +1657,12 @@ public: BitArray opCatAssign(bool b) pure nothrow { - length = len + 1; - this[len - 1] = b; + length = _len + 1; + this[_len - 1] = b; return this; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opCatAssign unittest\n"); @@ -1634,14 +1688,14 @@ public: BitArray opCatAssign(BitArray b) pure nothrow { - auto istart = len; - length = len + b.length; - for (auto i = istart; i < len; i++) + auto istart = _len; + length = _len + b.length; + for (auto i = istart; i < _len; i++) this[i] = b[i - istart]; return this; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opCatAssign unittest\n"); @@ -1671,8 +1725,8 @@ public: BitArray r; r = this.dup; - r.length = len + 1; - r[len] = b; + r.length = _len + 1; + r[_len] = b; return r; } @@ -1681,9 +1735,9 @@ public: { BitArray r; - r.length = len + 1; + r.length = _len + 1; r[0] = b; - foreach (i; 0 .. len) + foreach (i; 0 .. _len) r[1 + i] = this[i]; return r; } @@ -1698,7 +1752,7 @@ public: return r; } - unittest + @system unittest { debug(bitarray) printf("BitArray.opCat unittest\n"); @@ -1743,7 +1797,7 @@ public: return (upper << (bitsPerSizeT - nbits)) | (lower >> nbits); } - unittest + @safe unittest { static if (size_t.sizeof == 8) { @@ -1778,7 +1832,7 @@ public: return (upper << nbits) | (lower >> (bitsPerSizeT - nbits)); } - unittest + @safe unittest { static if (size_t.sizeof == 8) { @@ -1819,16 +1873,16 @@ public: { foreach_reverse (i; 1 .. dim - wordsToShift) { - ptr[i + wordsToShift] = rollLeft(ptr[i], ptr[i-1], + _ptr[i + wordsToShift] = rollLeft(_ptr[i], _ptr[i-1], bitsToShift); } - ptr[wordsToShift] = rollLeft(ptr[0], 0, bitsToShift); + _ptr[wordsToShift] = rollLeft(_ptr[0], 0, bitsToShift); } - import std.algorithm : min; + import std.algorithm.comparison : min; foreach (i; 0 .. min(wordsToShift, dim)) { - ptr[i] = 0; + _ptr[i] = 0; } } @@ -1853,8 +1907,8 @@ public: { foreach (i; 0 .. dim - wordsToShift - 1) { - ptr[i] = rollRight(ptr[i + wordsToShift + 1], - ptr[i + wordsToShift], bitsToShift); + _ptr[i] = rollRight(_ptr[i + wordsToShift + 1], + _ptr[i + wordsToShift], bitsToShift); } } @@ -1862,18 +1916,18 @@ public: // end of the array. if (wordsToShift < dim) { - ptr[dim - wordsToShift - 1] = rollRight(0, ptr[dim - 1] & endMask, + _ptr[dim - wordsToShift - 1] = rollRight(0, _ptr[dim - 1] & endMask, bitsToShift); } - import std.algorithm : min; + import std.algorithm.comparison : min; foreach (i; 0 .. min(wordsToShift, dim)) { - ptr[dim - i - 1] = 0; + _ptr[dim - i - 1] = 0; } } - unittest + @system unittest { import std.format : format; @@ -1901,7 +1955,7 @@ public: } // Test multi-word case - unittest + @system unittest { import std.format : format; @@ -1967,7 +2021,7 @@ public: void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const { - switch(fmt.spec) + switch (fmt.spec) { case 'b': return formatBitString(sink); @@ -1979,7 +2033,7 @@ public: } /// - unittest + @system unittest { import std.format : format; @@ -1998,19 +2052,19 @@ public: */ @property auto bitsSet() const nothrow { - import std.algorithm : filter, map, joiner; + import std.algorithm.iteration : filter, map, joiner; import std.range : iota; return iota(dim). - filter!(i => ptr[i])(). - map!(i => BitsSet!size_t(ptr[i], i * bitsPerSizeT))(). + filter!(i => _ptr[i])(). + map!(i => BitsSet!size_t(_ptr[i], i * bitsPerSizeT))(). joiner(); } /// - unittest + @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto b1 = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]); assert(b1.bitsSet.equal([4, 5, 6, 7, 12, 13, 14, 15])); @@ -2023,9 +2077,9 @@ public: assert(b2.bitsSet.equal([333, 666, 999])); } - unittest + @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : iota; debug(bitarray) printf("BitArray.bitsSet unittest\n"); @@ -2050,22 +2104,22 @@ public: if (!length) return; - auto leftover = len % 8; + auto leftover = _len % 8; foreach (idx; 0 .. leftover) { char[1] res = cast(char)(this[idx] + '0'); sink.put(res[]); } - if (leftover && len > 8) + if (leftover && _len > 8) sink.put("_"); size_t count; - foreach (idx; leftover .. len) + foreach (idx; leftover .. _len) { char[1] res = cast(char)(this[idx] + '0'); sink.put(res[]); - if (++count == 8 && idx != len - 1) + if (++count == 8 && idx != _len - 1) { sink.put("_"); count = 0; @@ -2076,18 +2130,18 @@ public: private void formatBitArray(scope void delegate(const(char)[]) sink) const { sink("["); - foreach (idx; 0 .. len) + foreach (idx; 0 .. _len) { char[1] res = cast(char)(this[idx] + '0'); sink(res[]); - if (idx+1 < len) + if (idx+1 < _len) sink(", "); } sink("]"); } } -unittest +@system unittest { import std.format : format; @@ -2123,18 +2177,18 @@ unittest Swaps the endianness of the given integral value or character. +/ T swapEndian(T)(T val) @safe pure nothrow @nogc - if(isIntegral!T || isSomeChar!T || isBoolean!T) +if (isIntegral!T || isSomeChar!T || isBoolean!T) { - static if(val.sizeof == 1) + static if (val.sizeof == 1) return val; - else static if(isUnsigned!T) + else static if (isUnsigned!T) return swapEndianImpl(val); - else static if(isIntegral!T) - return cast(T)swapEndianImpl(cast(Unsigned!T) val); - else static if(is(Unqual!T == wchar)) - return cast(T)swapEndian(cast(ushort)val); - else static if(is(Unqual!T == dchar)) - return cast(T)swapEndian(cast(uint)val); + else static if (isIntegral!T) + return cast(T) swapEndianImpl(cast(Unsigned!T) val); + else static if (is(Unqual!T == wchar)) + return cast(T) swapEndian(cast(ushort) val); + else static if (is(Unqual!T == dchar)) + return cast(T) swapEndian(cast(uint) val); else static assert(0, T.stringof ~ " unsupported by swapEndian."); } @@ -2147,21 +2201,21 @@ private ushort swapEndianImpl(ushort val) @safe pure nothrow @nogc private uint swapEndianImpl(uint val) @trusted pure nothrow @nogc { - import core.bitop: bswap; + import core.bitop : bswap; return bswap(val); } private ulong swapEndianImpl(ulong val) @trusted pure nothrow @nogc { - import core.bitop: bswap; - immutable ulong res = bswap(cast(uint)val); + import core.bitop : bswap; + immutable ulong res = bswap(cast(uint) val); return res << 32 | bswap(cast(uint)(val >> 32)); } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar)) + import std.meta; + foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar)) { scope(failure) writefln("Failed type: %s", T.stringof); T val; @@ -2174,28 +2228,28 @@ unittest assert(swapEndian(swapEndian(T.min)) == T.min); assert(swapEndian(swapEndian(T.max)) == T.max); - foreach(i; 2 .. 10) + foreach (i; 2 .. 10) { immutable T maxI = cast(T)(T.max / i); immutable T minI = cast(T)(T.min / i); assert(swapEndian(swapEndian(maxI)) == maxI); - static if(isSigned!T) + static if (isSigned!T) assert(swapEndian(swapEndian(minI)) == minI); } - static if(isSigned!T) - assert(swapEndian(swapEndian(cast(T)0)) == 0); + static if (isSigned!T) + assert(swapEndian(swapEndian(cast(T) 0)) == 0); // used to trigger BUG6354 - static if(T.sizeof > 1 && isUnsigned!T) + static if (T.sizeof > 1 && isUnsigned!T) { T left = 0xffU; left <<= (T.sizeof - 1) * 8; T right = 0xffU; - for(size_t i = 1; i < T.sizeof; ++i) + for (size_t i = 1; i < T.sizeof; ++i) { assert(swapEndian(left) == right); assert(swapEndian(right) == left); @@ -2208,14 +2262,14 @@ unittest private union EndianSwapper(T) - if(canSwapEndianness!T) +if (canSwapEndianness!T) { Unqual!T value; ubyte[T.sizeof] array; - static if(is(FloatingPointTypeOf!T == float)) + static if (is(FloatingPointTypeOf!T == float)) uint intValue; - else static if(is(FloatingPointTypeOf!T == double)) + else static if (is(FloatingPointTypeOf!T == double)) ulong intValue; } @@ -2235,13 +2289,13 @@ private union EndianSwapper(T) unusable if you tried to transfer it to another machine). +/ auto nativeToBigEndian(T)(T val) @safe pure nothrow @nogc - if(canSwapEndianness!T) +if (canSwapEndianness!T) { return nativeToBigEndianImpl(val); } /// -unittest +@safe unittest { int i = 12345; ubyte[4] swappedI = nativeToBigEndian(i); @@ -2253,7 +2307,7 @@ unittest } private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc - if(isIntegral!T || isSomeChar!T || isBoolean!T) +if (isIntegral!T || isSomeChar!T || isBoolean!T) { EndianSwapper!T es = void; @@ -2266,7 +2320,7 @@ private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc } private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc - if(isFloatOrDouble!T) +if (isFloatOrDouble!T) { version(LittleEndian) return floatEndianImpl!(T, true)(val); @@ -2274,11 +2328,11 @@ private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc return floatEndianImpl!(T, false)(val); } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, - char, wchar, dchar + import std.meta; + foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, + char, wchar, dchar /* The trouble here is with floats and doubles being compared against nan * using a bit compare. There are two kinds of nans, quiet and signaling. * When a nan passes through the x87, it converts signaling to quiet. @@ -2302,28 +2356,28 @@ unittest assert(bigEndianToNative!T(nativeToBigEndian(T.min)) == T.min); assert(bigEndianToNative!T(nativeToBigEndian(T.max)) == T.max); - static if(isSigned!T) - assert(bigEndianToNative!T(nativeToBigEndian(cast(T)0)) == 0); + static if (isSigned!T) + assert(bigEndianToNative!T(nativeToBigEndian(cast(T) 0)) == 0); - static if(!is(T == bool)) + static if (!is(T == bool)) { - foreach(i; [2, 4, 6, 7, 9, 11]) + foreach (i; [2, 4, 6, 7, 9, 11]) { immutable T maxI = cast(T)(T.max / i); immutable T minI = cast(T)(T.min / i); assert(bigEndianToNative!T(nativeToBigEndian(maxI)) == maxI); - static if(T.sizeof > 1) + static if (T.sizeof > 1) assert(nativeToBigEndian(maxI) != nativeToLittleEndian(maxI)); else assert(nativeToBigEndian(maxI) == nativeToLittleEndian(maxI)); - static if(isSigned!T) + static if (isSigned!T) { assert(bigEndianToNative!T(nativeToBigEndian(minI)) == minI); - static if(T.sizeof > 1) + static if (T.sizeof > 1) assert(nativeToBigEndian(minI) != nativeToLittleEndian(minI)); else assert(nativeToBigEndian(minI) == nativeToLittleEndian(minI)); @@ -2331,12 +2385,12 @@ unittest } } - static if(isUnsigned!T || T.sizeof == 1 || is(T == wchar)) + static if (isUnsigned!T || T.sizeof == 1 || is(T == wchar)) assert(nativeToBigEndian(T.max) == nativeToLittleEndian(T.max)); else assert(nativeToBigEndian(T.max) != nativeToLittleEndian(T.max)); - static if(isUnsigned!T || T.sizeof == 1 || isSomeChar!T) + static if (isUnsigned!T || T.sizeof == 1 || isSomeChar!T) assert(nativeToBigEndian(T.min) == nativeToLittleEndian(T.min)); else assert(nativeToBigEndian(T.min) != nativeToLittleEndian(T.min)); @@ -2357,13 +2411,13 @@ unittest can't actually have swapped floating point values as floating point values). +/ T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc - if(canSwapEndianness!T && n == T.sizeof) +if (canSwapEndianness!T && n == T.sizeof) { return bigEndianToNativeImpl!(T, n)(val); } /// -unittest +@safe unittest { ushort i = 12345; ubyte[2] swappedI = nativeToBigEndian(i); @@ -2375,8 +2429,8 @@ unittest } private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc - if((isIntegral!T || isSomeChar!T || isBoolean!T) && - n == T.sizeof) +if ((isIntegral!T || isSomeChar!T || isBoolean!T) && + n == T.sizeof) { EndianSwapper!T es = void; es.array = val; @@ -2390,7 +2444,7 @@ private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @n } private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc - if(isFloatOrDouble!T && n == T.sizeof) +if (isFloatOrDouble!T && n == T.sizeof) { version(LittleEndian) return cast(T) floatEndianImpl!(n, true)(val); @@ -2409,13 +2463,13 @@ private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @n can't actually have swapped floating point values as floating point values). +/ auto nativeToLittleEndian(T)(T val) @safe pure nothrow @nogc - if(canSwapEndianness!T) +if (canSwapEndianness!T) { return nativeToLittleEndianImpl(val); } /// -unittest +@safe unittest { int i = 12345; ubyte[4] swappedI = nativeToLittleEndian(i); @@ -2427,7 +2481,7 @@ unittest } private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc - if(isIntegral!T || isSomeChar!T || isBoolean!T) +if (isIntegral!T || isSomeChar!T || isBoolean!T) { EndianSwapper!T es = void; @@ -2440,7 +2494,7 @@ private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc } private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc - if(isFloatOrDouble!T) +if (isFloatOrDouble!T) { version(BigEndian) return floatEndianImpl!(T, true)(val); @@ -2448,12 +2502,12 @@ private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc return floatEndianImpl!(T, false)(val); } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, - char, wchar, dchar/*, - float, double*/)) + import std.meta; + foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, + char, wchar, dchar/*, + float, double*/)) { scope(failure) writefln("Failed type: %s", T.stringof); T val; @@ -2467,19 +2521,19 @@ unittest assert(littleEndianToNative!T(nativeToLittleEndian(T.min)) == T.min); assert(littleEndianToNative!T(nativeToLittleEndian(T.max)) == T.max); - static if(isSigned!T) - assert(littleEndianToNative!T(nativeToLittleEndian(cast(T)0)) == 0); + static if (isSigned!T) + assert(littleEndianToNative!T(nativeToLittleEndian(cast(T) 0)) == 0); - static if(!is(T == bool)) + static if (!is(T == bool)) { - foreach(i; 2 .. 10) + foreach (i; 2 .. 10) { immutable T maxI = cast(T)(T.max / i); immutable T minI = cast(T)(T.min / i); assert(littleEndianToNative!T(nativeToLittleEndian(maxI)) == maxI); - static if(isSigned!T) + static if (isSigned!T) assert(littleEndianToNative!T(nativeToLittleEndian(minI)) == minI); } } @@ -2504,13 +2558,13 @@ unittest unusable if you tried to transfer it to another machine). +/ T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc - if(canSwapEndianness!T && n == T.sizeof) +if (canSwapEndianness!T && n == T.sizeof) { return littleEndianToNativeImpl!T(val); } /// -unittest +@safe unittest { ushort i = 12345; ubyte[2] swappedI = nativeToLittleEndian(i); @@ -2522,8 +2576,8 @@ unittest } private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc - if((isIntegral!T || isSomeChar!T || isBoolean!T) && - n == T.sizeof) +if ((isIntegral!T || isSomeChar!T || isBoolean!T) && + n == T.sizeof) { EndianSwapper!T es = void; es.array = val; @@ -2537,8 +2591,8 @@ private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow } private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc - if(((isFloatOrDouble!T) && - n == T.sizeof)) +if (((isFloatOrDouble!T) && + n == T.sizeof)) { version(BigEndian) return floatEndianImpl!(n, true)(val); @@ -2547,26 +2601,26 @@ private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow } private auto floatEndianImpl(T, bool swap)(T val) @safe pure nothrow @nogc - if(isFloatOrDouble!T) +if (isFloatOrDouble!T) { EndianSwapper!T es = void; es.value = val; - static if(swap) + static if (swap) es.intValue = swapEndian(es.intValue); return es.array; } private auto floatEndianImpl(size_t n, bool swap)(ubyte[n] val) @safe pure nothrow @nogc - if(n == 4 || n == 8) +if (n == 4 || n == 8) { - static if(n == 4) EndianSwapper!float es = void; - else static if(n == 8) EndianSwapper!double es = void; + static if (n == 4) EndianSwapper!float es = void; + else static if (n == 8) EndianSwapper!double es = void; es.array = val; - static if(swap) + static if (swap) es.intValue = swapEndian(es.intValue); return es.value; @@ -2578,10 +2632,10 @@ private template isFloatOrDouble(T) !is(Unqual!(FloatingPointTypeOf!T) == real); } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(float, double)) + import std.meta; + foreach (T; AliasSeq!(float, double)) { static assert(isFloatOrDouble!(T)); static assert(isFloatOrDouble!(const T)); @@ -2607,11 +2661,11 @@ private template canSwapEndianness(T) isFloatOrDouble!T; } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(bool, ubyte, byte, ushort, short, uint, int, ulong, - long, char, wchar, dchar, float, double)) + import std.meta; + foreach (T; AliasSeq!(bool, ubyte, byte, ushort, short, uint, int, ulong, + long, char, wchar, dchar, float, double)) { static assert(canSwapEndianness!(T)); static assert(canSwapEndianness!(const T)); @@ -2622,7 +2676,7 @@ unittest } //! - foreach(T; TypeTuple!(real, string, wstring, dstring)) + foreach (T; AliasSeq!(real, string, wstring, dstring)) { static assert(!canSwapEndianness!(T)); static assert(!canSwapEndianness!(const T)); @@ -2649,11 +2703,11 @@ unittest +/ T peek(T, Endian endianness = Endian.bigEndian, R)(R range) - if (canSwapEndianness!T && - isForwardRange!R && - is(ElementType!R : const ubyte)) +if (canSwapEndianness!T && + isForwardRange!R && + is(ElementType!R : const ubyte)) { - static if(hasSlicing!R) + static if (hasSlicing!R) const ubyte[T.sizeof] bytes = range[0 .. T.sizeof]; else { @@ -2661,14 +2715,14 @@ T peek(T, Endian endianness = Endian.bigEndian, R)(R range) //Make sure that range is not consumed, even if it's a class. range = range.save; - foreach(ref e; bytes) + foreach (ref e; bytes) { e = range.front; range.popFront(); } } - static if(endianness == Endian.bigEndian) + static if (endianness == Endian.bigEndian) return bigEndianToNative!T(bytes); else return littleEndianToNative!T(bytes); @@ -2676,20 +2730,20 @@ T peek(T, Endian endianness = Endian.bigEndian, R)(R range) /++ Ditto +/ T peek(T, Endian endianness = Endian.bigEndian, R)(R range, size_t index) - if(canSwapEndianness!T && - isForwardRange!R && - hasSlicing!R && - is(ElementType!R : const ubyte)) +if (canSwapEndianness!T && + isForwardRange!R && + hasSlicing!R && + is(ElementType!R : const ubyte)) { return peek!(T, endianness)(range, &index); } /++ Ditto +/ T peek(T, Endian endianness = Endian.bigEndian, R)(R range, size_t* index) - if(canSwapEndianness!T && - isForwardRange!R && - hasSlicing!R && - is(ElementType!R : const ubyte)) +if (canSwapEndianness!T && + isForwardRange!R && + hasSlicing!R && + is(ElementType!R : const ubyte)) { assert(index); @@ -2698,14 +2752,14 @@ T peek(T, Endian endianness = Endian.bigEndian, R)(R range, size_t* index) const ubyte[T.sizeof] bytes = range[begin .. end]; *index = end; - static if(endianness == Endian.bigEndian) + static if (endianness == Endian.bigEndian) return bigEndianToNative!T(bytes); else return littleEndianToNative!T(bytes); } /// -unittest +@system unittest { ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8]; assert(buffer.peek!uint() == 17110537); @@ -2727,7 +2781,7 @@ unittest assert(index == 7); } -unittest +@system unittest { { //bool @@ -2928,9 +2982,9 @@ unittest } } -unittest +@safe unittest { - import std.algorithm; + import std.algorithm.iteration : filter; ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7]; auto range = filter!"true"(buffer); assert(range.peek!uint() == 17110537); @@ -2951,9 +3005,9 @@ unittest range = The range to read from. +/ T read(T, Endian endianness = Endian.bigEndian, R)(ref R range) - if(canSwapEndianness!T && isInputRange!R && is(ElementType!R : const ubyte)) +if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const ubyte)) { - static if(hasSlicing!R) + static if (hasSlicing!R && is(typeof(R.init[0 .. 0]) : const(ubyte)[])) { const ubyte[T.sizeof] bytes = range[0 .. T.sizeof]; range.popFrontN(T.sizeof); @@ -2962,22 +3016,23 @@ T read(T, Endian endianness = Endian.bigEndian, R)(ref R range) { ubyte[T.sizeof] bytes; - foreach(ref e; bytes) + foreach (ref e; bytes) { e = range.front; range.popFront(); } } - static if(endianness == Endian.bigEndian) + static if (endianness == Endian.bigEndian) return bigEndianToNative!T(bytes); else return littleEndianToNative!T(bytes); } /// -unittest +@safe unittest { + import std.range.primitives : empty; ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8]; assert(buffer.length == 7); @@ -2991,7 +3046,7 @@ unittest assert(buffer.empty); } -unittest +@safe unittest { { //bool @@ -3164,9 +3219,9 @@ unittest } } -unittest +@safe unittest { - import std.algorithm; + import std.algorithm.iteration : filter; ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8]; auto range = filter!"true"(buffer); assert(walkLength(range) == 7); @@ -3181,6 +3236,32 @@ unittest assert(range.empty); } +// issue 17247 +@safe unittest +{ + struct UbyteRange + { + ubyte[] impl; + @property bool empty() { return impl.empty; } + @property ubyte front() { return impl.front; } + void popFront() { impl.popFront(); } + @property UbyteRange save() { return this; } + + // N.B. support slicing but do not return ubyte[] slices. + UbyteRange opSlice(size_t start, size_t end) + { + return UbyteRange(impl[start .. end]); + } + @property size_t length() { return impl.length; } + size_t opDollar() { return impl.length; } + } + static assert(hasSlicing!UbyteRange); + + auto r = UbyteRange([0x01, 0x00, 0x00, 0x00]); + int x = r.read!(int, Endian.littleEndian)(); + assert(x == 1); +} + /++ Takes an integral value, converts it to the given endianness, and writes it @@ -3189,30 +3270,31 @@ unittest Params: T = The integral type to convert the first $(D T.sizeof) bytes to. - endianness = The endianness to write the bytes in. - range = The range to write to. + endianness = The endianness to _write the bytes in. + range = The range to _write to. + value = The value to _write. index = The index to start writing to. If index is a pointer, then it is updated to the index after the bytes read. +/ void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t index) - if(canSwapEndianness!T && - isForwardRange!R && - hasSlicing!R && - is(ElementType!R : ubyte)) +if (canSwapEndianness!T && + isForwardRange!R && + hasSlicing!R && + is(ElementType!R : ubyte)) { write!(T, endianness)(range, value, &index); } /++ Ditto +/ void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t* index) - if(canSwapEndianness!T && - isForwardRange!R && - hasSlicing!R && - is(ElementType!R : ubyte)) +if (canSwapEndianness!T && + isForwardRange!R && + hasSlicing!R && + is(ElementType!R : ubyte)) { assert(index); - static if(endianness == Endian.bigEndian) + static if (endianness == Endian.bigEndian) immutable bytes = nativeToBigEndian!T(value); else immutable bytes = nativeToLittleEndian!T(value); @@ -3224,7 +3306,7 @@ void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t* } /// -unittest +@system unittest { { ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0]; @@ -3267,7 +3349,7 @@ unittest } } -unittest +@system unittest { { //bool @@ -3535,12 +3617,13 @@ unittest Params: T = The integral type to convert the first $(D T.sizeof) bytes to. endianness = The endianness to write the bytes in. - range = The range to append to. + range = The range to _append to. + value = The value to _append. +/ void append(T, Endian endianness = Endian.bigEndian, R)(R range, T value) - if(canSwapEndianness!T && isOutputRange!(R, ubyte)) +if (canSwapEndianness!T && isOutputRange!(R, ubyte)) { - static if(endianness == Endian.bigEndian) + static if (endianness == Endian.bigEndian) immutable bytes = nativeToBigEndian!T(value); else immutable bytes = nativeToLittleEndian!T(value); @@ -3549,7 +3632,7 @@ void append(T, Endian endianness = Endian.bigEndian, R)(R range, T value) } /// -unittest +@safe unittest { import std.array; auto buffer = appender!(const ubyte[])(); @@ -3563,7 +3646,7 @@ unittest assert(buffer.data == [1, 5, 22, 9, 44, 255, 8]); } -unittest +@safe unittest { import std.array; { @@ -3694,23 +3777,23 @@ unittest } } -unittest +@system unittest { import std.format : format; import std.array; - import std.typetuple; - foreach(endianness; TypeTuple!(Endian.bigEndian, Endian.littleEndian)) + import std.meta; + foreach (endianness; AliasSeq!(Endian.bigEndian, Endian.littleEndian)) { auto toWrite = appender!(ubyte[])(); - alias Types = TypeTuple!(uint, int, long, ulong, short, ubyte, ushort, byte, uint); + alias Types = AliasSeq!(uint, int, long, ulong, short, ubyte, ushort, byte, uint); ulong[] values = [42, -11, long.max, 1098911981329L, 16, 255, 19012, 2, 17]; assert(Types.length == values.length); size_t index = 0; size_t length = 0; - foreach(T; Types) + foreach (T; Types) { - toWrite.append!(T, endianness)(cast(T)values[index++]); + toWrite.append!(T, endianness)(cast(T) values[index++]); length += T.sizeof; } @@ -3718,7 +3801,7 @@ unittest assert(toRead.length == length); index = 0; - foreach(T; Types) + foreach (T; Types) { assert(toRead.peek!(T, endianness)() == values[index], format("Failed Index: %s", index)); assert(toRead.peek!(T, endianness)(0) == values[index], format("Failed Index: %s", index)); @@ -3734,76 +3817,12 @@ unittest } } -/** -Counts the number of trailing zeros in the binary representation of $(D value). -For signed integers, the sign bit is included in the count. -*/ -private uint countTrailingZeros(T)(T value) @nogc pure nothrow - if (isIntegral!T) -{ - import core.bitop : bsf; - // bsf doesn't give the correct result for 0. - if (!value) - return 8 * T.sizeof; - - static if (T.sizeof == 8 && size_t.sizeof == 4) - { - // bsf's parameter is size_t, so it doesn't work with 64-bit integers - // on a 32-bit machine. For this case, we call bsf on each 32-bit half. - uint lower = cast(uint)value; - if (lower) - return bsf(lower); - value >>>= 32; - return 32 + bsf(cast(uint)value); - } - else - { - return bsf(value); - } -} - -/// -unittest -{ - assert(countTrailingZeros(1) == 0); - assert(countTrailingZeros(0) == 32); - assert(countTrailingZeros(int.min) == 31); - assert(countTrailingZeros(256) == 8); -} - -unittest -{ - import std.typetuple; - foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - assert(countTrailingZeros(cast(T)0) == 8 * T.sizeof); - assert(countTrailingZeros(cast(T)1) == 0); - assert(countTrailingZeros(cast(T)2) == 1); - assert(countTrailingZeros(cast(T)3) == 0); - assert(countTrailingZeros(cast(T)4) == 2); - assert(countTrailingZeros(cast(T)5) == 0); - assert(countTrailingZeros(cast(T)64) == 6); - static if (isSigned!T) - { - assert(countTrailingZeros(cast(T)-1) == 0); - assert(countTrailingZeros(T.min) == 8 * T.sizeof - 1); - } - else - { - assert(countTrailingZeros(T.max) == 0); - } - } - assert(countTrailingZeros(1_000_000) == 6); - foreach (i; 0..63) - assert(countTrailingZeros(1UL << i) == i); -} - /** Counts the number of set bits in the binary representation of $(D value). For signed integers, the sign bit is included in the count. */ private uint countBitsSet(T)(T value) @nogc pure nothrow - if (isIntegral!T) +if (isIntegral!T) { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel static if (T.sizeof == 8) @@ -3838,13 +3857,12 @@ private uint countBitsSet(T)(T value) @nogc pure nothrow } else { - static assert("countBitsSet only supports 1, 2, 4, or 8 byte sized integers."); + static assert(false, "countBitsSet only supports 1, 2, 4, or 8 byte sized integers."); } - return cast(uint)c; + return cast(uint) c; } -/// -unittest +@safe unittest { assert(countBitsSet(1) == 1); assert(countBitsSet(0) == 0); @@ -3852,18 +3870,18 @@ unittest assert(countBitsSet(uint.max) == 32); } -unittest +@safe unittest { - import std.typetuple; - foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + import std.meta; + foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { - assert(countBitsSet(cast(T)0) == 0); - assert(countBitsSet(cast(T)1) == 1); - assert(countBitsSet(cast(T)2) == 1); - assert(countBitsSet(cast(T)3) == 2); - assert(countBitsSet(cast(T)4) == 1); - assert(countBitsSet(cast(T)5) == 2); - assert(countBitsSet(cast(T)127) == 7); + assert(countBitsSet(cast(T) 0) == 0); + assert(countBitsSet(cast(T) 1) == 1); + assert(countBitsSet(cast(T) 2) == 1); + assert(countBitsSet(cast(T) 3) == 2); + assert(countBitsSet(cast(T) 4) == 1); + assert(countBitsSet(cast(T) 5) == 2); + assert(countBitsSet(cast(T) 127) == 7); static if (isSigned!T) { assert(countBitsSet(cast(T)-1) == 8 * T.sizeof); @@ -3875,7 +3893,7 @@ unittest } } assert(countBitsSet(1_000_000) == 7); - foreach (i; 0..63) + foreach (i; 0 .. 63) assert(countBitsSet(1UL << i) == 1); } @@ -3888,9 +3906,14 @@ private struct BitsSet(T) this(T value, size_t startIndex = 0) { _value = value; - uint n = countTrailingZeros(value); - _index = startIndex + n; - _value >>>= n; + // Further calculation is only valid and needed when the range is non-empty. + if (!_value) + return; + + import core.bitop : bsf; + immutable trailingZerosCount = bsf(value); + _value >>>= trailingZerosCount; + _index = startIndex + trailingZerosCount; } @property size_t front() @@ -3908,9 +3931,14 @@ private struct BitsSet(T) assert(_value, "Cannot call popFront on empty range."); _value >>>= 1; - uint n = countTrailingZeros(_value); - _value >>>= n; - _index += n + 1; + // Further calculation is only valid and needed when the range is non-empty. + if (!_value) + return; + + import core.bitop : bsf; + immutable trailingZerosCount = bsf(_value); + _value >>>= trailingZerosCount; + _index += trailingZerosCount + 1; } @property auto save() @@ -3933,15 +3961,15 @@ Index 0 corresponds to the least significant bit. For signed integers, the highest index corresponds to the sign bit. */ auto bitsSet(T)(T value) @nogc pure nothrow - if (isIntegral!T) +if (isIntegral!T) { return BitsSet!T(value); } /// -unittest +@safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : iota; assert(bitsSet(1).equal([0])); @@ -3950,21 +3978,21 @@ unittest assert(bitsSet(int.min).equal([31])); } -unittest +@safe unittest { - import std.algorithm : equal; - import std.range: iota; + import std.algorithm.comparison : equal; + import std.range : iota; - import std.typetuple; - foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + import std.meta; + foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { - assert(bitsSet(cast(T)0).empty); - assert(bitsSet(cast(T)1).equal([0])); - assert(bitsSet(cast(T)2).equal([1])); - assert(bitsSet(cast(T)3).equal([0, 1])); - assert(bitsSet(cast(T)4).equal([2])); - assert(bitsSet(cast(T)5).equal([0, 2])); - assert(bitsSet(cast(T)127).equal(iota(7))); + assert(bitsSet(cast(T) 0).empty); + assert(bitsSet(cast(T) 1).equal([0])); + assert(bitsSet(cast(T) 2).equal([1])); + assert(bitsSet(cast(T) 3).equal([0, 1])); + assert(bitsSet(cast(T) 4).equal([2])); + assert(bitsSet(cast(T) 5).equal([0, 2])); + assert(bitsSet(cast(T) 127).equal(iota(7))); static if (isSigned!T) { assert(bitsSet(cast(T)-1).equal(iota(8 * T.sizeof))); @@ -3976,6 +4004,6 @@ unittest } } assert(bitsSet(1_000_000).equal([6, 9, 14, 16, 17, 18, 19])); - foreach (i; 0..63) + foreach (i; 0 .. 63) assert(bitsSet(1UL << i).equal([i])); } diff --git a/std/c/fenv.d b/std/c/fenv.d index d574a726316..63e27553b87 100644 --- a/std/c/fenv.d +++ b/std/c/fenv.d @@ -1,15 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.fenv) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.fenv) instead. This module will be + * removed in June 2017.) + * * C's <fenv.h> * Authors: Walter Bright, Digital Mars, http://www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCFenv */ - -/// Please import core.stdc.fenv instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.fenv instead") module std.c.fenv; public import core.stdc.fenv; diff --git a/std/c/freebsd/socket.d b/std/c/freebsd/socket.d index 0b19b2e046d..5a73d47572c 100644 --- a/std/c/freebsd/socket.d +++ b/std/c/freebsd/socket.d @@ -1,10 +1,12 @@ // Written in the D programming language. -/* - * This module is just for making std.socket work under FreeBSD, and these - * definitions should actually be in druntime. (core.sys.posix.netdb or sth) - */ -/// Please import the core.sys.posix.* modules you need instead. This module will be deprecated in DMD 2.068. +// @@@DEPRECATED_2017-06@@@ + +/++ + $(RED Deprecated. Use the appropriate $(D core.sys.posix.*) modules instead. + This module will be removed in June 2017.) + +/ +deprecated("Import the appropriate core.sys.posix.* modules instead") module std.c.freebsd.socket; version (FreeBSD): diff --git a/std/c/linux/linux.d b/std/c/linux/linux.d index 8e21afc093b..165068a1034 100644 --- a/std/c/linux/linux.d +++ b/std/c/linux/linux.d @@ -1,3 +1,4 @@ +// @@@DEPRECATED_2017-06@@@ /* Written by Walter Bright, Christopher E. Miller, and many others. * http://www.digitalmars.com/d/ @@ -6,7 +7,11 @@ * countries. */ -/// Please import the core.sys.posix.* modules you need instead. This module will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Use the appropriate $(D core.sys.posix.*) modules instead. + This module will be removed in June 2017.) + +/ +deprecated("Import the appropriate core.sys.posix.* modules instead") module std.c.linux.linux; version (linux): diff --git a/std/c/linux/linuxextern.d b/std/c/linux/linuxextern.d index fcf3b3ee722..f2c15ec7c2a 100644 --- a/std/c/linux/linuxextern.d +++ b/std/c/linux/linuxextern.d @@ -1,3 +1,4 @@ +// @@@DEPRECATED_2017-06@@@ /* Written by Walter Bright. * www.digitalmars.com @@ -6,11 +7,11 @@ * countries. */ -/* These are all the globals defined by the linux C runtime library. - * Put them separate so they'll be externed - do not link in linuxextern.o - */ - -/// Please remove this import. This module is empty and will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Remove this import. This module no longer contains + anything.) + +/ +deprecated("This module no longer contains anything. Just remove the import.") module std.c.linux.linuxextern; // No longer needed since "extern" storage class diff --git a/std/c/linux/pthread.d b/std/c/linux/pthread.d index f2976f0fc1a..041bc570c26 100644 --- a/std/c/linux/pthread.d +++ b/std/c/linux/pthread.d @@ -1,9 +1,16 @@ +// @@@DEPRECATED_2017-06@@@ + /* Written by Walter Bright, Christopher E. Miller, and many others. * www.digitalmars.com * Placed into public domain. */ -/// Please import core.sys.posix.pthread or the other core.sys.posix.* modules you need instead. This module will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Use $(core.sys.posix.pthread) or the appropriate + $(D core.sys.posix.*) modules instead. This module will be removed in + June 2017.) + +/ +deprecated("Import core.sys.posix.pthread or the appropriate core.sys.posix.* modules instead") module std.c.linux.pthread; version (linux): diff --git a/std/c/linux/socket.d b/std/c/linux/socket.d index 9dba848708d..59c85c1c470 100644 --- a/std/c/linux/socket.d +++ b/std/c/linux/socket.d @@ -3,8 +3,13 @@ Placed into public domain. */ +// @@@DEPRECATED_2017-06@@@ -/// Please import the core.sys.posix.* modules you need instead. This module will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Use the appropriate $(D core.sys.posix.*) modules instead. + This module will be removed in June 2017.) + +/ +deprecated("Import the appropriate core.sys.posix.* modules instead") module std.c.linux.socket; version (linux): diff --git a/std/c/linux/termios.d b/std/c/linux/termios.d index bb5565751c1..2a9699f39e5 100644 --- a/std/c/linux/termios.d +++ b/std/c/linux/termios.d @@ -1,6 +1,11 @@ +// @@@DEPRECATED_2017-06@@@ -/// Please import core.sys.posix.termios instead. This module will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Use $(D core.sys.posix.termios) instead. This module will + be removed in June 2017.) + +/ +deprecated("Import core.sys.posix.termios instead") module std.c.linux.termios; version (linux): diff --git a/std/c/linux/tipc.d b/std/c/linux/tipc.d index c17dec52f25..44eba9ffe3f 100644 --- a/std/c/linux/tipc.d +++ b/std/c/linux/tipc.d @@ -1,6 +1,9 @@ +// @@@DEPRECATED_2017-06@@@ + /** - * $(RED Deprecated. Please use $(D core.sys.linux.tipc) instead. This module - * will be removed in December 2015.) + * $(RED Deprecated. Use $(D core.sys.linux.tipc) instead. This module will be + * removed in June 2017.) + * * Interface for Linux TIPC sockets, /usr/include/linux/tipc.h * * Copyright: Public Domain @@ -8,7 +11,7 @@ * Authors: Leandro Lucarella */ -/// Please import core.sys.linux.tipc instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.sys.linux.tipc instead") module std.c.linux.tipc; public import core.sys.linux.tipc; diff --git a/std/c/locale.d b/std/c/locale.d index b2d5eb207d8..39924a6f758 100644 --- a/std/c/locale.d +++ b/std/c/locale.d @@ -1,14 +1,15 @@ +// @@@DEPRECATED_2017-06@@@ + /** - * $(RED Deprecated. Please use $(D core.stdc.locale) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.locale) instead. This module will be + * removed in June 2017.) + * * C's <locale.h> * License: Public Domain * Standards: * ISO/IEC 9899:1999 7.11 - * Macros: - * WIKI=Phobos/StdCLocale */ -/// Please import core.stdc.locale instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.locale instead") module std.c.locale; public import core.stdc.locale; diff --git a/std/c/math.d b/std/c/math.d index d179237c6b1..4a7be70f948 100644 --- a/std/c/math.d +++ b/std/c/math.d @@ -1,15 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.math) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.math) instead. This module will be + * removed in June 2017.) + * * C's <math.h> * Authors: Walter Bright, Digital Mars, www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCMath */ - -/// Please import core.stdc.math instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.math instead") module std.c.math; public import core.stdc.math; diff --git a/std/c/osx/socket.d b/std/c/osx/socket.d index 262a196ccc7..835ed276cd2 100644 --- a/std/c/osx/socket.d +++ b/std/c/osx/socket.d @@ -3,8 +3,13 @@ Placed into public domain. */ +// @@@DEPRECATED_2017-06@@@ -/// Please import the core.sys.posix.* modules you need instead. This module will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Use the appropriate $(D core.sys.posix.*) modules instead. + This module will be removed in June 2017.) + +/ +deprecated("Import the appropriate core.sys.posix.* instead") module std.c.osx.socket; version (OSX): diff --git a/std/c/process.d b/std/c/process.d index 6aafec6fc20..14fc729c6c7 100644 --- a/std/c/process.d +++ b/std/c/process.d @@ -1,15 +1,15 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.stdlib) or the core.sys.posix.* - * modules you need instead. This module will be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.stdlib) or the appropriate + * core.sys.posix.* modules instead. This module will be removed in June + * 2017.) + * * C's <process.h> * Authors: Walter Bright, Digital Mars, www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCProcess */ - -/// Please import core.stdc.stdlib or the core.sys.posix.* modules you need instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.stdlib or the appropriate core.sys.posix.* modules instead") module std.c.process; private import core.stdc.stddef; @@ -29,7 +29,7 @@ int execlpe(in char *, in char *,...); //These constants are undefined elsewhere and only used in the deprecated part //of std.process. -enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY }; +enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY } //These declarations are defined for Posix in core.sys.posix.unistd but unused //from here. diff --git a/std/c/stdarg.d b/std/c/stdarg.d index e82a357abe0..2f446b2a43b 100644 --- a/std/c/stdarg.d +++ b/std/c/stdarg.d @@ -1,17 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.stdarg) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.stdarg) instead. This module will be + * removed in June 2017.) + * * C's <stdarg.h> * Authors: Hauke Duden and Walter Bright, Digital Mars, www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCStdarg */ - -/* This is for use with extern(C) variable argument lists. */ - -/// Please import core.stdc.stdarg instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.stdarg instead") module std.c.stdarg; public import core.stdc.stdarg; diff --git a/std/c/stddef.d b/std/c/stddef.d index 4eb74cff70c..adb78a338c8 100644 --- a/std/c/stddef.d +++ b/std/c/stddef.d @@ -1,15 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.stddef) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.stddef) instead. This module will be + * removed in June 2017.) + * * C's <stddef.h> * Authors: Walter Bright, Digital Mars, http://www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCStddef */ - -/// Please import core.stdc.stddef instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.stddef instead") module std.c.stddef; public import core.stdc.stddef; diff --git a/std/c/stdio.d b/std/c/stdio.d index 95fcdfefd97..330c61cc529 100644 --- a/std/c/stdio.d +++ b/std/c/stdio.d @@ -1,17 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.stdio) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.stdio) instead. This module will be + * removed in June 2017.) + * * C's <stdio.h> for the D programming language * Authors: Walter Bright, Digital Mars, http://www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCStdio */ - - - -/// Please import core.stdc.stdio instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.stdio instead") module std.c.stdio; public import core.stdc.stdio; diff --git a/std/c/stdlib.d b/std/c/stdlib.d index 412d94f39e1..e05da65a1c1 100644 --- a/std/c/stdlib.d +++ b/std/c/stdlib.d @@ -1,17 +1,16 @@ +// @@@DEPRECATED_2017-06@@@ + /** - * $(RED Deprecated. Please use $(D core.stdc.stdlib) or $(D core.sys.posix.stdlib) - * instead. This module will be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.stdlib) or $(D core.sys.posix.stdlib) + * instead. This module will be removed in June 2017.) + * * C's <stdlib.h> * D Programming Language runtime library * Authors: Walter Bright, Digital Mars, http://www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCStdlib */ - - -/// Please import core.stdc.stdlib or core.sys.posix.stdlib instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.stdlib or core.sys.posix.stdlib instead") module std.c.stdlib; public import core.stdc.stdlib; -version(Posix) public import core.sys.posix.stdlib: setenv, unsetenv; +version(Posix) public import core.sys.posix.stdlib : setenv, unsetenv; diff --git a/std/c/string.d b/std/c/string.d index cfd5c00771c..ea766a0efbe 100644 --- a/std/c/string.d +++ b/std/c/string.d @@ -1,15 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.string) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.string) instead. This module will be + * removed in June 2017.) + * * C's <string.h> * Authors: Walter Bright, Digital Mars, http://www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCString */ - -/// Please import core.stdc.string instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.string instead") module std.c.string; public import core.stdc.string; diff --git a/std/c/time.d b/std/c/time.d index 24e5bc79064..eb211168d2a 100644 --- a/std/c/time.d +++ b/std/c/time.d @@ -1,15 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.time) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.time) instead. This module will be + * removed in June 2017.) + * * C's <time.h> * Authors: Walter Bright, Digital Mars, www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCTime */ - -/// Please import core.stdc.time instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.time instead") module std.c.time; public import core.stdc.time; diff --git a/std/c/wcharh.d b/std/c/wcharh.d index 561d4f10054..adf8392c249 100644 --- a/std/c/wcharh.d +++ b/std/c/wcharh.d @@ -1,15 +1,14 @@ +// @@@DEPRECATED_2017-06@@@ /** - * $(RED Deprecated. Please use $(D core.stdc.wchar_) instead. This module will - * be removed in December 2015.) + * $(RED Deprecated. Use $(D core.stdc.wchar_) instead. This module will be + * removed in June 2017.) + * * C's <wchar.h> * Authors: Walter Bright, Digital Mars, www.digitalmars.com * License: Public Domain - * Macros: - * WIKI=Phobos/StdCWchar */ - -/// Please import core.stdc.wchar_ instead. This module will be deprecated in DMD 2.068. +deprecated("Import core.stdc.wchar_ instead") module std.c.wcharh; public import core.stdc.wchar_; diff --git a/std/c/windows/com.d b/std/c/windows/com.d index cd0fb93a2cb..e96aceb8503 100644 --- a/std/c/windows/com.d +++ b/std/c/windows/com.d @@ -1,4 +1,10 @@ -/// Please import core.sys.windows.com instead. This module will be deprecated in DMD 2.068. +// @@@DEPRECATED_2017-06@@@ + +/++ + $(RED Deprecated. Use $(D core.sys.windows.com) instead. This module will be + removed in June 2017.) + +/ +deprecated("Import core.sys.windows.com instead") module std.c.windows.com; version (Windows): diff --git a/std/c/windows/stat.d b/std/c/windows/stat.d index 018b2a07591..8aa082efd91 100644 --- a/std/c/windows/stat.d +++ b/std/c/windows/stat.d @@ -2,7 +2,13 @@ /// Placed into public domain /// Author: Walter Bright -/// Please import core.sys.windows.stat instead. This module will be deprecated in DMD 2.068. +// @@@DEPRECATED_2017-06@@@ + +/++ + $(RED Deprecated. Use $(D core.sys.windows.stat) instead. This module will be + removed in June 2017.) + +/ +deprecated("Import core.sys.windows.stat instead") module std.c.windows.stat; version (Windows): diff --git a/std/c/windows/windows.d b/std/c/windows/windows.d index 936c8377586..7fcdb8ef20d 100644 --- a/std/c/windows/windows.d +++ b/std/c/windows/windows.d @@ -2,7 +2,13 @@ /* Windows is a registered trademark of Microsoft Corporation in the United States and other countries. */ -/// Please import core.sys.windows.windows instead. This module will be deprecated in DMD 2.068. +// @@@DEPRECATED_2017-06@@@ + +/++ + $(RED Deprecated. Use $(D core.sys.windows.windows) instead. This module will + be removed in June 2017.) + +/ +deprecated("Import core.sys.windows.windows instead") module std.c.windows.windows; version (Windows): diff --git a/std/c/windows/winsock.d b/std/c/windows/winsock.d index 926fc2f4d97..5304e32b527 100644 --- a/std/c/windows/winsock.d +++ b/std/c/windows/winsock.d @@ -3,8 +3,13 @@ Placed into public domain. */ +// @@@DEPRECATED_2017-06@@@ -/// Please import core.sys.windows.winsock2 instead.This module will be deprecated in DMD 2.068. +/++ + $(RED Deprecated. Use $(D core.sys.windows.winsock2) instead. This module + will be removed in June 2017.) + +/ +deprecated("Import core.sys.windows.winsock2 instead") module std.c.windows.winsock; version (Windows): diff --git a/std/compiler.d b/std/compiler.d index 25936c94aec..6ceabcefb45 100644 --- a/std/compiler.d +++ b/std/compiler.d @@ -3,12 +3,9 @@ /** * Identify the compiler used and its various features. * - * Macros: - * WIKI = Phobos/StdCompiler - * * Copyright: Copyright Digital Mars 2000 - 2011. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright), Alex Rønne Petersen + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright), Alex Rønne Petersen * Source: $(PHOBOSSRC std/_compiler.d) */ /* Copyright Digital Mars 2000 - 2011. diff --git a/std/complex.d b/std/complex.d index 79c2c3ef97d..1ce874dbd24 100644 --- a/std/complex.d +++ b/std/complex.d @@ -3,13 +3,14 @@ /** This module contains the $(LREF Complex) type, which is used to represent _complex numbers, along with related mathematical operations and functions. - $(LREF Complex) will eventually $(LINK2 ../deprecate.html, replace) + $(LREF Complex) will eventually + $(DDLINK deprecate, Deprecated Features, replace) the built-in types $(D cfloat), $(D cdouble), $(D creal), $(D ifloat), $(D idouble), and $(D ireal). Authors: Lars Tandle Kyllingstad, Don Clugston Copyright: Copyright (c) 2010, Lars T. Kyllingstad. - License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) + License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) Source: $(PHOBOSSRC std/_complex.d) */ module std.complex; @@ -33,7 +34,8 @@ import std.traits; be $(D Complex!double). Otherwise, the return type is deduced using $(D std.traits.CommonType!(R, I)). */ -auto complex(R)(R re) @safe pure nothrow @nogc if (is(R : double)) +auto complex(R)(R re) @safe pure nothrow @nogc +if (is(R : double)) { static if (isFloatingPoint!R) return Complex!R(re, 0); @@ -43,7 +45,7 @@ auto complex(R)(R re) @safe pure nothrow @nogc if (is(R : double)) /// ditto auto complex(R, I)(R re, I im) @safe pure nothrow @nogc - if (is(R : double) && is(I : double)) +if (is(R : double) && is(I : double)) { static if (isFloatingPoint!R || isFloatingPoint!I) return Complex!(CommonType!(R, I))(re, im); @@ -52,51 +54,53 @@ auto complex(R, I)(R re, I im) @safe pure nothrow @nogc } /// -unittest +@safe pure nothrow unittest { auto a = complex(1.0); - static assert (is(typeof(a) == Complex!double)); - assert (a.re == 1.0); - assert (a.im == 0.0); + static assert(is(typeof(a) == Complex!double)); + assert(a.re == 1.0); + assert(a.im == 0.0); auto b = complex(2.0L); - static assert (is(typeof(b) == Complex!real)); - assert (b.re == 2.0L); - assert (b.im == 0.0L); + static assert(is(typeof(b) == Complex!real)); + assert(b.re == 2.0L); + assert(b.im == 0.0L); auto c = complex(1.0, 2.0); - static assert (is(typeof(c) == Complex!double)); - assert (c.re == 1.0); - assert (c.im == 2.0); + static assert(is(typeof(c) == Complex!double)); + assert(c.re == 1.0); + assert(c.im == 2.0); auto d = complex(3.0, 4.0L); - static assert (is(typeof(d) == Complex!real)); - assert (d.re == 3.0); - assert (d.im == 4.0L); + static assert(is(typeof(d) == Complex!real)); + assert(d.re == 3.0); + assert(d.im == 4.0L); auto e = complex(1); - static assert (is(typeof(e) == Complex!double)); - assert (e.re == 1); - assert (e.im == 0); + static assert(is(typeof(e) == Complex!double)); + assert(e.re == 1); + assert(e.im == 0); auto f = complex(1L, 2); - static assert (is(typeof(f) == Complex!double)); - assert (f.re == 1L); - assert (f.im == 2); + static assert(is(typeof(f) == Complex!double)); + assert(f.re == 1L); + assert(f.im == 2); auto g = complex(3, 4.0L); - static assert (is(typeof(g) == Complex!real)); - assert (g.re == 3); - assert (g.im == 4.0L); + static assert(is(typeof(g) == Complex!real)); + assert(g.re == 3); + assert(g.im == 4.0L); } /** A complex number parametrised by a type $(D T), which must be either $(D float), $(D double) or $(D real). */ -struct Complex(T) if (isFloatingPoint!T) +struct Complex(T) +if (isFloatingPoint!T) { import std.format : FormatSpec; + import std.range.primitives : isOutputRange; /** The real part of the number. */ T re; @@ -107,25 +111,26 @@ struct Complex(T) if (isFloatingPoint!T) /** Converts the complex number to a string representation. The second form of this function is usually not called directly; - instead, it is used via $(XREF string,format), as shown in the examples + instead, it is used via $(REF format, std,string), as shown in the examples below. Supported format characters are 'e', 'f', 'g', 'a', and 's'. - See the $(LINK2 std_format.html, std.format) and $(XREF string, format) + See the $(MREF std, format) and $(REF format, std,string) documentation for more information. */ - string toString() const /* TODO: @safe pure nothrow */ + string toString() const @safe /* TODO: pure nothrow */ { import std.exception : assumeUnique; char[] buf; buf.reserve(100); auto fmt = FormatSpec!char("%s"); toString((const(char)[] s) { buf ~= s; }, fmt); - return assumeUnique(buf); + static trustedAssumeUnique(T)(T t) @trusted { return assumeUnique(t); } + return trustedAssumeUnique(buf); } static if (is(T == double)) /// - unittest + @safe unittest { auto c = complex(1.2, 3.4); @@ -141,15 +146,18 @@ struct Complex(T) if (isFloatingPoint!T) } /// ditto - void toString(Char)(scope void delegate(const(Char)[]) sink, + void toString(Writer, Char)(scope Writer w, FormatSpec!Char formatSpec) const + if (isOutputRange!(Writer, const(Char)[])) { import std.math : signbit; import std.format : formatValue; - formatValue(sink, re, formatSpec); - if (signbit(im) == 0) sink("+"); - formatValue(sink, im, formatSpec); - sink("i"); + import std.range.primitives : put; + formatValue(w, re, formatSpec); + if (signbit(im) == 0) + put(w, "+"); + formatValue(w, im, formatSpec); + put(w, "i"); } @safe pure nothrow @nogc: @@ -283,7 +291,7 @@ struct Complex(T) if (isFloatingPoint!T) Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R lhs) const if (op == "^^" && isNumeric!R) { - import std.math : log, exp, PI; + import std.math : cos, exp, log, sin, PI; Unqual!(CommonType!(T, R)) ab = void, ar = void; if (lhs >= 0) @@ -301,7 +309,7 @@ struct Complex(T) if (isFloatingPoint!T) ar = PI * this.re + log(-lhs) * this.im; } - return typeof(return)(ab * std.math.cos(ar), ab * std.math.sin(ar)); + return typeof(return)(ab * cos(ar), ab * sin(ar)); } // OP-ASSIGN OPERATORS @@ -355,14 +363,14 @@ struct Complex(T) if (isFloatingPoint!T) ref Complex opOpAssign(string op, C)(C z) if (op == "^^" && is(C R == Complex!R)) { - import std.math : exp, log; + import std.math : exp, log, cos, sin; immutable r = abs(this); immutable t = arg(this); immutable ab = r^^z.re * exp(-t*z.im); immutable ar = t*z.re + log(r)*z.im; - re = ab*std.math.cos(ar); - im = ab*std.math.sin(ar); + re = ab*cos(ar); + im = ab*sin(ar); return this; } @@ -387,10 +395,11 @@ struct Complex(T) if (isFloatingPoint!T) ref Complex opOpAssign(string op, R)(R r) if (op == "^^" && isFloatingPoint!R) { + import std.math : cos, sin; immutable ab = abs(this)^^r; immutable ar = arg(this)*r; - re = ab*std.math.cos(ar); - im = ab*std.math.sin(ar); + re = ab*cos(ar); + im = ab*sin(ar); return this; } @@ -422,7 +431,7 @@ struct Complex(T) if (isFloatingPoint!T) } } -unittest +@safe pure nothrow unittest { import std.math; import std.complex; @@ -433,69 +442,73 @@ unittest // Check unary operations. auto c2 = Complex!double(0.5, 2.0); - assert (c2 == +c2); + assert(c2 == +c2); - assert ((-c2).re == -(c2.re)); - assert ((-c2).im == -(c2.im)); - assert (c2 == -(-c2)); + assert((-c2).re == -(c2.re)); + assert((-c2).im == -(c2.im)); + assert(c2 == -(-c2)); // Check complex-complex operations. auto cpc = c1 + c2; - assert (cpc.re == c1.re + c2.re); - assert (cpc.im == c1.im + c2.im); + assert(cpc.re == c1.re + c2.re); + assert(cpc.im == c1.im + c2.im); auto cmc = c1 - c2; - assert (cmc.re == c1.re - c2.re); - assert (cmc.im == c1.im - c2.im); + assert(cmc.re == c1.re - c2.re); + assert(cmc.im == c1.im - c2.im); auto ctc = c1 * c2; - assert (approxEqual(abs(ctc), abs(c1)*abs(c2), EPS)); - assert (approxEqual(arg(ctc), arg(c1)+arg(c2), EPS)); + assert(approxEqual(abs(ctc), abs(c1)*abs(c2), EPS)); + assert(approxEqual(arg(ctc), arg(c1)+arg(c2), EPS)); auto cdc = c1 / c2; - assert (approxEqual(abs(cdc), abs(c1)/abs(c2), EPS)); - assert (approxEqual(arg(cdc), arg(c1)-arg(c2), EPS)); + assert(approxEqual(abs(cdc), abs(c1)/abs(c2), EPS)); + assert(approxEqual(arg(cdc), arg(c1)-arg(c2), EPS)); auto cec = c1^^c2; - assert (approxEqual(cec.re, 0.11524131979943839881, EPS)); - assert (approxEqual(cec.im, 0.21870790452746026696, EPS)); + assert(approxEqual(cec.re, 0.11524131979943839881, EPS)); + assert(approxEqual(cec.im, 0.21870790452746026696, EPS)); // Check complex-real operations. double a = 123.456; auto cpr = c1 + a; - assert (cpr.re == c1.re + a); - assert (cpr.im == c1.im); + assert(cpr.re == c1.re + a); + assert(cpr.im == c1.im); auto cmr = c1 - a; - assert (cmr.re == c1.re - a); - assert (cmr.im == c1.im); + assert(cmr.re == c1.re - a); + assert(cmr.im == c1.im); auto ctr = c1 * a; - assert (ctr.re == c1.re*a); - assert (ctr.im == c1.im*a); + assert(ctr.re == c1.re*a); + assert(ctr.im == c1.im*a); auto cdr = c1 / a; - assert (approxEqual(abs(cdr), abs(c1)/a, EPS)); - assert (approxEqual(arg(cdr), arg(c1), EPS)); + assert(approxEqual(abs(cdr), abs(c1)/a, EPS)); + assert(approxEqual(arg(cdr), arg(c1), EPS)); auto cer = c1^^3.0; - assert (approxEqual(abs(cer), abs(c1)^^3, EPS)); - assert (approxEqual(arg(cer), arg(c1)*3, EPS)); + assert(approxEqual(abs(cer), abs(c1)^^3, EPS)); + assert(approxEqual(arg(cer), arg(c1)*3, EPS)); auto rpc = a + c1; - assert (rpc == cpr); + assert(rpc == cpr); auto rmc = a - c1; - assert (rmc.re == a-c1.re); - assert (rmc.im == -c1.im); + assert(rmc.re == a-c1.re); + assert(rmc.im == -c1.im); auto rtc = a * c1; - assert (rtc == ctr); + assert(rtc == ctr); auto rdc = a / c1; - assert (approxEqual(abs(rdc), a/abs(c1), EPS)); - assert (approxEqual(arg(rdc), -arg(c1), EPS)); + assert(approxEqual(abs(rdc), a/abs(c1), EPS)); + assert(approxEqual(arg(rdc), -arg(c1), EPS)); + + rdc = a / c2; + assert(approxEqual(abs(rdc), a/abs(c2), EPS)); + assert(approxEqual(arg(rdc), -arg(c2), EPS)); auto rec1a = 1.0 ^^ c1; assert(rec1a.re == 1.0); @@ -553,13 +566,13 @@ unittest assert(rer4.im == 0.0); // Check Complex-int operations. - foreach (i; 0..6) + foreach (i; 0 .. 6) { auto cei = c1^^i; - assert (approxEqual(abs(cei), abs(c1)^^i, EPS)); + assert(approxEqual(abs(cei), abs(c1)^^i, EPS)); // Use cos() here to deal with arguments that go outside // the (-pi,pi] interval (only an issue for i>3). - assert (approxEqual(std.math.cos(arg(cei)), std.math.cos(arg(c1)*i), EPS)); + assert(approxEqual(std.math.cos(arg(cei)), std.math.cos(arg(c1)*i), EPS)); } // Check operations between different complex types. @@ -567,49 +580,70 @@ unittest auto cr = Complex!real(1.0, 1.0); auto c1pcf = c1 + cf; auto c1pcr = c1 + cr; - static assert (is(typeof(c1pcf) == Complex!double)); - static assert (is(typeof(c1pcr) == Complex!real)); - assert (c1pcf.re == c1pcr.re); - assert (c1pcf.im == c1pcr.im); + static assert(is(typeof(c1pcf) == Complex!double)); + static assert(is(typeof(c1pcr) == Complex!real)); + assert(c1pcf.re == c1pcr.re); + assert(c1pcf.im == c1pcr.im); + + auto c1c = c1; + auto c2c = c2; + + c1c /= c1; + assert(approxEqual(c1c.re, 1.0, EPS)); + assert(approxEqual(c1c.im, 0.0, EPS)); + + c1c = c1; + c1c /= c2; + assert(approxEqual(c1c.re, 0.588235, EPS)); + assert(approxEqual(c1c.im, -0.352941, EPS)); + + c2c /= c1; + assert(approxEqual(c2c.re, 1.25, EPS)); + assert(approxEqual(c2c.im, 0.75, EPS)); + + c2c = c2; + c2c /= c2; + assert(approxEqual(c2c.re, 1.0, EPS)); + assert(approxEqual(c2c.im, 0.0, EPS)); } -unittest +@safe pure nothrow unittest { // Initialization Complex!double a = 1; - assert (a.re == 1 && a.im == 0); + assert(a.re == 1 && a.im == 0); Complex!double b = 1.0; - assert (b.re == 1.0 && b.im == 0); + assert(b.re == 1.0 && b.im == 0); Complex!double c = Complex!real(1.0, 2); - assert (c.re == 1.0 && c.im == 2); + assert(c.re == 1.0 && c.im == 2); } -unittest +@safe pure nothrow unittest { // Assignments and comparisons Complex!double z; z = 1; - assert (z == 1); - assert (z.re == 1.0 && z.im == 0.0); + assert(z == 1); + assert(z.re == 1.0 && z.im == 0.0); z = 2.0; - assert (z == 2.0); - assert (z.re == 2.0 && z.im == 0.0); + assert(z == 2.0); + assert(z.re == 2.0 && z.im == 0.0); z = 1.0L; - assert (z == 1.0L); - assert (z.re == 1.0 && z.im == 0.0); + assert(z == 1.0L); + assert(z.re == 1.0 && z.im == 0.0); auto w = Complex!real(1.0, 1.0); z = w; - assert (z == w); - assert (z.re == 1.0 && z.im == 1.0); + assert(z == w); + assert(z.re == 1.0 && z.im == 1.0); auto c = Complex!float(2.0, 2.0); z = c; - assert (z == c); - assert (z.re == 2.0 && z.im == 2.0); + assert(z == c); + assert(z.re == 2.0 && z.im == 2.0); } @@ -626,14 +660,15 @@ unittest --- The above will work if T is both real and complex. */ -template Complex(T) if (is(T R == Complex!R)) +template Complex(T) +if (is(T R == Complex!R)) { alias Complex = T; } -unittest +@safe pure nothrow unittest { - static assert (is(Complex!(Complex!real) == Complex!real)); + static assert(is(Complex!(Complex!real) == Complex!real)); Complex!T addI(T)(T x) { @@ -641,113 +676,143 @@ unittest } auto z1 = addI(1.0); - assert (z1.re == 1.0 && z1.im == 1.0); + assert(z1.re == 1.0 && z1.im == 1.0); enum one = Complex!double(1.0, 0.0); auto z2 = addI(one); - assert (z1 == z2); + assert(z1 == z2); } -/** Calculates the absolute value (or modulus) of a complex number. */ +/** + Params: z = A complex number. + Returns: The absolute value (or modulus) of `z`. +*/ T abs(T)(Complex!T z) @safe pure nothrow @nogc { import std.math : hypot; return hypot(z.re, z.im); } -unittest +/// +@safe pure nothrow unittest { - assert (abs(complex(1.0)) == 1.0); - assert (abs(complex(0.0, 1.0)) == 1.0); - assert (abs(complex(1.0L, -2.0L)) == std.math.sqrt(5.0L)); + static import std.math; + assert(abs(complex(1.0)) == 1.0); + assert(abs(complex(0.0, 1.0)) == 1.0); + assert(abs(complex(1.0L, -2.0L)) == std.math.sqrt(5.0L)); } /++ - Calculates the squared modulus of a complex number. - For genericity, if called on a real number, $(D sqAbs) returns its square. + Params: + z = A complex number. + x = A real number. + Returns: The squared modulus of `z`. + For genericity, if called on a real number, returns its square. +/ T sqAbs(T)(Complex!T z) @safe pure nothrow @nogc { return z.re*z.re + z.im*z.im; } -unittest +/// +@safe pure nothrow unittest { import std.math; - assert (sqAbs(complex(0.0)) == 0.0); - assert (sqAbs(complex(1.0)) == 1.0); - assert (sqAbs(complex(0.0, 1.0)) == 1.0); - assert (approxEqual(sqAbs(complex(1.0L, -2.0L)), 5.0L)); - assert (approxEqual(sqAbs(complex(-3.0L, 1.0L)), 10.0L)); - assert (approxEqual(sqAbs(complex(1.0f,-1.0f)), 2.0f)); + assert(sqAbs(complex(0.0)) == 0.0); + assert(sqAbs(complex(1.0)) == 1.0); + assert(sqAbs(complex(0.0, 1.0)) == 1.0); + assert(approxEqual(sqAbs(complex(1.0L, -2.0L)), 5.0L)); + assert(approxEqual(sqAbs(complex(-3.0L, 1.0L)), 10.0L)); + assert(approxEqual(sqAbs(complex(1.0f,-1.0f)), 2.0f)); } /// ditto T sqAbs(T)(T x) @safe pure nothrow @nogc - if (isFloatingPoint!T) +if (isFloatingPoint!T) { return x*x; } -unittest +@safe pure nothrow unittest { import std.math; - assert (sqAbs(0.0) == 0.0); - assert (sqAbs(-1.0) == 1.0); - assert (approxEqual(sqAbs(-3.0L), 9.0L)); - assert (approxEqual(sqAbs(-5.0f), 25.0f)); + assert(sqAbs(0.0) == 0.0); + assert(sqAbs(-1.0) == 1.0); + assert(approxEqual(sqAbs(-3.0L), 9.0L)); + assert(approxEqual(sqAbs(-5.0f), 25.0f)); } -/** Calculates the argument (or phase) of a complex number. */ +/** + Params: z = A complex number. + Returns: The argument (or phase) of `z`. + */ T arg(T)(Complex!T z) @safe pure nothrow @nogc { import std.math : atan2; return atan2(z.im, z.re); } -unittest +/// +@safe pure nothrow unittest { import std.math; - assert (arg(complex(1.0)) == 0.0); - assert (arg(complex(0.0L, 1.0L)) == PI_2); - assert (arg(complex(1.0L, 1.0L)) == PI_4); + assert(arg(complex(1.0)) == 0.0); + assert(arg(complex(0.0L, 1.0L)) == PI_2); + assert(arg(complex(1.0L, 1.0L)) == PI_4); } -/** Returns the complex conjugate of a complex number. */ +/** + Params: z = A complex number. + Returns: The complex conjugate of `z`. +*/ Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc { return Complex!T(z.re, -z.im); } -unittest +/// +@safe pure nothrow unittest { - assert (conj(complex(1.0)) == complex(1.0)); - assert (conj(complex(1.0, 2.0)) == complex(1.0, -2.0)); + assert(conj(complex(1.0)) == complex(1.0)); + assert(conj(complex(1.0, 2.0)) == complex(1.0, -2.0)); } -/** Constructs a complex number given its absolute value and argument. */ +/** + Constructs a complex number given its absolute value and argument. + Params: + modulus = The modulus + argument = The argument + Returns: The complex number with the given modulus and argument. +*/ Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) @safe pure nothrow @nogc { + import std.math : sin, cos; return Complex!(CommonType!(T,U)) - (modulus*std.math.cos(argument), modulus*std.math.sin(argument)); + (modulus*cos(argument), modulus*sin(argument)); } -unittest +/// +@safe pure nothrow unittest { import std.math; auto z = fromPolar(std.math.sqrt(2.0), PI_4); - assert (approxEqual(z.re, 1.0L, real.epsilon)); - assert (approxEqual(z.im, 1.0L, real.epsilon)); + assert(approxEqual(z.re, 1.0L, real.epsilon)); + assert(approxEqual(z.im, 1.0L, real.epsilon)); } -/** Trigonometric functions. */ +/** + Trigonometric functions on complex numbers. + + Params: z = A complex number. + Returns: The sine and cosine of `z`, respectively. +*/ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc { import std.math : expi, coshisinh; @@ -756,10 +821,12 @@ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc return typeof(return)(cs.im * csh.re, cs.re * csh.im); } -unittest +/// +@safe pure nothrow unittest { - assert(sin(complex(0.0)) == 0.0); - assert(sin(complex(2.0L, 0)) == std.math.sin(2.0L)); + static import std.math; + assert(sin(complex(0.0)) == 0.0); + assert(sin(complex(2.0L, 0)) == std.math.sin(2.0L)); } @@ -772,7 +839,9 @@ Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc return typeof(return)(cs.re * csh.re, - cs.im * csh.im); } -unittest{ +/// +@safe pure nothrow unittest +{ import std.math; import std.complex; assert(cos(complex(0.0)) == 1.0); @@ -781,11 +850,13 @@ unittest{ } -/** Calculates cos(y) + i sin(y). +/** + Params: y = A real number. + Returns: The value of cos(y) + i sin(y). Note: $(D expi) is included here for convenience and for easy migration of code - that uses $(XREF math,_expi). Unlike $(XREF math,_expi), which uses the + that uses $(REF _expi, std,math). Unlike $(REF _expi, std,math), which uses the x87 $(I fsincos) instruction when possible, this function is no faster than calculating cos(y) and sin(y) separately. */ @@ -795,8 +866,11 @@ Complex!real expi(real y) @trusted pure nothrow @nogc return Complex!real(cos(y), sin(y)); } -unittest +/// +@safe pure nothrow unittest { + static import std.math; + assert(expi(1.3e5L) == complex(std.math.cos(1.3e5L), std.math.sin(1.3e5L))); assert(expi(0.0L) == 1.0L); auto z1 = expi(1.234); @@ -805,10 +879,13 @@ unittest } -/** Square root. */ +/** + Params: z = A complex number. + Returns: The square root of `z`. +*/ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc { - import std.math : fabs; + static import std.math; typeof(return) c; real x,y,w,r; @@ -821,8 +898,8 @@ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc real z_re = z.re; real z_im = z.im; - x = fabs(z_re); - y = fabs(z_im); + x = std.math.fabs(z_re); + y = std.math.fabs(z_im); if (x >= y) { r = y / x; @@ -850,15 +927,33 @@ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc return c; } -unittest +/// +@safe pure nothrow unittest { - assert (sqrt(complex(0.0)) == 0.0); - assert (sqrt(complex(1.0L, 0)) == std.math.sqrt(1.0L)); - assert (sqrt(complex(-1.0L, 0)) == complex(0, 1.0L)); + static import std.math; + assert(sqrt(complex(0.0)) == 0.0); + assert(sqrt(complex(1.0L, 0)) == std.math.sqrt(1.0L)); + assert(sqrt(complex(-1.0L, 0)) == complex(0, 1.0L)); +} + +@safe pure nothrow unittest +{ + import std.math : approxEqual; + + auto c1 = complex(1.0, 1.0); + auto c2 = Complex!double(0.5, 2.0); + + auto c1s = sqrt(c1); + assert(approxEqual(c1s.re, 1.09868411)); + assert(approxEqual(c1s.im, 0.45508986)); + + auto c2s = sqrt(c2); + assert(approxEqual(c2s.re, 1.1317134)); + assert(approxEqual(c2s.im, 0.8836155)); } // Issue 10881: support %f formatting of complex numbers -unittest +@safe unittest { import std.format : format; @@ -869,7 +964,7 @@ unittest assert(format("%.2f", y) == "1.20-3.40i"); } -unittest +@safe unittest { // Test wide string formatting import std.format; @@ -885,7 +980,7 @@ unittest assert(wformat("%.2f", x) == "1.20+3.40i"w); } -unittest +@safe unittest { // Test ease of use (vanilla toString() should be supported) assert(complex(1.2, 3.4).toString() == "1.2+3.4i"); diff --git a/std/concurrency.d b/std/concurrency.d index d49b9020fa8..44b9d8f8285 100644 --- a/std/concurrency.d +++ b/std/concurrency.d @@ -63,29 +63,21 @@ */ module std.concurrency; +public import std.variant; + +import core.atomic; +import core.sync.condition; +import core.sync.mutex; +import core.thread; +import std.concurrencybase; +import std.range.primitives; +import std.traits; -public -{ - import std.variant; -} private { - import core.atomic; - import core.thread; - import core.sync.mutex; - import core.sync.condition; - import std.algorithm; - import std.datetime; - import std.exception; - import std.range; - import std.string; - import std.traits; - import std.typecons; - import std.typetuple; - template hasLocalAliasing(T...) { - static if( !T.length ) + static if (!T.length) enum hasLocalAliasing = false; else enum hasLocalAliasing = (std.traits.hasUnsharedAliasing!(T[0]) && !is(T[0] == Tid)) || @@ -104,114 +96,110 @@ private MsgType type; Variant data; - this(T...)( MsgType t, T vals ) - if( T.length < 1 ) - { - static assert( false, "messages must contain at least one item" ); - } - - this(T...)( MsgType t, T vals ) - if( T.length == 1 ) + this(T...)(MsgType t, T vals) if (T.length > 0) { - type = t; - data = vals[0]; - } + static if (T.length == 1) + { + type = t; + data = vals[0]; + } + else + { + import std.typecons : Tuple; - this(T...)( MsgType t, T vals ) - if( T.length > 1 ) - { - type = t; - data = Tuple!(T)( vals ); + type = t; + data = Tuple!(T)(vals); + } } @property auto convertsTo(T...)() { - static if( T.length == 1 ) - return is( T[0] == Variant ) || - data.convertsTo!(T); + static if (T.length == 1) + { + return is(T[0] == Variant) || data.convertsTo!(T); + } else + { + import std.typecons : Tuple; return data.convertsTo!(Tuple!(T)); + } } @property auto get(T...)() { - static if( T.length == 1 ) + static if (T.length == 1) { - static if( is( T[0] == Variant ) ) + static if (is(T[0] == Variant)) return data; else return data.get!(T); } else { + import std.typecons : Tuple; return data.get!(Tuple!(T)); } } - auto map(Op)( Op op ) + auto map(Op)(Op op) { - alias Args = ParameterTypeTuple!(Op); + alias Args = Parameters!(Op); - static if( Args.length == 1 ) + static if (Args.length == 1) { - static if( is( Args[0] == Variant ) ) - return op( data ); + static if (is(Args[0] == Variant)) + return op(data); else - return op( data.get!(Args) ); + return op(data.get!(Args)); } else { - return op( data.get!(Tuple!(Args)).expand ); + import std.typecons : Tuple; + return op(data.get!(Tuple!(Args)).expand); } } } - void checkops(T...)( T ops ) + void checkops(T...)(T ops) { - foreach( i, t1; T ) + foreach (i, t1; T) { - static assert( isFunctionPointer!t1 || isDelegate!t1 ); - alias a1 = ParameterTypeTuple!(t1); + static assert(isFunctionPointer!t1 || isDelegate!t1); + alias a1 = Parameters!(t1); alias r1 = ReturnType!(t1); - static if( i < T.length - 1 && is( r1 == void ) ) + static if (i < T.length - 1 && is(r1 == void)) { - static assert( a1.length != 1 || !is( a1[0] == Variant ), - "function with arguments " ~ a1.stringof ~ - " occludes successive function" ); + static assert(a1.length != 1 || !is(a1[0] == Variant), + "function with arguments " ~ a1.stringof ~ + " occludes successive function"); - foreach( t2; T[i+1 .. $] ) + foreach (t2; T[i + 1 .. $]) { - static assert( isFunctionPointer!t2 || isDelegate!t2 ); - alias a2 = ParameterTypeTuple!(t2); + static assert(isFunctionPointer!t2 || isDelegate!t2); + alias a2 = Parameters!(t2); - static assert( !is( a1 == a2 ), - "function with arguments " ~ a1.stringof ~ - " occludes successive function" ); + static assert(!is(a1 == a2), + "function with arguments " ~ a1.stringof ~ " occludes successive function"); } } } } - @property ref ThreadInfo thisInfo() + @property ref ThreadInfo thisInfo() nothrow { - if( scheduler is null ) + if (scheduler is null) return ThreadInfo.thisInfo; return scheduler.thisInfo; } } - static ~this() { thisInfo.cleanup(); } - -////////////////////////////////////////////////////////////////////////////// // Exceptions -////////////////////////////////////////////////////////////////////////////// - /** * Thrown on calls to $(D receiveOnly) if a message other than the type @@ -219,54 +207,55 @@ static ~this() */ class MessageMismatch : Exception { - this( string msg = "Unexpected message type" ) + /// + this(string msg = "Unexpected message type") @safe pure nothrow @nogc { - super( msg ); + super(msg); } } - /** * Thrown on calls to $(D receive) if the thread that spawned the receiving * thread has terminated and no more messages exist. */ class OwnerTerminated : Exception { - this( Tid t, string msg = "Owner terminated" ) + /// + this(Tid t, string msg = "Owner terminated") @safe pure nothrow @nogc { - super( msg ); + super(msg); tid = t; } Tid tid; } - /** * Thrown if a linked thread has terminated. */ class LinkTerminated : Exception { - this( Tid t, string msg = "Link terminated" ) + /// + this(Tid t, string msg = "Link terminated") @safe pure nothrow @nogc { - super( msg ); + super(msg); tid = t; } Tid tid; } - /** * Thrown if a message was sent to a thread via - * $(XREF concurrency, prioritySend) and the receiver does not have a handler + * $(REF prioritySend, std,concurrency) and the receiver does not have a handler * for a message of this type. */ class PriorityMessageException : Exception { - this( Variant vals ) + /// + this(Variant vals) { - super( "Priority message" ); + super("Priority message"); message = vals; } @@ -276,39 +265,35 @@ class PriorityMessageException : Exception Variant message; } - /** * Thrown on mailbox crowding if the mailbox is configured with * $(D OnCrowding.throwException). */ class MailboxFull : Exception { - this( Tid t, string msg = "Mailbox full" ) + /// + this(Tid t, string msg = "Mailbox full") @safe pure nothrow @nogc { - super( msg ); + super(msg); tid = t; } Tid tid; } - /** * Thrown when a Tid is missing, e.g. when $(D ownerTid) doesn't * find an owner thread. */ class TidMissingException : Exception { - this(string msg, string file = __FILE__, size_t line = __LINE__) - { - super(msg, file, line); - } + import std.exception : basicExceptionCtors; + /// + mixin basicExceptionCtors; } -////////////////////////////////////////////////////////////////////////////// // Thread ID -////////////////////////////////////////////////////////////////////////////// /** @@ -317,13 +302,12 @@ class TidMissingException : Exception struct Tid { private: - this( MessageBox m ) @safe + this(MessageBox m) @safe pure nothrow @nogc { mbox = m; } - - MessageBox mbox; + MessageBox mbox; public: @@ -336,26 +320,37 @@ public: */ void toString(scope void delegate(const(char)[]) sink) { - import std.format; - formattedWrite(sink, "Tid(%x)", &mbox); + import std.format : formattedWrite; + formattedWrite(sink, "Tid(%x)", cast(void*) mbox); } } +@system unittest +{ + // text!Tid is @system + import std.conv : text; + Tid tid; + assert(text(tid) == "Tid(0)"); + auto tid2 = thisTid; + assert(text(tid2) != "Tid(0)"); + auto tid3 = tid2; + assert(text(tid2) == text(tid3)); +} /** - * Returns the caller's Tid. + * Returns: The $(LREF Tid) of the caller's thread. */ @property Tid thisTid() @safe { // TODO: remove when concurrency is safe - auto trus = delegate() @trusted + static auto trus() @trusted { - if( thisInfo.ident != Tid.init ) + if (thisInfo.ident != Tid.init) return thisInfo.ident; - thisInfo.ident = Tid( new MessageBox ); + thisInfo.ident = Tid(new MessageBox); return thisInfo.ident; - }; + } return trus(); } @@ -368,13 +363,16 @@ public: */ @property Tid ownerTid() { - enforce!TidMissingException(thisInfo.owner.mbox !is null, - "Error: Thread has no owner thread."); + import std.exception : enforce; + + enforce!TidMissingException(thisInfo.owner.mbox !is null, "Error: Thread has no owner thread."); return thisInfo.owner; } -unittest +@system unittest { + import std.exception : assertThrown; + static void fun() { string res = receiveOnly!string(); @@ -389,30 +387,28 @@ unittest assert(res == "Child responding"); } -////////////////////////////////////////////////////////////////////////////// // Thread Creation -////////////////////////////////////////////////////////////////////////////// private template isSpawnable(F, T...) { - template isParamsImplicitlyConvertible(F1, F2, int i=0) + template isParamsImplicitlyConvertible(F1, F2, int i = 0) { - alias param1 = ParameterTypeTuple!F1; - alias param2 = ParameterTypeTuple!F2; + alias param1 = Parameters!F1; + alias param2 = Parameters!F2; static if (param1.length != param2.length) enum isParamsImplicitlyConvertible = false; else static if (param1.length == i) enum isParamsImplicitlyConvertible = true; else static if (isImplicitlyConvertible!(param2[i], param1[i])) - enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, F2, i+1); + enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, + F2, i + 1); else enum isParamsImplicitlyConvertible = false; } - enum isSpawnable = isCallable!F - && is(ReturnType!F == void) - && isParamsImplicitlyConvertible!(F, void function(T)) - && ( isFunctionPointer!F - || !hasUnsharedAliasing!F); + + enum isSpawnable = isCallable!F && is(ReturnType!F == void) + && isParamsImplicitlyConvertible!(F, void function(T)) + && (isFunctionPointer!F || !hasUnsharedAliasing!F); } /** @@ -460,18 +456,18 @@ private template isSpawnable(F, T...) * * // Fails: char[] has mutable aliasing. * auto tid2 = spawn(&f2, str.dup); + * + * // New thread with anonymous function + * spawn({ writeln("This is so great!"); }); * } * --- */ -Tid spawn(F, T...)( F fn, T args ) - if ( isSpawnable!(F, T) ) +Tid spawn(F, T...)(F fn, T args) if (isSpawnable!(F, T)) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - return _spawn( false, fn, args ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + return _spawn(false, fn, args); } - /** * Starts fn(args) in a logical thread and will receive a LinkTerminated * message when the operation terminates. @@ -491,77 +487,73 @@ Tid spawn(F, T...)( F fn, T args ) * Returns: * A Tid representing the new thread. */ -Tid spawnLinked(F, T...)( F fn, T args ) - if ( isSpawnable!(F, T) ) +Tid spawnLinked(F, T...)(F fn, T args) if (isSpawnable!(F, T)) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - return _spawn( true, fn, args ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + return _spawn(true, fn, args); } - /* * */ -private Tid _spawn(F, T...)( bool linked, F fn, T args ) - if ( isSpawnable!(F, T) ) +private Tid _spawn(F, T...)(bool linked, F fn, T args) if (isSpawnable!(F, T)) { // TODO: MessageList and &exec should be shared. - auto spawnTid = Tid( new MessageBox ); + auto spawnTid = Tid(new MessageBox); auto ownerTid = thisTid; void exec() { thisInfo.ident = spawnTid; thisInfo.owner = ownerTid; - fn( args ); + fn(args); } // TODO: MessageList and &exec should be shared. - if( scheduler !is null ) - scheduler.spawn( &exec ); + if (scheduler !is null) + scheduler.spawn(&exec); else { - auto t = new Thread( &exec ); + auto t = new Thread(&exec); t.start(); } thisInfo.links[spawnTid] = linked; return spawnTid; } -unittest +@system unittest { - void function() fn1; - void function(int) fn2; - static assert( __traits(compiles, spawn(fn1))); - static assert( __traits(compiles, spawn(fn2, 2))); + void function() fn1; + void function(int) fn2; + static assert(__traits(compiles, spawn(fn1))); + static assert(__traits(compiles, spawn(fn2, 2))); static assert(!__traits(compiles, spawn(fn1, 1))); static assert(!__traits(compiles, spawn(fn2))); - void delegate(int) shared dg1; - shared(void delegate(int)) dg2; - shared(void delegate(long) shared) dg3; - shared(void delegate(real, int , long) shared) dg4; - void delegate(int) immutable dg5; - void delegate(int) dg6; - static assert( __traits(compiles, spawn(dg1, 1))); - static assert( __traits(compiles, spawn(dg2, 2))); - static assert( __traits(compiles, spawn(dg3, 3))); - static assert( __traits(compiles, spawn(dg4, 4, 4, 4))); - static assert( __traits(compiles, spawn(dg5, 5))); + void delegate(int) shared dg1; + shared(void delegate(int)) dg2; + shared(void delegate(long) shared) dg3; + shared(void delegate(real, int, long) shared) dg4; + void delegate(int) immutable dg5; + void delegate(int) dg6; + static assert(__traits(compiles, spawn(dg1, 1))); + static assert(__traits(compiles, spawn(dg2, 2))); + static assert(__traits(compiles, spawn(dg3, 3))); + static assert(__traits(compiles, spawn(dg4, 4, 4, 4))); + static assert(__traits(compiles, spawn(dg5, 5))); static assert(!__traits(compiles, spawn(dg6, 6))); auto callable1 = new class{ void opCall(int) shared {} }; - auto callable2 = cast(shared)new class{ void opCall(int) shared {} }; + auto callable2 = cast(shared) new class{ void opCall(int) shared {} }; auto callable3 = new class{ void opCall(int) immutable {} }; - auto callable4 = cast(immutable)new class{ void opCall(int) immutable {} }; + auto callable4 = cast(immutable) new class{ void opCall(int) immutable {} }; auto callable5 = new class{ void opCall(int) {} }; - auto callable6 = cast(shared)new class{ void opCall(int) immutable {} }; - auto callable7 = cast(immutable)new class{ void opCall(int) shared {} }; - auto callable8 = cast(shared)new class{ void opCall(int) const shared {} }; - auto callable9 = cast(const shared)new class{ void opCall(int) shared {} }; - auto callable10 = cast(const shared)new class{ void opCall(int) const shared {} }; - auto callable11 = cast(immutable)new class{ void opCall(int) const shared {} }; + auto callable6 = cast(shared) new class{ void opCall(int) immutable {} }; + auto callable7 = cast(immutable) new class{ void opCall(int) shared {} }; + auto callable8 = cast(shared) new class{ void opCall(int) const shared {} }; + auto callable9 = cast(const shared) new class{ void opCall(int) shared {} }; + auto callable10 = cast(const shared) new class{ void opCall(int) const shared {} }; + auto callable11 = cast(immutable) new class{ void opCall(int) const shared {} }; static assert(!__traits(compiles, spawn(callable1, 1))); static assert( __traits(compiles, spawn(callable2, 2))); static assert(!__traits(compiles, spawn(callable3, 3))); @@ -575,26 +567,18 @@ unittest static assert( __traits(compiles, spawn(callable11, 11))); } - -////////////////////////////////////////////////////////////////////////////// -// Sending and Receiving Messages -////////////////////////////////////////////////////////////////////////////// - - /** * Places the values as a message at the back of tid's message queue. * * Sends the supplied value to the thread represented by tid. As with - * $(XREF concurrency, spawn), $(D T) must not have unshared aliasing. + * $(REF spawn, std,concurrency), $(D T) must not have unshared aliasing. */ -void send(T...)( Tid tid, T vals ) +void send(T...)(Tid tid, T vals) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - _send( tid, vals ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + _send(tid, vals); } - /** * Places the values as a message on the front of tid's message queue. * @@ -602,34 +586,30 @@ void send(T...)( Tid tid, T vals ) * queue instead of at the back. This function is typically used for * out-of-band communication, to signal exceptional conditions, etc. */ -void prioritySend(T...)( Tid tid, T vals ) +void prioritySend(T...)(Tid tid, T vals) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - _send( MsgType.priority, tid, vals ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + _send(MsgType.priority, tid, vals); } - /* * ditto */ -private void _send(T...)( Tid tid, T vals ) +private void _send(T...)(Tid tid, T vals) { - _send( MsgType.standard, tid, vals ); + _send(MsgType.standard, tid, vals); } - /* * Implementation of send. This allows parameter checking to be different for * both Tid.send() and .send(). */ -private void _send(T...)( MsgType type, Tid tid, T vals ) +private void _send(T...)(MsgType type, Tid tid, T vals) { - auto msg = Message( type, vals ); - tid.mbox.put( msg ); + auto msg = Message(type, vals); + tid.mbox.put(msg); } - /** * Receives a message from another thread. * @@ -637,10 +617,10 @@ private void _send(T...)( MsgType type, Tid tid, T vals ) * specified types are available. This function works by pattern matching * a message against a set of delegates and executing the first match found. * - * If a delegate that accepts a $(XREF variant, Variant) is included as + * If a delegate that accepts a $(REF Variant, std,variant) is included as * the last argument to $(D receive), it will match any message that was not * matched by an earlier delegate. If more than one argument is sent, - * the $(D Variant) will contain a $(XREF typecons, Tuple) of all values + * the $(D Variant) will contain a $(REF Tuple, std,typecons) of all values * sent. * * Example: @@ -670,7 +650,7 @@ in { assert(thisInfo.ident.mbox !is null, "Cannot receive a message until a thread was spawned " - "or thisTid was passed to a running thread."); + ~ "or thisTid was passed to a running thread."); } body { @@ -680,20 +660,20 @@ body } -unittest +@safe unittest { - assert( __traits( compiles, + static assert( __traits( compiles, { receive( (Variant x) {} ); receive( (int x) {}, (Variant x) {} ); } ) ); - assert( !__traits( compiles, + static assert( !__traits( compiles, { receive( (Variant x) {}, (int x) {} ); } ) ); - assert( !__traits( compiles, + static assert( !__traits( compiles, { receive( (int x) {}, (int x) {} ); } ) ); @@ -704,9 +684,9 @@ version (unittest) { private void receiveFunction(int x) {} } -unittest +@safe unittest { - assert( __traits( compiles, + static assert( __traits( compiles, { receive( &receiveFunction ); receive( &receiveFunction, (Variant x) {} ); @@ -716,10 +696,15 @@ unittest private template receiveOnlyRet(T...) { - static if( T.length == 1 ) + static if ( T.length == 1 ) + { alias receiveOnlyRet = T[0]; + } else + { + import std.typecons : Tuple; alias receiveOnlyRet = Tuple!(T); + } } /** @@ -729,7 +714,7 @@ private template receiveOnlyRet(T...) * is received. * * Returns: The received message. If $(D T.length) is greater than one, - * the message will be packed into a $(XREF typecons, Tuple). + * the message will be packed into a $(REF Tuple, std,typecons). * * Example: * --- @@ -753,45 +738,37 @@ receiveOnlyRet!(T) receiveOnly(T...)() in { assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned " - "or thisTid was passed to a running thread."); + "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread."); } body { + import std.format : format; + import std.typecons : Tuple; + Tuple!(T) ret; - thisInfo.ident.mbox.get( - ( T val ) - { - static if( T.length ) - ret.field = val; - }, - ( LinkTerminated e ) - { - throw e; - }, - ( OwnerTerminated e ) - { - throw e; - }, - ( Variant val ) - { - static if (T.length > 1) - string exp = T.stringof; - else - string exp = T[0].stringof; + thisInfo.ident.mbox.get((T val) { + static if (T.length) + ret.field = val; + }, + (LinkTerminated e) { throw e; }, + (OwnerTerminated e) { throw e; }, + (Variant val) { + static if (T.length > 1) + string exp = T.stringof; + else + string exp = T[0].stringof; - throw new MessageMismatch( - format("Unexpected message type: expected '%s', got '%s'", - exp, val.type.toString())); - } ); - static if( T.length == 1 ) + throw new MessageMismatch( + format("Unexpected message type: expected '%s', got '%s'", exp, val.type.toString())); + }); + static if (T.length == 1) return ret[0]; else return ret; } -unittest +@system unittest { static void t1(Tid mainTid) { @@ -814,88 +791,76 @@ unittest /** * Tries to receive but will give up if no matches arrive within duration. + * Won't wait at all if provided $(REF Duration, core,time) is negative. * * Same as $(D receive) except that rather than wait forever for a message, * it waits until either it receives a message or the given - * $(CXREF time, Duration) has passed. It returns $(D true) if it received a + * $(REF Duration, core,time) has passed. It returns $(D true) if it received a * message and $(D false) if it timed out waiting for one. */ -bool receiveTimeout(T...)( Duration duration, T ops ) +bool receiveTimeout(T...)(Duration duration, T ops) in { assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned " - "or thisTid was passed to a running thread."); + "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread."); } body { - checkops( ops ); + checkops(ops); - return thisInfo.ident.mbox.get( duration, ops ); + return thisInfo.ident.mbox.get(duration, ops); } -unittest +@safe unittest { - assert( __traits( compiles, - { - receiveTimeout( msecs(0), (Variant x) {} ); - receiveTimeout( msecs(0), (int x) {}, (Variant x) {} ); - } ) ); - - assert( !__traits( compiles, - { - receiveTimeout( msecs(0), (Variant x) {}, (int x) {} ); - } ) ); - - assert( !__traits( compiles, - { - receiveTimeout( msecs(0), (int x) {}, (int x) {} ); - } ) ); - - assert( __traits( compiles, - { - receiveTimeout( msecs(10), (int x) {}, (Variant x) {} ); - } ) ); + static assert(__traits(compiles, { + receiveTimeout(msecs(0), (Variant x) {}); + receiveTimeout(msecs(0), (int x) {}, (Variant x) {}); + })); + + static assert(!__traits(compiles, { + receiveTimeout(msecs(0), (Variant x) {}, (int x) {}); + })); + + static assert(!__traits(compiles, { + receiveTimeout(msecs(0), (int x) {}, (int x) {}); + })); + + static assert(__traits(compiles, { + receiveTimeout(msecs(10), (int x) {}, (Variant x) {}); + })); } - -////////////////////////////////////////////////////////////////////////////// // MessageBox Limits -////////////////////////////////////////////////////////////////////////////// - /** * These behaviors may be specified when a mailbox is full. */ enum OnCrowding { - block, /// Wait until room is available. + block, /// Wait until room is available. throwException, /// Throw a MailboxFull exception. - ignore /// Abort the send and return. + ignore /// Abort the send and return. } - private { - bool onCrowdingBlock( Tid tid ) + bool onCrowdingBlock(Tid tid) @safe pure nothrow @nogc { return true; } - - bool onCrowdingThrow( Tid tid ) + bool onCrowdingThrow(Tid tid) @safe pure { - throw new MailboxFull( tid ); + throw new MailboxFull(tid); } - - bool onCrowdingIgnore( Tid tid ) + bool onCrowdingIgnore(Tid tid) @safe pure nothrow @nogc { return false; } } - /** * Sets a maximum mailbox size. * @@ -910,20 +875,19 @@ private * doThis = The behavior executed when a message is sent to a full * mailbox. */ -void setMaxMailboxSize( Tid tid, size_t messages, OnCrowding doThis ) +void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure { - final switch( doThis ) + final switch (doThis) { case OnCrowding.block: - return tid.mbox.setMaxMsgs( messages, &onCrowdingBlock ); + return tid.mbox.setMaxMsgs(messages, &onCrowdingBlock); case OnCrowding.throwException: - return tid.mbox.setMaxMsgs( messages, &onCrowdingThrow ); + return tid.mbox.setMaxMsgs(messages, &onCrowdingThrow); case OnCrowding.ignore: - return tid.mbox.setMaxMsgs( messages, &onCrowdingIgnore ); + return tid.mbox.setMaxMsgs(messages, &onCrowdingIgnore); } } - /** * Sets a maximum mailbox size. * @@ -937,47 +901,40 @@ void setMaxMailboxSize( Tid tid, size_t messages, OnCrowding doThis ) * onCrowdingDoThis = The routine called when a message is sent to a full * mailbox. */ -void setMaxMailboxSize( Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis ) +void setMaxMailboxSize(Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis) { - tid.mbox.setMaxMsgs( messages, onCrowdingDoThis ); + tid.mbox.setMaxMsgs(messages, onCrowdingDoThis); } - -////////////////////////////////////////////////////////////////////////////// -// Name Registration -////////////////////////////////////////////////////////////////////////////// - - private { - __gshared Tid[string] tidByName; + __gshared Tid[string] tidByName; __gshared string[][Tid] namesByTid; - __gshared Mutex registryLock; + __gshared Mutex registryLock; } - -shared static this() +extern (C) void std_concurrency_static_this() { registryLock = new Mutex; } - private void unregisterMe() { - auto me = thisTid; - - synchronized( registryLock ) + auto me = thisInfo.ident; + if (thisInfo.ident != Tid.init) { - if( auto allNames = me in namesByTid ) + synchronized (registryLock) { - foreach( name; *allNames ) - tidByName.remove( name ); - namesByTid.remove( me ); + if (auto allNames = me in namesByTid) + { + foreach (name; *allNames) + tidByName.remove(name); + namesByTid.remove(me); + } } } } - /** * Associates name with tid. * @@ -993,13 +950,13 @@ private void unregisterMe() * true if the name is available and tid is not known to represent a * defunct thread. */ -bool register( string name, Tid tid ) +bool register(string name, Tid tid) { - synchronized( registryLock ) + synchronized (registryLock) { - if( name in tidByName ) + if (name in tidByName) return false; - if( tid.mbox.isClosed ) + if (tid.mbox.isClosed) return false; namesByTid[tid] ~= name; tidByName[name] = tid; @@ -1007,7 +964,6 @@ bool register( string name, Tid tid ) } } - /** * Removes the registered name associated with a tid. * @@ -1017,23 +973,25 @@ bool register( string name, Tid tid ) * Returns: * true if the name is registered, false if not. */ -bool unregister( string name ) +bool unregister(string name) { - synchronized( registryLock ) + import std.algorithm.searching : countUntil; + import std.algorithm.mutation : remove, SwapStrategy; + + synchronized (registryLock) { - if( auto tid = name in tidByName ) + if (auto tid = name in tidByName) { auto allNames = *tid in namesByTid; - auto pos = countUntil( *allNames, name ); - remove!(SwapStrategy.unstable)( *allNames, pos ); - tidByName.remove( name ); + auto pos = countUntil(*allNames, name); + remove!(SwapStrategy.unstable)(*allNames, pos); + tidByName.remove(name); return true; } return false; } } - /** * Gets the Tid associated with name. * @@ -1043,34 +1001,28 @@ bool unregister( string name ) * Returns: * The associated Tid or Tid.init if name is not registered. */ -Tid locate( string name ) +Tid locate(string name) { - synchronized( registryLock ) + synchronized (registryLock) { - if( auto tid = name in tidByName ) + if (auto tid = name in tidByName) return *tid; return Tid.init; } } - -////////////////////////////////////////////////////////////////////////////// -// Scheduler -////////////////////////////////////////////////////////////////////////////// - - /** * Encapsulates all implementation-level data needed for scheduling. * - * When definining a Scheduler, an instance of this struct must be associated + * When defining a Scheduler, an instance of this struct must be associated * with each logical thread. It contains all implementation-level information * needed by the internal API. */ struct ThreadInfo { - Tid ident; + Tid ident; bool[Tid] links; - Tid owner; + Tid owner; /** * Gets a thread-local instance of ThreadInfo. @@ -1085,7 +1037,6 @@ struct ThreadInfo return val; } - /** * Cleans up this ThreadInfo. * @@ -1095,17 +1046,16 @@ struct ThreadInfo */ void cleanup() { - if( ident.mbox !is null ) + if (ident.mbox !is null) ident.mbox.close(); - foreach( tid; links.keys ) - _send( MsgType.linkDead, tid, ident ); - if( owner != Tid.init ) - _send( MsgType.linkDead, owner, ident ); + foreach (tid; links.keys) + _send(MsgType.linkDead, tid, ident); + if (owner != Tid.init) + _send(MsgType.linkDead, owner, ident); unregisterMe(); // clean up registry entries } } - /** * A Scheduler controls how threading is performed by spawn. * @@ -1154,7 +1104,7 @@ interface Scheduler * absence of a custom scheduler. It will be automatically executed * via a call to spawn by the Scheduler. */ - void start( void delegate() op ); + void start(void delegate() op); /** * Assigns a logical thread to execute the supplied op. @@ -1169,7 +1119,7 @@ interface Scheduler * op = The function to execute. This may be the actual function passed * by the user to spawn itself, or may be a wrapper function. */ - void spawn( void delegate() op ); + void spawn(void delegate() op); /** * Yields execution to another logical thread. @@ -1192,7 +1142,7 @@ interface Scheduler @property ref ThreadInfo thisInfo() nothrow; /** - * Creates a Condition varialbe analog for signaling. + * Creates a Condition variable analog for signaling. * * Creates a new Condition variable analog which is used to check for and * to signal the addition of messages to a thread's message queue. Like @@ -1206,10 +1156,9 @@ interface Scheduler * cases a Scheduler may need to hold this reference and unlock the * mutex before yielding execution to another logical thread. */ - Condition newCondition( Mutex m ) nothrow; + Condition newCondition(Mutex m) nothrow; } - /** * An example Scheduler using kernel threads. * @@ -1218,29 +1167,26 @@ interface Scheduler * and may be instantiated and used, but is not a necessary part of the * default functioning of this module. */ -class ThreadScheduler : - Scheduler +class ThreadScheduler : Scheduler { /** * This simply runs op directly, since no real scheduling is needed by * this approach. */ - void start( void delegate() op ) + void start(void delegate() op) { op(); } - /** * Creates a new kernel thread and assigns it to run the supplied op. */ - void spawn( void delegate() op ) + void spawn(void delegate() op) { - auto t = new Thread( op ); + auto t = new Thread(op); t.start(); } - /** * This scheduler does no explicit multiplexing, so this is a no-op. */ @@ -1249,7 +1195,6 @@ class ThreadScheduler : // no explicit yield needed } - /** * Returns ThreadInfo.thisInfo, since it is a thread-local instance of * ThreadInfo, which is the correct behavior for this scheduler. @@ -1259,48 +1204,43 @@ class ThreadScheduler : return ThreadInfo.thisInfo; } - /** * Creates a new Condition variable. No custom behavior is needed here. */ - Condition newCondition( Mutex m ) nothrow + Condition newCondition(Mutex m) nothrow { - return new Condition( m ); + return new Condition(m); } } - /** * An example Scheduler using Fibers. * * This is an example scheduler that creates a new Fiber per call to spawn * and multiplexes the execution of all fibers within the main thread. */ -class FiberScheduler : - Scheduler +class FiberScheduler : Scheduler { /** * This creates a new Fiber for the supplied op and then starts the * dispatcher. */ - void start( void delegate() op ) + void start(void delegate() op) { - create( op ); + create(op); dispatch(); } - /** * This created a new Fiber for the supplied op and adds it to the * dispatch list. */ - void spawn( void delegate() op ) nothrow + void spawn(void delegate() op) nothrow { - create( op ); + create(op); yield(); } - /** * If the caller is a scheduled Fiber, this yields execution to another * scheduled Fiber. @@ -1310,11 +1250,10 @@ class FiberScheduler : // NOTE: It's possible that we should test whether the calling Fiber // is an InfoFiber before yielding, but I think it's reasonable // that any (non-Generator) fiber should yield here. - if(Fiber.getThis()) + if (Fiber.getThis()) Fiber.yield(); } - /** * Returns an appropriate ThreadInfo instance. * @@ -1326,38 +1265,33 @@ class FiberScheduler : { auto f = cast(InfoFiber) Fiber.getThis(); - if( f !is null ) + if (f !is null) return f.info; return ThreadInfo.thisInfo; } - /** * Returns a Condition analog that yields when wait or notify is called. */ - Condition newCondition( Mutex m ) nothrow + Condition newCondition(Mutex m) nothrow { - return new FiberCondition( m ); + return new FiberCondition(m); } - private: - static class InfoFiber : - Fiber + static class InfoFiber : Fiber { ThreadInfo info; - this( void delegate() op ) nothrow + this(void delegate() op) nothrow { - super( op ); + super(op); } } - - class FiberCondition : - Condition + class FiberCondition : Condition { - this( Mutex m ) nothrow + this(Mutex m) nothrow { super(m); notified = false; @@ -1365,19 +1299,21 @@ private: override void wait() nothrow { - scope(exit) notified = false; + scope (exit) notified = false; - while( !notified ) + while (!notified) switchContext(); } - override bool wait( Duration period ) nothrow + override bool wait(Duration period) nothrow { - scope(exit) notified = false; + import core.time : MonoTime; - for( auto limit = Clock.currSystemTick + period; + scope (exit) notified = false; + + for (auto limit = MonoTime.currTime + period; !notified && !period.isNegative; - period = limit - Clock.currSystemTick ) + period = limit - MonoTime.currTime) { yield(); } @@ -1397,61 +1333,60 @@ private: } private: - final void switchContext() nothrow + void switchContext() nothrow { mutex_nothrow.unlock_nothrow(); - scope(exit) mutex_nothrow.lock_nothrow(); + scope (exit) mutex_nothrow.lock_nothrow(); yield(); } private bool notified; } - private: - final void dispatch() + void dispatch() { - import std.algorithm : remove; + import std.algorithm.mutation : remove; - while( m_fibers.length > 0 ) + while (m_fibers.length > 0) { - auto t = m_fibers[m_pos].call( Fiber.Rethrow.no ); + auto t = m_fibers[m_pos].call(Fiber.Rethrow.no); if (t !is null && !(cast(OwnerTerminated) t)) + { throw t; - if( m_fibers[m_pos].state == Fiber.State.TERM ) + } + if (m_fibers[m_pos].state == Fiber.State.TERM) { - if( m_pos >= (m_fibers = remove( m_fibers, m_pos )).length ) + if (m_pos >= (m_fibers = remove(m_fibers, m_pos)).length) m_pos = 0; } - else if( m_pos++ >= m_fibers.length - 1 ) + else if (m_pos++ >= m_fibers.length - 1) { m_pos = 0; } } } - - final void create( void delegate() op ) nothrow + void create(void delegate() op) nothrow { void wrap() { - scope(exit) + scope (exit) { thisInfo.cleanup(); } op(); } - m_fibers ~= new InfoFiber( &wrap ); - } + m_fibers ~= new InfoFiber(&wrap); + } private: Fiber[] m_fibers; - size_t m_pos; + size_t m_pos; } - -unittest +@system unittest { static void receive(Condition cond, ref size_t received) { @@ -1482,7 +1417,7 @@ unittest auto cond = fs.newCondition(mtx); size_t received, sent; - auto waiter = new Fiber({receive(cond, received);}), notifier = new Fiber({send(cond, sent);}); + auto waiter = new Fiber({ receive(cond, received); }), notifier = new Fiber({ send(cond, sent); }); waiter.call(); assert(received == 0); notifier.call(); @@ -1494,7 +1429,6 @@ unittest assert(received == 1); } - /** * Sets the Scheduler behavior within the program. * @@ -1504,11 +1438,7 @@ unittest */ __gshared Scheduler scheduler; - -////////////////////////////////////////////////////////////////////////////// // Generator -////////////////////////////////////////////////////////////////////////////// - /** * If the caller is a Fiber and is not a Generator, this function will call @@ -1524,11 +1454,11 @@ void yield() nothrow if (fiber) return Fiber.yield(); } - else scheduler.yield(); + else + scheduler.yield(); } } - /// Used to determine whether a Generator is running. private interface IsGenerator {} @@ -1566,8 +1496,7 @@ private interface IsGenerator {} * } * --- */ -class Generator(T) : - Fiber, IsGenerator +class Generator(T) : Fiber, IsGenerator { /** * Initializes a generator object which is associated with a static @@ -1586,7 +1515,6 @@ class Generator(T) : call(); } - /** * Initializes a generator object which is associated with a static * D function. The function will be called once to prepare the range @@ -1605,7 +1533,6 @@ class Generator(T) : call(); } - /** * Initializes a generator object which is associated with a dynamic * D function. The function will be called once to prepare the range @@ -1623,7 +1550,6 @@ class Generator(T) : call(); } - /** * Initializes a generator object which is associated with a dynamic * D function. The function will be called once to prepare the range @@ -1642,7 +1568,6 @@ class Generator(T) : call(); } - /** * Returns true if the generator is empty. */ @@ -1651,7 +1576,6 @@ class Generator(T) : return m_value is null || state == State.TERM; } - /** * Obtains the next value from the underlying function. */ @@ -1660,7 +1584,6 @@ class Generator(T) : call(); } - /** * Returns the most recently generated value. */ @@ -1669,12 +1592,10 @@ class Generator(T) : return *m_value; } - private: - T* m_value; + T* m_value; } - /** * Yields a value of type T to the caller of the currently executing * generator. @@ -1690,41 +1611,32 @@ void yield(T)(ref T value) cur.m_value = &value; return Fiber.yield(); } - throw new Exception("yield(T) called with no active generator for the supplied type", - __FILE__, __LINE__); + throw new Exception("yield(T) called with no active generator for the supplied type"); } - /// ditto void yield(T)(T value) { yield(value); } - -version (Win64) { - // fibers are broken on Win64 -} else version (Win32) { - // fibers are broken in Win32 under server 2012: bug 13821 -} else unittest { +@system unittest +{ import core.exception; import std.exception; static void testScheduler(Scheduler s) { scheduler = s; - scheduler.start( - { - auto tid = spawn( - { + scheduler.start({ + auto tid = spawn({ int i; try { for (i = 1; i < 10; i++) { - assertNotThrown!AssertError( - assert(receiveOnly!int() == i)); + assertNotThrown!AssertError(assert(receiveOnly!int() == i)); } } catch (OwnerTerminated e) @@ -1736,8 +1648,7 @@ version (Win64) { assert(i == 4); }); - auto r = new Generator!int( - { + auto r = new Generator!int({ assertThrown!Exception(yield(2.0)); yield(); // ensure this is a no-op yield(1); @@ -1758,12 +1669,6 @@ version (Win64) { testScheduler(new FiberScheduler); } - -////////////////////////////////////////////////////////////////////////////// -// MessageBox Implementation -////////////////////////////////////////////////////////////////////////////// - - private { /* @@ -1775,37 +1680,27 @@ private */ class MessageBox { - this() @trusted /* TODO: make @safe after relevant druntime PR gets merged */ + this() @trusted nothrow /* TODO: make @safe after relevant druntime PR gets merged */ { - m_lock = new Mutex; - m_closed = false; + m_lock = new Mutex; + m_closed = false; - if( scheduler is null ) + if (scheduler is null) { - m_putMsg = new Condition( m_lock ); - m_notFull = new Condition( m_lock ); + m_putMsg = new Condition(m_lock); + m_notFull = new Condition(m_lock); } else { - m_putMsg = scheduler.newCondition( m_lock ); - m_notFull = scheduler.newCondition( m_lock ); + m_putMsg = scheduler.newCondition(m_lock); + m_notFull = scheduler.newCondition(m_lock); } } /// - deprecated("isClosed can't be used with a const MessageBox") - final @property bool isClosed() const + final @property bool isClosed() @safe @nogc pure { - synchronized( m_lock ) - { - return m_closed; - } - } - - /// - final @property bool isClosed() - { - synchronized( m_lock ) + synchronized (m_lock) { return m_closed; } @@ -1822,16 +1717,15 @@ private * unbounded. * call = The routine to call when the queue is full. */ - final void setMaxMsgs( size_t num, bool function(Tid) call ) + final void setMaxMsgs(size_t num, bool function(Tid) call) @safe @nogc pure { - synchronized( m_lock ) + synchronized (m_lock) { - m_maxMsgs = num; + m_maxMsgs = num; m_onMaxMsgs = call; } } - /* * If maxMsgs is not set, the message is added to the queue and the * owner is notified. If the queue is full, the message will still be @@ -1846,29 +1740,29 @@ private * Throws: * An exception if the queue is full and onCrowdingDoThis throws. */ - final void put( ref Message msg ) + final void put(ref Message msg) { - synchronized( m_lock ) + synchronized (m_lock) { // TODO: Generate an error here if m_closed is true, or maybe // put a message in the caller's queue? - if( !m_closed ) + if (!m_closed) { - while( true ) + while (true) { - if( isPriorityMsg( msg ) ) + if (isPriorityMsg(msg)) { - m_sharedPty.put( msg ); + m_sharedPty.put(msg); m_putMsg.notify(); return; } - if( !mboxFull() || isControlMsg( msg ) ) + if (!mboxFull() || isControlMsg(msg)) { - m_sharedBox.put( msg ); + m_sharedBox.put(msg); m_putMsg.notify(); return; } - if( m_onMaxMsgs !is null && !m_onMaxMsgs( thisTid ) ) + if (m_onMaxMsgs !is null && !m_onMaxMsgs(thisTid)) { return; } @@ -1880,7 +1774,6 @@ private } } - /* * Matches ops against each message in turn until a match is found. * @@ -1897,41 +1790,42 @@ private * if the owner thread terminates and no existing messages match the * supplied ops. */ - final bool get(T...)( scope T vals ) + bool get(T...)(scope T vals) { - static assert( T.length ); + import std.meta : AliasSeq; - static if( isImplicitlyConvertible!(T[0], Duration) ) + static assert(T.length); + + static if (isImplicitlyConvertible!(T[0], Duration)) { - alias Ops = TypeTuple!(T[1 .. $]); + alias Ops = AliasSeq!(T[1 .. $]); alias ops = vals[1 .. $]; - assert( vals[0] >= msecs(0) ); enum timedWait = true; Duration period = vals[0]; } else { - alias Ops = TypeTuple!(T); + alias Ops = AliasSeq!(T); alias ops = vals[0 .. $]; enum timedWait = false; } - bool onStandardMsg( ref Message msg ) + bool onStandardMsg(ref Message msg) { - foreach( i, t; Ops ) + foreach (i, t; Ops) { - alias Args = ParameterTypeTuple!(t); - auto op = ops[i]; + alias Args = Parameters!(t); + auto op = ops[i]; - if( msg.convertsTo!(Args) ) + if (msg.convertsTo!(Args)) { - static if( is( ReturnType!(t) == bool ) ) + static if (is(ReturnType!(t) == bool)) { - return msg.map( op ); + return msg.map(op); } else { - msg.map( op ); + msg.map(op); return true; } } @@ -1939,59 +1833,60 @@ private return false; } - bool onLinkDeadMsg( ref Message msg ) + bool onLinkDeadMsg(ref Message msg) { - assert( msg.convertsTo!(Tid) ); + assert(msg.convertsTo!(Tid)); auto tid = msg.get!(Tid); - if( bool* pDepends = (tid in thisInfo.links) ) + if (bool* pDepends = tid in thisInfo.links) { auto depends = *pDepends; - thisInfo.links.remove( tid ); + thisInfo.links.remove(tid); // Give the owner relationship precedence. - if( depends && tid != thisInfo.owner ) + if (depends && tid != thisInfo.owner) { - auto e = new LinkTerminated( tid ); - auto m = Message( MsgType.standard, e ); - if( onStandardMsg( m ) ) + auto e = new LinkTerminated(tid); + auto m = Message(MsgType.standard, e); + if (onStandardMsg(m)) return true; throw e; } } - if( tid == thisInfo.owner ) + if (tid == thisInfo.owner) { thisInfo.owner = Tid.init; - auto e = new OwnerTerminated( tid ); - auto m = Message( MsgType.standard, e ); - if( onStandardMsg( m ) ) + auto e = new OwnerTerminated(tid); + auto m = Message(MsgType.standard, e); + if (onStandardMsg(m)) return true; throw e; } return false; } - bool onControlMsg( ref Message msg ) + bool onControlMsg(ref Message msg) { - switch( msg.type ) + switch (msg.type) { case MsgType.linkDead: - return onLinkDeadMsg( msg ); + return onLinkDeadMsg(msg); default: return false; } } - bool scan( ref ListT list ) + bool scan(ref ListT list) { - for( auto range = list[]; !range.empty; ) + for (auto range = list[]; !range.empty;) { // Only the message handler will throw, so if this occurs // we can be certain that the message was handled. - scope(failure) list.removeAt( range ); + scope (failure) + list.removeAt(range); - if( isControlMsg( range.front ) ) + if (isControlMsg(range.front)) { - if( onControlMsg( range.front ) ) + if (onControlMsg(range.front)) { // Although the linkDead message is a control message, // it can be handled by the user. Since the linkDead @@ -1999,12 +1894,12 @@ private // it has been handled and we can return from receive. // This is a weird special case that will have to be // handled in a more general way if more are added. - if( !isLinkDeadMsg( range.front ) ) + if (!isLinkDeadMsg(range.front)) { - list.removeAt( range ); + list.removeAt(range); continue; } - list.removeAt( range ); + list.removeAt(range); return true; } range.popFront(); @@ -2012,9 +1907,9 @@ private } else { - if( onStandardMsg( range.front ) ) + if (onStandardMsg(range.front)) { - list.removeAt( range ); + list.removeAt(range); return true; } range.popFront(); @@ -2024,46 +1919,46 @@ private return false; } - - bool pty( ref ListT list ) + bool pty(ref ListT list) { - if( !list.empty ) + if (!list.empty) { auto range = list[]; - if( onStandardMsg( range.front ) ) + if (onStandardMsg(range.front)) { - list.removeAt( range ); + list.removeAt(range); return true; } - if( range.front.convertsTo!(Throwable) ) + if (range.front.convertsTo!(Throwable)) throw range.front.get!(Throwable); - else if( range.front.convertsTo!(shared(Throwable)) ) + else if (range.front.convertsTo!(shared(Throwable))) throw range.front.get!(shared(Throwable)); - else throw new PriorityMessageException( range.front.data ); + else + throw new PriorityMessageException(range.front.data); } return false; } - static if( timedWait ) + static if (timedWait) { - auto limit = Clock.currSystemTick + period; + import core.time : MonoTime; + auto limit = MonoTime.currTime + period; } - while( true ) + while (true) { ListT arrived; - if( pty( m_localPty ) || - scan( m_localBox ) ) + if (pty(m_localPty) || scan(m_localBox)) { return true; } yield(); - synchronized( m_lock ) + synchronized (m_lock) { updateMsgCount(); - while( m_sharedPty.empty && m_sharedBox.empty ) + while (m_sharedPty.empty && m_sharedBox.empty) { // NOTE: We're notifying all waiters here instead of just // a few because the onCrowding behavior may have @@ -2071,11 +1966,11 @@ private // unnecessarily if the new behavior is not to block. // This will admittedly result in spurious wakeups // in other situations, but what can you do? - if( m_putQueue && !mboxFull() ) + if (m_putQueue && !mboxFull()) m_notFull.notifyAll(); - static if( timedWait ) + static if (timedWait) { - if( period.isNegative || !m_putMsg.wait( period ) ) + if (period <= Duration.zero || !m_putMsg.wait(period)) return false; } else @@ -2083,30 +1978,31 @@ private m_putMsg.wait(); } } - m_localPty.put( m_sharedPty ); - arrived.put( m_sharedBox ); + m_localPty.put(m_sharedPty); + arrived.put(m_sharedBox); } - if( m_localPty.empty ) + if (m_localPty.empty) { - scope(exit) m_localBox.put( arrived ); - if( scan( arrived ) ) + scope (exit) m_localBox.put(arrived); + if (scan(arrived)) + { return true; + } else { - static if( timedWait ) + static if (timedWait) { - period = limit - Clock.currSystemTick; + period = limit - MonoTime.currTime; } continue; } } - m_localBox.put( arrived ); - pty( m_localPty ); + m_localBox.put(arrived); + pty(m_localPty); return true; } } - /* * Called on thread termination. This routine processes any remaining * control messages, clears out message queues, and sets a flag to @@ -2114,121 +2010,83 @@ private */ final void close() { - void onLinkDeadMsg( ref Message msg ) + static void onLinkDeadMsg(ref Message msg) { - assert( msg.convertsTo!(Tid) ); + assert(msg.convertsTo!(Tid)); auto tid = msg.get!(Tid); - thisInfo.links.remove( tid ); - if( tid == thisInfo.owner ) + thisInfo.links.remove(tid); + if (tid == thisInfo.owner) thisInfo.owner = Tid.init; } - void sweep( ref ListT list ) + static void sweep(ref ListT list) { - for( auto range = list[]; !range.empty; range.popFront() ) + for (auto range = list[]; !range.empty; range.popFront()) { - if( range.front.type == MsgType.linkDead ) - onLinkDeadMsg( range.front ); + if (range.front.type == MsgType.linkDead) + onLinkDeadMsg(range.front); } } ListT arrived; - sweep( m_localBox ); - synchronized( m_lock ) + sweep(m_localBox); + synchronized (m_lock) { - arrived.put( m_sharedBox ); + arrived.put(m_sharedBox); m_closed = true; } m_localBox.clear(); - sweep( arrived ); + sweep(arrived); } - private: - ////////////////////////////////////////////////////////////////////// - // Routines involving shared data, m_lock must be held. - ////////////////////////////////////////////////////////////////////// - + // Routines involving local data only, no lock needed. - bool mboxFull() + bool mboxFull() @safe @nogc pure nothrow { - return m_maxMsgs && - m_maxMsgs <= m_localMsgs + m_sharedBox.length; + return m_maxMsgs && m_maxMsgs <= m_localMsgs + m_sharedBox.length; } - - void updateMsgCount() + void updateMsgCount() @safe @nogc pure nothrow { m_localMsgs = m_localBox.length; } - - private: - ////////////////////////////////////////////////////////////////////// - // Routines involving local data only, no lock needed. - ////////////////////////////////////////////////////////////////////// - - - pure final bool isControlMsg( ref Message msg ) + bool isControlMsg(ref Message msg) @safe @nogc pure nothrow { - return msg.type != MsgType.standard && - msg.type != MsgType.priority; + return msg.type != MsgType.standard && msg.type != MsgType.priority; } - - pure final bool isPriorityMsg( ref Message msg ) + bool isPriorityMsg(ref Message msg) @safe @nogc pure nothrow { return msg.type == MsgType.priority; } - - pure final bool isLinkDeadMsg( ref Message msg ) + bool isLinkDeadMsg(ref Message msg) @safe @nogc pure nothrow { return msg.type == MsgType.linkDead; } - - private: - ////////////////////////////////////////////////////////////////////// - // Type declarations. - ////////////////////////////////////////////////////////////////////// - - alias OnMaxFn = bool function(Tid); - alias ListT = List!(Message); - - private: - ////////////////////////////////////////////////////////////////////// - // Local data, no lock needed. - ////////////////////////////////////////////////////////////////////// - + alias ListT = List!(Message); - ListT m_localBox; - ListT m_localPty; - - - private: - ////////////////////////////////////////////////////////////////////// - // Shared data, m_lock must be held on access. - ////////////////////////////////////////////////////////////////////// - - - Mutex m_lock; - Condition m_putMsg; - Condition m_notFull; - size_t m_putQueue; - ListT m_sharedBox; - ListT m_sharedPty; - OnMaxFn m_onMaxMsgs; - size_t m_localMsgs; - size_t m_maxMsgs; - bool m_closed; + ListT m_localBox; + ListT m_localPty; + Mutex m_lock; + Condition m_putMsg; + Condition m_notFull; + size_t m_putQueue; + ListT m_sharedBox; + ListT m_sharedPty; + OnMaxFn m_onMaxMsgs; + size_t m_localMsgs; + size_t m_maxMsgs; + bool m_closed; } - /* * */ @@ -2236,6 +2094,8 @@ private { struct Range { + import std.exception : enforce; + @property bool empty() const { return !m_prev.next; @@ -2243,29 +2103,23 @@ private @property ref T front() { - enforce( m_prev.next ); + enforce(m_prev.next, "invalid list node"); return m_prev.next.val; } - @property void front( T val ) + @property void front(T val) { - enforce( m_prev.next ); + enforce(m_prev.next, "invalid list node"); m_prev.next.val = val; } void popFront() { - enforce( m_prev.next ); + enforce(m_prev.next, "invalid list node"); m_prev = m_prev.next; } - //T moveFront() - //{ - // enforce( m_prev.next ); - // return move( m_prev.next.val ); - //} - - private this( Node* p ) + private this(Node* p) { m_prev = p; } @@ -2273,100 +2127,73 @@ private private Node* m_prev; } - - /* - * - */ - void put( T val ) + void put(T val) { - put( newNode( val ) ); + put(newNode(val)); } - - /* - * - */ - void put( ref List!(T) rhs ) + void put(ref List!(T) rhs) { - if( !rhs.empty ) + if (!rhs.empty) { - put( rhs.m_first ); - while( m_last.next !is null ) + put(rhs.m_first); + while (m_last.next !is null) { m_last = m_last.next; m_count++; } rhs.m_first = null; - rhs.m_last = null; + rhs.m_last = null; rhs.m_count = 0; } } - - /* - * - */ Range opSlice() { - return Range( cast(Node*) &m_first ); + return Range(cast(Node*)&m_first); } - - /* - * - */ - void removeAt( Range r ) + void removeAt(Range r) { - assert( m_count ); + import std.exception : enforce; + + assert(m_count); Node* n = r.m_prev; - enforce( n && n.next ); + enforce(n && n.next, "attempting to remove invalid list node"); - if( m_last is m_first ) + if (m_last is m_first) m_last = null; - else if( m_last is n.next ) + else if (m_last is n.next) m_last = n; Node* to_free = n.next; n.next = n.next.next; - freeNode( to_free ); + freeNode(to_free); m_count--; } - - /* - * - */ @property size_t length() { return m_count; } - - /* - * - */ void clear() { m_first = m_last = null; m_count = 0; } - - /* - * - */ @property bool empty() { return m_first is null; } - private: struct Node { - Node* next; - T val; + Node* next; + T val; - this( T v ) + this(T v) { val = v; } @@ -2378,26 +2205,32 @@ private void unlock() { atomicStore!(MemoryOrder.rel)(locked, false); } bool locked; } + static shared SpinLock sm_lock; static shared Node* sm_head; Node* newNode(T v) { - Node *n; + Node* n; { sm_lock.lock(); scope (exit) sm_lock.unlock(); if (sm_head) { - n = cast(Node*)sm_head; + n = cast(Node*) sm_head; sm_head = sm_head.next; } } if (n) - *n = Node(v); + { + import std.conv : emplace; + emplace!Node(n, v); + } else + { n = new Node(v); + } return n; } @@ -2409,19 +2242,15 @@ private sm_lock.lock(); scope (exit) sm_lock.unlock(); - auto sn = cast(shared(Node)*)n; + auto sn = cast(shared(Node)*) n; sn.next = sm_head; sm_head = sn; } - - /* - * - */ - void put( Node* n ) + void put(Node* n) { m_count++; - if( !empty ) + if (!empty) { m_last.next = n; m_last = n; @@ -2431,73 +2260,58 @@ private m_last = n; } - - Node* m_first; - Node* m_last; - size_t m_count; + Node* m_first; + Node* m_last; + size_t m_count; } } - -version( unittest ) +version (unittest) { import std.stdio; + import std.typecons : tuple, Tuple; - void testfn( Tid tid ) - { - receive( (float val) { assert(0); }, - (int val, int val2) - { - assert( val == 42 && val2 == 86 ); - } ); - receive( (Tuple!(int, int) val) - { - assert( val[0] == 42 && - val[1] == 86 ); - } ); - receive( (Variant val) {} ); - receive( (string val) - { - if( "the quick brown fox" != val ) - return false; - return true; - }, - (string val) - { - assert( false ); - } ); - prioritySend( tid, "done" ); - } - - void runTest( Tid tid ) - { - send( tid, 42, 86 ); - send( tid, tuple(42, 86) ); - send( tid, "hello", "there" ); - send( tid, "the quick brown fox" ); - receive( (string val) { assert(val == "done"); } ); + void testfn(Tid tid) + { + receive((float val) { assert(0); }, (int val, int val2) { + assert(val == 42 && val2 == 86); + }); + receive((Tuple!(int, int) val) { assert(val[0] == 42 && val[1] == 86); }); + receive((Variant val) { }); + receive((string val) { + if ("the quick brown fox" != val) + return false; + return true; + }, (string val) { assert(false); }); + prioritySend(tid, "done"); } + void runTest(Tid tid) + { + send(tid, 42, 86); + send(tid, tuple(42, 86)); + send(tid, "hello", "there"); + send(tid, "the quick brown fox"); + receive((string val) { assert(val == "done"); }); + } void simpleTest() { - auto tid = spawn( &testfn, thisTid ); - runTest( tid ); + auto tid = spawn(&testfn, thisTid); + runTest(tid); // Run the test again with a limited mailbox size. - tid = spawn( &testfn, thisTid ); - setMaxMailboxSize( tid, 2, OnCrowding.block ); - runTest( tid ); + tid = spawn(&testfn, thisTid); + setMaxMailboxSize(tid, 2, OnCrowding.block); + runTest(tid); } - - unittest + @system unittest { simpleTest(); } - - unittest + @system unittest { scheduler = new ThreadScheduler; simpleTest(); @@ -2505,23 +2319,15 @@ version( unittest ) } } -////////////////////////////////////////////////////////////////////////////// -// initOnce -////////////////////////////////////////////////////////////////////////////// - -private template initOnceLock() +private @property Mutex initOnceLock() { __gshared Mutex lock; - - shared static this() - { - lock = new Mutex; - } - - @property Mutex initOnceLock() - { - return lock; - } + if (auto mtx = atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock)) + return mtx; + auto mtx = new Mutex; + if (cas(cast(shared)&lock, cast(shared) null, cast(shared) mtx)) + return mtx; + return atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock); } /** @@ -2546,7 +2352,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init) } /// A typical use-case is to perform lazy but thread-safe initialization. -unittest +@system unittest { static class MySingleton { @@ -2556,10 +2362,11 @@ unittest return initOnce!inst(new MySingleton); } } + assert(MySingleton.instance !is null); } -unittest +@system unittest { static class MySingleton { @@ -2568,6 +2375,7 @@ unittest static __gshared MySingleton inst; return initOnce!inst(new MySingleton); } + private: this() { val = ++cnt; } size_t val; @@ -2575,7 +2383,7 @@ unittest } foreach (_; 0 .. 10) - spawn({ownerTid.send(MySingleton.instance.val);}); + spawn({ ownerTid.send(MySingleton.instance.val); }); foreach (_; 0 .. 10) assert(receiveOnly!size_t == MySingleton.instance.val); assert(MySingleton.cnt == 1); @@ -2600,8 +2408,9 @@ unittest auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) { // check that var is global, can't take address of a TLS variable - static assert(is(typeof({__gshared p = &var;})), "var must be 'static shared' or '__gshared'."); - import core.atomic; + static assert(is(typeof({ __gshared p = &var; })), + "var must be 'static shared' or '__gshared'."); + import core.atomic : atomicLoad, MemoryOrder, atomicStore; static shared bool flag; if (!atomicLoad!(MemoryOrder.acq)(flag)) @@ -2619,8 +2428,10 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) } /// Use a separate mutex when init blocks on another thread that might also call initOnce. -unittest +@system unittest { + import core.sync.mutex : Mutex; + static shared bool varA, varB; __gshared Mutex m; m = new Mutex; @@ -2636,10 +2447,10 @@ unittest assert(varB == true); } -unittest +@system unittest { - static shared bool a; - __gshared bool b; + static shared bool a; + __gshared bool b; static bool c; bool d; initOnce!a(true); diff --git a/std/concurrencybase.d b/std/concurrencybase.d new file mode 100644 index 00000000000..2d26d8e4009 --- /dev/null +++ b/std/concurrencybase.d @@ -0,0 +1,20 @@ +// Written in the D programming language. + +/** + * The only purpose of this module is to do the static construction for + * std.concurrency, to eliminate cyclic construction errors. + * + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Source: $(PHOBOSSRC std/_concurrencybase.d) + */ +module std.concurrencybase; + +import core.sync.mutex; + +extern(C) void std_concurrency_static_this(); + +shared static this() +{ + std_concurrency_static_this(); +} + diff --git a/std/container/array.d b/std/container/array.d index 93e861b7e95..a3b570f074f 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -1,36 +1,87 @@ /** -This module provides an $(D Array) type with deterministic memory usage not -reliant on the GC, as an alternative to the built-in arrays. - -This module is a submodule of $(LINK2 std_container.html, std.container). - -Source: $(PHOBOSSRC std/container/_array.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 - -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB -boost.org/LICENSE_1_0.txt)). - -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) -*/ + * This module provides an `Array` type with deterministic memory usage not + * reliant on the GC, as an alternative to the built-in arrays. + * + * This module is a submodule of $(MREF std, container). + * + * Source: $(PHOBOSSRC std/container/_array.d) + * + * Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. + * + * License: Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at $(HTTP + * boost.org/LICENSE_1_0.txt)). + * + * Authors: $(HTTP erdani.com, Andrei Alexandrescu) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ module std.container.array; import std.range.primitives; import std.traits; import core.exception : RangeError; -import std.algorithm : move; public import std.container.util; +/// +@system unittest +{ + auto arr = Array!int(0, 2, 3); + assert(arr[0] == 0); + assert(arr.front == 0); + assert(arr.back == 3); + + // reserve space + arr.reserve(1000); + assert(arr.length == 3); + assert(arr.capacity >= 1000); + + // insertion + arr.insertBefore(arr[1..$], 1); + assert(arr.front == 0); + assert(arr.length == 4); + + arr.insertBack(4); + assert(arr.back == 4); + assert(arr.length == 5); + + // set elements + arr[1] *= 42; + assert(arr[1] == 42); +} + +/// +@system unittest +{ + import std.algorithm.comparison : equal; + auto arr = Array!int(1, 2, 3); + + // concat + auto b = Array!int(11, 12, 13); + arr ~= b; + assert(arr.length == 6); + + // slicing + assert(arr[1 .. 3].equal([2, 3])); + + // remove + arr.linearRemove(arr[1 .. 3]); + assert(arr[0 .. 2].equal([1, 11])); +} + +/// `Array!bool` packs together values efficiently by allocating one bit per element +@system unittest +{ + Array!bool arr; + arr.insert([true, true, false, true, false]); + assert(arr.length == 5); +} + private struct RangeT(A) { /* Workaround for Issue 13629 at https://issues.dlang.org/show_bug.cgi?id=13629 - See also: http://forum.dlang.org/thread/vbmwhzvawhnkoxrhbnyb@forum.dlang.org?page=1 + See also: http://forum.dlang.org/post/vbmwhzvawhnkoxrhbnyb@forum.dlang.org */ private A[1] _outer_; private @property ref inout(A) _outer() inout { return _outer_[0]; } @@ -66,51 +117,53 @@ private struct RangeT(A) @property ref inout(E) front() inout { - version (assert) if (empty) throw new RangeError(); + assert(!empty, "Attempting to access the front of an empty Array"); return _outer[_a]; } @property ref inout(E) back() inout { - version (assert) if (empty) throw new RangeError(); + assert(!empty, "Attempting to access the back of an empty Array"); return _outer[_b - 1]; } - void popFront() @safe pure nothrow + void popFront() @safe @nogc pure nothrow { - version (assert) if (empty) throw new RangeError(); + assert(!empty, "Attempting to popFront an empty Array"); ++_a; } - void popBack() @safe pure nothrow + void popBack() @safe @nogc pure nothrow { - version (assert) if (empty) throw new RangeError(); + assert(!empty, "Attempting to popBack an empty Array"); --_b; } static if (isMutable!A) { + import std.algorithm.mutation : move; + E moveFront() { - version (assert) if (empty || _a >= _outer.length) throw new RangeError(); + assert(!empty && _a < _outer.length); return move(_outer._data._payload[_a]); } E moveBack() { - version (assert) if (empty || _b > _outer.length) throw new RangeError(); + assert(!empty && _b <= _outer.length); return move(_outer._data._payload[_b - 1]); } E moveAt(size_t i) { - version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError(); + assert(_a + i < _b && _a + i < _outer.length); return move(_outer._data._payload[_a + i]); } } ref inout(E) opIndex(size_t i) inout { - version (assert) if (_a + i >= _b) throw new RangeError(); + assert(_a + i < _b); return _outer[_a + i]; } @@ -121,7 +174,7 @@ private struct RangeT(A) RangeT opSlice(size_t i, size_t j) { - version (assert) if (i > j || _a + j > _b) throw new RangeError(); + assert(i <= j && _a + j <= _b); return typeof(return)(_outer, _a + i, _a + j); } @@ -132,7 +185,7 @@ private struct RangeT(A) RangeT!(const(A)) opSlice(size_t i, size_t j) const { - version (assert) if (i > j || _a + j > _b) throw new RangeError(); + assert(i <= j && _a + j <= _b); return typeof(return)(_outer, _a + i, _a + j); } @@ -140,70 +193,69 @@ private struct RangeT(A) { void opSliceAssign(E value) { - version (assert) if (_b > _outer.length) throw new RangeError(); + assert(_b <= _outer.length); _outer[_a .. _b] = value; } void opSliceAssign(E value, size_t i, size_t j) { - version (assert) if (_a + j > _b) throw new RangeError(); + assert(_a + j <= _b); _outer[_a + i .. _a + j] = value; } void opSliceUnary(string op)() if (op == "++" || op == "--") { - version (assert) if (_b > _outer.length) throw new RangeError(); + assert(_b <= _outer.length); mixin(op~"_outer[_a .. _b];"); } void opSliceUnary(string op)(size_t i, size_t j) if (op == "++" || op == "--") { - version (assert) if (_a + j > _b) throw new RangeError(); + assert(_a + j <= _b); mixin(op~"_outer[_a + i .. _a + j];"); } void opSliceOpAssign(string op)(E value) { - version (assert) if (_b > _outer.length) throw new RangeError(); + assert(_b <= _outer.length); mixin("_outer[_a .. _b] "~op~"= value;"); } void opSliceOpAssign(string op)(E value, size_t i, size_t j) { - version (assert) if (_a + j > _b) throw new RangeError(); + assert(_a + j <= _b); mixin("_outer[_a + i .. _a + j] "~op~"= value;"); } } } /** -Array type with deterministic control of memory. The memory allocated -for the array is reclaimed as soon as possible; there is no reliance -on the garbage collector. $(D Array) uses $(D malloc) and $(D free) -for managing its own memory. - -This means that pointers to elements of an $(D Array) will become -dangling as soon as the element is removed from the $(D Array). On the other hand -the memory allocated by an $(D Array) will be scanned by the GC and -GC managed objects referenced from an $(D Array) will be kept alive. - -Note: - -When using $(D Array) with range-based functions like those in $(D std.algorithm), -$(D Array) must be sliced to get a range (for example, use $(D array[].map!) -instead of $(D array.map!)). The container itself is not a range. + * _Array type with deterministic control of memory. The memory allocated + * for the array is reclaimed as soon as possible; there is no reliance + * on the garbage collector. `Array` uses `malloc`, `realloc` and `free` + * for managing its own memory. + * + * This means that pointers to elements of an `Array` will become + * dangling as soon as the element is removed from the `Array`. On the other hand + * the memory allocated by an `Array` will be scanned by the GC and + * GC managed objects referenced from an `Array` will be kept alive. + * + * Note: + * + * When using `Array` with range-based functions like those in `std.algorithm`, + * `Array` must be sliced to get a range (for example, use `array[].map!` + * instead of `array.map!`). The container itself is not a range. */ struct Array(T) if (!is(Unqual!T == bool)) { - import core.stdc.stdlib; - import core.stdc.string; + import core.stdc.stdlib : malloc, realloc, free; + import core.stdc.string : memcpy, memmove, memset; - import core.memory; + import core.memory : GC; - import std.algorithm : initializeAll, copy; import std.exception : enforce; import std.typecons : RefCounted, RefCountedAutoInitialize; @@ -213,14 +265,13 @@ if (!is(Unqual!T == bool)) size_t _capacity; T[] _payload; - // Convenience constructor this(T[] p) { _capacity = p.length; _payload = p; } // Destructor releases array memory ~this() { - //Warning: destroy will also destroy class instances. - //The hasElaborateDestructor protects us here. + // Warning: destroy would destroy also class instances. + // The hasElaborateDestructor protects us here. static if (hasElaborateDestructor!T) foreach (ref e; _payload) .destroy(e); @@ -231,35 +282,19 @@ if (!is(Unqual!T == bool)) free(_payload.ptr); } - this(this) - { - assert(0); - } + this(this) @disable; - void opAssign(Payload rhs) - { - assert(false); - } + void opAssign(Payload rhs) @disable; - // Duplicate data - // @property Payload dup() - // { - // Payload result; - // result._payload = _payload.dup; - // // Conservatively assume initial capacity == length - // result._capacity = result._payload.length; - // return result; - // } - - // length @property size_t length() const { return _payload.length; } - // length @property void length(size_t newLength) { + import std.algorithm.mutation : initializeAll; + if (length >= newLength) { // shorten @@ -270,25 +305,40 @@ if (!is(Unqual!T == bool)) _payload = _payload.ptr[0 .. newLength]; return; } - // enlarge - auto startEmplace = length; - _payload = (cast(T*) realloc(_payload.ptr, - T.sizeof * newLength))[0 .. newLength]; - initializeAll(_payload.ptr[startEmplace .. length]); + immutable startEmplace = length; + if (_capacity < newLength) + { + // enlarge + import core.checkedint : mulu; + + bool overflow; + const nbytes = mulu(newLength, T.sizeof, overflow); + if (overflow) + assert(0); + _payload = (cast(T*) realloc(_payload.ptr, nbytes))[0 .. newLength]; + _capacity = newLength; + } + else + { + _payload = _payload.ptr[0 .. newLength]; + } + initializeAll(_payload.ptr[startEmplace .. newLength]); } - // capacity @property size_t capacity() const { return _capacity; } - // reserve void reserve(size_t elements) { if (elements <= capacity) return; - immutable sz = elements * T.sizeof; - static if (hasIndirections!T) // should use hasPointers instead + import core.checkedint : mulu; + bool overflow; + const sz = mulu(elements, T.sizeof, overflow); + if (overflow) + assert(0); + static if (hasIndirections!T) { /* Because of the transactional nature of this * relative to the garbage collector, ensure no @@ -296,12 +346,14 @@ if (!is(Unqual!T == bool)) * than realloc. */ immutable oldLength = length; - auto newPayload = - enforce(cast(T*) malloc(sz))[0 .. oldLength]; + + auto newPayloadPtr = cast(T*) malloc(sz); + newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory"); + auto newPayload = newPayloadPtr[0 .. oldLength]; + // copy old data over to new array memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength); - // Zero out unused capacity to prevent gc from seeing - // false pointers + // Zero out unused capacity to prevent gc from seeing false pointers memset(newPayload.ptr + oldLength, 0, (elements - oldLength) * T.sizeof); @@ -312,19 +364,18 @@ if (!is(Unqual!T == bool)) } else { - /* These can't have pointers, so no need to zero - * unused region - */ - auto newPayload = - enforce(cast(T*) realloc(_payload.ptr, sz))[0 .. length]; + // These can't have pointers, so no need to zero unused region + auto newPayloadPtr = cast(T*) realloc(_payload.ptr, sz); + newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory"); + auto newPayload = newPayloadPtr[0 .. length]; _payload = newPayload; } _capacity = elements; } // Insert one item - size_t insertBack(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) + size_t insertBack(Elem)(Elem elem) + if (isImplicitlyConvertible!(Elem, T)) { import std.conv : emplace; if (_capacity == length) @@ -332,29 +383,29 @@ if (!is(Unqual!T == bool)) reserve(1 + capacity * 3 / 2); } assert(capacity > length && _payload.ptr); - emplace(_payload.ptr + _payload.length, stuff); + emplace(_payload.ptr + _payload.length, elem); _payload = _payload.ptr[0 .. _payload.length + 1]; return 1; } - /// Insert a range of items - size_t insertBack(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + // Insert a range of items + size_t insertBack(Range)(Range r) + if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T)) { - static if (hasLength!Stuff) + static if (hasLength!Range) { immutable oldLength = length; - reserve(oldLength + stuff.length); + reserve(oldLength + r.length); } size_t result; - foreach (item; stuff) + foreach (item; r) { insertBack(item); ++result; } - static if (hasLength!Stuff) + static if (hasLength!Range) { - assert(length == oldLength + stuff.length); + assert(length == oldLength + r.length); } return result; } @@ -362,13 +413,18 @@ if (!is(Unqual!T == bool)) private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no); private Data _data; -/** -Constructor taking a number of items + /** + * Constructor taking a number of items. */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + this(U)(U[] values...) + if (isImplicitlyConvertible!(U, T)) { import std.conv : emplace; - auto p = cast(T*) malloc(T.sizeof * values.length); + import core.checkedint : mulu; + bool overflow; + const nbytes = mulu(values.length, T.sizeof, overflow); + if (overflow) assert(0); + auto p = cast(T*) malloc(nbytes); static if (hasIndirections!T) { if (p) @@ -378,23 +434,21 @@ Constructor taking a number of items foreach (i, e; values) { emplace(p + i, e); - assert(p[i] == e); } _data = Data(p[0 .. values.length]); } -/** -Constructor taking an input range + /** + * Constructor taking an input range */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T) && !is(Stuff == T[])) + this(Range)(Range r) + if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) { - insertBack(stuff); + insertBack(r); } - -/** -Comparison for equality. + /** + * Comparison for equality. */ bool opEquals(const Array rhs) const { @@ -409,21 +463,25 @@ Comparison for equality. return _data._payload == rhs._data._payload; } -/** - Defines the container's primary range, which is a random-access range. - - ConstRange is a variant with const elements. - ImmutableRange is a variant with immutable elements. -*/ + /** + * Defines the array's primary range, which is a random-access range. + * + * `ConstRange` is a variant with `const` elements. + * `ImmutableRange` is a variant with `immutable` elements. + */ alias Range = RangeT!Array; - alias ConstRange = RangeT!(const Array); /// ditto - alias ImmutableRange = RangeT!(immutable Array); /// ditto -/** -Duplicates the container. The elements themselves are not transitively -duplicated. + /// ditto + alias ConstRange = RangeT!(const Array); + + /// ditto + alias ImmutableRange = RangeT!(immutable Array); -Complexity: $(BIGOH n). + /** + * Duplicates the array. The elements themselves are not transitively + * duplicated. + * + * Complexity: $(BIGOH length). */ @property Array dup() { @@ -431,21 +489,20 @@ Complexity: $(BIGOH n). return Array(_data._payload); } -/** -Property returning $(D true) if and only if the container has no -elements. - -Complexity: $(BIGOH 1) + /** + * Returns: `true` if and only if the array has no elements. + * + * Complexity: $(BIGOH 1) */ @property bool empty() const { return !_data.refCountedStore.isInitialized || _data._payload.empty; } -/** -Returns the number of elements in the container. - -Complexity: $(BIGOH 1). + /** + * Returns: The number of elements in the array. + * + * Complexity: $(BIGOH 1). */ @property size_t length() const { @@ -458,31 +515,39 @@ Complexity: $(BIGOH 1). return length; } -/** -Returns the maximum number of elements the container can store without - (a) allocating memory, (b) invalidating iterators upon insertion. - -Complexity: $(BIGOH 1) + /** + * Returns: The maximum number of elements the array can store without + * reallocating memory and invalidating iterators upon insertion. + * + * Complexity: $(BIGOH 1) */ @property size_t capacity() { return _data.refCountedStore.isInitialized ? _data._capacity : 0; } -/** -Ensures sufficient capacity to accommodate $(D e) elements. - -Postcondition: $(D capacity >= e) - -Complexity: $(BIGOH 1) + /** + * Ensures sufficient capacity to accommodate `e` _elements. + * If `e < capacity`, this method does nothing. + * + * Postcondition: `capacity >= e` + * + * Note: If the capacity is increased, one should assume that all + * iterators to the elements are invalidated. + * + * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). */ void reserve(size_t elements) { if (!_data.refCountedStore.isInitialized) { if (!elements) return; - immutable sz = elements * T.sizeof; - auto p = enforce(malloc(sz)); + import core.checkedint : mulu; + bool overflow; + const sz = mulu(elements, T.sizeof, overflow); + if (overflow) assert(0); + auto p = malloc(sz); + p || assert(false, "std.container.Array.reserve failed to allocate memory"); static if (hasIndirections!T) { GC.addRange(p, sz); @@ -496,88 +561,98 @@ Complexity: $(BIGOH 1) } } -/** -Returns a range that iterates over elements of the container, in -forward order. - -Complexity: $(BIGOH 1) + /** + * Returns: A range that iterates over elements of the array in + * forward order. + * + * Complexity: $(BIGOH 1) */ Range opSlice() { return typeof(return)(this, 0, length); } + ConstRange opSlice() const { return typeof(return)(this, 0, length); } + ImmutableRange opSlice() immutable { return typeof(return)(this, 0, length); } -/** -Returns a range that iterates over elements of the container from -index $(D a) up to (excluding) index $(D b). - -Precondition: $(D a <= b && b <= length) - -Complexity: $(BIGOH 1) -*/ + /** + * Returns: A range that iterates over elements of the array from + * index `i` up to (excluding) index `j`. + * + * Precondition: `i <= j && j <= length` + * + * Complexity: $(BIGOH 1) + */ Range opSlice(size_t i, size_t j) { - version (assert) if (i > j || j > length) throw new RangeError(); + assert(i <= j && j <= length); return typeof(return)(this, i, j); } + ConstRange opSlice(size_t i, size_t j) const { - version (assert) if (i > j || j > length) throw new RangeError(); + assert(i <= j && j <= length); return typeof(return)(this, i, j); } + ImmutableRange opSlice(size_t i, size_t j) immutable { - version (assert) if (i > j || j > length) throw new RangeError(); + assert(i <= j && j <= length); return typeof(return)(this, i, j); } -/** -Forward to $(D opSlice().front) and $(D opSlice().back), respectively. - -Precondition: $(D !empty) - -Complexity: $(BIGOH 1) + /** + * Returns: The first element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) */ @property ref inout(T) front() inout { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); + assert(_data.refCountedStore.isInitialized); return _data._payload[0]; } - /// ditto + /** + * Returns: The last element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) + */ @property ref inout(T) back() inout { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); + assert(_data.refCountedStore.isInitialized); return _data._payload[$ - 1]; } -/** -Indexing operators yield or modify the value at a specified index. - -Precondition: $(D i < length) - -Complexity: $(BIGOH 1) + /** + * Returns: The element or a reference to the element at the specified index. + * + * Precondition: `i < length` + * + * Complexity: $(BIGOH 1) */ ref inout(T) opIndex(size_t i) inout { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); + assert(_data.refCountedStore.isInitialized); return _data._payload[i]; } -/** -Slicing operations execute an operation on an entire slice. - -Precondition: $(D i < j && j < length) - -Complexity: $(BIGOH slice.length) + /** + * Slicing operators executing the specified operation on the entire slice. + * + * Precondition: `i < j && j < length` + * + * Complexity: $(BIGOH slice.length) */ void opSliceAssign(T value) { @@ -596,7 +671,7 @@ Complexity: $(BIGOH slice.length) /// ditto void opSliceUnary(string op)() - if (op == "++" || op == "--") + if (op == "++" || op == "--") { if (!_data.refCountedStore.isInitialized) return; mixin(op~"_data._payload[];"); @@ -604,7 +679,7 @@ Complexity: $(BIGOH slice.length) /// ditto void opSliceUnary(string op)(size_t i, size_t j) - if (op == "++" || op == "--") + if (op == "++" || op == "--") { auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; mixin(op~"slice[i .. j];"); @@ -624,32 +699,64 @@ Complexity: $(BIGOH slice.length) mixin("slice[i .. j] "~op~"= value;"); } -/** -Returns a new container that's the concatenation of $(D this) and its -argument. $(D opBinaryRight) is only defined if $(D Stuff) does not -define $(D opBinary). + private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; })); -Complexity: $(BIGOH n + m), where m is the number of elements in $(D -stuff) + /** + * Returns: A new array which is a concatenation of `this` and its argument. + * + * Complexity: + * $(BIGOH length + m), where `m` is the number of elements in `stuff`. */ Array opBinary(string op, Stuff)(Stuff stuff) - if (op == "~") + if (op == "~") { - // TODO: optimize Array result; - // @@@BUG@@ result ~= this[] doesn't work - auto r = this[]; - result ~= r; - assert(result.length == length); - result ~= stuff[]; + + static if (hasLength!Stuff || isNarrowString!Stuff) + result.reserve(length + stuff.length); + else static if (hasSliceWithLength!Stuff) + result.reserve(length + stuff[].length); + else static if (isImplicitlyConvertible!(Stuff, T)) + result.reserve(length + 1); + + result.insertBack(this[]); + result ~= stuff; return result; } -/** -Forwards to $(D insertBack(stuff)). + @nogc @system unittest + { + auto a = Array!int(0, 1, 2); + int[3] b = [3, 4, 5]; + short[3] ci = [0, 1, 0]; + auto c = Array!short(ci); + assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); + assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); + assert(Array!int(0, 1, 2, 3) == a ~ 3); + assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); + } + + @nogc @system unittest + { + auto a = Array!char('a', 'b'); + assert(Array!char("abc") == a ~ 'c'); + import std.utf : byCodeUnit; + assert(Array!char("abcd") == a ~ "cd".byCodeUnit); + } + + @nogc @system unittest + { + auto a = Array!dchar("ąćę"d); + assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); + wchar x = 'Ï¢'; + assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); + } + + /** + * Forwards to `insertBack`. */ void opOpAssign(string op, Stuff)(Stuff stuff) - if (op == "~") + if (op == "~") { static if (is(typeof(stuff[]))) { @@ -661,28 +768,28 @@ Forwards to $(D insertBack(stuff)). } } -/** -Removes all contents from the container. The container decides how $(D -capacity) is affected. - -Postcondition: $(D empty) - -Complexity: $(BIGOH n) + /** + * Removes all the elements from the array and releases allocated memory. + * + * Postcondition: `empty == true && capacity == 0` + * + * Complexity: $(BIGOH length) */ void clear() { _data = Data.init; } -/** -Sets the number of elements in the container to $(D newSize). If $(D -newSize) is greater than $(D length), the added elements are added to -unspecified positions in the container and initialized with $(D -T.init). - -Complexity: $(BIGOH abs(n - newLength)) - -Postcondition: $(D length == newLength) + /** + * Sets the number of elements in the array to `newLength`. If `newLength` + * is greater than `length`, the new elements are added to the end of the + * array and initialized with `T.init`. + * + * Complexity: + * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. + * If `capacity < newLength` the worst case is $(BIGOH newLength). + * + * Postcondition: `length == newLength` */ @property void length(size_t newLength) { @@ -690,16 +797,18 @@ Postcondition: $(D length == newLength) _data.length = newLength; } -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. The stable version behaves the same, -but guarantees that ranges iterating over the container are never invalidated. - -Precondition: $(D !empty) - -Returns: The element removed. - -Complexity: $(BIGOH log(n)). + /** + * Removes the last element from the array and returns it. + * Both stable and non-stable versions behave the same and guarantee + * that ranges iterating over the array are never invalidated. + * + * Precondition: `empty == false` + * + * Returns: The element removed. + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ T removeAny() { @@ -707,19 +816,19 @@ Complexity: $(BIGOH log(n)). removeBack(); return result; } + /// ditto alias stableRemoveAny = removeAny; -/** -Inserts $(D value) to the front or back of the container. $(D stuff) -can be a value convertible to $(D T) or a range of objects convertible -to $(D T). The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH m * log(n)), where $(D m) is the number of -elements in $(D stuff) + /** + * Inserts the specified elements at the back of the array. `stuff` can be + * a value convertible to `T` or a range of objects convertible to `T`. + * + * Returns: The number of elements inserted. + * + * Complexity: + * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), + * where `m` is the number of elements in `stuff`. */ size_t insertBack(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, T) || @@ -728,17 +837,20 @@ elements in $(D stuff) _data.refCountedStore.ensureInitialized(); return _data.insertBack(stuff); } + /// ditto alias insert = insertBack; -/** -Removes the value at the back of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Precondition: $(D !empty) - -Complexity: $(BIGOH log(n)). + /** + * Removes the value from the back of the array. Both stable and non-stable + * versions behave the same and guarantee that ranges iterating over the + * array are never invalidated. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ void removeBack() { @@ -748,21 +860,22 @@ Complexity: $(BIGOH log(n)). _data._payload = _data._payload[0 .. $ - 1]; } + /// ditto alias stableRemoveBack = removeBack; -/** -Removes $(D howMany) values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove $(D howMany) elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany). + /** + * Removes `howMany` values from the back of the array. + * Unlike the unparameterized versions above, these functions + * do not throw if they could not remove `howMany` elements. Instead, + * if `howMany > n`, all elements are removed. The returned value is + * the effective number of elements removed. Both stable and non-stable + * versions behave the same and guarantee that ranges iterating over + * the array are never invalidated. + * + * Returns: The number of elements removed. + * + * Complexity: $(BIGOH howMany). */ size_t removeBack(size_t howMany) { @@ -774,19 +887,22 @@ Complexity: $(BIGOH howMany). _data._payload = _data._payload[0 .. $ - howMany]; return howMany; } + /// ditto alias stableRemoveBack = removeBack; -/** -Inserts $(D stuff) before, after, or instead range $(D r), which must -be a valid range previously extracted from this container. $(D stuff) -can be a value convertible to $(D T) or a range of objects convertible -to $(D T). The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + /** + * Inserts `stuff` before, after, or instead range `r`, which must + * be a valid range previously extracted from this array. `stuff` + * can be a value convertible to `T` or a range of objects convertible + * to `T`. Both stable and non-stable version behave the same and + * guarantee that ranges iterating over the array are never invalidated. + * + * Returns: The number of values inserted. + * + * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. + * + * Throws: `Exception` if `r` is not a range extracted from this array. */ size_t insertBefore(Stuff)(Range r, Stuff stuff) if (isImplicitlyConvertible!(Stuff, T)) @@ -833,7 +949,7 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) } else { - import std.algorithm : bringToFront; + import std.algorithm.mutation : bringToFront; enforce(_data); immutable offset = r._a; enforce(offset <= length); @@ -844,10 +960,13 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) } } + /// ditto + alias stableInsertBefore = insertBefore; + /// ditto size_t insertAfter(Stuff)(Range r, Stuff stuff) { - import std.algorithm : bringToFront; + import std.algorithm.mutation : bringToFront; enforce(r._outer._data is _data); // TODO: optimize immutable offset = r._b; @@ -898,20 +1017,21 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) return 1; } -/** -Removes all elements belonging to $(D r), which must be a range -obtained originally from this container. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: A range spanning the remaining elements in the container that -initially were right after $(D r). - -Complexity: $(BIGOH n - m), where $(D m) is the number of elements in -$(D r) + /** + * Removes all elements belonging to `r`, which must be a range + * obtained originally from this array. + * + * Returns: A range spanning the remaining elements in the array that + * initially were right after `r`. + * + * Complexity: $(BIGOH length) + * + * Throws: `Exception` if `r` is not a valid range extracted from this array. */ Range linearRemove(Range r) { + import std.algorithm.mutation : copy; + enforce(r._outer._data is _data); enforce(_data.refCountedStore.isInitialized); enforce(r._a <= r._b && r._b <= length); @@ -925,13 +1045,65 @@ $(D r) } } -unittest +@system unittest { Array!int a; assert(a.empty); } -unittest +@system unittest +{ + Array!int a; + a.length = 10; + assert(a.length == 10); + assert(a.capacity >= a.length); +} + +@system unittest +{ + struct Dumb { int x = 5; } + Array!Dumb a; + a.length = 10; + assert(a.length == 10); + assert(a.capacity >= a.length); + immutable cap = a.capacity; + foreach (ref e; a) + e.x = 10; + a.length = 5; + assert(a.length == 5); + // do not realloc if length decreases + assert(a.capacity == cap); + foreach (ref e; a) + assert(e.x == 10); + + a.length = 8; + assert(a.length == 8); + // do not realloc if capacity sufficient + assert(a.capacity == cap); + assert(Dumb.init.x == 5); + foreach (i; 0 .. 5) + assert(a[i].x == 10); + foreach (i; 5 .. a.length) + assert(a[i].x == Dumb.init.x); + + // realloc required, check if values properly copied + a[] = Dumb(1); + a.length = 20; + assert(a.capacity >= 20); + foreach (i; 0 .. 8) + assert(a[i].x == 1); + foreach (i; 8 .. a.length) + assert(a[i].x == Dumb.init.x); + + // check if overlapping elements properly initialized + a.length = 1; + a.length = 20; + assert(a[0].x == 1); + foreach (e; a[1 .. $]) + assert(e.x == Dumb.init.x); +} + +@system unittest { Array!int a = Array!int(1, 2, 3); //a._data._refCountedDebug = true; @@ -942,13 +1114,13 @@ unittest assert(a == Array!int(1, 2, 3)); } -unittest +@system unittest { auto a = Array!int(1, 2, 3); assert(a.length == 3); } -unittest +@system unittest { const Array!int a = [1, 2]; @@ -969,14 +1141,14 @@ unittest } } -unittest +@safe unittest { // REG https://issues.dlang.org/show_bug.cgi?id=13621 import std.container : Array, BinaryHeap; alias Heap = BinaryHeap!(Array!int); } -unittest +@system unittest { Array!int a; a.reserve(1000); @@ -991,24 +1163,24 @@ unittest assert(p == a._data._payload.ptr); } -unittest +@system unittest { auto a = Array!int(1, 2, 3); a[1] *= 42; assert(a[1] == 84); } -unittest +@system unittest { auto a = Array!int(1, 2, 3); auto b = Array!int(11, 12, 13); auto c = a ~ b; - //foreach (e; c) writeln(e); assert(c == Array!int(1, 2, 3, 11, 12, 13)); - //assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); + assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); + assert(a ~ [4,5] == Array!int(1,2,3,4,5)); } -unittest +@system unittest { auto a = Array!int(1, 2, 3); auto b = Array!int(11, 12, 13); @@ -1016,14 +1188,14 @@ unittest assert(a == Array!int(1, 2, 3, 11, 12, 13)); } -unittest +@system unittest { auto a = Array!int(1, 2, 3, 4); assert(a.removeAny() == 4); assert(a == Array!int(1, 2, 3)); } -unittest +@system unittest { auto a = Array!int(1, 2, 3, 4, 5); auto r = a[2 .. a.length]; @@ -1034,7 +1206,7 @@ unittest assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5)); } -unittest +@system unittest { auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8); a.linearRemove(a[4 .. 6]); @@ -1042,9 +1214,9 @@ unittest } // Give the Range object some testing. -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : retro; auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[]; auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[]; @@ -1057,10 +1229,10 @@ unittest assert(equal(retro(b), a)); assert(a.length == 7); - assert(equal(a[1..4], [1, 2, 3])); + assert(equal(a[1 .. 4], [1, 2, 3])); } // Test issue 5920 -unittest +@system unittest { struct structBug5920 { @@ -1077,7 +1249,7 @@ unittest uint dMask; auto arr = Array!S(cast(S[])[]); - foreach (i; 0..8) + foreach (i; 0 .. 8) arr.insertBack(S(i, &dMask)); // don't check dMask now as S may be copied multiple times (it's ok?) { @@ -1099,7 +1271,7 @@ unittest assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. } // Test issue 5792 (mainly just to check if this piece of code is compilable) -unittest +@system unittest { auto a = Array!(int[])([[1,2],[3,4]]); a.reserve(4); @@ -1115,47 +1287,48 @@ unittest } // test replace!Stuff with range Stuff -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = Array!int([1, 42, 5]); a.replace(a[1 .. 2], [2, 3, 4]); assert(equal(a[], [1, 2, 3, 4, 5])); } // test insertBefore and replace with empty Arrays -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = Array!int(); a.insertBefore(a[], 1); assert(equal(a[], [1])); } -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = Array!int(); a.insertBefore(a[], [1, 2]); assert(equal(a[], [1, 2])); } -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = Array!int(); a.replace(a[], [1, 2]); assert(equal(a[], [1, 2])); } -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = Array!int(); a.replace(a[], 1); assert(equal(a[], [1])); } // make sure that Array instances refuse ranges that don't belong to them -unittest +@system unittest { - import std.exception; + import std.exception : assertThrown; + Array!int a = [1, 2, 3]; auto r = a.dup[]; assertThrown(a.insertBefore(r, 42)); @@ -1165,7 +1338,7 @@ unittest assertThrown(a.replace(r, [42])); assertThrown(a.linearRemove(r)); } -unittest +@system unittest { auto a = Array!int([1, 1]); a[1] = 0; //Check Array.opIndexAssign @@ -1196,9 +1369,9 @@ unittest assert(~r[0] == ~3); } -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; //Test "array-wide" operations auto a = Array!int([0, 1, 2]); //Array @@ -1242,7 +1415,8 @@ unittest } // Test issue 11194 -unittest { +@system unittest +{ static struct S { int i = 1337; void* p; @@ -1255,7 +1429,7 @@ unittest { arr ~= s; } -unittest //11459 +@safe unittest //11459 { static struct S { @@ -1266,18 +1440,18 @@ unittest //11459 alias B = Array!(shared bool); } -unittest //11884 +@system unittest //11884 { - import std.algorithm : filter; + import std.algorithm.iteration : filter; auto a = Array!int([1, 2, 2].filter!"true"()); } -unittest //8282 +@safe unittest //8282 { auto arr = new Array!int; } -unittest //6998 +@system unittest //6998 { static int i = 0; class C @@ -1300,9 +1474,9 @@ unittest //6998 assert(i == 1); //Just to make sure the GC doesn't collect before the above test. - assert(c.dummy ==1); + assert(c.dummy == 1); } -unittest //6998-2 +@system unittest //6998-2 { static class C {int i;} auto c = new C; @@ -1313,13 +1487,13 @@ unittest //6998-2 assert(c.i == 42); //fails } -unittest +@safe unittest { static assert(is(Array!int.Range)); static assert(is(Array!int.ConstRange)); } -unittest // const/immutable Array and Ranges +@system unittest // const/immutable Array and Ranges { static void test(A, R, E, S)() { @@ -1348,14 +1522,28 @@ unittest // const/immutable Array and Ranges A.ImmutableRange); } +// ensure @nogc +@nogc @system unittest +{ + Array!int ai; + ai ~= 1; + assert(ai.front == 1); + + ai.reserve(10); + assert(ai.capacity == 10); + + static immutable arr = [1, 2, 3]; + ai.insertBack(arr); +} + //////////////////////////////////////////////////////////////////////////////// // Array!bool //////////////////////////////////////////////////////////////////////////////// /** -_Array specialized for $(D bool). Packs together values efficiently by -allocating one bit per element. + * _Array specialized for `bool`. Packs together values efficiently by + * allocating one bit per element. */ struct Array(T) if (is(Unqual!T == bool)) @@ -1378,7 +1566,7 @@ if (is(Unqual!T == bool)) } /** - Defines the container's primary range. + * Defines the array's primary range. */ struct Range { @@ -1405,49 +1593,49 @@ if (is(Unqual!T == bool)) /// Ditto @property T front() { - enforce(!empty); + enforce(!empty, "Attempting to access the front of an empty Array"); return _outer[_a]; } /// Ditto @property void front(bool value) { - enforce(!empty); + enforce(!empty, "Attempting to set the front of an empty Array"); _outer[_a] = value; } /// Ditto T moveFront() { - enforce(!empty); + enforce(!empty, "Attempting to move the front of an empty Array"); return _outer.moveAt(_a); } /// Ditto void popFront() { - enforce(!empty); + enforce(!empty, "Attempting to popFront an empty Array"); ++_a; } /// Ditto @property T back() { - enforce(!empty); + enforce(!empty, "Attempting to access the back of an empty Array"); return _outer[_b - 1]; } /// Ditto @property void back(bool value) { - enforce(!empty); + enforce(!empty, "Attempting to set the back of an empty Array"); _outer[_b - 1] = value; } /// Ditto T moveBack() { - enforce(!empty); + enforce(!empty, "Attempting to move the back of an empty Array"); return _outer.moveAt(_b - 1); } /// Ditto void popBack() { - enforce(!empty); + enforce(!empty, "Attempting to popBack an empty Array"); --_b; } /// Ditto @@ -1475,36 +1663,37 @@ if (is(Unqual!T == bool)) /// ditto Range opSlice(size_t low, size_t high) { - assert(_a <= low && low <= high && high <= _b); + assert( + _a <= low && low <= high && high <= _b, + "Using out of bounds indexes on an Array" + ); return Range(_outer, _a + low, _a + high); } } /** - Property returning $(D true) if and only if the container has - no elements. - - Complexity: $(BIGOH 1) + * Property returning `true` if and only if the array has + * no elements. + * + * Complexity: $(BIGOH 1) */ @property bool empty() { return !length; } - unittest + @system unittest { Array!bool a; - //a._store._refCountedDebug = true; assert(a.empty); a.insertBack(false); assert(!a.empty); } /** - Returns a duplicate of the container. The elements themselves - are not transitively duplicated. - - Complexity: $(BIGOH n). + * Returns: A duplicate of the array. + * + * Complexity: $(BIGOH length). */ @property Array dup() { @@ -1513,7 +1702,7 @@ if (is(Unqual!T == bool)) return result; } - unittest + @system unittest { Array!bool a; assert(a.empty); @@ -1524,10 +1713,10 @@ if (is(Unqual!T == bool)) } /** - Returns the number of elements in the container. - - Complexity: $(BIGOH log(n)). - */ + * Returns the number of elements in the array. + * + * Complexity: $(BIGOH 1). + */ @property size_t length() const { return _store.refCountedStore.isInitialized ? _store._length : 0; @@ -1537,7 +1726,7 @@ if (is(Unqual!T == bool)) return length; } - unittest + @system unittest { import std.conv : to; Array!bool a; @@ -1547,11 +1736,10 @@ if (is(Unqual!T == bool)) } /** - Returns the maximum number of elements the container can store - without (a) allocating memory, (b) invalidating iterators upon - insertion. - - Complexity: $(BIGOH log(n)). + * Returns: The maximum number of elements the array can store without + * reallocating memory and invalidating iterators upon insertion. + * + * Complexity: $(BIGOH 1). */ @property size_t capacity() { @@ -1560,7 +1748,7 @@ if (is(Unqual!T == bool)) : 0; } - unittest + @system unittest { import std.conv : to; Array!bool a; @@ -1573,12 +1761,15 @@ if (is(Unqual!T == bool)) } /** - Ensures sufficient capacity to accommodate $(D n) elements. - - Postcondition: $(D capacity >= n) - - Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), - otherwise $(BIGOH 1). + * Ensures sufficient capacity to accommodate `e` _elements. + * If `e < capacity`, this method does nothing. + * + * Postcondition: `capacity >= e` + * + * Note: If the capacity is increased, one should assume that all + * iterators to the elements are invalidated. + * + * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). */ void reserve(size_t e) { @@ -1587,28 +1778,27 @@ if (is(Unqual!T == bool)) _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); } - unittest + @system unittest { Array!bool a; assert(a.capacity == 0); a.reserve(15657); assert(a.capacity >= 15657); + a.reserve(100); + assert(a.capacity >= 15657); } /** - Returns a range that iterates over all elements of the - container, in a container-defined order. The container should - choose the most convenient and fast method of iteration for $(D - opSlice()). - - Complexity: $(BIGOH log(n)) + * Returns: A range that iterates over all elements of the array in forward order. + * + * Complexity: $(BIGOH 1) */ Range opSlice() { return Range(this, 0, length); } - unittest + @system unittest { Array!bool a; a.insertBack([true, false, true, true]); @@ -1616,10 +1806,9 @@ if (is(Unqual!T == bool)) } /** - Returns a range that iterates the container between two - specified positions. - - Complexity: $(BIGOH log(n)) + * Returns: A range that iterates the array between two specified positions. + * + * Complexity: $(BIGOH 1) */ Range opSlice(size_t a, size_t b) { @@ -1627,7 +1816,7 @@ if (is(Unqual!T == bool)) return Range(this, a, b); } - unittest + @system unittest { Array!bool a; a.insertBack([true, false, true, true]); @@ -1635,10 +1824,13 @@ if (is(Unqual!T == bool)) } /** - Equivalent to $(D opSlice().front) and $(D opSlice().back), - respectively. - - Complexity: $(BIGOH log(n)) + * Returns: The first element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) + * + * Throws: `Exception` if the array is empty. */ @property bool front() { @@ -1654,7 +1846,7 @@ if (is(Unqual!T == bool)) else data.ptr[0] &= ~cast(size_t) 1; } - unittest + @system unittest { Array!bool a; a.insertBack([true, false, true, true]); @@ -1663,11 +1855,19 @@ if (is(Unqual!T == bool)) assert(!a.front); } - /// Ditto + /** + * Returns: The last element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) + * + * Throws: `Exception` if the array is empty. + */ @property bool back() { enforce(!empty); - return cast(bool)(data.back & (cast(size_t)1 << ((_store._length - 1) % bitsPerWord))); + return cast(bool)(data.back & (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord))); } /// Ditto @@ -1676,16 +1876,16 @@ if (is(Unqual!T == bool)) enforce(!empty); if (value) { - data.back |= (cast(size_t)1 << ((_store._length - 1) % bitsPerWord)); + data.back |= (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); } else { data.back &= - ~(cast(size_t)1 << ((_store._length - 1) % bitsPerWord)); + ~(cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)); } } - unittest + @system unittest { Array!bool a; a.insertBack([true, false, true, true]); @@ -1695,47 +1895,54 @@ if (is(Unqual!T == bool)) } /** - Indexing operators yield or modify the value at a specified index. + * Indexing operators yielding or modifyng the value at the specified index. + * + * Precondition: `i < length` + * + * Complexity: $(BIGOH 1) */ bool opIndex(size_t i) { auto div = cast(size_t) (i / bitsPerWord); auto rem = i % bitsPerWord; enforce(div < data.length); - return cast(bool)(data.ptr[div] & (cast(size_t)1 << rem)); + return cast(bool)(data.ptr[div] & (cast(size_t) 1 << rem)); } + /// ditto void opIndexAssign(bool value, size_t i) { auto div = cast(size_t) (i / bitsPerWord); auto rem = i % bitsPerWord; enforce(div < data.length); - if (value) data.ptr[div] |= (cast(size_t)1 << rem); - else data.ptr[div] &= ~(cast(size_t)1 << rem); + if (value) data.ptr[div] |= (cast(size_t) 1 << rem); + else data.ptr[div] &= ~(cast(size_t) 1 << rem); } + /// ditto void opIndexOpAssign(string op)(bool value, size_t i) { auto div = cast(size_t) (i / bitsPerWord); auto rem = i % bitsPerWord; enforce(div < data.length); - auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t)1 << rem)); + auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t) 1 << rem)); // Do the deed auto newValue = mixin("oldValue "~op~" value"); // Write back the value if (newValue != oldValue) { - if (newValue) data.ptr[div] |= (cast(size_t)1 << rem); - else data.ptr[div] &= ~(cast(size_t)1 << rem); + if (newValue) data.ptr[div] |= (cast(size_t) 1 << rem); + else data.ptr[div] &= ~(cast(size_t) 1 << rem); } } + /// Ditto T moveAt(size_t i) { return this[i]; } - unittest + @system unittest { Array!bool a; a.insertBack([true, false, true, true]); @@ -1745,50 +1952,57 @@ if (is(Unqual!T == bool)) } /** - Returns a new container that's the concatenation of $(D this) - and its argument. - - Complexity: $(BIGOH n + m), where m is the number of elements - in $(D stuff) + * Returns: A new array which is a concatenation of `this` and its argument. + * + * Complexity: + * $(BIGOH length + m), where `m` is the number of elements in `stuff`. */ - Array!bool opBinary(string op, Stuff)(Stuff rhs) if (op == "~") + Array!bool opBinary(string op, Stuff)(Stuff rhs) + if (op == "~") { - auto result = this; - return result ~= rhs; + Array!bool result; + + static if (hasLength!Stuff) + result.reserve(length + rhs.length); + else static if (is(typeof(rhs[])) && hasLength!(typeof(rhs[]))) + result.reserve(length + rhs[].length); + else static if (isImplicitlyConvertible!(Stuff, bool)) + result.reserve(length + 1); + + result.insertBack(this[]); + result ~= rhs; + return result; } - unittest + @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; Array!bool a; a.insertBack([true, false, true, true]); Array!bool b; b.insertBack([true, true, false, true]); assert(equal((a ~ b)[], [true, false, true, true, true, true, false, true])); + assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); + Array!bool c; + c.insertBack(true); + assert((c ~ false)[].equal([true, false])); } - // /// ditto - // TotalContainer opBinaryRight(Stuff, string op)(Stuff lhs) if (op == "~") - // { - // assert(0); - // } - /** - Forwards to $(D insertAfter(this[], stuff)). + * Forwards to `insertBack`. */ - // @@@BUG@@@ - //ref Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") - Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") + Array!bool opOpAssign(string op, Stuff)(Stuff stuff) + if (op == "~") { static if (is(typeof(stuff[]))) insertBack(stuff[]); else insertBack(stuff); return this; } - unittest + @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; Array!bool a; a.insertBack([true, false, true, true]); Array!bool b; @@ -1800,19 +2014,18 @@ if (is(Unqual!T == bool)) } /** - Removes all contents from the container. The container decides - how $(D capacity) is affected. - - Postcondition: $(D empty) - - Complexity: $(BIGOH n) + * Removes all the elements from the array and releases allocated memory. + * + * Postcondition: `empty == true && capacity == 0` + * + * Complexity: $(BIGOH length) */ void clear() { this = Array(); } - unittest + @system unittest { Array!bool a; a.insertBack([true, false, true, true]); @@ -1821,14 +2034,15 @@ if (is(Unqual!T == bool)) } /** - Sets the number of elements in the container to $(D - newSize). If $(D newSize) is greater than $(D length), the - added elements are added to the container and initialized with - $(D ElementType.init). - - Complexity: $(BIGOH abs(n - newLength)) - - Postcondition: $(D _length == newLength) + * Sets the number of elements in the array to `newLength`. If `newLength` + * is greater than `length`, the new elements are added to the end of the + * array and initialized with `false`. + * + * Complexity: + * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. + * If `capacity < newLength` the worst case is $(BIGOH newLength). + * + * Postcondition: `length == newLength` */ @property void length(size_t newLength) { @@ -1840,54 +2054,35 @@ if (is(Unqual!T == bool)) _store._length = newLength; } - unittest + @system unittest { Array!bool a; a.length = 1057; assert(a.length == 1057); + assert(a.capacity >= a.length); foreach (e; a) { assert(!e); } + immutable cap = a.capacity; + a.length = 100; + assert(a.length == 100); + // do not realloc if length decreases + assert(a.capacity == cap); } /** - Inserts $(D stuff) in the container. $(D stuff) can be a value - convertible to $(D ElementType) or a range of objects - convertible to $(D ElementType). - - The $(D stable) version guarantees that ranges iterating over - the container are never invalidated. Client code that counts on - non-invalidating insertion should use $(D stableInsert). - - Returns: The number of elements added. - - Complexity: $(BIGOH m * log(n)), where $(D m) is the number of - elements in $(D stuff) - */ - alias insert = insertBack; - ///ditto - alias stableInsert = insertBack; - - /** - Same as $(D insert(stuff)) and $(D stableInsert(stuff)) - respectively, but relax the complexity constraint to linear. - */ - alias linearInsert = insertBack; - ///ditto - alias stableLinearInsert = insertBack; - - /** - Picks one value in the container, removes it from the - container, and returns it. The stable version behaves the same, - but guarantees that ranges iterating over the container are - never invalidated. - - Precondition: $(D !empty) - - Returns: The element removed. - - Complexity: $(BIGOH log(n)) + * Removes the last element from the array and returns it. + * Both stable and non-stable versions behave the same and guarantee + * that ranges iterating over the array are never invalidated. + * + * Precondition: `empty == false` + * + * Returns: The element removed. + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ T removeAny() { @@ -1895,10 +2090,11 @@ if (is(Unqual!T == bool)) removeBack(); return result; } + /// ditto alias stableRemoveAny = removeAny; - unittest + @system unittest { Array!bool a; a.length = 1057; @@ -1911,17 +2107,17 @@ if (is(Unqual!T == bool)) } /** - Inserts $(D value) to the back of the container. $(D stuff) can - be a value convertible to $(D ElementType) or a range of - objects convertible to $(D ElementType). The stable version - behaves the same, but guarantees that ranges iterating over the - container are never invalidated. - - Returns: The number of elements inserted - - Complexity: $(BIGOH log(n)) + * Inserts the specified elements at the back of the array. `stuff` can be + * a value convertible to `bool` or a range of objects convertible to `bool`. + * + * Returns: The number of elements inserted. + * + * Complexity: + * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), + * where `m` is the number of elements in `stuff`. */ - size_t insertBack(Stuff)(Stuff stuff) if (is(Stuff : bool)) + size_t insertBack(Stuff)(Stuff stuff) + if (is(Stuff : bool)) { _store.refCountedStore.ensureInitialized(); auto rem = _store._length % bitsPerWord; @@ -1930,11 +2126,11 @@ if (is(Unqual!T == bool)) // Fits within the current array if (stuff) { - data[$ - 1] |= (1u << rem); + data[$ - 1] |= (cast(size_t) 1 << rem); } else { - data[$ - 1] &= ~(1u << rem); + data[$ - 1] &= ~(cast(size_t) 1 << rem); } } else @@ -1945,7 +2141,8 @@ if (is(Unqual!T == bool)) ++_store._length; return 1; } - /// Ditto + + /// ditto size_t insertBack(Stuff)(Stuff stuff) if (isInputRange!Stuff && is(ElementType!Stuff : bool)) { @@ -1958,20 +2155,41 @@ if (is(Unqual!T == bool)) static if (!hasLength!Stuff) return result; else return stuff.length; } + /// ditto alias stableInsertBack = insertBack; - /** - Removes the value at the front or back of the container. The - stable version behaves the same, but guarantees that ranges - iterating over the container are never invalidated. The - optional parameter $(D howMany) instructs removal of that many - elements. If $(D howMany > n), all elements are removed and no - exception is thrown. + /// ditto + alias insert = insertBack; - Precondition: $(D !empty) + /// ditto + alias stableInsert = insertBack; + + /// ditto + alias linearInsert = insertBack; + + /// ditto + alias stableLinearInsert = insertBack; + + @system unittest + { + Array!bool a; + for (int i = 0; i < 100; ++i) + a.insertBack(true); + foreach (e; a) + assert(e); + } - Complexity: $(BIGOH log(n)). + /** + * Removes the value from the back of the array. Both stable and non-stable + * versions behave the same and guarantee that ranges iterating over the + * array are never invalidated. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ void removeBack() { @@ -1988,23 +2206,22 @@ if (is(Unqual!T == bool)) _store._backend.length = _store._backend.length - 1; } } + /// ditto alias stableRemoveBack = removeBack; /** - Removes $(D howMany) values at the front or back of the - container. Unlike the unparameterized versions above, these - functions do not throw if they could not remove $(D howMany) - elements. Instead, if $(D howMany > n), all elements are - removed. The returned value is the effective number of elements - removed. The stable version behaves the same, but guarantees - that ranges iterating over the container are never invalidated. - - Returns: The number of elements removed - - Complexity: $(BIGOH howMany * log(n)). + * Removes `howMany` values from the back of the array. Unlike the + * unparameterized versions above, these functions do not throw if + * they could not remove `howMany` elements. Instead, if `howMany > n`, + * all elements are removed. The returned value is the effective number + * of elements removed. Both stable and non-stable versions behave the same + * and guarantee that ranges iterating over the array are never invalidated. + * + * Returns: The number of elements removed. + * + * Complexity: $(BIGOH howMany). */ - /// ditto size_t removeBack(size_t howMany) { if (howMany >= length) @@ -2019,7 +2236,10 @@ if (is(Unqual!T == bool)) return howMany; } - unittest + /// ditto + alias stableRemoveBack = removeBack; + + @system unittest { Array!bool a; a.length = 1057; @@ -2032,21 +2252,19 @@ if (is(Unqual!T == bool)) } /** - Inserts $(D stuff) before, after, or instead range $(D r), - which must be a valid range previously extracted from this - container. $(D stuff) can be a value convertible to $(D - ElementType) or a range of objects convertible to $(D - ElementType). The stable version behaves the same, but - guarantees that ranges iterating over the container are never - invalidated. - - Returns: The number of values inserted. - - Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + * Inserts `stuff` before, after, or instead range `r`, which must + * be a valid range previously extracted from this array. `stuff` + * can be a value convertible to `bool` or a range of objects convertible + * to `bool`. Both stable and non-stable version behave the same and + * guarantee that ranges iterating over the array are never invalidated. + * + * Returns: The number of values inserted. + * + * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. */ size_t insertBefore(Stuff)(Range r, Stuff stuff) { - import std.algorithm : bringToFront; + import std.algorithm.mutation : bringToFront; // TODO: make this faster, it moves one bit at a time immutable inserted = stableInsertBack(stuff); immutable tailLength = length - inserted; @@ -2055,10 +2273,11 @@ if (is(Unqual!T == bool)) this[tailLength .. length]); return inserted; } + /// ditto alias stableInsertBefore = insertBefore; - unittest + @system unittest { import std.conv : to; Array!bool a; @@ -2070,12 +2289,15 @@ if (is(Unqual!T == bool)) assert(a.length == 1, to!string(a.length)); a.insertBefore(a[], false); assert(a.length == 2, to!string(a.length)); + a.insertBefore(a[1 .. $], true); + import std.algorithm.comparison : equal; + assert(a[].equal([false, true, true])); } /// ditto size_t insertAfter(Stuff)(Range r, Stuff stuff) { - import std.algorithm : bringToFront; + import std.algorithm.mutation : bringToFront; // TODO: make this faster, it moves one bit at a time immutable inserted = stableInsertBack(stuff); immutable tailLength = length - inserted; @@ -2084,10 +2306,11 @@ if (is(Unqual!T == bool)) this[tailLength .. length]); return inserted; } + /// ditto alias stableInsertAfter = insertAfter; - unittest + @system unittest { import std.conv : to; Array!bool a; @@ -2096,8 +2319,10 @@ if (is(Unqual!T == bool)) assert(a.length == 11, to!string(a.length)); assert(a[5]); } + /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) if (is(Stuff : bool)) + size_t replace(Stuff)(Range r, Stuff stuff) + if (is(Stuff : bool)) { if (!r.empty) { @@ -2113,10 +2338,11 @@ if (is(Unqual!T == bool)) } return 1; } + /// ditto alias stableReplace = replace; - unittest + @system unittest { import std.conv : to; Array!bool a; @@ -2127,32 +2353,30 @@ if (is(Unqual!T == bool)) } /** - Removes all elements belonging to $(D r), which must be a range - obtained originally from this container. The stable version - behaves the same, but guarantees that ranges iterating over the - container are never invalidated. - - Returns: A range spanning the remaining elements in the container that - initially were right after $(D r). - - Complexity: $(BIGOH n) + * Removes all elements belonging to `r`, which must be a range + * obtained originally from this array. + * + * Returns: A range spanning the remaining elements in the array that + * initially were right after `r`. + * + * Complexity: $(BIGOH length) */ Range linearRemove(Range r) { - import std.algorithm : copy; + import std.algorithm.mutation : copy; copy(this[r._b .. length], this[r._a .. length]); length = length - r.length; return this[r._a .. length]; } } -unittest +@system unittest { Array!bool a; assert(a.empty); } -unittest +@system unittest { Array!bool arr; arr.insert([false, false, false, false]); @@ -2172,3 +2396,10 @@ unittest assert(slice.moveBack == true); assert(slice.moveAt(1) == true); } + +// issue 16331 - uncomparable values are valid values for an array +@system unittest +{ + double[] values = [double.nan, double.nan]; + auto arr = Array!double(values); +} diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index 70768f731db..4ce3953ab0d 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -1,22 +1,18 @@ /** -This module provides a $(D BinaryHeap) adaptor that makes a binary heap out of -any user-provided random-access range. +This module provides a $(D BinaryHeap) (aka priority queue) +adaptor that makes a binary heap out of any user-provided random-access range. -This module is a submodule of $(LINK2 std_container.html, std.container). +This module is a submodule of $(MREF std, container). Source: $(PHOBOSSRC std/container/_binaryheap.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. +Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB +(See accompanying file LICENSE_1_0.txt or copy at $(HTTP boost.org/LICENSE_1_0.txt)). -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) */ module std.container.binaryheap; @@ -25,9 +21,21 @@ import std.traits; public import std.container.util; +/// +@system unittest +{ + import std.algorithm.comparison : equal; + import std.range : take; + auto maxHeap = heapify([4, 7, 3, 1, 5]); + assert(maxHeap.take(3).equal([7, 5, 4])); + + auto minHeap = heapify!"a > b"([4, 7, 3, 1, 5]); + assert(minHeap.take(3).equal([1, 3, 4])); +} + // BinaryHeap /** -Implements a $(WEB en.wikipedia.org/wiki/Binary_heap, binary heap) +Implements a $(HTTP en.wikipedia.org/wiki/Binary_heap, binary heap) container on top of a given random-access range type (usually $(D T[])) or a random-access container type (usually $(D Array!T)). The documentation of $(D BinaryHeap) will refer to the underlying range or @@ -59,9 +67,18 @@ if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) { import std.functional : binaryFun; import std.exception : enforce; - import std.algorithm : move, min; + import std.algorithm.comparison : min; + import std.algorithm.mutation : move, swapAt; + import std.algorithm.sorting : HeapOps; import std.typecons : RefCounted, RefCountedAutoInitialize; + static if (isRandomAccessRange!Store) + alias Range = Store; + else + alias Range = typeof(Store.init[]); + alias percolate = HeapOps!(less, Range).percolate; + alias buildHeap = HeapOps!(less, Range).buildHeap; + // Really weird @@BUG@@: if you comment out the "private:" label below, // std.algorithm can't unittest anymore //private: @@ -103,62 +120,16 @@ if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) } } - // Assuming the element at index i perturbs the heap property in - // store r, percolates it down the heap such that the heap - // property is restored. - private void percolateDown(Store r, size_t i, size_t length) - { - for (;;) - { - auto left = i * 2 + 1, right = left + 1; - if (right == length) - { - if (comp(r[i], r[left])) swap(r, i, left); - return; - } - if (right > length) return; - assert(left < length && right < length); - auto largest = comp(r[i], r[left]) - ? (comp(r[left], r[right]) ? right : left) - : (comp(r[i], r[right]) ? right : i); - if (largest == i) return; - swap(r, i, largest); - i = largest; - } - } - // @@@BUG@@@: add private here, std.algorithm doesn't unittest anymore /*private*/ void pop(Store store) { assert(!store.empty, "Cannot pop an empty store."); if (store.length == 1) return; - auto t1 = moveFront(store[]); - auto t2 = moveBack(store[]); + auto t1 = store[].moveFront(); + auto t2 = store[].moveBack(); store.front = move(t2); store.back = move(t1); - percolateDown(store, 0, store.length - 1); - } - - /*private*/ static void swap(Store _store, size_t i, size_t j) - { - static if (is(typeof(swap(_store[i], _store[j])))) - { - swap(_store[i], _store[j]); - } - else static if (is(typeof(_store.moveAt(i)))) - { - auto t1 = _store.moveAt(i); - auto t2 = _store.moveAt(j); - _store[i] = move(t2); - _store[j] = move(t1); - } - else // assume it's a container and access its range with [] - { - auto t1 = _store[].moveAt(i); - auto t2 = _store[].moveAt(j); - _store[i] = move(t2); - _store[j] = move(t1); - } + percolate(store[], 0, store.length - 1); } public: @@ -186,11 +157,7 @@ the heap work incorrectly. _store = move(s); _length = min(_store.length, initialSize); if (_length < 2) return; - for (auto i = (_length - 2) / 2; ; ) - { - this.percolateDown(_store, i, _length); - if (i-- == 0) break; - } + buildHeap(_store[]); assertValid(); } @@ -208,7 +175,8 @@ heap. /** Clears the heap. Returns the portion of the store from $(D 0) up to -$(D length), which satisfies the $(LUCKY heap property). +$(D length), which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure), +heap property). */ auto release() { @@ -231,15 +199,18 @@ Returns $(D true) if the heap is _empty, $(D false) otherwise. } /** -Returns a duplicate of the heap. The underlying store must also -support a $(D dup) method. +Returns a duplicate of the heap. The $(D dup) method is available only if the +underlying store supports it. */ - @property BinaryHeap dup() + static if (is(typeof((Store s) { return s.dup; }(Store.init)) == Store)) { - BinaryHeap result; - if (!_payload.refCountedStore.isInitialized) return result; - result.assume(_store.dup, length); - return result; + @property BinaryHeap dup() + { + BinaryHeap result; + if (!_payload.refCountedStore.isInitialized) return result; + result.assume(_store.dup, length); + return result; + } } /** @@ -308,10 +279,19 @@ and $(D length == capacity), throws an exception. } else { - // can't grow - enforce(length < _store.length, - "Cannot grow a heap created over a range"); - _store[_length] = value; + import std.traits : isDynamicArray; + static if (isDynamicArray!Store) + { + if (length == _store.length) + _store.length = (length < 6 ? 8 : length * 3 / 2); + _store[_length] = value; + } + else + { + // can't grow + enforce(length < _store.length, + "Cannot grow a heap created over a range"); + } } // sink down the element @@ -320,7 +300,7 @@ and $(D length == capacity), throws an exception. auto parentIdx = (n - 1) / 2; if (!comp(_store[parentIdx], _store[n])) break; // done! // must swap and continue - swap(_store, parentIdx, n); + _store.swapAt(parentIdx, n); n = parentIdx; } ++_length; @@ -336,13 +316,13 @@ Removes the largest element from the heap. enforce(!empty, "Cannot call removeFront on an empty heap."); if (_length > 1) { - auto t1 = moveFront(_store[]); - auto t2 = moveAt(_store[], _length - 1); + auto t1 = _store[].moveFront(); + auto t2 = _store[].moveAt(_length - 1); _store.front = move(t2); _store[_length - 1] = move(t1); } --_length; - percolateDown(_store, 0, _length); + percolate(_store[], 0, _length); } /// ditto @@ -368,7 +348,7 @@ Replaces the largest element in the store with $(D value). // must replace the top assert(!empty, "Cannot call replaceFront on an empty heap."); _store.front = value; - percolateDown(_store, 0, _length); + percolate(_store[], 0, _length); debug(BinaryHeap) assertValid(); } @@ -388,20 +368,43 @@ must be collected. insert(value); return true; } - // must replace the top + assert(!_store.empty, "Cannot replace front of an empty heap."); if (!comp(value, _store.front)) return false; // value >= largest _store.front = value; - percolateDown(_store, 0, _length); + + percolate(_store[], 0, _length); debug(BinaryHeap) assertValid(); return true; } + +/** +Swapping is allowed if the heap is full. If $(D less(value, front)), the +method exchanges store.front and value and returns $(D true). Otherwise, it +leaves the heap unaffected and returns $(D false). + */ + bool conditionalSwap(ref ElementType!Store value) + { + _payload.refCountedStore.ensureInitialized(); + assert(_length == _store.length); + assert(!_store.empty, "Cannot swap front of an empty heap."); + + if (!comp(value, _store.front)) return false; // value >= largest + + import std.algorithm.mutation : swap; + swap(_store.front, value); + + percolate(_store[], 0, _length); + debug(BinaryHeap) assertValid(); + + return true; + } } /// Example from "Introduction to Algorithms" Cormen et al, p 146 -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; auto h = heapify(a); // largest element @@ -412,9 +415,9 @@ unittest /// $(D BinaryHeap) implements the standard input range interface, allowing /// lazy iteration of the underlying range in descending order. -unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : take; int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; auto top5 = heapify(a).take(5); @@ -428,12 +431,15 @@ initialized with $(D s) and $(D initialSize). BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, size_t initialSize = size_t.max) { + return BinaryHeap!(Store, less)(s, initialSize); } -unittest +/// +@system unittest { import std.conv : to; + import std.range.primitives; { // example from "Introduction to Algorithms" Cormen et al., p 146 int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; @@ -461,12 +467,129 @@ unittest } } -unittest +@system unittest { // Test range interface. - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; auto h = heapify(a); static assert(isInputRange!(typeof(h))); assert(h.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); } + +@system unittest // 15675 +{ + import std.container.array : Array; + + Array!int elements = [1, 2, 10, 12]; + auto heap = heapify(elements); + assert(heap.front == 12); +} + +@system unittest // 16072 +{ + auto q = heapify!"a > b"([2, 4, 5]); + q.insert(1); + q.insert(6); + assert(q.front == 1); + + // test more multiple grows + int[] arr; + auto r = heapify!"a < b"(arr); + foreach (i; 0 .. 100) + r.insert(i); + + assert(r.front == 99); +} + +@system unittest +{ + import std.algorithm.comparison : equal; + int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; + auto heap = heapify(a); + auto dup = heap.dup(); + assert(dup.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); +} + +@safe unittest +{ + static struct StructWithoutDup + { + int[] a; + @disable StructWithoutDup dup() + { + StructWithoutDup d; + return d; + } + alias a this; + } + + // Assert Binary heap can be created when Store doesn't have dup + // if dup is not used. + assert(__traits(compiles, () + { + auto s = StructWithoutDup([1,2]); + auto h = heapify(s); + })); + + // Assert dup can't be used on BinaryHeaps when Store doesn't have dup + assert(!__traits(compiles, () + { + auto s = StructWithoutDup([1,2]); + auto h = heapify(s); + h.dup(); + })); +} + +@safe unittest +{ + static struct StructWithDup + { + int[] a; + StructWithDup dup() + { + StructWithDup d; + return d; + } + alias a this; + } + + // Assert dup can be used on BinaryHeaps when Store has dup + assert(__traits(compiles, () + { + auto s = StructWithDup([1, 2]); + auto h = heapify(s); + h.dup(); + })); +} + +@system unittest +{ + import std.internal.test.dummyrange; + import std.algorithm.comparison : equal; + + alias RefRange = DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random); + + RefRange a; + RefRange b; + a.reinit(); + b.reinit(); + + auto heap = heapify(a); + foreach (ref elem; b) + { + heap.conditionalSwap(elem); + } + + assert(equal(heap, [ 5, 5, 4, 4, 3, 3, 2, 2, 1, 1])); + assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10])); +} + +@system unittest // Issue 17314 +{ + import std.algorithm.comparison : equal; + int[] a = [5]; + auto heap = heapify(a); + heap.insert(6); + assert(equal(heap, [6, 5])); +} diff --git a/std/container/dlist.d b/std/container/dlist.d index 017b3c2b680..d2e90782794 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -1,24 +1,56 @@ /** This module implements a generic doubly-linked list container. +It can be used as a queue, dequeue or stack. -This module is a submodule of $(LINK2 std_container.html, std.container). +This module is a submodule of $(MREF std, container). Source: $(PHOBOSSRC std/container/_dlist.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. +Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB +(See accompanying file LICENSE_1_0.txt or copy at $(HTTP boost.org/LICENSE_1_0.txt)). -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) + +$(SCRIPT inhibitQuickIndex = 1;) */ module std.container.dlist; +/// +@safe unittest +{ + import std.container : DList; + import std.algorithm.comparison : equal; + + auto s = DList!int(1, 2, 3); + assert(equal(s[], [1, 2, 3])); + + s.removeFront(); + assert(equal(s[], [2, 3])); + s.removeBack(); + assert(equal(s[], [2])); + + s.insertFront([4, 5]); + assert(equal(s[], [4, 5, 2])); + s.insertBack([6, 7]); + assert(equal(s[], [4, 5, 2, 6, 7])); + + // If you want to apply range operations, simply slice it. + import std.algorithm.searching : countUntil; + import std.range : popFrontN, popBackN, walkLength; + + auto sl = DList!int([1, 2, 3, 4, 5]); + assert(countUntil(sl[], 2) == 1); + + auto r = sl[]; + popFrontN(r, 2); + popBackN(r, 2); + assert(r.equal([3])); + assert(walkLength(r) == 1); +} + import std.range.primitives; import std.traits; @@ -60,7 +92,7 @@ The base DList Range. Contains Range primitives that don't depend on payload typ +/ private struct DRange { - unittest + @safe unittest { static assert(isBidirectionalRange!DRange); static assert(is(ElementType!DRange == BaseNode*)); @@ -163,7 +195,7 @@ struct DList(T) private { //Construct as new PayNode, and returns it as a BaseNode. - static BaseNode* createNode(Stuff)(ref Stuff arg, BaseNode* prev = null, BaseNode* next = null) + static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null) { return (new PayNode(BaseNode(prev, next), arg)).asBaseNode(); } @@ -379,14 +411,6 @@ Appends the contents of the argument $(D rhs) into $(D this). return this; } -/// ditto - deprecated("Please, use `dlist ~= dlist[];` instead.") - DList opOpAssign(string op)(DList rhs) - if (op == "~") - { - return this ~= rhs[]; - } - /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ /+ BEGIN INSERT FUNCTIONS HERE +/ /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ @@ -486,7 +510,7 @@ Complexity: $(BIGOH 1). */ T removeAny() { - import std.algorithm : move; + import std.algorithm.mutation : move; assert(!empty, "DList.removeAny: List is empty"); auto result = move(back); @@ -695,7 +719,7 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; //Tests construction signatures alias IntList = DList!int; @@ -714,7 +738,7 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; alias IntList = DList!int; IntList list = IntList([0,1,2,3]); @@ -731,13 +755,13 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : take; alias IntList = DList!int; IntList list = IntList([0,1,2,3]); auto range = list[]; - for( ; !range.empty; range.popFront()) + for ( ; !range.empty; range.popFront()) { int item = range.front; if (item == 2) @@ -750,7 +774,7 @@ private: list = IntList([0,1,2,3]); range = list[]; - for( ; !range.empty; range.popFront()) + for ( ; !range.empty; range.popFront()) { int item = range.front; if (item == 2) @@ -763,7 +787,7 @@ private: list = IntList([0,1,2,3]); range = list[]; - for( ; !range.empty; range.popFront()) + for ( ; !range.empty; range.popFront()) { int item = range.front; if (item == 0) @@ -776,7 +800,7 @@ private: list = IntList([0,1,2,3]); range = list[]; - for( ; !range.empty; range.popFront()) + for ( ; !range.empty; range.popFront()) { int item = range.front; if (item == 1) @@ -790,7 +814,7 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto dl = DList!string(["a", "b", "d"]); dl.insertAfter(dl[], "e"); // insert at the end @@ -803,7 +827,7 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto dl = DList!string(["a", "b", "d"]); dl.insertBefore(dl[], "e"); // insert at the front @@ -857,7 +881,7 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; //Verify all flavors of ~ auto a = DList!int(); @@ -893,7 +917,7 @@ private: @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; //8905 auto a = DList!int([1, 2, 3, 4]); @@ -936,3 +960,11 @@ private: DList!ITest().insertBack(new Test()); } + +@safe unittest //15263 +{ + import std.range : iota; + auto a = DList!int(); + a.insertFront(iota(0, 5)); // can insert range with non-ref front + assert(a.front == 0 && a.back == 4); +} diff --git a/std/container/package.d b/std/container/package.d index df9441ed918..fa0050555d3 100644 --- a/std/container/package.d +++ b/std/container/package.d @@ -6,7 +6,7 @@ This module defines generic containers. Construction: To implement the different containers both struct and class based -approaches have been used. $(XREF container_util, make) allows for +approaches have been used. $(REF make, std,_container,util) allows for uniform construction with either approach. --- @@ -48,7 +48,7 @@ assert(secondArray[0] == 12); // secondArray now refers to an independent copy of originalArray secondArray = originalArray.dup; secondArray[0] = 1; -// assert that originalArray has not been effected +// assert that originalArray has not been affected assert(originalArray[0] == 12); --- @@ -80,22 +80,21 @@ assert(array1.empty); // after initialization reference semantics work as expected array1 = array2; -// now effects array2 as well +// now affects array2 as well array1.removeBack(); assert(array2.empty); --- -It is therefore recommended to always construct containers using $(XREF container_util, make). +It is therefore recommended to always construct containers using +$(REF make, std,_container,util). This is in fact necessary to put containers into another _container. For example, to construct an $(D Array) of ten empty $(D Array)s, use the following that calls $(D make) ten times. --- -import std.range, std.container, std.algorithm; +import std.container, std.range; -Array!(Array!int) arrayOfArrays = make!(Array!(Array!int))( - repeat(0, 10).map!(x => make!(Array!int)) -); +auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10)); --- Submodules: @@ -104,29 +103,29 @@ This module consists of the following submodules: $(UL $(LI - The $(LINK2 std_container_array.html, std._container.array) module provides + The $(MREF std, _container, array) module provides an array type with deterministic control of memory, not reliant on the GC unlike built-in arrays. ) $(LI - The $(LINK2 std_container_binaryheap.html, std._container.binaryheap) module + The $(MREF std, _container, binaryheap) module provides a binary heap implementation that can be applied to any user-provided random-access range. ) $(LI - The $(LINK2 std_container_dlist.html, std._container.dlist) module provides + The $(MREF std, _container, dlist) module provides a doubly-linked list implementation. ) $(LI - The $(LINK2 std_container_rbtree.html, std._container.rbtree) module + The $(MREF std, _container, rbtree) module implements red-black trees. ) $(LI - The $(LINK2 std_container_slist.html, std._container.slist) module + The $(MREF std, _container, slist) module implements singly-linked lists. ) $(LI - The $(LINK2 std_container_util.html, std._container.util) module contains + The $(MREF std, _container, util) module contains some generic tools commonly used by _container implementations. ) ) @@ -136,11 +135,11 @@ The_primary_range_of_a_container: While some _containers offer direct access to their elements e.g. via $(D opIndex), $(D c.front) or $(D c.back), access and modification of a _container's contents is generally done through -its primary $(LINK2 std_range.html, range) type, +its primary $(MREF_ALTTEXT range, std, range) type, which is aliased as $(D C.Range). For example, the primary range type of $(D Array!int) is $(D Array!int.Range). -If the documentation of a member function of a _container takes a +If the documentation of a member function of a _container takes a parameter of type $(D Range), then it refers to the primary range type of this _container. Oftentimes $(D Take!Range) will be used, in which case the range refers to a span of the elements in the _container. Arguments to @@ -149,9 +148,10 @@ as the one being worked with. It is important to note that many generic range algorithms return the same range type as their input range. --- -import std.algorithm : equal, find; +import std.algorithm.comparison : equal; +import std.algorithm.iteration : find; import std.container; -import std.range : takeOne; +import std.range : take; auto array = make!Array(1, 2, 3); @@ -164,17 +164,17 @@ array = make!Array(1, 2, 3); // the range given to `linearRemove` is a Take!(Array!int.Range) // spanning just the element "2" -array.linearRemove(array[].find(2).takeOne()); +array.linearRemove(array[].find(2).take(1)); assert(array[].equal([1, 3])); --- -When any $(LINK2 std_range.html, range) can be passed as an argument to +When any $(MREF_ALTTEXT range, std, range) can be passed as an argument to a member function, the documention usually refers to the parameter's templated type as $(D Stuff). --- -import std.algorithm : equal; +import std.algorithm.comparison : equal; import std.container; import std.range : iota; @@ -196,14 +196,14 @@ independently of the _container implementation. For example the primitives $(D c.remove(r)) and $(D c.linearRemove(r)) both remove the sequence of elements in range $(D r) from the _container $(D c). -The primitive $(D c.remove(r)) guarantees $(BIGOH 1) complexity and -$(D c.linearRemove(r)) relaxes this guarantee to $(BIGOH n) (where $(D n) -is the length of the _container $(D c)). +The primitive $(D c.remove(r)) guarantees +$(BIGOH n$(SUBSCRIPT r) log n$(SUBSCRIPT c)) complexity in the worst case and +$(D c.linearRemove(r)) relaxes this guarantee to $(BIGOH n$(SUBSCRIPT c)). -Since a sequence of elements can be removed from a $(LINK2 std_container_dlist.html, doubly linked list) +Since a sequence of elements can be removed from a $(MREF_ALTTEXT doubly linked list,std,_container,dlist) in constant time, $(D DList) provides the primitive $(D c.remove(r)) -as well as $(D c.linearRemove(r)). On the other hand a -$(LINK2 std_container_array.html, Array) only offers $(D c.linearRemove(r)). +as well as $(D c.linearRemove(r)). On the other hand +$(MREF_ALTTEXT Array, std,_container, array) only offers $(D c.linearRemove(r)). The following table describes the common set of primitives that containers implement. A _container need not implement all primitives, but if a @@ -216,204 +216,299 @@ value $(D x), which could be a single element (in which case $(D n$(SUBSCRIPT x) $(D 1)), a _container, or a range. $(BOOKTABLE Container primitives, - -$(TR $(TH Syntax) $(TH $(BIGOH ·)) $(TH Description)) - -$(TR $(TDNW $(D C(x))) $(TDNW $(D n$(SUBSCRIPT x))) $(TD Creates a -_container of type $(D C) from either another _container or a range. The created _container must not be a null reference even if x is empty.)) - -$(TR $(TDNW $(D c.dup)) $(TDNW $(D n$(SUBSCRIPT c))) $(TD Returns a -duplicate of the _container.)) - -$(TR $(TDNW $(D c ~ x)) $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) $(TD -Returns the concatenation of $(D c) and $(D r). $(D x) may be a single -element or an input range.)) - -$(TR $(TDNW $(D x ~ c)) $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) $(TD -Returns the concatenation of $(D x) and $(D c). $(D x) may be a -single element or an input range type.)) - -$(LEADINGROW Iteration) - -$(TR $(TD $(D c.Range)) $(TD) $(TD The primary range -type associated with the _container.)) - -$(TR $(TD $(D c[])) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Returns a range -iterating over the entire _container, in a _container-defined order.)) - -$(TR $(TDNW $(D c[a .. b])) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Fetches a -portion of the _container from key $(D a) to key $(D b).)) - -$(LEADINGROW Capacity) - -$(TR $(TD $(D c.empty)) $(TD $(D 1)) $(TD Returns $(D true) if the -_container has no elements, $(D false) otherwise.)) - -$(TR $(TD $(D c.length)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Returns the -number of elements in the _container.)) - -$(TR $(TDNW $(D c.length = n)) $(TDNW $(D n$(SUBSCRIPT c) + n)) $(TD Forces -the number of elements in the _container to $(D n). If the _container -ends up growing, the added elements are initialized in a -_container-dependent manner (usually with $(D T.init)).)) - -$(TR $(TD $(D c.capacity)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Returns the -maximum number of elements that can be stored in the _container -without triggering a reallocation.)) - -$(TR $(TD $(D c.reserve(x))) $(TD $(D n$(SUBSCRIPT c))) $(TD Forces $(D -capacity) to at least $(D x) without reducing it.)) - -$(LEADINGROW Access) - -$(TR $(TDNW $(D c.front)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Returns the -first element of the _container, in a _container-defined order.)) - -$(TR $(TDNW $(D c.moveFront)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Destructively reads and returns the first element of the -_container. The slot is not removed from the _container; it is left -initialized with $(D T.init). This routine need not be defined if $(D -front) returns a $(D ref).)) - -$(TR $(TDNW $(D c.front = v)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Assigns -$(D v) to the first element of the _container.)) - -$(TR $(TDNW $(D c.back)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Returns the -last element of the _container, in a _container-defined order.)) - -$(TR $(TDNW $(D c.moveBack)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Destructively reads and returns the last element of the -_container. The slot is not removed from the _container; it is left -initialized with $(D T.init). This routine need not be defined if $(D -front) returns a $(D ref).)) - -$(TR $(TDNW $(D c.back = v)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Assigns -$(D v) to the last element of the _container.)) - -$(TR $(TDNW $(D c[x])) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Provides -indexed access into the _container. The index type is -_container-defined. A _container may define several index types (and -consequently overloaded indexing).)) - -$(TR $(TDNW $(D c.moveAt(x))) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Destructively reads and returns the value at position $(D x). The slot -is not removed from the _container; it is left initialized with $(D -T.init).)) - -$(TR $(TDNW $(D c[x] = v)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD Sets -element at specified index into the _container.)) - -$(TR $(TDNW $(D c[x] $(I op)= v)) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Performs read-modify-write operation at specified index into the -_container.)) - -$(LEADINGROW Operations) - -$(TR $(TDNW $(D e in c)) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Returns nonzero if e is found in $(D c).)) - -$(TR $(TDNW $(D c.lowerBound(v))) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Returns a range of all elements strictly less than $(D v).)) - -$(TR $(TDNW $(D c.upperBound(v))) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Returns a range of all elements strictly greater than $(D v).)) - -$(TR $(TDNW $(D c.equalRange(v))) $(TDNW $(D log n$(SUBSCRIPT c))) $(TD -Returns a range of all elements in $(D c) that are equal to $(D v).)) - -$(LEADINGROW Modifiers) - -$(TR $(TDNW $(D c ~= x)) $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) -$(TD Appends $(D x) to $(D c). $(D x) may be a single element or an -input range type.)) - -$(TR $(TDNW $(D c.clear())) $(TDNW $(D n$(SUBSCRIPT c))) $(TD Removes all -elements in $(D c).)) - -$(TR $(TDNW $(D c.insert(x))) $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c))) -$(TD Inserts $(D x) in $(D c) at a position (or positions) chosen by $(D c).)) - -$(TR $(TDNW $(D c.stableInsert(x))) -$(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c))) $(TD Same as $(D c.insert(x)), -but is guaranteed to not invalidate any ranges.)) - -$(TR $(TDNW $(D c.linearInsert(v))) $(TDNW $(D n$(SUBSCRIPT c))) $(TD Same -as $(D c.insert(v)) but relaxes complexity to linear.)) - -$(TR $(TDNW $(D c.stableLinearInsert(v))) $(TDNW $(D n$(SUBSCRIPT c))) -$(TD Same as $(D c.stableInsert(v)) but relaxes complexity to linear.)) - -$(TR $(TDNW $(D c.removeAny())) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Removes some element from $(D c) and returns it.)) - -$(TR $(TDNW $(D c.stableRemoveAny())) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Same as $(D c.removeAny()), but is guaranteed to not invalidate any -iterators.)) - -$(TR $(TDNW $(D c.insertFront(v))) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Inserts $(D v) at the front of $(D c).)) - -$(TR $(TDNW $(D c.stableInsertFront(v))) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Same as $(D c.insertFront(v)), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.insertBack(v))) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Inserts $(D v) at the back of $(D c).)) - -$(TR $(TDNW $(D c.stableInsertBack(v))) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Same as $(D c.insertBack(v)), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.removeFront())) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Removes the element at the front of $(D c).)) - -$(TR $(TDNW $(D c.stableRemoveFront())) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Same as $(D c.removeFront()), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.removeBack())) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Removes the value at the back of $(D c).)) - -$(TR $(TDNW $(D c.stableRemoveBack())) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Same as $(D c.removeBack()), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.remove(r))) $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c))) -$(TD Removes range $(D r) from $(D c).)) - -$(TR $(TDNW $(D c.stableRemove(r))) -$(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c))) -$(TD Same as $(D c.remove(r)), but guarantees iterators are not -invalidated.)) - -$(TR $(TDNW $(D c.linearRemove(r))) $(TDNW $(D n$(SUBSCRIPT c))) -$(TD Removes range $(D r) from $(D c).)) - -$(TR $(TDNW $(D c.stableLinearRemove(r))) $(TDNW $(D n$(SUBSCRIPT c))) -$(TD Same as $(D c.linearRemove(r)), but guarantees iterators are not -invalidated.)) - -$(TR $(TDNW $(D c.removeKey(k))) $(TDNW $(D log n$(SUBSCRIPT c))) -$(TD Removes an element from $(D c) by using its key $(D k). -The key's type is defined by the _container.)) - -$(TR $(TDNW $(D )) $(TDNW $(D )) $(TD )) - +$(TR + $(TH Syntax) + $(TH $(BIGOH ·)) + $(TH Description) +) +$(TR + $(TDNW $(D C(x))) + $(TDNW $(D n$(SUBSCRIPT x))) + $(TD Creates a _container of type $(D C) from either another _container or a range. + The created _container must not be a null reference even if x is empty.) +) +$(TR + $(TDNW $(D c.dup)) + $(TDNW $(D n$(SUBSCRIPT c))) + $(TD Returns a duplicate of the _container.) +) +$(TR + $(TDNW $(D c ~ x)) + $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) + $(TD Returns the concatenation of $(D c) and $(D r). $(D x) may be a single + element or an input range.) +) +$(TR + $(TDNW $(D x ~ c)) + $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) + $(TD Returns the concatenation of $(D x) and $(D c). $(D x) may be a + single element or an input range type.) +) +$(LEADINGROWN 3, Iteration +) +$(TR + $(TD $(D c.Range)) + $(TD) + $(TD The primary range type associated with the _container.) +) +$(TR + $(TD $(D c[])) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns a range + iterating over the entire _container, in a _container-defined order.) +) +$(TR + $(TDNW $(D c[a .. b])) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Fetches a portion of the _container from key $(D a) to key $(D b).) +) +$(LEADINGROWN 3, Capacity +) +$(TR + $(TD $(D c.empty)) + $(TD $(D 1)) + $(TD Returns $(D true) if the _container has no elements, $(D false) otherwise.) +) +$(TR + $(TD $(D c.length)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns the number of elements in the _container.) +) +$(TR + $(TDNW $(D c.length = n)) + $(TDNW $(D n$(SUBSCRIPT c) + n)) + $(TD Forces the number of elements in the _container to $(D n). + If the _container ends up growing, the added elements are initialized + in a _container-dependent manner (usually with $(D T.init)).) +) +$(TR + $(TD $(D c.capacity)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns the maximum number of elements that can be stored in the + _container without triggering a reallocation.) +) +$(TR + $(TD $(D c.reserve(x))) + $(TD $(D n$(SUBSCRIPT c))) + $(TD Forces $(D capacity) to at least $(D x) without reducing it.) +) +$(LEADINGROWN 3, Access +) +$(TR + $(TDNW $(D c.front)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns the first element of the _container, in a _container-defined order.) +) +$(TR + $(TDNW $(D c.moveFront)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Destructively reads and returns the first element of the + _container. The slot is not removed from the _container; it is left + initialized with $(D T.init). This routine need not be defined if $(D + front) returns a $(D ref).) +) +$(TR + $(TDNW $(D c.front = v)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Assigns $(D v) to the first element of the _container.) +) +$(TR + $(TDNW $(D c.back)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns the last element of the _container, in a _container-defined order.) +) +$(TR + $(TDNW $(D c.moveBack)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Destructively reads and returns the last element of the + _container. The slot is not removed from the _container; it is left + initialized with $(D T.init). This routine need not be defined if $(D + front) returns a $(D ref).) +) +$(TR + $(TDNW $(D c.back = v)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Assigns $(D v) to the last element of the _container.) +) +$(TR + $(TDNW $(D c[x])) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Provides indexed access into the _container. The index type is + _container-defined. A _container may define several index types (and + consequently overloaded indexing).) +) +$(TR + $(TDNW $(D c.moveAt(x))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Destructively reads and returns the value at position $(D x). The slot + is not removed from the _container; it is left initialized with $(D + T.init).) +) +$(TR + $(TDNW $(D c[x] = v)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Sets element at specified index into the _container.) +) +$(TR + $(TDNW $(D c[x] $(I op)= v)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Performs read-modify-write operation at specified index into the + _container.) +) +$(LEADINGROWN 3, Operations +) +$(TR + $(TDNW $(D e in c)) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns nonzero if e is found in $(D c).) +) +$(TR + $(TDNW $(D c.lowerBound(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns a range of all elements strictly less than $(D v).) +) +$(TR + $(TDNW $(D c.upperBound(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns a range of all elements strictly greater than $(D v).) +) +$(TR + $(TDNW $(D c.equalRange(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Returns a range of all elements in $(D c) that are equal to $(D v).) +) +$(LEADINGROWN 3, Modifiers +) +$(TR + $(TDNW $(D c ~= x)) + $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x))) + $(TD Appends $(D x) to $(D c). $(D x) may be a single element or an input range type.) +) +$(TR + $(TDNW $(D c.clear())) + $(TDNW $(D n$(SUBSCRIPT c))) + $(TD Removes all elements in $(D c).) +) +$(TR + $(TDNW $(D c.insert(x))) + $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c))) + $(TD Inserts $(D x) in $(D c) at a position (or positions) chosen by $(D c).) +) +$(TR + $(TDNW $(D c.stableInsert(x))) + $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c))) + $(TD Same as $(D c.insert(x)), but is guaranteed to not invalidate any ranges.) +) +$(TR + $(TDNW $(D c.linearInsert(v))) + $(TDNW $(D n$(SUBSCRIPT c))) + $(TD Same as $(D c.insert(v)) but relaxes complexity to linear.) +) +$(TR + $(TDNW $(D c.stableLinearInsert(v))) + $(TDNW $(D n$(SUBSCRIPT c))) + $(TD Same as $(D c.stableInsert(v)) but relaxes complexity to linear.) +) +$(TR + $(TDNW $(D c.removeAny())) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Removes some element from $(D c) and returns it.) +) +$(TR + $(TDNW $(D c.stableRemoveAny())) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Same as $(D c.removeAny()), but is guaranteed to not invalidate any + iterators.) +) +$(TR + $(TDNW $(D c.insertFront(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Inserts $(D v) at the front of $(D c).) +) +$(TR + $(TDNW $(D c.stableInsertFront(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Same as $(D c.insertFront(v)), but guarantees no ranges will be + invalidated.) +) +$(TR + $(TDNW $(D c.insertBack(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Inserts $(D v) at the back of $(D c).) +) +$(TR + $(TDNW $(D c.stableInsertBack(v))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Same as $(D c.insertBack(v)), but guarantees no ranges will be + invalidated.) +) +$(TR + $(TDNW $(D c.removeFront())) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Removes the element at the front of $(D c).) +) +$(TR + $(TDNW $(D c.stableRemoveFront())) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Same as $(D c.removeFront()), but guarantees no ranges will be + invalidated.) +) +$(TR + $(TDNW $(D c.removeBack())) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Removes the value at the back of $(D c).) +) +$(TR + $(TDNW $(D c.stableRemoveBack())) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Same as $(D c.removeBack()), but guarantees no ranges will be + invalidated.) +) +$(TR + $(TDNW $(D c.remove(r))) + $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c))) + $(TD Removes range $(D r) from $(D c).) +) +$(TR + $(TDNW $(D c.stableRemove(r))) + $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c))) + $(TD Same as $(D c.remove(r)), but guarantees iterators are not + invalidated.) +) +$(TR + $(TDNW $(D c.linearRemove(r))) + $(TDNW $(D n$(SUBSCRIPT c))) + $(TD Removes range $(D r) from $(D c).) +) +$(TR + $(TDNW $(D c.stableLinearRemove(r))) + $(TDNW $(D n$(SUBSCRIPT c))) + $(TD Same as $(D c.linearRemove(r)), but guarantees iterators are not + invalidated.) +) +$(TR + $(TDNW $(D c.removeKey(k))) + $(TDNW $(D log n$(SUBSCRIPT c))) + $(TD Removes an element from $(D c) by using its key $(D k). + The key's type is defined by the _container.) +) +$(TR + $(TDNW $(D )) + $(TDNW $(D )) + $(TD ) +) ) Source: $(PHOBOSSRC std/_container/package.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB +(See accompanying file LICENSE_1_0.txt or copy at $(HTTP boost.org/LICENSE_1_0.txt)). -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) +Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu) */ module std.container; @@ -424,7 +519,7 @@ public import std.container.dlist; public import std.container.rbtree; public import std.container.slist; -import std.typetuple; +import std.meta; /* The following documentation and type $(D TotalContainer) are @@ -469,7 +564,7 @@ the type of the $(D k)th key of the container. A container may define both $(D KeyType) and $(D KeyTypes), e.g. in the case it has the notion of primary/preferred key. */ - alias KeyTypes = TypeTuple!T; + alias KeyTypes = AliasSeq!T; /** If the container has a notion of key-value mapping, $(D ValueType) @@ -481,7 +576,7 @@ defines $(D KeyType) to be $(D K) and $(D ValueType) to be $(D V). /** Defines the container's primary range, which embodies one of the -ranges defined in $(XREFMODULE range). +ranges defined in $(MREF std,range). Generally a container may define several types of ranges. */ @@ -1055,6 +1150,7 @@ Complexity: $(BIGOH n) } } -unittest { +@safe unittest +{ TotalContainer!int test; } diff --git a/std/container/rbtree.d b/std/container/rbtree.d index 07d9d3def19..524282e1764 100644 --- a/std/container/rbtree.d +++ b/std/container/rbtree.d @@ -1,26 +1,63 @@ /** This module implements a red-black tree container. -This module is a submodule of $(LINK2 std_container.html, std.container). +This module is a submodule of $(MREF std, container). Source: $(PHOBOSSRC std/container/_rbtree.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB +(See accompanying file LICENSE_1_0.txt or copy at $(HTTP boost.org/LICENSE_1_0.txt)). -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) +Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu) */ module std.container.rbtree; -// FIXME -import std.functional; // : binaryFun; +/// +@safe pure unittest +{ + import std.container.rbtree; + import std.algorithm.comparison : equal; + + auto rbt = redBlackTree(3, 1, 4, 2, 5); + assert(rbt.front == 1); + assert(equal(rbt[], [1, 2, 3, 4, 5])); + + rbt.removeKey(1, 4); + assert(equal(rbt[], [2, 3, 5])); + + rbt.removeFront(); + assert(equal(rbt[], [3, 5])); + + rbt.insert([1, 2, 4]); + assert(equal(rbt[], [1, 2, 3, 4, 5])); + + // Query bounds in O(log(n)) + assert(rbt.lowerBound(3).equal([1, 2])); + assert(rbt.equalRange(3).equal([3])); + assert(rbt.upperBound(3).equal([4, 5])); + + // A Red Black tree with the highest element at front: + import std.range : iota; + auto maxTree = redBlackTree!"a > b"(iota(5)); + assert(equal(maxTree[], [4, 3, 2, 1, 0])); + + // adding duplicates will not add them, but return 0 + auto rbt2 = redBlackTree(1, 3); + assert(rbt2.insert(1) == 0); + assert(equal(rbt2[], [1, 3])); + assert(rbt2.insert(2) == 1); + + // however you can allow duplicates + auto ubt = redBlackTree!true([0, 1, 0, 1]); + assert(equal(ubt[], [0, 0, 1, 1])); +} + +import std.functional : binaryFun; +import std.format; public import std.container.util; @@ -74,7 +111,7 @@ struct RBNode(V) /** * Get the left child */ - @property Node left() + @property inout(RBNode)* left() inout { return _left; } @@ -82,7 +119,7 @@ struct RBNode(V) /** * Get the right child */ - @property Node right() + @property inout(RBNode)* right() inout { return _right; } @@ -90,7 +127,7 @@ struct RBNode(V) /** * Get the parent */ - @property Node parent() + @property inout(RBNode)* parent() inout { return _parent; } @@ -104,7 +141,7 @@ struct RBNode(V) @property Node left(Node newNode) { _left = newNode; - if(newNode !is null) + if (newNode !is null) newNode._parent = &this; return newNode; } @@ -118,7 +155,7 @@ struct RBNode(V) @property Node right(Node newNode) { _right = newNode; - if(newNode !is null) + if (newNode !is null) newNode._parent = &this; return newNode; } @@ -151,7 +188,7 @@ struct RBNode(V) body { // sets _left._parent also - if(isLeftNode) + if (isLeftNode) parent.left = _left; else parent.right = _left; @@ -194,7 +231,7 @@ struct RBNode(V) body { // sets _right._parent also - if(isLeftNode) + if (isLeftNode) parent.left = _right; else parent.right = _right; @@ -236,24 +273,24 @@ struct RBNode(V) void setColor(Node end) { // test against the marker node - if(_parent !is end) + if (_parent !is end) { - if(_parent.color == Color.Red) + if (_parent.color == Color.Red) { Node cur = &this; - while(true) + while (true) { // because root is always black, _parent._parent always exists - if(cur._parent.isLeftNode) + if (cur._parent.isLeftNode) { // parent is left node, y is 'uncle', could be null Node y = cur._parent._parent._right; - if(y !is null && y.color == Color.Red) + if (y !is null && y.color == Color.Red) { cur._parent.color = Color.Black; y.color = Color.Black; cur = cur._parent._parent; - if(cur._parent is end) + if (cur._parent is end) { // root node cur.color = Color.Black; @@ -263,14 +300,14 @@ struct RBNode(V) { // not root node cur.color = Color.Red; - if(cur._parent.color == Color.Black) + if (cur._parent.color == Color.Black) // satisfied, exit the loop break; } } else { - if(!cur.isLeftNode) + if (!cur.isLeftNode) cur = cur._parent.rotateL(); cur._parent.color = Color.Black; cur = cur._parent._parent.rotateR(); @@ -283,12 +320,12 @@ struct RBNode(V) { // parent is right node, y is 'uncle' Node y = cur._parent._parent._left; - if(y !is null && y.color == Color.Red) + if (y !is null && y.color == Color.Red) { cur._parent.color = Color.Black; y.color = Color.Black; cur = cur._parent._parent; - if(cur._parent is end) + if (cur._parent is end) { // root node cur.color = Color.Black; @@ -298,14 +335,14 @@ struct RBNode(V) { // not root node cur.color = Color.Red; - if(cur._parent.color == Color.Black) + if (cur._parent.color == Color.Black) // satisfied, exit the loop break; } } else { - if(cur.isLeftNode) + if (cur.isLeftNode) cur = cur._parent.rotateR(); cur._parent.color = Color.Black; cur = cur._parent._parent.rotateL(); @@ -363,7 +400,7 @@ struct RBNode(V) // // replace y's structure with structure of this node. // - if(isLeftNode) + if (isLeftNode) _parent.left = y; else _parent.right = y; @@ -371,7 +408,7 @@ struct RBNode(V) // need special case so y doesn't point back to itself // y.left = _left; - if(_right is y) + if (_right is y) y.right = &this; else y.right = _right; @@ -382,9 +419,9 @@ struct RBNode(V) // left = yl; right = yr; - if(_parent !is y) + if (_parent !is y) { - if(isyleft) + if (isyleft) yp.left = &this; else yp.right = &this; @@ -393,34 +430,34 @@ struct RBNode(V) } // if this has less than 2 children, remove it - if(_left !is null) + if (_left !is null) x = _left; else x = _right; bool deferedUnlink = false; - if(x is null) + if (x is null) { // pretend this is a null node, defer unlinking the node x = &this; deferedUnlink = true; } - else if(isLeftNode) + else if (isLeftNode) _parent.left = x; else _parent.right = x; // if the color of this is black, then it needs to be fixed - if(color == color.Black) + if (color == color.Black) { // need to recolor the tree. - while(x._parent !is end && x.color == Node.Color.Black) + while (x._parent !is end && x.color == Node.Color.Black) { - if(x.isLeftNode) + if (x.isLeftNode) { // left node Node w = x._parent._right; - if(w.color == Node.Color.Red) + if (w.color == Node.Color.Red) { w.color = Node.Color.Black; x._parent.color = Node.Color.Red; @@ -429,7 +466,7 @@ struct RBNode(V) } Node wl = w.left; Node wr = w.right; - if((wl is null || wl.color == Node.Color.Black) && + if ((wl is null || wl.color == Node.Color.Black) && (wr is null || wr.color == Node.Color.Black)) { w.color = Node.Color.Red; @@ -437,7 +474,7 @@ struct RBNode(V) } else { - if(wr is null || wr.color == Node.Color.Black) + if (wr is null || wr.color == Node.Color.Black) { // wl cannot be null here wl.color = Node.Color.Black; @@ -457,7 +494,7 @@ struct RBNode(V) { // right node Node w = x._parent._left; - if(w.color == Node.Color.Red) + if (w.color == Node.Color.Red) { w.color = Node.Color.Black; x._parent.color = Node.Color.Red; @@ -466,7 +503,7 @@ struct RBNode(V) } Node wl = w.left; Node wr = w.right; - if((wl is null || wl.color == Node.Color.Black) && + if ((wl is null || wl.color == Node.Color.Black) && (wr is null || wr.color == Node.Color.Black)) { w.color = Node.Color.Red; @@ -474,7 +511,7 @@ struct RBNode(V) } else { - if(wl is null || wl.color == Node.Color.Black) + if (wl is null || wl.color == Node.Color.Black) { // wr cannot be null here wr.color = Node.Color.Black; @@ -494,12 +531,12 @@ struct RBNode(V) x.color = Node.Color.Black; } - if(deferedUnlink) + if (deferedUnlink) { // // unlink this node from the tree // - if(isLeftNode) + if (isLeftNode) _parent.left = null; else _parent.right = null; @@ -514,10 +551,10 @@ struct RBNode(V) /** * Return the leftmost descendant of this node. */ - @property Node leftmost() + @property inout(RBNode)* leftmost() inout { - Node result = &this; - while(result._left !is null) + inout(RBNode)* result = &this; + while (result._left !is null) result = result._left; return result; } @@ -525,10 +562,10 @@ struct RBNode(V) /** * Return the rightmost descendant of this node */ - @property Node rightmost() + @property inout(RBNode)* rightmost() inout { - Node result = &this; - while(result._right !is null) + inout(RBNode)* result = &this; + while (result._right !is null) result = result._right; return result; } @@ -539,12 +576,12 @@ struct RBNode(V) * You should never call this on the marker node, as it is assumed that * there is a valid next node. */ - @property Node next() + @property inout(RBNode)* next() inout { - Node n = &this; - if(n.right is null) + inout(RBNode)* n = &this; + if (n.right is null) { - while(!n.isLeftNode) + while (!n.isLeftNode) n = n._parent; return n._parent; } @@ -558,12 +595,12 @@ struct RBNode(V) * You should never call this on the leftmost node of the tree as it is * assumed that there is a valid previous node. */ - @property Node prev() + @property inout(RBNode)* prev() inout { - Node n = &this; - if(n.left is null) + inout(RBNode)* n = &this; + if (n.left is null) { - while(n.isLeftNode) + while (n.isLeftNode) n = n._parent; return n._parent; } @@ -581,9 +618,9 @@ struct RBNode(V) // Node copy = alloc(value); copy.color = color; - if(_left !is null) + if (_left !is null) copy.left = _left.dup(alloc); - if(_right !is null) + if (_right !is null) copy.right = _right.dup(alloc); return copy; } @@ -593,22 +630,100 @@ struct RBNode(V) Node copy = new RBNode!V; copy.value = value; copy.color = color; - if(_left !is null) + if (_left !is null) copy.left = _left.dup(); - if(_right !is null) + if (_right !is null) copy.right = _right.dup(); return copy; } } +//constness checks +@safe pure unittest +{ + const RBNode!int n; + static assert(is(typeof(n.leftmost))); + static assert(is(typeof(n.rightmost))); + static assert(is(typeof(n.next))); + static assert(is(typeof(n.prev))); +} + +private struct RBRange(N) +{ + alias Node = N; + alias Elem = typeof(Node.value); + + private Node _begin; + private Node _end; + + private this(Node b, Node e) + { + _begin = b; + _end = e; + } + + /** + * Returns $(D true) if the range is _empty + */ + @property bool empty() const + { + return _begin is _end; + } + + /** + * Returns the first element in the range + */ + @property Elem front() + { + return _begin.value; + } + + /** + * Returns the last element in the range + */ + @property Elem back() + { + return _end.prev.value; + } + + /** + * pop the front element from the range + * + * complexity: amortized $(BIGOH 1) + */ + void popFront() + { + _begin = _begin.next; + } + + /** + * pop the back element from the range + * + * complexity: amortized $(BIGOH 1) + */ + void popBack() + { + _end = _end.prev; + } + + /** + * Trivial _save implementation, needed for $(D isForwardRange). + */ + @property RBRange save() + { + return this; + } +} + /** - * Implementation of a $(LUCKY red-black tree) container. + * Implementation of a $(LINK2 https://en.wikipedia.org/wiki/Red%E2%80%93black_tree, + * red-black tree) container. * * All inserts, removes, searches, and any function in general has complexity * of $(BIGOH lg(n)). * * To use a different comparison than $(D "a < b"), pass a different operator string - * that can be used by $(XREF functional, binaryFun), or pass in a + * that can be used by $(REF binaryFun, std,functional), or pass in a * function, delegate, functor, or any type where $(D less(a, b)) results in a $(D bool) * value. * @@ -622,101 +737,18 @@ struct RBNode(V) * inserted after all existing duplicate elements. */ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) - if(is(typeof(binaryFun!less(T.init, T.init)))) +if (is(typeof(binaryFun!less(T.init, T.init)))) { - import std.range.primitives; + import std.meta : allSatisfy; + import std.range.primitives : isInputRange, walkLength; import std.range : Take; - import std.typetuple : allSatisfy; - import std.traits; + import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible; alias _less = binaryFun!less; - // BUG: this must come first in the struct due to issue 2810 - - // add an element to the tree, returns the node added, or the existing node - // if it has already been added and allowDuplicates is false - - private auto _add(Elem n) - { - Node result; - static if(!allowDuplicates) - bool added = true; - - if(!_end.left) - { - _end.left = _begin = result = allocate(n); - } - else - { - Node newParent = _end.left; - Node nxt = void; - while(true) - { - if(_less(n, newParent.value)) - { - nxt = newParent.left; - if(nxt is null) - { - // - // add to right of new parent - // - newParent.left = result = allocate(n); - break; - } - } - else - { - static if(!allowDuplicates) - { - if(!_less(newParent.value, n)) - { - result = newParent; - added = false; - break; - } - } - nxt = newParent.right; - if(nxt is null) - { - // - // add to right of new parent - // - newParent.right = result = allocate(n); - break; - } - } - newParent = nxt; - } - if(_begin.left) - _begin = _begin.left; - } - - static if(allowDuplicates) - { - result.setColor(_end); - debug(RBDoChecks) - check(); - ++_length; - return result; - } - else - { - import std.typecons : Tuple; - - if(added) - { - ++_length; - result.setColor(_end); - } - debug(RBDoChecks) - check(); - return Tuple!(bool, "added", Node, "n")(added, result); - } - } - version(unittest) { - static if(is(typeof(less) == string)) + static if (is(typeof(less) == string)) { private enum doUnittest = isIntegral!T && (less == "a < b" || less == "a > b"); } @@ -724,13 +756,13 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) enum doUnittest = false; // note, this must be final so it does not affect the vtable layout - final bool arrayEqual(T[] arr) + bool arrayEqual(T[] arr) { - if(walkLength(this[]) == arr.length) + if (walkLength(this[]) == arr.length) { - foreach(v; arr) + foreach (v; arr) { - if(!(v in this)) + if (!(v in this)) return false; } return true; @@ -749,7 +781,8 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) alias Elem = T; // used for convenience - private alias Node = RBNode!Elem.Node; + private alias RBNode = .RBNode!Elem; + private alias Node = RBNode.Node; private Node _end; private Node _begin; @@ -763,7 +796,7 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) static private Node allocate() { - return new RBNode!Elem; + return new RBNode; } static private Node allocate(Elem v) @@ -774,81 +807,21 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) } /** - * The range type for $(D RedBlackTree) + * The range types for $(D RedBlackTree) */ - struct Range - { - private Node _begin; - private Node _end; - - private this(Node b, Node e) - { - _begin = b; - _end = e; - } - - /** - * Returns $(D true) if the range is _empty - */ - @property bool empty() const - { - return _begin is _end; - } - - /** - * Returns the first element in the range - */ - @property Elem front() - { - return _begin.value; - } + alias Range = RBRange!(RBNode*); + alias ConstRange = RBRange!(const(RBNode)*); /// Ditto + alias ImmutableRange = RBRange!(immutable(RBNode)*); /// Ditto - /** - * Returns the last element in the range - */ - @property Elem back() - { - return _end.prev.value; - } - - /** - * pop the front element from the range - * - * complexity: amortized $(BIGOH 1) - */ - void popFront() - { - _begin = _begin.next; - } - - /** - * pop the back element from the range - * - * complexity: amortized $(BIGOH 1) - */ - void popBack() - { - _end = _end.prev; - } - - /** - * Trivial _save implementation, needed for $(D isForwardRange). - */ - @property Range save() - { - return this; - } - } - - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range.primitives; auto ts = new RedBlackTree(1, 2, 3, 4, 5); assert(ts.length == 5); auto r = ts[]; - static if(less == "a < b") + static if (less == "a < b") auto vals = [1, 2, 3, 4, 5]; else auto vals = [5, 4, 3, 2, 1]; @@ -867,17 +840,17 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) } // find a node based on an element value - private Node _find(Elem e) + private inout(RBNode)* _find(Elem e) inout { - static if(allowDuplicates) + static if (allowDuplicates) { - Node cur = _end.left; - Node result = null; - while(cur) + inout(RBNode)* cur = _end.left; + inout(RBNode)* result = null; + while (cur) { - if(_less(cur.value, e)) + if (_less(cur.value, e)) cur = cur.right; - else if(_less(e, cur.value)) + else if (_less(e, cur.value)) cur = cur.left; else { @@ -890,12 +863,12 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) } else { - Node cur = _end.left; - while(cur) + inout(RBNode)* cur = _end.left; + while (cur) { - if(_less(cur.value, e)) + if (_less(cur.value, e)) cur = cur.right; - else if(_less(e, cur.value)) + else if (_less(e, cur.value)) cur = cur.left; else return cur; @@ -904,6 +877,87 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) } } + // add an element to the tree, returns the node added, or the existing node + // if it has already been added and allowDuplicates is false + private auto _add(Elem n) + { + Node result; + static if (!allowDuplicates) + bool added = true; + + if (!_end.left) + { + _end.left = _begin = result = allocate(n); + } + else + { + Node newParent = _end.left; + Node nxt; + while (true) + { + if (_less(n, newParent.value)) + { + nxt = newParent.left; + if (nxt is null) + { + // + // add to right of new parent + // + newParent.left = result = allocate(n); + break; + } + } + else + { + static if (!allowDuplicates) + { + if (!_less(newParent.value, n)) + { + result = newParent; + added = false; + break; + } + } + nxt = newParent.right; + if (nxt is null) + { + // + // add to right of new parent + // + newParent.right = result = allocate(n); + break; + } + } + newParent = nxt; + } + if (_begin.left) + _begin = _begin.left; + } + + static if (allowDuplicates) + { + result.setColor(_end); + debug(RBDoChecks) + check(); + ++_length; + return result; + } + else + { + import std.typecons : Tuple; + + if (added) + { + ++_length; + result.setColor(_end); + } + debug(RBDoChecks) + check(); + return Tuple!(bool, "added", Node, "n")(added, result); + } + } + + /** * Check if any elements exist in the container. Returns $(D false) if at least * one element exists. @@ -934,15 +988,15 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) return new RedBlackTree(_end.dup(), _length); } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto ts = new RedBlackTree(1, 2, 3, 4, 5); assert(ts.length == 5); auto ts2 = ts.dup; assert(ts2.length == 5); assert(equal(ts[], ts2[])); - ts2.insert(cast(Elem)6); + ts2.insert(cast(Elem) 6); assert(!equal(ts[], ts2[])); assert(ts.length == 5 && ts2.length == 6); } @@ -957,6 +1011,18 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) return Range(_begin, _end); } + /// Ditto + ConstRange opSlice() const + { + return ConstRange(_begin, _end); + } + + /// Ditto + ImmutableRange opSlice() immutable + { + return ImmutableRange(_begin, _end); + } + /** * The front element in the container * @@ -983,40 +1049,40 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) Complexity: $(BIGOH log(n)) +/ - bool opBinaryRight(string op)(Elem e) if (op == "in") + bool opBinaryRight(string op)(Elem e) const if (op == "in") { return _find(e) !is null; } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(cast(Elem)3 in ts); - assert(cast(Elem)6 !in ts); + assert(cast(Elem) 3 in ts); + assert(cast(Elem) 6 !in ts); } /** * Compares two trees for equality. * - * Complexity: $(BIGOH n*log(n)) + * Complexity: $(BIGOH n) */ override bool opEquals(Object rhs) { - import std.algorithm : equal; - RedBlackTree that = cast(RedBlackTree)rhs; + import std.algorithm.comparison : equal; + + RedBlackTree that = cast(RedBlackTree) rhs; if (that is null) return false; // If there aren't the same number of nodes, we can't be equal. if (this._length != that._length) return false; - // FIXME: use a more efficient algo (if one exists?) auto thisRange = this[]; auto thatRange = that[]; return equal!(function(Elem a, Elem b) => !_less(a,b) && !_less(b,a)) (thisRange, thatRange); } - static if(doUnittest) unittest + static if (doUnittest) @system unittest { auto t1 = new RedBlackTree(1,2,3,4); auto t2 = new RedBlackTree(1,2,3,4); @@ -1024,10 +1090,11 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) auto t4 = new RedBlackTree(1,2,3,4,5); auto o = new Object(); - assert(t1==t2); - assert(t1!=t3); - assert(t1!=t4); - assert(t1!=o); // pathological case, must not crash + assert(t1 == t1); + assert(t1 == t2); + assert(t1 != t3); + assert(t1 != t4); + assert(t1 != o); // pathological case, must not crash } /** @@ -1042,7 +1109,7 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) _length = 0; } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { auto ts = new RedBlackTree(1,2,3,4,5); assert(ts.length == 5); @@ -1054,11 +1121,13 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) * Insert a single element in the container. Note that this does not * invalidate any ranges currently iterating the container. * + * Returns: The number of elements inserted. + * * Complexity: $(BIGOH log(n)) */ size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem)) { - static if(allowDuplicates) + static if (allowDuplicates) { _add(stuff); return 1; @@ -1073,14 +1142,16 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) * Insert a range of elements in the container. Note that this does not * invalidate any ranges currently iterating the container. * + * Returns: The number of elements inserted. + * * Complexity: $(BIGOH m * log(n)) */ - size_t stableInsert(Stuff)(Stuff stuff) if(isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) + size_t stableInsert(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) { size_t result = 0; - static if(allowDuplicates) + static if (allowDuplicates) { - foreach(e; stuff) + foreach (e; stuff) { ++result; _add(e); @@ -1088,9 +1159,9 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) } else { - foreach(e; stuff) + foreach (e; stuff) { - if(_add(e).added) + if (_add(e).added) ++result; } } @@ -1100,18 +1171,18 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) /// ditto alias insert = stableInsert; - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { auto ts = new RedBlackTree(2,1,3,4,5,2,5); - static if(allowDuplicates) + static if (allowDuplicates) { assert(ts.length == 7); assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 6); assert(ts.length == 13); - assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 14); - assert(ts.stableInsert(cast(Elem)7) == 1 && ts.length == 15); + assert(ts.stableInsert(cast(Elem) 11) == 1 && ts.length == 14); + assert(ts.stableInsert(cast(Elem) 7) == 1 && ts.length == 15); - static if(less == "a < b") + static if (less == "a < b") assert(ts.arrayEqual([1,2,2,3,4,5,5,6,7,7,8,8,9,10,11])); else assert(ts.arrayEqual([11,10,9,8,8,7,7,6,5,5,4,3,2,2,1])); @@ -1121,10 +1192,10 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) assert(ts.length == 5); assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 5); assert(ts.length == 10); - assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 11); - assert(ts.stableInsert(cast(Elem)7) == 0 && ts.length == 11); + assert(ts.stableInsert(cast(Elem) 11) == 1 && ts.length == 11); + assert(ts.stableInsert(cast(Elem) 7) == 0 && ts.length == 11); - static if(less == "a < b") + static if (less == "a < b") assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11])); else assert(ts.arrayEqual([11,10,9,8,7,6,5,4,3,2,1])); @@ -1148,15 +1219,15 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) return result; } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { auto ts = new RedBlackTree(1,2,3,4,5); assert(ts.length == 5); auto x = ts.removeAny(); assert(ts.length == 4); Elem[] arr; - foreach(Elem i; 1..6) - if(i != x) arr ~= i; + foreach (Elem i; 1 .. 6) + if (i != x) arr ~= i; assert(ts.arrayEqual(arr)); } @@ -1184,7 +1255,7 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) scope(success) --_length; auto lastnode = _end.prev; - if(lastnode is _begin) + if (lastnode is _begin) _begin = _begin.remove(_end); else lastnode.remove(_end); @@ -1192,14 +1263,14 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) check(); } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { auto ts = new RedBlackTree(1,2,3,4,5); assert(ts.length == 5); ts.removeBack(); assert(ts.length == 4); - static if(less == "a < b") + static if (less == "a < b") assert(ts.arrayEqual([1,2,3,4])); else assert(ts.arrayEqual([2,3,4,5])); @@ -1221,9 +1292,9 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) { auto b = r._begin; auto e = r._end; - if(_begin is b) + if (_begin is b) _begin = e; - while(b !is e) + while (b !is e) { b = b.remove(_end); --_length; @@ -1233,9 +1304,9 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) return Range(e, _end); } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto ts = new RedBlackTree(1,2,3,4,5); assert(ts.length == 5); auto r = ts[]; @@ -1246,7 +1317,7 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) assert(ts.length == 2); assert(ts.arrayEqual([1,5])); - static if(less == "a < b") + static if (less == "a < b") assert(equal(r2, [5])); else assert(equal(r2, [1])); @@ -1266,22 +1337,22 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) immutable isBegin = (r.source._begin is _begin); auto b = r.source._begin; - while(!r.empty) + while (!r.empty) { r.popFront(); b = b.remove(_end); --_length; } - if(isBegin) + if (isBegin) _begin = b; return Range(b, _end); } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : take; auto ts = new RedBlackTree(1,2,3,4,5); auto r = ts[]; @@ -1289,7 +1360,7 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) assert(ts.length == 5); auto r2 = ts.remove(take(r, 0)); - static if(less == "a < b") + static if (less == "a < b") { assert(equal(r2, [2,3,4,5])); auto r3 = ts.remove(take(r, 2)); @@ -1315,7 +1386,7 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove) - Examples: + Example: -------------------- auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); rbt.removeKey(1, 4, 7); @@ -1325,11 +1396,11 @@ assert(equal(rbt[], [5])); -------------------- +/ size_t removeKey(U...)(U elems) - if(allSatisfy!(isImplicitlyConvertibleToElem, U)) + if (allSatisfy!(isImplicitlyConvertibleToElem, U)) { Elem[U.length] toRemove; - foreach(i, e; elems) + foreach (i, e; elems) toRemove[i] = e; return removeKey(toRemove[]); @@ -1337,19 +1408,19 @@ assert(equal(rbt[], [5])); /++ Ditto +/ size_t removeKey(U)(U[] elems) - if(isImplicitlyConvertible!(U, Elem)) + if (isImplicitlyConvertible!(U, Elem)) { immutable lenBefore = length; - foreach(e; elems) + foreach (e; elems) { auto beg = _firstGreaterEqual(e); - if(beg is _end || _less(e, beg.value)) + if (beg is _end || _less(e, beg.value)) // no values are equal continue; immutable isBegin = (beg is _begin); beg = beg.remove(_end); - if(isBegin) + if (isBegin) _begin = beg; --_length; } @@ -1359,7 +1430,7 @@ assert(equal(rbt[], [5])); /++ Ditto +/ size_t removeKey(Stuff)(Stuff stuff) - if(isInputRange!Stuff && + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem) && !isDynamicArray!Stuff) { @@ -1375,28 +1446,28 @@ assert(equal(rbt[], [5])); enum isImplicitlyConvertibleToElem = isImplicitlyConvertible!(U, Elem); } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : take; auto rbt = new RedBlackTree(5, 4, 3, 7, 2, 1, 7, 6, 2, 19, 45); //The cast(Elem) is because these tests are instantiated with a variety //of numeric types, and the literals are all int, which is not always //implicitly convertible to Elem (e.g. short). - static if(allowDuplicates) + static if (allowDuplicates) { assert(rbt.length == 11); - assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 10); + assert(rbt.removeKey(cast(Elem) 4) == 1 && rbt.length == 10); assert(rbt.arrayEqual([1,2,2,3,5,6,7,7,19,45]) && rbt.length == 10); - assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3); + assert(rbt.removeKey(cast(Elem) 6, cast(Elem) 2, cast(Elem) 1) == 3); assert(rbt.arrayEqual([2,3,5,7,7,19,45]) && rbt.length == 7); assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 7); assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 4); - static if(less == "a < b") + static if (less == "a < b") assert(equal(rbt[], [7,7,19,45])); else assert(equal(rbt[], [7,5,3,2])); @@ -1404,16 +1475,16 @@ assert(equal(rbt[], [5])); else { assert(rbt.length == 9); - assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 8); + assert(rbt.removeKey(cast(Elem) 4) == 1 && rbt.length == 8); assert(rbt.arrayEqual([1,2,3,5,6,7,19,45])); - assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3); + assert(rbt.removeKey(cast(Elem) 6, cast(Elem) 2, cast(Elem) 1) == 3); assert(rbt.arrayEqual([3,5,7,19,45]) && rbt.length == 5); assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 5); assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 2); - static if(less == "a < b") + static if (less == "a < b") assert(equal(rbt[], [19,45])); else assert(equal(rbt[], [5,3])); @@ -1421,14 +1492,14 @@ assert(equal(rbt[], [5])); } // find the first node where the value is > e - private Node _firstGreater(Elem e) + private inout(RBNode)* _firstGreater(Elem e) inout { // can't use _find, because we cannot return null auto cur = _end.left; - auto result = _end; - while(cur) + inout(RBNode)* result = _end; + while (cur) { - if(_less(e, cur.value)) + if (_less(e, cur.value)) { result = cur; cur = cur.left; @@ -1440,14 +1511,14 @@ assert(equal(rbt[], [5])); } // find the first node where the value is >= e - private Node _firstGreaterEqual(Elem e) + private inout(RBNode)* _firstGreaterEqual(Elem e) inout { // can't use _find, because we cannot return null. auto cur = _end.left; - auto result = _end; - while(cur) + inout(RBNode)* result = _end; + while (cur) { - if(_less(cur.value, e)) + if (_less(cur.value, e)) cur = cur.right; else { @@ -1470,6 +1541,18 @@ assert(equal(rbt[], [5])); return Range(_firstGreater(e), _end); } + /// Ditto + ConstRange upperBound(Elem e) const + { + return ConstRange(_firstGreater(e), _end); + } + + /// Ditto + ImmutableRange upperBound(Elem e) immutable + { + return ImmutableRange(_firstGreater(e), _end); + } + /** * Get a range from the container with all elements that are < e according * to the less comparator @@ -1481,39 +1564,52 @@ assert(equal(rbt[], [5])); return Range(_begin, _firstGreaterEqual(e)); } + /// Ditto + ConstRange lowerBound(Elem e) const + { + return ConstRange(_begin, _firstGreaterEqual(e)); + } + + /// Ditto + ImmutableRange lowerBound(Elem e) immutable + { + return ImmutableRange(_begin, _firstGreaterEqual(e)); + } + /** * Get a range from the container with all elements that are == e according * to the less comparator * * Complexity: $(BIGOH log(n)) */ - Range equalRange(Elem e) + auto equalRange(this This)(Elem e) { auto beg = _firstGreaterEqual(e); - if(beg is _end || _less(e, beg.value)) + alias RangeType = RBRange!(typeof(beg)); + if (beg is _end || _less(e, beg.value)) // no values are equal - return Range(beg, beg); - static if(allowDuplicates) + return RangeType(beg, beg); + static if (allowDuplicates) { - return Range(beg, _firstGreater(e)); + return RangeType(beg, _firstGreater(e)); } else { // no sense in doing a full search, no duplicates are allowed, // so we just get the next node. - return Range(beg, beg.next); + return RangeType(beg, beg.next); } } - static if(doUnittest) unittest + static if (doUnittest) @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto ts = new RedBlackTree(1, 2, 3, 4, 5); auto rl = ts.lowerBound(3); auto ru = ts.upperBound(3); auto re = ts.equalRange(3); - static if(less == "a < b") + static if (less == "a < b") { assert(equal(rl, [1,2])); assert(equal(ru, [4,5])); @@ -1536,22 +1632,22 @@ assert(equal(rbt[], [5])); */ void printTree(Node n, int indent = 0) { - import std.stdio; - if(n !is null) + import std.stdio : write, writeln; + if (n !is null) { printTree(n.right, indent + 2); - for(int i = 0; i < indent; i++) + for (int i = 0; i < indent; i++) write("."); writeln(n.color == n.color.Black ? "B" : "R"); printTree(n.left, indent + 2); } else { - for(int i = 0; i < indent; i++) + for (int i = 0; i < indent; i++) write("."); writeln("N"); } - if(indent is 0) + if (indent is 0) writeln(); } @@ -1566,36 +1662,38 @@ assert(equal(rbt[], [5])); // int recurse(Node n, string path) { - import std.stdio; - if(n is null) + import std.stdio : writeln; + if (n is null) return 1; - if(n.parent.left !is n && n.parent.right !is n) + if (n.parent.left !is n && n.parent.right !is n) throw new Exception("Node at path " ~ path ~ " has inconsistent pointers"); Node next = n.next; - static if(allowDuplicates) + static if (allowDuplicates) { - if(next !is _end && _less(next.value, n.value)) + if (next !is _end && _less(next.value, n.value)) throw new Exception("ordering invalid at path " ~ path); } else { - if(next !is _end && !_less(n.value, next.value)) + if (next !is _end && !_less(n.value, next.value)) throw new Exception("ordering invalid at path " ~ path); } - if(n.color == n.color.Red) + if (n.color == n.color.Red) { - if((n.left !is null && n.left.color == n.color.Red) || + if ((n.left !is null && n.left.color == n.color.Red) || (n.right !is null && n.right.color == n.color.Red)) throw new Exception("Node at path " ~ path ~ " is red with a red child"); } int l = recurse(n.left, path ~ "L"); int r = recurse(n.right, path ~ "R"); - if(l != r) + if (l != r) { writeln("bad tree at:"); debug printTree(n); - throw new Exception("Node at path " ~ path ~ " has different number of black nodes on left and right paths"); + throw new Exception( + "Node at path " ~ path ~ " has different number of black nodes on left and right paths" + ); } return l + (n.color == n.color.Black ? 1 : 0); } @@ -1604,7 +1702,7 @@ assert(equal(rbt[], [5])); { recurse(_end.left, ""); } - catch(Exception e) + catch (Exception e) { debug printTree(_end.left, 0); throw e; @@ -1612,6 +1710,21 @@ assert(equal(rbt[], [5])); } } + /** + Formats the RedBlackTree into a sink function. For more info see $(D + std.format.formatValue). Note that this only is available when the + element type can be formatted. Otherwise, the default toString from + Object is used. + */ + static if (is(typeof((){FormatSpec!(char) fmt; formatValue((const(char)[]) {}, ConstRange.init, fmt);}))) + { + void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const { + sink("RedBlackTree("); + sink.formatValue(this[], fmt); + sink(")"); + } + } + /** * Constructor. Pass in an array of elements, or individual elements to * initialize the tree with. @@ -1646,9 +1759,9 @@ assert(equal(rbt[], [5])); } //Verify Example for removeKey. -pure unittest +@safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); rbt.removeKey(1, 4, 7); assert(equal(rbt[], [0, 1, 1, 5])); @@ -1657,9 +1770,9 @@ pure unittest } //Tests for removeKey -pure unittest +@safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; { auto rbt = redBlackTree(["hello", "world", "foo", "bar"]); assert(equal(rbt[], ["bar", "foo", "hello", "world"])); @@ -1678,16 +1791,16 @@ pure unittest assert(equal(rbt[], [1, 2, 4, 12, 27, 500])); assert(rbt.removeKey(1u) == 1); assert(equal(rbt[], [2, 4, 12, 27, 500])); - assert(rbt.removeKey(cast(byte)1) == 0); + assert(rbt.removeKey(cast(byte) 1) == 0); assert(equal(rbt[], [2, 4, 12, 27, 500])); - assert(rbt.removeKey(1, 12u, cast(byte)27) == 2); + assert(rbt.removeKey(1, 12u, cast(byte) 27) == 2); assert(equal(rbt[], [2, 4, 500])); - assert(rbt.removeKey([cast(short)0, cast(short)500, cast(short)1]) == 1); + assert(rbt.removeKey([cast(short) 0, cast(short) 500, cast(short) 1]) == 1); assert(equal(rbt[], [2, 4])); } } -pure unittest +@safe pure unittest { void test(T)() { @@ -1707,9 +1820,18 @@ pure unittest test!byte(); } +import std.range.primitives : isInputRange, isSomeString, ElementType; +import std.traits : isArray; + /++ Convenience function for creating a $(D RedBlackTree!E) from a list of values. + + Params: + allowDuplicates = Whether duplicates should be allowed (optional, default: false) + less = predicate to sort by (optional) + elems = elements to insert into the rbtree (variadic arguments) + range = range elements to insert into the rbtree (alternative to elems) +/ auto redBlackTree(E)(E[] elems...) { @@ -1724,13 +1846,14 @@ auto redBlackTree(bool allowDuplicates, E)(E[] elems...) /++ Ditto +/ auto redBlackTree(alias less, E)(E[] elems...) +if (is(typeof(binaryFun!less(E.init, E.init)))) { return new RedBlackTree!(E, less)(elems); } /++ Ditto +/ auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...) - if(is(typeof(binaryFun!less(E.init, E.init)))) +if (is(typeof(binaryFun!less(E.init, E.init)))) { //We shouldn't need to instantiate less here, but for some reason, //dmd can't handle it if we don't (even though the template which @@ -1738,18 +1861,59 @@ auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...) return new RedBlackTree!(E, binaryFun!less, allowDuplicates)(elems); } +/++ Ditto +/ +auto redBlackTree(Stuff)(Stuff range) +if (isInputRange!Stuff && !isArray!(Stuff)) +{ + return new RedBlackTree!(ElementType!Stuff)(range); +} + +/++ Ditto +/ +auto redBlackTree(bool allowDuplicates, Stuff)(Stuff range) +if (isInputRange!Stuff && !isArray!(Stuff)) +{ + return new RedBlackTree!(ElementType!Stuff, "a < b", allowDuplicates)(range); +} + +/++ Ditto +/ +auto redBlackTree(alias less, Stuff)(Stuff range) +if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init))) + && isInputRange!Stuff && !isArray!(Stuff)) +{ + return new RedBlackTree!(ElementType!Stuff, less)(range); +} + +/++ Ditto +/ +auto redBlackTree(alias less, bool allowDuplicates, Stuff)(Stuff range) +if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init))) + && isInputRange!Stuff && !isArray!(Stuff)) +{ + //We shouldn't need to instantiate less here, but for some reason, + //dmd can't handle it if we don't (even though the template which + //takes less but not allowDuplicates works just fine). + return new RedBlackTree!(ElementType!Stuff, binaryFun!less, allowDuplicates)(range); +} + /// -pure unittest +@safe pure unittest { + import std.range : iota; + auto rbt1 = redBlackTree(0, 1, 5, 7); auto rbt2 = redBlackTree!string("hello", "world"); auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5); auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7); auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9); + + // also works with ranges + auto rbt6 = redBlackTree(iota(3)); + auto rbt7 = redBlackTree!true(iota(3)); + auto rbt8 = redBlackTree!"a > b"(iota(3)); + auto rbt9 = redBlackTree!("a > b", true)(iota(3)); } //Combinations not in examples. -pure unittest +@safe pure unittest { auto rbt1 = redBlackTree!(true, string)("hello", "hello"); auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3); @@ -1757,17 +1921,74 @@ pure unittest } //Range construction. -pure unittest +@safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : iota; auto rbt = new RedBlackTree!(int, "a > b")(iota(5)); assert(equal(rbt[], [4, 3, 2, 1, 0])); } -pure unittest + +// construction with arrays +@safe pure unittest +{ + import std.algorithm.comparison : equal; + + auto rbt = redBlackTree!"a > b"([0, 1, 2, 3, 4]); + assert(equal(rbt[], [4, 3, 2, 1, 0])); + + auto rbt2 = redBlackTree!"a > b"(["a", "b"]); + assert(equal(rbt2[], ["b", "a"])); + + auto rbt3 = redBlackTree!"a > b"([1, 2]); + assert(equal(rbt3[], [2, 1])); + + auto rbt4 = redBlackTree([0, 1, 7, 5]); + assert(equal(rbt4[], [0, 1, 5, 7])); + + auto rbt5 = redBlackTree(["hello", "world"]); + assert(equal(rbt5[], ["hello", "world"])); + + auto rbt6 = redBlackTree!true([0, 1, 5, 7, 5]); + assert(equal(rbt6[], [0, 1, 5, 5, 7])); + + auto rbt7 = redBlackTree!"a > b"([0, 1, 5, 7]); + assert(equal(rbt7[], [7, 5, 1, 0])); + + auto rbt8 = redBlackTree!("a > b", true)([0.1, 1.3, 5.9, 7.2, 5.9]); + assert(equal(rbt8[], [7.2, 5.9, 5.9, 1.3, 0.1])); +} + +// convenience wrapper range construction +@safe pure unittest +{ + import std.algorithm.comparison : equal; + import std.range : chain, iota; + + auto rbt = redBlackTree(iota(3)); + assert(equal(rbt[], [0, 1, 2])); + + auto rbt2 = redBlackTree!"a > b"(iota(2)); + assert(equal(rbt2[], [1, 0])); + + auto rbt3 = redBlackTree(chain([0, 1], [7, 5])); + assert(equal(rbt3[], [0, 1, 5, 7])); + + auto rbt4 = redBlackTree(chain(["hello"], ["world"])); + assert(equal(rbt4[], ["hello", "world"])); + + auto rbt5 = redBlackTree!true(chain([0, 1], [5, 7, 5])); + assert(equal(rbt5[], [0, 1, 5, 5, 7])); + + auto rbt6 = redBlackTree!("a > b", true)(chain([0.1, 1.3], [5.9, 7.2, 5.9])); + assert(equal(rbt6[], [7.2, 5.9, 5.9, 1.3, 0.1])); +} + +@safe pure unittest { import std.array : array; + auto rt1 = redBlackTree(5, 4, 3, 2, 1); assert(rt1.length == 5); assert(array(rt1[]) == [1, 2, 3, 4, 5]); @@ -1785,9 +2006,53 @@ pure unittest assert(array(rt4[]) == ["hello"]); } +@system unittest +{ + import std.conv : to; + + auto rt1 = redBlackTree!string(); + assert(rt1.to!string == "RedBlackTree([])"); + + auto rt2 = redBlackTree!string("hello"); + assert(rt2.to!string == "RedBlackTree([\"hello\"])"); + + auto rt3 = redBlackTree!string("hello", "world", "!"); + assert(rt3.to!string == "RedBlackTree([\"!\", \"hello\", \"world\"])"); + + // type deduction can be done automatically + auto rt4 = redBlackTree(["hello"]); + assert(rt4.to!string == "RedBlackTree([\"hello\"])"); +} + //constness checks -unittest +@safe pure unittest { const rt1 = redBlackTree(5,4,3,2,1); static assert(is(typeof(rt1.length))); + static assert(is(typeof(5 in rt1))); + + static assert(is(typeof(rt1.upperBound(3).front) == const(int))); + import std.algorithm.comparison : equal; + assert(rt1.upperBound(3).equal([4, 5])); + assert(rt1.lowerBound(3).equal([1, 2])); + assert(rt1.equalRange(3).equal([3])); + assert(rt1[].equal([1, 2, 3, 4, 5])); +} + +//immutable checks +@safe pure unittest +{ + immutable rt1 = redBlackTree(5,4,3,2,1); + static assert(is(typeof(rt1.length))); + + static assert(is(typeof(rt1.upperBound(3).front) == immutable(int))); + import std.algorithm.comparison : equal; + assert(rt1.upperBound(2).equal([3, 4, 5])); +} + +// issue 15941 +@safe pure unittest +{ + class C {} + RedBlackTree!(C, "cast(void*)a < cast(void*) b") tree; } diff --git a/std/container/slist.d b/std/container/slist.d index b015aec7bea..3cd67a81ff1 100644 --- a/std/container/slist.d +++ b/std/container/slist.d @@ -1,28 +1,55 @@ /** This module implements a singly-linked list container. +It can be used as a stack. -This module is a submodule of $(LINK2 std_container.html, std.container). +This module is a submodule of $(MREF std, container). Source: $(PHOBOSSRC std/container/_slist.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. +Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB +(See accompanying file LICENSE_1_0.txt or copy at $(HTTP boost.org/LICENSE_1_0.txt)). -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) + +$(SCRIPT inhibitQuickIndex = 1;) */ module std.container.slist; +/// +@safe unittest +{ + import std.container : SList; + import std.algorithm.comparison : equal; + + auto s = SList!int(1, 2, 3); + assert(equal(s[], [1, 2, 3])); + + s.removeFront(); + assert(equal(s[], [2, 3])); + + s.insertFront([5, 6]); + assert(equal(s[], [5, 6, 2, 3])); + + // If you want to apply range operations, simply slice it. + import std.algorithm.searching : countUntil; + import std.range : popFrontN, walkLength; + + auto sl = SList!int(1, 2, 3, 4, 5); + assert(countUntil(sl[], 2) == 1); + + auto r = sl[]; + popFrontN(r, 2); + assert(walkLength(r) == 3); +} + public import std.container.util; /** Implements a simple and fast singly-linked list. + It can be used as a stack. $(D SList) uses reference semantics. */ @@ -30,8 +57,8 @@ struct SList(T) { import std.exception : enforce; import std.range : Take; - import std.range.primitives; - import std.traits; + import std.range.primitives : isInputRange, isForwardRange, ElementType; + import std.traits : isImplicitlyConvertible; private struct Node { @@ -172,7 +199,7 @@ Defines the container's primary range, which embodies a forward range. T moveFront() { - import std.algorithm : move; + import std.algorithm.mutation : move; assert(!empty, "SList.Range.moveFront: Range is empty"); return move(_head._payload); @@ -184,7 +211,7 @@ Defines the container's primary range, which embodies a forward range. } } - unittest + @safe unittest { static assert(isForwardRange!Range); } @@ -236,7 +263,7 @@ Complexity: $(BIGOH 1) return _first._payload; } - unittest + @safe unittest { auto s = SList!int(1, 2, 3); s.front = 42; @@ -280,7 +307,29 @@ Complexity: $(BIGOH 1) */ void clear() { - _first = null; + if (_root) + _first = null; + } + +/** +Reverses SList in-place. Performs no memory allocation. + +Complexity: $(BIGOH n) + */ + void reverse() + { + if (!empty) + { + Node* prev; + while (_first) + { + auto next = _first._next; + _first._next = prev; + prev = _first; + _first = next; + } + _first = prev; + } } /** @@ -345,7 +394,7 @@ Complexity: $(BIGOH 1). */ T removeAny() { - import std.algorithm : move; + import std.algorithm.mutation : move; assert(!empty, "SList.removeAny: List is empty"); auto result = move(_first._payload); @@ -418,7 +467,7 @@ Returns: The number of values inserted. Complexity: $(BIGOH k + m), where $(D k) is the number of elements in $(D r) and $(D m) is the length of $(D stuff). -Examples: +Example: -------------------- auto sl = SList!string(["a", "b", "d"]); sl.insertAfter(sl[], "e"); // insert at the end (slowest) @@ -430,6 +479,7 @@ assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); size_t insertAfter(Stuff)(Range r, Stuff stuff) { + initialize(); if (!_first) { enforce(!r._head); @@ -541,9 +591,9 @@ Complexity: $(BIGOH n) alias stableLinearRemove = linearRemove; } -unittest +@safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = SList!int(5); auto b = a; @@ -557,21 +607,21 @@ unittest assert(equal(b[], [2, 1, 9])); } -unittest +@safe unittest { auto s = SList!int(1, 2, 3); auto n = s.findLastNode(s._root); assert(n && n._payload == 3); } -unittest +@safe unittest { import std.range.primitives; auto s = SList!int(1, 2, 5, 10); assert(walkLength(s[]) == 4); } -unittest +@safe unittest { import std.range : take; auto src = take([0, 1, 2, 3], 3); @@ -579,7 +629,7 @@ unittest assert(s == SList!int(0, 1, 2)); } -unittest +@safe unittest { auto a = SList!int(); auto b = SList!int(); @@ -587,7 +637,7 @@ unittest assert(c.empty); } -unittest +@safe unittest { auto a = SList!int(1, 2, 3); auto b = SList!int(4, 5, 6); @@ -595,7 +645,7 @@ unittest assert(c == SList!int(1, 2, 3, 4, 5, 6)); } -unittest +@safe unittest { auto a = SList!int(1, 2, 3); auto b = [4, 5, 6]; @@ -603,21 +653,21 @@ unittest assert(c == SList!int(1, 2, 3, 4, 5, 6)); } -unittest +@safe unittest { auto a = SList!int(1, 2, 3); auto c = a ~ 4; assert(c == SList!int(1, 2, 3, 4)); } -unittest +@safe unittest { auto a = SList!int(2, 3, 4); auto b = 1 ~ a; assert(b == SList!int(1, 2, 3, 4)); } -unittest +@safe unittest { auto a = [1, 2, 3]; auto b = SList!int(4, 5, 6); @@ -625,14 +675,14 @@ unittest assert(c == SList!int(1, 2, 3, 4, 5, 6)); } -unittest +@safe unittest { auto s = SList!int(1, 2, 3, 4); s.insertFront([ 42, 43 ]); assert(s == SList!int(42, 43, 1, 2, 3, 4)); } -unittest +@safe unittest { auto s = SList!int(1, 2, 3); assert(s.removeAny() == 1); @@ -641,9 +691,9 @@ unittest assert(s == SList!int(3)); } -unittest +@safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto s = SList!int(1, 2, 3); s.removeFront(); @@ -652,21 +702,21 @@ unittest assert(equal(s[], [3])); } -unittest +@safe unittest { auto s = SList!int(1, 2, 3, 4, 5, 6, 7); assert(s.removeFront(3) == 3); assert(s == SList!int(4, 5, 6, 7)); } -unittest +@safe unittest { auto a = SList!int(1, 2, 3); auto b = SList!int(1, 2, 3); assert(a.insertAfter(a[], b[]) == 3); } -unittest +@safe unittest { import std.range : take; auto s = SList!int(1, 2, 3, 4); @@ -675,19 +725,20 @@ unittest assert(s == SList!int(1, 2, 5, 3, 4)); } -unittest +@safe unittest { - import std.algorithm; + import std.algorithm.comparison : equal; + import std.range : take; // insertAfter documentation example auto sl = SList!string(["a", "b", "d"]); sl.insertAfter(sl[], "e"); // insert at the end (slowest) - assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); - sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" - assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); + assert(equal(sl[], ["a", "b", "d", "e"])); + sl.insertAfter(take(sl[], 2), "c"); // insert after "b" + assert(equal(sl[], ["a", "b", "c", "d", "e"])); } -unittest +@safe unittest { import std.range.primitives; auto s = SList!int(1, 2, 3, 4, 5); @@ -698,7 +749,7 @@ unittest assert(r1.empty); } -unittest +@safe unittest { auto s = SList!int(1, 2, 3, 4, 5); auto r = s[]; @@ -707,9 +758,9 @@ unittest assert(r1.empty); } -unittest +@safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range; auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -722,7 +773,7 @@ unittest assert(equal(r2, [8, 9, 10])); } -unittest +@safe unittest { import std.range.primitives; auto lst = SList!int(1, 5, 42, 9); @@ -737,12 +788,12 @@ unittest assert(walkLength(lst3[]) == 5); } -unittest +@safe unittest { auto s = make!(SList!int)(1, 2, 3); } -unittest +@safe unittest { // 5193 static struct Data @@ -752,7 +803,7 @@ unittest SList!Data list; } -unittest +@safe unittest { auto s = SList!int([1, 2, 3]); s.front = 5; //test frontAssign @@ -761,3 +812,35 @@ unittest r.front = 1; //test frontAssign assert(r.front == 1); } + +@safe unittest +{ + // issue 14920 + SList!int s; + s.insertAfter(s[], 1); + assert(s.front == 1); +} + +@safe unittest +{ + // issue 15659 + SList!int s; + s.clear(); +} + +@safe unittest +{ + SList!int s; + s.reverse(); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + + auto s = SList!int([1, 2, 3]); + assert(s[].equal([1, 2, 3])); + + s.reverse(); + assert(s[].equal([3, 2, 1])); +} diff --git a/std/container/util.d b/std/container/util.d index 6e6d4a0fe3f..aed1f1aa680 100644 --- a/std/container/util.d +++ b/std/container/util.d @@ -1,21 +1,19 @@ /** This module contains some common utilities used by containers. -This module is a submodule of $(LINK2 std_container.html, std.container). +This module is a submodule of $(MREF std, container). Source: $(PHOBOSSRC std/container/_util.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. +Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB +(See accompanying file LICENSE_1_0.txt or copy at $(HTTP boost.org/LICENSE_1_0.txt)). -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) +Authors: $(HTTP erdani.com, Andrei Alexandrescu) + +$(SCRIPT inhibitQuickIndex = 1;) */ module std.container.util; @@ -35,9 +33,9 @@ if (is(T == struct) || is(T == class)) // to a null reference. We therefore construct an empty container // by passing an empty array to its constructor. // Issue #13872. - static if(arguments.length == 0) + static if (arguments.length == 0) { - import std.range; + import std.range.primitives : ElementType; alias ET = ElementType!(T.Range); return T(ET[].init); } @@ -54,11 +52,10 @@ if (is(T == struct) || is(T == class)) /// -unittest +@system unittest { import std.container; - import std.algorithm : equal; - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto arr = make!(Array!int)([4, 2, 3, 1]); assert(equal(arr[], [4, 2, 3, 1])); @@ -71,10 +68,10 @@ unittest assert(equal(slist[], [1, 2, 3])); } -unittest +@system unittest { import std.container; - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto arr1 = make!(Array!dchar)(); assert(arr1.empty); @@ -88,10 +85,10 @@ unittest } // Issue 8895 -unittest +@safe unittest { import std.container; - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = make!(DList!int)(1,2,3,4); auto b = make!(DList!int)(1,2,3,4); @@ -106,30 +103,41 @@ unittest * Convenience function for constructing a generic container. */ template make(alias Container, Args...) - if(!is(Container)) +if (!is(Container)) { - import std.range : isInputRange; + import std.range : isInputRange, isInfinite; import std.traits : isDynamicArray; auto make(Range)(Range range) - if(!isDynamicArray!Range && isInputRange!Range) + if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range) { import std.range : ElementType; return .make!(Container!(ElementType!Range, Args))(range); } auto make(T)(T[] items...) + if (!isInfinite!T) { return .make!(Container!(T, Args))(items); } } +/// forbid construction from infinite range +@safe unittest +{ + import std.container.array : Array; + import std.range : only, repeat; + import std.range.primitives : isInfinite; + static assert(__traits(compiles, { auto arr = make!Array(only(5)); })); + static assert(!__traits(compiles, { auto arr = make!Array(repeat(5)); })); +} + /// -unittest +@system unittest { import std.container.array, std.container.rbtree, std.container.slist; import std.range : iota; - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto arr = make!Array(iota(5)); assert(equal(arr[], [0, 1, 2, 3, 4])); @@ -145,17 +153,17 @@ unittest assert(equal(list[], [1, 7, 42])); } -unittest +@safe unittest { import std.container.rbtree; - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto rbtmin = make!(RedBlackTree, "a < b", false)(3, 2, 2, 1); assert(equal(rbtmin[], [1, 2, 3])); } // Issue 13872 -unittest +@system unittest { import std.container; diff --git a/std/conv.d b/std/conv.d index 5f549bc126c..473e7d0f600 100644 --- a/std/conv.d +++ b/std/conv.d @@ -3,34 +3,62 @@ /** A one-stop shop for converting values from one type to another. +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Generic) $(TD + $(LREF castFrom) + $(LREF emplace) + $(LREF parse) + $(LREF to) + $(LREF toChars) +)) +$(TR $(TD Strings) $(TD + $(LREF text) + $(LREF wtext) + $(LREF dtext) + $(LREF hexString) +)) +$(TR $(TD Numeric) $(TD + $(LREF octal) + $(LREF roundTo) + $(LREF signed) + $(LREF unsigned) +)) +$(TR $(TD Exceptions) $(TD + $(LREF ConvException) + $(LREF ConvOverflowException) +)) +) + Copyright: Copyright Digital Mars 2007-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB digitalmars.com, Walter Bright), - $(WEB erdani.org, Andrei Alexandrescu), +Authors: $(HTTP digitalmars.com, Walter Bright), + $(HTTP erdani.org, Andrei Alexandrescu), Shin Fujishiro, Adam D. Ruppe, Kenji Hara Source: $(PHOBOSSRC std/_conv.d) -Macros: -WIKI = Phobos/StdConv - */ module std.conv; public import std.ascii : LetterCase; +import std.meta; import std.range.primitives; import std.traits; -import std.typetuple; -private string convFormat(Char, Args...)(in Char[] fmt, Args args) +// Same as std.string.format, but "self-importing". +// Helps reduce code and imports, particularly in static asserts. +// Also helps with missing imports errors. +package template convFormat() { import std.format : format; - return std.format.format(fmt, args); + alias convFormat = format; } /* ************* Exceptions *************** */ @@ -40,33 +68,37 @@ private string convFormat(Char, Args...)(in Char[] fmt, Args args) */ class ConvException : Exception { - @safe pure nothrow - this(string s, string fn = __FILE__, size_t ln = __LINE__) - { - super(s, fn, ln); - } -} - -private string convError_unexpected(S)(S source) -{ - return source.empty ? "end of input" : text("'", source.front, "'"); + import std.exception : basicExceptionCtors; + /// + mixin basicExceptionCtors; } private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__) { - return new ConvException( - text("Unexpected ", convError_unexpected(source), - " when converting from type "~S.stringof~" to type "~T.stringof), - fn, ln); + string msg; + + if (source.empty) + msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof; + else + msg = text("Unexpected '", source.front, + "' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); + + return new ConvException(msg, fn, ln); } private auto convError(S, T)(S source, int radix, string fn = __FILE__, size_t ln = __LINE__) { - return new ConvException( - text("Unexpected ", convError_unexpected(source), - " when converting from type "~S.stringof~" base ", radix, - " to type "~T.stringof), - fn, ln); + string msg; + + if (source.empty) + msg = text("Unexpected end of input when converting from type " ~ S.stringof ~ " base ", radix, + " to type " ~ T.stringof); + else + msg = text("Unexpected '", source.front, + "' when converting from type " ~ S.stringof ~ " base ", radix, + " to type " ~ T.stringof); + + return new ConvException(msg, fn, ln); } @safe pure/* nothrow*/ // lazy parameter bug @@ -86,24 +118,8 @@ private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = private { - template isImaginary(T) - { - enum bool isImaginary = staticIndexOf!(Unqual!T, - ifloat, idouble, ireal) >= 0; - } - template isComplex(T) - { - enum bool isComplex = staticIndexOf!(Unqual!T, - cfloat, cdouble, creal) >= 0; - } - template isNarrowInteger(T) - { - enum bool isNarrowInteger = staticIndexOf!(Unqual!T, - byte, ubyte, short, ushort) >= 0; - } - T toStr(T, S)(S src) - if (isSomeString!T) + if (isSomeString!T) { // workaround for Bugzilla 14198 static if (is(S == bool) && is(typeof({ T s = "string"; }))) @@ -137,17 +153,6 @@ private enum isNullToStr = isImplicitlyConvertible!(S, T) && (is(Unqual!S == typeof(null))) && isExactSomeString!T; } - - template isRawStaticArray(T, A...) - { - enum isRawStaticArray = - A.length == 0 && - isStaticArray!T && - !is(T == class) && - !is(T == interface) && - !is(T == struct) && - !is(T == union); - } } /** @@ -163,146 +168,221 @@ class ConvOverflowException : ConvException } /** - -The $(D_PARAM to) family of functions converts a value from type -$(D_PARAM Source) to type $(D_PARAM Target). The source type is -deduced and the target type must be specified, for example the -expression $(D_PARAM to!int(42.0)) converts the number 42 from -$(D_PARAM double) to $(D_PARAM int). The conversion is "safe", i.e., -it checks for overflow; $(D_PARAM to!int(4.2e10)) would throw the -$(D_PARAM ConvOverflowException) exception. Overflow checks are only -inserted when necessary, e.g., $(D_PARAM to!double(42)) does not do -any checking because any int fits in a double. - -Converting a value to its own type (useful mostly for generic code) -simply returns its argument. - -Example: -------------------------- -int a = 42; -auto b = to!int(a); // b is int with value 42 -auto c = to!double(3.14); // c is double with value 3.14 -------------------------- - -Converting among numeric types is a safe way to cast them around. - -Conversions from floating-point types to integral types allow loss of -precision (the fractional part of a floating-point number). The -conversion is truncating towards zero, the same way a cast would -truncate. (To round a floating point value when casting to an -integral, use $(D_PARAM roundTo).) - -Examples: -------------------------- -int a = 420; -auto b = to!long(a); // same as long b = a; -auto c = to!byte(a / 10); // fine, c = 42 -auto d = to!byte(a); // throw ConvOverflowException -double e = 4.2e6; -auto f = to!int(e); // f == 4200000 -e = -3.14; -auto g = to!uint(e); // fails: floating-to-integral negative overflow -e = 3.14; -auto h = to!uint(e); // h = 3 -e = 3.99; -h = to!uint(a); // h = 3 -e = -3.99; -f = to!int(a); // f = -3 -------------------------- - -Conversions from integral types to floating-point types always -succeed, but might lose accuracy. The largest integers with a -predecessor representable in floating-point format are 2^24-1 for -float, 2^53-1 for double, and 2^64-1 for $(D_PARAM real) (when -$(D_PARAM real) is 80-bit, e.g. on Intel machines). - -Example: -------------------------- -int a = 16_777_215; // 2^24 - 1, largest proper integer representable as float -assert(to!int(to!float(a)) == a); -assert(to!int(to!float(-a)) == -a); -a += 2; -assert(to!int(to!float(a)) == a); // fails! -------------------------- - -Conversions from string to numeric types differ from the C equivalents -$(D_PARAM atoi()) and $(D_PARAM atol()) by checking for overflow and -not allowing whitespace. - -For conversion of strings to signed types, the grammar recognized is: -
-$(I Integer): $(I Sign UnsignedInteger)
+The `to` template converts a value from one type _to another.
+The source type is deduced and the target type must be specified, for example the
+expression `to!int(42.0)` converts the number 42 from
+`double` _to `int`. The conversion is "safe", i.e.,
+it checks for overflow; `to!int(4.2e10)` would throw the
+`ConvOverflowException` exception. Overflow checks are only
+inserted when necessary, e.g., `to!double(42)` does not do
+any checking because any `int` fits in a `double`.
+
+Conversions from string _to numeric types differ from the C equivalents
+`atoi()` and `atol()` by checking for overflow and not allowing whitespace.
+
+For conversion of strings _to signed types, the grammar recognized is:
+$(PRE $(I Integer): $(I Sign UnsignedInteger)
 $(I UnsignedInteger)
 $(I Sign):
     $(B +)
-    $(B -)
-
+ $(B -)) -For conversion to unsigned types, the grammar recognized is: -
-$(I UnsignedInteger):
+For conversion _to unsigned types, the grammar recognized is:
+$(PRE $(I UnsignedInteger):
     $(I DecimalDigit)
-    $(I DecimalDigit) $(I UnsignedInteger)
-
- -Converting an array to another array type works by converting each -element in turn. Associative arrays can be converted to associative -arrays as long as keys and values can in turn be converted. - -Example: -------------------------- -int[] a = [1, 2, 3]; -auto b = to!(float[])(a); -assert(b == [1.0f, 2, 3]); -string str = "1 2 3 4 5 6"; -auto numbers = to!(double[])(split(str)); -assert(numbers == [1.0, 2, 3, 4, 5, 6]); -int[string] c; -c["a"] = 1; -c["b"] = 2; -auto d = to!(double[wstring])(c); -assert(d["a"w] == 1 && d["b"w] == 2); -------------------------- - -Conversions operate transitively, meaning that they work on arrays and -associative arrays of any complexity: - -------------------------- -int[string][double[int[]]] a; -... -auto b = to!(short[wstring][string[double[]]])(a); -------------------------- - -This conversion works because $(D_PARAM to!short) applies to an -$(D_PARAM int), $(D_PARAM to!wstring) applies to a $(D_PARAM -string), $(D_PARAM to!string) applies to a $(D_PARAM double), and -$(D_PARAM to!(double[])) applies to an $(D_PARAM int[]). The -conversion might throw an exception because $(D_PARAM to!short) -might fail the range check. - - */ - -/** - Entry point that dispatches to the appropriate conversion - primitive. Client code normally calls $(D _to!TargetType(value)) - (and not some variant of $(D toImpl)). + $(I DecimalDigit) $(I UnsignedInteger)) */ template to(T) { T to(A...)(A args) - if (!isRawStaticArray!A) + if (A.length > 0) { return toImpl!T(args); } // Fix issue 6175 T to(S)(ref S arg) - if (isRawStaticArray!S) + if (isStaticArray!S) { return toImpl!T(arg); } } +/** + * Converting a value _to its own type (useful mostly for generic code) + * simply returns its argument. + */ +@safe pure unittest +{ + int a = 42; + int b = to!int(a); + double c = to!double(3.14); // c is double with value 3.14 +} + +/** + * Converting among numeric types is a safe way _to cast them around. + * + * Conversions from floating-point types _to integral types allow loss of + * precision (the fractional part of a floating-point number). The + * conversion is truncating towards zero, the same way a cast would + * truncate. (_To round a floating point value when casting _to an + * integral, use `roundTo`.) + */ +@safe pure unittest +{ + import std.exception : assertThrown; + + int a = 420; + assert(to!long(a) == a); + assertThrown!ConvOverflowException(to!byte(a)); + + assert(to!int(4.2e6) == 4200000); + assertThrown!ConvOverflowException(to!uint(-3.14)); + assert(to!uint(3.14) == 3); + assert(to!uint(3.99) == 3); + assert(to!int(-3.99) == -3); +} + +/** + * When converting strings _to numeric types, note that the D hexadecimal and binary + * literals are not handled. Neither the prefixes that indicate the base, nor the + * horizontal bar used _to separate groups of digits are recognized. This also + * applies to the suffixes that indicate the type. + * + * _To work around this, you can specify a radix for conversions involving numbers. + */ +@safe pure unittest +{ + auto str = to!string(42, 16); + assert(str == "2A"); + auto i = to!int(str, 16); + assert(i == 42); +} + +/** + * Conversions from integral types _to floating-point types always + * succeed, but might lose accuracy. The largest integers with a + * predecessor representable in floating-point format are `2^24-1` for + * `float`, `2^53-1` for `double`, and `2^64-1` for `real` (when + * `real` is 80-bit, e.g. on Intel machines). + */ +@safe pure unittest +{ + // 2^24 - 1, largest proper integer representable as float + int a = 16_777_215; + assert(to!int(to!float(a)) == a); + assert(to!int(to!float(-a)) == -a); +} + +/** + * Converting an array _to another array type works by converting each + * element in turn. Associative arrays can be converted _to associative + * arrays as long as keys and values can in turn be converted. + */ +@safe pure unittest +{ + import std.string : split; + + int[] a = [1, 2, 3]; + auto b = to!(float[])(a); + assert(b == [1.0f, 2, 3]); + string str = "1 2 3 4 5 6"; + auto numbers = to!(double[])(split(str)); + assert(numbers == [1.0, 2, 3, 4, 5, 6]); + int[string] c; + c["a"] = 1; + c["b"] = 2; + auto d = to!(double[wstring])(c); + assert(d["a"w] == 1 && d["b"w] == 2); +} + +/** + * Conversions operate transitively, meaning that they work on arrays and + * associative arrays of any complexity. + * + * This conversion works because `to!short` applies _to an `int`, `to!wstring` + * applies _to a `string`, `to!string` applies _to a `double`, and + * `to!(double[])` applies _to an `int[]`. The conversion might throw an + * exception because `to!short` might fail the range check. + */ +@safe unittest +{ + int[string][double[int[]]] a; + auto b = to!(short[wstring][string[double[]]])(a); +} + +/** + * Object-to-object conversions by dynamic casting throw exception when + * the source is non-null and the target is null. + */ +@safe pure unittest +{ + import std.exception : assertThrown; + // Testing object conversions + class A {} + class B : A {} + class C : A {} + A a1 = new A, a2 = new B, a3 = new C; + assert(to!B(a2) is a2); + assert(to!C(a3) is a3); + assertThrown!ConvException(to!B(a3)); +} + +/** + * Stringize conversion from all types is supported. + * $(UL + * $(LI String _to string conversion works for any two string types having + * ($(D char), $(D wchar), $(D dchar)) character widths and any + * combination of qualifiers (mutable, $(D const), or $(D immutable)).) + * $(LI Converts array (other than strings) _to string. + * Each element is converted by calling $(D to!T).) + * $(LI Associative array _to string conversion. + * Each element is printed by calling $(D to!T).) + * $(LI Object _to string conversion calls $(D toString) against the object or + * returns $(D "null") if the object is null.) + * $(LI Struct _to string conversion calls $(D toString) against the struct if + * it is defined.) + * $(LI For structs that do not define $(D toString), the conversion _to string + * produces the list of fields.) + * $(LI Enumerated types are converted _to strings as their symbolic names.) + * $(LI Boolean values are printed as $(D "true") or $(D "false").) + * $(LI $(D char), $(D wchar), $(D dchar) _to a string type.) + * $(LI Unsigned or signed integers _to strings. + * $(DL $(DT [special case]) + * $(DD Convert integral value _to string in $(D_PARAM radix) radix. + * radix must be a value from 2 to 36. + * value is treated as a signed value only if radix is 10. + * The characters A through Z are used to represent values 10 through 36 + * and their case is determined by the $(D_PARAM letterCase) parameter.))) + * $(LI All floating point types _to all string types.) + * $(LI Pointer to string conversions prints the pointer as a $(D size_t) value. + * If pointer is $(D char*), treat it as C-style strings. + * In that case, this function is $(D @system).)) + */ +@system pure unittest // @system due to cast and ptr +{ + // Conversion representing dynamic/static array with string + long[] a = [ 1, 3, 5 ]; + assert(to!string(a) == "[1, 3, 5]"); + + // Conversion representing associative array with string + int[string] associativeArray = ["0":1, "1":2]; + assert(to!string(associativeArray) == `["0":1, "1":2]` || + to!string(associativeArray) == `["1":2, "0":1]`); + + // char* to string conversion + assert(to!string(cast(char*) null) == ""); + assert(to!string("foo\0".ptr) == "foo"); + + // Conversion reinterpreting void array to string + auto w = "abcx"w; + const(void)[] b = w; + assert(b.length == 8); + + auto c = to!(wchar[])(b); + assert(c == "abcx"); +} + // Tests for issue 6175 @safe pure nothrow unittest { @@ -332,12 +412,12 @@ template to(T) @safe pure unittest { import std.exception; - foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { assertThrown!ConvException(to!T(" 0")); assertThrown!ConvException(to!T(" 0", 8)); } - foreach (T; TypeTuple!(float, double, real)) + foreach (T; AliasSeq!(float, double, real)) { assertThrown!ConvException(to!T(" 0")); } @@ -358,9 +438,9 @@ template to(T) If the source type is implicitly convertible to the target type, $(D to) simply performs the implicit conversion. */ -T toImpl(T, S)(S value) - if (isImplicitlyConvertible!(S, T) && - !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) +private T toImpl(T, S)(S value) +if (isImplicitlyConvertible!(S, T) && + !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) { template isSignedInt(T) { @@ -372,7 +452,7 @@ T toImpl(T, S)(S value) static if (isUnsignedInt!S && isSignedInt!T && S.sizeof == T.sizeof) { // unsigned to signed & same size import std.exception : enforce; - enforce(value <= cast(S)T.max, + enforce(value <= cast(S) T.max, new ConvOverflowException("Conversion positive overflow")); } else static if (isSignedInt!S && isUnsignedInt!T) @@ -404,12 +484,12 @@ T toImpl(T, S)(S value) { import std.exception; // Conversion between same size - foreach (S; TypeTuple!(byte, short, int, long)) + foreach (S; AliasSeq!(byte, short, int, long)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 alias U = Unsigned!S; - foreach (Sint; TypeTuple!(S, const S, immutable S)) - foreach (Uint; TypeTuple!(U, const U, immutable U)) + foreach (Sint; AliasSeq!(S, const S, immutable S)) + foreach (Uint; AliasSeq!(U, const U, immutable U)) { // positive overflow Uint un = Uint.max; @@ -424,8 +504,8 @@ T toImpl(T, S)(S value) }(); // Conversion between different size - foreach (i, S1; TypeTuple!(byte, short, int, long)) - foreach ( S2; TypeTuple!(byte, short, int, long)[i+1..$]) + foreach (i, S1; AliasSeq!(byte, short, int, long)) + foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$]) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 alias U1 = Unsigned!S1; alias U2 = Unsigned!S2; @@ -433,8 +513,8 @@ T toImpl(T, S)(S value) static assert(U1.sizeof < S2.sizeof); // small unsigned to big signed - foreach (Uint; TypeTuple!(U1, const U1, immutable U1)) - foreach (Sint; TypeTuple!(S2, const S2, immutable S2)) + foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) + foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) { Uint un = Uint.max; assertNotThrown(to!Sint(un)); @@ -442,8 +522,8 @@ T toImpl(T, S)(S value) } // big unsigned to small signed - foreach (Uint; TypeTuple!(U2, const U2, immutable U2)) - foreach (Sint; TypeTuple!(S1, const S1, immutable S1)) + foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) + foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) { Uint un = Uint.max; assertThrown(to!Sint(un)); @@ -452,16 +532,16 @@ T toImpl(T, S)(S value) static assert(S1.sizeof < U2.sizeof); // small signed to big unsigned - foreach (Sint; TypeTuple!(S1, const S1, immutable S1)) - foreach (Uint; TypeTuple!(U2, const U2, immutable U2)) + foreach (Sint; AliasSeq!(S1, const S1, immutable S1)) + foreach (Uint; AliasSeq!(U2, const U2, immutable U2)) { Sint sn = -1; assertThrown!ConvOverflowException(to!Uint(sn)); } // big signed to small unsigned - foreach (Sint; TypeTuple!(S2, const S2, immutable S2)) - foreach (Uint; TypeTuple!(U1, const U1, immutable U1)) + foreach (Sint; AliasSeq!(S2, const S2, immutable S2)) + foreach (Uint; AliasSeq!(U1, const U1, immutable U1)) { Sint sn = -1; assertThrown!ConvOverflowException(to!Uint(sn)); @@ -472,8 +552,8 @@ T toImpl(T, S)(S value) /* Converting static arrays forwards to their dynamic counterparts. */ -T toImpl(T, S)(ref S s) - if (isRawStaticArray!S) +private T toImpl(T, S)(ref S s) +if (isStaticArray!S) { return toImpl!(T, typeof(s[0])[])(s); } @@ -488,11 +568,11 @@ T toImpl(T, S)(ref S s) /** When source type supports member template function opCast, it is used. */ -T toImpl(T, S)(S value) - if (!isImplicitlyConvertible!(S, T) && - is(typeof(S.init.opCast!T()) : T) && - !isExactSomeString!T && - !is(typeof(T(value)))) +private T toImpl(T, S)(S value) +if (!isImplicitlyConvertible!(S, T) && + is(typeof(S.init.opCast!T()) : T) && + !isExactSomeString!T && + !is(typeof(T(value)))) { return value.opCast!T(); } @@ -510,7 +590,7 @@ T toImpl(T, S)(S value) T opCast(U)() @safe pure { assert(false); } } } - to!(Test.T)(Test.S()); + cast(void) to!(Test.T)(Test.S()); // make sure std.conv.to is doing the same thing as initialization Test.S s; @@ -539,9 +619,9 @@ When target type supports 'converting construction', it is used. $(UL $(LI If target type is struct, $(D T(value)) is used.) $(LI If target type is class, $(D new T(value)) is used.)) */ -T toImpl(T, S)(S value) - if (!isImplicitlyConvertible!(S, T) && - is(T == struct) && is(typeof(T(value)))) +private T toImpl(T, S)(S value) +if (!isImplicitlyConvertible!(S, T) && + is(T == struct) && is(typeof(T(value)))) { return T(value); } @@ -588,9 +668,9 @@ T toImpl(T, S)(S value) } /// ditto -T toImpl(T, S)(S value) - if (!isImplicitlyConvertible!(S, T) && - is(T == class) && is(typeof(new T(value)))) +private T toImpl(T, S)(S value) +if (!isImplicitlyConvertible!(S, T) && + is(T == class) && is(typeof(new T(value)))) { return new T(value); } @@ -641,7 +721,7 @@ T toImpl(T, S)(S value) } S.B b = new S.B(); - S.A a = to!(S.A)(b); // == cast(S.A)b + S.A a = to!(S.A)(b); // == cast(S.A) b // (do not run construction conversion like new S.A(b)) assert(b is a); @@ -661,10 +741,10 @@ T toImpl(T, S)(S value) Object-to-object conversions by dynamic casting throw exception when the source is non-null and the target is null. */ -T toImpl(T, S)(S value) - if (!isImplicitlyConvertible!(S, T) && - (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) && - (is(T == class) || is(T == interface)) && !is(typeof(new T(value)))) +private T toImpl(T, S)(S value) +if (!isImplicitlyConvertible!(S, T) && + (is(S == class) || is(S == interface)) && !is(typeof(value.opCast!T()) : T) && + (is(T == class) || is(T == interface)) && !is(typeof(new T(value)))) { static if (is(T == immutable)) { @@ -712,19 +792,6 @@ T toImpl(T, S)(S value) return result; } -@safe pure unittest -{ - import std.exception; - // Testing object conversions - class A {} - class B : A {} - class C : A {} - A a1 = new A, a2 = new B, a3 = new C; - assert(to!B(a2) is a2); - assert(to!C(a3) is a3); - assertThrown!ConvException(to!B(a3)); -} - // Unittest for 6288 @safe pure unittest { @@ -735,7 +802,8 @@ T toImpl(T, S)(S value) alias toShared(T) = shared T; alias toSharedConst(T) = shared const T; alias toImmutable(T) = immutable T; - template AddModifier(int n) if (0 <= n && n < 5) + template AddModifier(int n) + if (0 <= n && n < 5) { static if (n == 0) alias AddModifier = Identity; else static if (n == 1) alias AddModifier = toConst; @@ -752,13 +820,11 @@ T toImpl(T, S)(S value) class C : B, I, J {} class D : I {} - foreach (m1; TypeTuple!(0,1,2,3,4)) // enumerate modifiers - foreach (m2; TypeTuple!(0,1,2,3,4)) // ditto + foreach (m1; AliasSeq!(0,1,2,3,4)) // enumerate modifiers + foreach (m2; AliasSeq!(0,1,2,3,4)) // ditto (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 alias srcmod = AddModifier!m1; alias tgtmod = AddModifier!m2; - //pragma(msg, srcmod!Object, " -> ", tgtmod!Object, ", convertible = ", - // isImplicitlyConvertible!(srcmod!Object, tgtmod!Object)); // Compile time convertible equals to modifier convertible. static if (isImplicitlyConvertible!(srcmod!Object, tgtmod!Object)) @@ -794,40 +860,12 @@ T toImpl(T, S)(S value) } /** -Stringize conversion from all types is supported. -$(UL - $(LI String _to string conversion works for any two string types having - ($(D char), $(D wchar), $(D dchar)) character widths and any - combination of qualifiers (mutable, $(D const), or $(D immutable)).) - $(LI Converts array (other than strings) to string. - Each element is converted by calling $(D to!T).) - $(LI Associative array to string conversion. - Each element is printed by calling $(D to!T).) - $(LI Object to string conversion calls $(D toString) against the object or - returns $(D "null") if the object is null.) - $(LI Struct to string conversion calls $(D toString) against the struct if - it is defined.) - $(LI For structs that do not define $(D toString), the conversion to string - produces the list of fields.) - $(LI Enumerated types are converted to strings as their symbolic names.) - $(LI Boolean values are printed as $(D "true") or $(D "false").) - $(LI $(D char), $(D wchar), $(D dchar) to a string type.) - $(LI Unsigned or signed integers to strings. - $(DL $(DT [special case]) - $(DD Convert integral value to string in $(D_PARAM radix) radix. - radix must be a value from 2 to 36. - value is treated as a signed value only if radix is 10. - The characters A through Z are used to represent values 10 through 36 - and their case is determined by the $(D_PARAM letterCase) parameter.))) - $(LI All floating point types to all string types.) - $(LI Pointer to string conversions prints the pointer as a $(D size_t) value. - If pointer is $(D char*), treat it as C-style strings. - In that case, this function is $(D @system).)) +Handles type _to string conversions */ -T toImpl(T, S)(S value) - if (!(isImplicitlyConvertible!(S, T) && - !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && - !isInfinite!S && isExactSomeString!T) +private T toImpl(T, S)(S value) +if (!(isImplicitlyConvertible!(S, T) && + !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && + !isInfinite!S && isExactSomeString!T) { static if (isExactSomeString!S && value[0].sizeof == ElementEncodingType!T.sizeof) { @@ -872,17 +910,27 @@ T toImpl(T, S)(S value) ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }(); return cast(T) result; } - else static if (isPointer!S && is(S : const(char)*)) + else static if (isPointer!S && isSomeChar!(PointerTarget!S)) { - import core.stdc.string : strlen; - // It is unsafe because we cannot guarantee that the pointer is null terminated. - return value ? cast(T) value[0 .. strlen(value)].dup : null; + // This is unsafe because we cannot guarantee that the pointer is null terminated. + return () @system { + static if (is(S : const(char)*)) + import core.stdc.string : strlen; + else + size_t strlen(S s) nothrow + { + S p = s; + while (*p++) {} + return p-s-1; + } + return toImpl!T(value ? value[0 .. strlen(value)].dup : null); + }(); } else static if (isSomeString!T && is(S == enum)) { static if (isSwitchable!(OriginalType!S) && EnumMembers!S.length <= 50) { - switch(value) + switch (value) { foreach (member; NoDuplicates!(EnumMembers!S)) { @@ -911,7 +959,7 @@ T toImpl(T, S)(S value) app.put(S.stringof); app.put(')'); FormatSpec!char f; - formatValue(app, cast(OriginalType!S)value, f); + formatValue(app, cast(OriginalType!S) value, f); return app.data; } else @@ -922,11 +970,40 @@ T toImpl(T, S)(S value) } // Bugzilla 14042 -unittest +@system unittest { immutable(char)* ptr = "hello".ptr; auto result = ptr.to!(char[]); } +// Bugzilla 8384 +@system unittest +{ + void test1(T)(T lp, string cmp) + { + foreach (e; AliasSeq!(char, wchar, dchar)) + { + test2!(e[])(lp, cmp); + test2!(const(e)[])(lp, cmp); + test2!(immutable(e)[])(lp, cmp); + } + } + + void test2(D, S)(S lp, string cmp) + { + assert(to!string(to!D(lp)) == cmp); + } + + foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d)) + { + test1(e, "Hello, world!"); + test1(e.ptr, "Hello, world!"); + } + foreach (e; AliasSeq!("", ""w, ""d)) + { + test1(e, ""); + test1(e.ptr, ""); + } +} /* Check whether type $(D T) can be used in a switch statement. @@ -940,7 +1017,7 @@ private template isSwitchable(E) } // -unittest +@safe unittest { static assert(isSwitchable!int); static assert(!isSwitchable!double); @@ -962,15 +1039,15 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) void dg() { // string to string conversion - alias Chars = TypeTuple!(char, wchar, dchar); + alias Chars = AliasSeq!(char, wchar, dchar); foreach (LhsC; Chars) { - alias LhStrings = TypeTuple!(LhsC[], const(LhsC)[], immutable(LhsC)[]); + alias LhStrings = AliasSeq!(LhsC[], const(LhsC)[], immutable(LhsC)[]); foreach (Lhs; LhStrings) { foreach (RhsC; Chars) { - alias RhStrings = TypeTuple!(RhsC[], const(RhsC)[], immutable(RhsC)[]); + alias RhStrings = AliasSeq!(RhsC[], const(RhsC)[], immutable(RhsC)[]); foreach (Rhs; RhStrings) { Lhs s1 = to!Lhs("wyda"); @@ -1001,24 +1078,6 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) } @safe pure unittest -{ - // Conversion reinterpreting void array to string - auto a = "abcx"w; - const(void)[] b = a; - assert(b.length == 8); - - auto c = to!(wchar[])(b); - assert(c == "abcx"); -} - -@system pure nothrow unittest -{ - // char* to string conversion - assert(to!string(cast(char*) null) == ""); - assert(to!string("foo\0".ptr) == "foo"); -} - -@safe pure /+nothrow+/ unittest { // Conversion representing bool value with string bool b; @@ -1031,9 +1090,9 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) { // Conversion representing character value with string alias AllChars = - TypeTuple!( char, const( char), immutable( char), - wchar, const(wchar), immutable(wchar), - dchar, const(dchar), immutable(dchar)); + AliasSeq!( char, const( char), immutable( char), + wchar, const(wchar), immutable(wchar), + dchar, const(dchar), immutable(dchar)); foreach (Char1; AllChars) { foreach (Char2; AllChars) @@ -1051,7 +1110,6 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) { s2 ~= to!string(c); } - //printf("%.*s", s2); assert(s2 == "foo"); } @@ -1060,14 +1118,14 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) import std.exception; // Conversion representing integer values with string - foreach (Int; TypeTuple!(ubyte, ushort, uint, ulong)) + foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong)) { assert(to!string(Int(0)) == "0"); assert(to!string(Int(9)) == "9"); assert(to!string(Int(123)) == "123"); } - foreach (Int; TypeTuple!(byte, short, int, long)) + foreach (Int; AliasSeq!(byte, short, int, long)) { assert(to!string(Int(0)) == "0"); assert(to!string(Int(9)) == "9"); @@ -1090,28 +1148,13 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) }); } -@safe pure /+nothrow+/ unittest -{ - // Conversion representing dynamic/static array with string - long[] b = [ 1, 3, 5 ]; - auto s = to!string(b); - assert(to!string(b) == "[1, 3, 5]", s); -} -/*@safe pure */unittest // sprintf issue +@safe unittest // sprintf issue { double[2] a = [ 1.5, 2.5 ]; assert(to!string(a) == "[1.5, 2.5]"); } -/*@safe pure */unittest -{ - // Conversion representing associative array with string - int[string] a = ["0":1, "1":2]; - assert(to!string(a) == `["0":1, "1":2]` || - to!string(a) == `["1":2, "0":1]`); -} - -unittest +@system unittest { // Conversion representing class object with string class A @@ -1130,7 +1173,7 @@ unittest assert(to!string(s) == "C"); } -unittest +@safe unittest { // Conversion representing struct object with string struct S1 @@ -1158,7 +1201,7 @@ unittest assert(to!string(s8080) == ""); } -/+nothrow+/ unittest +@safe unittest { // Conversion representing enum value with string enum EB : bool { a = true } @@ -1168,7 +1211,7 @@ unittest enum EC : char { a = 'x', b = 'y' } enum ES : string { a = "aaa", b = "bbb" } - foreach (E; TypeTuple!(EB, EU, EI, EF, EC, ES)) + foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) { assert(to! string(E.a) == "a"c); assert(to!wstring(E.a) == "a"w); @@ -1176,13 +1219,13 @@ unittest } // Test an value not corresponding to an enum member. - auto o = cast(EU)5; + auto o = cast(EU) 5; assert(to! string(o) == "cast(EU)5"c); assert(to!wstring(o) == "cast(EU)5"w); assert(to!dstring(o) == "cast(EU)5"d); } -unittest +@safe unittest { enum E { @@ -1196,7 +1239,7 @@ unittest assert(to!string(E.doo) == "foo"); assert(to!string(E.bar) == "bar"); - foreach (S; TypeTuple!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[]))) + foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[]))) { auto s1 = to!S(E.foo); auto s2 = to!S(E.foo); @@ -1205,7 +1248,7 @@ unittest assert(s1 is s2); } - foreach (S; TypeTuple!(char[], wchar[], dchar[])) + foreach (S; AliasSeq!(char[], wchar[], dchar[])) { auto s1 = to!S(E.foo); auto s2 = to!S(E.foo); @@ -1215,10 +1258,10 @@ unittest } } -/// ditto -@trusted pure T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper) - if (isIntegral!S && - isExactSomeString!T) +// ditto +@trusted pure private T toImpl(T, S)(S value, uint radix, LetterCase letterCase = LetterCase.upper) +if (isIntegral!S && + isExactSomeString!T) in { assert(radix >= 2 && radix <= 36); @@ -1227,12 +1270,9 @@ body { alias EEType = Unqual!(ElementEncodingType!T); - T toStringRadixConvert(size_t bufLen, uint radix = 0, bool neg = false)(uint runtimeRadix = 0) + T toStringRadixConvert(size_t bufLen)(uint runtimeRadix = 0) { - static if (neg) - ulong div = void, mValue = unsigned(-value); - else - Unsigned!(Unqual!S) div = void, mValue = unsigned(value); + Unsigned!(Unqual!S) div = void, mValue = unsigned(value); size_t index = bufLen; EEType[bufLen] buffer = void; @@ -1241,55 +1281,41 @@ body do { - static if (radix == 0) - { - div = cast(S)(mValue / runtimeRadix ); - mod = cast(ubyte)(mValue % runtimeRadix); - mod += mod < 10 ? '0' : baseChar - 10; - } - else static if (radix > 10) - { - div = cast(S)(mValue / radix ); - mod = cast(ubyte)(mValue % radix); - mod += mod < 10 ? '0' : baseChar - 10; - } - else - { - div = cast(S)(mValue / radix); - mod = mValue % radix + '0'; - } - buffer[--index] = cast(char)mod; + div = cast(S)(mValue / runtimeRadix ); + mod = cast(ubyte)(mValue % runtimeRadix); + mod += mod < 10 ? '0' : baseChar - 10; + buffer[--index] = cast(char) mod; mValue = div; } while (mValue); - static if (neg) - { - buffer[--index] = '-'; - } - return cast(T)buffer[index .. $].dup; + return cast(T) buffer[index .. $].dup; } - switch(radix) + import std.array : array; + switch (radix) { case 10: - if (value < 0) - return toStringRadixConvert!(S.sizeof * 3 + 1, 10, true)(); - else - return toStringRadixConvert!(S.sizeof * 3, 10)(); + // The (value+0) is so integral promotions happen to the type + return toChars!(10, EEType)(value + 0).array; case 16: - return toStringRadixConvert!(S.sizeof * 2, 16)(); + // The unsigned(unsigned(value)+0) is so unsigned integral promotions happen to the type + if (letterCase == letterCase.upper) + return toChars!(16, EEType, LetterCase.upper)(unsigned(unsigned(value) + 0)).array; + else + return toChars!(16, EEType, LetterCase.lower)(unsigned(unsigned(value) + 0)).array; case 2: - return toStringRadixConvert!(S.sizeof * 8, 2)(); + return toChars!(2, EEType)(unsigned(unsigned(value) + 0)).array; case 8: - return toStringRadixConvert!(S.sizeof * 3, 8)(); + return toChars!(8, EEType)(unsigned(unsigned(value) + 0)).array; + default: - return toStringRadixConvert!(S.sizeof * 6)(radix); + return toStringRadixConvert!(S.sizeof * 6)(radix); } } @safe pure nothrow unittest { - foreach (Int; TypeTuple!(uint, ulong)) + foreach (Int; AliasSeq!(uint, ulong)) { assert(to!string(Int(16), 16) == "10"); assert(to!string(Int(15), 2u) == "1111"); @@ -1299,7 +1325,7 @@ body assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af"); } - foreach (Int; TypeTuple!(int, long)) + foreach (Int; AliasSeq!(int, long)) { assert(to!string(Int(-10), 10u) == "-10"); } @@ -1309,15 +1335,14 @@ body assert(to!string(long.max) == "9223372036854775807"); } - /** Narrowing numeric-numeric conversions throw when the value does not fit in the narrower type. */ -T toImpl(T, S)(S value) - if (!isImplicitlyConvertible!(S, T) && - (isNumeric!S || isSomeChar!S || isBoolean!S) && - (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum)) +private T toImpl(T, S)(S value) +if (!isImplicitlyConvertible!(S, T) && + (isNumeric!S || isSomeChar!S || isBoolean!S) && + (isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum)) { enum sSmallest = mostNegative!S; enum tSmallest = mostNegative!T; @@ -1370,25 +1395,25 @@ T toImpl(T, S)(S value) dchar to4 = to!dchar(from4); } -unittest +@safe unittest { import std.exception; // Narrowing conversions from enum -> integral should be allowed, but they // should throw at runtime if the enum value doesn't fit in the target // type. - enum E1 : ulong { A = 1, B = 1UL<<48, C = 0 } + enum E1 : ulong { A = 1, B = 1UL << 48, C = 0 } assert(to!int(E1.A) == 1); assert(to!bool(E1.A) == true); assertThrown!ConvOverflowException(to!int(E1.B)); // E1.B overflows int assertThrown!ConvOverflowException(to!bool(E1.B)); // E1.B overflows bool assert(to!bool(E1.C) == false); - enum E2 : long { A = -1L<<48, B = -1<<31, C = 1<<31 } + enum E2 : long { A = -1L << 48, B = -1 << 31, C = 1 << 31 } assertThrown!ConvOverflowException(to!int(E2.A)); // E2.A overflows int assertThrown!ConvOverflowException(to!uint(E2.B)); // E2.B overflows uint - assert(to!int(E2.B) == -1<<31); // but does not overflow int - assert(to!int(E2.C) == 1<<31); // E2.C does not overflow int + assert(to!int(E2.B) == -1 << 31); // but does not overflow int + assert(to!int(E2.C) == 1 << 31); // E2.C does not overflow int enum E3 : int { A = -1, B = 1, C = 255, D = 0 } assertThrown!ConvOverflowException(to!ubyte(E3.A)); @@ -1407,10 +1432,10 @@ unittest Array-to-array conversion (except when target is a string type) converts each element in turn by using $(D to). */ -T toImpl(T, S)(S value) - if (!isImplicitlyConvertible!(S, T) && - !isSomeString!S && isDynamicArray!S && - !isExactSomeString!T && isArray!T) +private T toImpl(T, S)(S value) +if (!isImplicitlyConvertible!(S, T) && + !isSomeString!S && isDynamicArray!S && + !isExactSomeString!T && isArray!T) { alias E = typeof(T.init[0]); @@ -1444,9 +1469,6 @@ T toImpl(T, S)(S value) auto b = to!(float[])(a); assert(b == [ 1.0f, 2, 3 ]); - //auto c = to!(string[])(b); - //assert(c[0] == "1" && c[1] == "2" && c[2] == "3"); - immutable(int)[3] d = [ 1, 2, 3 ]; b = to!(float[])(d); assert(b == [ 1.0f, 2, 3 ]); @@ -1479,7 +1501,8 @@ T toImpl(T, S)(S value) ex ? ex.msg : "Exception was not thrown!"); } } -/*@safe pure */unittest + +@safe unittest { auto b = [ 1.0f, 2, 3 ]; @@ -1491,9 +1514,9 @@ T toImpl(T, S)(S value) Associative array to associative array conversion converts each key and each value in turn. */ -T toImpl(T, S)(S value) - if (isAssociativeArray!S && - isAssociativeArray!T && !is(T == enum)) +private T toImpl(T, S)(S value) +if (isAssociativeArray!S && + isAssociativeArray!T && !is(T == enum)) { /* This code is potentially unsafe. */ @@ -1509,10 +1532,10 @@ T toImpl(T, S)(S value) result[to!K2(k1)] = cast(Unqual!V2) to!V2(v1); } // Cast back to original type - return cast(T)result; + return cast(T) result; } -@safe /*pure */unittest +@safe unittest { // hash to hash conversions int[string] a; @@ -1521,7 +1544,7 @@ T toImpl(T, S)(S value) auto b = to!(double[dstring])(a); assert(b["0"d] == 1 && b["1"d] == 2); } -@safe /*pure */unittest // Bugzilla 8705, from doc +@safe unittest // Bugzilla 8705, from doc { import std.exception; int[string][double[int[]]] a; @@ -1529,7 +1552,7 @@ T toImpl(T, S)(S value) a = [null:["hello":int.max]]; assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a)); } -unittest // Extra cases for AA with qualifiers conversion +@system unittest // Extra cases for AA with qualifiers conversion { int[][int[]] a;// = [[], []]; auto b = to!(immutable(short[])[immutable short[]])(a); @@ -1587,8 +1610,6 @@ private void testFloatingToIntegral(Floating, Integral)() assert(convFails!(Floating, Integral, ConvOverflowException)(a) || Floating.sizeof <= Integral.sizeof); a = 0.0 + Integral.max; -// fwritefln(stderr, "%s a=%g, %s conv=%s", Floating.stringof, a, -// Integral.stringof, to!Integral(a)); assert(to!Integral(a) == Integral.max || Floating.sizeof <= Integral.sizeof); ++a; // no more representable as an Integral assert(convFails!(Floating, Integral, ConvOverflowException)(a) @@ -1609,9 +1630,9 @@ private void testFloatingToIntegral(Floating, Integral)() @safe pure unittest { - alias AllInts = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong); - alias AllFloats = TypeTuple!(float, double, real); - alias AllNumerics = TypeTuple!(AllInts, AllFloats); + alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); + alias AllFloats = AliasSeq!(float, double, real); + alias AllNumerics = AliasSeq!(AllInts, AllFloats); // test with same type { foreach (T; AllNumerics) @@ -1634,10 +1655,11 @@ private void testFloatingToIntegral(Floating, Integral)() assert(to!long(to!double(b)) == b); assert(to!long(to!double(-b)) == -b); // real - // @@@ BUG IN COMPILER @@@ -// ulong c = 18_446_744_073_709_551_615UL; // 2^64 - 1 -// assert(to!ulong(to!real(c)) == c); -// assert(to!ulong(-to!real(c)) == c); + static if (real.mant_dig >= 64) + { + ulong c = 18_446_744_073_709_551_615UL; // 2^64 - 1 + assert(to!ulong(to!real(c)) == c); + } } // test conversions floating => integral { @@ -1683,24 +1705,25 @@ private void testFloatingToIntegral(Floating, Integral)() } } } -/*@safe pure */unittest + +@safe unittest { - alias AllInts = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong); - alias AllFloats = TypeTuple!(float, double, real); - alias AllNumerics = TypeTuple!(AllInts, AllFloats); + alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong); + alias AllFloats = AliasSeq!(float, double, real); + alias AllNumerics = AliasSeq!(AllInts, AllFloats); // test conversions to string { foreach (T; AllNumerics) { T a = 42; assert(to!string(a) == "42"); - //assert(to!wstring(a) == "42"w); - //assert(to!dstring(a) == "42"d); + assert(to!wstring(a) == "42"w); + assert(to!dstring(a) == "42"d); // array test -// T[] b = new T[2]; -// b[0] = 42; -// b[1] = 33; -// assert(to!string(b) == "[42,33]"); + T[] b = new T[2]; + b[0] = 42; + b[1] = 33; + assert(to!string(b) == "[42, 33]"); } } // test array to string conversion @@ -1710,27 +1733,27 @@ private void testFloatingToIntegral(Floating, Integral)() assert(to!string(a) == "[1, 2, 3]"); } // test enum to int conversion - // enum Testing { Test1, Test2 }; - // Testing t; - // auto a = to!string(t); - // assert(a == "0"); + enum Testing { Test1, Test2 } + Testing t; + auto a = to!string(t); + assert(a == "Test1"); } /** -String to non-string conversion runs parsing. +String, or string-like input range, to non-string conversion runs parsing. $(UL $(LI When the source is a wide string, it is first converted to a narrow string and then parsed.) $(LI When the source is a narrow string, normal text parsing occurs.)) */ -T toImpl(T, S)(S value) - if ( isExactSomeString!S && isDynamicArray!S && - !isExactSomeString!T && is(typeof(parse!T(value)))) +private T toImpl(T, S)(S value) +if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && + !isExactSomeString!T && is(typeof(parse!T(value)))) { scope(success) { - if (value.length) + if (!value.empty) { throw convError!(S, T)(value); } @@ -1739,13 +1762,13 @@ T toImpl(T, S)(S value) } /// ditto -T toImpl(T, S)(S value, uint radix) - if ( isExactSomeString!S && isDynamicArray!S && - !isExactSomeString!T && is(typeof(parse!T(value, radix)))) +private T toImpl(T, S)(S value, uint radix) +if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) && + !isExactSomeString!T && is(typeof(parse!T(value, radix)))) { scope(success) { - if (value.length) + if (!value.empty) { throw convError!(S, T)(value); } @@ -1762,7 +1785,7 @@ T toImpl(T, S)(S value, uint radix) @safe pure unittest { - foreach (Str; TypeTuple!(string, wstring, dstring)) + foreach (Str; AliasSeq!(string, wstring, dstring)) { Str a = "123"; assert(to!int(a) == 123); @@ -1774,16 +1797,34 @@ T toImpl(T, S)(S value, uint radix) assert(n == 255); } +// bugzilla 15800 +@safe unittest +{ + import std.utf : byCodeUnit, byChar, byWchar, byDchar; + + assert(to!int(byCodeUnit("10")) == 10); + assert(to!int(byCodeUnit("10"), 10) == 10); + assert(to!int(byCodeUnit("10"w)) == 10); + assert(to!int(byCodeUnit("10"w), 10) == 10); + + assert(to!int(byChar("10")) == 10); + assert(to!int(byChar("10"), 10) == 10); + assert(to!int(byWchar("10")) == 10); + assert(to!int(byWchar("10"), 10) == 10); + assert(to!int(byDchar("10")) == 10); + assert(to!int(byDchar("10"), 10) == 10); +} + /** Convert a value that is implicitly convertible to the enum base type into an Enum value. If the value does not match any enum member values a ConvException is thrown. Enums with floating-point or string base types are not supported. */ -T toImpl(T, S)(S value) - if (is(T == enum) && !is(S == enum) - && is(typeof(value == OriginalType!T.init)) - && !isFloatingPoint!(OriginalType!T) && !isSomeString!(OriginalType!T)) +private T toImpl(T, S)(S value) +if (is(T == enum) && !is(S == enum) + && is(typeof(value == OriginalType!T.init)) + && !isFloatingPoint!(OriginalType!T) && !isSomeString!(OriginalType!T)) { foreach (Member; EnumMembers!T) { @@ -1826,7 +1867,7 @@ template roundTo(Target) } /// -unittest +@safe unittest { assert(roundTo!int(3.14) == 3); assert(roundTo!int(3.49) == 3); @@ -1839,11 +1880,11 @@ unittest assert(roundTo!(const int)(to!(const double)(-3.999)) == -4); } -unittest +@safe unittest { import std.exception; // boundary values - foreach (Int; TypeTuple!(byte, ubyte, short, ushort, int, uint)) + foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint)) { assert(roundTo!Int(Int.min - 0.4L) == Int.min); assert(roundTo!Int(Int.max + 0.4L) == Int.max); @@ -1852,25 +1893,53 @@ unittest } } -/*************************************************************** - * The $(D_PARAM parse) family of functions works quite like the - * $(D_PARAM to) family, except that (1) it only works with character ranges - * as input, (2) takes the input by reference and advances it to - * the position following the conversion, and (3) does not throw if it - * could not convert the entire input. It still throws if an overflow - * occurred during conversion or if no character of the input - * was meaningfully converted. - */ -Target parse(Target, Source)(ref Source s) - if (isInputRange!Source && - isSomeChar!(ElementType!Source) && - is(Unqual!Target == bool)) +/** +The $(D parse) family of functions works quite like the $(D to) +family, except that: +$(OL + $(LI It only works with character ranges as input.) + $(LI It takes the input by reference. (This means that rvalues - such + as string literals - are not accepted: use $(D to) instead.)) + $(LI It advances the input to the position following the conversion.) + $(LI It does not throw if it could not convert the entire input.)) + +This overload converts an character input range to a `bool`. + +Params: + Target = the type to convert to + source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + +Returns: + A `bool` + +Throws: + A $(LREF ConvException) if the range does not represent a `bool`. + +Note: + All character input range conversions using $(LREF to) are forwarded + to `parse` and do not require lvalues. +*/ +Target parse(Target, Source)(ref Source source) +if (isInputRange!Source && + isSomeChar!(ElementType!Source) && + is(Unqual!Target == bool)) { import std.ascii : toLower; + + static if (isNarrowString!Source) + { + import std.string : representation, assumeUTF; + auto s = source.representation; + } + else + { + alias s = source; + } + if (!s.empty) { auto c1 = toLower(s.front); - bool result = (c1 == 't'); + bool result = c1 == 't'; if (result || c1 == 'f') { s.popFront(); @@ -1880,6 +1949,10 @@ Target parse(Target, Source)(ref Source s) goto Lerr; s.popFront(); } + + static if (isNarrowString!Source) + source = s.assumeUTF; + return result; } } @@ -1888,24 +1961,17 @@ Lerr: } /// -unittest +@safe unittest { - import std.string : munch; - string test = "123 \t 76.14"; - auto a = parse!uint(test); - assert(a == 123); - assert(test == " \t 76.14"); // parse bumps string - munch(test, " \t\n\r"); // skip ws - assert(test == "76.14"); - auto b = parse!double(test); - assert(b == 76.14); - assert(test == ""); + auto s = "true"; + bool b = parse!bool(s); + assert(b); } -unittest +@safe unittest { import std.exception; - import std.algorithm : equal; + import std.algorithm.comparison : equal; struct InputString { string _s; @@ -1935,9 +2001,24 @@ unittest } } +/** +Parses a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives) +to an integral value. + +Params: + Target = the integral type to convert to + s = the lvalue of an input range + +Returns: + A number of type `Target` + +Throws: + A $(LREF ConvException) If an overflow occurred during conversion or + if no character of the input was meaningfully converted. +*/ Target parse(Target, Source)(ref Source s) - if (isSomeChar!(ElementType!Source) && - isIntegral!Target && !is(Target == enum)) +if (isSomeChar!(ElementType!Source) && + isIntegral!Target && !is(Target == enum)) { static if (Target.sizeof < int.sizeof) { @@ -1950,21 +2031,31 @@ Target parse(Target, Source)(ref Source s) } else { - // Larger than int types + // int or larger types static if (Target.min < 0) - bool sign = 0; + bool sign = false; else - enum bool sign = 0; + enum bool sign = false; enum char maxLastDigit = Target.min < 0 ? 7 : 5; - Unqual!(typeof(s.front)) c; + uint c; - if (s.empty) + static if (isNarrowString!Source) + { + import std.string : representation, assumeUTF; + auto source = s.representation; + } + else + { + alias source = s; + } + + if (source.empty) goto Lerr; - c = s.front; - s.popFront(); + c = source.front; + static if (Target.min < 0) { switch (c) @@ -1973,10 +2064,13 @@ Target parse(Target, Source)(ref Source s) sign = true; goto case '+'; case '+': - if (s.empty) + source.popFront(); + + if (source.empty) goto Lerr; - c = s.front; - s.popFront(); + + c = source.front; + break; default: @@ -1986,10 +2080,14 @@ Target parse(Target, Source)(ref Source s) c -= '0'; if (c <= 9) { - Target v = cast(Target)c; - while (!s.empty) + Target v = cast(Target) c; + + source.popFront(); + + while (!source.empty) { - c = s.front - '0'; + c = cast(typeof(c)) (source.front - '0'); + if (c > 9) break; @@ -1999,7 +2097,8 @@ Target parse(Target, Source)(ref Source s) // Note: `v` can become negative here in case of parsing // the most negative value: v = cast(Target) (v * 10 + c); - s.popFront(); + + source.popFront(); } else throw new ConvOverflowException("Overflow in integral conversion"); @@ -2007,24 +2106,51 @@ Target parse(Target, Source)(ref Source s) if (sign) v = -v; + + static if (isNarrowString!Source) + s = source.assumeUTF; + return v; } Lerr: - throw convError!(Source, Target)(s); + static if (isNarrowString!Source) + throw convError!(Source, Target)(source.assumeUTF); + else + throw convError!(Source, Target)(source); } } +/// @safe pure unittest { string s = "123"; auto a = parse!int(s); + assert(a == 123); + + // parse only accepts lvalues + static assert(!__traits(compiles, parse!int("123"))); } +/// @safe pure unittest { - foreach (Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) - { - { + import std.string : munch; + string test = "123 \t 76.14"; + auto a = parse!uint(test); + assert(a == 123); + assert(test == " \t 76.14"); // parse bumps string + munch(test, " \t\n\r"); // skip ws + assert(test == "76.14"); + auto b = parse!double(test); + assert(b == 76.14); + assert(test == ""); +} + +@safe pure unittest +{ + foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) + { + { assert(to!Int("0") == 0); static if (isSigned!Int) @@ -2118,7 +2244,7 @@ Lerr: { import std.exception; // parsing error check - foreach (Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { { immutable string[] errors1 = @@ -2157,7 +2283,7 @@ Lerr: } // positive overflow check - foreach (i, Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { immutable string[] errors = [ @@ -2175,7 +2301,7 @@ Lerr: } // negative overflow check - foreach (i, Int; TypeTuple!(byte, short, int, long)) + foreach (i, Int; AliasSeq!(byte, short, int, long)) { immutable string[] errors = [ @@ -2189,6 +2315,36 @@ Lerr: } } +@safe pure unittest +{ + void checkErrMsg(string input, dchar charInMsg, dchar charNotInMsg) + { + try + { + int x = input.to!int(); + assert(false, "Invalid conversion did not throw"); + } + catch (ConvException e) + { + // Ensure error message contains failing character, not the character + // beyond. + import std.algorithm.searching : canFind; + assert( e.msg.canFind(charInMsg) && + !e.msg.canFind(charNotInMsg)); + } + catch (Exception e) + { + assert(false, "Did not throw ConvException"); + } + } + checkErrMsg("@$", '@', '$'); + checkErrMsg("@$123", '@', '$'); + checkErrMsg("1@$23", '@', '$'); + checkErrMsg("1@$", '@', '$'); + checkErrMsg("1@$2", '@', '$'); + checkErrMsg("12@$", '@', '$'); +} + @safe pure unittest { import std.exception; @@ -2206,10 +2362,26 @@ Lerr: assertThrown!ConvOverflowException("-92233720368547758080".to!long()); } +// Issue 14396 +@safe pure unittest +{ + struct StrInputRange + { + this (string s) { str = s; } + char front() const @property { return str[front_index]; } + char popFront() { return str[front_index++]; } + bool empty() const @property { return str.length <= front_index; } + string str; + size_t front_index = 0; + } + auto input = StrInputRange("777"); + assert(parse!int(input) == 777); +} + /// ditto -Target parse(Target, Source)(ref Source s, uint radix) - if (isSomeChar!(ElementType!Source) && - isIntegral!Target && !is(Target == enum)) +Target parse(Target, Source)(ref Source source, uint radix) +if (isSomeChar!(ElementType!Source) && + isIntegral!Target && !is(Target == enum)) in { assert(radix >= 2 && radix <= 36); @@ -2217,15 +2389,27 @@ in body { import core.checkedint : mulu, addu; + import std.exception : enforce; + if (radix == 10) - return parse!Target(s); + return parse!Target(source); - immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix; + enforce!ConvException(!source.empty, "s must not be empty in integral parse"); + immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix; Target v = 0; - size_t atStart = true; - for (; !s.empty; s.popFront()) + static if (isNarrowString!Source) + { + import std.string : representation, assumeUTF; + auto s = source.representation; + } + else + { + alias s = source; + } + + do { uint c = s.front; if (c < '0') @@ -2248,54 +2432,35 @@ body bool overflow = false; auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow); - if (overflow || nextv > Target.max) - goto Loverflow; + enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion"); v = cast(Target) nextv; + s.popFront(); + } while (!s.empty); - atStart = false; - } - if (atStart) - goto Lerr; - return v; + static if (isNarrowString!Source) + source = s.assumeUTF; -Loverflow: - throw new ConvOverflowException("Overflow in integral conversion"); -Lerr: - throw convError!(Source, Target)(s, radix); + return v; } @safe pure unittest { - // @@@BUG@@@ the size of China - // foreach (i; 2..37) - // { - // assert(parse!int("0",i) == 0); - // assert(parse!int("1",i) == 1); - // assert(parse!byte("10",i) == i); - // } - foreach (i; 2..37) - { - string s = "0"; - assert(parse!int(s,i) == 0); - s = "1"; - assert(parse!int(s,i) == 1); - s = "10"; - assert(parse!byte(s,i) == i); - } - // Same @@@BUG@@@ as above - //assert(parse!int("0011001101101", 2) == 0b0011001101101); - // assert(parse!int("765",8) == 0765); - // assert(parse!int("fCDe",16) == 0xfcde); - auto s = "0011001101101"; - assert(parse!int(s, 2) == 0b0011001101101); - s = "765"; - assert(parse!int(s, 8) == octal!765); - s = "fCDe"; - assert(parse!int(s, 16) == 0xfcde); + string s; // parse doesn't accept rvalues + foreach (i; 2 .. 37) + { + assert(parse!int(s = "0", i) == 0); + assert(parse!int(s = "1", i) == 1); + assert(parse!byte(s = "10", i) == i); + } + + assert(parse!int(s = "0011001101101", 2) == 0b0011001101101); + assert(parse!int(s = "765", 8) == octal!765); + assert(parse!int(s = "fCDe", 16) == 0xfcde); // 6609 - s = "-42"; - assert(parse!int(s, 10) == -42); + assert(parse!int(s = "-42", 10) == -42); + + assert(parse!ubyte(s = "ff", 16) == 0xFF); } @safe pure unittest // bugzilla 7302 @@ -2314,11 +2479,25 @@ Lerr: assertThrown!ConvOverflowException(s.parse!ubyte(16)); } +/** + * Takes a string representing an `enum` type and returns that type. + * + * Params: + * Target = the `enum` type to convert to + * s = the lvalue of the range to _parse + * + * Returns: + * An `enum` of type `Target` + * + * Throws: + * A $(LREF ConvException) if type `Target` does not have a member + * represented by `s`. + */ Target parse(Target, Source)(ref Source s) - if (isExactSomeString!Source && - is(Target == enum)) +if (isSomeString!Source && !is(Source == enum) && + is(Target == enum)) { - import std.algorithm : startsWith; + import std.algorithm.searching : startsWith; Target result; size_t longest_match = 0; @@ -2343,7 +2522,16 @@ Target parse(Target, Source)(ref Source s) ~ to!string(s) ~ "'"); } -unittest +/// +@safe unittest +{ + enum EnumType : bool { a = true, b = false, c = a } + + auto str = "a"; + assert(parse!EnumType(str) == EnumType.a); +} + +@safe unittest { import std.exception; @@ -2354,7 +2542,7 @@ unittest enum EC : char { a = 'a', b = 'b', c = 'c' } enum ES : string { a = "aaa", b = "bbb", c = "ccc" } - foreach (E; TypeTuple!(EB, EU, EI, EF, EC, ES)) + foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES)) { assert(to!E("a"c) == E.a); assert(to!E("b"w) == E.b); @@ -2374,22 +2562,44 @@ unittest assert(parse!A(s) == A.member111 && s == "1"); } -Target parse(Target, Source)(ref Source p) - if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && - isFloatingPoint!Target && !is(Target == enum)) +/** + * Parses a character range to a floating point number. + * + * Params: + * Target = a floating point type + * source = the lvalue of the range to _parse + * + * Returns: + * A floating point number of type `Target` + * + * Throws: + * A $(LREF ConvException) if `p` is empty, if no number could be + * parsed, or if an overflow occurred. + */ +Target parse(Target, Source)(ref Source source) +if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && + isFloatingPoint!Target && !is(Target == enum)) { import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; import std.exception : enforce; import core.stdc.math : HUGE_VAL; + static if (isNarrowString!Source) + { + import std.string : representation, assumeUTF; + auto p = source.representation; + } + else + { + alias p = source; + } + static immutable real[14] negtab = [ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L, 1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ]; static immutable real[13] postab = [ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L, 1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ]; - // static immutable string infinity = "infinity"; - // static immutable string nans = "nans"; ConvException bailOut()(string msg = null, string fn = __FILE__, size_t ln = __LINE__) { @@ -2398,13 +2608,14 @@ Target parse(Target, Source)(ref Source p) return new ConvException(text(msg, " for input \"", p, "\"."), fn, ln); } + enforce(!p.empty, bailOut()); - char sign = 0; /* indicating + */ + bool sign = false; switch (p.front) { case '-': - sign++; + sign = true; p.popFront(); enforce(!p.empty, bailOut()); if (toLower(p.front) == 'i') @@ -2426,6 +2637,8 @@ Target parse(Target, Source)(ref Source p) { // 'inf' p.popFront(); + static if (isNarrowString!Source) + source = p.assumeUTF; return sign ? -Target.infinity : Target.infinity; } } @@ -2435,12 +2648,14 @@ Target parse(Target, Source)(ref Source p) bool isHex = false; bool startsWithZero = p.front == '0'; - if(startsWithZero) + if (startsWithZero) { p.popFront(); - if(p.empty) + if (p.empty) { - return (sign) ? -0.0 : 0.0; + static if (isNarrowString!Source) + source = p.assumeUTF; + return sign ? -0.0 : 0.0; } isHex = p.front == 'x' || p.front == 'X'; @@ -2596,25 +2811,25 @@ Target parse(Target, Source)(ref Source p) // shift one time less, do rounding, shift again while ((msdec & 0xFFC0_0000_0000_0000) != 0) { - msdec = ((cast(ulong)msdec) >> 1); + msdec = ((cast(ulong) msdec) >> 1); e2++; } //Have to shift one more time //and do rounding - if((msdec & 0xFFE0_0000_0000_0000) != 0) + if ((msdec & 0xFFE0_0000_0000_0000) != 0) { auto roundUp = (msdec & 0x1); - msdec = ((cast(ulong)msdec) >> 1); + msdec = ((cast(ulong) msdec) >> 1); e2++; - if(roundUp) + if (roundUp) { msdec += 1; //If mantissa was 0b1111... and we added +1 //the mantissa should be 0b10000 (think of implicit bit) //and the exponent increased - if((msdec & 0x0020_0000_0000_0000) != 0) + if ((msdec & 0x0020_0000_0000_0000) != 0) { msdec = 0x0010_0000_0000_0000; e2++; @@ -2662,6 +2877,8 @@ Target parse(Target, Source)(ref Source p) new ConvException("error converting input to floating point")); // skip past the last 'n' p.popFront(); + static if (isNarrowString!Source) + source = p.assumeUTF; return typeof(return).nan; } @@ -2774,10 +2991,21 @@ Target parse(Target, Source)(ref Source p) enforce(ldval != HUGE_VAL, new ConvException("Range error")); L1: - return (sign) ? -ldval : ldval; + static if (isNarrowString!Source) + source = p.assumeUTF; + return sign ? -ldval : ldval; } -unittest +/// +@safe unittest +{ + import std.math : approxEqual; + auto str = "123.456"; + + assert(parse!double(str).approxEqual(123.456)); +} + +@safe unittest { import std.exception; import std.math : isNaN, fabs; @@ -2789,7 +3017,7 @@ unittest return 1; if (isNaN(rx)) - return cast(bool)isNaN(ry); + return cast(bool) isNaN(ry); if (isNaN(ry)) return 0; @@ -2803,7 +3031,7 @@ unittest return f; } - foreach (Float; TypeTuple!(float, double, real)) + foreach (Float; AliasSeq!(float, double, real)) { assert(to!Float("123") == Literal!Float(123)); assert(to!Float("+123") == Literal!Float(+123)); @@ -2826,15 +3054,15 @@ unittest // min and max float f = to!float("1.17549e-38"); - assert(feq(cast(real)f, cast(real)1.17549e-38)); - assert(feq(cast(real)f, cast(real)float.min_normal)); + assert(feq(cast(real) f, cast(real) 1.17549e-38)); + assert(feq(cast(real) f, cast(real) float.min_normal)); f = to!float("3.40282e+38"); assert(to!string(f) == to!string(3.40282e+38)); // min and max double d = to!double("2.22508e-308"); - assert(feq(cast(real)d, cast(real)2.22508e-308)); - assert(feq(cast(real)d, cast(real)double.min_normal)); + assert(feq(cast(real) d, cast(real) 2.22508e-308)); + assert(feq(cast(real) d, cast(real) double.min_normal)); d = to!double("1.79769e+308"); assert(to!string(d) == to!string(1.79769e+308)); assert(to!string(d) == to!string(double.max)); @@ -2843,17 +3071,32 @@ unittest // min and max real r = to!real(to!string(real.min_normal)); - assert(to!string(r) == to!string(real.min_normal)); + version(NetBSD) + { + // NetBSD notice + // to!string returns 3.3621e-4932L. It is less than real.min_normal and it is subnormal value + // Simple C code + // long double rd = 3.3621e-4932L; + // printf("%Le\n", rd); + // has unexpected result: 1.681050e-4932 + // + // Bug report: http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50937 + } + else + { + assert(to!string(r) == to!string(real.min_normal)); + } r = to!real(to!string(real.max)); assert(to!string(r) == to!string(real.max)); } //Tests for the double implementation -unittest +@safe unittest { - import core.stdc.stdlib, std.math; - static if(real.mant_dig == 53) + static if (real.mant_dig == 53) { + import core.stdc.stdlib, std.exception, std.math; + //Should be parsed exactly: 53 bit mantissa string s = "0x1A_BCDE_F012_3456p10"; auto x = parse!real(s); @@ -2920,7 +3163,7 @@ unittest } } -unittest +@system unittest { import core.stdc.errno; import core.stdc.stdlib; @@ -2928,11 +3171,11 @@ unittest errno = 0; // In case it was set by another unittest in a different module. struct longdouble { - static if(real.mant_dig == 64) + static if (real.mant_dig == 64) { ushort[5] value; } - else static if(real.mant_dig == 53) + else static if (real.mant_dig == 53) { ushort[4] value; } @@ -2946,9 +3189,9 @@ unittest longdouble x1; int i; - static if(real.mant_dig == 64) + static if (real.mant_dig == 64) enum s = "0x1.FFFFFFFFFFFFFFFEp-16382"; - else static if(real.mant_dig == 53) + else static if (real.mant_dig == 53) enum s = "0x1.FFFFFFFFFFFFFFFEp-1000"; else static assert(false, "Floating point format for real not supported"); @@ -2957,20 +3200,22 @@ unittest ld = parse!real(s2); assert(s2.empty); x = *cast(longdouble *)&ld; - version (CRuntime_Microsoft) - ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod - else version (Android) - ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod + + static if (real.mant_dig == 64) + { + version (CRuntime_Microsoft) + ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod + else version (CRuntime_Bionic) + ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod + else + ld1 = strtold(s.ptr, null); + } else ld1 = strtold(s.ptr, null); + x1 = *cast(longdouble *)&ld1; assert(x1 == x && ld1 == ld); - // for (i = 4; i >= 0; i--) - // { - // printf("%04x ", x.value[i]); - // } - // printf("\n"); assert(!errno); s2 = "1.0e5"; @@ -2979,12 +3224,6 @@ unittest x = *cast(longdouble *)&ld; ld1 = strtold("1.0e5", null); x1 = *cast(longdouble *)&ld1; - - // for (i = 4; i >= 0; i--) - // { - // printf("%04x ", x.value[i]); - // } - // printf("\n"); } @safe pure unittest @@ -3025,12 +3264,21 @@ unittest } /** -Parsing one character off a string returns the character and bumps the -string up one position. +Parsing one character off a range returns the first element and calls `popFront`. + +Params: + Target = the type to convert to + s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + +Returns: + A character of type `Target` + +Throws: + A $(LREF ConvException) if the range is empty. */ Target parse(Target, Source)(ref Source s) - if (isExactSomeString!Source && - staticIndexOf!(Unqual!Target, dchar, Unqual!(ElementEncodingType!Source)) >= 0) +if (isSomeString!Source && !is(Source == enum) && + staticIndexOf!(Unqual!Target, dchar, Unqual!(ElementEncodingType!Source)) >= 0) { if (s.empty) throw convError!(Source, Target)(s); @@ -3051,9 +3299,9 @@ Target parse(Target, Source)(ref Source s) @safe pure unittest { - foreach (Str; TypeTuple!(string, wstring, dstring)) + foreach (Str; AliasSeq!(string, wstring, dstring)) { - foreach (Char; TypeTuple!(char, wchar, dchar)) + foreach (Char; AliasSeq!(char, wchar, dchar)) { static if (is(Unqual!Char == dchar) || Char.sizeof == ElementEncodingType!Str.sizeof) @@ -3066,9 +3314,10 @@ Target parse(Target, Source)(ref Source s) } } +/// ditto Target parse(Target, Source)(ref Source s) - if (!isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source) && - isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum)) +if (!isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source) && + isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum)) { if (s.empty) throw convError!(Source, Target)(s); @@ -3077,6 +3326,15 @@ Target parse(Target, Source)(ref Source s) return result; } +/// +@safe pure unittest +{ + auto s = "Hello, World!"; + char first = parse!char(s); + assert(first == 'H'); + assert(s == "ello, World!"); +} + /* Tests for to!bool and parse!bool @@ -3085,32 +3343,45 @@ Target parse(Target, Source)(ref Source s) { import std.exception; - assert (to!bool("TruE") == true); - assert (to!bool("faLse"d) == false); + assert(to!bool("TruE") == true); + assert(to!bool("faLse"d) == false); assertThrown!ConvException(to!bool("maybe")); auto t = "TrueType"; - assert (parse!bool(t) == true); - assert (t == "Type"); + assert(parse!bool(t) == true); + assert(t == "Type"); auto f = "False killer whale"d; - assert (parse!bool(f) == false); - assert (f == " killer whale"d); + assert(parse!bool(f) == false); + assert(f == " killer whale"d); auto m = "maybe"; assertThrown!ConvException(parse!bool(m)); - assert (m == "maybe"); // m shouldn't change on failure + assert(m == "maybe"); // m shouldn't change on failure auto s = "true"; auto b = parse!(const(bool))(s); assert(b == true); } -// input range to null literal conversions +/** +Parsing a character range to `typeof(null)` returns `null` if the range +spells `"null"`. This function is case insensitive. + +Params: + Target = the type to convert to + s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + +Returns: + `null` + +Throws: + A $(LREF ConvException) if the range doesn't represent `null`. + */ Target parse(Target, Source)(ref Source s) - if (isInputRange!Source && - isSomeChar!(ElementType!Source) && - is(Unqual!Target == typeof(null))) +if (isInputRange!Source && + isSomeChar!(ElementType!Source) && + is(Unqual!Target == typeof(null))) { import std.ascii : toLower; foreach (c; "null") @@ -3122,9 +3393,10 @@ Target parse(Target, Source)(ref Source s) return null; } +/// @safe pure unittest { - import std.exception; + import std.exception : assertThrown; alias NullType = typeof(null); auto s1 = "null"; @@ -3150,7 +3422,7 @@ package void skipWS(R)(ref R r) static if (isSomeString!R) { //Implementation inspired from stripLeft. - foreach (i, dchar c; r) + foreach (i, c; r) { if (!isWhite(c)) { @@ -3171,13 +3443,24 @@ package void skipWS(R)(ref R r) /** * Parses an array from a string given the left bracket (default $(D * '[')), right bracket (default $(D ']')), and element separator (by - * default $(D ',')). + * default $(D ',')). A trailing separator is allowed. + * + * Params: + * s = The string to parse + * lbracket = the character that starts the array + * rbracket = the character that ends the array + * comma = the character that separates the elements of the array + * + * Returns: + * An array of type `Target` */ Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket = ']', dchar comma = ',') - if (isExactSomeString!Source && - isDynamicArray!Target && !is(Target == enum)) +if (isSomeString!Source && !is(Source == enum) && + isDynamicArray!Target && !is(Target == enum)) { - Target result; + import std.array : appender; + + auto result = appender!Target(); parseCheck!s(lbracket); skipWS(s); @@ -3186,10 +3469,12 @@ Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket if (s.front == rbracket) { s.popFront(); - return result; + return result.data; } for (;; s.popFront(), skipWS(s)) { + if (!s.empty && s.front == rbracket) + break; result ~= parseElement!(ElementType!Target)(s); skipWS(s); if (s.empty) @@ -3199,24 +3484,57 @@ Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket } parseCheck!s(rbracket); - return result; + return result.data; } -unittest +/// +@safe pure unittest +{ + auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; + auto a1 = parse!(string[])(s1); + assert(a1 == ["hello", "world"]); + + auto s2 = `["aaa", "bbb", "ccc"]`; + auto a2 = parse!(string[])(s2); + assert(a2 == ["aaa", "bbb", "ccc"]); +} + +@safe unittest // Bugzilla 9615 +{ + string s0 = "[1,2, ]"; + string s1 = "[1,2, \t\v\r\n]"; + string s2 = "[1,2]"; + assert(s0.parse!(int[]) == [1,2]); + assert(s1.parse!(int[]) == [1,2]); + assert(s2.parse!(int[]) == [1,2]); + + string s3 = `["a","b",]`; + string s4 = `["a","b"]`; + assert(s3.parse!(string[]) == ["a","b"]); + assert(s4.parse!(string[]) == ["a","b"]); + + import std.exception : assertThrown; + string s5 = "[,]"; + string s6 = "[, \t,]"; + assertThrown!ConvException(parse!(string[])(s5)); + assertThrown!ConvException(parse!(int[])(s6)); +} + +@safe unittest { int[] a = [1, 2, 3, 4, 5]; auto s = to!string(a); assert(to!(int[])(s) == a); } -unittest +@safe unittest { int[][] a = [ [1, 2] , [3], [4, 5] ]; auto s = to!string(a); assert(to!(int[][])(s) == a); } -unittest +@safe unittest { int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ]; @@ -3227,24 +3545,13 @@ unittest assert( ia == ia2); } -@safe pure unittest -{ - auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`; - auto a1 = parse!(string[])(s1); - assert(a1 == ["hello", "world"]); - - auto s2 = `["aaa", "bbb", "ccc"]`; - auto a2 = parse!(string[])(s2); - assert(a2 == ["aaa", "bbb", "ccc"]); -} - @safe pure unittest { import std.exception; //Check proper failure auto s = "[ 1 , 2 , 3 ]"; - foreach (i ; 0..s.length-1) + foreach (i ; 0 .. s.length-1) { auto ss = s[0 .. i]; assertThrown!ConvException(parse!(int[])(ss)); @@ -3282,8 +3589,8 @@ unittest /// ditto Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket = ']', dchar comma = ',') - if (isExactSomeString!Source && - isStaticArray!Target && !is(Target == enum)) +if (isExactSomeString!Source && + isStaticArray!Target && !is(Target == enum)) { static if (hasIndirections!Target) Target result = Target.init[0].init; @@ -3353,10 +3660,21 @@ Lfewerr: * Parses an associative array from a string given the left bracket (default $(D * '[')), right bracket (default $(D ']')), key-value separator (default $(D * ':')), and element seprator (by default $(D ',')). + * + * Params: + * s = the string to parse + * lbracket = the character that starts the associative array + * rbracket = the character that ends the associative array + * keyval = the character that associates the key with the value + * comma = the character that separates the elements of the associative array + * + * Returns: + * An associative array of type `Target` */ -Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket = ']', dchar keyval = ':', dchar comma = ',') - if (isExactSomeString!Source && - isAssociativeArray!Target && !is(Target == enum)) +Target parse(Target, Source)(ref Source s, dchar lbracket = '[', + dchar rbracket = ']', dchar keyval = ':', dchar comma = ',') +if (isSomeString!Source && !is(Source == enum) && + isAssociativeArray!Target && !is(Target == enum)) { alias KeyType = typeof(Target.init.keys[0]); alias ValType = typeof(Target.init.values[0]); @@ -3391,6 +3709,7 @@ Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket return result; } +/// @safe pure unittest { auto s1 = "[1:10, 2:20, 3:30]"; @@ -3421,7 +3740,7 @@ Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket } private dchar parseEscape(Source)(ref Source s) - if (isInputRange!Source && isSomeChar!(ElementType!Source)) +if (isInputRange!Source && isSomeChar!(ElementType!Source)) { parseCheck!s('\\'); if (s.empty) @@ -3536,8 +3855,8 @@ private dchar parseEscape(Source)(ref Source s) // Undocumented Target parseElement(Target, Source)(ref Source s) - if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && - isExactSomeString!Target) +if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && + isExactSomeString!Target) { import std.array : appender; auto result = appender!Target(); @@ -3579,8 +3898,8 @@ Target parseElement(Target, Source)(ref Source s) // ditto Target parseElement(Target, Source)(ref Source s) - if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && - isSomeChar!Target && !is(Target == enum)) +if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && + isSomeChar!Target && !is(Target == enum)) { Target c; @@ -3601,22 +3920,50 @@ Target parseElement(Target, Source)(ref Source s) // ditto Target parseElement(Target, Source)(ref Source s) - if (isInputRange!Source && isSomeChar!(ElementType!Source) && - !isSomeString!Target && !isSomeChar!Target) +if (isInputRange!Source && isSomeChar!(ElementType!Source) && + !isSomeString!Target && !isSomeChar!Target) { return parse!Target(s); } /*************************************************************** - * Convenience functions for converting any number and types of - * arguments into _text (the three character widths). + * Convenience functions for converting one or more arguments + * of any type into _text (the three character widths). */ -string text(T...)(T args) { return textImpl!string(args); } +string text(T...)(T args) +if (T.length > 0) { return textImpl!string(args); } + +// @@@DEPRECATED_2017-06@@@ +deprecated("Calling `text` with 0 arguments is deprecated") +string text(T...)(T args) +if (T.length == 0) { return textImpl!string(args); } + ///ditto -wstring wtext(T...)(T args) { return textImpl!wstring(args); } +wstring wtext(T...)(T args) +if (T.length > 0) { return textImpl!wstring(args); } + +// @@@DEPRECATED_2017-06@@@ +deprecated("Calling `wtext` with 0 arguments is deprecated") +wstring wtext(T...)(T args) +if (T.length == 0) { return textImpl!wstring(args); } + ///ditto -dstring dtext(T...)(T args) { return textImpl!dstring(args); } +dstring dtext(T...)(T args) +if (T.length > 0) { return textImpl!dstring(args); } + +/// +@safe unittest +{ + assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c); + assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w); + assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d); +} + +// @@@DEPRECATED_2017-06@@@ +deprecated("Calling `dtext` with 0 arguments is deprecated") +dstring dtext(T...)(T args) +if (T.length == 0) { return textImpl!dstring(args); } private S textImpl(S, U...)(U args) { @@ -3624,36 +3971,40 @@ private S textImpl(S, U...)(U args) { return null; } + else static if (U.length == 1) + { + return to!S(args[0]); + } else { - auto result = to!S(args[0]); - foreach (arg; args[1 .. $]) - result ~= to!S(arg); - return result; + import std.array : appender; + + auto app = appender!S(); + + // assume that on average, parameters will have less + // than 20 elements + app.reserve(U.length * 20); + + foreach (arg; args) + { + static if ( + is(Unqual!(typeof(arg)) == uint) || is(Unqual!(typeof(arg)) == ulong) || + is(Unqual!(typeof(arg)) == int) || is(Unqual!(typeof(arg)) == long) + ) + app.put(arg.toChars); + else + app.put(to!S(arg)); + } + + return app.data; } } -/// -unittest -{ - assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c); - assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w); - assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d); -} -unittest -{ - assert(text() is null); - assert(wtext() is null); - assert(dtext() is null); -} /*************************************************************** -The $(D octal) facility is intended as an experimental facility to -replace _octal literals starting with $(D '0'), which many find -confusing. Using $(D octal!177) or $(D octal!"177") instead of $(D -0177) as an _octal literal makes code clearer and the intent more -visible. If use of this facility becomes predominant, a future -version of the language may deem old-style _octal literals deprecated. +The $(D octal) facility provides a means to declare a number in base 8. +Using $(D octal!177) or $(D octal!"177") for 127 represented in octal +(same as 0177 in C). The rules for strings are the usual for literals: If it can fit in an $(D int), it is an $(D int). Otherwise, it is a $(D long). But, if the @@ -3661,43 +4012,34 @@ user specifically asks for a $(D long) with the $(D L) suffix, always give the $(D long). Give an unsigned iff it is asked for with the $(D U) or $(D u) suffix. _Octals created from integers preserve the type of the passed-in integral. - */ -@property int octal(string num)() - if((octalFitsInInt!(num) && !literalIsLong!(num)) && !literalIsUnsigned!(num)) -{ - return octal!(int, num); -} -/// Ditto -@property long octal(string num)() - if((!octalFitsInInt!(num) || literalIsLong!(num)) && !literalIsUnsigned!(num)) -{ - return octal!(long, num); -} - -/// Ditto -@property uint octal(string num)() - if((octalFitsInInt!(num) && !literalIsLong!(num)) && literalIsUnsigned!(num)) -{ - return octal!(int, num); -} - -/// Ditto -@property ulong octal(string num)() - if((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num)) -{ - return octal!(long, num); +See_Also: + $(LREF parse) for parsing octal strings at runtime. + */ +template octal(string num) +if (isOctalLiteral(num)) +{ + static if ((octalFitsInInt!num && !literalIsLong!num) && !literalIsUnsigned!num) + enum octal = octal!int(num); + else static if ((!octalFitsInInt!num || literalIsLong!num) && !literalIsUnsigned!num) + enum octal = octal!long(num); + else static if ((octalFitsInInt!num && !literalIsLong!num) && literalIsUnsigned!num) + enum octal = octal!uint(num); + else static if ((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num)) + enum octal = octal!ulong(num); + else + static assert(false); } /// Ditto -template octal(alias s) - if (isIntegral!(typeof(s))) +template octal(alias decimalInteger) +if (isIntegral!(typeof(decimalInteger))) { - enum auto octal = octal!(typeof(s), to!string(s)); + enum octal = octal!(typeof(decimalInteger))(to!string(decimalInteger)); } /// -unittest +@safe unittest { // same as 0177 auto x = octal!177; @@ -3711,13 +4053,14 @@ unittest Takes a string, num, which is an octal literal, and returns its value, in the type T specified. */ -@property T octal(T, string num)() - if (isOctalLiteral!num) +private T octal(T)(const string num) { + assert(isOctalLiteral(num)); + ulong pow = 1; T value = 0; - for (int pos = num.length - 1; pos >= 0; pos--) + foreach_reverse (immutable pos; 0 .. num.length) { char s = num[pos]; if (s < '0' || s > '7') // we only care about digits; skip the rest @@ -3732,11 +4075,9 @@ unittest return value; } -/// -unittest +@system unittest { - int a = octal!(int, "10"); - + int a = octal!int("10"); assert(a == 8); } @@ -3744,7 +4085,7 @@ unittest Take a look at int.max and int.max+1 in octal and the logic for this function follows directly. */ -template octalFitsInInt(string octalNum) +private template octalFitsInInt(string octalNum) { // note it is important to strip the literal of all // non-numbers. kill the suffix and underscores lest they mess up @@ -3754,7 +4095,7 @@ template octalFitsInInt(string octalNum) strippedOctalLiteral(octalNum)[0] == '1'; } -string strippedOctalLiteral(string original) +private string strippedOctalLiteral(string original) { string stripped = ""; foreach (c; original) @@ -3763,7 +4104,7 @@ string strippedOctalLiteral(string original) return stripped; } -template literalIsLong(string num) +private template literalIsLong(string num) { static if (num.length > 1) // can be xxL or xxLu according to spec @@ -3772,7 +4113,7 @@ template literalIsLong(string num) enum literalIsLong = false; } -template literalIsUnsigned(string num) +private template literalIsUnsigned(string num) { static if (num.length > 1) // can be xxU or xxUL according to spec @@ -3786,10 +4127,11 @@ template literalIsUnsigned(string num) /* Returns if the given string is a correctly formatted octal literal. -The format is specified in lex.html. The leading zero is allowed, but +The format is specified in spec/lex.html. The leading zero is allowed, but not required. */ -bool isOctalLiteralString(string num) +@safe pure nothrow @nogc +private bool isOctalLiteral(const string num) { if (num.length == 0) return false; @@ -3828,15 +4170,7 @@ bool isOctalLiteralString(string num) return true; } -/* - Returns true if the given compile time string is an octal literal. -*/ -template isOctalLiteral(string num) -{ - enum bool isOctalLiteral = isOctalLiteralString(num); -} - -unittest +@safe unittest { // ensure that you get the right types, even with embedded underscores auto w = octal!"100_000_000_000"; @@ -3871,25 +4205,55 @@ unittest static assert(!__traits(compiles, octal!"spam")); static assert(!__traits(compiles, octal!"77%")); + static assert(is(typeof(octal!"17777777777") == int)); + static assert(octal!"17777777777" == int.max); + + static assert(is(typeof(octal!"20000000000U") == ulong)); // Shouldn't this be uint? + static assert(octal!"20000000000" == uint(int.max) + 1); + + static assert(is(typeof(octal!"777777777777777777777") == long)); + static assert(octal!"777777777777777777777" == long.max); + + static assert(is(typeof(octal!"1000000000000000000000U") == ulong)); + static assert(octal!"1000000000000000000000" == ulong(long.max) + 1); + int a; long b; // biggest value that should fit in an it - static assert(__traits(compiles, a = octal!"17777777777")); + a = octal!"17777777777"; + assert(a == int.max); // should not fit in the int static assert(!__traits(compiles, a = octal!"20000000000")); // ... but should fit in a long - static assert(__traits(compiles, b = octal!"20000000000")); - - static assert(!__traits(compiles, a = octal!"1L")); + b = octal!"20000000000"; + assert(b == 1L + int.max); - // this should pass, but it doesn't, since the int converter - // doesn't pass along its suffix to helper templates + b = octal!"1L"; + assert(b == 1); + b = octal!1L; + assert(b == 1); +} - //static assert(!__traits(compiles, a = octal!1L)); +// asOriginalType +/** +Returns the representation of an enumerated value, i.e. the value converted to +the base type of the enumeration. +*/ +OriginalType!E asOriginalType(E)(E value) if (is(E == enum)) +{ + return value; +} - static assert(__traits(compiles, b = octal!"1L")); - static assert(__traits(compiles, b = octal!1L)); +/// +unittest +{ + enum A { a = 42 } + static assert(is(typeof(A.a.asOriginalType) == int)); + assert(A.a.asOriginalType == 42); + enum B : double { a = 43 } + static assert(is(typeof(B.a.asOriginalType) == double)); + assert(B.a.asOriginalType == 43); } /+ @@ -3903,184 +4267,79 @@ Furthermore, emplaceRef optionally takes a type paremeter, which specifies the type we want to build. This helps to build qualified objects on mutable buffer, without breaking the type system with unsafe casts. +/ -package ref UT emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) -if (is(UT == Unqual!UT)) -{ - return emplaceImpl!UT(chunk, args); -} -// ditto -package ref UT emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) -if (is(UT == Unqual!T) && !is(T == UT)) -{ - return emplaceImpl!T(chunk, args); -} - - -private template emplaceImpl(T) +package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) { - alias UT = Unqual!T; - - ref UT emplaceImpl()(ref UT chunk) + static if (args.length == 0) { - static assert (is(typeof({static T i;})), + static assert(is(typeof({static T i;})), convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); - - return emplaceInitializer(chunk); - } - - static if (!is(T == struct)) - ref UT emplaceImpl(Arg)(ref UT chunk, auto ref Arg arg) - { - static assert(is(typeof({T t = arg;})), - convFormat("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); - - static if (isStaticArray!T) + static if (is(T == class)) static assert(!isAbstractClass!T, + T.stringof ~ " is abstract and it can't be emplaced"); + emplaceInitializer(chunk); + } + else static if ( + !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */ + || + Args.length == 1 && is(typeof({T t = args[0];})) /* conversions */ + || + is(typeof(T(args))) /* general constructors */) + { + static struct S { - alias UArg = Unqual!Arg; - alias E = ElementEncodingType!(typeof(T.init[])); - alias UE = Unqual!E; - enum N = T.length; - - static if (is(Arg : T)) - { - //Matching static array - static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg)) - chunk = arg; - else static if (is(UArg == UT)) - { - import core.stdc.string : memcpy; - memcpy(&chunk, &arg, T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); - } - else - .emplaceImpl!T(chunk, cast(T)arg); - } - else static if (is(Arg : E[])) + T payload; + this(ref Args x) { - //Matching dynamic array - static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg[]))) - chunk[] = arg[]; - else static if (is(Unqual!(ElementEncodingType!Arg) == UE)) - { - import core.stdc.string : memcpy; - assert(N == chunk.length, "Array length missmatch in emplace"); - memcpy(cast(void*)&chunk, arg.ptr, T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); - } + static if (Args.length == 1) + static if (is(typeof(payload = x[0]))) + payload = x[0]; + else + payload = T(x[0]); else - .emplaceImpl!T(chunk, cast(E[])arg); + payload = T(x); } - else static if (is(Arg : E)) - { - //Case matching single element to array. - static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) - chunk[] = arg; - else static if (is(UArg == Unqual!E)) - { - import core.stdc.string : memcpy; - //Note: We copy everything, and then postblit just once. - //This is as exception safe as what druntime can provide us. - foreach(i; 0 .. N) - memcpy(cast(void*)&(chunk[i]), &arg, E.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); - } - else - //Alias this. Coerce. - .emplaceImpl!T(chunk, cast(E)arg); - } - else static if (is(typeof(.emplaceImpl!E(chunk[0], arg)))) - { - //Final case for everything else: - //Types that don't match (int to uint[2]) - //Recursion for multidimensions - static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) - chunk[] = arg; - else - foreach(i; 0 .. N) - .emplaceImpl!E(chunk[i], arg); - } - else - static assert(0, convFormat("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); - - return chunk; - } - else - { - chunk = arg; - return chunk; - } - } - // ditto - static if (is(T == struct)) - ref UT emplaceImpl(Args...)(ref UT chunk, auto ref Args args) - { - static if (Args.length == 1 && is(Args[0] : T) && - is (typeof({T t = args[0];})) //Check for legal postblit - ) - { - static if (is(Unqual!T == Unqual!(Args[0]))) - { - //Types match exactly: we postblit - static if (!hasElaborateAssign!UT && isAssignable!(UT, T)) - chunk = args[0]; - else - { - import core.stdc.string : memcpy; - memcpy(&chunk, &args[0], T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(&chunk); - } - } - else - //Alias this. Coerce to type T. - .emplaceImpl!T(chunk, cast(T)args[0]); - } - else static if (is(typeof(chunk.__ctor(args)))) - { - // T defines a genuine constructor accepting args - // Go the classic route: write .init first, then call ctor - emplaceInitializer(chunk); - chunk.__ctor(args); } - else static if (is(typeof(T.opCall(args)))) + if (__ctfe) { - //Can be built calling opCall - emplaceOpCaller(chunk, args); //emplaceOpCaller is deprecated - } - else static if (is(typeof(T(args)))) - { - // Struct without constructor that has one matching field for - // each argument. Individually emplace each field - emplaceInitializer(chunk); - foreach (i, ref field; chunk.tupleof[0 .. Args.length]) - { - alias Field = typeof(field); - alias UField = Unqual!Field; - static if (is(Field == UField)) - .emplaceImpl!Field(field, args[i]); - else - .emplaceImpl!Field(*cast(Unqual!Field*)&field, args[i]); - } + static if (is(typeof(chunk = T(args)))) + chunk = T(args); + else static if (args.length == 1 && is(typeof(chunk = args[0]))) + chunk = args[0]; + else assert(0, "CTFE emplace doesn't support " + ~ T.stringof ~ " from " ~ Args.stringof); } else { - //We can't emplace. Try to diagnose a disabled postblit. - static assert(!(Args.length == 1 && is(Args[0] : T)), - convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); - - //We can't emplace. - static assert(false, - convFormat("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); + S* p = () @trusted { return cast(S*) &chunk; }(); + emplaceInitializer(*p); + p.__ctor(args); } + } + else static if (is(typeof(chunk.__ctor(args)))) + { + // This catches the rare case of local types that keep a frame pointer + emplaceInitializer(chunk); + chunk.__ctor(args); + } + else + { + //We can't emplace. Try to diagnose a disabled postblit. + static assert(!(Args.length == 1 && is(Args[0] : T)), + convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); - return chunk; + //We can't emplace. + static assert(false, + convFormat("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); } } +// ditto +package void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) +if (is(UT == Unqual!UT)) +{ + emplaceRef!(UT, UT)(chunk, args); +} + //emplace helper functions -private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow +private void emplaceInitializer(T)(ref T chunk) @trusted pure nothrow { static if (!hasElaborateAssign!T && isAssignable!T) chunk = T.init; @@ -4090,36 +4349,55 @@ private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow static immutable T init = T.init; memcpy(&chunk, &init, T.sizeof); } - return chunk; -} -private deprecated("Using static opCall for emplace is deprecated. Plase use emplace(chunk, T(args)) instead.") -ref T emplaceOpCaller(T, Args...)(ref T chunk, auto ref Args args) -{ - static assert (is(typeof({T t = T.opCall(args);})), - convFormat("%s.opCall does not return adequate data for construction.", T.stringof)); - return emplaceImpl!T(chunk, chunk.opCall(args)); } - // emplace /** Given a pointer $(D chunk) to uninitialized memory (but already typed as $(D T)), constructs an object of non-$(D class) type $(D T) at that -address. +address. If `T` is a class, initializes the class reference to null. Returns: A pointer to the newly constructed object (which is the same as $(D chunk)). */ T* emplace(T)(T* chunk) @safe pure nothrow { - emplaceImpl!T(*chunk); + emplaceRef!T(*chunk); return chunk; } +/// +@system unittest +{ + static struct S + { + int i = 42; + } + S[2] s2 = void; + emplace(&s2); + assert(s2[0].i == 42 && s2[1].i == 42); +} + +/// +@system unittest +{ + interface I {} + class K : I {} + + K k = void; + emplace(&k); + assert(k is null); + + I i = void; + emplace(&i); + assert(i is null); +} + /** Given a pointer $(D chunk) to uninitialized memory (but already typed as a non-class type $(D T)), constructs an object of type $(D T) at -that address from arguments $(D args). +that address from arguments $(D args). If `T` is a class, initializes +the class reference to `args[0]`. This function can be $(D @trusted) if the corresponding constructor of $(D T) is $(D @safe). @@ -4128,17 +4406,191 @@ Returns: A pointer to the newly constructed object (which is the same as $(D chunk)). */ T* emplace(T, Args...)(T* chunk, auto ref Args args) -if (!is(T == struct) && Args.length == 1) +if (is(T == struct) || Args.length == 1) { - emplaceImpl!T(*chunk, args); + emplaceRef!T(*chunk, args); return chunk; } -/// ditto -T* emplace(T, Args...)(T* chunk, auto ref Args args) -if (is(T == struct)) + +/// +@system unittest { - emplaceImpl!T(*chunk, args); - return chunk; + int a; + int b = 42; + assert(*emplace!int(&a, b) == 42); +} + +@system unittest +{ + shared int i; + emplace(&i, 42); + assert(i == 42); +} + +private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) @nogc pure nothrow +{ + assert(chunk.length >= typeSize, "emplace: Chunk size too small."); + assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); +} + +/** +Given a raw memory area $(D chunk), constructs an object of $(D class) +type $(D T) at that address. The constructor is passed the arguments +$(D Args). + +If `T` is an inner class whose `outer` field can be used to access an instance +of the enclosing class, then `Args` must not be empty, and the first member of it +must be a valid initializer for that `outer` field. Correct initialization of +this field is essential to access members of the outer class inside `T` methods. + +Preconditions: +$(D chunk) must be at least as large as $(D T) needs +and should have an alignment multiple of $(D T)'s alignment. (The size +of a $(D class) instance is obtained by using $(D +__traits(classInstanceSize, T))). + +Note: +This function can be $(D @trusted) if the corresponding constructor of +$(D T) is $(D @safe). + +Returns: The newly constructed object. + */ +T emplace(T, Args...)(void[] chunk, auto ref Args args) +if (is(T == class)) +{ + static assert(!isAbstractClass!T, T.stringof ~ + " is abstract and it can't be emplaced"); + + enum classSize = __traits(classInstanceSize, T); + testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof); + auto result = cast(T) chunk.ptr; + + // Initialize the object in its pre-ctor state + chunk[0 .. classSize] = typeid(T).initializer[]; + + static if (isInnerClass!T) + { + static assert(Args.length > 0, + "Initializing an inner class requires a pointer to the outer class"); + static assert(is(Args[0] : typeof(T.outer)), + "The first argument must be a pointer to the outer class"); + + result.outer = args[0]; + alias args1 = args[1..$]; + } + else alias args1 = args; + + // Call the ctor if any + static if (is(typeof(result.__ctor(args1)))) + { + // T defines a genuine constructor accepting args + // Go the classic route: write .init first, then call ctor + result.__ctor(args1); + } + else + { + static assert(args1.length == 0 && !is(typeof(&T.__ctor)), + "Don't know how to initialize an object of type " + ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof); + } + return result; +} + +/// +@system unittest +{ + static class C + { + int i; + this(int i){this.i = i;} + } + auto buf = new void[__traits(classInstanceSize, C)]; + auto c = emplace!C(buf, 5); + assert(c.i == 5); +} + +@system unittest +{ + class Outer + { + int i = 3; + class Inner + { + auto getI() { return i; } + } + } + auto outerBuf = new void[__traits(classInstanceSize, Outer)]; + auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; + auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer); + assert(inner.getI == 3); +} + +@nogc pure nothrow @system unittest +{ + int var = 6; + align(__conv_EmplaceTestClass.alignof) ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf; + auto k = emplace!__conv_EmplaceTestClass(buf, 5, var); + assert(k.i == 5); + assert(var == 7); +} + +/** +Given a raw memory area $(D chunk), constructs an object of non-$(D +class) type $(D T) at that address. The constructor is passed the +arguments $(D args), if any. + +Preconditions: +$(D chunk) must be at least as large +as $(D T) needs and should have an alignment multiple of $(D T)'s +alignment. + +Note: +This function can be $(D @trusted) if the corresponding constructor of +$(D T) is $(D @safe). + +Returns: A pointer to the newly constructed object. + */ +T* emplace(T, Args...)(void[] chunk, auto ref Args args) +if (!is(T == class)) +{ + testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof); + emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, args); + return cast(T*) chunk.ptr; +} + +/// +@system unittest +{ + struct S + { + int a, b; + } + auto buf = new void[S.sizeof]; + S s; + s.a = 42; + s.b = 43; + auto s1 = emplace!S(buf, s); + assert(s1.a == 42 && s1.b == 43); +} + +// Bulk of emplace unittests starts here + +@system unittest /* unions */ +{ + static union U + { + string a; + int b; + struct + { + long c; + int[] d; + } + } + U u1 = void; + U u2 = { "hello" }; + emplace(&u1, u2); + assert(u1.a == "hello"); } version(unittest) private struct __conv_EmplaceTest @@ -4178,37 +4630,28 @@ version(unittest) private class __conv_EmplaceTestClass } } -unittest +@system unittest // bugzilla 15772 { - struct S { @disable this(); } - S s = void; - static assert(!__traits(compiles, emplace(&s))); - static assert( __traits(compiles, emplace(&s, S.init))); + abstract class Foo {} + class Bar: Foo {} + void[] memory; + // test in emplaceInitializer + static assert(!is(typeof(emplace!Foo(cast(Foo*) memory.ptr)))); + static assert( is(typeof(emplace!Bar(cast(Bar*) memory.ptr)))); + // test in the emplace overload that takes void[] + static assert(!is(typeof(emplace!Foo(memory)))); + static assert( is(typeof(emplace!Bar(memory)))); } -unittest +@system unittest { - interface I {} - class K : I {} - - K k = void; - emplace(&k); - assert(k is null); - - I i = void; - emplace(&i); - assert(i is null); -} - -unittest -{ - static struct S {int i = 5;} - S[2] s2 = void; - emplace(&s2); - assert(s2[0].i == 5 && s2[1].i == 5); + struct S { @disable this(); } + S s = void; + static assert(!__traits(compiles, emplace(&s))); + emplace(&s, S.init); } -unittest +@system unittest { struct S1 {} @@ -4228,7 +4671,7 @@ unittest emplace(&as2); } -unittest +@system unittest { static struct S1 { @@ -4240,15 +4683,15 @@ unittest } S1[2] ss1 = void; S2[2] ss2 = void; - static assert( __traits(compiles, emplace(&ss1))); + emplace(&ss1); static assert(!__traits(compiles, emplace(&ss2))); S1 s1 = S1.init; S2 s2 = S2.init; static assert(!__traits(compiles, emplace(&ss1, s1))); - static assert( __traits(compiles, emplace(&ss2, s2))); + emplace(&ss2, s2); } -unittest +@system unittest { struct S { @@ -4258,20 +4701,16 @@ unittest S[2] ss1 = void; S[2] ss2 = void; emplace(&s, 5); + assert(s.i == 5); emplace(&ss1, s); + assert(ss1[0].i == 5 && ss1[1].i == 5); emplace(&ss2, ss1); + assert(ss2 == ss1); } //Start testing emplace-args here -unittest -{ - int a; - int b = 42; - assert(*emplace!int(&a, b) == 42); -} - -unittest +@system unittest { interface I {} class K : I {} @@ -4287,7 +4726,7 @@ unittest assert(i is k); } -unittest +@system unittest { static struct S { @@ -4303,7 +4742,7 @@ unittest //Start testing emplace-struct here // Test constructor branch -unittest +@system unittest { struct S { @@ -4322,14 +4761,14 @@ unittest assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45)); } -unittest +@system unittest { __conv_EmplaceTest k = void; emplace(&k, 5); assert(k.i == 5); } -unittest +@system unittest { int var = 6; __conv_EmplaceTest k = void; @@ -4339,7 +4778,7 @@ unittest } // Test matching fields branch -unittest +@system unittest { struct S { uint n; } S s; @@ -4347,14 +4786,14 @@ unittest assert(s.n == 2); } -unittest +@safe unittest { struct S { int a, b; this(int){} } S s; static assert(!__traits(compiles, emplace!S(&s, 2, 3))); } -unittest +@system unittest { struct S { int a, b = 7; } S s1 = void, s2 = void; @@ -4367,7 +4806,7 @@ unittest } //opAssign -unittest +@system unittest { static struct S { @@ -4385,7 +4824,7 @@ unittest } //postblit precedence -unittest +@system unittest { //Works, but breaks in "-w -O" because of @@@9332@@@. //Uncomment test when 9332 is fixed. @@ -4407,7 +4846,7 @@ unittest static struct S2 { int* p; - this(const S2){}; + this(const S2){} } static assert(!is(immutable S2 : S2)); S2 s2 = void; @@ -4416,7 +4855,7 @@ unittest } //nested structs and postblit -unittest +@system unittest { static struct S { @@ -4444,7 +4883,7 @@ unittest } //disabled postblit -unittest +@system unittest { static struct S1 { @@ -4452,7 +4891,8 @@ unittest @disable this(this); } S1 s1 = void; - static assert( __traits(compiles, emplace(&s1, 1))); + emplace(&s1, 1); + assert(s1.i == 1); static assert(!__traits(compiles, emplace(&s1, S1.init))); static struct S2 @@ -4463,14 +4903,14 @@ unittest } S2 s2 = void; static assert(!__traits(compiles, emplace(&s2, 1))); - static assert( __traits(compiles, emplace(&s2, S2.init))); + emplace(&s2, S2.init); static struct SS1 { S1 s; } SS1 ss1 = void; - static assert( __traits(compiles, emplace(&ss1))); + emplace(&ss1); static assert(!__traits(compiles, emplace(&ss1, SS1.init))); static struct SS2 @@ -4478,7 +4918,7 @@ unittest S2 s; } SS2 ss2 = void; - static assert( __traits(compiles, emplace(&ss2))); + emplace(&ss2); static assert(!__traits(compiles, emplace(&ss2, SS2.init))); @@ -4490,7 +4930,7 @@ unittest } //Imutability -unittest +@system unittest { //Castable immutability { @@ -4517,7 +4957,7 @@ unittest } } -unittest +@system unittest { static struct S { @@ -4531,7 +4971,7 @@ unittest } //Context pointer -unittest +@system unittest { int i = 0; { @@ -4557,24 +4997,10 @@ unittest sa.foo(); assert(i == 2); } - - ////NOTE: THESE WILL COMPILE - ////But will not correctly emplace the context pointer - ////The problem lies with voldemort, and not emplace. - //{ - // struct S3 - // { - // int k; - // void foo(){++i;} - // } - //} - //S3 s3 = void; - //emplace(&s3); //S3.init has no context pointer information - //emplace(&s3, 1); //No way to obtain context pointer once inside emplace } //Alias this -unittest +@system unittest { static struct S { @@ -4629,7 +5055,7 @@ version(unittest) alias foo this; } static assert(is(__std_conv_SS : __std_conv_S)); - unittest + @system unittest { __std_conv_S s = void; __std_conv_SS ss = __std_conv_SS(1); @@ -4641,7 +5067,7 @@ version(unittest) } //Nested classes -unittest +@system unittest { class A{} static struct S @@ -4655,7 +5081,7 @@ unittest } //safety & nothrow & CTFE -unittest +@system unittest { //emplace should be safe for anything with no elaborate opassign static struct S1 @@ -4687,6 +5113,7 @@ unittest emplace(ps2, 5); emplace(ps2, S2.init); } + foo(); T bar(T)() @property { @@ -4694,13 +5121,24 @@ unittest emplace(&t, 5); return t; } + // CTFE enum a = bar!int; + static assert(a == 5); enum b = bar!S1; + static assert(b.i == 5); enum c = bar!S2; + static assert(c.i == 5); + // runtime + auto aa = bar!int; + assert(aa == 5); + auto bb = bar!S1; + assert(bb.i == 5); + auto cc = bar!S2; + assert(cc.i == 5); } -unittest +@system unittest { struct S { @@ -4725,7 +5163,7 @@ unittest } //disable opAssign -unittest +@system unittest { static struct S { @@ -4736,7 +5174,7 @@ unittest } //opCall -unittest +@system unittest { int i; //Without constructor @@ -4748,7 +5186,6 @@ unittest } S1 s = void; static assert(!__traits(compiles, emplace(&s, 1))); - static assert( __traits(compiles, emplace(&s, &i))); //(works, but deprected) } //With constructor { @@ -4760,8 +5197,6 @@ unittest this(int i){this.i = i;} } S2 s = void; - static assert( __traits(compiles, emplace(&s, 1))); //(works, but deprected) - static assert( __traits(compiles, emplace(&s, &i))); //(works, but deprected) emplace(&s, 1); assert(s.i == 1); } @@ -4773,20 +5208,21 @@ unittest static S3 opCall(ref S3){assert(0);} } S3 s = void; - static assert( __traits(compiles, emplace(&s, S3.init))); + emplace(&s, S3.init); } } -unittest //@@@9559@@@ +@safe unittest //@@@9559@@@ { - import std.algorithm : map; + import std.algorithm.iteration : map; import std.typecons : Nullable; + import std.array : array; alias I = Nullable!int; auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); - auto asArray = std.array.array(ints); + auto asArray = array(ints); } -unittest //http://forum.dlang.org/thread/nxbdgtdlmwscocbiypjs@forum.dlang.org +@system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org { import std.array : array; import std.datetime : SysTime, UTC; @@ -4801,7 +5237,7 @@ unittest //http://forum.dlang.org/thread/nxbdgtdlmwscocbiypjs@forum.dlang.org { invariant() { - if(j == 0) + if (j == 0) assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?"); else assert(!a.i.isNaN()); @@ -4827,7 +5263,7 @@ unittest //http://forum.dlang.org/thread/nxbdgtdlmwscocbiypjs@forum.dlang.org } //static arrays -unittest +@system unittest { static struct S { @@ -4850,7 +5286,7 @@ unittest static assert(!__traits(compiles, emplace(&s, uu))); } -unittest +@system unittest { int[2] sii; int[2] sii2; @@ -4870,7 +5306,7 @@ unittest emplace(&uii, uii2[]); } -unittest +@system unittest { bool allowDestruction = false; struct S @@ -4892,7 +5328,7 @@ unittest allowDestruction = true; } -unittest +@system unittest { //Checks postblit, construction, and context pointer int count = 0; @@ -4917,7 +5353,7 @@ unittest assert(count == 0); } -unittest +@system unittest { struct S { @@ -4928,7 +5364,7 @@ unittest emplace(&sss, s); } -unittest //Constness +@system unittest //Constness { import std.stdio; @@ -4954,7 +5390,7 @@ unittest //Constness emplaceRef!(IS[2])(ss, iss[]); } -unittest +pure nothrow @safe @nogc unittest { int i; emplaceRef(i); @@ -4963,95 +5399,57 @@ unittest emplaceRef!int(i, 5); } -private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) @nogc pure nothrow +// Test attribute propagation for UDTs +pure nothrow @safe /* @nogc */ unittest { - assert(chunk.length >= typeSize, "emplace: Chunk size too small."); - assert((cast(size_t)chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); -} - -/** -Given a raw memory area $(D chunk), constructs an object of $(D class) -type $(D T) at that address. The constructor is passed the arguments -$(D Args). The $(D chunk) must be as least as large as $(D T) needs -and should have an alignment multiple of $(D T)'s alignment. (The size -of a $(D class) instance is obtained by using $(D -__traits(classInstanceSize, T))). + static struct Safe + { + this(this) pure nothrow @safe @nogc {} + } -This function can be $(D @trusted) if the corresponding constructor of -$(D T) is $(D @safe). + Safe safe = void; + emplaceRef(safe, Safe()); -Returns: A pointer to the newly constructed object. - */ -T emplace(T, Args...)(void[] chunk, auto ref Args args) - if (is(T == class)) -{ - enum classSize = __traits(classInstanceSize, T); - testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof); - auto result = cast(T) chunk.ptr; + Safe[1] safeArr = [Safe()]; + Safe[1] uninitializedSafeArr = void; + emplaceRef(uninitializedSafeArr, safe); + emplaceRef(uninitializedSafeArr, safeArr); - // Initialize the object in its pre-ctor state - chunk[0 .. classSize] = typeid(T).init[]; - - // Call the ctor if any - static if (is(typeof(result.__ctor(args)))) - { - // T defines a genuine constructor accepting args - // Go the classic route: write .init first, then call ctor - result.__ctor(args); - } - else + static struct Unsafe { - static assert(args.length == 0 && !is(typeof(&T.__ctor)), - "Don't know how to initialize an object of type " - ~ T.stringof ~ " with arguments " ~ Args.stringof); + this(this) @system {} } - return result; -} -@nogc pure nothrow unittest -{ - int var = 6; - ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf; - auto k = emplace!__conv_EmplaceTestClass(buf, 5, var); - assert(k.i == 5); - assert(var == 7); -} - -/** -Given a raw memory area $(D chunk), constructs an object of non-$(D -class) type $(D T) at that address. The constructor is passed the -arguments $(D args), if any. The $(D chunk) must be as least as large -as $(D T) needs and should have an alignment multiple of $(D T)'s -alignment. + Unsafe unsafe = void; + static assert(!__traits(compiles, emplaceRef(unsafe, Unsafe()))); -This function can be $(D @trusted) if the corresponding constructor of -$(D T) is $(D @safe). - -Returns: A pointer to the newly constructed object. - */ -T* emplace(T, Args...)(void[] chunk, auto ref Args args) - if (!is(T == class)) -{ - testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof); - return emplace(cast(T*) chunk.ptr, args); + Unsafe[1] unsafeArr = [Unsafe()]; + Unsafe[1] uninitializedUnsafeArr = void; + static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe))); + static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr))); } -/// -unittest +@system unittest { - struct S + // Issue 15313 + static struct Node { - int a, b; + int payload; + Node* next; + uint refs; } - auto p = new void[S.sizeof]; - S s; - s.a = 42; - s.b = 43; - auto s1 = emplace!S(p, s); - assert(s1.a == 42 && s1.b == 43); + + import core.stdc.stdlib : malloc; + void[] buf = malloc(Node.sizeof)[0 .. Node.sizeof]; + + import std.conv : emplace; + const Node* n = emplace!(const Node)(buf, 42, null, 10); + assert(n.payload == 42); + assert(n.next == null); + assert(n.refs == 10); } -unittest +@system unittest { int var = 6; auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var); @@ -5059,7 +5457,7 @@ unittest assert(var == 7); } -unittest +@system unittest { class A { @@ -5073,7 +5471,7 @@ unittest } void[] buf; - static byte[__traits(classInstanceSize, A)] sbuf; + static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf; buf = sbuf[]; auto a = emplace!A(buf, 55); assert(a.x == 55 && a.y == 55); @@ -5086,17 +5484,19 @@ unittest // need ctor args static assert(!is(typeof(emplace!A(buf)))); } +// Bulk of emplace unittests ends here -unittest +@safe unittest { - import std.algorithm : equal, map; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; // Check fix for http://d.puremagic.com/issues/show_bug.cgi?id=2971 assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345])); } // Undocumented for the time being void toTextRange(T, W)(T value, W writer) - if (isIntegral!T && isOutputRange!(W, char)) +if (isIntegral!T && isOutputRange!(W, char)) { char[value.sizeof * 4] buffer = void; uint i = cast(uint) (buffer.length - 1); @@ -5117,7 +5517,7 @@ void toTextRange(T, W)(T value, W writer) put(writer, buffer[i .. $]); } -unittest +@safe unittest { import std.array : appender; auto result = appender!(char[])(); @@ -5133,16 +5533,18 @@ unittest (e.g from $(D int) to $(D long)). Note that the result is always mutable even if the original type was const - or immutable. In order to retain the constness, use $(XREF traits, Unsigned). + or immutable. In order to retain the constness, use $(REF Unsigned, std,traits). */ -auto unsigned(T)(T x) if (isIntegral!T) +auto unsigned(T)(T x) +if (isIntegral!T) { return cast(Unqual!(Unsigned!T))x; } /// -unittest +@safe unittest { + import std.traits : Unsigned; immutable int s = 42; auto u1 = unsigned(s); //not qualified static assert(is(typeof(u1) == uint)); @@ -5151,47 +5553,48 @@ unittest immutable u3 = unsigned(s); //explicitly qualified } -unittest +@safe unittest { - foreach(T; TypeTuple!(byte, ubyte)) + foreach (T; AliasSeq!(byte, ubyte)) { - static assert(is(typeof(unsigned(cast(T)1)) == ubyte)); - static assert(is(typeof(unsigned(cast(const T)1)) == ubyte)); - static assert(is(typeof(unsigned(cast(immutable T)1)) == ubyte)); + static assert(is(typeof(unsigned(cast(T) 1)) == ubyte)); + static assert(is(typeof(unsigned(cast(const T) 1)) == ubyte)); + static assert(is(typeof(unsigned(cast(immutable T) 1)) == ubyte)); } - foreach(T; TypeTuple!(short, ushort)) + foreach (T; AliasSeq!(short, ushort)) { - static assert(is(typeof(unsigned(cast(T)1)) == ushort)); - static assert(is(typeof(unsigned(cast(const T)1)) == ushort)); - static assert(is(typeof(unsigned(cast(immutable T)1)) == ushort)); + static assert(is(typeof(unsigned(cast(T) 1)) == ushort)); + static assert(is(typeof(unsigned(cast(const T) 1)) == ushort)); + static assert(is(typeof(unsigned(cast(immutable T) 1)) == ushort)); } - foreach(T; TypeTuple!(int, uint)) + foreach (T; AliasSeq!(int, uint)) { - static assert(is(typeof(unsigned(cast(T)1)) == uint)); - static assert(is(typeof(unsigned(cast(const T)1)) == uint)); - static assert(is(typeof(unsigned(cast(immutable T)1)) == uint)); + static assert(is(typeof(unsigned(cast(T) 1)) == uint)); + static assert(is(typeof(unsigned(cast(const T) 1)) == uint)); + static assert(is(typeof(unsigned(cast(immutable T) 1)) == uint)); } - foreach(T; TypeTuple!(long, ulong)) + foreach (T; AliasSeq!(long, ulong)) { - static assert(is(typeof(unsigned(cast(T)1)) == ulong)); - static assert(is(typeof(unsigned(cast(const T)1)) == ulong)); - static assert(is(typeof(unsigned(cast(immutable T)1)) == ulong)); + static assert(is(typeof(unsigned(cast(T) 1)) == ulong)); + static assert(is(typeof(unsigned(cast(const T) 1)) == ulong)); + static assert(is(typeof(unsigned(cast(immutable T) 1)) == ulong)); } } -auto unsigned(T)(T x) if (isSomeChar!T) +auto unsigned(T)(T x) +if (isSomeChar!T) { // All characters are unsigned static assert(T.min == 0); return cast(Unqual!T) x; } -unittest +@safe unittest { - foreach(T; TypeTuple!(char, wchar, dchar)) + foreach (T; AliasSeq!(char, wchar, dchar)) { static assert(is(typeof(unsigned(cast(T)'A')) == T)); static assert(is(typeof(unsigned(cast(const T)'A')) == T)); @@ -5207,16 +5610,19 @@ unittest (e.g from $(D uint) to $(D ulong)). Note that the result is always mutable even if the original type was const - or immutable. In order to retain the constness, use $(XREF traits, Signed). + or immutable. In order to retain the constness, use $(REF Signed, std,traits). */ -auto signed(T)(T x) if (isIntegral!T) +auto signed(T)(T x) +if (isIntegral!T) { return cast(Unqual!(Signed!T))x; } /// -unittest +@safe unittest { + import std.traits : Signed; + immutable uint u = 42; auto s1 = signed(u); //not qualified static assert(is(typeof(s1) == int)); @@ -5225,38 +5631,38 @@ unittest immutable s3 = signed(u); //explicitly qualified } -unittest +@system unittest { - foreach(T; TypeTuple!(byte, ubyte)) + foreach (T; AliasSeq!(byte, ubyte)) { - static assert(is(typeof(signed(cast(T)1)) == byte)); - static assert(is(typeof(signed(cast(const T)1)) == byte)); - static assert(is(typeof(signed(cast(immutable T)1)) == byte)); + static assert(is(typeof(signed(cast(T) 1)) == byte)); + static assert(is(typeof(signed(cast(const T) 1)) == byte)); + static assert(is(typeof(signed(cast(immutable T) 1)) == byte)); } - foreach(T; TypeTuple!(short, ushort)) + foreach (T; AliasSeq!(short, ushort)) { - static assert(is(typeof(signed(cast(T)1)) == short)); - static assert(is(typeof(signed(cast(const T)1)) == short)); - static assert(is(typeof(signed(cast(immutable T)1)) == short)); + static assert(is(typeof(signed(cast(T) 1)) == short)); + static assert(is(typeof(signed(cast(const T) 1)) == short)); + static assert(is(typeof(signed(cast(immutable T) 1)) == short)); } - foreach(T; TypeTuple!(int, uint)) + foreach (T; AliasSeq!(int, uint)) { - static assert(is(typeof(signed(cast(T)1)) == int)); - static assert(is(typeof(signed(cast(const T)1)) == int)); - static assert(is(typeof(signed(cast(immutable T)1)) == int)); + static assert(is(typeof(signed(cast(T) 1)) == int)); + static assert(is(typeof(signed(cast(const T) 1)) == int)); + static assert(is(typeof(signed(cast(immutable T) 1)) == int)); } - foreach(T; TypeTuple!(long, ulong)) + foreach (T; AliasSeq!(long, ulong)) { - static assert(is(typeof(signed(cast(T)1)) == long)); - static assert(is(typeof(signed(cast(const T)1)) == long)); - static assert(is(typeof(signed(cast(immutable T)1)) == long)); + static assert(is(typeof(signed(cast(T) 1)) == long)); + static assert(is(typeof(signed(cast(const T) 1)) == long)); + static assert(is(typeof(signed(cast(immutable T) 1)) == long)); } } -unittest +@safe unittest { // issue 10874 enum Test { a = 0 } @@ -5275,25 +5681,28 @@ unittest Params: From = The type to cast from. The programmer must ensure it is legal to make this cast. - To = The type to cast to - value = The value to cast. It must be of type $(D From), - otherwise a compile-time error is emitted. - - Returns: - the value after the cast, returned by reference if possible */ template castFrom(From) { + /** + Params: + To = The type _to cast _to. + value = The value _to cast. It must be of type $(D From), + otherwise a compile-time error is emitted. + + Returns: + the value after the cast, returned by reference if possible. + */ auto ref to(To, T)(auto ref T value) @system { - static assert ( + static assert( is(From == T), "the value to cast is not of specified type '" ~ From.stringof ~ "', it is of type '" ~ T.stringof ~ "'" ); - static assert ( - is(typeof(cast(To)value)), + static assert( + is(typeof(cast(To) value)), "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'" ); @@ -5302,7 +5711,7 @@ template castFrom(From) } /// -unittest +@system unittest { // Regular cast, which has been verified to be legal by the programmer: { @@ -5326,7 +5735,7 @@ unittest // allowing bad casts to be caught before it's too late: { long* x; - static assert ( + static assert( !__traits(compiles, castFrom!long.to!int(x)) ); @@ -5334,3 +5743,502 @@ unittest auto y = castFrom!(long*).to!int(x); } } + +// https://issues.dlang.org/show_bug.cgi?id=16667 +@system unittest +{ + ubyte[] a = ['a', 'b', 'c']; + assert(castFrom!(ubyte[]).to!(string)(a) == "abc"); +} + +/** +Check the correctness of a string for $(D hexString). +The result is true if and only if the input string is composed of whitespace +characters (\f\n\r\t\v lineSep paraSep nelSep) and +an even number of hexadecimal digits (regardless of the case). +*/ +@safe pure @nogc +private bool isHexLiteral(String)(scope const String hexData) +{ + import std.ascii : isHexDigit; + import std.uni : lineSep, paraSep, nelSep; + size_t i; + foreach (const dchar c; hexData) + { + switch (c) + { + case ' ': + case '\t': + case '\v': + case '\f': + case '\r': + case '\n': + case lineSep: + case paraSep: + case nelSep: + continue; + + default: + break; + } + if (c.isHexDigit) + ++i; + else + return false; + } + return !(i & 1); +} + +@safe unittest +{ + // test all the hex digits + static assert( ("0123456789abcdefABCDEF").isHexLiteral); + // empty or white strings are not valid + static assert( "\r\n\t".isHexLiteral); + // but are accepted if the count of hex digits is even + static assert( "A\r\n\tB".isHexLiteral); +} + +@safe unittest +{ + import std.ascii; + // empty/whites + static assert( "".isHexLiteral); + static assert( " \r".isHexLiteral); + static assert( whitespace.isHexLiteral); + static assert( ""w.isHexLiteral); + static assert( " \r"w.isHexLiteral); + static assert( ""d.isHexLiteral); + static assert( " \r"d.isHexLiteral); + static assert( "\u2028\u2029\u0085"d.isHexLiteral); + // odd x strings + static assert( !("5" ~ whitespace).isHexLiteral); + static assert( !"123".isHexLiteral); + static assert( !"1A3".isHexLiteral); + static assert( !"1 23".isHexLiteral); + static assert( !"\r\n\tC".isHexLiteral); + static assert( !"123"w.isHexLiteral); + static assert( !"1A3"w.isHexLiteral); + static assert( !"1 23"w.isHexLiteral); + static assert( !"\r\n\tC"w.isHexLiteral); + static assert( !"123"d.isHexLiteral); + static assert( !"1A3"d.isHexLiteral); + static assert( !"1 23"d.isHexLiteral); + static assert( !"\r\n\tC"d.isHexLiteral); + // even x strings with invalid charset + static assert( !"12gG".isHexLiteral); + static assert( !"2A 3q".isHexLiteral); + static assert( !"12gG"w.isHexLiteral); + static assert( !"2A 3q"w.isHexLiteral); + static assert( !"12gG"d.isHexLiteral); + static assert( !"2A 3q"d.isHexLiteral); + // valid x strings + static assert( ("5A" ~ whitespace).isHexLiteral); + static assert( ("5A 01A C FF de 1b").isHexLiteral); + static assert( ("0123456789abcdefABCDEF").isHexLiteral); + static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF").isHexLiteral); + static assert( ("5A 01A C FF de 1b"w).isHexLiteral); + static assert( ("0123456789abcdefABCDEF"w).isHexLiteral); + static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"w).isHexLiteral); + static assert( ("5A 01A C FF de 1b"d).isHexLiteral); + static assert( ("0123456789abcdefABCDEF"d).isHexLiteral); + static assert( (" 012 34 5 6789 abcd ef\rAB\nCDEF"d).isHexLiteral); + // library version allows what's pointed by issue 10454 + static assert( ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").isHexLiteral); +} + +/** +Converts a hex literal to a string at compile time. + +Takes a string made of hexadecimal digits and returns +the matching string by converting each pair of digits to a character. +The input string can also include white characters, which can be used +to keep the literal string readable in the source code. + +The function is intended to replace the hexadecimal literal strings +starting with $(D 'x'), which could be removed to simplify the core language. + +Params: + hexData = string to be converted. + +Returns: + a $(D string), a $(D wstring) or a $(D dstring), according to the type of hexData. + */ +template hexString(string hexData) +if (hexData.isHexLiteral) +{ + immutable hexString = hexStrImpl(hexData); +} + +/// ditto +template hexString(wstring hexData) +if (hexData.isHexLiteral) +{ + immutable hexString = hexStrImpl(hexData); +} + +/// ditto +template hexString(dstring hexData) +if (hexData.isHexLiteral) +{ + immutable hexString = hexStrImpl(hexData); +} + +/// +@safe unittest +{ + // conversion at compile time + auto string1 = hexString!"304A314B"; + assert(string1 == "0J1K"); + auto string2 = hexString!"304A314B"w; + assert(string2 == "0J1K"w); + auto string3 = hexString!"304A314B"d; + assert(string3 == "0J1K"d); +} + +/* + Takes a hexadecimal string literal and returns its representation. + hexData is granted to be a valid string by the caller. + C is granted to be a valid char type by the caller. +*/ +@safe nothrow pure +private auto hexStrImpl(String)(scope String hexData) +{ + import std.ascii : isHexDigit; + alias C = Unqual!(ElementEncodingType!String); + C[] result; + result.length = hexData.length / 2; + size_t cnt; + ubyte v; + foreach (c; hexData) + { + if (c.isHexDigit) + { + ubyte x; + if (c >= '0' && c <= '9') + x = cast(ubyte)(c - '0'); + else if (c >= 'a' && c <= 'f') + x = cast(ubyte)(c - ('a' - 10)); + else if (c >= 'A' && c <= 'F') + x = cast(ubyte)(c - ('A' - 10)); + if (cnt & 1) + { + v = cast(ubyte)((v << 4) | x); + result[cnt / 2] = v; + } + else + v = x; + ++cnt; + } + } + result.length = cnt / 2; + return result; +} + +@safe unittest +{ + // compile time + assert(hexString!"46 47 48 49 4A 4B" == "FGHIJK"); + assert(hexString!"30\r\n\t\f\v31 32 33 32 31 30" == "0123210"); + assert(hexString!"ab cd" == hexString!"ABCD"); +} + + +/** + * Convert integer to a range of characters. + * Intended to be lightweight and fast. + * + * Params: + * radix = 2, 8, 10, 16 + * Char = character type for output + * letterCase = lower for deadbeef, upper for DEADBEEF + * value = integer to convert. Can be uint or ulong. If radix is 10, can also be + * int or long. + * Returns: + * Random access range with slicing and everything + */ + +auto toChars(ubyte radix = 10, Char = char, LetterCase letterCase = LetterCase.lower, T)(T value) + pure nothrow @nogc @safe +if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) && + (is(Unqual!T == uint) || is(Unqual!T == ulong) || + radix == 10 && (is(Unqual!T == int) || is(Unqual!T == long)))) +{ + alias UT = Unqual!T; + + static if (radix == 10) + { + /* uint.max is 42_9496_7295 + * int.max is 21_4748_3647 + * ulong.max is 1844_6744_0737_0955_1615 + * long.max is 922_3372_0368_5477_5807 + */ + static struct Result + { + void initialize(UT value) + { + bool neg = false; + if (value < 10) + { + if (value >= 0) + { + lwr = 0; + upr = 1; + buf[0] = cast(char)(cast(uint) value + '0'); + return; + } + value = -value; + neg = true; + } + auto i = cast(uint) buf.length - 1; + while (cast(Unsigned!UT) value >= 10) + { + buf[i] = cast(ubyte)('0' + cast(Unsigned!UT) value % 10); + value = unsigned(value) / 10; + --i; + } + buf[i] = cast(char)(cast(uint) value + '0'); + if (neg) + { + buf[i - 1] = '-'; + --i; + } + lwr = i; + upr = cast(uint) buf.length; + } + + @property size_t length() { return upr - lwr; } + + alias opDollar = length; + + @property bool empty() { return upr == lwr; } + + @property Char front() { return buf[lwr]; } + + void popFront() { ++lwr; } + + @property Char back() { return buf[upr - 1]; } + + void popBack() { --upr; } + + @property Result save() { return this; } + + Char opIndex(size_t i) { return buf[lwr + i]; } + + Result opSlice(size_t lwr, size_t upr) + { + Result result = void; + result.buf = buf; + result.lwr = cast(uint)(this.lwr + lwr); + result.upr = cast(uint)(this.lwr + upr); + return result; + } + + private: + uint lwr = void, upr = void; + char[(UT.sizeof == 4) ? 10 + isSigned!T : 20] buf = void; + } + + Result result = void; + result.initialize(value); + return result; + } + else + { + static if (radix == 2) + enum SHIFT = 1; + else static if (radix == 8) + enum SHIFT = 3; + else static if (radix == 16) + enum SHIFT = 4; + else + static assert(0); + static struct Result + { + this(UT value) + { + this.value = value; + + ubyte len = 1; + while (value >>>= SHIFT) + ++len; + this.len = len; + } + + @property size_t length() { return len; } + + @property bool empty() { return len == 0; } + + @property Char front() { return opIndex(0); } + + void popFront() { --len; } + + @property Char back() { return opIndex(len - 1); } + + void popBack() + { + value >>>= SHIFT; + --len; + } + + @property Result save() { return this; } + + Char opIndex(size_t i) + { + Char c = (value >>> ((len - i - 1) * SHIFT)) & ((1 << SHIFT) - 1); + return cast(Char)((radix < 10 || c < 10) ? c + '0' + : (letterCase == LetterCase.upper ? c + 'A' - 10 + : c + 'a' - 10)); + } + + Result opSlice(size_t lwr, size_t upr) + { + Result result = void; + result.value = value >>> ((len - upr) * SHIFT); + result.len = cast(ubyte)(upr - lwr); + return result; + } + + private: + UT value; + ubyte len; + } + + return Result(value); + } +} + + +@safe unittest +{ + import std.array; + import std.range; + + { + assert(toChars!2(0u).array == "0"); + assert(toChars!2(0Lu).array == "0"); + assert(toChars!2(1u).array == "1"); + assert(toChars!2(1Lu).array == "1"); + + auto r = toChars!2(2u); + assert(r.length == 2); + assert(r[0] == '1'); + assert(r[1 .. 2].array == "0"); + auto s = r.save; + assert(r.array == "10"); + assert(s.retro.array == "01"); + } + { + assert(toChars!8(0u).array == "0"); + assert(toChars!8(0Lu).array == "0"); + assert(toChars!8(1u).array == "1"); + assert(toChars!8(1234567Lu).array == "4553207"); + + auto r = toChars!8(8u); + assert(r.length == 2); + assert(r[0] == '1'); + assert(r[1 .. 2].array == "0"); + auto s = r.save; + assert(r.array == "10"); + assert(s.retro.array == "01"); + } + { + assert(toChars!10(0u).array == "0"); + assert(toChars!10(0Lu).array == "0"); + assert(toChars!10(1u).array == "1"); + assert(toChars!10(1234567Lu).array == "1234567"); + assert(toChars!10(uint.max).array == "4294967295"); + assert(toChars!10(ulong.max).array == "18446744073709551615"); + + auto r = toChars(10u); + assert(r.length == 2); + assert(r[0] == '1'); + assert(r[1 .. 2].array == "0"); + auto s = r.save; + assert(r.array == "10"); + assert(s.retro.array == "01"); + } + { + assert(toChars!10(0).array == "0"); + assert(toChars!10(0L).array == "0"); + assert(toChars!10(1).array == "1"); + assert(toChars!10(1234567L).array == "1234567"); + assert(toChars!10(int.max).array == "2147483647"); + assert(toChars!10(long.max).array == "9223372036854775807"); + assert(toChars!10(-int.max).array == "-2147483647"); + assert(toChars!10(-long.max).array == "-9223372036854775807"); + assert(toChars!10(int.min).array == "-2147483648"); + assert(toChars!10(long.min).array == "-9223372036854775808"); + + auto r = toChars!10(10); + assert(r.length == 2); + assert(r[0] == '1'); + assert(r[1 .. 2].array == "0"); + auto s = r.save; + assert(r.array == "10"); + assert(s.retro.array == "01"); + } + { + assert(toChars!(16)(0u).array == "0"); + assert(toChars!(16)(0Lu).array == "0"); + assert(toChars!(16)(10u).array == "a"); + assert(toChars!(16, char, LetterCase.upper)(0x12AF34567Lu).array == "12AF34567"); + + auto r = toChars!(16)(16u); + assert(r.length == 2); + assert(r[0] == '1'); + assert(r[1 .. 2].array == "0"); + auto s = r.save; + assert(r.array == "10"); + assert(s.retro.array == "01"); + } +} + +@safe unittest // opSlice (issue 16192) +{ + import std.meta : AliasSeq; + + static struct Test { ubyte radix; uint number; } + + alias tests = AliasSeq!( + Test(2, 0b1_0110_0111u), + Test(2, 0b10_1100_1110u), + Test(8, octal!123456701u), + Test(8, octal!1234567012u), + Test(10, 123456789u), + Test(10, 1234567890u), + Test(16, 0x789ABCDu), + Test(16, 0x789ABCDEu), + ); + + foreach (test; tests) + { + enum ubyte radix = test.radix; + auto original = toChars!radix(test.number); + + // opSlice vs popFront + auto r = original.save; + size_t i = 0; + for (; !r.empty; r.popFront(), ++i) + { + assert(original[i .. original.length].tupleof == r.tupleof); + // tupleof is used to work around issue 16216. + } + + // opSlice vs popBack + r = original.save; + i = 0; + for (; !r.empty; r.popBack(), ++i) + { + assert(original[0 .. original.length - i].tupleof == r.tupleof); + } + + // opSlice vs both popFront and popBack + r = original.save; + i = 0; + for (; r.length >= 2; r.popFront(), r.popBack(), ++i) + { + assert(original[i .. original.length - i].tupleof == r.tupleof); + } + } +} diff --git a/std/cstream.d b/std/cstream.d deleted file mode 100644 index ef54367f11f..00000000000 --- a/std/cstream.d +++ /dev/null @@ -1,250 +0,0 @@ -// Written in the D programming language. - -/** - * $(RED Warning: This module is considered out-dated and not up to Phobos' - * current standards. It will remain until we have a suitable replacement, - * but be aware that it will not remain long term.) - * - * The std.cstream module bridges core.stdc.stdio (or std.stdio) and std.stream. - * Both core.stdc.stdio and std.stream are publicly imported by std.cstream. - * - * Macros: - * WIKI=Phobos/StdCstream - * - * Copyright: Copyright Ben Hinkle 2007 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Ben Hinkle - * Source: $(PHOBOSSRC std/_cstream.d) - */ -/* Copyright Ben Hinkle 2007 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module std.cstream; - -public import core.stdc.stdio; -public import std.stream; -version(unittest) import std.stdio; - -import std.algorithm; - -/** - * A Stream wrapper for a C file of type FILE*. - */ -class CFile : Stream { - FILE* cfile; - - /** - * Create the stream wrapper for the given C file. - * Params: - * cfile = a valid C $(B FILE) pointer to wrap. - * mode = a bitwise combination of $(B FileMode.In) for a readable file - * and $(B FileMode.Out) for a writeable file. - * seekable = indicates if the stream should be _seekable. - */ - this(FILE* cfile, FileMode mode, bool seekable = false) { - super(); - this.file = cfile; - readable = cast(bool)(mode & FileMode.In); - writeable = cast(bool)(mode & FileMode.Out); - this.seekable = seekable; - } - - /** - * Closes the stream. - */ - ~this() { close(); } - - /** - * Property to get or set the underlying file for this stream. - * Setting the file marks the stream as open. - */ - @property FILE* file() { return cfile; } - - /** - * Ditto - */ - @property void file(FILE* cfile) { - this.cfile = cfile; - isopen = true; - } - - /** - * Overrides of the $(B Stream) methods to call the underlying $(B FILE*) - * C functions. - */ - override void flush() { fflush(cfile); } - - /** - * Ditto - */ - override void close() { - if (isopen) - fclose(cfile); - isopen = readable = writeable = seekable = false; - } - - /** - * Ditto - */ - override bool eof() { - return cast(bool)(readEOF || feof(cfile)); - } - - /** - * Ditto - */ - override char getc() { - return cast(char)fgetc(cfile); - } - - /** - * Ditto - */ - override char ungetc(char c) { - return cast(char)core.stdc.stdio.ungetc(c,cfile); - } - - /** - * Ditto - */ - override size_t readBlock(void* buffer, size_t size) { - size_t n = fread(buffer,1,size,cfile); - readEOF = cast(bool)(n == 0); - return n; - } - - /** - * Ditto - */ - override size_t writeBlock(const void* buffer, size_t size) { - return fwrite(buffer,1,size,cfile); - } - - /** - * Ditto - */ - override ulong seek(long offset, SeekPos rel) { - readEOF = false; - if (fseek(cfile,cast(int)offset,rel) != 0) - throw new SeekException("unable to move file pointer"); - return ftell(cfile); - } - - /** - * Ditto - */ - override void writeLine(const(char)[] s) { - writeString(s); - writeString("\n"); - } - - /** - * Ditto - */ - override void writeLineW(const(wchar)[] s) { - writeStringW(s); - writeStringW("\n"); - } - - // run a few tests - unittest { - import std.file : deleteme; - import std.internal.cstring : tempCString; - - auto stream_file = (std.file.deleteme ~ "-stream.txt").tempCString(); - FILE* f = fopen(stream_file,"w"); - assert(f !is null); - CFile file = new CFile(f,FileMode.Out); - int i = 666; - // should be ok to write - assert(file.writeable); - file.writeLine("Testing stream.d:"); - file.writeString("Hello, world!"); - file.write(i); - // string#1 + string#2 + int should give exacly that - version (Windows) - assert(file.position == 19 + 13 + 4); - version (Posix) - assert(file.position == 18 + 13 + 4); - file.close(); - // no operations are allowed when file is closed - assert(!file.readable && !file.writeable && !file.seekable); - f = fopen(stream_file,"r"); - file = new CFile(f,FileMode.In,true); - // should be ok to read - assert(file.readable); - auto line = file.readLine(); - auto exp = "Testing stream.d:"; - assert(line[0] == 'T'); - assert(line.length == exp.length); - assert(!std.algorithm.cmp(line, "Testing stream.d:")); - // jump over "Hello, " - file.seek(7, SeekPos.Current); - version (Windows) - assert(file.position == 19 + 7); - version (Posix) - assert(file.position == 18 + 7); - assert(!std.algorithm.cmp(file.readString(6), "world!")); - i = 0; file.read(i); - assert(i == 666); - // string#1 + string#2 + int should give exacly that - version (Windows) - assert(file.position == 19 + 13 + 4); - version (Posix) - assert(file.position == 18 + 13 + 4); - // we must be at the end of file - file.close(); - f = fopen(stream_file,"w+"); - file = new CFile(f,FileMode.In|FileMode.Out,true); - file.writeLine("Testing stream.d:"); - file.writeLine("Another line"); - file.writeLine(""); - file.writeLine("That was blank"); - file.position = 0; - char[][] lines; - foreach(char[] line; file) { - lines ~= line.dup; - } - assert( lines.length == 5 ); - assert( lines[0] == "Testing stream.d:"); - assert( lines[1] == "Another line"); - assert( lines[2] == ""); - assert( lines[3] == "That was blank"); - file.position = 0; - lines = new char[][5]; - foreach(ulong n, char[] line; file) { - lines[cast(size_t)(n-1)] = line.dup; - } - assert( lines[0] == "Testing stream.d:"); - assert( lines[1] == "Another line"); - assert( lines[2] == ""); - assert( lines[3] == "That was blank"); - file.close(); - remove(stream_file); - } -} - -/** - * CFile wrapper of core.stdc.stdio.stdin (not seekable). - */ -__gshared CFile din; - -/** - * CFile wrapper of core.stdc.stdio.stdout (not seekable). - */ -__gshared CFile dout; - -/** - * CFile wrapper of core.stdc.stdio.stderr (not seekable). - */ -__gshared CFile derr; - -shared static this() { - // open standard I/O devices - din = new CFile(core.stdc.stdio.stdin,FileMode.In); - dout = new CFile(core.stdc.stdio.stdout,FileMode.Out); - derr = new CFile(core.stdc.stdio.stderr,FileMode.Out); -} - diff --git a/std/csv.d b/std/csv.d index 3d91f048c1f..be286c9a7e7 100644 --- a/std/csv.d +++ b/std/csv.d @@ -2,13 +2,13 @@ /** * Implements functionality to read Comma Separated Values and its variants - * from a input range of $(D dchar). + * from an input range of $(D dchar). * * Comma Separated Values provide a simple means to transfer and store * tabular data. It has been common for programs to use their own * variant of the CSV format. This parser will loosely follow the - * $(WEB tools.ietf.org/html/rfc4180, RFC-4180). CSV input should adhered - * to the following criteria, differences from RFC-4180 in parentheses. + * $(HTTP tools.ietf.org/html/rfc4180, RFC-4180). CSV input should adhere + * to the following criteria (differences from RFC-4180 in parentheses): * * $(UL * $(LI A record is separated by a new line (CRLF,LF,CR)) @@ -34,11 +34,22 @@ * { * auto text = "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"; * - * foreach(record; csvReader!(Tuple!(string,string,int))(text)) + * foreach (record; csvReader!(Tuple!(string, string, int))(text)) * { * writefln("%s works as a %s and earns $%d per year", * record[0], record[1], record[2]); * } + * + * // To read the same string from the file "filename.csv": + * + * auto file = File("filename.csv", "r"); + * foreach (record; + * file.byLine.joiner("\n").csvReader!(Tuple!(string, string, int))) + * { + * writefln("%s works as a %s and earns $%d per year", + * record[0], record[1], record[2]); + * } + } * } * ------- * @@ -49,7 +60,7 @@ * auto text = "Name,Occupation,Salary\r" * "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"; * - * foreach(record; csvReader!(string[string]) + * foreach (record; csvReader!(string[string]) * (text, null)) * { * writefln("%s works as a %s and earns $%s per year.", @@ -70,11 +81,11 @@ * closed. * * See_Also: - * $(WEB en.wikipedia.org/wiki/Comma-separated_values, Wikipedia + * $(HTTP en.wikipedia.org/wiki/Comma-separated_values, Wikipedia * Comma-separated values) * * Copyright: Copyright 2011 - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Jesse Phillips * Source: $(PHOBOSSRC std/_csv.d) */ @@ -83,6 +94,7 @@ module std.csv; import std.conv; import std.range.primitives; import std.traits; +import std.exception; // basicExceptionCtors /** * Exception containing the row and column for when an exception was thrown. @@ -92,35 +104,37 @@ import std.traits; * be made when there is failure to match the header see $(LREF * HeaderMismatchException) for details. * - * When performing type conversions, $(XREF ConvException) is stored in the $(D - * next) field. + * When performing type conversions, $(REF ConvException, std,conv) is stored in + * the $(D next) field. */ class CSVException : Exception { /// size_t row, col; + // FIXME: Use std.exception.basicExceptionCtors here once bug #11500 is fixed + this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) @safe pure + Throwable next = null) @nogc @safe pure nothrow { super(msg, file, line, next); } this(string msg, Throwable next, string file = __FILE__, - size_t line = __LINE__) @safe pure + size_t line = __LINE__) @nogc @safe pure nothrow { super(msg, file, line, next); } this(string msg, size_t row, size_t col, Throwable next = null, - string file = __FILE__, size_t line = __LINE__) @safe pure + string file = __FILE__, size_t line = __LINE__) @nogc @safe pure nothrow { super(msg, next, file, line); this.row = row; this.col = col; } - override string toString() @safe pure + override string toString() @safe pure const { return "(Row: " ~ to!string(row) ~ ", Col: " ~ to!string(col) ~ ") " ~ msg; @@ -153,24 +167,16 @@ class CSVException : Exception */ class IncompleteCellException : CSVException { - /// Data pulled from input before finding a problem - /// - /// This field is populated when using $(LREF csvReader) - /// but not by $(LREF csvNextToken) as this data will have - /// already been fed to the output range. + /** + * Data pulled from input before finding a problem + * + * This field is populated when using $(LREF csvReader) + * but not by $(LREF csvNextToken) as this data will have + * already been fed to the output range. + */ dstring partialData; - this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) @safe pure - { - super(msg, file, line); - } - - this(string msg, Throwable next, string file = __FILE__, size_t line = - __LINE__) @safe pure - { - super(msg, next, file, line); - } + mixin basicExceptionCtors; } @safe pure unittest @@ -202,17 +208,7 @@ class IncompleteCellException : CSVException */ class HeaderMismatchException : CSVException { - this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable next = null) @safe pure - { - super(msg, file, line); - } - - this(string msg, Throwable next, string file = __FILE__, - size_t line = __LINE__) @safe pure - { - super(msg, next, file, line); - } + mixin basicExceptionCtors; } @safe pure unittest @@ -240,10 +236,8 @@ class HeaderMismatchException : CSVException */ enum Malformed { - /// No exceptions are thrown due to incorrect CSV. - ignore, - /// Use exceptions when input has incorrect CSV. - throwException + ignore, /// No exceptions are thrown due to incorrect CSV. + throwException /// Use exceptions when input has incorrect CSV. } /** @@ -258,7 +252,7 @@ enum Malformed * int[] ans = [76,26,22]; * auto records = csvReader!int(str); * - * foreach(record; records) + * foreach (record; records) * { * assert(equal(record, ans)); * } @@ -277,7 +271,7 @@ enum Malformed * * auto records = csvReader!Layout(str,';'); * - * foreach(record; records) + * foreach (record; records) * { * writeln(record.name); * writeln(record.value); @@ -298,10 +292,11 @@ enum Malformed * ------- * * Returns: - * An input range R as defined by $(XREF range, isInputRange). When $(D - * Contents) is a struct, class, or an associative array, the element - * type of R is $(D Contents), otherwise the element type of R is itself - * a range with element type $(D Contents). + * An input range R as defined by + * $(REF isInputRange, std,range,primitives). When $(D Contents) is a + * struct, class, or an associative array, the element type of R is + * $(D Contents), otherwise the element type of R is itself a range with + * element type $(D Contents). * * Throws: * $(LREF CSVException) When a quote is found in an unquoted field, @@ -316,9 +311,9 @@ enum Malformed */ auto csvReader(Contents = string,Malformed ErrorLevel = Malformed.throwException, Range, Separator = char)(Range input, Separator delimiter = ',', Separator quote = '"') - if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar) - && isSomeChar!(Separator) - && !is(Contents T : T[U], U : string)) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar) + && isSomeChar!(Separator) + && !is(Contents T : T[U], U : string)) { return CsvReader!(Contents,ErrorLevel,Range, Unqual!(ElementType!Range),string[]) @@ -339,7 +334,7 @@ auto csvReader(Contents = string,Malformed ErrorLevel = Malformed.throwException * auto records = csvReader!int(str, ["b"]); * * auto ans = [[65],[123]]; - * foreach(record; records) + * foreach (record; records) * { * assert(equal(record, ans.front)); * ans.popFront(); @@ -372,10 +367,11 @@ auto csvReader(Contents = string,Malformed ErrorLevel = Malformed.throwException * ------- * * Returns: - * An input range R as defined by $(XREF range, isInputRange). When $(D - * Contents) is a struct, class, or an associative array, the element - * type of R is $(D Contents), otherwise the element type of R is itself - * a range with element type $(D Contents). + * An input range R as defined by + * $(REF isInputRange, std,range,primitives). When $(D Contents) is a + * struct, class, or an associative array, the element type of R is + * $(D Contents), otherwise the element type of R is itself a range with + * element type $(D Contents). * * The returned range provides a header field for accessing the header * from the input in array form. @@ -403,10 +399,10 @@ auto csvReader(Contents = string, Range, Header, Separator = char) (Range input, Header header, Separator delimiter = ',', Separator quote = '"') - if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar) - && isSomeChar!(Separator) - && isForwardRange!Header - && isSomeString!(ElementType!Header)) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar) + && isSomeChar!(Separator) + && isForwardRange!Header + && isSomeString!(ElementType!Header)) { return CsvReader!(Contents,ErrorLevel,Range, Unqual!(ElementType!Range),Header) @@ -419,13 +415,13 @@ auto csvReader(Contents = string, Range, Header, Separator = char) (Range input, Header header, Separator delimiter = ',', Separator quote = '"') - if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar) - && isSomeChar!(Separator) - && is(Header : typeof(null))) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar) + && isSomeChar!(Separator) + && is(Header : typeof(null))) { return CsvReader!(Contents,ErrorLevel,Range, Unqual!(ElementType!Range),string[]) - (input, cast(string[])null, delimiter, quote); + (input, cast(string[]) null, delimiter, quote); } // Test standard iteration over input. @@ -435,9 +431,9 @@ auto csvReader(Contents = string, auto records = csvReader(str); int count; - foreach(record; records) + foreach (record; records) { - foreach(cell; record) + foreach (cell; record) { count++; } @@ -476,7 +472,7 @@ auto csvReader(Contents = string, auto records = csvReader!(Layout,Malformed.ignore)(str); int count; - foreach(record; records) + foreach (record; records) { assert(ans[count].name == record.name); assert(ans[count].value == record.value); @@ -499,10 +495,10 @@ auto csvReader(Contents = string, "one\ntwo,2\nthree,3", "one,1\ntwo\nthree,3"]; - foreach(str; strs) + foreach (str; strs) { auto records = csvReader!A(str); - assertThrown!CSVException((){foreach(record; records) { }}()); + assertThrown!CSVException((){foreach (record; records) { }}()); } } @@ -531,7 +527,7 @@ auto csvReader(Contents = string, auto records = csvReader!Layout(str); int count; - foreach(record; records) + foreach (record; records) { assert(ans[count].name == record.name); assert(ans[count].value == record.value); @@ -549,14 +545,14 @@ auto csvReader(Contents = string, int[] ans = [76,26,22]; auto records = csvReader!int(str); - foreach(record; records) + foreach (record; records) { assert(equal(record, ans)); } } // Test struct & header interface and same unicode -unittest +@safe unittest { import std.math : abs; @@ -591,7 +587,7 @@ unittest } // Test header interface -unittest +@safe unittest { import std.algorithm; @@ -599,7 +595,7 @@ unittest auto records = csvReader!int(str, ["b"]); auto ans = [[65],[123]]; - foreach(record; records) + foreach (record; records) { assert(equal(record, ans.front)); ans.popFront(); @@ -610,7 +606,7 @@ unittest csvReader(str, ["c","b"]); assert(0); } - catch(HeaderMismatchException e) + catch (HeaderMismatchException e) { assert(e.col == 2); } @@ -618,7 +614,8 @@ unittest (str, ["b","a"], ',', '"'); auto ans2 = [["Hello","65"],["World","123"]]; - foreach(record; records2) { + foreach (record; records2) + { assert(equal(record, ans2.front)); ans2.popFront(); } @@ -628,14 +625,15 @@ unittest (str, ["a","b","c","d"], ',', '"'); ans2 = [["Joe","Carpenter"],["Fred","Fly"]]; - foreach(record; records2) { + foreach (record; records2) + { assert(equal(record, ans2.front)); ans2.popFront(); } } // Test null header interface -unittest +@safe unittest { string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562"; auto records = csvReader(str, ["a"]); @@ -647,9 +645,9 @@ unittest @safe pure unittest { string str = "one \"quoted\""; - foreach(record; csvReader!(string,Malformed.ignore)(str)) + foreach (record; csvReader!(string,Malformed.ignore)(str)) { - foreach(cell; record) + foreach (cell; record) { assert(cell == "one \"quoted\""); } @@ -660,7 +658,7 @@ unittest { string a,b; } - foreach(record; csvReader!(Ans,Malformed.ignore)(str)) + foreach (record; csvReader!(Ans,Malformed.ignore)(str)) { assert(record.a == "one \"quoted\""); assert(record.b == "two \"quoted\" end"); @@ -674,7 +672,7 @@ unittest try { - foreach(record; csvReader(str)) + foreach (record; csvReader(str)) {} assert(0); } @@ -701,13 +699,13 @@ unittest // Test associative array support with unicode separator -unittest +@safe unittest { string str = "1â2â3\n34â65â63\n34â65â63"; auto records = csvReader!(string[string])(str,["3","1"],'â'); int count; - foreach(record; records) + foreach (record; records) { count++; assert(record["1"] == "34"); @@ -717,7 +715,7 @@ unittest } // Test restricted range -unittest +@safe unittest { import std.typecons; struct InputRange @@ -734,7 +732,7 @@ unittest return text.empty; } - auto popFront() + void popFront() { text.popFront(); } @@ -747,18 +745,18 @@ unittest auto ir = InputRange("Name,Occupation,Salary\r"d~ "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"d); - foreach(record; csvReader(ir, cast(string[])null)) - foreach(cell; record) {} - foreach(record; csvReader!(Tuple!(string,string,int)) - (ir,cast(string[])null)) {} - foreach(record; csvReader!(string[string]) - (ir,cast(string[])null)) {} + foreach (record; csvReader(ir, cast(string[]) null)) + foreach (cell; record) {} + foreach (record; csvReader!(Tuple!(string, string, int)) + (ir,cast(string[]) null)) {} + foreach (record; csvReader!(string[string]) + (ir,cast(string[]) null)) {} } -unittest // const/immutable dchars +@safe unittest // const/immutable dchars { - import std.algorithm: map; - import std.array: array; + import std.algorithm.iteration : map; + import std.array : array; const(dchar)[] c = "foo,bar\n"; assert(csvReader(c).map!array.array == [["foo", "bar"]]); immutable(dchar)[] i = "foo,bar\n"; @@ -785,9 +783,9 @@ private pure struct Input(Range, Malformed ErrorLevel) * Malformed).ignore if best guess processing should take place. */ private struct CsvReader(Contents, Malformed ErrorLevel, Range, Separator, Header) - if (isSomeChar!Separator && isInputRange!Range - && is(Unqual!(ElementType!Range) == dchar) - && isForwardRange!Header && isSomeString!(ElementType!Header)) +if (isSomeChar!Separator && isInputRange!Range + && is(Unqual!(ElementType!Range) == dchar) + && isForwardRange!Header && isSomeString!(ElementType!Header)) { private: Input!(Range, ErrorLevel)* _input; @@ -830,15 +828,15 @@ public: * auto records = CsvReader!(int,Malformed.ignore,string,char,string[]) * (str, ';', '^'); * - * foreach(record; records) { + * foreach (record; records) + * { * assert(equal(record, ans)); * } * ------- */ this(Range input, Separator delimiter, Separator quote) { - _input = new Input!(Range, ErrorLevel); - _input.range = input; + _input = new Input!(Range, ErrorLevel)(input); _separator = delimiter; _quote = quote; @@ -855,7 +853,8 @@ public: * (str, ["high","low"], ';', '^'); * * int[] ans = [76,22]; - * foreach(record; records) { + * foreach (record; records) + * { * assert(equal(record, ans)); * } * ------- @@ -867,13 +866,12 @@ public: */ this(Range input, Header colHeaders, Separator delimiter, Separator quote) { - _input = new Input!(Range, ErrorLevel); - _input.range = input; + _input = new Input!(Range, ErrorLevel)(input); _separator = delimiter; _quote = quote; size_t[string] colToIndex; - foreach(h; colHeaders) + foreach (h; colHeaders) { colToIndex[h] = size_t.max; } @@ -882,7 +880,7 @@ public: (_input, _separator, _quote, indices); size_t colIndex; - foreach(col; r) + foreach (col; r) { header ~= col; auto ptr = col in colToIndex; @@ -895,7 +893,7 @@ public: indices.length = colToIndex.length; int i; - foreach(h; colHeaders) + foreach (h; colHeaders) { immutable index = colToIndex[h]; static if (ErrorLevel != Malformed.ignore) @@ -909,17 +907,18 @@ public: { static if (is(Contents T : T[U], U : string)) { - import std.algorithm : sort; + import std.algorithm.sorting : sort; sort(indices); } else static if (ErrorLevel == Malformed.ignore) { - import std.algorithm : sort; + import std.algorithm.sorting : sort; sort(indices); } else { - import std.algorithm : isSorted, findAdjacent; + import std.algorithm.searching : findAdjacent; + import std.algorithm.sorting : isSorted; if (!isSorted(indices)) { auto ex = new HeaderMismatchException @@ -937,7 +936,8 @@ public: } /** - * Part of an input range as defined by $(XREF range, isInputRange). + * Part of an input range as defined by + * $(REF isInputRange, std,range,primitives). * * Returns: * If $(D Contents) is a struct, will be filled with record data. @@ -968,7 +968,8 @@ public: } /** - * Part of an input range as defined by $(XREF range, isInputRange). + * Part of an input range as defined by + * $(REF isInputRange, std,range,primitives). */ @property bool empty() @safe @nogc pure nothrow const { @@ -976,7 +977,8 @@ public: } /** - * Part of an input range as defined by $(XREF range, isInputRange). + * Part of an input range as defined by + * $(REF isInputRange, std,range,primitives). * * Throws: * $(LREF CSVException) When a quote is found in an unquoted field, @@ -986,7 +988,7 @@ public: */ void popFront() { - while(!recordRange.empty) + while (!recordRange.empty) { recordRange.popFront(); } @@ -999,14 +1001,14 @@ public: if (!_input.range.empty) { - if (_input.range.front == '\r') - { - _input.range.popFront(); - if (_input.range.front == '\n') - _input.range.popFront(); - } - else if (_input.range.front == '\n') - _input.range.popFront(); + if (_input.range.front == '\r') + { + _input.range.popFront(); + if (!_input.range.empty && _input.range.front == '\n') + _input.range.popFront(); + } + else if (_input.range.front == '\n') + _input.range.popFront(); } if (_input.range.empty) @@ -1039,12 +1041,12 @@ public: T[U] aa; try { - for(; !recordRange.empty; recordRange.popFront()) + for (; !recordRange.empty; recordRange.popFront()) { aa[header[_input.col-1]] = recordRange.front; } } - catch(ConvException e) + catch (ConvException e) { throw new CSVException(e.msg, _input.row, _input.col, e); } @@ -1060,13 +1062,13 @@ public: size_t colIndex; try { - for(; !recordRange.empty;) + for (; !recordRange.empty;) { auto colData = recordRange.front; scope(exit) colIndex++; if (indices.length > 0) { - foreach(ti, ToType; FieldTypeTuple!(Contents)) + foreach (ti, ToType; Fields!(Contents)) { if (indices[ti] == colIndex) { @@ -1077,7 +1079,7 @@ public: } else { - foreach(ti, ToType; FieldTypeTuple!(Contents)) + foreach (ti, ToType; Fields!(Contents)) { if (ti == colIndex) { @@ -1089,7 +1091,7 @@ public: recordRange.popFront(); } } - catch(ConvException e) + catch (ConvException e) { throw new CSVException(e.msg, _input.row, colIndex, e); } @@ -1100,25 +1102,37 @@ public: /// @safe pure unittest { - import std.algorithm; + import std.algorithm.comparison : equal; string str = `76;^26^;22`; int[] ans = [76,26,22]; auto records = CsvReader!(int,Malformed.ignore,string,char,string[]) (str, ';', '^'); - foreach(record; records) + foreach (record; records) { assert(equal(record, ans)); } } +// Bugzilla 15545 +// @system due to the catch for Throwable +@system pure unittest +{ + import std.exception : assertNotThrown; + enum failData = + "name, surname, age + Joe, Joker, 99\r"; + auto r = csvReader(failData); + assertNotThrown((){foreach (entry; r){}}()); +} + /* * This input range is accessible through $(LREF CsvReader) when the * requested $(D Contents) type is neither a structure or an associative array. */ private struct CsvRecord(Contents, Malformed ErrorLevel, Range, Separator) - if (!is(Contents == class) && !is(Contents == struct)) +if (!is(Contents == class) && !is(Contents == struct)) { import std.array : appender; private: @@ -1151,7 +1165,8 @@ public: // to eliminate so many tokens. This calculates // how many will be skipped to get to the next header column size_t normalizer; - foreach(ref c; _popCount) { + foreach (ref c; _popCount) + { static if (ErrorLevel == Malformed.ignore) { // If we are not throwing exceptions @@ -1168,7 +1183,8 @@ public: } /** - * Part of an input range as defined by $(XREF range, isInputRange). + * Part of an input range as defined by + * $(REF isInputRange, std,range,primitives). */ @property Contents front() @safe pure { @@ -1177,7 +1193,8 @@ public: } /** - * Part of an input range as defined by $(XREF range, isInputRange). + * Part of an input range as defined by + * $(REF isInputRange, std,range,primitives). */ @property bool empty() @safe pure nothrow @nogc const { @@ -1201,7 +1218,8 @@ public: /** - * Part of an input range as defined by $(XREF range, isInputRange). + * Part of an input range as defined by + * $(REF isInputRange, std,range,primitives). * * Throws: * $(LREF CSVException) When a quote is found in an unquoted field, @@ -1215,7 +1233,7 @@ public: import std.format : format; // Skip last of record when header is depleted. if (_popCount.ptr && _popCount.empty) - while(!recordEnd()) + while (!recordEnd()) { prime(1); } @@ -1231,7 +1249,9 @@ public: "previous length of %s.", _input.row, _input.col, _input.rowLength)); return; - } else { + } + else + { static if (ErrorLevel == Malformed.throwException) if (_input.rowLength != 0) if (_input.col > _input.rowLength) @@ -1257,7 +1277,7 @@ public: */ private void prime(size_t skipNum) { - foreach(i; 0..skipNum) + foreach (i; 0 .. skipNum) { _input.col++; _front.shrinkTo(0); @@ -1267,14 +1287,14 @@ public: try csvNextToken!(Range, ErrorLevel, Separator) (_input.range, _front, _separator, _quote,false); - catch(IncompleteCellException ice) + catch (IncompleteCellException ice) { ice.row = _input.row; ice.col = _input.col; ice.partialData = _front.data.idup; throw ice; } - catch(ConvException e) + catch (ConvException e) { throw new CSVException(e.msg, _input.row, _input.col, e); } @@ -1289,7 +1309,7 @@ public: csvNextToken!(Range, ErrorLevel, Separator) (_input.range, _front, _separator, _quote,false); } - catch(IncompleteCellException ice) + catch (IncompleteCellException ice) { ice.row = _input.row; ice.col = _input.col; @@ -1301,8 +1321,9 @@ public: if (!_popCount.empty) _popCount.popFront(); - if (skipNum == size_t.max) { - while(!recordEnd()) + if (skipNum == size_t.max) + { + while (!recordEnd()) prime(1); _empty = true; return; @@ -1314,7 +1335,7 @@ public: auto data = _front.data; static if (!isSomeString!Contents) skipWS(data); try curContentsoken = to!Contents(data); - catch(ConvException e) + catch (ConvException e) { throw new CSVException(e.msg, _input.row, _input.col, e); } @@ -1346,9 +1367,9 @@ void csvNextToken(Range, Malformed ErrorLevel = Malformed.throwException, (ref Range input, ref Output ans, Separator sep, Separator quote, bool startQuoted = false) - if (isSomeChar!Separator && isInputRange!Range - && is(Unqual!(ElementType!Range) == dchar) - && isOutputRange!(Output, dchar)) +if (isSomeChar!Separator && isInputRange!Range + && is(Unqual!(ElementType!Range) == dchar) + && isOutputRange!(Output, dchar)) { bool quoted = startQuoted; bool escQuote; @@ -1366,7 +1387,7 @@ void csvNextToken(Range, Malformed ErrorLevel = Malformed.throwException, input.popFront(); } - while(!input.empty) + while (!input.empty) { assert(!(quoted && escQuote)); if (!quoted) @@ -1444,9 +1465,11 @@ void csvNextToken(Range, Malformed ErrorLevel = Malformed.throwException, } /// -unittest +@safe unittest { import std.array : appender; + import std.range.primitives : popFront; + string str = "65,63\n123,3673"; auto a = appender!(char[])(); @@ -1655,7 +1678,7 @@ unittest size_t i = 0; foreach (data; csvReader!Data(csv)) with (data) { - int[] row = [cast(int)a, cast(int)b, cast(int)c]; + int[] row = [cast(int) a, cast(int) b, cast(int) c]; if (i == 0) assert(row == [1, 2, 3]); else @@ -1669,7 +1692,7 @@ unittest auto a = data.front; data.popFront(); auto b = data.front; data.popFront(); auto c = data.front; - int[] row = [cast(int)a, cast(int)b, cast(int)c]; + int[] row = [cast(int) a, cast(int) b, cast(int) c]; if (i == 0) assert(row == [1, 2, 3]); else diff --git a/std/datetime.d b/std/datetime.d deleted file mode 100644 index 8e4ed3b28b4..00000000000 --- a/std/datetime.d +++ /dev/null @@ -1,33586 +0,0 @@ -//Written in the D programming language - -/++ - Module containing Date/Time functionality. - - This module provides: - $(UL - $(LI Types to represent points in time: $(LREF SysTime), $(LREF Date), - $(LREF TimeOfDay), and $(LREF2 .DateTime, DateTime).) - $(LI Types to represent intervals of time.) - $(LI Types to represent ranges over intervals of time.) - $(LI Types to represent time zones (used by $(LREF SysTime)).) - $(LI A platform-independent, high precision stopwatch type: - $(LREF StopWatch)) - $(LI Benchmarking functions.) - $(LI Various helper functions.) - ) - - Closely related to std.datetime is $(D core.time), - and some of the time types used in std.datetime come from there - such as - $(CXREF time, Duration), $(CXREF time, TickDuration), and - $(CXREF time, FracSec). - core.time is publically imported into std.datetime, it isn't necessary - to import it separately. - - Three of the main concepts used in this module are time points, time - durations, and time intervals. - - A time point is a specific point in time. e.g. January 5th, 2010 - or 5:00. - - A time duration is a length of time with units. e.g. 5 days or 231 seconds. - - A time interval indicates a period of time associated with a fixed point in - time. It is either two time points associated with each other, - indicating the time starting at the first point up to, but not including, - the second point - e.g. [January 5th, 2010 - March 10th, 2010$(RPAREN) - or - it is a time point and a time duration associated with one another. e.g. - January 5th, 2010 and 5 days, indicating [January 5th, 2010 - - January 10th, 2010$(RPAREN). - - Various arithmetic operations are supported between time points and - durations (e.g. the difference between two time points is a time duration), - and ranges can be gotten from time intervals, so range-based operations may - be done on a series of time points. - - The types that the typical user is most likely to be interested in are - $(LREF Date) (if they want dates but don't care about time), $(LREF DateTime) - (if they want dates and times but don't care about time zones), $(LREF SysTime) - (if they want the date and time from the OS and/or do care about time - zones), and StopWatch (a platform-independent, high precision stop watch). - $(LREF Date) and $(LREF DateTime) are optimized for calendar-based operations, - while $(LREF SysTime) is designed for dealing with time from the OS. Check out - their specific documentation for more details. - - To get the current time, use $(LREF2 .Clock.currTime, Clock.currTime). - It will return the current - time as a $(LREF SysTime). To print it, $(D toString) is - sufficient, but if using $(D toISOString), $(D toISOExtString), or - $(D toSimpleString), use the corresponding $(D fromISOString), - $(D fromISOExtString), or $(D fromSimpleString) to create a - $(LREF SysTime) from the string. - --------------------- -auto currentTime = Clock.currTime(); -auto timeString = currentTime.toISOExtString(); -auto restoredTime = SysTime.fromISOExtString(timeString); --------------------- - - Various functions take a string (or strings) to represent a unit of time - (e.g. $(D convert!("days", "hours")(numDays))). The valid strings to use - with such functions are $(D "years"), $(D "months"), $(D "weeks"), - $(D "days"), $(D "hours"), $(D "minutes"), $(D "seconds"), - $(D "msecs") (milliseconds), $(D "usecs") (microseconds), - $(D "hnsecs") (hecto-nanoseconds - i.e. 100 ns), or some subset thereof. - There are a few functions in core.time which take $(D "nsecs"), but because - nothing in std.datetime has precision greater than hnsecs, and very little - in core.time does, no functions in std.datetime accept $(D "nsecs"). - To remember which units are abbreviated and which aren't, - all units seconds and greater use their full names, and all - sub-second units are abbreviated (since they'd be rather long if they - weren't). - - Note: - $(LREF DateTimeException) is an alias for $(CXREF time, TimeException), - so you don't need to worry about core.time functions and std.datetime - functions throwing different exception types (except in the rare case - that they throw something other than $(CXREF time, TimeException) or - $(LREF DateTimeException)). - - See_Also: - Introduction to std._datetime
- $(WEB en.wikipedia.org/wiki/ISO_8601, ISO 8601)
- $(WEB en.wikipedia.org/wiki/Tz_database, - Wikipedia entry on TZ Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, - List of Time Zones)
- - Copyright: Copyright 2010 - 2011 - License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Jonathan M Davis and Kato Shoichi - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) -+/ -module std.datetime; - -public import core.time; - -import core.exception; -import core.stdc.time; - -import std.exception; -import std.range.primitives; -import std.traits; -// FIXME -import std.functional; //: unaryFun; - -version(Windows) -{ - import core.sys.windows.windows; - import core.sys.windows.winsock2; - import std.windows.registry; -} -else version(Posix) -{ - import core.sys.posix.stdlib; - import core.sys.posix.sys.time; -} - -version(unittest) -{ - import std.stdio; -} - -unittest -{ - initializeTests(); -} - -//Verify module example. -unittest -{ - auto currentTime = Clock.currTime(); - auto timeString = currentTime.toISOExtString(); - auto restoredTime = SysTime.fromISOExtString(timeString); -} - -//Verify Examples for core.time.Duration which couldn't be in core.time. -unittest -{ - assert(std.datetime.Date(2010, 9, 7) + dur!"days"(5) == - std.datetime.Date(2010, 9, 12)); - - assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) == - dur!"days"(-26)); -} - - -//============================================================================== -// Section with public enums and constants. -//============================================================================== - -/++ - Represents the 12 months of the Gregorian year (January is 1). - +/ -enum Month : ubyte { jan = 1, /// - feb, /// - mar, /// - apr, /// - may, /// - jun, /// - jul, /// - aug, /// - sep, /// - oct, /// - nov, /// - dec /// - } - -/++ - Represents the 7 days of the Gregorian week (Sunday is 0). - +/ -enum DayOfWeek : ubyte { sun = 0, /// - mon, /// - tue, /// - wed, /// - thu, /// - fri, /// - sat /// - } - -/++ - In some date calculations, adding months or years can cause the date to fall - on a day of the month which is not valid (e.g. February 29th 2001 or - June 31st 2000). If overflow is allowed (as is the default), then the month - will be incremented accordingly (so, February 29th 2001 would become - March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow - is not allowed, then the day will be adjusted to the last valid day in that - month (so, February 29th 2001 would become February 28th 2001 and - June 31st 2000 would become June 30th 2000). - - AllowDayOverflow only applies to calculations involving months or years. - +/ -enum AllowDayOverflow -{ - /// No, don't allow day overflow. - no, - - /// Yes, allow day overflow. - yes -} - -/++ - Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s - $(LREF expand, expand) function which uses it to indicate whether the interval should - be expanded backwards (into the past), forwards (into the future), or both. - +/ -enum Direction -{ - /// Backward. - bwd, - - /// Forward. - fwd, - - /// Both backward and forward. - both -} - -/++ - Used to indicate whether $(D popFront) should be called immediately upon - creating a range. The idea is that for some functions used to generate a - range for an interval, $(D front) is not necessarily a time point which - would ever be generated by the range. To get the first time point - in the range to match what the function generates, then use - $(D PopFirst.yes) to indicate that the range should have $(D popFront) - called on it before the range is returned so that $(D front) is a time point - which the function would generate. - - For instance, if the function used to generate a range of time points - generated successive Easters (i.e. you're iterating over all of the Easters - within the interval), the initial date probably isn't an Easter. Using - $(D PopFirst.yes) would tell the function which returned the - range that $(D popFront) was to be called so that front would then be - an Easter - the next one generated by the function (which when - iterating forward would be the Easter following the original $(D front), - while when iterating backward, it would be the Easter prior to the - original $(D front)). If $(D PopFirst.no) were used, then $(D front) would - remain the original time point and it would not necessarily be a time point - which would be generated by the range-generating function (which in many - cases is exactly what is desired - - e.g. if iterating over every day starting at the beginning - of the interval). - +/ -enum PopFirst -{ - /// No, don't call popFront() before returning the range. - no, - - /// Yes, call popFront() before returning the range. - yes -} - -/++ - Used by StopWatch to indicate whether it should start immediately upon - construction. - +/ -enum AutoStart -{ - /// No, don't start the StopWatch when it is constructed. - no, - - /// Yes, do start the StopWatch when it is constructed. - yes -} - -/++ - Array of the strings representing time units, starting with the smallest - unit and going to the largest. It does not include $(D "nsecs"). - - Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)), - $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"), - $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and - $(D "years") - +/ -immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", - "hours", "days", "weeks", "months", "years"]; - - -//============================================================================== -// Section with other types. -//============================================================================== - -/++ - Exception type used by std.datetime. It's an alias to $(CXREF time, TimeException). - Either can be caught without concern about which - module it came from. - +/ -alias TimeException DateTimeException; - -/++ - Effectively a namespace to make it clear that the methods it contains are - getting the time from the system clock. It cannot be instantiated. - +/ -final class Clock -{ -public: - - /++ - Returns the current time in the given time zone. - - Throws: - $(XREF exception, ErrnoException) (on Posix) or $(XREF exception, Exception) (on Windows) - if it fails to get the time of day. - +/ - static SysTime currTime(immutable TimeZone tz = LocalTime()) @safe - { - return SysTime(currStdTime, tz); - } - - unittest - { - assert(currTime(UTC()).timezone is UTC()); - - //I have no idea why, but for some reason, Windows/Wine likes to get - //time_t wrong when getting it with core.stdc.time.time. On one box - //I have (which has its local time set to UTC), it always gives time_t - //in the real local time (America/Los_Angeles), and after the most recent - //DST switch, every Windows box that I've tried it in is reporting - //time_t as being 1 hour off of where it's supposed to be. So, I really - //don't know what the deal is, but given what I'm seeing, I don't trust - //core.stdc.time.time on Windows, so I'm just going to disable this test - //on Windows. - version(Posix) - { - immutable unixTimeD = currTime().toUnixTime(); - immutable unixTimeC = core.stdc.time.time(null); - immutable diff = unixTimeC - unixTimeD; - - assert(diff >= -2); - assert(diff <= 2); - } - } - - /++ - Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the - current time. - - Throws: - $(LREF DateTimeException) if it fails to get the time. - +/ - static @property long currStdTime() @trusted - { - version(Windows) - { - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - - return FILETIMEToStdTime(&fileTime); - } - else version(Posix) - { - enum hnsecsToUnixEpoch = 621_355_968_000_000_000L; - - static if(is(typeof(clock_gettime))) - { - timespec ts; - - if(clock_gettime(CLOCK_REALTIME, &ts) != 0) - throw new TimeException("Failed in clock_gettime()."); - - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - else - { - timeval tv; - - if(gettimeofday(&tv, null) != 0) - throw new TimeException("Failed in gettimeofday()."); - - return convert!("seconds", "hnsecs")(tv.tv_sec) + - convert!("usecs", "hnsecs")(tv.tv_usec) + - hnsecsToUnixEpoch; - } - } - } - - /++ - The current system tick. The number of ticks per second varies from - system to system. currSystemTick uses a monotonic clock, so it's - intended for precision timing by comparing relative time values, not - for getting the current system time. - - Warning: - On some systems, the monotonic clock may stop counting when - the computer goes to sleep or hibernates. So, the monotonic - clock could be off if that occurs. This is known to happen - on Mac OS X. It has not been tested whether it occurs on - either Windows or Linux. - - Throws: - $(LREF DateTimeException) if it fails to get the time. - +/ - static @property TickDuration currSystemTick() @safe nothrow - { - return TickDuration.currSystemTick; - } - - unittest - { - assert(Clock.currSystemTick.length > 0); - } - - /++ - The current number of system ticks since the application started. - The number of ticks per second varies from system to system. - This uses a monotonic clock. - - Warning: - On some systems, the monotonic clock may stop counting when - the computer goes to sleep or hibernates. So, the monotonic - clock could be off if that occurs. This is known to happen - on Mac OS X. It has not been tested whether it occurs on - either Windows or on Linux. - - Throws: - $(LREF DateTimeException) if it fails to get the time. - +/ - static @property TickDuration currAppTick() @safe - { - return currSystemTick - TickDuration.appOrigin; - } - - unittest - { - auto a = Clock.currSystemTick; - auto b = Clock.currAppTick; - assert(a.length); - assert(b.length); - assert(a > b); - } - -private: - - @disable this() {} -} - -//============================================================================== -// Section with time points. -//============================================================================== - -/++ - $(D SysTime) is the type used to get the current time from the - system or doing anything that involves time zones. Unlike - $(LREF DateTime), the time zone is an integral part of $(D SysTime) (though for - local time applications, time zones can be ignored and - it will work, since it defaults to using the local time zone). It holds its - internal time in std time (hnsecs since midnight, January 1st, 1 A.D. UTC), - so it interfaces well with the system time. However, that means that, unlike - $(LREF DateTime), it is not optimized for calendar-based operations, and - getting individual units from it such as years or days is going to involve - conversions and be less efficient. - - For calendar-based operations that don't - care about time zones, then $(LREF DateTime) would be the type to - use. For system time, use $(D SysTime). - - $(LREF2 .Clock.currTime, Clock.currTime) will return the current time as a $(D SysTime). - To convert a $(D SysTime) to a $(LREF Date) or $(LREF DateTime), simply cast - it. To convert a $(LREF Date) or $(LREF DateTime) to a - $(D SysTime), use $(D SysTime)'s constructor, and pass in the - intended time zone with it (or don't pass in a $(LREF2 .TimeZone, TimeZone), and the local - time zone will be used). Be aware, however, that converting from a - $(LREF DateTime) to a $(D SysTime) will not necessarily be 100% accurate due to - DST (one hour of the year doesn't exist and another occurs twice). - To not risk any conversion errors, keep times as - $(D SysTime)s. Aside from DST though, there shouldn't be any conversion - problems. - - For using time zones other than local time or UTC, use - $(LREF PosixTimeZone) on Posix systems (or on Windows, if providing the TZ - Database files), and use $(LREF WindowsTimeZone) on Windows systems. - The time in $(D SysTime) is kept internally in hnsecs from midnight, - January 1st, 1 A.D. UTC. Conversion error cannot happen when changing - the time zone of a $(D SysTime). $(LREF LocalTime) is the $(LREF2 .TimeZone, TimeZone) class - which represents the local time, and $(D UTC) is the $(LREF2 .TimeZone, TimeZone) class - which represents UTC. $(D SysTime) uses $(LREF LocalTime) if no $(LREF2 .TimeZone, TimeZone) - is provided. For more details on time zones, see the documentation for - $(LREF2 .TimeZone, TimeZone), $(LREF PosixTimeZone), and $(LREF WindowsTimeZone). - - $(D SysTime)'s range is from approximately 29,000 B.C. to approximately - 29,000 A.D. - +/ -struct SysTime -{ - import std.typecons : Rebindable; - -public: - - /++ - Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of - time zone, tz is used as its time zone. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. - +/ - this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow - { - try - this(dateTime, Duration.zero, tz); - catch(Exception e) - assert(0, "SysTime's constructor threw when it shouldn't have."); - } - - unittest - { - import std.format : format; - static void test(DateTime dt, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(dt, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s", dt)); - } - - test(DateTime.init, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), UTC(), 0); - test(DateTime(1, 1, 1, 0, 0, 1), UTC(), 10_000_000L); - test(DateTime(0, 12, 31, 23, 59, 59), UTC(), -10_000_000L); - - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0); - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L); - } - - /++ - Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of - time zone, tz is used as its time zone. - fracSecs = The fractional seconds portion of the time. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. - - Throws: - $(LREF DateTimeException) if $(D fracSecs) is negative or if it's - greater than or equal to one second. - +/ - this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe - { - enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); - auto nonNullTZ = tz is null ? LocalTime() : tz; - - immutable dateDiff = dateTime.date - Date.init; - immutable todDiff = dateTime.timeOfDay - TimeOfDay.init; - - immutable adjustedTime = dateDiff + todDiff + fracSecs; - immutable standardTime = nonNullTZ.tzToUTC(adjustedTime.total!"hnsecs"); - - this(standardTime, nonNullTZ); - } - - unittest - { - import std.format : format; - static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(dt, fracSecs, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s, Given Duration: %s", dt, fracSecs)); - } - - test(DateTime.init, Duration.zero, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), Duration.zero, UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), Duration.zero, UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), msecs(1), UTC(), 10_000L); - test(DateTime(0, 12, 31, 23, 59, 59), msecs(999), UTC(), -10_000L); - - test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC(), -1); - test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC(), -9_999_999); - test(DateTime(0, 12, 31, 23, 59, 59), Duration.zero, UTC(), -10_000_000); - - assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC())); - assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC())); - } - - /++ - $(RED Scheduled for deprecation. Please use the overload which takes a - $(CXREF time, Duration) for the fractional seconds. This overload - will be deprecated in 2.068). - - Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of - time zone, tz is used as its time zone. - fracSec = The fractional seconds portion of the time. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. - - Throws: - $(LREF DateTimeException) if $(D fracSec) is negative. - +/ - //deprecated("Please use the overload which takes a Duration instead of a FracSec.") - this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) @safe - { - immutable fracHNSecs = fracSec.hnsecs; - enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - _timezone = tz is null ? LocalTime() : tz; - - try - { - immutable dateDiff = (dateTime.date - Date(1, 1, 1)).total!"hnsecs"; - immutable todDiff = (dateTime.timeOfDay - TimeOfDay(0, 0, 0)).total!"hnsecs"; - - immutable adjustedTime = dateDiff + todDiff + fracHNSecs; - immutable standardTime = _timezone.tzToUTC(adjustedTime); - - this(standardTime, _timezone); - } - catch(Exception e) - assert(0, "Date, TimeOfDay, or DateTime's constructor threw when it shouldn't have."); - } - - /+deprecated+/ unittest - { - import std.format : format; - - static void test(DateTime dt, - FracSec fracSec, - immutable TimeZone tz, - long expected) - { - auto sysTime = SysTime(dt, fracSec, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s, Given FracSec: %s", dt, fracSec)); - } - - test(DateTime.init, FracSec.init, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), FracSec.init, UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), FracSec.init, UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC(), 10_000L); - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC(), -10_000L); - - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC(), -1); - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC(), -9_999_999); - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0), UTC(), -10_000_000); - - assertThrown!DateTimeException(SysTime(DateTime.init, FracSec.from!"hnsecs"(-1), UTC())); - } - - /++ - Params: - date = The $(LREF Date) to use to set this $(LREF SysTime)'s internal std - time. As $(LREF Date) has no concept of time zone, tz is used as - its time zone. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF Date) is assumed - to be in the given time zone. - +/ - this(in Date date, immutable TimeZone tz = null) @safe nothrow - { - _timezone = tz is null ? LocalTime() : tz; - - try - { - immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; - immutable standardTime = _timezone.tzToUTC(adjustedTime); - - this(standardTime, _timezone); - } - catch(Exception e) - assert(0, "Date's constructor through when it shouldn't have."); - } - - unittest - { - static void test(Date d, immutable TimeZone tz, long expected) - { - import std.format : format; - auto sysTime = SysTime(d, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given Date: %s", d)); - } - - test(Date.init, UTC(), 0); - test(Date(1, 1, 1), UTC(), 0); - test(Date(1, 1, 2), UTC(), 864000000000); - test(Date(0, 12, 31), UTC(), -864000000000); - } - - /++ - Note: - Whereas the other constructors take in the given date/time, assume - that it's in the given time zone, and convert it to hnsecs in UTC - since midnight, January 1st, 1 A.D. UTC - i.e. std time - this - constructor takes a std time, which is specifically already in UTC, - so no conversion takes place. Of course, the various getter - properties and functions will use the given time zone's conversion - function to convert the results to that time zone, but no conversion - of the arguments to this constructor takes place. - - Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. - +/ - this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow - { - _stdTime = stdTime; - _timezone = tz is null ? LocalTime() : tz; - } - - unittest - { - static void test(long stdTime, immutable TimeZone tz) - { - import std.format : format; - auto sysTime = SysTime(stdTime, tz); - assert(sysTime._stdTime == stdTime); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given stdTime: %s", stdTime)); - } - - foreach(stdTime; [-1234567890L, -250, 0, 250, 1235657390L]) - { - foreach(tz; testTZs) - test(stdTime, tz); - } - } - - /++ - Params: - rhs = The $(LREF SysTime) to assign to this one. - +/ - ref SysTime opAssign(const ref SysTime rhs) return @safe pure nothrow - { - _stdTime = rhs._stdTime; - _timezone = rhs._timezone; - - return this; - } - - /++ - Params: - rhs = The $(LREF SysTime) to assign to this one. - +/ - ref SysTime opAssign(SysTime rhs) return @safe pure nothrow - { - _stdTime = rhs._stdTime; - _timezone = rhs._timezone; - - return this; - } - - /++ - Checks for equality between this $(LREF SysTime) and the given - $(LREF SysTime). - - Note that the time zone is ignored. Only the internal - std times (which are in UTC) are compared. - +/ - bool opEquals(const SysTime rhs) @safe const pure nothrow - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(const ref SysTime rhs) @safe const pure nothrow - { - return _stdTime == rhs._stdTime; - } - - unittest - { - import std.range; - assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC())); - assert(SysTime(DateTime.init, UTC()) == SysTime(0)); - assert(SysTime(Date.init, UTC()) == SysTime(0)); - assert(SysTime(0) == SysTime(0)); - - static void test(DateTime dt, - immutable TimeZone tz1, - immutable TimeZone tz2) - { - auto st1 = SysTime(dt); - st1.timezone = tz1; - - auto st2 = SysTime(dt); - st2.timezone = tz2; - - assert(st1 == st2); - } - - foreach(tz1; testTZs) - { - foreach(tz2; testTZs) - { - foreach(dt; chain(testDateTimesBC, testDateTimesAD)) - test(dt, tz1, tz2); - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - static assert(__traits(compiles, st == st)); - static assert(__traits(compiles, st == cst)); - //static assert(__traits(compiles, st == ist)); - static assert(__traits(compiles, cst == st)); - static assert(__traits(compiles, cst == cst)); - //static assert(__traits(compiles, cst == ist)); - //static assert(__traits(compiles, ist == st)); - //static assert(__traits(compiles, ist == cst)); - //static assert(__traits(compiles, ist == ist)); - } - - /++ - Compares this $(LREF SysTime) with the given $(LREF SysTime). - - Time zone is irrelevant when comparing $(LREF SysTime)s. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in SysTime rhs) @safe const pure nothrow - { - if(_stdTime < rhs._stdTime) - return -1; - if(_stdTime > rhs._stdTime) - return 1; - - return 0; - } - - unittest - { - import std.range; - assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0); - assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0); - assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0); - assert(SysTime(0).opCmp(SysTime(0)) == 0); - - static void testEqual(SysTime st, - immutable TimeZone tz1, - immutable TimeZone tz2) - { - auto st1 = st; - st1.timezone = tz1; - - auto st2 = st; - st2.timezone = tz2; - - assert(st1.opCmp(st2) == 0); - } - - auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD))); - - foreach(st; sts) - foreach(tz1; testTZs) - foreach(tz2; testTZs) - testEqual(st, tz1, tz2); - - static void testCmp(SysTime st1, - immutable TimeZone tz1, - SysTime st2, - immutable TimeZone tz2) - { - st1.timezone = tz1; - st2.timezone = tz2; - assert(st1.opCmp(st2) < 0); - assert(st2.opCmp(st1) > 0); - } - - foreach(si, st1; sts) - foreach(st2; sts[si+1 .. $]) - foreach(tz1; testTZs) - foreach(tz2; testTZs) - testCmp(st1, tz1, st2, tz2); - - auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - static assert(__traits(compiles, st.opCmp(st))); - static assert(__traits(compiles, st.opCmp(cst))); - //static assert(__traits(compiles, st.opCmp(ist))); - static assert(__traits(compiles, cst.opCmp(st))); - static assert(__traits(compiles, cst.opCmp(cst))); - //static assert(__traits(compiles, cst.opCmp(ist))); - //static assert(__traits(compiles, ist.opCmp(st))); - //static assert(__traits(compiles, ist.opCmp(cst))); - //static assert(__traits(compiles, ist.opCmp(ist))); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const nothrow - { - return (cast(Date)this).year; - } - - unittest - { - import std.range; - static void test(SysTime sysTime, long expected) - { - import std.format : format; - assert(sysTime.year == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 1); - test(SysTime(1, UTC()), 1); - test(SysTime(-1, UTC()), 0); - - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach(tz; testTZs) - { - foreach(fs; testFracSecs) - test(SysTime(dt, fs, tz), year); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.year)); - //static assert(__traits(compiles, ist.year)); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF SysTime)'s year to. - - Throws: - $(LREF DateTimeException) if the new year is not a leap year and the - resulting date would be on February 29th. - +/ - @property void year(int year) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int)days); - date.year = year; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - /// - unittest - { - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); - } - - unittest - { - import std.range; - static void test(SysTime st, int year, in SysTime expected) - { - st.year = year; - assert(st == expected); - } - - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - - foreach(year; chain(testYearsBC, testYearsAD)) - { - auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, year, e); - } - } - - foreach(fs; testFracSecs) - { - foreach(tz; testTZs) - { - foreach(tod; testTODs) - { - test(SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz), 2000, - SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz), 1999, - SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz)); - } - - foreach(tod; testTODsThrown) - { - auto st = SysTime(DateTime(Date(2000, 2, 29), tod), fs, tz); - assertThrown!DateTimeException(st.year = 1999); - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.year = 7)); - //static assert(!__traits(compiles, ist.year = 7)); - } - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(LREF DateTimeException) if $(D isAD) is true. - +/ - @property ushort yearBC() @safe const - { - return (cast(Date)this).yearBC; - } - - /// - unittest - { - assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); - assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); - assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); - } - - unittest - { - import std.format : format; - foreach(st; testSysTimesBC) - { - auto msg = format("SysTime: %s", st); - assertNotThrown!DateTimeException(st.yearBC, msg); - assert(st.yearBC == (st.year * -1) + 1, msg); - } - - foreach(st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]]) - assertThrown!DateTimeException(st.yearBC, format("SysTime: %s", st)); - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.year = 12)); - static assert(!__traits(compiles, cst.year = 12)); - //static assert(!__traits(compiles, ist.year = 12)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF SysTime)'s year to. - - Throws: - $(LREF DateTimeException) if a non-positive value is given. - +/ - @property void yearBC(int year) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int)days); - date.yearBC = year; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - unittest - { - auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); - st.yearBC = 1; - assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0))); - - st.yearBC = 10; - assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); - } - - unittest - { - import std.range; - static void test(SysTime st, int year, in SysTime expected) - { - import std.format : format; - st.yearBC = year; - assert(st == expected, format("SysTime: %s", st)); - } - - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - - foreach(year; testYearsBC) - { - auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, (year * -1) + 1, e); - } - } - - foreach(st; [testSysTimesBC[0], testSysTimesBC[$ - 1], - testSysTimesAD[0], testSysTimesAD[$ - 1]]) - { - foreach(year; testYearsBC) - assertThrown!DateTimeException(st.yearBC = year); - } - - foreach(fs; testFracSecs) - { - foreach(tz; testTZs) - { - foreach(tod; testTODs) - { - test(SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz), 2001, - SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz), 2000, - SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz)); - } - - foreach(tod; testTODsThrown) - { - auto st = SysTime(DateTime(Date(-2000, 2, 29), tod), fs, tz); - assertThrown!DateTimeException(st.year = -1999); - } - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.yearBC = 12)); - static assert(!__traits(compiles, cst.yearBC = 12)); - //static assert(!__traits(compiles, ist.yearBC = 12)); - } - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const nothrow - { - return (cast(Date)this).month; - } - - /// - unittest - { - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); - } - - unittest - { - import std.range; - static void test(SysTime sysTime, Month expected) - { - import std.format : format; - assert(sysTime.month == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), Month.jan); - test(SysTime(1, UTC()), Month.jan); - test(SysTime(-1, UTC()), Month.dec); - - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach(fs; testFracSecs) - { - foreach(tz; testTZs) - test(SysTime(dt, fs, tz), md.month); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.month)); - //static assert(__traits(compiles, ist.month)); - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF SysTime)'s month to. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month. - +/ - @property void month(Month month) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int)days); - date.month = month; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - unittest - { - import std.range; - - static void test(SysTime st, Month month, in SysTime expected) - { - st.month = cast(Month)month; - assert(st == expected); - } - - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - - foreach(md; testMonthDays) - { - if(st.day > maxDay(dt.year, md.month)) - continue; - auto e = SysTime(DateTime(dt.year, md.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, md.month, e); - } - } - - foreach(fs; testFracSecs) - { - foreach(tz; testTZs) - { - foreach(tod; testTODs) - { - foreach(year; filter!((a){return yearIsLeapYear(a);}) - (chain(testYearsBC, testYearsAD))) - { - test(SysTime(DateTime(Date(year, 1, 29), tod), fs, tz), - Month.feb, - SysTime(DateTime(Date(year, 2, 29), tod), fs, tz)); - } - - foreach(year; chain(testYearsBC, testYearsAD)) - { - test(SysTime(DateTime(Date(year, 1, 28), tod), fs, tz), - Month.feb, - SysTime(DateTime(Date(year, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(year, 7, 30), tod), fs, tz), - Month.jun, - SysTime(DateTime(Date(year, 6, 30), tod), fs, tz)); - } - } - } - } - - foreach(fs; [testFracSecs[0], testFracSecs[$-1]]) - { - foreach(tz; testTZs) - { - foreach(tod; testTODsThrown) - { - foreach(year; [testYearsBC[$-3], testYearsBC[$-2], - testYearsBC[$-2], testYearsAD[0], - testYearsAD[$-2], testYearsAD[$-1]]) - { - auto day = yearIsLeapYear(year) ? 30 : 29; - auto st1 = SysTime(DateTime(Date(year, 1, day), tod), fs, tz); - assertThrown!DateTimeException(st1.month = Month.feb); - - auto st2 = SysTime(DateTime(Date(year, 7, 31), tod), fs, tz); - assertThrown!DateTimeException(st2.month = Month.jun); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.month = 12)); - //static assert(!__traits(compiles, ist.month = 12)); - } - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const nothrow - { - return (cast(Date)this).day; - } - - /// - unittest - { - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); - } - - unittest - { - import std.range; - - static void test(SysTime sysTime, int expected) - { - import std.format : format; - assert(sysTime.day == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 1); - test(SysTime(1, UTC()), 1); - test(SysTime(-1, UTC()), 31); - - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach(tz; testTZs) - { - foreach(fs; testFracSecs) - test(SysTime(dt, fs, tz), md.day); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.day)); - //static assert(__traits(compiles, ist.day)); - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF SysTime)'s day to. - - Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. - +/ - @property void day(int day) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int)days); - date.day = day; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - unittest - { - import std.format : format; - import std.range; - - foreach(day; chain(testDays)) - { - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - - if(day > maxDay(dt.year, dt.month)) - continue; - auto expected = SysTime(DateTime(dt.year, dt.month, day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - st.day = day; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - foreach(tz; testTZs) - { - foreach(tod; testTODs) - { - foreach(fs; testFracSecs) - { - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(month; EnumMembers!Month) - { - auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); - immutable max = maxDay(year, month); - auto expected = SysTime(DateTime(Date(year, month, max), tod), fs, tz); - - st.day = max; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - } - } - } - - foreach(tz; testTZs) - { - foreach(tod; testTODsThrown) - { - foreach(fs; [testFracSecs[0], testFracSecs[$-1]]) - { - foreach(year; [testYearsBC[$-3], testYearsBC[$-2], - testYearsBC[$-2], testYearsAD[0], - testYearsAD[$-2], testYearsAD[$-1]]) - { - foreach(month; EnumMembers!Month) - { - auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); - immutable max = maxDay(year, month); - - assertThrown!DateTimeException(st.day = max + 1); - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.day = 27)); - //static assert(!__traits(compiles, ist.day = 27)); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const nothrow - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - return cast(ubyte)getUnitsFromHNSecs!"hours"(hnsecs); - } - - unittest - { - import std.range; - import std.format : format; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.hour == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 23); - - foreach(tz; testTZs) - { - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(hour; testHours) - { - foreach(minute; testMinSecs) - { - foreach(second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach(fs; testFracSecs) - test(SysTime(dt, fs, tz), hour); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.hour)); - //static assert(__traits(compiles, ist.hour)); - } - - - /++ - Hours past midnight. - - Params: - hour = The hours to set this $(LREF SysTime)'s hour to. - - Throws: - $(LREF DateTimeException) if the given hour are not a valid hour of - the day. - +/ - @property void hour(int hour) @safe - { - enforceValid!"hours"(hour); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if(negative) - hnsecs += convert!("hours", "hnsecs")(24); - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - hnsecs += convert!("hours", "hnsecs")(hour); - - if(negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - unittest - { - import std.range; - import std.format : format; - - foreach(hour; chain(testHours)) - { - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - st.hour = hour; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.hour = -1); - assertThrown!DateTimeException(st.hour = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.hour = 27)); - //static assert(!__traits(compiles, ist.hour = 27)); - } - - - /++ - Minutes past the current hour. - +/ - @property ubyte minute() @safe const nothrow - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - - return cast(ubyte)getUnitsFromHNSecs!"minutes"(hnsecs); - } - - unittest - { - import std.range; - import std.format : format; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.minute == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 59); - - foreach(tz; testTZs) - { - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(hour; testHours) - { - foreach(minute; testMinSecs) - { - foreach(second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach(fs; testFracSecs) - test(SysTime(dt, fs, tz), minute); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.minute)); - //static assert(__traits(compiles, ist.minute)); - } - - - /++ - Minutes past the current hour. - - Params: - minute = The minute to set this $(LREF SysTime)'s minute to. - - Throws: - $(LREF DateTimeException) if the given minute are not a valid minute - of an hour. - +/ - @property void minute(int minute) @safe - { - enforceValid!"minutes"(minute); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if(negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); - - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - - if(negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - unittest - { - import std.range; - import std.format : format; - - foreach(minute; testMinSecs) - { - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, minute, dt.second), - st.fracSecs, - st.timezone); - st.minute = minute; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.minute = -1); - assertThrown!DateTimeException(st.minute = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.minute = 27)); - //static assert(!__traits(compiles, ist.minute = 27)); - } - - - /++ - Seconds past the current minute. - +/ - @property ubyte second() @safe const nothrow - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); - - return cast(ubyte)getUnitsFromHNSecs!"seconds"(hnsecs); - } - - unittest - { - import std.range; - import std.format : format; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.second == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 59); - - foreach(tz; testTZs) - { - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(hour; testHours) - { - foreach(minute; testMinSecs) - { - foreach(second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach(fs; testFracSecs) - test(SysTime(dt, fs, tz), second); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.second)); - //static assert(__traits(compiles, ist.second)); - } - - - /++ - Seconds past the current minute. - - Params: - second = The second to set this $(LREF SysTime)'s second to. - - Throws: - $(LREF DateTimeException) if the given second are not a valid second - of a minute. - +/ - @property void second(int second) @safe - { - enforceValid!"seconds"(second); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if(negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); - - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - hnsecs += convert!("seconds", "hnsecs")(second); - - if(negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - unittest - { - import std.range; - import std.format : format; - - foreach(second; testMinSecs) - { - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, second), - st.fracSecs, - st.timezone); - st.second = second; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.second = -1); - assertThrown!DateTimeException(st.second = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.seconds = 27)); - //static assert(!__traits(compiles, ist.seconds = 27)); - } - - - /++ - Fractional seconds past the second (i.e. the portion of a - $(LREF SysTime) which is less than a second). - +/ - @property Duration fracSecs() @safe const nothrow - { - auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); - - if(hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - return dur!"hnsecs"(removeUnitsFromHNSecs!"seconds"(hnsecs)); - } - - /// - unittest - { - auto dt = DateTime(1982, 4, 1, 20, 59, 22); - assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); - assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202)); - assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567)); - - // SysTime and Duration both have a precision of hnsecs (100 ns), - // so nsecs are going to be truncated. - assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700)); - } - - unittest - { - import std.range; - - assert(SysTime(0, UTC()).fracSecs == Duration.zero); - assert(SysTime(1, UTC()).fracSecs == hnsecs(1)); - assert(SysTime(-1, UTC()).fracSecs == hnsecs(9_999_999)); - - foreach(tz; testTZs) - { - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(hour; testHours) - { - foreach(minute; testMinSecs) - { - foreach(second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); - foreach(fs; testFracSecs) - assert(SysTime(dt, fs, tz).fracSecs == fs); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.fracSecs)); - //static assert(__traits(compiles, ist.fracSecs)); - } - - - /++ - Fractional seconds past the second (i.e. the portion of a - $(LREF SysTime) which is less than a second). - - Params: - fracSecs = The duration to set this $(LREF SysTime)'s fractional - seconds to. - - Throws: - $(LREF DateTimeException) if the given duration is negative or if - it's greater than or equal to one second. - +/ - @property void fracSecs(Duration fracSecs) @safe - { - enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); - - auto oldHNSecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(oldHNSecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = oldHNSecs < 0; - - if(negative) - oldHNSecs += convert!("hours", "hnsecs")(24); - - immutable seconds = splitUnitsFromHNSecs!"seconds"(oldHNSecs); - immutable secondsHNSecs = convert!("seconds", "hnsecs")(seconds); - auto newHNSecs = fracSecs.total!"hnsecs" + secondsHNSecs; - - if(negative) - newHNSecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + newHNSecs; - } - - /// - unittest - { - auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); - assert(st.fracSecs == Duration.zero); - - st.fracSecs = msecs(213); - assert(st.fracSecs == msecs(213)); - - st.fracSecs = hnsecs(1234567); - assert(st.fracSecs == hnsecs(1234567)); - - // SysTime has a precision of hnsecs (100 ns), so nsecs are - // going to be truncated. - st.fracSecs = nsecs(123456789); - assert(st.fracSecs == hnsecs(1234567)); - } - - unittest - { - import std.range; - import std.format : format; - - foreach(fracSec; testFracSecs) - { - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - auto expected = SysTime(dt, fracSec, st.timezone); - st.fracSecs = fracSec; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.fracSecs = hnsecs(-1)); - assertThrown!DateTimeException(st.fracSecs = seconds(1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.fracSecs = msecs(7))); - //static assert(!__traits(compiles, ist.fracSecs = msecs(7))); - } - - - /++ - $(RED Scheduled for deprecation. Please use $(LREF fracSecs) instead of - fracSec. It uses a $(CXREF time, Duration) to represent the - fractional seconds instead of a $(CXREF time, FracSec). This - overload will be deprecated in 2.068). - - Fractional seconds past the second. - +/ - //deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). It returns a Duration instead of a FracSec, as FracSec is being deprecated.") - @property FracSec fracSec() @safe const nothrow - { - try - { - auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); - - if(hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); - - return FracSec.from!"hnsecs"(cast(int)hnsecs); - } - catch(Exception e) - assert(0, "FracSec.from!\"hnsecs\"() threw."); - } - - /+deprecated+/ unittest - { - import std.range; - import std.format : format; - - static void test(SysTime sysTime, FracSec expected, size_t line = __LINE__) - { - if(sysTime.fracSec != expected) - throw new AssertError(format("Value given: %s", sysTime.fracSec), __FILE__, line); - } - - test(SysTime(0, UTC()), FracSec.from!"hnsecs"(0)); - test(SysTime(1, UTC()), FracSec.from!"hnsecs"(1)); - test(SysTime(-1, UTC()), FracSec.from!"hnsecs"(9_999_999)); - - foreach(tz; testTZs) - { - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(hour; testHours) - { - foreach(minute; testMinSecs) - { - foreach(second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach(fs; testFracSecs) - test(SysTime(dt, fs, tz), FracSec.from!"hnsecs"(fs.total!"hnsecs")); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.fracSec)); - //static assert(__traits(compiles, ist.fracSec)); - } - - - /++ - $(RED Scheduled for deprecation. Please use $(LREF fracSecs) instead of - fracSec. It uses a $(CXREF time, Duration) to represent the - fractional seconds instead of a $(CXREF time, FracSec). This - overload will be deprecated in 2.068). - - Fractional seconds past the second. - - Params: - fracSec = The fractional seconds to set this $(LREF SysTime)'s - fractional seconds to. - - Throws: - $(LREF DateTimeException) if $(D fracSec) is negative. - +/ - //deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). It takes a Duration instead of a FracSec, as FracSec is being deprecated.") - @property void fracSec(FracSec fracSec) @safe - { - immutable fracHNSecs = fracSec.hnsecs; - enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if(negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - hnsecs = fracHNSecs; - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - hnsecs += convert!("seconds", "hnsecs")(second); - - if(negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - /+deprecated+/ unittest - { - import std.range; - import std.format : format; - - foreach(fracSec; testFracSecs) - { - foreach(st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime)st; - auto expected = SysTime(dt, fracSec, st.timezone); - st.fracSec = FracSec.from!"hnsecs"(fracSec.total!"hnsecs"); - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.fracSec = FracSec.from!"hnsecs"(-1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.fracSec = FracSec.from!"msecs"(7))); - //static assert(!__traits(compiles, ist.fracSec = FracSec.from!"msecs"(7))); - } - - - /++ - The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the - internal representation of $(LREF SysTime). - +/ - @property long stdTime() @safe const pure nothrow - { - return _stdTime; - } - - unittest - { - assert(SysTime(0).stdTime == 0); - assert(SysTime(1).stdTime == 1); - assert(SysTime(-1).stdTime == -1); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()).stdTime == 330000502L); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621355968000000000L); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.stdTime)); - //static assert(__traits(compiles, ist.stdTime)); - } - - - /++ - The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the - internal representation of $(LREF SysTime). - - Params: - stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. - +/ - @property void stdTime(long stdTime) @safe pure nothrow - { - _stdTime = stdTime; - } - - unittest - { - static void test(long stdTime, in SysTime expected, size_t line = __LINE__) - { - auto st = SysTime(0, UTC()); - st.stdTime = stdTime; - assert(st == expected); - } - - test(0, SysTime(Date(1, 1, 1), UTC())); - test(1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC())); - test(-1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC())); - test(330_000_502L, SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC())); - test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC())); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.stdTime = 27)); - //static assert(!__traits(compiles, ist.stdTime = 27)); - } - - - /++ - The current time zone of this $(LREF SysTime). Its internal time is always - kept in UTC, so there are no conversion issues between time zones due to - DST. Functions which return all or part of the time - such as hours - - adjust the time to this $(LREF SysTime)'s time zone before returning. - +/ - @property immutable(TimeZone) timezone() @safe const pure nothrow - { - return _timezone; - } - - - /++ - The current time zone of this $(LREF SysTime). It's internal time is always - kept in UTC, so there are no conversion issues between time zones due to - DST. Functions which return all or part of the time - such as hours - - adjust the time to this $(LREF SysTime)'s time zone before returning. - - Params: - timezone = The $(LREF2 .TimeZone, TimeZone) to set this $(LREF SysTime)'s time zone to. - +/ - @property void timezone(immutable TimeZone timezone) @safe pure nothrow - { - if(timezone is null) - _timezone = LocalTime(); - else - _timezone = timezone; - } - - - /++ - Returns whether DST is in effect for this $(LREF SysTime). - +/ - @property bool dstInEffect() @safe const nothrow - { - return _timezone.dstInEffect(_stdTime); - //This function's unit testing is done in the time zone classes. - } - - - /++ - Returns what the offset from UTC is for this $(LREF SysTime). - It includes the DST offset in effect at that time (if any). - +/ - @property Duration utcOffset() @safe const nothrow - { - return _timezone.utcOffsetAt(_stdTime); - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - $(LREF LocalTime) as its time zone. - +/ - SysTime toLocalTime() @safe const pure nothrow - { - return SysTime(_stdTime, LocalTime()); - } - - unittest - { - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toLocalTime()); - assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone is sysTime.timezone); - assert(sysTime.toLocalTime().timezone !is UTC()); - } - - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27), stz); - assert(sysTime == sysTime.toLocalTime()); - assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone !is UTC()); - assert(sysTime.toLocalTime().timezone !is stz); - } - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - $(D UTC) as its time zone. - +/ - SysTime toUTC() @safe const pure nothrow - { - return SysTime(_stdTime, UTC()); - } - - unittest - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toUTC()); - assert(sysTime._stdTime == sysTime.toUTC()._stdTime); - assert(sysTime.toUTC().timezone is UTC()); - assert(sysTime.toUTC().timezone !is LocalTime()); - assert(sysTime.toUTC().timezone !is sysTime.timezone); - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - given time zone as its time zone. - +/ - SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow - { - if(tz is null) - return SysTime(_stdTime, LocalTime()); - else - return SysTime(_stdTime, tz); - } - - unittest - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toOtherTZ(stz)); - assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime); - assert(sysTime.toOtherTZ(stz).timezone is stz); - assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); - assert(sysTime.toOtherTZ(stz).timezone !is UTC()); - } - - - /++ - Returns a $(D time_t) which represents the same time as this - $(LREF SysTime). - - Note that like all conversions in std.datetime, this is a truncating - conversion. - - If $(D time_t) is 32 bits, rather than 64, and the result can't fit in a - 32-bit value, then the closest value that can be held in 32 bits will be - used (so $(D time_t.max) if it goes over and $(D time_t.min) if it goes - under). - +/ - time_t toUnixTime() @safe const pure nothrow - { - return stdTimeToUnixTime(_stdTime); - } - - unittest - { - assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), usecs(1), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), msecs(1), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1); - } - - - /++ - Returns a $(D timeval) which represents this $(LREF SysTime). - - Note that like all conversions in std.datetime, this is a truncating - conversion. - - If $(D time_t) is 32 bits, rather than 64, and the result can't fit in a - 32-bit value, then the closest value that can be held in 32 bits will be - used for $(D tv_sec). (so $(D time_t.max) if it goes over and - $(D time_t.min) if it goes under). - +/ - timeval toTimeVal() @safe const pure nothrow - { - immutable tv_sec = toUnixTime(); - - immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621355968000000000L); - immutable tv_usec = cast(int)convert!("hnsecs", "usecs")(fracHNSecs); - - return timeval(tv_sec, tv_usec); - } - - unittest - { - assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1)); - assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeVal() == timeval(0, 7)); - - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeVal() == timeval(1, 1)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeVal() == timeval(1, 7)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeVal() == timeval(0, -1)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeVal() == timeval(0, -1)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeVal() == timeval(0, -999_001)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983)); - } - - - /++ - Returns a $(D tm) which represents this $(LREF SysTime). - +/ - tm toTM() @safe const nothrow - { - auto dateTime = cast(DateTime)this; - tm timeInfo; - - timeInfo.tm_sec = dateTime.second; - timeInfo.tm_min = dateTime.minute; - timeInfo.tm_hour = dateTime.hour; - timeInfo.tm_mday = dateTime.day; - timeInfo.tm_mon = dateTime.month - 1; - timeInfo.tm_year = dateTime.year - 1900; - timeInfo.tm_wday = dateTime.dayOfWeek; - timeInfo.tm_yday = dateTime.dayOfYear - 1; - timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); - - version(Posix) - { - import std.utf : toUTFz; - timeInfo.tm_gmtoff = cast(int)convert!("hnsecs", "seconds")(adjTime - _stdTime); - auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName); - timeInfo.tm_zone = zone.toUTFz!(char*)(); - } - - return timeInfo; - } - - unittest - { - import std.conv : to; - version(Posix) - { - scope(exit) clearTZEnvVar(); - setTZEnvVar("America/Los_Angeles"); - } - - { - auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); - - assert(timeInfo.tm_sec == 0); - assert(timeInfo.tm_min == 0); - assert(timeInfo.tm_hour == 0); - assert(timeInfo.tm_mday == 1); - assert(timeInfo.tm_mon == 0); - assert(timeInfo.tm_year == 70); - assert(timeInfo.tm_wday == 4); - assert(timeInfo.tm_yday == 0); - - version(Posix) - assert(timeInfo.tm_isdst == 0); - else version(Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version(Posix) - { - assert(timeInfo.tm_gmtoff == -8 * 60 * 60); - assert(to!string(timeInfo.tm_zone) == "PST"); - } - } - - { - auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), hnsecs(15)).toTM(); - - assert(timeInfo.tm_sec == 7); - assert(timeInfo.tm_min == 15); - assert(timeInfo.tm_hour == 12); - assert(timeInfo.tm_mday == 4); - assert(timeInfo.tm_mon == 6); - assert(timeInfo.tm_year == 110); - assert(timeInfo.tm_wday == 0); - assert(timeInfo.tm_yday == 184); - - version(Posix) - assert(timeInfo.tm_isdst == 1); - else version(Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version(Posix) - { - assert(timeInfo.tm_gmtoff == -7 * 60 * 60); - assert(to!string(timeInfo.tm_zone) == "PDT"); - } - } - } - - - /++ - Adds the given number of years or months to this $(LREF SysTime). A - negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF SysTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow - if(units == "years" || - units == "months") - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int)days); - date.add!units(value, allowOverflow); - days = date.dayOfGregorianCal - 1; - - if(days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - - adjTime = newDaysHNSecs + hnsecs; - - return this; - } - - unittest - { - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st1.add!"months"(11); - assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); - - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st2.add!"months"(-11); - assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); - - auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st3.add!"years"(1); - assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - - auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st4.add!"years"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } - - //Test add!"years"() with AllowDayOverlow.yes - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7); - assert(sysTime == SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9); - assert(sysTime == SysTime(Date(1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(Date(1999, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(7); - assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(-9); - assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); - } - - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), usecs(1207))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7); - assert(sysTime == SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9); - assert(sysTime == SysTime(Date(-1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(Date(-1999, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(-7); - assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(9); - assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); - } - - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), hnsecs(3))); - } - - //Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8); - assert(sysTime == SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8); - assert(sysTime == SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(1, 3, 1))); - } - - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-1, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5).add!"years"(7); - assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), msecs(555))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"years"(4))); - //static assert(!__traits(compiles, ist.add!"years"(4))); - } - - //Test add!"years"() with AllowDayOverlow.no - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(7, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(-9, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); - } - - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(-7, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(9, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); - } - - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3))); - } - - //Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 2, 28))); - } - - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), msecs(555))); - } - } - - //Test add!"months"() with AllowDayOverlow.yes - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6); - assert(sysTime == SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27); - assert(sysTime == SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12); - assert(sysTime == SysTime(Date(2001, 3, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(1999, 10, 1))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(1999, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(1998, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(2000, 3, 2))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(1999, 1, 2))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(2001, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(2000, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6); - assert(sysTime == SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27); - assert(sysTime == SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(-1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12); - assert(sysTime == SysTime(Date(-2001, 3, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(-1997, 10, 1))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(-1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-1995, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-1996, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-2000, 3, 2))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-2001, 1, 2))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-1999, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-2000, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48); - assert(sysTime == SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49); - assert(sysTime == SysTime(Date(0, 3, 2))); - sysTime.add!"months"(49); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(Date(-3, 3, 3))); - sysTime.add!"months"(85); - assert(sysTime == SysTime(Date(4, 4, 3))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), msecs(9))); - sysTime.add!"months"(85); - assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85); - assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), msecs(9))); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85).add!"months"(-83); - assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"months"(4))); - //static assert(!__traits(compiles, ist.add!"months"(4))); - } - - //Test add!"months"() with AllowDayOverlow.no - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2001, 2, 28))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 9, 30))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 29))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 12, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2001, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 9, 30))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1995, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2002, 12, 29))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(0, 2, 29))); - sysTime.add!"months"(49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-3, 2, 28))); - sysTime.add!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 28))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.add!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), msecs(9))); - sysTime.add!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), msecs(9))); - sysTime.add!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); - } - } - - - /++ - Adds the given number of years or months to this $(LREF SysTime). A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF SysTime) 12 months - gets the exact same $(LREF SysTime). However, the days can still be affected - due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF SysTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow - if(units == "years") - { - return add!"years"(value, allowOverflow); - } - - /// - unittest - { - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st1.roll!"months"(1); - assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); - - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st2.roll!"months"(-1); - assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); - - auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st3.roll!"months"(1); - assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); - - auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st4.roll!"months"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); - - auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st5.roll!"years"(1); - assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - - auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st6.roll!"years"(1, AllowDayOverflow.no); - assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.roll!"years"(4))); - static assert(!__traits(compiles, cst.roll!"years"(4))); - //static assert(!__traits(compiles, ist.roll!"years"(4))); - } - - - //Shares documentation with "years" overload. - ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow - if(units == "months") - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int)days); - date.roll!"months"(value, allowOverflow); - days = date.dayOfGregorianCal - 1; - - if(days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - //Test roll!"months"() with AllowDayOverlow.yes - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6); - assert(sysTime == SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(1998, 10, 1))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1997, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1997, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1998, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1998, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1999, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1999, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6); - assert(sysTime == SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27); - assert(sysTime == SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(-1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(-1998, 10, 1))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(-1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-1997, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-1997, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-2002, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-2002, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-2001, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-2001, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), hnsecs(5007)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), hnsecs(5007))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), hnsecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(-2002, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(-2002, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(-2001, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(-2001, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48); - assert(sysTime == SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49); - assert(sysTime == SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(49); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48); - assert(sysTime == SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48); - assert(sysTime == SysTime(Date(-4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49); - assert(sysTime == SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(49); - assert(sysTime == SysTime(Date(-4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(Date(-4, 4, 2))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(DateTime(4, 3, 2, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(DateTime(4, 4, 2, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(DateTime(-3, 5, 1, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85).roll!"months"(-83); - assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"months"(4))); - //static assert(!__traits(compiles, ist.roll!"months"(4))); - } - - //Test roll!"months"() with AllowDayOverlow.no - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 9, 30))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1998, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1999, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1998, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1998, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 9, 30))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2002, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2002, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-2001, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2002, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2002, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2001, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(49, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(Date(-4, 3, 29))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 2, 29, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(4, 3, 29, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 4, 30, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); - assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); - } - } - - - /++ - Adds the given number of units to this $(LREF SysTime). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF SysTime) one - year's worth of days gets the exact same $(LREF SysTime). - - Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), - $(D "minutes"), $(D "seconds"), $(D "msecs"), $(D "usecs"), and - $(D "hnsecs"). - - Note that when rolling msecs, usecs or hnsecs, they all add up to a - second. So, for example, rolling 1000 msecs is exactly the same as - rolling 100,000 usecs. - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this $(LREF SysTime). - +/ - ref SysTime roll(string units)(long value) @safe nothrow - if(units == "days") - { - auto hnsecs = adjTime; - auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --gdays; - } - - auto date = Date(cast(int)gdays); - date.roll!"days"(value); - gdays = date.dayOfGregorianCal - 1; - - if(gdays < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++gdays; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - /// - unittest - { - auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); - st1.roll!"days"(1); - assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); - st1.roll!"days"(365); - assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); - st1.roll!"days"(-32); - assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); - - auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); - st2.roll!"hours"(1); - assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); - - auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); - st3.roll!"hours"(-1); - assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); - - auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st4.roll!"minutes"(1); - assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); - - auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st5.roll!"minutes"(-1); - assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); - - auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st6.roll!"seconds"(1); - assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); - - auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st7.roll!"seconds"(-1); - assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); - - auto dt = DateTime(2010, 1, 1, 0, 0, 0); - auto st8 = SysTime(dt); - st8.roll!"msecs"(1); - assert(st8 == SysTime(dt, msecs(1))); - - auto st9 = SysTime(dt); - st9.roll!"msecs"(-1); - assert(st9 == SysTime(dt, msecs(999))); - - auto st10 = SysTime(dt); - st10.roll!"hnsecs"(1); - assert(st10 == SysTime(dt, hnsecs(1))); - - auto st11 = SysTime(dt); - st11.roll!"hnsecs"(-1); - assert(st11 == SysTime(dt, hnsecs(9_999_999))); - } - - unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(2000, 2, 29))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(2000, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 6, 30)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 6, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(1999, 1, 1)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 1, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(Date(1999, 7, 15))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(Date(1999, 7, 4))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(Date(1999, 7, 3))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(Date(1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1999, 7, 30))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1999, 7, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(1999, 7, 31))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1999, 7, 17))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1999, 2, 7))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1999, 2, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(1999, 2, 8))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1999, 2, 10))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(1999, 2, 6))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1999, 2, 1, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(DateTime(1999, 7, 15, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(DateTime(1999, 7, 4, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(DateTime(1999, 7, 3, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(DateTime(1999, 7, 31, 7, 9, 2), usecs(234578))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-2000, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 6, 30)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 6, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(-1999, 1, 1)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 1, 31))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(Date(-1999, 7, 15))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(Date(-1999, 7, 4))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(Date(-1999, 7, 3))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(-1999, 7, 30))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(-1999, 7, 17))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(-1999, 2, 1, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(DateTime(-1999, 7, 15, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(DateTime(-1999, 7, 4, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(DateTime(-1999, 7, 3, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-3); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 7, 6)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1, 7, 13))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(Date(1, 7, 19))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1, 7, 5))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 31, 0, 0, 0))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(DateTime(1, 7, 13, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(DateTime(1, 7, 19, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(DateTime(1, 7, 5, 13, 13, 9), msecs(22))); - } - - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(DateTime(0, 7, 13, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(DateTime(0, 7, 19, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(DateTime(0, 7, 5, 13, 13, 9), msecs(22))); - } - - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); - assert(sysTime == SysTime(DateTime(0, 7, 8, 13, 13, 9), msecs(22))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"days"(4))); - //static assert(!__traits(compiles, ist.roll!"days"(4))); - } - - - //Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow - if(units == "hours" || - units == "minutes" || - units == "seconds") - { - try - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); - dateTime.roll!units(value); - --days; - - hnsecs += convert!("hours", "hnsecs")(dateTime.hour); - hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); - hnsecs += convert!("seconds", "hnsecs")(dateTime.second); - - if(days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - catch(Exception e) - assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); - } - - //Test roll!"hours"(). - unittest - { - static void testST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"hours"(hours); - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - immutable d = msecs(45); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); - testST(beforeAD, 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); - testST(beforeAD, 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); - testST(beforeAD, 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - testST(beforeAD, 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - testST(beforeAD, 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(beforeAD, 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(beforeAD, 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(beforeAD, 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); - testST(beforeAD, 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - testST(beforeAD, 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); - testST(beforeAD, 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); - testST(beforeAD, 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); - testST(beforeAD, 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); - testST(beforeAD, 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); - testST(beforeAD, 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); - testST(beforeAD, -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); - testST(beforeAD, -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); - testST(beforeAD, -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - testST(beforeAD, -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); - testST(beforeAD, -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(beforeAD, -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(beforeAD, -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(beforeAD, -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); - testST(beforeAD, -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - testST(beforeAD, -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); - testST(beforeAD, -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); - testST(beforeAD, -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); - testST(beforeAD, -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); - testST(beforeAD, -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); - testST(beforeAD, -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), d)); - - testST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), d)); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); - testST(beforeBC, 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); - testST(beforeBC, 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); - testST(beforeBC, 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - testST(beforeBC, 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - testST(beforeBC, 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(beforeBC, 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(beforeBC, 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(beforeBC, 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); - testST(beforeBC, 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - testST(beforeBC, 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); - testST(beforeBC, 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); - testST(beforeBC, 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); - testST(beforeBC, 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); - testST(beforeBC, 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); - testST(beforeBC, 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); - testST(beforeBC, -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); - testST(beforeBC, -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); - testST(beforeBC, -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - testST(beforeBC, -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); - testST(beforeBC, -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(beforeBC, -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(beforeBC, -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(beforeBC, -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); - testST(beforeBC, -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - testST(beforeBC, -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); - testST(beforeBC, -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); - testST(beforeBC, -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); - testST(beforeBC, -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); - testST(beforeBC, -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); - testST(beforeBC, -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), d)); - - //Test Both - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 0, 0))); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0)); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(1).roll!"hours"(-67); - assert(sysTime == SysTime(DateTime(0, 12, 31, 5, 59, 59), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hours"(4))); - //static assert(!__traits(compiles, ist.roll!"hours"(4))); - } - - //Test roll!"minutes"(). - unittest - { - static void testST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"minutes"(minutes); - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - immutable d = usecs(7203); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), d)); - - testST(beforeAD, 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), d)); - - testST(beforeAD, -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), d)); - - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), d)); - - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), d)); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), d)); - - testST(beforeBC, 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), d)); - - testST(beforeBC, -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), d)); - - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), d)); - - //Test Both - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); - testST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); - - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); - testST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 52, 33), d), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 0))); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0)); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 59), hnsecs(9_999_999))); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(1).roll!"minutes"(-79); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 41, 59), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"minutes"(4))); - //static assert(!__traits(compiles, ist.roll!"minutes"(4))); - } - - //Test roll!"seconds"(). - unittest - { - static void testST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"seconds"(seconds); - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - immutable d = msecs(274); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), d)); - testST(beforeAD, 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), d)); - testST(beforeAD, 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - - testST(beforeAD, 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); - testST(beforeAD, 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), d)); - testST(beforeAD, -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), d)); - testST(beforeAD, -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), d)); - - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), d)); - - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), d)); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), d)); - testST(beforeBC, 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), d)); - testST(beforeBC, 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - - testST(beforeBC, 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); - testST(beforeBC, 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), d)); - testST(beforeBC, -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), d)); - testST(beforeBC, -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), d)); - - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), d)); - - //Test Both - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), d)); - testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), d)); - - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), d)); - testST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 50), d), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59))); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999)); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59)); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), hnsecs(9_999_999))); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"seconds"(1).roll!"seconds"(-102); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 18), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"seconds"(4))); - //static assert(!__traits(compiles, ist.roll!"seconds"(4))); - } - - - //Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow - if(units == "msecs" || - units == "usecs" || - units == "hnsecs") - { - auto hnsecs = adjTime; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable negative = hnsecs < 0; - - if(negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - hnsecs += convert!(units, "hnsecs")(value); - hnsecs %= convert!("seconds", "hnsecs")(1); - - if(hnsecs < 0) - hnsecs += convert!("seconds", "hnsecs")(1); - hnsecs += convert!("seconds", "hnsecs")(seconds); - - if(negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - - //Test roll!"msecs"(). - unittest - { - static void testST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"msecs"(milliseconds); - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(1))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(1))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(999))); - testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(998))); - testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(445))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_989_999))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); - testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); - testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(5_549_999))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"msecs"(1202).roll!"msecs"(-703); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(4_989_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.addMSecs(4))); - //static assert(!__traits(compiles, ist.addMSecs(4))); - } - - //Test roll!"usecs"(). - unittest - { - static void testST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"usecs"(microseconds); - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(666_667))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_989))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9))); - testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19))); - testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); - testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); - testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(25_549))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(3_333_329))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"usecs"(9_020_027); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(200_269))); - } - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"usecs"(9_020_027).roll!"usecs"(-70_034); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_499_929))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"usecs"(4))); - //static assert(!__traits(compiles, ist.roll!"usecs"(4))); - } - - //Test roll!"hnsecs"(). - unittest - { - static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"hnsecs"(hnsecs); - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - auto dtAD = DateTime(1999, 7, 6, 12, 30, 33); - auto beforeAD = SysTime(dtAD, hnsecs(274)); - testST(beforeAD, 0, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 1, SysTime(dtAD, hnsecs(275))); - testST(beforeAD, 2, SysTime(dtAD, hnsecs(276))); - testST(beforeAD, 10, SysTime(dtAD, hnsecs(284))); - testST(beforeAD, 100, SysTime(dtAD, hnsecs(374))); - testST(beforeAD, 725, SysTime(dtAD, hnsecs(999))); - testST(beforeAD, 726, SysTime(dtAD, hnsecs(1000))); - testST(beforeAD, 1000, SysTime(dtAD, hnsecs(1274))); - testST(beforeAD, 1001, SysTime(dtAD, hnsecs(1275))); - testST(beforeAD, 2000, SysTime(dtAD, hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(dtAD, hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(dtAD, hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(dtAD, hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(dtAD, hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(dtAD, hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(dtAD, hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(dtAD, hnsecs(274))); - - testST(beforeAD, -1, SysTime(dtAD, hnsecs(273))); - testST(beforeAD, -2, SysTime(dtAD, hnsecs(272))); - testST(beforeAD, -10, SysTime(dtAD, hnsecs(264))); - testST(beforeAD, -100, SysTime(dtAD, hnsecs(174))); - testST(beforeAD, -274, SysTime(dtAD)); - testST(beforeAD, -275, SysTime(dtAD, hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(dtAD, hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(dtAD, hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(dtAD, hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(dtAD, hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(dtAD, hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(dtAD, hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(dtAD, hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(dtAD, hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(dtAD, hnsecs(274))); - - //Test B.C. - auto dtBC = DateTime(-1999, 7, 6, 12, 30, 33); - auto beforeBC = SysTime(dtBC, hnsecs(274)); - testST(beforeBC, 0, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 1, SysTime(dtBC, hnsecs(275))); - testST(beforeBC, 2, SysTime(dtBC, hnsecs(276))); - testST(beforeBC, 10, SysTime(dtBC, hnsecs(284))); - testST(beforeBC, 100, SysTime(dtBC, hnsecs(374))); - testST(beforeBC, 725, SysTime(dtBC, hnsecs(999))); - testST(beforeBC, 726, SysTime(dtBC, hnsecs(1000))); - testST(beforeBC, 1000, SysTime(dtBC, hnsecs(1274))); - testST(beforeBC, 1001, SysTime(dtBC, hnsecs(1275))); - testST(beforeBC, 2000, SysTime(dtBC, hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(dtBC, hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(dtBC, hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(dtBC, hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(dtBC, hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(dtBC, hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(dtBC, hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(dtBC, hnsecs(274))); - - testST(beforeBC, -1, SysTime(dtBC, hnsecs(273))); - testST(beforeBC, -2, SysTime(dtBC, hnsecs(272))); - testST(beforeBC, -10, SysTime(dtBC, hnsecs(264))); - testST(beforeBC, -100, SysTime(dtBC, hnsecs(174))); - testST(beforeBC, -274, SysTime(dtBC)); - testST(beforeBC, -275, SysTime(dtBC, hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(dtBC, hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(dtBC, hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(dtBC, hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(dtBC, hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(dtBC, hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(dtBC, hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(dtBC, hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(dtBC, hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(dtBC, hnsecs(274))); - - //Test Both - auto dtBoth1 = DateTime(1, 1, 1, 0, 0, 0); - auto beforeBoth1 = SysTime(dtBoth1); - testST(beforeBoth1, 1, SysTime(dtBoth1, hnsecs(1))); - testST(beforeBoth1, 0, SysTime(dtBoth1)); - testST(beforeBoth1, -1, SysTime(dtBoth1, hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(dtBoth1, hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(dtBoth1, hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(dtBoth1, hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(dtBoth1, hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(dtBoth1, hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(dtBoth1, hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(dtBoth1, hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(dtBoth1)); - testST(beforeBoth1, -20_000_000, SysTime(dtBoth1)); - testST(beforeBoth1, -20_888_888, SysTime(dtBoth1, hnsecs(9_111_112))); - - auto dtBoth2 = DateTime(0, 12, 31, 23, 59, 59); - auto beforeBoth2 = SysTime(dtBoth2, hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(dtBoth2, hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(dtBoth2)); - testST(beforeBoth2, 2, SysTime(dtBoth2, hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(dtBoth2, hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(dtBoth2, hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(dtBoth2, hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(dtBoth2, hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(dtBoth2, hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(dtBoth2, hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(dtBoth2, hnsecs(888_887))); - - { - auto st = SysTime(dtBoth2, hnsecs(9_999_999)); - st.roll!"hnsecs"(70_777_222).roll!"hnsecs"(-222_555_292); - assert(st == SysTime(dtBoth2, hnsecs(8_221_929))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); - //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF SysTime). - - The legal types of arithmetic for $(LREF SysTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD +) $(TD duration) $(TD -->) $(TD SysTime)) - $(TR $(TD SysTime) $(TD -) $(TD duration) $(TD -->) $(TD SysTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF SysTime). - +/ - SysTime opBinary(string op, D)(in D duration) @safe const pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - SysTime retval = SysTime(this._stdTime, this._timezone); - - static if(is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if(is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format("retval._stdTime %s= hnsecs;", op)); - return retval; - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - - assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); - assert(st + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); - assert(st + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); - assert(st + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); - assert(st + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); - assert(st + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); - assert(st + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - assert(st + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); - assert(st + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(st + TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st + TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - } - - assert(st - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); - assert(st - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); - assert(st - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); - assert(st - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); - assert(st - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); - assert(st - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); - assert(st - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); - assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(st - TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st - TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - } - - static void testST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - auto result = orig + dur!"hnsecs"(hnsecs); - if(result != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", result, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); - testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst + duration)); - //static assert(__traits(compiles, ist + duration)); - static assert(__traits(compiles, cst - duration)); - //static assert(__traits(compiles, ist - duration)); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF SysTime), as well as assigning the result to this $(LREF SysTime). - - The legal types of arithmetic for $(LREF SysTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD +) $(TD duration) $(TD -->) $(TD SysTime)) - $(TR $(TD SysTime) $(TD -) $(TD duration) $(TD -->) $(TD SysTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF SysTime). - +/ - ref SysTime opOpAssign(string op, D)(in D duration) @safe pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - static if(is(Unqual!D == Duration)) - auto hnsecs = duration.total!"hnsecs"; - else static if(is(Unqual!D == TickDuration)) - auto hnsecs = duration.hnsecs; - - mixin(format("_stdTime %s= hnsecs;", op)); - return this; - } - - unittest - { - auto before = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(before + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - assert(before + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - assert(before + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - assert(before + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - assert(before + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - assert(before + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - assert(before + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - assert(before + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - assert(before + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - assert(before + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - assert(before + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); - assert(before + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); - assert(before + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); - assert(before + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); - assert(before + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); - assert(before + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - - assert(before - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - assert(before - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - assert(before - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - assert(before - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - assert(before - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - assert(before - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - assert(before - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - assert(before - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - assert(before - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - assert(before - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - assert(before - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); - assert(before - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); - assert(before - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); - assert(before - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); - assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); - assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - - static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - - auto r = orig += dur!"hnsecs"(hnsecs); - if(orig != expected) - throw new AssertError(format("Failed 1. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - if(r != expected) - throw new AssertError(format("Failed 2. actual [%s] != expected [%s]", r, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); - testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - (st += dur!"hnsecs"(52)) += dur!"seconds"(-907); - assert(st == SysTime(DateTime(0, 12, 31, 23, 44, 53), hnsecs(51))); - } - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst += duration)); - //static assert(!__traits(compiles, ist += duration)); - static assert(!__traits(compiles, cst -= duration)); - //static assert(!__traits(compiles, ist -= duration)); - } - - - /++ - Gives the difference between two $(LREF SysTime)s. - - The legal types of arithmetic for $(LREF SysTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow - if(op == "-") - { - return dur!"hnsecs"(_stdTime - rhs._stdTime); - } - - unittest - { - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)) == - dur!"seconds"(86_400)); - assert(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)) == - dur!"seconds"(3600)); - assert(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(60)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)) == - dur!"seconds"(-60)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(1)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)) == - dur!"seconds"(-1)); - - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - assert(SysTime(dt, msecs(532)) - SysTime(dt) == msecs(532)); - assert(SysTime(dt) - SysTime(dt, msecs(532)) == msecs(-532)); - - assert(SysTime(dt, usecs(333_347)) - SysTime(dt) == usecs(333_347)); - assert(SysTime(dt) - SysTime(dt, usecs(333_347)) == usecs(-333_347)); - - assert(SysTime(dt, hnsecs(1_234_567)) - SysTime(dt) == hnsecs(1_234_567)); - assert(SysTime(dt) - SysTime(dt, hnsecs(1_234_567)) == hnsecs(-1_234_567)); - } - - assert(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(45033)); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)) == dur!"seconds"(-45033)); - assert(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(-41367)); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)) == dur!"seconds"(41367)); - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) == - dur!"hnsecs"(1)); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == - dur!"hnsecs"(-1)); - - auto tz = TimeZone.getTimeZone("America/Los_Angeles"); - - { - auto dt = DateTime(2011, 1, 13, 8, 17, 2); - auto d = msecs(296); - assert(SysTime(dt, d, tz) - SysTime(dt, d, tz) == Duration.zero); - assert(SysTime(dt, d, tz) - SysTime(dt, d, UTC()) == hours(8)); - assert(SysTime(dt, d, UTC()) - SysTime(dt, d, tz) == hours(-8)); - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st - st)); - static assert(__traits(compiles, cst - st)); - //static assert(__traits(compiles, ist - st)); - - static assert(__traits(compiles, st - cst)); - static assert(__traits(compiles, cst - cst)); - //static assert(__traits(compiles, ist - cst)); - - //static assert(__traits(compiles, st - ist)); - //static assert(__traits(compiles, cst - ist)); - //static assert(__traits(compiles, ist - ist)); - } - - - /++ - Returns the difference between the two $(LREF SysTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(CXREF time, Duration) - that results. Because converting between months and smaller - units requires a specific date (which $(CXREF time, Duration)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF SysTime) to subtract from this one. - +/ - int diffMonths(in SysTime rhs) @safe const nothrow - { - return (cast(Date)this).diffMonths(cast(Date)rhs); - } - - /// - unittest - { - assert(SysTime(Date(1999, 2, 1)).diffMonths( - SysTime(Date(1999, 1, 31))) == 1); - - assert(SysTime(Date(1999, 1, 31)).diffMonths( - SysTime(Date(1999, 2, 1))) == -1); - - assert(SysTime(Date(1999, 3, 1)).diffMonths( - SysTime(Date(1999, 1, 1))) == 2); - - assert(SysTime(Date(1999, 1, 1)).diffMonths( - SysTime(Date(1999, 3, 31))) == -2); - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.diffMonths(st))); - static assert(__traits(compiles, cst.diffMonths(st))); - //static assert(__traits(compiles, ist.diffMonths(st))); - - static assert(__traits(compiles, st.diffMonths(cst))); - static assert(__traits(compiles, cst.diffMonths(cst))); - //static assert(__traits(compiles, ist.diffMonths(cst))); - - //static assert(__traits(compiles, st.diffMonths(ist))); - //static assert(__traits(compiles, cst.diffMonths(ist))); - //static assert(__traits(compiles, ist.diffMonths(ist))); - } - - - /++ - Whether this $(LREF SysTime) is in a leap year. - +/ - @property bool isLeapYear() @safe const nothrow - { - return (cast(Date)this).isLeapYear; - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.isLeapYear)); - static assert(__traits(compiles, cst.isLeapYear)); - //static assert(__traits(compiles, ist.isLeapYear)); - } - - - /++ - Day of the week this $(LREF SysTime) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const nothrow - { - return getDayOfWeek(dayOfGregorianCal); - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.dayOfWeek)); - static assert(__traits(compiles, cst.dayOfWeek)); - //static assert(__traits(compiles, ist.dayOfWeek)); - } - - - /++ - Day of the year this $(LREF SysTime) is on. - +/ - @property ushort dayOfYear() @safe const nothrow - { - return (cast(Date)this).dayOfYear; - } - - /// - unittest - { - assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); - assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); - assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.dayOfYear)); - static assert(__traits(compiles, cst.dayOfYear)); - //static assert(__traits(compiles, ist.dayOfYear)); - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF SysTime) is on. - +/ - @property void dayOfYear(int day) @safe - { - immutable hnsecs = adjTime; - immutable days = convert!("hnsecs", "days")(hnsecs); - immutable theRest = hnsecs - convert!("days", "hnsecs")(days); - - auto date = Date(cast(int)days); - date.dayOfYear = day; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - - adjTime = newDaysHNSecs + theRest; - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.dayOfYear = 12)); - static assert(!__traits(compiles, cst.dayOfYear = 12)); - //static assert(!__traits(compiles, ist.dayOfYear = 12)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - +/ - @property int dayOfGregorianCal() @safe const nothrow - { - immutable adjustedTime = adjTime; - - //We have to add one because 0 would be midnight, January 1st, 1 A.D., - //which would be the 1st day of the Gregorian Calendar, not the 0th. So, - //simply casting to days is one day off. - if(adjustedTime > 0) - return cast(int)getUnitsFromHNSecs!"days"(adjustedTime) + 1; - - long hnsecs = adjustedTime; - immutable days = cast(int)splitUnitsFromHNSecs!"days"(hnsecs); - - return hnsecs == 0 ? days + 1 : days; - } - - /// - unittest - { - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); - assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); - - assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); - assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); - } - - unittest - { - //Test A.D. - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 1); - - assert(SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212)).dayOfGregorianCal == 2); - assert(SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 32); - assert(SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 366); - assert(SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 731); - assert(SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1096); - assert(SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1462); - assert(SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 17_898); - assert(SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 35_065); - assert(SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_160); - assert(SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_525); - assert(SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 37_986); - assert(SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 72_684); - assert(SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 73_049); - assert(SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_208); - assert(SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_573); - assert(SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 145_732); - assert(SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 146_098); - assert(SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_257); - assert(SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_622); - assert(SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 364_878); - assert(SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 365_243); - assert(SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_023); - assert(SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_389); - assert(SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_596); - assert(SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_961); - assert(SysTime(DateTime(1945, 11, 12, 12, 2, 9), msecs(212)).dayOfGregorianCal == 710_347); - assert(SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 729_755); - assert(SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_486); - - assert(SysTime(DateTime(2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_773); - assert(SysTime(DateTime(2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_803); - assert(SysTime(DateTime(2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_804); - assert(SysTime(DateTime(2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_831); - assert(SysTime(DateTime(2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_832); - assert(SysTime(DateTime(2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_862); - assert(SysTime(DateTime(2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_863); - assert(SysTime(DateTime(2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_892); - assert(SysTime(DateTime(2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_893); - assert(SysTime(DateTime(2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_923); - assert(SysTime(DateTime(2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_924); - assert(SysTime(DateTime(2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_953); - assert(SysTime(DateTime(2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_954); - assert(SysTime(DateTime(2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_984); - assert(SysTime(DateTime(2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_985); - assert(SysTime(DateTime(2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_015); - assert(SysTime(DateTime(2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_016); - assert(SysTime(DateTime(2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_045); - assert(SysTime(DateTime(2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_046); - assert(SysTime(DateTime(2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_076); - assert(SysTime(DateTime(2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_077); - assert(SysTime(DateTime(2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_106); - assert(SysTime(DateTime(2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_107); - assert(SysTime(DateTime(2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_137); - - assert(SysTime(DateTime(2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == 734_534); - assert(SysTime(DateTime(2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == 734_561); - assert(SysTime(DateTime(2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == 734_562); - assert(SysTime(DateTime(2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == 734_563); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).dayOfGregorianCal == 0); - - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 0, 0, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1); - assert(SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -30); - assert(SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -31); - - assert(SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -367); - assert(SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730); - assert(SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731); - assert(SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1095); - assert(SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1096); - assert(SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1460); - assert(SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1461); - assert(SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1826); - assert(SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1827); - assert(SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -2191); - assert(SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -3652); - - assert(SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_262); - assert(SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_627); - assert(SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -35_794); - assert(SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_160); - assert(SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_524); - assert(SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_889); - assert(SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -37_254); - assert(SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -38_715); - assert(SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_413); - assert(SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_778); - assert(SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -109_937); - assert(SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -110_302); - assert(SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_097); - assert(SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_462); - assert(SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_827); - assert(SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_621); - assert(SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_986); - assert(SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -183_351); - assert(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_607); - assert(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_972); - assert(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_387); - assert(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_388); - assert(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_753); - assert(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -585_118); - assert(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_325); - assert(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_690); - assert(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_484); - assert(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_485); - assert(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_850); - assert(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731_215); - - assert(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_502); - assert(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_472); - assert(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_471); - assert(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_444); - assert(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_443); - assert(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_413); - assert(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_412); - assert(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_383); - assert(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_382); - assert(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_352); - assert(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_351); - assert(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_322); - assert(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_321); - assert(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_291); - assert(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_290); - assert(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_260); - assert(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_259); - assert(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_230); - assert(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_229); - assert(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_199); - assert(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_198); - assert(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_169); - assert(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_168); - assert(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_138); - - assert(SysTime(DateTime(-2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == -735_202); - assert(SysTime(DateTime(-2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == -735_175); - assert(SysTime(DateTime(-2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == -735_174); - assert(SysTime(DateTime(-2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == -735_173); - - // Start of Hebrew Calendar - assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.dayOfGregorianCal)); - //static assert(__traits(compiles, ist.dayOfGregorianCal)); - } - - - //Test that the logic for the day of the Gregorian Calendar is consistent - //between Date and SysTime. - unittest - { - void test(Date date, SysTime st, size_t line = __LINE__) - { - import std.format : format; - - if(date.dayOfGregorianCal != st.dayOfGregorianCal) - { - throw new AssertError(format("Date [%s] SysTime [%s]", date.dayOfGregorianCal, st.dayOfGregorianCal), - __FILE__, line); - } - } - - //Test A.D. - test(Date(1, 1, 1), SysTime(DateTime(1, 1, 1, 0, 0, 0))); - test(Date(1, 1, 2), SysTime(DateTime(1, 1, 2, 0, 0, 0), hnsecs(500))); - test(Date(1, 2, 1), SysTime(DateTime(1, 2, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(2, 1, 1), SysTime(DateTime(2, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(3, 1, 1), SysTime(DateTime(3, 1, 1, 12, 13, 14))); - test(Date(4, 1, 1), SysTime(DateTime(4, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(5, 1, 1), SysTime(DateTime(5, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(50, 1, 1), SysTime(DateTime(50, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(97, 1, 1), SysTime(DateTime(97, 1, 1, 23, 59, 59))); - test(Date(100, 1, 1), SysTime(DateTime(100, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(101, 1, 1), SysTime(DateTime(101, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(105, 1, 1), SysTime(DateTime(105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(200, 1, 1), SysTime(DateTime(200, 1, 1, 0, 0, 0))); - test(Date(201, 1, 1), SysTime(DateTime(201, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(300, 1, 1), SysTime(DateTime(300, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(301, 1, 1), SysTime(DateTime(301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(400, 1, 1), SysTime(DateTime(400, 1, 1, 12, 13, 14))); - test(Date(401, 1, 1), SysTime(DateTime(401, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(500, 1, 1), SysTime(DateTime(500, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(501, 1, 1), SysTime(DateTime(501, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(1000, 1, 1), SysTime(DateTime(1000, 1, 1, 23, 59, 59))); - test(Date(1001, 1, 1), SysTime(DateTime(1001, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(1600, 1, 1), SysTime(DateTime(1600, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(1601, 1, 1), SysTime(DateTime(1601, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(1900, 1, 1), SysTime(DateTime(1900, 1, 1, 0, 0, 0))); - test(Date(1901, 1, 1), SysTime(DateTime(1901, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(1945, 11, 12), SysTime(DateTime(1945, 11, 12, 0, 0, 0), hnsecs(50_000))); - test(Date(1999, 1, 1), SysTime(DateTime(1999, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(1999, 7, 6), SysTime(DateTime(1999, 7, 6, 12, 13, 14))); - test(Date(2000, 1, 1), SysTime(DateTime(2000, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(2001, 1, 1), SysTime(DateTime(2001, 1, 1, 12, 13, 14), hnsecs(50_000))); - - test(Date(2010, 1, 1), SysTime(DateTime(2010, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(2010, 1, 31), SysTime(DateTime(2010, 1, 31, 23, 0, 0))); - test(Date(2010, 2, 1), SysTime(DateTime(2010, 2, 1, 23, 59, 59), hnsecs(500))); - test(Date(2010, 2, 28), SysTime(DateTime(2010, 2, 28, 23, 59, 59), hnsecs(50_000))); - test(Date(2010, 3, 1), SysTime(DateTime(2010, 3, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(2010, 3, 31), SysTime(DateTime(2010, 3, 31, 0, 0, 0))); - test(Date(2010, 4, 1), SysTime(DateTime(2010, 4, 1, 0, 0, 0), hnsecs(500))); - test(Date(2010, 4, 30), SysTime(DateTime(2010, 4, 30, 0, 0, 0), hnsecs(50_000))); - test(Date(2010, 5, 1), SysTime(DateTime(2010, 5, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(2010, 5, 31), SysTime(DateTime(2010, 5, 31, 12, 13, 14))); - test(Date(2010, 6, 1), SysTime(DateTime(2010, 6, 1, 12, 13, 14), hnsecs(500))); - test(Date(2010, 6, 30), SysTime(DateTime(2010, 6, 30, 12, 13, 14), hnsecs(50_000))); - test(Date(2010, 7, 1), SysTime(DateTime(2010, 7, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(2010, 7, 31), SysTime(DateTime(2010, 7, 31, 23, 59, 59))); - test(Date(2010, 8, 1), SysTime(DateTime(2010, 8, 1, 23, 59, 59), hnsecs(500))); - test(Date(2010, 8, 31), SysTime(DateTime(2010, 8, 31, 23, 59, 59), hnsecs(50_000))); - test(Date(2010, 9, 1), SysTime(DateTime(2010, 9, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(2010, 9, 30), SysTime(DateTime(2010, 9, 30, 12, 0, 0))); - test(Date(2010, 10, 1), SysTime(DateTime(2010, 10, 1, 0, 12, 0), hnsecs(500))); - test(Date(2010, 10, 31), SysTime(DateTime(2010, 10, 31, 0, 0, 12), hnsecs(50_000))); - test(Date(2010, 11, 1), SysTime(DateTime(2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); - test(Date(2010, 11, 30), SysTime(DateTime(2010, 11, 30, 0, 59, 0))); - test(Date(2010, 12, 1), SysTime(DateTime(2010, 12, 1, 0, 0, 59), hnsecs(500))); - test(Date(2010, 12, 31), SysTime(DateTime(2010, 12, 31, 0, 59, 59), hnsecs(50_000))); - - test(Date(2012, 2, 1), SysTime(DateTime(2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); - test(Date(2012, 2, 28), SysTime(DateTime(2012, 2, 28, 23, 59, 0))); - test(Date(2012, 2, 29), SysTime(DateTime(2012, 2, 29, 7, 7, 7), hnsecs(7))); - test(Date(2012, 3, 1), SysTime(DateTime(2012, 3, 1, 7, 7, 7), hnsecs(7))); - - //Test B.C. - test(Date(0, 12, 31), SysTime(DateTime(0, 12, 31, 0, 0, 0))); - test(Date(0, 12, 30), SysTime(DateTime(0, 12, 30, 0, 0, 0), hnsecs(500))); - test(Date(0, 12, 1), SysTime(DateTime(0, 12, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(0, 11, 30), SysTime(DateTime(0, 11, 30, 0, 0, 0), hnsecs(9_999_999))); - - test(Date(-1, 12, 31), SysTime(DateTime(-1, 12, 31, 12, 13, 14))); - test(Date(-1, 12, 30), SysTime(DateTime(-1, 12, 30, 12, 13, 14), hnsecs(500))); - test(Date(-1, 1, 1), SysTime(DateTime(-1, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-2, 12, 31), SysTime(DateTime(-2, 12, 31, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2, 1, 1), SysTime(DateTime(-2, 1, 1, 23, 59, 59))); - test(Date(-3, 12, 31), SysTime(DateTime(-3, 12, 31, 23, 59, 59), hnsecs(500))); - test(Date(-3, 1, 1), SysTime(DateTime(-3, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-4, 12, 31), SysTime(DateTime(-4, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-4, 1, 1), SysTime(DateTime(-4, 1, 1, 0, 0, 0))); - test(Date(-5, 12, 31), SysTime(DateTime(-5, 12, 31, 0, 0, 0), hnsecs(500))); - test(Date(-5, 1, 1), SysTime(DateTime(-5, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-9, 1, 1), SysTime(DateTime(-9, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - - test(Date(-49, 1, 1), SysTime(DateTime(-49, 1, 1, 12, 13, 14))); - test(Date(-50, 1, 1), SysTime(DateTime(-50, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-97, 1, 1), SysTime(DateTime(-97, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-99, 12, 31), SysTime(DateTime(-99, 12, 31, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-99, 1, 1), SysTime(DateTime(-99, 1, 1, 23, 59, 59))); - test(Date(-100, 1, 1), SysTime(DateTime(-100, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-101, 1, 1), SysTime(DateTime(-101, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-105, 1, 1), SysTime(DateTime(-105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-200, 1, 1), SysTime(DateTime(-200, 1, 1, 0, 0, 0))); - test(Date(-201, 1, 1), SysTime(DateTime(-201, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(-300, 1, 1), SysTime(DateTime(-300, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-301, 1, 1), SysTime(DateTime(-301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-400, 12, 31), SysTime(DateTime(-400, 12, 31, 12, 13, 14))); - test(Date(-400, 1, 1), SysTime(DateTime(-400, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-401, 1, 1), SysTime(DateTime(-401, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-499, 1, 1), SysTime(DateTime(-499, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-500, 1, 1), SysTime(DateTime(-500, 1, 1, 23, 59, 59))); - test(Date(-501, 1, 1), SysTime(DateTime(-501, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-1000, 1, 1), SysTime(DateTime(-1000, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-1001, 1, 1), SysTime(DateTime(-1001, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-1599, 1, 1), SysTime(DateTime(-1599, 1, 1, 0, 0, 0))); - test(Date(-1600, 12, 31), SysTime(DateTime(-1600, 12, 31, 0, 0, 0), hnsecs(500))); - test(Date(-1600, 1, 1), SysTime(DateTime(-1600, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-1601, 1, 1), SysTime(DateTime(-1601, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-1900, 1, 1), SysTime(DateTime(-1900, 1, 1, 12, 13, 14))); - test(Date(-1901, 1, 1), SysTime(DateTime(-1901, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-1999, 1, 1), SysTime(DateTime(-1999, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-1999, 7, 6), SysTime(DateTime(-1999, 7, 6, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2000, 12, 31), SysTime(DateTime(-2000, 12, 31, 23, 59, 59))); - test(Date(-2000, 1, 1), SysTime(DateTime(-2000, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-2001, 1, 1), SysTime(DateTime(-2001, 1, 1, 23, 59, 59), hnsecs(50_000))); - - test(Date(-2010, 1, 1), SysTime(DateTime(-2010, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-2010, 1, 31), SysTime(DateTime(-2010, 1, 31, 0, 0, 0))); - test(Date(-2010, 2, 1), SysTime(DateTime(-2010, 2, 1, 0, 0, 0), hnsecs(500))); - test(Date(-2010, 2, 28), SysTime(DateTime(-2010, 2, 28, 0, 0, 0), hnsecs(50_000))); - test(Date(-2010, 3, 1), SysTime(DateTime(-2010, 3, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 3, 31), SysTime(DateTime(-2010, 3, 31, 12, 13, 14))); - test(Date(-2010, 4, 1), SysTime(DateTime(-2010, 4, 1, 12, 13, 14), hnsecs(500))); - test(Date(-2010, 4, 30), SysTime(DateTime(-2010, 4, 30, 12, 13, 14), hnsecs(50_000))); - test(Date(-2010, 5, 1), SysTime(DateTime(-2010, 5, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2010, 5, 31), SysTime(DateTime(-2010, 5, 31, 23, 59, 59))); - test(Date(-2010, 6, 1), SysTime(DateTime(-2010, 6, 1, 23, 59, 59), hnsecs(500))); - test(Date(-2010, 6, 30), SysTime(DateTime(-2010, 6, 30, 23, 59, 59), hnsecs(50_000))); - test(Date(-2010, 7, 1), SysTime(DateTime(-2010, 7, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-2010, 7, 31), SysTime(DateTime(-2010, 7, 31, 0, 0, 0))); - test(Date(-2010, 8, 1), SysTime(DateTime(-2010, 8, 1, 0, 0, 0), hnsecs(500))); - test(Date(-2010, 8, 31), SysTime(DateTime(-2010, 8, 31, 0, 0, 0), hnsecs(50_000))); - test(Date(-2010, 9, 1), SysTime(DateTime(-2010, 9, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 9, 30), SysTime(DateTime(-2010, 9, 30, 12, 0, 0))); - test(Date(-2010, 10, 1), SysTime(DateTime(-2010, 10, 1, 0, 12, 0), hnsecs(500))); - test(Date(-2010, 10, 31), SysTime(DateTime(-2010, 10, 31, 0, 0, 12), hnsecs(50_000))); - test(Date(-2010, 11, 1), SysTime(DateTime(-2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 11, 30), SysTime(DateTime(-2010, 11, 30, 0, 59, 0))); - test(Date(-2010, 12, 1), SysTime(DateTime(-2010, 12, 1, 0, 0, 59), hnsecs(500))); - test(Date(-2010, 12, 31), SysTime(DateTime(-2010, 12, 31, 0, 59, 59), hnsecs(50_000))); - - test(Date(-2012, 2, 1), SysTime(DateTime(-2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); - test(Date(-2012, 2, 28), SysTime(DateTime(-2012, 2, 28, 23, 59, 0))); - test(Date(-2012, 2, 29), SysTime(DateTime(-2012, 2, 29, 7, 7, 7), hnsecs(7))); - test(Date(-2012, 3, 1), SysTime(DateTime(-2012, 3, 1, 7, 7, 7), hnsecs(7))); - - test(Date(-3760, 9, 7), SysTime(DateTime(-3760, 9, 7, 0, 0, 0))); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - Setting this property does not affect the time portion of $(LREF SysTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF SysTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe nothrow - { - auto hnsecs = adjTime; - hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); - - if(hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - if(--days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - - adjTime = newDaysHNSecs + hnsecs; - } - - /// - unittest - { - auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); - st.dayOfGregorianCal = 1; - assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 365; - assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 366; - assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 0; - assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = -365; - assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = -366; - assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 730_120; - assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 734_137; - assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); - } - - unittest - { - void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - - orig.dayOfGregorianCal = day; - if(orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 1, - SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - - //Test B.C. - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(1)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - - //Test Both. - testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), hnsecs(9_999_999)), 1, - SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - - testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); - testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), hnsecs(9_999_999)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), hnsecs(1)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); - testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - - - auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)); - - void testST2(int day, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - - st.dayOfGregorianCal = day; - if(st != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", st, expected), __FILE__, line); - } - - //Test A.D. - testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212))); - testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212))); - testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212))); - testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212))); - testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212))); - testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212))); - testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212))); - testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212))); - testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212))); - testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212))); - testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212))); - testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212))); - testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212))); - testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212))); - testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212))); - testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212))); - testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212))); - testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212))); - testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212))); - testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212))); - testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212))); - testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212))); - testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212))); - testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212))); - testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212))); - testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212))); - testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212))); - testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212))); - testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212))); - - testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), msecs(212))); - testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), msecs(212))); - testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), msecs(212))); - testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), msecs(212))); - testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), msecs(212))); - testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), msecs(212))); - testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), msecs(212))); - testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), msecs(212))); - testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), msecs(212))); - testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), msecs(212))); - testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), msecs(212))); - testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), msecs(212))); - testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), msecs(212))); - testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), msecs(212))); - testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), msecs(212))); - testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), msecs(212))); - testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), msecs(212))); - testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), msecs(212))); - testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), msecs(212))); - testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), msecs(212))); - testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), msecs(212))); - testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), msecs(212))); - testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), msecs(212))); - testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), msecs(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); - - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); - - //Test B.C. - testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212))); - testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212))); - testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212))); - - testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212))); - testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212))); - testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212))); - testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212))); - testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212))); - testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212))); - - testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212))); - testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212))); - testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212))); - testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212))); - testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212))); - testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212))); - testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212))); - testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212))); - testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212))); - testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212))); - testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212))); - testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212))); - testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212))); - testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212))); - testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212))); - testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212))); - testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212))); - testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212))); - testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212))); - testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212))); - testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212))); - testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212))); - testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212))); - testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212))); - testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212))); - testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212))); - testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212))); - testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212))); - testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212))); - testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212))); - - testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), msecs(212))); - testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), msecs(212))); - testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), msecs(212))); - testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), msecs(212))); - testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), msecs(212))); - testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), msecs(212))); - testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), msecs(212))); - testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), msecs(212))); - testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), msecs(212))); - testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), msecs(212))); - testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), msecs(212))); - testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), msecs(212))); - testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), msecs(212))); - testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), msecs(212))); - testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), msecs(212))); - testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), msecs(212))); - testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), msecs(212))); - testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), msecs(212))); - testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), msecs(212))); - testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), msecs(212))); - testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), msecs(212))); - testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), msecs(212))); - testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), msecs(212))); - testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), msecs(212))); - - testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), msecs(212))); - testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); - //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF SysTime) is in. - - See_Also: - $(WEB en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). - +/ - @property ubyte isoWeek() @safe const nothrow - { - return (cast(Date)this).isoWeek; - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.isoWeek)); - static assert(__traits(compiles, cst.isoWeek)); - //static assert(__traits(compiles, ist.isoWeek)); - } - - - /++ - $(LREF SysTime) for the last day in the month that this Date is in. - The time portion of endOfMonth is always 23:59:59.9999999. - +/ - @property SysTime endOfMonth() @safe const nothrow - { - immutable hnsecs = adjTime; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - - auto date = Date(cast(int)days + 1).endOfMonth; - auto newDays = date.dayOfGregorianCal - 1; - long theTimeHNSecs; - - if(newDays < 0) - { - theTimeHNSecs = -1; - ++newDays; - } - else - theTimeHNSecs = convert!("days", "hnsecs")(1) - 1; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(newDays); - - auto retval = SysTime(this._stdTime, this._timezone); - retval.adjTime = newDaysHNSecs + theTimeHNSecs; - - return retval; - } - - /// - unittest - { - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == - SysTime(DateTime(1999, 1, 31, 23, 59, 59), - hnsecs(9_999_999))); - - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), - msecs(24)).endOfMonth == - SysTime(DateTime(1999, 2, 28, 23, 59, 59), - hnsecs(9_999_999))); - - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), - usecs(5203)).endOfMonth == - SysTime(DateTime(2000, 2, 29, 23, 59, 59), - hnsecs(9_999_999))); - - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), - hnsecs(12345)).endOfMonth == - SysTime(DateTime(2000, 6, 30, 23, 59, 59), - hnsecs(9_999_999))); - } - - unittest - { - //Test A.D. - assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(2000, 2, 1)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 3, 1)).endOfMonth == SysTime(DateTime(1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 4, 1)).endOfMonth == SysTime(DateTime(1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 5, 1)).endOfMonth == SysTime(DateTime(1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 6, 1)).endOfMonth == SysTime(DateTime(1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 7, 1)).endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 8, 1)).endOfMonth == SysTime(DateTime(1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 9, 1)).endOfMonth == SysTime(DateTime(1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 10, 1)).endOfMonth == SysTime(DateTime(1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 11, 1)).endOfMonth == SysTime(DateTime(1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 12, 1)).endOfMonth == SysTime(DateTime(1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - - //Test B.C. - assert(SysTime(Date(-1999, 1, 1)).endOfMonth == SysTime(DateTime(-1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 2, 1)).endOfMonth == SysTime(DateTime(-1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-2000, 2, 1)).endOfMonth == SysTime(DateTime(-2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 3, 1)).endOfMonth == SysTime(DateTime(-1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 4, 1)).endOfMonth == SysTime(DateTime(-1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 5, 1)).endOfMonth == SysTime(DateTime(-1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 6, 1)).endOfMonth == SysTime(DateTime(-1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 7, 1)).endOfMonth == SysTime(DateTime(-1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 8, 1)).endOfMonth == SysTime(DateTime(-1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 9, 1)).endOfMonth == SysTime(DateTime(-1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 10, 1)).endOfMonth == - SysTime(DateTime(-1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 11, 1)).endOfMonth == - SysTime(DateTime(-1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 12, 1)).endOfMonth == - SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.endOfMonth)); - //static assert(__traits(compiles, ist.endOfMonth)); - } - - - /++ - The last day in the month that this $(LREF SysTime) is in. - +/ - @property ubyte daysInMonth() @safe const nothrow - { - return Date(dayOfGregorianCal).daysInMonth; - } - - /// - unittest - { - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); - } - - unittest - { - //Test A.D. - assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth == 29); - assert(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - - //Test B.C. - assert(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth == 28); - assert(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth == 29); - assert(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.daysInMonth)); - //static assert(__traits(compiles, ist.daysInMonth)); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const nothrow - { - return adjTime >= 0; - } - - /// - unittest - { - assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); - assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); - } - - unittest - { - assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); - assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.isAD)); - //static assert(__traits(compiles, ist.isAD)); - } - - - /++ - The $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) - for this $(LREF SysTime) at the given time. For example, - prior to noon, 1996-03-31 would be the Julian day number 2_450_173, so - this function returns 2_450_173, while from noon onward, the Julian - day number would be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() @safe const nothrow - { - immutable jd = dayOfGregorianCal + 1_721_425; - - return hour < 12 ? jd - 1 : jd; - } - - unittest - { - assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1); - assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0); - - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay == 1_721_424); - assert(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay == 1_721_425); - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay == 1_721_425); - assert(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay == 1_721_426); - - assert(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay == 2_299_160); - assert(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay == 2_299_161); - - assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay == 2_400_000); - assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay == 2_400_001); - - assert(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay == 2_444_973); - assert(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay == 2_444_974); - - assert(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay == 2_450_173); - assert(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay == 2_450_174); - - assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay == 2_455_432); - assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.julianDay)); - //static assert(__traits(compiles, ist.julianDay)); - } - - - /++ - The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified - Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const nothrow - { - return (dayOfGregorianCal + 1_721_425) - 2_400_001; - } - - unittest - { - assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0); - assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0); - - assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay == 55_432); - assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.modJulianDay)); - //static assert(__traits(compiles, ist.modJulianDay)); - } - - - /++ - Returns a $(LREF Date) equivalent to this $(LREF SysTime). - +/ - Date opCast(T)() @safe const nothrow - if(is(Unqual!T == Date)) - { - return Date(dayOfGregorianCal); - } - - unittest - { - assert(cast(Date)SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6)); - assert(cast(Date)SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31)); - assert(cast(Date)SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1)); - - assert(cast(Date)SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == Date(1999, 7, 6)); - assert(cast(Date)SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == Date(2000, 12, 31)); - assert(cast(Date)SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == Date(2001, 1, 1)); - - assert(cast(Date)SysTime(Date(-1999, 7, 6)) == Date(-1999, 7, 6)); - assert(cast(Date)SysTime(Date(-2000, 12, 31)) == Date(-2000, 12, 31)); - assert(cast(Date)SysTime(Date(-2001, 1, 1)) == Date(-2001, 1, 1)); - - assert(cast(Date)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == Date(-1999, 7, 6)); - assert(cast(Date)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == Date(-2000, 12, 31)); - assert(cast(Date)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(Date)cst)); - //static assert(__traits(compiles, cast(Date)ist)); - } - - - /++ - Returns a $(LREF DateTime) equivalent to this $(LREF SysTime). - +/ - DateTime opCast(T)() @safe const nothrow - if(is(Unqual!T == DateTime)) - { - try - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - return DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); - } - catch(Exception e) - assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); - } - - unittest - { - assert(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22)); - assert(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(1, 1, 6, 7, 12, 22)); - assert(cast(DateTime)SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0)); - assert(cast(DateTime)SysTime(Date(2000, 12, 31)) == DateTime(2000, 12, 31, 0, 0, 0)); - assert(cast(DateTime)SysTime(Date(2001, 1, 1)) == DateTime(2001, 1, 1, 0, 0, 0)); - - assert(cast(DateTime)SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == DateTime(1999, 7, 6, 12, 10, 9)); - assert(cast(DateTime)SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == DateTime(2000, 12, 31, 13, 11, 10)); - assert(cast(DateTime)SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == DateTime(2001, 1, 1, 14, 12, 11)); - - assert(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22)) == DateTime(-1, 1, 6, 7, 12, 22)); - assert(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(-1, 1, 6, 7, 12, 22)); - assert(cast(DateTime)SysTime(Date(-1999, 7, 6)) == DateTime(-1999, 7, 6, 0, 0, 0)); - assert(cast(DateTime)SysTime(Date(-2000, 12, 31)) == DateTime(-2000, 12, 31, 0, 0, 0)); - assert(cast(DateTime)SysTime(Date(-2001, 1, 1)) == DateTime(-2001, 1, 1, 0, 0, 0)); - - assert(cast(DateTime)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == DateTime(-1999, 7, 6, 12, 10, 9)); - assert(cast(DateTime)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == DateTime(-2000, 12, 31, 13, 11, 10)); - assert(cast(DateTime)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == DateTime(-2001, 1, 1, 14, 12, 11)); - - assert(cast(DateTime)SysTime(DateTime(2011, 1, 13, 8, 17, 2), msecs(296), LocalTime()) == - DateTime(2011, 1, 13, 8, 17, 2)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(DateTime)cst)); - //static assert(__traits(compiles, cast(DateTime)ist)); - } - - - /++ - Returns a $(LREF TimeOfDay) equivalent to this $(LREF SysTime). - +/ - TimeOfDay opCast(T)() @safe const nothrow - if(is(Unqual!T == TimeOfDay)) - { - try - { - auto hnsecs = adjTime; - hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); - - if(hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - return TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second); - } - catch(Exception e) - assert(0, "TimeOfDay's constructor threw."); - } - - unittest - { - assert(cast(TimeOfDay)SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay)SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay)SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0)); - - assert(cast(TimeOfDay)SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); - assert(cast(TimeOfDay)SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); - assert(cast(TimeOfDay)SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - - assert(cast(TimeOfDay)SysTime(Date(-1999, 7, 6)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay)SysTime(Date(-2000, 12, 31)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay)SysTime(Date(-2001, 1, 1)) == TimeOfDay(0, 0, 0)); - - assert(cast(TimeOfDay)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); - assert(cast(TimeOfDay)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); - assert(cast(TimeOfDay)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); - } - - - //Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed. - //This allows assignment from const(SysTime) to SysTime. - //It may be a good idea to keep it though, since casting from a type to itself - //should be allowed, and it doesn't work without this opCast() since opCast() - //has already been defined for other types. - SysTime opCast(T)() @safe const pure nothrow - if(is(Unqual!T == SysTime)) - { - return SysTime(_stdTime, _timezone); - } - - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds and TZ is time - zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. - If its time zone is $(D UTC), then it is "Z". Otherwise, it is the - offset from UTC (e.g. +1:00 or -7:00). Note that the offset from UTC - is $(I not) enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - +/ - string toISOString() @safe const nothrow - { - import std.format : format; - try - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); - auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); - auto fracSecStr = fracSecsToISOString(cast(int)hnsecs); - - if(_timezone is LocalTime()) - return dateTime.toISOString() ~ fracSecsToISOString(cast(int)hnsecs); - - if(_timezone is UTC()) - return dateTime.toISOString() ~ fracSecsToISOString(cast(int)hnsecs) ~ "Z"; - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - return format("%s%s%s", - dateTime.toISOString(), - fracSecsToISOString(cast(int)hnsecs), - SimpleTimeZone.toISOString(utcOffset)); - } - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - msecs(24)).toISOString() == - "19981225T021500.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - hnsecs(520_920)).toISOString() == - "-00040105T000002.052092"); - } - - unittest - { - //Test A.D. - assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOString() == "00010101T000000.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString() == "00091204T000000"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString() == "00991204T050612"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString() == "09991204T134459"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString() == "99990704T235959"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString() == "+100001020T010101"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "00091204T000000.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "00991204T050612.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "09991204T134459.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "99990704T235959.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "+100001020T010101.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString() == - "20121221T121212-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString() == - "20121221T121212+07:00"); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOString() == - "00001231T235959.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOString() == "00001231T235959.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString() == "00001231T235959Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString() == "00001204T001204"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString() == "-00091204T000000"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString() == "-00991204T050612"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString() == "-09991204T134459"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString() == "-99990704T235959"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString() == "-100001020T010101"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOString() == "00001204T000000.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "-00091204T000000.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "-00991204T050612.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "-09991204T134459.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "-99990704T235959.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); - } - - - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If - its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset - from UTC (e.g. +1:00 or -7:00). Note that the offset from UTC is - $(I not) enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - +/ - string toISOExtString() @safe const nothrow - { - import std.format : format; - try - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); - auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); - auto fracSecStr = fracSecsToISOString(cast(int)hnsecs); - - if(_timezone is LocalTime()) - return dateTime.toISOExtString() ~ fracSecsToISOString(cast(int)hnsecs); - - if(_timezone is UTC()) - return dateTime.toISOExtString() ~ fracSecsToISOString(cast(int)hnsecs) ~ "Z"; - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - return format("%s%s%s", - dateTime.toISOExtString(), - fracSecsToISOString(cast(int)hnsecs), - SimpleTimeZone.toISOString(utcOffset)); - } - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - msecs(24)).toISOExtString() == - "1998-12-25T02:15:00.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - hnsecs(520_920)).toISOExtString() == - "-0004-01-05T00:00:02.052092"); - } - - unittest - { - //Test A.D. - assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOExtString() == - "0001-01-01T00:00:00.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "0009-12-04T00:00:00.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "0099-12-04T05:06:12.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == "0999-12-04T13:44:59.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == "9999-07-04T23:59:59.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == - "+10000-10-20T01:01:01.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString() == - "2012-12-21T12:12:12-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString() == - "2012-12-21T12:12:12+07:00"); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOExtString() == - "0000-12-31T23:59:59.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOExtString() == - "0000-12-31T23:59:59.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString() == "0000-12-31T23:59:59Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOExtString() == "0000-12-04T00:00:00.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "-0009-12-04T00:00:00.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "-0099-12-04T05:06:12.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == - "-0999-12-04T13:44:59.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == - "-9999-07-04T23:59:59.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == - "-10000-10-20T01:01:01.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); - } - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If - its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset - from UTC (e.g. +1:00 or -7:00). Note that the offset from UTC is - $(I not) enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - +/ - string toSimpleString() @safe const nothrow - { - import std.format : format; - try - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if(hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); - auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); - auto fracSecStr = fracSecsToISOString(cast(int)hnsecs); - - if(_timezone is LocalTime()) - return dateTime.toSimpleString() ~ fracSecsToISOString(cast(int)hnsecs); - - if(_timezone is UTC()) - return dateTime.toSimpleString() ~ fracSecsToISOString(cast(int)hnsecs) ~ "Z"; - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - return format("%s%s%s", - dateTime.toSimpleString(), - fracSecsToISOString(cast(int)hnsecs), - SimpleTimeZone.toISOString(utcOffset)); - } - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - msecs(24)).toSimpleString() == - "1998-Dec-25 02:15:00.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - hnsecs(520_920)).toSimpleString() == - "-0004-Jan-05 00:00:02.052092"); - } - - unittest - { - //Test A.D. - assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "0009-Dec-04 00:00:00.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "0099-Dec-04 05:06:12.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == - "0999-Dec-04 13:44:59.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == - "9999-Jul-04 23:59:59.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == - "+10000-Oct-20 01:01:01.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString() == - "2012-Dec-21 12:12:12-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString() == - "2012-Dec-21 12:12:12+07:00"); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toSimpleString() == - "0000-Dec-31 23:59:59.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toSimpleString() == - "0000-Dec-31 23:59:59.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString() == "0000-Dec-31 23:59:59Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toSimpleString() == "0000-Dec-04 00:00:00.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "-0009-Dec-04 00:00:00.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "-0099-Dec-04 05:06:12.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == - "-0999-Dec-04 13:44:59.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == - "-9999-Jul-04 23:59:59.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == - "-10000-Oct-20 01:01:01.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); - } - - - /++ - Converts this $(LREF SysTime) to a string. - +/ - string toString() @safe const nothrow - { - return toSimpleString(); - } - - unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.toString())); - static assert(__traits(compiles, cst.toString())); - //static assert(__traits(compiles, ist.toString())); - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds is the time - zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(D toISOString) except that - trailing zeroes are permitted - including having fractional seconds with - all zeroes. However, a decimal point with nothing following it is - invalid. - - If there is no time zone in the string, then $(LREF LocalTime) is used. If - the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). - - The accepted formats for time zone offsets - are +H, -H, +HH, -HH, +H:MM, -H:MM, +HH:MM, and -HH:MM. - - Params: - isoString = A string formatted in the ISO format for dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF SysTime) would not be valid. - +/ - static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe - if(isSomeString!S) - { - import std.string : strip; - import std.conv : to; - import std.algorithm : startsWith, find; - import std.format : format; - - auto dstr = to!dstring(strip(isoString)); - immutable skipFirst = dstr.startsWith('+', '-') != 0; - - auto found = (skipFirst ? dstr[1..$] : dstr).find('.', 'Z', '+', '-'); - auto dateTimeStr = dstr[0 .. $ - found[0].length]; - - dstring fracSecStr; - dstring zoneStr; - - if(found[1] != 0) - { - if(found[1] == 1) - { - auto foundTZ = found[0].find('Z', '+', '-'); - - if(foundTZ[1] != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ[0].length]; - zoneStr = foundTZ[0]; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromISOString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if(zoneStr.empty) - parsedZone = LocalTime(); - else if(zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if(tz !is null) - retval.timezone = tz; - - return retval; - } - catch(DateTimeException dte) - throw new DateTimeException(format("Invalid ISO String: %s", isoString)); - } - - /// - unittest - { - assert(SysTime.fromISOString("20100704T070612") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - assert(SysTime.fromISOString("19981225T021500.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - assert(SysTime.fromISOString("00000105T230959.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - assert(SysTime.fromISOString("-00040105T000002") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - assert(SysTime.fromISOString(" 20100704T070612 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOString("20100704T070612Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - assert(SysTime.fromISOString("20100704T070612-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(dur!"hours"(-8)))); - assert(SysTime.fromISOString("20100704T070612+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(dur!"hours"(8)))); - } - - unittest - { - assertThrown!DateTimeException(SysTime.fromISOString("")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.A")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.Z")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-1:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:0")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-24.00")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+24.00")); - - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04T00:00:00.")); - - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00.")); - - assertThrown!DateTimeException(SysTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Dec-22 17:22:01")); - - assert(SysTime.fromISOString("20101222T172201") == SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - assert(SysTime.fromISOString("19990706T123033") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOString("-19990706T123033") == SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOString("+019990706T123033") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOString("19990706T123033 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOString(" 19990706T123033") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOString(" 19990706T123033 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - assert(SysTime.fromISOString("19070707T121212.0") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - assert(SysTime.fromISOString("19070707T121212.0000000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - assert(SysTime.fromISOString("19070707T121212.0000001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); - assert(SysTime.fromISOString("19070707T121212.000001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - assert(SysTime.fromISOString("19070707T121212.0000010") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - assert(SysTime.fromISOString("19070707T121212.001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - assert(SysTime.fromISOString("19070707T121212.0010000") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - - assert(SysTime.fromISOString("20101222T172201Z") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - assert(SysTime.fromISOString("20101222T172201-1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOString("20101222T172201-1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOString("20101222T172201-1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); - assert(SysTime.fromISOString("20101222T172201-8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); - assert(SysTime.fromISOString("20101222T172201+1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOString("20101222T172201+1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOString("20101222T172201+1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); - assert(SysTime.fromISOString("20101222T172201+8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); - - assert(SysTime.fromISOString("20101103T065106.57159Z") == - SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - - assert(SysTime.fromISOString("20101222T172201.23412Z") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); - assert(SysTime.fromISOString("20101222T172201.23112-1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOString("20101222T172201.45-1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOString("20101222T172201.1-1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), - new immutable SimpleTimeZone(dur!"minutes"(-90)))); - assert(SysTime.fromISOString("20101222T172201.55-8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-480)))); - assert(SysTime.fromISOString("20101222T172201.1234567+1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOString("20101222T172201.0+1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOString("20101222T172201.0000000+1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), - new immutable SimpleTimeZone(dur!"minutes"(90)))); - assert(SysTime.fromISOString("20101222T172201.45+8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(480)))); - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the - time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(D toISOExtString) - except that trailing zeroes are permitted - including having fractional - seconds with all zeroes. However, a decimal point with nothing following - it is invalid. - - If there is no time zone in the string, then $(LREF LocalTime) is used. If - the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). - - The accepted formats for time zone offsets - are +H, -H, +HH, -HH, +H:MM, -H:MM, +HH:MM, and -HH:MM. - - Params: - isoExtString = A string formatted in the ISO Extended format for dates - and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF SysTime) would not be valid. - +/ - static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe - if(isSomeString!(S)) - { - import std.string : strip; - import std.conv : to; - import std.algorithm : countUntil, find; - import std.format : format; - - auto dstr = to!dstring(strip(isoExtString)); - - auto tIndex = dstr.countUntil('T'); - enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto found = dstr[tIndex + 1 .. $].find('.', 'Z', '+', '-'); - auto dateTimeStr = dstr[0 .. $ - found[0].length]; - - dstring fracSecStr; - dstring zoneStr; - - if(found[1] != 0) - { - if(found[1] == 1) - { - auto foundTZ = found[0].find('Z', '+', '-'); - - if(foundTZ[1] != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ[0].length]; - zoneStr = foundTZ[0]; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromISOExtString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if(zoneStr.empty) - parsedZone = LocalTime(); - else if(zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if(tz !is null) - retval.timezone = tz; - - return retval; - } - catch(DateTimeException dte) - throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); - } - - /// - unittest - { - assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - assert(SysTime.fromISOExtString("2010-07-04T07:06:12-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(dur!"hours"(-8)))); - assert(SysTime.fromISOExtString("2010-07-04T07:06:12+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(dur!"hours"(8)))); - } - - unittest - { - assertThrown!DateTimeException(SysTime.fromISOExtString("")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704T000000.0")); - - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.A")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.Z")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-1:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+1:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+1:0")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-24.00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+24.00")); - - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(SysTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Dec-22 17:22:01")); - - assert(SysTime.fromISOExtString("2010-12-22T17:22:01") == SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - assert(SysTime.fromISOExtString("1999-07-06T12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOExtString("-1999-07-06T12:30:33") == SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOExtString("+01999-07-06T12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOExtString("1999-07-06T12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOExtString(" 1999-07-06T12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromISOExtString(" 1999-07-06T12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0000000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0000001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.000001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0000010") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0010000") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - - assert(SysTime.fromISOExtString("2010-12-22T17:22:01Z") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01-1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01-1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01-1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01-8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01+1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01+1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01+1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01+8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); - - assert(SysTime.fromISOExtString("2010-11-03T06:51:06.57159Z") == - SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.23412Z") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.23112-1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.45-1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.1-1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), - new immutable SimpleTimeZone(dur!"minutes"(-90)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.55-8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-480)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.1234567+1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.0+1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.0000000+1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), - new immutable SimpleTimeZone(dur!"minutes"(90)))); - assert(SysTime.fromISOExtString("2010-12-22T17:22:01.45+8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(480)))); - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYY-MM-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the - time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(D toSimpleString) except - that trailing zeroes are permitted - including having fractional seconds - with all zeroes. However, a decimal point with nothing following it is - invalid. - - If there is no time zone in the string, then $(LREF LocalTime) is used. If - the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). - - The accepted formats for time zone offsets - are +H, -H, +HH, -HH, +H:MM, -H:MM, +HH:MM, and -HH:MM. - - - Params: - simpleString = A string formatted in the way that - $(D toSimpleString) formats dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF SysTime) would not be valid. - +/ - static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe - if(isSomeString!(S)) - { - import std.string : strip; - import std.conv : to; - import std.algorithm : countUntil, find; - import std.format : format; - - auto dstr = to!dstring(strip(simpleString)); - - auto spaceIndex = dstr.countUntil(' '); - enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString))); - - auto found = dstr[spaceIndex + 1 .. $].find('.', 'Z', '+', '-'); - auto dateTimeStr = dstr[0 .. $ - found[0].length]; - - dstring fracSecStr; - dstring zoneStr; - - if(found[1] != 0) - { - if(found[1] == 1) - { - auto foundTZ = found[0].find('Z', '+', '-'); - - if(foundTZ[1] != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ[0].length]; - zoneStr = foundTZ[0]; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromSimpleString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if(zoneStr.empty) - parsedZone = LocalTime(); - else if(zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if(tz !is null) - retval.timezone = tz; - - return retval; - } - catch(DateTimeException dte) - throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); - } - - /// - unittest - { - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(dur!"hours"(-8)))); - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(dur!"hours"(8)))); - } - - unittest - { - assertThrown!DateTimeException(SysTime.fromSimpleString("")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704 000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704t000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.0")); - - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.A")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.Z")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-1:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:0")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-24.00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+24.00")); - - assertThrown!DateTimeException(SysTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-12-22T172201")); - - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01") == SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - assert(SysTime.fromSimpleString("1999-Jul-06 12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromSimpleString("-1999-Jul-06 12:30:33") == SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromSimpleString("+01999-Jul-06 12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromSimpleString("1999-Jul-06 12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - assert(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.000001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000010") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.001") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0010000") == - SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01Z") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); - - assert(SysTime.fromSimpleString("2010-Nov-03 06:51:06.57159Z") == - SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23412Z") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23112-1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45-1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1-1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), - new immutable SimpleTimeZone(dur!"minutes"(-90)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.55-8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-480)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1234567+1:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0+1") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0000000+1:30") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), - new immutable SimpleTimeZone(dur!"minutes"(90)))); - assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45+8:00") == - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(480)))); - } - - - /++ - Returns the $(LREF SysTime) farthest in the past which is representable - by $(LREF SysTime). - - The $(LREF SysTime) which is returned is in UTC. - +/ - @property static SysTime min() @safe pure nothrow - { - return SysTime(long.min, UTC()); - } - - unittest - { - assert(SysTime.min.year < 0); - assert(SysTime.min < SysTime.max); - } - - - /++ - Returns the $(LREF SysTime) farthest in the future which is representable - by $(LREF SysTime). - - The $(LREF SysTime) which is returned is in UTC. - +/ - @property static SysTime max() @safe pure nothrow - { - return SysTime(long.max, UTC()); - } - - unittest - { - assert(SysTime.max.year > 0); - assert(SysTime.max > SysTime.min); - } - - -private: - - /+ - Returns $(D stdTime) converted to $(LREF SysTime)'s time zone. - +/ - @property long adjTime() @safe const nothrow - { - return _timezone.utcToTZ(_stdTime); - } - - - /+ - Converts the given hnsecs from $(LREF SysTime)'s time zone to std time. - +/ - @property void adjTime(long adjTime) @safe nothrow - { - _stdTime = _timezone.tzToUTC(adjTime); - } - - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058 - /+ - invariant() - { - assert(_timezone !is null, "Invariant Failure: timezone is null. Were you foolish enough to use SysTime.init? (since timezone for SysTime.init can't be set at compile time)."); - } - +/ - - - long _stdTime; - Rebindable!(immutable TimeZone) _timezone; -} - - -/++ - Represents a date in the - $(WEB en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic Gregorian Calendar) - ranging from - 32,768 B.C. to 32,767 A.D. Positive years are A.D. Non-positive years are - B.C. - - Year, month, and day are kept separately internally so that $(D Date) is - optimized for calendar-based operations. - - $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian - leap year calculations for its entire length. As per - $(WEB en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as - year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. as - a positive integer with 1 B.C. being the year prior to 1 A.D. - - Year 0 is a leap year. - +/ -struct Date -{ -public: - - /++ - Throws: - $(LREF DateTimeException) if the resulting $(LREF Date) would not be valid. - - Params: - year = Year of the Gregorian Calendar. Positive values are A.D. - Non-positive values are B.C. with year 0 being the year - prior to 1 A.D. - month = Month of the year. - day = Day of the month. - +/ - this(int year, int month, int day) @safe pure - { - enforceValid!"months"(cast(Month)month); - enforceValid!"days"(year, cast(Month)month, day); - - _year = cast(short)year; - _month = cast(Month)month; - _day = cast(ubyte)day; - } - - unittest - { - assert(Date(1, 1, 1) == Date.init); - - static void testDate(in Date date, int year, int month, int day) - { - assert(date._year == year); - assert(date._month == month); - assert(date._day == day); - } - - testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); - testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); - testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); - - //Test A.D. - assertThrown!DateTimeException(Date(1, 0, 1)); - assertThrown!DateTimeException(Date(1, 1, 0)); - assertThrown!DateTimeException(Date(1999, 13, 1)); - assertThrown!DateTimeException(Date(1999, 1, 32)); - assertThrown!DateTimeException(Date(1999, 2, 29)); - assertThrown!DateTimeException(Date(2000, 2, 30)); - assertThrown!DateTimeException(Date(1999, 3, 32)); - assertThrown!DateTimeException(Date(1999, 4, 31)); - assertThrown!DateTimeException(Date(1999, 5, 32)); - assertThrown!DateTimeException(Date(1999, 6, 31)); - assertThrown!DateTimeException(Date(1999, 7, 32)); - assertThrown!DateTimeException(Date(1999, 8, 32)); - assertThrown!DateTimeException(Date(1999, 9, 31)); - assertThrown!DateTimeException(Date(1999, 10, 32)); - assertThrown!DateTimeException(Date(1999, 11, 31)); - assertThrown!DateTimeException(Date(1999, 12, 32)); - - assertNotThrown!DateTimeException(Date(1999, 1, 31)); - assertNotThrown!DateTimeException(Date(1999, 2, 28)); - assertNotThrown!DateTimeException(Date(2000, 2, 29)); - assertNotThrown!DateTimeException(Date(1999, 3, 31)); - assertNotThrown!DateTimeException(Date(1999, 4, 30)); - assertNotThrown!DateTimeException(Date(1999, 5, 31)); - assertNotThrown!DateTimeException(Date(1999, 6, 30)); - assertNotThrown!DateTimeException(Date(1999, 7, 31)); - assertNotThrown!DateTimeException(Date(1999, 8, 31)); - assertNotThrown!DateTimeException(Date(1999, 9, 30)); - assertNotThrown!DateTimeException(Date(1999, 10, 31)); - assertNotThrown!DateTimeException(Date(1999, 11, 30)); - assertNotThrown!DateTimeException(Date(1999, 12, 31)); - - //Test B.C. - assertNotThrown!DateTimeException(Date(0, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 12, 31)); - assertNotThrown!DateTimeException(Date(-1, 2, 28)); - assertNotThrown!DateTimeException(Date(-4, 2, 29)); - - assertThrown!DateTimeException(Date(-1, 2, 29)); - assertThrown!DateTimeException(Date(-2, 2, 29)); - assertThrown!DateTimeException(Date(-3, 2, 29)); - } - - - /++ - Params: - day = The Xth day of the Gregorian Calendar that the constructed - $(LREF Date) will be for. - +/ - this(int day) @safe pure nothrow - { - if(day > 0) - { - int years = (day / daysIn400Years) * 400 + 1; - day %= daysIn400Years; - - { - immutable tempYears = day / daysIn100Years; - - if(tempYears == 4) - { - years += 300; - day -= daysIn100Years * 3; - } - else - { - years += tempYears * 100; - day %= daysIn100Years; - } - } - - years += (day / daysIn4Years) * 4; - day %= daysIn4Years; - - { - immutable tempYears = day / daysInYear; - - if(tempYears == 4) - { - years += 3; - day -= daysInYear * 3; - } - else - { - years += tempYears; - day %= daysInYear; - } - } - - if(day == 0) - { - _year = cast(short)(years - 1); - _month = Month.dec; - _day = 31; - } - else - { - _year = cast(short)years; - - try - dayOfYear = day; - catch(Exception e) - assert(0, "dayOfYear assignment threw."); - } - } - else if(day <= 0 && -day < daysInLeapYear) - { - _year = 0; - - try - dayOfYear = (daysInLeapYear + day); - catch(Exception e) - assert(0, "dayOfYear assignment threw."); - } - else - { - day += daysInLeapYear - 1; - int years = (day / daysIn400Years) * 400 - 1; - day %= daysIn400Years; - - { - immutable tempYears = day / daysIn100Years; - - if(tempYears == -4) - { - years -= 300; - day += daysIn100Years * 3; - } - else - { - years += tempYears * 100; - day %= daysIn100Years; - } - } - - years += (day / daysIn4Years) * 4; - day %= daysIn4Years; - - { - immutable tempYears = day / daysInYear; - - if(tempYears == -4) - { - years -= 3; - day += daysInYear * 3; - } - else - { - years += tempYears; - day %= daysInYear; - } - } - - if(day == 0) - { - _year = cast(short)(years + 1); - _month = Month.jan; - _day = 1; - } - else - { - _year = cast(short)years; - immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1; - - try - dayOfYear = newDoY; - catch(Exception e) - assert(0, "dayOfYear assignment threw."); - } - } - } - - unittest - { - import std.range; - - //Test A.D. - foreach(gd; chain(testGregDaysBC, testGregDaysAD)) - assert(Date(gd.day) == gd.date); - } - - - /++ - Compares this $(LREF Date) with the given $(LREF Date). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in Date rhs) @safe const pure nothrow - { - if(_year < rhs._year) - return -1; - if(_year > rhs._year) - return 1; - - if(_month < rhs._month) - return -1; - if(_month > rhs._month) - return 1; - - if(_day < rhs._day) - return -1; - if(_day > rhs._day) - return 1; - - return 0; - } - - unittest - { - //Test A.D. - assert(Date(1, 1, 1).opCmp(Date.init) == 0); - - assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); - assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); - assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); - - assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); - - assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); - - assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); - assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); - assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); - - assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); - assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); - assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - - //Test B.C. - assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); - assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); - assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); - assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); - - assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); - - assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); - - assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); - assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); - assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); - - assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); - assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); - - //Test Both - assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); - - assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); - - assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); - - assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); - - assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); - assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); - - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.opCmp(date))); - static assert(__traits(compiles, date.opCmp(cdate))); - static assert(__traits(compiles, date.opCmp(idate))); - static assert(__traits(compiles, cdate.opCmp(date))); - static assert(__traits(compiles, cdate.opCmp(cdate))); - static assert(__traits(compiles, cdate.opCmp(idate))); - static assert(__traits(compiles, idate.opCmp(date))); - static assert(__traits(compiles, idate.opCmp(cdate))); - static assert(__traits(compiles, idate.opCmp(idate))); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const pure nothrow - { - return _year; - } - - /// - unittest - { - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } - - unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.year == 1999)); - static assert(__traits(compiles, idate.year == 1999)); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this Date's year to. - - Throws: - $(LREF DateTimeException) if the new year is not a leap year and the - resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - enforceValid!"days"(year, _month, _day); - _year = cast(short)year; - } - - /// - unittest - { - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } - - unittest - { - static void testDateInvalid(Date date, int year) - { - date.year = year; - } - - static void testDate(Date date, int year, in Date expected) - { - date.year = year; - assert(date == expected); - } - - assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); - - testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); - testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); - testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.year = 1999)); - static assert(!__traits(compiles, idate.year = 1999)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(LREF DateTimeException) if $(D isAD) is true. - +/ - @property ushort yearBC() @safe const pure - { - import std.format : format; - - if(isAD) - throw new DateTimeException(format("Year %s is A.D.", _year)); - return cast(ushort)((_year * -1) + 1); - } - - /// - unittest - { - assert(Date(0, 1, 1).yearBC == 1); - assert(Date(-1, 1, 1).yearBC == 2); - assert(Date(-100, 1, 1).yearBC == 101); - } - - unittest - { - assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); - - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - static assert(__traits(compiles, date.yearBC)); - static assert(__traits(compiles, cdate.yearBC)); - static assert(__traits(compiles, idate.yearBC)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF Date)'s year to. - - Throws: - $(LREF DateTimeException) if a non-positive value is given. - +/ - @property void yearBC(int year) @safe pure - { - if(year <= 0) - throw new DateTimeException("The given year is not a year B.C."); - - _year = cast(short)((year - 1) * -1); - } - - /// - unittest - { - auto date = Date(2010, 1, 1); - date.yearBC = 1; - assert(date == Date(0, 1, 1)); - - date.yearBC = 10; - assert(date == Date(-9, 1, 1)); - } - - unittest - { - assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); - - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - static assert(__traits(compiles, date.yearBC = 7)); - static assert(!__traits(compiles, cdate.yearBC = 7)); - static assert(!__traits(compiles, idate.yearBC = 7)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const pure nothrow - { - return _month; - } - - /// - unittest - { - assert(Date(1999, 7, 6).month == 7); - assert(Date(2010, 10, 4).month == 10); - assert(Date(-7, 4, 5).month == 4); - } - - unittest - { - assert(Date.init.month == 1); - assert(Date(1999, 7, 6).month == 7); - assert(Date(-1999, 7, 6).month == 7); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.month == 7)); - static assert(__traits(compiles, idate.month == 7)); - } - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF Date)'s month to. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month or if - the current day would not be valid in the given month. - +/ - @property void month(Month month) @safe pure - { - enforceValid!"months"(month); - enforceValid!"days"(_year, month, _day); - _month = cast(Month)month; - } - - unittest - { - static void testDate(Date date, Month month, in Date expected = Date.init) - { - date.month = month; - assert(expected != Date.init); - assert(date == expected); - } - - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)13)); - assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month)2)); - assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month)2)); - - testDate(Date(1, 1, 1), cast(Month)7, Date(1, 7, 1)); - testDate(Date(-1, 1, 1), cast(Month)7, Date(-1, 7, 1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.month = 7)); - static assert(!__traits(compiles, idate.month = 7)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const pure nothrow - { - return _day; - } - - /// - unittest - { - assert(Date(1999, 7, 6).day == 6); - assert(Date(2010, 10, 4).day == 4); - assert(Date(-7, 4, 5).day == 5); - } - - unittest - { - import std.range; - import std.format : format; - - static void test(Date date, int expected) - { - assert(date.day == expected, - format("Value given: %s", date)); - } - - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - test(Date(year, md.month, md.day), md.day); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.day == 6)); - static assert(__traits(compiles, idate.day == 6)); - } - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF Date)'s day to. - - Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. - +/ - @property void day(int day) @safe pure - { - enforceValid!"days"(_year, _month, day); - _day = cast(ubyte)day; - } - - unittest - { - static void testDate(Date date, int day) - { - date.day = day; - } - - //Test A.D. - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); - - { - auto date = Date(1, 1, 1); - date.day = 6; - assert(date == Date(1, 1, 6)); - } - - //Test B.C. - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); - - { - auto date = Date(-1, 1, 1); - date.day = 6; - assert(date == Date(-1, 1, 6)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.day = 6)); - static assert(!__traits(compiles, idate.day = 6)); - } - - - /++ - Adds the given number of years or months to this $(LREF Date). A negative - number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF Date). - allowOverflow = Whether the day should be allowed to overflow, - causing the month to increment. - +/ - ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if(units == "years") - { - immutable newYear = _year + value; - - _year += value; - - if(_month == Month.feb && _day == 29 && !yearIsLeapYear(_year)) - { - if(allowOverflow == AllowDayOverflow.yes) - { - _month = Month.mar; - _day = 1; - } - else - _day = 28; - } - - return this; - } - - /// - unittest - { - auto d1 = Date(2010, 1, 1); - d1.add!"months"(11); - assert(d1 == Date(2010, 12, 1)); - - auto d2 = Date(2010, 1, 1); - d2.add!"months"(-11); - assert(d2 == Date(2009, 2, 1)); - - auto d3 = Date(2000, 2, 29); - d3.add!"years"(1); - assert(d3 == Date(2001, 3, 1)); - - auto d4 = Date(2000, 2, 29); - d4.add!"years"(1, AllowDayOverflow.no); - assert(d4 == Date(2001, 2, 28)); - } - - //Test add!"years"() with AllowDayOverlow.yes - unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7); - assert(date == Date(2006, 7, 6)); - date.add!"years"(-9); - assert(date == Date(1997, 7, 6)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"years"(1); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1); - assert(date == Date(1999, 3, 1)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7); - assert(date == Date(-2006, 7, 6)); - date.add!"years"(9); - assert(date == Date(-1997, 7, 6)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1); - assert(date == Date(-1999, 3, 1)); - } - - //Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5); - assert(date == Date(-1, 7, 6)); - date.add!"years"(5); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(5); - assert(date == Date(1, 7, 6)); - date.add!"years"(-5); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(4, 7, 6); - date.add!"years"(-8); - assert(date == Date(-4, 7, 6)); - date.add!"years"(8); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(8); - assert(date == Date(4, 7, 6)); - date.add!"years"(-8); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(-4, 2, 29); - date.add!"years"(5); - assert(date == Date(1, 3, 1)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5); - assert(date == Date(-1, 3, 1)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5).add!"years"(7); - assert(date == Date(6, 3, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"years"(7))); - static assert(!__traits(compiles, idate.add!"years"(7))); - } - - //Test add!"years"() with AllowDayOverlow.no - unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7, AllowDayOverflow.no); - assert(date == Date(2006, 7, 6)); - date.add!"years"(-9, AllowDayOverflow.no); - assert(date == Date(1997, 7, 6)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"years"(1, AllowDayOverflow.no); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7, AllowDayOverflow.no); - assert(date == Date(-2006, 7, 6)); - date.add!"years"(9, AllowDayOverflow.no); - assert(date == Date(-1997, 7, 6)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 2, 28)); - } - - //Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5, AllowDayOverflow.no); - assert(date == Date(-1, 7, 6)); - date.add!"years"(5, AllowDayOverflow.no); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(5, AllowDayOverflow.no); - assert(date == Date(1, 7, 6)); - date.add!"years"(-5, AllowDayOverflow.no); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(4, 7, 6); - date.add!"years"(-8, AllowDayOverflow.no); - assert(date == Date(-4, 7, 6)); - date.add!"years"(8, AllowDayOverflow.no); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(8, AllowDayOverflow.no); - assert(date == Date(4, 7, 6)); - date.add!"years"(-8, AllowDayOverflow.no); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(-4, 2, 29); - date.add!"years"(5, AllowDayOverflow.no); - assert(date == Date(1, 2, 28)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, AllowDayOverflow.no); - assert(date == Date(-1, 2, 28)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); - assert(date == Date(6, 2, 28)); - } - } - - - //Shares documentation with "years" version. - ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if(units == "months") - { - auto years = months / 12; - months %= 12; - auto newMonth = _month + months; - - if(months < 0) - { - if(newMonth < 1) - { - newMonth += 12; - --years; - } - } - else if(newMonth > 12) - { - newMonth -= 12; - ++years; - } - - _year += years; - _month = cast(Month)newMonth; - - immutable currMaxDay = maxDay(_year, _month); - immutable overflow = _day - currMaxDay; - - if(overflow > 0) - { - if(allowOverflow == AllowDayOverflow.yes) - { - ++_month; - _day = cast(ubyte)overflow; - } - else - _day = cast(ubyte)currMaxDay; - } - - return this; - } - - //Test add!"months"() with AllowDayOverlow.yes - unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3); - assert(date == Date(1999, 10, 6)); - date.add!"months"(-4); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(6); - assert(date == Date(2000, 1, 6)); - date.add!"months"(-6); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(27); - assert(date == Date(2001, 10, 6)); - date.add!"months"(-28); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(1); - assert(date == Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1); - assert(date == Date(1999, 5, 1)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"months"(12); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"months"(12); - assert(date == Date(2001, 3, 1)); - } - - { - auto date = Date(1999, 7, 31); - date.add!"months"(1); - assert(date == Date(1999, 8, 31)); - date.add!"months"(1); - assert(date == Date(1999, 10, 1)); - } - - { - auto date = Date(1998, 8, 31); - date.add!"months"(13); - assert(date == Date(1999, 10, 1)); - date.add!"months"(-13); - assert(date == Date(1998, 9, 1)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(13); - assert(date == Date(1999, 1, 31)); - date.add!"months"(-13); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(14); - assert(date == Date(1999, 3, 3)); - date.add!"months"(-14); - assert(date == Date(1998, 1, 3)); - } - - { - auto date = Date(1998, 12, 31); - date.add!"months"(14); - assert(date == Date(2000, 3, 2)); - date.add!"months"(-14); - assert(date == Date(1999, 1, 2)); - } - - { - auto date = Date(1999, 12, 31); - date.add!"months"(14); - assert(date == Date(2001, 3, 3)); - date.add!"months"(-14); - assert(date == Date(2000, 1, 3)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3); - assert(date == Date(-1999, 10, 6)); - date.add!"months"(-4); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6); - assert(date == Date(-1998, 1, 6)); - date.add!"months"(-6); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27); - assert(date == Date(-2001, 4, 6)); - date.add!"months"(28); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1); - assert(date == Date(-1999, 7, 1)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1); - assert(date == Date(-1999, 5, 1)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12); - assert(date == Date(-2001, 3, 1)); - } - - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1); - assert(date == Date(-1999, 8, 31)); - date.add!"months"(1); - assert(date == Date(-1999, 10, 1)); - } - - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13); - assert(date == Date(-1997, 10, 1)); - date.add!"months"(-13); - assert(date == Date(-1998, 9, 1)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13); - assert(date == Date(-1995, 1, 31)); - date.add!"months"(-13); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14); - assert(date == Date(-1995, 3, 3)); - date.add!"months"(-14); - assert(date == Date(-1996, 1, 3)); - } - - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14); - assert(date == Date(-2000, 3, 2)); - date.add!"months"(-14); - assert(date == Date(-2001, 1, 2)); - } - - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14); - assert(date == Date(-1999, 3, 3)); - date.add!"months"(-14); - assert(date == Date(-2000, 1, 3)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1); - assert(date == Date(0, 12, 1)); - date.add!"months"(1); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.add!"months"(-48); - assert(date == Date(0, 1, 1)); - date.add!"months"(48); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-49); - assert(date == Date(0, 3, 2)); - date.add!"months"(49); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-85); - assert(date == Date(-3, 3, 3)); - date.add!"months"(85); - assert(date == Date(4, 4, 3)); - } - - { - auto date = Date(-3, 3, 31); - date.add!"months"(85).add!"months"(-83); - assert(date == Date(-3, 6, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"months"(3))); - static assert(!__traits(compiles, idate.add!"months"(3))); - } - - //Test add!"months"() with AllowDayOverlow.no - unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3, AllowDayOverflow.no); - assert(date == Date(1999, 10, 6)); - date.add!"months"(-4, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(6, AllowDayOverflow.no); - assert(date == Date(2000, 1, 6)); - date.add!"months"(-6, AllowDayOverflow.no); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(27, AllowDayOverflow.no); - assert(date == Date(2001, 10, 6)); - date.add!"months"(-28, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1, AllowDayOverflow.no); - assert(date == Date(1999, 4, 30)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"months"(12, AllowDayOverflow.no); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"months"(12, AllowDayOverflow.no); - assert(date == Date(2001, 2, 28)); - } - - { - auto date = Date(1999, 7, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 8, 31)); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 9, 30)); - } - - { - auto date = Date(1998, 8, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(1999, 9, 30)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1998, 8, 30)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(1999, 1, 31)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1997, 12, 28)); - } - - { - auto date = Date(1998, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(2000, 2, 29)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1998, 12, 29)); - } - - { - auto date = Date(1999, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(2001, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1999, 12, 28)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3, AllowDayOverflow.no); - assert(date == Date(-1999, 10, 6)); - date.add!"months"(-4, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6, AllowDayOverflow.no); - assert(date == Date(-1998, 1, 6)); - date.add!"months"(-6, AllowDayOverflow.no); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27, AllowDayOverflow.no); - assert(date == Date(-2001, 4, 6)); - date.add!"months"(28, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1, AllowDayOverflow.no); - assert(date == Date(-1999, 4, 30)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-2001, 2, 28)); - } - - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 31)); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 9, 30)); - } - - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1997, 9, 30)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1998, 8, 30)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1995, 1, 31)); - date.add!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(-1995, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 28)); - } - - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 29)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2002, 12, 29)); - } - - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - assert(date == Date(-1999, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2001, 12, 28)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1, AllowDayOverflow.no); - assert(date == Date(0, 12, 1)); - date.add!"months"(1, AllowDayOverflow.no); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.add!"months"(-48, AllowDayOverflow.no); - assert(date == Date(0, 1, 1)); - date.add!"months"(48, AllowDayOverflow.no); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-49, AllowDayOverflow.no); - assert(date == Date(0, 2, 29)); - date.add!"months"(49, AllowDayOverflow.no); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-85, AllowDayOverflow.no); - assert(date == Date(-3, 2, 28)); - date.add!"months"(85, AllowDayOverflow.no); - assert(date == Date(4, 3, 28)); - } - - { - auto date = Date(-3, 3, 31); - date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); - assert(date == Date(-3, 5, 30)); - } - } - - - /++ - Adds the given number of years or months to this $(LREF Date). A negative - number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF Date) 12 months gets - the exact same $(LREF Date). However, the days can still be affected due to - the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF Date). - allowOverflow = Whether the day should be allowed to overflow, - causing the month to increment. - +/ - ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if(units == "years") - { - return add!"years"(value, allowOverflow); - } - - /// - unittest - { - auto d1 = Date(2010, 1, 1); - d1.roll!"months"(1); - assert(d1 == Date(2010, 2, 1)); - - auto d2 = Date(2010, 1, 1); - d2.roll!"months"(-1); - assert(d2 == Date(2010, 12, 1)); - - auto d3 = Date(1999, 1, 29); - d3.roll!"months"(1); - assert(d3 == Date(1999, 3, 1)); - - auto d4 = Date(1999, 1, 29); - d4.roll!"months"(1, AllowDayOverflow.no); - assert(d4 == Date(1999, 2, 28)); - - auto d5 = Date(2000, 2, 29); - d5.roll!"years"(1); - assert(d5 == Date(2001, 3, 1)); - - auto d6 = Date(2000, 2, 29); - d6.roll!"years"(1, AllowDayOverflow.no); - assert(d6 == Date(2001, 2, 28)); - } - - unittest - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"years"(3))); - static assert(!__traits(compiles, idate.rolYears(3))); - } - - - //Shares documentation with "years" version. - ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if(units == "months") - { - months %= 12; - auto newMonth = _month + months; - - if(months < 0) - { - if(newMonth < 1) - newMonth += 12; - } - else - { - if(newMonth > 12) - newMonth -= 12; - } - - _month = cast(Month)newMonth; - - immutable currMaxDay = maxDay(_year, _month); - immutable overflow = _day - currMaxDay; - - if(overflow > 0) - { - if(allowOverflow == AllowDayOverflow.yes) - { - ++_month; - _day = cast(ubyte)overflow; - } - else - _day = cast(ubyte)currMaxDay; - } - - return this; - } - - //Test roll!"months"() with AllowDayOverlow.yes - unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-4); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6); - assert(date == Date(1999, 1, 6)); - date.roll!"months"(-6); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-28); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1); - assert(date == Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1); - assert(date == Date(1999, 5, 1)); - } - - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1); - assert(date == Date(1999, 8, 31)); - date.roll!"months"(1); - assert(date == Date(1999, 10, 1)); - } - - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13); - assert(date == Date(1998, 10, 1)); - date.roll!"months"(-13); - assert(date == Date(1998, 9, 1)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13); - assert(date == Date(1997, 1, 31)); - date.roll!"months"(-13); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14); - assert(date == Date(1997, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1997, 1, 3)); - } - - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14); - assert(date == Date(1998, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1998, 1, 3)); - } - - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14); - assert(date == Date(1999, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1999, 1, 3)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3); - assert(date == Date(-1999, 10, 6)); - date.roll!"months"(-4); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6); - assert(date == Date(-1999, 1, 6)); - date.roll!"months"(-6); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27); - assert(date == Date(-1999, 4, 6)); - date.roll!"months"(28); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1); - assert(date == Date(-1999, 7, 1)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1); - assert(date == Date(-1999, 5, 1)); - } - - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1); - assert(date == Date(-1999, 8, 31)); - date.roll!"months"(1); - assert(date == Date(-1999, 10, 1)); - } - - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13); - assert(date == Date(-1998, 10, 1)); - date.roll!"months"(-13); - assert(date == Date(-1998, 9, 1)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13); - assert(date == Date(-1997, 1, 31)); - date.roll!"months"(-13); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14); - assert(date == Date(-1997, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-1997, 1, 3)); - } - - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14); - assert(date == Date(-2002, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-2002, 1, 3)); - } - - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14); - assert(date == Date(-2001, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-2001, 1, 3)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1); - assert(date == Date(1, 12, 1)); - date.roll!"months"(1); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48); - assert(date == Date(4, 1, 1)); - date.roll!"months"(48); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49); - assert(date == Date(4, 3, 2)); - date.roll!"months"(49); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85); - assert(date == Date(4, 3, 2)); - date.roll!"months"(85); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1); - assert(date == Date(-1, 12, 1)); - date.roll!"months"(1); - assert(date == Date(-1, 1, 1)); - } - - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48); - assert(date == Date(-4, 1, 1)); - date.roll!"months"(48); - assert(date == Date(-4, 1, 1)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49); - assert(date == Date(-4, 3, 2)); - date.roll!"months"(49); - assert(date == Date(-4, 4, 2)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85); - assert(date == Date(-4, 3, 2)); - date.roll!"months"(85); - assert(date == Date(-4, 4, 2)); - } - - { - auto date = Date(-3, 3, 31); - date.roll!"months"(85).roll!"months"(-83); - assert(date == Date(-3, 6, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"months"(3))); - static assert(!__traits(compiles, idate.roll!"months"(3))); - } - - //Test roll!"months"() with AllowDayOverlow.no - unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3, AllowDayOverflow.no); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-4, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6, AllowDayOverflow.no); - assert(date == Date(1999, 1, 6)); - date.roll!"months"(-6, AllowDayOverflow.no); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27, AllowDayOverflow.no); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-28, AllowDayOverflow.no); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(1999, 4, 30)); - } - - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12, AllowDayOverflow.no); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 8, 31)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1999, 9, 30)); - } - - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(1998, 9, 30)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1998, 8, 30)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(1997, 1, 31)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(1997, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1997, 12, 28)); - } - - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(1998, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1998, 12, 28)); - } - - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(1999, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(1999, 12, 28)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3, AllowDayOverflow.no); - assert(date == Date(-1999, 10, 6)); - date.roll!"months"(-4, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6, AllowDayOverflow.no); - assert(date == Date(-1999, 1, 6)); - date.roll!"months"(-6, AllowDayOverflow.no); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27, AllowDayOverflow.no); - assert(date == Date(-1999, 4, 6)); - date.roll!"months"(28, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(-1999, 4, 30)); - } - - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12, AllowDayOverflow.no); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 8, 31)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1999, 9, 30)); - } - - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1998, 9, 30)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1998, 8, 30)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13, AllowDayOverflow.no); - assert(date == Date(-1997, 1, 31)); - date.roll!"months"(-13, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(-1997, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-1997, 12, 28)); - } - - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(-2002, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2002, 12, 28)); - } - - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - assert(date == Date(-2001, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - assert(date == Date(-2001, 12, 28)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(1, 12, 1)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48, AllowDayOverflow.no); - assert(date == Date(4, 1, 1)); - date.roll!"months"(48, AllowDayOverflow.no); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49, AllowDayOverflow.no); - assert(date == Date(4, 2, 29)); - date.roll!"months"(49, AllowDayOverflow.no); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85, AllowDayOverflow.no); - assert(date == Date(4, 2, 29)); - date.roll!"months"(85, AllowDayOverflow.no); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1, AllowDayOverflow.no); - assert(date == Date(-1, 12, 1)); - date.roll!"months"(1, AllowDayOverflow.no); - assert(date == Date(-1, 1, 1)); - } - - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48, AllowDayOverflow.no); - assert(date == Date(-4, 1, 1)); - date.roll!"months"(48, AllowDayOverflow.no); - assert(date == Date(-4, 1, 1)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49, AllowDayOverflow.no); - assert(date == Date(-4, 2, 29)); - date.roll!"months"(49, AllowDayOverflow.no); - assert(date == Date(-4, 3, 29)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85, AllowDayOverflow.no); - assert(date == Date(-4, 2, 29)); - date.roll!"months"(85, AllowDayOverflow.no); - assert(date == Date(-4, 3, 29)); - } - - { - auto date = Date(-3, 3, 31); - date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); - assert(date == Date(-3, 5, 30)); - } - } - - - /++ - Adds the given number of units to this $(LREF Date). A negative number will - subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF Date) one - year's worth of days gets the exact same $(LREF Date). - - The only accepted units are $(D "days"). - - Params: - units = The units to add. Must be $(D "days"). - days = The number of days to add to this $(LREF Date). - +/ - ref Date roll(string units)(long days) @safe pure nothrow - if(units == "days") - { - immutable limit = maxDay(_year, _month); - days %= limit; - auto newDay = _day + days; - - if(days < 0) - { - if(newDay < 1) - newDay += limit; - } - else if(newDay > limit) - newDay -= limit; - - _day = cast(ubyte)newDay; - return this; - } - - /// - unittest - { - auto d = Date(2010, 1, 1); - d.roll!"days"(1); - assert(d == Date(2010, 1, 2)); - d.roll!"days"(365); - assert(d == Date(2010, 1, 26)); - d.roll!"days"(-32); - assert(d == Date(2010, 1, 25)); - } - - unittest - { - //Test A.D. - { - auto date = Date(1999, 2, 28); - date.roll!"days"(1); - assert(date == Date(1999, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date.roll!"days"(1); - assert(date == Date(2000, 2, 29)); - date.roll!"days"(1); - assert(date == Date(2000, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date.roll!"days"(1); - assert(date == Date(1999, 6, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"days"(1); - assert(date == Date(1999, 7, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 1, 1); - date.roll!"days"(-1); - assert(date == Date(1999, 1, 31)); - date.roll!"days"(1); - assert(date == Date(1999, 1, 1)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"days"(9); - assert(date == Date(1999, 7, 15)); - date.roll!"days"(-11); - assert(date == Date(1999, 7, 4)); - date.roll!"days"(30); - assert(date == Date(1999, 7, 3)); - date.roll!"days"(-3); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"days"(365); - assert(date == Date(1999, 7, 30)); - date.roll!"days"(-365); - assert(date == Date(1999, 7, 6)); - date.roll!"days"(366); - assert(date == Date(1999, 7, 31)); - date.roll!"days"(730); - assert(date == Date(1999, 7, 17)); - date.roll!"days"(-1096); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 2, 6); - date.roll!"days"(365); - assert(date == Date(1999, 2, 7)); - date.roll!"days"(-365); - assert(date == Date(1999, 2, 6)); - date.roll!"days"(366); - assert(date == Date(1999, 2, 8)); - date.roll!"days"(730); - assert(date == Date(1999, 2, 10)); - date.roll!"days"(-1096); - assert(date == Date(1999, 2, 6)); - } - - //Test B.C. - { - auto date = Date(-1999, 2, 28); - date.roll!"days"(1); - assert(date == Date(-1999, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 28); - date.roll!"days"(1); - assert(date == Date(-2000, 2, 29)); - date.roll!"days"(1); - assert(date == Date(-2000, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 6, 30); - date.roll!"days"(1); - assert(date == Date(-1999, 6, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"days"(1); - assert(date == Date(-1999, 7, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 1, 1); - date.roll!"days"(-1); - assert(date == Date(-1999, 1, 31)); - date.roll!"days"(1); - assert(date == Date(-1999, 1, 1)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(9); - assert(date == Date(-1999, 7, 15)); - date.roll!"days"(-11); - assert(date == Date(-1999, 7, 4)); - date.roll!"days"(30); - assert(date == Date(-1999, 7, 3)); - date.roll!"days"(-3); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(365); - assert(date == Date(-1999, 7, 30)); - date.roll!"days"(-365); - assert(date == Date(-1999, 7, 6)); - date.roll!"days"(366); - assert(date == Date(-1999, 7, 31)); - date.roll!"days"(730); - assert(date == Date(-1999, 7, 17)); - date.roll!"days"(-1096); - assert(date == Date(-1999, 7, 6)); - } - - //Test Both - { - auto date = Date(1, 7, 6); - date.roll!"days"(-365); - assert(date == Date(1, 7, 13)); - date.roll!"days"(365); - assert(date == Date(1, 7, 6)); - date.roll!"days"(-731); - assert(date == Date(1, 7, 19)); - date.roll!"days"(730); - assert(date == Date(1, 7, 5)); - } - - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365); - assert(date == Date(0, 7, 13)); - date.roll!"days"(365); - assert(date == Date(0, 7, 6)); - date.roll!"days"(-731); - assert(date == Date(0, 7, 19)); - date.roll!"days"(730); - assert(date == Date(0, 7, 5)); - } - - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); - assert(date == Date(0, 7, 8)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"days"(12))); - static assert(!__traits(compiles, idate.roll!"days"(12))); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF Date). - - The legal types of arithmetic for Date using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD +) $(TD duration) $(TD -->) $(TD Date)) - $(TR $(TD Date) $(TD -) $(TD duration) $(TD -->) $(TD Date)) - ) - - Params: - duration = The duration to add to or subtract from this $(LREF Date). - +/ - Date opBinary(string op, D)(in D duration) @safe const pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - Date retval = this; - - static if(is(Unqual!D == Duration)) - immutable days = duration.total!"days"; - else static if(is(Unqual!D == TickDuration)) - immutable days = convert!("hnsecs", "days")(duration.hnsecs); - - mixin(format("return retval._addDays(%sdays);", op)); - } - - unittest - { - auto date = Date(1999, 7, 6); - - assert(date + dur!"weeks"(7) == Date(1999, 8, 24)); - assert(date + dur!"weeks"(-7) == Date(1999, 5, 18)); - assert(date + dur!"days"(7) == Date(1999, 7, 13)); - assert(date + dur!"days"(-7) == Date(1999, 6, 29)); - - assert(date + dur!"hours"(24) == Date(1999, 7, 7)); - assert(date + dur!"hours"(-24) == Date(1999, 7, 5)); - assert(date + dur!"minutes"(1440) == Date(1999, 7, 7)); - assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5)); - assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7)); - assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5)); - assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); - assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); - assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); - assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(date + TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(date + TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - } - - assert(date - dur!"weeks"(-7) == Date(1999, 8, 24)); - assert(date - dur!"weeks"(7) == Date(1999, 5, 18)); - assert(date - dur!"days"(-7) == Date(1999, 7, 13)); - assert(date - dur!"days"(7) == Date(1999, 6, 29)); - - assert(date - dur!"hours"(-24) == Date(1999, 7, 7)); - assert(date - dur!"hours"(24) == Date(1999, 7, 5)); - assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7)); - assert(date - dur!"minutes"(1440) == Date(1999, 7, 5)); - assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7)); - assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5)); - assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); - assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); - assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); - assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(date - TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(date - TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - } - - auto duration = dur!"days"(12); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date + duration)); - static assert(__traits(compiles, cdate + duration)); - static assert(__traits(compiles, idate + duration)); - - static assert(__traits(compiles, date - duration)); - static assert(__traits(compiles, cdate - duration)); - static assert(__traits(compiles, idate - duration)); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF Date), as well as assigning the result to this $(LREF Date). - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD +) $(TD duration) $(TD -->) $(TD Date)) - $(TR $(TD Date) $(TD -) $(TD duration) $(TD -->) $(TD Date)) - ) - - Params: - duration = The duration to add to or subtract from this $(LREF Date). - +/ - ref Date opOpAssign(string op, D)(in D duration) @safe pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - static if(is(Unqual!D == Duration)) - immutable days = duration.total!"days"; - else static if(is(Unqual!D == TickDuration)) - immutable days = convert!("hnsecs", "days")(duration.hnsecs); - - mixin(format("return _addDays(%sdays);", op)); - } - - unittest - { - assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24)); - assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18)); - assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13)); - assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29)); - - assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); - - assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24)); - assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18)); - assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13)); - assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29)); - - assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); - - { - auto date = Date(0, 1, 31); - (date += dur!"days"(507)) += dur!"days"(-2); - assert(date == Date(1, 6, 19)); - } - - auto duration = dur!"days"(12); - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date += duration)); - static assert(!__traits(compiles, cdate += duration)); - static assert(!__traits(compiles, idate += duration)); - - static assert(__traits(compiles, date -= duration)); - static assert(!__traits(compiles, cdate -= duration)); - static assert(!__traits(compiles, idate -= duration)); - } - - - /++ - Gives the difference between two $(LREF Date)s. - - The legal types of arithmetic for Date using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in Date rhs) @safe const pure nothrow - if(op == "-") - { - return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); - } - - unittest - { - auto date = Date(1999, 7, 6); - - assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365)); - assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365)); - assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31)); - assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31)); - assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1)); - assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date - date)); - static assert(__traits(compiles, cdate - date)); - static assert(__traits(compiles, idate - date)); - - static assert(__traits(compiles, date - cdate)); - static assert(__traits(compiles, cdate - cdate)); - static assert(__traits(compiles, idate - cdate)); - - static assert(__traits(compiles, date - idate)); - static assert(__traits(compiles, cdate - idate)); - static assert(__traits(compiles, idate - idate)); - } - - - /++ - Returns the difference between the two $(LREF Date)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(CXREF time, Duration) - that results. Because converting between months and smaller - units requires a specific date (which $(CXREF time, Duration)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. - - Note that the number of days in the months or how far into the month - either $(LREF Date) is is irrelevant. It is the difference in the month - property combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF Date) to subtract from this one. - +/ - int diffMonths(in Date rhs) @safe const pure nothrow - { - immutable yearDiff = _year - rhs._year; - immutable monthDiff = _month - rhs._month; - - return yearDiff * 12 + monthDiff; - } - - /// - unittest - { - assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); - assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); - assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); - assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); - } - - unittest - { - auto date = Date(1999, 7, 6); - - //Test A.D. - assert(date.diffMonths(Date(1998, 6, 5)) == 13); - assert(date.diffMonths(Date(1998, 7, 5)) == 12); - assert(date.diffMonths(Date(1998, 8, 5)) == 11); - assert(date.diffMonths(Date(1998, 9, 5)) == 10); - assert(date.diffMonths(Date(1998, 10, 5)) == 9); - assert(date.diffMonths(Date(1998, 11, 5)) == 8); - assert(date.diffMonths(Date(1998, 12, 5)) == 7); - assert(date.diffMonths(Date(1999, 1, 5)) == 6); - assert(date.diffMonths(Date(1999, 2, 6)) == 5); - assert(date.diffMonths(Date(1999, 3, 6)) == 4); - assert(date.diffMonths(Date(1999, 4, 6)) == 3); - assert(date.diffMonths(Date(1999, 5, 6)) == 2); - assert(date.diffMonths(Date(1999, 6, 6)) == 1); - assert(date.diffMonths(date) == 0); - assert(date.diffMonths(Date(1999, 8, 6)) == -1); - assert(date.diffMonths(Date(1999, 9, 6)) == -2); - assert(date.diffMonths(Date(1999, 10, 6)) == -3); - assert(date.diffMonths(Date(1999, 11, 6)) == -4); - assert(date.diffMonths(Date(1999, 12, 6)) == -5); - assert(date.diffMonths(Date(2000, 1, 6)) == -6); - assert(date.diffMonths(Date(2000, 2, 6)) == -7); - assert(date.diffMonths(Date(2000, 3, 6)) == -8); - assert(date.diffMonths(Date(2000, 4, 6)) == -9); - assert(date.diffMonths(Date(2000, 5, 6)) == -10); - assert(date.diffMonths(Date(2000, 6, 6)) == -11); - assert(date.diffMonths(Date(2000, 7, 6)) == -12); - assert(date.diffMonths(Date(2000, 8, 6)) == -13); - - assert(Date(1998, 6, 5).diffMonths(date) == -13); - assert(Date(1998, 7, 5).diffMonths(date) == -12); - assert(Date(1998, 8, 5).diffMonths(date) == -11); - assert(Date(1998, 9, 5).diffMonths(date) == -10); - assert(Date(1998, 10, 5).diffMonths(date) == -9); - assert(Date(1998, 11, 5).diffMonths(date) == -8); - assert(Date(1998, 12, 5).diffMonths(date) == -7); - assert(Date(1999, 1, 5).diffMonths(date) == -6); - assert(Date(1999, 2, 6).diffMonths(date) == -5); - assert(Date(1999, 3, 6).diffMonths(date) == -4); - assert(Date(1999, 4, 6).diffMonths(date) == -3); - assert(Date(1999, 5, 6).diffMonths(date) == -2); - assert(Date(1999, 6, 6).diffMonths(date) == -1); - assert(Date(1999, 8, 6).diffMonths(date) == 1); - assert(Date(1999, 9, 6).diffMonths(date) == 2); - assert(Date(1999, 10, 6).diffMonths(date) == 3); - assert(Date(1999, 11, 6).diffMonths(date) == 4); - assert(Date(1999, 12, 6).diffMonths(date) == 5); - assert(Date(2000, 1, 6).diffMonths(date) == 6); - assert(Date(2000, 2, 6).diffMonths(date) == 7); - assert(Date(2000, 3, 6).diffMonths(date) == 8); - assert(Date(2000, 4, 6).diffMonths(date) == 9); - assert(Date(2000, 5, 6).diffMonths(date) == 10); - assert(Date(2000, 6, 6).diffMonths(date) == 11); - assert(Date(2000, 7, 6).diffMonths(date) == 12); - assert(Date(2000, 8, 6).diffMonths(date) == 13); - - assert(date.diffMonths(Date(1999, 6, 30)) == 1); - assert(date.diffMonths(Date(1999, 7, 1)) == 0); - assert(date.diffMonths(Date(1999, 7, 6)) == 0); - assert(date.diffMonths(Date(1999, 7, 11)) == 0); - assert(date.diffMonths(Date(1999, 7, 16)) == 0); - assert(date.diffMonths(Date(1999, 7, 21)) == 0); - assert(date.diffMonths(Date(1999, 7, 26)) == 0); - assert(date.diffMonths(Date(1999, 7, 31)) == 0); - assert(date.diffMonths(Date(1999, 8, 1)) == -1); - - assert(date.diffMonths(Date(1990, 6, 30)) == 109); - assert(date.diffMonths(Date(1990, 7, 1)) == 108); - assert(date.diffMonths(Date(1990, 7, 6)) == 108); - assert(date.diffMonths(Date(1990, 7, 11)) == 108); - assert(date.diffMonths(Date(1990, 7, 16)) == 108); - assert(date.diffMonths(Date(1990, 7, 21)) == 108); - assert(date.diffMonths(Date(1990, 7, 26)) == 108); - assert(date.diffMonths(Date(1990, 7, 31)) == 108); - assert(date.diffMonths(Date(1990, 8, 1)) == 107); - - assert(Date(1999, 6, 30).diffMonths(date) == -1); - assert(Date(1999, 7, 1).diffMonths(date) == 0); - assert(Date(1999, 7, 6).diffMonths(date) == 0); - assert(Date(1999, 7, 11).diffMonths(date) == 0); - assert(Date(1999, 7, 16).diffMonths(date) == 0); - assert(Date(1999, 7, 21).diffMonths(date) == 0); - assert(Date(1999, 7, 26).diffMonths(date) == 0); - assert(Date(1999, 7, 31).diffMonths(date) == 0); - assert(Date(1999, 8, 1).diffMonths(date) == 1); - - assert(Date(1990, 6, 30).diffMonths(date) == -109); - assert(Date(1990, 7, 1).diffMonths(date) == -108); - assert(Date(1990, 7, 6).diffMonths(date) == -108); - assert(Date(1990, 7, 11).diffMonths(date) == -108); - assert(Date(1990, 7, 16).diffMonths(date) == -108); - assert(Date(1990, 7, 21).diffMonths(date) == -108); - assert(Date(1990, 7, 26).diffMonths(date) == -108); - assert(Date(1990, 7, 31).diffMonths(date) == -108); - assert(Date(1990, 8, 1).diffMonths(date) == -107); - - //Test B.C. - auto dateBC = Date(-1999, 7, 6); - - assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13); - assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12); - assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11); - assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10); - assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9); - assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8); - assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7); - assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6); - assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5); - assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4); - assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3); - assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2); - assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1); - assert(dateBC.diffMonths(dateBC) == 0); - assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1); - assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2); - assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3); - assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4); - assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5); - assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6); - assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7); - assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8); - assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9); - assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10); - assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11); - assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12); - assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13); - - assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13); - assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12); - assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11); - assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10); - assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9); - assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8); - assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7); - assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6); - assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5); - assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4); - assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3); - assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2); - assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1); - assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1); - assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2); - assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3); - assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4); - assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5); - assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6); - assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7); - assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8); - assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9); - assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10); - assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11); - assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12); - assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13); - - assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1); - assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0); - assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1); - - assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109); - assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108); - assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107); - - assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1); - assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0); - assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1); - - assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109); - assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108); - assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107); - - //Test Both - assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94); - assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.diffMonths(date))); - static assert(__traits(compiles, cdate.diffMonths(date))); - static assert(__traits(compiles, idate.diffMonths(date))); - - static assert(__traits(compiles, date.diffMonths(cdate))); - static assert(__traits(compiles, cdate.diffMonths(cdate))); - static assert(__traits(compiles, idate.diffMonths(cdate))); - - static assert(__traits(compiles, date.diffMonths(idate))); - static assert(__traits(compiles, cdate.diffMonths(idate))); - static assert(__traits(compiles, idate.diffMonths(idate))); - } - - - /++ - Whether this $(LREF Date) is in a leap year. - +/ - @property bool isLeapYear() @safe const pure nothrow - { - return yearIsLeapYear(_year); - } - - unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, date.isLeapYear = true)); - static assert(!__traits(compiles, cdate.isLeapYear = true)); - static assert(!__traits(compiles, idate.isLeapYear = true)); - } - - - /++ - Day of the week this $(LREF Date) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const pure nothrow - { - return getDayOfWeek(dayOfGregorianCal); - } - - unittest - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.dayOfWeek == DayOfWeek.sun)); - static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); - static assert(__traits(compiles, idate.dayOfWeek == DayOfWeek.sun)); - static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); - } - - - /++ - Day of the year this $(LREF Date) is on. - +/ - @property ushort dayOfYear() @safe const pure nothrow - { - if (_month >= Month.jan && _month <= Month.dec) - { - immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; - auto monthIndex = _month - Month.jan; - - return cast(ushort)(lastDay[monthIndex] + _day); - } - assert(0, "Invalid month."); - } - - /// - unittest - { - assert(Date(1999, 1, 1).dayOfYear == 1); - assert(Date(1999, 12, 31).dayOfYear == 365); - assert(Date(2000, 12, 31).dayOfYear == 366); - } - - unittest - { - import std.range; - - foreach(year; filter!((a){return !yearIsLeapYear(a);}) - (chain(testYearsBC, testYearsAD))) - { - foreach(doy; testDaysOfYear) - { - assert(Date(year, doy.md.month, doy.md.day).dayOfYear == - doy.day); - } - } - - foreach(year; filter!((a){return yearIsLeapYear(a);}) - (chain(testYearsBC, testYearsAD))) - { - foreach(doy; testDaysOfLeapYear) - { - assert(Date(year, doy.md.month, doy.md.day).dayOfYear == - doy.day); - } - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.dayOfYear == 187)); - static assert(__traits(compiles, idate.dayOfYear == 187)); - } - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF Date) is on. - - Throws: - $(LREF DateTimeException) if the given day is an invalid day of the - year. - +/ - @property void dayOfYear(int day) @safe pure - { - immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; - - if(day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear) ) - throw new DateTimeException("Invalid day of the year."); - - foreach (i; 1..lastDay.length) - { - if (day <= lastDay[i]) - { - _month = cast(Month)(cast(int)Month.jan + i - 1); - _day = cast(ubyte)(day - lastDay[i - 1]); - return; - } - } - assert(0, "Invalid day of the year."); - } - - unittest - { - static void test(Date date, int day, MonthDay expected, size_t line = __LINE__) - { - date.dayOfYear = day; - assert(date.month == expected.month); - assert(date.day == expected.day); - } - - foreach(doy; testDaysOfYear) - { - test(Date(1999, 1, 1), doy.day, doy.md); - test(Date(-1, 1, 1), doy.day, doy.md); - } - - foreach(doy; testDaysOfLeapYear) - { - test(Date(2000, 1, 1), doy.day, doy.md); - test(Date(-4, 1, 1), doy.day, doy.md); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.dayOfYear = 187)); - static assert(!__traits(compiles, idate.dayOfYear = 187)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - +/ - @property int dayOfGregorianCal() @safe const pure nothrow - { - if(isAD) - { - if(_year == 1) - return dayOfYear; - - int years = _year - 1; - auto days = (years / 400) * daysIn400Years; - years %= 400; - - days += (years / 100) * daysIn100Years; - years %= 100; - - days += (years / 4) * daysIn4Years; - years %= 4; - - days += years * daysInYear; - - days += dayOfYear; - - return days; - } - else if(_year == 0) - return dayOfYear - daysInLeapYear; - else - { - int years = _year; - auto days = (years / 400) * daysIn400Years; - years %= 400; - - days += (years / 100) * daysIn100Years; - years %= 100; - - days += (years / 4) * daysIn4Years; - years %= 4; - - if(years < 0) - { - days -= daysInLeapYear; - ++years; - - days += years * daysInYear; - - days -= daysInYear - dayOfYear; - } - else - days -= daysInLeapYear - dayOfYear; - - return days; - } - } - - /// - unittest - { - assert(Date(1, 1, 1).dayOfGregorianCal == 1); - assert(Date(1, 12, 31).dayOfGregorianCal == 365); - assert(Date(2, 1, 1).dayOfGregorianCal == 366); - - assert(Date(0, 12, 31).dayOfGregorianCal == 0); - assert(Date(0, 1, 1).dayOfGregorianCal == -365); - assert(Date(-1, 12, 31).dayOfGregorianCal == -366); - - assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); - assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); - } - - unittest - { - import std.range; - - foreach(gd; chain(testGregDaysBC, testGregDaysAD)) - assert(gd.date.dayOfGregorianCal == gd.day); - - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.dayOfGregorianCal)); - static assert(__traits(compiles, cdate.dayOfGregorianCal)); - static assert(__traits(compiles, idate.dayOfGregorianCal)); - } - - /++ - The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - - Params: - day = The day of the Gregorian Calendar to set this $(LREF Date) to. - +/ - @property void dayOfGregorianCal(int day) @safe pure nothrow - { - this = Date(day); - } - - /// - unittest - { - auto date = Date.init; - date.dayOfGregorianCal = 1; - assert(date == Date(1, 1, 1)); - - date.dayOfGregorianCal = 365; - assert(date == Date(1, 12, 31)); - - date.dayOfGregorianCal = 366; - assert(date == Date(2, 1, 1)); - - date.dayOfGregorianCal = 0; - assert(date == Date(0, 12, 31)); - - date.dayOfGregorianCal = -365; - assert(date == Date(-0, 1, 1)); - - date.dayOfGregorianCal = -366; - assert(date == Date(-1, 12, 31)); - - date.dayOfGregorianCal = 730_120; - assert(date == Date(2000, 1, 1)); - - date.dayOfGregorianCal = 734_137; - assert(date == Date(2010, 12, 31)); - } - - unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.dayOfGregorianCal = 187)); - static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); - static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF Date) is in. - - See_Also: - $(WEB en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() @safe const pure nothrow - { - immutable weekday = dayOfWeek; - immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; - immutable week = (dayOfYear - adjustedWeekday + 10) / 7; - - try - { - if(week == 53) - { - switch(Date(_year + 1, 1, 1).dayOfWeek) - { - case DayOfWeek.mon: - case DayOfWeek.tue: - case DayOfWeek.wed: - case DayOfWeek.thu: - return 1; - case DayOfWeek.fri: - case DayOfWeek.sat: - case DayOfWeek.sun: - return 53; - default: - assert(0, "Invalid ISO Week"); - } - } - else if(week > 0) - return cast(ubyte)week; - else - return Date(_year - 1, 12, 31).isoWeek; - } - catch(Exception e) - assert(0, "Date's constructor threw."); - } - - unittest - { - //Test A.D. - assert(Date(2009, 12, 28).isoWeek == 53); - assert(Date(2009, 12, 29).isoWeek == 53); - assert(Date(2009, 12, 30).isoWeek == 53); - assert(Date(2009, 12, 31).isoWeek == 53); - assert(Date(2010, 1, 1).isoWeek == 53); - assert(Date(2010, 1, 2).isoWeek == 53); - assert(Date(2010, 1, 3).isoWeek == 53); - assert(Date(2010, 1, 4).isoWeek == 1); - assert(Date(2010, 1, 5).isoWeek == 1); - assert(Date(2010, 1, 6).isoWeek == 1); - assert(Date(2010, 1, 7).isoWeek == 1); - assert(Date(2010, 1, 8).isoWeek == 1); - assert(Date(2010, 1, 9).isoWeek == 1); - assert(Date(2010, 1, 10).isoWeek == 1); - assert(Date(2010, 1, 11).isoWeek == 2); - assert(Date(2010, 12, 31).isoWeek == 52); - - assert(Date(2004, 12, 26).isoWeek == 52); - assert(Date(2004, 12, 27).isoWeek == 53); - assert(Date(2004, 12, 28).isoWeek == 53); - assert(Date(2004, 12, 29).isoWeek == 53); - assert(Date(2004, 12, 30).isoWeek == 53); - assert(Date(2004, 12, 31).isoWeek == 53); - assert(Date(2005, 1, 1).isoWeek == 53); - assert(Date(2005, 1, 2).isoWeek == 53); - - assert(Date(2005, 12, 31).isoWeek == 52); - assert(Date(2007, 1, 1).isoWeek == 1); - - assert(Date(2007, 12, 30).isoWeek == 52); - assert(Date(2007, 12, 31).isoWeek == 1); - assert(Date(2008, 1, 1).isoWeek == 1); - - assert(Date(2008, 12, 28).isoWeek == 52); - assert(Date(2008, 12, 29).isoWeek == 1); - assert(Date(2008, 12, 30).isoWeek == 1); - assert(Date(2008, 12, 31).isoWeek == 1); - assert(Date(2009, 1, 1).isoWeek == 1); - assert(Date(2009, 1, 2).isoWeek == 1); - assert(Date(2009, 1, 3).isoWeek == 1); - assert(Date(2009, 1, 4).isoWeek == 1); - - //Test B.C. - //The algorithm should work identically for both A.D. and B.C. since - //it doesn't really take the year into account, so B.C. testing - //probably isn't really needed. - assert(Date(0, 12, 31).isoWeek == 52); - assert(Date(0, 1, 4).isoWeek == 1); - assert(Date(0, 1, 1).isoWeek == 52); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.isoWeek == 3)); - static assert(!__traits(compiles, cdate.isoWeek = 3)); - static assert(__traits(compiles, idate.isoWeek == 3)); - static assert(!__traits(compiles, idate.isoWeek = 3)); - } - - - /++ - $(LREF Date) for the last day in the month that this $(LREF Date) is in. - +/ - @property Date endOfMonth() @safe const pure nothrow - { - try - return Date(_year, _month, maxDay(_year, _month)); - catch(Exception e) - assert(0, "Date's constructor threw."); - } - - /// - unittest - { - assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); - assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); - } - - unittest - { - //Test A.D. - assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); - assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); - assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); - assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); - assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); - assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); - assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); - assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); - assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); - assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); - assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); - - //Test B.C. - assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); - assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); - assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); - assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); - assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); - assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); - assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); - assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); - assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); - assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); - assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); - assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); - assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); - static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); - } - - - /++ - The last day in the month that this $(LREF Date) is in. - +/ - @property ubyte daysInMonth() @safe const pure nothrow - { - return maxDay(_year, _month); - } - - /// - unittest - { - assert(Date(1999, 1, 6).daysInMonth == 31); - assert(Date(1999, 2, 7).daysInMonth == 28); - assert(Date(2000, 2, 7).daysInMonth == 29); - assert(Date(2000, 6, 4).daysInMonth == 30); - } - - unittest - { - //Test A.D. - assert(Date(1999, 1, 1).daysInMonth == 31); - assert(Date(1999, 2, 1).daysInMonth == 28); - assert(Date(2000, 2, 1).daysInMonth == 29); - assert(Date(1999, 3, 1).daysInMonth == 31); - assert(Date(1999, 4, 1).daysInMonth == 30); - assert(Date(1999, 5, 1).daysInMonth == 31); - assert(Date(1999, 6, 1).daysInMonth == 30); - assert(Date(1999, 7, 1).daysInMonth == 31); - assert(Date(1999, 8, 1).daysInMonth == 31); - assert(Date(1999, 9, 1).daysInMonth == 30); - assert(Date(1999, 10, 1).daysInMonth == 31); - assert(Date(1999, 11, 1).daysInMonth == 30); - assert(Date(1999, 12, 1).daysInMonth == 31); - - //Test B.C. - assert(Date(-1999, 1, 1).daysInMonth == 31); - assert(Date(-1999, 2, 1).daysInMonth == 28); - assert(Date(-2000, 2, 1).daysInMonth == 29); - assert(Date(-1999, 3, 1).daysInMonth == 31); - assert(Date(-1999, 4, 1).daysInMonth == 30); - assert(Date(-1999, 5, 1).daysInMonth == 31); - assert(Date(-1999, 6, 1).daysInMonth == 30); - assert(Date(-1999, 7, 1).daysInMonth == 31); - assert(Date(-1999, 8, 1).daysInMonth == 31); - assert(Date(-1999, 9, 1).daysInMonth == 30); - assert(Date(-1999, 10, 1).daysInMonth == 31); - assert(Date(-1999, 11, 1).daysInMonth == 30); - assert(Date(-1999, 12, 1).daysInMonth == 31); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.daysInMonth = 30)); - static assert(!__traits(compiles, idate.daysInMonth = 30)); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const pure nothrow - { - return _year > 0; - } - - /// - unittest - { - assert(Date(1, 1, 1).isAD); - assert(Date(2010, 12, 31).isAD); - assert(!Date(0, 12, 31).isAD); - assert(!Date(-2010, 1, 1).isAD); - } - - unittest - { - assert(Date(2010, 7, 4).isAD); - assert(Date(1, 1, 1).isAD); - assert(!Date(0, 1, 1).isAD); - assert(!Date(-1, 1, 1).isAD); - assert(!Date(-2010, 7, 4).isAD); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.isAD)); - static assert(__traits(compiles, idate.isAD)); - } - - - /++ - The $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for this $(LREF Date) at noon (since the Julian day changes - at noon). - +/ - @property long julianDay() @safe const pure nothrow - { - return dayOfGregorianCal + 1_721_425; - } - - unittest - { - assert(Date(-4713, 11, 24).julianDay == 0); - assert(Date(0, 12, 31).julianDay == 1_721_425); - assert(Date(1, 1, 1).julianDay == 1_721_426); - assert(Date(1582, 10, 15).julianDay == 2_299_161); - assert(Date(1858, 11, 17).julianDay == 2_400_001); - assert(Date(1982, 1, 4).julianDay == 2_444_974); - assert(Date(1996, 3, 31).julianDay == 2_450_174); - assert(Date(2010, 8, 24).julianDay == 2_455_433); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.julianDay)); - static assert(__traits(compiles, idate.julianDay)); - } - - - /++ - The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified - Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const pure nothrow - { - return julianDay - 2_400_001; - } - - unittest - { - assert(Date(1858, 11, 17).modJulianDay == 0); - assert(Date(2010, 8, 24).modJulianDay == 55_432); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.modJulianDay)); - static assert(__traits(compiles, idate.modJulianDay)); - } - - - /++ - Converts this $(LREF Date) to a string with the format YYYYMMDD. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - { - if(_year >= 0) - { - if(_year < 10_000) - return format("%04d%02d%02d", _year, _month, _day); - else - return format("+%05d%02d%02d", _year, _month, _day); - } - else if(_year > -10_000) - return format("%05d%02d%02d", _year, _month, _day); - else - return format("%06d%02d%02d", _year, _month, _day); - } - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(Date(2010, 7, 4).toISOString() == "20100704"); - assert(Date(1998, 12, 25).toISOString() == "19981225"); - assert(Date(0, 1, 5).toISOString() == "00000105"); - assert(Date(-4, 1, 5).toISOString() == "-00040105"); - } - - unittest - { - //Test A.D. - assert(Date(9, 12, 4).toISOString() == "00091204"); - assert(Date(99, 12, 4).toISOString() == "00991204"); - assert(Date(999, 12, 4).toISOString() == "09991204"); - assert(Date(9999, 7, 4).toISOString() == "99990704"); - assert(Date(10000, 10, 20).toISOString() == "+100001020"); - - //Test B.C. - assert(Date(0, 12, 4).toISOString() == "00001204"); - assert(Date(-9, 12, 4).toISOString() == "-00091204"); - assert(Date(-99, 12, 4).toISOString() == "-00991204"); - assert(Date(-999, 12, 4).toISOString() == "-09991204"); - assert(Date(-9999, 7, 4).toISOString() == "-99990704"); - assert(Date(-10000, 10, 20).toISOString() == "-100001020"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.toISOString())); - static assert(__traits(compiles, idate.toISOString())); - } - - /++ - Converts this $(LREF Date) to a string with the format YYYY-MM-DD. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - { - if(_year >= 0) - { - if(_year < 10_000) - return format("%04d-%02d-%02d", _year, _month, _day); - else - return format("+%05d-%02d-%02d", _year, _month, _day); - } - else if(_year > -10_000) - return format("%05d-%02d-%02d", _year, _month, _day); - else - return format("%06d-%02d-%02d", _year, _month, _day); - } - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); - assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); - assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); - assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); - } - - unittest - { - //Test A.D. - assert(Date(9, 12, 4).toISOExtString() == "0009-12-04"); - assert(Date(99, 12, 4).toISOExtString() == "0099-12-04"); - assert(Date(999, 12, 4).toISOExtString() == "0999-12-04"); - assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04"); - assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20"); - - //Test B.C. - assert(Date(0, 12, 4).toISOExtString() == "0000-12-04"); - assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04"); - assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04"); - assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04"); - assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04"); - assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.toISOExtString())); - static assert(__traits(compiles, idate.toISOExtString())); - } - - /++ - Converts this $(LREF Date) to a string with the format YYYY-Mon-DD. - +/ - string toSimpleString() @safe const pure nothrow - { - import std.format : format; - try - { - if(_year >= 0) - { - if(_year < 10_000) - return format("%04d-%s-%02d", _year, monthToString(_month), _day); - else - return format("+%05d-%s-%02d", _year, monthToString(_month), _day); - } - else if(_year > -10_000) - return format("%05d-%s-%02d", _year, monthToString(_month), _day); - else - return format("%06d-%s-%02d", _year, monthToString(_month), _day); - } - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); - assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); - assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); - assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); - } - - unittest - { - //Test A.D. - assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04"); - assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04"); - assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04"); - assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04"); - assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20"); - - //Test B.C. - assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04"); - assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04"); - assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04"); - assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04"); - assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04"); - assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.toSimpleString())); - static assert(__traits(compiles, idate.toSimpleString())); - } - - - /++ - Converts this $(LREF Date) to a string. - +/ - string toString() @safe const pure nothrow - { - return toSimpleString(); - } - - unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.toString())); - static assert(__traits(compiles, cdate.toString())); - static assert(__traits(compiles, idate.toString())); - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace - is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF Date) would not be valid. - +/ - static Date fromISOString(S)(in S isoString) @safe pure - if(isSomeString!S) - { - import std.ascii : isDigit; - import std.string : strip; - import std.conv : to; - import std.algorithm : all, startsWith; - import std.format : format; - - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.length >= 8, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - auto day = dstr[$-2 .. $]; - auto month = dstr[$-4 .. $-2]; - auto year = dstr[0 .. $-4]; - - enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - if(year.length > 4) - { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid ISO String: %s", isoString))); - } - else - enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - return Date(to!short(year), to!ubyte(month), to!ubyte(day)); - } - - /// - unittest - { - assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); - assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); - assert(Date.fromISOString("00000105") == Date(0, 1, 5)); - assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); - assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); - } - - unittest - { - assertThrown!DateTimeException(Date.fromISOString("")); - assertThrown!DateTimeException(Date.fromISOString("990704")); - assertThrown!DateTimeException(Date.fromISOString("0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070")); - assertThrown!DateTimeException(Date.fromISOString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOString("120100704")); - assertThrown!DateTimeException(Date.fromISOString("-0100704")); - assertThrown!DateTimeException(Date.fromISOString("+0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070a")); - assertThrown!DateTimeException(Date.fromISOString("20100a04")); - assertThrown!DateTimeException(Date.fromISOString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); - - assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); - assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); - assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); - assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6)); - assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6)); - assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6)); - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYY-MM-DD. Whitespace - is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for - dates. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF Date) would not be valid. - +/ - static Date fromISOExtString(S)(in S isoExtString) @safe pure - if(isSomeString!(S)) - { - import std.ascii : isDigit; - import std.string : strip; - import std.conv : to; - import std.algorithm : all, startsWith; - import std.format : format; - - auto dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto day = dstr[$-2 .. $]; - auto month = dstr[$-5 .. $-3]; - auto year = dstr[0 .. $-6]; - - enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(day), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(month), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - if(year.length > 4) - { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - } - else - enforce(all!isDigit(year), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - return Date(to!short(year), to!ubyte(month), to!ubyte(day)); - } - - /// - unittest - { - assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); - assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); - assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); - assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); - assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); - } - - unittest - { - assertThrown!DateTimeException(Date.fromISOExtString("")); - assertThrown!DateTimeException(Date.fromISOExtString("990704")); - assertThrown!DateTimeException(Date.fromISOExtString("0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOExtString("120100704")); - assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); - assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOExtString("20100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); - - assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); - assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6)); - assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6)); - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYY-Mon-DD. - Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates. - - Throws: - $(LREF DateTimeException) if the given string is not in the correct - format or if the resulting $(LREF Date) would not be valid. - +/ - static Date fromSimpleString(S)(in S simpleString) @safe pure - if(isSomeString!(S)) - { - import std.ascii : isDigit; - import std.string : strip; - import std.conv : to; - import std.algorithm : all, startsWith; - import std.format : format; - - auto dstr = to!dstring(strip(simpleString)); - - enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString))); - - auto day = dstr[$-2 .. $]; - auto month = monthFromString(to!string(dstr[$-6 .. $-3])); - auto year = dstr[0 .. $-7]; - - enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString))); - - if(year.length > 4) - { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid string format: %s", simpleString))); - } - else - enforce(all!isDigit(year), - new DateTimeException(format("Invalid string format: %s", simpleString))); - - return Date(to!short(year), month, to!ubyte(day)); - } - - /// - unittest - { - assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); - assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); - assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); - assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); - assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); - } - - unittest - { - assertThrown!DateTimeException(Date.fromSimpleString("")); - assertThrown!DateTimeException(Date.fromSimpleString("990704")); - assertThrown!DateTimeException(Date.fromSimpleString("0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); - assertThrown!DateTimeException(Date.fromSimpleString("120100704")); - assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); - assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromSimpleString("20100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); - - assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); - assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6)); - assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6)); - } - - - /++ - Returns the $(LREF Date) farthest in the past which is representable by - $(LREF Date). - +/ - @property static Date min() @safe pure nothrow - { - auto date = Date.init; - date._year = short.min; - date._month = Month.jan; - date._day = 1; - - return date; - } - - unittest - { - assert(Date.min.year < 0); - assert(Date.min < Date.max); - } - - - /++ - Returns the $(LREF Date) farthest in the future which is representable by - $(LREF Date). - +/ - @property static Date max() @safe pure nothrow - { - auto date = Date.init; - date._year = short.max; - date._month = Month.dec; - date._day = 31; - - return date; - } - - unittest - { - assert(Date.max.year > 0); - assert(Date.max > Date.min); - } - - -private: - - /+ - Whether the given values form a valid date. - - Params: - year = The year to test. - month = The month of the Gregorian Calendar to test. - day = The day of the month to test. - +/ - static bool _valid(int year, int month, int day) @safe pure nothrow - { - if(!valid!"months"(month)) - return false; - - return valid!"days"(year, month, day); - } - - /+ - Adds the given number of days to this $(LREF Date). A negative number will - subtract. - - The month will be adjusted along with the day if the number of days - added (or subtracted) would overflow (or underflow) the current month. - The year will be adjusted along with the month if the increase (or - decrease) to the month would cause it to overflow (or underflow) the - current year. - - $(D _addDays(numDays)) is effectively equivalent to - $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). - - Params: - days = The number of days to add to this Date. - +/ - ref Date _addDays(long days) return @safe pure nothrow - { - dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); - return this; - } - - unittest - { - //Test A.D. - { - auto date = Date(1999, 2, 28); - date._addDays(1); - assert(date == Date(1999, 3, 1)); - date._addDays(-1); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date._addDays(1); - assert(date == Date(2000, 2, 29)); - date._addDays(1); - assert(date == Date(2000, 3, 1)); - date._addDays(-1); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date._addDays(1); - assert(date == Date(1999, 7, 1)); - date._addDays(-1); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date._addDays(1); - assert(date == Date(1999, 8, 1)); - date._addDays(-1); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 1, 1); - date._addDays(-1); - assert(date == Date(1998, 12, 31)); - date._addDays(1); - assert(date == Date(1999, 1, 1)); - } - - { - auto date = Date(1999, 7, 6); - date._addDays(9); - assert(date == Date(1999, 7, 15)); - date._addDays(-11); - assert(date == Date(1999, 7, 4)); - date._addDays(30); - assert(date == Date(1999, 8, 3)); - date._addDays(-3); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 7, 6); - date._addDays(365); - assert(date == Date(2000, 7, 5)); - date._addDays(-365); - assert(date == Date(1999, 7, 6)); - date._addDays(366); - assert(date == Date(2000, 7, 6)); - date._addDays(730); - assert(date == Date(2002, 7, 6)); - date._addDays(-1096); - assert(date == Date(1999, 7, 6)); - } - - //Test B.C. - { - auto date = Date(-1999, 2, 28); - date._addDays(1); - assert(date == Date(-1999, 3, 1)); - date._addDays(-1); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 28); - date._addDays(1); - assert(date == Date(-2000, 2, 29)); - date._addDays(1); - assert(date == Date(-2000, 3, 1)); - date._addDays(-1); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 6, 30); - date._addDays(1); - assert(date == Date(-1999, 7, 1)); - date._addDays(-1); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 7, 31); - date._addDays(1); - assert(date == Date(-1999, 8, 1)); - date._addDays(-1); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 1, 1); - date._addDays(-1); - assert(date == Date(-2000, 12, 31)); - date._addDays(1); - assert(date == Date(-1999, 1, 1)); - } - - { - auto date = Date(-1999, 7, 6); - date._addDays(9); - assert(date == Date(-1999, 7, 15)); - date._addDays(-11); - assert(date == Date(-1999, 7, 4)); - date._addDays(30); - assert(date == Date(-1999, 8, 3)); - date._addDays(-3); - } - - { - auto date = Date(-1999, 7, 6); - date._addDays(365); - assert(date == Date(-1998, 7, 6)); - date._addDays(-365); - assert(date == Date(-1999, 7, 6)); - date._addDays(366); - assert(date == Date(-1998, 7, 7)); - date._addDays(730); - assert(date == Date(-1996, 7, 6)); - date._addDays(-1096); - assert(date == Date(-1999, 7, 6)); - } - - //Test Both - { - auto date = Date(1, 7, 6); - date._addDays(-365); - assert(date == Date(0, 7, 6)); - date._addDays(365); - assert(date == Date(1, 7, 6)); - date._addDays(-731); - assert(date == Date(-1, 7, 6)); - date._addDays(730); - assert(date == Date(1, 7, 5)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate._addDays(12))); - static assert(!__traits(compiles, idate._addDays(12))); - } - - - @safe pure invariant() - { - import std.format : format; - assert(valid!"months"(_month), - format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); - assert(valid!"days"(_year, _month, _day), - format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); - } - - - short _year = 1; - Month _month = Month.jan; - ubyte _day = 1; -} - - - -/++ - Represents a time of day with hours, minutes, and seconds. It uses 24 hour - time. -+/ -struct TimeOfDay -{ -public: - - /++ - Params: - hour = Hour of the day [0 - 24$(RPAREN). - minute = Minute of the hour [0 - 60$(RPAREN). - second = Second of the minute [0 - 60$(RPAREN). - - Throws: - $(LREF DateTimeException) if the resulting $(LREF TimeOfDay) would be not - be valid. - +/ - this(int hour, int minute, int second = 0) @safe pure - { - enforceValid!"hours"(hour); - enforceValid!"minutes"(minute); - enforceValid!"seconds"(second); - - _hour = cast(ubyte)hour; - _minute = cast(ubyte)minute; - _second = cast(ubyte)second; - } - - unittest - { - assert(TimeOfDay(0, 0) == TimeOfDay.init); - - { - auto tod = TimeOfDay(0, 0); - assert(tod._hour == 0); - assert(tod._minute == 0); - assert(tod._second == 0); - } - - { - auto tod = TimeOfDay(12, 30, 33); - assert(tod._hour == 12); - assert(tod._minute == 30); - assert(tod._second == 33); - } - - { - auto tod = TimeOfDay(23, 59, 59); - assert(tod._hour == 23); - assert(tod._minute == 59); - assert(tod._second == 59); - } - - assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); - } - - - /++ - Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in TimeOfDay rhs) @safe const pure nothrow - { - if(_hour < rhs._hour) - return -1; - if(_hour > rhs._hour) - return 1; - - if(_minute < rhs._minute) - return -1; - if(_minute > rhs._minute) - return 1; - - if(_second < rhs._second) - return -1; - if(_second > rhs._second) - return 1; - - return 0; - } - - unittest - { - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); - - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); - assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); - assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); - - assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); - - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, ctod.opCmp(itod))); - static assert(__traits(compiles, itod.opCmp(ctod))); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const pure nothrow - { - return _hour; - } - - unittest - { - assert(TimeOfDay.init.hour == 0); - assert(TimeOfDay(12, 0, 0).hour == 12); - - const ctod = TimeOfDay(12, 0, 0); - immutable itod = TimeOfDay(12, 0, 0); - static assert(__traits(compiles, ctod.hour == 12)); - static assert(__traits(compiles, itod.hour == 12)); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. - - Throws: - $(LREF DateTimeException) if the given hour would result in an invalid - $(LREF TimeOfDay). - +/ - @property void hour(int hour) @safe pure - { - enforceValid!"hours"(hour); - _hour = cast(ubyte)hour; - } - - unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.hour = 12; - assert(tod == TimeOfDay(12, 0, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.hour = 12)); - static assert(!__traits(compiles, itod.hour = 12)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() @safe const pure nothrow - { - return _minute; - } - - unittest - { - assert(TimeOfDay.init.minute == 0); - assert(TimeOfDay(0, 30, 0).minute == 30); - - const ctod = TimeOfDay(0, 30, 0); - immutable itod = TimeOfDay(0, 30, 0); - static assert(__traits(compiles, ctod.minute == 30)); - static assert(__traits(compiles, itod.minute == 30)); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF TimeOfDay)'s minute to. - - Throws: - $(LREF DateTimeException) if the given minute would result in an - invalid $(LREF TimeOfDay). - +/ - @property void minute(int minute) @safe pure - { - enforceValid!"minutes"(minute); - _minute = cast(ubyte)minute; - } - - unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.minute = 30; - assert(tod == TimeOfDay(0, 30, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.minute = 30)); - static assert(!__traits(compiles, itod.minute = 30)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() @safe const pure nothrow - { - return _second; - } - - unittest - { - assert(TimeOfDay.init.second == 0); - assert(TimeOfDay(0, 0, 33).second == 33); - - const ctod = TimeOfDay(0, 0, 33); - immutable itod = TimeOfDay(0, 0, 33); - static assert(__traits(compiles, ctod.second == 33)); - static assert(__traits(compiles, itod.second == 33)); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF TimeOfDay)'s second to. - - Throws: - $(LREF DateTimeException) if the given second would result in an - invalid $(LREF TimeOfDay). - +/ - @property void second(int second) @safe pure - { - enforceValid!"seconds"(second); - _second = cast(ubyte)second; - } - - unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.second = 33; - assert(tod == TimeOfDay(0, 0, 33)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.second = 33)); - static assert(!__traits(compiles, itod.second = 33)); - } - - - /++ - Adds the given number of units to this $(LREF TimeOfDay). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF TimeOfDay) - one hours's worth of minutes gets the exact same - $(LREF TimeOfDay). - - Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds"). - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF TimeOfDay). - +/ - ref TimeOfDay roll(string units)(long value) @safe pure nothrow - if(units == "hours") - { - return this += dur!"hours"(value); - } - - /// - unittest - { - auto tod1 = TimeOfDay(7, 12, 0); - tod1.roll!"hours"(1); - assert(tod1 == TimeOfDay(8, 12, 0)); - - auto tod2 = TimeOfDay(7, 12, 0); - tod2.roll!"hours"(-1); - assert(tod2 == TimeOfDay(6, 12, 0)); - - auto tod3 = TimeOfDay(23, 59, 0); - tod3.roll!"minutes"(1); - assert(tod3 == TimeOfDay(23, 0, 0)); - - auto tod4 = TimeOfDay(0, 0, 0); - tod4.roll!"minutes"(-1); - assert(tod4 == TimeOfDay(0, 59, 0)); - - auto tod5 = TimeOfDay(23, 59, 59); - tod5.roll!"seconds"(1); - assert(tod5 == TimeOfDay(23, 59, 0)); - - auto tod6 = TimeOfDay(0, 0, 0); - tod6.roll!"seconds"(-1); - assert(tod6 == TimeOfDay(0, 0, 59)); - } - - unittest - { - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"hours"(22).roll!"hours"(-7); - assert(tod == TimeOfDay(3, 27, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"hours"(53))); - static assert(!__traits(compiles, itod.roll!"hours"(53))); - } - - - //Shares documentation with "hours" version. - ref TimeOfDay roll(string units)(long value) @safe pure nothrow - if(units == "minutes" || - units == "seconds") - { - import std.format : format; - - enum memberVarStr = units[0 .. $ - 1]; - value %= 60; - mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); - - if(value < 0) - { - if(newVal < 0) - newVal += 60; - } - else if(newVal >= 60) - newVal -= 60; - - mixin(format("_%s = cast(ubyte)newVal;", memberVarStr)); - return this; - } - - //Test roll!"minutes"(). - unittest - { - static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); - - testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); - - testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); - - testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); - testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); - testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); - - testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); - testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); - testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); - - testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); - testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); - testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"minutes"(97).roll!"minutes"(-102); - assert(tod == TimeOfDay(12, 22, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"minutes"(7))); - static assert(!__traits(compiles, itod.roll!"minutes"(7))); - } - - //Test roll!"seconds"(). - unittest - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"seconds"(105).roll!"seconds"(-77); - assert(tod == TimeOfDay(12, 27, 30)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"seconds"(7))); - static assert(!__traits(compiles, itod.roll!"seconds"(7))); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF TimeOfDay). - +/ - TimeOfDay opBinary(string op, D)(in D duration) @safe const pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - TimeOfDay retval = this; - - static if(is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if(is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format(`return retval._addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - unittest - { - auto tod = TimeOfDay(12, 30, 33); - - assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - } - - assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - } - - auto duration = dur!"hours"(11); - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(__traits(compiles, tod + duration)); - static assert(__traits(compiles, ctod + duration)); - static assert(__traits(compiles, itod + duration)); - - static assert(__traits(compiles, tod - duration)); - static assert(__traits(compiles, ctod - duration)); - static assert(__traits(compiles, itod - duration)); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF TimeOfDay), as well as assigning the result to this - $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF TimeOfDay). - +/ - ref TimeOfDay opOpAssign(string op, D)(in D duration) @safe pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - static if(is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if(is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - unittest - { - auto duration = dur!"hours"(12); - - assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto tod = TimeOfDay(19, 17, 22); - (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); - assert(tod == TimeOfDay(17, 15, 59)); - - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(!__traits(compiles, ctod += duration)); - static assert(!__traits(compiles, itod += duration)); - static assert(!__traits(compiles, ctod -= duration)); - static assert(!__traits(compiles, itod -= duration)); - } - - - /++ - Gives the difference between two $(LREF TimeOfDay)s. - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) - ) - - Params: - rhs = The $(LREF TimeOfDay) to subtract from this one. - +/ - Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow - if(op == "-") - { - immutable lhsSec = _hour * 3600 + _minute * 60 + _second; - immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; - - return dur!"seconds"(lhsSec - rhsSec); - } - - unittest - { - auto tod = TimeOfDay(12, 30, 33); - - assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); - assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); - assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); - assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod - tod)); - static assert(__traits(compiles, ctod - tod)); - static assert(__traits(compiles, itod - tod)); - - static assert(__traits(compiles, tod - ctod)); - static assert(__traits(compiles, ctod - ctod)); - static assert(__traits(compiles, itod - ctod)); - - static assert(__traits(compiles, tod - itod)); - static assert(__traits(compiles, ctod - itod)); - static assert(__traits(compiles, itod - itod)); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - return format("%02d%02d%02d", _hour, _minute, _second); - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); - assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); - } - - unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod.toISOString())); - static assert(__traits(compiles, ctod.toISOString())); - static assert(__traits(compiles, itod.toISOString())); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - return format("%02d:%02d:%02d", _hour, _minute, _second); - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); - assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); - } - - unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod.toISOExtString())); - static assert(__traits(compiles, ctod.toISOExtString())); - static assert(__traits(compiles, itod.toISOExtString())); - } - - - /++ - Converts this TimeOfDay to a string. - +/ - string toString() @safe const pure nothrow - { - return toISOExtString(); - } - - unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod.toString())); - static assert(__traits(compiles, ctod.toString())); - static assert(__traits(compiles, itod.toString())); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF TimeOfDay) would not be valid. - +/ - static TimeOfDay fromISOString(S)(in S isoString) @safe pure - if(isSomeString!S) - { - import std.ascii : isDigit; - import std.string : strip; - import std.conv : to; - import std.algorithm : all; - import std.format : format; - - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - auto hours = dstr[0 .. 2]; - auto minutes = dstr[2 .. 4]; - auto seconds = dstr[4 .. $]; - - enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); - } - - /// - unittest - { - assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); - } - - unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); - - assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. - Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF TimeOfDay) would not be - valid. - +/ - static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure - if(isSomeString!S) - { - import std.ascii : isDigit; - import std.string : strip; - import std.conv : to; - import std.algorithm : all; - import std.format : format; - - auto dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto hours = dstr[0 .. 2]; - auto minutes = dstr[3 .. 5]; - auto seconds = dstr[6 .. $]; - - enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(hours), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(minutes), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(seconds), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); - } - - /// - unittest - { - assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); - } - - unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); - - assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); - } - - - /++ - Returns midnight. - +/ - @property static TimeOfDay min() @safe pure nothrow - { - return TimeOfDay.init; - } - - unittest - { - assert(TimeOfDay.min.hour == 0); - assert(TimeOfDay.min.minute == 0); - assert(TimeOfDay.min.second == 0); - assert(TimeOfDay.min < TimeOfDay.max); - } - - - /++ - Returns one second short of midnight. - +/ - @property static TimeOfDay max() @safe pure nothrow - { - auto tod = TimeOfDay.init; - tod._hour = maxHour; - tod._minute = maxMinute; - tod._second = maxSecond; - - return tod; - } - - unittest - { - assert(TimeOfDay.max.hour == 23); - assert(TimeOfDay.max.minute == 59); - assert(TimeOfDay.max.second == 59); - assert(TimeOfDay.max > TimeOfDay.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. If the - number of minutes overflows (or underflows), then the minutes will wrap. - If the number of minutes overflows(or underflows), then the hour will - wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). - - Params: - seconds = The number of seconds to add to this TimeOfDay. - +/ - ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow - { - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_hour); - hnsecs += convert!("minutes", "hnsecs")(_minute); - hnsecs += convert!("seconds", "hnsecs")(_second); - - hnsecs %= convert!("days", "hnsecs")(1); - - if(hnsecs < 0) - hnsecs += convert!("days", "hnsecs")(1); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _hour = cast(ubyte)newHours; - _minute = cast(ubyte)newMinutes; - _second = cast(ubyte)newSeconds; - - return this; - } - - unittest - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); - - testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); - testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod._addSeconds(7))); - static assert(!__traits(compiles, itod._addSeconds(7))); - } - - - /+ - Whether the given values form a valid $(LREF TimeOfDay). - +/ - static bool _valid(int hour, int minute, int second) @safe pure nothrow - { - return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); - } - - - @safe pure invariant() - { - import std.format : format; - assert(_valid(_hour, _minute, _second), - format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); - } - - ubyte _hour; - ubyte _minute; - ubyte _second; - - enum ubyte maxHour = 24 - 1; - enum ubyte maxMinute = 60 - 1; - enum ubyte maxSecond = 60 - 1; -} - - -/++ - Combines the $(LREF Date) and $(LREF TimeOfDay) structs to give an object - which holds both the date and the time. It is optimized for calendar-based - operations and has no concept of time zone. For an object which is - optimized for time operations based on the system time, use - $(LREF SysTime). $(LREF SysTime) has a concept of time zone and has much higher - precision (hnsecs). $(D DateTime) is intended primarily for calendar-based - uses rather than precise time operations. - +/ -struct DateTime -{ -public: - - /++ - Params: - date = The date portion of $(LREF DateTime). - tod = The time portion of $(LREF DateTime). - +/ - this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow - { - _date = date; - _tod = tod; - } - - unittest - { - { - auto dt = DateTime.init; - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Params: - year = The year portion of the date. - month = The month portion of the date. - day = The day portion of the date. - hour = The hour portion of the time; - minute = The minute portion of the time; - second = The second portion of the time; - +/ - this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure - { - _date = Date(year, month, day); - _tod = TimeOfDay(hour, minute, second); - } - - unittest - { - { - auto dt = DateTime(1999, 7 ,6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(1999, 7 ,6, 12, 30, 33); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Compares this $(LREF DateTime) with the given $(D DateTime.). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in DateTime rhs) @safe const pure nothrow - { - immutable dateResult = _date.opCmp(rhs._date); - - if(dateResult != 0) - return dateResult; - - return _tod.opCmp(rhs._tod); - } - - unittest - { - //Test A.D. - assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); - - assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); - assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); - assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); - - assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); - - assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); - - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - - assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - //Test B.C. - assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - //Test Both - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - static assert(__traits(compiles, dt.opCmp(dt))); - static assert(__traits(compiles, dt.opCmp(cdt))); - static assert(__traits(compiles, dt.opCmp(idt))); - static assert(__traits(compiles, cdt.opCmp(dt))); - static assert(__traits(compiles, cdt.opCmp(cdt))); - static assert(__traits(compiles, cdt.opCmp(idt))); - static assert(__traits(compiles, idt.opCmp(dt))); - static assert(__traits(compiles, idt.opCmp(cdt))); - static assert(__traits(compiles, idt.opCmp(idt))); - } - - - /++ - The date portion of $(LREF DateTime). - +/ - @property Date date() @safe const pure nothrow - { - return _date; - } - - unittest - { - { - auto dt = DateTime.init; - assert(dt.date == Date.init); - } - - { - auto dt = DateTime(Date(1999, 7, 6)); - assert(dt.date == Date(1999, 7, 6)); - } - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(__traits(compiles, cdt.date == Date(2010, 1, 1))); - static assert(__traits(compiles, idt.date == Date(2010, 1, 1))); - } - - - /++ - The date portion of $(LREF DateTime). - - Params: - date = The Date to set this $(LREF DateTime)'s date portion to. - +/ - @property void date(in Date date) @safe pure nothrow - { - _date = date; - } - - unittest - { - auto dt = DateTime.init; - dt.date = Date(1999, 7, 6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); - static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); - } - - - /++ - The time portion of $(LREF DateTime). - +/ - @property TimeOfDay timeOfDay() @safe const pure nothrow - { - return _tod; - } - - unittest - { - { - auto dt = DateTime.init; - assert(dt.timeOfDay == TimeOfDay.init); - } - - { - auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); - assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.timeOfDay == TimeOfDay(12, 30, 33))); - static assert(__traits(compiles, idt.timeOfDay == TimeOfDay(12, 30, 33))); - } - - - /++ - The time portion of $(LREF DateTime). - - Params: - tod = The $(LREF TimeOfDay) to set this $(LREF DateTime)'s time portion - to. - +/ - @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow - { - _tod = tod; - } - - unittest - { - auto dt = DateTime.init; - dt.timeOfDay = TimeOfDay(12, 30, 33); - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay(12, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); - static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const pure nothrow - { - return _date.year; - } - - unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, idt.year)); - static assert(__traits(compiles, idt.year)); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF DateTime)'s year to. - - Throws: - $(LREF DateTimeException) if the new year is not a leap year and if the - resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - _date.year = year; - } - - /// - unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); - } - - unittest - { - static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) - { - dt.year = year; - assert(dt == expected); - } - - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 1999, DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 0, DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), -1999, DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.year = 7)); - static assert(!__traits(compiles, idt.year = 7)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(LREF DateTimeException) if $(D isAD) is true. - +/ - @property short yearBC() @safe const pure - { - return _date.yearBC; - } - - /// - unittest - { - assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); - assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); - assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); - } - - unittest - { - assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, dt.yearBC = 12)); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF DateTime)'s year to. - - Throws: - $(LREF DateTimeException) if a non-positive value is given. - +/ - @property void yearBC(int year) @safe pure - { - _date.yearBC = year; - } - - /// - unittest - { - auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); - dt.yearBC = 1; - assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); - - dt.yearBC = 10; - assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); - } - - unittest - { - assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, dt.yearBC = 12)); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const pure nothrow - { - return _date.month; - } - - /// - unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); - } - - unittest - { - assert(DateTime.init.month == 1); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.month)); - static assert(__traits(compiles, idt.month)); - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF DateTime)'s month to. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month. - +/ - @property void month(Month month) @safe pure - { - _date.month = month; - } - - unittest - { - static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) - { - dt.month = month; - assert(expected != DateTime.init); - assert(dt == expected); - } - - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)13)); - - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.month = 12)); - static assert(!__traits(compiles, idt.month = 12)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const pure nothrow - { - return _date.day; - } - - /// - unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); - } - - unittest - { - import std.range; - import std.format : format; - - static void test(DateTime dateTime, int expected) - { - assert(dateTime.day == expected, format("Value given: %s", dateTime)); - } - - foreach(year; chain(testYearsBC, testYearsAD)) - { - foreach(md; testMonthDays) - { - foreach(tod; testTODs) - test(DateTime(Date(year, md.month, md.day), tod), md.day); - } - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.day)); - static assert(__traits(compiles, idt.day)); - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF DateTime)'s day to. - - Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. - +/ - @property void day(int day) @safe pure - { - _date.day = day; - } - - unittest - { - static void testDT(DateTime dt, int day) - { - dt.day = day; - } - - //Test A.D. - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); - - { - auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); - } - - //Test B.C. - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); - - auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.day = 27)); - static assert(!__traits(compiles, idt.day = 27)); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const pure nothrow - { - return _tod.hour; - } - - unittest - { - assert(DateTime.init.hour == 0); - assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.hour)); - static assert(__traits(compiles, idt.hour)); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF DateTime)'s hour to. - - Throws: - $(LREF DateTimeException) if the given hour would result in an invalid - $(LREF DateTime). - +/ - @property void hour(int hour) @safe pure - { - _tod.hour = hour; - } - - unittest - { - assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); - - auto dt = DateTime.init; - dt.hour = 12; - assert(dt == DateTime(1, 1, 1, 12, 0, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.hour = 27)); - static assert(!__traits(compiles, idt.hour = 27)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() @safe const pure nothrow - { - return _tod.minute; - } - - unittest - { - assert(DateTime.init.minute == 0); - assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.minute)); - static assert(__traits(compiles, idt.minute)); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF DateTime)'s minute to. - - Throws: - $(LREF DateTimeException) if the given minute would result in an - invalid $(LREF DateTime). - +/ - @property void minute(int minute) @safe pure - { - _tod.minute = minute; - } - - unittest - { - assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); - - auto dt = DateTime.init; - dt.minute = 30; - assert(dt == DateTime(1, 1, 1, 0, 30, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.minute = 27)); - static assert(!__traits(compiles, idt.minute = 27)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() @safe const pure nothrow - { - return _tod.second; - } - - unittest - { - assert(DateTime.init.second == 0); - assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.second)); - static assert(__traits(compiles, idt.second)); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF DateTime)'s second to. - - Throws: - $(LREF DateTimeException) if the given seconds would result in an - invalid $(LREF DateTime). - +/ - @property void second(int second) @safe pure - { - _tod.second = second; - } - - unittest - { - assertThrown!DateTimeException((){DateTime.init.second = 60;}()); - - auto dt = DateTime.init; - dt.second = 33; - assert(dt == DateTime(1, 1, 1, 0, 0, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.second = 27)); - static assert(!__traits(compiles, idt.second = 27)); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime). A - negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref DateTime add(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if(units == "years" || - units == "months") - { - _date.add!units(value, allowOverflow); - return this; - } - - /// - unittest - { - auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); - dt1.add!"months"(11); - assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); - dt2.add!"months"(-11); - assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); - - auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); - dt3.add!"years"(1); - assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); - dt4.add!"years"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - unittest - { - auto dt = DateTime(2000, 1, 31); - dt.add!"years"(7).add!"months"(-4); - assert(dt == DateTime(2006, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.add!"years"(4))); - static assert(!__traits(compiles, idt.add!"years"(4))); - static assert(!__traits(compiles, cdt.add!"months"(4))); - static assert(!__traits(compiles, idt.add!"months"(4))); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime). A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF DateTime) 12 months - gets the exact same $(LREF DateTime). However, the days can still be - affected due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref DateTime roll(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if(units == "years" || - units == "months") - { - _date.roll!units(value, allowOverflow); - return this; - } - - /// - unittest - { - auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); - dt1.roll!"months"(1); - assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); - dt2.roll!"months"(-1); - assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); - - auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); - dt3.roll!"months"(1); - assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); - - auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); - dt4.roll!"months"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); - - auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); - dt5.roll!"years"(1); - assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); - dt6.roll!"years"(1, AllowDayOverflow.no); - assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"years"(7).roll!"months"(-4); - assert(dt == DateTime(2007, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"years"(4))); - static assert(!__traits(compiles, idt.roll!"years"(4))); - static assert(!__traits(compiles, cdt.roll!"months"(4))); - static assert(!__traits(compiles, idt.roll!"months"(4))); - } - - - /++ - Adds the given number of units to this $(LREF DateTime). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF DateTime) one - year's worth of days gets the exact same $(LREF DateTime). - - Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), - $(D "minutes"), and $(D "seconds"). - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this $(LREF DateTime). - +/ - ref DateTime roll(string units)(long value) @safe pure nothrow - if(units == "days") - { - _date.roll!"days"(value); - return this; - } - - /// - unittest - { - auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); - dt1.roll!"days"(1); - assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); - dt1.roll!"days"(365); - assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); - dt1.roll!"days"(-32); - assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); - - auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); - dt2.roll!"hours"(1); - assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); - - auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); - dt3.roll!"seconds"(-1); - assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); - } - - unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"days"(7).roll!"days"(-4); - assert(dt == DateTime(2000, 1, 3)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"days"(4))); - static assert(!__traits(compiles, idt.roll!"days"(4))); - } - - - //Shares documentation with "days" version. - ref DateTime roll(string units)(long value) @safe pure nothrow - if(units == "hours" || - units == "minutes" || - units == "seconds") - { - _tod.roll!units(value); - return this; - } - - //Test roll!"hours"(). - unittest - { - static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"hours"(hours); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); - - //Test Both - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"hours"(27).roll!"hours"(-9); - assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"hours"(4))); - static assert(!__traits(compiles, idt.roll!"hours"(4))); - } - - //Test roll!"minutes"(). - unittest - { - static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); - - //Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"minutes"(92).roll!"minutes"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"minutes"(4))); - static assert(!__traits(compiles, idt.roll!"minutes"(4))); - } - - //Test roll!"seconds"(). - unittest - { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); - - //Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"seconds"(92).roll!"seconds"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"seconds"(4))); - static assert(!__traits(compiles, idt.roll!"seconds"(4))); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF DateTime). - +/ - DateTime opBinary(string op, D)(in D duration) @safe const pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - DateTime retval = this; - - static if(is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if(is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format(`return retval._addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - - assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - - assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt + duration)); - static assert(__traits(compiles, idt + duration)); - static assert(__traits(compiles, cdt - duration)); - static assert(__traits(compiles, idt - duration)); - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF DateTime), as well as assigning the result to this $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF DateTime). - +/ - ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow - if((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - DateTime retval = this; - - static if(is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if(is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - (dt += dur!"seconds"(92)) -= dur!"days"(-500); - assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt += duration)); - static assert(!__traits(compiles, idt += duration)); - static assert(!__traits(compiles, cdt -= duration)); - static assert(!__traits(compiles, idt -= duration)); - } - - - /++ - Gives the difference between two $(LREF DateTime)s. - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow - if(op == "-") - { - immutable dateResult = _date - rhs.date; - immutable todResult = _tod - rhs._tod; - - return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); - } - - unittest - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == - dur!"seconds"(86_400)); - assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == - dur!"seconds"(3600)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(60)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == - dur!"seconds"(-60)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(1)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == - dur!"seconds"(-1)); - - assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); - assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt - dt)); - static assert(__traits(compiles, cdt - dt)); - static assert(__traits(compiles, idt - dt)); - - static assert(__traits(compiles, dt - cdt)); - static assert(__traits(compiles, cdt - cdt)); - static assert(__traits(compiles, idt - cdt)); - - static assert(__traits(compiles, dt - idt)); - static assert(__traits(compiles, cdt - idt)); - static assert(__traits(compiles, idt - idt)); - } - - - /++ - Returns the difference between the two $(LREF DateTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(CXREF time, Duration) - that results. Because converting between months and smaller - units requires a specific date (which $(CXREF time, Duration)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF DateTime) to subtract from this one. - +/ - int diffMonths(in DateTime rhs) @safe const pure nothrow - { - return _date.diffMonths(rhs._date); - } - - /// - unittest - { - assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( - DateTime(1999, 1, 31, 23, 59, 59)) == 1); - - assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( - DateTime(1999, 2, 1, 12, 3, 42)) == -1); - - assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( - DateTime(1999, 1, 1, 2, 4, 7)) == 2); - - assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( - DateTime(1999, 3, 31, 0, 30, 58)) == -2); - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.diffMonths(dt))); - static assert(__traits(compiles, cdt.diffMonths(dt))); - static assert(__traits(compiles, idt.diffMonths(dt))); - - static assert(__traits(compiles, dt.diffMonths(cdt))); - static assert(__traits(compiles, cdt.diffMonths(cdt))); - static assert(__traits(compiles, idt.diffMonths(cdt))); - - static assert(__traits(compiles, dt.diffMonths(idt))); - static assert(__traits(compiles, cdt.diffMonths(idt))); - static assert(__traits(compiles, idt.diffMonths(idt))); - } - - - /++ - Whether this $(LREF DateTime) is in a leap year. - +/ - @property bool isLeapYear() @safe const pure nothrow - { - return _date.isLeapYear; - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.isLeapYear)); - static assert(__traits(compiles, cdt.isLeapYear)); - static assert(__traits(compiles, idt.isLeapYear)); - } - - - /++ - Day of the week this $(LREF DateTime) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const pure nothrow - { - return _date.dayOfWeek; - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.dayOfWeek)); - static assert(__traits(compiles, cdt.dayOfWeek)); - static assert(__traits(compiles, idt.dayOfWeek)); - } - - - /++ - Day of the year this $(LREF DateTime) is on. - +/ - @property ushort dayOfYear() @safe const pure nothrow - { - return _date.dayOfYear; - } - - /// - unittest - { - assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); - assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); - assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.dayOfYear)); - static assert(__traits(compiles, cdt.dayOfYear)); - static assert(__traits(compiles, idt.dayOfYear)); - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF DateTime) is on. - +/ - @property void dayOfYear(int day) @safe pure - { - _date.dayOfYear = day; - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.dayOfYear = 12)); - static assert(!__traits(compiles, cdt.dayOfYear = 12)); - static assert(!__traits(compiles, idt.dayOfYear = 12)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - +/ - @property int dayOfGregorianCal() @safe const pure nothrow - { - return _date.dayOfGregorianCal; - } - - /// - unittest - { - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); - assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); - assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); - assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); - assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); - - assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); - } - - unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.dayOfGregorianCal)); - static assert(__traits(compiles, idt.dayOfGregorianCal)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - Setting this property does not affect the time portion of - $(LREF DateTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF DateTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe pure nothrow - { - _date.dayOfGregorianCal = days; - } - - /// - unittest - { - auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); - dt.dayOfGregorianCal = 1; - assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 365; - assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 366; - assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 0; - assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -365; - assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -366; - assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 730_120; - assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 734_137; - assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); - } - - unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); - static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF DateTime) is in. - - See_Also: - $(WEB en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() @safe const pure nothrow - { - return _date.isoWeek; - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.isoWeek)); - static assert(__traits(compiles, cdt.isoWeek)); - static assert(__traits(compiles, idt.isoWeek)); - } - - - /++ - $(LREF DateTime) for the last day in the month that this $(LREF DateTime) is - in. The time portion of endOfMonth is always 23:59:59. - +/ - @property DateTime endOfMonth() @safe const pure nothrow - { - try - return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); - catch(Exception e) - assert(0, "DateTime constructor threw."); - } - - /// - unittest - { - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == - DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == - DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == - DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == - DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); - } - - unittest - { - //Test A.D. - assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); - assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); - assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); - assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); - assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); - assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); - assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); - assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); - assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); - assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); - assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); - assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); - - //Test B.C. - assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); - assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); - assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); - assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); - assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); - assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); - assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); - assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); - assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); - assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); - assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); - assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); - assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.endOfMonth)); - static assert(__traits(compiles, idt.endOfMonth)); - } - - - /++ - The last day in the month that this $(LREF DateTime) is in. - +/ - @property ubyte daysInMonth() @safe const pure nothrow - { - return _date.daysInMonth; - } - - /// - unittest - { - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); - } - - unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.daysInMonth)); - static assert(__traits(compiles, idt.daysInMonth)); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const pure nothrow - { - return _date.isAD; - } - - /// - unittest - { - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); - assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); - assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); - } - - unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.isAD)); - static assert(__traits(compiles, idt.isAD)); - } - - - /++ - The $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for this - $(LREF DateTime) at the given time. For example, prior to noon, - 1996-03-31 would be the Julian day number 2_450_173, so this function - returns 2_450_173, while from noon onward, the julian day number would - be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() @safe const pure nothrow - { - if(_tod._hour < 12) - return _date.julianDay - 1; - else - return _date.julianDay; - } - - unittest - { - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); - assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); - - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); - - assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); - assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); - - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); - - assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); - assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); - - assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); - assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.julianDay)); - static assert(__traits(compiles, idt.julianDay)); - } - - - /++ - The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any - time on this date (since, the modified Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const pure nothrow - { - return _date.modJulianDay; - } - - unittest - { - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.modJulianDay)); - static assert(__traits(compiles, idt.modJulianDay)); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - return format("%sT%s", _date.toISOString(), _tod.toISOString()); - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == - "19981225T021500"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == - "-00040105T000002"); - } - - unittest - { - //Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); - - //Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.toISOString())); - static assert(__traits(compiles, idt.toISOString())); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-MM-DDTHH:MM:SS. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == - "1998-12-25T02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == - "-0004-01-05T00:00:02"); - } - - unittest - { - //Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - //Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.toISOExtString())); - static assert(__traits(compiles, idt.toISOExtString())); - } - - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-Mon-DD HH:MM:SS. - +/ - string toSimpleString() @safe const pure nothrow - { - import std.format : format; - try - return format("%s %s", _date.toSimpleString(), _tod.toString()); - catch(Exception e) - assert(0, "format() threw."); - } - - /// - unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == - "1998-Dec-25 02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == - "-0004-Jan-05 00:00:02"); - } - - unittest - { - //Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - //Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.toSimpleString())); - static assert(__traits(compiles, idt.toSimpleString())); - } - - - /++ - Converts this $(LREF DateTime) to a string. - +/ - string toString() @safe const pure nothrow - { - return toSimpleString(); - } - - unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.toString())); - static assert(__traits(compiles, cdt.toString())); - static assert(__traits(compiles, idt.toString())); - } - - - - /++ - Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates and times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF DateTime) would not be valid. - +/ - static DateTime fromISOString(S)(in S isoString) @safe pure - if(isSomeString!S) - { - import std.string : strip; - import std.conv : to; - import std.algorithm : countUntil; - import std.format : format; - - immutable dstr = to!dstring(strip(isoString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); - auto t = dstr.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - immutable date = Date.fromISOString(dstr[0..t]); - immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - unittest - { - assert(DateTime.fromISOString("20100704T070612") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOString("19981225T021500") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOString("00000105T230959") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOString("-00040105T000002") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOString(" 20100704T070612 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for dates - and times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF DateTime) would not be - valid. - +/ - static DateTime fromISOExtString(S)(in S isoExtString) @safe pure - if(isSomeString!(S)) - { - import std.string : strip; - import std.conv : to; - import std.algorithm : countUntil; - import std.format : format; - - immutable dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - auto t = dstr.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - immutable date = Date.fromISOExtString(dstr[0..t]); - immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - unittest - { - assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - unittest - { - assertThrown!DateTimeException(DateTime.fromISOExtString("")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates and times. - - Throws: - $(LREF DateTimeException) if the given string is not in the correct - format or if the resulting $(LREF DateTime) would not be valid. - +/ - static DateTime fromSimpleString(S)(in S simpleString) @safe pure - if(isSomeString!(S)) - { - import std.string : strip; - import std.conv : to; - import std.algorithm : countUntil; - import std.format : format; - - immutable dstr = to!dstring(strip(simpleString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); - auto t = dstr.countUntil(' '); - - enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); - - immutable date = Date.fromSimpleString(dstr[0..t]); - immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - unittest - { - assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); - - assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Returns the $(LREF DateTime) farthest in the past which is representable by - $(LREF DateTime). - +/ - @property static DateTime min() @safe pure nothrow - out(result) - { - assert(result._date == Date.min); - assert(result._tod == TimeOfDay.min); - } - body - { - auto dt = DateTime.init; - dt._date._year = short.min; - dt._date._month = Month.jan; - dt._date._day = 1; - - return dt; - } - - unittest - { - assert(DateTime.min.year < 0); - assert(DateTime.min < DateTime.max); - } - - - /++ - Returns the $(LREF DateTime) farthest in the future which is representable - by $(LREF DateTime). - +/ - @property static DateTime max() @safe pure nothrow - out(result) - { - assert(result._date == Date.max); - assert(result._tod == TimeOfDay.max); - } - body - { - auto dt = DateTime.init; - dt._date._year = short.max; - dt._date._month = Month.dec; - dt._date._day = 31; - dt._tod._hour = TimeOfDay.maxHour; - dt._tod._minute = TimeOfDay.maxMinute; - dt._tod._second = TimeOfDay.maxSecond; - - return dt; - } - - unittest - { - assert(DateTime.max.year > 0); - assert(DateTime.max > DateTime.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. The - same goes for any larger units. - - Params: - seconds = The number of seconds to add to this $(LREF DateTime). - +/ - ref DateTime _addSeconds(long seconds) return @safe pure nothrow - { - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_tod._hour); - hnsecs += convert!("minutes", "hnsecs")(_tod._minute); - hnsecs += convert!("seconds", "hnsecs")(_tod._second); - - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - - if(hnsecs < 0) - { - hnsecs += convert!("days", "hnsecs")(1); - --days; - } - - _date._addDays(days); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _tod._hour = cast(ubyte)newHours; - _tod._minute = cast(ubyte)newMinutes; - _tod._second = cast(ubyte)newSeconds; - - return this; - } - - unittest - { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); - - testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); - - //Test B.C. - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); - - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); - - //Test Both - testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); - testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); - - testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); - testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); - testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); - testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt._addSeconds(4))); - static assert(!__traits(compiles, idt._addSeconds(4))); - } - - - Date _date; - TimeOfDay _tod; -} - - -//============================================================================== -// Section with intervals. -//============================================================================== - -/++ - Represents an interval of time. - - An $(D Interval) has a starting point and an end point. The interval of time - is therefore the time starting at the starting point up to, but not - including, the end point. e.g. - - $(BOOKTABLE, - $(TR $(TD [January 5th, 2010 - March 10th, 2010$(RPAREN))) - $(TR $(TD [05:00:30 - 12:00:00$(RPAREN))) - $(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN))) - ) - - A range can be obtained from an $(D Interval), allowing iteration over - that interval, with the exact time points which are iterated over depending - on the function which generates the range. - +/ -struct Interval(TP) -{ -public: - - /++ - Params: - begin = The time point which begins the interval. - end = The time point which ends (but is not included in) the - interval. - - Throws: - $(LREF DateTimeException) if $(D_PARAM end) is before $(D_PARAM begin). - - Examples: --------------------- -Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); --------------------- - +/ - this(U)(in TP begin, in U end) pure - if(is(Unqual!TP == Unqual!U)) - { - if(!_valid(begin, end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - - _begin = cast(TP)begin; - _end = cast(TP)end; - } - - - /++ - Params: - begin = The time point which begins the interval. - duration = The duration from the starting point to the end point. - - Throws: - $(LREF DateTimeException) if the resulting $(D end) is before - $(D begin). - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), dur!"years"(3)) == - Interval!Date(Date(1996, 1, 2), Date(1999, 1, 2))); --------------------- - +/ - this(D)(in TP begin, in D duration) pure - if(__traits(compiles, begin + duration)) - { - _begin = cast(TP)begin; - _end = begin + duration; - - if(!_valid(_begin, _end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - } - - - /++ - Params: - rhs = The $(LREF2 .Interval, Interval) to assign to this one. - +/ - ref Interval opAssign(const ref Interval rhs) pure nothrow - { - _begin = cast(TP)rhs._begin; - _end = cast(TP)rhs._end; - return this; - } - - - /++ - Params: - rhs = The $(LREF2 .Interval, Interval) to assign to this one. - +/ - ref Interval opAssign(Interval rhs) pure nothrow - { - _begin = cast(TP)rhs._begin; - _end = cast(TP)rhs._end; - return this; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == - Date(1996, 1, 2)); --------------------- - +/ - @property TP begin() const pure nothrow - { - return cast(TP)_begin; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Params: - timePoint = The time point to set $(D begin) to. - - Throws: - $(LREF DateTimeException) if the resulting interval would be invalid. - +/ - @property void begin(TP timePoint) pure - { - if(!_valid(timePoint, _end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - - _begin = timePoint; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == - Date(2012, 3, 1)); --------------------- - +/ - @property TP end() const pure nothrow - { - return cast(TP)_end; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Params: - timePoint = The time point to set end to. - - Throws: - $(LREF DateTimeException) if the resulting interval would be invalid. - +/ - @property void end(TP timePoint) pure - { - if(!_valid(_begin, timePoint)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - - _end = timePoint; - } - - - /++ - Returns the duration between $(D begin) and $(D end). - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == - dur!"days"(5903)); --------------------- - +/ - @property auto length() const pure nothrow - { - return _end - _begin; - } - - - /++ - Whether the interval's length is 0, that is, whether $(D begin == end). - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); --------------------- - +/ - @property bool empty() const pure nothrow - { - return _begin == _end; - } - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(1994, 12, 24))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(2000, 1, 5))); -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(2012, 3, 1))); --------------------- - +/ - bool contains(in TP timePoint) const pure - { - _enforceNotEmpty(); - - return timePoint >= _begin && timePoint < _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return interval._begin >= _begin && - interval._begin < _end && - interval._end <= _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false (unless this interval is empty), because an - interval going to positive infinity can never be contained in a finite - interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool contains(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false (unless this interval is empty), because an - interval beginning at negative infinity can never be contained in a - finite interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool contains(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is before the given time point. - - Params: - timePoint = The time point to check whether this interval is before - it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(1994, 12, 24))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(2000, 1, 5))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(2012, 3, 1))); --------------------- - +/ - bool isBefore(in TP timePoint) const pure - { - _enforceNotEmpty(); - - return _end <= timePoint; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); --------------------- - +/ - bool isBefore(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(2013, 3, 7)))); --------------------- - +/ - bool isBefore(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Always returns false (unless this interval is empty) because a finite - interval can never be before an interval beginning at negative infinity. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool isBefore(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is after the given time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(1994, 12, 24))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(2000, 1, 5))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(2012, 3, 1))); --------------------- - +/ - bool isAfter(in TP timePoint) const pure - { - _enforceNotEmpty(); - - return timePoint < _begin; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return _begin >= interval._end; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false (unless this interval is empty) because a finite - interval can never be after an interval going to positive infinity. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool isAfter(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _begin >= interval._end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool intersects(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return interval._begin < _end && interval._end > _begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool intersects(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _end > interval._begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(2000, 1, 2)))); --------------------- - +/ - bool intersects(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _begin < interval._end; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - either interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); --------------------- - +/ - Interval intersection(in Interval interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - auto begin = _begin > interval._begin ? _begin : interval._begin; - auto end = _end < interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval intersection(in PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval(_begin > interval._begin ? _begin : interval._begin, _end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); --------------------- - +/ - Interval intersection(in NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval(_begin, _end < interval._end ? _end : interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return _begin == interval._end || _end == interval._begin; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _end == interval._begin; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(2000, 1, 2)))); --------------------- - +/ - bool isAdjacent(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _begin == interval._end; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if either interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); --------------------- - +/ - Interval merge(in Interval interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - auto begin = _begin < interval._begin ? _begin : interval._begin; - auto end = _end > interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - PosInfInterval!Date(Date(2012, 3, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval!TP merge(in PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are not - adjacent or if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(1996, 1, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval!TP merge(in NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return NegInfInterval!TP(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); --------------------- - +/ - Interval span(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - auto begin = _begin < interval._begin ? _begin : interval._begin; - auto end = _end > interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - PosInfInterval!Date(Date(2050, 1, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval!TP span(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Examples: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(1602, 5, 21))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval!TP span(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return NegInfInterval!TP(_end > interval._end ? _end : interval._end); - } - - - /++ - Shifts the interval forward or backwards in time by the given duration - (a positive duration shifts the interval forward; a negative duration - shifts it backward). Effectively, it does $(D begin += duration) and - $(D end += duration). - - Params: - duration = The duration to shift the interval by. - - Throws: - $(LREF DateTimeException) this interval is empty or if the resulting - interval would be invalid. - - Examples: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); --------------------- - +/ - void shift(D)(D duration) pure - if(__traits(compiles, begin + duration)) - { - _enforceNotEmpty(); - - auto begin = _begin + duration; - auto end = _end + duration; - - if(!_valid(begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - - _begin = begin; - _end = end; - } - - - static if(__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Shifts the interval forward or backwards in time by the given number - of years and/or months (a positive number of years and months shifts - the interval forward; a negative number shifts it backward). - It adds the years the given years and months to both begin and end. - It effectively calls $(D add!"years"()) and then $(D add!"months"()) - on begin and end with the given number of years and months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin) and $(D end), causing their month - to increment. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Examples: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -interval1.shift(2); -assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - -interval2.shift(-2); -assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if(isIntegral!T) - { - _enforceNotEmpty(); - - auto begin = _begin; - auto end = _end; - - begin.add!"years"(years, allowOverflow); - begin.add!"months"(months, allowOverflow); - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - - _begin = begin; - _end = end; - } - } - - - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it does $(D begin -= duration) and/or $(D end += duration). Whether - it expands forwards and/or backwards in time is determined by - $(D_PARAM dir). - - Params: - duration = The duration to expand the interval by. - dir = The direction in time to expand the interval. - - Throws: - $(LREF DateTimeException) this interval is empty or if the resulting - interval would be invalid. - - Examples: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); --------------------- - +/ - void expand(D)(D duration, Direction dir = Direction.both) pure - if(__traits(compiles, begin + duration)) - { - _enforceNotEmpty(); - - switch(dir) - { - case Direction.both: - { - auto begin = _begin - duration; - auto end = _end + duration; - - if(!_valid(begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - - _begin = begin; - _end = end; - - return; - } - case Direction.fwd: - { - auto end = _end + duration; - - if(!_valid(_begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - _end = end; - - return; - } - case Direction.bwd: - { - auto begin = _begin - duration; - - if(!_valid(begin, _end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - _begin = begin; - - return; - } - default: - assert(0, "Invalid Direction."); - } - } - - static if(__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it subtracts the given number of months/years from $(D begin) and - adds them to $(D end). Whether it expands forwards and/or backwards - in time is determined by $(D_PARAM dir). - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin) and $(D end), causing their month - to increment. - dir = The direction in time to expand the interval. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Examples: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes, Direction dir = Direction.both) - if(isIntegral!T) - { - _enforceNotEmpty(); - - switch(dir) - { - case Direction.both: - { - auto begin = _begin; - auto end = _end; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - _begin = begin; - _end = end; - - return; - } - case Direction.fwd: - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(_begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - _end = end; - - return; - } - case Direction.bwd: - { - auto begin = _begin; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - - enforce(_valid(begin, _end), new DateTimeException("Argument would result in an invalid Interval.")); - _begin = begin; - - return; - } - default: - assert(0, "Invalid Direction."); - } - } - } - - - /++ - Returns a range which iterates forward over the interval, starting - at $(D begin), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point less than or equal to the - current $(D front) of the range, then a $(LREF DateTimeException) will be - thrown. The range will be empty and iteration complete when - $(D_PARAM func) generates a time point equal to or beyond the $(D end) - of the interval. - - There are helper functions in this module which generate common - delegates to pass to $(D fwdRange). Their documentation starts with - "Range-generating function," making them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant if when creating a custom delegate. - - Examples: --------------------- -auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); -auto func = (in Date date) //For iterating over even-numbered days. - { - if((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; -auto range = interval.fwdRange(func); - - //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). -assert(range.front == Date(2010, 9, 1)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.empty); --------------------- - +/ - IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const - { - _enforceNotEmpty(); - - auto range = IntervalRange!(TP, Direction.fwd)(this, func); - - if(popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /++ - Returns a range which iterates backwards over the interval, starting - at $(D end), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D end). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point greater than or equal to - the current $(D front) of the range, then a $(LREF DateTimeException) will - be thrown. The range will be empty and iteration complete when - $(D_PARAM func) generates a time point equal to or less than the - $(D begin) of the interval. - - There are helper functions in this module which generate common - delegates to pass to $(D bwdRange). Their documentation starts with - "Range-generating function," making them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Examples: --------------------- -auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); -auto func = (in Date date) //For iterating over even-numbered days. - { - if((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; -auto range = interval.bwdRange(func); - -//An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). -assert(range.front == Date(2010, 9, 9)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.empty); --------------------- - +/ - IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const - { - _enforceNotEmpty(); - - auto range = IntervalRange!(TP, Direction.bwd)(this, func); - - if(popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - - -private: - - /+ - Since we have two versions of toString, we have _toStringImpl - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[%s - %s)", _begin, _end); - catch(Exception e) - assert(0, "format() threw."); - } - - - /+ - Throws: - $(LREF DateTimeException) if this interval is empty. - +/ - void _enforceNotEmpty(size_t line = __LINE__) const pure - { - if(empty) - throw new DateTimeException("Invalid operation for an empty Interval.", __FILE__, line); - } - - - /+ - Whether the given values form a valid time interval. - - Params: - begin = The starting point of the interval. - end = The end point of the interval. - +/ - static bool _valid(in TP begin, in TP end) pure nothrow - { - return begin <= end; - } - - - pure invariant() - { - assert(_valid(_begin, _end), "Invariant Failure: begin is not before or equal to end."); - } - - - TP _begin; - TP _end; -} - -//Test Interval's constructors. -unittest -{ - assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); - - Interval!Date(Date.init, Date.init); - Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); - Interval!DateTime(DateTime.init, DateTime.init); - Interval!SysTime(SysTime(0), SysTime(0)); - - Interval!DateTime(DateTime.init, dur!"days"(7)); - - //Verify Examples. - Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); - assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); -} - -//Test Interval's begin. -unittest -{ - assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); - assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.begin)); - static assert(__traits(compiles, iInterval.begin)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); -} - -//Test Interval's end. -unittest -{ - assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.end)); - static assert(__traits(compiles, iInterval.end)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); -} - -//Test Interval's length. -unittest -{ - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); - assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); - assert(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length == dur!"seconds"(129_127)); - assert(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length == dur!"seconds"(129_127)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.length)); - static assert(__traits(compiles, iInterval.length)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); -} - -//Test Interval's empty. -unittest -{ - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); - assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); - assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); - assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); - assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.empty)); - static assert(__traits(compiles, iInterval.empty)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); -} - -//Test Interval's contains(time point). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); - - assert(!interval.contains(Date(2009, 7, 4))); - assert(!interval.contains(Date(2010, 7, 3))); - assert(interval.contains(Date(2010, 7, 4))); - assert(interval.contains(Date(2010, 7, 5))); - assert(interval.contains(Date(2011, 7, 1))); - assert(interval.contains(Date(2012, 1, 6))); - assert(!interval.contains(Date(2012, 1, 7))); - assert(!interval.contains(Date(2012, 1, 8))); - assert(!interval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, interval.contains(cdate))); - static assert(__traits(compiles, cInterval.contains(cdate))); - static assert(__traits(compiles, iInterval.contains(cdate))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); -} - -//Test Interval's contains(Interval). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.contains(interval)); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); - - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.contains(interval))); - static assert(__traits(compiles, interval.contains(cInterval))); - static assert(__traits(compiles, interval.contains(iInterval))); - static assert(__traits(compiles, interval.contains(posInfInterval))); - static assert(__traits(compiles, interval.contains(cPosInfInterval))); - static assert(__traits(compiles, interval.contains(iPosInfInterval))); - static assert(__traits(compiles, interval.contains(negInfInterval))); - static assert(__traits(compiles, interval.contains(cNegInfInterval))); - static assert(__traits(compiles, interval.contains(iNegInfInterval))); - static assert(__traits(compiles, cInterval.contains(interval))); - static assert(__traits(compiles, cInterval.contains(cInterval))); - static assert(__traits(compiles, cInterval.contains(iInterval))); - static assert(__traits(compiles, cInterval.contains(posInfInterval))); - static assert(__traits(compiles, cInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, cInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, cInterval.contains(negInfInterval))); - static assert(__traits(compiles, cInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, cInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, iInterval.contains(interval))); - static assert(__traits(compiles, iInterval.contains(cInterval))); - static assert(__traits(compiles, iInterval.contains(iInterval))); - static assert(__traits(compiles, iInterval.contains(posInfInterval))); - static assert(__traits(compiles, iInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, iInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, iInterval.contains(negInfInterval))); - static assert(__traits(compiles, iInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, iInterval.contains(iNegInfInterval))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test Interval's isBefore(time point). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); - - assert(!interval.isBefore(Date(2009, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 4))); - assert(!interval.isBefore(Date(2010, 7, 5))); - assert(!interval.isBefore(Date(2011, 7, 1))); - assert(!interval.isBefore(Date(2012, 1, 6))); - assert(interval.isBefore(Date(2012, 1, 7))); - assert(interval.isBefore(Date(2012, 1, 8))); - assert(interval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isBefore(cdate))); - static assert(__traits(compiles, cInterval.isBefore(cdate))); - static assert(__traits(compiles, iInterval.isBefore(cdate))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -} - -//Test Interval's isBefore(Interval). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isBefore(interval)); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); - - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isBefore(interval))); - static assert(__traits(compiles, interval.isBefore(cInterval))); - static assert(__traits(compiles, interval.isBefore(iInterval))); - static assert(__traits(compiles, interval.isBefore(posInfInterval))); - static assert(__traits(compiles, interval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, interval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, interval.isBefore(negInfInterval))); - static assert(__traits(compiles, interval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, interval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(interval))); - static assert(__traits(compiles, cInterval.isBefore(cInterval))); - static assert(__traits(compiles, cInterval.isBefore(iInterval))); - static assert(__traits(compiles, cInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(interval))); - static assert(__traits(compiles, iInterval.isBefore(cInterval))); - static assert(__traits(compiles, iInterval.isBefore(iInterval))); - static assert(__traits(compiles, iInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(iNegInfInterval))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test Interval's isAfter(time point). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); - - assert(interval.isAfter(Date(2009, 7, 4))); - assert(interval.isAfter(Date(2010, 7, 3))); - assert(!interval.isAfter(Date(2010, 7, 4))); - assert(!interval.isAfter(Date(2010, 7, 5))); - assert(!interval.isAfter(Date(2011, 7, 1))); - assert(!interval.isAfter(Date(2012, 1, 6))); - assert(!interval.isAfter(Date(2012, 1, 7))); - assert(!interval.isAfter(Date(2012, 1, 8))); - assert(!interval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isAfter(cdate))); - static assert(__traits(compiles, cInterval.isAfter(cdate))); - static assert(__traits(compiles, iInterval.isAfter(cdate))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); -} - -//Test Interval's isAfter(Interval). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAfter(interval)); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); - - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isAfter(interval))); - static assert(__traits(compiles, interval.isAfter(cInterval))); - static assert(__traits(compiles, interval.isAfter(iInterval))); - static assert(__traits(compiles, interval.isAfter(posInfInterval))); - static assert(__traits(compiles, interval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, interval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, interval.isAfter(negInfInterval))); - static assert(__traits(compiles, interval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, interval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(interval))); - static assert(__traits(compiles, cInterval.isAfter(cInterval))); - static assert(__traits(compiles, cInterval.isAfter(iInterval))); - static assert(__traits(compiles, cInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(interval))); - static assert(__traits(compiles, iInterval.isAfter(cInterval))); - static assert(__traits(compiles, iInterval.isAfter(iInterval))); - static assert(__traits(compiles, iInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(iNegInfInterval))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); -} - -//Test Interval's intersects(). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.intersects(interval)); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); - - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.intersects(interval))); - static assert(__traits(compiles, interval.intersects(cInterval))); - static assert(__traits(compiles, interval.intersects(iInterval))); - static assert(__traits(compiles, interval.intersects(posInfInterval))); - static assert(__traits(compiles, interval.intersects(cPosInfInterval))); - static assert(__traits(compiles, interval.intersects(iPosInfInterval))); - static assert(__traits(compiles, interval.intersects(negInfInterval))); - static assert(__traits(compiles, interval.intersects(cNegInfInterval))); - static assert(__traits(compiles, interval.intersects(iNegInfInterval))); - static assert(__traits(compiles, cInterval.intersects(interval))); - static assert(__traits(compiles, cInterval.intersects(cInterval))); - static assert(__traits(compiles, cInterval.intersects(iInterval))); - static assert(__traits(compiles, cInterval.intersects(posInfInterval))); - static assert(__traits(compiles, cInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, cInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, cInterval.intersects(negInfInterval))); - static assert(__traits(compiles, cInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, cInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, iInterval.intersects(interval))); - static assert(__traits(compiles, iInterval.intersects(cInterval))); - static assert(__traits(compiles, iInterval.intersects(iInterval))); - static assert(__traits(compiles, iInterval.intersects(posInfInterval))); - static assert(__traits(compiles, iInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, iInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, iInterval.intersects(negInfInterval))); - static assert(__traits(compiles, iInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, iInterval.intersects(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); -} - -//Test Interval's intersection(). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); - - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); - - assert(interval.intersection(interval) == interval); - assert(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.intersection(interval))); - static assert(__traits(compiles, interval.intersection(cInterval))); - static assert(__traits(compiles, interval.intersection(iInterval))); - static assert(__traits(compiles, interval.intersection(posInfInterval))); - static assert(__traits(compiles, interval.intersection(cPosInfInterval))); - static assert(__traits(compiles, interval.intersection(iPosInfInterval))); - static assert(__traits(compiles, interval.intersection(negInfInterval))); - static assert(__traits(compiles, interval.intersection(cNegInfInterval))); - static assert(__traits(compiles, interval.intersection(iNegInfInterval))); - static assert(__traits(compiles, cInterval.intersection(interval))); - static assert(__traits(compiles, cInterval.intersection(cInterval))); - static assert(__traits(compiles, cInterval.intersection(iInterval))); - static assert(__traits(compiles, cInterval.intersection(posInfInterval))); - static assert(__traits(compiles, cInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, cInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, cInterval.intersection(negInfInterval))); - static assert(__traits(compiles, cInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, cInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, iInterval.intersection(interval))); - static assert(__traits(compiles, iInterval.intersection(cInterval))); - static assert(__traits(compiles, iInterval.intersection(iInterval))); - static assert(__traits(compiles, iInterval.intersection(posInfInterval))); - static assert(__traits(compiles, iInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, iInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, iInterval.intersection(negInfInterval))); - static assert(__traits(compiles, iInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, iInterval.intersection(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); -} - -//Test Interval's isAdjacent(). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) - { - interval1.isAdjacent(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAdjacent(interval)); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); - - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isAdjacent(interval))); - static assert(__traits(compiles, interval.isAdjacent(cInterval))); - static assert(__traits(compiles, interval.isAdjacent(iInterval))); - static assert(__traits(compiles, interval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(interval))); - static assert(__traits(compiles, cInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(interval))); - static assert(__traits(compiles, iInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); -} - -//Test Interval's merge(). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(I)(in Interval!Date interval1, in I interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); - - assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); - - assert(interval.merge(interval) == interval); - assert(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.merge(interval))); - static assert(__traits(compiles, interval.merge(cInterval))); - static assert(__traits(compiles, interval.merge(iInterval))); - static assert(__traits(compiles, interval.merge(posInfInterval))); - static assert(__traits(compiles, interval.merge(cPosInfInterval))); - static assert(__traits(compiles, interval.merge(iPosInfInterval))); - static assert(__traits(compiles, interval.merge(negInfInterval))); - static assert(__traits(compiles, interval.merge(cNegInfInterval))); - static assert(__traits(compiles, interval.merge(iNegInfInterval))); - static assert(__traits(compiles, cInterval.merge(interval))); - static assert(__traits(compiles, cInterval.merge(cInterval))); - static assert(__traits(compiles, cInterval.merge(iInterval))); - static assert(__traits(compiles, cInterval.merge(posInfInterval))); - static assert(__traits(compiles, cInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, cInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, cInterval.merge(negInfInterval))); - static assert(__traits(compiles, cInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, cInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, iInterval.merge(interval))); - static assert(__traits(compiles, iInterval.merge(cInterval))); - static assert(__traits(compiles, iInterval.merge(iInterval))); - static assert(__traits(compiles, iInterval.merge(posInfInterval))); - static assert(__traits(compiles, iInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, iInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, iInterval.merge(negInfInterval))); - static assert(__traits(compiles, iInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, iInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); -} - -//Test Interval's span(). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.span(interval) == interval); - assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.span(interval))); - static assert(__traits(compiles, interval.span(cInterval))); - static assert(__traits(compiles, interval.span(iInterval))); - static assert(__traits(compiles, interval.span(posInfInterval))); - static assert(__traits(compiles, interval.span(cPosInfInterval))); - static assert(__traits(compiles, interval.span(iPosInfInterval))); - static assert(__traits(compiles, interval.span(negInfInterval))); - static assert(__traits(compiles, interval.span(cNegInfInterval))); - static assert(__traits(compiles, interval.span(iNegInfInterval))); - static assert(__traits(compiles, cInterval.span(interval))); - static assert(__traits(compiles, cInterval.span(cInterval))); - static assert(__traits(compiles, cInterval.span(iInterval))); - static assert(__traits(compiles, cInterval.span(posInfInterval))); - static assert(__traits(compiles, cInterval.span(cPosInfInterval))); - static assert(__traits(compiles, cInterval.span(iPosInfInterval))); - static assert(__traits(compiles, cInterval.span(negInfInterval))); - static assert(__traits(compiles, cInterval.span(cNegInfInterval))); - static assert(__traits(compiles, cInterval.span(iNegInfInterval))); - static assert(__traits(compiles, iInterval.span(interval))); - static assert(__traits(compiles, iInterval.span(cInterval))); - static assert(__traits(compiles, iInterval.span(iInterval))); - static assert(__traits(compiles, iInterval.span(posInfInterval))); - static assert(__traits(compiles, iInterval.span(cPosInfInterval))); - static assert(__traits(compiles, iInterval.span(iPosInfInterval))); - static assert(__traits(compiles, iInterval.span(negInfInterval))); - static assert(__traits(compiles, iInterval.span(cNegInfInterval))); - static assert(__traits(compiles, iInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); -} - -//Test Interval's shift(duration). -unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, in Duration duration) - { - interval.shift(duration); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); -} - -//Test Interval's shift(int, int, AllowDayOverflow). -unittest -{ - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.shift(years, months); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(5))); - static assert(!__traits(compiles, iInterval.shift(5))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); -} - -//Test Interval's expand(Duration). -unittest -{ - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(I)(I interval, in Duration duration) - { - interval.expand(duration); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); -} - -//Test Interval's expand(int, int, AllowDayOverflow, Direction) -unittest -{ - { - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.expand(years, months); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow, dir); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); - - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); - - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(5))); - static assert(!__traits(compiles, iInterval.expand(5))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); -} - -//Test Interval's fwdRange. -unittest -{ - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - static void testInterval2(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval2(interval)); - - assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); - - assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == - Date(2010, 9, 12)); - - assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == - Date(2010, 9, 17)); - } - - //Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.empty); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - static assert(__traits(compiles, iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); -} - -//Test Interval's bwdRange. -unittest -{ - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - static void testInterval2(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval2(interval)); - - assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); - assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); - - assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == - Date(2010, 10, 1)); - - assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == - Date(2010, 9, 24)); - } - - //Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.empty); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - static assert(__traits(compiles, iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); -} - -//Test Interval's toString(). -unittest -{ - assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.toString())); - static assert(__traits(compiles, iInterval.toString())); -} - - -/++ - Represents an interval of time which has positive infinity as its end point. - - Any ranges which iterate over a $(D PosInfInterval) are infinite. So, the - main purpose of using $(D PosInfInterval) is to create an infinite range - which starts at a fixed point in time and goes to positive infinity. - +/ -struct PosInfInterval(TP) -{ -public: - - /++ - Params: - begin = The time point which begins the interval. - - Examples: --------------------- -auto interval = PosInfInterval!Date(Date(1996, 1, 2)); --------------------- - +/ - this(in TP begin) pure nothrow - { - _begin = cast(TP)begin; - } - - - /++ - Params: - rhs = The $(D PosInfInterval) to assign to this one. - +/ - ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow - { - _begin = cast(TP)rhs._begin; - return this; - } - - - /++ - Params: - rhs = The $(D PosInfInterval) to assign to this one. - +/ - ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow - { - _begin = cast(TP)rhs._begin; - return this; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); --------------------- - +/ - @property TP begin() const pure nothrow - { - return cast(TP)_begin; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Params: - timePoint = The time point to set $(D begin) to. - +/ - @property void begin(TP timePoint) pure nothrow - { - _begin = timePoint; - } - - - /++ - Whether the interval's length is 0. Always returns false. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); --------------------- - +/ - @property bool empty() const pure nothrow - { - return false; - } - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); --------------------- - +/ - bool contains(TP timePoint) const pure nothrow - { - return timePoint >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._begin >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - PosInfInterval!Date(Date(1995, 7, 2)))); --------------------- - +/ - bool contains(in PosInfInterval interval) const pure nothrow - { - return interval._begin >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false because an interval going to positive infinity - can never contain an interval beginning at negative infinity. - - Params: - interval = The interval to check for inclusion in this interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool contains(in NegInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given time point. - - Always returns false because an interval going to positive infinity - can never be before any time point. - - Params: - timePoint = The time point to check whether this interval is before - it. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); --------------------- - +/ - bool isBefore(in TP timePoint) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false (unless the given interval is empty) because an - interval going to positive infinity can never be before any other - interval. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); --------------------- - +/ - bool isBefore(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval going to positive infinity can - never be before any other interval. - - Params: - interval = The interval to check for against this interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - PosInfInterval!Date(Date(1992, 5, 4)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - PosInfInterval!Date(Date(2013, 3, 7)))); --------------------- - +/ - bool isBefore(in PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval going to positive infinity can - never be before any other interval. - - Params: - interval = The interval to check for against this interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool isBefore(in NegInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); --------------------- - +/ - bool isAfter(in TP timePoint) const pure nothrow - { - return timePoint < _begin; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return _begin >= interval._end; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval going to positive infinity can - never be after another interval going to positive infinity. - - Params: - interval = The interval to check against this interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool isAfter(in PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool isAfter(in NegInfInterval!TP interval) const pure nothrow - { - return _begin >= interval._end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool intersects(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._end > _begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Always returns true because two intervals going to positive infinity - always overlap. - - Params: - interval = The interval to check for intersection with this - interval. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool intersects(in PosInfInterval interval) const pure nothrow - { - return true; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this - interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool intersects(in NegInfInterval!TP interval) const pure nothrow - { - return _begin < interval._end; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - the given interval is empty. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); --------------------- - +/ - Interval!TP intersection(in Interval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - auto begin = _begin > interval._begin ? _begin : interval._begin; - - return Interval!TP(begin, interval._end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1999, 1 , 12))); --------------------- - +/ - PosInfInterval intersection(in PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); --------------------- - +/ - Interval!TP intersection(in NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval!TP(_begin, interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); --------------------- - +/ - bool isAdjacent(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return _begin == interval._end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Always returns false because two intervals going to positive infinity - can never be adjacent to one another. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Examples: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - PosInfInterval!Date(Date(1996, 1, 2)))); --------------------- - +/ - bool isAdjacent(in PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow - { - return _begin == interval._end; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if the given interval is empty. - - Note: - There is no overload for $(D merge) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval merge(in Interval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Note: - There is no overload for $(D merge) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval merge(in PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Note: - There is no overload for $(D span) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == - PosInfInterval!Date(Date(500, 8, 9))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval span(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Note: - There is no overload for $(D span) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval span(in PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Shifts the $(D begin) of this interval forward or backwards in time by - the given duration (a positive duration shifts the interval forward; a - negative duration shifts it backward). Effectively, it does - $(D begin += duration). - - Params: - duration = The duration to shift the interval by. - - Examples: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); --------------------- - +/ - void shift(D)(D duration) pure nothrow - if(__traits(compiles, begin + duration)) - { - _begin += duration; - } - - - static if(__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Shifts the $(D begin) of this interval forward or backwards in time - by the given number of years and/or months (a positive number of years - and months shifts the interval forward; a negative number shifts it - backward). It adds the years the given years and months to - $(D begin). It effectively calls $(D add!"years"()) and then - $(D add!"months"()) on $(D begin) with the given number of years and - months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin), causing its month to increment. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Examples: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if(isIntegral!T) - { - auto begin = _begin; - - begin.add!"years"(years, allowOverflow); - begin.add!"months"(months, allowOverflow); - - _begin = begin; - } - } - - - /++ - Expands the interval backwards in time. Effectively, it does - $(D begin -= duration). - - Params: - duration = The duration to expand the interval by. - - Examples: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.expand(dur!"days"(2)); -assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - -interval2.expand(dur!"days"(-2)); -assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); --------------------- - +/ - void expand(D)(D duration) pure nothrow - if(__traits(compiles, begin + duration)) - { - _begin -= duration; - } - - - static if(__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it subtracts the given number of months/years from $(D begin). - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin), causing its month to increment. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Examples: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.expand(2); -assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - -interval2.expand(-2); -assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if(isIntegral!T) - { - auto begin = _begin; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - - _begin = begin; - - return; - } - } - - - /++ - Returns a range which iterates forward over the interval, starting - at $(D begin), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point less than or equal to the - current $(D front) of the range, then a $(LREF DateTimeException) will be - thrown. - - There are helper functions in this module which generate common - delegates to pass to $(D fwdRange). Their documentation starts with - "Range-generating function," to make them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Examples: --------------------- -auto interval = PosInfInterval!Date(Date(2010, 9, 1)); -auto func = (in Date date) //For iterating over even-numbered days. - { - if((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; -auto range = interval.fwdRange(func); - -//An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). -assert(range.front == Date(2010, 9, 1)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(!range.empty); --------------------- - +/ - PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const - { - auto range = PosInfIntervalRange!(TP)(this, func); - - if(popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - -private: - - /+ - Since we have two versions of toString(), we have _toStringImpl() - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[%s - ∞)", _begin); - catch(Exception e) - assert(0, "format() threw."); - } - - - TP _begin; -} - -//Test PosInfInterval's constructor. -unittest -{ - PosInfInterval!Date(Date.init); - PosInfInterval!TimeOfDay(TimeOfDay.init); - PosInfInterval!DateTime(DateTime.init); - PosInfInterval!SysTime(SysTime(0)); - - //Verify Examples. - auto interval = PosInfInterval!Date(Date(1996, 1, 2)); -} - -//Test PosInfInterval's begin. -unittest -{ - assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); - assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); - assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, cPosInfInterval.begin)); - static assert(__traits(compiles, iPosInfInterval.begin)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); -} - -//Test PosInfInterval's empty. -unittest -{ - assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - - const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cPosInfInterval.empty)); - static assert(__traits(compiles, iPosInfInterval.empty)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); -} - -//Test PosInfInterval's contains(time point). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(!posInfInterval.contains(Date(2009, 7, 4))); - assert(!posInfInterval.contains(Date(2010, 7, 3))); - assert(posInfInterval.contains(Date(2010, 7, 4))); - assert(posInfInterval.contains(Date(2010, 7, 5))); - assert(posInfInterval.contains(Date(2011, 7, 1))); - assert(posInfInterval.contains(Date(2012, 1, 6))); - assert(posInfInterval.contains(Date(2012, 1, 7))); - assert(posInfInterval.contains(Date(2012, 1, 8))); - assert(posInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, posInfInterval.contains(cdate))); - static assert(__traits(compiles, cPosInfInterval.contains(cdate))); - static assert(__traits(compiles, iPosInfInterval.contains(cdate))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); -} - -//Test PosInfInterval's contains(Interval). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.contains(posInfInterval)); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); - - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.contains(interval))); - static assert(__traits(compiles, posInfInterval.contains(cInterval))); - static assert(__traits(compiles, posInfInterval.contains(iInterval))); - static assert(__traits(compiles, posInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(interval))); - static assert(__traits(compiles, cPosInfInterval.contains(cInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(iInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(interval))); - static assert(__traits(compiles, iPosInfInterval.contains(cInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(iInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(iNegInfInterval))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test PosInfInterval's isBefore(time point). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(!posInfInterval.isBefore(Date(2009, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 4))); - assert(!posInfInterval.isBefore(Date(2010, 7, 5))); - assert(!posInfInterval.isBefore(Date(2011, 7, 1))); - assert(!posInfInterval.isBefore(Date(2012, 1, 6))); - assert(!posInfInterval.isBefore(Date(2012, 1, 7))); - assert(!posInfInterval.isBefore(Date(2012, 1, 8))); - assert(!posInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, posInfInterval.isBefore(cdate))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cdate))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cdate))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); -} - -//Test PosInfInterval's isBefore(Interval). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isBefore(posInfInterval)); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); - - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.isBefore(interval))); - static assert(__traits(compiles, posInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(interval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(interval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(iNegInfInterval))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test PosInfInterval's isAfter(time point). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(posInfInterval.isAfter(Date(2009, 7, 3))); - assert(posInfInterval.isAfter(Date(2010, 7, 3))); - assert(!posInfInterval.isAfter(Date(2010, 7, 4))); - assert(!posInfInterval.isAfter(Date(2010, 7, 5))); - assert(!posInfInterval.isAfter(Date(2011, 7, 1))); - assert(!posInfInterval.isAfter(Date(2012, 1, 6))); - assert(!posInfInterval.isAfter(Date(2012, 1, 7))); - assert(!posInfInterval.isAfter(Date(2012, 1, 8))); - assert(!posInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, posInfInterval.isAfter(cdate))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cdate))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cdate))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); -} - -//Test PosInfInterval's isAfter(Interval). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAfter(posInfInterval)); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); - - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.isAfter(interval))); - static assert(__traits(compiles, posInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(interval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(interval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(iNegInfInterval))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's intersects(). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.intersects(posInfInterval)); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); - - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.intersects(interval))); - static assert(__traits(compiles, posInfInterval.intersects(cInterval))); - static assert(__traits(compiles, posInfInterval.intersects(iInterval))); - static assert(__traits(compiles, posInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(interval))); - static assert(__traits(compiles, cPosInfInterval.intersects(cInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(iInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(interval))); - static assert(__traits(compiles, iPosInfInterval.intersects(cInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(iInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's intersection(). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); - - assert(posInfInterval.intersection(posInfInterval) == - posInfInterval); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); - - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 5))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2012, 1, 6))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2012, 1, 7))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2012, 1, 8))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 5))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2012, 1, 6))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2012, 1, 7))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2012, 1, 8))); - - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.intersection(interval))); - static assert(__traits(compiles, posInfInterval.intersection(cInterval))); - static assert(__traits(compiles, posInfInterval.intersection(iInterval))); - static assert(__traits(compiles, posInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(interval))); - static assert(__traits(compiles, cPosInfInterval.intersection(cInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(iInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(interval))); - static assert(__traits(compiles, iPosInfInterval.intersection(cInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(iInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1996, 1 , 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1999, 1 , 12))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); -} - -//Test PosInfInterval's isAdjacent(). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAdjacent(posInfInterval)); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); - - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's merge(). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.merge(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - - assert(posInfInterval.merge(posInfInterval) == - posInfInterval); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.merge(interval))); - static assert(__traits(compiles, posInfInterval.merge(cInterval))); - static assert(__traits(compiles, posInfInterval.merge(iInterval))); - static assert(__traits(compiles, posInfInterval.merge(posInfInterval))); - static assert(__traits(compiles, posInfInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.merge(iPosInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(interval))); - static assert(__traits(compiles, cPosInfInterval.merge(cInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(iInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(iPosInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(interval))); - static assert(__traits(compiles, iPosInfInterval.merge(cInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(iInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(iPosInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); -} - -//Test PosInfInterval's span(). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.span(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.span(posInfInterval) == - posInfInterval); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.span(interval))); - static assert(__traits(compiles, posInfInterval.span(cInterval))); - static assert(__traits(compiles, posInfInterval.span(iInterval))); - static assert(__traits(compiles, posInfInterval.span(posInfInterval))); - static assert(__traits(compiles, posInfInterval.span(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.span(iPosInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.span(interval))); - static assert(__traits(compiles, cPosInfInterval.span(cInterval))); - static assert(__traits(compiles, cPosInfInterval.span(iInterval))); - static assert(__traits(compiles, cPosInfInterval.span(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.span(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.span(iPosInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.span(interval))); - static assert(__traits(compiles, iPosInfInterval.span(cInterval))); - static assert(__traits(compiles, iPosInfInterval.span(iInterval))); - static assert(__traits(compiles, iPosInfInterval.span(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.span(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.span(iPosInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == PosInfInterval!Date(Date(500, 8, 9))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); -} - -//Test PosInfInterval's shift(). -unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); - - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -} - -//Test PosInfInterval's shift(int, int, AllowDayOverflow). -unittest -{ - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); - - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); - } - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.shift(1))); - static assert(!__traits(compiles, iPosInfInterval.shift(1))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.shift(2); - assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); - - interval2.shift(-2); - assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); -} - -//Test PosInfInterval's expand(). -unittest -{ - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); - - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); -} - -//Test PosInfInterval's expand(int, int, AllowDayOverflow). -unittest -{ - { - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); - - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); - } - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.expand(1))); - static assert(!__traits(compiles, iPosInfInterval.expand(1))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.expand(2); - assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - - interval2.expand(-2); - assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); -} - -//Test PosInfInterval's fwdRange(). -unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); - - static void testInterval(PosInfInterval!Date posInfInterval) - { - posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval(posInfInterval)); - - assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == - Date(2010, 9, 12)); - - assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == - Date(2010, 9, 17)); - - //Verify Examples. - auto interval = PosInfInterval!Date(Date(2010, 9, 1)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(!range.empty); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - static assert(__traits(compiles, iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); -} - -//Test PosInfInterval's toString(). -unittest -{ - assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, cPosInfInterval.toString())); - static assert(__traits(compiles, iPosInfInterval.toString())); -} - - -/++ - Represents an interval of time which has negative infinity as its starting - point. - - Any ranges which iterate over a $(D NegInfInterval) are infinite. So, the - main purpose of using $(D NegInfInterval) is to create an infinite range - which starts at negative infinity and goes to a fixed end point. - Iterate over it in reverse. - +/ -struct NegInfInterval(TP) -{ -public: - - /++ - Params: - end = The time point which ends the interval. - - Examples: --------------------- -auto interval = PosInfInterval!Date(Date(1996, 1, 2)); --------------------- - +/ - this(in TP end) pure nothrow - { - _end = cast(TP)end; - } - - - /++ - Params: - rhs = The $(D NegInfInterval) to assign to this one. - +/ - ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow - { - _end = cast(TP)rhs._end; - return this; - } - - - /++ - Params: - rhs = The $(D NegInfInterval) to assign to this one. - +/ - ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow - { - _end = cast(TP)rhs._end; - return this; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); --------------------- - +/ - @property TP end() const pure nothrow - { - return cast(TP)_end; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Params: - timePoint = The time point to set end to. - +/ - @property void end(TP timePoint) pure nothrow - { - _end = timePoint; - } - - - /++ - Whether the interval's length is 0. Always returns false. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); --------------------- - +/ - @property bool empty() const pure nothrow - { - return false; - } - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); --------------------- - +/ - bool contains(TP timePoint) const pure nothrow - { - return timePoint < _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._end <= _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false because an interval beginning at negative - infinity can never contain an interval going to positive infinity. - - Params: - interval = The interval to check for inclusion in this interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool contains(in PosInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool contains(in NegInfInterval interval) const pure nothrow - { - return interval._end <= _end; - } - - - /++ - Whether this interval is before the given time point. - - Params: - timePoint = The time point to check whether this interval is - before it. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); --------------------- - +/ - bool isBefore(in TP timePoint) const pure nothrow - { - return timePoint >= _end; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isBefore(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Params: - interval = The interval to check for against this interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isBefore(in PosInfInterval!TP interval) const pure nothrow - { - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval beginning at negative - infinity can never be before another interval beginning at negative - infinity. - - Params: - interval = The interval to check for against this interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool isBefore(in NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given time point. - - Always returns false because an interval beginning at negative infinity - can never be after any time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); --------------------- - +/ - bool isAfter(in TP timePoint) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not - intersect it. - - Always returns false (unless the given interval is empty) because an - interval beginning at negative infinity can never be after any other - interval. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isAfter(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval beginning at negative infinity - can never be after any other interval. - - Params: - interval = The interval to check against this interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAfter(in PosInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval beginning at negative infinity - can never be after any other interval. - - Params: - interval = The interval to check against this interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool isAfter(in NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool intersects(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._begin < _end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this - interval. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool intersects(in PosInfInterval!TP interval) const pure nothrow - { - return interval._begin < _end; - } - - - /++ - Whether the given interval overlaps this interval. - - Always returns true because two intervals beginning at negative infinity - always overlap. - - Params: - interval = The interval to check for intersection with this interval. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool intersects(in NegInfInterval!TP interval) const pure nothrow - { - return true; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - the given interval is empty. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval!TP intersection(in Interval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - auto end = _end < interval._end ? _end : interval._end; - - return Interval!TP(interval._begin, end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval!TP intersection(in PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); - - return Interval!TP(interval._begin, _end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(1999, 7 , 6))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2012, 3 , 1))); --------------------- - +/ - NegInfInterval intersection(in NegInfInterval interval) const nothrow - { - return NegInfInterval(_end < interval._end ? _end : interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isAdjacent(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._begin == _end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow - { - return interval._begin == _end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Always returns false because two intervals beginning at negative - infinity can never be adjacent to one another. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Examples: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if the given interval is empty. - - Note: - There is no overload for $(D merge) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9 , 2))); --------------------- - +/ - NegInfInterval merge(in Interval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Note: - There is no overload for $(D merge) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval merge(in NegInfInterval interval) const pure nothrow - { - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Note: - There is no overload for $(D span) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9 , 2))); - -assert(NegInfInterval!Date(Date(1600, 1, 7)).span( - Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == - NegInfInterval!Date(Date(2017, 7 , 1))); --------------------- - +/ - NegInfInterval span(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Note: - There is no overload for $(D span) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Examples: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval span(in NegInfInterval interval) const pure nothrow - { - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Shifts the $(D end) of this interval forward or backwards in time by the - given duration (a positive duration shifts the interval forward; a - negative duration shifts it backward). Effectively, it does - $(D end += duration). - - Params: - duration = The duration to shift the interval by. - - Examples: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); -auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); --------------------- - +/ - void shift(D)(D duration) pure nothrow - if(__traits(compiles, end + duration)) - { - _end += duration; - } - - - static if(__traits(compiles, end.add!"months"(1)) && - __traits(compiles, end.add!"years"(1))) - { - /++ - Shifts the $(D end) of this interval forward or backwards in time by - the given number of years and/or months (a positive number of years - and months shifts the interval forward; a negative number shifts it - backward). It adds the years the given years and months to end. It - effectively calls $(D add!"years"()) and then $(D add!"months"()) - on end with the given number of years and months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D end), causing its month to increment. - - Throws: - $(LREF DateTimeException) if empty is true or if the resulting - interval would be invalid. - - Examples: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.shift(2); -assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - -interval2.shift(-2); -assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if(isIntegral!T) - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - _end = end; - } - } - - - /++ - Expands the interval forwards in time. Effectively, it does - $(D end += duration). - - Params: - duration = The duration to expand the interval by. - - Examples: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.expand(dur!"days"(2)); -assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - -interval2.expand(dur!"days"(-2)); -assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); --------------------- - +/ - void expand(D)(D duration) pure nothrow - if(__traits(compiles, end + duration)) - { - _end += duration; - } - - - static if(__traits(compiles, end.add!"months"(1)) && - __traits(compiles, end.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it adds the given number of months/years to end. - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D end), causing their month to increment. - - Throws: - $(LREF DateTimeException) if empty is true or if the resulting - interval would be invalid. - - Examples: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if(isIntegral!T) - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - _end = end; - - return; - } - } - - - /++ - Returns a range which iterates backwards over the interval, starting - at $(D end), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D end). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point greater than or equal to - the current $(D front) of the range, then a $(LREF DateTimeException) will - be thrown. - - There are helper functions in this module which generate common - delegates to pass to $(D bwdRange). Their documentation starts with - "Range-generating function," to make them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Examples: --------------------- -auto interval = NegInfInterval!Date(Date(2010, 9, 9)); -auto func = (in Date date) //For iterating over even-numbered days. - { - if((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; -auto range = interval.bwdRange(func); - -assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(!range.empty); --------------------- - +/ - NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const - { - auto range = NegInfIntervalRange!(TP)(this, func); - - if(popFirst == PopFirst.yes) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - -private: - - /+ - Since we have two versions of toString(), we have _toStringImpl() - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[-∞ - %s)", _end); - catch(Exception e) - assert(0, "format() threw."); - } - - - TP _end; -} - -//Test NegInfInterval's constructor. -unittest -{ - NegInfInterval!Date(Date.init); - NegInfInterval!TimeOfDay(TimeOfDay.init); - NegInfInterval!DateTime(DateTime.init); - NegInfInterval!SysTime(SysTime(0)); -} - -//Test NegInfInterval's end. -unittest -{ - assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.end)); - static assert(__traits(compiles, iNegInfInterval.end)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); -} - -//Test NegInfInterval's empty. -unittest -{ - assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.empty)); - static assert(__traits(compiles, iNegInfInterval.empty)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); -} - -//Test NegInfInterval's contains(time point). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(negInfInterval.contains(Date(2009, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 3))); - assert(negInfInterval.contains(Date(2010, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 5))); - assert(negInfInterval.contains(Date(2011, 7, 1))); - assert(negInfInterval.contains(Date(2012, 1, 6))); - assert(!negInfInterval.contains(Date(2012, 1, 7))); - assert(!negInfInterval.contains(Date(2012, 1, 8))); - assert(!negInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.contains(cdate))); - static assert(__traits(compiles, cNegInfInterval.contains(cdate))); - static assert(__traits(compiles, iNegInfInterval.contains(cdate))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); -} - -//Test NegInfInterval's contains(Interval). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.contains(negInfInterval)); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); - - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.contains(interval))); - static assert(__traits(compiles, negInfInterval.contains(cInterval))); - static assert(__traits(compiles, negInfInterval.contains(iInterval))); - static assert(__traits(compiles, negInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(interval))); - static assert(__traits(compiles, cNegInfInterval.contains(cInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(iInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(interval))); - static assert(__traits(compiles, iNegInfInterval.contains(cInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(iInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's isBefore(time point). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isBefore(Date(2009, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 3))); - assert(!negInfInterval.isBefore(Date(2010, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 5))); - assert(!negInfInterval.isBefore(Date(2011, 7, 1))); - assert(!negInfInterval.isBefore(Date(2012, 1, 6))); - assert(negInfInterval.isBefore(Date(2012, 1, 7))); - assert(negInfInterval.isBefore(Date(2012, 1, 8))); - assert(negInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isBefore(cdate))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cdate))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cdate))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -} - -//Test NegInfInterval's isBefore(Interval). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isBefore(negInfInterval)); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); - - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isBefore(interval))); - static assert(__traits(compiles, negInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(interval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(interval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(iNegInfInterval))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's isAfter(time point). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isAfter(Date(2009, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 3))); - assert(!negInfInterval.isAfter(Date(2010, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 5))); - assert(!negInfInterval.isAfter(Date(2011, 7, 1))); - assert(!negInfInterval.isAfter(Date(2012, 1, 6))); - assert(!negInfInterval.isAfter(Date(2012, 1, 7))); - assert(!negInfInterval.isAfter(Date(2012, 1, 8))); - assert(!negInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isAfter(cdate))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cdate))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cdate))); -} - -//Test NegInfInterval's isAfter(Interval). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAfter(negInfInterval)); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); - - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isAfter(interval))); - static assert(__traits(compiles, negInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(interval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(interval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(iNegInfInterval))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's intersects(). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.intersects(negInfInterval)); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); - - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.intersects(interval))); - static assert(__traits(compiles, negInfInterval.intersects(cInterval))); - static assert(__traits(compiles, negInfInterval.intersects(iInterval))); - static assert(__traits(compiles, negInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(interval))); - static assert(__traits(compiles, cNegInfInterval.intersects(cInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(iInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(interval))); - static assert(__traits(compiles, iNegInfInterval.intersects(cInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(iInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's intersection(). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(negInfInterval.intersection(negInfInterval) == - negInfInterval); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2010, 7, 3))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2010, 7, 4))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2010, 7, 5))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 6))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 7))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2010, 7, 3))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2010, 7, 4))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2010, 7, 5))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 6))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.intersection(interval))); - static assert(__traits(compiles, negInfInterval.intersection(cInterval))); - static assert(__traits(compiles, negInfInterval.intersection(iInterval))); - static assert(__traits(compiles, negInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(interval))); - static assert(__traits(compiles, cNegInfInterval.intersection(cInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(iInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(interval))); - static assert(__traits(compiles, iNegInfInterval.intersection(cInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(iInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(1999, 7 , 6))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2012, 3 , 1))); -} - -//Test NegInfInterval's isAdjacent(). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAdjacent(negInfInterval)); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); - - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(iNegInfInterval))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); -} - -//Test NegInfInterval's merge(). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.merge(negInfInterval) == - negInfInterval); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - NegInfInterval!Date(Date(2013, 7, 3))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.merge(interval))); - static assert(__traits(compiles, negInfInterval.merge(cInterval))); - static assert(__traits(compiles, negInfInterval.merge(iInterval))); - static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.merge(negInfInterval))); - static assert(__traits(compiles, negInfInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(interval))); - static assert(__traits(compiles, cNegInfInterval.merge(cInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(iInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(interval))); - static assert(__traits(compiles, iNegInfInterval.merge(cInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(iInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); -} - -//Test NegInfInterval's span(). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.span(negInfInterval) == - negInfInterval); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - NegInfInterval!Date(Date(2013, 7, 3))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - NegInfInterval!Date(Date(2012, 1, 9))); - - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.span(interval))); - static assert(__traits(compiles, negInfInterval.span(cInterval))); - static assert(__traits(compiles, negInfInterval.span(iInterval))); - static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.span(negInfInterval))); - static assert(__traits(compiles, negInfInterval.span(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(interval))); - static assert(__traits(compiles, cNegInfInterval.span(cInterval))); - static assert(__traits(compiles, cNegInfInterval.span(iInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(interval))); - static assert(__traits(compiles, iNegInfInterval.span(cInterval))); - static assert(__traits(compiles, iNegInfInterval.span(iInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); - assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == NegInfInterval!Date(Date(2017, 7 , 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); -} - -//Test NegInfInterval's shift(). -unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); - auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); -} - -//Test NegInfInterval's shift(int, int, AllowDayOverflow). -unittest -{ - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testIntervalFail(I)(I interval, int years, int months) - { - interval.shift(years, months); - } - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); - - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); - } - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.shift(1))); - static assert(!__traits(compiles, iNegInfInterval.shift(1))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -} - -//Test NegInfInterval's expand(). -unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); -} - -//Test NegInfInterval's expand(int, int, AllowDayOverflow). -unittest -{ - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); - - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); - } - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.expand(1))); - static assert(!__traits(compiles, iNegInfInterval.expand(1))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -} - -//Test NegInfInterval's bwdRange(). -unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(NegInfInterval!Date negInfInterval) - { - negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval(negInfInterval)); - - assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == - Date(2010, 10, 1)); - - assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == - Date(2010, 9, 24)); - - //Verify Examples. - auto interval = NegInfInterval!Date(Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - assert(range.front == Date(2010, 9, 9)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(!range.empty); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); - static assert(__traits(compiles, iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); -} - -//Test NegInfInterval's toString(). -unittest -{ - assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.toString())); - static assert(__traits(compiles, iNegInfInterval.toString())); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point with the given - $(D DayOfWeek) in a range. - - Using this delegate allows iteration over successive time points which - are all the same day of the week. e.g. passing $(D DayOfWeek.mon) to - $(D everyDayOfWeek) would result in a delegate which could be used to - iterate over all of the Mondays in a range. - - Params: - dir = The direction to iterate in. If passing the return value to - $(D fwdRange), use $(D Direction.fwd). If passing it to - $(D bwdRange), use $(D Direction.bwd). - dayOfWeek = The week that each time point in the range will be. - +/ -static TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow - if(isTimePoint!TP && - (dir == Direction.fwd || dir == Direction.bwd) && - __traits(hasMember, TP, "dayOfWeek") && - !__traits(isStaticFunction, TP.dayOfWeek) && - is(typeof(TP.dayOfWeek) == DayOfWeek)) -{ - TP func(in TP tp) - { - TP retval = cast(TP)tp; - immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); - - static if(dir == Direction.fwd) - immutable adjustedDays = days == 0 ? 7 : days; - else - immutable adjustedDays = days == 0 ? -7 : days - 7; - - return retval += dur!"days"(adjustedDays); - } - - return &func; -} - -/// -unittest -{ - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDayOfWeek!Date(DayOfWeek.mon); - auto range = interval.fwdRange(func); - - //A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 13)); - - range.popFront(); - assert(range.front == Date(2010, 9, 20)); - - range.popFront(); - assert(range.empty); -} - -unittest -{ - auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); - auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); - - assert(funcFwd(Date(2010, 8, 28)) == Date(2010, 8, 30)); - assert(funcFwd(Date(2010, 8, 29)) == Date(2010, 8, 30)); - assert(funcFwd(Date(2010, 8, 30)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 8, 31)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 1)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 2)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 3)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 4)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 5)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 6)) == Date(2010, 9, 13)); - assert(funcFwd(Date(2010, 9, 7)) == Date(2010, 9, 13)); - - assert(funcBwd(Date(2010, 8, 28)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 29)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 30)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 1)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 2)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 3)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 4)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 5)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); - - static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); - static assert(__traits(compiles, everyDayOfWeek!(DateTime)(DayOfWeek.mon))); - static assert(__traits(compiles, everyDayOfWeek!(SysTime)(DayOfWeek.mon))); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point with the given month - which would be reached by adding months to the given time point. - - So, using this delegate allows iteration over successive time points - which are in the same month but different years. For example, - iterate over each successive December 25th in an interval by starting with a - date which had the 25th as its day and passed $(D Month.dec) to - $(D everyMonth) to create the delegate. - - Since it wouldn't really make sense to be iterating over a specific month - and end up with some of the time points in the succeeding month or two years - after the previous time point, $(D AllowDayOverflow.no) is always used when - calculating the next time point. - - Params: - dir = The direction to iterate in. If passing the return value to - $(D fwdRange), use $(D Direction.fwd). If passing it to - $(D bwdRange), use $(D Direction.bwd). - month = The month that each time point in the range will be in. - +/ -static TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) - if(isTimePoint!TP && - (dir == Direction.fwd || dir == Direction.bwd) && - __traits(hasMember, TP, "month") && - !__traits(isStaticFunction, TP.month) && - is(typeof(TP.month) == Month)) -{ - enforceValid!"months"(month); - - TP func(in TP tp) - { - TP retval = cast(TP)tp; - immutable months = monthsToMonth(retval.month, month); - - static if(dir == Direction.fwd) - immutable adjustedMonths = months == 0 ? 12 : months; - else - immutable adjustedMonths = months == 0 ? -12 : months - 12; - - retval.add!"months"(adjustedMonths, AllowDayOverflow.no); - - if(retval.month != month) - { - retval.add!"months"(-1); - assert(retval.month == month); - } - - return retval; - } - - return &func; -} - -/// -unittest -{ - auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); - auto func = everyMonth!(Date)(Month.feb); - auto range = interval.fwdRange(func); - - //Using PopFirst.yes would have made this Date(2010, 2, 29). - assert(range.front == Date(2000, 1, 30)); - - range.popFront(); - assert(range.front == Date(2000, 2, 29)); - - range.popFront(); - assert(range.front == Date(2001, 2, 28)); - - range.popFront(); - assert(range.front == Date(2002, 2, 28)); - - range.popFront(); - assert(range.front == Date(2003, 2, 28)); - - range.popFront(); - assert(range.front == Date(2004, 2, 28)); - - range.popFront(); - assert(range.empty); -} - -unittest -{ - auto funcFwd = everyMonth!Date(Month.jun); - auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); - - assert(funcFwd(Date(2010, 5, 31)) == Date(2010, 6, 30)); - assert(funcFwd(Date(2010, 6, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 7, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 8, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 9, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 10, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 11, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 12, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 1, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 2, 28)) == Date(2011, 6, 28)); - assert(funcFwd(Date(2011, 3, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 4, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 5, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 6, 30)) == Date(2012, 6, 30)); - assert(funcFwd(Date(2011, 7, 31)) == Date(2012, 6, 30)); - - assert(funcBwd(Date(2010, 5, 31)) == Date(2009, 6, 30)); - assert(funcBwd(Date(2010, 6, 30)) == Date(2009, 6, 30)); - assert(funcBwd(Date(2010, 7, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 9, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 10, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 11, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 12, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 1, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 2, 28)) == Date(2010, 6, 28)); - assert(funcBwd(Date(2011, 3, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 4, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 5, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 6, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 7, 30)) == Date(2011, 6, 30)); - - static assert(!__traits(compiles, everyMonth!(TimeOfDay)(Month.jan))); - static assert(__traits(compiles, everyMonth!(DateTime)(Month.jan))); - static assert(__traits(compiles, everyMonth!(SysTime)(Month.jan))); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point which is the given - duration later. - - Using this delegate allows iteration over successive time points which - are apart by the given duration e.g. passing $(D dur!"days"(3)) to - $(D everyDuration) would result in a delegate which could be used to iterate - over a range of days which are each 3 days apart. - - Params: - dir = The direction to iterate in. If passing the return value to - $(D fwdRange), use $(D Direction.fwd). If passing it to - $(D bwdRange), use $(D Direction.bwd). - duration = The duration which separates each successive time point in - the range. - +/ -static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) - (D duration) nothrow - if(isTimePoint!TP && - __traits(compiles, TP.init + duration) && - (dir == Direction.fwd || dir == Direction.bwd)) -{ - TP func(in TP tp) - { - static if(dir == Direction.fwd) - return tp + duration; - else - return tp - duration; - } - - return &func; -} - -/// -unittest -{ - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDuration!Date(dur!"days"(8)); - auto range = interval.fwdRange(func); - - //Using PopFirst.yes would have made this Date(2010, 9, 10). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 10)); - - range.popFront(); - assert(range.front == Date(2010, 9, 18)); - - range.popFront(); - assert(range.front == Date(2010, 9, 26)); - - range.popFront(); - assert(range.empty); -} - -unittest -{ - auto funcFwd = everyDuration!Date(dur!"days"(27)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2010, 1, 21)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2010, 1, 22)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2010, 1, 23)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2010, 1, 24)); - - assert(funcBwd(Date(2010, 1, 21)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2010, 1, 22)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2010, 1, 23)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2010, 1, 24)) == Date(2009, 12, 28)); - - static assert(__traits(compiles, everyDuration!Date(dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!TimeOfDay(dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!DateTime(dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!SysTime(dur!"hnsecs"(1)))); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point which is the given - number of years, month, and duration later. - - The difference between this version of $(D everyDuration) and the version - which just takes a $(CXREF time, Duration) is that this one also takes the number of - years and months (along with an $(D AllowDayOverflow) to indicate whether - adding years and months should allow the days to overflow). - - Note that if iterating forward, $(D add!"years"()) is called on the given - time point, then $(D add!"months"()), and finally the duration is added - to it. However, if iterating backwards, the duration is added first, then - $(D add!"months"()) is called, and finally $(D add!"years"()) is called. - That way, going backwards generates close to the same time points that - iterating forward does, but since adding years and months is not entirely - reversible (due to possible day overflow, regardless of whether - $(D AllowDayOverflow.yes) or $(D AllowDayOverflow.no) is used), it can't be - guaranteed that iterating backwards will give the same time points as - iterating forward would have (even assuming that the end of the range is a - time point which would be returned by the delegate when iterating forward - from $(D begin)). - - Params: - dir = The direction to iterate in. If passing the return - value to $(D fwdRange), use $(D Direction.fwd). If - passing it to $(D bwdRange), use $(D Direction.bwd). - years = The number of years to add to the time point passed to - the delegate. - months = The number of months to add to the time point passed to - the delegate. - allowOverflow = Whether the days should be allowed to overflow on - $(D begin) and $(D end), causing their month to - increment. - duration = The duration to add to the time point passed to the - delegate. - +/ -static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) - (int years, - int months = 0, - AllowDayOverflow allowOverflow = AllowDayOverflow.yes, - D duration = dur!"days"(0)) nothrow - if(isTimePoint!TP && - __traits(compiles, TP.init + duration) && - __traits(compiles, TP.init.add!"years"(years)) && - __traits(compiles, TP.init.add!"months"(months)) && - (dir == Direction.fwd || dir == Direction.bwd)) -{ - TP func(in TP tp) - { - static if(dir == Direction.fwd) - { - TP retval = cast(TP)tp; - - retval.add!"years"(years, allowOverflow); - retval.add!"months"(months, allowOverflow); - - return retval + duration; - } - else - { - TP retval = tp - duration; - - retval.add!"months"(-months, allowOverflow); - retval.add!"years"(-years, allowOverflow); - - return retval; - } - } - - return &func; -} - -/// -unittest -{ - auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); - auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); - auto range = interval.fwdRange(func); - - //Using PopFirst.yes would have made this Date(2014, 10, 12). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2014, 10, 4)); - - range.popFront(); - assert(range.front == Date(2018, 11, 6)); - - range.popFront(); - assert(range.front == Date(2022, 12, 8)); - - range.popFront(); - assert(range.empty); -} - -unittest -{ - { - auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); - assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 4)); - - assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); - assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); - } - - { - auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); - assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 3)); - - assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); - assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); - } - - static assert(__traits(compiles, everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); -} - - -//TODO Add function to create a range generating function based on a date recurrence pattern string. -// This may or may not involve creating a date recurrence pattern class of some sort - probably -// yes if we want to make it easy to build them. However, there is a standard recurrence -// pattern string format which we'd want to support with a range generator (though if we have -// the class/struct, we'd probably want a version of the range generating function which took -// that rather than a string). - - -//============================================================================== -// Section with ranges. -//============================================================================== - - -/++ - A range over an $(LREF2 .Interval, Interval). - - $(D IntervalRange) is only ever constructed by $(LREF2 .Interval, Interval). However, when - it is constructed, it is given a function, $(D func), which is used to - generate the time points which are iterated over. $(D func) takes a time - point and returns a time point of the same type. For instance, - to iterate over all of the days in - the interval $(D Interval!Date), pass a function to $(LREF2 .Interval, Interval)'s $(D fwdRange) - where that function took a $(LREF Date) and returned a $(LREF Date) which was one - day later. That function would then be used by $(D IntervalRange)'s - $(D popFront) to iterate over the $(LREF Date)s in the interval. - - If $(D dir == Direction.fwd), then a range iterates forward in time, whereas - if $(D dir == Direction.bwd), then it iterates backwards in time. So, if - $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if - $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must - generate a time point going in the proper direction of iteration, or a - $(LREF DateTimeException) will be thrown. So, to iterate forward in - time, the time point that $(D func) generates must be later in time than the - one passed to it. If it's either identical or earlier in time, then a - $(LREF DateTimeException) will be thrown. To iterate backwards, then - the generated time point must be before the time point which was passed in. - - If the generated time point is ever passed the edge of the range in the - proper direction, then the edge of that range will be used instead. So, if - iterating forward, and the generated time point is past the interval's - $(D end), then $(D front) becomes $(D end). If iterating backwards, and the - generated time point is before $(D begin), then $(D front) becomes - $(D begin). In either case, the range would then be empty. - - Also note that while normally the $(D begin) of an interval is included in - it and its $(D end) is excluded from it, if $(D dir == Direction.bwd), then - $(D begin) is treated as excluded and $(D end) is treated as included. This - allows for the same behavior in both directions. This works because none of - $(LREF2 .Interval, Interval)'s functions which care about whether $(D begin) or $(D end) is - included or excluded are ever called by $(D IntervalRange). $(D interval) - returns a normal interval, regardless of whether $(D dir == Direction.fwd) - or if $(D dir == Direction.bwd), so any $(LREF2 .Interval, Interval) functions which are - called on it which care about whether $(D begin) or $(D end) are included or - excluded will treat $(D begin) as included and $(D end) as excluded. - +/ -struct IntervalRange(TP, Direction dir) - if(isTimePoint!TP && dir != Direction.both) -{ -public: - - /++ - Params: - rhs = The $(D IntervalRange) to assign to this one. - +/ - ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - return this; - } - - - /++ Ditto +/ - ref IntervalRange opAssign(IntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - Whether this $(D IntervalRange) is empty. - +/ - @property bool empty() const pure nothrow - { - return _interval.empty; - } - - - /++ - The first time point in the range. - - Throws: - $(LREF DateTimeException) if the range is empty. - +/ - @property TP front() const pure - { - _enforceNotEmpty(); - - static if(dir == Direction.fwd) - return _interval.begin; - else - return _interval.end; - } - - - /++ - Pops $(D front) from the range, using $(D func) to generate the next - time point in the range. If the generated time point is beyond the edge - of the range, then $(D front) is set to that edge, and the range is then - empty. So, if iterating forwards, and the generated time point is - greater than the interval's $(D end), then $(D front) is set to - $(D end). If iterating backwards, and the generated time point is less - than the interval's $(D begin), then $(D front) is set to $(D begin). - - Throws: - $(LREF DateTimeException) if the range is empty or if the generated - time point is in the wrong direction (i.e. if iterating - forward and the generated time point is before $(D front), or if - iterating backwards and the generated time point is after - $(D front)). - +/ - void popFront() - { - _enforceNotEmpty(); - - static if(dir == Direction.fwd) - { - auto begin = _func(_interval.begin); - - if(begin > _interval.end) - begin = _interval.end; - - _enforceCorrectDirection(begin); - - _interval.begin = begin; - } - else - { - auto end = _func(_interval.end); - - if(end < _interval.begin) - end = _interval.begin; - - _enforceCorrectDirection(end); - - _interval.end = end; - } - } - - - /++ - Returns a copy of $(D this). - +/ - @property IntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this $(D IntervalRange) currently covers. - +/ - @property Interval!TP interval() const pure nothrow - { - return cast(Interval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(in TP) func() pure nothrow @property - { - return _func; - } - - - /++ - The $(D Direction) that this range iterates in. - +/ - @property Direction direction() const pure nothrow - { - return dir; - } - - -private: - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(LREF DateTimeException) if this interval is empty. - +/ - void _enforceNotEmpty(size_t line = __LINE__) const pure - { - if(empty) - throw new DateTimeException("Invalid operation for an empty IntervalRange.", __FILE__, line); - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. - +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - static if(dir == Direction.fwd) - { - enforce(newTP > _interval._begin, - new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", - interval._begin, - newTP), - __FILE__, - line)); - } - else - { - enforce(newTP < _interval._end, - new DateTimeException(format("Generated time point is after previous end: prev [%s] new [%s]", - interval._end, - newTP), - __FILE__, - line)); - } - } - - - Interval!TP _interval; - TP delegate(in TP) _func; -} - -//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. -unittest -{ - static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); - static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); - - static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); - static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); - - static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); - static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); - static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); - static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); -} - -//Test construction of IntervalRange. -unittest -{ - { - Date dateFunc(in Date date) - { - return date; - } - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); - } - - { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } - - auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); - - auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); - } - - { - DateTime dtFunc(in DateTime dt) - { - return dt; - } - - auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); - - auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); - } - - { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)st; - } - - auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - - auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); - } -} - -//Test IntervalRange's empty(). -unittest -{ - //fwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - - assert(!range.empty); - range.popFront(); - assert(range.empty); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.empty)); - - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } - - //bwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - - assert(!range.empty); - range.popFront(); - assert(range.empty); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.empty)); - - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } -} - -//Test IntervalRange's front. -unittest -{ - //fwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - assert(range.front == Date(2010, 7, 4)); - - auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2010, 7, 7)); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } - - //bwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - assert(range.front == Date(2012, 1, 7)); - - auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2012, 1, 4)); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } -} - -//Test IntervalRange's popFront(). -unittest -{ - //fwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; range) - { - assert(date == expected); - expected += dur!"days"(7); - } - - assert(walkLength(range) == 79); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } - - //bwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; range) - { - assert(date == expected); - expected += dur!"days"(-7); - } - - assert(walkLength(range) == 79); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); - } -} - -//Test IntervalRange's save. -unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.save == range); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.save == range); - } -} - -//Test IntervalRange's interval. -unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - static assert(__traits(compiles, cRange.interval)); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - static assert(__traits(compiles, cRange.interval)); - } -} - -//Test IntervalRange's func. -unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.func == func); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.func == func); - } -} - -//Test IntervalRange's direction. -unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.direction == Direction.fwd); - - const cRange = range; - static assert(__traits(compiles, cRange.direction)); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.direction == Direction.bwd); - - const cRange = range; - static assert(__traits(compiles, cRange.direction)); - } -} - - -/++ - A range over a $(D PosInfInterval). It is an infinite range. - - $(D PosInfIntervalRange) is only ever constructed by $(D PosInfInterval). - However, when it is constructed, it is given a function, $(D func), which - is used to generate the time points which are iterated over. $(D func) - takes a time point and returns a time point of the same type. For - instance, to iterate - over all of the days in the interval $(D PosInfInterval!Date), pass a function to - $(D PosInfInterval)'s $(D fwdRange) where that function took a $(LREF Date) and - returned a $(LREF Date) which was one day later. That function would then be - used by $(D PosInfIntervalRange)'s $(D popFront) to iterate over the - $(LREF Date)s in the interval - though obviously, since the range is infinite, - use a function such as $(D std.range.take) with it rather than - iterating over $(I all) of the dates. - - As the interval goes to positive infinity, the range is always iterated over - forwards, never backwards. $(D func) must generate a time point going in - the proper direction of iteration, or a $(LREF DateTimeException) will be - thrown. So, the time points that $(D func) generates must be later in time - than the one passed to it. If it's either identical or earlier in time, then - a $(LREF DateTimeException) will be thrown. - +/ -struct PosInfIntervalRange(TP) - if(isTimePoint!TP) -{ -public: - - /++ - Params: - rhs = The $(D PosInfIntervalRange) to assign to this one. - +/ - ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - - return this; - } - - - /++ Ditto +/ - ref PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - This is an infinite range, so it is never empty. - +/ - enum bool empty = false; - - - /++ - The first time point in the range. - +/ - @property TP front() const pure nothrow - { - return _interval.begin; - } - - - /++ - Pops $(D front) from the range, using $(D func) to generate the next - time point in the range. - - Throws: - $(LREF DateTimeException) if the generated time point is less than - $(D front). - +/ - void popFront() - { - auto begin = _func(_interval.begin); - - _enforceCorrectDirection(begin); - - _interval.begin = begin; - } - - - /++ - Returns a copy of $(D this). - +/ - @property PosInfIntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this range currently covers. - +/ - @property PosInfInterval!TP interval() const pure nothrow - { - return cast(PosInfInterval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(in TP) func() pure nothrow @property - { - return _func; - } - - -private: - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D_PARAME newTP) is in the wrong - direction. - +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - enforce(newTP > _interval._begin, - new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", - interval._begin, - newTP), - __FILE__, - line)); - } - - - PosInfInterval!TP _interval; - TP delegate(in TP) _func; -} - -//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. -unittest -{ - static assert(isInputRange!(PosInfIntervalRange!Date)); - static assert(isForwardRange!(PosInfIntervalRange!Date)); - static assert(isInfinite!(PosInfIntervalRange!Date)); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); - static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); - static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); - static assert(!hasLength!(PosInfIntervalRange!Date)); - static assert(!hasSlicing!(PosInfIntervalRange!Date)); - - static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); - static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); -} - -//Test construction of PosInfIntervalRange. -unittest -{ - { - Date dateFunc(in Date date) - { - return date; - } - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); - } - - { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } - - auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); - - auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); - } - - { - DateTime dtFunc(in DateTime dt) - { - return dt; - } - - auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); - - auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); - } - - { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)st; - } - - auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); - - auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); - } -} - -//Test PosInfIntervalRange's front. -unittest -{ - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - assert(range.front == Date(2010, 7, 4)); - - auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2010, 7, 7)); - - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); -} - -//Test PosInfIntervalRange's popFront(). -unittest -{ - import std.range; - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; take(range, 79)) - { - assert(date == expected); - expected += dur!"days"(7); - } - - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); -} - -//Test PosInfIntervalRange's save. -unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.save == range); -} - -//Test PosInfIntervalRange's interval. -unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - static assert(__traits(compiles, cRange.interval)); -} - -//Test PosInfIntervalRange's func. -unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.func == func); -} - - -/++ - A range over a $(D NegInfInterval). It is an infinite range. - - $(D NegInfIntervalRange) is only ever constructed by $(D NegInfInterval). - However, when it is constructed, it is given a function, $(D func), which - is used to generate the time points which are iterated over. $(D func) - takes a time point and returns a time point of the same type. For - instance, to iterate - over all of the days in the interval $(D NegInfInterval!Date), pass a function to - $(D NegInfInterval)'s $(D bwdRange) where that function took a $(LREF Date) and - returned a $(LREF Date) which was one day earlier. That function would then be - used by $(D NegInfIntervalRange)'s $(D popFront) to iterate over the - $(LREF Date)s in the interval - though obviously, since the range is infinite, - use a function such as $(D std.range.take) with it rather than - iterating over $(I all) of the dates. - - As the interval goes to negative infinity, the range is always iterated over - backwards, never forwards. $(D func) must generate a time point going in - the proper direction of iteration, or a $(LREF DateTimeException) will be - thrown. So, the time points that $(D func) generates must be earlier in time - than the one passed to it. If it's either identical or later in time, then a - $(LREF DateTimeException) will be thrown. - - Also note that while normally the $(D end) of an interval is excluded from - it, $(D NegInfIntervalRange) treats it as if it were included. This allows - for the same behavior as with $(D PosInfIntervalRange). This works - because none of $(D NegInfInterval)'s functions which care about whether - $(D end) is included or excluded are ever called by - $(D NegInfIntervalRange). $(D interval) returns a normal interval, so any - $(D NegInfInterval) functions which are called on it which care about - whether $(D end) is included or excluded will treat $(D end) as excluded. - +/ -struct NegInfIntervalRange(TP) - if(isTimePoint!TP) -{ -public: - - /++ - Params: - rhs = The $(D NegInfIntervalRange) to assign to this one. - +/ - ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - - return this; - } - - - /++ Ditto +/ - ref NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - This is an infinite range, so it is never empty. - +/ - enum bool empty = false; - - - /++ - The first time point in the range. - +/ - @property TP front() const pure nothrow - { - return _interval.end; - } - - - /++ - Pops $(D front) from the range, using $(D func) to generate the next - time point in the range. - - Throws: - $(LREF DateTimeException) if the generated time point is greater than - $(D front). - +/ - void popFront() - { - auto end = _func(_interval.end); - - _enforceCorrectDirection(end); - - _interval.end = end; - } - - - /++ - Returns a copy of $(D this). - +/ - @property NegInfIntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this range currently covers. - +/ - @property NegInfInterval!TP interval() const pure nothrow - { - return cast(NegInfInterval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(in TP) func() pure nothrow @property - { - return _func; - } - - -private: - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. - +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - enforce(newTP < _interval._end, - new DateTimeException(format("Generated time point is before previous end: prev [%s] new [%s]", - interval._end, - newTP), - __FILE__, - line)); - } - - - NegInfInterval!TP _interval; - TP delegate(in TP) _func; -} - -//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. -unittest -{ - static assert(isInputRange!(NegInfIntervalRange!Date)); - static assert(isForwardRange!(NegInfIntervalRange!Date)); - static assert(isInfinite!(NegInfIntervalRange!Date)); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); - static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); - static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); - static assert(!hasLength!(NegInfIntervalRange!Date)); - static assert(!hasSlicing!(NegInfIntervalRange!Date)); - - static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); -} - -//Test construction of NegInfIntervalRange. -unittest -{ - { - Date dateFunc(in Date date) - { - return date; - } - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); - } - - { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } - - auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); - - auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); - } - - { - DateTime dtFunc(in DateTime dt) - { - return dt; - } - - auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); - - auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); - } - - { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)(st); - } - - auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - - auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); - } -} - -//Test NegInfIntervalRange's front. -unittest -{ - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - assert(range.front == Date(2012, 1, 7)); - - auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assert(poppedRange.front == Date(2012, 1, 4)); - - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); -} - -//Test NegInfIntervalRange's popFront(). -unittest -{ - import std.range; - - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; take(range, 79)) - { - assert(date == expected); - expected += dur!"days"(-7); - } - - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); -} - -//Test NegInfIntervalRange's save. -unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.save == range); -} - -//Test NegInfIntervalRange's interval. -unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - static assert(__traits(compiles, cRange.interval)); -} - -//Test NegInfIntervalRange's func. -unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.func == func); -} - - -//============================================================================== -// Section with time zones. -//============================================================================== - -/++ - Represents a time zone. It is used with $(LREF SysTime) to indicate the time - zone of a $(LREF SysTime). - +/ -abstract class TimeZone -{ -public: - - /++ - The name of the time zone per the TZ Database. This is the name used to - get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). - - See_Also: - $(WEB en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones) - +/ - @property string name() @safe const nothrow - { - return _name; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Standard Time). Regardless, it is not the same as name. - +/ - @property string stdName() @safe const nothrow - { - return _stdName; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Daylight Time). Regardless, it is not the same as name. - +/ - @property string dstName() @safe const nothrow - { - return _dstName; - } - - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current dates - but will still return true for $(D hasDST) because the time zone did at - some point have DST. - +/ - @property abstract bool hasDST() @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - abstract bool dstInEffect(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - abstract long utcToTZ(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - abstract long tzToUTC(long adjTime) @safe const nothrow; - - - /++ - Returns what the offset from UTC is at the given std time. - It includes the DST offset in effect at that time (if any). - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - Duration utcOffsetAt(long stdTime) @safe const nothrow - { - return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); - } - - - /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. - - This returns a $(LREF PosixTimeZone) on Posix systems and a - $(LREF WindowsTimeZone) on Windows systems. For - $(LREF PosixTimeZone) on Windows, call $(D PosixTimeZone.getTimeZone) - directly and give it the location of the TZ Database time zone files on - disk. - - On Windows, the given TZ Database name is converted to the corresponding - time zone name on Windows prior to calling - $(D WindowsTimeZone.getTimeZone). This function allows for - the same time zone names on both Windows and Posix systems. - - See_Also: - $(WEB en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones)
- $(WEB unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, - Windows <-> TZ Database Name Conversion Table) - - Params: - name = The TZ Database name of the desired time zone - - Throws: - $(LREF DateTimeException) if the given time zone could not be found. - - Examples: --------------------- -auto tz = TimeZone.getTimeZone("America/Los_Angeles"); --------------------- - +/ - static immutable(TimeZone) getTimeZone(string name) @safe - { - version(Posix) - return PosixTimeZone.getTimeZone(name); - else version(Windows) - { - import std.format : format; - auto windowsTZName = tzDatabaseNameToWindowsTZName(name); - if(windowsTZName != null) - { - try - return WindowsTimeZone.getTimeZone(windowsTZName); - catch(DateTimeException dte) - { - auto oldName = _getOldName(windowsTZName); - if(oldName != null) - return WindowsTimeZone.getTimeZone(oldName); - throw dte; - } - } - else - throw new DateTimeException(format("%s does not have an equivalent Windows time zone.", name)); - } - } - - // The purpose of this is to handle the case where a Windows time zone is - // new and exists on an up-to-date Windows box but does not exist on Windows - // boxes which have not been properly updated. The "date added" is included - // on the theory that we'll be able to remove them at some point in the - // the future once enough time has passed, and that way, we know how much - // time has passed. - private static string _getOldName(string windowsTZName) @safe pure nothrow - { - switch(windowsTZName) - { - case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08 - default: return null; - } - } - - //Since reading in the time zone files could be expensive, most unit tests - //are consolidated into this one unittest block which minimizes how often it - //reads a time zone file. - unittest - { - import std.path : buildPath; - import std.file : exists, isFile; - import std.conv : to; - import std.format : format; - - - version(Posix) scope(exit) clearTZEnvVar(); - - static immutable(TimeZone) testTZ(string tzName, - string stdName, - string dstName, - Duration utcOffset, - Duration dstOffset, - bool north = true) - { - scope(failure) writefln("Failed time zone: %s", tzName); - - immutable tz = TimeZone.getTimeZone(tzName); - immutable hasDST = dstOffset != dur!"hnsecs"(0); - - version(Posix) - assert(tz.name == tzName); - else version(Windows) - assert(tz.name == stdName); - - //assert(tz.stdName == stdName); //Locale-dependent - //assert(tz.dstName == dstName); //Locale-dependent - assert(tz.hasDST == hasDST); - - immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0); - immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0); - auto std = SysTime(stdDate, tz); - auto dst = SysTime(dstDate, tz); - auto stdUTC = SysTime(stdDate - utcOffset, UTC()); - auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC()); - - assert(!std.dstInEffect); - assert(dst.dstInEffect == hasDST); - assert(tz.utcOffsetAt(std.stdTime) == utcOffset); - assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset); - - assert(cast(DateTime)std == stdDate); - assert(cast(DateTime)dst == dstDate); - assert(std == stdUTC); - - version(Posix) - { - setTZEnvVar(tzName); - - static void testTM(in SysTime st) - { - time_t unixTime = st.toUnixTime(); - tm* osTimeInfo = localtime(&unixTime); - tm ourTimeInfo = st.toTM(); - - assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec); - assert(ourTimeInfo.tm_min == osTimeInfo.tm_min); - assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour); - assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday); - assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon); - assert(ourTimeInfo.tm_year == osTimeInfo.tm_year); - assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday); - assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday); - assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst); - assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff); - assert(to!string(ourTimeInfo.tm_zone) == - to!string(osTimeInfo.tm_zone)); - } - - testTM(std); - testTM(dst); - - //Apparently, right/ does not exist on Mac OS X. I don't know - //whether or not it exists on FreeBSD. It's rather pointless - //normally, since the Posix standard requires that leap seconds - //be ignored, so it does make some sense that right/ wouldn't - //be there, but since PosixTimeZone _does_ use leap seconds if - //the time zone file does, we'll test that functionality if the - //appropriate files exist. - if(buildPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) - { - auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); - - assert(leapTZ.name == "right/" ~ tzName); - //assert(leapTZ.stdName == stdName); //Locale-dependent - //assert(leapTZ.dstName == dstName); //Locale-dependent - assert(leapTZ.hasDST == hasDST); - - auto leapSTD = SysTime(std.stdTime, leapTZ); - auto leapDST = SysTime(dst.stdTime, leapTZ); - - assert(!leapSTD.dstInEffect); - assert(leapDST.dstInEffect == hasDST); - - assert(leapSTD.stdTime == std.stdTime); - assert(leapDST.stdTime == dst.stdTime); - - //Whenever a leap second is added/removed, - //this will have to be adjusted. - //enum leapDiff = convert!("seconds", "hnsecs")(25); - //assert(leapSTD.adjTime - leapDiff == std.adjTime); - //assert(leapDST.adjTime - leapDiff == dst.adjTime); - } - } - - return tz; - } - - auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - /+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - ///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - /+Europe/London+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - /+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - /+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - - version(Posix) - { - version(FreeBSD) enum utcZone = "Etc/UTC"; - else version(linux) enum utcZone = "UTC"; - else version(OSX) enum utcZone = "UTC"; - else version(Android) enum utcZone = "UTC"; - else static assert(0, "The location of the UTC timezone file on this Posix platform must be set."); - - auto tzs = [testTZ("America/Los_Angeles", "PST", "PDT", dur!"hours"(-8), dur!"hours"(1)), - testTZ("America/New_York", "EST", "EDT", dur!"hours"(-5), dur!"hours"(1)), - //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false), - testTZ("Europe/London", "GMT", "BST", dur!"hours"(0), dur!"hours"(1)), - testTZ("Europe/Paris", "CET", "CEST", dur!"hours"(1), dur!"hours"(1)), - //Per www.timeanddate.com, it should be "CST" and "CDT", - //but the OS insists that it's "CST" for both. We should - //probably figure out how to report an error in the TZ - //database and report it. - testTZ("Australia/Adelaide", "CST", "CST", - dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; - - testTZ(utcZone, "UTC", "UTC", dur!"hours"(0), dur!"hours"(0)); - assertThrown!DateTimeException(PosixTimeZone.getTimeZone("hello_world")); - } - else version(Windows) - { - auto tzs = [testTZ("America/Los_Angeles", "Pacific Standard Time", - "Pacific Daylight Time", dur!"hours"(-8), dur!"hours"(1)), - testTZ("America/New_York", "Eastern Standard Time", - "Eastern Daylight Time", dur!"hours"(-5), dur!"hours"(1)), - //testTZ("America/Santiago", "Pacific SA Standard Time", - //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false), - testTZ("Europe/London", "GMT Standard Time", - "GMT Daylight Time", dur!"hours"(0), dur!"hours"(1)), - testTZ("Europe/Paris", "Romance Standard Time", - "Romance Daylight Time", dur!"hours"(1), dur!"hours"(1)), - testTZ("Australia/Adelaide", "Cen. Australia Standard Time", - "Cen. Australia Daylight Time", - dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; - - testTZ("Atlantic/Reykjavik", "Greenwich Standard Time", - "Greenwich Daylight Time", dur!"hours"(0), dur!"hours"(0)); - assertThrown!DateTimeException(WindowsTimeZone.getTimeZone("hello_world")); - } - else - assert(0, "OS not supported."); - - foreach(i; 0 .. tzs.length) - { - auto tz = tzs[i]; - immutable spring = dstSwitches[i][2]; - immutable fall = dstSwitches[i][3]; - auto stdOffset = SysTime(dstSwitches[i][0] + dur!"days"(-1), tz).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - //Verify that creating a SysTime in the given time zone results - //in a SysTime with the correct std time during and surrounding - //a DST switch. - foreach(hour; -12 .. 13) - { - auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz); - immutable targetHour = hour < 0 ? hour + 24 : hour; - - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) - { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); - } - - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tz.name, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } - - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } - - if(hour == spring) - { - testHour(st, spring + 1, tz.name); - testHour(st + dur!"minutes"(1), spring + 1, tz.name); - } - else - { - testHour(st, targetHour, tz.name); - testHour(st + dur!"minutes"(1), targetHour, tz.name); - } - - if(hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); - - st = SysTime(dstSwitches[i][1] + dur!"hours"(hour), tz); - testHour(st, targetHour, tz.name); - - //Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if(hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tz.name); - - if(hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } - - //Verify that converting a time in UTC to a time in another - //time zone results in the correct time during and surrounding - //a DST switch. - bool first = true; - auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset; - //@@@BUG@@@ 3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - - foreach(hour; -24 .. 25) - { - auto utc = SysTime(dstSwitches[i][0] + dur!"hours"(hour), UTC()); - auto local = utc.toOtherTZ(tz); - - void testOffset2(Duration offset, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tz.name, utc, local), - __FILE__, line); - } - - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); - } - - if(utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - - utc = SysTime(dstSwitches[i][1] + dur!"hours"(hour), UTC()); - local = utc.toOtherTZ(tz); - - if(utc == fallSwitch || utc == fallSwitchMinus1) - { - if(first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); - } - else if(utc > fallSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - } - } - } - - - /++ - Returns a list of the names of the time zones installed on the system. - - Providing a sub-name narrows down the list of time zones (which - can number in the thousands). For example, - passing in "America" as the sub-name returns only the time zones which - begin with "America". - - On Windows, this function will convert the Windows time zone names to - the corresponding TZ Database names with - $(D windowsTZNameToTZDatabaseName). To get the actual Windows time - zone names, use $(D WindowsTimeZone.getInstalledTZNames) directly. - - Params: - subName = The first part of the time zones desired. - - Throws: - $(D FileException) on Posix systems if it fails to read from disk. - $(LREF DateTimeException) on Windows systems if it fails to read the - registry. - +/ - static string[] getInstalledTZNames(string subName = "") @safe - { - version(Posix) - return PosixTimeZone.getInstalledTZNames(subName); - else version(Windows) - { - import std.array : appender; - import std.algorithm : startsWith, sort; - import std.format : format; - - auto windowsNames = WindowsTimeZone.getInstalledTZNames(); - auto retval = appender!(string[])(); - - foreach(winName; windowsNames) - { - auto tzName = windowsTZNameToTZDatabaseName(winName); - - version(unittest) - { - import std.string; - assert(tzName !is null, format("TZName which is missing: %s", winName)); - } - - if(tzName !is null && tzName.startsWith(subName)) - retval.put(tzName); - } - - sort(retval.data); - return retval.data; - } - } - - unittest - { - static void testPZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - TimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - // This was not previously tested, and it's currently failing, so I'm - // leaving it commented out until I can sort it out. - //assert(equal(tzNames, tzNames.uniq())); - - foreach(tzName; tzNames) - assertNotThrown!DateTimeException(testPZSuccess(tzName)); - } - - -private: - - /+ - Params: - name = The TZ Database name for the time zone. - stdName = The abbreviation for the time zone during std time. - dstName = The abbreviation for the time zone during DST. - +/ - this(string name, string stdName, string dstName) @safe immutable pure - { - _name = name; - _stdName = stdName; - _dstName = dstName; - } - - - immutable string _name; - immutable string _stdName; - immutable string _dstName; -} - - -/++ - A TimeZone which represents the current local time zone on - the system running your program. - - This uses the underlying C calls to adjust the time rather than using - specific D code based off of system settings to calculate the time such as - $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that it will - use whatever the current time zone is on the system, even if the system's - time zone changes while the program is running. - +/ -final class LocalTime : TimeZone -{ -public: - - /++ - $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its only - instance. - +/ - static immutable(LocalTime) opCall() @trusted pure nothrow - { - alias @safe pure nothrow immutable(LocalTime) function() FuncType; - return (cast(FuncType)&singleton)(); - } - - - version(StdDdoc) - { - /++ - The name of the time zone per the TZ Database. This is the name used to - get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). - - Note that this always returns the empty string. This is because time - zones cannot be uniquely identified by the attributes given by the - OS (such as the $(D stdName) and $(D dstName)), and neither Posix - systems nor Windows systems provide an easy way to get the TZ - Database name of the local time zone. - - See_Also: - $(WEB en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List - of Time Zones) - +/ - @property override string name() @safe const nothrow; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Standard Time). Regardless, it is not the same as name. - - This property is overridden because the local time of the system could - change while the program is running and we need to determine it - dynamically rather than it being fixed like it would be with most time - zones. - +/ - @property override string stdName() @trusted const nothrow - { - version(Posix) - { - import std.conv : to; - try - return to!string(tzname[0]); - catch(Exception e) - assert(0, "to!string(tzname[0]) failed."); - } - else version(Windows) - { - try - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - //Cannot use to!string() like this should, probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 - //return to!string(tzInfo.StandardName); - - wchar[32] str; - - foreach(i, ref wchar c; str) - c = tzInfo.StandardName[i]; - - string retval; - - foreach(dchar c; str) - { - if(c == '\0') - break; - - retval ~= c; - } - - return retval; - } - catch(Exception e) - assert(0, "GetTimeZoneInformation() threw."); - } - } - - unittest -{ - assert(LocalTime().stdName !is null); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().stdName == "PST"); - - setTZEnvVar("America/New_York"); - assert(LocalTime().stdName == "EST"); - } - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Daylight Time). Regardless, it is not the same as name. - - This property is overridden because the local time of the system could - change while the program is running and we need to determine it - dynamically rather than it being fixed like it would be with most time - zones. - +/ - @property override string dstName() @trusted const nothrow - { - version(Posix) - { - import std.conv : to; - try - return to!string(tzname[1]); - catch(Exception e) - assert(0, "to!string(tzname[1]) failed."); - } - else version(Windows) - { - try - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - //Cannot use to!string() like this should, probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 - //return to!string(tzInfo.DaylightName); - - wchar[32] str; - - foreach(i, ref wchar c; str) - c = tzInfo.DaylightName[i]; - - string retval; - - foreach(dchar c; str) - { - if(c == '\0') - break; - - retval ~= c; - } - - return retval; - } - catch(Exception e) - assert(0, "GetTimeZoneInformation() threw."); - } - } - - unittest -{ - assert(LocalTime().dstName !is null); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().dstName == "PDT"); - - setTZEnvVar("America/New_York"); - assert(LocalTime().dstName == "EDT"); - } - } - - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current - dates but will still return true for $(D hasDST) because the time zone - did at some point have DST. - +/ - @property override bool hasDST() @trusted const nothrow - { - version(Posix) - { - static if(is(typeof(daylight))) - return cast(bool)(daylight); - else - { - try - { - auto currYear = (cast(Date)Clock.currTime()).year; - auto janOffset = SysTime(Date(currYear, 1, 4), cast(immutable)this).stdTime - - SysTime(Date(currYear, 1, 4), UTC()).stdTime; - auto julyOffset = SysTime(Date(currYear, 7, 4), cast(immutable)this).stdTime - - SysTime(Date(currYear, 7, 4), UTC()).stdTime; - - return janOffset != julyOffset; - } - catch(Exception e) - assert(0, "Clock.currTime() threw."); - } - } - else version(Windows) - { - try - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return tzInfo.DaylightDate.wMonth != 0; - } - catch(Exception e) - assert(0, "GetTimeZoneInformation() threw."); - } - } - - unittest -{ - LocalTime().hasDST; - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().hasDST); - - setTZEnvVar("America/New_York"); - assert(LocalTime().hasDST); - - setTZEnvVar("UTC"); - assert(!LocalTime().hasDST); - } - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is in effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - override bool dstInEffect(long stdTime) @trusted const nothrow - { - time_t unixTime = stdTimeToUnixTime(stdTime); - - version(Posix) - { - tm* timeInfo = localtime(&unixTime); - - return cast(bool)(timeInfo.tm_isdst); - } - else version(Windows) - { - //Apparently Windows isn't smart enough to deal with negative time_t. - if(unixTime >= 0) - { - tm* timeInfo = localtime(&unixTime); - - if(timeInfo) - return cast(bool)(timeInfo.tm_isdst); - } - - TIME_ZONE_INFORMATION tzInfo; - - try - GetTimeZoneInformation(&tzInfo); - catch(Exception e) - assert(0, "The impossible happened. GetTimeZoneInformation() threw."); - - return WindowsTimeZone._dstInEffect(&tzInfo, stdTime); - } - } - - unittest - { - auto currTime = Clock.currStdTime; - LocalTime().dstInEffect(currTime); - } - - - /++ - Returns hnsecs in the local time zone using the standard C function - calls on Posix systems and the standard Windows system calls on Windows - systems to adjust the time to the appropriate time zone from std time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - - See_Also: - $(D TimeZone.utcToTZ) - +/ - override long utcToTZ(long stdTime) @trusted const nothrow - { - version(Posix) - { - time_t unixTime = stdTimeToUnixTime(stdTime); - tm* timeInfo = localtime(&unixTime); - - return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - - try - GetTimeZoneInformation(&tzInfo); - catch(Exception e) - assert(0, "GetTimeZoneInformation() threw."); - - return WindowsTimeZone._utcToTZ(&tzInfo, stdTime, hasDST); - } - } - - unittest - { - LocalTime().utcToTZ(0); - } - - - /++ - Returns std time using the standard C function calls on Posix systems - and the standard Windows system calls on Windows systems to adjust the - time to UTC from the appropriate time zone. - - See_Also: - $(D TimeZone.tzToUTC) - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @trusted const nothrow - { - version(Posix) - { - time_t unixTime = stdTimeToUnixTime(adjTime); - - immutable past = unixTime - cast(time_t)convert!("days", "seconds")(1); - tm* timeInfo = localtime(past < unixTime ? &past : &unixTime); - immutable pastOffset = timeInfo.tm_gmtoff; - - immutable future = unixTime + cast(time_t)convert!("days", "seconds")(1); - timeInfo = localtime(future > unixTime ? &future : &unixTime); - immutable futureOffset = timeInfo.tm_gmtoff; - - if(pastOffset == futureOffset) - return adjTime - convert!("seconds", "hnsecs")(pastOffset); - - if(pastOffset < futureOffset) - unixTime -= cast(time_t)convert!("hours", "seconds")(1); - - unixTime -= pastOffset; - timeInfo = localtime(&unixTime); - - return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - - try - GetTimeZoneInformation(&tzInfo); - catch(Exception e) - assert(0, "GetTimeZoneInformation() threw."); - - return WindowsTimeZone._tzToUTC(&tzInfo, adjTime, hasDST); - } - } - - unittest - { - import std.format : format; - - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), - tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - - foreach(i; 0 .. tzInfos.length) - { - auto tzName = tzInfos[i][0]; - setTZEnvVar(tzName); - immutable spring = tzInfos[i][3]; - immutable fall = tzInfos[i][4]; - auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - //Verify that creating a SysTime in the given time zone results - //in a SysTime with the correct std time during and surrounding - //a DST switch. - foreach(hour; -12 .. 13) - { - auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); - immutable targetHour = hour < 0 ? hour + 24 : hour; - - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) - { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); - } - - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tzName, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } - - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } - - if(hour == spring) - { - testHour(st, spring + 1, tzName); - testHour(st + dur!"minutes"(1), spring + 1, tzName); - } - else - { - testHour(st, targetHour, tzName); - testHour(st + dur!"minutes"(1), targetHour, tzName); - } - - if(hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); - - st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); - testHour(st, targetHour, tzName); - - //Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if(hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tzName); - - if(hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } - - //Verify that converting a time in UTC to a time in another - //time zone results in the correct time during and surrounding - //a DST switch. - bool first = true; - auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; - //@@@BUG@@@ 3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - - foreach(hour; -24 .. 25) - { - auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); - auto local = utc.toLocalTime(); - - void testOffset2(Duration offset, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), - __FILE__, line); - } - - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); - } - - if(utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - - utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); - local = utc.toLocalTime(); - - if(utc == fallSwitch || utc == fallSwitchMinus1) - { - if(first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); - } - else if(utc > fallSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - } - } - } - } - - -private: - - this() @safe immutable pure - { - super("", "", ""); - } - - - static immutable LocalTime _localTime = new immutable(LocalTime)(); - // Use low-lock singleton pattern with _tzsetWasCalled (see http://dconf.org/talks/simcha.html) - static bool _lowLock; - static shared bool _tzsetWasCalled; - - - // This is done so that we can maintain purity in spite of doing an impure - // operation the first time that LocalTime() is called. - static immutable(LocalTime) singleton() @trusted - { - if(!_lowLock) - { - synchronized - { - if(!_tzsetWasCalled) - { - tzset(); - _tzsetWasCalled = true; - } - } - - _lowLock = true; - } - - return _localTime; - } -} - - -/++ - A $(LREF2 .TimeZone, TimeZone) which represents UTC. - +/ -final class UTC : TimeZone -{ -public: - - /++ - $(D UTC) is a singleton class. $(D UTC) returns its only instance. - +/ - static immutable(UTC) opCall() @safe pure nothrow - { - return _utc; - } - - - /++ - Always returns false. - +/ - @property override bool hasDST() @safe const nothrow - { - return false; - } - - - /++ - Always returns false. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow - { - return false; - } - - - /++ - Returns the given hnsecs without changing them at all. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - - See_Also: - $(D TimeZone.utcToTZ) - +/ - override long utcToTZ(long stdTime) @safe const nothrow - { - return stdTime; - } - - unittest - { - assert(UTC().utcToTZ(0) == 0); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("UTC"); - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - assert(UTC().utcToTZ(std.stdTime) == std.stdTime); - assert(UTC().utcToTZ(dst.stdTime) == dst.stdTime); - } - } - - - /++ - Returns the given hnsecs without changing them at all. - - See_Also: - $(D TimeZone.tzToUTC) - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow - { - return adjTime; - } - - unittest - { - assert(UTC().tzToUTC(0) == 0); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("UTC"); - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - assert(UTC().tzToUTC(std.stdTime) == std.stdTime); - assert(UTC().tzToUTC(dst.stdTime) == dst.stdTime); - } - } - - - /++ - Returns a $(CXREF time, Duration) of 0. - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - override Duration utcOffsetAt(long stdTime) @safe const nothrow - { - return dur!"hnsecs"(0); - } - - -private: - - this() @safe immutable pure - { - super("UTC", "UTC", "UTC"); - } - - - static immutable UTC _utc = new immutable(UTC)(); -} - - -/++ - Represents a time zone with an offset (in minutes, west is negative) from - UTC but no DST. - - It's primarily used as the time zone in the result of $(LREF SysTime)'s - $(D fromISOString), $(D fromISOExtString), and $(D fromSimpleString). - - $(D name) and $(D dstName) are always the empty string since this time zone - has no DST, and while it may be meant to represent a time zone which is in - the TZ Database, obviously it's not likely to be following the exact rules - of any of the time zones in the TZ Database, so it makes no sense to set it. - +/ -final class SimpleTimeZone : TimeZone -{ -public: - - /++ - Always returns false. - +/ - @property override bool hasDST() @safe const nothrow - { - return false; - } - - - /++ - Always returns false. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow - { - return false; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - override long utcToTZ(long stdTime) @safe const nothrow - { - return stdTime + _utcOffset.total!"hnsecs"; - } - - unittest - { - auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto east = new immutable SimpleTimeZone(dur!"hours"(8)); - - assert(west.utcToTZ(0) == -288_000_000_000L); - assert(east.utcToTZ(0) == 288_000_000_000L); - assert(west.utcToTZ(54_321_234_567_890L) == 54_033_234_567_890L); - - const cstz = west; - static assert(__traits(compiles, west.utcToTZ(50002))); - static assert(__traits(compiles, cstz.utcToTZ(50002))); - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow - { - return adjTime - _utcOffset.total!"hnsecs"; - } - - unittest - { - auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto east = new immutable SimpleTimeZone(dur!"hours"(8)); - - assert(west.tzToUTC(-288_000_000_000L) == 0); - assert(east.tzToUTC(288_000_000_000L) == 0); - assert(west.tzToUTC(54_033_234_567_890L) == 54_321_234_567_890L); - - const cstz = west; - static assert(__traits(compiles, west.tzToUTC(20005))); - static assert(__traits(compiles, cstz.tzToUTC(20005))); - } - - - /++ - Returns utcOffset as a $(CXREF time, Duration). - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - override Duration utcOffsetAt(long stdTime) @safe const nothrow - { - return _utcOffset; - } - - - /++ - Params: - utcOffset = This time zone's offset from UTC with west of UTC being - negative (it is added to UTC to get the adjusted time). - stdName = The $(D stdName) for this time zone. - +/ - this(Duration utcOffset, string stdName = "") @safe immutable pure - { - //FIXME This probably needs to be changed to something like (-12 - 13). - enforce!DateTimeException(abs(utcOffset) < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - - super("", stdName, ""); - this._utcOffset = utcOffset; - } - - /++ - $(RED Deprecated. Please use the overload which takes a Duration. This - overload will be removed in December 2014). - - Params: - utcOffset = This time zone's offset from UTC in minutes with west of - negative (it is added to UTC to get the adjusted time). - stdName = The $(D stdName) for this time zone. - +/ - deprecated("Please use the overload which takes a Duration.") - this(int utcOffset, string stdName = "") @safe immutable pure - { - this(dur!"minutes"(utcOffset), stdName); - } - - unittest - { - auto stz = new immutable SimpleTimeZone(dur!"hours"(-8), "PST"); - assert(stz.name == ""); - assert(stz.stdName == "PST"); - assert(stz.dstName == ""); - assert(stz.utcOffset == dur!"hours"(-8)); - } - - - /++ - The amount of time the offset from UTC is (negative is west of UTC, - positive is east). - +/ - @property Duration utcOffset() @safe const pure nothrow - { - return _utcOffset; - } - - -private: - - /+ - Returns a time zone as a string with an offset from UTC. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - - Params: - utcOffset = The number of minutes offset from UTC (negative means - west). - +/ - static string toISOString(Duration utcOffset) @safe pure - { - import std.format : format; - - immutable absOffset = abs(utcOffset); - enforce!DateTimeException(absOffset < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - int hours; - int minutes; - absOffset.split!("hours", "minutes")(hours, minutes); - return format(utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", hours, minutes); - } - - unittest - { - static string testSTZInvalid(Duration offset) - { - return SimpleTimeZone.toISOString(offset); - } - - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); - - assert(toISOString(dur!"minutes"(0)) == "+00:00"); - assert(toISOString(dur!"minutes"(1)) == "+00:01"); - assert(toISOString(dur!"minutes"(10)) == "+00:10"); - assert(toISOString(dur!"minutes"(59)) == "+00:59"); - assert(toISOString(dur!"minutes"(60)) == "+01:00"); - assert(toISOString(dur!"minutes"(90)) == "+01:30"); - assert(toISOString(dur!"minutes"(120)) == "+02:00"); - assert(toISOString(dur!"minutes"(480)) == "+08:00"); - assert(toISOString(dur!"minutes"(1439)) == "+23:59"); - - assert(toISOString(dur!"minutes"(-1)) == "-00:01"); - assert(toISOString(dur!"minutes"(-10)) == "-00:10"); - assert(toISOString(dur!"minutes"(-59)) == "-00:59"); - assert(toISOString(dur!"minutes"(-60)) == "-01:00"); - assert(toISOString(dur!"minutes"(-90)) == "-01:30"); - assert(toISOString(dur!"minutes"(-120)) == "-02:00"); - assert(toISOString(dur!"minutes"(-480)) == "-08:00"); - assert(toISOString(dur!"minutes"(-1439)) == "-23:59"); - } - - - /+ - Takes a time zone as a string with an offset from UTC and returns a - $(LREF SimpleTimeZone) which matches. - - The accepted formats for time zone offsets - are +H, -H, +HH, -HH, +H:MM, -H:MM, +HH:MM, and -HH:MM. - - Params: - isoString = A string which represents a time zone in the ISO format. - +/ - static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure - if(isSomeString!S) - { - import std.ascii : isDigit; - import std.string : strip; - import std.conv : to; - import std.algorithm : startsWith, countUntil, all; - import std.format : format; - - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.startsWith('-', '+'), new DateTimeException("Invalid ISO String")); - - auto sign = dstr.startsWith('-') ? -1 : 1; - - dstr.popFront(); - enforce(!dstr.empty, new DateTimeException("Invalid ISO String")); - - immutable colon = dstr.countUntil(':'); - - dstring hoursStr; - dstring minutesStr; - - if(colon != -1) - { - hoursStr = dstr[0 .. colon]; - minutesStr = dstr[colon + 1 .. $]; - enforce(minutesStr.length == 2, new DateTimeException(format("Invalid ISO String: %s", dstr))); - } - else - hoursStr = dstr; - - enforce(all!isDigit(hoursStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); - enforce(all!isDigit(minutesStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); - - immutable hours = to!int(hoursStr); - immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr); - - return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); - } - - unittest - { - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("Z")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-:")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+:")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-1:")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+1:")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("1")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-24:00")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+24:00")); - assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+1:0")); - - assert(SimpleTimeZone.fromISOString("+00:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(0))).utcOffset); - assert(SimpleTimeZone.fromISOString("+00:01").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(1))).utcOffset); - assert(SimpleTimeZone.fromISOString("+00:10").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(10))).utcOffset); - assert(SimpleTimeZone.fromISOString("+00:59").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(59))).utcOffset); - assert(SimpleTimeZone.fromISOString("+01:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(60))).utcOffset); - assert(SimpleTimeZone.fromISOString("+01:30").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(90))).utcOffset); - assert(SimpleTimeZone.fromISOString("+02:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(120))).utcOffset); - assert(SimpleTimeZone.fromISOString("+08:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(480))).utcOffset); - assert(SimpleTimeZone.fromISOString("+23:59").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(1439))).utcOffset); - - assert(SimpleTimeZone.fromISOString("-00:01").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-1))).utcOffset); - assert(SimpleTimeZone.fromISOString("-00:10").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-10))).utcOffset); - assert(SimpleTimeZone.fromISOString("-00:59").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-59))).utcOffset); - assert(SimpleTimeZone.fromISOString("-01:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-60))).utcOffset); - assert(SimpleTimeZone.fromISOString("-01:30").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-90))).utcOffset); - assert(SimpleTimeZone.fromISOString("-02:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-120))).utcOffset); - assert(SimpleTimeZone.fromISOString("-08:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-480))).utcOffset); - assert(SimpleTimeZone.fromISOString("-23:59").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-1439))).utcOffset); - - assert(SimpleTimeZone.fromISOString("+0").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(0))).utcOffset); - assert(SimpleTimeZone.fromISOString("+1").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(60))).utcOffset); - assert(SimpleTimeZone.fromISOString("+2").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(120))).utcOffset); - assert(SimpleTimeZone.fromISOString("+23").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(1380))).utcOffset); - assert(SimpleTimeZone.fromISOString("+2").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(120))).utcOffset); - - assert(SimpleTimeZone.fromISOString("+0").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(0))).utcOffset); - assert(SimpleTimeZone.fromISOString("+1").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(60))).utcOffset); - assert(SimpleTimeZone.fromISOString("+2").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(120))).utcOffset); - assert(SimpleTimeZone.fromISOString("+23").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(1380))).utcOffset); - assert(SimpleTimeZone.fromISOString("+1:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(60))).utcOffset); - assert(SimpleTimeZone.fromISOString("+1:01").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(61))).utcOffset); - - assert(SimpleTimeZone.fromISOString("-0").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(0))).utcOffset); - assert(SimpleTimeZone.fromISOString("-1").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-60))).utcOffset); - assert(SimpleTimeZone.fromISOString("-2").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-120))).utcOffset); - assert(SimpleTimeZone.fromISOString("-23").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-1380))).utcOffset); - assert(SimpleTimeZone.fromISOString("-1:00").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-60))).utcOffset); - assert(SimpleTimeZone.fromISOString("-1:01").utcOffset == - (new immutable SimpleTimeZone(dur!"minutes"(-61))).utcOffset); - } - - //Test that converting from an ISO string to a SimpleTimeZone to an ISO String works properly. - unittest - { - static void testSTZ(in string isoString, int expectedOffset, size_t line = __LINE__) - { - auto stz = SimpleTimeZone.fromISOString(isoString); - assert(stz.utcOffset == dur!"minutes"(expectedOffset)); - - auto result = SimpleTimeZone.toISOString(stz.utcOffset); - assert(result == isoString); - } - - testSTZ("+00:00", 0); - testSTZ("+00:01", 1); - testSTZ("+00:10", 10); - testSTZ("+00:59", 59); - testSTZ("+01:00", 60); - testSTZ("+01:30", 90); - testSTZ("+02:00", 120); - testSTZ("+08:00", 480); - testSTZ("+08:00", 480); - testSTZ("+23:59", 1439); - - testSTZ("-00:01", -1); - testSTZ("-00:10", -10); - testSTZ("-00:59", -59); - testSTZ("-01:00", -60); - testSTZ("-01:30", -90); - testSTZ("-02:00", -120); - testSTZ("-08:00", -480); - testSTZ("-08:00", -480); - testSTZ("-23:59", -1439); - } - - - immutable Duration _utcOffset; -} - - -/++ - Represents a time zone from a TZ Database time zone file. Files from the TZ - Database are how Posix systems hold their time zone information. - Unfortunately, Windows does not use the TZ Database. To use the TZ Database, - use $(D PosixTimeZone) (which reads its information from the TZ Database - files on disk) on Windows by providing the TZ Database files and telling - $(D PosixTimeZone.getTimeZone) where the directory holding them is. - - To get a $(D PosixTimeZone), either call $(D PosixTimeZone.getTimeZone) - (which allows specifying the location the time zone files) or call - $(D TimeZone.getTimeZone) (which will give a $(D PosixTimeZone) on Posix - systems and a $(LREF WindowsTimeZone) on Windows systems). - - Note: - Unless your system's local time zone deals with leap seconds (which is - highly unlikely), then the only way to get a time zone which - takes leap seconds into account is to use $(LREF PosixTimeZone) with a - time zone whose name starts with "right/". Those time zone files do - include leap seconds, and $(LREF PosixTimeZone) will take them into account - (though posix systems which use a "right/" time zone as their local time - zone will $(I not) take leap seconds into account even though they're - in the file). - - See_Also: - $(WEB www.iana.org/time-zones, Home of the TZ Database files)
- $(WEB en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time - Zones) - +/ -final class PosixTimeZone : TimeZone -{ - import std.stdio : File; - import std.path : buildNormalizedPath, extension; - import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; - import std.string : strip, representation; - import std.algorithm : countUntil, canFind, startsWith; -public: - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current - dates but will still return true for $(D hasDST) because the time zone - did at some point have DST. - +/ - @property override bool hasDST() @safe const nothrow - { - return _hasDST; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is in effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow - { - assert(!_transitions.empty); - - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - - if(found == -1) - return _transitions.back.ttInfo.isDST; - - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - - return transition.ttInfo.isDST; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - override long utcToTZ(long stdTime) @safe const nothrow - { - assert(!_transitions.empty); - - immutable leapSecs = calculateLeapSeconds(stdTime); - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - - if(found == -1) - return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - - return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow - { - assert(!_transitions.empty); - - immutable leapSecs = calculateLeapSeconds(adjTime); - time_t unixTime = stdTimeToUnixTime(adjTime); - immutable past = unixTime - convert!("days", "seconds")(1); - immutable future = unixTime + convert!("days", "seconds")(1); - - immutable pastFound = countUntil!"b < a.timeT"(_transitions, past); - - if(pastFound == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable futureFound = countUntil!"b < a.timeT"(_transitions[pastFound .. $], future); - immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; - - if(futureFound == 0) - return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); - - immutable futureTrans = futureFound == -1 ? _transitions.back - : _transitions[pastFound + futureFound - 1]; - immutable pastOffset = pastTrans.ttInfo.utcOffset; - - if(pastOffset < futureTrans.ttInfo.utcOffset) - unixTime -= convert!("hours", "seconds")(1); - - immutable found = countUntil!"b < a.timeT"(_transitions[pastFound .. $], unixTime - pastOffset); - - if(found == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; - - return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - - - version(Posix) - { - /++ - The default directory where the TZ Database files are. It's empty - for Windows, since Windows doesn't have them. - +/ - enum defaultTZDatabaseDir = "/usr/share/zoneinfo/"; - } - else version(Windows) - { - /++ The default directory where the TZ Database files are. It's empty - for Windows, since Windows doesn't have them. - +/ - enum defaultTZDatabaseDir = ""; - } - - - /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. The time - zone information is fetched from the TZ Database time zone files in the - given directory. - - See_Also: - $(WEB en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones) - - Params: - name = The TZ Database name of the desired time zone - tzDatabaseDir = The directory where the TZ Database files are - located. Because these files are not located on - Windows systems, provide them - and give their location here to - use $(LREF PosixTimeZone)s. - - Throws: - $(LREF DateTimeException) if the given time zone could not be found or - $(D FileException) if the TZ Database file could not be opened. - - Examples: --------------------- -auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); - -assert(tz.name == "America/Los_Angeles"); -assert(tz.stdName == "PST"); -assert(tz.dstName == "PDT"); --------------------- - +/ - //TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed - // directory. - static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted - { - import std.algorithm : sort; - import std.range : retro; - import std.format : format; - - name = strip(name); - - enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); - enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - - immutable file = buildNormalizedPath(tzDatabaseDir, name); - - enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file))); - enforce(file.isFile, new DateTimeException(format("%s is not a file.", file))); - - auto tzFile = File(file); - immutable gmtZone = file.representation().canFind("GMT"); - - try - { - _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); - - immutable char tzFileVersion = readVal!char(tzFile); - _enforceValidTZFile(tzFileVersion == '\0' || tzFileVersion == '2' || tzFileVersion == '3'); - - { - auto zeroBlock = readVal!(ubyte[])(tzFile, 15); - bool allZeroes = true; - - foreach(val; zeroBlock) - { - if(val != 0) - { - allZeroes = false; - break; - } - } - - _enforceValidTZFile(allZeroes); - } - - - //The number of UTC/local indicators stored in the file. - auto tzh_ttisgmtcnt = readVal!int(tzFile); - - //The number of standard/wall indicators stored in the file. - auto tzh_ttisstdcnt = readVal!int(tzFile); - - //The number of leap seconds for which data is stored in the file. - auto tzh_leapcnt = readVal!int(tzFile); - - //The number of "transition times" for which data is stored in the file. - auto tzh_timecnt = readVal!int(tzFile); - - //The number of "local time types" for which data is stored in the file (must not be zero). - auto tzh_typecnt = readVal!int(tzFile); - _enforceValidTZFile(tzh_typecnt != 0); - - //The number of characters of "timezone abbreviation strings" stored in the file. - auto tzh_charcnt = readVal!int(tzFile); - - //time_ts where DST transitions occur. - auto transitionTimeTs = new long[](tzh_timecnt); - foreach(ref transition; transitionTimeTs) - transition = readVal!int(tzFile); - - //Indices into ttinfo structs indicating the changes - //to be made at the corresponding DST transition. - auto ttInfoIndices = new ubyte[](tzh_timecnt); - foreach(ref ttInfoIndex; ttInfoIndices) - ttInfoIndex = readVal!ubyte(tzFile); - - //ttinfos which give info on DST transitions. - auto tempTTInfos = new TempTTInfo[](tzh_typecnt); - foreach(ref ttInfo; tempTTInfos) - ttInfo = readVal!TempTTInfo(tzFile); - - //The array of time zone abbreviation characters. - auto tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); - - auto leapSeconds = new LeapSecond[](tzh_leapcnt); - foreach(ref leapSecond; leapSeconds) - { - //The time_t when the leap second occurs. - auto timeT = readVal!int(tzFile); - - //The total number of leap seconds to be applied after - //the corresponding leap second. - auto total = readVal!int(tzFile); - - leapSecond = LeapSecond(timeT, total); - } - - //Indicate whether each corresponding DST transition were specified - //in standard time or wall clock time. - auto transitionIsStd = new bool[](tzh_ttisstdcnt); - foreach(ref isStd; transitionIsStd) - isStd = readVal!bool(tzFile); - - //Indicate whether each corresponding DST transition associated with - //local time types are specified in UTC or local time. - auto transitionInUTC = new bool[](tzh_ttisgmtcnt); - foreach(ref inUTC; transitionInUTC) - inUTC = readVal!bool(tzFile); - - _enforceValidTZFile(!tzFile.eof); - - //If version 2 or 3, the information is duplicated in 64-bit. - if(tzFileVersion == '2' || tzFileVersion == '3') - { - _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); - - immutable char tzFileVersion2 = readVal!(char)(tzFile); - _enforceValidTZFile(tzFileVersion2 == '2' || tzFileVersion2 == '3'); - - { - auto zeroBlock = readVal!(ubyte[])(tzFile, 15); - bool allZeroes = true; - - foreach(val; zeroBlock) - { - if(val != 0) - { - allZeroes = false; - break; - } - } - - _enforceValidTZFile(allZeroes); - } - - - //The number of UTC/local indicators stored in the file. - tzh_ttisgmtcnt = readVal!int(tzFile); - - //The number of standard/wall indicators stored in the file. - tzh_ttisstdcnt = readVal!int(tzFile); - - //The number of leap seconds for which data is stored in the file. - tzh_leapcnt = readVal!int(tzFile); - - //The number of "transition times" for which data is stored in the file. - tzh_timecnt = readVal!int(tzFile); - - //The number of "local time types" for which data is stored in the file (must not be zero). - tzh_typecnt = readVal!int(tzFile); - _enforceValidTZFile(tzh_typecnt != 0); - - //The number of characters of "timezone abbreviation strings" stored in the file. - tzh_charcnt = readVal!int(tzFile); - - //time_ts where DST transitions occur. - transitionTimeTs = new long[](tzh_timecnt); - foreach(ref transition; transitionTimeTs) - transition = readVal!long(tzFile); - - //Indices into ttinfo structs indicating the changes - //to be made at the corresponding DST transition. - ttInfoIndices = new ubyte[](tzh_timecnt); - foreach(ref ttInfoIndex; ttInfoIndices) - ttInfoIndex = readVal!ubyte(tzFile); - - //ttinfos which give info on DST transitions. - tempTTInfos = new TempTTInfo[](tzh_typecnt); - foreach(ref ttInfo; tempTTInfos) - ttInfo = readVal!TempTTInfo(tzFile); - - //The array of time zone abbreviation characters. - tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); - - leapSeconds = new LeapSecond[](tzh_leapcnt); - foreach(ref leapSecond; leapSeconds) - { - //The time_t when the leap second occurs. - auto timeT = readVal!long(tzFile); - - //The total number of leap seconds to be applied after - //the corresponding leap second. - auto total = readVal!int(tzFile); - - leapSecond = LeapSecond(timeT, total); - } - - //Indicate whether each corresponding DST transition were specified - //in standard time or wall clock time. - transitionIsStd = new bool[](tzh_ttisstdcnt); - foreach(ref isStd; transitionIsStd) - isStd = readVal!bool(tzFile); - - //Indicate whether each corresponding DST transition associated with - //local time types are specified in UTC or local time. - transitionInUTC = new bool[](tzh_ttisgmtcnt); - foreach(ref inUTC; transitionInUTC) - inUTC = readVal!bool(tzFile); - } - - _enforceValidTZFile(tzFile.readln().strip().empty); - - auto posixEnvStr = tzFile.readln().strip(); - - _enforceValidTZFile(tzFile.readln().strip().empty); - _enforceValidTZFile(tzFile.eof); - - - auto transitionTypes = new TransitionType*[](tempTTInfos.length); - - foreach(i, ref ttype; transitionTypes) - { - bool isStd = false; - - if(i < transitionIsStd.length && !transitionIsStd.empty) - isStd = transitionIsStd[i]; - - bool inUTC = false; - - if(i < transitionInUTC.length && !transitionInUTC.empty) - inUTC = transitionInUTC[i]; - - ttype = new TransitionType(isStd, inUTC); - } - - auto ttInfos = new immutable(TTInfo)*[](tempTTInfos.length); - foreach(i, ref ttInfo; ttInfos) - { - auto tempTTInfo = tempTTInfos[i]; - - if(gmtZone) - tempTTInfo.tt_gmtoff = -tempTTInfo.tt_gmtoff; - - auto abbrevChars = tzAbbrevChars[tempTTInfo.tt_abbrind .. $]; - string abbrev = abbrevChars[0 .. abbrevChars.countUntil('\0')].idup; - - ttInfo = new immutable(TTInfo)(tempTTInfos[i], abbrev); - } - - auto tempTransitions = new TempTransition[](transitionTimeTs.length); - foreach(i, ref tempTransition; tempTransitions) - { - immutable ttiIndex = ttInfoIndices[i]; - auto transitionTimeT = transitionTimeTs[i]; - auto ttype = transitionTypes[ttiIndex]; - auto ttInfo = ttInfos[ttiIndex]; - - tempTransition = TempTransition(transitionTimeT, ttInfo, ttype); - } - - if(tempTransitions.empty) - { - _enforceValidTZFile(ttInfos.length == 1 && transitionTypes.length == 1); - tempTransitions ~= TempTransition(0, ttInfos[0], transitionTypes[0]); - } - - sort!"a.timeT < b.timeT"(tempTransitions); - sort!"a.timeT < b.timeT"(leapSeconds); - - auto transitions = new Transition[](tempTransitions.length); - foreach(i, ref transition; transitions) - { - auto tempTransition = tempTransitions[i]; - auto transitionTimeT = tempTransition.timeT; - auto ttInfo = tempTransition.ttInfo; - auto ttype = tempTransition.ttype; - - _enforceValidTZFile(i == 0 || transitionTimeT > tempTransitions[i - 1].timeT); - - transition = Transition(transitionTimeT, ttInfo); - } - - string stdName; - string dstName; - bool hasDST = false; - - foreach(transition; retro(transitions)) - { - auto ttInfo = transition.ttInfo; - - if(ttInfo.isDST) - { - if(dstName.empty) - dstName = ttInfo.abbrev; - - hasDST = true; - } - else - { - if(stdName.empty) - stdName = ttInfo.abbrev; - } - - if(!stdName.empty && !dstName.empty) - break; - } - - return new immutable PosixTimeZone(transitions.idup, leapSeconds.idup, name, stdName, dstName, hasDST); - } - catch(DateTimeException dte) - throw dte; - catch(Exception e) - throw new DateTimeException("Not a valid TZ data file", __FILE__, __LINE__, e); - } - - /++ - Returns a list of the names of the time zones installed on the system. - - Providing a sub-name narrows down the list of time zones (which - can number in the thousands). For example, - passing in "America" as the sub-name returns only the time zones which - begin with "America". - - Params: - subName = The first part of the desired time zones. - tzDatabaseDir = The directory where the TZ Database files are - located. - - Throws: - $(D FileException) if it fails to read from disk. - +/ - static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @trusted - { - import std.array : appender; - import std.algorithm : sort; - import std.format : format; - - version(Posix) - subName = strip(subName); - else version(Windows) - { - import std.array : replace; - import std.path : dirSeparator; - subName = replace(strip(subName), "/", dirSeparator); - } - - enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); - enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - - auto timezones = appender!(string[])(); - - foreach(DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth)) - { - if(dentry.isFile) - { - auto tzName = dentry.name[tzDatabaseDir.length .. $]; - - if(!tzName.extension().empty || - !tzName.startsWith(subName) || - tzName == "+VERSION") - { - continue; - } - - timezones.put(tzName); - } - } - - sort(timezones.data); - - return timezones.data; - } - - version(Posix) unittest - { - static void testPTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - PosixTimeZone.getTimeZone(tzName); - } - - static void testPTZFailure(string tzName) - { - scope(success) writefln("TZName which was supposed to throw: %s", tzName); - - PosixTimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - - foreach(tzName; tzNames) - assertNotThrown!DateTimeException(testPTZSuccess(tzName)); - - foreach(DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) - { - if(dentry.isFile) - { - auto tzName = dentry.name[defaultTZDatabaseDir.length .. $]; - - if(!canFind(tzNames, tzName)) - assertThrown!DateTimeException(testPTZFailure(tzName)); - } - } - } - - -private: - - /+ - Holds information on when a time transition occures (usually a - transition to or from DST) as well as a pointer to the $(D TTInfo) which - holds information on the utc offset past the transition. - +/ - struct Transition - { - this(long timeT, immutable (TTInfo)* ttInfo) @safe pure - { - this.timeT = timeT; - this.ttInfo = ttInfo; - } - - long timeT; - immutable (TTInfo)* ttInfo; - } - - - /+ - Holds information on when a leap second occurs. - +/ - struct LeapSecond - { - this(long timeT, int total) @safe pure - { - this.timeT = timeT; - this.total = total; - } - - long timeT; - int total; - } - - /+ - Holds information on the utc offset after a transition as well as - whether DST is in effect after that transition. - +/ - struct TTInfo - { - this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure - { - utcOffset = tempTTInfo.tt_gmtoff; - isDST = tempTTInfo.tt_isdst; - this.abbrev = abbrev; - } - - immutable int utcOffset; /// Offset from UTC. - immutable bool isDST; /// Whether DST is in effect. - immutable string abbrev; /// The current abbreviation for the time zone. - } - - - /+ - Struct used to hold information relating to $(D TTInfo) while organizing - the time zone information prior to putting it in its final form. - +/ - struct TempTTInfo - { - this(int gmtOff, bool isDST, ubyte abbrInd) @safe pure - { - tt_gmtoff = gmtOff; - tt_isdst = isDST; - tt_abbrind = abbrInd; - } - - int tt_gmtoff; - bool tt_isdst; - ubyte tt_abbrind; - } - - - /+ - Struct used to hold information relating to $(D Transition) while - organizing the time zone information prior to putting it in its final - form. - +/ - struct TempTransition - { - this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) @safe pure - { - this.timeT = timeT; - this.ttInfo = ttInfo; - this.ttype = ttype; - } - - long timeT; - immutable (TTInfo)* ttInfo; - TransitionType* ttype; - } - - - /+ - Struct used to hold information relating to $(D Transition) and - $(D TTInfo) while organizing the time zone information prior to putting - it in its final form. - +/ - struct TransitionType - { - this(bool isStd, bool inUTC) @safe pure - { - this.isStd = isStd; - this.inUTC = inUTC; - } - - /// Whether the transition is in std time (as opposed to wall clock time). - bool isStd; - - /// Whether the transition is in UTC (as opposed to local time). - bool inUTC; - } - - - /+ - Reads an int from a TZ file. - +/ - static T readVal(T)(ref File tzFile) @trusted - if((isIntegral!T || isSomeChar!T) || is(Unqual!T == bool)) - { - import std.bitmanip; - T[1] buff; - - _enforceValidTZFile(!tzFile.eof); - tzFile.rawRead(buff); - - return bigEndianToNative!T(cast(ubyte[T.sizeof])buff); - } - - /+ - Reads an array of values from a TZ file. - +/ - static T readVal(T)(ref File tzFile, size_t length) @trusted - if(isArray!T) - { - auto buff = new T(length); - - _enforceValidTZFile(!tzFile.eof); - tzFile.rawRead(buff); - - return buff; - } - - - /+ - Reads a $(D TempTTInfo) from a TZ file. - +/ - static T readVal(T)(ref File tzFile) @safe - if(is(T == TempTTInfo)) - { - return TempTTInfo(readVal!int(tzFile), - readVal!bool(tzFile), - readVal!ubyte(tzFile)); - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D result) is false. - +/ - static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure - { - if(!result) - throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); - } - - - int calculateLeapSeconds(long stdTime) @safe const pure nothrow - { - if(_leapSeconds.empty) - return 0; - - immutable unixTime = stdTimeToUnixTime(stdTime); - - if(_leapSeconds.front.timeT >= unixTime) - return 0; - - immutable found = countUntil!"b < a.timeT"(_leapSeconds, unixTime); - - if(found == -1) - return _leapSeconds.back.total; - - immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; - - return leapSecond.total; - } - - - this(immutable Transition[] transitions, - immutable LeapSecond[] leapSeconds, - string name, - string stdName, - string dstName, - bool hasDST) @safe immutable pure - { - if(dstName.empty && !stdName.empty) - dstName = stdName; - else if(stdName.empty && !dstName.empty) - stdName = dstName; - - super(name, stdName, dstName); - - if(!transitions.empty) - { - foreach(i, transition; transitions[0 .. $-1]) - _enforceValidTZFile(transition.timeT < transitions[i + 1].timeT); - } - - foreach(i, leapSecond; leapSeconds) - _enforceValidTZFile(i == leapSeconds.length - 1 || leapSecond.timeT < leapSeconds[i + 1].timeT); - - _transitions = transitions; - _leapSeconds = leapSeconds; - _hasDST = hasDST; - } - - /// List of times when the utc offset changes. - immutable Transition[] _transitions; - - /// List of leap second occurrences. - immutable LeapSecond[] _leapSeconds; - - /// Whether DST is in effect for this time zone at any point in time. - immutable bool _hasDST; -} - - - -version(StdDdoc) -{ - /++ - $(BLUE This class is Windows-Only.) - - Represents a time zone from the Windows registry. Unfortunately, Windows - does not use the TZ Database. To use the TZ Database, use - $(LREF PosixTimeZone) (which reads its information from the TZ Database - files on disk) on Windows by providing the TZ Database files and telling - $(D PosixTimeZone.getTimeZone) where the directory holding them is. - - The TZ Database files and Windows' time zone information frequently - do not match. Windows has many errors with regards to when DST switches - occur (especially for historical dates). Also, the TZ Database files - include far more time zones than Windows does. So, for accurate - time zone information, use the TZ Database files with - $(LREF PosixTimeZone) rather than $(LREF WindowsTimeZone). However, because - $(LREF WindowsTimeZone) uses Windows system calls to deal with the time, - it's far more likely to match the behavior of other Windows programs. - Be aware of the differences when selecting a method. - - $(LREF WindowsTimeZone) does not exist on Posix systems. - - To get a $(LREF WindowsTimeZone), either call - $(D WindowsTimeZone.getTimeZone) or call $(D TimeZone.getTimeZone) - (which will give a $(LREF PosixTimeZone) on Posix systems and a - $(LREF WindowsTimeZone) on Windows systems). - - See_Also: - $(WEB www.iana.org/time-zones, Home of the TZ Database files) - +/ - final class WindowsTimeZone : TimeZone - { - public: - - /++ - Whether this time zone has Daylight Savings Time at any point in - time. Note that for some time zone types it may not have DST for - current dates but will still return true for $(D hasDST) because the - time zone did at some point have DST. - +/ - @property override bool hasDST() @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in UTC time (i.e. std time) and returns whether DST is in - effect in this time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this - time zone. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in UTC time (i.e. std time) and converts it to this time - zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time - zone's time. - +/ - override long utcToTZ(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in this time zone's time and converts it to UTC (i.e. std - time). - - Params: - adjTime = The time in this time zone that needs to be adjusted - to UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow; - - - /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the given name per the Windows time - zone names. The time zone information is fetched from the Windows - registry. - - See_Also: - $(WEB en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List - of Time Zones) - - Params: - name = The TZ Database name of the desired time zone. - - Throws: - $(LREF DateTimeException) if the given time zone could not be - found. - - Examples: - -------------------- - auto tz = TimeZone.getTimeZone("America/Los_Angeles"); - -------------------- - +/ - static immutable(WindowsTimeZone) getTimeZone(string name) @safe; - - - /++ - Returns a list of the names of the time zones installed on the - system. The list returned by WindowsTimeZone contains the Windows - TZ names, not the TZ Database names. However, - $(D TimeZone.getinstalledTZNames) will return the TZ Database names - which are equivalent to the Windows TZ names. - +/ - static string[] getInstalledTZNames() @safe; - - private: - - version(Windows) {} - else - alias void* TIME_ZONE_INFORMATION; - - static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow; - static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow; - static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow; - - this() immutable pure - { - super("", "", ""); - } - } - -} -else version(Windows) -{ - final class WindowsTimeZone : TimeZone - { - import std.format : format; - import std.conv : to; - import std.algorithm : sort; - import std.array : appender; - - public: - - @property override bool hasDST() @safe const nothrow - { - return _tzInfo.DaylightDate.wMonth != 0; - } - - - override bool dstInEffect(long stdTime) @safe const nothrow - { - return _dstInEffect(&_tzInfo, stdTime); - } - - - override long utcToTZ(long stdTime) @safe const nothrow - { - return _utcToTZ(&_tzInfo, stdTime, hasDST); - } - - - override long tzToUTC(long adjTime) @safe const nothrow - { - return _tzToUTC(&_tzInfo, adjTime, hasDST); - } - - - static immutable(WindowsTimeZone) getTimeZone(string name) @trusted - { - import std.utf : toUTF16; - - scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); - - foreach (tzKeyName; baseKey.keyNames) - { - if (tzKeyName != name) - continue; - - scope tzKey = baseKey.getKey(tzKeyName); - - scope stdVal = tzKey.getValue("Std"); - auto stdName = stdVal.value_SZ; - - scope dstVal = tzKey.getValue("Dlt"); - auto dstName = dstVal.value_SZ; - - scope tziVal = tzKey.getValue("TZI"); - auto binVal = tziVal.value_BINARY; - assert(binVal.length == REG_TZI_FORMAT.sizeof); - auto tziFmt = cast(REG_TZI_FORMAT*)binVal.ptr; - - TIME_ZONE_INFORMATION tzInfo; - - auto wstdName = toUTF16(stdName); - auto wdstName = toUTF16(dstName); - auto wstdNameLen = wstdName.length > 32 ? 32 : wstdName.length; - auto wdstNameLen = wdstName.length > 32 ? 32 : wdstName.length; - - tzInfo.Bias = tziFmt.Bias; - tzInfo.StandardName[0 .. wstdNameLen] = wstdName[0 .. wstdNameLen]; - tzInfo.StandardName[wstdNameLen .. $] = '\0'; - tzInfo.StandardDate = tziFmt.StandardDate; - tzInfo.StandardBias = tziFmt.StandardBias; - tzInfo.DaylightName[0 .. wdstNameLen] = wdstName[0 .. wdstNameLen]; - tzInfo.DaylightName[wdstNameLen .. $] = '\0'; - tzInfo.DaylightDate = tziFmt.DaylightDate; - tzInfo.DaylightBias = tziFmt.DaylightBias; - - return new immutable WindowsTimeZone(name, tzInfo); - } - throw new DateTimeException(format("Failed to find time zone: %s", name)); - } - - static string[] getInstalledTZNames() @trusted - { - auto timezones = appender!(string[])(); - - scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); - - foreach (tzKeyName; baseKey.keyNames) - { - timezones.put(tzKeyName); - } - sort(timezones.data); - - return timezones.data; - } - - unittest - { - static void testWTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - WindowsTimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - - foreach(tzName; tzNames) - assertNotThrown!DateTimeException(testWTZSuccess(tzName)); - } - - - private: - - static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) @trusted nothrow - { - try - { - if(tzInfo.DaylightDate.wMonth == 0) - return false; - - auto utcDateTime = cast(DateTime)SysTime(stdTime, UTC()); - - //The limits of what SystemTimeToTzSpecificLocalTime will accept. - if(utcDateTime.year < 1601) - { - if(utcDateTime.month == Month.feb && utcDateTime.day == 29) - utcDateTime.day = 28; - - utcDateTime.year = 1601; - } - else if(utcDateTime.year > 30_827) - { - if(utcDateTime.month == Month.feb && utcDateTime.day == 29) - utcDateTime.day = 28; - - utcDateTime.year = 30_827; - } - - //SystemTimeToTzSpecificLocalTime doesn't act correctly at the - //beginning or end of the year (bleh). Unless some bizarre time - //zone changes DST on January 1st or December 31st, this should - //fix the problem. - if(utcDateTime.month == Month.jan) - { - if(utcDateTime.day == 1) - utcDateTime.day = 2; - } - else if(utcDateTime.month == Month.dec && utcDateTime.day == 31) - utcDateTime.day = 30; - - SYSTEMTIME utcTime = void; - SYSTEMTIME otherTime = void; - - utcTime.wYear = utcDateTime.year; - utcTime.wMonth = utcDateTime.month; - utcTime.wDay = utcDateTime.day; - utcTime.wHour = utcDateTime.hour; - utcTime.wMinute = utcDateTime.minute; - utcTime.wSecond = utcDateTime.second; - utcTime.wMilliseconds = 0; - - immutable result = SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*)tzInfo, - &utcTime, - &otherTime); - assert(result); - - immutable otherDateTime = DateTime(otherTime.wYear, - otherTime.wMonth, - otherTime.wDay, - otherTime.wHour, - otherTime.wMinute, - otherTime.wSecond); - immutable diff = utcDateTime - otherDateTime; - immutable minutes = diff.total!"minutes" - tzInfo.Bias; - - if(minutes == tzInfo.DaylightBias) - return true; - - assert(minutes == tzInfo.StandardBias); - - return false; - } - catch(Exception e) - assert(0, "DateTime's constructor threw."); - } - - unittest - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - foreach(year; [1600, 1601, 30_827, 30_828]) - WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(year, 1, 1)).stdTime); - } - - - static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) @safe nothrow - { - if(hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) - return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); - - return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); - } - - - static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) @trusted nothrow - { - if(hasDST) - { - try - { - bool dstInEffectForLocalDateTime(DateTime localDateTime) - { - //The limits of what SystemTimeToTzSpecificLocalTime will accept. - if(localDateTime.year < 1601) - { - if(localDateTime.month == Month.feb && localDateTime.day == 29) - localDateTime.day = 28; - - localDateTime.year = 1601; - } - else if(localDateTime.year > 30_827) - { - if(localDateTime.month == Month.feb && localDateTime.day == 29) - localDateTime.day = 28; - - localDateTime.year = 30_827; - } - - //SystemTimeToTzSpecificLocalTime doesn't act correctly at the - //beginning or end of the year (bleh). Unless some bizarre time - //zone changes DST on January 1st or December 31st, this should - //fix the problem. - if(localDateTime.month == Month.jan) - { - if(localDateTime.day == 1) - localDateTime.day = 2; - } - else if(localDateTime.month == Month.dec && localDateTime.day == 31) - localDateTime.day = 30; - - SYSTEMTIME utcTime = void; - SYSTEMTIME localTime = void; - - localTime.wYear = localDateTime.year; - localTime.wMonth = localDateTime.month; - localTime.wDay = localDateTime.day; - localTime.wHour = localDateTime.hour; - localTime.wMinute = localDateTime.minute; - localTime.wSecond = localDateTime.second; - localTime.wMilliseconds = 0; - - immutable result = TzSpecificLocalTimeToSystemTime(cast(TIME_ZONE_INFORMATION*)tzInfo, - &localTime, - &utcTime); - assert(result); - - immutable utcDateTime = DateTime(utcTime.wYear, - utcTime.wMonth, - utcTime.wDay, - utcTime.wHour, - utcTime.wMinute, - utcTime.wSecond); - - immutable diff = localDateTime - utcDateTime; - immutable minutes = -tzInfo.Bias - diff.total!"minutes"; - - if(minutes == tzInfo.DaylightBias) - return true; - - assert(minutes == tzInfo.StandardBias); - - return false; - } - - auto localDateTime = cast(DateTime)SysTime(adjTime, UTC()); - auto localDateTimeBefore = localDateTime - dur!"hours"(1); - auto localDateTimeAfter = localDateTime + dur!"hours"(1); - - auto dstInEffectNow = dstInEffectForLocalDateTime(localDateTime); - auto dstInEffectBefore = dstInEffectForLocalDateTime(localDateTimeBefore); - auto dstInEffectAfter = dstInEffectForLocalDateTime(localDateTimeAfter); - - bool isDST; - - if(dstInEffectBefore && dstInEffectNow && dstInEffectAfter) - isDST = true; - else if(!dstInEffectBefore && !dstInEffectNow && !dstInEffectAfter) - isDST = false; - else if(!dstInEffectBefore && dstInEffectAfter) - isDST = false; - else if(dstInEffectBefore && !dstInEffectAfter) - isDST = dstInEffectNow; - else - assert(0, "Bad Logic."); - - if(isDST) - return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); - } - catch(Exception e) - assert(0, "SysTime's constructor threw."); - } - - return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); - } - - - this(string name, TIME_ZONE_INFORMATION tzInfo) @safe immutable pure - { - super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); - _tzInfo = tzInfo; - } - - - TIME_ZONE_INFORMATION _tzInfo; - } -} - - -version(StdDdoc) -{ - /++ - $(BLUE This function is Posix-Only.) - - Sets the local time zone on Posix systems with the TZ - Database name by setting the TZ environment variable. - - Unfortunately, there is no way to do it on Windows using the TZ - Database name, so this function only exists on Posix systems. - +/ - void setTZEnvVar(string tzDatabaseName) @safe nothrow; - - - /++ - $(BLUE This function is Posix-Only.) - - Clears the TZ environment variable. - +/ - void clearTZEnvVar() @safe nothrow; -} -else version(Posix) -{ - void setTZEnvVar(string tzDatabaseName) @trusted nothrow - { - import std.internal.cstring : tempCString; - import std.path : buildNormalizedPath; - - try - { - immutable value = buildNormalizedPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName); - setenv("TZ", value.tempCString(), 1); - tzset(); - } - catch(Exception e) - assert(0, "The impossible happened. setenv or tzset threw."); - } - - - void clearTZEnvVar() @trusted nothrow - { - try - { - unsetenv("TZ"); - tzset(); - } - catch(Exception e) - assert(0, "The impossible happened. unsetenv or tzset threw."); - } -} - - -/++ - Converts the given TZ Database name to the corresponding Windows time zone - name. - - Note that in a few cases, a TZ Dabatase name corresponds to two different - Windows time zone names. So, while in most cases converting from one to the - other and back again will result in the same time zone name started - with, in a few case, it'll get a different name. - - Also, there are far more TZ Database names than Windows time zones, so some - of the more exotic TZ Database names don't have corresponding Windows time - zone names. - - Returns null if the given time zone name cannot be converted. - - See_Also: - $(WEB unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, - Windows <-> TZ Database Name Conversion Table) - - Params: - tzName = The TZ Database name to convert. - +/ -string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc -{ - switch(tzName) - { - case "Africa/Abidjan": return "Greenwich Standard Time"; - case "Africa/Accra": return "Greenwich Standard Time"; - case "Africa/Addis_Ababa": return "E. Africa Standard Time"; - case "Africa/Algiers": return "W. Central Africa Standard Time"; - case "Africa/Asmera": return "E. Africa Standard Time"; - case "Africa/Bamako": return "Greenwich Standard Time"; - case "Africa/Bangui": return "W. Central Africa Standard Time"; - case "Africa/Banjul": return "Greenwich Standard Time"; - case "Africa/Bissau": return "Greenwich Standard Time"; - case "Africa/Blantyre": return "South Africa Standard Time"; - case "Africa/Brazzaville": return "W. Central Africa Standard Time"; - case "Africa/Bujumbura": return "South Africa Standard Time"; - case "Africa/Cairo": return "Egypt Standard Time"; - case "Africa/Casablanca": return "Morocco Standard Time"; - case "Africa/Ceuta": return "Romance Standard Time"; - case "Africa/Conakry": return "Greenwich Standard Time"; - case "Africa/Dakar": return "Greenwich Standard Time"; - case "Africa/Dar_es_Salaam": return "E. Africa Standard Time"; - case "Africa/Djibouti": return "E. Africa Standard Time"; - case "Africa/Douala": return "W. Central Africa Standard Time"; - case "Africa/El_Aaiun": return "Morocco Standard Time"; - case "Africa/Freetown": return "Greenwich Standard Time"; - case "Africa/Gaborone": return "South Africa Standard Time"; - case "Africa/Harare": return "South Africa Standard Time"; - case "Africa/Johannesburg": return "South Africa Standard Time"; - case "Africa/Juba": return "E. Africa Standard Time"; - case "Africa/Kampala": return "E. Africa Standard Time"; - case "Africa/Khartoum": return "E. Africa Standard Time"; - case "Africa/Kigali": return "South Africa Standard Time"; - case "Africa/Kinshasa": return "W. Central Africa Standard Time"; - case "Africa/Lagos": return "W. Central Africa Standard Time"; - case "Africa/Libreville": return "W. Central Africa Standard Time"; - case "Africa/Lome": return "Greenwich Standard Time"; - case "Africa/Luanda": return "W. Central Africa Standard Time"; - case "Africa/Lubumbashi": return "South Africa Standard Time"; - case "Africa/Lusaka": return "South Africa Standard Time"; - case "Africa/Malabo": return "W. Central Africa Standard Time"; - case "Africa/Maputo": return "South Africa Standard Time"; - case "Africa/Maseru": return "South Africa Standard Time"; - case "Africa/Mbabane": return "South Africa Standard Time"; - case "Africa/Mogadishu": return "E. Africa Standard Time"; - case "Africa/Monrovia": return "Greenwich Standard Time"; - case "Africa/Nairobi": return "E. Africa Standard Time"; - case "Africa/Ndjamena": return "W. Central Africa Standard Time"; - case "Africa/Niamey": return "W. Central Africa Standard Time"; - case "Africa/Nouakchott": return "Greenwich Standard Time"; - case "Africa/Ouagadougou": return "Greenwich Standard Time"; - case "Africa/Porto-Novo": return "W. Central Africa Standard Time"; - case "Africa/Sao_Tome": return "Greenwich Standard Time"; - case "Africa/Tripoli": return "Libya Standard Time"; - case "Africa/Tunis": return "W. Central Africa Standard Time"; - case "Africa/Windhoek": return "Namibia Standard Time"; - case "America/Anchorage": return "Alaskan Standard Time"; - case "America/Anguilla": return "SA Western Standard Time"; - case "America/Antigua": return "SA Western Standard Time"; - case "America/Araguaina": return "SA Eastern Standard Time"; - case "America/Argentina/La_Rioja": return "Argentina Standard Time"; - case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time"; - case "America/Argentina/Salta": return "Argentina Standard Time"; - case "America/Argentina/San_Juan": return "Argentina Standard Time"; - case "America/Argentina/San_Luis": return "Argentina Standard Time"; - case "America/Argentina/Tucuman": return "Argentina Standard Time"; - case "America/Argentina/Ushuaia": return "Argentina Standard Time"; - case "America/Aruba": return "SA Western Standard Time"; - case "America/Asuncion": return "Paraguay Standard Time"; - case "America/Bahia": return "Bahia Standard Time"; - case "America/Bahia_Banderas": return "Central Standard Time (Mexico)"; - case "America/Barbados": return "SA Western Standard Time"; - case "America/Belem": return "SA Eastern Standard Time"; - case "America/Belize": return "Central America Standard Time"; - case "America/Blanc-Sablon": return "SA Western Standard Time"; - case "America/Boa_Vista": return "SA Western Standard Time"; - case "America/Bogota": return "SA Pacific Standard Time"; - case "America/Boise": return "Mountain Standard Time"; - case "America/Buenos_Aires": return "Argentina Standard Time"; - case "America/Cambridge_Bay": return "Mountain Standard Time"; - case "America/Campo_Grande": return "Central Brazilian Standard Time"; - case "America/Cancun": return "Central Standard Time (Mexico)"; - case "America/Caracas": return "Venezuela Standard Time"; - case "America/Catamarca": return "Argentina Standard Time"; - case "America/Cayenne": return "SA Eastern Standard Time"; - case "America/Cayman": return "SA Pacific Standard Time"; - case "America/Chicago": return "Central Standard Time"; - case "America/Chihuahua": return "Mountain Standard Time (Mexico)"; - case "America/Coral_Harbour": return "SA Pacific Standard Time"; - case "America/Cordoba": return "Argentina Standard Time"; - case "America/Costa_Rica": return "Central America Standard Time"; - case "America/Creston": return "US Mountain Standard Time"; - case "America/Cuiaba": return "Central Brazilian Standard Time"; - case "America/Curacao": return "SA Western Standard Time"; - case "America/Danmarkshavn": return "UTC"; - case "America/Dawson": return "Pacific Standard Time"; - case "America/Dawson_Creek": return "US Mountain Standard Time"; - case "America/Denver": return "Mountain Standard Time"; - case "America/Detroit": return "Eastern Standard Time"; - case "America/Dominica": return "SA Western Standard Time"; - case "America/Edmonton": return "Mountain Standard Time"; - case "America/Eirunepe": return "SA Pacific Standard Time"; - case "America/El_Salvador": return "Central America Standard Time"; - case "America/Fortaleza": return "SA Eastern Standard Time"; - case "America/Glace_Bay": return "Atlantic Standard Time"; - case "America/Godthab": return "Greenland Standard Time"; - case "America/Goose_Bay": return "Atlantic Standard Time"; - case "America/Grand_Turk": return "SA Western Standard Time"; - case "America/Grenada": return "SA Western Standard Time"; - case "America/Guadeloupe": return "SA Western Standard Time"; - case "America/Guatemala": return "Central America Standard Time"; - case "America/Guayaquil": return "SA Pacific Standard Time"; - case "America/Guyana": return "SA Western Standard Time"; - case "America/Halifax": return "Atlantic Standard Time"; - case "America/Havana": return "Eastern Standard Time"; - case "America/Hermosillo": return "US Mountain Standard Time"; - case "America/Indiana/Knox": return "Central Standard Time"; - case "America/Indiana/Marengo": return "US Eastern Standard Time"; - case "America/Indiana/Petersburg": return "Eastern Standard Time"; - case "America/Indiana/Tell_City": return "Central Standard Time"; - case "America/Indiana/Vevay": return "US Eastern Standard Time"; - case "America/Indiana/Vincennes": return "Eastern Standard Time"; - case "America/Indiana/Winamac": return "Eastern Standard Time"; - case "America/Indianapolis": return "US Eastern Standard Time"; - case "America/Inuvik": return "Mountain Standard Time"; - case "America/Iqaluit": return "Eastern Standard Time"; - case "America/Jamaica": return "SA Pacific Standard Time"; - case "America/Jujuy": return "Argentina Standard Time"; - case "America/Juneau": return "Alaskan Standard Time"; - case "America/Kentucky/Monticello": return "Eastern Standard Time"; - case "America/Kralendijk": return "SA Western Standard Time"; - case "America/La_Paz": return "SA Western Standard Time"; - case "America/Lima": return "SA Pacific Standard Time"; - case "America/Los_Angeles": return "Pacific Standard Time"; - case "America/Louisville": return "Eastern Standard Time"; - case "America/Lower_Princes": return "SA Western Standard Time"; - case "America/Maceio": return "SA Eastern Standard Time"; - case "America/Managua": return "Central America Standard Time"; - case "America/Manaus": return "SA Western Standard Time"; - case "America/Marigot": return "SA Western Standard Time"; - case "America/Martinique": return "SA Western Standard Time"; - case "America/Matamoros": return "Central Standard Time"; - case "America/Mazatlan": return "Mountain Standard Time (Mexico)"; - case "America/Mendoza": return "Argentina Standard Time"; - case "America/Menominee": return "Central Standard Time"; - case "America/Merida": return "Central Standard Time (Mexico)"; - case "America/Mexico_City": return "Central Standard Time (Mexico)"; - case "America/Moncton": return "Atlantic Standard Time"; - case "America/Monterrey": return "Central Standard Time (Mexico)"; - case "America/Montevideo": return "Montevideo Standard Time"; - case "America/Montreal": return "Eastern Standard Time"; - case "America/Montserrat": return "SA Western Standard Time"; - case "America/Nassau": return "Eastern Standard Time"; - case "America/New_York": return "Eastern Standard Time"; - case "America/Nipigon": return "Eastern Standard Time"; - case "America/Nome": return "Alaskan Standard Time"; - case "America/Noronha": return "UTC-02"; - case "America/North_Dakota/Beulah": return "Central Standard Time"; - case "America/North_Dakota/Center": return "Central Standard Time"; - case "America/North_Dakota/New_Salem": return "Central Standard Time"; - case "America/Ojinaga": return "Mountain Standard Time"; - case "America/Panama": return "SA Pacific Standard Time"; - case "America/Pangnirtung": return "Eastern Standard Time"; - case "America/Paramaribo": return "SA Eastern Standard Time"; - case "America/Phoenix": return "US Mountain Standard Time"; - case "America/Port-au-Prince": return "Eastern Standard Time"; - case "America/Port_of_Spain": return "SA Western Standard Time"; - case "America/Porto_Velho": return "SA Western Standard Time"; - case "America/Puerto_Rico": return "SA Western Standard Time"; - case "America/Rainy_River": return "Central Standard Time"; - case "America/Rankin_Inlet": return "Central Standard Time"; - case "America/Recife": return "SA Eastern Standard Time"; - case "America/Regina": return "Canada Central Standard Time"; - case "America/Resolute": return "Central Standard Time"; - case "America/Rio_Branco": return "SA Pacific Standard Time"; - case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)"; - case "America/Santarem": return "SA Eastern Standard Time"; - case "America/Santiago": return "Pacific SA Standard Time"; - case "America/Santo_Domingo": return "SA Western Standard Time"; - case "America/Sao_Paulo": return "E. South America Standard Time"; - case "America/Scoresbysund": return "Azores Standard Time"; - case "America/Sitka": return "Alaskan Standard Time"; - case "America/St_Barthelemy": return "SA Western Standard Time"; - case "America/St_Johns": return "Newfoundland Standard Time"; - case "America/St_Kitts": return "SA Western Standard Time"; - case "America/St_Lucia": return "SA Western Standard Time"; - case "America/St_Thomas": return "SA Western Standard Time"; - case "America/St_Vincent": return "SA Western Standard Time"; - case "America/Swift_Current": return "Canada Central Standard Time"; - case "America/Tegucigalpa": return "Central America Standard Time"; - case "America/Thule": return "Atlantic Standard Time"; - case "America/Thunder_Bay": return "Eastern Standard Time"; - case "America/Tijuana": return "Pacific Standard Time"; - case "America/Toronto": return "Eastern Standard Time"; - case "America/Tortola": return "SA Western Standard Time"; - case "America/Vancouver": return "Pacific Standard Time"; - case "America/Whitehorse": return "Pacific Standard Time"; - case "America/Winnipeg": return "Central Standard Time"; - case "America/Yakutat": return "Alaskan Standard Time"; - case "America/Yellowknife": return "Mountain Standard Time"; - case "Antarctica/Casey": return "W. Australia Standard Time"; - case "Antarctica/Davis": return "SE Asia Standard Time"; - case "Antarctica/DumontDUrville": return "West Pacific Standard Time"; - case "Antarctica/Macquarie": return "Central Pacific Standard Time"; - case "Antarctica/Mawson": return "West Asia Standard Time"; - case "Antarctica/McMurdo": return "New Zealand Standard Time"; - case "Antarctica/Palmer": return "Pacific SA Standard Time"; - case "Antarctica/Rothera": return "SA Eastern Standard Time"; - case "Antarctica/Syowa": return "E. Africa Standard Time"; - case "Antarctica/Vostok": return "Central Asia Standard Time"; - case "Arctic/Longyearbyen": return "W. Europe Standard Time"; - case "Asia/Aden": return "Arab Standard Time"; - case "Asia/Almaty": return "Central Asia Standard Time"; - case "Asia/Amman": return "Jordan Standard Time"; - case "Asia/Anadyr": return "Russia Time Zone 11"; - case "Asia/Aqtau": return "West Asia Standard Time"; - case "Asia/Aqtobe": return "West Asia Standard Time"; - case "Asia/Ashgabat": return "West Asia Standard Time"; - case "Asia/Baghdad": return "Arabic Standard Time"; - case "Asia/Bahrain": return "Arab Standard Time"; - case "Asia/Baku": return "Azerbaijan Standard Time"; - case "Asia/Bangkok": return "SE Asia Standard Time"; - case "Asia/Beirut": return "Middle East Standard Time"; - case "Asia/Bishkek": return "Central Asia Standard Time"; - case "Asia/Brunei": return "Singapore Standard Time"; - case "Asia/Calcutta": return "India Standard Time"; - case "Asia/Chita": return "North Asia East Standard Time"; - case "Asia/Choibalsan": return "Ulaanbaatar Standard Time"; - case "Asia/Colombo": return "Sri Lanka Standard Time"; - case "Asia/Damascus": return "Syria Standard Time"; - case "Asia/Dhaka": return "Bangladesh Standard Time"; - case "Asia/Dili": return "Tokyo Standard Time"; - case "Asia/Dubai": return "Arabian Standard Time"; - case "Asia/Dushanbe": return "West Asia Standard Time"; - case "Asia/Hong_Kong": return "China Standard Time"; - case "Asia/Hovd": return "SE Asia Standard Time"; - case "Asia/Irkutsk": return "North Asia East Standard Time"; - case "Asia/Jakarta": return "SE Asia Standard Time"; - case "Asia/Jayapura": return "Tokyo Standard Time"; - case "Asia/Jerusalem": return "Israel Standard Time"; - case "Asia/Kabul": return "Afghanistan Standard Time"; - case "Asia/Kamchatka": return "Russia Time Zone 11"; - case "Asia/Karachi": return "Pakistan Standard Time"; - case "Asia/Katmandu": return "Nepal Standard Time"; - case "Asia/Khandyga": return "Yakutsk Standard Time"; - case "Asia/Krasnoyarsk": return "North Asia Standard Time"; - case "Asia/Kuala_Lumpur": return "Singapore Standard Time"; - case "Asia/Kuching": return "Singapore Standard Time"; - case "Asia/Kuwait": return "Arab Standard Time"; - case "Asia/Macau": return "China Standard Time"; - case "Asia/Magadan": return "Magadan Standard Time"; - case "Asia/Makassar": return "Singapore Standard Time"; - case "Asia/Manila": return "Singapore Standard Time"; - case "Asia/Muscat": return "Arabian Standard Time"; - case "Asia/Nicosia": return "GTB Standard Time"; - case "Asia/Novokuznetsk": return "North Asia Standard Time"; - case "Asia/Novosibirsk": return "N. Central Asia Standard Time"; - case "Asia/Omsk": return "N. Central Asia Standard Time"; - case "Asia/Oral": return "West Asia Standard Time"; - case "Asia/Phnom_Penh": return "SE Asia Standard Time"; - case "Asia/Pontianak": return "SE Asia Standard Time"; - case "Asia/Pyongyang": return "Korea Standard Time"; - case "Asia/Qatar": return "Arab Standard Time"; - case "Asia/Qyzylorda": return "Central Asia Standard Time"; - case "Asia/Rangoon": return "Myanmar Standard Time"; - case "Asia/Riyadh": return "Arab Standard Time"; - case "Asia/Saigon": return "SE Asia Standard Time"; - case "Asia/Sakhalin": return "Vladivostok Standard Time"; - case "Asia/Samarkand": return "West Asia Standard Time"; - case "Asia/Seoul": return "Korea Standard Time"; - case "Asia/Shanghai": return "China Standard Time"; - case "Asia/Singapore": return "Singapore Standard Time"; - case "Asia/Srednekolymsk": return "Russia Time Zone 10"; - case "Asia/Taipei": return "Taipei Standard Time"; - case "Asia/Tashkent": return "West Asia Standard Time"; - case "Asia/Tbilisi": return "Georgian Standard Time"; - case "Asia/Tehran": return "Iran Standard Time"; - case "Asia/Thimphu": return "Bangladesh Standard Time"; - case "Asia/Tokyo": return "Tokyo Standard Time"; - case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time"; - case "Asia/Urumqi": return "Central Asia Standard Time"; - case "Asia/Ust-Nera": return "Vladivostok Standard Time"; - case "Asia/Vientiane": return "SE Asia Standard Time"; - case "Asia/Vladivostok": return "Vladivostok Standard Time"; - case "Asia/Yakutsk": return "Yakutsk Standard Time"; - case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time"; - case "Asia/Yerevan": return "Caucasus Standard Time"; - case "Atlantic/Azores": return "Azores Standard Time"; - case "Atlantic/Bermuda": return "Atlantic Standard Time"; - case "Atlantic/Canary": return "GMT Standard Time"; - case "Atlantic/Cape_Verde": return "Cape Verde Standard Time"; - case "Atlantic/Faeroe": return "GMT Standard Time"; - case "Atlantic/Madeira": return "GMT Standard Time"; - case "Atlantic/Reykjavik": return "Greenwich Standard Time"; - case "Atlantic/South_Georgia": return "UTC-02"; - case "Atlantic/St_Helena": return "Greenwich Standard Time"; - case "Atlantic/Stanley": return "SA Eastern Standard Time"; - case "Australia/Adelaide": return "Cen. Australia Standard Time"; - case "Australia/Brisbane": return "E. Australia Standard Time"; - case "Australia/Broken_Hill": return "Cen. Australia Standard Time"; - case "Australia/Currie": return "Tasmania Standard Time"; - case "Australia/Darwin": return "AUS Central Standard Time"; - case "Australia/Hobart": return "Tasmania Standard Time"; - case "Australia/Lindeman": return "E. Australia Standard Time"; - case "Australia/Melbourne": return "AUS Eastern Standard Time"; - case "Australia/Perth": return "W. Australia Standard Time"; - case "Australia/Sydney": return "AUS Eastern Standard Time"; - case "CST6CDT": return "Central Standard Time"; - case "EST5EDT": return "Eastern Standard Time"; - case "Etc/GMT": return "UTC"; - case "Etc/GMT+1": return "Cape Verde Standard Time"; - case "Etc/GMT+10": return "Hawaiian Standard Time"; - case "Etc/GMT+11": return "UTC-11"; - case "Etc/GMT+12": return "Dateline Standard Time"; - case "Etc/GMT+2": return "UTC-02"; - case "Etc/GMT+3": return "SA Eastern Standard Time"; - case "Etc/GMT+4": return "SA Western Standard Time"; - case "Etc/GMT+5": return "SA Pacific Standard Time"; - case "Etc/GMT+6": return "Central America Standard Time"; - case "Etc/GMT+7": return "US Mountain Standard Time"; - case "Etc/GMT-1": return "W. Central Africa Standard Time"; - case "Etc/GMT-10": return "West Pacific Standard Time"; - case "Etc/GMT-11": return "Central Pacific Standard Time"; - case "Etc/GMT-12": return "UTC+12"; - case "Etc/GMT-13": return "Tonga Standard Time"; - case "Etc/GMT-14": return "Line Islands Standard Time"; - case "Etc/GMT-2": return "South Africa Standard Time"; - case "Etc/GMT-3": return "E. Africa Standard Time"; - case "Etc/GMT-4": return "Arabian Standard Time"; - case "Etc/GMT-5": return "West Asia Standard Time"; - case "Etc/GMT-6": return "Central Asia Standard Time"; - case "Etc/GMT-7": return "SE Asia Standard Time"; - case "Etc/GMT-8": return "Singapore Standard Time"; - case "Etc/GMT-9": return "Tokyo Standard Time"; - case "Europe/Amsterdam": return "W. Europe Standard Time"; - case "Europe/Andorra": return "W. Europe Standard Time"; - case "Europe/Athens": return "GTB Standard Time"; - case "Europe/Belgrade": return "Central Europe Standard Time"; - case "Europe/Berlin": return "W. Europe Standard Time"; - case "Europe/Bratislava": return "Central Europe Standard Time"; - case "Europe/Brussels": return "Romance Standard Time"; - case "Europe/Bucharest": return "GTB Standard Time"; - case "Europe/Budapest": return "Central Europe Standard Time"; - case "Europe/Busingen": return "W. Europe Standard Time"; - case "Europe/Chisinau": return "GTB Standard Time"; - case "Europe/Copenhagen": return "Romance Standard Time"; - case "Europe/Dublin": return "GMT Standard Time"; - case "Europe/Gibraltar": return "W. Europe Standard Time"; - case "Europe/Guernsey": return "GMT Standard Time"; - case "Europe/Helsinki": return "FLE Standard Time"; - case "Europe/Isle_of_Man": return "GMT Standard Time"; - case "Europe/Istanbul": return "Turkey Standard Time"; - case "Europe/Jersey": return "GMT Standard Time"; - case "Europe/Kaliningrad": return "Kaliningrad Standard Time"; - case "Europe/Kiev": return "FLE Standard Time"; - case "Europe/Lisbon": return "GMT Standard Time"; - case "Europe/Ljubljana": return "Central Europe Standard Time"; - case "Europe/London": return "GMT Standard Time"; - case "Europe/Luxembourg": return "W. Europe Standard Time"; - case "Europe/Madrid": return "Romance Standard Time"; - case "Europe/Malta": return "W. Europe Standard Time"; - case "Europe/Mariehamn": return "FLE Standard Time"; - case "Europe/Minsk": return "Belarus Standard Time"; - case "Europe/Monaco": return "W. Europe Standard Time"; - case "Europe/Moscow": return "Russian Standard Time"; - case "Europe/Oslo": return "W. Europe Standard Time"; - case "Europe/Paris": return "Romance Standard Time"; - case "Europe/Podgorica": return "Central Europe Standard Time"; - case "Europe/Prague": return "Central Europe Standard Time"; - case "Europe/Riga": return "FLE Standard Time"; - case "Europe/Rome": return "W. Europe Standard Time"; - case "Europe/Samara": return "Russia Time Zone 3"; - case "Europe/San_Marino": return "W. Europe Standard Time"; - case "Europe/Sarajevo": return "Central European Standard Time"; - case "Europe/Simferopol": return "Russian Standard Time"; - case "Europe/Skopje": return "Central European Standard Time"; - case "Europe/Sofia": return "FLE Standard Time"; - case "Europe/Stockholm": return "W. Europe Standard Time"; - case "Europe/Tallinn": return "FLE Standard Time"; - case "Europe/Tirane": return "Central Europe Standard Time"; - case "Europe/Uzhgorod": return "FLE Standard Time"; - case "Europe/Vaduz": return "W. Europe Standard Time"; - case "Europe/Vatican": return "W. Europe Standard Time"; - case "Europe/Vienna": return "W. Europe Standard Time"; - case "Europe/Vilnius": return "FLE Standard Time"; - case "Europe/Volgograd": return "Russian Standard Time"; - case "Europe/Warsaw": return "Central European Standard Time"; - case "Europe/Zagreb": return "Central European Standard Time"; - case "Europe/Zaporozhye": return "FLE Standard Time"; - case "Europe/Zurich": return "W. Europe Standard Time"; - case "Indian/Antananarivo": return "E. Africa Standard Time"; - case "Indian/Chagos": return "Central Asia Standard Time"; - case "Indian/Christmas": return "SE Asia Standard Time"; - case "Indian/Cocos": return "Myanmar Standard Time"; - case "Indian/Comoro": return "E. Africa Standard Time"; - case "Indian/Kerguelen": return "West Asia Standard Time"; - case "Indian/Mahe": return "Mauritius Standard Time"; - case "Indian/Maldives": return "West Asia Standard Time"; - case "Indian/Mauritius": return "Mauritius Standard Time"; - case "Indian/Mayotte": return "E. Africa Standard Time"; - case "Indian/Reunion": return "Mauritius Standard Time"; - case "MST7MDT": return "Mountain Standard Time"; - case "PST8PDT": return "Pacific Standard Time"; - case "Pacific/Apia": return "Samoa Standard Time"; - case "Pacific/Auckland": return "New Zealand Standard Time"; - case "Pacific/Efate": return "Central Pacific Standard Time"; - case "Pacific/Enderbury": return "Tonga Standard Time"; - case "Pacific/Fakaofo": return "Tonga Standard Time"; - case "Pacific/Fiji": return "Fiji Standard Time"; - case "Pacific/Funafuti": return "UTC+12"; - case "Pacific/Galapagos": return "Central America Standard Time"; - case "Pacific/Guadalcanal": return "Central Pacific Standard Time"; - case "Pacific/Guam": return "West Pacific Standard Time"; - case "Pacific/Honolulu": return "Hawaiian Standard Time"; - case "Pacific/Johnston": return "Hawaiian Standard Time"; - case "Pacific/Kiritimati": return "Line Islands Standard Time"; - case "Pacific/Kosrae": return "Central Pacific Standard Time"; - case "Pacific/Kwajalein": return "UTC+12"; - case "Pacific/Majuro": return "UTC+12"; - case "Pacific/Midway": return "UTC-11"; - case "Pacific/Nauru": return "UTC+12"; - case "Pacific/Niue": return "UTC-11"; - case "Pacific/Noumea": return "Central Pacific Standard Time"; - case "Pacific/Pago_Pago": return "UTC-11"; - case "Pacific/Palau": return "Tokyo Standard Time"; - case "Pacific/Ponape": return "Central Pacific Standard Time"; - case "Pacific/Port_Moresby": return "West Pacific Standard Time"; - case "Pacific/Rarotonga": return "Hawaiian Standard Time"; - case "Pacific/Saipan": return "West Pacific Standard Time"; - case "Pacific/Tahiti": return "Hawaiian Standard Time"; - case "Pacific/Tarawa": return "UTC+12"; - case "Pacific/Tongatapu": return "Tonga Standard Time"; - case "Pacific/Truk": return "West Pacific Standard Time"; - case "Pacific/Wake": return "UTC+12"; - case "Pacific/Wallis": return "UTC+12"; - default: return null; - } -} - -version(Windows) unittest -{ - import std.format : format; - foreach(tzName; TimeZone.getInstalledTZNames()) - assert(tzDatabaseNameToWindowsTZName(tzName) !is null, format("TZName which failed: %s", tzName)); -} - - -/++ - Converts the given Windows time zone name to a corresponding TZ Database - name. - - Returns null if the given time zone name cannot be converted. - - See_Also: - $(WEB unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, - Windows <-> TZ Database Name Conversion Table) - - Params: - tzName = The TZ Database name to convert. - +/ -string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc -{ - switch(tzName) - { - case "AUS Central Standard Time": return "Australia/Darwin"; - case "AUS Eastern Standard Time": return "Australia/Sydney"; - case "Afghanistan Standard Time": return "Asia/Kabul"; - case "Alaskan Standard Time": return "America/Anchorage"; - case "Arab Standard Time": return "Asia/Riyadh"; - case "Arabian Standard Time": return "Asia/Dubai"; - case "Arabic Standard Time": return "Asia/Baghdad"; - case "Argentina Standard Time": return "America/Buenos_Aires"; - case "Atlantic Standard Time": return "America/Halifax"; - case "Azerbaijan Standard Time": return "Asia/Baku"; - case "Azores Standard Time": return "Atlantic/Azores"; - case "Bahia Standard Time": return "America/Bahia"; - case "Bangladesh Standard Time": return "Asia/Dhaka"; - case "Belarus Standard Time": return "Europe/Minsk"; - case "Canada Central Standard Time": return "America/Regina"; - case "Cape Verde Standard Time": return "Atlantic/Cape_Verde"; - case "Caucasus Standard Time": return "Asia/Yerevan"; - case "Cen. Australia Standard Time": return "Australia/Adelaide"; - case "Central America Standard Time": return "America/Guatemala"; - case "Central Asia Standard Time": return "Asia/Almaty"; - case "Central Brazilian Standard Time": return "America/Cuiaba"; - case "Central Europe Standard Time": return "Europe/Budapest"; - case "Central European Standard Time": return "Europe/Warsaw"; - case "Central Pacific Standard Time": return "Pacific/Guadalcanal"; - case "Central Standard Time": return "America/Chicago"; - case "Central Standard Time (Mexico)": return "America/Mexico_City"; - case "China Standard Time": return "Asia/Shanghai"; - case "Dateline Standard Time": return "Etc/GMT+12"; - case "E. Africa Standard Time": return "Africa/Nairobi"; - case "E. Australia Standard Time": return "Australia/Brisbane"; - // This doesn't appear to be in the current stuff from MS, but the autotester - // is failing without it (probably because its time zone data hasn't been - // updated recently enough). - case "E. Europe Standard Time": return "Europe/Minsk"; - case "E. South America Standard Time": return "America/Sao_Paulo"; - case "Eastern Standard Time": return "America/New_York"; - case "Egypt Standard Time": return "Africa/Cairo"; - case "Ekaterinburg Standard Time": return "Asia/Yekaterinburg"; - case "FLE Standard Time": return "Europe/Kiev"; - case "Fiji Standard Time": return "Pacific/Fiji"; - case "GMT Standard Time": return "Europe/London"; - case "GTB Standard Time": return "Europe/Athens"; - case "Georgian Standard Time": return "Asia/Tbilisi"; - case "Greenland Standard Time": return "America/Godthab"; - case "Greenwich Standard Time": return "Atlantic/Reykjavik"; - case "Hawaiian Standard Time": return "Pacific/Honolulu"; - case "India Standard Time": return "Asia/Calcutta"; - case "Iran Standard Time": return "Asia/Tehran"; - case "Israel Standard Time": return "Asia/Jerusalem"; - case "Jordan Standard Time": return "Asia/Amman"; - case "Kaliningrad Standard Time": return "Europe/Kaliningrad"; - // Same as with E. Europe Standard Time. - case "Kamchatka Standard Time": return "Asia/Kamchatka"; - case "Korea Standard Time": return "Asia/Seoul"; - case "Libya Standard Time": return "Africa/Tripoli"; - case "Line Islands Standard Time": return "Pacific/Kiritimati"; - case "Magadan Standard Time": return "Asia/Magadan"; - case "Mauritius Standard Time": return "Indian/Mauritius"; - // Same as with E. Europe Standard Time. - case "Mexico Standard Time": return "America/Mexico_City"; - // Same as with E. Europe Standard Time. - case "Mexico Standard Time 2": return "America/Chihuahua"; - // Same as with E. Europe Standard Time. - case "Mid-Atlantic Standard Time": return "Etc/GMT+2"; - case "Middle East Standard Time": return "Asia/Beirut"; - case "Montevideo Standard Time": return "America/Montevideo"; - case "Morocco Standard Time": return "Africa/Casablanca"; - case "Mountain Standard Time": return "America/Denver"; - case "Mountain Standard Time (Mexico)": return "America/Chihuahua"; - case "Myanmar Standard Time": return "Asia/Rangoon"; - case "N. Central Asia Standard Time": return "Asia/Novosibirsk"; - case "Namibia Standard Time": return "Africa/Windhoek"; - case "Nepal Standard Time": return "Asia/Katmandu"; - case "New Zealand Standard Time": return "Pacific/Auckland"; - case "Newfoundland Standard Time": return "America/St_Johns"; - case "North Asia East Standard Time": return "Asia/Irkutsk"; - case "North Asia Standard Time": return "Asia/Krasnoyarsk"; - case "Pacific SA Standard Time": return "America/Santiago"; - case "Pacific Standard Time": return "America/Los_Angeles"; - case "Pacific Standard Time (Mexico)": return "America/Santa_Isabel"; - case "Pakistan Standard Time": return "Asia/Karachi"; - case "Paraguay Standard Time": return "America/Asuncion"; - case "Romance Standard Time": return "Europe/Paris"; - case "Russia Time Zone 10": return "Asia/Srednekolymsk"; - case "Russia Time Zone 11": return "Asia/Anadyr"; - case "Russia Time Zone 3": return "Europe/Samara"; - case "Russian Standard Time": return "Europe/Moscow"; - case "SA Eastern Standard Time": return "America/Cayenne"; - case "SA Pacific Standard Time": return "America/Bogota"; - case "SA Western Standard Time": return "America/La_Paz"; - case "SE Asia Standard Time": return "Asia/Bangkok"; - case "Samoa Standard Time": return "Pacific/Apia"; - case "Singapore Standard Time": return "Asia/Singapore"; - case "South Africa Standard Time": return "Africa/Johannesburg"; - case "Sri Lanka Standard Time": return "Asia/Colombo"; - case "Syria Standard Time": return "Asia/Damascus"; - case "Taipei Standard Time": return "Asia/Taipei"; - case "Tasmania Standard Time": return "Australia/Hobart"; - case "Tokyo Standard Time": return "Asia/Tokyo"; - case "Tonga Standard Time": return "Pacific/Tongatapu"; - case "Turkey Standard Time": return "Europe/Istanbul"; - case "US Eastern Standard Time": return "America/Indianapolis"; - case "US Mountain Standard Time": return "America/Phoenix"; - case "UTC": return "Etc/GMT"; - case "UTC+12": return "Etc/GMT-12"; - case "UTC-02": return "Etc/GMT+2"; - case "UTC-11": return "Etc/GMT+11"; - case "Ulaanbaatar Standard Time": return "Asia/Ulaanbaatar"; - case "Venezuela Standard Time": return "America/Caracas"; - case "Vladivostok Standard Time": return "Asia/Vladivostok"; - case "W. Australia Standard Time": return "Australia/Perth"; - case "W. Central Africa Standard Time": return "Africa/Lagos"; - case "W. Europe Standard Time": return "Europe/Berlin"; - case "West Asia Standard Time": return "Asia/Tashkent"; - case "West Pacific Standard Time": return "Pacific/Port_Moresby"; - case "Yakutsk Standard Time": return "Asia/Yakutsk"; - default: return null; - } -} - -version(Windows) unittest -{ - import std.format : format; - foreach(tzName; WindowsTimeZone.getInstalledTZNames()) - assert(windowsTZNameToTZDatabaseName(tzName) !is null, format("TZName which failed: %s", tzName)); -} - - -//============================================================================== -// Section with StopWatch and Benchmark Code. -//============================================================================== - -/++ - $(D StopWatch) measures time as precisely as possible. - - This class uses a high-performance counter. On Windows systems, it uses - $(D QueryPerformanceCounter), and on Posix systems, it uses - $(D clock_gettime) if available, and $(D gettimeofday) otherwise. - - But the precision of $(D StopWatch) differs from system to system. It is - impossible to for it to be the same from system to system since the precision - of the system clock varies from system to system, and other system-dependent - and situation-dependent stuff (such as the overhead of a context switch - between threads) can also affect $(D StopWatch)'s accuracy. - - Examples: --------------------- -void foo() -{ - StopWatch sw; - enum n = 100; - TickDuration[n] times; - TickDuration last = TickDuration.from!"seconds"(0); - foreach(i; 0..n) - { - sw.start(); //start/resume mesuring. - foreach(unused; 0..1_000_000) - bar(); - sw.stop(); //stop/pause measuring. - //Return value of peek() after having stopped are the always same. - writeln((i + 1) * 1_000_000, " times done, lap time: ", - sw.peek().msecs, "[ms]"); - times[i] = sw.peek() - last; - last = sw.peek(); - } - real sum = 0; - // To know the number of seconds, - // use properties of TickDuration. - // (seconds, msecs, usecs, hnsecs) - foreach(t; times) - sum += t.hnsecs; - writeln("Average time: ", sum/n, " hnsecs"); -} --------------------- - +/ -@safe struct StopWatch -{ -public: - //Verify Example - @safe unittest - { - void writeln(S...)(S args){} - static void bar() {} - - StopWatch sw; - enum n = 100; - TickDuration[n] times; - TickDuration last = TickDuration.from!"seconds"(0); - foreach(i; 0..n) - { - sw.start(); //start/resume mesuring. - foreach(unused; 0..1_000_000) - bar(); - sw.stop(); //stop/pause measuring. - //Return value of peek() after having stopped are the always same. - writeln((i + 1) * 1_000_000, " times done, lap time: ", - sw.peek().msecs, "[ms]"); - times[i] = sw.peek() - last; - last = sw.peek(); - } - real sum = 0; - // To get the number of seconds, - // use properties of TickDuration. - // (seconds, msecs, usecs, hnsecs) - foreach(t; times) - sum += t.hnsecs; - writeln("Average time: ", sum/n, " hnsecs"); - } - - /++ - Auto start with constructor. - +/ - this(AutoStart autostart) - { - if(autostart) - start(); - } - - @safe unittest - { - auto sw = StopWatch(AutoStart.yes); - sw.stop(); - } - - - /// - bool opEquals(const StopWatch rhs) const pure nothrow - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(const ref StopWatch rhs) const pure nothrow - { - return _timeStart == rhs._timeStart && - _timeMeasured == rhs._timeMeasured; - } - - - /++ - Resets the stop watch. - +/ - void reset() - { - if(_flagStarted) - { - // Set current system time if StopWatch is measuring. - _timeStart = Clock.currSystemTick; - } - else - { - // Set zero if StopWatch is not measuring. - _timeStart.length = 0; - } - - _timeMeasured.length = 0; - } - - @safe unittest - { - StopWatch sw; - sw.start(); - sw.stop(); - sw.reset(); - assert(sw.peek().to!("seconds", real)() == 0); - } - - - /++ - Starts the stop watch. - +/ - void start() - { - assert(!_flagStarted); - _flagStarted = true; - _timeStart = Clock.currSystemTick; - } - - @trusted unittest - { - StopWatch sw; - sw.start(); - auto t1 = sw.peek(); - bool doublestart = true; - try - sw.start(); - catch(AssertError e) - doublestart = false; - assert(!doublestart); - sw.stop(); - assert((t1 - sw.peek()).to!("seconds", real)() <= 0); - } - - - /++ - Stops the stop watch. - +/ - void stop() - { - assert(_flagStarted); - _flagStarted = false; - _timeMeasured += Clock.currSystemTick - _timeStart; - } - - @trusted unittest - { - StopWatch sw; - sw.start(); - sw.stop(); - auto t1 = sw.peek(); - bool doublestop = true; - try - sw.stop(); - catch(AssertError e) - doublestop = false; - assert(!doublestop); - assert((t1 - sw.peek()).to!("seconds", real)() == 0); - } - - - /++ - Peek at the amount of time which has passed since the stop watch was - started. - +/ - TickDuration peek() const - { - if(_flagStarted) - return Clock.currSystemTick - _timeStart + _timeMeasured; - - return _timeMeasured; - } - - @safe unittest - { - StopWatch sw; - sw.start(); - auto t1 = sw.peek(); - sw.stop(); - auto t2 = sw.peek(); - auto t3 = sw.peek(); - assert(t1 <= t2); - assert(t2 == t3); - } - - - /++ - Set the amount of time which has been measured since the stop watch was - started. - +/ - void setMeasured(TickDuration d) - { - reset(); - _timeMeasured = d; - } - - @safe unittest - { - StopWatch sw; - TickDuration t0; - t0.length = 100; - sw.setMeasured(t0); - auto t1 = sw.peek(); - assert(t0 == t1); - } - - - /++ - Confirm whether this stopwatch is measuring time. - +/ - bool running() @property const pure nothrow - { - return _flagStarted; - } - - @safe unittest - { - StopWatch sw1; - assert(!sw1.running); - sw1.start(); - assert(sw1.running); - sw1.stop(); - assert(!sw1.running); - StopWatch sw2 = AutoStart.yes; - assert(sw2.running); - sw2.stop(); - assert(!sw2.running); - sw2.start(); - assert(sw2.running); - } - - - - -private: - - // true if observing. - bool _flagStarted = false; - - // TickDuration at the time of StopWatch starting measurement. - TickDuration _timeStart; - - // Total time that StopWatch ran. - TickDuration _timeMeasured; -} - - -/++ - Benchmarks code for speed assessment and comparison. - - Params: - fun = aliases of callable objects (e.g. function names). Each should - take no arguments. - n = The number of times each function is to be executed. - - Returns: - The amount of time (as a $(CXREF time, TickDuration)) that it took to - call each function $(D n) times. The first value is the length of time - that it took to call $(D fun[0]) $(D n) times. The second value is the - length of time it took to call $(D fun[1]) $(D n) times. Etc. - - Note that casting the TickDurations to $(CXREF time, Duration)s will make - the results easier to deal with (and it may change in the future that - benchmark will return an array of Durations rather than TickDurations). - - See_Also: - $(LREF measureTime) - +/ -TickDuration[fun.length] benchmark(fun...)(uint n) -{ - TickDuration[fun.length] result; - StopWatch sw; - sw.start(); - - foreach(i, unused; fun) - { - sw.reset(); - foreach(j; 0 .. n) - fun[i](); - result[i] = sw.peek(); - } - - return result; -} - -/// -unittest -{ - import std.conv : to; - int a; - void f0() {} - void f1() {auto b = a;} - void f2() {auto b = to!string(a);} - auto r = benchmark!(f0, f1, f2)(10_000); - auto f0Result = to!Duration(r[0]); // time f0 took to run 10,000 times - auto f1Result = to!Duration(r[1]); // time f1 took to run 10,000 times - auto f2Result = to!Duration(r[2]); // time f2 took to run 10,000 times -} - -@safe unittest -{ - int a; - void f0() {} - //void f1() {auto b = to!(string)(a);} - void f2() {auto b = (a);} - auto r = benchmark!(f0, f2)(100); -} - - -/++ - Return value of benchmark with two functions comparing. - +/ -@safe struct ComparingBenchmarkResult -{ - /++ - Evaluation value - - This returns the evaluation value of performance as the ratio of - baseFunc's time over targetFunc's time. If performance is high, this - returns a high value. - +/ - @property real point() const pure nothrow - { - return _baseTime.length / cast(const real)_targetTime.length; - } - - - /++ - The time required of the base function - +/ - @property public TickDuration baseTime() const pure nothrow - { - return _baseTime; - } - - - /++ - The time required of the target function - +/ - @property public TickDuration targetTime() const pure nothrow - { - return _targetTime; - } - -private: - - this(TickDuration baseTime, TickDuration targetTime) pure nothrow - { - _baseTime = baseTime; - _targetTime = targetTime; - } - - TickDuration _baseTime; - TickDuration _targetTime; -} - - -/++ - Benchmark with two functions comparing. - - Params: - baseFunc = The function to become the base of the speed. - targetFunc = The function that wants to measure speed. - times = The number of times each function is to be executed. - - Examples: --------------------- -void f1() { - // ... -} -void f2() { - // ... -} - -void main() { - auto b = comparingBenchmark!(f1, f2, 0x80); - writeln(b.point); -} --------------------- - +/ -ComparingBenchmarkResult comparingBenchmark(alias baseFunc, - alias targetFunc, - int times = 0xfff)() -{ - auto t = benchmark!(baseFunc, targetFunc)(times); - return ComparingBenchmarkResult(t[0], t[1]); -} - -@safe unittest -{ - void f1x() {} - void f2x() {} - @safe void f1o() {} - @safe void f2o() {} - auto b1 = comparingBenchmark!(f1o, f2o, 1)(); // OK - //static auto b2 = comparingBenchmark!(f1x, f2x, 1); // NG -} - -unittest -{ - void f1x() {} - void f2x() {} - @safe void f1o() {} - @safe void f2o() {} - auto b1 = comparingBenchmark!(f1o, f2o, 1)(); // OK - auto b2 = comparingBenchmark!(f1x, f2x, 1)(); // OK -} - -//Bug# 8450 -unittest -{ - @safe void safeFunc() {} - @trusted void trustFunc() {} - @system void sysFunc() {} - auto safeResult = comparingBenchmark!((){safeFunc();}, (){safeFunc();})(); - auto trustResult = comparingBenchmark!((){trustFunc();}, (){trustFunc();})(); - auto sysResult = comparingBenchmark!((){sysFunc();}, (){sysFunc();})(); - auto mixedResult1 = comparingBenchmark!((){safeFunc();}, (){trustFunc();})(); - auto mixedResult2 = comparingBenchmark!((){trustFunc();}, (){sysFunc();})(); - auto mixedResult3 = comparingBenchmark!((){safeFunc();}, (){sysFunc();})(); -} - - -//============================================================================== -// Section with public helper functions and templates. -//============================================================================== - - -/++ - Whether the given type defines all of the necessary functions for it to - function as a time point. - +/ -template isTimePoint(T) -{ - enum isTimePoint = hasMin!T && - hasMax!T && - hasOverloadedOpBinaryWithDuration!T && - hasOverloadedOpAssignWithDuration!T && - hasOverloadedOpBinaryWithSelf!T; -} - -unittest -{ - static assert(isTimePoint!(Date)); - static assert(isTimePoint!(DateTime)); - static assert(isTimePoint!(TimeOfDay)); - static assert(isTimePoint!(SysTime)); - static assert(isTimePoint!(const Date)); - static assert(isTimePoint!(const DateTime)); - static assert(isTimePoint!(const TimeOfDay)); - static assert(isTimePoint!(const SysTime)); - static assert(isTimePoint!(immutable Date)); - static assert(isTimePoint!(immutable DateTime)); - static assert(isTimePoint!(immutable TimeOfDay)); - static assert(isTimePoint!(immutable SysTime)); -} - -/++ - Whether the given Gregorian Year is a leap year. - - Params: - year = The year to to be tested. - +/ -static bool yearIsLeapYear(int year) @safe pure nothrow -{ - if(year % 400 == 0) - return true; - - if(year % 100 == 0) - return false; - - return year % 4 == 0; -} - -unittest -{ - import std.format : format; - foreach(year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, - 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) - { - assert(!yearIsLeapYear(year), format("year: %s.", year)); - assert(!yearIsLeapYear(-year), format("year: %s.", year)); - } - - foreach(year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) - { - assert(yearIsLeapYear(year), format("year: %s.", year)); - assert(yearIsLeapYear(-year), format("year: %s.", year)); - } -} - -/++ - Converts a $(D time_t) (which uses midnight, January 1st, 1970 UTC as its - epoch and seconds as its units) to std time (which uses midnight, - January 1st, 1 A.D. UTC and hnsecs as its units). - - Params: - unixTime = The $(D time_t) to convert. - +/ -long unixTimeToStdTime(time_t unixTime) @safe pure nothrow -{ - return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime); - -} - -unittest -{ - assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); //Midnight, January 1st, 1970 - assert(unixTimeToStdTime(86_400) == 621_355_968_000_000_000L + 864_000_000_000L); //Midnight, January 2nd, 1970 - assert(unixTimeToStdTime(-86_400) == 621_355_968_000_000_000L - 864_000_000_000L); //Midnight, December 31st, 1969 - - assert(unixTimeToStdTime(0) == (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); - assert(unixTimeToStdTime(0) == (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); -} - - -/++ - Converts std time (which uses midnight, January 1st, 1 A.D. UTC as its epoch - and hnsecs as its units) to $(D time_t) (which uses midnight, January 1st, - 1970 UTC as its epoch and seconds as its units). If $(D time_t) is 32 bits, - rather than 64, and the result can't fit in a 32-bit value, then the closest - value that can be held in 32 bits will be used (so $(D time_t.max) if it - goes over and $(D time_t.min) if it goes under). - - Note: - While Windows systems require that $(D time_t) be non-negative (in spite - of $(D time_t) being signed), this function still returns negative - numbers on Windows, since it's more flexible to allow negative time_t - for those who need it. If on Windows and using the - standard C functions or Win32 API functions which take a $(D time_t), - check whether the return value of - $(D stdTimeToUnixTime) is non-negative. - - Params: - stdTime = The std time to convert. - +/ -time_t stdTimeToUnixTime(long stdTime) @safe pure nothrow -{ - immutable unixTime = convert!("hnsecs", "seconds")(stdTime - 621_355_968_000_000_000L); - - static if(time_t.sizeof >= long.sizeof) - return cast(time_t)unixTime; - else - { - if(unixTime > 0) - { - if(unixTime > time_t.max) - return time_t.max; - return cast(time_t)unixTime; - } - - if(unixTime < time_t.min) - return time_t.min; - - return cast(time_t)unixTime; - } -} - -unittest -{ - assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0); //Midnight, January 1st, 1970 - assert(stdTimeToUnixTime(621_355_968_000_000_000L + 864_000_000_000L) == 86_400); //Midnight, January 2nd, 1970 - assert(stdTimeToUnixTime(621_355_968_000_000_000L - 864_000_000_000L) == -86_400); //Midnight, December 31st, 1969 - - assert(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs") == 0); - assert(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs") == 0); -} - - -version(StdDdoc) -{ - version(Windows) {} - else - { - alias void* SYSTEMTIME; - alias void* FILETIME; - } - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(D SYSTEMTIME) struct to a $(LREF SysTime). - - Params: - st = The $(D SYSTEMTIME) struct to convert. - tz = The time zone that the time in the $(D SYSTEMTIME) struct is - assumed to be (if the $(D SYSTEMTIME) was supplied by a Windows - system call, the $(D SYSTEMTIME) will either be in local time - or UTC, depending on the call). - - Throws: - $(LREF DateTimeException) if the given $(D SYSTEMTIME) will not fit in - a $(LREF SysTime), which is highly unlikely to happen given that - $(D SysTime.max) is in 29,228 A.D. and the maximum $(D SYSTEMTIME) - is in 30,827 A.D. - +/ - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(LREF SysTime) to a $(D SYSTEMTIME) struct. - - The $(D SYSTEMTIME) which is returned will be set using the given - $(LREF SysTime)'s time zone, so to get the $(D SYSTEMTIME) in - UTC, set the $(LREF SysTime)'s time zone to UTC. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a - $(D SYSTEMTIME). This will only happen if the $(LREF SysTime)'s date is - prior to 1601 A.D. - +/ - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(D FILETIME) struct to the number of hnsecs since midnight, - January 1st, 1 A.D. - - Params: - ft = The $(D FILETIME) struct to convert. - - Throws: - $(LREF DateTimeException) if the given $(D FILETIME) cannot be - represented as the return value. - +/ - long FILETIMEToStdTime(const FILETIME* ft) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(D FILETIME) struct to a $(LREF SysTime). - - Params: - ft = The $(D FILETIME) struct to convert. - tz = The time zone that the $(LREF SysTime) will be in ($(D FILETIME)s - are in UTC). - - Throws: - $(LREF DateTimeException) if the given $(D FILETIME) will not fit in a - $(LREF SysTime). - +/ - SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a number of hnsecs since midnight, January 1st, 1 A.D. to a - $(D FILETIME) struct. - - Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. - - Throws: - $(LREF DateTimeException) if the given value will not fit in a - $(D FILETIME). - +/ - FILETIME stdTimeToFILETIME(long stdTime) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(LREF SysTime) to a $(D FILETIME) struct. - - $(D FILETIME)s are always in UTC. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a - $(D FILETIME). - +/ - FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; -} -else version(Windows) -{ - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe - { - const max = SysTime.max; - - static void throwLaterThanMax() - { - throw new DateTimeException("The given SYSTEMTIME is for a date greater than SysTime.max."); - } - - if(st.wYear > max.year) - throwLaterThanMax(); - else if(st.wYear == max.year) - { - if(st.wMonth > max.month) - throwLaterThanMax(); - else if(st.wMonth == max.month) - { - if(st.wDay > max.day) - throwLaterThanMax(); - else if(st.wDay == max.day) - { - if(st.wHour > max.hour) - throwLaterThanMax(); - else if(st.wHour == max.hour) - { - if(st.wMinute > max.minute) - throwLaterThanMax(); - else if(st.wMinute == max.minute) - { - if(st.wSecond > max.second) - throwLaterThanMax(); - else if(st.wSecond == max.second) - { - if(st.wMilliseconds > max.fracSecs.total!"msecs") - throwLaterThanMax(); - } - } - } - } - } - } - - auto dt = DateTime(st.wYear, st.wMonth, st.wDay, - st.wHour, st.wMinute, st.wSecond); - - return SysTime(dt, msecs(st.wMilliseconds), tz); - } - - unittest - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - auto converted = SYSTEMTIMEToSysTime(&st, UTC()); - - assert(abs((converted - sysTime)) <= dur!"seconds"(2)); - } - - - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe - { - immutable dt = cast(DateTime)sysTime; - - if(dt.year < 1601) - throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the year 1601."); - - SYSTEMTIME st; - - st.wYear = dt.year; - st.wMonth = dt.month; - st.wDayOfWeek = dt.dayOfWeek; - st.wDay = dt.day; - st.wHour = dt.hour; - st.wMinute = dt.minute; - st.wSecond = dt.second; - st.wMilliseconds = cast(ushort)sysTime.fracSecs.total!"msecs"; - - return st; - } - - unittest - { - SYSTEMTIME st = void; - GetSystemTime(&st); - auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); - - SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); - - assert(st.wYear == result.wYear); - assert(st.wMonth == result.wMonth); - assert(st.wDayOfWeek == result.wDayOfWeek); - assert(st.wDay == result.wDay); - assert(st.wHour == result.wHour); - assert(st.wMinute == result.wMinute); - assert(st.wSecond == result.wSecond); - assert(st.wMilliseconds == result.wMilliseconds); - } - - private enum hnsecsFrom1601 = 504_911_232_000_000_000L; - - long FILETIMEToStdTime(const FILETIME* ft) @safe - { - ULARGE_INTEGER ul; - ul.HighPart = ft.dwHighDateTime; - ul.LowPart = ft.dwLowDateTime; - ulong tempHNSecs = ul.QuadPart; - - if(tempHNSecs > long.max - hnsecsFrom1601) - throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); - - return cast(long)tempHNSecs + hnsecsFrom1601; - } - - SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe - { - auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); - sysTime.timezone = tz; - - return sysTime; - } - - unittest - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - - auto converted = FILETIMEToSysTime(&ft); - - assert(abs((converted - sysTime)) <= dur!"seconds"(2)); - } - - - FILETIME stdTimeToFILETIME(long stdTime) @safe - { - if(stdTime < hnsecsFrom1601) - throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); - - ULARGE_INTEGER ul; - ul.QuadPart = cast(ulong)stdTime - hnsecsFrom1601; - - FILETIME ft; - ft.dwHighDateTime = ul.HighPart; - ft.dwLowDateTime = ul.LowPart; - - return ft; - } - - FILETIME SysTimeToFILETIME(SysTime sysTime) @safe - { - return stdTimeToFILETIME(sysTime.stdTime); - } - - unittest - { - SYSTEMTIME st = void; - GetSystemTime(&st); - - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - auto sysTime = FILETIMEToSysTime(&ft, UTC()); - - FILETIME result = SysTimeToFILETIME(sysTime); - - assert(ft.dwLowDateTime == result.dwLowDateTime); - assert(ft.dwHighDateTime == result.dwHighDateTime); - } -} - - -/++ - Type representing the DOS file date/time format. - +/ -alias uint DosFileTime; - -/++ - Converts from DOS file date/time to $(LREF SysTime). - - Params: - dft = The DOS file time to convert. - tz = The time zone which the DOS file time is assumed to be in. - - Throws: - $(LREF DateTimeException) if the $(D DosFileTime) is invalid. - +/ -SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe -{ - uint dt = cast(uint)dft; - - if(dt == 0) - throw new DateTimeException("Invalid DosFileTime."); - - int year = ((dt >> 25) & 0x7F) + 1980; - int month = ((dt >> 21) & 0x0F); // 1..12 - int dayOfMonth = ((dt >> 16) & 0x1F); // 1..31 - int hour = (dt >> 11) & 0x1F; // 0..23 - int minute = (dt >> 5) & 0x3F; // 0..59 - int second = (dt << 1) & 0x3E; // 0..58 (in 2 second increments) - - try - return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); - catch(DateTimeException dte) - throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); -} - -unittest -{ - assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == - SysTime(DateTime(1980, 1, 1, 0, 0, 0))); - - assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == - SysTime(DateTime(2107, 12, 31, 23, 59, 58))); - - assert(DosFileTimeToSysTime(0x3E3F8456) == - SysTime(DateTime(2011, 1, 31, 16, 34, 44))); -} - - -/++ - Converts from $(LREF SysTime) to DOS file date/time. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) cannot be converted to - a $(D DosFileTime). - +/ -DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe -{ - auto dateTime = cast(DateTime)sysTime; - - if(dateTime.year < 1980) - throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); - - if(dateTime.year > 2107) - throw new DateTimeException("DOS File Times cannot hold dates past 2107."); - - uint retval = 0; - retval = (dateTime.year - 1980) << 25; - retval |= (dateTime.month & 0x0F) << 21; - retval |= (dateTime.day & 0x1F) << 16; - retval |= (dateTime.hour & 0x1F) << 11; - retval |= (dateTime.minute & 0x3F) << 5; - retval |= (dateTime.second >> 1) & 0x1F; - - return cast(DosFileTime)retval; -} - -unittest -{ - assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == - 0b00000000001000010000000000000000); - - assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == - 0b11111111100111111011111101111101); - - assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == - 0x3E3F8456); -} - - -/++ - The given array of $(D char) or random-access range of $(D char) or - $(D ubyte) is expected to be in the format specified in - $(WEB tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the - grammar rule $(I date-time). It is the date-time format commonly used in - internet messages such as e-mail and HTTP. The corresponding - $(LREF SysTime) will be returned. - - RFC 822 was the original spec (hence the function's name), whereas RFC 5322 - is the current spec. - - The day of the week is ignored beyond verifying that it's a valid day of the - week, as the day of the week can be inferred from the date. It is not - checked whether the given day of the week matches the actual day of the week - of the given date (though it is technically invalid per the spec if the - day of the week doesn't match the actual day of the week of the given date). - - If the time zone is $(D "-0000") (or considered to be equivalent to - $(D "-0000") by section 4.3 of the spec), a $(LREF SimpleTimeZone) with a - utc offset of $(D 0) is used rather than $(LREF UTC), whereas $(D "+0000") - uses $(LREF UTC). - - Note that because $(LREF SysTime) does not currently support having a second - value of 60 (as is sometimes done for leap seconds), if the date-time value - does have a value of 60 for the seconds, it is treated as 59. - - The one area in which this function violates RFC 5322 is that it accepts - $(D "\n") in folding whitespace in the place of $(D "\r\n"), because the - HTTP spec requires it. - - Throws: - $(LREF DateTimeException) if the given string doesn't follow the grammar - for a date-time field or if the resulting $(LREF SysTime) is invalid. - +/ -SysTime parseRFC822DateTime()(in char[] value) @safe -{ - import std.string : representation; - return parseRFC822DateTime(value.representation); -} - -/++ Ditto +/ -SysTime parseRFC822DateTime(R)(R value) @safe - if(isRandomAccessRange!R && hasSlicing!R && hasLength!R && - (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) -{ - import std.functional : not; - import std.ascii : isDigit; - import std.typecons : Rebindable; - import std.string : capitalize, format; - import std.conv : to; - import std.algorithm : find, all; - - void stripAndCheckLen(R valueBefore, size_t minLen, size_t line = __LINE__) - { - value = _stripCFWS(valueBefore); - if(value.length < minLen) - throw new DateTimeException("date-time value too short", __FILE__, line); - } - stripAndCheckLen(value, "7Dec1200:00A".length); - - static if(isArray!R && (is(ElementEncodingType!R == char) || is(ElementEncodingType!R == ubyte))) - { - static string sliceAsString(R str) @trusted - { - return cast(string)str; - } - } - else - { - char[4] temp; - char[] sliceAsString(R str) @trusted - { - size_t i = 0; - foreach(c; str) - temp[i++] = cast(char)c; - return temp[0 .. str.length]; - } - } - - // day-of-week - if(std.ascii.isAlpha(value[0])) - { - auto dowStr = sliceAsString(value[0 .. 3]); - switch(dowStr) - { - foreach(dow; EnumMembers!DayOfWeek) - { - enum dowC = capitalize(to!string(dow)); - case dowC: - goto afterDoW; - } - default: throw new DateTimeException(format("Invalid day-of-week: %s", dowStr)); - } -afterDoW: stripAndCheckLen(value[3 .. value.length], ",7Dec1200:00A".length); - if(value[0] != ',') - throw new DateTimeException("day-of-week missing comma"); - stripAndCheckLen(value[1 .. value.length], "7Dec1200:00A".length); - } - - // day - immutable digits = std.ascii.isDigit(value[1]) ? 2 : 1; - immutable day = _convDigits!short(value[0 .. digits]); - if(day == -1) - throw new DateTimeException("Invalid day"); - stripAndCheckLen(value[digits .. value.length], "Dec1200:00A".length); - - // month - Month month; - { - auto monStr = sliceAsString(value[0 .. 3]); - switch(monStr) - { - foreach(mon; EnumMembers!Month) - { - enum monC = capitalize(to!string(mon)); - case monC: - { - month = mon; - goto afterMon; - } - } - default: throw new DateTimeException(format("Invalid month: %s", monStr)); - } -afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); - } - - // year - auto found = value[2 .. value.length].find!(not!(std.ascii.isDigit))(); - size_t yearLen = value.length - found.length; - if(found.length == 0) - throw new DateTimeException("Invalid year"); - if(found[0] == ':') - yearLen -= 2; - auto year = _convDigits!short(value[0 .. yearLen]); - if(year < 1900) - { - if(year == -1) - throw new DateTimeException("Invalid year"); - if(yearLen < 4) - { - if(yearLen == 3) - year += 1900; - else if(yearLen == 2) - year += year < 50 ? 2000 : 1900; - else - throw new DateTimeException("Invalid year. Too few digits."); - } - else - throw new DateTimeException("Invalid year. Cannot be earlier than 1900."); - } - stripAndCheckLen(value[yearLen .. value.length], "00:00A".length); - - // hour - immutable hour = _convDigits!short(value[0 .. 2]); - stripAndCheckLen(value[2 .. value.length], ":00A".length); - if(value[0] != ':') - throw new DateTimeException("Invalid hour"); - stripAndCheckLen(value[1 .. value.length], "00A".length); - - // minute - immutable minute = _convDigits!short(value[0 .. 2]); - stripAndCheckLen(value[2 .. value.length], "A".length); - - // second - short second; - if(value[0] == ':') - { - stripAndCheckLen(value[1 .. value.length], "00A".length); - second = _convDigits!short(value[0 .. 2]); - // this is just if/until SysTime is sorted out to fully support leap seconds - if(second == 60) - second = 59; - stripAndCheckLen(value[2 .. value.length], "A".length); - } - - immutable(TimeZone) parseTZ(int sign) - { - if(value.length < 5) - throw new DateTimeException("Invalid timezone"); - immutable zoneHours = _convDigits!short(value[1 .. 3]); - immutable zoneMinutes = _convDigits!short(value[3 .. 5]); - if(zoneHours == -1 || zoneMinutes == -1 || zoneMinutes > 59) - throw new DateTimeException("Invalid timezone"); - value = value[5 .. value.length]; - immutable utcOffset = (dur!"hours"(zoneHours) + dur!"minutes"(zoneMinutes)) * sign; - if(utcOffset == Duration.zero) - { - return sign == 1 ? cast(immutable(TimeZone))UTC() - : cast(immutable(TimeZone))new immutable SimpleTimeZone(Duration.zero); - } - return new immutable(SimpleTimeZone)(utcOffset); - } - - // zone - Rebindable!(immutable TimeZone) tz; - if(value[0] == '-') - tz = parseTZ(-1); - else if(value[0] == '+') - tz = parseTZ(1); - else - { - // obs-zone - immutable tzLen = value.length - find(value, ' ', '\t', '(')[0].length; - switch(sliceAsString(value[0 .. tzLen <= 4 ? tzLen : 4])) - { - case "UT": case "GMT": tz = UTC(); break; - case "EST": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; - case "EDT": tz = new immutable SimpleTimeZone(dur!"hours"(-4)); break; - case "CST": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; - case "CDT": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; - case "MST": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; - case "MDT": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; - case "PST": tz = new immutable SimpleTimeZone(dur!"hours"(-8)); break; - case "PDT": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; - case "J": case "j": throw new DateTimeException("Invalid timezone"); - default: - { - if(all!(std.ascii.isAlpha)(value[0 .. tzLen])) - { - tz = new immutable SimpleTimeZone(Duration.zero); - break; - } - throw new DateTimeException("Invalid timezone"); - } - } - value = value[tzLen .. value.length]; - } - - // This is kind of arbitrary. Technically, nothing but CFWS is legal past - // the end of the timezone, but we don't want to be picky about that in a - // function that's just parsing rather than validating. So, the idea here is - // that if the next character is printable (and not part of CFWS), then it - // might be part of the timezone and thus affect what the timezone was - // supposed to be, so we'll throw, but otherwise, we'll just ignore it. - if(!value.empty && std.ascii.isPrintable(value[0]) && value[0] != ' ' && value[0] != '(') - throw new DateTimeException("Invalid timezone"); - - try - return SysTime(DateTime(year, month, day, hour, minute, second), tz); - catch(DateTimeException dte) - throw new DateTimeException("date-time format is correct, but the resulting SysTime is invalid.", dte); -} - -/// -unittest -{ - auto tz = new immutable SimpleTimeZone(hours(-8)); - assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") == - SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz)); - - assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") == - SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC())); - - auto badStr = "29 Feb 2001 12:17:16 +0200"; - assertThrown!DateTimeException(parseRFC822DateTime(badStr)); -} - -version(unittest) void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__) -{ - import std.string; - import std.format : format; - auto value = cr(str); - auto result = parseRFC822DateTime(value); - if(result != expected) - throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line); -} - -version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LINE__) -{ - try - parseRFC822DateTime(cr(str)); - catch(DateTimeException) - return; - throw new AssertError("No DateTimeException was thrown", __FILE__, line); -} - -unittest -{ - import std.algorithm; - import std.ascii; - import std.format : format; - import std.range; - import std.string; - import std.typecons; - import std.typetuple; - - static struct Rand3Letters - { - enum empty = false; - @property auto front() { return _mon; } - void popFront() - { - import std.random; - _mon = rndGen.map!(a => letters[a % letters.length])().take(3).array().assumeUnique(); - } - string _mon; - static auto start() { Rand3Letters retval; retval.popFront(); return retval; } - } - - foreach(cr; TypeTuple!(function(string a){return cast(char[])a;}, - function(string a){return cast(ubyte[])a;}, - function(string a){return a;}, - function(string a){return map!(b => cast(char)b)(a.representation);})) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - scope(failure) writeln(typeof(cr).stringof); - alias testParse822!cr test; - alias testBadParse822!cr testBad; - - immutable std1 = DateTime(2012, 12, 21, 13, 14, 15); - immutable std2 = DateTime(2012, 12, 21, 13, 14, 0); - immutable dst1 = DateTime(1976, 7, 4, 5, 4, 22); - immutable dst2 = DateTime(1976, 7, 4, 5, 4, 0); - - test("21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); - test("21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); - test("Fri, 21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); - test("Fri, 21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); - - test("04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - test("04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - - test("4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - test("4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - - auto badTZ = new immutable SimpleTimeZone(Duration.zero); - test("21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); - test("21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); - test("Fri, 21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); - test("Fri, 21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); - - test("04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - test("04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - - test("4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - test("4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - - auto pst = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto pdt = new immutable SimpleTimeZone(dur!"hours"(-7)); - test("21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); - test("21 Dec 2012 13:14 -0800", SysTime(std2, pst)); - test("Fri, 21 Dec 2012 13:14 -0800", SysTime(std2, pst)); - test("Fri, 21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); - - test("04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - test("04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - - test("4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - test("4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - - auto cet = new immutable SimpleTimeZone(dur!"hours"(1)); - auto cest = new immutable SimpleTimeZone(dur!"hours"(2)); - test("21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - test("21 Dec 2012 13:14 +0100", SysTime(std2, cet)); - test("Fri, 21 Dec 2012 13:14 +0100", SysTime(std2, cet)); - test("Fri, 21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - - test("04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - test("04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - - test("4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - test("4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - - // dst and std times are switched in the Southern Hemisphere which is why the - // time zone names and DateTime variables don't match. - auto cstStd = new immutable SimpleTimeZone(dur!"hours"(9) + dur!"minutes"(30)); - auto cstDST = new immutable SimpleTimeZone(dur!"hours"(10) + dur!"minutes"(30)); - test("21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - test("21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); - test("Fri, 21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); - test("Fri, 21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - - test("04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - - test("4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - - foreach(int i, mon; _monthNames) - { - test(format("17 %s 2012 00:05:02 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 2), UTC())); - test(format("17 %s 2012 00:05 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 0), UTC())); - } - - import std.uni; - foreach(mon; chain(_monthNames[].map!(a => toLower(a))(), - _monthNames[].map!(a => toUpper(a))(), - ["Jam", "Jen", "Fec", "Fdb", "Mas", "Mbr", "Aps", "Aqr", "Mai", "Miy", - "Jum", "Jbn", "Jup", "Jal", "Aur", "Apg", "Sem", "Sap", "Ocm", "Odt", - "Nom", "Nav", "Dem", "Dac"], - Rand3Letters.start().take(20))) - { - scope(failure) writefln("Month: %s", mon); - testBad(format("17 %s 2012 00:05:02 +0000", mon)); - testBad(format("17 %s 2012 00:05 +0000", mon)); - } - - immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - - { - auto start = SysTime(DateTime(2012, 11, 11, 9, 42, 0), UTC()); - int day = 11; - - foreach(int i, dow; daysOfWeekNames) - { - auto curr = start + dur!"days"(i); - test(format("%s, %s Nov 2012 09:42:00 +0000", dow, day), curr); - test(format("%s, %s Nov 2012 09:42 +0000", dow, day++), curr); - - // Whether the day of the week matches the date is ignored. - test(format("%s, 11 Nov 2012 09:42:00 +0000", dow), start); - test(format("%s, 11 Nov 2012 09:42 +0000", dow), start); - } - } - - foreach(dow; chain(daysOfWeekNames[].map!(a => toLower(a))(), - daysOfWeekNames[].map!(a => toUpper(a))(), - ["Sum", "Spn", "Mom", "Man", "Tuf", "Tae", "Wem", "Wdd", "The", "Tur", - "Fro", "Fai", "San", "Sut"], - Rand3Letters.start().take(20))) - { - scope(failure) writefln("Day of Week: %s", dow); - testBad(format("%s, 11 Nov 2012 09:42:00 +0000", dow)); - testBad(format("%s, 11 Nov 2012 09:42 +0000", dow)); - } - - testBad("31 Dec 1899 23:59:59 +0000"); - test("01 Jan 1900 00:00:00 +0000", SysTime(Date(1900, 1, 1), UTC())); - test("01 Jan 1900 00:00:00 -0000", SysTime(Date(1900, 1, 1), - new immutable SimpleTimeZone(Duration.zero))); - test("01 Jan 1900 00:00:00 -0700", SysTime(Date(1900, 1, 1), - new immutable SimpleTimeZone(dur!"hours"(-7)))); - - { - auto st1 = SysTime(Date(1900, 1, 1), UTC()); - auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); - foreach(i; 1900 .. 2102) - { - test(format("1 Jan %05d 00:00 +0000", i), st1); - test(format("1 Jan %05d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - st1.year = 9998; - st2.year = 9998; - foreach(i; 9998 .. 11_002) - { - test(format("1 Jan %05d 00:00 +0000", i), st1); - test(format("1 Jan %05d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - testBad("12 Feb 1907 23:17:09 0000"); - testBad("12 Feb 1907 23:17:09 +000"); - testBad("12 Feb 1907 23:17:09 -000"); - testBad("12 Feb 1907 23:17:09 +00000"); - testBad("12 Feb 1907 23:17:09 -00000"); - testBad("12 Feb 1907 23:17:09 +A"); - testBad("12 Feb 1907 23:17:09 +PST"); - testBad("12 Feb 1907 23:17:09 -A"); - testBad("12 Feb 1907 23:17:09 -PST"); - - // test trailing stuff that gets ignored - { - foreach(c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) - { - scope(failure) writefln("c: %d", c); - test(format("21 Dec 2012 13:14:15 +0000%c", cast(char)c), SysTime(std1, UTC())); - test(format("21 Dec 2012 13:14:15 +0000%c ", cast(char)c), SysTime(std1, UTC())); - test(format("21 Dec 2012 13:14:15 +0000%chello", cast(char)c), SysTime(std1, UTC())); - } - } - - // test trailing stuff that doesn't get ignored - { - foreach(c; chain(iota(33, '('), iota('(' + 1, 127))) - { - scope(failure) writefln("c: %d", c); - testBad(format("21 Dec 2012 13:14:15 +0000%c", cast(char)c)); - testBad(format("21 Dec 2012 13:14:15 +0000%c ", cast(char)c)); - testBad(format("21 Dec 2012 13:14:15 +0000%chello", cast(char)c)); - } - } - - testBad("32 Jan 2012 12:13:14 -0800"); - testBad("31 Jan 2012 24:13:14 -0800"); - testBad("31 Jan 2012 12:60:14 -0800"); - testBad("31 Jan 2012 12:13:61 -0800"); - testBad("31 Jan 2012 12:13:14 -0860"); - test("31 Jan 2012 12:13:14 -0859", - SysTime(DateTime(2012, 1, 31, 12, 13, 14), - new immutable SimpleTimeZone(dur!"hours"(-8) + dur!"minutes"(-59)))); - - // leap-seconds - test("21 Dec 2012 15:59:60 -0800", SysTime(DateTime(2012, 12, 21, 15, 59, 59), pst)); - - // FWS - test("Sun,4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun,4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("Sun,4 Jul 1976 05:04 +0930 (foo)", SysTime(dst2, cstStd)); - test("Sun,4 Jul 1976 05:04:22 +0930 (foo)", SysTime(dst1, cstStd)); - test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04 \r\n +0930 \r\n (foo)", SysTime(dst2, cstStd)); - test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04:22 \r\n +0930 \r\n (foo)", SysTime(dst1, cstStd)); - - auto str = "01 Jan 2012 12:13:14 -0800 "; - test(str, SysTime(DateTime(2012, 1, 1, 12, 13, 14), new immutable SimpleTimeZone(hours(-8)))); - foreach(i; 0 .. str.length) - { - auto currStr = str.dup; - currStr[i] = 'x'; - scope(failure) writefln("failed: %s", currStr); - testBad(cast(string)currStr); - } - foreach(i; 2 .. str.length) - { - auto currStr = str[0 .. $ - i]; - scope(failure) writefln("failed: %s", currStr); - testBad(cast(string)currStr); - testBad((cast(string)currStr) ~ " "); - } - }(); -} - -// Obsolete Format per section 4.3 of RFC 5322. -unittest -{ - import std.algorithm; - import std.ascii; - import std.format : format; - import std.range; - import std.string; - import std.typecons; - import std.typetuple; - - auto std1 = SysTime(DateTime(2012, 12, 21, 13, 14, 15), UTC()); - auto std2 = SysTime(DateTime(2012, 12, 21, 13, 14, 0), UTC()); - auto std3 = SysTime(DateTime(1912, 12, 21, 13, 14, 15), UTC()); - auto std4 = SysTime(DateTime(1912, 12, 21, 13, 14, 0), UTC()); - auto dst1 = SysTime(DateTime(1976, 7, 4, 5, 4, 22), UTC()); - auto dst2 = SysTime(DateTime(1976, 7, 4, 5, 4, 0), UTC()); - auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC()); - auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC()); - - foreach(cr; TypeTuple!(function(string a){return cast(char[])a;}, - function(string a){return cast(ubyte[])a;}, - function(string a){return a;}, - function(string a){return map!(b => cast(char)b)(a.representation);})) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - scope(failure) writeln(typeof(cr).stringof); - alias testParse822!cr test; - { - auto list = ["", " ", " \r\n\t", "\t\r\n (hello world( frien(dog)) silly \r\n ) \t\t \r\n ()", - " \n ", "\t\n\t", " \n\t (foo) \n (bar) \r\n (baz) \n "]; - - foreach(i, cfws; list) - { - scope(failure) writefln("i: %s", i); - - test(format("%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - test(format("%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - - test(format("%1$s04%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04:22 +0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - test(format("%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - - test(format("%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s76 05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s76 05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - test(format("%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - - test(format("%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); - test(format("%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); - test(format("%1$sSat%1$s,%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); - test(format("%1$sSun%1$s,%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); - } - } - - // test years of 1, 2, and 3 digits. - { - auto st1 = SysTime(Date(2000, 1, 1), UTC()); - auto st2 = SysTime(Date(2000, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); - foreach(i; 0 .. 50) - { - test(format("1 Jan %02d 00:00 GMT", i), st1); - test(format("1 Jan %02d 00:00 -1200", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - { - auto st1 = SysTime(Date(1950, 1, 1), UTC()); - auto st2 = SysTime(Date(1950, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); - foreach(i; 50 .. 100) - { - test(format("1 Jan %02d 00:00 GMT", i), st1); - test(format("1 Jan %02d 00:00 -1200", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - { - auto st1 = SysTime(Date(1900, 1, 1), UTC()); - auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); - foreach(i; 0 .. 1000) - { - test(format("1 Jan %03d 00:00 GMT", i), st1); - test(format("1 Jan %03d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - foreach(i; 0 .. 10) - { - auto str1 = cr(format("1 Jan %d 00:00 GMT", i)); - auto str2 = cr(format("1 Jan %d 00:00 -1200", i)); - assertThrown!DateTimeException(parseRFC822DateTime(str1)); - assertThrown!DateTimeException(parseRFC822DateTime(str1)); - } - - // test time zones - { - auto dt = DateTime(1982, 05, 03, 12, 22, 04); - test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC())); - test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC())); - test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); - test("Wed, 03 May 1982 12:22:04 EDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-4)))); - test("Wed, 03 May 1982 12:22:04 CST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); - test("Wed, 03 May 1982 12:22:04 CDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); - test("Wed, 03 May 1982 12:22:04 MST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - test("Wed, 03 May 1982 12:22:04 MDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); - test("Wed, 03 May 1982 12:22:04 PST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-8)))); - test("Wed, 03 May 1982 12:22:04 PDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - - auto badTZ = new immutable SimpleTimeZone(Duration.zero); - foreach(dchar c; filter!(a => a != 'j' && a != 'J')(letters)) - { - scope(failure) writefln("c: %s", c); - test(format("Wed, 03 May 1982 12:22:04 %s", c), SysTime(dt, badTZ)); - test(format("Wed, 03 May 1982 12:22:04%s", c), SysTime(dt, badTZ)); - } - - foreach(dchar c; ['j', 'J']) - { - scope(failure) writefln("c: %s", c); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04 %s", c)))); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04%s", c)))); - } - - foreach(string s; ["AAA", "GQW", "DDT", "PDA", "GT", "GM"]) - { - scope(failure) writefln("s: %s", s); - test(format("Wed, 03 May 1982 12:22:04 %s", s), SysTime(dt, badTZ)); - } - - // test trailing stuff that gets ignored - { - foreach(c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) - { - scope(failure) writefln("c: %d", c); - test(format("21Dec1213:14:15+0000%c", cast(char)c), std1); - test(format("21Dec1213:14:15+0000%c ", cast(char)c), std1); - test(format("21Dec1213:14:15+0000%chello", cast(char)c), std1); - } - } - - // test trailing stuff that doesn't get ignored - { - foreach(c; chain(iota(33, '('), iota('(' + 1, 127))) - { - scope(failure) writefln("c: %d", c); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c", cast(char)c)))); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c ", cast(char)c)))); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%chello", cast(char)c)))); - } - } - } - - // test that the checks for minimum length work correctly and avoid - // any RangeErrors. - test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - - auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime("")); - foreach(str; ["Fri,7Dec1200:00:00", "7Dec1200:00:00"]) - { - foreach(i; 0 .. str.length) - { - auto value = str[0 .. $ - i]; - scope(failure) writeln(value); - assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg); - } - } - }(); -} - - -/++ - Whether all of the given strings are valid units of time. - - $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime - can handle precision greater than hnsecs, and the few functions in core.time - which deal with "nsecs" deal with it explicitly. - +/ -bool validTimeUnits(string[] units...) @safe pure nothrow -{ - import std.algorithm : canFind; - foreach(str; units) - { - if(!canFind(timeStrings[], str)) - return false; - } - - return true; -} - - -/++ - Compares two time unit strings. $(D "years") are the largest units and - $(D "hnsecs") are the smallest. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - - Throws: - $(LREF DateTimeException) if either of the given strings is not a valid - time unit string. - +/ -int cmpTimeUnits(string lhs, string rhs) @safe pure -{ - import std.format : format; - import std.algorithm : countUntil; - - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); - enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); - - if(indexOfLHS < indexOfRHS) - return -1; - if(indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -unittest -{ - foreach(i, outerUnits; timeStrings) - { - assert(cmpTimeUnits(outerUnits, outerUnits) == 0); - - //For some reason, $ won't compile. - foreach(innerUnits; timeStrings[i+1 .. timeStrings.length]) - assert(cmpTimeUnits(outerUnits, innerUnits) == -1); - } - - foreach(i, outerUnits; timeStrings) - { - foreach(innerUnits; timeStrings[0 .. i]) - assert(cmpTimeUnits(outerUnits, innerUnits) == 1); - } -} - - -/++ - Compares two time unit strings at compile time. $(D "years") are the largest - units and $(D "hnsecs") are the smallest. - - This template is used instead of $(D cmpTimeUnits) because exceptions - can't be thrown at compile time and $(D cmpTimeUnits) must enforce that - the strings it's given are valid time unit strings. This template uses a - template constraint instead. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ -template CmpTimeUnits(string lhs, string rhs) - if(validTimeUnits(lhs, rhs)) -{ - enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); -} - - -/+ - Helper function for $(D CmpTimeUnits). - +/ -private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow -{ - import std.algorithm : countUntil; - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - if(indexOfLHS < indexOfRHS) - return -1; - if(indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -unittest -{ - import std.format : format; - import std.string; - import std.typecons; - import std.typetuple; - - static string genTest(size_t index) - { - auto currUnits = timeStrings[index]; - auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); - - foreach(units; timeStrings[index + 1 .. $]) - test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); - - foreach(units; timeStrings[0 .. index]) - test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); - - return test; - } - - static assert(timeStrings.length == 10); - foreach(n; TypeTuple!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) - mixin(genTest(n)); -} - - -/++ - Returns whether the given value is valid for the given unit type when in a - time point. Naturally, a duration is not held to a particular range, but - the values in a time point are (e.g. a month must be in the range of - 1 - 12 inclusive). - - Params: - units = The units of time to validate. - value = The number to validate. - +/ -bool valid(string units)(int value) @safe pure nothrow - if(units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - static if(units == "months") - return value >= Month.jan && value <= Month.dec; - else static if(units == "hours") - return value >= 0 && value <= TimeOfDay.maxHour; - else static if(units == "minutes") - return value >= 0 && value <= TimeOfDay.maxMinute; - else static if(units == "seconds") - return value >= 0 && value <= TimeOfDay.maxSecond; -} - -/// -unittest -{ - assert(valid!"hours"(12)); - assert(!valid!"hours"(32)); - assert(valid!"months"(12)); - assert(!valid!"months"(13)); -} - - -/++ - Returns whether the given day is valid for the given year and month. - - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - +/ -bool valid(string units)(int year, int month, int day) @safe pure nothrow - if(units == "days") -{ - return day > 0 && day <= maxDay(year, month); -} - - -/++ - Params: - units = The units of time to validate. - value = The number to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!units(value)) is false. - +/ -void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure - if(units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - import std.format : format; - - static if(units == "months") - { - if(!valid!units(value)) - throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); - } - else static if(units == "hours") - { - if(!valid!units(value)) - throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); - } - else static if(units == "minutes") - { - if(!valid!units(value)) - throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); - } - else static if(units == "seconds") - { - if(!valid!units(value)) - throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); - } -} - - -/++ - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. - +/ -void enforceValid(string units) - (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure - if(units == "days") -{ - import std.format : format; - if(!valid!"days"(year, month, day)) - throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); -} - - -/++ - Returns the number of months from the current months of the year to the - given month of the year. If they are the same, then the result is 0. - - Params: - currMonth = The current month of the year. - month = The month of the year to get the number of months to. - +/ -static int monthsToMonth(int currMonth, int month) @safe pure -{ - enforceValid!"months"(currMonth); - enforceValid!"months"(month); - - if(currMonth == month) - return 0; - - if(currMonth < month) - return month - currMonth; - - return (Month.dec - currMonth) + month; -} - -unittest -{ - assert(monthsToMonth(Month.jan, Month.jan) == 0); - assert(monthsToMonth(Month.jan, Month.feb) == 1); - assert(monthsToMonth(Month.jan, Month.mar) == 2); - assert(monthsToMonth(Month.jan, Month.apr) == 3); - assert(monthsToMonth(Month.jan, Month.may) == 4); - assert(monthsToMonth(Month.jan, Month.jun) == 5); - assert(monthsToMonth(Month.jan, Month.jul) == 6); - assert(monthsToMonth(Month.jan, Month.aug) == 7); - assert(monthsToMonth(Month.jan, Month.sep) == 8); - assert(monthsToMonth(Month.jan, Month.oct) == 9); - assert(monthsToMonth(Month.jan, Month.nov) == 10); - assert(monthsToMonth(Month.jan, Month.dec) == 11); - - assert(monthsToMonth(Month.may, Month.jan) == 8); - assert(monthsToMonth(Month.may, Month.feb) == 9); - assert(monthsToMonth(Month.may, Month.mar) == 10); - assert(monthsToMonth(Month.may, Month.apr) == 11); - assert(monthsToMonth(Month.may, Month.may) == 0); - assert(monthsToMonth(Month.may, Month.jun) == 1); - assert(monthsToMonth(Month.may, Month.jul) == 2); - assert(monthsToMonth(Month.may, Month.aug) == 3); - assert(monthsToMonth(Month.may, Month.sep) == 4); - assert(monthsToMonth(Month.may, Month.oct) == 5); - assert(monthsToMonth(Month.may, Month.nov) == 6); - assert(monthsToMonth(Month.may, Month.dec) == 7); - - assert(monthsToMonth(Month.oct, Month.jan) == 3); - assert(monthsToMonth(Month.oct, Month.feb) == 4); - assert(monthsToMonth(Month.oct, Month.mar) == 5); - assert(monthsToMonth(Month.oct, Month.apr) == 6); - assert(monthsToMonth(Month.oct, Month.may) == 7); - assert(monthsToMonth(Month.oct, Month.jun) == 8); - assert(monthsToMonth(Month.oct, Month.jul) == 9); - assert(monthsToMonth(Month.oct, Month.aug) == 10); - assert(monthsToMonth(Month.oct, Month.sep) == 11); - assert(monthsToMonth(Month.oct, Month.oct) == 0); - assert(monthsToMonth(Month.oct, Month.nov) == 1); - assert(monthsToMonth(Month.oct, Month.dec) == 2); - - assert(monthsToMonth(Month.dec, Month.jan) == 1); - assert(monthsToMonth(Month.dec, Month.feb) == 2); - assert(monthsToMonth(Month.dec, Month.mar) == 3); - assert(monthsToMonth(Month.dec, Month.apr) == 4); - assert(monthsToMonth(Month.dec, Month.may) == 5); - assert(monthsToMonth(Month.dec, Month.jun) == 6); - assert(monthsToMonth(Month.dec, Month.jul) == 7); - assert(monthsToMonth(Month.dec, Month.aug) == 8); - assert(monthsToMonth(Month.dec, Month.sep) == 9); - assert(monthsToMonth(Month.dec, Month.oct) == 10); - assert(monthsToMonth(Month.dec, Month.nov) == 11); - assert(monthsToMonth(Month.dec, Month.dec) == 0); -} - - -/++ - Returns the number of days from the current day of the week to the given - day of the week. If they are the same, then the result is 0. - - Params: - currDoW = The current day of the week. - dow = The day of the week to get the number of days to. - +/ -static int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow -{ - if(currDoW == dow) - return 0; - - if(currDoW < dow) - return dow - currDoW; - - return (DayOfWeek.sat - currDoW) + dow + 1; -} - -unittest -{ - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); - - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); - - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); - - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); - - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); - - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); - - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); -} - - -version(StdDdoc) -{ - /++ - Function for starting to a stop watch time when the function is called - and stopping it when its return value goes out of scope and is destroyed. - - When the value that is returned by this function is destroyed, - $(D func) will run. $(D func) is a unary function that takes a - $(CXREF time, TickDuration). - - Examples: --------------------- -{ - auto mt = measureTime!((TickDuration a) - { /+ do something when the scope is exited +/ }); - // do something that needs to be timed -} --------------------- - - which is functionally equivalent to - --------------------- -{ - auto sw = StopWatch(AutoStart.yes); - scope(exit) - { - TickDuration a = sw.peek(); - /+ do something when the scope is exited +/ - } - // do something that needs to be timed -} --------------------- - - See_Also: - $(LREF benchmark) - +/ - auto measureTime(alias func)(); -} -else -{ - @safe auto measureTime(alias func)() - if(isSafe!((){StopWatch sw; unaryFun!func(sw.peek());})) - { - struct Result - { - private StopWatch _sw = void; - this(AutoStart as) - { - _sw = StopWatch(as); - } - ~this() - { - unaryFun!(func)(_sw.peek()); - } - } - return Result(AutoStart.yes); - } - - auto measureTime(alias func)() - if(!isSafe!((){StopWatch sw; unaryFun!func(sw.peek());})) - { - struct Result - { - private StopWatch _sw = void; - this(AutoStart as) - { - _sw = StopWatch(as); - } - ~this() - { - unaryFun!(func)(_sw.peek()); - } - } - return Result(AutoStart.yes); - } -} - -// Verify Example. -unittest -{ - { - auto mt = measureTime!((TickDuration a) - { /+ do something when the scope is exited +/ }); - // do something that needs to be timed - } - - { - auto sw = StopWatch(AutoStart.yes); - scope(exit) - { - TickDuration a = sw.peek(); - /+ do something when the scope is exited +/ - } - // do something that needs to be timed - } -} - -@safe unittest -{ - import std.math : isNaN; - - @safe static void func(TickDuration td) - { - assert(!td.to!("seconds", real)().isNaN()); - } - - auto mt = measureTime!(func)(); - - /+ - with (measureTime!((a){assert(a.seconds);})) - { - // doSomething(); - // @@@BUG@@@ doesn't work yet. - } - +/ -} - -unittest -{ - import std.math : isNaN; - - static void func(TickDuration td) - { - assert(!td.to!("seconds", real)().isNaN()); - } - - auto mt = measureTime!(func)(); - - /+ - with (measureTime!((a){assert(a.seconds);})) - { - // doSomething(); - // @@@BUG@@@ doesn't work yet. - } - +/ -} - -//Bug# 8450 -unittest -{ - @safe void safeFunc() {} - @trusted void trustFunc() {} - @system void sysFunc() {} - auto safeResult = measureTime!((a){safeFunc();})(); - auto trustResult = measureTime!((a){trustFunc();})(); - auto sysResult = measureTime!((a){sysFunc();})(); -} - -//============================================================================== -// Private Section. -//============================================================================== -private: - -//============================================================================== -// Section with private enums and constants. -//============================================================================== - -enum daysInYear = 365; // The number of days in a non-leap year. -enum daysInLeapYear = 366; // The numbef or days in a leap year. -enum daysIn4Years = daysInYear * 3 + daysInLeapYear; /// Number of days in 4 years. -enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. -enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. - -/+ - Array of integers representing the last days of each month in a year. - +/ -immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; - -/+ - Array of integers representing the last days of each month in a leap year. - +/ -immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; - -/+ - Array of the short (three letter) names of each month. - +/ -immutable string[12] _monthNames = ["Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec"]; - - -//============================================================================== -// Section with private helper functions and templates. -//============================================================================== - -/+ - Template to help with converting between time units. - +/ -template hnsecsPer(string units) - if(CmpTimeUnits!(units, "months") < 0) -{ - static if(units == "hnsecs") - enum hnsecsPer = 1L; - else static if(units == "usecs") - enum hnsecsPer = 10L; - else static if(units == "msecs") - enum hnsecsPer = 1000 * hnsecsPer!"usecs"; - else static if(units == "seconds") - enum hnsecsPer = 1000 * hnsecsPer!"msecs"; - else static if(units == "minutes") - enum hnsecsPer = 60 * hnsecsPer!"seconds"; - else static if(units == "hours") - enum hnsecsPer = 60 * hnsecsPer!"minutes"; - else static if(units == "days") - enum hnsecsPer = 24 * hnsecsPer!"hours"; - else static if(units == "weeks") - enum hnsecsPer = 7 * hnsecsPer!"days"; -} - - -/+ - Splits out a particular unit from hnsecs and gives the value for that - unit and the remaining hnsecs. It really shouldn't be used unless unless - all units larger than the given units have already been split out. - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left - after splitting out the given units. - - Returns: - The number of the given units from converting hnsecs to those units. - +/ -long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow - if(validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - immutable value = convert!("hnsecs", units)(hnsecs); - hnsecs -= convert!(units, "hnsecs")(value); - - return value; -} - -unittest -{ - auto hnsecs = 2595000000007L; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 3000000007); - - immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - assert(minutes == 5); - assert(hnsecs == 7); -} - - -/+ - This function is used to split out the units without getting the remaining - hnsecs. - - See_Also: - $(LREF splitUnitsFromHNSecs) - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. - - Returns: - The split out value. - +/ -long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow - if(validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - return convert!("hnsecs", units)(hnsecs); -} - -unittest -{ - auto hnsecs = 2595000000007L; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 2595000000007L); -} - - -/+ - This function is used to split out the units without getting the units but - just the remaining hnsecs. - - See_Also: - $(LREF splitUnitsFromHNSecs) - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. - - Returns: - The remaining hnsecs. - +/ -long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow - if(validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - immutable value = convert!("hnsecs", units)(hnsecs); - - return hnsecs - convert!(units, "hnsecs")(value); -} - -unittest -{ - auto hnsecs = 2595000000007L; - auto returned = removeUnitsFromHNSecs!"days"(hnsecs); - assert(returned == 3000000007); - assert(hnsecs == 2595000000007L); -} - - -/+ - The maximum valid Day in the given month in the given year. - - Params: - year = The year to get the day for. - month = The month of the Gregorian Calendar to get the day for. - +/ -static ubyte maxDay(int year, int month) @safe pure nothrow -in -{ - assert(valid!"months"(month)); -} -body -{ - switch(month) - { - case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: - return 31; - case Month.feb: - return yearIsLeapYear(year) ? 29 : 28; - case Month.apr, Month.jun, Month.sep, Month.nov: - return 30; - default: - assert(0, "Invalid month."); - } -} - -unittest -{ - //Test A.D. - assert(maxDay(1999, 1) == 31); - assert(maxDay(1999, 2) == 28); - assert(maxDay(1999, 3) == 31); - assert(maxDay(1999, 4) == 30); - assert(maxDay(1999, 5) == 31); - assert(maxDay(1999, 6) == 30); - assert(maxDay(1999, 7) == 31); - assert(maxDay(1999, 8) == 31); - assert(maxDay(1999, 9) == 30); - assert(maxDay(1999, 10) == 31); - assert(maxDay(1999, 11) == 30); - assert(maxDay(1999, 12) == 31); - - assert(maxDay(2000, 1) == 31); - assert(maxDay(2000, 2) == 29); - assert(maxDay(2000, 3) == 31); - assert(maxDay(2000, 4) == 30); - assert(maxDay(2000, 5) == 31); - assert(maxDay(2000, 6) == 30); - assert(maxDay(2000, 7) == 31); - assert(maxDay(2000, 8) == 31); - assert(maxDay(2000, 9) == 30); - assert(maxDay(2000, 10) == 31); - assert(maxDay(2000, 11) == 30); - assert(maxDay(2000, 12) == 31); - - //Test B.C. - assert(maxDay(-1999, 1) == 31); - assert(maxDay(-1999, 2) == 28); - assert(maxDay(-1999, 3) == 31); - assert(maxDay(-1999, 4) == 30); - assert(maxDay(-1999, 5) == 31); - assert(maxDay(-1999, 6) == 30); - assert(maxDay(-1999, 7) == 31); - assert(maxDay(-1999, 8) == 31); - assert(maxDay(-1999, 9) == 30); - assert(maxDay(-1999, 10) == 31); - assert(maxDay(-1999, 11) == 30); - assert(maxDay(-1999, 12) == 31); - - assert(maxDay(-2000, 1) == 31); - assert(maxDay(-2000, 2) == 29); - assert(maxDay(-2000, 3) == 31); - assert(maxDay(-2000, 4) == 30); - assert(maxDay(-2000, 5) == 31); - assert(maxDay(-2000, 6) == 30); - assert(maxDay(-2000, 7) == 31); - assert(maxDay(-2000, 8) == 31); - assert(maxDay(-2000, 9) == 30); - assert(maxDay(-2000, 10) == 31); - assert(maxDay(-2000, 11) == 30); - assert(maxDay(-2000, 12) == 31); -} - - -/+ - Returns the day of the week for the given day of the Gregorian Calendar. - - Params: - day = The day of the Gregorian Calendar for which to get the day of - the week. - +/ -DayOfWeek getDayOfWeek(int day) @safe pure nothrow -{ - //January 1st, 1 A.D. was a Monday - if(day >= 0) - return cast(DayOfWeek)(day % 7); - else - { - immutable dow = cast(DayOfWeek)((day % 7) + 7); - - if(dow == 7) - return DayOfWeek.sun; - else - return dow; - } -} - -unittest -{ - //Test A.D. - assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun); - - //Test B.C. - assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat); -} - - -/+ - Returns the string representation of the given month. - +/ -string monthToString(Month month) @safe pure -{ - import std.format : format; - assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month)); - return _monthNames[month - Month.jan]; -} - -unittest -{ - assert(monthToString(Month.jan) == "Jan"); - assert(monthToString(Month.feb) == "Feb"); - assert(monthToString(Month.mar) == "Mar"); - assert(monthToString(Month.apr) == "Apr"); - assert(monthToString(Month.may) == "May"); - assert(monthToString(Month.jun) == "Jun"); - assert(monthToString(Month.jul) == "Jul"); - assert(monthToString(Month.aug) == "Aug"); - assert(monthToString(Month.sep) == "Sep"); - assert(monthToString(Month.oct) == "Oct"); - assert(monthToString(Month.nov) == "Nov"); - assert(monthToString(Month.dec) == "Dec"); -} - - -/+ - Returns the Month corresponding to the given string. - - Params: - monthStr = The string representation of the month to get the Month for. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month string. - +/ -Month monthFromString(string monthStr) @safe pure -{ - import std.format : format; - switch(monthStr) - { - case "Jan": - return Month.jan; - case "Feb": - return Month.feb; - case "Mar": - return Month.mar; - case "Apr": - return Month.apr; - case "May": - return Month.may; - case "Jun": - return Month.jun; - case "Jul": - return Month.jul; - case "Aug": - return Month.aug; - case "Sep": - return Month.sep; - case "Oct": - return Month.oct; - case "Nov": - return Month.nov; - case "Dec": - return Month.dec; - default: - throw new DateTimeException(format("Invalid month %s", monthStr)); - } -} - -unittest -{ - foreach(badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY", - "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"]) - { - scope(failure) writeln(badStr); - assertThrown!DateTimeException(monthFromString(badStr)); - } - - foreach(month; EnumMembers!Month) - { - scope(failure) writeln(month); - assert(monthFromString(monthToString(month)) == month); - } -} - - -/+ - The time units which are one step smaller than the given units. - +/ -template nextSmallerTimeUnits(string units) - if(validTimeUnits(units) && - timeStrings.front != units) -{ - import std.algorithm : countUntil; - enum nextSmallerTimeUnits = timeStrings[countUntil(timeStrings, units) - 1]; -} - -unittest -{ - assert(nextSmallerTimeUnits!"years" == "months"); - assert(nextSmallerTimeUnits!"months" == "weeks"); - assert(nextSmallerTimeUnits!"weeks" == "days"); - assert(nextSmallerTimeUnits!"days" == "hours"); - assert(nextSmallerTimeUnits!"hours" == "minutes"); - assert(nextSmallerTimeUnits!"minutes" == "seconds"); - assert(nextSmallerTimeUnits!"seconds" == "msecs"); - assert(nextSmallerTimeUnits!"msecs" == "usecs"); - assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); - static assert(!__traits(compiles, nextSmallerTimeUnits!"hnsecs")); -} - - -/+ - The time units which are one step larger than the given units. - +/ -template nextLargerTimeUnits(string units) - if(validTimeUnits(units) && - timeStrings.back != units) -{ - import std.algorithm : countUntil; - enum nextLargerTimeUnits = timeStrings[countUntil(timeStrings, units) + 1]; -} - -unittest -{ - assert(nextLargerTimeUnits!"hnsecs" == "usecs"); - assert(nextLargerTimeUnits!"usecs" == "msecs"); - assert(nextLargerTimeUnits!"msecs" == "seconds"); - assert(nextLargerTimeUnits!"seconds" == "minutes"); - assert(nextLargerTimeUnits!"minutes" == "hours"); - assert(nextLargerTimeUnits!"hours" == "days"); - assert(nextLargerTimeUnits!"days" == "weeks"); - assert(nextLargerTimeUnits!"weeks" == "months"); - assert(nextLargerTimeUnits!"months" == "years"); - static assert(!__traits(compiles, nextLargerTimeUnits!"years")); -} - - -/+ - Returns the given hnsecs as an ISO string of fractional seconds. - +/ -static string fracSecsToISOString(int hnsecs) @safe pure nothrow -{ - import std.format : format; - assert(hnsecs >= 0); - - try - { - if(hnsecs == 0) - return ""; - - string isoString = format(".%07d", hnsecs); - - while(isoString[$ - 1] == '0') - isoString.popBack(); - - return isoString; - } - catch(Exception e) - assert(0, "format() threw."); -} - -unittest -{ - assert(fracSecsToISOString(0) == ""); - assert(fracSecsToISOString(1) == ".0000001"); - assert(fracSecsToISOString(10) == ".000001"); - assert(fracSecsToISOString(100) == ".00001"); - assert(fracSecsToISOString(1000) == ".0001"); - assert(fracSecsToISOString(10_000) == ".001"); - assert(fracSecsToISOString(100_000) == ".01"); - assert(fracSecsToISOString(1_000_000) == ".1"); - assert(fracSecsToISOString(1_000_001) == ".1000001"); - assert(fracSecsToISOString(1_001_001) == ".1001001"); - assert(fracSecsToISOString(1_071_601) == ".1071601"); - assert(fracSecsToISOString(1_271_641) == ".1271641"); - assert(fracSecsToISOString(9_999_999) == ".9999999"); - assert(fracSecsToISOString(9_999_990) == ".999999"); - assert(fracSecsToISOString(9_999_900) == ".99999"); - assert(fracSecsToISOString(9_999_000) == ".9999"); - assert(fracSecsToISOString(9_990_000) == ".999"); - assert(fracSecsToISOString(9_900_000) == ".99"); - assert(fracSecsToISOString(9_000_000) == ".9"); - assert(fracSecsToISOString(999) == ".0000999"); - assert(fracSecsToISOString(9990) == ".000999"); - assert(fracSecsToISOString(99_900) == ".00999"); - assert(fracSecsToISOString(999_000) == ".0999"); -} - - -/+ - Returns a Duration corresponding to to the given ISO string of - fractional seconds. - +/ -static Duration fracSecsFromISOString(S)(in S isoString) @trusted pure - if(isSomeString!S) -{ - import std.ascii : isDigit; - import std.string : representation; - import std.conv : to; - import std.algorithm : all; - - if(isoString.empty) - return Duration.zero; - - auto str = isoString.representation; - - enforce(str[0] == '.', new DateTimeException("Invalid ISO String")); - str.popFront(); - - enforce(!str.empty && str.length <= 7, new DateTimeException("Invalid ISO String")); - enforce(all!isDigit(str), new DateTimeException("Invalid ISO String")); - - dchar[7] fullISOString = void; - foreach(i, ref dchar c; fullISOString) - { - if(i < str.length) - c = str[i]; - else - c = '0'; - } - - return hnsecs(to!int(fullISOString[])); -} - -unittest -{ - static void testFSInvalid(string isoString) - { - fracSecsFromISOString(isoString); - } - - assertThrown!DateTimeException(testFSInvalid(".")); - assertThrown!DateTimeException(testFSInvalid("0.")); - assertThrown!DateTimeException(testFSInvalid("0")); - assertThrown!DateTimeException(testFSInvalid("0000000")); - assertThrown!DateTimeException(testFSInvalid(".00000000")); - assertThrown!DateTimeException(testFSInvalid(".00000001")); - assertThrown!DateTimeException(testFSInvalid("T")); - assertThrown!DateTimeException(testFSInvalid("T.")); - assertThrown!DateTimeException(testFSInvalid(".T")); - - assert(fracSecsFromISOString("") == Duration.zero); - assert(fracSecsFromISOString(".0000001") == hnsecs(1)); - assert(fracSecsFromISOString(".000001") == hnsecs(10)); - assert(fracSecsFromISOString(".00001") == hnsecs(100)); - assert(fracSecsFromISOString(".0001") == hnsecs(1000)); - assert(fracSecsFromISOString(".001") == hnsecs(10_000)); - assert(fracSecsFromISOString(".01") == hnsecs(100_000)); - assert(fracSecsFromISOString(".1") == hnsecs(1_000_000)); - assert(fracSecsFromISOString(".1000001") == hnsecs(1_000_001)); - assert(fracSecsFromISOString(".1001001") == hnsecs(1_001_001)); - assert(fracSecsFromISOString(".1071601") == hnsecs(1_071_601)); - assert(fracSecsFromISOString(".1271641") == hnsecs(1_271_641)); - assert(fracSecsFromISOString(".9999999") == hnsecs(9_999_999)); - assert(fracSecsFromISOString(".9999990") == hnsecs(9_999_990)); - assert(fracSecsFromISOString(".999999") == hnsecs(9_999_990)); - assert(fracSecsFromISOString(".9999900") == hnsecs(9_999_900)); - assert(fracSecsFromISOString(".99999") == hnsecs(9_999_900)); - assert(fracSecsFromISOString(".9999000") == hnsecs(9_999_000)); - assert(fracSecsFromISOString(".9999") == hnsecs(9_999_000)); - assert(fracSecsFromISOString(".9990000") == hnsecs(9_990_000)); - assert(fracSecsFromISOString(".999") == hnsecs(9_990_000)); - assert(fracSecsFromISOString(".9900000") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".9900") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".99") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".9000000") == hnsecs(9_000_000)); - assert(fracSecsFromISOString(".9") == hnsecs(9_000_000)); - assert(fracSecsFromISOString(".0000999") == hnsecs(999)); - assert(fracSecsFromISOString(".0009990") == hnsecs(9990)); - assert(fracSecsFromISOString(".000999") == hnsecs(9990)); - assert(fracSecsFromISOString(".0099900") == hnsecs(99_900)); - assert(fracSecsFromISOString(".00999") == hnsecs(99_900)); - assert(fracSecsFromISOString(".0999000") == hnsecs(999_000)); - assert(fracSecsFromISOString(".0999") == hnsecs(999_000)); -} - - -/+ - Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand - side of the given range (it strips comments delimited by $(D '(') and - $(D ')') as well as folding whitespace). - - It is assumed that the given range contains the value of a header field and - no terminating CRLF for the line (though the CRLF for folding whitespace is - of course expected and stripped) and thus that the only case of CR or LF is - in folding whitespace. - - If a comment does not terminate correctly (e.g. mismatched parens) or if the - the FWS is malformed, then the range will be empty when stripCWFS is done. - However, only minimal validation of the content is done (e.g. quoted pairs - within a comment aren't validated beyond \$LPAREN or \$RPAREN, because - they're inside a comment, and thus their value doesn't matter anyway). It's - only when the content does not conform to the grammar rules for FWS and thus - literally cannot be parsed that content is considered invalid, and an empty - range is returned. - - Note that _stripCFWS is eager, not lazy. It does not create a new range. - Rather, it pops off the CFWS from the range and returns it. - +/ -R _stripCFWS(R)(R range) - if(isRandomAccessRange!R && hasSlicing!R && hasLength!R && - (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) -{ - immutable e = range.length; - outer: for(size_t i = 0; i < e; ) - { - switch(range[i]) - { - case ' ': case '\t': - { - ++i; - break; - } - case '\r': - { - if(i + 2 < e && range[i + 1] == '\n' && (range[i + 2] == ' ' || range[i + 2] == '\t')) - { - i += 3; - break; - } - break outer; - } - case '\n': - { - if(i + 1 < e && (range[i + 1] == ' ' || range[i + 1] == '\t')) - { - i += 2; - break; - } - break outer; - } - case '(': - { - ++i; - size_t commentLevel = 1; - while(i < e) - { - if(range[i] == '(') - ++commentLevel; - else if(range[i] == ')') - { - ++i; - if(--commentLevel == 0) - continue outer; - continue; - } - else if(range[i] == '\\') - { - if(++i == e) - break outer; - } - ++i; - } - break outer; - } - default: return range[i .. e]; - } - } - return range[e .. e]; -} - -unittest -{ - import std.algorithm; - import std.string; - import std.typecons; - import std.typetuple; - - foreach(cr; TypeTuple!(function(string a){return cast(ubyte[])a;}, - function(string a){return map!(b => cast(char)b)(a.representation);})) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - scope(failure) writeln(typeof(cr).stringof); - - assert(_stripCFWS(cr("")).empty); - assert(_stripCFWS(cr("\r")).empty); - assert(_stripCFWS(cr("\r\n")).empty); - assert(_stripCFWS(cr("\r\n ")).empty); - assert(_stripCFWS(cr(" \t\r\n")).empty); - assert(equal(_stripCFWS(cr(" \t\r\n hello")), cr("hello"))); - assert(_stripCFWS(cr(" \t\r\nhello")).empty); - assert(_stripCFWS(cr(" \t\r\n\v")).empty); - assert(equal(_stripCFWS(cr("\v \t\r\n\v")), cr("\v \t\r\n\v"))); - assert(_stripCFWS(cr("()")).empty); - assert(_stripCFWS(cr("(hello world)")).empty); - assert(_stripCFWS(cr("(hello world)(hello world)")).empty); - assert(_stripCFWS(cr("(hello world\r\n foo\r where's\nwaldo)")).empty); - assert(_stripCFWS(cr(" \t (hello \tworld\r\n foo\r where's\nwaldo)\t\t ")).empty); - assert(_stripCFWS(cr(" ")).empty); - assert(_stripCFWS(cr("\t\t\t")).empty); - assert(_stripCFWS(cr("\t \r\n\r \n")).empty); - assert(_stripCFWS(cr("(hello world) (can't find waldo) (he's lost)")).empty); - assert(_stripCFWS(cr("(hello\\) world) (can't \\(find waldo) (he's \\(\\)lost)")).empty); - assert(_stripCFWS(cr("(((((")).empty); - assert(_stripCFWS(cr("(((()))")).empty); - assert(_stripCFWS(cr("(((())))")).empty); - assert(equal(_stripCFWS(cr("(((()))))")), cr(")"))); - assert(equal(_stripCFWS(cr(")))))")), cr(")))))"))); - assert(equal(_stripCFWS(cr("()))))")), cr("))))"))); - assert(equal(_stripCFWS(cr(" hello hello ")), cr("hello hello "))); - assert(equal(_stripCFWS(cr("\thello (world)")), cr("hello (world)"))); - assert(equal(_stripCFWS(cr(" \r\n \\((\\)) foo")), cr("\\((\\)) foo"))); - assert(equal(_stripCFWS(cr(" \r\n (\\((\\))) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" \r\n (\\(())) foo")), cr(") foo"))); - assert(_stripCFWS(cr(" \r\n (((\\))) foo")).empty); - - assert(_stripCFWS(cr("(hello)(hello)")).empty); - assert(_stripCFWS(cr(" \r\n (hello)\r\n (hello)")).empty); - assert(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n ")).empty); - assert(_stripCFWS(cr("\t\t\t\t(hello)\t\t\t\t(hello)\t\t\t\t")).empty); - assert(equal(_stripCFWS(cr(" \r\n (hello)\r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\r\n\t(hello)\t\r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\t\r\n\t(hello)\t\r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n \r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n \r\n (hello)\t\r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n\t\r\n\t(hello)\t\r\n (hello) \r\n hello")), cr("hello"))); - - assert(equal(_stripCFWS(cr(" (\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\t\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n\t( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\t\r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\r\n\t) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\t\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n\t) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) \r\n foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\t\r\n foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\r\n foo")), cr("foo"))); - - assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\t\r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n\t( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n\t) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\t\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\r\n ) foo")), cr("foo"))); - - assert(equal(_stripCFWS(cr(" ( \r\n bar \r\n ( \r\n bar \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n () \r\n ( \r\n () \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n \\\\ \r\n ( \r\n \\\\ \r\n ) \r\n ) foo")), cr("foo"))); - - assert(_stripCFWS(cr("(hello)(hello)")).empty); - assert(_stripCFWS(cr(" \n (hello)\n (hello) \n ")).empty); - assert(_stripCFWS(cr(" \n (hello) \n (hello) \n ")).empty); - assert(equal(_stripCFWS(cr(" \n (hello)\n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\n\t(hello)\n\t(hello)\t\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\n\t(hello)\t\n\t(hello)\t\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n \n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello"))); - }(); -} - -// This is so that we don't have to worry about std.conv.to throwing. It also -// doesn't have to worry about quite as many cases as std.conv.to, since it -// doesn't have to worry about a sign on the value or about whether it fits. -T _convDigits(T, R)(R str) - if(isIntegral!T && isSigned!T) // The constraints on R were already covered by parseRFC822DateTime. -{ - import std.ascii : isDigit; - - assert(!str.empty); - T num = 0; - foreach(i; 0 .. str.length) - { - if(i != 0) - num *= 10; - if(!std.ascii.isDigit(str[i])) - return -1; - num += str[i] - '0'; - } - return num; -} - -unittest -{ - import std.conv : to; - import std.range; - foreach(i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999])) - { - scope(failure) writeln(i); - assert(_convDigits!int(to!string(i)) == i); - } - foreach(str; ["-42", "+42", "1a", "1 ", " ", " 42 "]) - { - scope(failure) writeln(str); - assert(_convDigits!int(str) == -1); - } -} - - -/+ - Whether the given type defines the static property min which returns the - minimum value for the type. - +/ -template hasMin(T) -{ - enum hasMin = __traits(hasMember, T, "min") && - __traits(isStaticFunction, T.min) && - is(typeof(T.min) == Unqual!T); -} - -unittest -{ - static assert(hasMin!(Date)); - static assert(hasMin!(TimeOfDay)); - static assert(hasMin!(DateTime)); - static assert(hasMin!(SysTime)); - static assert(hasMin!(const Date)); - static assert(hasMin!(const TimeOfDay)); - static assert(hasMin!(const DateTime)); - static assert(hasMin!(const SysTime)); - static assert(hasMin!(immutable Date)); - static assert(hasMin!(immutable TimeOfDay)); - static assert(hasMin!(immutable SysTime)); -} - - -/+ - Whether the given type defines the static property max which returns the - maximum value for the type. - +/ -template hasMax(T) -{ - enum hasMax = __traits(hasMember, T, "max") && - __traits(isStaticFunction, T.max) && - is(typeof(T.max) == Unqual!T); -} - -unittest -{ - static assert(hasMax!(Date)); - static assert(hasMax!(TimeOfDay)); - static assert(hasMax!(DateTime)); - static assert(hasMax!(SysTime)); - static assert(hasMax!(const Date)); - static assert(hasMax!(const TimeOfDay)); - static assert(hasMax!(const DateTime)); - static assert(hasMax!(const SysTime)); - static assert(hasMax!(immutable Date)); - static assert(hasMax!(immutable TimeOfDay)); - static assert(hasMax!(immutable DateTime)); - static assert(hasMax!(immutable SysTime)); -} - - -/+ - Whether the given type defines the overloaded opBinary operators that a time - point is supposed to define which work with time durations. Namely: - - $(BOOKTABLE, - $(TR $(TD TimePoint opBinary"+"(duration))) - $(TR $(TD TimePoint opBinary"-"(duration))) - ) - +/ -template hasOverloadedOpBinaryWithDuration(T) -{ - enum hasOverloadedOpBinaryWithDuration = __traits(compiles, T.init + dur!"days"(5)) && - is(typeof(T.init + dur!"days"(5)) == Unqual!T) && - __traits(compiles, T.init - dur!"days"(5)) && - is(typeof(T.init - dur!"days"(5)) == Unqual!T) && - __traits(compiles, T.init + TickDuration.from!"hnsecs"(5)) && - is(typeof(T.init + TickDuration.from!"hnsecs"(5)) == Unqual!T) && - __traits(compiles, T.init - TickDuration.from!"hnsecs"(5)) && - is(typeof(T.init - TickDuration.from!"hnsecs"(5)) == Unqual!T); -} - -unittest -{ - static assert(hasOverloadedOpBinaryWithDuration!(Date)); - static assert(hasOverloadedOpBinaryWithDuration!(TimeOfDay)); - static assert(hasOverloadedOpBinaryWithDuration!(DateTime)); - static assert(hasOverloadedOpBinaryWithDuration!(SysTime)); - static assert(hasOverloadedOpBinaryWithDuration!(const Date)); - static assert(hasOverloadedOpBinaryWithDuration!(const TimeOfDay)); - static assert(hasOverloadedOpBinaryWithDuration!(const DateTime)); - static assert(hasOverloadedOpBinaryWithDuration!(const SysTime)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable Date)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable TimeOfDay)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable DateTime)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable SysTime)); -} - - -/+ - Whether the given type defines the overloaded opOpAssign operators that a time point is supposed - to define. Namely: - - $(BOOKTABLE, - $(TR $(TD TimePoint opOpAssign"+"(duration))) - $(TR $(TD TimePoint opOpAssign"-"(duration))) - ) - +/ -template hasOverloadedOpAssignWithDuration(T) -{ - enum hasOverloadedOpAssignWithDuration = is(typeof( - { - auto d = dur!"days"(5); - auto td = TickDuration.from!"hnsecs"(5); - alias U = Unqual!T; - static assert(is(typeof(U.init += d) == U)); - static assert(is(typeof(U.init -= d) == U)); - static assert(is(typeof(U.init += td) == U)); - static assert(is(typeof(U.init -= td) == U)); - })); -} - -unittest -{ - static assert(hasOverloadedOpAssignWithDuration!(Date)); - static assert(hasOverloadedOpAssignWithDuration!(TimeOfDay)); - static assert(hasOverloadedOpAssignWithDuration!(DateTime)); - static assert(hasOverloadedOpAssignWithDuration!(SysTime)); - static assert(hasOverloadedOpAssignWithDuration!(const Date)); - static assert(hasOverloadedOpAssignWithDuration!(const TimeOfDay)); - static assert(hasOverloadedOpAssignWithDuration!(const DateTime)); - static assert(hasOverloadedOpAssignWithDuration!(const SysTime)); - static assert(hasOverloadedOpAssignWithDuration!(immutable Date)); - static assert(hasOverloadedOpAssignWithDuration!(immutable TimeOfDay)); - static assert(hasOverloadedOpAssignWithDuration!(immutable DateTime)); - static assert(hasOverloadedOpAssignWithDuration!(immutable SysTime)); -} - - -/+ - Whether the given type defines the overloaded opBinary operator that a time point is supposed - to define which works with itself. Namely: - - $(BOOKTABLE, - $(TR $(TD duration opBinary"-"(Date))) - ) - +/ -template hasOverloadedOpBinaryWithSelf(T) -{ - enum hasOverloadedOpBinaryWithSelf = __traits(compiles, T.init - T.init) && - is(Unqual!(typeof(T.init - T.init)) == Duration); -} - -unittest -{ - static assert(hasOverloadedOpBinaryWithSelf!(Date)); - static assert(hasOverloadedOpBinaryWithSelf!(TimeOfDay)); - static assert(hasOverloadedOpBinaryWithSelf!(DateTime)); - static assert(hasOverloadedOpBinaryWithSelf!(SysTime)); - static assert(hasOverloadedOpBinaryWithSelf!(const Date)); - static assert(hasOverloadedOpBinaryWithSelf!(const TimeOfDay)); - static assert(hasOverloadedOpBinaryWithSelf!(const DateTime)); - static assert(hasOverloadedOpBinaryWithSelf!(const SysTime)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable Date)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable TimeOfDay)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable DateTime)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable SysTime)); -} - - -version(unittest) -{ - import std.typecons; - import std.algorithm; - //Variables to help in testing. - Duration currLocalDiffFromUTC; - immutable (TimeZone)[] testTZs; - - //All of these helper arrays are sorted in ascending order. - auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; - auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; - - //I'd use a Tuple, but I get forward reference errors if I try. - struct MonthDay - { - Month month; - short day; - - this(int m, short d) - { - month = cast(Month)m; - day = d; - } - } - - MonthDay[] testMonthDays = [MonthDay(1, 1), - MonthDay(1, 2), - MonthDay(3, 17), - MonthDay(7, 4), - MonthDay(10, 27), - MonthDay(12, 30), - MonthDay(12, 31)]; - - auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; - - auto testTODs = [TimeOfDay(0, 0, 0), - TimeOfDay(0, 0, 1), - TimeOfDay(0, 1, 0), - TimeOfDay(1, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - - auto testHours = [0, 1, 12, 22, 23]; - auto testMinSecs = [0, 1, 30, 58, 59]; - - //Throwing exceptions is incredibly expensive, so we want to use a smaller - //set of values for tests using assertThrown. - auto testTODsThrown = [TimeOfDay(0, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - - Date[] testDatesBC; - Date[] testDatesAD; - - DateTime[] testDateTimesBC; - DateTime[] testDateTimesAD; - - Duration[] testFracSecs; - - SysTime[] testSysTimesBC; - SysTime[] testSysTimesAD; - - //I'd use a Tuple, but I get forward reference errors if I try. - struct GregDay { int day; Date date; } - auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), //Start of the Hebrew Calendar - GregDay(-735_233, Date(-2012, 1, 1)), - GregDay(-735_202, Date(-2012, 2, 1)), - GregDay(-735_175, Date(-2012, 2, 28)), - GregDay(-735_174, Date(-2012, 2, 29)), - GregDay(-735_173, Date(-2012, 3, 1)), - GregDay(-734_502, Date(-2010, 1, 1)), - GregDay(-734_472, Date(-2010, 1, 31)), - GregDay(-734_471, Date(-2010, 2, 1)), - GregDay(-734_444, Date(-2010, 2, 28)), - GregDay(-734_443, Date(-2010, 3, 1)), - GregDay(-734_413, Date(-2010, 3, 31)), - GregDay(-734_412, Date(-2010, 4, 1)), - GregDay(-734_383, Date(-2010, 4, 30)), - GregDay(-734_382, Date(-2010, 5, 1)), - GregDay(-734_352, Date(-2010, 5, 31)), - GregDay(-734_351, Date(-2010, 6, 1)), - GregDay(-734_322, Date(-2010, 6, 30)), - GregDay(-734_321, Date(-2010, 7, 1)), - GregDay(-734_291, Date(-2010, 7, 31)), - GregDay(-734_290, Date(-2010, 8, 1)), - GregDay(-734_260, Date(-2010, 8, 31)), - GregDay(-734_259, Date(-2010, 9, 1)), - GregDay(-734_230, Date(-2010, 9, 30)), - GregDay(-734_229, Date(-2010, 10, 1)), - GregDay(-734_199, Date(-2010, 10, 31)), - GregDay(-734_198, Date(-2010, 11, 1)), - GregDay(-734_169, Date(-2010, 11, 30)), - GregDay(-734_168, Date(-2010, 12, 1)), - GregDay(-734_139, Date(-2010, 12, 30)), - GregDay(-734_138, Date(-2010, 12, 31)), - GregDay(-731_215, Date(-2001, 1, 1)), - GregDay(-730_850, Date(-2000, 1, 1)), - GregDay(-730_849, Date(-2000, 1, 2)), - GregDay(-730_486, Date(-2000, 12, 30)), - GregDay(-730_485, Date(-2000, 12, 31)), - GregDay(-730_484, Date(-1999, 1, 1)), - GregDay(-694_690, Date(-1901, 1, 1)), - GregDay(-694_325, Date(-1900, 1, 1)), - GregDay(-585_118, Date(-1601, 1, 1)), - GregDay(-584_753, Date(-1600, 1, 1)), - GregDay(-584_388, Date(-1600, 12, 31)), - GregDay(-584_387, Date(-1599, 1, 1)), - GregDay(-365_972, Date(-1001, 1, 1)), - GregDay(-365_607, Date(-1000, 1, 1)), - GregDay(-183_351, Date(-501, 1, 1)), - GregDay(-182_986, Date(-500, 1, 1)), - GregDay(-182_621, Date(-499, 1, 1)), - GregDay(-146_827, Date(-401, 1, 1)), - GregDay(-146_462, Date(-400, 1, 1)), - GregDay(-146_097, Date(-400, 12, 31)), - GregDay(-110_302, Date(-301, 1, 1)), - GregDay(-109_937, Date(-300, 1, 1)), - GregDay(-73_778, Date(-201, 1, 1)), - GregDay(-73_413, Date(-200, 1, 1)), - GregDay(-38_715, Date(-105, 1, 1)), - GregDay(-37_254, Date(-101, 1, 1)), - GregDay(-36_889, Date(-100, 1, 1)), - GregDay(-36_524, Date(-99, 1, 1)), - GregDay(-36_160, Date(-99, 12, 31)), - GregDay(-35_794, Date(-97, 1, 1)), - GregDay(-18_627, Date(-50, 1, 1)), - GregDay(-18_262, Date(-49, 1, 1)), - GregDay(-3652, Date(-9, 1, 1)), - GregDay(-2191, Date(-5, 1, 1)), - GregDay(-1827, Date(-5, 12, 31)), - GregDay(-1826, Date(-4, 1, 1)), - GregDay(-1825, Date(-4, 1, 2)), - GregDay(-1462, Date(-4, 12, 30)), - GregDay(-1461, Date(-4, 12, 31)), - GregDay(-1460, Date(-3, 1, 1)), - GregDay(-1096, Date(-3, 12, 31)), - GregDay(-1095, Date(-2, 1, 1)), - GregDay(-731, Date(-2, 12, 31)), - GregDay(-730, Date(-1, 1, 1)), - GregDay(-367, Date(-1, 12, 30)), - GregDay(-366, Date(-1, 12, 31)), - GregDay(-365, Date(0, 1, 1)), - GregDay(-31, Date(0, 11, 30)), - GregDay(-30, Date(0, 12, 1)), - GregDay(-1, Date(0, 12, 30)), - GregDay(0, Date(0, 12, 31))]; - - auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), - GregDay(2, Date(1, 1, 2)), - GregDay(32, Date(1, 2, 1)), - GregDay(365, Date(1, 12, 31)), - GregDay(366, Date(2, 1, 1)), - GregDay(731, Date(3, 1, 1)), - GregDay(1096, Date(4, 1, 1)), - GregDay(1097, Date(4, 1, 2)), - GregDay(1460, Date(4, 12, 30)), - GregDay(1461, Date(4, 12, 31)), - GregDay(1462, Date(5, 1, 1)), - GregDay(17_898, Date(50, 1, 1)), - GregDay(35_065, Date(97, 1, 1)), - GregDay(36_160, Date(100, 1, 1)), - GregDay(36_525, Date(101, 1, 1)), - GregDay(37_986, Date(105, 1, 1)), - GregDay(72_684, Date(200, 1, 1)), - GregDay(73_049, Date(201, 1, 1)), - GregDay(109_208, Date(300, 1, 1)), - GregDay(109_573, Date(301, 1, 1)), - GregDay(145_732, Date(400, 1, 1)), - GregDay(146_098, Date(401, 1, 1)), - GregDay(182_257, Date(500, 1, 1)), - GregDay(182_622, Date(501, 1, 1)), - GregDay(364_878, Date(1000, 1, 1)), - GregDay(365_243, Date(1001, 1, 1)), - GregDay(584_023, Date(1600, 1, 1)), - GregDay(584_389, Date(1601, 1, 1)), - GregDay(693_596, Date(1900, 1, 1)), - GregDay(693_961, Date(1901, 1, 1)), - GregDay(729_755, Date(1999, 1, 1)), - GregDay(730_120, Date(2000, 1, 1)), - GregDay(730_121, Date(2000, 1, 2)), - GregDay(730_484, Date(2000, 12, 30)), - GregDay(730_485, Date(2000, 12, 31)), - GregDay(730_486, Date(2001, 1, 1)), - GregDay(733_773, Date(2010, 1, 1)), - GregDay(733_774, Date(2010, 1, 2)), - GregDay(733_803, Date(2010, 1, 31)), - GregDay(733_804, Date(2010, 2, 1)), - GregDay(733_831, Date(2010, 2, 28)), - GregDay(733_832, Date(2010, 3, 1)), - GregDay(733_862, Date(2010, 3, 31)), - GregDay(733_863, Date(2010, 4, 1)), - GregDay(733_892, Date(2010, 4, 30)), - GregDay(733_893, Date(2010, 5, 1)), - GregDay(733_923, Date(2010, 5, 31)), - GregDay(733_924, Date(2010, 6, 1)), - GregDay(733_953, Date(2010, 6, 30)), - GregDay(733_954, Date(2010, 7, 1)), - GregDay(733_984, Date(2010, 7, 31)), - GregDay(733_985, Date(2010, 8, 1)), - GregDay(734_015, Date(2010, 8, 31)), - GregDay(734_016, Date(2010, 9, 1)), - GregDay(734_045, Date(2010, 9, 30)), - GregDay(734_046, Date(2010, 10, 1)), - GregDay(734_076, Date(2010, 10, 31)), - GregDay(734_077, Date(2010, 11, 1)), - GregDay(734_106, Date(2010, 11, 30)), - GregDay(734_107, Date(2010, 12, 1)), - GregDay(734_136, Date(2010, 12, 30)), - GregDay(734_137, Date(2010, 12, 31)), - GregDay(734_503, Date(2012, 1, 1)), - GregDay(734_534, Date(2012, 2, 1)), - GregDay(734_561, Date(2012, 2, 28)), - GregDay(734_562, Date(2012, 2, 29)), - GregDay(734_563, Date(2012, 3, 1)), - GregDay(734_858, Date(2012, 12, 21))]; - - //I'd use a Tuple, but I get forward reference errors if I try. - struct DayOfYear { int day; MonthDay md; } - auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(3, 1)), - DayOfYear(90, MonthDay(3, 31)), - DayOfYear(91, MonthDay(4, 1)), - DayOfYear(120, MonthDay(4, 30)), - DayOfYear(121, MonthDay(5, 1)), - DayOfYear(151, MonthDay(5, 31)), - DayOfYear(152, MonthDay(6, 1)), - DayOfYear(181, MonthDay(6, 30)), - DayOfYear(182, MonthDay(7, 1)), - DayOfYear(212, MonthDay(7, 31)), - DayOfYear(213, MonthDay(8, 1)), - DayOfYear(243, MonthDay(8, 31)), - DayOfYear(244, MonthDay(9, 1)), - DayOfYear(273, MonthDay(9, 30)), - DayOfYear(274, MonthDay(10, 1)), - DayOfYear(304, MonthDay(10, 31)), - DayOfYear(305, MonthDay(11, 1)), - DayOfYear(334, MonthDay(11, 30)), - DayOfYear(335, MonthDay(12, 1)), - DayOfYear(363, MonthDay(12, 29)), - DayOfYear(364, MonthDay(12, 30)), - DayOfYear(365, MonthDay(12, 31))]; - - auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(2, 29)), - DayOfYear(61, MonthDay(3, 1)), - DayOfYear(91, MonthDay(3, 31)), - DayOfYear(92, MonthDay(4, 1)), - DayOfYear(121, MonthDay(4, 30)), - DayOfYear(122, MonthDay(5, 1)), - DayOfYear(152, MonthDay(5, 31)), - DayOfYear(153, MonthDay(6, 1)), - DayOfYear(182, MonthDay(6, 30)), - DayOfYear(183, MonthDay(7, 1)), - DayOfYear(213, MonthDay(7, 31)), - DayOfYear(214, MonthDay(8, 1)), - DayOfYear(244, MonthDay(8, 31)), - DayOfYear(245, MonthDay(9, 1)), - DayOfYear(274, MonthDay(9, 30)), - DayOfYear(275, MonthDay(10, 1)), - DayOfYear(305, MonthDay(10, 31)), - DayOfYear(306, MonthDay(11, 1)), - DayOfYear(335, MonthDay(11, 30)), - DayOfYear(336, MonthDay(12, 1)), - DayOfYear(364, MonthDay(12, 29)), - DayOfYear(365, MonthDay(12, 30)), - DayOfYear(366, MonthDay(12, 31))]; - - void initializeTests() - { - immutable lt = LocalTime().utcToTZ(0); - currLocalDiffFromUTC = dur!"hnsecs"(lt); - - immutable otherTZ = lt < 0 ? TimeZone.getTimeZone("Australia/Sydney") - : TimeZone.getTimeZone("America/Denver"); - immutable ot = otherTZ.utcToTZ(0); - - auto diffs = [0L, lt, ot]; - auto diffAA = [0L : Rebindable!(immutable TimeZone)(UTC())]; - diffAA[lt] = Rebindable!(immutable TimeZone)(LocalTime()); - diffAA[ot] = Rebindable!(immutable TimeZone)(otherTZ); - - sort(diffs); - testTZs = [diffAA[diffs[0]], diffAA[diffs[1]], diffAA[diffs[2]]]; - - testFracSecs = [Duration.zero, hnsecs(1), hnsecs(5007), hnsecs(9999999)]; - - foreach(year; testYearsBC) - { - foreach(md; testMonthDays) - testDatesBC ~= Date(year, md.month, md.day); - } - - foreach(year; testYearsAD) - { - foreach(md; testMonthDays) - testDatesAD ~= Date(year, md.month, md.day); - } - - foreach(dt; testDatesBC) - { - foreach(tod; testTODs) - testDateTimesBC ~= DateTime(dt, tod); - } - - foreach(dt; testDatesAD) - { - foreach(tod; testTODs) - testDateTimesAD ~= DateTime(dt, tod); - } - - foreach(dt; testDateTimesBC) - { - foreach(tz; testTZs) - { - foreach(fs; testFracSecs) - testSysTimesBC ~= SysTime(dt, fs, tz); - } - } - - foreach(dt; testDateTimesAD) - { - foreach(tz; testTZs) - { - foreach(fs; testFracSecs) - testSysTimesAD ~= SysTime(dt, fs, tz); - } - } - } -} - - -unittest -{ - /* Issue 6642 */ - static assert(!hasUnsharedAliasing!Date); - static assert(!hasUnsharedAliasing!TimeOfDay); - static assert(!hasUnsharedAliasing!DateTime); - static assert(!hasUnsharedAliasing!SysTime); -} - - - -// This script is for regenerating tzDatabaseNameToWindowsTZName and -// windowsTZNameToTZDatabaseName from -// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml - -/+ -#!/bin/rdmd - -import std.algorithm; -import std.array; -import std.conv; -import std.datetime; -import std.exception; -import std.path; -import std.stdio; -import std.string; - -int main(string[] args) -{ - if(args.length != 4 || args[1].baseName != "windowsZones.xml") - { - stderr.writeln("genTZs.d windowsZones.xml "); - return -1; - } - - string[][string] win2Nix; - string[][string] nix2Win; - immutable f1 = ` %s", nix, wins)); - - // We'll try to eliminate multiples by favoring a conversion if it's already - // in Phobos, but if it's new, then the correct one will have to be chosen - // manually from the results. - string[] haveMultiple; - foreach(win, nixes; win2Nix) - { - if(nixes.length > 1) - haveMultiple ~= win; - } - bool[string] haveConflicts; - foreach(win; haveMultiple) - { - if(auto curr = windowsTZNameToTZDatabaseName(win)) - { - if(auto other = curr in nix2Win) - { - if((*other)[0] == win) - { - win2Nix[win] = [curr]; - continue; - } - } - } - haveConflicts[win] = true; - writefln("Warning: %s -> %s", win, win2Nix[win]); - } - - - string[] nix2WinLines = [ - `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`, - `{`, - ` switch(tzName)`, - ` {`]; - - foreach(nix; nix2Win.keys.sort()) - nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]); - - nix2WinLines ~= [ - ` default: return null;`, - ` }`, - `}`]; - - - string[] win2NixLines = [ - `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`, - `{`, - ` switch(tzName)`, - ` {`]; - foreach(win; win2Nix.keys.sort()) - { - immutable hasMultiple = cast(bool)(win in haveConflicts); - foreach(nix; win2Nix[win]) - win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : ""); - } - - win2NixLines ~= [ - ` default: return null;`, - ` }`, - `}`]; - - - auto nix2WinFile = args[2]; - std.file.write(nix2WinFile, nix2WinLines.join("\n")); - - auto win2NixFile = args[3]; - std.file.write(win2NixFile, win2NixLines.join("\n")); - - return 0; -} -+/ diff --git a/std/datetime/date.d b/std/datetime/date.d new file mode 100644 index 00000000000..8fde94edad6 --- /dev/null +++ b/std/datetime/date.d @@ -0,0 +1,10324 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/datetime/_date.d) ++/ +module std.datetime.date; + +import core.time; +import std.traits : isSomeString, Unqual; +import std.typecons : Flag; + +version(unittest) import std.exception : assertThrown; + + +@safe unittest +{ + initializeTests(); +} + + +/++ + Exception type used by std.datetime. It's an alias to + $(REF TimeException,core,time). Either can be caught without concern about + which module it came from. + +/ +alias DateTimeException = TimeException; + + +/++ + Represents the 12 months of the Gregorian year (January is 1). + +/ +enum Month : ubyte +{ + jan = 1, /// + feb, /// + mar, /// + apr, /// + may, /// + jun, /// + jul, /// + aug, /// + sep, /// + oct, /// + nov, /// + dec /// +} + + +/++ + Represents the 7 days of the Gregorian week (Sunday is 0). + +/ +enum DayOfWeek : ubyte +{ + sun = 0, /// + mon, /// + tue, /// + wed, /// + thu, /// + fri, /// + sat /// +} + + +/++ + In some date calculations, adding months or years can cause the date to fall + on a day of the month which is not valid (e.g. February 29th 2001 or + June 31st 2000). If overflow is allowed (as is the default), then the month + will be incremented accordingly (so, February 29th 2001 would become + March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow + is not allowed, then the day will be adjusted to the last valid day in that + month (so, February 29th 2001 would become February 28th 2001 and + June 31st 2000 would become June 30th 2000). + + AllowDayOverflow only applies to calculations involving months or years. + + If set to $(D AllowDayOverflow.no), then day overflow is not allowed. + + Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is + allowed. + +/ +alias AllowDayOverflow = Flag!"allowDayOverflow"; + + +/++ + Array of the strings representing time units, starting with the smallest + unit and going to the largest. It does not include $(D "nsecs"). + + Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)), + $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"), + $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and + $(D "years") + +/ +immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", + "hours", "days", "weeks", "months", "years"]; + + +/++ + Combines the $(REF Date,std,datetime,date) and + $(REF TimeOfDay,std,datetime,date) structs to give an object which holds + both the date and the time. It is optimized for calendar-based operations and + has no concept of time zone. For an object which is optimized for time + operations based on the system time, use $(REF SysTime,std,datetime,systime). + $(REF SysTime,std,datetime,systime) has a concept of time zone and has much + higher precision (hnsecs). $(D DateTime) is intended primarily for + calendar-based uses rather than precise time operations. + +/ +struct DateTime +{ +public: + + /++ + Params: + date = The date portion of $(LREF DateTime). + tod = The time portion of $(LREF DateTime). + +/ + this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow + { + _date = date; + _tod = tod; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); + } + } + + + /++ + Params: + year = The year portion of the date. + month = The month portion of the date. + day = The day portion of the date. + hour = The hour portion of the time; + minute = The minute portion of the time; + second = The second portion of the time; + +/ + this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure + { + _date = Date(year, month, day); + _tod = TimeOfDay(hour, minute, second); + } + + @safe unittest + { + { + auto dt = DateTime(1999, 7 ,6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(1999, 7 ,6, 12, 30, 33); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); + } + } + + + /++ + Compares this $(LREF DateTime) with the given $(D DateTime.). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in DateTime rhs) @safe const pure nothrow + { + immutable dateResult = _date.opCmp(rhs._date); + + if (dateResult != 0) + return dateResult; + + return _tod.opCmp(rhs._tod); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); + + assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); + assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); + assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); + + assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); + + assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); + + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + + assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + // Test B.C. + assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + // Test Both + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + assert(dt.opCmp(dt) == 0); + assert(dt.opCmp(cdt) == 0); + assert(dt.opCmp(idt) == 0); + assert(cdt.opCmp(dt) == 0); + assert(cdt.opCmp(cdt) == 0); + assert(cdt.opCmp(idt) == 0); + assert(idt.opCmp(dt) == 0); + assert(idt.opCmp(cdt) == 0); + assert(idt.opCmp(idt) == 0); + } + + + /++ + The date portion of $(LREF DateTime). + +/ + @property Date date() @safe const pure nothrow + { + return _date; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt.date == Date.init); + } + + { + auto dt = DateTime(Date(1999, 7, 6)); + assert(dt.date == Date(1999, 7, 6)); + } + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + assert(cdt.date == Date(1999, 7, 6)); + assert(idt.date == Date(1999, 7, 6)); + } + + + /++ + The date portion of $(LREF DateTime). + + Params: + date = The Date to set this $(LREF DateTime)'s date portion to. + +/ + @property void date(in Date date) @safe pure nothrow + { + _date = date; + } + + @safe unittest + { + auto dt = DateTime.init; + dt.date = Date(1999, 7, 6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); + static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); + } + + + /++ + The time portion of $(LREF DateTime). + +/ + @property TimeOfDay timeOfDay() @safe const pure nothrow + { + return _tod; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt.timeOfDay == TimeOfDay.init); + } + + { + auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); + assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.timeOfDay == TimeOfDay(12, 30, 33)); + assert(idt.timeOfDay == TimeOfDay(12, 30, 33)); + } + + + /++ + The time portion of $(LREF DateTime). + + Params: + tod = The $(REF TimeOfDay,std,datetime,date) to set this + $(LREF DateTime)'s time portion to. + +/ + @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow + { + _tod = tod; + } + + @safe unittest + { + auto dt = DateTime.init; + dt.timeOfDay = TimeOfDay(12, 30, 33); + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay(12, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); + static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const pure nothrow + { + return _date.year; + } + + @safe unittest + { + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(idt.year == 1999); + assert(idt.year == 1999); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this $(LREF DateTime)'s year to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the new year is not + a leap year and if the resulting date would be on February 29th. + +/ + @property void year(int year) @safe pure + { + _date.year = year; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); + } + + @safe unittest + { + static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) + { + dt.year = year; + assert(dt == expected); + } + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + 1999, + DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + 0, + DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + -1999, + DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.year = 7)); + static assert(!__traits(compiles, idt.year = 7)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(REF DateTimeException,std,datetime,date) if $(D isAD) is true. + +/ + @property short yearBC() @safe const pure + { + return _date.yearBC; + } + + /// + @safe unittest + { + assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); + assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); + assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); + } + + @safe unittest + { + assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + dt.yearBC = 12; + assert(dt.yearBC == 12); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF DateTime)'s year to. + + Throws: + $(REF DateTimeException,std,datetime,date) if a non-positive value + is given. + +/ + @property void yearBC(int year) @safe pure + { + _date.yearBC = year; + } + + /// + @safe unittest + { + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); + dt.yearBC = 1; + assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); + + dt.yearBC = 10; + assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); + } + + @safe unittest + { + assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + dt.yearBC = 12; + assert(dt.yearBC == 12); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const pure nothrow + { + return _date.month; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); + } + + @safe unittest + { + assert(DateTime.init.month == 1); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.month == 7); + assert(idt.month == 7); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF DateTime)'s month to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given month is + not a valid month. + +/ + @property void month(Month month) @safe pure + { + _date.month = month; + } + + @safe unittest + { + static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) + { + dt.month = month; + assert(expected != DateTime.init); + assert(dt == expected); + } + + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13)); + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + cast(Month) 7, + DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), + cast(Month) 7, + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.month = 12)); + static assert(!__traits(compiles, idt.month = 12)); + } + + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const pure nothrow + { + return _date.day; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); + } + + @safe unittest + { + import std.format : format; + import std.range : chain; + + static void test(DateTime dateTime, int expected) + { + assert(dateTime.day == expected, format("Value given: %s", dateTime)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + test(DateTime(Date(year, md.month, md.day), tod), md.day); + } + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.day == 6); + assert(idt.day == 6); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF DateTime)'s day to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given day is not + a valid day of the current month. + +/ + @property void day(int day) @safe pure + { + _date.day = day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + + static void testDT(DateTime dt, int day) + { + dt.day = day; + } + + // Test A.D. + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); + + { + auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); + } + + // Test B.C. + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); + + auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.day = 27)); + static assert(!__traits(compiles, idt.day = 27)); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const pure nothrow + { + return _tod.hour; + } + + @safe unittest + { + assert(DateTime.init.hour == 0); + assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.hour == 12); + assert(idt.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hour of the day to set this $(LREF DateTime)'s hour to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given hour would + result in an invalid $(LREF DateTime). + +/ + @property void hour(int hour) @safe pure + { + _tod.hour = hour; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); + + auto dt = DateTime.init; + dt.hour = 12; + assert(dt == DateTime(1, 1, 1, 12, 0, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.hour = 27)); + static assert(!__traits(compiles, idt.hour = 27)); + } + + + /++ + Minutes past the hour. + +/ + @property ubyte minute() @safe const pure nothrow + { + return _tod.minute; + } + + @safe unittest + { + assert(DateTime.init.minute == 0); + assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.minute == 30); + assert(idt.minute == 30); + } + + + /++ + Minutes past the hour. + + Params: + minute = The minute to set this $(LREF DateTime)'s minute to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given minute + would result in an invalid $(LREF DateTime). + +/ + @property void minute(int minute) @safe pure + { + _tod.minute = minute; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); + + auto dt = DateTime.init; + dt.minute = 30; + assert(dt == DateTime(1, 1, 1, 0, 30, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.minute = 27)); + static assert(!__traits(compiles, idt.minute = 27)); + } + + + /++ + Seconds past the minute. + +/ + @property ubyte second() @safe const pure nothrow + { + return _tod.second; + } + + @safe unittest + { + assert(DateTime.init.second == 0); + assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.second == 33); + assert(idt.second == 33); + } + + + /++ + Seconds past the minute. + + Params: + second = The second to set this $(LREF DateTime)'s second to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given seconds + would result in an invalid $(LREF DateTime). + +/ + @property void second(int second) @safe pure + { + _tod.second = second; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime.init.second = 60;}()); + + auto dt = DateTime.init; + dt.second = 33; + assert(dt == DateTime(1, 1, 1, 0, 0, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.second = 27)); + static assert(!__traits(compiles, idt.second = 27)); + } + + + /++ + Adds the given number of years or months to this $(LREF DateTime). A + negative number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF DateTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref DateTime add(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years" || units == "months") + { + _date.add!units(value, allowOverflow); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); + dt1.add!"months"(11); + assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); + + auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); + dt2.add!"months"(-11); + assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); + + auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); + dt3.add!"years"(1); + assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); + + auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); + dt4.add!"years"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.add!"years"(7).add!"months"(-4); + assert(dt == DateTime(2006, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.add!"years"(4))); + static assert(!__traits(compiles, idt.add!"years"(4))); + static assert(!__traits(compiles, cdt.add!"months"(4))); + static assert(!__traits(compiles, idt.add!"months"(4))); + } + + + /++ + Adds the given number of years or months to this $(LREF DateTime). A + negative number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF DateTime) 12 months + gets the exact same $(LREF DateTime). However, the days can still be + affected due to the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF DateTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref DateTime roll(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years" || units == "months") + { + _date.roll!units(value, allowOverflow); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); + dt1.roll!"months"(1); + assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); + + auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); + dt2.roll!"months"(-1); + assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); + + auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); + dt3.roll!"months"(1); + assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); + + auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); + dt4.roll!"months"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); + + auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); + dt5.roll!"years"(1); + assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); + + auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); + dt6.roll!"years"(1, AllowDayOverflow.no); + assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.roll!"years"(7).roll!"months"(-4); + assert(dt == DateTime(2007, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"years"(4))); + static assert(!__traits(compiles, idt.roll!"years"(4))); + static assert(!__traits(compiles, cdt.roll!"months"(4))); + static assert(!__traits(compiles, idt.roll!"months"(4))); + } + + + /++ + Adds the given number of units to this $(LREF DateTime). A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF DateTime) one + year's worth of days gets the exact same $(LREF DateTime). + + Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), + $(D "minutes"), and $(D "seconds"). + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this + $(LREF DateTime). + +/ + ref DateTime roll(string units)(long value) @safe pure nothrow + if (units == "days") + { + _date.roll!"days"(value); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); + dt1.roll!"days"(1); + assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); + dt1.roll!"days"(365); + assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); + dt1.roll!"days"(-32); + assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); + + auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); + dt2.roll!"hours"(1); + assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); + + auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); + dt3.roll!"seconds"(-1); + assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.roll!"days"(7).roll!"days"(-4); + assert(dt == DateTime(2000, 1, 3)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"days"(4))); + static assert(!__traits(compiles, idt.roll!"days"(4))); + } + + + // Shares documentation with "days" version. + ref DateTime roll(string units)(long value) @safe pure nothrow + if (units == "hours" || + units == "minutes" || + units == "seconds") + { + _tod.roll!units(value); + return this; + } + + // Test roll!"hours"(). + @safe unittest + { + static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, + DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, + DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, + DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, + DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, + DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, + DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, + DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, + DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, + DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, + DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, + DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, + DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, + DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, + DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, + DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, + DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, + DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, + DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, + DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, + DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, + DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, + DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, + DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, + DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, + DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, + DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, + DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, + DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, + DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, + DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, + DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, + DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, + DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, + DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, + DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, + DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, + DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, + DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, + DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, + DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, + DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, + DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); + + // Test Both + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, + DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, + DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"hours"(27).roll!"hours"(-9); + assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"hours"(4))); + static assert(!__traits(compiles, idt.roll!"hours"(4))); + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); + + // Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, + DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, + DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"minutes"(92).roll!"minutes"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"minutes"(4))); + static assert(!__traits(compiles, idt.roll!"minutes"(4))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); + + // Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"seconds"(92).roll!"seconds"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"seconds"(4))); + static assert(!__traits(compiles, idt.roll!"seconds"(4))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF DateTime). + + The legal types of arithmetic for $(LREF DateTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF DateTime). + +/ + DateTime opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = duration.total!"seconds"; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + /// + @safe unittest + { + import core.time : hours, seconds; + + assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == + DateTime(2016, 1, 1, 0, 0, 0)); + + assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == + DateTime(2016, 1, 1, 0, 59, 59)); + + assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) == + DateTime(2015, 12, 31, 23, 59, 59)); + + assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == + DateTime(2015, 12, 31, 23, 59, 59)); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45)); + assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45)); + assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21)); + assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + DateTime opBinary(string op)(in TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = td.seconds; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + } + + + /++ + Gives the result of adding or subtracting a duration from this + $(LREF DateTime), as well as assigning the result to this + $(LREF DateTime). + + The legal types of arithmetic for $(LREF DateTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The duration to add to or subtract from this + $(LREF DateTime). + +/ + ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow + if ((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + import std.format : format; + + DateTime retval = this; + + static if (is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if (is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); + } + + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == + DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == + DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == + DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == + DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == + DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == + DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == + DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == + DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + (dt += dur!"seconds"(92)) -= dur!"days"(-500); + assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt += duration)); + static assert(!__traits(compiles, idt += duration)); + static assert(!__traits(compiles, cdt -= duration)); + static assert(!__traits(compiles, idt -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = td.seconds; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt += TickDuration.from!"usecs"(7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt += TickDuration.from!"usecs"(-7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt -= TickDuration.from!"usecs"(-7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt -= TickDuration.from!"usecs"(7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + } + } + + + /++ + Gives the difference between two $(LREF DateTime)s. + + The legal types of arithmetic for $(LREF DateTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow + if (op == "-") + { + immutable dateResult = _date - rhs.date; + immutable todResult = _tod - rhs._tod; + + return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); + } + + @safe unittest + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == + dur!"seconds"(86_400)); + assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == + dur!"seconds"(3600)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(60)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == + dur!"seconds"(-60)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(1)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == + dur!"seconds"(-1)); + + assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); + assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt - dt == Duration.zero); + assert(cdt - dt == Duration.zero); + assert(idt - dt == Duration.zero); + + assert(dt - cdt == Duration.zero); + assert(cdt - cdt == Duration.zero); + assert(idt - cdt == Duration.zero); + + assert(dt - idt == Duration.zero); + assert(cdt - idt == Duration.zero); + assert(idt - idt == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF DateTime)s in months. + + To get the difference in years, subtract the year property + of two $(LREF DateTime)s. To get the difference in days or weeks, + subtract the $(LREF DateTime)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. + + Note that the number of days in the months or how far into the month + either date is is irrelevant. It is the difference in the month property + combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF DateTime) to subtract from this one. + +/ + int diffMonths(in DateTime rhs) @safe const pure nothrow + { + return _date.diffMonths(rhs._date); + } + + /// + @safe unittest + { + assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( + DateTime(1999, 1, 31, 23, 59, 59)) == 1); + + assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( + DateTime(1999, 2, 1, 12, 3, 42)) == -1); + + assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( + DateTime(1999, 1, 1, 2, 4, 7)) == 2); + + assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( + DateTime(1999, 3, 31, 0, 30, 58)) == -2); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.diffMonths(dt) == 0); + assert(cdt.diffMonths(dt) == 0); + assert(idt.diffMonths(dt) == 0); + + assert(dt.diffMonths(cdt) == 0); + assert(cdt.diffMonths(cdt) == 0); + assert(idt.diffMonths(cdt) == 0); + + assert(dt.diffMonths(idt) == 0); + assert(cdt.diffMonths(idt) == 0); + assert(idt.diffMonths(idt) == 0); + } + + + /++ + Whether this $(LREF DateTime) is in a leap year. + +/ + @property bool isLeapYear() @safe const pure nothrow + { + return _date.isLeapYear; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(!dt.isLeapYear); + assert(!cdt.isLeapYear); + assert(!idt.isLeapYear); + } + + + /++ + Day of the week this $(LREF DateTime) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const pure nothrow + { + return _date.dayOfWeek; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.dayOfWeek == DayOfWeek.tue); + assert(cdt.dayOfWeek == DayOfWeek.tue); + assert(idt.dayOfWeek == DayOfWeek.tue); + } + + + /++ + Day of the year this $(LREF DateTime) is on. + +/ + @property ushort dayOfYear() @safe const pure nothrow + { + return _date.dayOfYear; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); + assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); + assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.dayOfYear == 187); + assert(cdt.dayOfYear == 187); + assert(idt.dayOfYear == 187); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF DateTime) is on. + +/ + @property void dayOfYear(int day) @safe pure + { + _date.dayOfYear = day; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt.dayOfYear = 12; + assert(dt.dayOfYear == 12); + static assert(!__traits(compiles, cdt.dayOfYear = 12)); + static assert(!__traits(compiles, idt.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. + +/ + @property int dayOfGregorianCal() @safe const pure nothrow + { + return _date.dayOfGregorianCal; + } + + /// + @safe unittest + { + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); + assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); + assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); + assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); + assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); + + assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.dayOfGregorianCal == 729_941); + assert(idt.dayOfGregorianCal == 729_941); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. + Setting this property does not affect the time portion of + $(LREF DateTime). + + Params: + days = The day of the Gregorian Calendar to set this $(LREF DateTime) + to. + +/ + @property void dayOfGregorianCal(int days) @safe pure nothrow + { + _date.dayOfGregorianCal = days; + } + + /// + @safe unittest + { + auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); + dt.dayOfGregorianCal = 1; + assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 365; + assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 366; + assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 0; + assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -365; + assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -366; + assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 730_120; + assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 734_137; + assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); + static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF DateTime) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) + +/ + @property ubyte isoWeek() @safe const pure nothrow + { + return _date.isoWeek; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.isoWeek == 27); + assert(cdt.isoWeek == 27); + assert(idt.isoWeek == 27); + } + + + /++ + $(LREF DateTime) for the last day in the month that this + $(LREF DateTime) is in. The time portion of endOfMonth is always + 23:59:59. + +/ + @property DateTime endOfMonth() @safe const pure nothrow + { + try + return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); + catch (Exception e) + assert(0, "DateTime constructor threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == + DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == + DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == + DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == + DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); + assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); + assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); + assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); + assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); + assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); + assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); + assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); + assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); + assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); + assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); + assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); + + // Test B.C. + assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); + assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); + assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); + assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); + assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); + assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); + assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); + assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); + assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); + assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); + assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); + assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); + assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + } + + + /++ + The last day in the month that this $(LREF DateTime) is in. + +/ + @property ubyte daysInMonth() @safe const pure nothrow + { + return _date.daysInMonth; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.daysInMonth == 31); + assert(idt.daysInMonth == 31); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const pure nothrow + { + return _date.isAD; + } + + /// + @safe unittest + { + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); + assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); + assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.isAD); + assert(idt.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this + $(LREF DateTime) at the given time. For example, prior to noon, + 1996-03-31 would be the Julian day number 2_450_173, so this function + returns 2_450_173, while from noon onward, the julian day number would + be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() @safe const pure nothrow + { + if (_tod._hour < 12) + return _date.julianDay - 1; + else + return _date.julianDay; + } + + @safe unittest + { + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); + assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); + + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); + + assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); + assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); + + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); + + assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); + assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); + + assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); + assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); + + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.julianDay == 2_451_366); + assert(idt.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any + time on this date (since, the modified Julian day changes at midnight). + +/ + @property long modJulianDay() @safe const pure nothrow + { + return _date.modJulianDay; + } + + @safe unittest + { + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); + + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.modJulianDay == 51_365); + assert(idt.modJulianDay == 51_365); + } + + + /++ + Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + return format("%sT%s", _date.toISOString(), _tod.toISOString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == + "20100704T070612"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == + "19981225T021500"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == + "00000105T230959"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == + "-00040105T000002"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toISOString() == "19990706T123033"); + assert(idt.toISOString() == "19990706T123033"); + } + + + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-MM-DDTHH:MM:SS. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == + "1998-12-25T02:15:00"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == + "-0004-01-05T00:00:02"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toISOExtString() == "1999-07-06T12:30:33"); + assert(idt.toISOExtString() == "1999-07-06T12:30:33"); + } + + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-Mon-DD HH:MM:SS. + +/ + string toSimpleString() @safe const pure nothrow + { + import std.format : format; + try + return format("%s %s", _date.toSimpleString(), _tod.toString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == + "1998-Dec-25 02:15:00"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == + "-0004-Jan-05 00:00:02"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33"); + assert(idt.toSimpleString() == "1999-Jul-06 12:30:33"); + } + + + /++ + Converts this $(LREF DateTime) to a string. + +/ + string toString() @safe const pure nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.toString()); + assert(cdt.toString()); + assert(idt.toString()); + } + + + + /++ + Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates and times. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO format or if the resulting $(LREF DateTime) would not + be valid. + +/ + static DateTime fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); + auto t = dstr.countUntil('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + immutable date = Date.fromISOString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromISOString("20100704T070612") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + + assert(DateTime.fromISOString("19981225T021500") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + + assert(DateTime.fromISOString("00000105T230959") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + + assert(DateTime.fromISOString("-00040105T000002") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + + assert(DateTime.fromISOString(" 20100704T070612 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Creates a $(LREF DateTime) from a string with the format + YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for dates + and times. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO Extended format or if the resulting $(LREF DateTime) + would not be valid. + +/ + static DateTime fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + auto t = dstr.countUntil('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + immutable date = Date.fromISOExtString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + + assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + + assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + + assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + + assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOExtString("")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Creates a $(LREF DateTime) from a string with the format + YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString + formats dates and times. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the correct format or if the resulting $(LREF DateTime) + would not be valid. + +/ + static DateTime fromSimpleString(S)(in S simpleString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); + auto t = dstr.countUntil(' '); + + enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); + + immutable date = Date.fromSimpleString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); + + assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == + DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Returns the $(LREF DateTime) farthest in the past which is representable + by $(LREF DateTime). + +/ + @property static DateTime min() @safe pure nothrow + out(result) + { + assert(result._date == Date.min); + assert(result._tod == TimeOfDay.min); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.min; + dt._date._month = Month.jan; + dt._date._day = 1; + + return dt; + } + + @safe unittest + { + assert(DateTime.min.year < 0); + assert(DateTime.min < DateTime.max); + } + + + /++ + Returns the $(LREF DateTime) farthest in the future which is + representable by $(LREF DateTime). + +/ + @property static DateTime max() @safe pure nothrow + out(result) + { + assert(result._date == Date.max); + assert(result._tod == TimeOfDay.max); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.max; + dt._date._month = Month.dec; + dt._date._day = 31; + dt._tod._hour = TimeOfDay.maxHour; + dt._tod._minute = TimeOfDay.maxMinute; + dt._tod._second = TimeOfDay.maxSecond; + + return dt; + } + + @safe unittest + { + assert(DateTime.max.year > 0); + assert(DateTime.max > DateTime.min); + } + + +private: + + /+ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. The + same goes for any larger units. + + Params: + seconds = The number of seconds to add to this $(LREF DateTime). + +/ + ref DateTime _addSeconds(long seconds) return @safe pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_tod._hour); + hnsecs += convert!("minutes", "hnsecs")(_tod._minute); + hnsecs += convert!("seconds", "hnsecs")(_tod._second); + + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + { + hnsecs += convert!("days", "hnsecs")(1); + --days; + } + + _date._addDays(days); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _tod._hour = cast(ubyte) newHours; + _tod._minute = cast(ubyte) newMinutes; + _tod._second = cast(ubyte) newSeconds; + + return this; + } + + @safe unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); + + testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); + + // Test B.C. + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); + + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); + + // Test Both + testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); + testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); + + testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); + testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); + testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); + testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt._addSeconds(4))); + static assert(!__traits(compiles, idt._addSeconds(4))); + } + + + Date _date; + TimeOfDay _tod; +} + + +/++ + Represents a date in the + $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic + Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years + are A.D. Non-positive years are B.C. + + Year, month, and day are kept separately internally so that $(D Date) is + optimized for calendar-based operations. + + $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian + leap year calculations for its entire length. As per + $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as + year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. + as a positive integer with 1 B.C. being the year prior to 1 A.D. + + Year 0 is a leap year. + +/ +struct Date +{ +public: + + /++ + Throws: + $(REF DateTimeException,std,datetime,date) if the resulting + $(LREF Date) would not be valid. + + Params: + year = Year of the Gregorian Calendar. Positive values are A.D. + Non-positive values are B.C. with year 0 being the year + prior to 1 A.D. + month = Month of the year. + day = Day of the month. + +/ + this(int year, int month, int day) @safe pure + { + enforceValid!"months"(cast(Month) month); + enforceValid!"days"(year, cast(Month) month, day); + + _year = cast(short) year; + _month = cast(Month) month; + _day = cast(ubyte) day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + assert(Date(1, 1, 1) == Date.init); + + static void testDate(in Date date, int year, int month, int day) + { + assert(date._year == year); + assert(date._month == month); + assert(date._day == day); + } + + testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); + testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); + testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); + + // Test A.D. + assertThrown!DateTimeException(Date(1, 0, 1)); + assertThrown!DateTimeException(Date(1, 1, 0)); + assertThrown!DateTimeException(Date(1999, 13, 1)); + assertThrown!DateTimeException(Date(1999, 1, 32)); + assertThrown!DateTimeException(Date(1999, 2, 29)); + assertThrown!DateTimeException(Date(2000, 2, 30)); + assertThrown!DateTimeException(Date(1999, 3, 32)); + assertThrown!DateTimeException(Date(1999, 4, 31)); + assertThrown!DateTimeException(Date(1999, 5, 32)); + assertThrown!DateTimeException(Date(1999, 6, 31)); + assertThrown!DateTimeException(Date(1999, 7, 32)); + assertThrown!DateTimeException(Date(1999, 8, 32)); + assertThrown!DateTimeException(Date(1999, 9, 31)); + assertThrown!DateTimeException(Date(1999, 10, 32)); + assertThrown!DateTimeException(Date(1999, 11, 31)); + assertThrown!DateTimeException(Date(1999, 12, 32)); + + assertNotThrown!DateTimeException(Date(1999, 1, 31)); + assertNotThrown!DateTimeException(Date(1999, 2, 28)); + assertNotThrown!DateTimeException(Date(2000, 2, 29)); + assertNotThrown!DateTimeException(Date(1999, 3, 31)); + assertNotThrown!DateTimeException(Date(1999, 4, 30)); + assertNotThrown!DateTimeException(Date(1999, 5, 31)); + assertNotThrown!DateTimeException(Date(1999, 6, 30)); + assertNotThrown!DateTimeException(Date(1999, 7, 31)); + assertNotThrown!DateTimeException(Date(1999, 8, 31)); + assertNotThrown!DateTimeException(Date(1999, 9, 30)); + assertNotThrown!DateTimeException(Date(1999, 10, 31)); + assertNotThrown!DateTimeException(Date(1999, 11, 30)); + assertNotThrown!DateTimeException(Date(1999, 12, 31)); + + // Test B.C. + assertNotThrown!DateTimeException(Date(0, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 12, 31)); + assertNotThrown!DateTimeException(Date(-1, 2, 28)); + assertNotThrown!DateTimeException(Date(-4, 2, 29)); + + assertThrown!DateTimeException(Date(-1, 2, 29)); + assertThrown!DateTimeException(Date(-2, 2, 29)); + assertThrown!DateTimeException(Date(-3, 2, 29)); + } + + + /++ + Params: + day = The Xth day of the Gregorian Calendar that the constructed + $(LREF Date) will be for. + +/ + this(int day) @safe pure nothrow + { + if (day > 0) + { + int years = (day / daysIn400Years) * 400 + 1; + day %= daysIn400Years; + + { + immutable tempYears = day / daysIn100Years; + + if (tempYears == 4) + { + years += 300; + day -= daysIn100Years * 3; + } + else + { + years += tempYears * 100; + day %= daysIn100Years; + } + } + + years += (day / daysIn4Years) * 4; + day %= daysIn4Years; + + { + immutable tempYears = day / daysInYear; + + if (tempYears == 4) + { + years += 3; + day -= daysInYear * 3; + } + else + { + years += tempYears; + day %= daysInYear; + } + } + + if (day == 0) + { + _year = cast(short)(years - 1); + _month = Month.dec; + _day = 31; + } + else + { + _year = cast(short) years; + + try + dayOfYear = day; + catch (Exception e) + assert(0, "dayOfYear assignment threw."); + } + } + else if (day <= 0 && -day < daysInLeapYear) + { + _year = 0; + + try + dayOfYear = (daysInLeapYear + day); + catch (Exception e) + assert(0, "dayOfYear assignment threw."); + } + else + { + day += daysInLeapYear - 1; + int years = (day / daysIn400Years) * 400 - 1; + day %= daysIn400Years; + + { + immutable tempYears = day / daysIn100Years; + + if (tempYears == -4) + { + years -= 300; + day += daysIn100Years * 3; + } + else + { + years += tempYears * 100; + day %= daysIn100Years; + } + } + + years += (day / daysIn4Years) * 4; + day %= daysIn4Years; + + { + immutable tempYears = day / daysInYear; + + if (tempYears == -4) + { + years -= 3; + day += daysInYear * 3; + } + else + { + years += tempYears; + day %= daysInYear; + } + } + + if (day == 0) + { + _year = cast(short)(years + 1); + _month = Month.jan; + _day = 1; + } + else + { + _year = cast(short) years; + immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1; + + try + dayOfYear = newDoY; + catch (Exception e) + assert(0, "dayOfYear assignment threw."); + } + } + } + + @safe unittest + { + import std.range : chain; + + // Test A.D. + foreach (gd; chain(testGregDaysBC, testGregDaysAD)) + assert(Date(gd.day) == gd.date); + } + + + /++ + Compares this $(LREF Date) with the given $(LREF Date). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in Date rhs) @safe const pure nothrow + { + if (_year < rhs._year) + return -1; + if (_year > rhs._year) + return 1; + + if (_month < rhs._month) + return -1; + if (_month > rhs._month) + return 1; + + if (_day < rhs._day) + return -1; + if (_day > rhs._day) + return 1; + + return 0; + } + + @safe unittest + { + // Test A.D. + assert(Date(1, 1, 1).opCmp(Date.init) == 0); + + assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); + assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); + assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); + + assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); + + assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); + + assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); + assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); + assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); + + assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); + assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); + assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); + assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); + + // Test B.C. + assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); + assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); + assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); + assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); + + assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); + + assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); + + assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); + assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); + assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); + + assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); + assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); + + // Test Both + assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); + + assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); + + assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); + + assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); + + assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); + assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); + + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.opCmp(date) == 0); + assert(date.opCmp(cdate) == 0); + assert(date.opCmp(idate) == 0); + assert(cdate.opCmp(date) == 0); + assert(cdate.opCmp(cdate) == 0); + assert(cdate.opCmp(idate) == 0); + assert(idate.opCmp(date) == 0); + assert(idate.opCmp(cdate) == 0); + assert(idate.opCmp(idate) == 0); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const pure nothrow + { + return _year; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } + + @safe unittest + { + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.year == 1999); + assert(idate.year == 1999); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this Date's year to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the new year is not + a leap year and the resulting date would be on February 29th. + +/ + @property void year(int year) @safe pure + { + enforceValid!"days"(year, _month, _day); + _year = cast(short) year; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } + + @safe unittest + { + static void testDateInvalid(Date date, int year) + { + date.year = year; + } + + static void testDate(Date date, int year, in Date expected) + { + date.year = year; + assert(date == expected); + } + + assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); + + testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); + testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); + testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.year = 1999)); + static assert(!__traits(compiles, idate.year = 1999)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(REF DateTimeException,std,datetime,date) if $(D isAD) is true. + +/ + @property ushort yearBC() @safe const pure + { + import std.format : format; + + if (isAD) + throw new DateTimeException(format("Year %s is A.D.", _year)); + return cast(ushort)((_year * -1) + 1); + } + + /// + @safe unittest + { + assert(Date(0, 1, 1).yearBC == 1); + assert(Date(-1, 1, 1).yearBC == 2); + assert(Date(-100, 1, 1).yearBC == 101); + } + + @safe unittest + { + assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); + + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + assert(date.yearBC == 1); + assert(cdate.yearBC == 1); + assert(idate.yearBC == 1); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF Date)'s year to. + + Throws: + $(REF DateTimeException,std,datetime,date) if a non-positive value + is given. + +/ + @property void yearBC(int year) @safe pure + { + if (year <= 0) + throw new DateTimeException("The given year is not a year B.C."); + _year = cast(short)((year - 1) * -1); + } + + /// + @safe unittest + { + auto date = Date(2010, 1, 1); + date.yearBC = 1; + assert(date == Date(0, 1, 1)); + + date.yearBC = 10; + assert(date == Date(-9, 1, 1)); + } + + @safe unittest + { + assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); + + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + date.yearBC = 7; + assert(date.yearBC == 7); + static assert(!__traits(compiles, cdate.yearBC = 7)); + static assert(!__traits(compiles, idate.yearBC = 7)); + } + + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const pure nothrow + { + return _month; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).month == 7); + assert(Date(2010, 10, 4).month == 10); + assert(Date(-7, 4, 5).month == 4); + } + + @safe unittest + { + assert(Date.init.month == 1); + assert(Date(1999, 7, 6).month == 7); + assert(Date(-1999, 7, 6).month == 7); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.month == 7); + assert(idate.month == 7); + } + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF Date)'s month to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given month is + not a valid month or if the current day would not be valid in the + given month. + +/ + @property void month(Month month) @safe pure + { + enforceValid!"months"(month); + enforceValid!"days"(_year, month, _day); + _month = cast(Month) month; + } + + @safe unittest + { + static void testDate(Date date, Month month, in Date expected = Date.init) + { + date.month = month; + assert(expected != Date.init); + assert(date == expected); + } + + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13)); + assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2)); + assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2)); + + testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1)); + testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.month = 7)); + static assert(!__traits(compiles, idate.month = 7)); + } + + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const pure nothrow + { + return _day; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).day == 6); + assert(Date(2010, 10, 4).day == 4); + assert(Date(-7, 4, 5).day == 5); + } + + @safe unittest + { + import std.format : format; + import std.range : chain; + + static void test(Date date, int expected) + { + assert(date.day == expected, format("Value given: %s", date)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + test(Date(year, md.month, md.day), md.day); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.day == 6); + assert(idate.day == 6); + } + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF Date)'s day to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given day is not + a valid day of the current month. + +/ + @property void day(int day) @safe pure + { + enforceValid!"days"(_year, _month, day); + _day = cast(ubyte) day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + + static void testDate(Date date, int day) + { + date.day = day; + } + + // Test A.D. + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); + + { + auto date = Date(1, 1, 1); + date.day = 6; + assert(date == Date(1, 1, 6)); + } + + // Test B.C. + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); + + { + auto date = Date(-1, 1, 1); + date.day = 6; + assert(date == Date(-1, 1, 6)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.day = 6)); + static assert(!__traits(compiles, idate.day = 6)); + } + + + /++ + Adds the given number of years or months to this $(LREF Date). A + negative number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF Date). + allowOverflow = Whether the day should be allowed to overflow, + causing the month to increment. + +/ + ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years") + { + _year += value; + + if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year)) + { + if (allowOverflow == AllowDayOverflow.yes) + { + _month = Month.mar; + _day = 1; + } + else + _day = 28; + } + + return this; + } + + /// + @safe unittest + { + auto d1 = Date(2010, 1, 1); + d1.add!"months"(11); + assert(d1 == Date(2010, 12, 1)); + + auto d2 = Date(2010, 1, 1); + d2.add!"months"(-11); + assert(d2 == Date(2009, 2, 1)); + + auto d3 = Date(2000, 2, 29); + d3.add!"years"(1); + assert(d3 == Date(2001, 3, 1)); + + auto d4 = Date(2000, 2, 29); + d4.add!"years"(1, AllowDayOverflow.no); + assert(d4 == Date(2001, 2, 28)); + } + + // Test add!"years"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"years"(7); + assert(date == Date(2006, 7, 6)); + date.add!"years"(-9); + assert(date == Date(1997, 7, 6)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"years"(1); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1); + assert(date == Date(1999, 3, 1)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7); + assert(date == Date(-2006, 7, 6)); + date.add!"years"(9); + assert(date == Date(-1997, 7, 6)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1); + assert(date == Date(-1999, 3, 1)); + } + + // Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5); + assert(date == Date(-1, 7, 6)); + date.add!"years"(5); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(5); + assert(date == Date(1, 7, 6)); + date.add!"years"(-5); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(4, 7, 6); + date.add!"years"(-8); + assert(date == Date(-4, 7, 6)); + date.add!"years"(8); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(8); + assert(date == Date(4, 7, 6)); + date.add!"years"(-8); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(-4, 2, 29); + date.add!"years"(5); + assert(date == Date(1, 3, 1)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5); + assert(date == Date(-1, 3, 1)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5).add!"years"(7); + assert(date == Date(6, 3, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"years"(7))); + static assert(!__traits(compiles, idate.add!"years"(7))); + } + + // Test add!"years"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"years"(7, AllowDayOverflow.no); + assert(date == Date(2006, 7, 6)); + date.add!"years"(-9, AllowDayOverflow.no); + assert(date == Date(1997, 7, 6)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"years"(1, AllowDayOverflow.no); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7, AllowDayOverflow.no); + assert(date == Date(-2006, 7, 6)); + date.add!"years"(9, AllowDayOverflow.no); + assert(date == Date(-1997, 7, 6)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + } + + // Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-1, 7, 6)); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(1, 7, 6)); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(4, 7, 6); + date.add!"years"(-8, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + date.add!"years"(8, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(8, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + date.add!"years"(-8, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(-4, 2, 29); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(1, 2, 28)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-1, 2, 28)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); + assert(date == Date(6, 2, 28)); + } + } + + + // Shares documentation with "years" version. + ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "months") + { + auto years = months / 12; + months %= 12; + auto newMonth = _month + months; + + if (months < 0) + { + if (newMonth < 1) + { + newMonth += 12; + --years; + } + } + else if (newMonth > 12) + { + newMonth -= 12; + ++years; + } + + _year += years; + _month = cast(Month) newMonth; + + immutable currMaxDay = maxDay(_year, _month); + immutable overflow = _day - currMaxDay; + + if (overflow > 0) + { + if (allowOverflow == AllowDayOverflow.yes) + { + ++_month; + _day = cast(ubyte) overflow; + } + else + _day = cast(ubyte) currMaxDay; + } + + return this; + } + + // Test add!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"months"(3); + assert(date == Date(1999, 10, 6)); + date.add!"months"(-4); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(6); + assert(date == Date(2000, 1, 6)); + date.add!"months"(-6); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(27); + assert(date == Date(2001, 10, 6)); + date.add!"months"(-28); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(1); + assert(date == Date(1999, 7, 1)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1); + assert(date == Date(1999, 5, 1)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"months"(12); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"months"(12); + assert(date == Date(2001, 3, 1)); + } + + { + auto date = Date(1999, 7, 31); + date.add!"months"(1); + assert(date == Date(1999, 8, 31)); + date.add!"months"(1); + assert(date == Date(1999, 10, 1)); + } + + { + auto date = Date(1998, 8, 31); + date.add!"months"(13); + assert(date == Date(1999, 10, 1)); + date.add!"months"(-13); + assert(date == Date(1998, 9, 1)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(13); + assert(date == Date(1999, 1, 31)); + date.add!"months"(-13); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(14); + assert(date == Date(1999, 3, 3)); + date.add!"months"(-14); + assert(date == Date(1998, 1, 3)); + } + + { + auto date = Date(1998, 12, 31); + date.add!"months"(14); + assert(date == Date(2000, 3, 2)); + date.add!"months"(-14); + assert(date == Date(1999, 1, 2)); + } + + { + auto date = Date(1999, 12, 31); + date.add!"months"(14); + assert(date == Date(2001, 3, 3)); + date.add!"months"(-14); + assert(date == Date(2000, 1, 3)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3); + assert(date == Date(-1999, 10, 6)); + date.add!"months"(-4); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6); + assert(date == Date(-1998, 1, 6)); + date.add!"months"(-6); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27); + assert(date == Date(-2001, 4, 6)); + date.add!"months"(28); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1); + assert(date == Date(-1999, 7, 1)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1); + assert(date == Date(-1999, 5, 1)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12); + assert(date == Date(-2001, 3, 1)); + } + + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1); + assert(date == Date(-1999, 8, 31)); + date.add!"months"(1); + assert(date == Date(-1999, 10, 1)); + } + + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13); + assert(date == Date(-1997, 10, 1)); + date.add!"months"(-13); + assert(date == Date(-1998, 9, 1)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13); + assert(date == Date(-1995, 1, 31)); + date.add!"months"(-13); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14); + assert(date == Date(-1995, 3, 3)); + date.add!"months"(-14); + assert(date == Date(-1996, 1, 3)); + } + + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14); + assert(date == Date(-2000, 3, 2)); + date.add!"months"(-14); + assert(date == Date(-2001, 1, 2)); + } + + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14); + assert(date == Date(-1999, 3, 3)); + date.add!"months"(-14); + assert(date == Date(-2000, 1, 3)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1); + assert(date == Date(0, 12, 1)); + date.add!"months"(1); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.add!"months"(-48); + assert(date == Date(0, 1, 1)); + date.add!"months"(48); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-49); + assert(date == Date(0, 3, 2)); + date.add!"months"(49); + assert(date == Date(4, 4, 2)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-85); + assert(date == Date(-3, 3, 3)); + date.add!"months"(85); + assert(date == Date(4, 4, 3)); + } + + { + auto date = Date(-3, 3, 31); + date.add!"months"(85).add!"months"(-83); + assert(date == Date(-3, 6, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"months"(3))); + static assert(!__traits(compiles, idate.add!"months"(3))); + } + + // Test add!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assert(date == Date(2000, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(27, AllowDayOverflow.no); + assert(date == Date(2001, 10, 6)); + date.add!"months"(-28, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 4, 30)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"months"(12, AllowDayOverflow.no); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"months"(12, AllowDayOverflow.no); + assert(date == Date(2001, 2, 28)); + } + + { + auto date = Date(1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + } + + { + auto date = Date(1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1998, 8, 30)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(1999, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1997, 12, 28)); + } + + { + auto date = Date(1998, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1998, 12, 29)); + } + + { + auto date = Date(1999, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(2001, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1999, 12, 28)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assert(date == Date(-1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assert(date == Date(-1998, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27, AllowDayOverflow.no); + assert(date == Date(-2001, 4, 6)); + date.add!"months"(28, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 30)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2001, 2, 28)); + } + + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 9, 30)); + } + + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1997, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1998, 8, 30)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1995, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1995, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 28)); + } + + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2002, 12, 29)); + } + + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2001, 12, 28)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(0, 12, 1)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.add!"months"(-48, AllowDayOverflow.no); + assert(date == Date(0, 1, 1)); + date.add!"months"(48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-49, AllowDayOverflow.no); + assert(date == Date(0, 2, 29)); + date.add!"months"(49, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-85, AllowDayOverflow.no); + assert(date == Date(-3, 2, 28)); + date.add!"months"(85, AllowDayOverflow.no); + assert(date == Date(4, 3, 28)); + } + + { + auto date = Date(-3, 3, 31); + date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); + assert(date == Date(-3, 5, 30)); + } + } + + + /++ + Adds the given number of years or months to this $(LREF Date). A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF Date) 12 months gets + the exact same $(LREF Date). However, the days can still be affected due + to the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF Date). + allowOverflow = Whether the day should be allowed to overflow, + causing the month to increment. + +/ + ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years") + { + return add!"years"(value, allowOverflow); + } + + /// + @safe unittest + { + auto d1 = Date(2010, 1, 1); + d1.roll!"months"(1); + assert(d1 == Date(2010, 2, 1)); + + auto d2 = Date(2010, 1, 1); + d2.roll!"months"(-1); + assert(d2 == Date(2010, 12, 1)); + + auto d3 = Date(1999, 1, 29); + d3.roll!"months"(1); + assert(d3 == Date(1999, 3, 1)); + + auto d4 = Date(1999, 1, 29); + d4.roll!"months"(1, AllowDayOverflow.no); + assert(d4 == Date(1999, 2, 28)); + + auto d5 = Date(2000, 2, 29); + d5.roll!"years"(1); + assert(d5 == Date(2001, 3, 1)); + + auto d6 = Date(2000, 2, 29); + d6.roll!"years"(1, AllowDayOverflow.no); + assert(d6 == Date(2001, 2, 28)); + } + + @safe unittest + { + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"years"(3))); + static assert(!__traits(compiles, idate.rolYears(3))); + } + + + // Shares documentation with "years" version. + ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "months") + { + months %= 12; + auto newMonth = _month + months; + + if (months < 0) + { + if (newMonth < 1) + newMonth += 12; + } + else + { + if (newMonth > 12) + newMonth -= 12; + } + + _month = cast(Month) newMonth; + + immutable currMaxDay = maxDay(_year, _month); + immutable overflow = _day - currMaxDay; + + if (overflow > 0) + { + if (allowOverflow == AllowDayOverflow.yes) + { + ++_month; + _day = cast(ubyte) overflow; + } + else + _day = cast(ubyte) currMaxDay; + } + + return this; + } + + // Test roll!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.roll!"months"(3); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-4); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6); + assert(date == Date(1999, 1, 6)); + date.roll!"months"(-6); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-28); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1); + assert(date == Date(1999, 7, 1)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1); + assert(date == Date(1999, 5, 1)); + } + + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1); + assert(date == Date(1999, 8, 31)); + date.roll!"months"(1); + assert(date == Date(1999, 10, 1)); + } + + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13); + assert(date == Date(1998, 10, 1)); + date.roll!"months"(-13); + assert(date == Date(1998, 9, 1)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13); + assert(date == Date(1997, 1, 31)); + date.roll!"months"(-13); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14); + assert(date == Date(1997, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1997, 1, 3)); + } + + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14); + assert(date == Date(1998, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1998, 1, 3)); + } + + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14); + assert(date == Date(1999, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1999, 1, 3)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3); + assert(date == Date(-1999, 10, 6)); + date.roll!"months"(-4); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6); + assert(date == Date(-1999, 1, 6)); + date.roll!"months"(-6); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27); + assert(date == Date(-1999, 4, 6)); + date.roll!"months"(28); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1); + assert(date == Date(-1999, 7, 1)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1); + assert(date == Date(-1999, 5, 1)); + } + + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1); + assert(date == Date(-1999, 8, 31)); + date.roll!"months"(1); + assert(date == Date(-1999, 10, 1)); + } + + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13); + assert(date == Date(-1998, 10, 1)); + date.roll!"months"(-13); + assert(date == Date(-1998, 9, 1)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13); + assert(date == Date(-1997, 1, 31)); + date.roll!"months"(-13); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14); + assert(date == Date(-1997, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-1997, 1, 3)); + } + + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14); + assert(date == Date(-2002, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-2002, 1, 3)); + } + + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14); + assert(date == Date(-2001, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-2001, 1, 3)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1); + assert(date == Date(1, 12, 1)); + date.roll!"months"(1); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48); + assert(date == Date(4, 1, 1)); + date.roll!"months"(48); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49); + assert(date == Date(4, 3, 2)); + date.roll!"months"(49); + assert(date == Date(4, 4, 2)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85); + assert(date == Date(4, 3, 2)); + date.roll!"months"(85); + assert(date == Date(4, 4, 2)); + } + + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1); + assert(date == Date(-1, 12, 1)); + date.roll!"months"(1); + assert(date == Date(-1, 1, 1)); + } + + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48); + assert(date == Date(-4, 1, 1)); + date.roll!"months"(48); + assert(date == Date(-4, 1, 1)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49); + assert(date == Date(-4, 3, 2)); + date.roll!"months"(49); + assert(date == Date(-4, 4, 2)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85); + assert(date == Date(-4, 3, 2)); + date.roll!"months"(85); + assert(date == Date(-4, 4, 2)); + } + + { + auto date = Date(-3, 3, 31); + date.roll!"months"(85).roll!"months"(-83); + assert(date == Date(-3, 6, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"months"(3))); + static assert(!__traits(compiles, idate.roll!"months"(3))); + } + + // Test roll!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assert(date == Date(1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-28, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 4, 30)); + } + + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12, AllowDayOverflow.no); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + } + + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1998, 8, 30)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1997, 12, 28)); + } + + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1998, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1998, 12, 28)); + } + + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1999, 12, 28)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assert(date == Date(-1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assert(date == Date(-1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 6)); + date.roll!"months"(28, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 30)); + } + + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 9, 30)); + } + + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1998, 8, 30)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 28)); + } + + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2002, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2002, 12, 28)); + } + + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2001, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2001, 12, 28)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assert(date == Date(4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assert(date == Date(4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } + + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1, 1, 1)); + } + + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assert(date == Date(-4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assert(date == Date(-4, 1, 1)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assert(date == Date(-4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assert(date == Date(-4, 3, 29)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assert(date == Date(-4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assert(date == Date(-4, 3, 29)); + } + + { + auto date = Date(-3, 3, 31); + date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); + assert(date == Date(-3, 5, 30)); + } + } + + + /++ + Adds the given number of units to this $(LREF Date). A negative number + will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF Date) one + year's worth of days gets the exact same $(LREF Date). + + The only accepted units are $(D "days"). + + Params: + units = The units to add. Must be $(D "days"). + days = The number of days to add to this $(LREF Date). + +/ + ref Date roll(string units)(long days) @safe pure nothrow + if (units == "days") + { + immutable limit = maxDay(_year, _month); + days %= limit; + auto newDay = _day + days; + + if (days < 0) + { + if (newDay < 1) + newDay += limit; + } + else if (newDay > limit) + newDay -= limit; + + _day = cast(ubyte) newDay; + return this; + } + + /// + @safe unittest + { + auto d = Date(2010, 1, 1); + d.roll!"days"(1); + assert(d == Date(2010, 1, 2)); + d.roll!"days"(365); + assert(d == Date(2010, 1, 26)); + d.roll!"days"(-32); + assert(d == Date(2010, 1, 25)); + } + + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 2, 28); + date.roll!"days"(1); + assert(date == Date(1999, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 28); + date.roll!"days"(1); + assert(date == Date(2000, 2, 29)); + date.roll!"days"(1); + assert(date == Date(2000, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 6, 30); + date.roll!"days"(1); + assert(date == Date(1999, 6, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"days"(1); + assert(date == Date(1999, 7, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 1, 1); + date.roll!"days"(-1); + assert(date == Date(1999, 1, 31)); + date.roll!"days"(1); + assert(date == Date(1999, 1, 1)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"days"(9); + assert(date == Date(1999, 7, 15)); + date.roll!"days"(-11); + assert(date == Date(1999, 7, 4)); + date.roll!"days"(30); + assert(date == Date(1999, 7, 3)); + date.roll!"days"(-3); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"days"(365); + assert(date == Date(1999, 7, 30)); + date.roll!"days"(-365); + assert(date == Date(1999, 7, 6)); + date.roll!"days"(366); + assert(date == Date(1999, 7, 31)); + date.roll!"days"(730); + assert(date == Date(1999, 7, 17)); + date.roll!"days"(-1096); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 2, 6); + date.roll!"days"(365); + assert(date == Date(1999, 2, 7)); + date.roll!"days"(-365); + assert(date == Date(1999, 2, 6)); + date.roll!"days"(366); + assert(date == Date(1999, 2, 8)); + date.roll!"days"(730); + assert(date == Date(1999, 2, 10)); + date.roll!"days"(-1096); + assert(date == Date(1999, 2, 6)); + } + + // Test B.C. + { + auto date = Date(-1999, 2, 28); + date.roll!"days"(1); + assert(date == Date(-1999, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 28); + date.roll!"days"(1); + assert(date == Date(-2000, 2, 29)); + date.roll!"days"(1); + assert(date == Date(-2000, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 6, 30); + date.roll!"days"(1); + assert(date == Date(-1999, 6, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"days"(1); + assert(date == Date(-1999, 7, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 1, 1); + date.roll!"days"(-1); + assert(date == Date(-1999, 1, 31)); + date.roll!"days"(1); + assert(date == Date(-1999, 1, 1)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(9); + assert(date == Date(-1999, 7, 15)); + date.roll!"days"(-11); + assert(date == Date(-1999, 7, 4)); + date.roll!"days"(30); + assert(date == Date(-1999, 7, 3)); + date.roll!"days"(-3); + assert(date == Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(365); + assert(date == Date(-1999, 7, 30)); + date.roll!"days"(-365); + assert(date == Date(-1999, 7, 6)); + date.roll!"days"(366); + assert(date == Date(-1999, 7, 31)); + date.roll!"days"(730); + assert(date == Date(-1999, 7, 17)); + date.roll!"days"(-1096); + assert(date == Date(-1999, 7, 6)); + } + + // Test Both + { + auto date = Date(1, 7, 6); + date.roll!"days"(-365); + assert(date == Date(1, 7, 13)); + date.roll!"days"(365); + assert(date == Date(1, 7, 6)); + date.roll!"days"(-731); + assert(date == Date(1, 7, 19)); + date.roll!"days"(730); + assert(date == Date(1, 7, 5)); + } + + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365); + assert(date == Date(0, 7, 13)); + date.roll!"days"(365); + assert(date == Date(0, 7, 6)); + date.roll!"days"(-731); + assert(date == Date(0, 7, 19)); + date.roll!"days"(730); + assert(date == Date(0, 7, 5)); + } + + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); + assert(date == Date(0, 7, 8)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"days"(12))); + static assert(!__traits(compiles, idate.roll!"days"(12))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from + + The legal types of arithmetic for $(LREF Date) using this operator are + + $(BOOKTABLE, + $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) + $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF Date). + +/ + Date opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + Date retval = this; + immutable days = duration.total!"days"; + mixin("return retval._addDays(" ~ op ~ "days);"); + } + + /// + @safe unittest + { + import core.time : days; + + assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1)); + assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1)); + + assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31)); + assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26)); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + + assert(date + dur!"weeks"(7) == Date(1999, 8, 24)); + assert(date + dur!"weeks"(-7) == Date(1999, 5, 18)); + assert(date + dur!"days"(7) == Date(1999, 7, 13)); + assert(date + dur!"days"(-7) == Date(1999, 6, 29)); + + assert(date + dur!"hours"(24) == Date(1999, 7, 7)); + assert(date + dur!"hours"(-24) == Date(1999, 7, 5)); + assert(date + dur!"minutes"(1440) == Date(1999, 7, 7)); + assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5)); + assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7)); + assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5)); + assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); + assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); + assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); + assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); + + assert(date - dur!"weeks"(-7) == Date(1999, 8, 24)); + assert(date - dur!"weeks"(7) == Date(1999, 5, 18)); + assert(date - dur!"days"(-7) == Date(1999, 7, 13)); + assert(date - dur!"days"(7) == Date(1999, 6, 29)); + + assert(date - dur!"hours"(-24) == Date(1999, 7, 7)); + assert(date - dur!"hours"(24) == Date(1999, 7, 5)); + assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7)); + assert(date - dur!"minutes"(1440) == Date(1999, 7, 5)); + assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7)); + assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5)); + assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); + assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); + assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); + assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); + + auto duration = dur!"days"(12); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date + duration == Date(1999, 7, 18)); + assert(cdate + duration == Date(1999, 7, 18)); + assert(idate + duration == Date(1999, 7, 18)); + + assert(date - duration == Date(1999, 6, 24)); + assert(cdate - duration == Date(1999, 6, 24)); + assert(idate - duration == Date(1999, 6, 24)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + Date opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + Date retval = this; + immutable days = convert!("hnsecs", "days")(td.hnsecs); + mixin("return retval._addDays(" ~ op ~ "days);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto date = Date(1999, 7, 6); + + assert(date + TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(date + TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + + assert(date - TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(date - TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF Date), as well as assigning the result to this + $(LREF Date). + + The legal types of arithmetic for $(LREF Date) using this operator are + + $(BOOKTABLE, + $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) + $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF Date). + +/ + ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable days = duration.total!"days"; + mixin("return _addDays(" ~ op ~ "days);"); + } + + @safe unittest + { + assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24)); + assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18)); + assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13)); + assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29)); + + assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); + + assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24)); + assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18)); + assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13)); + assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29)); + + assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); + + { + auto date = Date(0, 1, 31); + (date += dur!"days"(507)) += dur!"days"(-2); + assert(date == Date(1, 6, 19)); + } + + auto duration = dur!"days"(12); + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + date += duration; + static assert(!__traits(compiles, cdate += duration)); + static assert(!__traits(compiles, idate += duration)); + + date -= duration; + static assert(!__traits(compiles, cdate -= duration)); + static assert(!__traits(compiles, idate -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref Date opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable days = convert!("seconds", "days")(td.seconds); + mixin("return _addDays(" ~ op ~ "days);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto date = Date(1999, 7, 6); + date += TickDuration.from!"usecs"(86_400_000_000); + assert(date == Date(1999, 7, 7)); + } + + { + auto date = Date(1999, 7, 6); + date += TickDuration.from!"usecs"(-86_400_000_000); + assert(date == Date(1999, 7, 5)); + } + + { + auto date = Date(1999, 7, 6); + date -= TickDuration.from!"usecs"(-86_400_000_000); + assert(date == Date(1999, 7, 7)); + } + + { + auto date = Date(1999, 7, 6); + date -= TickDuration.from!"usecs"(86_400_000_000); + assert(date == Date(1999, 7, 5)); + } + } + } + + + /++ + Gives the difference between two $(LREF Date)s. + + The legal types of arithmetic for $(LREF Date) using this operator are + + $(BOOKTABLE, + $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in Date rhs) @safe const pure nothrow + if (op == "-") + { + return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + + assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365)); + assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365)); + assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31)); + assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31)); + assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1)); + assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date - date == Duration.zero); + assert(cdate - date == Duration.zero); + assert(idate - date == Duration.zero); + + assert(date - cdate == Duration.zero); + assert(cdate - cdate == Duration.zero); + assert(idate - cdate == Duration.zero); + + assert(date - idate == Duration.zero); + assert(cdate - idate == Duration.zero); + assert(idate - idate == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF Date)s in months. + + To get the difference in years, subtract the year property + of two $(LREF Date)s. To get the difference in days or weeks, + subtract the $(LREF Date)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. + + Note that the number of days in the months or how far into the month + either $(LREF Date) is is irrelevant. It is the difference in the month + property combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF Date) to subtract from this one. + +/ + int diffMonths(in Date rhs) @safe const pure nothrow + { + immutable yearDiff = _year - rhs._year; + immutable monthDiff = _month - rhs._month; + + return yearDiff * 12 + monthDiff; + } + + /// + @safe unittest + { + assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); + assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); + assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); + assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + + // Test A.D. + assert(date.diffMonths(Date(1998, 6, 5)) == 13); + assert(date.diffMonths(Date(1998, 7, 5)) == 12); + assert(date.diffMonths(Date(1998, 8, 5)) == 11); + assert(date.diffMonths(Date(1998, 9, 5)) == 10); + assert(date.diffMonths(Date(1998, 10, 5)) == 9); + assert(date.diffMonths(Date(1998, 11, 5)) == 8); + assert(date.diffMonths(Date(1998, 12, 5)) == 7); + assert(date.diffMonths(Date(1999, 1, 5)) == 6); + assert(date.diffMonths(Date(1999, 2, 6)) == 5); + assert(date.diffMonths(Date(1999, 3, 6)) == 4); + assert(date.diffMonths(Date(1999, 4, 6)) == 3); + assert(date.diffMonths(Date(1999, 5, 6)) == 2); + assert(date.diffMonths(Date(1999, 6, 6)) == 1); + assert(date.diffMonths(date) == 0); + assert(date.diffMonths(Date(1999, 8, 6)) == -1); + assert(date.diffMonths(Date(1999, 9, 6)) == -2); + assert(date.diffMonths(Date(1999, 10, 6)) == -3); + assert(date.diffMonths(Date(1999, 11, 6)) == -4); + assert(date.diffMonths(Date(1999, 12, 6)) == -5); + assert(date.diffMonths(Date(2000, 1, 6)) == -6); + assert(date.diffMonths(Date(2000, 2, 6)) == -7); + assert(date.diffMonths(Date(2000, 3, 6)) == -8); + assert(date.diffMonths(Date(2000, 4, 6)) == -9); + assert(date.diffMonths(Date(2000, 5, 6)) == -10); + assert(date.diffMonths(Date(2000, 6, 6)) == -11); + assert(date.diffMonths(Date(2000, 7, 6)) == -12); + assert(date.diffMonths(Date(2000, 8, 6)) == -13); + + assert(Date(1998, 6, 5).diffMonths(date) == -13); + assert(Date(1998, 7, 5).diffMonths(date) == -12); + assert(Date(1998, 8, 5).diffMonths(date) == -11); + assert(Date(1998, 9, 5).diffMonths(date) == -10); + assert(Date(1998, 10, 5).diffMonths(date) == -9); + assert(Date(1998, 11, 5).diffMonths(date) == -8); + assert(Date(1998, 12, 5).diffMonths(date) == -7); + assert(Date(1999, 1, 5).diffMonths(date) == -6); + assert(Date(1999, 2, 6).diffMonths(date) == -5); + assert(Date(1999, 3, 6).diffMonths(date) == -4); + assert(Date(1999, 4, 6).diffMonths(date) == -3); + assert(Date(1999, 5, 6).diffMonths(date) == -2); + assert(Date(1999, 6, 6).diffMonths(date) == -1); + assert(Date(1999, 8, 6).diffMonths(date) == 1); + assert(Date(1999, 9, 6).diffMonths(date) == 2); + assert(Date(1999, 10, 6).diffMonths(date) == 3); + assert(Date(1999, 11, 6).diffMonths(date) == 4); + assert(Date(1999, 12, 6).diffMonths(date) == 5); + assert(Date(2000, 1, 6).diffMonths(date) == 6); + assert(Date(2000, 2, 6).diffMonths(date) == 7); + assert(Date(2000, 3, 6).diffMonths(date) == 8); + assert(Date(2000, 4, 6).diffMonths(date) == 9); + assert(Date(2000, 5, 6).diffMonths(date) == 10); + assert(Date(2000, 6, 6).diffMonths(date) == 11); + assert(Date(2000, 7, 6).diffMonths(date) == 12); + assert(Date(2000, 8, 6).diffMonths(date) == 13); + + assert(date.diffMonths(Date(1999, 6, 30)) == 1); + assert(date.diffMonths(Date(1999, 7, 1)) == 0); + assert(date.diffMonths(Date(1999, 7, 6)) == 0); + assert(date.diffMonths(Date(1999, 7, 11)) == 0); + assert(date.diffMonths(Date(1999, 7, 16)) == 0); + assert(date.diffMonths(Date(1999, 7, 21)) == 0); + assert(date.diffMonths(Date(1999, 7, 26)) == 0); + assert(date.diffMonths(Date(1999, 7, 31)) == 0); + assert(date.diffMonths(Date(1999, 8, 1)) == -1); + + assert(date.diffMonths(Date(1990, 6, 30)) == 109); + assert(date.diffMonths(Date(1990, 7, 1)) == 108); + assert(date.diffMonths(Date(1990, 7, 6)) == 108); + assert(date.diffMonths(Date(1990, 7, 11)) == 108); + assert(date.diffMonths(Date(1990, 7, 16)) == 108); + assert(date.diffMonths(Date(1990, 7, 21)) == 108); + assert(date.diffMonths(Date(1990, 7, 26)) == 108); + assert(date.diffMonths(Date(1990, 7, 31)) == 108); + assert(date.diffMonths(Date(1990, 8, 1)) == 107); + + assert(Date(1999, 6, 30).diffMonths(date) == -1); + assert(Date(1999, 7, 1).diffMonths(date) == 0); + assert(Date(1999, 7, 6).diffMonths(date) == 0); + assert(Date(1999, 7, 11).diffMonths(date) == 0); + assert(Date(1999, 7, 16).diffMonths(date) == 0); + assert(Date(1999, 7, 21).diffMonths(date) == 0); + assert(Date(1999, 7, 26).diffMonths(date) == 0); + assert(Date(1999, 7, 31).diffMonths(date) == 0); + assert(Date(1999, 8, 1).diffMonths(date) == 1); + + assert(Date(1990, 6, 30).diffMonths(date) == -109); + assert(Date(1990, 7, 1).diffMonths(date) == -108); + assert(Date(1990, 7, 6).diffMonths(date) == -108); + assert(Date(1990, 7, 11).diffMonths(date) == -108); + assert(Date(1990, 7, 16).diffMonths(date) == -108); + assert(Date(1990, 7, 21).diffMonths(date) == -108); + assert(Date(1990, 7, 26).diffMonths(date) == -108); + assert(Date(1990, 7, 31).diffMonths(date) == -108); + assert(Date(1990, 8, 1).diffMonths(date) == -107); + + // Test B.C. + auto dateBC = Date(-1999, 7, 6); + + assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13); + assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12); + assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11); + assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10); + assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9); + assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8); + assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7); + assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6); + assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5); + assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4); + assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3); + assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2); + assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1); + assert(dateBC.diffMonths(dateBC) == 0); + assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1); + assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2); + assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3); + assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4); + assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5); + assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6); + assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7); + assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8); + assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9); + assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10); + assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11); + assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12); + assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13); + + assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13); + assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12); + assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11); + assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10); + assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9); + assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8); + assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7); + assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6); + assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5); + assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4); + assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3); + assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2); + assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1); + assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1); + assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2); + assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3); + assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4); + assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5); + assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6); + assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7); + assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8); + assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9); + assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10); + assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11); + assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12); + assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13); + + assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1); + assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0); + assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1); + + assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109); + assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108); + assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107); + + assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1); + assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0); + assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1); + + assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109); + assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108); + assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107); + + // Test Both + assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94); + assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.diffMonths(date) == 0); + assert(cdate.diffMonths(date) == 0); + assert(idate.diffMonths(date) == 0); + + assert(date.diffMonths(cdate) == 0); + assert(cdate.diffMonths(cdate) == 0); + assert(idate.diffMonths(cdate) == 0); + + assert(date.diffMonths(idate) == 0); + assert(cdate.diffMonths(idate) == 0); + assert(idate.diffMonths(idate) == 0); + } + + + /++ + Whether this $(LREF Date) is in a leap year. + +/ + @property bool isLeapYear() @safe const pure nothrow + { + return yearIsLeapYear(_year); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, date.isLeapYear = true)); + static assert(!__traits(compiles, cdate.isLeapYear = true)); + static assert(!__traits(compiles, idate.isLeapYear = true)); + } + + + /++ + Day of the week this $(LREF Date) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const pure nothrow + { + return getDayOfWeek(dayOfGregorianCal); + } + + @safe unittest + { + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.dayOfWeek == DayOfWeek.tue); + static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); + assert(idate.dayOfWeek == DayOfWeek.tue); + static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); + } + + + /++ + Day of the year this $(LREF Date) is on. + +/ + @property ushort dayOfYear() @safe const pure nothrow + { + if (_month >= Month.jan && _month <= Month.dec) + { + immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; + auto monthIndex = _month - Month.jan; + + return cast(ushort)(lastDay[monthIndex] + _day); + } + assert(0, "Invalid month."); + } + + /// + @safe unittest + { + assert(Date(1999, 1, 1).dayOfYear == 1); + assert(Date(1999, 12, 31).dayOfYear == 365); + assert(Date(2000, 12, 31).dayOfYear == 366); + } + + @safe unittest + { + import std.algorithm.iteration : filter; + import std.range : chain; + + foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD))) + { + foreach (doy; testDaysOfYear) + assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); + } + + foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD))) + { + foreach (doy; testDaysOfLeapYear) + assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.dayOfYear == 187); + assert(idate.dayOfYear == 187); + } + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF Date) is on. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given day is an + invalid day of the year. + +/ + @property void dayOfYear(int day) @safe pure + { + immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; + + if (day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear)) + throw new DateTimeException("Invalid day of the year."); + + foreach (i; 1 .. lastDay.length) + { + if (day <= lastDay[i]) + { + _month = cast(Month)(cast(int) Month.jan + i - 1); + _day = cast(ubyte)(day - lastDay[i - 1]); + return; + } + } + assert(0, "Invalid day of the year."); + } + + @safe unittest + { + static void test(Date date, int day, MonthDay expected, size_t line = __LINE__) + { + date.dayOfYear = day; + assert(date.month == expected.month); + assert(date.day == expected.day); + } + + foreach (doy; testDaysOfYear) + { + test(Date(1999, 1, 1), doy.day, doy.md); + test(Date(-1, 1, 1), doy.day, doy.md); + } + + foreach (doy; testDaysOfLeapYear) + { + test(Date(2000, 1, 1), doy.day, doy.md); + test(Date(-4, 1, 1), doy.day, doy.md); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.dayOfYear = 187)); + static assert(!__traits(compiles, idate.dayOfYear = 187)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF Date) is on. + +/ + @property int dayOfGregorianCal() @safe const pure nothrow + { + if (isAD) + { + if (_year == 1) + return dayOfYear; + + int years = _year - 1; + auto days = (years / 400) * daysIn400Years; + years %= 400; + + days += (years / 100) * daysIn100Years; + years %= 100; + + days += (years / 4) * daysIn4Years; + years %= 4; + + days += years * daysInYear; + + days += dayOfYear; + + return days; + } + else if (_year == 0) + return dayOfYear - daysInLeapYear; + else + { + int years = _year; + auto days = (years / 400) * daysIn400Years; + years %= 400; + + days += (years / 100) * daysIn100Years; + years %= 100; + + days += (years / 4) * daysIn4Years; + years %= 4; + + if (years < 0) + { + days -= daysInLeapYear; + ++years; + + days += years * daysInYear; + + days -= daysInYear - dayOfYear; + } + else + days -= daysInLeapYear - dayOfYear; + + return days; + } + } + + /// + @safe unittest + { + assert(Date(1, 1, 1).dayOfGregorianCal == 1); + assert(Date(1, 12, 31).dayOfGregorianCal == 365); + assert(Date(2, 1, 1).dayOfGregorianCal == 366); + + assert(Date(0, 12, 31).dayOfGregorianCal == 0); + assert(Date(0, 1, 1).dayOfGregorianCal == -365); + assert(Date(-1, 12, 31).dayOfGregorianCal == -366); + + assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); + assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + import std.range : chain; + + foreach (gd; chain(testGregDaysBC, testGregDaysAD)) + assert(gd.date.dayOfGregorianCal == gd.day); + + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.dayOfGregorianCal == 729_941); + assert(cdate.dayOfGregorianCal == 729_941); + assert(idate.dayOfGregorianCal == 729_941); + } + + /++ + The Xth day of the Gregorian Calendar that this $(LREF Date) is on. + + Params: + day = The day of the Gregorian Calendar to set this $(LREF Date) to. + +/ + @property void dayOfGregorianCal(int day) @safe pure nothrow + { + this = Date(day); + } + + /// + @safe unittest + { + auto date = Date.init; + date.dayOfGregorianCal = 1; + assert(date == Date(1, 1, 1)); + + date.dayOfGregorianCal = 365; + assert(date == Date(1, 12, 31)); + + date.dayOfGregorianCal = 366; + assert(date == Date(2, 1, 1)); + + date.dayOfGregorianCal = 0; + assert(date == Date(0, 12, 31)); + + date.dayOfGregorianCal = -365; + assert(date == Date(-0, 1, 1)); + + date.dayOfGregorianCal = -366; + assert(date == Date(-1, 12, 31)); + + date.dayOfGregorianCal = 730_120; + assert(date == Date(2000, 1, 1)); + + date.dayOfGregorianCal = 734_137; + assert(date == Date(2010, 12, 31)); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + date.dayOfGregorianCal = 187; + assert(date.dayOfGregorianCal == 187); + static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); + static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF Date) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) + +/ + @property ubyte isoWeek() @safe const pure nothrow + { + immutable weekday = dayOfWeek; + immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; + immutable week = (dayOfYear - adjustedWeekday + 10) / 7; + + try + { + if (week == 53) + { + switch (Date(_year + 1, 1, 1).dayOfWeek) + { + case DayOfWeek.mon: + case DayOfWeek.tue: + case DayOfWeek.wed: + case DayOfWeek.thu: + return 1; + case DayOfWeek.fri: + case DayOfWeek.sat: + case DayOfWeek.sun: + return 53; + default: + assert(0, "Invalid ISO Week"); + } + } + else if (week > 0) + return cast(ubyte) week; + else + return Date(_year - 1, 12, 31).isoWeek; + } + catch (Exception e) + assert(0, "Date's constructor threw."); + } + + @safe unittest + { + // Test A.D. + assert(Date(2009, 12, 28).isoWeek == 53); + assert(Date(2009, 12, 29).isoWeek == 53); + assert(Date(2009, 12, 30).isoWeek == 53); + assert(Date(2009, 12, 31).isoWeek == 53); + assert(Date(2010, 1, 1).isoWeek == 53); + assert(Date(2010, 1, 2).isoWeek == 53); + assert(Date(2010, 1, 3).isoWeek == 53); + assert(Date(2010, 1, 4).isoWeek == 1); + assert(Date(2010, 1, 5).isoWeek == 1); + assert(Date(2010, 1, 6).isoWeek == 1); + assert(Date(2010, 1, 7).isoWeek == 1); + assert(Date(2010, 1, 8).isoWeek == 1); + assert(Date(2010, 1, 9).isoWeek == 1); + assert(Date(2010, 1, 10).isoWeek == 1); + assert(Date(2010, 1, 11).isoWeek == 2); + assert(Date(2010, 12, 31).isoWeek == 52); + + assert(Date(2004, 12, 26).isoWeek == 52); + assert(Date(2004, 12, 27).isoWeek == 53); + assert(Date(2004, 12, 28).isoWeek == 53); + assert(Date(2004, 12, 29).isoWeek == 53); + assert(Date(2004, 12, 30).isoWeek == 53); + assert(Date(2004, 12, 31).isoWeek == 53); + assert(Date(2005, 1, 1).isoWeek == 53); + assert(Date(2005, 1, 2).isoWeek == 53); + + assert(Date(2005, 12, 31).isoWeek == 52); + assert(Date(2007, 1, 1).isoWeek == 1); + + assert(Date(2007, 12, 30).isoWeek == 52); + assert(Date(2007, 12, 31).isoWeek == 1); + assert(Date(2008, 1, 1).isoWeek == 1); + + assert(Date(2008, 12, 28).isoWeek == 52); + assert(Date(2008, 12, 29).isoWeek == 1); + assert(Date(2008, 12, 30).isoWeek == 1); + assert(Date(2008, 12, 31).isoWeek == 1); + assert(Date(2009, 1, 1).isoWeek == 1); + assert(Date(2009, 1, 2).isoWeek == 1); + assert(Date(2009, 1, 3).isoWeek == 1); + assert(Date(2009, 1, 4).isoWeek == 1); + + // Test B.C. + // The algorithm should work identically for both A.D. and B.C. since + // it doesn't really take the year into account, so B.C. testing + // probably isn't really needed. + assert(Date(0, 12, 31).isoWeek == 52); + assert(Date(0, 1, 4).isoWeek == 1); + assert(Date(0, 1, 1).isoWeek == 52); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.isoWeek == 27); + static assert(!__traits(compiles, cdate.isoWeek = 3)); + assert(idate.isoWeek == 27); + static assert(!__traits(compiles, idate.isoWeek = 3)); + } + + + /++ + $(LREF Date) for the last day in the month that this $(LREF Date) is in. + +/ + @property Date endOfMonth() @safe const pure nothrow + { + try + return Date(_year, _month, maxDay(_year, _month)); + catch (Exception e) + assert(0, "Date's constructor threw."); + } + + /// + @safe unittest + { + assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); + assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); + } + + @safe unittest + { + // Test A.D. + assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); + assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); + assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); + assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); + assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); + assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); + assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); + assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); + assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); + assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); + assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); + + // Test B.C. + assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); + assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); + assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); + assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); + assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); + assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); + assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); + assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); + assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); + assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); + assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); + assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); + assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); + static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); + } + + + /++ + The last day in the month that this $(LREF Date) is in. + +/ + @property ubyte daysInMonth() @safe const pure nothrow + { + return maxDay(_year, _month); + } + + /// + @safe unittest + { + assert(Date(1999, 1, 6).daysInMonth == 31); + assert(Date(1999, 2, 7).daysInMonth == 28); + assert(Date(2000, 2, 7).daysInMonth == 29); + assert(Date(2000, 6, 4).daysInMonth == 30); + } + + @safe unittest + { + // Test A.D. + assert(Date(1999, 1, 1).daysInMonth == 31); + assert(Date(1999, 2, 1).daysInMonth == 28); + assert(Date(2000, 2, 1).daysInMonth == 29); + assert(Date(1999, 3, 1).daysInMonth == 31); + assert(Date(1999, 4, 1).daysInMonth == 30); + assert(Date(1999, 5, 1).daysInMonth == 31); + assert(Date(1999, 6, 1).daysInMonth == 30); + assert(Date(1999, 7, 1).daysInMonth == 31); + assert(Date(1999, 8, 1).daysInMonth == 31); + assert(Date(1999, 9, 1).daysInMonth == 30); + assert(Date(1999, 10, 1).daysInMonth == 31); + assert(Date(1999, 11, 1).daysInMonth == 30); + assert(Date(1999, 12, 1).daysInMonth == 31); + + // Test B.C. + assert(Date(-1999, 1, 1).daysInMonth == 31); + assert(Date(-1999, 2, 1).daysInMonth == 28); + assert(Date(-2000, 2, 1).daysInMonth == 29); + assert(Date(-1999, 3, 1).daysInMonth == 31); + assert(Date(-1999, 4, 1).daysInMonth == 30); + assert(Date(-1999, 5, 1).daysInMonth == 31); + assert(Date(-1999, 6, 1).daysInMonth == 30); + assert(Date(-1999, 7, 1).daysInMonth == 31); + assert(Date(-1999, 8, 1).daysInMonth == 31); + assert(Date(-1999, 9, 1).daysInMonth == 30); + assert(Date(-1999, 10, 1).daysInMonth == 31); + assert(Date(-1999, 11, 1).daysInMonth == 30); + assert(Date(-1999, 12, 1).daysInMonth == 31); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.daysInMonth = 30)); + static assert(!__traits(compiles, idate.daysInMonth = 30)); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const pure nothrow + { + return _year > 0; + } + + /// + @safe unittest + { + assert(Date(1, 1, 1).isAD); + assert(Date(2010, 12, 31).isAD); + assert(!Date(0, 12, 31).isAD); + assert(!Date(-2010, 1, 1).isAD); + } + + @safe unittest + { + assert(Date(2010, 7, 4).isAD); + assert(Date(1, 1, 1).isAD); + assert(!Date(0, 1, 1).isAD); + assert(!Date(-1, 1, 1).isAD); + assert(!Date(-2010, 7, 4).isAD); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.isAD); + assert(idate.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this + $(LREF Date) at noon (since the Julian day changes at noon). + +/ + @property long julianDay() @safe const pure nothrow + { + return dayOfGregorianCal + 1_721_425; + } + + @safe unittest + { + assert(Date(-4713, 11, 24).julianDay == 0); + assert(Date(0, 12, 31).julianDay == 1_721_425); + assert(Date(1, 1, 1).julianDay == 1_721_426); + assert(Date(1582, 10, 15).julianDay == 2_299_161); + assert(Date(1858, 11, 17).julianDay == 2_400_001); + assert(Date(1982, 1, 4).julianDay == 2_444_974); + assert(Date(1996, 3, 31).julianDay == 2_450_174); + assert(Date(2010, 8, 24).julianDay == 2_455_433); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.julianDay == 2_451_366); + assert(idate.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for + any time on this date (since, the modified Julian day changes at + midnight). + +/ + @property long modJulianDay() @safe const pure nothrow + { + return julianDay - 2_400_001; + } + + @safe unittest + { + assert(Date(1858, 11, 17).modJulianDay == 0); + assert(Date(2010, 8, 24).modJulianDay == 55_432); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.modJulianDay == 51_365); + assert(idate.modJulianDay == 51_365); + } + + + /++ + Converts this $(LREF Date) to a string with the format YYYYMMDD. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + { + if (_year >= 0) + { + if (_year < 10_000) + return format("%04d%02d%02d", _year, _month, _day); + else + return format("+%05d%02d%02d", _year, _month, _day); + } + else if (_year > -10_000) + return format("%05d%02d%02d", _year, _month, _day); + else + return format("%06d%02d%02d", _year, _month, _day); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(Date(2010, 7, 4).toISOString() == "20100704"); + assert(Date(1998, 12, 25).toISOString() == "19981225"); + assert(Date(0, 1, 5).toISOString() == "00000105"); + assert(Date(-4, 1, 5).toISOString() == "-00040105"); + } + + @safe unittest + { + // Test A.D. + assert(Date(9, 12, 4).toISOString() == "00091204"); + assert(Date(99, 12, 4).toISOString() == "00991204"); + assert(Date(999, 12, 4).toISOString() == "09991204"); + assert(Date(9999, 7, 4).toISOString() == "99990704"); + assert(Date(10000, 10, 20).toISOString() == "+100001020"); + + // Test B.C. + assert(Date(0, 12, 4).toISOString() == "00001204"); + assert(Date(-9, 12, 4).toISOString() == "-00091204"); + assert(Date(-99, 12, 4).toISOString() == "-00991204"); + assert(Date(-999, 12, 4).toISOString() == "-09991204"); + assert(Date(-9999, 7, 4).toISOString() == "-99990704"); + assert(Date(-10000, 10, 20).toISOString() == "-100001020"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.toISOString() == "19990706"); + assert(idate.toISOString() == "19990706"); + } + + /++ + Converts this $(LREF Date) to a string with the format YYYY-MM-DD. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + { + if (_year >= 0) + { + if (_year < 10_000) + return format("%04d-%02d-%02d", _year, _month, _day); + else + return format("+%05d-%02d-%02d", _year, _month, _day); + } + else if (_year > -10_000) + return format("%05d-%02d-%02d", _year, _month, _day); + else + return format("%06d-%02d-%02d", _year, _month, _day); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); + assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); + assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); + assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); + } + + @safe unittest + { + // Test A.D. + assert(Date(9, 12, 4).toISOExtString() == "0009-12-04"); + assert(Date(99, 12, 4).toISOExtString() == "0099-12-04"); + assert(Date(999, 12, 4).toISOExtString() == "0999-12-04"); + assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04"); + assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20"); + + // Test B.C. + assert(Date(0, 12, 4).toISOExtString() == "0000-12-04"); + assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04"); + assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04"); + assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04"); + assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04"); + assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.toISOExtString() == "1999-07-06"); + assert(idate.toISOExtString() == "1999-07-06"); + } + + /++ + Converts this $(LREF Date) to a string with the format YYYY-Mon-DD. + +/ + string toSimpleString() @safe const pure nothrow + { + import std.format : format; + try + { + if (_year >= 0) + { + if (_year < 10_000) + return format("%04d-%s-%02d", _year, monthToString(_month), _day); + else + return format("+%05d-%s-%02d", _year, monthToString(_month), _day); + } + else if (_year > -10_000) + return format("%05d-%s-%02d", _year, monthToString(_month), _day); + else + return format("%06d-%s-%02d", _year, monthToString(_month), _day); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); + assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); + assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); + assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); + } + + @safe unittest + { + // Test A.D. + assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04"); + assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04"); + assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04"); + assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04"); + assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20"); + + // Test B.C. + assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04"); + assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04"); + assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04"); + assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04"); + assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04"); + assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.toSimpleString() == "1999-Jul-06"); + assert(idate.toSimpleString() == "1999-Jul-06"); + } + + + /++ + Converts this $(LREF Date) to a string. + +/ + string toString() @safe const pure nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.toString()); + assert(cdate.toString()); + assert(idate.toString()); + } + + + /++ + Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace + is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO format or if the resulting $(LREF Date) would not be + valid. + +/ + static Date fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all, startsWith; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 8, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto day = dstr[$-2 .. $]; + auto month = dstr[$-4 .. $-2]; + auto year = dstr[0 .. $-4]; + + enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + if (year.length > 4) + { + enforce(year.startsWith('-', '+'), + new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(year[1..$]), + new DateTimeException(format("Invalid ISO String: %s", isoString))); + } + else + enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + } + + /// + @safe unittest + { + assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); + assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); + assert(Date.fromISOString("00000105") == Date(0, 1, 5)); + assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); + assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); + } + + @safe unittest + { + assertThrown!DateTimeException(Date.fromISOString("")); + assertThrown!DateTimeException(Date.fromISOString("990704")); + assertThrown!DateTimeException(Date.fromISOString("0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070")); + assertThrown!DateTimeException(Date.fromISOString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOString("120100704")); + assertThrown!DateTimeException(Date.fromISOString("-0100704")); + assertThrown!DateTimeException(Date.fromISOString("+0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070a")); + assertThrown!DateTimeException(Date.fromISOString("20100a04")); + assertThrown!DateTimeException(Date.fromISOString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); + + assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); + assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); + assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); + assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6)); + assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6)); + assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6)); + } + + + /++ + Creates a $(LREF Date) from a string with the format YYYY-MM-DD. + Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for + dates. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO Extended format or if the resulting $(LREF Date) + would not be valid. + +/ + static Date fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : all, startsWith; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto day = dstr[$-2 .. $]; + auto month = dstr[$-5 .. $-3]; + auto year = dstr[0 .. $-6]; + + enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(day), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(month), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + if (year.length > 4) + { + enforce(year.startsWith('-', '+'), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(year[1..$]), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + } + else + enforce(all!isDigit(year), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + } + + /// + @safe unittest + { + assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); + assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); + assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); + assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); + assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); + } + + @safe unittest + { + assertThrown!DateTimeException(Date.fromISOExtString("")); + assertThrown!DateTimeException(Date.fromISOExtString("990704")); + assertThrown!DateTimeException(Date.fromISOExtString("0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOExtString("120100704")); + assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); + assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOExtString("20100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); + + assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); + assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6)); + assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6)); + } + + + /++ + Creates a $(LREF Date) from a string with the format YYYY-Mon-DD. + Whitespace is stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString + formats dates. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the correct format or if the resulting $(LREF Date) would not + be valid. + +/ + static Date fromSimpleString(S)(in S simpleString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : all, startsWith; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString))); + + auto day = dstr[$-2 .. $]; + auto month = monthFromString(to!string(dstr[$-6 .. $-3])); + auto year = dstr[0 .. $-7]; + + enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString))); + + if (year.length > 4) + { + enforce(year.startsWith('-', '+'), + new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(all!isDigit(year[1..$]), + new DateTimeException(format("Invalid string format: %s", simpleString))); + } + else + enforce(all!isDigit(year), + new DateTimeException(format("Invalid string format: %s", simpleString))); + + return Date(to!short(year), month, to!ubyte(day)); + } + + /// + @safe unittest + { + assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); + assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); + assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); + assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); + assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); + } + + @safe unittest + { + assertThrown!DateTimeException(Date.fromSimpleString("")); + assertThrown!DateTimeException(Date.fromSimpleString("990704")); + assertThrown!DateTimeException(Date.fromSimpleString("0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); + assertThrown!DateTimeException(Date.fromSimpleString("120100704")); + assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); + assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromSimpleString("20100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); + + assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); + assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6)); + assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6)); + } + + + /++ + Returns the $(LREF Date) farthest in the past which is representable by + $(LREF Date). + +/ + @property static Date min() @safe pure nothrow + { + auto date = Date.init; + date._year = short.min; + date._month = Month.jan; + date._day = 1; + + return date; + } + + @safe unittest + { + assert(Date.min.year < 0); + assert(Date.min < Date.max); + } + + + /++ + Returns the $(LREF Date) farthest in the future which is representable + by $(LREF Date). + +/ + @property static Date max() @safe pure nothrow + { + auto date = Date.init; + date._year = short.max; + date._month = Month.dec; + date._day = 31; + + return date; + } + + @safe unittest + { + assert(Date.max.year > 0); + assert(Date.max > Date.min); + } + + +private: + + /+ + Whether the given values form a valid date. + + Params: + year = The year to test. + month = The month of the Gregorian Calendar to test. + day = The day of the month to test. + +/ + static bool _valid(int year, int month, int day) @safe pure nothrow + { + if (!valid!"months"(month)) + return false; + return valid!"days"(year, month, day); + } + + +package: + + /+ + Adds the given number of days to this $(LREF Date). A negative number + will subtract. + + The month will be adjusted along with the day if the number of days + added (or subtracted) would overflow (or underflow) the current month. + The year will be adjusted along with the month if the increase (or + decrease) to the month would cause it to overflow (or underflow) the + current year. + + $(D _addDays(numDays)) is effectively equivalent to + $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). + + Params: + days = The number of days to add to this Date. + +/ + ref Date _addDays(long days) return @safe pure nothrow + { + dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); + return this; + } + + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 2, 28); + date._addDays(1); + assert(date == Date(1999, 3, 1)); + date._addDays(-1); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 28); + date._addDays(1); + assert(date == Date(2000, 2, 29)); + date._addDays(1); + assert(date == Date(2000, 3, 1)); + date._addDays(-1); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 6, 30); + date._addDays(1); + assert(date == Date(1999, 7, 1)); + date._addDays(-1); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 7, 31); + date._addDays(1); + assert(date == Date(1999, 8, 1)); + date._addDays(-1); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 1, 1); + date._addDays(-1); + assert(date == Date(1998, 12, 31)); + date._addDays(1); + assert(date == Date(1999, 1, 1)); + } + + { + auto date = Date(1999, 7, 6); + date._addDays(9); + assert(date == Date(1999, 7, 15)); + date._addDays(-11); + assert(date == Date(1999, 7, 4)); + date._addDays(30); + assert(date == Date(1999, 8, 3)); + date._addDays(-3); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 7, 6); + date._addDays(365); + assert(date == Date(2000, 7, 5)); + date._addDays(-365); + assert(date == Date(1999, 7, 6)); + date._addDays(366); + assert(date == Date(2000, 7, 6)); + date._addDays(730); + assert(date == Date(2002, 7, 6)); + date._addDays(-1096); + assert(date == Date(1999, 7, 6)); + } + + // Test B.C. + { + auto date = Date(-1999, 2, 28); + date._addDays(1); + assert(date == Date(-1999, 3, 1)); + date._addDays(-1); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 28); + date._addDays(1); + assert(date == Date(-2000, 2, 29)); + date._addDays(1); + assert(date == Date(-2000, 3, 1)); + date._addDays(-1); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 6, 30); + date._addDays(1); + assert(date == Date(-1999, 7, 1)); + date._addDays(-1); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 7, 31); + date._addDays(1); + assert(date == Date(-1999, 8, 1)); + date._addDays(-1); + assert(date == Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 1, 1); + date._addDays(-1); + assert(date == Date(-2000, 12, 31)); + date._addDays(1); + assert(date == Date(-1999, 1, 1)); + } + + { + auto date = Date(-1999, 7, 6); + date._addDays(9); + assert(date == Date(-1999, 7, 15)); + date._addDays(-11); + assert(date == Date(-1999, 7, 4)); + date._addDays(30); + assert(date == Date(-1999, 8, 3)); + date._addDays(-3); + } + + { + auto date = Date(-1999, 7, 6); + date._addDays(365); + assert(date == Date(-1998, 7, 6)); + date._addDays(-365); + assert(date == Date(-1999, 7, 6)); + date._addDays(366); + assert(date == Date(-1998, 7, 7)); + date._addDays(730); + assert(date == Date(-1996, 7, 6)); + date._addDays(-1096); + assert(date == Date(-1999, 7, 6)); + } + + // Test Both + { + auto date = Date(1, 7, 6); + date._addDays(-365); + assert(date == Date(0, 7, 6)); + date._addDays(365); + assert(date == Date(1, 7, 6)); + date._addDays(-731); + assert(date == Date(-1, 7, 6)); + date._addDays(730); + assert(date == Date(1, 7, 5)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate._addDays(12))); + static assert(!__traits(compiles, idate._addDays(12))); + } + + + @safe pure invariant() + { + import std.format : format; + assert(valid!"months"(_month), + format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); + assert(valid!"days"(_year, _month, _day), + format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); + } + + short _year = 1; + Month _month = Month.jan; + ubyte _day = 1; +} + + +/++ + Represents a time of day with hours, minutes, and seconds. It uses 24 hour + time. ++/ +struct TimeOfDay +{ +public: + + /++ + Params: + hour = Hour of the day [0 - 24$(RPAREN). + minute = Minute of the hour [0 - 60$(RPAREN). + second = Second of the minute [0 - 60$(RPAREN). + + Throws: + $(REF DateTimeException,std,datetime,date) if the resulting + $(LREF TimeOfDay) would be not be valid. + +/ + this(int hour, int minute, int second = 0) @safe pure + { + enforceValid!"hours"(hour); + enforceValid!"minutes"(minute); + enforceValid!"seconds"(second); + + _hour = cast(ubyte) hour; + _minute = cast(ubyte) minute; + _second = cast(ubyte) second; + } + + @safe unittest + { + assert(TimeOfDay(0, 0) == TimeOfDay.init); + + { + auto tod = TimeOfDay(0, 0); + assert(tod._hour == 0); + assert(tod._minute == 0); + assert(tod._second == 0); + } + + { + auto tod = TimeOfDay(12, 30, 33); + assert(tod._hour == 12); + assert(tod._minute == 30); + assert(tod._second == 33); + } + + { + auto tod = TimeOfDay(23, 59, 59); + assert(tod._hour == 23); + assert(tod._minute == 59); + assert(tod._second == 59); + } + + assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); + } + + + /++ + Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in TimeOfDay rhs) @safe const pure nothrow + { + if (_hour < rhs._hour) + return -1; + if (_hour > rhs._hour) + return 1; + + if (_minute < rhs._minute) + return -1; + if (_minute > rhs._minute) + return 1; + + if (_second < rhs._second) + return -1; + if (_second > rhs._second) + return 1; + + return 0; + } + + @safe unittest + { + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); + + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); + assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); + assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); + + assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); + + assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); + + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); + + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(ctod.opCmp(itod) == 0); + assert(itod.opCmp(ctod) == 0); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const pure nothrow + { + return _hour; + } + + @safe unittest + { + assert(TimeOfDay.init.hour == 0); + assert(TimeOfDay(12, 0, 0).hour == 12); + + const ctod = TimeOfDay(12, 0, 0); + immutable itod = TimeOfDay(12, 0, 0); + assert(ctod.hour == 12); + assert(itod.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given hour would + result in an invalid $(LREF TimeOfDay). + +/ + @property void hour(int hour) @safe pure + { + enforceValid!"hours"(hour); + _hour = cast(ubyte) hour; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.hour = 12; + assert(tod == TimeOfDay(12, 0, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.hour = 12)); + static assert(!__traits(compiles, itod.hour = 12)); + } + + + /++ + Minutes past the hour. + +/ + @property ubyte minute() @safe const pure nothrow + { + return _minute; + } + + @safe unittest + { + assert(TimeOfDay.init.minute == 0); + assert(TimeOfDay(0, 30, 0).minute == 30); + + const ctod = TimeOfDay(0, 30, 0); + immutable itod = TimeOfDay(0, 30, 0); + assert(ctod.minute == 30); + assert(itod.minute == 30); + } + + + /++ + Minutes past the hour. + + Params: + minute = The minute to set this $(LREF TimeOfDay)'s minute to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given minute + would result in an invalid $(LREF TimeOfDay). + +/ + @property void minute(int minute) @safe pure + { + enforceValid!"minutes"(minute); + _minute = cast(ubyte) minute; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.minute = 30; + assert(tod == TimeOfDay(0, 30, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.minute = 30)); + static assert(!__traits(compiles, itod.minute = 30)); + } + + + /++ + Seconds past the minute. + +/ + @property ubyte second() @safe const pure nothrow + { + return _second; + } + + @safe unittest + { + assert(TimeOfDay.init.second == 0); + assert(TimeOfDay(0, 0, 33).second == 33); + + const ctod = TimeOfDay(0, 0, 33); + immutable itod = TimeOfDay(0, 0, 33); + assert(ctod.second == 33); + assert(itod.second == 33); + } + + + /++ + Seconds past the minute. + + Params: + second = The second to set this $(LREF TimeOfDay)'s second to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given second + would result in an invalid $(LREF TimeOfDay). + +/ + @property void second(int second) @safe pure + { + enforceValid!"seconds"(second); + _second = cast(ubyte) second; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.second = 33; + assert(tod == TimeOfDay(0, 0, 33)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.second = 33)); + static assert(!__traits(compiles, itod.second = 33)); + } + + + /++ + Adds the given number of units to this $(LREF TimeOfDay). A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF TimeOfDay) + one hours's worth of minutes gets the exact same + $(LREF TimeOfDay). + + Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds"). + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this + $(LREF TimeOfDay). + +/ + ref TimeOfDay roll(string units)(long value) @safe pure nothrow + if (units == "hours") + { + return this += dur!"hours"(value); + } + + /// + @safe unittest + { + auto tod1 = TimeOfDay(7, 12, 0); + tod1.roll!"hours"(1); + assert(tod1 == TimeOfDay(8, 12, 0)); + + auto tod2 = TimeOfDay(7, 12, 0); + tod2.roll!"hours"(-1); + assert(tod2 == TimeOfDay(6, 12, 0)); + + auto tod3 = TimeOfDay(23, 59, 0); + tod3.roll!"minutes"(1); + assert(tod3 == TimeOfDay(23, 0, 0)); + + auto tod4 = TimeOfDay(0, 0, 0); + tod4.roll!"minutes"(-1); + assert(tod4 == TimeOfDay(0, 59, 0)); + + auto tod5 = TimeOfDay(23, 59, 59); + tod5.roll!"seconds"(1); + assert(tod5 == TimeOfDay(23, 59, 0)); + + auto tod6 = TimeOfDay(0, 0, 0); + tod6.roll!"seconds"(-1); + assert(tod6 == TimeOfDay(0, 0, 59)); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"hours"(22).roll!"hours"(-7); + assert(tod == TimeOfDay(3, 27, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"hours"(53))); + static assert(!__traits(compiles, itod.roll!"hours"(53))); + } + + + // Shares documentation with "hours" version. + ref TimeOfDay roll(string units)(long value) @safe pure nothrow + if (units == "minutes" || units == "seconds") + { + import std.format : format; + + enum memberVarStr = units[0 .. $ - 1]; + value %= 60; + mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); + + if (value < 0) + { + if (newVal < 0) + newVal += 60; + } + else if (newVal >= 60) + newVal -= 60; + + mixin(format("_%s = cast(ubyte) newVal;", memberVarStr)); + return this; + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); + + testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); + + testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); + + testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); + testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); + testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); + + testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); + testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); + testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); + + testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); + testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); + testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"minutes"(97).roll!"minutes"(-102); + assert(tod == TimeOfDay(12, 22, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"minutes"(7))); + static assert(!__traits(compiles, itod.roll!"minutes"(7))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"seconds"(105).roll!"seconds"(-77); + assert(tod == TimeOfDay(12, 27, 30)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"seconds"(7))); + static assert(!__traits(compiles, itod.roll!"seconds"(7))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF TimeOfDay). + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF TimeOfDay). + +/ + TimeOfDay opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + TimeOfDay retval = this; + immutable seconds = duration.total!"seconds"; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + /// + @safe unittest + { + import core.time : hours, minutes, seconds; + + assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); + assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); + assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); + assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0)); + + assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11)); + assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); + assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); + assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); + + assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); + + assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + auto duration = dur!"hours"(11); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod + duration == TimeOfDay(23, 30, 33)); + assert(ctod + duration == TimeOfDay(23, 30, 33)); + assert(itod + duration == TimeOfDay(23, 30, 33)); + + assert(tod - duration == TimeOfDay(1, 30, 33)); + assert(ctod - duration == TimeOfDay(1, 30, 33)); + assert(itod - duration == TimeOfDay(1, 30, 33)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + TimeOfDay opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + TimeOfDay retval = this; + immutable seconds = td.seconds; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto tod = TimeOfDay(12, 30, 33); + + assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + + assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF TimeOfDay), as well as assigning the result to this + $(LREF TimeOfDay). + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF TimeOfDay). + +/ + ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable seconds = duration.total!"seconds"; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + @safe unittest + { + auto duration = dur!"hours"(12); + + assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + auto tod = TimeOfDay(19, 17, 22); + (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); + assert(tod == TimeOfDay(17, 15, 59)); + + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(!__traits(compiles, ctod += duration)); + static assert(!__traits(compiles, itod += duration)); + static assert(!__traits(compiles, ctod -= duration)); + static assert(!__traits(compiles, itod -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable seconds = td.seconds; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto tod = TimeOfDay(12, 30, 33); + tod += TickDuration.from!"usecs"(7_000_000); + assert(tod == TimeOfDay(12, 30, 40)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod += TickDuration.from!"usecs"(-7_000_000); + assert(tod == TimeOfDay(12, 30, 26)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod -= TickDuration.from!"usecs"(-7_000_000); + assert(tod == TimeOfDay(12, 30, 40)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod -= TickDuration.from!"usecs"(7_000_000); + assert(tod == TimeOfDay(12, 30, 26)); + } + } + } + + + /++ + Gives the difference between two $(LREF TimeOfDay)s. + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) + ) + + Params: + rhs = The $(LREF TimeOfDay) to subtract from this one. + +/ + Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow + if (op == "-") + { + immutable lhsSec = _hour * 3600 + _minute * 60 + _second; + immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; + + return dur!"seconds"(lhsSec - rhsSec); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); + assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); + assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); + assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod - tod == Duration.zero); + assert(ctod - tod == Duration.zero); + assert(itod - tod == Duration.zero); + + assert(tod - ctod == Duration.zero); + assert(ctod - ctod == Duration.zero); + assert(itod - ctod == Duration.zero); + + assert(tod - itod == Duration.zero); + assert(ctod - itod == Duration.zero); + assert(itod - itod == Duration.zero); + } + + + /++ + Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + return format("%02d%02d%02d", _hour, _minute, _second); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); + assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toISOString() == "123033"); + assert(ctod.toISOString() == "123033"); + assert(itod.toISOString() == "123033"); + } + + + /++ + Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + return format("%02d:%02d:%02d", _hour, _minute, _second); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); + assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toISOExtString() == "12:30:33"); + assert(ctod.toISOExtString() == "12:30:33"); + assert(itod.toISOExtString() == "12:30:33"); + } + + + /++ + Converts this TimeOfDay to a string. + +/ + string toString() @safe const pure nothrow + { + return toISOExtString(); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toString()); + assert(ctod.toString()); + assert(itod.toString()); + } + + + /++ + Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for times. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO format or if the resulting $(LREF TimeOfDay) would + not be valid. + +/ + static TimeOfDay fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[2 .. 4]; + auto seconds = dstr[4 .. $]; + + enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + /// + @safe unittest + { + assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); + } + + @safe unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); + + assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); + } + + + /++ + Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. + Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for + times. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO Extended format or if the resulting $(LREF TimeOfDay) + would not be valid. + +/ + static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[3 .. 5]; + auto seconds = dstr[6 .. $]; + + enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(hours), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(minutes), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(seconds), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + /// + @safe unittest + { + assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); + } + + @safe unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); + + assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); + } + + + /++ + Returns midnight. + +/ + @property static TimeOfDay min() @safe pure nothrow + { + return TimeOfDay.init; + } + + @safe unittest + { + assert(TimeOfDay.min.hour == 0); + assert(TimeOfDay.min.minute == 0); + assert(TimeOfDay.min.second == 0); + assert(TimeOfDay.min < TimeOfDay.max); + } + + + /++ + Returns one second short of midnight. + +/ + @property static TimeOfDay max() @safe pure nothrow + { + auto tod = TimeOfDay.init; + tod._hour = maxHour; + tod._minute = maxMinute; + tod._second = maxSecond; + + return tod; + } + + @safe unittest + { + assert(TimeOfDay.max.hour == 23); + assert(TimeOfDay.max.minute == 59); + assert(TimeOfDay.max.second == 59); + assert(TimeOfDay.max > TimeOfDay.min); + } + + +private: + + /+ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. If the + number of minutes overflows (or underflows), then the minutes will wrap. + If the number of minutes overflows(or underflows), then the hour will + wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). + + Params: + seconds = The number of seconds to add to this TimeOfDay. + +/ + ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_hour); + hnsecs += convert!("minutes", "hnsecs")(_minute); + hnsecs += convert!("seconds", "hnsecs")(_second); + + hnsecs %= convert!("days", "hnsecs")(1); + + if (hnsecs < 0) + hnsecs += convert!("days", "hnsecs")(1); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _hour = cast(ubyte) newHours; + _minute = cast(ubyte) newMinutes; + _second = cast(ubyte) newSeconds; + + return this; + } + + @safe unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); + + testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); + testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod._addSeconds(7))); + static assert(!__traits(compiles, itod._addSeconds(7))); + } + + + /+ + Whether the given values form a valid $(LREF TimeOfDay). + +/ + static bool _valid(int hour, int minute, int second) @safe pure nothrow + { + return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); + } + + + @safe pure invariant() + { + import std.format : format; + assert(_valid(_hour, _minute, _second), + format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); + } + + +package: + + ubyte _hour; + ubyte _minute; + ubyte _second; + + enum ubyte maxHour = 24 - 1; + enum ubyte maxMinute = 60 - 1; + enum ubyte maxSecond = 60 - 1; +} + + +/++ + Returns whether the given value is valid for the given unit type when in a + time point. Naturally, a duration is not held to a particular range, but + the values in a time point are (e.g. a month must be in the range of + 1 - 12 inclusive). + + Params: + units = The units of time to validate. + value = The number to validate. + +/ +bool valid(string units)(int value) @safe pure nothrow +if (units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + static if (units == "months") + return value >= Month.jan && value <= Month.dec; + else static if (units == "hours") + return value >= 0 && value <= 23; + else static if (units == "minutes") + return value >= 0 && value <= 59; + else static if (units == "seconds") + return value >= 0 && value <= 59; +} + +/// +@safe unittest +{ + assert(valid!"hours"(12)); + assert(!valid!"hours"(32)); + assert(valid!"months"(12)); + assert(!valid!"months"(13)); +} + +/++ + Returns whether the given day is valid for the given year and month. + + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + +/ +bool valid(string units)(int year, int month, int day) @safe pure nothrow +if (units == "days") +{ + return day > 0 && day <= maxDay(year, month); +} + + +/++ + Params: + units = The units of time to validate. + value = The number to validate. + file = The file that the $(LREF DateTimeException) will list if thrown. + line = The line number that the $(LREF DateTimeException) will list if + thrown. + + Throws: + $(LREF DateTimeException) if $(D valid!units(value)) is false. + +/ +void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure +if (units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + import std.format : format; + + static if (units == "months") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); + } + else static if (units == "hours") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); + } + else static if (units == "minutes") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); + } + else static if (units == "seconds") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); + } +} + + +/++ + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + file = The file that the $(LREF DateTimeException) will list if thrown. + line = The line number that the $(LREF DateTimeException) will list if + thrown. + + Throws: + $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. + +/ +void enforceValid(string units) + (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure +if (units == "days") +{ + import std.format : format; + if (!valid!"days"(year, month, day)) + throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); +} + + +/++ + Returns the number of days from the current day of the week to the given + day of the week. If they are the same, then the result is 0. + + Params: + currDoW = The current day of the week. + dow = The day of the week to get the number of days to. + +/ +int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow +{ + if (currDoW == dow) + return 0; + if (currDoW < dow) + return dow - currDoW; + return DayOfWeek.sat - currDoW + dow + 1; +} + +@safe unittest +{ + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); + + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); + + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); + + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); + + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); + + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); + + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); +} + + +/++ + Returns the number of months from the current months of the year to the + given month of the year. If they are the same, then the result is 0. + + Params: + currMonth = The current month of the year. + month = The month of the year to get the number of months to. + +/ +int monthsToMonth(int currMonth, int month) @safe pure +{ + enforceValid!"months"(currMonth); + enforceValid!"months"(month); + + if (currMonth == month) + return 0; + if (currMonth < month) + return month - currMonth; + return Month.dec - currMonth + month; +} + +@safe unittest +{ + assert(monthsToMonth(Month.jan, Month.jan) == 0); + assert(monthsToMonth(Month.jan, Month.feb) == 1); + assert(monthsToMonth(Month.jan, Month.mar) == 2); + assert(monthsToMonth(Month.jan, Month.apr) == 3); + assert(monthsToMonth(Month.jan, Month.may) == 4); + assert(monthsToMonth(Month.jan, Month.jun) == 5); + assert(monthsToMonth(Month.jan, Month.jul) == 6); + assert(monthsToMonth(Month.jan, Month.aug) == 7); + assert(monthsToMonth(Month.jan, Month.sep) == 8); + assert(monthsToMonth(Month.jan, Month.oct) == 9); + assert(monthsToMonth(Month.jan, Month.nov) == 10); + assert(monthsToMonth(Month.jan, Month.dec) == 11); + + assert(monthsToMonth(Month.may, Month.jan) == 8); + assert(monthsToMonth(Month.may, Month.feb) == 9); + assert(monthsToMonth(Month.may, Month.mar) == 10); + assert(monthsToMonth(Month.may, Month.apr) == 11); + assert(monthsToMonth(Month.may, Month.may) == 0); + assert(monthsToMonth(Month.may, Month.jun) == 1); + assert(monthsToMonth(Month.may, Month.jul) == 2); + assert(monthsToMonth(Month.may, Month.aug) == 3); + assert(monthsToMonth(Month.may, Month.sep) == 4); + assert(monthsToMonth(Month.may, Month.oct) == 5); + assert(monthsToMonth(Month.may, Month.nov) == 6); + assert(monthsToMonth(Month.may, Month.dec) == 7); + + assert(monthsToMonth(Month.oct, Month.jan) == 3); + assert(monthsToMonth(Month.oct, Month.feb) == 4); + assert(monthsToMonth(Month.oct, Month.mar) == 5); + assert(monthsToMonth(Month.oct, Month.apr) == 6); + assert(monthsToMonth(Month.oct, Month.may) == 7); + assert(monthsToMonth(Month.oct, Month.jun) == 8); + assert(monthsToMonth(Month.oct, Month.jul) == 9); + assert(monthsToMonth(Month.oct, Month.aug) == 10); + assert(monthsToMonth(Month.oct, Month.sep) == 11); + assert(monthsToMonth(Month.oct, Month.oct) == 0); + assert(monthsToMonth(Month.oct, Month.nov) == 1); + assert(monthsToMonth(Month.oct, Month.dec) == 2); + + assert(monthsToMonth(Month.dec, Month.jan) == 1); + assert(monthsToMonth(Month.dec, Month.feb) == 2); + assert(monthsToMonth(Month.dec, Month.mar) == 3); + assert(monthsToMonth(Month.dec, Month.apr) == 4); + assert(monthsToMonth(Month.dec, Month.may) == 5); + assert(monthsToMonth(Month.dec, Month.jun) == 6); + assert(monthsToMonth(Month.dec, Month.jul) == 7); + assert(monthsToMonth(Month.dec, Month.aug) == 8); + assert(monthsToMonth(Month.dec, Month.sep) == 9); + assert(monthsToMonth(Month.dec, Month.oct) == 10); + assert(monthsToMonth(Month.dec, Month.nov) == 11); + assert(monthsToMonth(Month.dec, Month.dec) == 0); +} + + +/++ + Whether the given Gregorian Year is a leap year. + + Params: + year = The year to to be tested. + +/ +bool yearIsLeapYear(int year) @safe pure nothrow +{ + if (year % 400 == 0) + return true; + if (year % 100 == 0) + return false; + return year % 4 == 0; +} + +@safe unittest +{ + import std.format : format; + foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, + 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) + { + assert(!yearIsLeapYear(year), format("year: %s.", year)); + assert(!yearIsLeapYear(-year), format("year: %s.", year)); + } + + foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) + { + assert(yearIsLeapYear(year), format("year: %s.", year)); + assert(yearIsLeapYear(-year), format("year: %s.", year)); + } +} + + +/++ + Whether the given type defines all of the necessary functions for it to + function as a time point. + + 1. $(D T) must define a static property named $(D min) which is the smallest + value of $(D T) as $(Unqual!T). + + 2. $(D T) must define a static property named $(D max) which is the largest + value of $(D T) as $(Unqual!T). + + 3. $(D T) must define an $(D opBinary) for addition and subtraction that + accepts $(REF Duration, core,time) and returns $(D Unqual!T). + + 4. $(D T) must define an $(D opOpAssign) for addition and subtraction that + accepts $(REF Duration, core,time) and returns $(D ref Unqual!T). + + 5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T) + and returns returns $(REF Duration, core,time). + +/ +template isTimePoint(T) +{ + import core.time : Duration; + import std.traits : FunctionAttribute, functionAttributes, Unqual; + + enum isTimePoint = hasMin && + hasMax && + hasOverloadedOpBinaryWithDuration && + hasOverloadedOpAssignWithDuration && + hasOverloadedOpBinaryWithSelf && + !is(U == Duration); + +private: + + alias U = Unqual!T; + + enum hasMin = __traits(hasMember, T, "min") && + is(typeof(T.min) == U) && + is(typeof({static assert(__traits(isStaticFunction, T.min));})); + + enum hasMax = __traits(hasMember, T, "max") && + is(typeof(T.max) == U) && + is(typeof({static assert(__traits(isStaticFunction, T.max));})); + + enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) && + is(typeof(T.init - Duration.init) == U); + + enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) && + is(typeof(U.init -= Duration.init) == U) && + is(typeof( + { + // Until the overload with TickDuration is removed, this is ambiguous. + //alias add = U.opOpAssign!"+"; + //alias sub = U.opOpAssign!"-"; + U u; + auto ref add() { return u += Duration.init; } + auto ref sub() { return u -= Duration.init; } + alias FA = FunctionAttribute; + static assert((functionAttributes!add & FA.ref_) != 0); + static assert((functionAttributes!sub & FA.ref_) != 0); + })); + + enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration); +} + +/// +@safe unittest +{ + import core.time : Duration; + import std.datetime.interval : Interval; + import std.datetime.systime : SysTime; + + static assert(isTimePoint!Date); + static assert(isTimePoint!DateTime); + static assert(isTimePoint!SysTime); + static assert(isTimePoint!TimeOfDay); + + static assert(!isTimePoint!int); + static assert(!isTimePoint!Duration); + static assert(!isTimePoint!(Interval!SysTime)); +} + +@safe unittest +{ + import core.time; + import std.datetime.interval; + import std.datetime.systime; + import std.meta : AliasSeq; + + foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) + { + static assert(isTimePoint!(const TP), TP.stringof); + static assert(isTimePoint!(immutable TP), TP.stringof); + } + + foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date)) + static assert(!isTimePoint!T, T.stringof); +} + + +/++ + Whether all of the given strings are valid units of time. + + $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime + can handle precision greater than hnsecs, and the few functions in core.time + which deal with "nsecs" deal with it explicitly. + +/ +bool validTimeUnits(string[] units...) @safe pure nothrow +{ + import std.algorithm.searching : canFind; + foreach (str; units) + { + if (!canFind(timeStrings[], str)) + return false; + } + return true; +} + + +/++ + Compares two time unit strings. $(D "years") are the largest units and + $(D "hnsecs") are the smallest. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + + Throws: + $(LREF DateTimeException) if either of the given strings is not a valid + time unit string. + +/ +int cmpTimeUnits(string lhs, string rhs) @safe pure +{ + import std.algorithm.searching : countUntil; + import std.exception : enforce; + import std.format : format; + + auto tstrings = timeStrings; + immutable indexOfLHS = countUntil(tstrings, lhs); + immutable indexOfRHS = countUntil(tstrings, rhs); + + enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); + enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); + + if (indexOfLHS < indexOfRHS) + return -1; + if (indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +@safe unittest +{ + foreach (i, outerUnits; timeStrings) + { + assert(cmpTimeUnits(outerUnits, outerUnits) == 0); + + // For some reason, $ won't compile. + foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length]) + assert(cmpTimeUnits(outerUnits, innerUnits) == -1); + } + + foreach (i, outerUnits; timeStrings) + { + foreach (innerUnits; timeStrings[0 .. i]) + assert(cmpTimeUnits(outerUnits, innerUnits) == 1); + } +} + + +/++ + Compares two time unit strings at compile time. $(D "years") are the largest + units and $(D "hnsecs") are the smallest. + + This template is used instead of $(D cmpTimeUnits) because exceptions + can't be thrown at compile time and $(D cmpTimeUnits) must enforce that + the strings it's given are valid time unit strings. This template uses a + template constraint instead. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ +template CmpTimeUnits(string lhs, string rhs) +if (validTimeUnits(lhs, rhs)) +{ + enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); +} + + +// Helper function for CmpTimeUnits. +private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow +{ + import std.algorithm.searching : countUntil; + auto tstrings = timeStrings; + immutable indexOfLHS = countUntil(tstrings, lhs); + immutable indexOfRHS = countUntil(tstrings, rhs); + + if (indexOfLHS < indexOfRHS) + return -1; + if (indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +@safe unittest +{ + import std.format : format; + import std.meta : AliasSeq; + + static string genTest(size_t index) + { + auto currUnits = timeStrings[index]; + auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); + + foreach (units; timeStrings[index + 1 .. $]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); + + foreach (units; timeStrings[0 .. index]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); + + return test; + } + + static assert(timeStrings.length == 10); + foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) + mixin(genTest(n)); +} + + +package: + + +/+ + Array of the short (three letter) names of each month. + +/ +immutable string[12] _monthNames = ["Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"]; + +/+ + The maximum valid Day in the given month in the given year. + + Params: + year = The year to get the day for. + month = The month of the Gregorian Calendar to get the day for. + +/ +ubyte maxDay(int year, int month) @safe pure nothrow +in +{ + assert(valid!"months"(month)); +} +body +{ + switch (month) + { + case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: + return 31; + case Month.feb: + return yearIsLeapYear(year) ? 29 : 28; + case Month.apr, Month.jun, Month.sep, Month.nov: + return 30; + default: + assert(0, "Invalid month."); + } +} + +@safe unittest +{ + // Test A.D. + assert(maxDay(1999, 1) == 31); + assert(maxDay(1999, 2) == 28); + assert(maxDay(1999, 3) == 31); + assert(maxDay(1999, 4) == 30); + assert(maxDay(1999, 5) == 31); + assert(maxDay(1999, 6) == 30); + assert(maxDay(1999, 7) == 31); + assert(maxDay(1999, 8) == 31); + assert(maxDay(1999, 9) == 30); + assert(maxDay(1999, 10) == 31); + assert(maxDay(1999, 11) == 30); + assert(maxDay(1999, 12) == 31); + + assert(maxDay(2000, 1) == 31); + assert(maxDay(2000, 2) == 29); + assert(maxDay(2000, 3) == 31); + assert(maxDay(2000, 4) == 30); + assert(maxDay(2000, 5) == 31); + assert(maxDay(2000, 6) == 30); + assert(maxDay(2000, 7) == 31); + assert(maxDay(2000, 8) == 31); + assert(maxDay(2000, 9) == 30); + assert(maxDay(2000, 10) == 31); + assert(maxDay(2000, 11) == 30); + assert(maxDay(2000, 12) == 31); + + // Test B.C. + assert(maxDay(-1999, 1) == 31); + assert(maxDay(-1999, 2) == 28); + assert(maxDay(-1999, 3) == 31); + assert(maxDay(-1999, 4) == 30); + assert(maxDay(-1999, 5) == 31); + assert(maxDay(-1999, 6) == 30); + assert(maxDay(-1999, 7) == 31); + assert(maxDay(-1999, 8) == 31); + assert(maxDay(-1999, 9) == 30); + assert(maxDay(-1999, 10) == 31); + assert(maxDay(-1999, 11) == 30); + assert(maxDay(-1999, 12) == 31); + + assert(maxDay(-2000, 1) == 31); + assert(maxDay(-2000, 2) == 29); + assert(maxDay(-2000, 3) == 31); + assert(maxDay(-2000, 4) == 30); + assert(maxDay(-2000, 5) == 31); + assert(maxDay(-2000, 6) == 30); + assert(maxDay(-2000, 7) == 31); + assert(maxDay(-2000, 8) == 31); + assert(maxDay(-2000, 9) == 30); + assert(maxDay(-2000, 10) == 31); + assert(maxDay(-2000, 11) == 30); + assert(maxDay(-2000, 12) == 31); +} + +/+ + Splits out a particular unit from hnsecs and gives the value for that + unit and the remaining hnsecs. It really shouldn't be used unless unless + all units larger than the given units have already been split out. + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left + after splitting out the given units. + + Returns: + The number of the given units from converting hnsecs to those units. + +/ +long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) +{ + import core.time : convert; + immutable value = convert!("hnsecs", units)(hnsecs); + hnsecs -= convert!(units, "hnsecs")(value); + return value; +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 3000000007); + + immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + assert(minutes == 5); + assert(hnsecs == 7); +} + + +/+ + Returns the day of the week for the given day of the Gregorian Calendar. + + Params: + day = The day of the Gregorian Calendar for which to get the day of + the week. + +/ +DayOfWeek getDayOfWeek(int day) @safe pure nothrow +{ + // January 1st, 1 A.D. was a Monday + if (day >= 0) + return cast(DayOfWeek)(day % 7); + else + { + immutable dow = cast(DayOfWeek)((day % 7) + 7); + + if (dow == 7) + return DayOfWeek.sun; + else + return dow; + } +} + +@safe unittest +{ + import std.datetime.systime : SysTime; + + // Test A.D. + assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun); + + // Test B.C. + assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat); +} + + +private: + +enum daysInYear = 365; // The number of days in a non-leap year. +enum daysInLeapYear = 366; // The numbef or days in a leap year. +enum daysIn4Years = daysInYear * 3 + daysInLeapYear; // Number of days in 4 years. +enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. +enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. + +/+ + Array of integers representing the last days of each month in a year. + +/ +immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + +/+ + Array of integers representing the last days of each month in a leap year. + +/ +immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + + +/+ + Returns the string representation of the given month. + +/ +string monthToString(Month month) @safe pure +{ + import std.format : format; + assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month)); + return _monthNames[month - Month.jan]; +} + +@safe unittest +{ + assert(monthToString(Month.jan) == "Jan"); + assert(monthToString(Month.feb) == "Feb"); + assert(monthToString(Month.mar) == "Mar"); + assert(monthToString(Month.apr) == "Apr"); + assert(monthToString(Month.may) == "May"); + assert(monthToString(Month.jun) == "Jun"); + assert(monthToString(Month.jul) == "Jul"); + assert(monthToString(Month.aug) == "Aug"); + assert(monthToString(Month.sep) == "Sep"); + assert(monthToString(Month.oct) == "Oct"); + assert(monthToString(Month.nov) == "Nov"); + assert(monthToString(Month.dec) == "Dec"); +} + + +/+ + Returns the Month corresponding to the given string. + + Params: + monthStr = The string representation of the month to get the Month for. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given month is not a + valid month string. + +/ +Month monthFromString(string monthStr) @safe pure +{ + import std.format : format; + switch (monthStr) + { + case "Jan": + return Month.jan; + case "Feb": + return Month.feb; + case "Mar": + return Month.mar; + case "Apr": + return Month.apr; + case "May": + return Month.may; + case "Jun": + return Month.jun; + case "Jul": + return Month.jul; + case "Aug": + return Month.aug; + case "Sep": + return Month.sep; + case "Oct": + return Month.oct; + case "Nov": + return Month.nov; + case "Dec": + return Month.dec; + default: + throw new DateTimeException(format("Invalid month %s", monthStr)); + } +} + +@safe unittest +{ + import std.stdio : writeln; + import std.traits : EnumMembers; + foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY", + "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"]) + { + scope(failure) writeln(badStr); + assertThrown!DateTimeException(monthFromString(badStr)); + } + + foreach (month; EnumMembers!Month) + { + scope(failure) writeln(month); + assert(monthFromString(monthToString(month)) == month); + } +} + + +version(unittest) +{ + // All of these helper arrays are sorted in ascending order. + auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; + auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct MonthDay + { + Month month; + short day; + + this(int m, short d) + { + month = cast(Month) m; + day = d; + } + } + + MonthDay[] testMonthDays = [MonthDay(1, 1), + MonthDay(1, 2), + MonthDay(3, 17), + MonthDay(7, 4), + MonthDay(10, 27), + MonthDay(12, 30), + MonthDay(12, 31)]; + + auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; + + auto testTODs = [TimeOfDay(0, 0, 0), + TimeOfDay(0, 0, 1), + TimeOfDay(0, 1, 0), + TimeOfDay(1, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + auto testHours = [0, 1, 12, 22, 23]; + auto testMinSecs = [0, 1, 30, 58, 59]; + + // Throwing exceptions is incredibly expensive, so we want to use a smaller + // set of values for tests using assertThrown. + auto testTODsThrown = [TimeOfDay(0, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + Date[] testDatesBC; + Date[] testDatesAD; + + DateTime[] testDateTimesBC; + DateTime[] testDateTimesAD; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct GregDay { int day; Date date; } + auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar + GregDay(-735_233, Date(-2012, 1, 1)), + GregDay(-735_202, Date(-2012, 2, 1)), + GregDay(-735_175, Date(-2012, 2, 28)), + GregDay(-735_174, Date(-2012, 2, 29)), + GregDay(-735_173, Date(-2012, 3, 1)), + GregDay(-734_502, Date(-2010, 1, 1)), + GregDay(-734_472, Date(-2010, 1, 31)), + GregDay(-734_471, Date(-2010, 2, 1)), + GregDay(-734_444, Date(-2010, 2, 28)), + GregDay(-734_443, Date(-2010, 3, 1)), + GregDay(-734_413, Date(-2010, 3, 31)), + GregDay(-734_412, Date(-2010, 4, 1)), + GregDay(-734_383, Date(-2010, 4, 30)), + GregDay(-734_382, Date(-2010, 5, 1)), + GregDay(-734_352, Date(-2010, 5, 31)), + GregDay(-734_351, Date(-2010, 6, 1)), + GregDay(-734_322, Date(-2010, 6, 30)), + GregDay(-734_321, Date(-2010, 7, 1)), + GregDay(-734_291, Date(-2010, 7, 31)), + GregDay(-734_290, Date(-2010, 8, 1)), + GregDay(-734_260, Date(-2010, 8, 31)), + GregDay(-734_259, Date(-2010, 9, 1)), + GregDay(-734_230, Date(-2010, 9, 30)), + GregDay(-734_229, Date(-2010, 10, 1)), + GregDay(-734_199, Date(-2010, 10, 31)), + GregDay(-734_198, Date(-2010, 11, 1)), + GregDay(-734_169, Date(-2010, 11, 30)), + GregDay(-734_168, Date(-2010, 12, 1)), + GregDay(-734_139, Date(-2010, 12, 30)), + GregDay(-734_138, Date(-2010, 12, 31)), + GregDay(-731_215, Date(-2001, 1, 1)), + GregDay(-730_850, Date(-2000, 1, 1)), + GregDay(-730_849, Date(-2000, 1, 2)), + GregDay(-730_486, Date(-2000, 12, 30)), + GregDay(-730_485, Date(-2000, 12, 31)), + GregDay(-730_484, Date(-1999, 1, 1)), + GregDay(-694_690, Date(-1901, 1, 1)), + GregDay(-694_325, Date(-1900, 1, 1)), + GregDay(-585_118, Date(-1601, 1, 1)), + GregDay(-584_753, Date(-1600, 1, 1)), + GregDay(-584_388, Date(-1600, 12, 31)), + GregDay(-584_387, Date(-1599, 1, 1)), + GregDay(-365_972, Date(-1001, 1, 1)), + GregDay(-365_607, Date(-1000, 1, 1)), + GregDay(-183_351, Date(-501, 1, 1)), + GregDay(-182_986, Date(-500, 1, 1)), + GregDay(-182_621, Date(-499, 1, 1)), + GregDay(-146_827, Date(-401, 1, 1)), + GregDay(-146_462, Date(-400, 1, 1)), + GregDay(-146_097, Date(-400, 12, 31)), + GregDay(-110_302, Date(-301, 1, 1)), + GregDay(-109_937, Date(-300, 1, 1)), + GregDay(-73_778, Date(-201, 1, 1)), + GregDay(-73_413, Date(-200, 1, 1)), + GregDay(-38_715, Date(-105, 1, 1)), + GregDay(-37_254, Date(-101, 1, 1)), + GregDay(-36_889, Date(-100, 1, 1)), + GregDay(-36_524, Date(-99, 1, 1)), + GregDay(-36_160, Date(-99, 12, 31)), + GregDay(-35_794, Date(-97, 1, 1)), + GregDay(-18_627, Date(-50, 1, 1)), + GregDay(-18_262, Date(-49, 1, 1)), + GregDay(-3652, Date(-9, 1, 1)), + GregDay(-2191, Date(-5, 1, 1)), + GregDay(-1827, Date(-5, 12, 31)), + GregDay(-1826, Date(-4, 1, 1)), + GregDay(-1825, Date(-4, 1, 2)), + GregDay(-1462, Date(-4, 12, 30)), + GregDay(-1461, Date(-4, 12, 31)), + GregDay(-1460, Date(-3, 1, 1)), + GregDay(-1096, Date(-3, 12, 31)), + GregDay(-1095, Date(-2, 1, 1)), + GregDay(-731, Date(-2, 12, 31)), + GregDay(-730, Date(-1, 1, 1)), + GregDay(-367, Date(-1, 12, 30)), + GregDay(-366, Date(-1, 12, 31)), + GregDay(-365, Date(0, 1, 1)), + GregDay(-31, Date(0, 11, 30)), + GregDay(-30, Date(0, 12, 1)), + GregDay(-1, Date(0, 12, 30)), + GregDay(0, Date(0, 12, 31))]; + + auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), + GregDay(2, Date(1, 1, 2)), + GregDay(32, Date(1, 2, 1)), + GregDay(365, Date(1, 12, 31)), + GregDay(366, Date(2, 1, 1)), + GregDay(731, Date(3, 1, 1)), + GregDay(1096, Date(4, 1, 1)), + GregDay(1097, Date(4, 1, 2)), + GregDay(1460, Date(4, 12, 30)), + GregDay(1461, Date(4, 12, 31)), + GregDay(1462, Date(5, 1, 1)), + GregDay(17_898, Date(50, 1, 1)), + GregDay(35_065, Date(97, 1, 1)), + GregDay(36_160, Date(100, 1, 1)), + GregDay(36_525, Date(101, 1, 1)), + GregDay(37_986, Date(105, 1, 1)), + GregDay(72_684, Date(200, 1, 1)), + GregDay(73_049, Date(201, 1, 1)), + GregDay(109_208, Date(300, 1, 1)), + GregDay(109_573, Date(301, 1, 1)), + GregDay(145_732, Date(400, 1, 1)), + GregDay(146_098, Date(401, 1, 1)), + GregDay(182_257, Date(500, 1, 1)), + GregDay(182_622, Date(501, 1, 1)), + GregDay(364_878, Date(1000, 1, 1)), + GregDay(365_243, Date(1001, 1, 1)), + GregDay(584_023, Date(1600, 1, 1)), + GregDay(584_389, Date(1601, 1, 1)), + GregDay(693_596, Date(1900, 1, 1)), + GregDay(693_961, Date(1901, 1, 1)), + GregDay(729_755, Date(1999, 1, 1)), + GregDay(730_120, Date(2000, 1, 1)), + GregDay(730_121, Date(2000, 1, 2)), + GregDay(730_484, Date(2000, 12, 30)), + GregDay(730_485, Date(2000, 12, 31)), + GregDay(730_486, Date(2001, 1, 1)), + GregDay(733_773, Date(2010, 1, 1)), + GregDay(733_774, Date(2010, 1, 2)), + GregDay(733_803, Date(2010, 1, 31)), + GregDay(733_804, Date(2010, 2, 1)), + GregDay(733_831, Date(2010, 2, 28)), + GregDay(733_832, Date(2010, 3, 1)), + GregDay(733_862, Date(2010, 3, 31)), + GregDay(733_863, Date(2010, 4, 1)), + GregDay(733_892, Date(2010, 4, 30)), + GregDay(733_893, Date(2010, 5, 1)), + GregDay(733_923, Date(2010, 5, 31)), + GregDay(733_924, Date(2010, 6, 1)), + GregDay(733_953, Date(2010, 6, 30)), + GregDay(733_954, Date(2010, 7, 1)), + GregDay(733_984, Date(2010, 7, 31)), + GregDay(733_985, Date(2010, 8, 1)), + GregDay(734_015, Date(2010, 8, 31)), + GregDay(734_016, Date(2010, 9, 1)), + GregDay(734_045, Date(2010, 9, 30)), + GregDay(734_046, Date(2010, 10, 1)), + GregDay(734_076, Date(2010, 10, 31)), + GregDay(734_077, Date(2010, 11, 1)), + GregDay(734_106, Date(2010, 11, 30)), + GregDay(734_107, Date(2010, 12, 1)), + GregDay(734_136, Date(2010, 12, 30)), + GregDay(734_137, Date(2010, 12, 31)), + GregDay(734_503, Date(2012, 1, 1)), + GregDay(734_534, Date(2012, 2, 1)), + GregDay(734_561, Date(2012, 2, 28)), + GregDay(734_562, Date(2012, 2, 29)), + GregDay(734_563, Date(2012, 3, 1)), + GregDay(734_858, Date(2012, 12, 21))]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct DayOfYear { int day; MonthDay md; } + auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(3, 1)), + DayOfYear(90, MonthDay(3, 31)), + DayOfYear(91, MonthDay(4, 1)), + DayOfYear(120, MonthDay(4, 30)), + DayOfYear(121, MonthDay(5, 1)), + DayOfYear(151, MonthDay(5, 31)), + DayOfYear(152, MonthDay(6, 1)), + DayOfYear(181, MonthDay(6, 30)), + DayOfYear(182, MonthDay(7, 1)), + DayOfYear(212, MonthDay(7, 31)), + DayOfYear(213, MonthDay(8, 1)), + DayOfYear(243, MonthDay(8, 31)), + DayOfYear(244, MonthDay(9, 1)), + DayOfYear(273, MonthDay(9, 30)), + DayOfYear(274, MonthDay(10, 1)), + DayOfYear(304, MonthDay(10, 31)), + DayOfYear(305, MonthDay(11, 1)), + DayOfYear(334, MonthDay(11, 30)), + DayOfYear(335, MonthDay(12, 1)), + DayOfYear(363, MonthDay(12, 29)), + DayOfYear(364, MonthDay(12, 30)), + DayOfYear(365, MonthDay(12, 31))]; + + auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(2, 29)), + DayOfYear(61, MonthDay(3, 1)), + DayOfYear(91, MonthDay(3, 31)), + DayOfYear(92, MonthDay(4, 1)), + DayOfYear(121, MonthDay(4, 30)), + DayOfYear(122, MonthDay(5, 1)), + DayOfYear(152, MonthDay(5, 31)), + DayOfYear(153, MonthDay(6, 1)), + DayOfYear(182, MonthDay(6, 30)), + DayOfYear(183, MonthDay(7, 1)), + DayOfYear(213, MonthDay(7, 31)), + DayOfYear(214, MonthDay(8, 1)), + DayOfYear(244, MonthDay(8, 31)), + DayOfYear(245, MonthDay(9, 1)), + DayOfYear(274, MonthDay(9, 30)), + DayOfYear(275, MonthDay(10, 1)), + DayOfYear(305, MonthDay(10, 31)), + DayOfYear(306, MonthDay(11, 1)), + DayOfYear(335, MonthDay(11, 30)), + DayOfYear(336, MonthDay(12, 1)), + DayOfYear(364, MonthDay(12, 29)), + DayOfYear(365, MonthDay(12, 30)), + DayOfYear(366, MonthDay(12, 31))]; + + void initializeTests() @safe + { + foreach (year; testYearsBC) + { + foreach (md; testMonthDays) + testDatesBC ~= Date(year, md.month, md.day); + } + + foreach (year; testYearsAD) + { + foreach (md; testMonthDays) + testDatesAD ~= Date(year, md.month, md.day); + } + + foreach (dt; testDatesBC) + { + foreach (tod; testTODs) + testDateTimesBC ~= DateTime(dt, tod); + } + + foreach (dt; testDatesAD) + { + foreach (tod; testTODs) + testDateTimesAD ~= DateTime(dt, tod); + } + } +} diff --git a/std/datetime/interval.d b/std/datetime/interval.d new file mode 100644 index 00000000000..302b4c28e99 --- /dev/null +++ b/std/datetime/interval.d @@ -0,0 +1,9130 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/datetime/_interval.d) ++/ +module std.datetime.interval; + +import core.time : Duration, dur; +import std.datetime.date : AllowDayOverflow, DateTimeException, daysToDayOfWeek, + DayOfWeek, isTimePoint, Month; +import std.exception : enforce; +import std.traits : isIntegral, Unqual; +import std.typecons : Flag; + +version(unittest) import std.exception : assertThrown; + + +/++ + Indicates a direction in time. One example of its use is $(LREF Interval)'s + $(LREF expand) function which uses it to indicate whether the interval + should be expanded backwards (into the past), forwards (into the future), or + both. + +/ +enum Direction +{ + /// Backward. + bwd, + + /// Forward. + fwd, + + /// Both backward and forward. + both +} + + +/++ + Used to indicate whether $(D popFront) should be called immediately upon + creating a range. The idea is that for some functions used to generate a + range for an interval, $(D front) is not necessarily a time point which + would ever be generated by the range (e.g. if the range were every Sunday + within an interval, but the interval started on a Monday), so there needs + to be a way to deal with that. To get the first time point in the range to + match what the function generates, then use $(D PopFirst.yes) to indicate + that the range should have $(D popFront) called on it before the range is + returned so that $(D front) is a time point which the function would + generate. To let the first time point not match the generator function, + use $(D PopFront.no). + + For instance, if the function used to generate a range of time points + generated successive Easters (i.e. you're iterating over all of the Easters + within the interval), the initial date probably isn't an Easter. Using + $(D PopFirst.yes) would tell the function which returned the range that + $(D popFront) was to be called so that front would then be an Easter - the + next one generated by the function (which when iterating forward would be + the Easter following the original $(D front), while when iterating backward, + it would be the Easter prior to the original $(D front)). If + $(D PopFirst.no) were used, then $(D front) would remain the original time + point and it would not necessarily be a time point which would be generated + by the range-generating function (which in many cases is exactly what is + desired - e.g. if iterating over every day starting at the beginning of the + interval). + + If set to $(D PopFirst.no), then popFront is not called before returning + the range. + + Otherwise, if set to $(D PopFirst.yes), then popFront is called before + returning the range. + +/ +alias PopFirst = Flag!"popFirst"; + + +/++ + Represents an interval of time. + + An $(D Interval) has a starting point and an end point. The interval of time + is therefore the time starting at the starting point up to, but not + including, the end point. e.g. + + $(BOOKTABLE, + $(TR $(TD [January 5th, 2010 - March 10th, 2010$(RPAREN))) + $(TR $(TD [05:00:30 - 12:00:00$(RPAREN))) + $(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN))) + ) + + A range can be obtained from an $(D Interval), allowing iteration over + that interval, with the exact time points which are iterated over depending + on the function which generates the range. + +/ +struct Interval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + end = The time point which ends (but is not included in) the + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if $(D_PARAM end) is + before $(D_PARAM begin). + + Example: + -------------------- + Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + -------------------- + +/ + this(U)(in TP begin, in U end) pure + if (is(Unqual!TP == Unqual!U)) + { + if (!_valid(begin, end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + _begin = cast(TP) begin; + _end = cast(TP) end; + } + + + /++ + Params: + begin = The time point which begins the interval. + duration = The duration from the starting point to the end point. + + Throws: + $(REF DateTimeException,std,datetime,date) if the resulting + $(D end) is before $(D begin). + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == + Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + -------------------- + +/ + this(D)(in TP begin, in D duration) pure + if (__traits(compiles, begin + duration)) + { + _begin = cast(TP) begin; + _end = begin + duration; + if (!_valid(_begin, _end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + } + + + /++ + Params: + rhs = The $(LREF Interval) to assign to this one. + +/ + ref Interval opAssign(const ref Interval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + _end = cast(TP) rhs._end; + return this; + } + + + /++ + Params: + rhs = The $(LREF Interval) to assign to this one. + +/ + ref Interval opAssign(Interval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + _end = cast(TP) rhs._end; + return this; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == + Date(1996, 1, 2)); + -------------------- + +/ + @property TP begin() const pure nothrow + { + return cast(TP) _begin; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Params: + timePoint = The time point to set $(D begin) to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the resulting + interval would be invalid. + +/ + @property void begin(TP timePoint) pure + { + if (!_valid(timePoint, _end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + _begin = timePoint; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == + Date(2012, 3, 1)); + -------------------- + +/ + @property TP end() const pure nothrow + { + return cast(TP) _end; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Params: + timePoint = The time point to set end to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the resulting + interval would be invalid. + +/ + @property void end(TP timePoint) pure + { + if (!_valid(_begin, timePoint)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + _end = timePoint; + } + + + /++ + Returns the duration between $(D begin) and $(D end). + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == + dur!"days"(5903)); + -------------------- + +/ + @property auto length() const pure nothrow + { + return _end - _begin; + } + + + /++ + Whether the interval's length is 0, that is, whether $(D begin == end). + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); + -------------------- + +/ + @property bool empty() const pure nothrow + { + return _begin == _end; + } + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Date(1994, 12, 24))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Date(2012, 3, 1))); + -------------------- + +/ + bool contains(in TP timePoint) const pure + { + _enforceNotEmpty(); + return timePoint >= _begin && timePoint < _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if either interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + -------------------- + +/ + bool contains(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return interval._begin >= _begin && + interval._begin < _end && + interval._end <= _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false (unless this interval is empty), because an + interval going to positive infinity can never be contained in a finite + interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + PosInfInterval!Date(Date(1999, 5, 4)))); + -------------------- + +/ + bool contains(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false (unless this interval is empty), because an + interval beginning at negative infinity can never be contained in a + finite interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + NegInfInterval!Date(Date(1996, 5, 4)))); + -------------------- + +/ + bool contains(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is before the given time point. + + Params: + timePoint = The time point to check whether this interval is before + it. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Date(1994, 12, 24))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Date(2000, 1, 5))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Date(2012, 3, 1))); + -------------------- + +/ + bool isBefore(in TP timePoint) const pure + { + _enforceNotEmpty(); + return _end <= timePoint; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if either interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); + -------------------- + +/ + bool isBefore(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(2013, 3, 7)))); + -------------------- + +/ + bool isBefore(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Always returns false (unless this interval is empty) because a finite + interval can never be before an interval beginning at negative infinity. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + NegInfInterval!Date(Date(1996, 5, 4)))); + -------------------- + +/ + bool isBefore(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is after the given time point. + + Params: + timePoint = The time point to check whether this interval is after + it. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Date(1994, 12, 24))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Date(2000, 1, 5))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Date(2012, 3, 1))); + -------------------- + +/ + bool isAfter(in TP timePoint) const pure + { + _enforceNotEmpty(); + return timePoint < _begin; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if either interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + -------------------- + +/ + bool isAfter(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return _begin >= interval._end; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false (unless this interval is empty) because a finite + interval can never be after an interval going to positive infinity. + + Params: + interval = The interval to check against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + PosInfInterval!Date(Date(1999, 5, 4)))); + -------------------- + +/ + bool isAfter(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + NegInfInterval!Date(Date(1996, 1, 2)))); + -------------------- + +/ + bool isAfter(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _begin >= interval._end; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if either interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + -------------------- + +/ + bool intersects(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return interval._begin < _end && interval._end > _begin; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(2012, 3, 1)))); + -------------------- + +/ + bool intersects(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _end > interval._begin; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(2000, 1, 2)))); + -------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _begin < interval._end; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect or if either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + -------------------- + +/ + Interval intersection(in Interval interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto begin = _begin > interval._begin ? _begin : interval._begin; + auto end = _end < interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect or if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + -------------------- + +/ + Interval intersection(in PosInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval(_begin > interval._begin ? _begin : interval._begin, _end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect or if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + -------------------- + +/ + Interval intersection(in NegInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval(_begin, _end < interval._end ? _end : interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if either interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); + -------------------- + +/ + bool isAdjacent(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return _begin == interval._end || _end == interval._begin; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(2012, 3, 1)))); + -------------------- + +/ + bool isAdjacent(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _end == interval._begin; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(1996, 1, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(2000, 1, 2)))); + -------------------- + +/ + bool isAdjacent(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _begin == interval._end; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect and are not adjacent or if either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + -------------------- + +/ + Interval merge(in Interval interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + auto begin = _begin < interval._begin ? _begin : interval._begin; + auto end = _end > interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect and are not adjacent or if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + PosInfInterval!Date(Date(2012, 3, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + -------------------- + +/ + PosInfInterval!TP merge(in PosInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect and are not adjacent or if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(1996, 1, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); + -------------------- + +/ + NegInfInterval!TP merge(in NegInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return NegInfInterval!TP(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if either interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + -------------------- + +/ + Interval span(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + auto begin = _begin < interval._begin ? _begin : interval._begin; + auto end = _end > interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + PosInfInterval!Date(Date(2050, 1, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + -------------------- + +/ + PosInfInterval!TP span(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(1602, 5, 21))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); + -------------------- + +/ + NegInfInterval!TP span(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return NegInfInterval!TP(_end > interval._end ? _end : interval._end); + } + + + /++ + Shifts the interval forward or backwards in time by the given duration + (a positive duration shifts the interval forward; a negative duration + shifts it backward). Effectively, it does $(D begin += duration) and + $(D end += duration). + + Params: + duration = The duration to shift the interval by. + + Throws: + $(REF DateTimeException,std,datetime,date) this interval is empty + or if the resulting interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); + -------------------- + +/ + void shift(D)(D duration) pure + if (__traits(compiles, begin + duration)) + { + _enforceNotEmpty(); + + auto begin = _begin + duration; + auto end = _end + duration; + + if (!_valid(begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + + _begin = begin; + _end = end; + } + + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Shifts the interval forward or backwards in time by the given number + of years and/or months (a positive number of years and months shifts + the interval forward; a negative number shifts it backward). + It adds the years the given years and months to both begin and end. + It effectively calls $(D add!"years"()) and then $(D add!"months"()) + on begin and end with the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin) and $(D end), causing their month + to increment. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty or if the resulting interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); + -------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + _enforceNotEmpty(); + + auto begin = _begin; + auto end = _end; + + begin.add!"years"(years, allowOverflow); + begin.add!"months"(months, allowOverflow); + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + + _begin = begin; + _end = end; + } + } + + + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it does $(D begin -= duration) and/or $(D end += duration). Whether + it expands forwards and/or backwards in time is determined by + $(D_PARAM dir). + + Params: + duration = The duration to expand the interval by. + dir = The direction in time to expand the interval. + + Throws: + $(REF DateTimeException,std,datetime,date) this interval is empty + or if the resulting interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); + -------------------- + +/ + void expand(D)(D duration, Direction dir = Direction.both) pure + if (__traits(compiles, begin + duration)) + { + _enforceNotEmpty(); + + switch (dir) + { + case Direction.both: + { + auto begin = _begin - duration; + auto end = _end + duration; + + if (!_valid(begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + + _begin = begin; + _end = end; + + return; + } + case Direction.fwd: + { + auto end = _end + duration; + + if (!_valid(_begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + _end = end; + + return; + } + case Direction.bwd: + { + auto begin = _begin - duration; + + if (!_valid(begin, _end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + _begin = begin; + + return; + } + default: + assert(0, "Invalid Direction."); + } + } + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it subtracts the given number of months/years from $(D begin) and + adds them to $(D end). Whether it expands forwards and/or backwards + in time is determined by $(D_PARAM dir). + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin) and $(D end), causing their month + to increment. + dir = The direction in time to expand the interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty or if the resulting interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); + -------------------- + +/ + void expand(T)(T years, + T months = 0, + AllowDayOverflow allowOverflow = AllowDayOverflow.yes, + Direction dir = Direction.both) + if (isIntegral!T) + { + _enforceNotEmpty(); + + switch (dir) + { + case Direction.both: + { + auto begin = _begin; + auto end = _end; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + _begin = begin; + _end = end; + + return; + } + case Direction.fwd: + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(_begin, end), + new DateTimeException("Argument would result in an invalid Interval.")); + _end = end; + + return; + } + case Direction.bwd: + { + auto begin = _begin; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + + enforce(_valid(begin, _end), + new DateTimeException("Argument would result in an invalid Interval.")); + _begin = begin; + + return; + } + default: + assert(0, "Invalid Direction."); + } + } + } + + + /++ + Returns a range which iterates forward over the interval, starting + at $(D begin), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point less than or equal to the + current $(D front) of the range, then a + $(REF DateTimeException,std,datetime,date) will be thrown. The range + will be empty and iteration complete when $(D_PARAM func) generates a + time point equal to or beyond the $(D end) of the interval. + + There are helper functions in this module which generate common + delegates to pass to $(D fwdRange). Their documentation starts with + "Range-generating function," making them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant if when creating a custom delegate. + + Example: + -------------------- + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) // For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + assert(range.front == Date(2010, 9, 1)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.empty); + -------------------- + +/ + IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + _enforceNotEmpty(); + + auto range = IntervalRange!(TP, Direction.fwd)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /++ + Returns a range which iterates backwards over the interval, starting + at $(D end), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D end). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point greater than or equal to + the current $(D front) of the range, then a + $(REF DateTimeException,std,datetime,date) will be thrown. The range + will be empty and iteration complete when $(D_PARAM func) generates a + time point equal to or less than the $(D begin) of the interval. + + There are helper functions in this module which generate common + delegates to pass to $(D bwdRange). Their documentation starts with + "Range-generating function," making them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant for custom delegates. + + Example: + -------------------- + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) // For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.empty); + -------------------- + +/ + IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + _enforceNotEmpty(); + + auto range = IntervalRange!(TP, Direction.bwd)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + // Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + // have versions of toString() with extra modifiers, so we define one version + // with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + // Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + // have versions of toString() with extra modifiers, so we define one version + // with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + + +private: + + /+ + Since we have two versions of toString, we have _toStringImpl + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + import std.format : format; + try + return format("[%s - %s)", _begin, _end); + catch (Exception e) + assert(0, "format() threw."); + } + + + /+ + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + +/ + void _enforceNotEmpty(size_t line = __LINE__) const pure + { + if (empty) + throw new DateTimeException("Invalid operation for an empty Interval.", __FILE__, line); + } + + + /+ + Whether the given values form a valid time interval. + + Params: + begin = The starting point of the interval. + end = The end point of the interval. + +/ + static bool _valid(in TP begin, in TP end) pure nothrow + { + return begin <= end; + } + + + pure invariant() + { + assert(_valid(_begin, _end), "Invariant Failure: begin is not before or equal to end."); + } + + + TP _begin; + TP _end; +} + +// Test Interval's constructors. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); + + Interval!Date(Date.init, Date.init); + Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); + Interval!DateTime(DateTime.init, DateTime.init); + Interval!SysTime(SysTime(0), SysTime(0)); + + Interval!DateTime(DateTime.init, dur!"days"(7)); + + // Verify Examples. + Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); +} + +// Test Interval's begin. +@safe unittest +{ + import std.datetime.date; + + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); + assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.begin == Date(2010, 7, 4)); + assert(iInterval.begin == Date(2010, 7, 4)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); +} + +// Test Interval's end. +@safe unittest +{ + import std.datetime.date; + + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.end == Date(2012, 1, 7)); + assert(iInterval.end == Date(2012, 1, 7)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); +} + +// Test Interval's length. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); + assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); + assert(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length == + dur!"seconds"(129_127)); + assert(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)),SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length == + dur!"seconds"(129_127)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.length != Duration.zero); + assert(iInterval.length != Duration.zero); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); +} + +// Test Interval's empty. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); + assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); + assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); + assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); + assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cInterval.empty); + assert(!iInterval.empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); +} + +// Test Interval's contains(time point). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); + + assert(!interval.contains(Date(2009, 7, 4))); + assert(!interval.contains(Date(2010, 7, 3))); + assert(interval.contains(Date(2010, 7, 4))); + assert(interval.contains(Date(2010, 7, 5))); + assert(interval.contains(Date(2011, 7, 1))); + assert(interval.contains(Date(2012, 1, 6))); + assert(!interval.contains(Date(2012, 1, 7))); + assert(!interval.contains(Date(2012, 1, 8))); + assert(!interval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(interval.contains(cdate)); + assert(cInterval.contains(cdate)); + assert(iInterval.contains(cdate)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +} + +// Test Interval's contains(Interval). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4),dur!"days"(0)).contains( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.contains(interval)); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); + + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(interval.contains(interval)); + assert(interval.contains(cInterval)); + assert(interval.contains(iInterval)); + assert(!interval.contains(posInfInterval)); + assert(!interval.contains(cPosInfInterval)); + assert(!interval.contains(iPosInfInterval)); + assert(!interval.contains(negInfInterval)); + assert(!interval.contains(cNegInfInterval)); + assert(!interval.contains(iNegInfInterval)); + assert(cInterval.contains(interval)); + assert(cInterval.contains(cInterval)); + assert(cInterval.contains(iInterval)); + assert(!cInterval.contains(posInfInterval)); + assert(!cInterval.contains(cPosInfInterval)); + assert(!cInterval.contains(iPosInfInterval)); + assert(!cInterval.contains(negInfInterval)); + assert(!cInterval.contains(cNegInfInterval)); + assert(!cInterval.contains(iNegInfInterval)); + assert(iInterval.contains(interval)); + assert(iInterval.contains(cInterval)); + assert(iInterval.contains(iInterval)); + assert(!iInterval.contains(posInfInterval)); + assert(!iInterval.contains(cPosInfInterval)); + assert(!iInterval.contains(iPosInfInterval)); + assert(!iInterval.contains(negInfInterval)); + assert(!iInterval.contains(cNegInfInterval)); + assert(!iInterval.contains(iNegInfInterval)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +// Test Interval's isBefore(time point). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); + + assert(!interval.isBefore(Date(2009, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 4))); + assert(!interval.isBefore(Date(2010, 7, 5))); + assert(!interval.isBefore(Date(2011, 7, 1))); + assert(!interval.isBefore(Date(2012, 1, 6))); + assert(interval.isBefore(Date(2012, 1, 7))); + assert(interval.isBefore(Date(2012, 1, 8))); + assert(interval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!interval.isBefore(cdate)); + assert(!cInterval.isBefore(cdate)); + assert(!iInterval.isBefore(cdate)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +} + +// Test Interval's isBefore(Interval). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isBefore(interval)); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); + + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.isBefore(interval)); + assert(!interval.isBefore(cInterval)); + assert(!interval.isBefore(iInterval)); + assert(!interval.isBefore(posInfInterval)); + assert(!interval.isBefore(cPosInfInterval)); + assert(!interval.isBefore(iPosInfInterval)); + assert(!interval.isBefore(negInfInterval)); + assert(!interval.isBefore(cNegInfInterval)); + assert(!interval.isBefore(iNegInfInterval)); + assert(!cInterval.isBefore(interval)); + assert(!cInterval.isBefore(cInterval)); + assert(!cInterval.isBefore(iInterval)); + assert(!cInterval.isBefore(posInfInterval)); + assert(!cInterval.isBefore(cPosInfInterval)); + assert(!cInterval.isBefore(iPosInfInterval)); + assert(!cInterval.isBefore(negInfInterval)); + assert(!cInterval.isBefore(cNegInfInterval)); + assert(!cInterval.isBefore(iNegInfInterval)); + assert(!iInterval.isBefore(interval)); + assert(!iInterval.isBefore(cInterval)); + assert(!iInterval.isBefore(iInterval)); + assert(!iInterval.isBefore(posInfInterval)); + assert(!iInterval.isBefore(cPosInfInterval)); + assert(!iInterval.isBefore(iPosInfInterval)); + assert(!iInterval.isBefore(negInfInterval)); + assert(!iInterval.isBefore(cNegInfInterval)); + assert(!iInterval.isBefore(iNegInfInterval)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +// Test Interval's isAfter(time point). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); + + assert(interval.isAfter(Date(2009, 7, 4))); + assert(interval.isAfter(Date(2010, 7, 3))); + assert(!interval.isAfter(Date(2010, 7, 4))); + assert(!interval.isAfter(Date(2010, 7, 5))); + assert(!interval.isAfter(Date(2011, 7, 1))); + assert(!interval.isAfter(Date(2012, 1, 6))); + assert(!interval.isAfter(Date(2012, 1, 7))); + assert(!interval.isAfter(Date(2012, 1, 8))); + assert(!interval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!interval.isAfter(cdate)); + assert(!cInterval.isAfter(cdate)); + assert(!iInterval.isAfter(cdate)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +} + +// Test Interval's isAfter(Interval). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAfter(interval)); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); + + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.isAfter(interval)); + assert(!interval.isAfter(cInterval)); + assert(!interval.isAfter(iInterval)); + assert(!interval.isAfter(posInfInterval)); + assert(!interval.isAfter(cPosInfInterval)); + assert(!interval.isAfter(iPosInfInterval)); + assert(!interval.isAfter(negInfInterval)); + assert(!interval.isAfter(cNegInfInterval)); + assert(!interval.isAfter(iNegInfInterval)); + assert(!cInterval.isAfter(interval)); + assert(!cInterval.isAfter(cInterval)); + assert(!cInterval.isAfter(iInterval)); + assert(!cInterval.isAfter(posInfInterval)); + assert(!cInterval.isAfter(cPosInfInterval)); + assert(!cInterval.isAfter(iPosInfInterval)); + assert(!cInterval.isAfter(negInfInterval)); + assert(!cInterval.isAfter(cNegInfInterval)); + assert(!cInterval.isAfter(iNegInfInterval)); + assert(!iInterval.isAfter(interval)); + assert(!iInterval.isAfter(cInterval)); + assert(!iInterval.isAfter(iInterval)); + assert(!iInterval.isAfter(posInfInterval)); + assert(!iInterval.isAfter(cPosInfInterval)); + assert(!iInterval.isAfter(iPosInfInterval)); + assert(!iInterval.isAfter(negInfInterval)); + assert(!iInterval.isAfter(cNegInfInterval)); + assert(!iInterval.isAfter(iNegInfInterval)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); +} + +// Test Interval's intersects(). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.intersects(interval)); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); + + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(interval.intersects(interval)); + assert(interval.intersects(cInterval)); + assert(interval.intersects(iInterval)); + assert(interval.intersects(posInfInterval)); + assert(interval.intersects(cPosInfInterval)); + assert(interval.intersects(iPosInfInterval)); + assert(interval.intersects(negInfInterval)); + assert(interval.intersects(cNegInfInterval)); + assert(interval.intersects(iNegInfInterval)); + assert(cInterval.intersects(interval)); + assert(cInterval.intersects(cInterval)); + assert(cInterval.intersects(iInterval)); + assert(cInterval.intersects(posInfInterval)); + assert(cInterval.intersects(cPosInfInterval)); + assert(cInterval.intersects(iPosInfInterval)); + assert(cInterval.intersects(negInfInterval)); + assert(cInterval.intersects(cNegInfInterval)); + assert(cInterval.intersects(iNegInfInterval)); + assert(iInterval.intersects(interval)); + assert(iInterval.intersects(cInterval)); + assert(iInterval.intersects(iInterval)); + assert(iInterval.intersects(posInfInterval)); + assert(iInterval.intersects(cPosInfInterval)); + assert(iInterval.intersects(iPosInfInterval)); + assert(iInterval.intersects(negInfInterval)); + assert(iInterval.intersects(cNegInfInterval)); + assert(iInterval.intersects(iNegInfInterval)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); +} + +// Test Interval's intersection(). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); + + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); + + assert(interval.intersection(interval) == interval); + assert(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.intersection(interval).empty); + assert(!interval.intersection(cInterval).empty); + assert(!interval.intersection(iInterval).empty); + assert(!interval.intersection(posInfInterval).empty); + assert(!interval.intersection(cPosInfInterval).empty); + assert(!interval.intersection(iPosInfInterval).empty); + assert(!interval.intersection(negInfInterval).empty); + assert(!interval.intersection(cNegInfInterval).empty); + assert(!interval.intersection(iNegInfInterval).empty); + assert(!cInterval.intersection(interval).empty); + assert(!cInterval.intersection(cInterval).empty); + assert(!cInterval.intersection(iInterval).empty); + assert(!cInterval.intersection(posInfInterval).empty); + assert(!cInterval.intersection(cPosInfInterval).empty); + assert(!cInterval.intersection(iPosInfInterval).empty); + assert(!cInterval.intersection(negInfInterval).empty); + assert(!cInterval.intersection(cNegInfInterval).empty); + assert(!cInterval.intersection(iNegInfInterval).empty); + assert(!iInterval.intersection(interval).empty); + assert(!iInterval.intersection(cInterval).empty); + assert(!iInterval.intersection(iInterval).empty); + assert(!iInterval.intersection(posInfInterval).empty); + assert(!iInterval.intersection(cPosInfInterval).empty); + assert(!iInterval.intersection(iPosInfInterval).empty); + assert(!iInterval.intersection(negInfInterval).empty); + assert(!iInterval.intersection(cNegInfInterval).empty); + assert(!iInterval.intersection(iNegInfInterval).empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1999, 1, 12),Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); +} + +// Test Interval's isAdjacent(). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.isAdjacent(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAdjacent(interval)); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); + + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.isAdjacent(interval)); + assert(!interval.isAdjacent(cInterval)); + assert(!interval.isAdjacent(iInterval)); + assert(!interval.isAdjacent(posInfInterval)); + assert(!interval.isAdjacent(cPosInfInterval)); + assert(!interval.isAdjacent(iPosInfInterval)); + assert(!interval.isAdjacent(negInfInterval)); + assert(!interval.isAdjacent(cNegInfInterval)); + assert(!interval.isAdjacent(iNegInfInterval)); + assert(!cInterval.isAdjacent(interval)); + assert(!cInterval.isAdjacent(cInterval)); + assert(!cInterval.isAdjacent(iInterval)); + assert(!cInterval.isAdjacent(posInfInterval)); + assert(!cInterval.isAdjacent(cPosInfInterval)); + assert(!cInterval.isAdjacent(iPosInfInterval)); + assert(!cInterval.isAdjacent(negInfInterval)); + assert(!cInterval.isAdjacent(cNegInfInterval)); + assert(!cInterval.isAdjacent(iNegInfInterval)); + assert(!iInterval.isAdjacent(interval)); + assert(!iInterval.isAdjacent(cInterval)); + assert(!iInterval.isAdjacent(iInterval)); + assert(!iInterval.isAdjacent(posInfInterval)); + assert(!iInterval.isAdjacent(cPosInfInterval)); + assert(!iInterval.isAdjacent(iPosInfInterval)); + assert(!iInterval.isAdjacent(negInfInterval)); + assert(!iInterval.isAdjacent(cNegInfInterval)); + assert(!iInterval.isAdjacent(iNegInfInterval)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)) .isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); +} + +// Test Interval's merge(). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(I)(in Interval!Date interval1, in I interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); + + assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); + + assert(interval.merge(interval) == interval); + assert(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.merge(interval).empty); + assert(!interval.merge(cInterval).empty); + assert(!interval.merge(iInterval).empty); + assert(!interval.merge(posInfInterval).empty); + assert(!interval.merge(cPosInfInterval).empty); + assert(!interval.merge(iPosInfInterval).empty); + assert(!interval.merge(negInfInterval).empty); + assert(!interval.merge(cNegInfInterval).empty); + assert(!interval.merge(iNegInfInterval).empty); + assert(!cInterval.merge(interval).empty); + assert(!cInterval.merge(cInterval).empty); + assert(!cInterval.merge(iInterval).empty); + assert(!cInterval.merge(posInfInterval).empty); + assert(!cInterval.merge(cPosInfInterval).empty); + assert(!cInterval.merge(iPosInfInterval).empty); + assert(!cInterval.merge(negInfInterval).empty); + assert(!cInterval.merge(cNegInfInterval).empty); + assert(!cInterval.merge(iNegInfInterval).empty); + assert(!iInterval.merge(interval).empty); + assert(!iInterval.merge(cInterval).empty); + assert(!iInterval.merge(iInterval).empty); + assert(!iInterval.merge(posInfInterval).empty); + assert(!iInterval.merge(cPosInfInterval).empty); + assert(!iInterval.merge(iPosInfInterval).empty); + assert(!iInterval.merge(negInfInterval).empty); + assert(!iInterval.merge(cNegInfInterval).empty); + assert(!iInterval.merge(iNegInfInterval).empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +} + +// Test Interval's span(). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)),interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.span(interval) == interval); + assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); + + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.span(interval).empty); + assert(!interval.span(cInterval).empty); + assert(!interval.span(iInterval).empty); + assert(!interval.span(posInfInterval).empty); + assert(!interval.span(cPosInfInterval).empty); + assert(!interval.span(iPosInfInterval).empty); + assert(!interval.span(negInfInterval).empty); + assert(!interval.span(cNegInfInterval).empty); + assert(!interval.span(iNegInfInterval).empty); + assert(!cInterval.span(interval).empty); + assert(!cInterval.span(cInterval).empty); + assert(!cInterval.span(iInterval).empty); + assert(!cInterval.span(posInfInterval).empty); + assert(!cInterval.span(cPosInfInterval).empty); + assert(!cInterval.span(iPosInfInterval).empty); + assert(!cInterval.span(negInfInterval).empty); + assert(!cInterval.span(cNegInfInterval).empty); + assert(!cInterval.span(iNegInfInterval).empty); + assert(!iInterval.span(interval).empty); + assert(!iInterval.span(cInterval).empty); + assert(!iInterval.span(iInterval).empty); + assert(!iInterval.span(posInfInterval).empty); + assert(!iInterval.span(cPosInfInterval).empty); + assert(!iInterval.span(iPosInfInterval).empty); + assert(!iInterval.span(negInfInterval).empty); + assert(!iInterval.span(cNegInfInterval).empty); + assert(!iInterval.span(iNegInfInterval).empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +} + +// Test Interval's shift(duration). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, in Duration duration) + { + interval.shift(duration); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); +} + +// Test Interval's shift(int, int, AllowDayOverflow). +@safe unittest +{ + import std.datetime.date; + + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.shift(years, months); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(5))); + static assert(!__traits(compiles, iInterval.shift(5))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); +} + +// Test Interval's expand(Duration). +@safe unittest +{ + import std.datetime.date; + + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(I)(I interval, in Duration duration) + { + interval.expand(duration); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); +} + +// Test Interval's expand(int, int, AllowDayOverflow, Direction) +@safe unittest +{ + import std.datetime.date; + + { + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.expand(years, months); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, + in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow, dir); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); + + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(5))); + static assert(!__traits(compiles, iInterval.expand(5))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); +} + +// Test Interval's fwdRange. +@system unittest +{ + import std.datetime.date; + + { + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + + static void testInterval1(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + } + + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + static void testInterval2(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval2(interval)); + + assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); + + assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == + Date(2010, 9, 12)); + + assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 17)); + } + + // Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + assert(range.front == Date(2010, 9, 1)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.empty); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(!iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); +} + +// Test Interval's bwdRange. +@system unittest +{ + import std.datetime.date; + + { + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + + static void testInterval1(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + } + + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + static void testInterval2(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval2(interval)); + + assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); + assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); + + assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == Date(2010, 10, 1)); + + assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 24)); + } + + // Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.empty); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(!iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); +} + +// Test Interval's toString(). +@safe unittest +{ + import std.datetime.date; + + assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.toString()); + assert(iInterval.toString()); +} + + +/++ + Represents an interval of time which has positive infinity as its end point. + + Any ranges which iterate over a $(D PosInfInterval) are infinite. So, the + main purpose of using $(D PosInfInterval) is to create an infinite range + which starts at a fixed point in time and goes to positive infinity. + +/ +struct PosInfInterval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + + Example: +-------------------- +auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +-------------------- + +/ + this(in TP begin) pure nothrow + { + _begin = cast(TP) begin; + } + + + /++ + Params: + rhs = The $(D PosInfInterval) to assign to this one. + +/ + ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + return this; + } + + + /++ + Params: + rhs = The $(D PosInfInterval) to assign to this one. + +/ + ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + return this; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); +-------------------- + +/ + @property TP begin() const pure nothrow + { + return cast(TP)_begin; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Params: + timePoint = The time point to set $(D begin) to. + +/ + @property void begin(TP timePoint) pure nothrow + { + _begin = timePoint; + } + + + /++ + Whether the interval's length is 0. Always returns false. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); +-------------------- + +/ + enum bool empty = false; + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); +-------------------- + +/ + bool contains(TP timePoint) const pure nothrow + { + return timePoint >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._begin >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( + PosInfInterval!Date(Date(1995, 7, 2)))); +-------------------- + +/ + bool contains(in PosInfInterval interval) const pure nothrow + { + return interval._begin >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false because an interval going to positive infinity + can never contain an interval beginning at negative infinity. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( + NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool contains(in NegInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given time point. + + Always returns false because an interval going to positive infinity + can never be before any time point. + + Params: + timePoint = The time point to check whether this interval is before + it. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false (unless the given interval is empty) because an + interval going to positive infinity can never be before any other + interval. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +-------------------- + +/ + bool isBefore(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false because an interval going to positive infinity can + never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + PosInfInterval!Date(Date(1992, 5, 4)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + PosInfInterval!Date(Date(2013, 3, 7)))); +-------------------- + +/ + bool isBefore(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false because an interval going to positive infinity can + never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool isBefore(in NegInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given time point. + + Params: + timePoint = The time point to check whether this interval is after + it. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure nothrow + { + return timePoint < _begin; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool isAfter(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return _begin >= interval._end; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false because an interval going to positive infinity can + never be after another interval going to positive infinity. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + PosInfInterval!Date(Date(1990, 1, 7)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool isAfter(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + NegInfInterval!Date(Date(1996, 1, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool isAfter(in NegInfInterval!TP interval) const pure nothrow + { + return _begin >= interval._end; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool intersects(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._end > _begin; + } + + + /++ + Whether the given interval overlaps this interval. + + Always returns true because two intervals going to positive infinity + always overlap. + + Params: + interval = The interval to check for intersection with this + interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + PosInfInterval!Date(Date(1990, 1, 7)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool intersects(in PosInfInterval interval) const pure nothrow + { + return true; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this + interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( + NegInfInterval!Date(Date(1996, 1, 2)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure nothrow + { + return _begin < interval._end; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect or if the given interval is empty. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); +-------------------- + +/ + Interval!TP intersection(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto begin = _begin > interval._begin ? _begin : interval._begin; + + return Interval!TP(begin, interval._end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1999, 1 , 12))); +-------------------- + +/ + PosInfInterval intersection(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); +-------------------- + +/ + Interval!TP intersection(in NegInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval!TP(_begin, interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + +assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +-------------------- + +/ + bool isAdjacent(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return _begin == interval._end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Always returns false because two intervals going to positive infinity + can never be adjacent to one another. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + PosInfInterval!Date(Date(1990, 1, 7)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + PosInfInterval!Date(Date(1996, 1, 2)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + NegInfInterval!Date(Date(1996, 1, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow + { + return _begin == interval._end; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect and are not adjacent or if the given interval is + empty. + + Note: + There is no overload for $(D merge) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval merge(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Note: + There is no overload for $(D merge) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval merge(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Note: + There is no overload for $(D span) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == + PosInfInterval!Date(Date(500, 8, 9))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval span(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Note: + There is no overload for $(D span) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval span(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Shifts the $(D begin) of this interval forward or backwards in time by + the given duration (a positive duration shifts the interval forward; a + negative duration shifts it backward). Effectively, it does + $(D begin += duration). + + Params: + duration = The duration to shift the interval by. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +-------------------- + +/ + void shift(D)(D duration) pure nothrow + if (__traits(compiles, begin + duration)) + { + _begin += duration; + } + + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Shifts the $(D begin) of this interval forward or backwards in time + by the given number of years and/or months (a positive number of + years and months shifts the interval forward; a negative number + shifts it backward). It adds the years the given years and months to + $(D begin). It effectively calls $(D add!"years"()) and then + $(D add!"months"()) on $(D begin) with the given number of years and + months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin), causing its month to increment. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty or if the resulting interval would be invalid. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto begin = _begin; + + begin.add!"years"(years, allowOverflow); + begin.add!"months"(months, allowOverflow); + + _begin = begin; + } + } + + + /++ + Expands the interval backwards in time. Effectively, it does + $(D begin -= duration). + + Params: + duration = The duration to expand the interval by. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.expand(dur!"days"(2)); +assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + +interval2.expand(dur!"days"(-2)); +assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); +-------------------- + +/ + void expand(D)(D duration) pure nothrow + if (__traits(compiles, begin + duration)) + { + _begin -= duration; + } + + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it subtracts the given number of months/years from $(D begin). + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin), causing its month to increment. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty or if the resulting interval would be invalid. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.expand(2); +assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + +interval2.expand(-2); +assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto begin = _begin; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + + _begin = begin; + } + } + + + /++ + Returns a range which iterates forward over the interval, starting + at $(D begin), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point less than or equal to the + current $(D front) of the range, then a + $(REF DateTimeException,std,datetime,date) will be thrown. + + There are helper functions in this module which generate common + delegates to pass to $(D fwdRange). Their documentation starts with + "Range-generating function," to make them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant for custom delegates. + + Example: +-------------------- +auto interval = PosInfInterval!Date(Date(2010, 9, 1)); +auto func = delegate (in Date date) //For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; +auto range = interval.fwdRange(func); + +//An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). +assert(range.front == Date(2010, 9, 1)); + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(!range.empty); +-------------------- + +/ + PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + auto range = PosInfIntervalRange!(TP)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + +private: + + /+ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + import std.format : format; + try + return format("[%s - ∞)", _begin); + catch (Exception e) + assert(0, "format() threw."); + } + + + TP _begin; +} + +//Test PosInfInterval's constructor. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + PosInfInterval!Date(Date.init); + PosInfInterval!TimeOfDay(TimeOfDay.init); + PosInfInterval!DateTime(DateTime.init); + PosInfInterval!SysTime(SysTime(0)); + + //Verify Examples. + auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +} + +//Test PosInfInterval's begin. +@safe unittest +{ + import std.datetime.date; + + assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); + assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); + assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(cPosInfInterval.begin != Date.init); + assert(iPosInfInterval.begin != Date.init); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); +} + +//Test PosInfInterval's empty. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + + const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cPosInfInterval.empty); + assert(!iPosInfInterval.empty); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); +} + +//Test PosInfInterval's contains(time point). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.contains(Date(2009, 7, 4))); + assert(!posInfInterval.contains(Date(2010, 7, 3))); + assert(posInfInterval.contains(Date(2010, 7, 4))); + assert(posInfInterval.contains(Date(2010, 7, 5))); + assert(posInfInterval.contains(Date(2011, 7, 1))); + assert(posInfInterval.contains(Date(2012, 1, 6))); + assert(posInfInterval.contains(Date(2012, 1, 7))); + assert(posInfInterval.contains(Date(2012, 1, 8))); + assert(posInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(posInfInterval.contains(cdate)); + assert(cPosInfInterval.contains(cdate)); + assert(iPosInfInterval.contains(cdate)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); +} + +//Test PosInfInterval's contains(Interval). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.contains(posInfInterval)); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); + + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(posInfInterval.contains(interval)); + assert(posInfInterval.contains(cInterval)); + assert(posInfInterval.contains(iInterval)); + assert(posInfInterval.contains(posInfInterval)); + assert(posInfInterval.contains(cPosInfInterval)); + assert(posInfInterval.contains(iPosInfInterval)); + assert(!posInfInterval.contains(negInfInterval)); + assert(!posInfInterval.contains(cNegInfInterval)); + assert(!posInfInterval.contains(iNegInfInterval)); + assert(cPosInfInterval.contains(interval)); + assert(cPosInfInterval.contains(cInterval)); + assert(cPosInfInterval.contains(iInterval)); + assert(cPosInfInterval.contains(posInfInterval)); + assert(cPosInfInterval.contains(cPosInfInterval)); + assert(cPosInfInterval.contains(iPosInfInterval)); + assert(!cPosInfInterval.contains(negInfInterval)); + assert(!cPosInfInterval.contains(cNegInfInterval)); + assert(!cPosInfInterval.contains(iNegInfInterval)); + assert(iPosInfInterval.contains(interval)); + assert(iPosInfInterval.contains(cInterval)); + assert(iPosInfInterval.contains(iInterval)); + assert(iPosInfInterval.contains(posInfInterval)); + assert(iPosInfInterval.contains(cPosInfInterval)); + assert(iPosInfInterval.contains(iPosInfInterval)); + assert(!iPosInfInterval.contains(negInfInterval)); + assert(!iPosInfInterval.contains(cNegInfInterval)); + assert(!iPosInfInterval.contains(iNegInfInterval)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test PosInfInterval's isBefore(time point). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.isBefore(Date(2009, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 4))); + assert(!posInfInterval.isBefore(Date(2010, 7, 5))); + assert(!posInfInterval.isBefore(Date(2011, 7, 1))); + assert(!posInfInterval.isBefore(Date(2012, 1, 6))); + assert(!posInfInterval.isBefore(Date(2012, 1, 7))); + assert(!posInfInterval.isBefore(Date(2012, 1, 8))); + assert(!posInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(!posInfInterval.isBefore(cdate)); + assert(!cPosInfInterval.isBefore(cdate)); + assert(!iPosInfInterval.isBefore(cdate)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); +} + +//Test PosInfInterval's isBefore(Interval). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isBefore(posInfInterval)); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); + + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.isBefore(interval)); + assert(!posInfInterval.isBefore(cInterval)); + assert(!posInfInterval.isBefore(iInterval)); + assert(!posInfInterval.isBefore(posInfInterval)); + assert(!posInfInterval.isBefore(cPosInfInterval)); + assert(!posInfInterval.isBefore(iPosInfInterval)); + assert(!posInfInterval.isBefore(negInfInterval)); + assert(!posInfInterval.isBefore(cNegInfInterval)); + assert(!posInfInterval.isBefore(iNegInfInterval)); + assert(!cPosInfInterval.isBefore(interval)); + assert(!cPosInfInterval.isBefore(cInterval)); + assert(!cPosInfInterval.isBefore(iInterval)); + assert(!cPosInfInterval.isBefore(posInfInterval)); + assert(!cPosInfInterval.isBefore(cPosInfInterval)); + assert(!cPosInfInterval.isBefore(iPosInfInterval)); + assert(!cPosInfInterval.isBefore(negInfInterval)); + assert(!cPosInfInterval.isBefore(cNegInfInterval)); + assert(!cPosInfInterval.isBefore(iNegInfInterval)); + assert(!iPosInfInterval.isBefore(interval)); + assert(!iPosInfInterval.isBefore(cInterval)); + assert(!iPosInfInterval.isBefore(iInterval)); + assert(!iPosInfInterval.isBefore(posInfInterval)); + assert(!iPosInfInterval.isBefore(cPosInfInterval)); + assert(!iPosInfInterval.isBefore(iPosInfInterval)); + assert(!iPosInfInterval.isBefore(negInfInterval)); + assert(!iPosInfInterval.isBefore(cNegInfInterval)); + assert(!iPosInfInterval.isBefore(iNegInfInterval)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test PosInfInterval's isAfter(time point). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(posInfInterval.isAfter(Date(2009, 7, 3))); + assert(posInfInterval.isAfter(Date(2010, 7, 3))); + assert(!posInfInterval.isAfter(Date(2010, 7, 4))); + assert(!posInfInterval.isAfter(Date(2010, 7, 5))); + assert(!posInfInterval.isAfter(Date(2011, 7, 1))); + assert(!posInfInterval.isAfter(Date(2012, 1, 6))); + assert(!posInfInterval.isAfter(Date(2012, 1, 7))); + assert(!posInfInterval.isAfter(Date(2012, 1, 8))); + assert(!posInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(!posInfInterval.isAfter(cdate)); + assert(!cPosInfInterval.isAfter(cdate)); + assert(!iPosInfInterval.isAfter(cdate)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); +} + +//Test PosInfInterval's isAfter(Interval). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAfter(posInfInterval)); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); + + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.isAfter(interval)); + assert(!posInfInterval.isAfter(cInterval)); + assert(!posInfInterval.isAfter(iInterval)); + assert(!posInfInterval.isAfter(posInfInterval)); + assert(!posInfInterval.isAfter(cPosInfInterval)); + assert(!posInfInterval.isAfter(iPosInfInterval)); + assert(!posInfInterval.isAfter(negInfInterval)); + assert(!posInfInterval.isAfter(cNegInfInterval)); + assert(!posInfInterval.isAfter(iNegInfInterval)); + assert(!cPosInfInterval.isAfter(interval)); + assert(!cPosInfInterval.isAfter(cInterval)); + assert(!cPosInfInterval.isAfter(iInterval)); + assert(!cPosInfInterval.isAfter(posInfInterval)); + assert(!cPosInfInterval.isAfter(cPosInfInterval)); + assert(!cPosInfInterval.isAfter(iPosInfInterval)); + assert(!cPosInfInterval.isAfter(negInfInterval)); + assert(!cPosInfInterval.isAfter(cNegInfInterval)); + assert(!cPosInfInterval.isAfter(iNegInfInterval)); + assert(!iPosInfInterval.isAfter(interval)); + assert(!iPosInfInterval.isAfter(cInterval)); + assert(!iPosInfInterval.isAfter(iInterval)); + assert(!iPosInfInterval.isAfter(posInfInterval)); + assert(!iPosInfInterval.isAfter(cPosInfInterval)); + assert(!iPosInfInterval.isAfter(iPosInfInterval)); + assert(!iPosInfInterval.isAfter(negInfInterval)); + assert(!iPosInfInterval.isAfter(cNegInfInterval)); + assert(!iPosInfInterval.isAfter(iNegInfInterval)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's intersects(). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.intersects(posInfInterval)); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); + + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(posInfInterval.intersects(interval)); + assert(posInfInterval.intersects(cInterval)); + assert(posInfInterval.intersects(iInterval)); + assert(posInfInterval.intersects(posInfInterval)); + assert(posInfInterval.intersects(cPosInfInterval)); + assert(posInfInterval.intersects(iPosInfInterval)); + assert(posInfInterval.intersects(negInfInterval)); + assert(posInfInterval.intersects(cNegInfInterval)); + assert(posInfInterval.intersects(iNegInfInterval)); + assert(cPosInfInterval.intersects(interval)); + assert(cPosInfInterval.intersects(cInterval)); + assert(cPosInfInterval.intersects(iInterval)); + assert(cPosInfInterval.intersects(posInfInterval)); + assert(cPosInfInterval.intersects(cPosInfInterval)); + assert(cPosInfInterval.intersects(iPosInfInterval)); + assert(cPosInfInterval.intersects(negInfInterval)); + assert(cPosInfInterval.intersects(cNegInfInterval)); + assert(cPosInfInterval.intersects(iNegInfInterval)); + assert(iPosInfInterval.intersects(interval)); + assert(iPosInfInterval.intersects(cInterval)); + assert(iPosInfInterval.intersects(iInterval)); + assert(iPosInfInterval.intersects(posInfInterval)); + assert(iPosInfInterval.intersects(cPosInfInterval)); + assert(iPosInfInterval.intersects(iPosInfInterval)); + assert(iPosInfInterval.intersects(negInfInterval)); + assert(iPosInfInterval.intersects(cNegInfInterval)); + assert(iPosInfInterval.intersects(iNegInfInterval)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's intersection(). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); + + assert(posInfInterval.intersection(posInfInterval) == posInfInterval); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); + + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 5))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2012, 1, 6))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2012, 1, 7))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2012, 1, 8))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 5))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 6))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 7))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 8))); + + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.intersection(interval).empty); + assert(!posInfInterval.intersection(cInterval).empty); + assert(!posInfInterval.intersection(iInterval).empty); + assert(!posInfInterval.intersection(posInfInterval).empty); + assert(!posInfInterval.intersection(cPosInfInterval).empty); + assert(!posInfInterval.intersection(iPosInfInterval).empty); + assert(!posInfInterval.intersection(negInfInterval).empty); + assert(!posInfInterval.intersection(cNegInfInterval).empty); + assert(!posInfInterval.intersection(iNegInfInterval).empty); + assert(!cPosInfInterval.intersection(interval).empty); + assert(!cPosInfInterval.intersection(cInterval).empty); + assert(!cPosInfInterval.intersection(iInterval).empty); + assert(!cPosInfInterval.intersection(posInfInterval).empty); + assert(!cPosInfInterval.intersection(cPosInfInterval).empty); + assert(!cPosInfInterval.intersection(iPosInfInterval).empty); + assert(!cPosInfInterval.intersection(negInfInterval).empty); + assert(!cPosInfInterval.intersection(cNegInfInterval).empty); + assert(!cPosInfInterval.intersection(iNegInfInterval).empty); + assert(!iPosInfInterval.intersection(interval).empty); + assert(!iPosInfInterval.intersection(cInterval).empty); + assert(!iPosInfInterval.intersection(iInterval).empty); + assert(!iPosInfInterval.intersection(posInfInterval).empty); + assert(!iPosInfInterval.intersection(cPosInfInterval).empty); + assert(!iPosInfInterval.intersection(iPosInfInterval).empty); + assert(!iPosInfInterval.intersection(negInfInterval).empty); + assert(!iPosInfInterval.intersection(cNegInfInterval).empty); + assert(!iPosInfInterval.intersection(iNegInfInterval).empty); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1, 2), Date(2000, 8, 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1996, 1, 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1999, 1, 12))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1, 2), Date(1999, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1, 2), Date(2013, 1, 12))); +} + +//Test PosInfInterval's isAdjacent(). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAdjacent(posInfInterval)); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); + + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.isAdjacent(interval)); + assert(!posInfInterval.isAdjacent(cInterval)); + assert(!posInfInterval.isAdjacent(iInterval)); + assert(!posInfInterval.isAdjacent(posInfInterval)); + assert(!posInfInterval.isAdjacent(cPosInfInterval)); + assert(!posInfInterval.isAdjacent(iPosInfInterval)); + assert(!posInfInterval.isAdjacent(negInfInterval)); + assert(!posInfInterval.isAdjacent(cNegInfInterval)); + assert(!posInfInterval.isAdjacent(iNegInfInterval)); + assert(!cPosInfInterval.isAdjacent(interval)); + assert(!cPosInfInterval.isAdjacent(cInterval)); + assert(!cPosInfInterval.isAdjacent(iInterval)); + assert(!cPosInfInterval.isAdjacent(posInfInterval)); + assert(!cPosInfInterval.isAdjacent(cPosInfInterval)); + assert(!cPosInfInterval.isAdjacent(iPosInfInterval)); + assert(!cPosInfInterval.isAdjacent(negInfInterval)); + assert(!cPosInfInterval.isAdjacent(cNegInfInterval)); + assert(!cPosInfInterval.isAdjacent(iNegInfInterval)); + assert(!iPosInfInterval.isAdjacent(interval)); + assert(!iPosInfInterval.isAdjacent(cInterval)); + assert(!iPosInfInterval.isAdjacent(iInterval)); + assert(!iPosInfInterval.isAdjacent(posInfInterval)); + assert(!iPosInfInterval.isAdjacent(cPosInfInterval)); + assert(!iPosInfInterval.isAdjacent(iPosInfInterval)); + assert(!iPosInfInterval.isAdjacent(negInfInterval)); + assert(!iPosInfInterval.isAdjacent(cNegInfInterval)); + assert(!iPosInfInterval.isAdjacent(iNegInfInterval)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's merge(). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.merge(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + + assert(posInfInterval.merge(posInfInterval) == posInfInterval); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.merge(interval).empty); + assert(!posInfInterval.merge(cInterval).empty); + assert(!posInfInterval.merge(iInterval).empty); + assert(!posInfInterval.merge(posInfInterval).empty); + assert(!posInfInterval.merge(cPosInfInterval).empty); + assert(!posInfInterval.merge(iPosInfInterval).empty); + static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); + assert(!cPosInfInterval.merge(interval).empty); + assert(!cPosInfInterval.merge(cInterval).empty); + assert(!cPosInfInterval.merge(iInterval).empty); + assert(!cPosInfInterval.merge(posInfInterval).empty); + assert(!cPosInfInterval.merge(cPosInfInterval).empty); + assert(!cPosInfInterval.merge(iPosInfInterval).empty); + static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); + assert(!iPosInfInterval.merge(interval).empty); + assert(!iPosInfInterval.merge(cInterval).empty); + assert(!iPosInfInterval.merge(iInterval).empty); + assert(!iPosInfInterval.merge(posInfInterval).empty); + assert(!iPosInfInterval.merge(cPosInfInterval).empty); + assert(!iPosInfInterval.merge(iPosInfInterval).empty); + static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1, 2))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1, 2))); +} + +//Test PosInfInterval's span(). +@safe unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.span(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.span(posInfInterval) == posInfInterval); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.span(interval).empty); + assert(!posInfInterval.span(cInterval).empty); + assert(!posInfInterval.span(iInterval).empty); + assert(!posInfInterval.span(posInfInterval).empty); + assert(!posInfInterval.span(cPosInfInterval).empty); + assert(!posInfInterval.span(iPosInfInterval).empty); + static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); + assert(!cPosInfInterval.span(interval).empty); + assert(!cPosInfInterval.span(cInterval).empty); + assert(!cPosInfInterval.span(iInterval).empty); + assert(!cPosInfInterval.span(posInfInterval).empty); + assert(!cPosInfInterval.span(cPosInfInterval).empty); + assert(!cPosInfInterval.span(iPosInfInterval).empty); + static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); + assert(!iPosInfInterval.span(interval).empty); + assert(!iPosInfInterval.span(cInterval).empty); + assert(!iPosInfInterval.span(iInterval).empty); + assert(!iPosInfInterval.span(posInfInterval).empty); + assert(!iPosInfInterval.span(cPosInfInterval).empty); + assert(!iPosInfInterval.span(iPosInfInterval).empty); + static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == + PosInfInterval!Date(Date(500, 8, 9))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1, 2))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1, 2))); +} + +//Test PosInfInterval's shift(). +@safe unittest +{ + import std.datetime.date; + + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); + + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +} + +//Test PosInfInterval's shift(int, int, AllowDayOverflow). +@safe unittest +{ + import std.datetime.date; + + { + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + } + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.shift(1))); + static assert(!__traits(compiles, iPosInfInterval.shift(1))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.shift(2); + assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); + + interval2.shift(-2); + assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); +} + +//Test PosInfInterval's expand(). +@safe unittest +{ + import std.datetime.date; + + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); + + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); +} + +//Test PosInfInterval's expand(int, int, AllowDayOverflow). +@safe unittest +{ + import std.datetime.date; + + { + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + } + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.expand(1))); + static assert(!__traits(compiles, iPosInfInterval.expand(1))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.expand(2); + assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + + interval2.expand(-2); + assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); +} + +//Test PosInfInterval's fwdRange(). +@system unittest +{ + import std.datetime.date; + + auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); + + static void testInterval(PosInfInterval!Date posInfInterval) + { + posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval(posInfInterval)); + + assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == + Date(2010, 9, 12)); + + assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == + Date(2010, 9, 17)); + + //Verify Examples. + auto interval = PosInfInterval!Date(Date(2010, 9, 1)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(!range.empty); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(!cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(!iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); +} + +//Test PosInfInterval's toString(). +@safe unittest +{ + import std.datetime.date; + assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(cPosInfInterval.toString()); + assert(iPosInfInterval.toString()); +} + + +/++ + Represents an interval of time which has negative infinity as its starting + point. + + Any ranges which iterate over a $(D NegInfInterval) are infinite. So, the + main purpose of using $(D NegInfInterval) is to create an infinite range + which starts at negative infinity and goes to a fixed end point. + Iterate over it in reverse. + +/ +struct NegInfInterval(TP) +{ +public: + + /++ + Params: + end = The time point which ends the interval. + + Example: +-------------------- +auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +-------------------- + +/ + this(in TP end) pure nothrow + { + _end = cast(TP) end; + } + + + /++ + Params: + rhs = The $(D NegInfInterval) to assign to this one. + +/ + ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow + { + _end = cast(TP) rhs._end; + return this; + } + + + /++ + Params: + rhs = The $(D NegInfInterval) to assign to this one. + +/ + ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow + { + _end = cast(TP) rhs._end; + return this; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); +-------------------- + +/ + @property TP end() const pure nothrow + { + return cast(TP)_end; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Params: + timePoint = The time point to set end to. + +/ + @property void end(TP timePoint) pure nothrow + { + _end = timePoint; + } + + + /++ + Whether the interval's length is 0. Always returns false. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); +-------------------- + +/ + enum bool empty = false; + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +-------------------- + +/ + bool contains(TP timePoint) const pure nothrow + { + return timePoint < _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._end <= _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false because an interval beginning at negative + infinity can never contain an interval going to positive infinity. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( + PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool contains(in PosInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool contains(in NegInfInterval interval) const pure nothrow + { + return interval._end <= _end; + } + + + /++ + Whether this interval is before the given time point. + + Params: + timePoint = The time point to check whether this interval is + before it. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure nothrow + { + return timePoint >= _end; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isBefore(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isBefore(in PosInfInterval!TP interval) const pure nothrow + { + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false because an interval beginning at negative + infinity can never be before another interval beginning at negative + infinity. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool isBefore(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given time point. + + Always returns false because an interval beginning at negative infinity + can never be after any time point. + + Params: + timePoint = The time point to check whether this interval is after + it. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not + intersect it. + + Always returns false (unless the given interval is empty) because an + interval beginning at negative infinity can never be after any other + interval. + + Params: + interval = The interval to check against this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isAfter(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false because an interval beginning at negative infinity + can never be after any other interval. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAfter(in PosInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false because an interval beginning at negative infinity + can never be after any other interval. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool isAfter(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool intersects(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._begin < _end; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this + interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool intersects(in PosInfInterval!TP interval) const pure nothrow + { + return interval._begin < _end; + } + + + /++ + Whether the given interval overlaps this interval. + + Always returns true because two intervals beginning at negative infinity + always overlap. + + Params: + interval = The interval to check for intersection with this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure nothrow + { + return true; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect or if the given interval is empty. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval!TP intersection(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto end = _end < interval._end ? _end : interval._end; + + return Interval!TP(interval._begin, end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval!TP intersection(in PosInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval!TP(interval._begin, _end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(1999, 7 , 6))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2012, 3 , 1))); +-------------------- + +/ + NegInfInterval intersection(in NegInfInterval interval) const nothrow + { + return NegInfInterval(_end < interval._end ? _end : interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isAdjacent(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._begin == _end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow + { + return interval._begin == _end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Always returns false because two intervals beginning at negative + infinity can never be adjacent to one another. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the two intervals do + not intersect and are not adjacent or if the given interval is empty. + + Note: + There is no overload for $(D merge) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9 , 2))); +-------------------- + +/ + NegInfInterval merge(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Note: + There is no overload for $(D merge) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval merge(in NegInfInterval interval) const pure nothrow + { + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given interval + is empty. + + Note: + There is no overload for $(D span) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9 , 2))); + +assert(NegInfInterval!Date(Date(1600, 1, 7)).span( + Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == + NegInfInterval!Date(Date(2017, 7 , 1))); +-------------------- + +/ + NegInfInterval span(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Note: + There is no overload for $(D span) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval span(in NegInfInterval interval) const pure nothrow + { + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Shifts the $(D end) of this interval forward or backwards in time by the + given duration (a positive duration shifts the interval forward; a + negative duration shifts it backward). Effectively, it does + $(D end += duration). + + Params: + duration = The duration to shift the interval by. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); +auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); +-------------------- + +/ + void shift(D)(D duration) pure nothrow + if (__traits(compiles, end + duration)) + { + _end += duration; + } + + + static if (__traits(compiles, end.add!"months"(1)) && + __traits(compiles, end.add!"years"(1))) + { + /++ + Shifts the $(D end) of this interval forward or backwards in time by + the given number of years and/or months (a positive number of years + and months shifts the interval forward; a negative number shifts it + backward). It adds the years the given years and months to end. It + effectively calls $(D add!"years"()) and then $(D add!"months"()) + on end with the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D end), causing its month to increment. + + Throws: + $(REF DateTimeException,std,datetime,date) if empty is true or + if the resulting interval would be invalid. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.shift(2); +assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + +interval2.shift(-2); +assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + _end = end; + } + } + + + /++ + Expands the interval forwards in time. Effectively, it does + $(D end += duration). + + Params: + duration = The duration to expand the interval by. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.expand(dur!"days"(2)); +assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + +interval2.expand(dur!"days"(-2)); +assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); +-------------------- + +/ + void expand(D)(D duration) pure nothrow + if (__traits(compiles, end + duration)) + { + _end += duration; + } + + + static if (__traits(compiles, end.add!"months"(1)) && + __traits(compiles, end.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it adds the given number of months/years to end. + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D end), causing their month to increment. + + Throws: + $(REF DateTimeException,std,datetime,date) if empty is true or + if the resulting interval would be invalid. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.expand(2); +assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + +interval2.expand(-2); +assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + _end = end; + } + } + + + /++ + Returns a range which iterates backwards over the interval, starting + at $(D end), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D end). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point greater than or equal to + the current $(D front) of the range, then a + $(REF DateTimeException,std,datetime,date) will be thrown. + + There are helper functions in this module which generate common + delegates to pass to $(D bwdRange). Their documentation starts with + "Range-generating function," to make them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant for custom delegates. + + Example: +-------------------- +auto interval = NegInfInterval!Date(Date(2010, 9, 9)); +auto func = delegate (in Date date) //For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; +auto range = interval.bwdRange(func); + +assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(!range.empty); +-------------------- + +/ + NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + auto range = NegInfIntervalRange!(TP)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + +private: + + /+ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + import std.format : format; + try + return format("[-∞ - %s)", _end); + catch (Exception e) + assert(0, "format() threw."); + } + + + TP _end; +} + +//Test NegInfInterval's constructor. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + NegInfInterval!Date(Date.init); + NegInfInterval!TimeOfDay(TimeOfDay.init); + NegInfInterval!DateTime(DateTime.init); + NegInfInterval!SysTime(SysTime(0)); +} + +//Test NegInfInterval's end. +@safe unittest +{ + import std.datetime.date; + + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(cNegInfInterval.end != Date.init); + assert(iNegInfInterval.end != Date.init); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); +} + +//Test NegInfInterval's empty. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!cNegInfInterval.empty); + assert(!iNegInfInterval.empty); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); +} + +//Test NegInfInterval's contains(time point). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(negInfInterval.contains(Date(2009, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 3))); + assert(negInfInterval.contains(Date(2010, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 5))); + assert(negInfInterval.contains(Date(2011, 7, 1))); + assert(negInfInterval.contains(Date(2012, 1, 6))); + assert(!negInfInterval.contains(Date(2012, 1, 7))); + assert(!negInfInterval.contains(Date(2012, 1, 8))); + assert(!negInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(negInfInterval.contains(cdate)); + assert(cNegInfInterval.contains(cdate)); + assert(iNegInfInterval.contains(cdate)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +} + +//Test NegInfInterval's contains(Interval). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.contains(negInfInterval)); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); + + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(negInfInterval.contains(interval)); + assert(negInfInterval.contains(cInterval)); + assert(negInfInterval.contains(iInterval)); + assert(!negInfInterval.contains(posInfInterval)); + assert(!negInfInterval.contains(cPosInfInterval)); + assert(!negInfInterval.contains(iPosInfInterval)); + assert(negInfInterval.contains(negInfInterval)); + assert(negInfInterval.contains(cNegInfInterval)); + assert(negInfInterval.contains(iNegInfInterval)); + assert(cNegInfInterval.contains(interval)); + assert(cNegInfInterval.contains(cInterval)); + assert(cNegInfInterval.contains(iInterval)); + assert(!cNegInfInterval.contains(posInfInterval)); + assert(!cNegInfInterval.contains(cPosInfInterval)); + assert(!cNegInfInterval.contains(iPosInfInterval)); + assert(cNegInfInterval.contains(negInfInterval)); + assert(cNegInfInterval.contains(cNegInfInterval)); + assert(cNegInfInterval.contains(iNegInfInterval)); + assert(iNegInfInterval.contains(interval)); + assert(iNegInfInterval.contains(cInterval)); + assert(iNegInfInterval.contains(iInterval)); + assert(!iNegInfInterval.contains(posInfInterval)); + assert(!iNegInfInterval.contains(cPosInfInterval)); + assert(!iNegInfInterval.contains(iPosInfInterval)); + assert(iNegInfInterval.contains(negInfInterval)); + assert(iNegInfInterval.contains(cNegInfInterval)); + assert(iNegInfInterval.contains(iNegInfInterval)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's isBefore(time point). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isBefore(Date(2009, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 3))); + assert(!negInfInterval.isBefore(Date(2010, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 5))); + assert(!negInfInterval.isBefore(Date(2011, 7, 1))); + assert(!negInfInterval.isBefore(Date(2012, 1, 6))); + assert(negInfInterval.isBefore(Date(2012, 1, 7))); + assert(negInfInterval.isBefore(Date(2012, 1, 8))); + assert(negInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isBefore(cdate)); + assert(!cNegInfInterval.isBefore(cdate)); + assert(!iNegInfInterval.isBefore(cdate)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +} + +//Test NegInfInterval's isBefore(Interval). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isBefore(negInfInterval)); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); + + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isBefore(interval)); + assert(!negInfInterval.isBefore(cInterval)); + assert(!negInfInterval.isBefore(iInterval)); + assert(!negInfInterval.isBefore(posInfInterval)); + assert(!negInfInterval.isBefore(cPosInfInterval)); + assert(!negInfInterval.isBefore(iPosInfInterval)); + assert(!negInfInterval.isBefore(negInfInterval)); + assert(!negInfInterval.isBefore(cNegInfInterval)); + assert(!negInfInterval.isBefore(iNegInfInterval)); + assert(!cNegInfInterval.isBefore(interval)); + assert(!cNegInfInterval.isBefore(cInterval)); + assert(!cNegInfInterval.isBefore(iInterval)); + assert(!cNegInfInterval.isBefore(posInfInterval)); + assert(!cNegInfInterval.isBefore(cPosInfInterval)); + assert(!cNegInfInterval.isBefore(iPosInfInterval)); + assert(!cNegInfInterval.isBefore(negInfInterval)); + assert(!cNegInfInterval.isBefore(cNegInfInterval)); + assert(!cNegInfInterval.isBefore(iNegInfInterval)); + assert(!iNegInfInterval.isBefore(interval)); + assert(!iNegInfInterval.isBefore(cInterval)); + assert(!iNegInfInterval.isBefore(iInterval)); + assert(!iNegInfInterval.isBefore(posInfInterval)); + assert(!iNegInfInterval.isBefore(cPosInfInterval)); + assert(!iNegInfInterval.isBefore(iPosInfInterval)); + assert(!iNegInfInterval.isBefore(negInfInterval)); + assert(!iNegInfInterval.isBefore(cNegInfInterval)); + assert(!iNegInfInterval.isBefore(iNegInfInterval)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's isAfter(time point). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isAfter(Date(2009, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 3))); + assert(!negInfInterval.isAfter(Date(2010, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 5))); + assert(!negInfInterval.isAfter(Date(2011, 7, 1))); + assert(!negInfInterval.isAfter(Date(2012, 1, 6))); + assert(!negInfInterval.isAfter(Date(2012, 1, 7))); + assert(!negInfInterval.isAfter(Date(2012, 1, 8))); + assert(!negInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isAfter(cdate)); + assert(!cNegInfInterval.isAfter(cdate)); + assert(!iNegInfInterval.isAfter(cdate)); +} + +//Test NegInfInterval's isAfter(Interval). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAfter(negInfInterval)); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); + + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isAfter(interval)); + assert(!negInfInterval.isAfter(cInterval)); + assert(!negInfInterval.isAfter(iInterval)); + assert(!negInfInterval.isAfter(posInfInterval)); + assert(!negInfInterval.isAfter(cPosInfInterval)); + assert(!negInfInterval.isAfter(iPosInfInterval)); + assert(!negInfInterval.isAfter(negInfInterval)); + assert(!negInfInterval.isAfter(cNegInfInterval)); + assert(!negInfInterval.isAfter(iNegInfInterval)); + assert(!cNegInfInterval.isAfter(interval)); + assert(!cNegInfInterval.isAfter(cInterval)); + assert(!cNegInfInterval.isAfter(iInterval)); + assert(!cNegInfInterval.isAfter(posInfInterval)); + assert(!cNegInfInterval.isAfter(cPosInfInterval)); + assert(!cNegInfInterval.isAfter(iPosInfInterval)); + assert(!cNegInfInterval.isAfter(negInfInterval)); + assert(!cNegInfInterval.isAfter(cNegInfInterval)); + assert(!cNegInfInterval.isAfter(iNegInfInterval)); + assert(!iNegInfInterval.isAfter(interval)); + assert(!iNegInfInterval.isAfter(cInterval)); + assert(!iNegInfInterval.isAfter(iInterval)); + assert(!iNegInfInterval.isAfter(posInfInterval)); + assert(!iNegInfInterval.isAfter(cPosInfInterval)); + assert(!iNegInfInterval.isAfter(iPosInfInterval)); + assert(!iNegInfInterval.isAfter(negInfInterval)); + assert(!iNegInfInterval.isAfter(cNegInfInterval)); + assert(!iNegInfInterval.isAfter(iNegInfInterval)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's intersects(). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.intersects(negInfInterval)); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); + + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(negInfInterval.intersects(interval)); + assert(negInfInterval.intersects(cInterval)); + assert(negInfInterval.intersects(iInterval)); + assert(negInfInterval.intersects(posInfInterval)); + assert(negInfInterval.intersects(cPosInfInterval)); + assert(negInfInterval.intersects(iPosInfInterval)); + assert(negInfInterval.intersects(negInfInterval)); + assert(negInfInterval.intersects(cNegInfInterval)); + assert(negInfInterval.intersects(iNegInfInterval)); + assert(cNegInfInterval.intersects(interval)); + assert(cNegInfInterval.intersects(cInterval)); + assert(cNegInfInterval.intersects(iInterval)); + assert(cNegInfInterval.intersects(posInfInterval)); + assert(cNegInfInterval.intersects(cPosInfInterval)); + assert(cNegInfInterval.intersects(iPosInfInterval)); + assert(cNegInfInterval.intersects(negInfInterval)); + assert(cNegInfInterval.intersects(cNegInfInterval)); + assert(cNegInfInterval.intersects(iNegInfInterval)); + assert(iNegInfInterval.intersects(interval)); + assert(iNegInfInterval.intersects(cInterval)); + assert(iNegInfInterval.intersects(iInterval)); + assert(iNegInfInterval.intersects(posInfInterval)); + assert(iNegInfInterval.intersects(cPosInfInterval)); + assert(iNegInfInterval.intersects(iPosInfInterval)); + assert(iNegInfInterval.intersects(negInfInterval)); + assert(iNegInfInterval.intersects(cNegInfInterval)); + assert(iNegInfInterval.intersects(iNegInfInterval)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's intersection(). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(negInfInterval.intersection(negInfInterval) == negInfInterval); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2010, 7, 3))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2010, 7, 4))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2010, 7, 5))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 6))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 7))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 3))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 4))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 5))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 6))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.intersection(interval).empty); + assert(!negInfInterval.intersection(cInterval).empty); + assert(!negInfInterval.intersection(iInterval).empty); + assert(!negInfInterval.intersection(posInfInterval).empty); + assert(!negInfInterval.intersection(cPosInfInterval).empty); + assert(!negInfInterval.intersection(iPosInfInterval).empty); + assert(!negInfInterval.intersection(negInfInterval).empty); + assert(!negInfInterval.intersection(cNegInfInterval).empty); + assert(!negInfInterval.intersection(iNegInfInterval).empty); + assert(!cNegInfInterval.intersection(interval).empty); + assert(!cNegInfInterval.intersection(cInterval).empty); + assert(!cNegInfInterval.intersection(iInterval).empty); + assert(!cNegInfInterval.intersection(posInfInterval).empty); + assert(!cNegInfInterval.intersection(cPosInfInterval).empty); + assert(!cNegInfInterval.intersection(iPosInfInterval).empty); + assert(!cNegInfInterval.intersection(negInfInterval).empty); + assert(!cNegInfInterval.intersection(cNegInfInterval).empty); + assert(!cNegInfInterval.intersection(iNegInfInterval).empty); + assert(!iNegInfInterval.intersection(interval).empty); + assert(!iNegInfInterval.intersection(cInterval).empty); + assert(!iNegInfInterval.intersection(iInterval).empty); + assert(!iNegInfInterval.intersection(posInfInterval).empty); + assert(!iNegInfInterval.intersection(cPosInfInterval).empty); + assert(!iNegInfInterval.intersection(iPosInfInterval).empty); + assert(!iNegInfInterval.intersection(negInfInterval).empty); + assert(!iNegInfInterval.intersection(cNegInfInterval).empty); + assert(!iNegInfInterval.intersection(iNegInfInterval).empty); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1990, 7, 6), Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(1999, 7, 6))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2012, 3, 1))); +} + +//Test NegInfInterval's isAdjacent(). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAdjacent(negInfInterval)); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); + + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isAdjacent(interval)); + assert(!negInfInterval.isAdjacent(cInterval)); + assert(!negInfInterval.isAdjacent(iInterval)); + assert(!negInfInterval.isAdjacent(posInfInterval)); + assert(!negInfInterval.isAdjacent(cPosInfInterval)); + assert(!negInfInterval.isAdjacent(iPosInfInterval)); + assert(!negInfInterval.isAdjacent(negInfInterval)); + assert(!negInfInterval.isAdjacent(cNegInfInterval)); + assert(!negInfInterval.isAdjacent(iNegInfInterval)); + assert(!cNegInfInterval.isAdjacent(interval)); + assert(!cNegInfInterval.isAdjacent(cInterval)); + assert(!cNegInfInterval.isAdjacent(iInterval)); + assert(!cNegInfInterval.isAdjacent(posInfInterval)); + assert(!cNegInfInterval.isAdjacent(cPosInfInterval)); + assert(!cNegInfInterval.isAdjacent(iPosInfInterval)); + assert(!cNegInfInterval.isAdjacent(negInfInterval)); + assert(!cNegInfInterval.isAdjacent(cNegInfInterval)); + assert(!cNegInfInterval.isAdjacent(iNegInfInterval)); + assert(!iNegInfInterval.isAdjacent(interval)); + assert(!iNegInfInterval.isAdjacent(cInterval)); + assert(!iNegInfInterval.isAdjacent(iInterval)); + assert(!iNegInfInterval.isAdjacent(posInfInterval)); + assert(!iNegInfInterval.isAdjacent(cPosInfInterval)); + assert(!iNegInfInterval.isAdjacent(iPosInfInterval)); + assert(!iNegInfInterval.isAdjacent(negInfInterval)); + assert(!iNegInfInterval.isAdjacent(cNegInfInterval)); + assert(!iNegInfInterval.isAdjacent(iNegInfInterval)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); +} + +//Test NegInfInterval's merge(). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.merge(negInfInterval) == negInfInterval); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + NegInfInterval!Date(Date(2013, 7, 3))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.merge(interval).empty); + assert(!negInfInterval.merge(cInterval).empty); + assert(!negInfInterval.merge(iInterval).empty); + static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); + assert(!negInfInterval.merge(negInfInterval).empty); + assert(!negInfInterval.merge(cNegInfInterval).empty); + assert(!negInfInterval.merge(iNegInfInterval).empty); + assert(!cNegInfInterval.merge(interval).empty); + assert(!cNegInfInterval.merge(cInterval).empty); + assert(!cNegInfInterval.merge(iInterval).empty); + static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); + assert(!cNegInfInterval.merge(negInfInterval).empty); + assert(!cNegInfInterval.merge(cNegInfInterval).empty); + assert(!cNegInfInterval.merge(iNegInfInterval).empty); + assert(!iNegInfInterval.merge(interval).empty); + assert(!iNegInfInterval.merge(cInterval).empty); + assert(!iNegInfInterval.merge(iInterval).empty); + static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); + assert(!iNegInfInterval.merge(negInfInterval).empty); + assert(!iNegInfInterval.merge(cNegInfInterval).empty); + assert(!iNegInfInterval.merge(iNegInfInterval).empty); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9, 2))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1, 12))); +} + +//Test NegInfInterval's span(). +@safe unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.span(negInfInterval) == negInfInterval); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + NegInfInterval!Date(Date(2013, 7, 3))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + NegInfInterval!Date(Date(2012, 1, 9))); + + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.span(interval).empty); + assert(!negInfInterval.span(cInterval).empty); + assert(!negInfInterval.span(iInterval).empty); + static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); + assert(!negInfInterval.span(negInfInterval).empty); + assert(!negInfInterval.span(cNegInfInterval).empty); + assert(!negInfInterval.span(iNegInfInterval).empty); + assert(!cNegInfInterval.span(interval).empty); + assert(!cNegInfInterval.span(cInterval).empty); + assert(!cNegInfInterval.span(iInterval).empty); + static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); + assert(!cNegInfInterval.span(negInfInterval).empty); + assert(!cNegInfInterval.span(cNegInfInterval).empty); + assert(!cNegInfInterval.span(iNegInfInterval).empty); + assert(!iNegInfInterval.span(interval).empty); + assert(!iNegInfInterval.span(cInterval).empty); + assert(!iNegInfInterval.span(iInterval).empty); + static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); + assert(!iNegInfInterval.span(negInfInterval).empty); + assert(!iNegInfInterval.span(cNegInfInterval).empty); + assert(!iNegInfInterval.span(iNegInfInterval).empty); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9, 2))); + assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == + NegInfInterval!Date(Date(2017, 7, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1, 12))); +} + +//Test NegInfInterval's shift(). +@safe unittest +{ + import std.datetime.date; + + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); + auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); +} + +//Test NegInfInterval's shift(int, int, AllowDayOverflow). +@safe unittest +{ + import std.datetime.date; + + { + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testIntervalFail(I)(I interval, int years, int months) + { + interval.shift(years, months); + } + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); + } + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.shift(1))); + static assert(!__traits(compiles, iNegInfInterval.shift(1))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +} + +//Test NegInfInterval's expand(). +@safe unittest +{ + import std.datetime.date; + + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); +} + +//Test NegInfInterval's expand(int, int, AllowDayOverflow). +@safe unittest +{ + import std.datetime.date; + + { + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); + } + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.expand(1))); + static assert(!__traits(compiles, iNegInfInterval.expand(1))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +} + +//Test NegInfInterval's bwdRange(). +@system unittest +{ + import std.datetime.date; + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(NegInfInterval!Date negInfInterval) + { + negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval(negInfInterval)); + + assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == + Date(2010, 10, 1)); + + assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 24)); + + //Verify Examples. + auto interval = NegInfInterval!Date(Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(!range.empty); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); + assert(!iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); +} + +//Test NegInfInterval's toString(). +@safe unittest +{ + import std.datetime.date; + + assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(cNegInfInterval.toString()); + assert(iNegInfInterval.toString()); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point with the given + $(D DayOfWeek) in a range. + + Using this delegate allows iteration over successive time points which + are all the same day of the week. e.g. passing $(D DayOfWeek.mon) to + $(D everyDayOfWeek) would result in a delegate which could be used to + iterate over all of the Mondays in a range. + + Params: + dir = The direction to iterate in. If passing the return value to + $(D fwdRange), use $(D Direction.fwd). If passing it to + $(D bwdRange), use $(D Direction.bwd). + dayOfWeek = The week that each time point in the range will be. + +/ +TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow +if (isTimePoint!TP && + (dir == Direction.fwd || dir == Direction.bwd) && + __traits(hasMember, TP, "dayOfWeek") && + !__traits(isStaticFunction, TP.dayOfWeek) && + is(typeof(TP.dayOfWeek) == DayOfWeek)) +{ + TP func(in TP tp) + { + TP retval = cast(TP) tp; + immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); + + static if (dir == Direction.fwd) + immutable adjustedDays = days == 0 ? 7 : days; + else + immutable adjustedDays = days == 0 ? -7 : days - 7; + + return retval += dur!"days"(adjustedDays); + } + + return &func; +} + +/// +@system unittest +{ + import std.datetime.date : Date, DayOfWeek; + + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDayOfWeek!Date(DayOfWeek.mon); + auto range = interval.fwdRange(func); + + // A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 13)); + + range.popFront(); + assert(range.front == Date(2010, 9, 20)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + import std.datetime.date; + import std.datetime.systime; + + auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); + auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); + + assert(funcFwd(Date(2010, 8, 28)) == Date(2010, 8, 30)); + assert(funcFwd(Date(2010, 8, 29)) == Date(2010, 8, 30)); + assert(funcFwd(Date(2010, 8, 30)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 8, 31)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 1)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 2)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 3)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 4)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 5)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 6)) == Date(2010, 9, 13)); + assert(funcFwd(Date(2010, 9, 7)) == Date(2010, 9, 13)); + + assert(funcBwd(Date(2010, 8, 28)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 29)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 30)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 1)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 2)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 3)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 4)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 5)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); + + static assert(!__traits(compiles, everyDayOfWeek!TimeOfDay(DayOfWeek.mon))); + assert(everyDayOfWeek!DateTime(DayOfWeek.mon) !is null); + assert(everyDayOfWeek!SysTime(DayOfWeek.mon) !is null); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point with the given month + which would be reached by adding months to the given time point. + + So, using this delegate allows iteration over successive time points + which are in the same month but different years. For example, + iterate over each successive December 25th in an interval by starting with a + date which had the 25th as its day and passed $(D Month.dec) to + $(D everyMonth) to create the delegate. + + Since it wouldn't really make sense to be iterating over a specific month + and end up with some of the time points in the succeeding month or two years + after the previous time point, $(D AllowDayOverflow.no) is always used when + calculating the next time point. + + Params: + dir = The direction to iterate in. If passing the return value to + $(D fwdRange), use $(D Direction.fwd). If passing it to + $(D bwdRange), use $(D Direction.bwd). + month = The month that each time point in the range will be in. + +/ +TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) +if (isTimePoint!TP && + (dir == Direction.fwd || dir == Direction.bwd) && + __traits(hasMember, TP, "month") && + !__traits(isStaticFunction, TP.month) && + is(typeof(TP.month) == Month)) +{ + import std.datetime.date : enforceValid, monthsToMonth; + + enforceValid!"months"(month); + + TP func(in TP tp) + { + TP retval = cast(TP) tp; + immutable months = monthsToMonth(retval.month, month); + + static if (dir == Direction.fwd) + immutable adjustedMonths = months == 0 ? 12 : months; + else + immutable adjustedMonths = months == 0 ? -12 : months - 12; + + retval.add!"months"(adjustedMonths, AllowDayOverflow.no); + + if (retval.month != month) + { + retval.add!"months"(-1); + assert(retval.month == month); + } + + return retval; + } + + return &func; +} + +/// +@system unittest +{ + import std.datetime.date : Date, Month; + + auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); + auto func = everyMonth!Date(Month.feb); + auto range = interval.fwdRange(func); + + // Using PopFirst.yes would have made this Date(2010, 2, 29). + assert(range.front == Date(2000, 1, 30)); + + range.popFront(); + assert(range.front == Date(2000, 2, 29)); + + range.popFront(); + assert(range.front == Date(2001, 2, 28)); + + range.popFront(); + assert(range.front == Date(2002, 2, 28)); + + range.popFront(); + assert(range.front == Date(2003, 2, 28)); + + range.popFront(); + assert(range.front == Date(2004, 2, 28)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + import std.datetime.date; + import std.datetime.systime; + + auto funcFwd = everyMonth!Date(Month.jun); + auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); + + assert(funcFwd(Date(2010, 5, 31)) == Date(2010, 6, 30)); + assert(funcFwd(Date(2010, 6, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 7, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 8, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 9, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 10, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 11, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 12, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 1, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 2, 28)) == Date(2011, 6, 28)); + assert(funcFwd(Date(2011, 3, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 4, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 5, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 6, 30)) == Date(2012, 6, 30)); + assert(funcFwd(Date(2011, 7, 31)) == Date(2012, 6, 30)); + + assert(funcBwd(Date(2010, 5, 31)) == Date(2009, 6, 30)); + assert(funcBwd(Date(2010, 6, 30)) == Date(2009, 6, 30)); + assert(funcBwd(Date(2010, 7, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 9, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 10, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 11, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 12, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 1, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 2, 28)) == Date(2010, 6, 28)); + assert(funcBwd(Date(2011, 3, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 4, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 5, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 6, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 7, 30)) == Date(2011, 6, 30)); + + static assert(!__traits(compiles, everyMonth!TimeOfDay(Month.jan))); + assert(everyMonth!DateTime(Month.jan) !is null); + assert(everyMonth!SysTime(Month.jan) !is null); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point which is the given + duration later. + + Using this delegate allows iteration over successive time points which + are apart by the given duration e.g. passing $(D dur!"days"(3)) to + $(D everyDuration) would result in a delegate which could be used to iterate + over a range of days which are each 3 days apart. + + Params: + dir = The direction to iterate in. If passing the return value to + $(D fwdRange), use $(D Direction.fwd). If passing it to + $(D bwdRange), use $(D Direction.bwd). + duration = The duration which separates each successive time point in + the range. + +/ +TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow +if (isTimePoint!TP && + __traits(compiles, TP.init + duration) && + (dir == Direction.fwd || dir == Direction.bwd)) +{ + TP func(in TP tp) + { + static if (dir == Direction.fwd) + return tp + duration; + else + return tp - duration; + } + + return &func; +} + +/// +@system unittest +{ + import core.time : dur; + import std.datetime.date : Date; + + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDuration!Date(dur!"days"(8)); + auto range = interval.fwdRange(func); + + // Using PopFirst.yes would have made this Date(2010, 9, 10). + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 10)); + + range.popFront(); + assert(range.front == Date(2010, 9, 18)); + + range.popFront(); + assert(range.front == Date(2010, 9, 26)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + import std.datetime.date; + import std.datetime.systime; + + auto funcFwd = everyDuration!Date(dur!"days"(27)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2010, 1, 21)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2010, 1, 22)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2010, 1, 23)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2010, 1, 24)); + + assert(funcBwd(Date(2010, 1, 21)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2010, 1, 22)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2010, 1, 23)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2010, 1, 24)) == Date(2009, 12, 28)); + + assert(everyDuration!Date(dur!"hnsecs"(1)) !is null); + assert(everyDuration!TimeOfDay(dur!"hnsecs"(1)) !is null); + assert(everyDuration!DateTime(dur!"hnsecs"(1)) !is null); + assert(everyDuration!SysTime(dur!"hnsecs"(1)) !is null); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point which is the given + number of years, month, and duration later. + + The difference between this version of $(D everyDuration) and the version + which just takes a $(REF Duration, core,time) is that this one also takes + the number of years and months (along with an $(D AllowDayOverflow) to + indicate whether adding years and months should allow the days to overflow). + + Note that if iterating forward, $(D add!"years"()) is called on the given + time point, then $(D add!"months"()), and finally the duration is added + to it. However, if iterating backwards, the duration is added first, then + $(D add!"months"()) is called, and finally $(D add!"years"()) is called. + That way, going backwards generates close to the same time points that + iterating forward does, but since adding years and months is not entirely + reversible (due to possible day overflow, regardless of whether + $(D AllowDayOverflow.yes) or $(D AllowDayOverflow.no) is used), it can't be + guaranteed that iterating backwards will give the same time points as + iterating forward would have (even assuming that the end of the range is a + time point which would be returned by the delegate when iterating forward + from $(D begin)). + + Params: + dir = The direction to iterate in. If passing the return + value to $(D fwdRange), use $(D Direction.fwd). If + passing it to $(D bwdRange), use $(D Direction.bwd). + years = The number of years to add to the time point passed to + the delegate. + months = The number of months to add to the time point passed to + the delegate. + allowOverflow = Whether the days should be allowed to overflow on + $(D begin) and $(D end), causing their month to + increment. + duration = The duration to add to the time point passed to the + delegate. + +/ +TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) + (int years, + int months = 0, + AllowDayOverflow allowOverflow = AllowDayOverflow.yes, + D duration = dur!"days"(0)) nothrow +if (isTimePoint!TP && + __traits(compiles, TP.init + duration) && + __traits(compiles, TP.init.add!"years"(years)) && + __traits(compiles, TP.init.add!"months"(months)) && + (dir == Direction.fwd || dir == Direction.bwd)) +{ + TP func(in TP tp) + { + static if (dir == Direction.fwd) + { + TP retval = cast(TP) tp; + + retval.add!"years"(years, allowOverflow); + retval.add!"months"(months, allowOverflow); + + return retval + duration; + } + else + { + TP retval = tp - duration; + + retval.add!"months"(-months, allowOverflow); + retval.add!"years"(-years, allowOverflow); + + return retval; + } + } + + return &func; +} + +/// +@system unittest +{ + import core.time : dur; + import std.datetime.date : AllowDayOverflow, Date; + + auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); + auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); + auto range = interval.fwdRange(func); + + // Using PopFirst.yes would have made this Date(2014, 10, 12). + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2014, 10, 4)); + + range.popFront(); + assert(range.front == Date(2018, 11, 6)); + + range.popFront(); + assert(range.front == Date(2022, 12, 8)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + import std.datetime.date; + import std.datetime.systime; + + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); + assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 4)); + + assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); + assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); + } + + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); + assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 3)); + + assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); + assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); + } + + assert(everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); + static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + assert(everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); + assert(everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); +} + + +/++ + A range over an $(LREF Interval). + + $(D IntervalRange) is only ever constructed by $(LREF Interval). However, when + it is constructed, it is given a function, $(D func), which is used to + generate the time points which are iterated over. $(D func) takes a time + point and returns a time point of the same type. For instance, + to iterate over all of the days in + the interval $(D Interval!Date), pass a function to $(LREF Interval)'s + $(D fwdRange) where that function took a $(REF Date,std,datetime,date) and + returned a $(REF Date,std,datetime,date) which was one day later. That + function would then be used by $(D IntervalRange)'s $(D popFront) to iterate + over the $(REF Date,std,datetime,date)s in the interval. + + If $(D dir == Direction.fwd), then a range iterates forward in time, whereas + if $(D dir == Direction.bwd), then it iterates backwards in time. So, if + $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if + $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must + generate a time point going in the proper direction of iteration, or a + $(REF DateTimeException,std,datetime,date) will be thrown. So, to iterate + forward in time, the time point that $(D func) generates must be later in + time than the one passed to it. If it's either identical or earlier in time, + then a $(REF DateTimeException,std,datetime,date) will be thrown. To + iterate backwards, then the generated time point must be before the time + point which was passed in. + + If the generated time point is ever passed the edge of the range in the + proper direction, then the edge of that range will be used instead. So, if + iterating forward, and the generated time point is past the interval's + $(D end), then $(D front) becomes $(D end). If iterating backwards, and the + generated time point is before $(D begin), then $(D front) becomes + $(D begin). In either case, the range would then be empty. + + Also note that while normally the $(D begin) of an interval is included in + it and its $(D end) is excluded from it, if $(D dir == Direction.bwd), then + $(D begin) is treated as excluded and $(D end) is treated as included. This + allows for the same behavior in both directions. This works because none of + $(LREF Interval)'s functions which care about whether $(D begin) or $(D end) + is included or excluded are ever called by $(D IntervalRange). $(D interval) + returns a normal interval, regardless of whether $(D dir == Direction.fwd) + or if $(D dir == Direction.bwd), so any $(LREF Interval) functions which are + called on it which care about whether $(D begin) or $(D end) are included or + excluded will treat $(D begin) as included and $(D end) as excluded. + +/ +struct IntervalRange(TP, Direction dir) +if (isTimePoint!TP && dir != Direction.both) +{ +public: + + /++ + Params: + rhs = The $(D IntervalRange) to assign to this one. + +/ + ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + return this; + } + + + /++ Ditto +/ + ref IntervalRange opAssign(IntervalRange rhs) pure nothrow + { + return this = rhs; + } + + + /++ + Whether this $(D IntervalRange) is empty. + +/ + @property bool empty() const pure nothrow + { + return _interval.empty; + } + + + /++ + The first time point in the range. + + Throws: + $(REF DateTimeException,std,datetime,date) if the range is empty. + +/ + @property TP front() const pure + { + _enforceNotEmpty(); + + static if (dir == Direction.fwd) + return _interval.begin; + else + return _interval.end; + } + + + /++ + Pops $(D front) from the range, using $(D func) to generate the next + time point in the range. If the generated time point is beyond the edge + of the range, then $(D front) is set to that edge, and the range is then + empty. So, if iterating forwards, and the generated time point is + greater than the interval's $(D end), then $(D front) is set to + $(D end). If iterating backwards, and the generated time point is less + than the interval's $(D begin), then $(D front) is set to $(D begin). + + Throws: + $(REF DateTimeException,std,datetime,date) if the range is empty + or if the generated time point is in the wrong direction (i.e. if + iterating forward and the generated time point is before $(D front), + or if iterating backwards and the generated time point is after + $(D front)). + +/ + void popFront() + { + _enforceNotEmpty(); + + static if (dir == Direction.fwd) + { + auto begin = _func(_interval.begin); + + if (begin > _interval.end) + begin = _interval.end; + + _enforceCorrectDirection(begin); + + _interval.begin = begin; + } + else + { + auto end = _func(_interval.end); + + if (end < _interval.begin) + end = _interval.begin; + + _enforceCorrectDirection(end); + + _interval.end = end; + } + } + + + /++ + Returns a copy of $(D this). + +/ + @property IntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this $(D IntervalRange) currently covers. + +/ + @property Interval!TP interval() const pure nothrow + { + return cast(Interval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + + /++ + The $(D Direction) that this range iterates in. + +/ + @property Direction direction() const pure nothrow + { + return dir; + } + + +private: + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + $(REF DateTimeException,std,datetime,date) if this interval is + empty. + +/ + void _enforceNotEmpty(size_t line = __LINE__) const pure + { + if (empty) + throw new DateTimeException("Invalid operation for an empty IntervalRange.", __FILE__, line); + } + + + /+ + Throws: + $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is + in the wrong direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + import std.format : format; + + static if (dir == Direction.fwd) + { + enforce(newTP > _interval._begin, + new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", + interval._begin, + newTP), + __FILE__, + line)); + } + else + { + enforce(newTP < _interval._end, + new DateTimeException(format("Generated time point is after previous end: prev [%s] new [%s]", + interval._end, + newTP), + __FILE__, + line)); + } + } + + + Interval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + import std.range.primitives; + + static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); + static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); + + static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); + static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); + + static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); + static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); + static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); + static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); +} + +//Test construction of IntervalRange. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + { + Date dateFunc(in Date date) { return date; } + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); + auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) { return dt; } + auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); + auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) { return cast(SysTime) st; } + auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), + SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); + } +} + +//Test IntervalRange's empty(). +@system unittest +{ + import std.datetime.date; + + //fwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + + assert(!range.empty); + range.popFront(); + assert(range.empty); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(!cRange.empty); + + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } + + //bwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + + assert(!range.empty); + range.popFront(); + assert(range.empty); + + const cRange = Interval!Date( Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + assert(!cRange.empty); + + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } +} + +//Test IntervalRange's front. +@system unittest +{ + import std.datetime.date; + + //fwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assert(range.front == Date(2010, 7, 4)); + + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2010, 7, 7)); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(cRange.front != Date.init); + } + + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assert(range.front == Date(2012, 1, 7)); + + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2012, 1, 4)); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + assert(cRange.front != Date.init); + } +} + +//Test IntervalRange's popFront(). +@system unittest +{ + import std.datetime.date; + import std.range.primitives : walkLength; + + //fwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; range) + { + assert(date == expected); + expected += dur!"days"(7); + } + + assert(walkLength(range) == 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(cRange.front != Date.init); + } + + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; range) + { + assert(date == expected); + expected += dur!"days"(-7); + } + + assert(walkLength(range) == 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); + } +} + +//Test IntervalRange's save. +@system unittest +{ + import std.datetime.date; + + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.save == range); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.save == range); + } +} + +//Test IntervalRange's interval. +@system unittest +{ + import std.datetime.date; + + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); + } +} + +//Test IntervalRange's func. +@system unittest +{ + import std.datetime.date; + + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.func == func); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.func == func); + } +} + +//Test IntervalRange's direction. +@system unittest +{ + import std.datetime.date; + + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.direction == Direction.fwd); + + const cRange = range; + assert(cRange.direction == Direction.fwd); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.direction == Direction.bwd); + + const cRange = range; + assert(cRange.direction == Direction.bwd); + } +} + + +/++ + A range over a $(D PosInfInterval). It is an infinite range. + + $(D PosInfIntervalRange) is only ever constructed by $(D PosInfInterval). + However, when it is constructed, it is given a function, $(D func), which + is used to generate the time points which are iterated over. $(D func) + takes a time point and returns a time point of the same type. For + instance, to iterate + over all of the days in the interval $(D PosInfInterval!Date), pass a + function to $(D PosInfInterval)'s $(D fwdRange) where that function took a + $(REF Date,std,datetime,date) and returned a $(REF Date,std,datetime,date) + which was one day later. That function would then be used by + $(D PosInfIntervalRange)'s $(D popFront) to iterate over the + $(REF Date,std,datetime,date)s in the interval - though obviously, since the + range is infinite, use a function such as $(D std.range.take) with it rather + than iterating over $(I all) of the dates. + + As the interval goes to positive infinity, the range is always iterated over + forwards, never backwards. $(D func) must generate a time point going in + the proper direction of iteration, or a + $(REF DateTimeException,std,datetime,date) will be thrown. So, the time + points that $(D func) generates must be later in time than the one passed to + it. If it's either identical or earlier in time, then a + $(REF DateTimeException,std,datetime,date) will be thrown. + +/ +struct PosInfIntervalRange(TP) +if (isTimePoint!TP) +{ +public: + + /++ + Params: + rhs = The $(D PosInfIntervalRange) to assign to this one. + +/ + ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + return this; + } + + + /++ Ditto +/ + ref PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow + { + return this = rhs; + } + + + /++ + This is an infinite range, so it is never empty. + +/ + enum bool empty = false; + + + /++ + The first time point in the range. + +/ + @property TP front() const pure nothrow + { + return _interval.begin; + } + + + /++ + Pops $(D front) from the range, using $(D func) to generate the next + time point in the range. + + Throws: + $(REF DateTimeException,std,datetime,date) if the generated time + point is less than $(D front). + +/ + void popFront() + { + auto begin = _func(_interval.begin); + _enforceCorrectDirection(begin); + _interval.begin = begin; + } + + + /++ + Returns a copy of $(D this). + +/ + @property PosInfIntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this range currently covers. + +/ + @property PosInfInterval!TP interval() const pure nothrow + { + return cast(PosInfInterval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + +private: + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is + in the wrong direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + import std.format : format; + + enforce(newTP > _interval._begin, + new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", + interval._begin, + newTP), + __FILE__, + line)); + } + + + PosInfInterval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + import std.range.primitives; + + static assert(isInputRange!(PosInfIntervalRange!Date)); + static assert(isForwardRange!(PosInfIntervalRange!Date)); + static assert(isInfinite!(PosInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); + static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); + static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); + static assert(!hasLength!(PosInfIntervalRange!Date)); + static assert(!hasSlicing!(PosInfIntervalRange!Date)); + + static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); + static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); +} + +//Test construction of PosInfIntervalRange. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + { + Date dateFunc(in Date date) { return date; } + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); + auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) { return dt; } + auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); + auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) { return cast(SysTime) st; } + auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); + auto ir = PosInfIntervalRange!SysTime(posInfInterval, &stFunc); + } +} + +//Test PosInfIntervalRange's front. +@system unittest +{ + import std.datetime.date; + + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assert(range.front == Date(2010, 7, 4)); + + auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2010, 7, 7)); + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(cRange.front != Date.init); +} + +//Test PosInfIntervalRange's popFront(). +@system unittest +{ + import std.datetime.date; + import std.range : take; + + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; take(range, 79)) + { + assert(date == expected); + expected += dur!"days"(7); + } + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); +} + +//Test PosInfIntervalRange's save. +@system unittest +{ + import std.datetime.date; + + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.save == range); +} + +//Test PosInfIntervalRange's interval. +@system unittest +{ + import std.datetime.date; + + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); +} + +//Test PosInfIntervalRange's func. +@system unittest +{ + import std.datetime.date; + + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.func == func); +} + + +/++ + A range over a $(D NegInfInterval). It is an infinite range. + + $(D NegInfIntervalRange) is only ever constructed by $(D NegInfInterval). + However, when it is constructed, it is given a function, $(D func), which + is used to generate the time points which are iterated over. $(D func) + takes a time point and returns a time point of the same type. For + instance, to iterate over all of the days in the interval + $(D NegInfInterval!Date), pass a function to $(D NegInfInterval)'s + $(D bwdRange) where that function took a $(REF Date,std,datetime,date) and + returned a $(REF Date,std,datetime,date) which was one day earlier. That + function would then be used by $(D NegInfIntervalRange)'s $(D popFront) to + iterate over the $(REF Date,std,datetime,date)s in the interval - though + obviously, since the range is infinite, use a function such as + $(D std.range.take) with it rather than iterating over $(I all) of the dates. + + As the interval goes to negative infinity, the range is always iterated over + backwards, never forwards. $(D func) must generate a time point going in + the proper direction of iteration, or a + $(REF DateTimeException,std,datetime,date) will be thrown. So, the time + points that $(D func) generates must be earlier in time than the one passed + to it. If it's either identical or later in time, then a + $(REF DateTimeException,std,datetime,date) will be thrown. + + Also note that while normally the $(D end) of an interval is excluded from + it, $(D NegInfIntervalRange) treats it as if it were included. This allows + for the same behavior as with $(D PosInfIntervalRange). This works + because none of $(D NegInfInterval)'s functions which care about whether + $(D end) is included or excluded are ever called by + $(D NegInfIntervalRange). $(D interval) returns a normal interval, so any + $(D NegInfInterval) functions which are called on it which care about + whether $(D end) is included or excluded will treat $(D end) as excluded. + +/ +struct NegInfIntervalRange(TP) +if (isTimePoint!TP) +{ +public: + + /++ + Params: + rhs = The $(D NegInfIntervalRange) to assign to this one. + +/ + ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + + return this; + } + + + /++ Ditto +/ + ref NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow + { + return this = rhs; + } + + + /++ + This is an infinite range, so it is never empty. + +/ + enum bool empty = false; + + + /++ + The first time point in the range. + +/ + @property TP front() const pure nothrow + { + return _interval.end; + } + + + /++ + Pops $(D front) from the range, using $(D func) to generate the next + time point in the range. + + Throws: + $(REF DateTimeException,std,datetime,date) if the generated time + point is greater than $(D front). + +/ + void popFront() + { + auto end = _func(_interval.end); + _enforceCorrectDirection(end); + _interval.end = end; + } + + + /++ + Returns a copy of $(D this). + +/ + @property NegInfIntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this range currently covers. + +/ + @property NegInfInterval!TP interval() const pure nothrow + { + return cast(NegInfInterval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + +private: + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is + in the wrong direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + import std.format : format; + + enforce(newTP < _interval._end, + new DateTimeException(format("Generated time point is before previous end: prev [%s] new [%s]", + interval._end, + newTP), + __FILE__, + line)); + } + + + NegInfInterval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. +@safe unittest +{ + import std.datetime.date; + import std.range.primitives; + + static assert(isInputRange!(NegInfIntervalRange!Date)); + static assert(isForwardRange!(NegInfIntervalRange!Date)); + static assert(isInfinite!(NegInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); + static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); + static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); + static assert(!hasLength!(NegInfIntervalRange!Date)); + static assert(!hasSlicing!(NegInfIntervalRange!Date)); + + static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); +} + +//Test construction of NegInfIntervalRange. +@safe unittest +{ + import std.datetime.date; + import std.datetime.systime; + + { + Date dateFunc(in Date date) { return date; } + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); + auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) { return dt; } + auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); + auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) { return cast(SysTime)(st); } + auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); + } +} + +//Test NegInfIntervalRange's front. +@system unittest +{ + import std.datetime.date; + + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assert(range.front == Date(2012, 1, 7)); + + auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2012, 1, 4)); + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + assert(cRange.front != Date.init); +} + +//Test NegInfIntervalRange's popFront(). +@system unittest +{ + import std.datetime.date; + import std.range : take; + + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; take(range, 79)) + { + assert(date == expected); + expected += dur!"days"(-7); + } + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); +} + +//Test NegInfIntervalRange's save. +@system unittest +{ + import std.datetime.date; + + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.save == range); +} + +//Test NegInfIntervalRange's interval. +@system unittest +{ + import std.datetime.date; + + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); +} + +//Test NegInfIntervalRange's func. +@system unittest +{ + import std.datetime.date; + + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.func == func); +} diff --git a/std/datetime/package.d b/std/datetime/package.d new file mode 100644 index 00000000000..9395590a002 --- /dev/null +++ b/std/datetime/package.d @@ -0,0 +1,733 @@ +// Written in the D programming language + +/++ + Module containing Date/Time functionality. + + This module provides: + $(UL + $(LI Types to represent points in time: + $(REF SysTime,std,_datetime,systime), + $(REF Date,std,_datetime,date), + $(REF TimeOfDay,std,_datetime,date), + $(REF DateTime,std,_datetime,date).) + $(LI Types to represent intervals of time.) + $(LI Types to represent ranges over intervals of time.) + $(LI Types to represent time zones (used by + $(REF SysTime,std,_datetime,systime)).) + $(LI A platform-independent, high precision stopwatch type: + $(LREF StopWatch)) + $(LI Benchmarking functions.) + $(LI Various helper functions.) + ) + + Closely related to std.datetime is $(D core.time), + and some of the time types used in std.datetime come from there - such as + $(REF Duration, core,time), $(REF TickDuration, core,time), and + $(REF FracSec, core,time). + core.time is publically imported into std.datetime, it isn't necessary + to import it separately. + + Three of the main concepts used in this module are time points, time + durations, and time intervals. + + A time point is a specific point in time. e.g. January 5th, 2010 + or 5:00. + + A time duration is a length of time with units. e.g. 5 days or 231 seconds. + + A time interval indicates a period of time associated with a fixed point in + time. It is either two time points associated with each other, + indicating the time starting at the first point up to, but not including, + the second point - e.g. [January 5th, 2010 - March 10th, 2010$(RPAREN) - or + it is a time point and a time duration associated with one another. e.g. + January 5th, 2010 and 5 days, indicating [January 5th, 2010 - + January 10th, 2010$(RPAREN). + + Various arithmetic operations are supported between time points and + durations (e.g. the difference between two time points is a time duration), + and ranges can be gotten from time intervals, so range-based operations may + be done on a series of time points. + + The types that the typical user is most likely to be interested in are + $(REF Date,std,_datetime,date) (if they want dates but don't care about + time), $(REF DateTime,std,_datetime,date) (if they want dates and times + but don't care about time zones), $(REF SysTime,std,_datetime,systime) (if + they want the date and time from the OS and/or do care about time zones), + and StopWatch (a platform-independent, high precision stop watch). + $(REF Date,std,_datetime,date) and $(REF DateTime,std,_datetime,date) are + optimized for calendar-based operations, while + $(REF SysTime,std,_datetime,systime) is designed for dealing with time from + the OS. Check out their specific documentation for more details. + + To get the current time, use $(REF Clock.currTime,std,_datetime,systime). + It will return the current time as a $(REF SysTime,std,_datetime,systime). To + print it, $(D toString) is sufficient, but if using $(D toISOString), + $(D toISOExtString), or $(D toSimpleString), use the corresponding + $(D fromISOString), $(D fromISOExtString), or $(D fromSimpleString) to + create a $(REF SysTime,std,_datetime,systime) from the string. + +-------------------- +auto currentTime = Clock.currTime(); +auto timeString = currentTime.toISOExtString(); +auto restoredTime = SysTime.fromISOExtString(timeString); +-------------------- + + Various functions take a string (or strings) to represent a unit of time + (e.g. $(D convert!("days", "hours")(numDays))). The valid strings to use + with such functions are $(D "years"), $(D "months"), $(D "weeks"), + $(D "days"), $(D "hours"), $(D "minutes"), $(D "seconds"), + $(D "msecs") (milliseconds), $(D "usecs") (microseconds), + $(D "hnsecs") (hecto-nanoseconds - i.e. 100 ns), or some subset thereof. + There are a few functions in core.time which take $(D "nsecs"), but because + nothing in std.datetime has precision greater than hnsecs, and very little + in core.time does, no functions in std.datetime accept $(D "nsecs"). + To remember which units are abbreviated and which aren't, + all units seconds and greater use their full names, and all + sub-second units are abbreviated (since they'd be rather long if they + weren't). + + Note: + $(REF DateTimeException,std,_datetime,date) is an alias for + $(REF TimeException, core,time), so you don't need to worry about + core.time functions and std.datetime functions throwing different + exception types (except in the rare case that they throw something other + than $(REF TimeException, core,time) or + $(REF DateTimeException,std,_datetime,date)). + + See_Also: + $(DDLINK intro-to-_datetime, Introduction to std.datetime, + Introduction to std._datetime)
+ $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601)
+ $(HTTP en.wikipedia.org/wiki/Tz_database, + Wikipedia entry on TZ Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, + List of Time Zones)
+ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis and Kato Shoichi + Source: $(PHOBOSSRC std/_datetime/package.d) ++/ +module std.datetime; + +public import core.time; +public import std.datetime.date; +public import std.datetime.interval; +public import std.datetime.systime; +public import std.datetime.timezone; + +import core.exception : AssertError; +import std.typecons : Flag, Yes, No; +import std.functional : unaryFun; +import std.traits; + + +// Verify module example. +@safe unittest +{ + auto currentTime = Clock.currTime(); + auto timeString = currentTime.toISOExtString(); + auto restoredTime = SysTime.fromISOExtString(timeString); +} + +// Verify Examples for core.time.Duration which couldn't be in core.time. +@safe unittest +{ + assert(std.datetime.Date(2010, 9, 7) + dur!"days"(5) == + std.datetime.Date(2010, 9, 12)); + + assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) == + dur!"days"(-26)); +} + +@safe unittest +{ + import std.traits : hasUnsharedAliasing; + /* Issue 6642 */ + static assert(!hasUnsharedAliasing!Date); + static assert(!hasUnsharedAliasing!TimeOfDay); + static assert(!hasUnsharedAliasing!DateTime); + static assert(!hasUnsharedAliasing!SysTime); +} + + +//============================================================================== +// Everything after here will be deprecated after we have replacements which +// use MonoTime and Duration. +//============================================================================== + + +/++ + Used by StopWatch to indicate whether it should start immediately upon + construction. + + If set to $(D AutoStart.no), then the stopwatch is not started when it is + constructed. + + Otherwise, if set to $(D AutoStart.yes), then the stopwatch is started when + it is constructed. + +/ +alias AutoStart = Flag!"autoStart"; + + +/++ + $(RED This will be deprecated in 2.076. Please use + $(REF StopWatch,std,datetime,stopwatch) instead. It uses + $(REF Monotime,core,time) and $(REF Duration,core,time) rather + than $(REF TickDuration,core,time), which will also be deprecated in + 2.076.) + + $(D StopWatch) measures time as precisely as possible. + + This class uses a high-performance counter. On Windows systems, it uses + $(D QueryPerformanceCounter), and on Posix systems, it uses + $(D clock_gettime) if available, and $(D gettimeofday) otherwise. + + But the precision of $(D StopWatch) differs from system to system. It is + impossible to for it to be the same from system to system since the precision + of the system clock varies from system to system, and other system-dependent + and situation-dependent stuff (such as the overhead of a context switch + between threads) can also affect $(D StopWatch)'s accuracy. + +/ +@safe struct StopWatch +{ +public: + + /++ + Auto start with constructor. + +/ + this(AutoStart autostart) @nogc + { + if (autostart) + start(); + } + + @nogc @safe unittest + { + auto sw = StopWatch(Yes.autoStart); + sw.stop(); + } + + + /// + bool opEquals(const StopWatch rhs) const pure nothrow @nogc + { + return opEquals(rhs); + } + + /// ditto + bool opEquals(const ref StopWatch rhs) const pure nothrow @nogc + { + return _timeStart == rhs._timeStart && + _timeMeasured == rhs._timeMeasured; + } + + + /++ + Resets the stop watch. + +/ + void reset() @nogc + { + if (_flagStarted) + { + // Set current system time if StopWatch is measuring. + _timeStart = TickDuration.currSystemTick; + } + else + { + // Set zero if StopWatch is not measuring. + _timeStart.length = 0; + } + + _timeMeasured.length = 0; + } + + /// + @nogc @safe unittest + { + StopWatch sw; + sw.start(); + sw.stop(); + sw.reset(); + assert(sw.peek().to!("seconds", real)() == 0); + } + + + /++ + Starts the stop watch. + +/ + void start() @nogc + { + assert(!_flagStarted); + _flagStarted = true; + _timeStart = TickDuration.currSystemTick; + } + + @nogc @system unittest + { + StopWatch sw; + sw.start(); + auto t1 = sw.peek(); + bool doublestart = true; + try + sw.start(); + catch (AssertError e) + doublestart = false; + assert(!doublestart); + sw.stop(); + assert((t1 - sw.peek()).to!("seconds", real)() <= 0); + } + + + /++ + Stops the stop watch. + +/ + void stop() @nogc + { + assert(_flagStarted); + _flagStarted = false; + _timeMeasured += TickDuration.currSystemTick - _timeStart; + } + + @nogc @system unittest + { + StopWatch sw; + sw.start(); + sw.stop(); + auto t1 = sw.peek(); + bool doublestop = true; + try + sw.stop(); + catch (AssertError e) + doublestop = false; + assert(!doublestop); + assert((t1 - sw.peek()).to!("seconds", real)() == 0); + } + + + /++ + Peek at the amount of time which has passed since the stop watch was + started. + +/ + TickDuration peek() const @nogc + { + if (_flagStarted) + return TickDuration.currSystemTick - _timeStart + _timeMeasured; + + return _timeMeasured; + } + + @nogc @safe unittest + { + StopWatch sw; + sw.start(); + auto t1 = sw.peek(); + sw.stop(); + auto t2 = sw.peek(); + auto t3 = sw.peek(); + assert(t1 <= t2); + assert(t2 == t3); + } + + + /++ + Set the amount of time which has been measured since the stop watch was + started. + +/ + void setMeasured(TickDuration d) @nogc + { + reset(); + _timeMeasured = d; + } + + @nogc @safe unittest + { + StopWatch sw; + TickDuration t0; + t0.length = 100; + sw.setMeasured(t0); + auto t1 = sw.peek(); + assert(t0 == t1); + } + + + /++ + Confirm whether this stopwatch is measuring time. + +/ + bool running() @property const pure nothrow @nogc + { + return _flagStarted; + } + + @nogc @safe unittest + { + StopWatch sw1; + assert(!sw1.running); + sw1.start(); + assert(sw1.running); + sw1.stop(); + assert(!sw1.running); + StopWatch sw2 = Yes.autoStart; + assert(sw2.running); + sw2.stop(); + assert(!sw2.running); + sw2.start(); + assert(sw2.running); + } + + + + +private: + + // true if observing. + bool _flagStarted = false; + + // TickDuration at the time of StopWatch starting measurement. + TickDuration _timeStart; + + // Total time that StopWatch ran. + TickDuration _timeMeasured; +} + +/// +@safe unittest +{ + void writeln(S...)(S args){} + static void bar() {} + + StopWatch sw; + enum n = 100; + TickDuration[n] times; + TickDuration last = TickDuration.from!"seconds"(0); + foreach (i; 0 .. n) + { + sw.start(); //start/resume mesuring. + foreach (unused; 0 .. 1_000_000) + bar(); + sw.stop(); //stop/pause measuring. + //Return value of peek() after having stopped are the always same. + writeln((i + 1) * 1_000_000, " times done, lap time: ", + sw.peek().msecs, "[ms]"); + times[i] = sw.peek() - last; + last = sw.peek(); + } + real sum = 0; + // To get the number of seconds, + // use properties of TickDuration. + // (seconds, msecs, usecs, hnsecs) + foreach (t; times) + sum += t.hnsecs; + writeln("Average time: ", sum/n, " hnsecs"); +} + + +/++ + $(RED This will be deprecated in 2.076. Please use + $(REF benchmark,std,datetime,stopwatch) instead. It uses + $(REF Monotime,core,time) and $(REF Duration,core,time) rather + than $(REF TickDuration,core,time), which will also be deprecated in + 2.076.) + + Benchmarks code for speed assessment and comparison. + + Params: + fun = aliases of callable objects (e.g. function names). Each should + take no arguments. + n = The number of times each function is to be executed. + + Returns: + The amount of time (as a $(REF TickDuration, core,time)) that it took to + call each function $(D n) times. The first value is the length of time + that it took to call $(D fun[0]) $(D n) times. The second value is the + length of time it took to call $(D fun[1]) $(D n) times. Etc. + + Note that casting the TickDurations to $(REF Duration, core,time)s will make + the results easier to deal with (and it may change in the future that + benchmark will return an array of Durations rather than TickDurations). + + See_Also: + $(LREF measureTime) + +/ +TickDuration[fun.length] benchmark(fun...)(uint n) +{ + TickDuration[fun.length] result; + StopWatch sw; + sw.start(); + + foreach (i, unused; fun) + { + sw.reset(); + foreach (j; 0 .. n) + fun[i](); + result[i] = sw.peek(); + } + + return result; +} + +/// +@safe unittest +{ + import std.conv : to; + int a; + void f0() {} + void f1() {auto b = a;} + void f2() {auto b = to!string(a);} + auto r = benchmark!(f0, f1, f2)(10_000); + auto f0Result = to!Duration(r[0]); // time f0 took to run 10,000 times + auto f1Result = to!Duration(r[1]); // time f1 took to run 10,000 times + auto f2Result = to!Duration(r[2]); // time f2 took to run 10,000 times +} + +@safe unittest +{ + int a; + void f0() {} + //void f1() {auto b = to!(string)(a);} + void f2() {auto b = (a);} + auto r = benchmark!(f0, f2)(100); +} + + +/++ + Return value of benchmark with two functions comparing. + +/ +@safe struct ComparingBenchmarkResult +{ + /++ + Evaluation value + + This returns the evaluation value of performance as the ratio of + baseFunc's time over targetFunc's time. If performance is high, this + returns a high value. + +/ + @property real point() const pure nothrow + { + return _baseTime.length / cast(const real)_targetTime.length; + } + + + /++ + The time required of the base function + +/ + @property public TickDuration baseTime() const pure nothrow + { + return _baseTime; + } + + + /++ + The time required of the target function + +/ + @property public TickDuration targetTime() const pure nothrow + { + return _targetTime; + } + +private: + + this(TickDuration baseTime, TickDuration targetTime) pure nothrow + { + _baseTime = baseTime; + _targetTime = targetTime; + } + + TickDuration _baseTime; + TickDuration _targetTime; +} + + +/++ + $(RED This will be deprecated in 2.076. Please use + $(REF benchmark,std,datetime,stopwatch) instead. This function has + not been ported to $(REF Monotime,core,time) and + $(REF Duration,core,time), because it is a trivial wrapper around + benchmark.) + + Benchmark with two functions comparing. + + Params: + baseFunc = The function to become the base of the speed. + targetFunc = The function that wants to measure speed. + times = The number of times each function is to be executed. + +/ +ComparingBenchmarkResult comparingBenchmark(alias baseFunc, + alias targetFunc, + int times = 0xfff)() +{ + auto t = benchmark!(baseFunc, targetFunc)(times); + return ComparingBenchmarkResult(t[0], t[1]); +} + +/// +@safe unittest +{ + void f1x() {} + void f2x() {} + @safe void f1o() {} + @safe void f2o() {} + auto b1 = comparingBenchmark!(f1o, f2o, 1)(); // OK + //writeln(b1.point); +} + +//Bug# 8450 +@system unittest +{ + @safe void safeFunc() {} + @trusted void trustFunc() {} + @system void sysFunc() {} + auto safeResult = comparingBenchmark!((){safeFunc();}, (){safeFunc();})(); + auto trustResult = comparingBenchmark!((){trustFunc();}, (){trustFunc();})(); + auto sysResult = comparingBenchmark!((){sysFunc();}, (){sysFunc();})(); + auto mixedResult1 = comparingBenchmark!((){safeFunc();}, (){trustFunc();})(); + auto mixedResult2 = comparingBenchmark!((){trustFunc();}, (){sysFunc();})(); + auto mixedResult3 = comparingBenchmark!((){safeFunc();}, (){sysFunc();})(); +} + + +/++ + $(RED This will be deprecated in 2.076. Please use + $(REF StopWatch,std,datetime,stopwatch) instead. This function has + not been ported to $(REF Monotime,core,time) and + $(REF Duration,core,time), because it is a trivial wrapper around + StopWatch.) + + Function for starting to a stop watch time when the function is called + and stopping it when its return value goes out of scope and is destroyed. + + When the value that is returned by this function is destroyed, + $(D func) will run. $(D func) is a unary function that takes a + $(REF TickDuration, core,time). + + Example: +-------------------- +{ + auto mt = measureTime!((TickDuration a) + { /+ do something when the scope is exited +/ }); + // do something that needs to be timed +} +-------------------- + + which is functionally equivalent to + +-------------------- +{ + auto sw = StopWatch(Yes.autoStart); + scope(exit) + { + TickDuration a = sw.peek(); + /+ do something when the scope is exited +/ + } + // do something that needs to be timed +} +-------------------- + + See_Also: + $(LREF benchmark) ++/ +@safe auto measureTime(alias func)() +if (isSafe!((){StopWatch sw; unaryFun!func(sw.peek());})) +{ + struct Result + { + private StopWatch _sw = void; + this(AutoStart as) + { + _sw = StopWatch(as); + } + ~this() + { + unaryFun!(func)(_sw.peek()); + } + } + return Result(Yes.autoStart); +} + +auto measureTime(alias func)() +if (!isSafe!((){StopWatch sw; unaryFun!func(sw.peek());})) +{ + struct Result + { + private StopWatch _sw = void; + this(AutoStart as) + { + _sw = StopWatch(as); + } + ~this() + { + unaryFun!(func)(_sw.peek()); + } + } + return Result(Yes.autoStart); +} + +// Verify Example. +@safe unittest +{ + { + auto mt = measureTime!((TickDuration a) + { /+ do something when the scope is exited +/ }); + // do something that needs to be timed + } + + { + auto sw = StopWatch(Yes.autoStart); + scope(exit) + { + TickDuration a = sw.peek(); + /+ do something when the scope is exited +/ + } + // do something that needs to be timed + } +} + +@safe unittest +{ + import std.math : isNaN; + + @safe static void func(TickDuration td) + { + assert(!td.to!("seconds", real)().isNaN()); + } + + auto mt = measureTime!(func)(); + + /+ + with (measureTime!((a){assert(a.seconds);})) + { + // doSomething(); + // @@@BUG@@@ doesn't work yet. + } + +/ +} + +@safe unittest +{ + import std.math : isNaN; + + static void func(TickDuration td) + { + assert(!td.to!("seconds", real)().isNaN()); + } + + auto mt = measureTime!(func)(); + + /+ + with (measureTime!((a){assert(a.seconds);})) + { + // doSomething(); + // @@@BUG@@@ doesn't work yet. + } + +/ +} + +//Bug# 8450 +@system unittest +{ + @safe void safeFunc() {} + @trusted void trustFunc() {} + @system void sysFunc() {} + auto safeResult = measureTime!((a){safeFunc();})(); + auto trustResult = measureTime!((a){trustFunc();})(); + auto sysResult = measureTime!((a){sysFunc();})(); +} diff --git a/std/datetime/stopwatch.d b/std/datetime/stopwatch.d new file mode 100644 index 00000000000..12605a92045 --- /dev/null +++ b/std/datetime/stopwatch.d @@ -0,0 +1,425 @@ +// Written in the D programming language + +/++ + Module containing some basic benchmarking and timing functionality. + + For convenience, this module publicly imports $(MREF core,time). + + $(RED Unlike the other modules in std.datetime, this module is not currently + publicly imported in std.datetime.package, because the old + versions of this functionality which use + $(REF TickDuration,core,time) are in std.datetime.package and would + conflict with the symbols in this module. After the old symbols have + gone through the deprecation cycle and have been removed, then this + module will be publicly imported in std.datetime.package.) + + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis and Kato Shoichi + Source: $(PHOBOSSRC std/datetime/_stopwatch.d) ++/ +module std.datetime.stopwatch; + +public import core.time; +import std.typecons : Flag; + +/++ + Used by StopWatch to indicate whether it should start immediately upon + construction. + + If set to $(D AutoStart.no), then the StopWatch is not started when it is + constructed. + + Otherwise, if set to $(D AutoStart.yes), then the StopWatch is started when + it is constructed. + +/ +alias AutoStart = Flag!"autoStart"; + + +/++ + StopWatch is used to measure time just like one would do with a physical + stopwatch, including stopping, restarting, and/or resetting it. + + $(REF MonoTime,core,time) is used to hold the time, and it uses the system's + monotonic clock, which is high precision and never counts backwards (unlike + the wall clock time, which $(I can) count backwards, which is why + $(REF SysTime,std,datetime,systime) should not be used for timing). + + Note that the precision of StopWatch differs from system to system. It is + impossible for it to be the same for all systems, since the precision of the + system clock and other system-dependent and situation-dependent factors + (such as the overhead of a context switch between threads) varies from system + to system and can affect StopWatch's accuracy. + +/ +struct StopWatch +{ +public: + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + + Duration t1 = sw.peek(); + Thread.sleep(usecs(1)); + Duration t2 = sw.peek(); + assert(t2 > t1); + + Thread.sleep(usecs(1)); + sw.stop(); + + Duration t3 = sw.peek(); + assert(t3 > t2); + Duration t4 = sw.peek(); + assert(t3 == t4); + + sw.start(); + Thread.sleep(usecs(1)); + + Duration t5 = sw.peek(); + assert(t5 > t4); + + // If stopping or resetting the StopWatch is not required, then + // MonoTime can easily be used by itself without StopWatch. + auto before = MonoTime.currTime; + // do stuff... + auto timeElapsed = MonoTime.currTime - before; + } + + /++ + Constructs a StopWatch. Whether it starts immediately depends on the + $(LREF AutoStart) argument. + + If $(D StopWatch.init) is used, then the constructed StopWatch isn't + running (and can't be, since no constructor ran). + +/ + this(AutoStart autostart) @safe nothrow @nogc + { + if (autostart) + start(); + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + { + auto sw = StopWatch(AutoStart.yes); + assert(sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() > Duration.zero); + } + { + auto sw = StopWatch(AutoStart.no); + assert(!sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() == Duration.zero); + } + { + StopWatch sw; + assert(!sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() == Duration.zero); + } + + assert(StopWatch.init == StopWatch(AutoStart.no)); + assert(StopWatch.init != StopWatch(AutoStart.yes)); + } + + + /++ + Resets the StopWatch. + + The StopWatch can be reset while it's running, and resetting it while + it's running will not cause it to stop. + +/ + void reset() @safe nothrow @nogc + { + if (_running) + _timeStarted = MonoTime.currTime; + _ticksElapsed = 0; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + Thread.sleep(usecs(1)); + sw.stop(); + assert(sw.peek() > Duration.zero); + sw.reset(); + assert(sw.peek() == Duration.zero); + } + + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + Thread.sleep(msecs(1)); + assert(sw.peek() > msecs(1)); + immutable before = MonoTime.currTime; + + // Just in case the system clock is slow enough or the system is fast + // enough for the call to MonoTime.currTime inside of reset to get + // the same that we just got by calling MonoTime.currTime. + Thread.sleep(usecs(1)); + + sw.reset(); + assert(sw.peek() < msecs(1)); + assert(sw._timeStarted > before); + assert(sw._timeStarted <= MonoTime.currTime); + } + + + /++ + Starts the StopWatch. + + start should not be called if the StopWatch is already running. + +/ + void start() @safe nothrow @nogc + in { assert(!_running, "start was called when the StopWatch was already running."); } + body + { + _running = true; + _timeStarted = MonoTime.currTime; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + StopWatch sw; + assert(!sw.running); + assert(sw.peek() == Duration.zero); + sw.start(); + assert(sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() > Duration.zero); + } + + + /++ + Stops the StopWatch. + + stop should not be called if the StopWatch is not running. + +/ + void stop() @safe nothrow @nogc + in { assert(_running, "stop was called when the StopWatch was not running."); } + body + { + _running = false; + _ticksElapsed += MonoTime.currTime.ticks - _timeStarted.ticks; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + assert(sw.running); + Thread.sleep(usecs(1)); + immutable t1 = sw.peek(); + assert(t1 > Duration.zero); + + sw.stop(); + assert(!sw.running); + immutable t2 = sw.peek(); + assert(t2 >= t1); + immutable t3 = sw.peek(); + assert(t2 == t3); + } + + + /++ + Peek at the amount of time that the the StopWatch has been running. + + This does not include any time during which the StopWatch was stopped but + does include $(I all) of the time that it was running and not just the + time since it was started last. + + Calling $(LREF reset) will reset this to $(D Duration.zero). + +/ + Duration peek() @safe const nothrow @nogc + { + enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); + immutable hnsecsMeasured = convClockFreq(_ticksElapsed, MonoTime.ticksPerSecond, hnsecsPerSecond); + return _running ? MonoTime.currTime - _timeStarted + hnsecs(hnsecsMeasured) + : hnsecs(hnsecsMeasured); + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.no); + assert(sw.peek() == Duration.zero); + sw.start(); + + Thread.sleep(usecs(1)); + assert(sw.peek() >= usecs(1)); + + Thread.sleep(usecs(1)); + assert(sw.peek() >= usecs(2)); + + sw.stop(); + immutable stopped = sw.peek(); + Thread.sleep(usecs(1)); + assert(sw.peek() == stopped); + + sw.start(); + Thread.sleep(usecs(1)); + assert(sw.peek() > stopped); + } + + @safe nothrow @nogc unittest + { + assert(StopWatch.init.peek() == Duration.zero); + } + + + /++ + Sets the total time which the StopWatch has been running (i.e. what peek + returns). + + The StopWatch does not have to be stopped for setTimeElapsed to be + called, nor will calling it cause the StopWatch to stop. + +/ + void setTimeElapsed(Duration timeElapsed) @safe nothrow @nogc + { + enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); + _ticksElapsed = convClockFreq(timeElapsed.total!"hnsecs", hnsecsPerSecond, MonoTime.ticksPerSecond); + _timeStarted = MonoTime.currTime; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + StopWatch sw; + sw.setTimeElapsed(hours(1)); + + // As discussed in MonoTime's documentation, converting between + // Duration and ticks is not exact, though it will be close. + // How exact it is depends on the frequency/resolution of the + // system's monotonic clock. + assert(abs(sw.peek() - hours(1)) < usecs(1)); + + sw.start(); + Thread.sleep(usecs(1)); + assert(sw.peek() > hours(1) + usecs(1)); + } + + + /++ + Returns whether this StopWatch is currently running. + +/ + @property bool running() @safe const pure nothrow @nogc + { + return _running; + } + + /// + @safe nothrow @nogc unittest + { + StopWatch sw; + assert(!sw.running); + sw.start(); + assert(sw.running); + sw.stop(); + assert(!sw.running); + } + + +private: + + // We track the ticks for the elapsed time rather than a Duration so that we + // don't lose any precision. + + bool _running = false; // Whether the StopWatch is currently running + MonoTime _timeStarted; // The time the StopWatch started measuring (i.e. when it was started or reset). + long _ticksElapsed; // Total time that the StopWatch ran before it was stopped last. +} + + +/++ + Benchmarks code for speed assessment and comparison. + + Params: + fun = aliases of callable objects (e.g. function names). Each callable + object should take no arguments. + n = The number of times each function is to be executed. + + Returns: + The amount of time (as a $(REF Duration,core,time)) that it took to call + each function $(D n) times. The first value is the length of time that + it took to call $(D fun[0]) $(D n) times. The second value is the length + of time it took to call $(D fun[1]) $(D n) times. Etc. + +/ +Duration[fun.length] benchmark(fun...)(uint n) +{ + Duration[fun.length] result; + auto sw = StopWatch(AutoStart.yes); + + foreach (i, unused; fun) + { + sw.reset(); + foreach (_; 0 .. n) + fun[i](); + result[i] = sw.peek(); + } + + return result; +} + +/// +@safe unittest +{ + import std.conv : to; + + int a; + void f0() {} + void f1() { auto b = a; } + void f2() { auto b = to!string(a); } + auto r = benchmark!(f0, f1, f2)(10_000); + Duration f0Result = r[0]; // time f0 took to run 10,000 times + Duration f1Result = r[1]; // time f1 took to run 10,000 times + Duration f2Result = r[2]; // time f2 took to run 10,000 times +} + +@safe nothrow unittest +{ + import std.conv : to; + + int a; + void f0() nothrow {} + void f1() nothrow { auto b = to!string(a); } + auto r = benchmark!(f0, f1)(1000); + assert(r[0] > Duration.zero); + assert(r[1] > Duration.zero); + assert(r[1] > r[0]); + assert(r[0] < seconds(1)); + assert(r[1] < seconds(1)); +} + +@safe nothrow @nogc unittest +{ + int f0Count; + int f1Count; + int f2Count; + void f0() nothrow @nogc { ++f0Count; } + void f1() nothrow @nogc { ++f1Count; } + void f2() nothrow @nogc { ++f2Count; } + auto r = benchmark!(f0, f1, f2)(552); + assert(f0Count == 552); + assert(f1Count == 552); + assert(f2Count == 552); +} diff --git a/std/datetime/systime.d b/std/datetime/systime.d new file mode 100644 index 00000000000..8bc284eeffe --- /dev/null +++ b/std/datetime/systime.d @@ -0,0 +1,11080 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/datetime/_systime.d) ++/ +module std.datetime.systime; + +import core.time; +import std.datetime.date; +import std.datetime.timezone; +import std.format : format; +import std.exception : enforce; +import std.range.primitives; +import std.traits : isIntegral, isSigned, isSomeString, Unqual; + +version(Windows) +{ + import core.stdc.time : time_t; + import core.sys.windows.windows; + import core.sys.windows.winsock2; +} +else version(Posix) +{ + import core.sys.posix.signal : timespec; + import core.sys.posix.sys.types : time_t; +} + +version(unittest) +{ + import core.exception : AssertError; + import std.exception : assertThrown; +} + + +@safe unittest +{ + initializeTests(); +} + + +/++ + Effectively a namespace to make it clear that the methods it contains are + getting the time from the system clock. It cannot be instantiated. + +/ +final class Clock +{ +public: + + /++ + Returns the current time in the given time zone. + + Params: + clockType = The $(REF ClockType, core,time) indicates which system + clock to use to get the current time. Very few programs + need to use anything other than the default. + tz = The time zone for the SysTime that's returned. + + Throws: + $(REF DateTimeException,std,datetime,date) if it fails to get the + time. + +/ + static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe + { + return SysTime(currStdTime!clockType, tz); + } + + @safe unittest + { + import std.format : format; + import std.stdio : writefln; + assert(currTime().timezone is LocalTime()); + assert(currTime(UTC()).timezone is UTC()); + + // core.stdc.time.time does not always use unix time on Windows systems. + // In particular, dmc does not use unix time. If we can guarantee that + // the MS runtime uses unix time, then we may be able run this test + // then, but for now, we're just not going to run this test on Windows. + version(Posix) + { + static import core.stdc.time; + static import std.math; + immutable unixTimeD = currTime().toUnixTime(); + immutable unixTimeC = core.stdc.time.time(null); + assert(std.math.abs(unixTimeC - unixTimeD) <= 2); + } + + auto norm1 = Clock.currTime; + auto norm2 = Clock.currTime(UTC()); + assert(norm1 <= norm2, format("%s %s", norm1, norm2)); + assert(abs(norm1 - norm2) <= seconds(2)); + + import std.meta : AliasSeq; + foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) + { + scope(failure) writefln("ClockType.%s", ct); + auto value1 = Clock.currTime!ct; + auto value2 = Clock.currTime!ct(UTC()); + assert(value1 <= value2, format("%s %s", value1, value2)); + assert(abs(value1 - value2) <= seconds(2)); + } + } + + + /++ + Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the + current time. + + Params: + clockType = The $(REF ClockType, core,time) indicates which system + clock to use to get the current time. Very few programs + need to use anything other than the default. + + Throws: + $(REF DateTimeException,std,datetime,date) if it fails to get the + time. + +/ + static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted + { + static if (clockType != ClockType.coarse && + clockType != ClockType.normal && + clockType != ClockType.precise && + clockType != ClockType.second) + { + static assert(0, format("ClockType.%s is not supported by Clock.currTime or Clock.currStdTime", clockType)); + } + + version(Windows) + { + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + immutable result = FILETIMEToStdTime(&fileTime); + static if (clockType == ClockType.second) + { + // Ideally, this would use core.std.time.time, but the C runtime + // has to be using unix time for that to work, and that's not + // guaranteed on Windows. Digital Mars does not use unix time. + // MS may or may not. If it does, then this can be made to use + // core.stdc.time for MS, but for now, we'll leave it like this. + return convert!("seconds", "hnsecs")(convert!("hnsecs", "seconds")(result)); + } + else + return result; + } + else version(Posix) + { + static import core.stdc.time; + enum hnsecsToUnixEpoch = unixTimeToStdTime(0); + + version(OSX) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.posix.sys.time : gettimeofday, timeval; + timeval tv; + if (gettimeofday(&tv, null) != 0) + throw new TimeException("Call to gettimeofday() failed"); + return convert!("seconds", "hnsecs")(tv.tv_sec) + + convert!("usecs", "hnsecs")(tv.tv_usec) + + hnsecsToUnixEpoch; + } + } + else version(linux) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.linux.time : CLOCK_REALTIME_COARSE; + import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + } + else version(FreeBSD) + { + import core.sys.freebsd.time : clock_gettime, CLOCK_REALTIME, + CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; + else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + else version(NetBSD) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.posix.sys.time : gettimeofday, timeval; + timeval tv; + if (gettimeofday(&tv, null) != 0) + throw new TimeException("Call to gettimeofday() failed"); + return convert!("seconds", "hnsecs")(tv.tv_sec) + + convert!("usecs", "hnsecs")(tv.tv_usec) + + hnsecsToUnixEpoch; + } + } + else version(Solaris) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.solaris.time : CLOCK_REALTIME; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + } + else static assert(0, "Unsupported OS"); + } + else static assert(0, "Unsupported OS"); + } + + @safe unittest + { + import std.format : format; + import std.math : abs; + import std.meta : AliasSeq; + import std.stdio : writefln; + enum limit = convert!("seconds", "hnsecs")(2); + + auto norm1 = Clock.currStdTime; + auto norm2 = Clock.currStdTime; + assert(norm1 <= norm2, format("%s %s", norm1, norm2)); + assert(abs(norm1 - norm2) <= limit); + + foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) + { + scope(failure) writefln("ClockType.%s", ct); + auto value1 = Clock.currStdTime!ct; + auto value2 = Clock.currStdTime!ct; + assert(value1 <= value2, format("%s %s", value1, value2)); + assert(abs(value1 - value2) <= limit); + } + } + + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use core.time.MonoTime.currTime instead") + static @property TickDuration currSystemTick() @safe nothrow + { + return TickDuration.currSystemTick; + } + + deprecated @safe unittest + { + assert(Clock.currSystemTick.length > 0); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use core.time.MonoTime instead. See currAppTick's documentation for details.") + static @property TickDuration currAppTick() @safe + { + return currSystemTick - TickDuration.appOrigin; + } + + deprecated @safe unittest + { + auto a = Clock.currSystemTick; + auto b = Clock.currAppTick; + assert(a.length); + assert(b.length); + assert(a > b); + } + +private: + + @disable this() {} +} + + +/++ + $(D SysTime) is the type used to get the current time from the + system or doing anything that involves time zones. Unlike + $(REF DateTime,std,datetime,date), the time zone is an integral part of + $(D SysTime) (though for local time applications, time zones can be ignored + and it will work, since it defaults to using the local time zone). It holds + its internal time in std time (hnsecs since midnight, January 1st, 1 A.D. + UTC), so it interfaces well with the system time. However, that means that, + unlike $(REF DateTime,std,datetime,date), it is not optimized for + calendar-based operations, and getting individual units from it such as + years or days is going to involve conversions and be less efficient. + + For calendar-based operations that don't + care about time zones, then $(REF DateTime,std,datetime,date) would be + the type to use. For system time, use $(D SysTime). + + $(LREF Clock.currTime) will return the current time as a $(D SysTime). + To convert a $(D SysTime) to a $(REF Date,std,datetime,date) or + $(REF DateTime,std,datetime,date), simply cast it. To convert a + $(REF Date,std,datetime,date) or $(REF DateTime,std,datetime,date) to a + $(D SysTime), use $(D SysTime)'s constructor, and pass in the ntended time + zone with it (or don't pass in a $(REF TimeZone,std,datetime,timezone), and + the local time zone will be used). Be aware, however, that converting from a + $(REF DateTime,std,datetime,date) to a $(D SysTime) will not necessarily + be 100% accurate due to DST (one hour of the year doesn't exist and another + occurs twice). To not risk any conversion errors, keep times as + $(D SysTime)s. Aside from DST though, there shouldn't be any conversion + problems. + + For using time zones other than local time or UTC, use + $(REF PosixTimeZone,std,datetime,timezone) on Posix systems (or on Windows, + if providing the TZ Database files), and use + $(REF WindowsTimeZone,std,datetime,timezone) on Windows systems. The time in + $(D SysTime) is kept internally in hnsecs from midnight, January 1st, 1 A.D. + UTC. Conversion error cannot happen when changing the time zone of a + $(D SysTime). $(REF LocalTime,std,datetime,timezone) is the + $(REF TimeZone,std,datetime,timezone) class which represents the local time, + and $(D UTC) is the $(REF TimeZone,std,datetime,timezone) class which + represents UTC. $(D SysTime) uses $(REF LocalTime,std,datetime,timezone) if + no $(REF TimeZone,std,datetime,timezone) is provided. For more details on + time zones, see the documentation for $(REF TimeZone,std,datetime,timezone), + $(REF PosixTimeZone,std,datetime,timezone), and + $(REF WindowsTimeZone,std,datetime,timezone). + + $(D SysTime)'s range is from approximately 29,000 B.C. to approximately + 29,000 A.D. + +/ +struct SysTime +{ + import core.stdc.time : tm; + version(Posix) import core.sys.posix.sys.time : timeval; + import std.typecons : Rebindable; + +public: + + /++ + Params: + dateTime = The $(REF DateTime,std,datetime,date) to use to set + this $(LREF SysTime)'s internal std time. As + $(REF DateTime,std,datetime,date) has no concept of + time zone, tz is used as its time zone. + tz = The $(REF TimeZone,std,datetime,timezone) to use for this + $(LREF SysTime). If null, + $(REF LocalTime,std,datetime,timezone) will be used. The + given $(REF DateTime,std,datetime,date) is assumed to + be in the given time zone. + +/ + this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow + { + try + this(dateTime, Duration.zero, tz); + catch (Exception e) + assert(0, "SysTime's constructor threw when it shouldn't have."); + } + + @safe unittest + { + static void test(DateTime dt, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(dt, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given DateTime: %s", dt)); + } + + test(DateTime.init, UTC(), 0); + test(DateTime(1, 1, 1, 12, 30, 33), UTC(), 450_330_000_000L); + test(DateTime(0, 12, 31, 12, 30, 33), UTC(), -413_670_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), UTC(), 0); + test(DateTime(1, 1, 1, 0, 0, 1), UTC(), 10_000_000L); + test(DateTime(0, 12, 31, 23, 59, 59), UTC(), -10_000_000L); + + test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0); + test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L); + } + + /++ + Params: + dateTime = The $(REF DateTime,std,datetime,date) to use to set + this $(LREF SysTime)'s internal std time. As + $(REF DateTime,std,datetime,date) has no concept of + time zone, tz is used as its time zone. + fracSecs = The fractional seconds portion of the time. + tz = The $(REF TimeZone,std,datetime,timezone) to use for this + $(LREF SysTime). If null, + $(REF LocalTime,std,datetime,timezone) will be used. The + given $(REF DateTime,std,datetime,date) is assumed to + be in the given time zone. + + Throws: + $(REF DateTimeException,std,datetime,date) if $(D fracSecs) is negative or if it's + greater than or equal to one second. + +/ + this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe + { + enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); + auto nonNullTZ = tz is null ? LocalTime() : tz; + + immutable dateDiff = dateTime.date - Date.init; + immutable todDiff = dateTime.timeOfDay - TimeOfDay.init; + + immutable adjustedTime = dateDiff + todDiff + fracSecs; + immutable standardTime = nonNullTZ.tzToUTC(adjustedTime.total!"hnsecs"); + + this(standardTime, nonNullTZ); + } + + @safe unittest + { + static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(dt, fracSecs, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), + format("Given DateTime: %s, Given Duration: %s", dt, fracSecs)); + } + + test(DateTime.init, Duration.zero, UTC(), 0); + test(DateTime(1, 1, 1, 12, 30, 33), Duration.zero, UTC(), 450_330_000_000L); + test(DateTime(0, 12, 31, 12, 30, 33), Duration.zero, UTC(), -413_670_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), msecs(1), UTC(), 10_000L); + test(DateTime(0, 12, 31, 23, 59, 59), msecs(999), UTC(), -10_000L); + + test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC(), -1); + test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC(), -9_999_999); + test(DateTime(0, 12, 31, 23, 59, 59), Duration.zero, UTC(), -10_000_000); + + assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC())); + assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC())); + } + + // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ + deprecated("Please use the overload which takes a Duration instead of a FracSec.") + this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) @safe + { + immutable fracHNSecs = fracSec.hnsecs; + enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + _timezone = tz is null ? LocalTime() : tz; + + try + { + immutable dateDiff = (dateTime.date - Date(1, 1, 1)).total!"hnsecs"; + immutable todDiff = (dateTime.timeOfDay - TimeOfDay(0, 0, 0)).total!"hnsecs"; + + immutable adjustedTime = dateDiff + todDiff + fracHNSecs; + immutable standardTime = _timezone.tzToUTC(adjustedTime); + + this(standardTime, _timezone); + } + catch (Exception e) + assert(0, "Date, TimeOfDay, or DateTime's constructor threw when it shouldn't have."); + } + + deprecated @safe unittest + { + static void test(DateTime dt, FracSec fracSec, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(dt, fracSec, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), + format("Given DateTime: %s, Given FracSec: %s", dt, fracSec)); + } + + test(DateTime.init, FracSec.init, UTC(), 0); + test(DateTime(1, 1, 1, 12, 30, 33), FracSec.init, UTC(), 450_330_000_000L); + test(DateTime(0, 12, 31, 12, 30, 33), FracSec.init, UTC(), -413_670_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC(), 10_000L); + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC(), -10_000L); + + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC(), -1); + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC(), -9_999_999); + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0), UTC(), -10_000_000); + + assertThrown!DateTimeException(SysTime(DateTime.init, FracSec.from!"hnsecs"(-1), UTC())); + } + + /++ + Params: + date = The $(REF Date,std,datetime,date) to use to set this + $(LREF SysTime)'s internal std time. As + $(REF Date,std,datetime,date) has no concept of time zone, tz + is used as its time zone. + tz = The $(REF TimeZone,std,datetime,timezone) to use for this + $(LREF SysTime). If null, + $(REF LocalTime,std,datetime,timezone) will be used. The + given $(REF Date,std,datetime,date) is assumed to be in the + given time zone. + +/ + this(in Date date, immutable TimeZone tz = null) @safe nothrow + { + _timezone = tz is null ? LocalTime() : tz; + + try + { + immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; + immutable standardTime = _timezone.tzToUTC(adjustedTime); + + this(standardTime, _timezone); + } + catch (Exception e) + assert(0, "Date's constructor through when it shouldn't have."); + } + + @safe unittest + { + static void test(Date d, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(d, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given Date: %s", d)); + } + + test(Date.init, UTC(), 0); + test(Date(1, 1, 1), UTC(), 0); + test(Date(1, 1, 2), UTC(), 864000000000); + test(Date(0, 12, 31), UTC(), -864000000000); + } + + /++ + Note: + Whereas the other constructors take in the given date/time, assume + that it's in the given time zone, and convert it to hnsecs in UTC + since midnight, January 1st, 1 A.D. UTC - i.e. std time - this + constructor takes a std time, which is specifically already in UTC, + so no conversion takes place. Of course, the various getter + properties and functions will use the given time zone's conversion + function to convert the results to that time zone, but no conversion + of the arguments to this constructor takes place. + + Params: + stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. + UTC. + tz = The $(REF TimeZone,std,datetime,timezone) to use for this + $(LREF SysTime). If null, + $(REF LocalTime,std,datetime,timezone) will be used. + +/ + this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow + { + _stdTime = stdTime; + _timezone = tz is null ? LocalTime() : tz; + } + + @safe unittest + { + static void test(long stdTime, immutable TimeZone tz) + { + auto sysTime = SysTime(stdTime, tz); + assert(sysTime._stdTime == stdTime); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given stdTime: %s", stdTime)); + } + + foreach (stdTime; [-1234567890L, -250, 0, 250, 1235657390L]) + { + foreach (tz; testTZs) + test(stdTime, tz); + } + } + + /++ + Params: + rhs = The $(LREF SysTime) to assign to this one. + +/ + ref SysTime opAssign(const ref SysTime rhs) return @safe pure nothrow + { + _stdTime = rhs._stdTime; + _timezone = rhs._timezone; + return this; + } + + /++ + Params: + rhs = The $(LREF SysTime) to assign to this one. + +/ + ref SysTime opAssign(SysTime rhs) scope return @safe pure nothrow + { + _stdTime = rhs._stdTime; + _timezone = rhs._timezone; + return this; + } + + /++ + Checks for equality between this $(LREF SysTime) and the given + $(LREF SysTime). + + Note that the time zone is ignored. Only the internal + std times (which are in UTC) are compared. + +/ + bool opEquals(const SysTime rhs) @safe const pure nothrow + { + return opEquals(rhs); + } + + /// ditto + bool opEquals(const ref SysTime rhs) @safe const pure nothrow + { + return _stdTime == rhs._stdTime; + } + + @safe unittest + { + import std.range : chain; + + assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC())); + assert(SysTime(DateTime.init, UTC()) == SysTime(0)); + assert(SysTime(Date.init, UTC()) == SysTime(0)); + assert(SysTime(0) == SysTime(0)); + + static void test(DateTime dt, immutable TimeZone tz1, immutable TimeZone tz2) + { + auto st1 = SysTime(dt); + st1.timezone = tz1; + + auto st2 = SysTime(dt); + st2.timezone = tz2; + + assert(st1 == st2); + } + + foreach (tz1; testTZs) + { + foreach (tz2; testTZs) + { + foreach (dt; chain(testDateTimesBC, testDateTimesAD)) + test(dt, tz1, tz2); + } + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + assert(st == st); + assert(st == cst); + //assert(st == ist); + assert(cst == st); + assert(cst == cst); + //assert(cst == ist); + //assert(ist == st); + //assert(ist == cst); + //assert(ist == ist); + } + + /++ + Compares this $(LREF SysTime) with the given $(LREF SysTime). + + Time zone is irrelevant when comparing $(LREF SysTime)s. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in SysTime rhs) @safe const pure nothrow + { + if (_stdTime < rhs._stdTime) + return -1; + if (_stdTime > rhs._stdTime) + return 1; + return 0; + } + + @safe unittest + { + import std.algorithm.iteration : map; + import std.array : array; + import std.range : chain; + + assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0); + assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0); + assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0); + assert(SysTime(0).opCmp(SysTime(0)) == 0); + + static void testEqual(SysTime st, immutable TimeZone tz1, immutable TimeZone tz2) + { + auto st1 = st; + st1.timezone = tz1; + + auto st2 = st; + st2.timezone = tz2; + + assert(st1.opCmp(st2) == 0); + } + + auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD))); + + foreach (st; sts) + { + foreach (tz1; testTZs) + { + foreach (tz2; testTZs) + testEqual(st, tz1, tz2); + } + } + + static void testCmp(SysTime st1, immutable TimeZone tz1, SysTime st2, immutable TimeZone tz2) + { + st1.timezone = tz1; + st2.timezone = tz2; + assert(st1.opCmp(st2) < 0); + assert(st2.opCmp(st1) > 0); + } + + foreach (si, st1; sts) + { + foreach (st2; sts[si + 1 .. $]) + { + foreach (tz1; testTZs) + { + foreach (tz2; testTZs) + testCmp(st1, tz1, st2, tz2); + } + } + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + assert(st.opCmp(st) == 0); + assert(st.opCmp(cst) == 0); + //assert(st.opCmp(ist) == 0); + assert(cst.opCmp(st) == 0); + assert(cst.opCmp(cst) == 0); + //assert(cst.opCmp(ist) == 0); + //assert(ist.opCmp(st) == 0); + //assert(ist.opCmp(cst) == 0); + //assert(ist.opCmp(ist) == 0); + } + + /** + * Returns: A hash of the $(LREF SysTime) + */ + size_t toHash() const @nogc pure nothrow @safe + { + static if (is(size_t == ulong)) + return _stdTime; + else + { + // MurmurHash2 + enum ulong m = 0xc6a4a7935bd1e995UL; + enum ulong n = m * 16; + enum uint r = 47; + + ulong k = _stdTime; + k *= m; + k ^= k >> r; + k *= m; + + ulong h = n; + h ^= k; + h *= m; + + return cast(size_t) h; + } + } + + @safe unittest + { + assert(SysTime(0).toHash == SysTime(0).toHash); + assert(SysTime(DateTime(2000, 1, 1)).toHash == SysTime(DateTime(2000, 1, 1)).toHash); + assert(SysTime(DateTime(2000, 1, 1)).toHash != SysTime(DateTime(2000, 1, 2)).toHash); + + // test that timezones aren't taken into account + assert(SysTime(0, LocalTime()).toHash == SysTime(0, LocalTime()).toHash); + assert(SysTime(0, LocalTime()).toHash == SysTime(0, UTC()).toHash); + assert(SysTime(DateTime(2000, 1, 1), LocalTime()).toHash == SysTime(DateTime(2000, 1, 1), LocalTime()).toHash); + immutable zone = new SimpleTimeZone(dur!"minutes"(60)); + assert(SysTime(DateTime(2000, 1, 1, 1), zone).toHash == SysTime(DateTime(2000, 1, 1), UTC()).toHash); + assert(SysTime(DateTime(2000, 1, 1), zone).toHash != SysTime(DateTime(2000, 1, 1), UTC()).toHash); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const nothrow + { + return (cast(Date) this).year; + } + + @safe unittest + { + import std.range : chain; + static void test(SysTime sysTime, long expected) + { + assert(sysTime.year == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 1); + test(SysTime(1, UTC()), 1); + test(SysTime(-1, UTC()), 0); + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + { + auto dt = DateTime(Date(year, md.month, md.day), tod); + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), year); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.year == 1999); + //assert(ist.year == 1999); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this $(LREF SysTime)'s year to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the new year is not + a leap year and the resulting date would be on February 29th. + +/ + @property void year(int year) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.year = year; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); + assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); + assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime st, int year, in SysTime expected) + { + st.year = year; + assert(st == expected); + } + + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + foreach (year; chain(testYearsBC, testYearsAD)) + { + auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + test(st, year, e); + } + } + + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + test(SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz), 2000, + SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz)); + test(SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz), 1999, + SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz)); + } + + foreach (tod; testTODsThrown) + { + auto st = SysTime(DateTime(Date(2000, 2, 29), tod), fs, tz); + assertThrown!DateTimeException(st.year = 1999); + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.year = 7)); + //static assert(!__traits(compiles, ist.year = 7)); + } + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(REF DateTimeException,std,datetime,date) if $(D isAD) is true. + +/ + @property ushort yearBC() @safe const + { + return (cast(Date) this).yearBC; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); + assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); + assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); + } + + @safe unittest + { + import std.exception : assertNotThrown; + foreach (st; testSysTimesBC) + { + auto msg = format("SysTime: %s", st); + assertNotThrown!DateTimeException(st.yearBC, msg); + assert(st.yearBC == (st.year * -1) + 1, msg); + } + + foreach (st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]]) + assertThrown!DateTimeException(st.yearBC, format("SysTime: %s", st)); + + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.year = 12; + assert(st.year == 12); + static assert(!__traits(compiles, cst.year = 12)); + //static assert(!__traits(compiles, ist.year = 12)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF SysTime)'s year to. + + Throws: + $(REF DateTimeException,std,datetime,date) if a non-positive value + is given. + +/ + @property void yearBC(int year) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.yearBC = year; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + @safe unittest + { + auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); + st.yearBC = 1; + assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0))); + + st.yearBC = 10; + assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); + } + + @safe unittest + { + import std.range : chain; + static void test(SysTime st, int year, in SysTime expected) + { + st.yearBC = year; + assert(st == expected, format("SysTime: %s", st)); + } + + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + foreach (year; testYearsBC) + { + auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + test(st, (year * -1) + 1, e); + } + } + + foreach (st; [testSysTimesBC[0], testSysTimesBC[$ - 1], testSysTimesAD[0], testSysTimesAD[$ - 1]]) + { + foreach (year; testYearsBC) + assertThrown!DateTimeException(st.yearBC = year); + } + + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + test(SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz), 2001, + SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz)); + test(SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz), 2000, + SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz)); + } + + foreach (tod; testTODsThrown) + { + auto st = SysTime(DateTime(Date(-2000, 2, 29), tod), fs, tz); + assertThrown!DateTimeException(st.year = -1999); + } + } + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.yearBC = 12; + assert(st.yearBC == 12); + static assert(!__traits(compiles, cst.yearBC = 12)); + //static assert(!__traits(compiles, ist.yearBC = 12)); + } + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const nothrow + { + return (cast(Date) this).month; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); + assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); + assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, Month expected) + { + assert(sysTime.month == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), Month.jan); + test(SysTime(1, UTC()), Month.jan); + test(SysTime(-1, UTC()), Month.dec); + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + { + auto dt = DateTime(Date(year, md.month, md.day), tod); + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + test(SysTime(dt, fs, tz), md.month); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.month == 7); + //assert(ist.month == 7); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF SysTime)'s month to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given month is + not a valid month. + +/ + @property void month(Month month) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.month = month; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + @safe unittest + { + import std.algorithm.iteration : filter; + import std.range : chain; + + static void test(SysTime st, Month month, in SysTime expected) + { + st.month = cast(Month) month; + assert(st == expected); + } + + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + foreach (md; testMonthDays) + { + if (st.day > maxDay(dt.year, md.month)) + continue; + auto e = SysTime(DateTime(dt.year, md.month, dt.day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + test(st, md.month, e); + } + } + + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + foreach (year; filter!((a){return yearIsLeapYear(a);}) (chain(testYearsBC, testYearsAD))) + { + test(SysTime(DateTime(Date(year, 1, 29), tod), fs, tz), + Month.feb, + SysTime(DateTime(Date(year, 2, 29), tod), fs, tz)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + test(SysTime(DateTime(Date(year, 1, 28), tod), fs, tz), + Month.feb, + SysTime(DateTime(Date(year, 2, 28), tod), fs, tz)); + test(SysTime(DateTime(Date(year, 7, 30), tod), fs, tz), + Month.jun, + SysTime(DateTime(Date(year, 6, 30), tod), fs, tz)); + } + } + } + } + + foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) + { + foreach (tz; testTZs) + { + foreach (tod; testTODsThrown) + { + foreach (year; [testYearsBC[$-3], testYearsBC[$-2], + testYearsBC[$-2], testYearsAD[0], + testYearsAD[$-2], testYearsAD[$-1]]) + { + auto day = yearIsLeapYear(year) ? 30 : 29; + auto st1 = SysTime(DateTime(Date(year, 1, day), tod), fs, tz); + assertThrown!DateTimeException(st1.month = Month.feb); + + auto st2 = SysTime(DateTime(Date(year, 7, 31), tod), fs, tz); + assertThrown!DateTimeException(st2.month = Month.jun); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.month = 12)); + //static assert(!__traits(compiles, ist.month = 12)); + } + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const nothrow + { + return (cast(Date) this).day; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); + assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); + assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.day == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 1); + test(SysTime(1, UTC()), 1); + test(SysTime(-1, UTC()), 31); + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + { + auto dt = DateTime(Date(year, md.month, md.day), tod); + + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), md.day); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.day == 6); + //assert(ist.day == 6); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF SysTime)'s day to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given day is not + a valid day of the current month. + +/ + @property void day(int day) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.day = day; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + import std.traits : EnumMembers; + + foreach (day; chain(testDays)) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + if (day > maxDay(dt.year, dt.month)) + continue; + auto expected = SysTime(DateTime(dt.year, dt.month, day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + st.day = day; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + foreach (fs; testFracSecs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (month; EnumMembers!Month) + { + auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); + immutable max = maxDay(year, month); + auto expected = SysTime(DateTime(Date(year, month, max), tod), fs, tz); + + st.day = max; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + } + } + } + + foreach (tz; testTZs) + { + foreach (tod; testTODsThrown) + { + foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) + { + foreach (year; [testYearsBC[$-3], testYearsBC[$-2], + testYearsBC[$-2], testYearsAD[0], + testYearsAD[$-2], testYearsAD[$-1]]) + { + foreach (month; EnumMembers!Month) + { + auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); + immutable max = maxDay(year, month); + + assertThrown!DateTimeException(st.day = max + 1); + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.day = 27)); + //static assert(!__traits(compiles, ist.day = 27)); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + return cast(ubyte) getUnitsFromHNSecs!"hours"(hnsecs); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.hour == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 0); + test(SysTime(1, UTC()), 0); + test(SysTime(-1, UTC()), 23); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), hour); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.hour == 12); + //assert(ist.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hours to set this $(LREF SysTime)'s hour to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given hour are + not a valid hour of the day. + +/ + @property void hour(int hour) @safe + { + enforceValid!"hours"(hour); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); + hnsecs += convert!("hours", "hnsecs")(hour); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + + foreach (hour; chain(testHours)) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + st.hour = hour; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.hour = -1); + assertThrown!DateTimeException(st.hour = 60); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.hour = 27)); + //static assert(!__traits(compiles, ist.hour = 27)); + } + + + /++ + Minutes past the current hour. + +/ + @property ubyte minute() @safe const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); + + return cast(ubyte) getUnitsFromHNSecs!"minutes"(hnsecs); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.minute == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 0); + test(SysTime(1, UTC()), 0); + test(SysTime(-1, UTC()), 59); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), minute); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.minute == 30); + //assert(ist.minute == 30); + } + + + /++ + Minutes past the current hour. + + Params: + minute = The minute to set this $(LREF SysTime)'s minute to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given minute are + not a valid minute of an hour. + +/ + @property void minute(int minute) @safe + { + enforceValid!"minutes"(minute); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); + + hnsecs += convert!("hours", "hnsecs")(hour); + hnsecs += convert!("minutes", "hnsecs")(minute); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + + foreach (minute; testMinSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, minute, dt.second), + st.fracSecs, + st.timezone); + st.minute = minute; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.minute = -1); + assertThrown!DateTimeException(st.minute = 60); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.minute = 27)); + //static assert(!__traits(compiles, ist.minute = 27)); + } + + + /++ + Seconds past the current minute. + +/ + @property ubyte second() @safe const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); + hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); + + return cast(ubyte) getUnitsFromHNSecs!"seconds"(hnsecs); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.second == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 0); + test(SysTime(1, UTC()), 0); + test(SysTime(-1, UTC()), 59); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), second); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.second == 33); + //assert(ist.second == 33); + } + + + /++ + Seconds past the current minute. + + Params: + second = The second to set this $(LREF SysTime)'s second to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given second are + not a valid second of a minute. + +/ + @property void second(int second) @safe + { + enforceValid!"seconds"(second); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); + + hnsecs += convert!("hours", "hnsecs")(hour); + hnsecs += convert!("minutes", "hnsecs")(minute); + hnsecs += convert!("seconds", "hnsecs")(second); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + + foreach (second; testMinSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, second), + st.fracSecs, + st.timezone); + st.second = second; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.second = -1); + assertThrown!DateTimeException(st.second = 60); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.seconds = 27)); + //static assert(!__traits(compiles, ist.seconds = 27)); + } + + + /++ + Fractional seconds past the second (i.e. the portion of a + $(LREF SysTime) which is less than a second). + +/ + @property Duration fracSecs() @safe const nothrow + { + auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + return dur!"hnsecs"(removeUnitsFromHNSecs!"seconds"(hnsecs)); + } + + /// + @safe unittest + { + import core.time : msecs, usecs, hnsecs, nsecs; + import std.datetime.date : DateTime; + + auto dt = DateTime(1982, 4, 1, 20, 59, 22); + assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); + assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202)); + assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567)); + + // SysTime and Duration both have a precision of hnsecs (100 ns), + // so nsecs are going to be truncated. + assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700)); + } + + @safe unittest + { + import std.range : chain; + + assert(SysTime(0, UTC()).fracSecs == Duration.zero); + assert(SysTime(1, UTC()).fracSecs == hnsecs(1)); + assert(SysTime(-1, UTC()).fracSecs == hnsecs(9_999_999)); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + assert(SysTime(dt, fs, tz).fracSecs == fs); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.fracSecs == Duration.zero); + //assert(ist.fracSecs == Duration.zero); + } + + + /++ + Fractional seconds past the second (i.e. the portion of a + $(LREF SysTime) which is less than a second). + + Params: + fracSecs = The duration to set this $(LREF SysTime)'s fractional + seconds to. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given duration + is negative or if it's greater than or equal to one second. + +/ + @property void fracSecs(Duration fracSecs) @safe + { + enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); + + auto oldHNSecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(oldHNSecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = oldHNSecs < 0; + + if (negative) + oldHNSecs += convert!("hours", "hnsecs")(24); + + immutable seconds = splitUnitsFromHNSecs!"seconds"(oldHNSecs); + immutable secondsHNSecs = convert!("seconds", "hnsecs")(seconds); + auto newHNSecs = fracSecs.total!"hnsecs" + secondsHNSecs; + + if (negative) + newHNSecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + newHNSecs; + } + + /// + @safe unittest + { + import core.time : Duration, msecs, hnsecs, nsecs; + import std.datetime.date : DateTime; + + auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); + assert(st.fracSecs == Duration.zero); + + st.fracSecs = msecs(213); + assert(st.fracSecs == msecs(213)); + + st.fracSecs = hnsecs(1234567); + assert(st.fracSecs == hnsecs(1234567)); + + // SysTime has a precision of hnsecs (100 ns), so nsecs are + // going to be truncated. + st.fracSecs = nsecs(123456789); + assert(st.fracSecs == hnsecs(1234567)); + } + + @safe unittest + { + import std.range : chain; + + foreach (fracSec; testFracSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(dt, fracSec, st.timezone); + st.fracSecs = fracSec; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.fracSecs = hnsecs(-1)); + assertThrown!DateTimeException(st.fracSecs = seconds(1)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.fracSecs = msecs(7))); + //static assert(!__traits(compiles, ist.fracSecs = msecs(7))); + } + + + // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ + deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). " ~ + "It returns a Duration instead of a FracSec, as FracSec is being deprecated.") + @property FracSec fracSec() @safe const nothrow + { + try + { + auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); + + return FracSec.from!"hnsecs"(cast(int) hnsecs); + } + catch (Exception e) + assert(0, "FracSec.from!\"hnsecs\"() threw."); + } + + deprecated @safe unittest + { + import std.range; + + static void test(SysTime sysTime, FracSec expected, size_t line = __LINE__) + { + if (sysTime.fracSec != expected) + throw new AssertError(format("Value given: %s", sysTime.fracSec), __FILE__, line); + } + + test(SysTime(0, UTC()), FracSec.from!"hnsecs"(0)); + test(SysTime(1, UTC()), FracSec.from!"hnsecs"(1)); + test(SysTime(-1, UTC()), FracSec.from!"hnsecs"(9_999_999)); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), FracSec.from!"hnsecs"(fs.total!"hnsecs")); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.fracSec == FracSec.zero); + //assert(ist.fracSec == FracSec.zero); + } + + + // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ + deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). " ~ + "It takes a Duration instead of a FracSec, as FracSec is being deprecated.") + @property void fracSec(FracSec fracSec) @safe + { + immutable fracHNSecs = fracSec.hnsecs; + enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + hnsecs = fracHNSecs; + hnsecs += convert!("hours", "hnsecs")(hour); + hnsecs += convert!("minutes", "hnsecs")(minute); + hnsecs += convert!("seconds", "hnsecs")(second); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + deprecated @safe unittest + { + import std.range; + + foreach (fracSec; testFracSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(dt, fracSec, st.timezone); + st.fracSec = FracSec.from!"hnsecs"(fracSec.total!"hnsecs"); + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.fracSec = FracSec.from!"hnsecs"(-1)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.fracSec = FracSec.from!"msecs"(7))); + //static assert(!__traits(compiles, ist.fracSec = FracSec.from!"msecs"(7))); + } + + + /++ + The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the + internal representation of $(LREF SysTime). + +/ + @property long stdTime() @safe const pure nothrow + { + return _stdTime; + } + + @safe unittest + { + assert(SysTime(0).stdTime == 0); + assert(SysTime(1).stdTime == 1); + assert(SysTime(-1).stdTime == -1); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()).stdTime == 330_000_502L); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.stdTime > 0); + //assert(ist.stdTime > 0); + } + + + /++ + The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the + internal representation of $(LREF SysTime). + + Params: + stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. + +/ + @property void stdTime(long stdTime) @safe pure nothrow + { + _stdTime = stdTime; + } + + @safe unittest + { + static void test(long stdTime, in SysTime expected, size_t line = __LINE__) + { + auto st = SysTime(0, UTC()); + st.stdTime = stdTime; + assert(st == expected); + } + + test(0, SysTime(Date(1, 1, 1), UTC())); + test(1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC())); + test(-1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC())); + test(330_000_502L, SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC())); + test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC())); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.stdTime = 27)); + //static assert(!__traits(compiles, ist.stdTime = 27)); + } + + + /++ + The current time zone of this $(LREF SysTime). Its internal time is + always kept in UTC, so there are no conversion issues between time zones + due to DST. Functions which return all or part of the time - such as + hours - adjust the time to this $(LREF SysTime)'s time zone before + returning. + +/ + @property immutable(TimeZone) timezone() @safe const pure nothrow + { + return _timezone; + } + + + /++ + The current time zone of this $(LREF SysTime). It's internal time is + always kept in UTC, so there are no conversion issues between time zones + due to DST. Functions which return all or part of the time - such as + hours - adjust the time to this $(LREF SysTime)'s time zone before + returning. + + Params: + timezone = The $(REF _TimeZone,std,datetime,_timezone) to set this + $(LREF SysTime)'s time zone to. + +/ + @property void timezone(immutable TimeZone timezone) @safe pure nothrow + { + if (timezone is null) + _timezone = LocalTime(); + else + _timezone = timezone; + } + + + /++ + Returns whether DST is in effect for this $(LREF SysTime). + +/ + @property bool dstInEffect() @safe const nothrow + { + return _timezone.dstInEffect(_stdTime); + // This function's unit testing is done in the time zone classes. + } + + + /++ + Returns what the offset from UTC is for this $(LREF SysTime). + It includes the DST offset in effect at that time (if any). + +/ + @property Duration utcOffset() @safe const nothrow + { + return _timezone.utcOffsetAt(_stdTime); + } + + + /++ + Returns a $(LREF SysTime) with the same std time as this one, but with + $(REF LocalTime,std,datetime,timezone) as its time zone. + +/ + SysTime toLocalTime() @safe const pure nothrow + { + return SysTime(_stdTime, LocalTime()); + } + + @safe unittest + { + { + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); + assert(sysTime == sysTime.toLocalTime()); + assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone is sysTime.timezone); + assert(sysTime.toLocalTime().timezone !is UTC()); + } + + { + auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27), stz); + assert(sysTime == sysTime.toLocalTime()); + assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone !is UTC()); + assert(sysTime.toLocalTime().timezone !is stz); + } + } + + + /++ + Returns a $(LREF SysTime) with the same std time as this one, but with + $(D UTC) as its time zone. + +/ + SysTime toUTC() @safe const pure nothrow + { + return SysTime(_stdTime, UTC()); + } + + @safe unittest + { + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); + assert(sysTime == sysTime.toUTC()); + assert(sysTime._stdTime == sysTime.toUTC()._stdTime); + assert(sysTime.toUTC().timezone is UTC()); + assert(sysTime.toUTC().timezone !is LocalTime()); + assert(sysTime.toUTC().timezone !is sysTime.timezone); + } + + + /++ + Returns a $(LREF SysTime) with the same std time as this one, but with + given time zone as its time zone. + +/ + SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow + { + if (tz is null) + return SysTime(_stdTime, LocalTime()); + else + return SysTime(_stdTime, tz); + } + + @safe unittest + { + auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); + assert(sysTime == sysTime.toOtherTZ(stz)); + assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime); + assert(sysTime.toOtherTZ(stz).timezone is stz); + assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); + assert(sysTime.toOtherTZ(stz).timezone !is UTC()); + } + + + /++ + Converts this $(LREF SysTime) to unix time (i.e. seconds from midnight, + January 1st, 1970 in UTC). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + By default, the return type is time_t (which is normally an alias for + int on 32-bit systems and long on 64-bit systems), but if a different + size is required than either int or long can be passed as a template + argument to get the desired size. + + If the return type is int, and the result can't fit in an int, then the + closest value that can be held in 32 bits will be used (so $(D int.max) + if it goes over and $(D int.min) if it goes under). However, no attempt + is made to deal with integer overflow if the return type is long. + + Params: + T = The return type (int or long). It defaults to time_t, which is + normally 32 bits on a 32-bit system and 64 bits on a 64-bit + system. + + Returns: + A signed integer representing the unix time which is equivalent to + this SysTime. + +/ + T toUnixTime(T = time_t)() @safe const pure nothrow + if (is(T == int) || is(T == long)) + { + return stdTimeToUnixTime!T(_stdTime); + } + + /// + @safe unittest + { + import core.time : hours; + import std.datetime.date : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + + assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); + + auto pst = new immutable SimpleTimeZone(hours(-8)); + assert(SysTime(DateTime(1970, 1, 1), pst).toUnixTime() == 28800); + + auto utc = SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()); + assert(utc.toUnixTime() == 1_198_311_285); + + auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst); + assert(ca.toUnixTime() == 1_198_340_085); + } + + @safe unittest + { + import std.meta : AliasSeq; + assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); + foreach (units; AliasSeq!("hnsecs", "usecs", "msecs")) + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), dur!units(1), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1); + } + + + /++ + Converts from unix time (i.e. seconds from midnight, January 1st, 1970 + in UTC) to a $(LREF SysTime). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + Params: + unixTime = Seconds from midnight, January 1st, 1970 in UTC. + tz = The time zone for the SysTime that's returned. + +/ + static SysTime fromUnixTime(long unixTime, immutable TimeZone tz = LocalTime()) @safe pure nothrow + { + return SysTime(unixTimeToStdTime(unixTime), tz); + } + + /// + @safe unittest + { + import core.time : hours; + import std.datetime.date : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + + assert(SysTime.fromUnixTime(0) == + SysTime(DateTime(1970, 1, 1), UTC())); + + auto pst = new immutable SimpleTimeZone(hours(-8)); + assert(SysTime.fromUnixTime(28800) == + SysTime(DateTime(1970, 1, 1), pst)); + + auto st1 = SysTime.fromUnixTime(1_198_311_285, UTC()); + assert(st1 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); + assert(st1.timezone is UTC()); + assert(st1 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); + + auto st2 = SysTime.fromUnixTime(1_198_311_285, pst); + assert(st2 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); + assert(st2.timezone is pst); + assert(st2 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); + } + + @safe unittest + { + assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC())); + assert(SysTime.fromUnixTime(1) == SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC())); + assert(SysTime.fromUnixTime(-1) == SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC())); + + auto st = SysTime.fromUnixTime(0); + auto dt = cast(DateTime) st; + assert(dt <= DateTime(1970, 2, 1) && dt >= DateTime(1969, 12, 31)); + assert(st.timezone is LocalTime()); + + auto aest = new immutable SimpleTimeZone(hours(10)); + assert(SysTime.fromUnixTime(-36000) == SysTime(DateTime(1970, 1, 1), aest)); + } + + + /++ + Returns a $(D timeval) which represents this $(LREF SysTime). + + Note that like all conversions in std.datetime, this is a truncating + conversion. + + If $(D timeval.tv_sec) is int, and the result can't fit in an int, then + the closest value that can be held in 32 bits will be used for + $(D tv_sec). (so $(D int.max) if it goes over and $(D int.min) if it + goes under). + +/ + timeval toTimeVal() @safe const pure nothrow + { + immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))(); + immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); + immutable tv_usec = cast(typeof(timeval.tv_usec))convert!("hnsecs", "usecs")(fracHNSecs); + return timeval(tv_sec, tv_usec); + } + + @safe unittest + { + assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1)); + assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeVal() == timeval(0, 7)); + + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeVal() == timeval(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeVal() == timeval(1, 1)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeVal() == timeval(1, 7)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeVal() == timeval(0, -1)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeVal() == timeval(0, -1)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeVal() == timeval(0, -999_001)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983)); + } + + + version(StdDdoc) + { + private struct timespec {} + /++ + Returns a $(D timespec) which represents this $(LREF SysTime). + + $(BLUE This function is Posix-Only.) + +/ + timespec toTimeSpec() @safe const pure nothrow; + } + else version(Posix) + { + timespec toTimeSpec() @safe const pure nothrow + { + immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))(); + immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); + immutable tv_nsec = cast(typeof(timespec.tv_nsec))convert!("hnsecs", "nsecs")(fracHNSecs); + return timespec(tv_sec, tv_nsec); + } + + @safe unittest + { + assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeSpec() == timespec(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(0, 900)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(0, 1000)); + assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeSpec() == timespec(0, 7000)); + + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeSpec() == timespec(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(1, 900)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(1, 1000)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeSpec() == timespec(1, 7000)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeSpec() == + timespec(0, -100)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeSpec() == + timespec(0, -1000)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeSpec() == + timespec(0, -1_000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeSpec() == + timespec(0, -999_001_000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeSpec() == + timespec(0, -1_000_000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeSpec() == + timespec(-1, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeSpec() == + timespec(-1, -999_983_000)); + } + } + + /++ + Returns a $(D tm) which represents this $(LREF SysTime). + +/ + tm toTM() @safe const nothrow + { + auto dateTime = cast(DateTime) this; + tm timeInfo; + + timeInfo.tm_sec = dateTime.second; + timeInfo.tm_min = dateTime.minute; + timeInfo.tm_hour = dateTime.hour; + timeInfo.tm_mday = dateTime.day; + timeInfo.tm_mon = dateTime.month - 1; + timeInfo.tm_year = dateTime.year - 1900; + timeInfo.tm_wday = dateTime.dayOfWeek; + timeInfo.tm_yday = dateTime.dayOfYear - 1; + timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); + + version(Posix) + { + import std.utf : toUTFz; + timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime); + auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName); + timeInfo.tm_zone = zone.toUTFz!(char*)(); + } + + return timeInfo; + } + + @system unittest + { + import std.conv : to; + + version(Posix) + { + scope(exit) clearTZEnvVar(); + setTZEnvVar("America/Los_Angeles"); + } + + { + auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); + + assert(timeInfo.tm_sec == 0); + assert(timeInfo.tm_min == 0); + assert(timeInfo.tm_hour == 0); + assert(timeInfo.tm_mday == 1); + assert(timeInfo.tm_mon == 0); + assert(timeInfo.tm_year == 70); + assert(timeInfo.tm_wday == 4); + assert(timeInfo.tm_yday == 0); + + version(Posix) + assert(timeInfo.tm_isdst == 0); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) + { + assert(timeInfo.tm_gmtoff == -8 * 60 * 60); + assert(to!string(timeInfo.tm_zone) == "PST"); + } + } + + { + auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), hnsecs(15)).toTM(); + + assert(timeInfo.tm_sec == 7); + assert(timeInfo.tm_min == 15); + assert(timeInfo.tm_hour == 12); + assert(timeInfo.tm_mday == 4); + assert(timeInfo.tm_mon == 6); + assert(timeInfo.tm_year == 110); + assert(timeInfo.tm_wday == 0); + assert(timeInfo.tm_yday == 184); + + version(Posix) + assert(timeInfo.tm_isdst == 1); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) + { + assert(timeInfo.tm_gmtoff == -7 * 60 * 60); + assert(to!string(timeInfo.tm_zone) == "PDT"); + } + } + } + + + /++ + Adds the given number of years or months to this $(LREF SysTime). A + negative number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF SysTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + if (units == "years" || units == "months") + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.add!units(value, allowOverflow); + days = date.dayOfGregorianCal - 1; + + if (days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + + return this; + } + + @safe unittest + { + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); + st1.add!"months"(11); + assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); + + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); + st2.add!"months"(-11); + assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); + + auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st3.add!"years"(1); + assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); + + auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st4.add!"years"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); + } + + // Test add!"years"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7); + assert(sysTime == SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9); + assert(sysTime == SysTime(Date(1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(Date(1999, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(7); + assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(-9); + assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); + } + + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), usecs(1207))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7); + assert(sysTime == SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9); + assert(sysTime == SysTime(Date(-1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(Date(-1999, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(-7); + assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(9); + assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); + } + + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), hnsecs(3))); + } + + // Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8); + assert(sysTime == SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8); + assert(sysTime == SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(1, 3, 1))); + } + + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-1, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5).add!"years"(7); + assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), msecs(555))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.add!"years"(4))); + //static assert(!__traits(compiles, ist.add!"years"(4))); + } + + // Test add!"years"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); + } + + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); + } + + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3))); + } + + // Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 2, 28))); + } + + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), msecs(555))); + } + } + + // Test add!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6); + assert(sysTime == SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27); + assert(sysTime == SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12); + assert(sysTime == SysTime(Date(2001, 3, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(1999, 10, 1))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(1999, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(1998, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(2000, 3, 2))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(1999, 1, 2))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(2001, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(2000, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6); + assert(sysTime == SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27); + assert(sysTime == SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(-1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12); + assert(sysTime == SysTime(Date(-2001, 3, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(-1997, 10, 1))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(-1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-1995, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-1996, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-2000, 3, 2))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-2001, 1, 2))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-1999, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-2000, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48); + assert(sysTime == SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49); + assert(sysTime == SysTime(Date(0, 3, 2))); + sysTime.add!"months"(49); + assert(sysTime == SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(Date(-3, 3, 3))); + sysTime.add!"months"(85); + assert(sysTime == SysTime(Date(4, 4, 3))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), msecs(9))); + sysTime.add!"months"(85); + assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85); + assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), msecs(9))); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85).add!"months"(-83); + assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.add!"months"(4))); + //static assert(!__traits(compiles, ist.add!"months"(4))); + } + + // Test add!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 2, 28))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 12, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1995, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 12, 29))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 2, 29))); + sysTime.add!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-3, 2, 28))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 28))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), msecs(9))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), msecs(9))); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); + } + } + + + /++ + Adds the given number of years or months to this $(LREF SysTime). A + negative number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF SysTime) 12 months + gets the exact same $(LREF SysTime). However, the days can still be + affected due to the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF SysTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + if (units == "years") + { + return add!"years"(value, allowOverflow); + } + + /// + @safe unittest + { + import std.datetime.date : AllowDayOverflow, DateTime; + + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); + st1.roll!"months"(1); + assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); + + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); + st2.roll!"months"(-1); + assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); + + auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); + st3.roll!"months"(1); + assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); + + auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); + st4.roll!"months"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); + + auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st5.roll!"years"(1); + assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); + + auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st6.roll!"years"(1, AllowDayOverflow.no); + assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.roll!"years"(4); + static assert(!__traits(compiles, cst.roll!"years"(4))); + //static assert(!__traits(compiles, ist.roll!"years"(4))); + } + + + // Shares documentation with "years" overload. + ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + if (units == "months") + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.roll!"months"(value, allowOverflow); + days = date.dayOfGregorianCal - 1; + + if (days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + + // Test roll!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6); + assert(sysTime == SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(1998, 10, 1))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1997, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1997, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1998, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1998, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1999, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1999, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6); + assert(sysTime == SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27); + assert(sysTime == SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(-1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(-1998, 10, 1))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(-1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-1997, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-1997, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-2002, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-2002, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-2001, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-2001, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), hnsecs(5007)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), hnsecs(5007))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), hnsecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(-2002, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(-2002, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(-2001, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(-2001, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48); + assert(sysTime == SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49); + assert(sysTime == SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(49); + assert(sysTime == SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48); + assert(sysTime == SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48); + assert(sysTime == SysTime(Date(-4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49); + assert(sysTime == SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(49); + assert(sysTime == SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(DateTime(4, 3, 2, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(DateTime(4, 4, 2, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(DateTime(-3, 5, 1, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85).roll!"months"(-83); + assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"months"(4))); + //static assert(!__traits(compiles, ist.roll!"months"(4))); + } + + // Test roll!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 3, 29))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 2, 29, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 3, 29, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 4, 30, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); + } + } + + + /++ + Adds the given number of units to this $(LREF SysTime). A negative number + will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF SysTime) one + year's worth of days gets the exact same $(LREF SysTime). + + Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), + $(D "minutes"), $(D "seconds"), $(D "msecs"), $(D "usecs"), and + $(D "hnsecs"). + + Note that when rolling msecs, usecs or hnsecs, they all add up to a + second. So, for example, rolling 1000 msecs is exactly the same as + rolling 100,000 usecs. + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this + $(LREF SysTime). + +/ + ref SysTime roll(string units)(long value) @safe nothrow + if (units == "days") + { + auto hnsecs = adjTime; + auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --gdays; + } + + auto date = Date(cast(int) gdays); + date.roll!"days"(value); + gdays = date.dayOfGregorianCal - 1; + + if (gdays < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++gdays; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + + /// + @safe unittest + { + import core.time : msecs, hnsecs; + import std.datetime.date : DateTime; + + auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); + st1.roll!"days"(1); + assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); + st1.roll!"days"(365); + assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); + st1.roll!"days"(-32); + assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); + + auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); + st2.roll!"hours"(1); + assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); + + auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); + st3.roll!"hours"(-1); + assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); + + auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st4.roll!"minutes"(1); + assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); + + auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st5.roll!"minutes"(-1); + assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); + + auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st6.roll!"seconds"(1); + assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); + + auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st7.roll!"seconds"(-1); + assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); + + auto dt = DateTime(2010, 1, 1, 0, 0, 0); + auto st8 = SysTime(dt); + st8.roll!"msecs"(1); + assert(st8 == SysTime(dt, msecs(1))); + + auto st9 = SysTime(dt); + st9.roll!"msecs"(-1); + assert(st9 == SysTime(dt, msecs(999))); + + auto st10 = SysTime(dt); + st10.roll!"hnsecs"(1); + assert(st10 == SysTime(dt, hnsecs(1))); + + auto st11 = SysTime(dt); + st11.roll!"hnsecs"(-1); + assert(st11 == SysTime(dt, hnsecs(9_999_999))); + } + + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(2000, 2, 29))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(2000, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 6, 30)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 6, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(1999, 1, 1)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 1, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(Date(1999, 7, 15))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(Date(1999, 7, 4))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(Date(1999, 7, 3))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(Date(1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1999, 7, 30))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1999, 7, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(1999, 7, 31))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1999, 7, 17))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1999, 2, 7))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1999, 2, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(1999, 2, 8))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1999, 2, 10))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(1999, 2, 6))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1999, 2, 1, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(DateTime(1999, 7, 15, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(DateTime(1999, 7, 4, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(DateTime(1999, 7, 3, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(DateTime(1999, 7, 31, 7, 9, 2), usecs(234578))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-2000, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 6, 30)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 6, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(-1999, 1, 1)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 1, 31))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(Date(-1999, 7, 15))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(Date(-1999, 7, 4))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(Date(-1999, 7, 3))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(-1999, 7, 30))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(-1999, 7, 17))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(-1999, 2, 1, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(DateTime(-1999, 7, 15, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(DateTime(-1999, 7, 4, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(DateTime(-1999, 7, 3, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-3); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 7, 6)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1, 7, 13))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(Date(1, 7, 19))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1, 7, 5))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 31, 0, 0, 0))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(DateTime(1, 7, 13, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(DateTime(1, 7, 19, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(DateTime(1, 7, 5, 13, 13, 9), msecs(22))); + } + + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(DateTime(0, 7, 13, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(DateTime(0, 7, 19, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(DateTime(0, 7, 5, 13, 13, 9), msecs(22))); + } + + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); + sysTime.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); + assert(sysTime == SysTime(DateTime(0, 7, 8, 13, 13, 9), msecs(22))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"days"(4))); + //static assert(!__traits(compiles, ist.roll!"days"(4))); + } + + + // Shares documentation with "days" version. + ref SysTime roll(string units)(long value) @safe nothrow + if (units == "hours" || units == "minutes" || units == "seconds") + { + try + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + dateTime.roll!units(value); + --days; + + hnsecs += convert!("hours", "hnsecs")(dateTime.hour); + hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); + hnsecs += convert!("seconds", "hnsecs")(dateTime.second); + + if (days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + catch (Exception e) + assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); + } + + // Test roll!"hours"(). + @safe unittest + { + static void testST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + immutable d = msecs(45); + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); + testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); + testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); + testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); + testST(beforeAD, 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); + testST(beforeAD, 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); + testST(beforeAD, 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); + testST(beforeAD, 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); + testST(beforeAD, 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + testST(beforeAD, 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(beforeAD, 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); + testST(beforeAD, 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); + testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); + testST(beforeAD, 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); + testST(beforeAD, 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); + testST(beforeAD, 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); + testST(beforeAD, 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); + testST(beforeAD, 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); + testST(beforeAD, 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); + testST(beforeAD, 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); + testST(beforeAD, 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); + testST(beforeAD, 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); + testST(beforeAD, 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); + testST(beforeAD, 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); + testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); + testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); + testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); + testST(beforeAD, -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); + testST(beforeAD, -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); + testST(beforeAD, -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); + testST(beforeAD, -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); + testST(beforeAD, -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); + testST(beforeAD, -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(beforeAD, -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + testST(beforeAD, -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); + testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); + testST(beforeAD, -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); + testST(beforeAD, -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); + testST(beforeAD, -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); + testST(beforeAD, -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); + testST(beforeAD, -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); + testST(beforeAD, -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); + testST(beforeAD, -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); + testST(beforeAD, -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); + testST(beforeAD, -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); + testST(beforeAD, -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); + testST(beforeAD, -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), d)); + + testST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), d)); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); + testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); + testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); + testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); + testST(beforeBC, 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); + testST(beforeBC, 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); + testST(beforeBC, 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); + testST(beforeBC, 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); + testST(beforeBC, 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + testST(beforeBC, 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(beforeBC, 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); + testST(beforeBC, 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); + testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); + testST(beforeBC, 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); + testST(beforeBC, 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); + testST(beforeBC, 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); + testST(beforeBC, 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); + testST(beforeBC, 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); + testST(beforeBC, 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); + testST(beforeBC, 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); + testST(beforeBC, 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); + testST(beforeBC, 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); + testST(beforeBC, 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); + testST(beforeBC, 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); + testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); + testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); + testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); + testST(beforeBC, -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); + testST(beforeBC, -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); + testST(beforeBC, -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); + testST(beforeBC, -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); + testST(beforeBC, -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); + testST(beforeBC, -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(beforeBC, -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + testST(beforeBC, -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); + testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); + testST(beforeBC, -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); + testST(beforeBC, -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); + testST(beforeBC, -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); + testST(beforeBC, -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); + testST(beforeBC, -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); + testST(beforeBC, -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); + testST(beforeBC, -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); + testST(beforeBC, -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); + testST(beforeBC, -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); + testST(beforeBC, -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); + testST(beforeBC, -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), d)); + + testST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), d)); + + // Test Both + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), d)); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 0, 0))); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0)); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"hours"(1).roll!"hours"(-67); + assert(sysTime == SysTime(DateTime(0, 12, 31, 5, 59, 59), hnsecs(9_999_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"hours"(4))); + //static assert(!__traits(compiles, ist.roll!"hours"(4))); + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + immutable d = usecs(7203); + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), d)); + testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), d)); + testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), d)); + testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), d)); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), d)); + testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); + testST(beforeAD, 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); + testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); + testST(beforeAD, 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), d)); + + testST(beforeAD, 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + testST(beforeAD, 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(beforeAD, 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); + testST(beforeAD, 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); + testST(beforeAD, 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), d)); + testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), d)); + testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), d)); + testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), d)); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), d)); + testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); + testST(beforeAD, -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(beforeAD, -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); + testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); + testST(beforeAD, -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), d)); + + testST(beforeAD, -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(beforeAD, -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + testST(beforeAD, -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); + testST(beforeAD, -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); + testST(beforeAD, -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), d)); + + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), d)); + + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), d)); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), d)); + testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), d)); + testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), d)); + testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), d)); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), d)); + testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); + testST(beforeBC, 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); + testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); + testST(beforeBC, 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), d)); + + testST(beforeBC, 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + testST(beforeBC, 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(beforeBC, 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); + testST(beforeBC, 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); + testST(beforeBC, 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), d)); + testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), d)); + testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), d)); + testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), d)); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), d)); + testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); + testST(beforeBC, -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(beforeBC, -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); + testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); + testST(beforeBC, -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), d)); + + testST(beforeBC, -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(beforeBC, -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + testST(beforeBC, -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); + testST(beforeBC, -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); + testST(beforeBC, -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), d)); + + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), d)); + + // Test Both + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); + testST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 52, 33), d), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 0))); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999)); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0)); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 59), hnsecs(9_999_999))); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"minutes"(1).roll!"minutes"(-79); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 41, 59), hnsecs(9_999_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"minutes"(4))); + //static assert(!__traits(compiles, ist.roll!"minutes"(4))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + immutable d = msecs(274); + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), d)); + testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), d)); + testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), d)); + testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), d)); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), d)); + testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), d)); + testST(beforeAD, 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + testST(beforeAD, 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), d)); + testST(beforeAD, 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + + testST(beforeAD, 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + testST(beforeAD, 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); + testST(beforeAD, 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + testST(beforeAD, 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + testST(beforeAD, 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), d)); + testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), d)); + testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), d)); + testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), d)); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), d)); + testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), d)); + testST(beforeAD, -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + testST(beforeAD, -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), d)); + testST(beforeAD, -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + + testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), d)); + + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), d)); + + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), d)); + + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), d)); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), d)); + testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), d)); + testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), d)); + testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), d)); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), d)); + testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), d)); + testST(beforeBC, 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + testST(beforeBC, 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), d)); + testST(beforeBC, 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + + testST(beforeBC, 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + testST(beforeBC, 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); + testST(beforeBC, 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + testST(beforeBC, 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + testST(beforeBC, 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), d)); + testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), d)); + testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), d)); + testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), d)); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), d)); + testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), d)); + testST(beforeBC, -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + testST(beforeBC, -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), d)); + testST(beforeBC, -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), d)); + + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), d)); + + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), d)); + + // Test Both + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), d)); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), d)); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), d)); + testST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), d)); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 50), d), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59))); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999)); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59)); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), hnsecs(9_999_999))); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"seconds"(1).roll!"seconds"(-102); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 18), hnsecs(9_999_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"seconds"(4))); + //static assert(!__traits(compiles, ist.roll!"seconds"(4))); + } + + + // Shares documentation with "days" version. + ref SysTime roll(string units)(long value) @safe nothrow + if (units == "msecs" || units == "usecs" || units == "hnsecs") + { + auto hnsecs = adjTime; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + hnsecs += convert!(units, "hnsecs")(value); + hnsecs %= convert!("seconds", "hnsecs")(1); + + if (hnsecs < 0) + hnsecs += convert!("seconds", "hnsecs")(1); + hnsecs += convert!("seconds", "hnsecs")(seconds); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + + + // Test roll!"msecs"(). + @safe unittest + { + static void testST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"msecs"(milliseconds); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(1))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(1))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(999))); + testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(998))); + testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(445))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_989_999))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); + testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); + testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(5_549_999))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + st.roll!"msecs"(1202).roll!"msecs"(-703); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(4_989_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.addMSecs(4))); + //static assert(!__traits(compiles, ist.addMSecs(4))); + } + + // Test roll!"usecs"(). + @safe unittest + { + static void testST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"usecs"(microseconds); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1000))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(2274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(26_999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_000))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(766_999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(767_000))); + testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(998_274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(967_000))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(966_999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(167_000))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(166_999))); + testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1000))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(2274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(26_999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_000))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(766_999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(767_000))); + testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(998_274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(967_000))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(966_999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(167_000))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(166_999))); + testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_999))); + testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_998))); + testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_000))); + testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(998_000))); + testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(997_445))); + testST(beforeBoth1, -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(666_667))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_989))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9))); + testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19))); + testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); + testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); + testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(25_549))); + testST(beforeBoth2, 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(3_333_329))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + st.roll!"usecs"(9_020_027); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(200_269))); + } + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + st.roll!"usecs"(9_020_027).roll!"usecs"(-70_034); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_499_929))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"usecs"(4))); + //static assert(!__traits(compiles, ist.roll!"usecs"(4))); + } + + // Test roll!"hnsecs"(). + @safe unittest + { + static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"hnsecs"(hnsecs); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + auto dtAD = DateTime(1999, 7, 6, 12, 30, 33); + auto beforeAD = SysTime(dtAD, hnsecs(274)); + testST(beforeAD, 0, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 1, SysTime(dtAD, hnsecs(275))); + testST(beforeAD, 2, SysTime(dtAD, hnsecs(276))); + testST(beforeAD, 10, SysTime(dtAD, hnsecs(284))); + testST(beforeAD, 100, SysTime(dtAD, hnsecs(374))); + testST(beforeAD, 725, SysTime(dtAD, hnsecs(999))); + testST(beforeAD, 726, SysTime(dtAD, hnsecs(1000))); + testST(beforeAD, 1000, SysTime(dtAD, hnsecs(1274))); + testST(beforeAD, 1001, SysTime(dtAD, hnsecs(1275))); + testST(beforeAD, 2000, SysTime(dtAD, hnsecs(2274))); + testST(beforeAD, 26_725, SysTime(dtAD, hnsecs(26_999))); + testST(beforeAD, 26_726, SysTime(dtAD, hnsecs(27_000))); + testST(beforeAD, 26_727, SysTime(dtAD, hnsecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(dtAD, hnsecs(1_766_999))); + testST(beforeAD, 1_766_726, SysTime(dtAD, hnsecs(1_767_000))); + testST(beforeAD, 1_000_000, SysTime(dtAD, hnsecs(1_000_274))); + testST(beforeAD, 60_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 36_000_000_000L, SysTime(dtAD, hnsecs(274))); + + testST(beforeAD, -1, SysTime(dtAD, hnsecs(273))); + testST(beforeAD, -2, SysTime(dtAD, hnsecs(272))); + testST(beforeAD, -10, SysTime(dtAD, hnsecs(264))); + testST(beforeAD, -100, SysTime(dtAD, hnsecs(174))); + testST(beforeAD, -274, SysTime(dtAD)); + testST(beforeAD, -275, SysTime(dtAD, hnsecs(9_999_999))); + testST(beforeAD, -1000, SysTime(dtAD, hnsecs(9_999_274))); + testST(beforeAD, -1001, SysTime(dtAD, hnsecs(9_999_273))); + testST(beforeAD, -2000, SysTime(dtAD, hnsecs(9_998_274))); + testST(beforeAD, -33_274, SysTime(dtAD, hnsecs(9_967_000))); + testST(beforeAD, -33_275, SysTime(dtAD, hnsecs(9_966_999))); + testST(beforeAD, -1_833_274, SysTime(dtAD, hnsecs(8_167_000))); + testST(beforeAD, -1_833_275, SysTime(dtAD, hnsecs(8_166_999))); + testST(beforeAD, -1_000_000, SysTime(dtAD, hnsecs(9_000_274))); + testST(beforeAD, -60_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, -600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, -36_000_000_000L, SysTime(dtAD, hnsecs(274))); + + // Test B.C. + auto dtBC = DateTime(-1999, 7, 6, 12, 30, 33); + auto beforeBC = SysTime(dtBC, hnsecs(274)); + testST(beforeBC, 0, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 1, SysTime(dtBC, hnsecs(275))); + testST(beforeBC, 2, SysTime(dtBC, hnsecs(276))); + testST(beforeBC, 10, SysTime(dtBC, hnsecs(284))); + testST(beforeBC, 100, SysTime(dtBC, hnsecs(374))); + testST(beforeBC, 725, SysTime(dtBC, hnsecs(999))); + testST(beforeBC, 726, SysTime(dtBC, hnsecs(1000))); + testST(beforeBC, 1000, SysTime(dtBC, hnsecs(1274))); + testST(beforeBC, 1001, SysTime(dtBC, hnsecs(1275))); + testST(beforeBC, 2000, SysTime(dtBC, hnsecs(2274))); + testST(beforeBC, 26_725, SysTime(dtBC, hnsecs(26_999))); + testST(beforeBC, 26_726, SysTime(dtBC, hnsecs(27_000))); + testST(beforeBC, 26_727, SysTime(dtBC, hnsecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(dtBC, hnsecs(1_766_999))); + testST(beforeBC, 1_766_726, SysTime(dtBC, hnsecs(1_767_000))); + testST(beforeBC, 1_000_000, SysTime(dtBC, hnsecs(1_000_274))); + testST(beforeBC, 60_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 36_000_000_000L, SysTime(dtBC, hnsecs(274))); + + testST(beforeBC, -1, SysTime(dtBC, hnsecs(273))); + testST(beforeBC, -2, SysTime(dtBC, hnsecs(272))); + testST(beforeBC, -10, SysTime(dtBC, hnsecs(264))); + testST(beforeBC, -100, SysTime(dtBC, hnsecs(174))); + testST(beforeBC, -274, SysTime(dtBC)); + testST(beforeBC, -275, SysTime(dtBC, hnsecs(9_999_999))); + testST(beforeBC, -1000, SysTime(dtBC, hnsecs(9_999_274))); + testST(beforeBC, -1001, SysTime(dtBC, hnsecs(9_999_273))); + testST(beforeBC, -2000, SysTime(dtBC, hnsecs(9_998_274))); + testST(beforeBC, -33_274, SysTime(dtBC, hnsecs(9_967_000))); + testST(beforeBC, -33_275, SysTime(dtBC, hnsecs(9_966_999))); + testST(beforeBC, -1_833_274, SysTime(dtBC, hnsecs(8_167_000))); + testST(beforeBC, -1_833_275, SysTime(dtBC, hnsecs(8_166_999))); + testST(beforeBC, -1_000_000, SysTime(dtBC, hnsecs(9_000_274))); + testST(beforeBC, -60_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, -600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, -36_000_000_000L, SysTime(dtBC, hnsecs(274))); + + // Test Both + auto dtBoth1 = DateTime(1, 1, 1, 0, 0, 0); + auto beforeBoth1 = SysTime(dtBoth1); + testST(beforeBoth1, 1, SysTime(dtBoth1, hnsecs(1))); + testST(beforeBoth1, 0, SysTime(dtBoth1)); + testST(beforeBoth1, -1, SysTime(dtBoth1, hnsecs(9_999_999))); + testST(beforeBoth1, -2, SysTime(dtBoth1, hnsecs(9_999_998))); + testST(beforeBoth1, -1000, SysTime(dtBoth1, hnsecs(9_999_000))); + testST(beforeBoth1, -2000, SysTime(dtBoth1, hnsecs(9_998_000))); + testST(beforeBoth1, -2555, SysTime(dtBoth1, hnsecs(9_997_445))); + testST(beforeBoth1, -1_000_000, SysTime(dtBoth1, hnsecs(9_000_000))); + testST(beforeBoth1, -2_000_000, SysTime(dtBoth1, hnsecs(8_000_000))); + testST(beforeBoth1, -2_333_333, SysTime(dtBoth1, hnsecs(7_666_667))); + testST(beforeBoth1, -10_000_000, SysTime(dtBoth1)); + testST(beforeBoth1, -20_000_000, SysTime(dtBoth1)); + testST(beforeBoth1, -20_888_888, SysTime(dtBoth1, hnsecs(9_111_112))); + + auto dtBoth2 = DateTime(0, 12, 31, 23, 59, 59); + auto beforeBoth2 = SysTime(dtBoth2, hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(dtBoth2, hnsecs(9_999_998))); + testST(beforeBoth2, 0, SysTime(dtBoth2, hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(dtBoth2)); + testST(beforeBoth2, 2, SysTime(dtBoth2, hnsecs(1))); + testST(beforeBoth2, 1000, SysTime(dtBoth2, hnsecs(999))); + testST(beforeBoth2, 2000, SysTime(dtBoth2, hnsecs(1999))); + testST(beforeBoth2, 2555, SysTime(dtBoth2, hnsecs(2554))); + testST(beforeBoth2, 1_000_000, SysTime(dtBoth2, hnsecs(999_999))); + testST(beforeBoth2, 2_000_000, SysTime(dtBoth2, hnsecs(1_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(dtBoth2, hnsecs(2_333_332))); + testST(beforeBoth2, 10_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); + testST(beforeBoth2, 20_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); + testST(beforeBoth2, 20_888_888, SysTime(dtBoth2, hnsecs(888_887))); + + { + auto st = SysTime(dtBoth2, hnsecs(9_999_999)); + st.roll!"hnsecs"(70_777_222).roll!"hnsecs"(-222_555_292); + assert(st == SysTime(dtBoth2, hnsecs(8_221_929))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); + //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF SysTime). + + The legal types of arithmetic for $(LREF SysTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) + $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF SysTime). + +/ + SysTime opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + SysTime retval = SysTime(this._stdTime, this._timezone); + immutable hnsecs = duration.total!"hnsecs"; + mixin("retval._stdTime " ~ op ~ "= hnsecs;"); + return retval; + } + + /// + @safe unittest + { + import core.time : hours, seconds; + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == + SysTime(DateTime(2016, 1, 1, 0, 0, 0))); + + assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + hours(1) == + SysTime(DateTime(2016, 1, 1, 0, 59, 59))); + + assert(SysTime(DateTime(2016, 1, 1, 0, 0, 0)) - seconds(1) == + SysTime(DateTime(2015, 12, 31, 23, 59, 59))); + + assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) == + SysTime(DateTime(2015, 12, 31, 23, 59, 59))); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + + assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); + assert(st + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); + assert(st + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); + assert(st + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); + assert(st + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); + assert(st + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); + assert(st + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + assert(st + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); + assert(st + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); + + assert(st - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); + assert(st - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); + assert(st - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); + assert(st - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); + assert(st - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); + assert(st - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); + assert(st - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); + assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); + + static void testST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + auto result = orig + dur!"hnsecs"(hnsecs); + if (result != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", result, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); + testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); + testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); + testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); + testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); + testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); + testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); + testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); + testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); + testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); + testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); + testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); + testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); + testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); + + auto duration = dur!"seconds"(12); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); + //assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); + assert(cst - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); + //assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + SysTime opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + SysTime retval = SysTime(this._stdTime, this._timezone); + immutable hnsecs = td.hnsecs; + mixin("retval._stdTime " ~ op ~ "= hnsecs;"); + return retval; + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + + assert(st + TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st + TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + + assert(st - TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st - TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF SysTime), as well as assigning the result to this + $(LREF SysTime). + + The legal types of arithmetic for $(LREF SysTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) + $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF SysTime). + +/ + ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable hnsecs = duration.total!"hnsecs"; + mixin("_stdTime " ~ op ~ "= hnsecs;"); + return this; + } + + @safe unittest + { + auto before = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(before + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assert(before + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assert(before + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assert(before + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assert(before + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assert(before + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assert(before + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assert(before + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assert(before + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assert(before + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assert(before + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); + assert(before + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); + assert(before + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); + assert(before + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); + assert(before + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); + assert(before + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); + + assert(before - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assert(before - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assert(before - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assert(before - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assert(before - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assert(before - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assert(before - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assert(before - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assert(before - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assert(before - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assert(before - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); + assert(before - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); + assert(before - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); + assert(before - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); + assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); + assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); + + static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + auto r = orig += dur!"hnsecs"(hnsecs); + if (orig != expected) + throw new AssertError(format("Failed 1. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + if (r != expected) + throw new AssertError(format("Failed 2. actual [%s] != expected [%s]", r, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); + testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); + testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); + testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); + testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); + testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); + testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); + testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); + testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); + testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); + testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); + testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); + testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); + testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + (st += dur!"hnsecs"(52)) += dur!"seconds"(-907); + assert(st == SysTime(DateTime(0, 12, 31, 23, 44, 53), hnsecs(51))); + } + + auto duration = dur!"seconds"(12); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst += duration)); + //static assert(!__traits(compiles, ist += duration)); + static assert(!__traits(compiles, cst -= duration)); + //static assert(!__traits(compiles, ist -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref SysTime opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable hnsecs = td.hnsecs; + mixin("_stdTime " ~ op ~ "= hnsecs;"); + return this; + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st += TickDuration.from!"usecs"(7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + } + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st += TickDuration.from!"usecs"(-7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + } + + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st -= TickDuration.from!"usecs"(-7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + } + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st -= TickDuration.from!"usecs"(7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + } + } + } + + + /++ + Gives the difference between two $(LREF SysTime)s. + + The legal types of arithmetic for $(LREF SysTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow + if (op == "-") + { + return dur!"hnsecs"(_stdTime - rhs._stdTime); + } + + @safe unittest + { + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)) == + dur!"seconds"(86_400)); + assert(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)) == + dur!"seconds"(3600)); + assert(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(60)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)) == + dur!"seconds"(-60)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(1)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)) == + dur!"seconds"(-1)); + + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + assert(SysTime(dt, msecs(532)) - SysTime(dt) == msecs(532)); + assert(SysTime(dt) - SysTime(dt, msecs(532)) == msecs(-532)); + + assert(SysTime(dt, usecs(333_347)) - SysTime(dt) == usecs(333_347)); + assert(SysTime(dt) - SysTime(dt, usecs(333_347)) == usecs(-333_347)); + + assert(SysTime(dt, hnsecs(1_234_567)) - SysTime(dt) == hnsecs(1_234_567)); + assert(SysTime(dt) - SysTime(dt, hnsecs(1_234_567)) == hnsecs(-1_234_567)); + } + + assert(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(45033)); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)) == dur!"seconds"(-45033)); + assert(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(-41367)); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)) == dur!"seconds"(41367)); + + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) == + dur!"hnsecs"(1)); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == + dur!"hnsecs"(-1)); + + version(Posix) + immutable tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); + else version(Windows) + immutable tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); + + { + auto dt = DateTime(2011, 1, 13, 8, 17, 2); + auto d = msecs(296); + assert(SysTime(dt, d, tz) - SysTime(dt, d, tz) == Duration.zero); + assert(SysTime(dt, d, tz) - SysTime(dt, d, UTC()) == hours(8)); + assert(SysTime(dt, d, UTC()) - SysTime(dt, d, tz) == hours(-8)); + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st - st == Duration.zero); + assert(cst - st == Duration.zero); + //assert(ist - st == Duration.zero); + + assert(st - cst == Duration.zero); + assert(cst - cst == Duration.zero); + //assert(ist - cst == Duration.zero); + + //assert(st - ist == Duration.zero); + //assert(cst - ist == Duration.zero); + //assert(ist - ist == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF SysTime)s in months. + + To get the difference in years, subtract the year property + of two $(LREF SysTime)s. To get the difference in days or weeks, + subtract the $(LREF SysTime)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. + + Note that the number of days in the months or how far into the month + either date is is irrelevant. It is the difference in the month property + combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF SysTime) to subtract from this one. + +/ + int diffMonths(in SysTime rhs) @safe const nothrow + { + return (cast(Date) this).diffMonths(cast(Date) rhs); + } + + /// + @safe unittest + { + import std.datetime.date : Date; + + assert(SysTime(Date(1999, 2, 1)).diffMonths( + SysTime(Date(1999, 1, 31))) == 1); + + assert(SysTime(Date(1999, 1, 31)).diffMonths( + SysTime(Date(1999, 2, 1))) == -1); + + assert(SysTime(Date(1999, 3, 1)).diffMonths( + SysTime(Date(1999, 1, 1))) == 2); + + assert(SysTime(Date(1999, 1, 1)).diffMonths( + SysTime(Date(1999, 3, 31))) == -2); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.diffMonths(st) == 0); + assert(cst.diffMonths(st) == 0); + //assert(ist.diffMonths(st) == 0); + + assert(st.diffMonths(cst) == 0); + assert(cst.diffMonths(cst) == 0); + //assert(ist.diffMonths(cst) == 0); + + //assert(st.diffMonths(ist) == 0); + //assert(cst.diffMonths(ist) == 0); + //assert(ist.diffMonths(ist) == 0); + } + + + /++ + Whether this $(LREF SysTime) is in a leap year. + +/ + @property bool isLeapYear() @safe const nothrow + { + return (cast(Date) this).isLeapYear; + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(!st.isLeapYear); + assert(!cst.isLeapYear); + //assert(!ist.isLeapYear); + } + + + /++ + Day of the week this $(LREF SysTime) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const nothrow + { + return getDayOfWeek(dayOfGregorianCal); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.dayOfWeek == DayOfWeek.tue); + assert(cst.dayOfWeek == DayOfWeek.tue); + //assert(ist.dayOfWeek == DayOfWeek.tue); + } + + + /++ + Day of the year this $(LREF SysTime) is on. + +/ + @property ushort dayOfYear() @safe const nothrow + { + return (cast(Date) this).dayOfYear; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); + assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); + assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.dayOfYear == 187); + assert(cst.dayOfYear == 187); + //assert(ist.dayOfYear == 187); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF SysTime) is on. + +/ + @property void dayOfYear(int day) @safe + { + immutable hnsecs = adjTime; + immutable days = convert!("hnsecs", "days")(hnsecs); + immutable theRest = hnsecs - convert!("days", "hnsecs")(days); + + auto date = Date(cast(int) days); + date.dayOfYear = day; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + + adjTime = newDaysHNSecs + theRest; + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.dayOfYear = 12; + assert(st.dayOfYear == 12); + static assert(!__traits(compiles, cst.dayOfYear = 12)); + //static assert(!__traits(compiles, ist.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. + +/ + @property int dayOfGregorianCal() @safe const nothrow + { + immutable adjustedTime = adjTime; + + // We have to add one because 0 would be midnight, January 1st, 1 A.D., + // which would be the 1st day of the Gregorian Calendar, not the 0th. So, + // simply casting to days is one day off. + if (adjustedTime > 0) + return cast(int) getUnitsFromHNSecs!"days"(adjustedTime) + 1; + + long hnsecs = adjustedTime; + immutable days = cast(int) splitUnitsFromHNSecs!"days"(hnsecs); + + return hnsecs == 0 ? days + 1 : days; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); + assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); + + assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); + assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 1); + + assert(SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212)).dayOfGregorianCal == 2); + assert(SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 32); + assert(SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 366); + assert(SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 731); + assert(SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1096); + assert(SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1462); + assert(SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 17_898); + assert(SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 35_065); + assert(SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_160); + assert(SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_525); + assert(SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 37_986); + assert(SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 72_684); + assert(SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 73_049); + assert(SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_208); + assert(SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_573); + assert(SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 145_732); + assert(SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 146_098); + assert(SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_257); + assert(SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_622); + assert(SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 364_878); + assert(SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 365_243); + assert(SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_023); + assert(SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_389); + assert(SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_596); + assert(SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_961); + assert(SysTime(DateTime(1945, 11, 12, 12, 2, 9), msecs(212)).dayOfGregorianCal == 710_347); + assert(SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 729_755); + assert(SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_486); + + assert(SysTime(DateTime(2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_773); + assert(SysTime(DateTime(2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_803); + assert(SysTime(DateTime(2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_804); + assert(SysTime(DateTime(2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_831); + assert(SysTime(DateTime(2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_832); + assert(SysTime(DateTime(2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_862); + assert(SysTime(DateTime(2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_863); + assert(SysTime(DateTime(2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_892); + assert(SysTime(DateTime(2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_893); + assert(SysTime(DateTime(2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_923); + assert(SysTime(DateTime(2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_924); + assert(SysTime(DateTime(2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_953); + assert(SysTime(DateTime(2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_954); + assert(SysTime(DateTime(2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_984); + assert(SysTime(DateTime(2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_985); + assert(SysTime(DateTime(2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_015); + assert(SysTime(DateTime(2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_016); + assert(SysTime(DateTime(2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_045); + assert(SysTime(DateTime(2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_046); + assert(SysTime(DateTime(2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_076); + assert(SysTime(DateTime(2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_077); + assert(SysTime(DateTime(2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_106); + assert(SysTime(DateTime(2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_107); + assert(SysTime(DateTime(2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_137); + + assert(SysTime(DateTime(2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == 734_534); + assert(SysTime(DateTime(2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == 734_561); + assert(SysTime(DateTime(2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == 734_562); + assert(SysTime(DateTime(2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == 734_563); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).dayOfGregorianCal == 0); + + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 0, 0, 0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1); + assert(SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -30); + assert(SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -31); + + assert(SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -367); + assert(SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730); + assert(SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731); + assert(SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1095); + assert(SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1096); + assert(SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1460); + assert(SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1461); + assert(SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1826); + assert(SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1827); + assert(SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -2191); + assert(SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -3652); + + assert(SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_262); + assert(SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_627); + assert(SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -35_794); + assert(SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_160); + assert(SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_524); + assert(SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_889); + assert(SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -37_254); + assert(SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -38_715); + assert(SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_413); + assert(SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_778); + assert(SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -109_937); + assert(SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -110_302); + assert(SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_097); + assert(SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_462); + assert(SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_827); + assert(SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_621); + assert(SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_986); + assert(SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -183_351); + assert(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_607); + assert(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_972); + assert(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_387); + assert(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_388); + assert(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_753); + assert(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -585_118); + assert(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_325); + assert(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_690); + assert(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_484); + assert(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_485); + assert(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_850); + assert(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731_215); + + assert(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_502); + assert(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_472); + assert(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_471); + assert(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_444); + assert(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_443); + assert(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_413); + assert(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_412); + assert(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_383); + assert(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_382); + assert(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_352); + assert(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_351); + assert(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_322); + assert(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_321); + assert(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_291); + assert(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_290); + assert(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_260); + assert(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_259); + assert(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_230); + assert(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_229); + assert(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_199); + assert(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_198); + assert(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_169); + assert(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_168); + assert(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_138); + + assert(SysTime(DateTime(-2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == -735_202); + assert(SysTime(DateTime(-2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == -735_175); + assert(SysTime(DateTime(-2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == -735_174); + assert(SysTime(DateTime(-2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == -735_173); + + // Start of Hebrew Calendar + assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.dayOfGregorianCal == 729_941); + //assert(ist.dayOfGregorianCal == 729_941); + } + + + // Test that the logic for the day of the Gregorian Calendar is consistent + // between Date and SysTime. + @safe unittest + { + void test(Date date, SysTime st, size_t line = __LINE__) + { + if (date.dayOfGregorianCal != st.dayOfGregorianCal) + { + throw new AssertError(format("Date [%s] SysTime [%s]", date.dayOfGregorianCal, st.dayOfGregorianCal), + __FILE__, line); + } + } + + // Test A.D. + test(Date(1, 1, 1), SysTime(DateTime(1, 1, 1, 0, 0, 0))); + test(Date(1, 1, 2), SysTime(DateTime(1, 1, 2, 0, 0, 0), hnsecs(500))); + test(Date(1, 2, 1), SysTime(DateTime(1, 2, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(2, 1, 1), SysTime(DateTime(2, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(3, 1, 1), SysTime(DateTime(3, 1, 1, 12, 13, 14))); + test(Date(4, 1, 1), SysTime(DateTime(4, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(5, 1, 1), SysTime(DateTime(5, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(50, 1, 1), SysTime(DateTime(50, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(97, 1, 1), SysTime(DateTime(97, 1, 1, 23, 59, 59))); + test(Date(100, 1, 1), SysTime(DateTime(100, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(101, 1, 1), SysTime(DateTime(101, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(105, 1, 1), SysTime(DateTime(105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(200, 1, 1), SysTime(DateTime(200, 1, 1, 0, 0, 0))); + test(Date(201, 1, 1), SysTime(DateTime(201, 1, 1, 0, 0, 0), hnsecs(500))); + test(Date(300, 1, 1), SysTime(DateTime(300, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(301, 1, 1), SysTime(DateTime(301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(400, 1, 1), SysTime(DateTime(400, 1, 1, 12, 13, 14))); + test(Date(401, 1, 1), SysTime(DateTime(401, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(500, 1, 1), SysTime(DateTime(500, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(501, 1, 1), SysTime(DateTime(501, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(1000, 1, 1), SysTime(DateTime(1000, 1, 1, 23, 59, 59))); + test(Date(1001, 1, 1), SysTime(DateTime(1001, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(1600, 1, 1), SysTime(DateTime(1600, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(1601, 1, 1), SysTime(DateTime(1601, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(1900, 1, 1), SysTime(DateTime(1900, 1, 1, 0, 0, 0))); + test(Date(1901, 1, 1), SysTime(DateTime(1901, 1, 1, 0, 0, 0), hnsecs(500))); + test(Date(1945, 11, 12), SysTime(DateTime(1945, 11, 12, 0, 0, 0), hnsecs(50_000))); + test(Date(1999, 1, 1), SysTime(DateTime(1999, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(1999, 7, 6), SysTime(DateTime(1999, 7, 6, 12, 13, 14))); + test(Date(2000, 1, 1), SysTime(DateTime(2000, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(2001, 1, 1), SysTime(DateTime(2001, 1, 1, 12, 13, 14), hnsecs(50_000))); + + test(Date(2010, 1, 1), SysTime(DateTime(2010, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(2010, 1, 31), SysTime(DateTime(2010, 1, 31, 23, 0, 0))); + test(Date(2010, 2, 1), SysTime(DateTime(2010, 2, 1, 23, 59, 59), hnsecs(500))); + test(Date(2010, 2, 28), SysTime(DateTime(2010, 2, 28, 23, 59, 59), hnsecs(50_000))); + test(Date(2010, 3, 1), SysTime(DateTime(2010, 3, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(2010, 3, 31), SysTime(DateTime(2010, 3, 31, 0, 0, 0))); + test(Date(2010, 4, 1), SysTime(DateTime(2010, 4, 1, 0, 0, 0), hnsecs(500))); + test(Date(2010, 4, 30), SysTime(DateTime(2010, 4, 30, 0, 0, 0), hnsecs(50_000))); + test(Date(2010, 5, 1), SysTime(DateTime(2010, 5, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(2010, 5, 31), SysTime(DateTime(2010, 5, 31, 12, 13, 14))); + test(Date(2010, 6, 1), SysTime(DateTime(2010, 6, 1, 12, 13, 14), hnsecs(500))); + test(Date(2010, 6, 30), SysTime(DateTime(2010, 6, 30, 12, 13, 14), hnsecs(50_000))); + test(Date(2010, 7, 1), SysTime(DateTime(2010, 7, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(2010, 7, 31), SysTime(DateTime(2010, 7, 31, 23, 59, 59))); + test(Date(2010, 8, 1), SysTime(DateTime(2010, 8, 1, 23, 59, 59), hnsecs(500))); + test(Date(2010, 8, 31), SysTime(DateTime(2010, 8, 31, 23, 59, 59), hnsecs(50_000))); + test(Date(2010, 9, 1), SysTime(DateTime(2010, 9, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(2010, 9, 30), SysTime(DateTime(2010, 9, 30, 12, 0, 0))); + test(Date(2010, 10, 1), SysTime(DateTime(2010, 10, 1, 0, 12, 0), hnsecs(500))); + test(Date(2010, 10, 31), SysTime(DateTime(2010, 10, 31, 0, 0, 12), hnsecs(50_000))); + test(Date(2010, 11, 1), SysTime(DateTime(2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); + test(Date(2010, 11, 30), SysTime(DateTime(2010, 11, 30, 0, 59, 0))); + test(Date(2010, 12, 1), SysTime(DateTime(2010, 12, 1, 0, 0, 59), hnsecs(500))); + test(Date(2010, 12, 31), SysTime(DateTime(2010, 12, 31, 0, 59, 59), hnsecs(50_000))); + + test(Date(2012, 2, 1), SysTime(DateTime(2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); + test(Date(2012, 2, 28), SysTime(DateTime(2012, 2, 28, 23, 59, 0))); + test(Date(2012, 2, 29), SysTime(DateTime(2012, 2, 29, 7, 7, 7), hnsecs(7))); + test(Date(2012, 3, 1), SysTime(DateTime(2012, 3, 1, 7, 7, 7), hnsecs(7))); + + // Test B.C. + test(Date(0, 12, 31), SysTime(DateTime(0, 12, 31, 0, 0, 0))); + test(Date(0, 12, 30), SysTime(DateTime(0, 12, 30, 0, 0, 0), hnsecs(500))); + test(Date(0, 12, 1), SysTime(DateTime(0, 12, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(0, 11, 30), SysTime(DateTime(0, 11, 30, 0, 0, 0), hnsecs(9_999_999))); + + test(Date(-1, 12, 31), SysTime(DateTime(-1, 12, 31, 12, 13, 14))); + test(Date(-1, 12, 30), SysTime(DateTime(-1, 12, 30, 12, 13, 14), hnsecs(500))); + test(Date(-1, 1, 1), SysTime(DateTime(-1, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-2, 12, 31), SysTime(DateTime(-2, 12, 31, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-2, 1, 1), SysTime(DateTime(-2, 1, 1, 23, 59, 59))); + test(Date(-3, 12, 31), SysTime(DateTime(-3, 12, 31, 23, 59, 59), hnsecs(500))); + test(Date(-3, 1, 1), SysTime(DateTime(-3, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(-4, 12, 31), SysTime(DateTime(-4, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-4, 1, 1), SysTime(DateTime(-4, 1, 1, 0, 0, 0))); + test(Date(-5, 12, 31), SysTime(DateTime(-5, 12, 31, 0, 0, 0), hnsecs(500))); + test(Date(-5, 1, 1), SysTime(DateTime(-5, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(-9, 1, 1), SysTime(DateTime(-9, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + + test(Date(-49, 1, 1), SysTime(DateTime(-49, 1, 1, 12, 13, 14))); + test(Date(-50, 1, 1), SysTime(DateTime(-50, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(-97, 1, 1), SysTime(DateTime(-97, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-99, 12, 31), SysTime(DateTime(-99, 12, 31, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-99, 1, 1), SysTime(DateTime(-99, 1, 1, 23, 59, 59))); + test(Date(-100, 1, 1), SysTime(DateTime(-100, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(-101, 1, 1), SysTime(DateTime(-101, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(-105, 1, 1), SysTime(DateTime(-105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-200, 1, 1), SysTime(DateTime(-200, 1, 1, 0, 0, 0))); + test(Date(-201, 1, 1), SysTime(DateTime(-201, 1, 1, 0, 0, 0), hnsecs(500))); + test(Date(-300, 1, 1), SysTime(DateTime(-300, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(-301, 1, 1), SysTime(DateTime(-301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-400, 12, 31), SysTime(DateTime(-400, 12, 31, 12, 13, 14))); + test(Date(-400, 1, 1), SysTime(DateTime(-400, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(-401, 1, 1), SysTime(DateTime(-401, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-499, 1, 1), SysTime(DateTime(-499, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-500, 1, 1), SysTime(DateTime(-500, 1, 1, 23, 59, 59))); + test(Date(-501, 1, 1), SysTime(DateTime(-501, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(-1000, 1, 1), SysTime(DateTime(-1000, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(-1001, 1, 1), SysTime(DateTime(-1001, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-1599, 1, 1), SysTime(DateTime(-1599, 1, 1, 0, 0, 0))); + test(Date(-1600, 12, 31), SysTime(DateTime(-1600, 12, 31, 0, 0, 0), hnsecs(500))); + test(Date(-1600, 1, 1), SysTime(DateTime(-1600, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(-1601, 1, 1), SysTime(DateTime(-1601, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-1900, 1, 1), SysTime(DateTime(-1900, 1, 1, 12, 13, 14))); + test(Date(-1901, 1, 1), SysTime(DateTime(-1901, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(-1999, 1, 1), SysTime(DateTime(-1999, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-1999, 7, 6), SysTime(DateTime(-1999, 7, 6, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-2000, 12, 31), SysTime(DateTime(-2000, 12, 31, 23, 59, 59))); + test(Date(-2000, 1, 1), SysTime(DateTime(-2000, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(-2001, 1, 1), SysTime(DateTime(-2001, 1, 1, 23, 59, 59), hnsecs(50_000))); + + test(Date(-2010, 1, 1), SysTime(DateTime(-2010, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-2010, 1, 31), SysTime(DateTime(-2010, 1, 31, 0, 0, 0))); + test(Date(-2010, 2, 1), SysTime(DateTime(-2010, 2, 1, 0, 0, 0), hnsecs(500))); + test(Date(-2010, 2, 28), SysTime(DateTime(-2010, 2, 28, 0, 0, 0), hnsecs(50_000))); + test(Date(-2010, 3, 1), SysTime(DateTime(-2010, 3, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-2010, 3, 31), SysTime(DateTime(-2010, 3, 31, 12, 13, 14))); + test(Date(-2010, 4, 1), SysTime(DateTime(-2010, 4, 1, 12, 13, 14), hnsecs(500))); + test(Date(-2010, 4, 30), SysTime(DateTime(-2010, 4, 30, 12, 13, 14), hnsecs(50_000))); + test(Date(-2010, 5, 1), SysTime(DateTime(-2010, 5, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-2010, 5, 31), SysTime(DateTime(-2010, 5, 31, 23, 59, 59))); + test(Date(-2010, 6, 1), SysTime(DateTime(-2010, 6, 1, 23, 59, 59), hnsecs(500))); + test(Date(-2010, 6, 30), SysTime(DateTime(-2010, 6, 30, 23, 59, 59), hnsecs(50_000))); + test(Date(-2010, 7, 1), SysTime(DateTime(-2010, 7, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-2010, 7, 31), SysTime(DateTime(-2010, 7, 31, 0, 0, 0))); + test(Date(-2010, 8, 1), SysTime(DateTime(-2010, 8, 1, 0, 0, 0), hnsecs(500))); + test(Date(-2010, 8, 31), SysTime(DateTime(-2010, 8, 31, 0, 0, 0), hnsecs(50_000))); + test(Date(-2010, 9, 1), SysTime(DateTime(-2010, 9, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-2010, 9, 30), SysTime(DateTime(-2010, 9, 30, 12, 0, 0))); + test(Date(-2010, 10, 1), SysTime(DateTime(-2010, 10, 1, 0, 12, 0), hnsecs(500))); + test(Date(-2010, 10, 31), SysTime(DateTime(-2010, 10, 31, 0, 0, 12), hnsecs(50_000))); + test(Date(-2010, 11, 1), SysTime(DateTime(-2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); + test(Date(-2010, 11, 30), SysTime(DateTime(-2010, 11, 30, 0, 59, 0))); + test(Date(-2010, 12, 1), SysTime(DateTime(-2010, 12, 1, 0, 0, 59), hnsecs(500))); + test(Date(-2010, 12, 31), SysTime(DateTime(-2010, 12, 31, 0, 59, 59), hnsecs(50_000))); + + test(Date(-2012, 2, 1), SysTime(DateTime(-2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); + test(Date(-2012, 2, 28), SysTime(DateTime(-2012, 2, 28, 23, 59, 0))); + test(Date(-2012, 2, 29), SysTime(DateTime(-2012, 2, 29, 7, 7, 7), hnsecs(7))); + test(Date(-2012, 3, 1), SysTime(DateTime(-2012, 3, 1, 7, 7, 7), hnsecs(7))); + + test(Date(-3760, 9, 7), SysTime(DateTime(-3760, 9, 7, 0, 0, 0))); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. + Setting this property does not affect the time portion of $(LREF SysTime). + + Params: + days = The day of the Gregorian Calendar to set this $(LREF SysTime) + to. + +/ + @property void dayOfGregorianCal(int days) @safe nothrow + { + auto hnsecs = adjTime; + hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + if (--days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); + st.dayOfGregorianCal = 1; + assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 365; + assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = 366; + assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 0; + assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = -365; + assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = -366; + assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = 730_120; + assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 734_137; + assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); + } + + @safe unittest + { + void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) + { + orig.dayOfGregorianCal = day; + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 1, + SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + + // Test B.C. + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(1)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + + // Test Both. + testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), hnsecs(9_999_999)), 1, + SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + + testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); + testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), hnsecs(9_999_999)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), hnsecs(1)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); + testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + + + auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)); + + void testST2(int day, in SysTime expected, size_t line = __LINE__) + { + st.dayOfGregorianCal = day; + if (st != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", st, expected), __FILE__, line); + } + + // Test A.D. + testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212))); + testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212))); + testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212))); + testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212))); + testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212))); + testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212))); + testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212))); + testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212))); + testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212))); + testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212))); + testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212))); + testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212))); + testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212))); + testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212))); + testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212))); + testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212))); + testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212))); + testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212))); + testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212))); + testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212))); + testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212))); + testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212))); + testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212))); + testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212))); + testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212))); + testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212))); + testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212))); + testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212))); + testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212))); + + testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), msecs(212))); + testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), msecs(212))); + testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), msecs(212))); + testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), msecs(212))); + testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), msecs(212))); + testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), msecs(212))); + testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), msecs(212))); + testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), msecs(212))); + testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), msecs(212))); + testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), msecs(212))); + testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), msecs(212))); + testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), msecs(212))); + testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), msecs(212))); + testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), msecs(212))); + testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), msecs(212))); + testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), msecs(212))); + testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), msecs(212))); + testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), msecs(212))); + testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), msecs(212))); + testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), msecs(212))); + testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), msecs(212))); + testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), msecs(212))); + testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), msecs(212))); + testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), msecs(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); + + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); + + // Test B.C. + testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212))); + testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212))); + testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212))); + + testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212))); + testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212))); + testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212))); + testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212))); + testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212))); + testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212))); + testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212))); + testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212))); + testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212))); + + testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212))); + testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212))); + testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212))); + testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212))); + testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212))); + testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212))); + testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212))); + testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212))); + testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212))); + testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212))); + testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212))); + testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212))); + testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212))); + testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212))); + testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212))); + testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212))); + testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212))); + testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212))); + testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212))); + testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212))); + testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212))); + testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212))); + testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212))); + testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212))); + testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212))); + testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212))); + testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212))); + testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212))); + testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212))); + testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212))); + + testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), msecs(212))); + testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), msecs(212))); + testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), msecs(212))); + testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), msecs(212))); + testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), msecs(212))); + testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), msecs(212))); + testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), msecs(212))); + testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), msecs(212))); + testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), msecs(212))); + testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), msecs(212))); + testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), msecs(212))); + testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), msecs(212))); + testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), msecs(212))); + testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), msecs(212))); + testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), msecs(212))); + testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), msecs(212))); + testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), msecs(212))); + testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), msecs(212))); + testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), msecs(212))); + testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), msecs(212))); + testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), msecs(212))); + testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), msecs(212))); + testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), msecs(212))); + testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), msecs(212))); + + testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), msecs(212))); + testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), msecs(212))); + testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), msecs(212))); + testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212))); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); + //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF SysTime) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). + +/ + @property ubyte isoWeek() @safe const nothrow + { + return (cast(Date) this).isoWeek; + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.isoWeek == 27); + assert(cst.isoWeek == 27); + //assert(ist.isoWeek == 27); + } + + + /++ + $(LREF SysTime) for the last day in the month that this Date is in. + The time portion of endOfMonth is always 23:59:59.9999999. + +/ + @property SysTime endOfMonth() @safe const nothrow + { + immutable hnsecs = adjTime; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + + auto date = Date(cast(int) days + 1).endOfMonth; + auto newDays = date.dayOfGregorianCal - 1; + long theTimeHNSecs; + + if (newDays < 0) + { + theTimeHNSecs = -1; + ++newDays; + } + else + theTimeHNSecs = convert!("days", "hnsecs")(1) - 1; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(newDays); + + auto retval = SysTime(this._stdTime, this._timezone); + retval.adjTime = newDaysHNSecs + theTimeHNSecs; + + return retval; + } + + /// + @safe unittest + { + import core.time : msecs, usecs, hnsecs; + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == + SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), msecs(24)).endOfMonth == + SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); + + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), usecs(5203)).endOfMonth == + SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); + + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), hnsecs(12345)).endOfMonth == + SysTime(DateTime(2000, 6, 30, 23, 59, 59), hnsecs(9_999_999))); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(2000, 2, 1)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 3, 1)).endOfMonth == SysTime(DateTime(1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 4, 1)).endOfMonth == SysTime(DateTime(1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 5, 1)).endOfMonth == SysTime(DateTime(1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 6, 1)).endOfMonth == SysTime(DateTime(1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 7, 1)).endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 8, 1)).endOfMonth == SysTime(DateTime(1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 9, 1)).endOfMonth == SysTime(DateTime(1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 10, 1)).endOfMonth == SysTime(DateTime(1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 11, 1)).endOfMonth == SysTime(DateTime(1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 12, 1)).endOfMonth == SysTime(DateTime(1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + + // Test B.C. + assert(SysTime(Date(-1999, 1, 1)).endOfMonth == SysTime(DateTime(-1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 2, 1)).endOfMonth == SysTime(DateTime(-1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-2000, 2, 1)).endOfMonth == SysTime(DateTime(-2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 3, 1)).endOfMonth == SysTime(DateTime(-1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 4, 1)).endOfMonth == SysTime(DateTime(-1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 5, 1)).endOfMonth == SysTime(DateTime(-1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 6, 1)).endOfMonth == SysTime(DateTime(-1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 7, 1)).endOfMonth == SysTime(DateTime(-1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 8, 1)).endOfMonth == SysTime(DateTime(-1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 9, 1)).endOfMonth == SysTime(DateTime(-1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 10, 1)).endOfMonth == + SysTime(DateTime(-1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 11, 1)).endOfMonth == + SysTime(DateTime(-1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 12, 1)).endOfMonth == + SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + //assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + + /++ + The last day in the month that this $(LREF SysTime) is in. + +/ + @property ubyte daysInMonth() @safe const nothrow + { + return Date(dayOfGregorianCal).daysInMonth; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28); + assert(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth == 29); + assert(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth == 31); + + // Test B.C. + assert(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth == 28); + assert(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth == 29); + assert(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.daysInMonth == 31); + //assert(ist.daysInMonth == 31); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const nothrow + { + return adjTime >= 0; + } + + /// + @safe unittest + { + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); + assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); + } + + @safe unittest + { + assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); + assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.isAD); + //assert(ist.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) + for this $(LREF SysTime) at the given time. For example, + prior to noon, 1996-03-31 would be the Julian day number 2_450_173, so + this function returns 2_450_173, while from noon onward, the Julian + day number would be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() @safe const nothrow + { + immutable jd = dayOfGregorianCal + 1_721_425; + return hour < 12 ? jd - 1 : jd; + } + + @safe unittest + { + assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1); + assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0); + + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay == 1_721_424); + assert(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay == 1_721_425); + + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay == 1_721_425); + assert(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay == 1_721_426); + + assert(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay == 2_299_160); + assert(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay == 2_299_161); + + assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay == 2_400_000); + assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay == 2_400_001); + + assert(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay == 2_444_973); + assert(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay == 2_444_974); + + assert(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay == 2_450_173); + assert(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay == 2_450_174); + + assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay == 2_455_432); + assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.julianDay == 2_451_366); + //assert(ist.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for + any time on this date (since, the modified Julian day changes at + midnight). + +/ + @property long modJulianDay() @safe const nothrow + { + return dayOfGregorianCal + 1_721_425 - 2_400_001; + } + + @safe unittest + { + assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0); + assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0); + + assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay == 55_432); + assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.modJulianDay == 51_365); + //assert(ist.modJulianDay == 51_365); + } + + + /++ + Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime). + +/ + Date opCast(T)() @safe const nothrow + if (is(Unqual!T == Date)) + { + return Date(dayOfGregorianCal); + } + + @safe unittest + { + assert(cast(Date) SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6)); + assert(cast(Date) SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31)); + assert(cast(Date) SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1)); + + assert(cast(Date) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == Date(1999, 7, 6)); + assert(cast(Date) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == Date(2000, 12, 31)); + assert(cast(Date) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == Date(2001, 1, 1)); + + assert(cast(Date) SysTime(Date(-1999, 7, 6)) == Date(-1999, 7, 6)); + assert(cast(Date) SysTime(Date(-2000, 12, 31)) == Date(-2000, 12, 31)); + assert(cast(Date) SysTime(Date(-2001, 1, 1)) == Date(-2001, 1, 1)); + + assert(cast(Date) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == Date(-1999, 7, 6)); + assert(cast(Date) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == Date(-2000, 12, 31)); + assert(cast(Date) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(Date) cst != Date.init); + //assert(cast(Date) ist != Date.init); + } + + + /++ + Returns a $(REF DateTime,std,datetime,date) equivalent to this + $(LREF SysTime). + +/ + DateTime opCast(T)() @safe const nothrow + if (is(Unqual!T == DateTime)) + { + try + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + return DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second)); + } + catch (Exception e) + assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); + } + + @safe unittest + { + assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(2000, 12, 31)) == DateTime(2000, 12, 31, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(2001, 1, 1)) == DateTime(2001, 1, 1, 0, 0, 0)); + + assert(cast(DateTime) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == DateTime(1999, 7, 6, 12, 10, 9)); + assert(cast(DateTime) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == DateTime(2000, 12, 31, 13, 11, 10)); + assert(cast(DateTime) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == DateTime(2001, 1, 1, 14, 12, 11)); + + assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22)) == DateTime(-1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(-1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(Date(-1999, 7, 6)) == DateTime(-1999, 7, 6, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(-2000, 12, 31)) == DateTime(-2000, 12, 31, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(-2001, 1, 1)) == DateTime(-2001, 1, 1, 0, 0, 0)); + + assert(cast(DateTime) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == DateTime(-1999, 7, 6, 12, 10, 9)); + assert(cast(DateTime) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == DateTime(-2000, 12, 31, 13, 11, 10)); + assert(cast(DateTime) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == DateTime(-2001, 1, 1, 14, 12, 11)); + + assert(cast(DateTime) SysTime(DateTime(2011, 1, 13, 8, 17, 2), msecs(296), LocalTime()) == + DateTime(2011, 1, 13, 8, 17, 2)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(DateTime) cst != DateTime.init); + //assert(cast(DateTime) ist != DateTime.init); + } + + + /++ + Returns a $(REF TimeOfDay,std,datetime,date) equivalent to this + $(LREF SysTime). + +/ + TimeOfDay opCast(T)() @safe const nothrow + if (is(Unqual!T == TimeOfDay)) + { + try + { + auto hnsecs = adjTime; + hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + return TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second); + } + catch (Exception e) + assert(0, "TimeOfDay's constructor threw."); + } + + @safe unittest + { + assert(cast(TimeOfDay) SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0)); + + assert(cast(TimeOfDay) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); + assert(cast(TimeOfDay) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); + assert(cast(TimeOfDay) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); + + assert(cast(TimeOfDay) SysTime(Date(-1999, 7, 6)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(-2000, 12, 31)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(-2001, 1, 1)) == TimeOfDay(0, 0, 0)); + + assert(cast(TimeOfDay) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); + assert(cast(TimeOfDay) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); + assert(cast(TimeOfDay) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + + // Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed. + // This allows assignment from const(SysTime) to SysTime. + // It may be a good idea to keep it though, since casting from a type to itself + // should be allowed, and it doesn't work without this opCast() since opCast() + // has already been defined for other types. + SysTime opCast(T)() @safe const pure nothrow + if (is(Unqual!T == SysTime)) + { + return SysTime(_stdTime, _timezone); + } + + + /++ + Converts this $(LREF SysTime) to a string with the format + YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds and TZ is time + zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be + hnsecs), but only has as many as are necessary to hold the correct value + (so no trailing zeroes), and if there are no fractional seconds, then + there is no decimal point. + + If this $(LREF SysTime)'s time zone is + $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time + zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC + (e.g. +0100 or -0700). Note that the offset from UTC is $(I not) enough + to uniquely identify the time zone. + + Time zone offsets will be in the form +HHMM or -HHMM. + + $(RED Warning: + Previously, toISOString did the same as $(LREF toISOExtString) and + generated +HH:MM or -HH:MM for the time zone when it was not + $(REF LocalTime,std,datetime,timezone) or + $(REF UTC,std,datetime,timezone), which is not in conformance with + ISO 9601 for the non-extended string format. This has now been + fixed. However, for now, fromISOString will continue to accept the + extended format for the time zone so that any code which has been + writing out the result of toISOString to read in later will continue + to work.) + +/ + string toISOString() @safe const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); + + if (_timezone is LocalTime()) + return dateTime.toISOString() ~ fracSecStr; + + if (_timezone is UTC()) + return dateTime.toISOString() ~ fracSecStr ~ "Z"; + + immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); + + return format("%s%s%s", + dateTime.toISOString(), + fracSecStr, + SimpleTimeZone.toISOExtString(utcOffset)); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + import core.time : msecs, hnsecs; + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == + "20100704T070612"); + + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOString() == + "19981225T021500.024"); + + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == + "00000105T230959"); + + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOString() == + "-00040105T000002.052092"); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOString() == "00010101T000000.0000001Z"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString() == "00091204T000000"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString() == "00991204T050612"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString() == "09991204T134459"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString() == "99990704T235959"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString() == "+100001020T010101"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "00091204T000000.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "00991204T050612.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "09991204T134459.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "99990704T235959.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "+100001020T010101.050789"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString() == + "20121221T121212-06:00"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString() == + "20121221T121212+07:00"); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOString() == + "00001231T235959.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOString() == "00001231T235959.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString() == "00001231T235959Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString() == "00001204T001204"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString() == "-00091204T000000"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString() == "-00991204T050612"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString() == "-09991204T134459"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString() == "-99990704T235959"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString() == "-100001020T010101"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOString() == "00001204T000000.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "-00091204T000000.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "-00991204T050612.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "-09991204T134459.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "-99990704T235959.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + + + /++ + Converts this $(LREF SysTime) to a string with the format + YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ + is the time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be + hnsecs), but only has as many as are necessary to hold the correct value + (so no trailing zeroes), and if there are no fractional seconds, then + there is no decimal point. + + If this $(LREF SysTime)'s time zone is + $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time + zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC + (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) + enough to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + +/ + string toISOExtString() @safe const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); + + if (_timezone is LocalTime()) + return dateTime.toISOExtString() ~ fracSecStr; + + if (_timezone is UTC()) + return dateTime.toISOExtString() ~ fracSecStr ~ "Z"; + + immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); + + return format("%s%s%s", + dateTime.toISOExtString(), + fracSecStr, + SimpleTimeZone.toISOExtString(utcOffset)); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + import core.time : msecs, hnsecs; + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); + + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOExtString() == + "1998-12-25T02:15:00.024"); + + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); + + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString() == + "-0004-01-05T00:00:02.052092"); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOExtString() == + "0001-01-01T00:00:00.0000001Z"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "0009-12-04T00:00:00.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "0099-12-04T05:06:12.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == "0999-12-04T13:44:59.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == "9999-07-04T23:59:59.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == + "+10000-10-20T01:01:01.050789"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString() == + "2012-12-21T12:12:12-06:00"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString() == + "2012-12-21T12:12:12+07:00"); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOExtString() == + "0000-12-31T23:59:59.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOExtString() == + "0000-12-31T23:59:59.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString() == "0000-12-31T23:59:59Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOExtString() == "0000-12-04T00:00:00.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "-0009-12-04T00:00:00.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "-0099-12-04T05:06:12.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == + "-0999-12-04T13:44:59.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == + "-9999-07-04T23:59:59.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == + "-10000-10-20T01:01:01.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + /++ + Converts this $(LREF SysTime) to a string with the format + YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ + is the time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be + hnsecs), but only has as many as are necessary to hold the correct value + (so no trailing zeroes), and if there are no fractional seconds, then + there is no decimal point. + + If this $(LREF SysTime)'s time zone is + $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time + zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC + (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) + enough to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + +/ + string toSimpleString() @safe const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); + + if (_timezone is LocalTime()) + return dateTime.toSimpleString() ~ fracSecStr; + + if (_timezone is UTC()) + return dateTime.toSimpleString() ~ fracSecStr ~ "Z"; + + immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); + + return format("%s%s%s", + dateTime.toSimpleString(), + fracSecStr, + SimpleTimeZone.toISOExtString(utcOffset)); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + import core.time : msecs, hnsecs; + import std.datetime.date : DateTime; + + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); + + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toSimpleString() == + "1998-Dec-25 02:15:00.024"); + + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); + + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toSimpleString() == + "-0004-Jan-05 00:00:02.052092"); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "0009-Dec-04 00:00:00.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "0099-Dec-04 05:06:12.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == + "0999-Dec-04 13:44:59.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == + "9999-Jul-04 23:59:59.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == + "+10000-Oct-20 01:01:01.050789"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString() == + "2012-Dec-21 12:12:12-06:00"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString() == + "2012-Dec-21 12:12:12+07:00"); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toSimpleString() == + "0000-Dec-31 23:59:59.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toSimpleString() == + "0000-Dec-31 23:59:59.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString() == "0000-Dec-31 23:59:59Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toSimpleString() == "0000-Dec-04 00:00:00.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "-0009-Dec-04 00:00:00.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "-0099-Dec-04 05:06:12.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == + "-0999-Dec-04 13:44:59.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == + "-9999-Jul-04 23:59:59.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == + "-10000-Oct-20 01:01:01.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + + /++ + Converts this $(LREF SysTime) to a string. + +/ + string toString() @safe const nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.toString()); + assert(cst.toString()); + //assert(ist.toString()); + } + + + /++ + Creates a $(LREF SysTime) from a string with the format + YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds is the time + zone). Whitespace is stripped from the given string. + + The exact format is exactly as described in $(D toISOString) except that + trailing zeroes are permitted - including having fractional seconds with + all zeroes. However, a decimal point with nothing following it is + invalid. Also, while $(LREF toISOString) will never generate a string + with more than 7 digits in the fractional seconds (because that's the + limit with hecto-nanosecond precision), it will allow more than 7 digits + in order to read strings from other sources that have higher precision + (however, any digits beyond 7 will be truncated). + + If there is no time zone in the string, then + $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", + then $(D UTC) is used. Otherwise, a + $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the + given offset from UTC is used. To get the returned $(LREF SysTime) to be + a particular time zone, pass in that time zone and the $(LREF SysTime) + to be returned will be converted to that time zone (though it will still + be read in as whatever time zone is in its string). + + The accepted formats for time zone offsets are +HH, -HH, +HHMM, and + -HHMM. + + $(RED Warning: + Previously, $(LREF toISOString) did the same as + $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time + zone when it was not $(REF LocalTime,std,datetime,timezone) or + $(REF UTC,std,datetime,timezone), which is not in conformance with + ISO 9601 for the non-extended string format. This has now been + fixed. However, for now, fromISOString will continue to accept the + extended format for the time zone so that any code which has been + writing out the result of toISOString to read in later will continue + to work.) + + Params: + isoString = A string formatted in the ISO format for dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO format or if the resulting $(LREF SysTime) would not + be valid. + +/ + static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe + if (isSomeString!S) + { + import std.algorithm.searching : startsWith, find; + import std.conv : to; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + immutable skipFirst = dstr.startsWith('+', '-') != 0; + + auto found = (skipFirst ? dstr[1..$] : dstr).find('.', 'Z', '+', '-'); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if (found[1] != 0) + { + if (found[1] == 1) + { + auto foundTZ = found[0].find('Z', '+', '-'); + + if (foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromISOString(dateTimeStr); + auto fracSec = fracSecsFromISOString(fracSecStr); + Rebindable!(immutable TimeZone) parsedZone; + + if (zoneStr.empty) + parsedZone = LocalTime(); + else if (zoneStr == "Z") + parsedZone = UTC(); + else + { + try + parsedZone = SimpleTimeZone.fromISOString(zoneStr); + catch (DateTimeException dte) + parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); + } + + auto retval = SysTime(dateTime, fracSec, parsedZone); + + if (tz !is null) + retval.timezone = tz; + + return retval; + } + catch (DateTimeException dte) + throw new DateTimeException(format("Invalid ISO String: %s", isoString)); + } + + /// + @safe unittest + { + import core.time : hours, msecs, usecs; + import std.datetime.date : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + + assert(SysTime.fromISOString("20100704T070612") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOString("19981225T021500.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); + + assert(SysTime.fromISOString("00000105T230959.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + + assert(SysTime.fromISOString("20130207T043937.000050392") == + SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); + + assert(SysTime.fromISOString("-00040105T000002") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + + assert(SysTime.fromISOString(" 20100704T070612 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOString("20100704T070612Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + + assert(SysTime.fromISOString("20100704T070612-0800") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(-8)))); + + assert(SysTime.fromISOString("20100704T070612+0800") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(8)))); + } + + @safe unittest + { + foreach (str; ["", "20100704000000", "20100704 000000", "20100704t000000", + "20100704T000000.", "20100704T000000.A", "20100704T000000.Z", + "20100704T000000.0000000A", "20100704T000000.00000000A", + "20100704T000000+", "20100704T000000-", "20100704T000000:", + "20100704T000000-:", "20100704T000000+:", "20100704T000000-1:", + "20100704T000000+1:", "20100704T000000+1:0", + "20100704T000000-12.00", "20100704T000000+12.00", + "20100704T000000-8", "20100704T000000+8", + "20100704T000000-800", "20100704T000000+800", + "20100704T000000-080", "20100704T000000+080", + "20100704T000000-2400", "20100704T000000+2400", + "20100704T000000-1260", "20100704T000000+1260", + "20100704T000000.0-8", "20100704T000000.0+8", + "20100704T000000.0-800", "20100704T000000.0+800", + "20100704T000000.0-080", "20100704T000000.0+080", + "20100704T000000.0-2400", "20100704T000000.0+2400", + "20100704T000000.0-1260", "20100704T000000.0+1260", + "20100704T000000-8:00", "20100704T000000+8:00", + "20100704T000000-08:0", "20100704T000000+08:0", + "20100704T000000-24:00", "20100704T000000+24:00", + "20100704T000000-12:60", "20100704T000000+12:60", + "20100704T000000.0-8:00", "20100704T000000.0+8:00", + "20100704T000000.0-08:0", "20100704T000000.0+08:0", + "20100704T000000.0-24:00", "20100704T000000.0+24:00", + "20100704T000000.0-12:60", "20100704T000000.0+12:60", + "2010-07-0400:00:00", "2010-07-04 00:00:00", + "2010-07-04t00:00:00", "2010-07-04T00:00:00.", + "2010-Jul-0400:00:00", "2010-Jul-04 00:00:00", "2010-Jul-04t00:00:00", + "2010-Jul-04T00:00:00", "2010-Jul-04 00:00:00.", + "2010-12-22T172201", "2010-Dec-22 17:22:01"]) + { + assertThrown!DateTimeException(SysTime.fromISOString(str), format("[%s]", str)); + } + + static void test(string str, SysTime st, size_t line = __LINE__) + { + if (SysTime.fromISOString(str) != st) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("20101222T172201", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + test("19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("-19990706T123033", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + test("+019990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + test("19070707T121212.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("19070707T121212.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("19070707T121212.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("20100704T000000.00000000", SysTime(Date(2010, 07, 04))); + test("20100704T000000.00000009", SysTime(Date(2010, 07, 04))); + test("20100704T000000.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1))); + test("19070707T121212.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("19070707T121212.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("19070707T121212.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + test("19070707T121212.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + + auto west60 = new immutable SimpleTimeZone(hours(-1)); + auto west90 = new immutable SimpleTimeZone(minutes(-90)); + auto west480 = new immutable SimpleTimeZone(hours(-8)); + auto east60 = new immutable SimpleTimeZone(hours(1)); + auto east90 = new immutable SimpleTimeZone(minutes(90)); + auto east480 = new immutable SimpleTimeZone(hours(8)); + + test("20101222T172201Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + test("20101222T172201-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("20101222T172201-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("20101222T172201-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("20101222T172201-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("20101222T172201+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("20101103T065106.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); + test("20101222T172201.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); + test("20101222T172201.23112-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("20101222T172201.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); + test("20101222T172201.1-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("20101222T172201.55-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("20101222T172201.1234567+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("20101222T172201.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + + // @@@DEPRECATED_2017-07@@@ + // This isn't deprecated per se, but that text will make it so that it + // pops up when deprecations are moved along around July 2017. At that + // time, the notice on the documentation should be removed, and we may + // or may not change the behavior of fromISOString to no longer accept + // ISO extended time zones (the concern being that programs will have + // written out strings somewhere to read in again that they'll still be + // reading in for years to come and may not be able to fix, even if the + // code is fixed). If/when we do change the behavior, these tests will + // start failing and will need to be updated accordingly. + test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("20101222T172201+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("20101222T172201.23112-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("20101222T172201.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("20101222T172201.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + } + + + /++ + Creates a $(LREF SysTime) from a string with the format + YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the + time zone). Whitespace is stripped from the given string. + + The exact format is exactly as described in $(D toISOExtString) + except that trailing zeroes are permitted - including having fractional + seconds with all zeroes. However, a decimal point with nothing following + it is invalid. Also, while $(LREF toISOExtString) will never generate a + string with more than 7 digits in the fractional seconds (because that's + the limit with hecto-nanosecond precision), it will allow more than 7 + digits in order to read strings from other sources that have higher + precision (however, any digits beyond 7 will be truncated). + + If there is no time zone in the string, then + $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", + then $(D UTC) is used. Otherwise, a + $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the + given offset from UTC is used. To get the returned $(LREF SysTime) to be + a particular time zone, pass in that time zone and the $(LREF SysTime) + to be returned will be converted to that time zone (though it will still + be read in as whatever time zone is in its string). + + The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and + -HH:MM. + + Params: + isoExtString = A string formatted in the ISO Extended format for + dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO format or if the resulting $(LREF SysTime) would not + be valid. + +/ + static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil, find; + import std.conv : to; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + auto tIndex = dstr.countUntil('T'); + enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto found = dstr[tIndex + 1 .. $].find('.', 'Z', '+', '-'); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if (found[1] != 0) + { + if (found[1] == 1) + { + auto foundTZ = found[0].find('Z', '+', '-'); + + if (foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromISOExtString(dateTimeStr); + auto fracSec = fracSecsFromISOString(fracSecStr); + Rebindable!(immutable TimeZone) parsedZone; + + if (zoneStr.empty) + parsedZone = LocalTime(); + else if (zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone); + + if (tz !is null) + retval.timezone = tz; + + return retval; + } + catch (DateTimeException dte) + throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); + } + + /// + @safe unittest + { + import core.time : hours, msecs, usecs; + import std.datetime.date : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + + assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); + + assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + + assert(SysTime.fromISOExtString("2013-02-07T04:39:37.000050392") == + SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); + + assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + + assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + + assert(SysTime.fromISOExtString("2010-07-04T07:06:12-08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(-8)))); + assert(SysTime.fromISOExtString("2010-07-04T07:06:12+08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(8)))); + } + + @safe unittest + { + foreach (str; ["", "20100704000000", "20100704 000000", + "20100704t000000", "20100704T000000.", "20100704T000000.0", + "2010-07:0400:00:00", "2010-07-04 00:00:00", + "2010-07-04 00:00:00", "2010-07-04t00:00:00", + "2010-07-04T00:00:00.", "2010-07-04T00:00:00.A", "2010-07-04T00:00:00.Z", + "2010-07-04T00:00:00.0000000A", "2010-07-04T00:00:00.00000000A", + "2010-07-04T00:00:00+", "2010-07-04T00:00:00-", + "2010-07-04T00:00:00:", "2010-07-04T00:00:00-:", "2010-07-04T00:00:00+:", + "2010-07-04T00:00:00-1:", "2010-07-04T00:00:00+1:", "2010-07-04T00:00:00+1:0", + "2010-07-04T00:00:00-12.00", "2010-07-04T00:00:00+12.00", + "2010-07-04T00:00:00-8", "2010-07-04T00:00:00+8", + "20100704T000000-800", "20100704T000000+800", + "20100704T000000-080", "20100704T000000+080", + "20100704T000000-2400", "20100704T000000+2400", + "20100704T000000-1260", "20100704T000000+1260", + "20100704T000000.0-800", "20100704T000000.0+800", + "20100704T000000.0-8", "20100704T000000.0+8", + "20100704T000000.0-080", "20100704T000000.0+080", + "20100704T000000.0-2400", "20100704T000000.0+2400", + "20100704T000000.0-1260", "20100704T000000.0+1260", + "2010-07-04T00:00:00-8:00", "2010-07-04T00:00:00+8:00", + "2010-07-04T00:00:00-24:00", "2010-07-04T00:00:00+24:00", + "2010-07-04T00:00:00-12:60", "2010-07-04T00:00:00+12:60", + "2010-07-04T00:00:00.0-8:00", "2010-07-04T00:00:00.0+8:00", + "2010-07-04T00:00:00.0-8", "2010-07-04T00:00:00.0+8", + "2010-07-04T00:00:00.0-24:00", "2010-07-04T00:00:00.0+24:00", + "2010-07-04T00:00:00.0-12:60", "2010-07-04T00:00:00.0+12:60", + "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", + "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.0", + "20101222T172201", "2010-Dec-22 17:22:01"]) + { + assertThrown!DateTimeException(SysTime.fromISOExtString(str), format("[%s]", str)); + } + + static void test(string str, SysTime st, size_t line = __LINE__) + { + if (SysTime.fromISOExtString(str) != st) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("2010-12-22T17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + test("1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("-1999-07-06T12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + test("+01999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("2010-07-04T00:00:00.00000000", SysTime(Date(2010, 07, 04))); + test("2010-07-04T00:00:00.00000009", SysTime(Date(2010, 07, 04))); + test("2010-07-04T00:00:00.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1))); + test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + test("1907-07-07T12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + + auto west60 = new immutable SimpleTimeZone(hours(-1)); + auto west90 = new immutable SimpleTimeZone(minutes(-90)); + auto west480 = new immutable SimpleTimeZone(hours(-8)); + auto east60 = new immutable SimpleTimeZone(hours(1)); + auto east90 = new immutable SimpleTimeZone(minutes(90)); + auto east480 = new immutable SimpleTimeZone(hours(8)); + + test("2010-12-22T17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + test("2010-12-22T17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-12-22T17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-12-22T17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("2010-12-22T17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("2010-12-22T17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-12-22T17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-12-22T17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-12-22T17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("2010-11-03T06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); + test("2010-12-22T17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); + test("2010-12-22T17:22:01.23112-01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("2010-12-22T17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); + test("2010-12-22T17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("2010-12-22T17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("2010-12-22T17:22:01.1234567+01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + } + + + /++ + Creates a $(LREF SysTime) from a string with the format + YYYY-MM-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the + time zone). Whitespace is stripped from the given string. + + The exact format is exactly as described in $(D toSimpleString) except + that trailing zeroes are permitted - including having fractional seconds + with all zeroes. However, a decimal point with nothing following it is + invalid. Also, while $(LREF toSimpleString) will never generate a + string with more than 7 digits in the fractional seconds (because that's + the limit with hecto-nanosecond precision), it will allow more than 7 + digits in order to read strings from other sources that have higher + precision (however, any digits beyond 7 will be truncated). + + If there is no time zone in the string, then + $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", + then $(D UTC) is used. Otherwise, a + $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the + given offset from UTC is used. To get the returned $(LREF SysTime) to be + a particular time zone, pass in that time zone and the $(LREF SysTime) + to be returned will be converted to that time zone (though it will still + be read in as whatever time zone is in its string). + + The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and + -HH:MM. + + Params: + simpleString = A string formatted in the way that + $(D toSimpleString) formats dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string is + not in the ISO format or if the resulting $(LREF SysTime) would not + be valid. + +/ + static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil, find; + import std.conv : to; + import std.string : strip; + + auto dstr = to!dstring(strip(simpleString)); + + auto spaceIndex = dstr.countUntil(' '); + enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString))); + + auto found = dstr[spaceIndex + 1 .. $].find('.', 'Z', '+', '-'); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if (found[1] != 0) + { + if (found[1] == 1) + { + auto foundTZ = found[0].find('Z', '+', '-'); + + if (foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromSimpleString(dateTimeStr); + auto fracSec = fracSecsFromISOString(fracSecStr); + Rebindable!(immutable TimeZone) parsedZone; + + if (zoneStr.empty) + parsedZone = LocalTime(); + else if (zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone); + + if (tz !is null) + retval.timezone = tz; + + return retval; + } + catch (DateTimeException dte) + throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); + } + + /// + @safe unittest + { + import core.time : hours, msecs, usecs; + import std.datetime.date : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); + + assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + + assert(SysTime.fromSimpleString("2013-Feb-07 04:39:37.000050392") == + SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); + + assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + + assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(-8)))); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(8)))); + } + + @safe unittest + { + foreach (str; ["", "20100704000000", "20100704 000000", + "20100704t000000", "20100704T000000.", "20100704T000000.0", + "2010-07-0400:00:00", "2010-07-04 00:00:00", "2010-07-04t00:00:00", + "2010-07-04T00:00:00.", "2010-07-04T00:00:00.0", + "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", "2010-Jul-04T00:00:00", + "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.A", "2010-Jul-04 00:00:00.Z", + "2010-Jul-04 00:00:00.0000000A", "2010-Jul-04 00:00:00.00000000A", + "2010-Jul-04 00:00:00+", "2010-Jul-04 00:00:00-", + "2010-Jul-04 00:00:00:", "2010-Jul-04 00:00:00-:", + "2010-Jul-04 00:00:00+:", "2010-Jul-04 00:00:00-1:", + "2010-Jul-04 00:00:00+1:", "2010-Jul-04 00:00:00+1:0", + "2010-Jul-04 00:00:00-12.00", "2010-Jul-04 00:00:00+12.00", + "2010-Jul-04 00:00:00-8", "2010-Jul-04 00:00:00+8", + "20100704T000000-800", "20100704T000000+800", + "20100704T000000-080", "20100704T000000+080", + "20100704T000000-2400", "20100704T000000+2400", + "20100704T000000-1260", "20100704T000000+1260", + "20100704T000000.0-800", "20100704T000000.0+800", + "20100704T000000.0-8", "20100704T000000.0+8", + "20100704T000000.0-080", "20100704T000000.0+080", + "20100704T000000.0-2400", "20100704T000000.0+2400", + "20100704T000000.0-1260", "20100704T000000.0+1260", + "2010-Jul-04 00:00:00-8:00", "2010-Jul-04 00:00:00+8:00", + "2010-Jul-04 00:00:00-08:0", "2010-Jul-04 00:00:00+08:0", + "2010-Jul-04 00:00:00-24:00", "2010-Jul-04 00:00:00+24:00", + "2010-Jul-04 00:00:00-12:60", "2010-Jul-04 00:00:00+24:60", + "2010-Jul-04 00:00:00.0-8:00", "2010-Jul-04 00:00:00+8:00", + "2010-Jul-04 00:00:00.0-8", "2010-Jul-04 00:00:00.0+8", + "2010-Jul-04 00:00:00.0-08:0", "2010-Jul-04 00:00:00.0+08:0", + "2010-Jul-04 00:00:00.0-24:00", "2010-Jul-04 00:00:00.0+24:00", + "2010-Jul-04 00:00:00.0-12:60", "2010-Jul-04 00:00:00.0+24:60", + "20101222T172201", "2010-12-22T172201"]) + { + assertThrown!DateTimeException(SysTime.fromSimpleString(str), format("[%s]", str)); + } + + static void test(string str, SysTime st, size_t line = __LINE__) + { + if (SysTime.fromSimpleString(str) != st) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("2010-Dec-22 17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + test("1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("-1999-Jul-06 12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + test("+01999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("2010-Jul-04 00:00:00.00000000", SysTime(Date(2010, 07, 04))); + test("2010-Jul-04 00:00:00.00000009", SysTime(Date(2010, 07, 04))); + test("2010-Jul-04 00:00:00.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1))); + test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-Jul-07 12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + test("1907-Jul-07 12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + + auto west60 = new immutable SimpleTimeZone(hours(-1)); + auto west90 = new immutable SimpleTimeZone(minutes(-90)); + auto west480 = new immutable SimpleTimeZone(hours(-8)); + auto east60 = new immutable SimpleTimeZone(hours(1)); + auto east90 = new immutable SimpleTimeZone(minutes(90)); + auto east480 = new immutable SimpleTimeZone(hours(8)); + + test("2010-Dec-22 17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + test("2010-Dec-22 17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-Dec-22 17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-Dec-22 17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("2010-Dec-22 17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("2010-Dec-22 17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-Dec-22 17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-Dec-22 17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-Dec-22 17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("2010-Nov-03 06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); + test("2010-Dec-22 17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); + test("2010-Dec-22 17:22:01.23112-01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("2010-Dec-22 17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); + test("2010-Dec-22 17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("2010-Dec-22 17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("2010-Dec-22 17:22:01.1234567+01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + } + + + /++ + Returns the $(LREF SysTime) farthest in the past which is representable + by $(LREF SysTime). + + The $(LREF SysTime) which is returned is in UTC. + +/ + @property static SysTime min() @safe pure nothrow + { + return SysTime(long.min, UTC()); + } + + @safe unittest + { + assert(SysTime.min.year < 0); + assert(SysTime.min < SysTime.max); + } + + + /++ + Returns the $(LREF SysTime) farthest in the future which is representable + by $(LREF SysTime). + + The $(LREF SysTime) which is returned is in UTC. + +/ + @property static SysTime max() @safe pure nothrow + { + return SysTime(long.max, UTC()); + } + + @safe unittest + { + assert(SysTime.max.year > 0); + assert(SysTime.max > SysTime.min); + } + + +private: + + /+ + Returns $(D stdTime) converted to $(LREF SysTime)'s time zone. + +/ + @property long adjTime() @safe const nothrow + { + return _timezone.utcToTZ(_stdTime); + } + + + /+ + Converts the given hnsecs from $(LREF SysTime)'s time zone to std time. + +/ + @property void adjTime(long adjTime) @safe nothrow + { + _stdTime = _timezone.tzToUTC(adjTime); + } + + + // Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058 + /+ + invariant() + { + assert(_timezone !is null, "Invariant Failure: timezone is null. Were you foolish enough to use " ~ + "SysTime.init? (since timezone for SysTime.init can't be set at compile time)."); + } + +/ + + + long _stdTime; + Rebindable!(immutable TimeZone) _timezone; +} + + +/++ + Converts from unix time (which uses midnight, January 1st, 1970 UTC as its + epoch and seconds as its units) to "std time" (which uses midnight, + January 1st, 1 A.D. UTC and hnsecs as its units). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO + 8601 and is what $(LREF SysTime) uses internally. However, holding the time + as an integer in hnescs since that epoch technically isn't actually part of + the standard, much as it's based on it, so the name "std time" isn't + particularly good, but there isn't an official name for it. C# uses "ticks" + for the same thing, but they aren't actually clock ticks, and the term + "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), + so it didn't make sense to use the term ticks here. So, for better or worse, + std.datetime uses the term "std time" for this. + + Params: + unixTime = The unix time to convert. + + See_Also: + SysTime.fromUnixTime + +/ +long unixTimeToStdTime(long unixTime) @safe pure nothrow +{ + return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime); +} + +/// +@safe unittest +{ + import std.datetime.date : DateTime; + import std.datetime.timezone : UTC; + + // Midnight, January 1st, 1970 + assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); + assert(SysTime(unixTimeToStdTime(0)) == + SysTime(DateTime(1970, 1, 1), UTC())); + + assert(unixTimeToStdTime(int.max) == 642_830_804_470_000_000L); + assert(SysTime(unixTimeToStdTime(int.max)) == + SysTime(DateTime(2038, 1, 19, 3, 14, 07), UTC())); + + assert(unixTimeToStdTime(-127_127) == 621_354_696_730_000_000L); + assert(SysTime(unixTimeToStdTime(-127_127)) == + SysTime(DateTime(1969, 12, 30, 12, 41, 13), UTC())); +} + +@safe unittest +{ + // Midnight, January 2nd, 1970 + assert(unixTimeToStdTime(86_400) == 621_355_968_000_000_000L + 864_000_000_000L); + // Midnight, December 31st, 1969 + assert(unixTimeToStdTime(-86_400) == 621_355_968_000_000_000L - 864_000_000_000L); + + assert(unixTimeToStdTime(0) == (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); + assert(unixTimeToStdTime(0) == (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); + + foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) + assert(unixTimeToStdTime((dt - DateTime(1970, 1, 1)).total!"seconds") == (dt - DateTime.init).total!"hnsecs"); +} + + +/++ + Converts std time (which uses midnight, January 1st, 1 A.D. UTC as its epoch + and hnsecs as its units) to unix time (which uses midnight, January 1st, + 1970 UTC as its epoch and seconds as its units). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO + 8601 and is what $(LREF SysTime) uses internally. However, holding the time + as an integer in hnescs since that epoch technically isn't actually part of + the standard, much as it's based on it, so the name "std time" isn't + particularly good, but there isn't an official name for it. C# uses "ticks" + for the same thing, but they aren't actually clock ticks, and the term + "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), + so it didn't make sense to use the term ticks here. So, for better or worse, + std.datetime uses the term "std time" for this. + + By default, the return type is time_t (which is normally an alias for + int on 32-bit systems and long on 64-bit systems), but if a different + size is required than either int or long can be passed as a template + argument to get the desired size. + + If the return type is int, and the result can't fit in an int, then the + closest value that can be held in 32 bits will be used (so $(D int.max) + if it goes over and $(D int.min) if it goes under). However, no attempt + is made to deal with integer overflow if the return type is long. + + Params: + T = The return type (int or long). It defaults to time_t, which is + normally 32 bits on a 32-bit system and 64 bits on a 64-bit + system. + stdTime = The std time to convert. + + Returns: + A signed integer representing the unix time which is equivalent to + the given std time. + + See_Also: + SysTime.toUnixTime + +/ +T stdTimeToUnixTime(T = time_t)(long stdTime) @safe pure nothrow +if (is(T == int) || is(T == long)) +{ + immutable unixTime = convert!("hnsecs", "seconds")(stdTime - 621_355_968_000_000_000L); + + static assert(is(time_t == int) || is(time_t == long), + "Currently, std.datetime only supports systems where time_t is int or long"); + + static if (is(T == long)) + return unixTime; + else static if (is(T == int)) + { + if (unixTime > int.max) + return int.max; + return unixTime < int.min ? int.min : cast(int) unixTime; + } + else + static assert(0, "Bug in template constraint. Only int and long allowed."); +} + +/// +@safe unittest +{ + // Midnight, January 1st, 1970 UTC + assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0); + + // 2038-01-19 03:14:07 UTC + assert(stdTimeToUnixTime(642_830_804_470_000_000L) == int.max); +} + +@safe unittest +{ + enum unixEpochAsStdTime = (Date(1970, 1, 1) - Date.init).total!"hnsecs"; + + assert(stdTimeToUnixTime(unixEpochAsStdTime) == 0); // Midnight, January 1st, 1970 + assert(stdTimeToUnixTime(unixEpochAsStdTime + 864_000_000_000L) == 86_400); // Midnight, January 2nd, 1970 + assert(stdTimeToUnixTime(unixEpochAsStdTime - 864_000_000_000L) == -86_400); // Midnight, December 31st, 1969 + + assert(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs") == 0); + assert(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs") == 0); + + foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) + assert(stdTimeToUnixTime((dt - DateTime.init).total!"hnsecs") == (dt - DateTime(1970, 1, 1)).total!"seconds"); + + enum max = convert!("seconds", "hnsecs")(int.max); + enum min = convert!("seconds", "hnsecs")(int.min); + enum one = convert!("seconds", "hnsecs")(1); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max) == int.max); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max) == int.max); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + one) == int.max + 1L); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + one) == int.max); + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + 9_999_999) == int.max); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + 9_999_999) == int.max); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min) == int.min); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min) == int.min); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - one) == int.min - 1L); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - one) == int.min); + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - 9_999_999) == int.min); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - 9_999_999) == int.min); +} + + +version(StdDdoc) +{ + version(Windows) + {} + else + { + alias SYSTEMTIME = void*; + alias FILETIME = void*; + } + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(D SYSTEMTIME) struct to a $(LREF SysTime). + + Params: + st = The $(D SYSTEMTIME) struct to convert. + tz = The time zone that the time in the $(D SYSTEMTIME) struct is + assumed to be (if the $(D SYSTEMTIME) was supplied by a Windows + system call, the $(D SYSTEMTIME) will either be in local time + or UTC, depending on the call). + + Throws: + $(REF DateTimeException,std,datetime,date) if the given + $(D SYSTEMTIME) will not fit in a $(LREF SysTime), which is highly + unlikely to happen given that $(D SysTime.max) is in 29,228 A.D. and + the maximum $(D SYSTEMTIME) is in 30,827 A.D. + +/ + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(LREF SysTime) to a $(D SYSTEMTIME) struct. + + The $(D SYSTEMTIME) which is returned will be set using the given + $(LREF SysTime)'s time zone, so to get the $(D SYSTEMTIME) in + UTC, set the $(LREF SysTime)'s time zone to UTC. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given + $(LREF SysTime) will not fit in a $(D SYSTEMTIME). This will only + happen if the $(LREF SysTime)'s date is prior to 1601 A.D. + +/ + SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(D FILETIME) struct to the number of hnsecs since midnight, + January 1st, 1 A.D. + + Params: + ft = The $(D FILETIME) struct to convert. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given + $(D FILETIME) cannot be represented as the return value. + +/ + long FILETIMEToStdTime(scope const FILETIME* ft) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(D FILETIME) struct to a $(LREF SysTime). + + Params: + ft = The $(D FILETIME) struct to convert. + tz = The time zone that the $(LREF SysTime) will be in + ($(D FILETIME)s are in UTC). + + Throws: + $(REF DateTimeException,std,datetime,date) if the given + $(D FILETIME) will not fit in a $(LREF SysTime). + +/ + SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a number of hnsecs since midnight, January 1st, 1 A.D. to a + $(D FILETIME) struct. + + Params: + stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. + UTC. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given value will + not fit in a $(D FILETIME). + +/ + FILETIME stdTimeToFILETIME(long stdTime) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(LREF SysTime) to a $(D FILETIME) struct. + + $(D FILETIME)s are always in UTC. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given + $(LREF SysTime) will not fit in a $(D FILETIME). + +/ + FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; +} +else version(Windows) +{ + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe + { + const max = SysTime.max; + + static void throwLaterThanMax() + { + throw new DateTimeException("The given SYSTEMTIME is for a date greater than SysTime.max."); + } + + if (st.wYear > max.year) + throwLaterThanMax(); + else if (st.wYear == max.year) + { + if (st.wMonth > max.month) + throwLaterThanMax(); + else if (st.wMonth == max.month) + { + if (st.wDay > max.day) + throwLaterThanMax(); + else if (st.wDay == max.day) + { + if (st.wHour > max.hour) + throwLaterThanMax(); + else if (st.wHour == max.hour) + { + if (st.wMinute > max.minute) + throwLaterThanMax(); + else if (st.wMinute == max.minute) + { + if (st.wSecond > max.second) + throwLaterThanMax(); + else if (st.wSecond == max.second) + { + if (st.wMilliseconds > max.fracSecs.total!"msecs") + throwLaterThanMax(); + } + } + } + } + } + } + + auto dt = DateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + + return SysTime(dt, msecs(st.wMilliseconds), tz); + } + + @system unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + auto converted = SYSTEMTIMEToSysTime(&st, UTC()); + + assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + } + + + SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe + { + immutable dt = cast(DateTime) sysTime; + + if (dt.year < 1601) + throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the year 1601."); + + SYSTEMTIME st; + + st.wYear = dt.year; + st.wMonth = dt.month; + st.wDayOfWeek = dt.dayOfWeek; + st.wDay = dt.day; + st.wHour = dt.hour; + st.wMinute = dt.minute; + st.wSecond = dt.second; + st.wMilliseconds = cast(ushort) sysTime.fracSecs.total!"msecs"; + + return st; + } + + @system unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); + + SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); + + assert(st.wYear == result.wYear); + assert(st.wMonth == result.wMonth); + assert(st.wDayOfWeek == result.wDayOfWeek); + assert(st.wDay == result.wDay); + assert(st.wHour == result.wHour); + assert(st.wMinute == result.wMinute); + assert(st.wSecond == result.wSecond); + assert(st.wMilliseconds == result.wMilliseconds); + } + + private enum hnsecsFrom1601 = 504_911_232_000_000_000L; + + long FILETIMEToStdTime(scope const FILETIME* ft) @safe + { + ULARGE_INTEGER ul; + ul.HighPart = ft.dwHighDateTime; + ul.LowPart = ft.dwLowDateTime; + ulong tempHNSecs = ul.QuadPart; + + if (tempHNSecs > long.max - hnsecsFrom1601) + throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); + + return cast(long) tempHNSecs + hnsecsFrom1601; + } + + SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe + { + auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); + sysTime.timezone = tz; + return sysTime; + } + + @system unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + + auto converted = FILETIMEToSysTime(&ft); + + assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + } + + + FILETIME stdTimeToFILETIME(long stdTime) @safe + { + if (stdTime < hnsecsFrom1601) + throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); + + ULARGE_INTEGER ul; + ul.QuadPart = cast(ulong) stdTime - hnsecsFrom1601; + + FILETIME ft; + ft.dwHighDateTime = ul.HighPart; + ft.dwLowDateTime = ul.LowPart; + + return ft; + } + + FILETIME SysTimeToFILETIME(SysTime sysTime) @safe + { + return stdTimeToFILETIME(sysTime.stdTime); + } + + @system unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + auto sysTime = FILETIMEToSysTime(&ft, UTC()); + + FILETIME result = SysTimeToFILETIME(sysTime); + + assert(ft.dwLowDateTime == result.dwLowDateTime); + assert(ft.dwHighDateTime == result.dwHighDateTime); + } +} + + +/++ + Type representing the DOS file date/time format. + +/ +alias DosFileTime = uint; + +/++ + Converts from DOS file date/time to $(LREF SysTime). + + Params: + dft = The DOS file time to convert. + tz = The time zone which the DOS file time is assumed to be in. + + Throws: + $(REF DateTimeException,std,datetime,date) if the $(D DosFileTime) is + invalid. + +/ +SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe +{ + uint dt = cast(uint) dft; + + if (dt == 0) + throw new DateTimeException("Invalid DosFileTime."); + + int year = ((dt >> 25) & 0x7F) + 1980; + int month = ((dt >> 21) & 0x0F); // 1 .. 12 + int dayOfMonth = ((dt >> 16) & 0x1F); // 1 .. 31 + int hour = (dt >> 11) & 0x1F; // 0 .. 23 + int minute = (dt >> 5) & 0x3F; // 0 .. 59 + int second = (dt << 1) & 0x3E; // 0 .. 58 (in 2 second increments) + + try + return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); + catch (DateTimeException dte) + throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); +} + +@safe unittest +{ + assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == SysTime(DateTime(1980, 1, 1, 0, 0, 0))); + assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == SysTime(DateTime(2107, 12, 31, 23, 59, 58))); + assert(DosFileTimeToSysTime(0x3E3F8456) == SysTime(DateTime(2011, 1, 31, 16, 34, 44))); +} + + +/++ + Converts from $(LREF SysTime) to DOS file date/time. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given + $(LREF SysTime) cannot be converted to a $(D DosFileTime). + +/ +DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe +{ + auto dateTime = cast(DateTime) sysTime; + + if (dateTime.year < 1980) + throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); + + if (dateTime.year > 2107) + throw new DateTimeException("DOS File Times cannot hold dates past 2107."); + + uint retval = 0; + retval = (dateTime.year - 1980) << 25; + retval |= (dateTime.month & 0x0F) << 21; + retval |= (dateTime.day & 0x1F) << 16; + retval |= (dateTime.hour & 0x1F) << 11; + retval |= (dateTime.minute & 0x3F) << 5; + retval |= (dateTime.second >> 1) & 0x1F; + + return cast(DosFileTime) retval; +} + +@safe unittest +{ + assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == 0b00000000001000010000000000000000); + assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == 0b11111111100111111011111101111101); + assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == 0x3E3F8456); +} + + +/++ + The given array of $(D char) or random-access range of $(D char) or + $(D ubyte) is expected to be in the format specified in + $(HTTP tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the + grammar rule $(I date-time). It is the date-time format commonly used in + internet messages such as e-mail and HTTP. The corresponding + $(LREF SysTime) will be returned. + + RFC 822 was the original spec (hence the function's name), whereas RFC 5322 + is the current spec. + + The day of the week is ignored beyond verifying that it's a valid day of the + week, as the day of the week can be inferred from the date. It is not + checked whether the given day of the week matches the actual day of the week + of the given date (though it is technically invalid per the spec if the + day of the week doesn't match the actual day of the week of the given date). + + If the time zone is $(D "-0000") (or considered to be equivalent to + $(D "-0000") by section 4.3 of the spec), a + $(REF SimpleTimeZone,std,datetime,timezone) with a utc offset of $(D 0) is + used rather than $(REF UTC,std,datetime,timezone), whereas $(D "+0000") uses + $(REF UTC,std,datetime,timezone). + + Note that because $(LREF SysTime) does not currently support having a second + value of 60 (as is sometimes done for leap seconds), if the date-time value + does have a value of 60 for the seconds, it is treated as 59. + + The one area in which this function violates RFC 5322 is that it accepts + $(D "\n") in folding whitespace in the place of $(D "\r\n"), because the + HTTP spec requires it. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given string doesn't + follow the grammar for a date-time field or if the resulting + $(LREF SysTime) is invalid. + +/ +SysTime parseRFC822DateTime()(in char[] value) @safe +{ + import std.string : representation; + return parseRFC822DateTime(value.representation); +} + +/++ Ditto +/ +SysTime parseRFC822DateTime(R)(R value) @safe +if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && + (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) +{ + import std.algorithm.searching : find, all; + import std.ascii : isDigit, isAlpha, isPrintable; + import std.conv : to; + import std.functional : not; + import std.string : capitalize, format; + import std.traits : EnumMembers, isArray; + import std.typecons : Rebindable; + + void stripAndCheckLen(R valueBefore, size_t minLen, size_t line = __LINE__) + { + value = _stripCFWS(valueBefore); + if (value.length < minLen) + throw new DateTimeException("date-time value too short", __FILE__, line); + } + stripAndCheckLen(value, "7Dec1200:00A".length); + + static if (isArray!R && (is(ElementEncodingType!R == char) || is(ElementEncodingType!R == ubyte))) + { + static string sliceAsString(R str) @trusted + { + return cast(string) str; + } + } + else + { + char[4] temp; + char[] sliceAsString(R str) @trusted + { + size_t i = 0; + foreach (c; str) + temp[i++] = cast(char) c; + return temp[0 .. str.length]; + } + } + + // day-of-week + if (isAlpha(value[0])) + { + auto dowStr = sliceAsString(value[0 .. 3]); + switch (dowStr) + { + foreach (dow; EnumMembers!DayOfWeek) + { + enum dowC = capitalize(to!string(dow)); + case dowC: + goto afterDoW; + } + default: throw new DateTimeException(format("Invalid day-of-week: %s", dowStr)); + } +afterDoW: stripAndCheckLen(value[3 .. value.length], ",7Dec1200:00A".length); + if (value[0] != ',') + throw new DateTimeException("day-of-week missing comma"); + stripAndCheckLen(value[1 .. value.length], "7Dec1200:00A".length); + } + + // day + immutable digits = isDigit(value[1]) ? 2 : 1; + immutable day = _convDigits!short(value[0 .. digits]); + if (day == -1) + throw new DateTimeException("Invalid day"); + stripAndCheckLen(value[digits .. value.length], "Dec1200:00A".length); + + // month + Month month; + { + auto monStr = sliceAsString(value[0 .. 3]); + switch (monStr) + { + foreach (mon; EnumMembers!Month) + { + enum monC = capitalize(to!string(mon)); + case monC: + { + month = mon; + goto afterMon; + } + } + default: throw new DateTimeException(format("Invalid month: %s", monStr)); + } +afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); + } + + // year + auto found = value[2 .. value.length].find!(not!(std.ascii.isDigit))(); + size_t yearLen = value.length - found.length; + if (found.length == 0) + throw new DateTimeException("Invalid year"); + if (found[0] == ':') + yearLen -= 2; + auto year = _convDigits!short(value[0 .. yearLen]); + if (year < 1900) + { + if (year == -1) + throw new DateTimeException("Invalid year"); + if (yearLen < 4) + { + if (yearLen == 3) + year += 1900; + else if (yearLen == 2) + year += year < 50 ? 2000 : 1900; + else + throw new DateTimeException("Invalid year. Too few digits."); + } + else + throw new DateTimeException("Invalid year. Cannot be earlier than 1900."); + } + stripAndCheckLen(value[yearLen .. value.length], "00:00A".length); + + // hour + immutable hour = _convDigits!short(value[0 .. 2]); + stripAndCheckLen(value[2 .. value.length], ":00A".length); + if (value[0] != ':') + throw new DateTimeException("Invalid hour"); + stripAndCheckLen(value[1 .. value.length], "00A".length); + + // minute + immutable minute = _convDigits!short(value[0 .. 2]); + stripAndCheckLen(value[2 .. value.length], "A".length); + + // second + short second; + if (value[0] == ':') + { + stripAndCheckLen(value[1 .. value.length], "00A".length); + second = _convDigits!short(value[0 .. 2]); + // this is just if/until SysTime is sorted out to fully support leap seconds + if (second == 60) + second = 59; + stripAndCheckLen(value[2 .. value.length], "A".length); + } + + immutable(TimeZone) parseTZ(int sign) + { + if (value.length < 5) + throw new DateTimeException("Invalid timezone"); + immutable zoneHours = _convDigits!short(value[1 .. 3]); + immutable zoneMinutes = _convDigits!short(value[3 .. 5]); + if (zoneHours == -1 || zoneMinutes == -1 || zoneMinutes > 59) + throw new DateTimeException("Invalid timezone"); + value = value[5 .. value.length]; + immutable utcOffset = (dur!"hours"(zoneHours) + dur!"minutes"(zoneMinutes)) * sign; + if (utcOffset == Duration.zero) + { + return sign == 1 ? cast(immutable(TimeZone))UTC() + : cast(immutable(TimeZone))new immutable SimpleTimeZone(Duration.zero); + } + return new immutable(SimpleTimeZone)(utcOffset); + } + + // zone + Rebindable!(immutable TimeZone) tz; + if (value[0] == '-') + tz = parseTZ(-1); + else if (value[0] == '+') + tz = parseTZ(1); + else + { + // obs-zone + immutable tzLen = value.length - find(value, ' ', '\t', '(')[0].length; + switch (sliceAsString(value[0 .. tzLen <= 4 ? tzLen : 4])) + { + case "UT": case "GMT": tz = UTC(); break; + case "EST": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; + case "EDT": tz = new immutable SimpleTimeZone(dur!"hours"(-4)); break; + case "CST": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; + case "CDT": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; + case "MST": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; + case "MDT": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; + case "PST": tz = new immutable SimpleTimeZone(dur!"hours"(-8)); break; + case "PDT": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; + case "J": case "j": throw new DateTimeException("Invalid timezone"); + default: + { + if (all!(std.ascii.isAlpha)(value[0 .. tzLen])) + { + tz = new immutable SimpleTimeZone(Duration.zero); + break; + } + throw new DateTimeException("Invalid timezone"); + } + } + value = value[tzLen .. value.length]; + } + + // This is kind of arbitrary. Technically, nothing but CFWS is legal past + // the end of the timezone, but we don't want to be picky about that in a + // function that's just parsing rather than validating. So, the idea here is + // that if the next character is printable (and not part of CFWS), then it + // might be part of the timezone and thus affect what the timezone was + // supposed to be, so we'll throw, but otherwise, we'll just ignore it. + if (!value.empty && isPrintable(value[0]) && value[0] != ' ' && value[0] != '(') + throw new DateTimeException("Invalid timezone"); + + try + return SysTime(DateTime(year, month, day, hour, minute, second), tz); + catch (DateTimeException dte) + throw new DateTimeException("date-time format is correct, but the resulting SysTime is invalid.", dte); +} + +/// +@safe unittest +{ + import core.time : hours; + import std.datetime.date : DateTime, DateTimeException; + import std.datetime.timezone : SimpleTimeZone, UTC; + import std.exception : assertThrown; + + auto tz = new immutable SimpleTimeZone(hours(-8)); + assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") == + SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz)); + + assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") == + SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC())); + + auto badStr = "29 Feb 2001 12:17:16 +0200"; + assertThrown!DateTimeException(parseRFC822DateTime(badStr)); +} + +version(unittest) void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__) +{ + import std.format : format; + auto value = cr(str); + auto result = parseRFC822DateTime(value); + if (result != expected) + throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line); +} + +version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LINE__) +{ + try + parseRFC822DateTime(cr(str)); + catch (DateTimeException) + return; + throw new AssertError("No DateTimeException was thrown", __FILE__, line); +} + +@system unittest +{ + import std.algorithm.iteration : filter, map; + import std.algorithm.searching : canFind; + import std.array : array; + import std.ascii : letters; + import std.format : format; + import std.meta : AliasSeq; + import std.range : chain, iota, take; + import std.stdio : writefln, writeln; + import std.string : representation; + + static struct Rand3Letters + { + enum empty = false; + @property auto front() { return _mon; } + void popFront() + { + import std.exception : assumeUnique; + import std.random : rndGen; + _mon = rndGen.map!(a => letters[a % letters.length])().take(3).array().assumeUnique(); + } + string _mon; + static auto start() { Rand3Letters retval; retval.popFront(); return retval; } + } + + foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, + function(string a){return cast(ubyte[]) a;}, + function(string a){return a;}, + function(string a){return map!(b => cast(char) b)(a.representation);})) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + scope(failure) writeln(typeof(cr).stringof); + alias test = testParse822!cr; + alias testBad = testBadParse822!cr; + + immutable std1 = DateTime(2012, 12, 21, 13, 14, 15); + immutable std2 = DateTime(2012, 12, 21, 13, 14, 0); + immutable dst1 = DateTime(1976, 7, 4, 5, 4, 22); + immutable dst2 = DateTime(1976, 7, 4, 5, 4, 0); + + test("21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); + test("21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); + test("Fri, 21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); + test("Fri, 21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); + + test("04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + test("04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + + test("4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + test("4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + + auto badTZ = new immutable SimpleTimeZone(Duration.zero); + test("21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); + test("21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); + test("Fri, 21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); + test("Fri, 21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); + + test("04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + test("04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + + test("4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + test("4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + + auto pst = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto pdt = new immutable SimpleTimeZone(dur!"hours"(-7)); + test("21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); + test("21 Dec 2012 13:14 -0800", SysTime(std2, pst)); + test("Fri, 21 Dec 2012 13:14 -0800", SysTime(std2, pst)); + test("Fri, 21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); + + test("04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + test("04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + + test("4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + test("4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + + auto cet = new immutable SimpleTimeZone(dur!"hours"(1)); + auto cest = new immutable SimpleTimeZone(dur!"hours"(2)); + test("21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); + test("21 Dec 2012 13:14 +0100", SysTime(std2, cet)); + test("Fri, 21 Dec 2012 13:14 +0100", SysTime(std2, cet)); + test("Fri, 21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); + + test("04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + test("04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + + test("4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + test("4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + + // dst and std times are switched in the Southern Hemisphere which is why the + // time zone names and DateTime variables don't match. + auto cstStd = new immutable SimpleTimeZone(dur!"hours"(9) + dur!"minutes"(30)); + auto cstDST = new immutable SimpleTimeZone(dur!"hours"(10) + dur!"minutes"(30)); + test("21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); + test("21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); + test("Fri, 21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); + test("Fri, 21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); + + test("04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + + test("4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + + foreach (int i, mon; _monthNames) + { + test(format("17 %s 2012 00:05:02 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 2), UTC())); + test(format("17 %s 2012 00:05 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 0), UTC())); + } + + import std.uni : toLower, toUpper; + foreach (mon; chain(_monthNames[].map!(a => toLower(a))(), + _monthNames[].map!(a => toUpper(a))(), + ["Jam", "Jen", "Fec", "Fdb", "Mas", "Mbr", "Aps", "Aqr", "Mai", "Miy", + "Jum", "Jbn", "Jup", "Jal", "Aur", "Apg", "Sem", "Sap", "Ocm", "Odt", + "Nom", "Nav", "Dem", "Dac"], + Rand3Letters.start().filter!(a => !_monthNames[].canFind(a)).take(20))) + { + scope(failure) writefln("Month: %s", mon); + testBad(format("17 %s 2012 00:05:02 +0000", mon)); + testBad(format("17 %s 2012 00:05 +0000", mon)); + } + + immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + + { + auto start = SysTime(DateTime(2012, 11, 11, 9, 42, 0), UTC()); + int day = 11; + + foreach (int i, dow; daysOfWeekNames) + { + auto curr = start + dur!"days"(i); + test(format("%s, %s Nov 2012 09:42:00 +0000", dow, day), curr); + test(format("%s, %s Nov 2012 09:42 +0000", dow, day++), curr); + + // Whether the day of the week matches the date is ignored. + test(format("%s, 11 Nov 2012 09:42:00 +0000", dow), start); + test(format("%s, 11 Nov 2012 09:42 +0000", dow), start); + } + } + + foreach (dow; chain(daysOfWeekNames[].map!(a => toLower(a))(), + daysOfWeekNames[].map!(a => toUpper(a))(), + ["Sum", "Spn", "Mom", "Man", "Tuf", "Tae", "Wem", "Wdd", "The", "Tur", + "Fro", "Fai", "San", "Sut"], + Rand3Letters.start().filter!(a => !daysOfWeekNames[].canFind(a)).take(20))) + { + scope(failure) writefln("Day of Week: %s", dow); + testBad(format("%s, 11 Nov 2012 09:42:00 +0000", dow)); + testBad(format("%s, 11 Nov 2012 09:42 +0000", dow)); + } + + testBad("31 Dec 1899 23:59:59 +0000"); + test("01 Jan 1900 00:00:00 +0000", SysTime(Date(1900, 1, 1), UTC())); + test("01 Jan 1900 00:00:00 -0000", SysTime(Date(1900, 1, 1), + new immutable SimpleTimeZone(Duration.zero))); + test("01 Jan 1900 00:00:00 -0700", SysTime(Date(1900, 1, 1), + new immutable SimpleTimeZone(dur!"hours"(-7)))); + + { + auto st1 = SysTime(Date(1900, 1, 1), UTC()); + auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); + foreach (i; 1900 .. 2102) + { + test(format("1 Jan %05d 00:00 +0000", i), st1); + test(format("1 Jan %05d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + st1.year = 9998; + st2.year = 9998; + foreach (i; 9998 .. 11_002) + { + test(format("1 Jan %05d 00:00 +0000", i), st1); + test(format("1 Jan %05d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + testBad("12 Feb 1907 23:17:09 0000"); + testBad("12 Feb 1907 23:17:09 +000"); + testBad("12 Feb 1907 23:17:09 -000"); + testBad("12 Feb 1907 23:17:09 +00000"); + testBad("12 Feb 1907 23:17:09 -00000"); + testBad("12 Feb 1907 23:17:09 +A"); + testBad("12 Feb 1907 23:17:09 +PST"); + testBad("12 Feb 1907 23:17:09 -A"); + testBad("12 Feb 1907 23:17:09 -PST"); + + // test trailing stuff that gets ignored + { + foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) + { + scope(failure) writefln("c: %d", c); + test(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c), SysTime(std1, UTC())); + test(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c), SysTime(std1, UTC())); + test(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c), SysTime(std1, UTC())); + } + } + + // test trailing stuff that doesn't get ignored + { + foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) + { + scope(failure) writefln("c: %d", c); + testBad(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c)); + testBad(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c)); + testBad(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c)); + } + } + + testBad("32 Jan 2012 12:13:14 -0800"); + testBad("31 Jan 2012 24:13:14 -0800"); + testBad("31 Jan 2012 12:60:14 -0800"); + testBad("31 Jan 2012 12:13:61 -0800"); + testBad("31 Jan 2012 12:13:14 -0860"); + test("31 Jan 2012 12:13:14 -0859", + SysTime(DateTime(2012, 1, 31, 12, 13, 14), + new immutable SimpleTimeZone(dur!"hours"(-8) + dur!"minutes"(-59)))); + + // leap-seconds + test("21 Dec 2012 15:59:60 -0800", SysTime(DateTime(2012, 12, 21, 15, 59, 59), pst)); + + // FWS + test("Sun,4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun,4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("Sun,4 Jul 1976 05:04 +0930 (foo)", SysTime(dst2, cstStd)); + test("Sun,4 Jul 1976 05:04:22 +0930 (foo)", SysTime(dst1, cstStd)); + test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04 \r\n +0930 \r\n (foo)", SysTime(dst2, cstStd)); + test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04:22 \r\n +0930 \r\n (foo)", SysTime(dst1, cstStd)); + + auto str = "01 Jan 2012 12:13:14 -0800 "; + test(str, SysTime(DateTime(2012, 1, 1, 12, 13, 14), new immutable SimpleTimeZone(hours(-8)))); + foreach (i; 0 .. str.length) + { + auto currStr = str.dup; + currStr[i] = 'x'; + scope(failure) writefln("failed: %s", currStr); + testBad(cast(string) currStr); + } + foreach (i; 2 .. str.length) + { + auto currStr = str[0 .. $ - i]; + scope(failure) writefln("failed: %s", currStr); + testBad(cast(string) currStr); + testBad((cast(string) currStr) ~ " "); + } + }(); +} + +// Obsolete Format per section 4.3 of RFC 5322. +@system unittest +{ + import std.algorithm.iteration : filter, map; + import std.ascii : letters; + import std.exception : collectExceptionMsg; + import std.format : format; + import std.meta : AliasSeq; + import std.range : chain, iota; + import std.stdio : writefln, writeln; + import std.string : representation; + + auto std1 = SysTime(DateTime(2012, 12, 21, 13, 14, 15), UTC()); + auto std2 = SysTime(DateTime(2012, 12, 21, 13, 14, 0), UTC()); + auto std3 = SysTime(DateTime(1912, 12, 21, 13, 14, 15), UTC()); + auto std4 = SysTime(DateTime(1912, 12, 21, 13, 14, 0), UTC()); + auto dst1 = SysTime(DateTime(1976, 7, 4, 5, 4, 22), UTC()); + auto dst2 = SysTime(DateTime(1976, 7, 4, 5, 4, 0), UTC()); + auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC()); + auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC()); + + foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, + function(string a){return cast(ubyte[]) a;}, + function(string a){return a;}, + function(string a){return map!(b => cast(char) b)(a.representation);})) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + scope(failure) writeln(typeof(cr).stringof); + alias test = testParse822!cr; + { + auto list = ["", " ", " \r\n\t", "\t\r\n (hello world( frien(dog)) silly \r\n ) \t\t \r\n ()", + " \n ", "\t\n\t", " \n\t (foo) \n (bar) \r\n (baz) \n "]; + + foreach (i, cfws; list) + { + scope(failure) writefln("i: %s", i); + + test(format("%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); + test(format("%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); + + test(format("%1$s04%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04:22 +0000%1$s", cfws), dst1); + + test(format("%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); + test(format("%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); + + test(format("%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s4%1$sJul%1$s76 05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s76 05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); + test(format("%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); + + test(format("%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); + test(format("%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); + test(format("%1$sSat%1$s,%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); + test(format("%1$sSun%1$s,%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); + } + } + + // test years of 1, 2, and 3 digits. + { + auto st1 = SysTime(Date(2000, 1, 1), UTC()); + auto st2 = SysTime(Date(2000, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); + foreach (i; 0 .. 50) + { + test(format("1 Jan %02d 00:00 GMT", i), st1); + test(format("1 Jan %02d 00:00 -1200", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + { + auto st1 = SysTime(Date(1950, 1, 1), UTC()); + auto st2 = SysTime(Date(1950, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); + foreach (i; 50 .. 100) + { + test(format("1 Jan %02d 00:00 GMT", i), st1); + test(format("1 Jan %02d 00:00 -1200", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + { + auto st1 = SysTime(Date(1900, 1, 1), UTC()); + auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); + foreach (i; 0 .. 1000) + { + test(format("1 Jan %03d 00:00 GMT", i), st1); + test(format("1 Jan %03d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + foreach (i; 0 .. 10) + { + auto str1 = cr(format("1 Jan %d 00:00 GMT", i)); + auto str2 = cr(format("1 Jan %d 00:00 -1200", i)); + assertThrown!DateTimeException(parseRFC822DateTime(str1)); + assertThrown!DateTimeException(parseRFC822DateTime(str1)); + } + + // test time zones + { + auto dt = DateTime(1982, 05, 03, 12, 22, 04); + test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC())); + test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC())); + test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); + test("Wed, 03 May 1982 12:22:04 EDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-4)))); + test("Wed, 03 May 1982 12:22:04 CST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); + test("Wed, 03 May 1982 12:22:04 CDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); + test("Wed, 03 May 1982 12:22:04 MST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); + test("Wed, 03 May 1982 12:22:04 MDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); + test("Wed, 03 May 1982 12:22:04 PST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-8)))); + test("Wed, 03 May 1982 12:22:04 PDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); + + auto badTZ = new immutable SimpleTimeZone(Duration.zero); + foreach (dchar c; filter!(a => a != 'j' && a != 'J')(letters)) + { + scope(failure) writefln("c: %s", c); + test(format("Wed, 03 May 1982 12:22:04 %s", c), SysTime(dt, badTZ)); + test(format("Wed, 03 May 1982 12:22:04%s", c), SysTime(dt, badTZ)); + } + + foreach (dchar c; ['j', 'J']) + { + scope(failure) writefln("c: %s", c); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04 %s", c)))); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04%s", c)))); + } + + foreach (string s; ["AAA", "GQW", "DDT", "PDA", "GT", "GM"]) + { + scope(failure) writefln("s: %s", s); + test(format("Wed, 03 May 1982 12:22:04 %s", s), SysTime(dt, badTZ)); + } + + // test trailing stuff that gets ignored + { + foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) + { + scope(failure) writefln("c: %d", c); + test(format("21Dec1213:14:15+0000%c", cast(char) c), std1); + test(format("21Dec1213:14:15+0000%c ", cast(char) c), std1); + test(format("21Dec1213:14:15+0000%chello", cast(char) c), std1); + } + } + + // test trailing stuff that doesn't get ignored + { + foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) + { + scope(failure) writefln("c: %d", c); + assertThrown!DateTimeException( + parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c", cast(char) c)))); + assertThrown!DateTimeException( + parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c ", cast(char) c)))); + assertThrown!DateTimeException( + parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%chello", cast(char) c)))); + } + } + } + + // test that the checks for minimum length work correctly and avoid + // any RangeErrors. + test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + + auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime("")); + foreach (str; ["Fri,7Dec1200:00:00", "7Dec1200:00:00"]) + { + foreach (i; 0 .. str.length) + { + auto value = str[0 .. $ - i]; + scope(failure) writeln(value); + assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg); + } + } + }(); +} + + +private: + +/+ + Returns the given hnsecs as an ISO string of fractional seconds. + +/ +static string fracSecsToISOString(int hnsecs) @safe pure nothrow +{ + assert(hnsecs >= 0); + + try + { + if (hnsecs == 0) + return ""; + + string isoString = format(".%07d", hnsecs); + + while (isoString[$ - 1] == '0') + isoString.popBack(); + + return isoString; + } + catch (Exception e) + assert(0, "format() threw."); +} + +@safe unittest +{ + assert(fracSecsToISOString(0) == ""); + assert(fracSecsToISOString(1) == ".0000001"); + assert(fracSecsToISOString(10) == ".000001"); + assert(fracSecsToISOString(100) == ".00001"); + assert(fracSecsToISOString(1000) == ".0001"); + assert(fracSecsToISOString(10_000) == ".001"); + assert(fracSecsToISOString(100_000) == ".01"); + assert(fracSecsToISOString(1_000_000) == ".1"); + assert(fracSecsToISOString(1_000_001) == ".1000001"); + assert(fracSecsToISOString(1_001_001) == ".1001001"); + assert(fracSecsToISOString(1_071_601) == ".1071601"); + assert(fracSecsToISOString(1_271_641) == ".1271641"); + assert(fracSecsToISOString(9_999_999) == ".9999999"); + assert(fracSecsToISOString(9_999_990) == ".999999"); + assert(fracSecsToISOString(9_999_900) == ".99999"); + assert(fracSecsToISOString(9_999_000) == ".9999"); + assert(fracSecsToISOString(9_990_000) == ".999"); + assert(fracSecsToISOString(9_900_000) == ".99"); + assert(fracSecsToISOString(9_000_000) == ".9"); + assert(fracSecsToISOString(999) == ".0000999"); + assert(fracSecsToISOString(9990) == ".000999"); + assert(fracSecsToISOString(99_900) == ".00999"); + assert(fracSecsToISOString(999_000) == ".0999"); +} + + +/+ + Returns a Duration corresponding to to the given ISO string of + fractional seconds. + +/ +static Duration fracSecsFromISOString(S)(in S isoString) @trusted pure +if (isSomeString!S) +{ + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.string : representation; + + if (isoString.empty) + return Duration.zero; + + auto str = isoString.representation; + + enforce(str[0] == '.', new DateTimeException("Invalid ISO String")); + str.popFront(); + + enforce(!str.empty && all!isDigit(str), new DateTimeException("Invalid ISO String")); + + dchar[7] fullISOString = void; + foreach (i, ref dchar c; fullISOString) + { + if (i < str.length) + c = str[i]; + else + c = '0'; + } + + return hnsecs(to!int(fullISOString[])); +} + +@safe unittest +{ + static void testFSInvalid(string isoString) + { + fracSecsFromISOString(isoString); + } + + assertThrown!DateTimeException(testFSInvalid(".")); + assertThrown!DateTimeException(testFSInvalid("0.")); + assertThrown!DateTimeException(testFSInvalid("0")); + assertThrown!DateTimeException(testFSInvalid("0000000")); + assertThrown!DateTimeException(testFSInvalid("T")); + assertThrown!DateTimeException(testFSInvalid("T.")); + assertThrown!DateTimeException(testFSInvalid(".T")); + assertThrown!DateTimeException(testFSInvalid(".00000Q0")); + assertThrown!DateTimeException(testFSInvalid(".000000Q")); + assertThrown!DateTimeException(testFSInvalid(".0000000Q")); + assertThrown!DateTimeException(testFSInvalid(".0000000000Q")); + + assert(fracSecsFromISOString("") == Duration.zero); + assert(fracSecsFromISOString(".0000001") == hnsecs(1)); + assert(fracSecsFromISOString(".000001") == hnsecs(10)); + assert(fracSecsFromISOString(".00001") == hnsecs(100)); + assert(fracSecsFromISOString(".0001") == hnsecs(1000)); + assert(fracSecsFromISOString(".001") == hnsecs(10_000)); + assert(fracSecsFromISOString(".01") == hnsecs(100_000)); + assert(fracSecsFromISOString(".1") == hnsecs(1_000_000)); + assert(fracSecsFromISOString(".1000001") == hnsecs(1_000_001)); + assert(fracSecsFromISOString(".1001001") == hnsecs(1_001_001)); + assert(fracSecsFromISOString(".1071601") == hnsecs(1_071_601)); + assert(fracSecsFromISOString(".1271641") == hnsecs(1_271_641)); + assert(fracSecsFromISOString(".9999999") == hnsecs(9_999_999)); + assert(fracSecsFromISOString(".9999990") == hnsecs(9_999_990)); + assert(fracSecsFromISOString(".999999") == hnsecs(9_999_990)); + assert(fracSecsFromISOString(".9999900") == hnsecs(9_999_900)); + assert(fracSecsFromISOString(".99999") == hnsecs(9_999_900)); + assert(fracSecsFromISOString(".9999000") == hnsecs(9_999_000)); + assert(fracSecsFromISOString(".9999") == hnsecs(9_999_000)); + assert(fracSecsFromISOString(".9990000") == hnsecs(9_990_000)); + assert(fracSecsFromISOString(".999") == hnsecs(9_990_000)); + assert(fracSecsFromISOString(".9900000") == hnsecs(9_900_000)); + assert(fracSecsFromISOString(".9900") == hnsecs(9_900_000)); + assert(fracSecsFromISOString(".99") == hnsecs(9_900_000)); + assert(fracSecsFromISOString(".9000000") == hnsecs(9_000_000)); + assert(fracSecsFromISOString(".9") == hnsecs(9_000_000)); + assert(fracSecsFromISOString(".0000999") == hnsecs(999)); + assert(fracSecsFromISOString(".0009990") == hnsecs(9990)); + assert(fracSecsFromISOString(".000999") == hnsecs(9990)); + assert(fracSecsFromISOString(".0099900") == hnsecs(99_900)); + assert(fracSecsFromISOString(".00999") == hnsecs(99_900)); + assert(fracSecsFromISOString(".0999000") == hnsecs(999_000)); + assert(fracSecsFromISOString(".0999") == hnsecs(999_000)); + assert(fracSecsFromISOString(".00000000") == Duration.zero); + assert(fracSecsFromISOString(".00000001") == Duration.zero); + assert(fracSecsFromISOString(".00000009") == Duration.zero); + assert(fracSecsFromISOString(".1234567890") == hnsecs(1_234_567)); + assert(fracSecsFromISOString(".12345678901234567890") == hnsecs(1_234_567)); +} + + +/+ + This function is used to split out the units without getting the remaining + hnsecs. + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. + + Returns: + The split out value. + +/ +long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + return convert!("hnsecs", units)(hnsecs); +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 2595000000007L); +} + + +/+ + This function is used to split out the units without getting the units but + just the remaining hnsecs. + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. + + Returns: + The remaining hnsecs. + +/ +long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + immutable value = convert!("hnsecs", units)(hnsecs); + return hnsecs - convert!(units, "hnsecs")(value); +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + auto returned = removeUnitsFromHNSecs!"days"(hnsecs); + assert(returned == 3000000007); + assert(hnsecs == 2595000000007L); +} + + +/+ + Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand + side of the given range (it strips comments delimited by $(D '(') and + $(D ')') as well as folding whitespace). + + It is assumed that the given range contains the value of a header field and + no terminating CRLF for the line (though the CRLF for folding whitespace is + of course expected and stripped) and thus that the only case of CR or LF is + in folding whitespace. + + If a comment does not terminate correctly (e.g. mismatched parens) or if the + the FWS is malformed, then the range will be empty when stripCWFS is done. + However, only minimal validation of the content is done (e.g. quoted pairs + within a comment aren't validated beyond \$LPAREN or \$RPAREN, because + they're inside a comment, and thus their value doesn't matter anyway). It's + only when the content does not conform to the grammar rules for FWS and thus + literally cannot be parsed that content is considered invalid, and an empty + range is returned. + + Note that _stripCFWS is eager, not lazy. It does not create a new range. + Rather, it pops off the CFWS from the range and returns it. + +/ +R _stripCFWS(R)(R range) +if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && + (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) +{ + immutable e = range.length; + outer: for (size_t i = 0; i < e; ) + { + switch (range[i]) + { + case ' ': case '\t': + { + ++i; + break; + } + case '\r': + { + if (i + 2 < e && range[i + 1] == '\n' && (range[i + 2] == ' ' || range[i + 2] == '\t')) + { + i += 3; + break; + } + break outer; + } + case '\n': + { + if (i + 1 < e && (range[i + 1] == ' ' || range[i + 1] == '\t')) + { + i += 2; + break; + } + break outer; + } + case '(': + { + ++i; + size_t commentLevel = 1; + while (i < e) + { + if (range[i] == '(') + ++commentLevel; + else if (range[i] == ')') + { + ++i; + if (--commentLevel == 0) + continue outer; + continue; + } + else if (range[i] == '\\') + { + if (++i == e) + break outer; + } + ++i; + } + break outer; + } + default: return range[i .. e]; + } + } + return range[e .. e]; +} + +@system unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.meta : AliasSeq; + import std.stdio : writeln; + import std.string : representation; + + foreach (cr; AliasSeq!(function(string a){return cast(ubyte[]) a;}, + function(string a){return map!(b => cast(char) b)(a.representation);})) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + scope(failure) writeln(typeof(cr).stringof); + + assert(_stripCFWS(cr("")).empty); + assert(_stripCFWS(cr("\r")).empty); + assert(_stripCFWS(cr("\r\n")).empty); + assert(_stripCFWS(cr("\r\n ")).empty); + assert(_stripCFWS(cr(" \t\r\n")).empty); + assert(equal(_stripCFWS(cr(" \t\r\n hello")), cr("hello"))); + assert(_stripCFWS(cr(" \t\r\nhello")).empty); + assert(_stripCFWS(cr(" \t\r\n\v")).empty); + assert(equal(_stripCFWS(cr("\v \t\r\n\v")), cr("\v \t\r\n\v"))); + assert(_stripCFWS(cr("()")).empty); + assert(_stripCFWS(cr("(hello world)")).empty); + assert(_stripCFWS(cr("(hello world)(hello world)")).empty); + assert(_stripCFWS(cr("(hello world\r\n foo\r where's\nwaldo)")).empty); + assert(_stripCFWS(cr(" \t (hello \tworld\r\n foo\r where's\nwaldo)\t\t ")).empty); + assert(_stripCFWS(cr(" ")).empty); + assert(_stripCFWS(cr("\t\t\t")).empty); + assert(_stripCFWS(cr("\t \r\n\r \n")).empty); + assert(_stripCFWS(cr("(hello world) (can't find waldo) (he's lost)")).empty); + assert(_stripCFWS(cr("(hello\\) world) (can't \\(find waldo) (he's \\(\\)lost)")).empty); + assert(_stripCFWS(cr("(((((")).empty); + assert(_stripCFWS(cr("(((()))")).empty); + assert(_stripCFWS(cr("(((())))")).empty); + assert(equal(_stripCFWS(cr("(((()))))")), cr(")"))); + assert(equal(_stripCFWS(cr(")))))")), cr(")))))"))); + assert(equal(_stripCFWS(cr("()))))")), cr("))))"))); + assert(equal(_stripCFWS(cr(" hello hello ")), cr("hello hello "))); + assert(equal(_stripCFWS(cr("\thello (world)")), cr("hello (world)"))); + assert(equal(_stripCFWS(cr(" \r\n \\((\\)) foo")), cr("\\((\\)) foo"))); + assert(equal(_stripCFWS(cr(" \r\n (\\((\\))) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" \r\n (\\(())) foo")), cr(") foo"))); + assert(_stripCFWS(cr(" \r\n (((\\))) foo")).empty); + + assert(_stripCFWS(cr("(hello)(hello)")).empty); + assert(_stripCFWS(cr(" \r\n (hello)\r\n (hello)")).empty); + assert(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n ")).empty); + assert(_stripCFWS(cr("\t\t\t\t(hello)\t\t\t\t(hello)\t\t\t\t")).empty); + assert(equal(_stripCFWS(cr(" \r\n (hello)\r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\r\n\t(hello)\t\r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\t\r\n\t(hello)\t\r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n \r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n \r\n (hello)\t\r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n\t\r\n\t(hello)\t\r\n (hello) \r\n hello")), cr("hello"))); + + assert(equal(_stripCFWS(cr(" (\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\t\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n\t( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\t\r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\r\n\t) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\t\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n\t) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) \r\n foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\t\r\n foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\r\n foo")), cr("foo"))); + + assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\t\r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n\t( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n\t) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\t\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\r\n ) foo")), cr("foo"))); + + assert(equal(_stripCFWS(cr(" ( \r\n bar \r\n ( \r\n bar \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n () \r\n ( \r\n () \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n \\\\ \r\n ( \r\n \\\\ \r\n ) \r\n ) foo")), cr("foo"))); + + assert(_stripCFWS(cr("(hello)(hello)")).empty); + assert(_stripCFWS(cr(" \n (hello)\n (hello) \n ")).empty); + assert(_stripCFWS(cr(" \n (hello) \n (hello) \n ")).empty); + assert(equal(_stripCFWS(cr(" \n (hello)\n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\n\t(hello)\n\t(hello)\t\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\n\t(hello)\t\n\t(hello)\t\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n \n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello"))); + }(); +} + +// This is so that we don't have to worry about std.conv.to throwing. It also +// doesn't have to worry about quite as many cases as std.conv.to, since it +// doesn't have to worry about a sign on the value or about whether it fits. +T _convDigits(T, R)(R str) +if (isIntegral!T && isSigned!T) // The constraints on R were already covered by parseRFC822DateTime. +{ + import std.ascii : isDigit; + + assert(!str.empty); + T num = 0; + foreach (i; 0 .. str.length) + { + if (i != 0) + num *= 10; + if (!isDigit(str[i])) + return -1; + num += str[i] - '0'; + } + return num; +} + +@safe unittest +{ + import std.conv : to; + import std.range : chain, iota; + import std.stdio : writeln; + foreach (i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999])) + { + scope(failure) writeln(i); + assert(_convDigits!int(to!string(i)) == i); + } + foreach (str; ["-42", "+42", "1a", "1 ", " ", " 42 "]) + { + scope(failure) writeln(str); + assert(_convDigits!int(str) == -1); + } +} + + +version(unittest) +{ + // Variables to help in testing. + Duration currLocalDiffFromUTC; + immutable (TimeZone)[] testTZs; + + // All of these helper arrays are sorted in ascending order. + auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; + auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct MonthDay + { + Month month; + short day; + + this(int m, short d) + { + month = cast(Month) m; + day = d; + } + } + + MonthDay[] testMonthDays = [MonthDay(1, 1), + MonthDay(1, 2), + MonthDay(3, 17), + MonthDay(7, 4), + MonthDay(10, 27), + MonthDay(12, 30), + MonthDay(12, 31)]; + + auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; + + auto testTODs = [TimeOfDay(0, 0, 0), + TimeOfDay(0, 0, 1), + TimeOfDay(0, 1, 0), + TimeOfDay(1, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + auto testHours = [0, 1, 12, 22, 23]; + auto testMinSecs = [0, 1, 30, 58, 59]; + + // Throwing exceptions is incredibly expensive, so we want to use a smaller + // set of values for tests using assertThrown. + auto testTODsThrown = [TimeOfDay(0, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + Date[] testDatesBC; + Date[] testDatesAD; + + DateTime[] testDateTimesBC; + DateTime[] testDateTimesAD; + + Duration[] testFracSecs; + + SysTime[] testSysTimesBC; + SysTime[] testSysTimesAD; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct GregDay { int day; Date date; } + auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar + GregDay(-735_233, Date(-2012, 1, 1)), + GregDay(-735_202, Date(-2012, 2, 1)), + GregDay(-735_175, Date(-2012, 2, 28)), + GregDay(-735_174, Date(-2012, 2, 29)), + GregDay(-735_173, Date(-2012, 3, 1)), + GregDay(-734_502, Date(-2010, 1, 1)), + GregDay(-734_472, Date(-2010, 1, 31)), + GregDay(-734_471, Date(-2010, 2, 1)), + GregDay(-734_444, Date(-2010, 2, 28)), + GregDay(-734_443, Date(-2010, 3, 1)), + GregDay(-734_413, Date(-2010, 3, 31)), + GregDay(-734_412, Date(-2010, 4, 1)), + GregDay(-734_383, Date(-2010, 4, 30)), + GregDay(-734_382, Date(-2010, 5, 1)), + GregDay(-734_352, Date(-2010, 5, 31)), + GregDay(-734_351, Date(-2010, 6, 1)), + GregDay(-734_322, Date(-2010, 6, 30)), + GregDay(-734_321, Date(-2010, 7, 1)), + GregDay(-734_291, Date(-2010, 7, 31)), + GregDay(-734_290, Date(-2010, 8, 1)), + GregDay(-734_260, Date(-2010, 8, 31)), + GregDay(-734_259, Date(-2010, 9, 1)), + GregDay(-734_230, Date(-2010, 9, 30)), + GregDay(-734_229, Date(-2010, 10, 1)), + GregDay(-734_199, Date(-2010, 10, 31)), + GregDay(-734_198, Date(-2010, 11, 1)), + GregDay(-734_169, Date(-2010, 11, 30)), + GregDay(-734_168, Date(-2010, 12, 1)), + GregDay(-734_139, Date(-2010, 12, 30)), + GregDay(-734_138, Date(-2010, 12, 31)), + GregDay(-731_215, Date(-2001, 1, 1)), + GregDay(-730_850, Date(-2000, 1, 1)), + GregDay(-730_849, Date(-2000, 1, 2)), + GregDay(-730_486, Date(-2000, 12, 30)), + GregDay(-730_485, Date(-2000, 12, 31)), + GregDay(-730_484, Date(-1999, 1, 1)), + GregDay(-694_690, Date(-1901, 1, 1)), + GregDay(-694_325, Date(-1900, 1, 1)), + GregDay(-585_118, Date(-1601, 1, 1)), + GregDay(-584_753, Date(-1600, 1, 1)), + GregDay(-584_388, Date(-1600, 12, 31)), + GregDay(-584_387, Date(-1599, 1, 1)), + GregDay(-365_972, Date(-1001, 1, 1)), + GregDay(-365_607, Date(-1000, 1, 1)), + GregDay(-183_351, Date(-501, 1, 1)), + GregDay(-182_986, Date(-500, 1, 1)), + GregDay(-182_621, Date(-499, 1, 1)), + GregDay(-146_827, Date(-401, 1, 1)), + GregDay(-146_462, Date(-400, 1, 1)), + GregDay(-146_097, Date(-400, 12, 31)), + GregDay(-110_302, Date(-301, 1, 1)), + GregDay(-109_937, Date(-300, 1, 1)), + GregDay(-73_778, Date(-201, 1, 1)), + GregDay(-73_413, Date(-200, 1, 1)), + GregDay(-38_715, Date(-105, 1, 1)), + GregDay(-37_254, Date(-101, 1, 1)), + GregDay(-36_889, Date(-100, 1, 1)), + GregDay(-36_524, Date(-99, 1, 1)), + GregDay(-36_160, Date(-99, 12, 31)), + GregDay(-35_794, Date(-97, 1, 1)), + GregDay(-18_627, Date(-50, 1, 1)), + GregDay(-18_262, Date(-49, 1, 1)), + GregDay(-3652, Date(-9, 1, 1)), + GregDay(-2191, Date(-5, 1, 1)), + GregDay(-1827, Date(-5, 12, 31)), + GregDay(-1826, Date(-4, 1, 1)), + GregDay(-1825, Date(-4, 1, 2)), + GregDay(-1462, Date(-4, 12, 30)), + GregDay(-1461, Date(-4, 12, 31)), + GregDay(-1460, Date(-3, 1, 1)), + GregDay(-1096, Date(-3, 12, 31)), + GregDay(-1095, Date(-2, 1, 1)), + GregDay(-731, Date(-2, 12, 31)), + GregDay(-730, Date(-1, 1, 1)), + GregDay(-367, Date(-1, 12, 30)), + GregDay(-366, Date(-1, 12, 31)), + GregDay(-365, Date(0, 1, 1)), + GregDay(-31, Date(0, 11, 30)), + GregDay(-30, Date(0, 12, 1)), + GregDay(-1, Date(0, 12, 30)), + GregDay(0, Date(0, 12, 31))]; + + auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), + GregDay(2, Date(1, 1, 2)), + GregDay(32, Date(1, 2, 1)), + GregDay(365, Date(1, 12, 31)), + GregDay(366, Date(2, 1, 1)), + GregDay(731, Date(3, 1, 1)), + GregDay(1096, Date(4, 1, 1)), + GregDay(1097, Date(4, 1, 2)), + GregDay(1460, Date(4, 12, 30)), + GregDay(1461, Date(4, 12, 31)), + GregDay(1462, Date(5, 1, 1)), + GregDay(17_898, Date(50, 1, 1)), + GregDay(35_065, Date(97, 1, 1)), + GregDay(36_160, Date(100, 1, 1)), + GregDay(36_525, Date(101, 1, 1)), + GregDay(37_986, Date(105, 1, 1)), + GregDay(72_684, Date(200, 1, 1)), + GregDay(73_049, Date(201, 1, 1)), + GregDay(109_208, Date(300, 1, 1)), + GregDay(109_573, Date(301, 1, 1)), + GregDay(145_732, Date(400, 1, 1)), + GregDay(146_098, Date(401, 1, 1)), + GregDay(182_257, Date(500, 1, 1)), + GregDay(182_622, Date(501, 1, 1)), + GregDay(364_878, Date(1000, 1, 1)), + GregDay(365_243, Date(1001, 1, 1)), + GregDay(584_023, Date(1600, 1, 1)), + GregDay(584_389, Date(1601, 1, 1)), + GregDay(693_596, Date(1900, 1, 1)), + GregDay(693_961, Date(1901, 1, 1)), + GregDay(729_755, Date(1999, 1, 1)), + GregDay(730_120, Date(2000, 1, 1)), + GregDay(730_121, Date(2000, 1, 2)), + GregDay(730_484, Date(2000, 12, 30)), + GregDay(730_485, Date(2000, 12, 31)), + GregDay(730_486, Date(2001, 1, 1)), + GregDay(733_773, Date(2010, 1, 1)), + GregDay(733_774, Date(2010, 1, 2)), + GregDay(733_803, Date(2010, 1, 31)), + GregDay(733_804, Date(2010, 2, 1)), + GregDay(733_831, Date(2010, 2, 28)), + GregDay(733_832, Date(2010, 3, 1)), + GregDay(733_862, Date(2010, 3, 31)), + GregDay(733_863, Date(2010, 4, 1)), + GregDay(733_892, Date(2010, 4, 30)), + GregDay(733_893, Date(2010, 5, 1)), + GregDay(733_923, Date(2010, 5, 31)), + GregDay(733_924, Date(2010, 6, 1)), + GregDay(733_953, Date(2010, 6, 30)), + GregDay(733_954, Date(2010, 7, 1)), + GregDay(733_984, Date(2010, 7, 31)), + GregDay(733_985, Date(2010, 8, 1)), + GregDay(734_015, Date(2010, 8, 31)), + GregDay(734_016, Date(2010, 9, 1)), + GregDay(734_045, Date(2010, 9, 30)), + GregDay(734_046, Date(2010, 10, 1)), + GregDay(734_076, Date(2010, 10, 31)), + GregDay(734_077, Date(2010, 11, 1)), + GregDay(734_106, Date(2010, 11, 30)), + GregDay(734_107, Date(2010, 12, 1)), + GregDay(734_136, Date(2010, 12, 30)), + GregDay(734_137, Date(2010, 12, 31)), + GregDay(734_503, Date(2012, 1, 1)), + GregDay(734_534, Date(2012, 2, 1)), + GregDay(734_561, Date(2012, 2, 28)), + GregDay(734_562, Date(2012, 2, 29)), + GregDay(734_563, Date(2012, 3, 1)), + GregDay(734_858, Date(2012, 12, 21))]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct DayOfYear { int day; MonthDay md; } + auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(3, 1)), + DayOfYear(90, MonthDay(3, 31)), + DayOfYear(91, MonthDay(4, 1)), + DayOfYear(120, MonthDay(4, 30)), + DayOfYear(121, MonthDay(5, 1)), + DayOfYear(151, MonthDay(5, 31)), + DayOfYear(152, MonthDay(6, 1)), + DayOfYear(181, MonthDay(6, 30)), + DayOfYear(182, MonthDay(7, 1)), + DayOfYear(212, MonthDay(7, 31)), + DayOfYear(213, MonthDay(8, 1)), + DayOfYear(243, MonthDay(8, 31)), + DayOfYear(244, MonthDay(9, 1)), + DayOfYear(273, MonthDay(9, 30)), + DayOfYear(274, MonthDay(10, 1)), + DayOfYear(304, MonthDay(10, 31)), + DayOfYear(305, MonthDay(11, 1)), + DayOfYear(334, MonthDay(11, 30)), + DayOfYear(335, MonthDay(12, 1)), + DayOfYear(363, MonthDay(12, 29)), + DayOfYear(364, MonthDay(12, 30)), + DayOfYear(365, MonthDay(12, 31))]; + + auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(2, 29)), + DayOfYear(61, MonthDay(3, 1)), + DayOfYear(91, MonthDay(3, 31)), + DayOfYear(92, MonthDay(4, 1)), + DayOfYear(121, MonthDay(4, 30)), + DayOfYear(122, MonthDay(5, 1)), + DayOfYear(152, MonthDay(5, 31)), + DayOfYear(153, MonthDay(6, 1)), + DayOfYear(182, MonthDay(6, 30)), + DayOfYear(183, MonthDay(7, 1)), + DayOfYear(213, MonthDay(7, 31)), + DayOfYear(214, MonthDay(8, 1)), + DayOfYear(244, MonthDay(8, 31)), + DayOfYear(245, MonthDay(9, 1)), + DayOfYear(274, MonthDay(9, 30)), + DayOfYear(275, MonthDay(10, 1)), + DayOfYear(305, MonthDay(10, 31)), + DayOfYear(306, MonthDay(11, 1)), + DayOfYear(335, MonthDay(11, 30)), + DayOfYear(336, MonthDay(12, 1)), + DayOfYear(364, MonthDay(12, 29)), + DayOfYear(365, MonthDay(12, 30)), + DayOfYear(366, MonthDay(12, 31))]; + + void initializeTests() @safe + { + import std.algorithm.sorting : sort; + import std.typecons : Rebindable; + immutable lt = LocalTime().utcToTZ(0); + currLocalDiffFromUTC = dur!"hnsecs"(lt); + + version(Posix) + { + immutable otherTZ = lt < 0 ? PosixTimeZone.getTimeZone("Australia/Sydney") + : PosixTimeZone.getTimeZone("America/Denver"); + } + else version(Windows) + { + immutable otherTZ = lt < 0 ? WindowsTimeZone.getTimeZone("AUS Eastern Standard Time") + : WindowsTimeZone.getTimeZone("Mountain Standard Time"); + } + + immutable ot = otherTZ.utcToTZ(0); + + auto diffs = [0L, lt, ot]; + auto diffAA = [0L : Rebindable!(immutable TimeZone)(UTC())]; + diffAA[lt] = Rebindable!(immutable TimeZone)(LocalTime()); + diffAA[ot] = Rebindable!(immutable TimeZone)(otherTZ); + + sort(diffs); + testTZs = [diffAA[diffs[0]], diffAA[diffs[1]], diffAA[diffs[2]]]; + + testFracSecs = [Duration.zero, hnsecs(1), hnsecs(5007), hnsecs(9_999_999)]; + + foreach (year; testYearsBC) + { + foreach (md; testMonthDays) + testDatesBC ~= Date(year, md.month, md.day); + } + + foreach (year; testYearsAD) + { + foreach (md; testMonthDays) + testDatesAD ~= Date(year, md.month, md.day); + } + + foreach (dt; testDatesBC) + { + foreach (tod; testTODs) + testDateTimesBC ~= DateTime(dt, tod); + } + + foreach (dt; testDatesAD) + { + foreach (tod; testTODs) + testDateTimesAD ~= DateTime(dt, tod); + } + + foreach (dt; testDateTimesBC) + { + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + testSysTimesBC ~= SysTime(dt, fs, tz); + } + } + + foreach (dt; testDateTimesAD) + { + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + testSysTimesAD ~= SysTime(dt, fs, tz); + } + } + } +} diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d new file mode 100644 index 00000000000..17841bbb2f3 --- /dev/null +++ b/std/datetime/timezone.d @@ -0,0 +1,4346 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/datetime/_timezone.d) ++/ +module std.datetime.timezone; + +import core.time; +import std.datetime.date; +import std.datetime.systime; +import std.exception : enforce; +import std.range.primitives; +import std.traits : isIntegral, isSomeString, Unqual; + +version(Windows) +{ + import core.stdc.time : time_t; + import core.sys.windows.windows; + import core.sys.windows.winsock2; + import std.windows.registry; + + // Uncomment and run unittests to print missing Windows TZ translations. + // Please subscribe to Microsoft Daylight Saving Time & Time Zone Blog + // (https://blogs.technet.microsoft.com/dst2007/) if you feel responsible + // for updating the translations. + // version = UpdateWindowsTZTranslations; +} +else version(Posix) +{ + import core.sys.posix.signal : timespec; + import core.sys.posix.sys.types : time_t; +} + +version(unittest) import std.exception : assertThrown; + + +/++ + Represents a time zone. It is used with $(REF SysTime,std,datetime,systime) + to indicate the time zone of a $(REF SysTime,std,datetime,systime). + +/ +abstract class TimeZone +{ +public: + + /++ + The name of the time zone per the TZ Database. This is the name used to + get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone). + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of + Time Zones) + +/ + @property string name() @safe const nothrow + { + return _name; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Standard Time). Regardless, it is not the same as name. + +/ + @property string stdName() @safe const nothrow + { + return _stdName; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Daylight Time). Regardless, it is not the same as name. + +/ + @property string dstName() @safe const nothrow + { + return _dstName; + } + + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current dates + but will still return true for $(D hasDST) because the time zone did at + some point have DST. + +/ + @property abstract bool hasDST() @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and returns whether DST is effect in this + time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + abstract bool dstInEffect(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + abstract long utcToTZ(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + abstract long tzToUTC(long adjTime) @safe const nothrow; + + + /++ + Returns what the offset from UTC is at the given std time. + It includes the DST offset in effect at that time (if any). + + Params: + stdTime = The UTC time for which to get the offset from UTC for this + time zone. + +/ + Duration utcOffsetAt(long stdTime) @safe const nothrow + { + return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); + } + + // @@@DEPRECATED_2017-07@@@ + /++ + $(RED Deprecated. Use either PosixTimeZone.getTimeZone or + WindowsTimeZone.getTimeZone. ($(LREF parseTZConversions) can be + used to convert time zone names if necessary). Microsoft changes + their time zones too often for us to compile the conversions into + Phobos and have them be properly up-to-date. TimeZone.getTimeZone + will be removed in July 2017.) + + Returns a $(LREF TimeZone) with the give name per the TZ Database. + + This returns a $(LREF PosixTimeZone) on Posix systems and a + $(LREF WindowsTimeZone) on Windows systems. For + $(LREF PosixTimeZone) on Windows, call $(D PosixTimeZone.getTimeZone) + directly and give it the location of the TZ Database time zone files on + disk. + + On Windows, the given TZ Database name is converted to the corresponding + time zone name on Windows prior to calling + $(D WindowsTimeZone.getTimeZone). This function allows for + the same time zone names on both Windows and Posix systems. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of + Time Zones)
+ $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, + Windows <-> TZ Database Name Conversion Table) + + Params: + name = The TZ Database name of the desired time zone + + Throws: + $(REF DateTimeException,std,datetime,date) if the given time zone + could not be found. + +/ + deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead") + static immutable(TimeZone) getTimeZone(string name) @safe + { + version(Posix) + return PosixTimeZone.getTimeZone(name); + else version(Windows) + { + import std.format : format; + auto windowsTZName = tzDatabaseNameToWindowsTZName(name); + if (windowsTZName != null) + { + try + return WindowsTimeZone.getTimeZone(windowsTZName); + catch (DateTimeException dte) + { + auto oldName = _getOldName(windowsTZName); + if (oldName != null) + return WindowsTimeZone.getTimeZone(oldName); + throw dte; + } + } + else + throw new DateTimeException(format("%s does not have an equivalent Windows time zone.", name)); + } + } + + /// + deprecated @safe unittest + { + auto tz = TimeZone.getTimeZone("America/Los_Angeles"); + } + + // The purpose of this is to handle the case where a Windows time zone is + // new and exists on an up-to-date Windows box but does not exist on Windows + // boxes which have not been properly updated. The "date added" is included + // on the theory that we'll be able to remove them at some point in the + // the future once enough time has passed, and that way, we know how much + // time has passed. + private static string _getOldName(string windowsTZName) @safe pure nothrow + { + switch (windowsTZName) + { + case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08 + case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08 + case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08 + case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08 + default: return null; + } + } + + // Since reading in the time zone files could be expensive, most unit tests + // are consolidated into this one unittest block which minimizes how often + // it reads a time zone file. + @system unittest + { + import core.exception : AssertError; + import std.conv : to; + import std.file : exists, isFile; + import std.format : format; + import std.path : chainPath; + import std.stdio : writefln; + import std.typecons : tuple; + + version(Posix) alias getTimeZone = PosixTimeZone.getTimeZone; + else version(Windows) alias getTimeZone = WindowsTimeZone.getTimeZone; + + version(Posix) scope(exit) clearTZEnvVar(); + + static immutable(TimeZone) testTZ(string tzName, + string stdName, + string dstName, + Duration utcOffset, + Duration dstOffset, + bool north = true) + { + scope(failure) writefln("Failed time zone: %s", tzName); + + version(Posix) + { + immutable tz = PosixTimeZone.getTimeZone(tzName); + assert(tz.name == tzName); + } + else version(Windows) + { + immutable tz = WindowsTimeZone.getTimeZone(tzName); + assert(tz.name == stdName); + } + + immutable hasDST = dstOffset != Duration.zero; + + //assert(tz.stdName == stdName); //Locale-dependent + //assert(tz.dstName == dstName); //Locale-dependent + assert(tz.hasDST == hasDST); + + immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0); + immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0); + auto std = SysTime(stdDate, tz); + auto dst = SysTime(dstDate, tz); + auto stdUTC = SysTime(stdDate - utcOffset, UTC()); + auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC()); + + assert(!std.dstInEffect); + assert(dst.dstInEffect == hasDST); + assert(tz.utcOffsetAt(std.stdTime) == utcOffset); + assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset); + + assert(cast(DateTime) std == stdDate); + assert(cast(DateTime) dst == dstDate); + assert(std == stdUTC); + + version(Posix) + { + setTZEnvVar(tzName); + + static void testTM(in SysTime st) + { + import core.stdc.time : localtime, tm; + time_t unixTime = st.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = st.toTM(); + + assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec); + assert(ourTimeInfo.tm_min == osTimeInfo.tm_min); + assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour); + assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday); + assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon); + assert(ourTimeInfo.tm_year == osTimeInfo.tm_year); + assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday); + assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday); + assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst); + assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff); + assert(to!string(ourTimeInfo.tm_zone) == to!string(osTimeInfo.tm_zone)); + } + + testTM(std); + testTM(dst); + + // Apparently, right/ does not exist on Mac OS X. I don't know + // whether or not it exists on FreeBSD. It's rather pointless + // normally, since the Posix standard requires that leap seconds + // be ignored, so it does make some sense that right/ wouldn't + // be there, but since PosixTimeZone _does_ use leap seconds if + // the time zone file does, we'll test that functionality if the + // appropriate files exist. + if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) + { + auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); + + assert(leapTZ.name == "right/" ~ tzName); + //assert(leapTZ.stdName == stdName); //Locale-dependent + //assert(leapTZ.dstName == dstName); //Locale-dependent + assert(leapTZ.hasDST == hasDST); + + auto leapSTD = SysTime(std.stdTime, leapTZ); + auto leapDST = SysTime(dst.stdTime, leapTZ); + + assert(!leapSTD.dstInEffect); + assert(leapDST.dstInEffect == hasDST); + + assert(leapSTD.stdTime == std.stdTime); + assert(leapDST.stdTime == dst.stdTime); + + // Whenever a leap second is added/removed, + // this will have to be adjusted. + //enum leapDiff = convert!("seconds", "hnsecs")(25); + //assert(leapSTD.adjTime - leapDiff == std.adjTime); + //assert(leapDST.adjTime - leapDiff == dst.adjTime); + } + } + + return tz; + } + + auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + /+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + ///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), + /+Europe/London+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), + /+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), + /+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; + + version(Posix) + { + version(FreeBSD) enum utcZone = "Etc/UTC"; + else version(NetBSD) enum utcZone = "UTC"; + else version(linux) enum utcZone = "UTC"; + else version(OSX) enum utcZone = "UTC"; + else static assert(0, "The location of the UTC timezone file on this Posix platform must be set."); + + auto tzs = [testTZ("America/Los_Angeles", "PST", "PDT", dur!"hours"(-8), dur!"hours"(1)), + testTZ("America/New_York", "EST", "EDT", dur!"hours"(-5), dur!"hours"(1)), + //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false), + testTZ("Europe/London", "GMT", "BST", dur!"hours"(0), dur!"hours"(1)), + testTZ("Europe/Paris", "CET", "CEST", dur!"hours"(1), dur!"hours"(1)), + // Per www.timeanddate.com, it should be "CST" and "CDT", + // but the OS insists that it's "CST" for both. We should + // probably figure out how to report an error in the TZ + // database and report it. + testTZ("Australia/Adelaide", "CST", "CST", + dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; + + testTZ(utcZone, "UTC", "UTC", dur!"hours"(0), dur!"hours"(0)); + assertThrown!DateTimeException(PosixTimeZone.getTimeZone("hello_world")); + } + else version(Windows) + { + auto tzs = [testTZ("Pacific Standard Time", "Pacific Standard Time", + "Pacific Daylight Time", dur!"hours"(-8), dur!"hours"(1)), + testTZ("Eastern Standard Time", "Eastern Standard Time", + "Eastern Daylight Time", dur!"hours"(-5), dur!"hours"(1)), + //testTZ("Pacific SA Standard Time", "Pacific SA Standard Time", + //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false), + testTZ("GMT Standard Time", "GMT Standard Time", + "GMT Daylight Time", dur!"hours"(0), dur!"hours"(1)), + testTZ("Romance Standard Time", "Romance Standard Time", + "Romance Daylight Time", dur!"hours"(1), dur!"hours"(1)), + testTZ("Cen. Australia Standard Time", "Cen. Australia Standard Time", + "Cen. Australia Daylight Time", + dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; + + testTZ("Greenwich Standard Time", "Greenwich Standard Time", + "Greenwich Daylight Time", dur!"hours"(0), dur!"hours"(0)); + assertThrown!DateTimeException(WindowsTimeZone.getTimeZone("hello_world")); + } + else + assert(0, "OS not supported."); + + foreach (i; 0 .. tzs.length) + { + auto tz = tzs[i]; + immutable spring = dstSwitches[i][2]; + immutable fall = dstSwitches[i][3]; + auto stdOffset = SysTime(dstSwitches[i][0] + dur!"days"(-1), tz).utcOffset; + auto dstOffset = stdOffset + dur!"hours"(1); + + // Verify that creating a SysTime in the given time zone results + // in a SysTime with the correct std time during and surrounding + // a DST switch. + foreach (hour; -12 .. 13) + { + auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz); + immutable targetHour = hour < 0 ? hour + 24 : hour; + + static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) + { + enforce(st.hour == hour, + new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), + __FILE__, line)); + } + + void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", + tag, st, tz.name, st.utcOffset, stdOffset, dstOffset), + __FILE__, line); + } + + enforce(st.dstInEffect == dstInEffect, msg("1")); + enforce(st.utcOffset == offset, msg("2")); + enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); + } + + if (hour == spring) + { + testHour(st, spring + 1, tz.name); + testHour(st + dur!"minutes"(1), spring + 1, tz.name); + } + else + { + testHour(st, targetHour, tz.name); + testHour(st + dur!"minutes"(1), targetHour, tz.name); + } + + if (hour < spring) + testOffset1(stdOffset, false); + else + testOffset1(dstOffset, true); + + st = SysTime(dstSwitches[i][1] + dur!"hours"(hour), tz); + testHour(st, targetHour, tz.name); + + // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). + if (hour == fall - 1) + testHour(st + dur!"hours"(1), targetHour, tz.name); + + if (hour < fall) + testOffset1(dstOffset, true); + else + testOffset1(stdOffset, false); + } + + // Verify that converting a time in UTC to a time in another + // time zone results in the correct time during and surrounding + // a DST switch. + bool first = true; + auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset; + auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset; + // @@@BUG@@@ 3659 makes this necessary. + auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); + + foreach (hour; -24 .. 25) + { + auto utc = SysTime(dstSwitches[i][0] + dur!"hours"(hour), UTC()); + auto local = utc.toOtherTZ(tz); + + void testOffset2(Duration offset, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tz.name, utc, local), + __FILE__, line); + } + + enforce((utc + offset).hour == local.hour, msg("1")); + enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); + } + + if (utc < springSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + + utc = SysTime(dstSwitches[i][1] + dur!"hours"(hour), UTC()); + local = utc.toOtherTZ(tz); + + if (utc == fallSwitch || utc == fallSwitchMinus1) + { + if (first) + { + testOffset2(dstOffset); + first = false; + } + else + testOffset2(stdOffset); + } + else if (utc > fallSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + } + } + } + + + // @@@DEPRECATED_2017-07@@@ + /++ + $(RED Deprecated. Use either PosixTimeZone.getInstalledTZNames or + WindowsTimeZone.getInstalledTZNames. ($(LREF parseTZConversions) + can be used to convert time zone names if necessary). Microsoft + changes their time zones too often for us to compile the + conversions into Phobos and have them be properly up-to-date. + TimeZone.getInstalledTZNames will be removed in July 2017.) + + Returns a list of the names of the time zones installed on the system. + + Providing a sub-name narrows down the list of time zones (which + can number in the thousands). For example, + passing in "America" as the sub-name returns only the time zones which + begin with "America". + + On Windows, this function will convert the Windows time zone names to + the corresponding TZ Database names with + $(D windowsTZNameToTZDatabaseName). To get the actual Windows time + zone names, use $(D WindowsTimeZone.getInstalledTZNames) directly. + + Params: + subName = The first part of the time zones desired. + + Throws: + $(D FileException) on Posix systems if it fails to read from disk. + $(REF DateTimeException,std,datetime,date) on Windows systems if + it fails to read the registry. + +/ + deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead") + static string[] getInstalledTZNames(string subName = "") @safe + { + version(Posix) + return PosixTimeZone.getInstalledTZNames(subName); + else version(Windows) + { + import std.algorithm.searching : startsWith; + import std.algorithm.sorting : sort; + import std.array : appender; + + auto windowsNames = WindowsTimeZone.getInstalledTZNames(); + auto retval = appender!(string[])(); + + foreach (winName; windowsNames) + { + auto tzName = windowsTZNameToTZDatabaseName(winName); + if (tzName !is null && tzName.startsWith(subName)) + retval.put(tzName); + } + + sort(retval.data); + return retval.data; + } + } + + deprecated @safe unittest + { + import std.exception : assertNotThrown; + import std.stdio : writefln; + static void testPZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + TimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + // This was not previously tested, and it's currently failing, so I'm + // leaving it commented out until I can sort it out. + //assert(equal(tzNames, tzNames.uniq())); + + foreach (tzName; tzNames) + assertNotThrown!DateTimeException(testPZSuccess(tzName)); + } + + +protected: + + /++ + Params: + name = The name of the time zone. + stdName = The abbreviation for the time zone during std time. + dstName = The abbreviation for the time zone during DST. + +/ + this(string name, string stdName, string dstName) @safe immutable pure + { + _name = name; + _stdName = stdName; + _dstName = dstName; + } + + +private: + + immutable string _name; + immutable string _stdName; + immutable string _dstName; +} + + +/++ + A TimeZone which represents the current local time zone on + the system running your program. + + This uses the underlying C calls to adjust the time rather than using + specific D code based off of system settings to calculate the time such as + $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that + it will use whatever the current time zone is on the system, even if the + system's time zone changes while the program is running. + +/ +final class LocalTime : TimeZone +{ +public: + + /++ + $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its + only instance. + +/ + static immutable(LocalTime) opCall() @trusted pure nothrow + { + alias FuncType = @safe pure nothrow immutable(LocalTime) function(); + return (cast(FuncType)&singleton)(); + } + + + version(StdDdoc) + { + /++ + The name of the time zone per the TZ Database. This is the name used + to get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone). + + Note that this always returns the empty string. This is because time + zones cannot be uniquely identified by the attributes given by the + OS (such as the $(D stdName) and $(D dstName)), and neither Posix + systems nor Windows systems provide an easy way to get the TZ + Database name of the local time zone. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List + of Time Zones) + +/ + @property override string name() @safe const nothrow; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Standard Time). Regardless, it is not the same as name. + + This property is overridden because the local time of the system could + change while the program is running and we need to determine it + dynamically rather than it being fixed like it would be with most time + zones. + +/ + @property override string stdName() @trusted const nothrow + { + version(Posix) + { + import core.stdc.time : tzname; + import std.conv : to; + try + return to!string(tzname[0]); + catch (Exception e) + assert(0, "to!string(tzname[0]) failed."); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + // Cannot use to!string() like this should, probably due to bug + // http://d.puremagic.com/issues/show_bug.cgi?id=5016 + //return to!string(tzInfo.StandardName); + + wchar[32] str; + + foreach (i, ref wchar c; str) + c = tzInfo.StandardName[i]; + + string retval; + + try + { + foreach (dchar c; str) + { + if (c == '\0') + break; + + retval ~= c; + } + + return retval; + } + catch (Exception e) + assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); + } + } + + @safe unittest + { + version(FreeBSD) + { + // A bug on FreeBSD 9+ makes it so that this test fails. + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 + } + else version(NetBSD) + { + // The same bug on NetBSD 7+ + } + else + { + assert(LocalTime().stdName !is null); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().stdName == "PST"); + + setTZEnvVar("America/New_York"); + assert(LocalTime().stdName == "EST"); + } + } + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Daylight Time). Regardless, it is not the same as name. + + This property is overridden because the local time of the system could + change while the program is running and we need to determine it + dynamically rather than it being fixed like it would be with most time + zones. + +/ + @property override string dstName() @trusted const nothrow + { + version(Posix) + { + import core.stdc.time : tzname; + import std.conv : to; + try + return to!string(tzname[1]); + catch (Exception e) + assert(0, "to!string(tzname[1]) failed."); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + // Cannot use to!string() like this should, probably due to bug + // http://d.puremagic.com/issues/show_bug.cgi?id=5016 + //return to!string(tzInfo.DaylightName); + + wchar[32] str; + + foreach (i, ref wchar c; str) + c = tzInfo.DaylightName[i]; + + string retval; + + try + { + foreach (dchar c; str) + { + if (c == '\0') + break; + + retval ~= c; + } + + return retval; + } + catch (Exception e) + assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); + } + } + + @safe unittest + { + assert(LocalTime().dstName !is null); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + version(FreeBSD) + { + // A bug on FreeBSD 9+ makes it so that this test fails. + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 + } + else version(NetBSD) + { + // The same bug on NetBSD 7+ + } + else + { + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().dstName == "PDT"); + + setTZEnvVar("America/New_York"); + assert(LocalTime().dstName == "EDT"); + } + } + } + + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current + dates but will still return true for $(D hasDST) because the time zone + did at some point have DST. + +/ + @property override bool hasDST() @trusted const nothrow + { + version(Posix) + { + static if (is(typeof(daylight))) + return cast(bool)(daylight); + else + { + try + { + auto currYear = (cast(Date) Clock.currTime()).year; + auto janOffset = SysTime(Date(currYear, 1, 4), cast(immutable) this).stdTime - + SysTime(Date(currYear, 1, 4), UTC()).stdTime; + auto julyOffset = SysTime(Date(currYear, 7, 4), cast(immutable) this).stdTime - + SysTime(Date(currYear, 7, 4), UTC()).stdTime; + + return janOffset != julyOffset; + } + catch (Exception e) + assert(0, "Clock.currTime() threw."); + } + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return tzInfo.DaylightDate.wMonth != 0; + } + } + + @safe unittest + { + LocalTime().hasDST; + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().hasDST); + + setTZEnvVar("America/New_York"); + assert(LocalTime().hasDST); + + setTZEnvVar("UTC"); + assert(!LocalTime().hasDST); + } + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and returns whether DST is in effect in this + time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + override bool dstInEffect(long stdTime) @trusted const nothrow + { + import core.stdc.time : localtime, tm; + time_t unixTime = stdTimeToUnixTime(stdTime); + + version(Posix) + { + tm* timeInfo = localtime(&unixTime); + + return cast(bool)(timeInfo.tm_isdst); + } + else version(Windows) + { + // Apparently Windows isn't smart enough to deal with negative time_t. + if (unixTime >= 0) + { + tm* timeInfo = localtime(&unixTime); + + if (timeInfo) + return cast(bool)(timeInfo.tm_isdst); + } + + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return WindowsTimeZone._dstInEffect(&tzInfo, stdTime); + } + } + + @safe unittest + { + auto currTime = Clock.currStdTime; + LocalTime().dstInEffect(currTime); + } + + + /++ + Returns hnsecs in the local time zone using the standard C function + calls on Posix systems and the standard Windows system calls on Windows + systems to adjust the time to the appropriate time zone from std time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + + See_Also: + $(D TimeZone.utcToTZ) + +/ + override long utcToTZ(long stdTime) @trusted const nothrow + { + version(Solaris) + return stdTime + convert!("seconds", "hnsecs")(tm_gmtoff(stdTime)); + else version(Posix) + { + import core.stdc.time : localtime, tm; + time_t unixTime = stdTimeToUnixTime(stdTime); + tm* timeInfo = localtime(&unixTime); + + return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return WindowsTimeZone._utcToTZ(&tzInfo, stdTime, hasDST); + } + } + + @safe unittest + { + LocalTime().utcToTZ(0); + } + + + /++ + Returns std time using the standard C function calls on Posix systems + and the standard Windows system calls on Windows systems to adjust the + time to UTC from the appropriate time zone. + + See_Also: + $(D TimeZone.tzToUTC) + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @trusted const nothrow + { + version(Posix) + { + import core.stdc.time : localtime, tm; + time_t unixTime = stdTimeToUnixTime(adjTime); + + immutable past = unixTime - cast(time_t) convert!("days", "seconds")(1); + tm* timeInfo = localtime(past < unixTime ? &past : &unixTime); + immutable pastOffset = timeInfo.tm_gmtoff; + + immutable future = unixTime + cast(time_t) convert!("days", "seconds")(1); + timeInfo = localtime(future > unixTime ? &future : &unixTime); + immutable futureOffset = timeInfo.tm_gmtoff; + + if (pastOffset == futureOffset) + return adjTime - convert!("seconds", "hnsecs")(pastOffset); + + if (pastOffset < futureOffset) + unixTime -= cast(time_t) convert!("hours", "seconds")(1); + + unixTime -= pastOffset; + timeInfo = localtime(&unixTime); + + return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return WindowsTimeZone._tzToUTC(&tzInfo, adjTime, hasDST); + } + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + import std.typecons : tuple; + + assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); + assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); + + assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); + assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), + tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), + tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), + tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), + tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; + + foreach (i; 0 .. tzInfos.length) + { + auto tzName = tzInfos[i][0]; + setTZEnvVar(tzName); + immutable spring = tzInfos[i][3]; + immutable fall = tzInfos[i][4]; + auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; + auto dstOffset = stdOffset + dur!"hours"(1); + + // Verify that creating a SysTime in the given time zone results + // in a SysTime with the correct std time during and surrounding + // a DST switch. + foreach (hour; -12 .. 13) + { + auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); + immutable targetHour = hour < 0 ? hour + 24 : hour; + + static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) + { + enforce(st.hour == hour, + new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), + __FILE__, line)); + } + + void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", + tag, st, tzName, st.utcOffset, stdOffset, dstOffset), + __FILE__, line); + } + + enforce(st.dstInEffect == dstInEffect, msg("1")); + enforce(st.utcOffset == offset, msg("2")); + enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); + } + + if (hour == spring) + { + testHour(st, spring + 1, tzName); + testHour(st + dur!"minutes"(1), spring + 1, tzName); + } + else + { + testHour(st, targetHour, tzName); + testHour(st + dur!"minutes"(1), targetHour, tzName); + } + + if (hour < spring) + testOffset1(stdOffset, false); + else + testOffset1(dstOffset, true); + + st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); + testHour(st, targetHour, tzName); + + // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). + if (hour == fall - 1) + testHour(st + dur!"hours"(1), targetHour, tzName); + + if (hour < fall) + testOffset1(dstOffset, true); + else + testOffset1(stdOffset, false); + } + + // Verify that converting a time in UTC to a time in another + // time zone results in the correct time during and surrounding + // a DST switch. + bool first = true; + auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; + auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; + // @@@BUG@@@ 3659 makes this necessary. + auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); + + foreach (hour; -24 .. 25) + { + auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); + auto local = utc.toLocalTime(); + + void testOffset2(Duration offset, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), + __FILE__, line); + } + + enforce((utc + offset).hour == local.hour, msg("1")); + enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); + } + + if (utc < springSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + + utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); + local = utc.toLocalTime(); + + if (utc == fallSwitch || utc == fallSwitchMinus1) + { + if (first) + { + testOffset2(dstOffset); + first = false; + } + else + testOffset2(stdOffset); + } + else if (utc > fallSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + } + } + } + } + + +private: + + this() @safe immutable pure + { + super("", "", ""); + } + + + // This is done so that we can maintain purity in spite of doing an impure + // operation the first time that LocalTime() is called. + static immutable(LocalTime) singleton() @trusted + { + import core.stdc.time : tzset; + import std.concurrency : initOnce; + static instance = new immutable(LocalTime)(); + static shared bool guard; + initOnce!guard({tzset(); return true;}()); + return instance; + } + + + // The Solaris version of struct tm has no tm_gmtoff field, so do it here + version(Solaris) + { + long tm_gmtoff(long stdTime) @trusted const nothrow + { + import core.stdc.time : localtime, gmtime, tm; + + time_t unixTime = stdTimeToUnixTime(stdTime); + tm* buf = localtime(&unixTime); + tm timeInfo = *buf; + buf = gmtime(&unixTime); + tm timeInfoGmt = *buf; + + return timeInfo.tm_sec - timeInfoGmt.tm_sec + + convert!("minutes", "seconds")(timeInfo.tm_min - timeInfoGmt.tm_min) + + convert!("hours", "seconds")(timeInfo.tm_hour - timeInfoGmt.tm_hour); + } + } +} + + +/++ + A $(LREF TimeZone) which represents UTC. + +/ +final class UTC : TimeZone +{ +public: + + /++ + $(D UTC) is a singleton class. $(D UTC) returns its only instance. + +/ + static immutable(UTC) opCall() @safe pure nothrow + { + return _utc; + } + + + /++ + Always returns false. + +/ + @property override bool hasDST() @safe const nothrow + { + return false; + } + + + /++ + Always returns false. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow + { + return false; + } + + + /++ + Returns the given hnsecs without changing them at all. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + + See_Also: + $(D TimeZone.utcToTZ) + +/ + override long utcToTZ(long stdTime) @safe const nothrow + { + return stdTime; + } + + @safe unittest + { + assert(UTC().utcToTZ(0) == 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assert(UTC().utcToTZ(std.stdTime) == std.stdTime); + assert(UTC().utcToTZ(dst.stdTime) == dst.stdTime); + } + } + + + /++ + Returns the given hnsecs without changing them at all. + + See_Also: + $(D TimeZone.tzToUTC) + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow + { + return adjTime; + } + + @safe unittest + { + assert(UTC().tzToUTC(0) == 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assert(UTC().tzToUTC(std.stdTime) == std.stdTime); + assert(UTC().tzToUTC(dst.stdTime) == dst.stdTime); + } + } + + + /++ + Returns a $(REF Duration, core,time) of 0. + + Params: + stdTime = The UTC time for which to get the offset from UTC for this + time zone. + +/ + override Duration utcOffsetAt(long stdTime) @safe const nothrow + { + return dur!"hnsecs"(0); + } + + +private: + + this() @safe immutable pure + { + super("UTC", "UTC", "UTC"); + } + + + static immutable UTC _utc = new immutable(UTC)(); +} + + +/++ + Represents a time zone with an offset (in minutes, west is negative) from + UTC but no DST. + + It's primarily used as the time zone in the result of + $(REF SysTime,std,datetime,systime)'s $(D fromISOString), + $(D fromISOExtString), and $(D fromSimpleString). + + $(D name) and $(D dstName) are always the empty string since this time zone + has no DST, and while it may be meant to represent a time zone which is in + the TZ Database, obviously it's not likely to be following the exact rules + of any of the time zones in the TZ Database, so it makes no sense to set it. + +/ +final class SimpleTimeZone : TimeZone +{ +public: + + /++ + Always returns false. + +/ + @property override bool hasDST() @safe const nothrow + { + return false; + } + + + /++ + Always returns false. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow + { + return false; + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + override long utcToTZ(long stdTime) @safe const nothrow + { + return stdTime + _utcOffset.total!"hnsecs"; + } + + @safe unittest + { + auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto east = new immutable SimpleTimeZone(dur!"hours"(8)); + + assert(west.utcToTZ(0) == -288_000_000_000L); + assert(east.utcToTZ(0) == 288_000_000_000L); + assert(west.utcToTZ(54_321_234_567_890L) == 54_033_234_567_890L); + + const cstz = west; + assert(cstz.utcToTZ(50002) == west.utcToTZ(50002)); + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow + { + return adjTime - _utcOffset.total!"hnsecs"; + } + + @safe unittest + { + auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto east = new immutable SimpleTimeZone(dur!"hours"(8)); + + assert(west.tzToUTC(-288_000_000_000L) == 0); + assert(east.tzToUTC(288_000_000_000L) == 0); + assert(west.tzToUTC(54_033_234_567_890L) == 54_321_234_567_890L); + + const cstz = west; + assert(cstz.tzToUTC(20005) == west.tzToUTC(20005)); + } + + + /++ + Returns utcOffset as a $(REF Duration, core,time). + + Params: + stdTime = The UTC time for which to get the offset from UTC for this + time zone. + +/ + override Duration utcOffsetAt(long stdTime) @safe const nothrow + { + return _utcOffset; + } + + + /++ + Params: + utcOffset = This time zone's offset from UTC with west of UTC being + negative (it is added to UTC to get the adjusted time). + stdName = The $(D stdName) for this time zone. + +/ + this(Duration utcOffset, string stdName = "") @safe immutable pure + { + // FIXME This probably needs to be changed to something like (-12 - 13). + enforce!DateTimeException(abs(utcOffset) < dur!"minutes"(1440), + "Offset from UTC must be within range (-24:00 - 24:00)."); + super("", stdName, ""); + this._utcOffset = utcOffset; + } + + @safe unittest + { + auto stz = new immutable SimpleTimeZone(dur!"hours"(-8), "PST"); + assert(stz.name == ""); + assert(stz.stdName == "PST"); + assert(stz.dstName == ""); + assert(stz.utcOffset == dur!"hours"(-8)); + } + + + /++ + The amount of time the offset from UTC is (negative is west of UTC, + positive is east). + +/ + @property Duration utcOffset() @safe const pure nothrow + { + return _utcOffset; + } + + +package: + + /+ + Returns a time zone as a string with an offset from UTC. + + Time zone offsets will be in the form +HHMM or -HHMM. + + Params: + utcOffset = The number of minutes offset from UTC (negative means + west). + +/ + static string toISOString(Duration utcOffset) @safe pure + { + import std.format : format; + immutable absOffset = abs(utcOffset); + enforce!DateTimeException(absOffset < dur!"minutes"(1440), + "Offset from UTC must be within range (-24:00 - 24:00)."); + int hours; + int minutes; + absOffset.split!("hours", "minutes")(hours, minutes); + return format(utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d", hours, minutes); + } + + @safe unittest + { + static string testSTZInvalid(Duration offset) + { + return SimpleTimeZone.toISOString(offset); + } + + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); + + assert(toISOString(dur!"minutes"(0)) == "+0000"); + assert(toISOString(dur!"minutes"(1)) == "+0001"); + assert(toISOString(dur!"minutes"(10)) == "+0010"); + assert(toISOString(dur!"minutes"(59)) == "+0059"); + assert(toISOString(dur!"minutes"(60)) == "+0100"); + assert(toISOString(dur!"minutes"(90)) == "+0130"); + assert(toISOString(dur!"minutes"(120)) == "+0200"); + assert(toISOString(dur!"minutes"(480)) == "+0800"); + assert(toISOString(dur!"minutes"(1439)) == "+2359"); + + assert(toISOString(dur!"minutes"(-1)) == "-0001"); + assert(toISOString(dur!"minutes"(-10)) == "-0010"); + assert(toISOString(dur!"minutes"(-59)) == "-0059"); + assert(toISOString(dur!"minutes"(-60)) == "-0100"); + assert(toISOString(dur!"minutes"(-90)) == "-0130"); + assert(toISOString(dur!"minutes"(-120)) == "-0200"); + assert(toISOString(dur!"minutes"(-480)) == "-0800"); + assert(toISOString(dur!"minutes"(-1439)) == "-2359"); + } + + + /+ + Returns a time zone as a string with an offset from UTC. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + + Params: + utcOffset = The number of minutes offset from UTC (negative means + west). + +/ + static string toISOExtString(Duration utcOffset) @safe pure + { + import std.format : format; + + immutable absOffset = abs(utcOffset); + enforce!DateTimeException(absOffset < dur!"minutes"(1440), + "Offset from UTC must be within range (-24:00 - 24:00)."); + int hours; + int minutes; + absOffset.split!("hours", "minutes")(hours, minutes); + return format(utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", hours, minutes); + } + + @safe unittest + { + static string testSTZInvalid(Duration offset) + { + return SimpleTimeZone.toISOExtString(offset); + } + + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); + + assert(toISOExtString(dur!"minutes"(0)) == "+00:00"); + assert(toISOExtString(dur!"minutes"(1)) == "+00:01"); + assert(toISOExtString(dur!"minutes"(10)) == "+00:10"); + assert(toISOExtString(dur!"minutes"(59)) == "+00:59"); + assert(toISOExtString(dur!"minutes"(60)) == "+01:00"); + assert(toISOExtString(dur!"minutes"(90)) == "+01:30"); + assert(toISOExtString(dur!"minutes"(120)) == "+02:00"); + assert(toISOExtString(dur!"minutes"(480)) == "+08:00"); + assert(toISOExtString(dur!"minutes"(1439)) == "+23:59"); + + assert(toISOExtString(dur!"minutes"(-1)) == "-00:01"); + assert(toISOExtString(dur!"minutes"(-10)) == "-00:10"); + assert(toISOExtString(dur!"minutes"(-59)) == "-00:59"); + assert(toISOExtString(dur!"minutes"(-60)) == "-01:00"); + assert(toISOExtString(dur!"minutes"(-90)) == "-01:30"); + assert(toISOExtString(dur!"minutes"(-120)) == "-02:00"); + assert(toISOExtString(dur!"minutes"(-480)) == "-08:00"); + assert(toISOExtString(dur!"minutes"(-1439)) == "-23:59"); + } + + + /+ + Takes a time zone as a string with an offset from UTC and returns a + $(LREF SimpleTimeZone) which matches. + + The accepted formats for time zone offsets are +HH, -HH, +HHMM, and + -HHMM. + + Params: + isoString = A string which represents a time zone in the ISO format. + +/ + static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : startsWith, countUntil, all; + import std.ascii : isDigit; + import std.conv : to; + import std.format : format; + + auto dstr = to!dstring(isoString); + + enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String"); + + auto sign = dstr.startsWith('-') ? -1 : 1; + + dstr.popFront(); + enforce!DateTimeException(all!isDigit(dstr), format("Invalid ISO String: %s", dstr)); + + int hours; + int minutes; + + if (dstr.length == 2) + hours = to!int(dstr); + else if (dstr.length == 4) + { + hours = to!int(dstr[0 .. 2]); + minutes = to!int(dstr[2 .. 4]); + } + else + throw new DateTimeException(format("Invalid ISO String: %s", dstr)); + + enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr)); + + return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", + "-24:00", "+24:00", "-24", "+24", "-2400", "+2400", + "1", "+1", "-1", "+9", "-9", + "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", + "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", + "000", "00000", "0160", "-0160", + " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", + " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", + " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", + " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", + "+ab:cd", "+abcd", "+0Z:00", "+Z", "+00Z", + "-ab:cd", "+abcd", "-0Z:00", "-Z", "-00Z", + "01:00", "12:00", "23:59"]) + { + assertThrown!DateTimeException(SimpleTimeZone.fromISOString(str), format("[%s]", str)); + } + + static void test(string str, Duration utcOffset, size_t line = __LINE__) + { + if (SimpleTimeZone.fromISOString(str).utcOffset != (new immutable SimpleTimeZone(utcOffset)).utcOffset) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("+0000", Duration.zero); + test("+0001", minutes(1)); + test("+0010", minutes(10)); + test("+0059", minutes(59)); + test("+0100", hours(1)); + test("+0130", hours(1) + minutes(30)); + test("+0200", hours(2)); + test("+0800", hours(8)); + test("+2359", hours(23) + minutes(59)); + + test("-0001", minutes(-1)); + test("-0010", minutes(-10)); + test("-0059", minutes(-59)); + test("-0100", hours(-1)); + test("-0130", hours(-1) - minutes(30)); + test("-0200", hours(-2)); + test("-0800", hours(-8)); + test("-2359", hours(-23) - minutes(59)); + + test("+00", Duration.zero); + test("+01", hours(1)); + test("+02", hours(2)); + test("+12", hours(12)); + test("+23", hours(23)); + + test("-00", Duration.zero); + test("-01", hours(-1)); + test("-02", hours(-2)); + test("-12", hours(-12)); + test("-23", hours(-23)); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + static void test(in string isoString, int expectedOffset, size_t line = __LINE__) + { + auto stz = SimpleTimeZone.fromISOExtString(isoString); + if (stz.utcOffset != dur!"minutes"(expectedOffset)) + throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); + + auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); + if (result != isoString) + throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoString), __FILE__, line); + } + + test("+00:00", 0); + test("+00:01", 1); + test("+00:10", 10); + test("+00:59", 59); + test("+01:00", 60); + test("+01:30", 90); + test("+02:00", 120); + test("+08:00", 480); + test("+08:00", 480); + test("+23:59", 1439); + + test("-00:01", -1); + test("-00:10", -10); + test("-00:59", -59); + test("-01:00", -60); + test("-01:30", -90); + test("-02:00", -120); + test("-08:00", -480); + test("-08:00", -480); + test("-23:59", -1439); + } + + + /+ + Takes a time zone as a string with an offset from UTC and returns a + $(LREF SimpleTimeZone) which matches. + + The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and + -HH:MM. + + Params: + isoExtString = A string which represents a time zone in the ISO format. + +/ + static immutable(SimpleTimeZone) fromISOExtString(S)(S isoExtString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : startsWith, countUntil, all; + import std.ascii : isDigit; + import std.conv : to; + import std.format : format; + + auto dstr = to!dstring(isoExtString); + + enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String"); + + auto sign = dstr.startsWith('-') ? -1 : 1; + + dstr.popFront(); + enforce!DateTimeException(!dstr.empty, "Invalid ISO String"); + + immutable colon = dstr.countUntil(':'); + + dstring hoursStr; + dstring minutesStr; + + if (colon != -1) + { + hoursStr = dstr[0 .. colon]; + minutesStr = dstr[colon + 1 .. $]; + enforce!DateTimeException(minutesStr.length == 2, format("Invalid ISO String: %s", dstr)); + } + else + hoursStr = dstr; + + enforce!DateTimeException(hoursStr.length == 2, format("Invalid ISO String: %s", dstr)); + enforce!DateTimeException(all!isDigit(hoursStr), format("Invalid ISO String: %s", dstr)); + enforce!DateTimeException(all!isDigit(minutesStr), format("Invalid ISO String: %s", dstr)); + + immutable hours = to!int(hoursStr); + immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr); + enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr)); + + return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", + "-24:00", "+24:00", "-24", "+24", "-2400", "-2400", + "1", "+1", "-1", "+9", "-9", + "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", + "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", + "000", "00000", "0160", "-0160", + " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", + " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", + " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", + " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", + "+ab:cd", "abcd", "+0Z:00", "+Z", "+00Z", + "-ab:cd", "abcd", "-0Z:00", "-Z", "-00Z", + "0100", "1200", "2359"]) + { + assertThrown!DateTimeException(SimpleTimeZone.fromISOExtString(str), format("[%s]", str)); + } + + static void test(string str, Duration utcOffset, size_t line = __LINE__) + { + if (SimpleTimeZone.fromISOExtString(str).utcOffset != (new immutable SimpleTimeZone(utcOffset)).utcOffset) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("+00:00", Duration.zero); + test("+00:01", minutes(1)); + test("+00:10", minutes(10)); + test("+00:59", minutes(59)); + test("+01:00", hours(1)); + test("+01:30", hours(1) + minutes(30)); + test("+02:00", hours(2)); + test("+08:00", hours(8)); + test("+23:59", hours(23) + minutes(59)); + + test("-00:01", minutes(-1)); + test("-00:10", minutes(-10)); + test("-00:59", minutes(-59)); + test("-01:00", hours(-1)); + test("-01:30", hours(-1) - minutes(30)); + test("-02:00", hours(-2)); + test("-08:00", hours(-8)); + test("-23:59", hours(-23) - minutes(59)); + + test("+00", Duration.zero); + test("+01", hours(1)); + test("+02", hours(2)); + test("+12", hours(12)); + test("+23", hours(23)); + + test("-00", Duration.zero); + test("-01", hours(-1)); + test("-02", hours(-2)); + test("-12", hours(-12)); + test("-23", hours(-23)); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + static void test(in string isoExtString, int expectedOffset, size_t line = __LINE__) + { + auto stz = SimpleTimeZone.fromISOExtString(isoExtString); + if (stz.utcOffset != dur!"minutes"(expectedOffset)) + throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); + + auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); + if (result != isoExtString) + throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoExtString), __FILE__, line); + } + + test("+00:00", 0); + test("+00:01", 1); + test("+00:10", 10); + test("+00:59", 59); + test("+01:00", 60); + test("+01:30", 90); + test("+02:00", 120); + test("+08:00", 480); + test("+08:00", 480); + test("+23:59", 1439); + + test("-00:01", -1); + test("-00:10", -10); + test("-00:59", -59); + test("-01:00", -60); + test("-01:30", -90); + test("-02:00", -120); + test("-08:00", -480); + test("-08:00", -480); + test("-23:59", -1439); + } + + +private: + + immutable Duration _utcOffset; +} + + +/++ + Represents a time zone from a TZ Database time zone file. Files from the TZ + Database are how Posix systems hold their time zone information. + Unfortunately, Windows does not use the TZ Database. To use the TZ Database, + use $(D PosixTimeZone) (which reads its information from the TZ Database + files on disk) on Windows by providing the TZ Database files and telling + $(D PosixTimeZone.getTimeZone) where the directory holding them is. + + To get a $(D PosixTimeZone), either call $(D PosixTimeZone.getTimeZone) + (which allows specifying the location the time zone files) or call + $(D TimeZone.getTimeZone) (which will give a $(D PosixTimeZone) on Posix + systems and a $(LREF WindowsTimeZone) on Windows systems). + + Note: + Unless your system's local time zone deals with leap seconds (which is + highly unlikely), then the only way to get a time zone which + takes leap seconds into account is to use $(D PosixTimeZone) with a + time zone whose name starts with "right/". Those time zone files do + include leap seconds, and $(D PosixTimeZone) will take them into account + (though posix systems which use a "right/" time zone as their local time + zone will $(I not) take leap seconds into account even though they're + in the file). + + See_Also: + $(HTTP www.iana.org/time-zones, Home of the TZ Database files)
+ $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time + Zones) + +/ +final class PosixTimeZone : TimeZone +{ + import std.algorithm.searching : countUntil, canFind, startsWith; + import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; + import std.path : extension; + import std.stdio : File; + import std.string : strip, representation; + import std.traits : isArray, isSomeChar; +public: + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current + dates but will still return true for $(D hasDST) because the time zone + did at some point have DST. + +/ + @property override bool hasDST() @safe const nothrow + { + return _hasDST; + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and returns whether DST is in effect in this + time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow + { + assert(!_transitions.empty); + + immutable unixTime = stdTimeToUnixTime(stdTime); + immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); + + if (found == -1) + return _transitions.back.ttInfo.isDST; + + immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return transition.ttInfo.isDST; + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + override long utcToTZ(long stdTime) @safe const nothrow + { + assert(!_transitions.empty); + + immutable leapSecs = calculateLeapSeconds(stdTime); + immutable unixTime = stdTimeToUnixTime(stdTime); + immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); + + if (found == -1) + return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow + { + assert(!_transitions.empty); + + immutable leapSecs = calculateLeapSeconds(adjTime); + time_t unixTime = stdTimeToUnixTime(adjTime); + immutable past = unixTime - convert!("days", "seconds")(1); + immutable future = unixTime + convert!("days", "seconds")(1); + + immutable pastFound = countUntil!"b < a.timeT"(_transitions, past); + + if (pastFound == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + immutable futureFound = countUntil!"b < a.timeT"(_transitions[pastFound .. $], future); + immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; + + if (futureFound == 0) + return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); + + immutable futureTrans = futureFound == -1 ? _transitions.back + : _transitions[pastFound + futureFound - 1]; + immutable pastOffset = pastTrans.ttInfo.utcOffset; + + if (pastOffset < futureTrans.ttInfo.utcOffset) + unixTime -= convert!("hours", "seconds")(1); + + immutable found = countUntil!"b < a.timeT"(_transitions[pastFound .. $], unixTime - pastOffset); + + if (found == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; + + return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); + } + + + version(Android) + { + // Android concatenates all time zone data into a single file and stores it here. + enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/"; + } + else version(Posix) + { + /++ + The default directory where the TZ Database files are. It's empty + for Windows, since Windows doesn't have them. + +/ + enum defaultTZDatabaseDir = "/usr/share/zoneinfo/"; + } + else version(Windows) + { + /++ The default directory where the TZ Database files are. It's empty + for Windows, since Windows doesn't have them. + +/ + enum defaultTZDatabaseDir = ""; + } + + + /++ + Returns a $(LREF TimeZone) with the give name per the TZ Database. The + time zone information is fetched from the TZ Database time zone files in + the given directory. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of + Time Zones) + + Params: + name = The TZ Database name of the desired time zone + tzDatabaseDir = The directory where the TZ Database files are + located. Because these files are not located on + Windows systems, provide them + and give their location here to + use $(LREF PosixTimeZone)s. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given time zone + could not be found or $(D FileException) if the TZ Database file + could not be opened. + +/ + // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed + // directory. + static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted + { + import std.algorithm.sorting : sort; + import std.range : retro; + import std.format : format; + import std.path : asNormalizedPath, chainPath; + import std.conv : to; + + name = strip(name); + + enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); + enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); + + version(Android) + { + auto tzfileOffset = name in tzdataIndex(tzDatabaseDir); + enforce(tzfileOffset, new DateTimeException(format("The time zone %s is not listed.", name))); + string tzFilename = separate_index ? "zoneinfo.dat" : "tzdata"; + const file = asNormalizedPath(chainPath(tzDatabaseDir, tzFilename)).to!string; + } + else + const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string; + + enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file))); + enforce(file.isFile, new DateTimeException(format("%s is not a file.", file))); + + auto tzFile = File(file); + version(Android) tzFile.seek(*tzfileOffset); + immutable gmtZone = name.representation().canFind("GMT"); + + try + { + _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); + + immutable char tzFileVersion = readVal!char(tzFile); + _enforceValidTZFile(tzFileVersion == '\0' || tzFileVersion == '2' || tzFileVersion == '3'); + + { + auto zeroBlock = readVal!(ubyte[])(tzFile, 15); + bool allZeroes = true; + + foreach (val; zeroBlock) + { + if (val != 0) + { + allZeroes = false; + break; + } + } + + _enforceValidTZFile(allZeroes); + } + + + // The number of UTC/local indicators stored in the file. + auto tzh_ttisgmtcnt = readVal!int(tzFile); + + // The number of standard/wall indicators stored in the file. + auto tzh_ttisstdcnt = readVal!int(tzFile); + + // The number of leap seconds for which data is stored in the file. + auto tzh_leapcnt = readVal!int(tzFile); + + // The number of "transition times" for which data is stored in the file. + auto tzh_timecnt = readVal!int(tzFile); + + // The number of "local time types" for which data is stored in the file (must not be zero). + auto tzh_typecnt = readVal!int(tzFile); + _enforceValidTZFile(tzh_typecnt != 0); + + // The number of characters of "timezone abbreviation strings" stored in the file. + auto tzh_charcnt = readVal!int(tzFile); + + // time_ts where DST transitions occur. + auto transitionTimeTs = new long[](tzh_timecnt); + foreach (ref transition; transitionTimeTs) + transition = readVal!int(tzFile); + + // Indices into ttinfo structs indicating the changes + // to be made at the corresponding DST transition. + auto ttInfoIndices = new ubyte[](tzh_timecnt); + foreach (ref ttInfoIndex; ttInfoIndices) + ttInfoIndex = readVal!ubyte(tzFile); + + // ttinfos which give info on DST transitions. + auto tempTTInfos = new TempTTInfo[](tzh_typecnt); + foreach (ref ttInfo; tempTTInfos) + ttInfo = readVal!TempTTInfo(tzFile); + + // The array of time zone abbreviation characters. + auto tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); + + auto leapSeconds = new LeapSecond[](tzh_leapcnt); + foreach (ref leapSecond; leapSeconds) + { + // The time_t when the leap second occurs. + auto timeT = readVal!int(tzFile); + + // The total number of leap seconds to be applied after + // the corresponding leap second. + auto total = readVal!int(tzFile); + + leapSecond = LeapSecond(timeT, total); + } + + // Indicate whether each corresponding DST transition were specified + // in standard time or wall clock time. + auto transitionIsStd = new bool[](tzh_ttisstdcnt); + foreach (ref isStd; transitionIsStd) + isStd = readVal!bool(tzFile); + + // Indicate whether each corresponding DST transition associated with + // local time types are specified in UTC or local time. + auto transitionInUTC = new bool[](tzh_ttisgmtcnt); + foreach (ref inUTC; transitionInUTC) + inUTC = readVal!bool(tzFile); + + _enforceValidTZFile(!tzFile.eof); + + // If version 2 or 3, the information is duplicated in 64-bit. + if (tzFileVersion == '2' || tzFileVersion == '3') + { + _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); + + immutable char tzFileVersion2 = readVal!(char)(tzFile); + _enforceValidTZFile(tzFileVersion2 == '2' || tzFileVersion2 == '3'); + + { + auto zeroBlock = readVal!(ubyte[])(tzFile, 15); + bool allZeroes = true; + + foreach (val; zeroBlock) + { + if (val != 0) + { + allZeroes = false; + break; + } + } + + _enforceValidTZFile(allZeroes); + } + + + // The number of UTC/local indicators stored in the file. + tzh_ttisgmtcnt = readVal!int(tzFile); + + // The number of standard/wall indicators stored in the file. + tzh_ttisstdcnt = readVal!int(tzFile); + + // The number of leap seconds for which data is stored in the file. + tzh_leapcnt = readVal!int(tzFile); + + // The number of "transition times" for which data is stored in the file. + tzh_timecnt = readVal!int(tzFile); + + // The number of "local time types" for which data is stored in the file (must not be zero). + tzh_typecnt = readVal!int(tzFile); + _enforceValidTZFile(tzh_typecnt != 0); + + // The number of characters of "timezone abbreviation strings" stored in the file. + tzh_charcnt = readVal!int(tzFile); + + // time_ts where DST transitions occur. + transitionTimeTs = new long[](tzh_timecnt); + foreach (ref transition; transitionTimeTs) + transition = readVal!long(tzFile); + + // Indices into ttinfo structs indicating the changes + // to be made at the corresponding DST transition. + ttInfoIndices = new ubyte[](tzh_timecnt); + foreach (ref ttInfoIndex; ttInfoIndices) + ttInfoIndex = readVal!ubyte(tzFile); + + // ttinfos which give info on DST transitions. + tempTTInfos = new TempTTInfo[](tzh_typecnt); + foreach (ref ttInfo; tempTTInfos) + ttInfo = readVal!TempTTInfo(tzFile); + + // The array of time zone abbreviation characters. + tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); + + leapSeconds = new LeapSecond[](tzh_leapcnt); + foreach (ref leapSecond; leapSeconds) + { + // The time_t when the leap second occurs. + auto timeT = readVal!long(tzFile); + + // The total number of leap seconds to be applied after + // the corresponding leap second. + auto total = readVal!int(tzFile); + + leapSecond = LeapSecond(timeT, total); + } + + // Indicate whether each corresponding DST transition were specified + // in standard time or wall clock time. + transitionIsStd = new bool[](tzh_ttisstdcnt); + foreach (ref isStd; transitionIsStd) + isStd = readVal!bool(tzFile); + + // Indicate whether each corresponding DST transition associated with + // local time types are specified in UTC or local time. + transitionInUTC = new bool[](tzh_ttisgmtcnt); + foreach (ref inUTC; transitionInUTC) + inUTC = readVal!bool(tzFile); + } + + _enforceValidTZFile(tzFile.readln().strip().empty); + + cast(void) tzFile.readln(); + + version(Android) + { + // Android uses a single file for all timezone data, so the file + // doesn't end here. + } + else + { + _enforceValidTZFile(tzFile.readln().strip().empty); + _enforceValidTZFile(tzFile.eof); + } + + + auto transitionTypes = new TransitionType*[](tempTTInfos.length); + + foreach (i, ref ttype; transitionTypes) + { + bool isStd = false; + + if (i < transitionIsStd.length && !transitionIsStd.empty) + isStd = transitionIsStd[i]; + + bool inUTC = false; + + if (i < transitionInUTC.length && !transitionInUTC.empty) + inUTC = transitionInUTC[i]; + + ttype = new TransitionType(isStd, inUTC); + } + + auto ttInfos = new immutable(TTInfo)*[](tempTTInfos.length); + foreach (i, ref ttInfo; ttInfos) + { + auto tempTTInfo = tempTTInfos[i]; + + if (gmtZone) + tempTTInfo.tt_gmtoff = -tempTTInfo.tt_gmtoff; + + auto abbrevChars = tzAbbrevChars[tempTTInfo.tt_abbrind .. $]; + string abbrev = abbrevChars[0 .. abbrevChars.countUntil('\0')].idup; + + ttInfo = new immutable(TTInfo)(tempTTInfos[i], abbrev); + } + + auto tempTransitions = new TempTransition[](transitionTimeTs.length); + foreach (i, ref tempTransition; tempTransitions) + { + immutable ttiIndex = ttInfoIndices[i]; + auto transitionTimeT = transitionTimeTs[i]; + auto ttype = transitionTypes[ttiIndex]; + auto ttInfo = ttInfos[ttiIndex]; + + tempTransition = TempTransition(transitionTimeT, ttInfo, ttype); + } + + if (tempTransitions.empty) + { + _enforceValidTZFile(ttInfos.length == 1 && transitionTypes.length == 1); + tempTransitions ~= TempTransition(0, ttInfos[0], transitionTypes[0]); + } + + sort!"a.timeT < b.timeT"(tempTransitions); + sort!"a.timeT < b.timeT"(leapSeconds); + + auto transitions = new Transition[](tempTransitions.length); + foreach (i, ref transition; transitions) + { + auto tempTransition = tempTransitions[i]; + auto transitionTimeT = tempTransition.timeT; + auto ttInfo = tempTransition.ttInfo; + + _enforceValidTZFile(i == 0 || transitionTimeT > tempTransitions[i - 1].timeT); + + transition = Transition(transitionTimeT, ttInfo); + } + + string stdName; + string dstName; + bool hasDST = false; + + foreach (transition; retro(transitions)) + { + auto ttInfo = transition.ttInfo; + + if (ttInfo.isDST) + { + if (dstName.empty) + dstName = ttInfo.abbrev; + hasDST = true; + } + else + { + if (stdName.empty) + stdName = ttInfo.abbrev; + } + + if (!stdName.empty && !dstName.empty) + break; + } + + return new immutable PosixTimeZone(transitions.idup, leapSeconds.idup, name, stdName, dstName, hasDST); + } + catch (DateTimeException dte) + throw dte; + catch (Exception e) + throw new DateTimeException("Not a valid TZ data file", __FILE__, __LINE__, e); + } + + /// + @safe unittest + { + version(Posix) + { + auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); + + assert(tz.name == "America/Los_Angeles"); + assert(tz.stdName == "PST"); + assert(tz.dstName == "PDT"); + } + } + + /++ + Returns a list of the names of the time zones installed on the system. + + Providing a sub-name narrows down the list of time zones (which + can number in the thousands). For example, + passing in "America" as the sub-name returns only the time zones which + begin with "America". + + Params: + subName = The first part of the desired time zones. + tzDatabaseDir = The directory where the TZ Database files are + located. + + Throws: + $(D FileException) if it fails to read from disk. + +/ + static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @trusted + { + import std.algorithm.sorting : sort; + import std.array : appender; + import std.format : format; + + version(Posix) + subName = strip(subName); + else version(Windows) + { + import std.array : replace; + import std.path : dirSeparator; + subName = replace(strip(subName), "/", dirSeparator); + } + + enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); + enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); + + auto timezones = appender!(string[])(); + + version(Android) + { + import std.algorithm.iteration : filter; + import std.algorithm.mutation : copy; + tzdataIndex(tzDatabaseDir).byKey.filter!(a => a.startsWith(subName)).copy(timezones); + } + else + { + foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth)) + { + if (de.isFile) + { + auto tzName = de.name[tzDatabaseDir.length .. $]; + + if (!tzName.extension().empty || + !tzName.startsWith(subName) || + tzName == "leapseconds" || + tzName == "+VERSION") + { + continue; + } + + timezones.put(tzName); + } + } + } + + sort(timezones.data); + + return timezones.data; + } + + version(Posix) @system unittest + { + import std.exception : assertNotThrown; + import std.stdio : writefln; + static void testPTZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + + PosixTimeZone.getTimeZone(tzName); + } + + static void testPTZFailure(string tzName) + { + scope(success) writefln("TZName which was supposed to throw: %s", tzName); + + PosixTimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + + foreach (tzName; tzNames) + assertNotThrown!DateTimeException(testPTZSuccess(tzName)); + + // No timezone directories on Android, just a single tzdata file + version(Android) + {} + else + { + foreach (DirEntry de; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) + { + if (de.isFile) + { + auto tzName = de.name[defaultTZDatabaseDir.length .. $]; + + if (!canFind(tzNames, tzName)) + assertThrown!DateTimeException(testPTZFailure(tzName)); + } + } + } + } + + +private: + + /+ + Holds information on when a time transition occures (usually a + transition to or from DST) as well as a pointer to the $(D TTInfo) which + holds information on the utc offset past the transition. + +/ + struct Transition + { + this(long timeT, immutable (TTInfo)* ttInfo) @safe pure + { + this.timeT = timeT; + this.ttInfo = ttInfo; + } + + long timeT; + immutable (TTInfo)* ttInfo; + } + + + /+ + Holds information on when a leap second occurs. + +/ + struct LeapSecond + { + this(long timeT, int total) @safe pure + { + this.timeT = timeT; + this.total = total; + } + + long timeT; + int total; + } + + /+ + Holds information on the utc offset after a transition as well as + whether DST is in effect after that transition. + +/ + struct TTInfo + { + this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure + { + utcOffset = tempTTInfo.tt_gmtoff; + isDST = tempTTInfo.tt_isdst; + this.abbrev = abbrev; + } + + immutable int utcOffset; // Offset from UTC. + immutable bool isDST; // Whether DST is in effect. + immutable string abbrev; // The current abbreviation for the time zone. + } + + + /+ + Struct used to hold information relating to $(D TTInfo) while organizing + the time zone information prior to putting it in its final form. + +/ + struct TempTTInfo + { + this(int gmtOff, bool isDST, ubyte abbrInd) @safe pure + { + tt_gmtoff = gmtOff; + tt_isdst = isDST; + tt_abbrind = abbrInd; + } + + int tt_gmtoff; + bool tt_isdst; + ubyte tt_abbrind; + } + + + /+ + Struct used to hold information relating to $(D Transition) while + organizing the time zone information prior to putting it in its final + form. + +/ + struct TempTransition + { + this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) @safe pure + { + this.timeT = timeT; + this.ttInfo = ttInfo; + this.ttype = ttype; + } + + long timeT; + immutable (TTInfo)* ttInfo; + TransitionType* ttype; + } + + + /+ + Struct used to hold information relating to $(D Transition) and + $(D TTInfo) while organizing the time zone information prior to putting + it in its final form. + +/ + struct TransitionType + { + this(bool isStd, bool inUTC) @safe pure + { + this.isStd = isStd; + this.inUTC = inUTC; + } + + // Whether the transition is in std time (as opposed to wall clock time). + bool isStd; + + // Whether the transition is in UTC (as opposed to local time). + bool inUTC; + } + + + /+ + Reads an int from a TZ file. + +/ + static T readVal(T)(ref File tzFile) @trusted + if ((isIntegral!T || isSomeChar!T) || is(Unqual!T == bool)) + { + import std.bitmanip : bigEndianToNative; + T[1] buff; + + _enforceValidTZFile(!tzFile.eof); + tzFile.rawRead(buff); + + return bigEndianToNative!T(cast(ubyte[T.sizeof]) buff); + } + + /+ + Reads an array of values from a TZ file. + +/ + static T readVal(T)(ref File tzFile, size_t length) @trusted + if (isArray!T) + { + auto buff = new T(length); + + _enforceValidTZFile(!tzFile.eof); + tzFile.rawRead(buff); + + return buff; + } + + + /+ + Reads a $(D TempTTInfo) from a TZ file. + +/ + static T readVal(T)(ref File tzFile) @safe + if (is(T == TempTTInfo)) + { + return TempTTInfo(readVal!int(tzFile), + readVal!bool(tzFile), + readVal!ubyte(tzFile)); + } + + + /+ + Throws: + $(REF DateTimeException,std,datetime,date) if $(D result) is false. + +/ + static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure + { + if (!result) + throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); + } + + + int calculateLeapSeconds(long stdTime) @safe const pure nothrow + { + if (_leapSeconds.empty) + return 0; + + immutable unixTime = stdTimeToUnixTime(stdTime); + + if (_leapSeconds.front.timeT >= unixTime) + return 0; + + immutable found = countUntil!"b < a.timeT"(_leapSeconds, unixTime); + + if (found == -1) + return _leapSeconds.back.total; + + immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; + + return leapSecond.total; + } + + + this(immutable Transition[] transitions, + immutable LeapSecond[] leapSeconds, + string name, + string stdName, + string dstName, + bool hasDST) @safe immutable pure + { + if (dstName.empty && !stdName.empty) + dstName = stdName; + else if (stdName.empty && !dstName.empty) + stdName = dstName; + + super(name, stdName, dstName); + + if (!transitions.empty) + { + foreach (i, transition; transitions[0 .. $-1]) + _enforceValidTZFile(transition.timeT < transitions[i + 1].timeT); + } + + foreach (i, leapSecond; leapSeconds) + _enforceValidTZFile(i == leapSeconds.length - 1 || leapSecond.timeT < leapSeconds[i + 1].timeT); + + _transitions = transitions; + _leapSeconds = leapSeconds; + _hasDST = hasDST; + } + + // Android concatenates the usual timezone directories into a single file, + // tzdata, along with an index to jump to each timezone's offset. In older + // versions of Android, the index was stored in a separate file, zoneinfo.idx, + // whereas now it's stored at the beginning of tzdata. + version(Android) + { + // Keep track of whether there's a separate index, zoneinfo.idx. Only + // check this after calling tzdataIndex, as it's initialized there. + static shared bool separate_index; + + // Extracts the name of each time zone and the offset where its data is + // located in the tzdata file from the index and caches it for later. + static const(uint[string]) tzdataIndex(string tzDir) + { + import std.concurrency : initOnce; + + static __gshared uint[string] _tzIndex; + + // _tzIndex is initialized once and then shared across all threads. + initOnce!_tzIndex( + { + import std.conv : to; + import std.format : format; + import std.path : asNormalizedPath, chainPath; + + enum indexEntrySize = 52; + const combinedFile = asNormalizedPath(chainPath(tzDir, "tzdata")).to!string; + const indexFile = asNormalizedPath(chainPath(tzDir, "zoneinfo.idx")).to!string; + File tzFile; + uint indexEntries, dataOffset; + uint[string] initIndex; + + // Check for the combined file tzdata, which stores the index + // and the time zone data together. + if (combinedFile.exists() && combinedFile.isFile) + { + tzFile = File(combinedFile); + _enforceValidTZFile(readVal!(char[])(tzFile, 6) == "tzdata"); + auto tzDataVersion = readVal!(char[])(tzFile, 6); + _enforceValidTZFile(tzDataVersion[5] == '\0'); + + uint indexOffset = readVal!uint(tzFile); + dataOffset = readVal!uint(tzFile); + readVal!uint(tzFile); + + indexEntries = (dataOffset - indexOffset) / indexEntrySize; + separate_index = false; + } + else if (indexFile.exists() && indexFile.isFile) + { + tzFile = File(indexFile); + indexEntries = to!uint(tzFile.size/indexEntrySize); + separate_index = true; + } + else + { + throw new DateTimeException(format("Both timezone files %s and %s do not exist.", + combinedFile, indexFile)); + } + + foreach (_; 0 .. indexEntries) + { + string tzName = to!string(readVal!(char[])(tzFile, 40).ptr); + uint tzOffset = readVal!uint(tzFile); + readVal!(uint[])(tzFile, 2); + initIndex[tzName] = dataOffset + tzOffset; + } + initIndex.rehash; + return initIndex; + }()); + return _tzIndex; + } + } + + // List of times when the utc offset changes. + immutable Transition[] _transitions; + + // List of leap second occurrences. + immutable LeapSecond[] _leapSeconds; + + // Whether DST is in effect for this time zone at any point in time. + immutable bool _hasDST; +} + + +version(StdDdoc) +{ + /++ + $(BLUE This class is Windows-Only.) + + Represents a time zone from the Windows registry. Unfortunately, Windows + does not use the TZ Database. To use the TZ Database, use + $(LREF PosixTimeZone) (which reads its information from the TZ Database + files on disk) on Windows by providing the TZ Database files and telling + $(D PosixTimeZone.getTimeZone) where the directory holding them is. + + The TZ Database files and Windows' time zone information frequently + do not match. Windows has many errors with regards to when DST switches + occur (especially for historical dates). Also, the TZ Database files + include far more time zones than Windows does. So, for accurate + time zone information, use the TZ Database files with + $(LREF PosixTimeZone) rather than $(D WindowsTimeZone). However, because + $(D WindowsTimeZone) uses Windows system calls to deal with the time, + it's far more likely to match the behavior of other Windows programs. + Be aware of the differences when selecting a method. + + $(D WindowsTimeZone) does not exist on Posix systems. + + To get a $(D WindowsTimeZone), either call + $(D WindowsTimeZone.getTimeZone) or call $(D TimeZone.getTimeZone) + (which will give a $(LREF PosixTimeZone) on Posix systems and a + $(D WindowsTimeZone) on Windows systems). + + See_Also: + $(HTTP www.iana.org/time-zones, Home of the TZ Database files) + +/ + final class WindowsTimeZone : TimeZone + { + public: + + /++ + Whether this time zone has Daylight Savings Time at any point in + time. Note that for some time zone types it may not have DST for + current dates but will still return true for $(D hasDST) because the + time zone did at some point have DST. + +/ + @property override bool hasDST() @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and returns whether DST is in + effect in this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this + time zone. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and converts it to this time + zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time + zone's time. + +/ + override long utcToTZ(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, + 1 A.D. in this time zone's time and converts it to UTC (i.e. std + time). + + Params: + adjTime = The time in this time zone that needs to be adjusted + to UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow; + + + /++ + Returns a $(LREF TimeZone) with the given name per the Windows time + zone names. The time zone information is fetched from the Windows + registry. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List + of Time Zones) + + Params: + name = The TZ Database name of the desired time zone. + + Throws: + $(REF DateTimeException,std,datetime,date) if the given time + zone could not be found. + + Example: + -------------------- + auto tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); + -------------------- + +/ + static immutable(WindowsTimeZone) getTimeZone(string name) @safe; + + + /++ + Returns a list of the names of the time zones installed on the + system. The list returned by WindowsTimeZone contains the Windows + TZ names, not the TZ Database names. However, + $(D TimeZone.getinstalledTZNames) will return the TZ Database names + which are equivalent to the Windows TZ names. + +/ + static string[] getInstalledTZNames() @safe; + + private: + + version(Windows) + {} + else + alias TIME_ZONE_INFORMATION = void*; + + static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow; + static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow; + static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow; + + this() immutable pure + { + super("", "", ""); + } + } + +} +else version(Windows) +{ + final class WindowsTimeZone : TimeZone + { + import std.algorithm.sorting : sort; + import std.array : appender; + import std.conv : to; + import std.format : format; + + public: + + @property override bool hasDST() @safe const nothrow + { + return _tzInfo.DaylightDate.wMonth != 0; + } + + + override bool dstInEffect(long stdTime) @safe const nothrow + { + return _dstInEffect(&_tzInfo, stdTime); + } + + + override long utcToTZ(long stdTime) @safe const nothrow + { + return _utcToTZ(&_tzInfo, stdTime, hasDST); + } + + + override long tzToUTC(long adjTime) @safe const nothrow + { + return _tzToUTC(&_tzInfo, adjTime, hasDST); + } + + + static immutable(WindowsTimeZone) getTimeZone(string name) @trusted + { + import std.utf : toUTF16; + + scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); + + foreach (tzKeyName; baseKey.keyNames) + { + if (tzKeyName != name) + continue; + + scope tzKey = baseKey.getKey(tzKeyName); + + scope stdVal = tzKey.getValue("Std"); + auto stdName = stdVal.value_SZ; + + scope dstVal = tzKey.getValue("Dlt"); + auto dstName = dstVal.value_SZ; + + scope tziVal = tzKey.getValue("TZI"); + auto binVal = tziVal.value_BINARY; + assert(binVal.length == REG_TZI_FORMAT.sizeof); + auto tziFmt = cast(REG_TZI_FORMAT*) binVal.ptr; + + TIME_ZONE_INFORMATION tzInfo; + + auto wstdName = toUTF16(stdName); + auto wdstName = toUTF16(dstName); + auto wstdNameLen = wstdName.length > 32 ? 32 : wstdName.length; + auto wdstNameLen = wdstName.length > 32 ? 32 : wdstName.length; + + tzInfo.Bias = tziFmt.Bias; + tzInfo.StandardName[0 .. wstdNameLen] = wstdName[0 .. wstdNameLen]; + tzInfo.StandardName[wstdNameLen .. $] = '\0'; + tzInfo.StandardDate = tziFmt.StandardDate; + tzInfo.StandardBias = tziFmt.StandardBias; + tzInfo.DaylightName[0 .. wdstNameLen] = wdstName[0 .. wdstNameLen]; + tzInfo.DaylightName[wdstNameLen .. $] = '\0'; + tzInfo.DaylightDate = tziFmt.DaylightDate; + tzInfo.DaylightBias = tziFmt.DaylightBias; + + return new immutable WindowsTimeZone(name, tzInfo); + } + throw new DateTimeException(format("Failed to find time zone: %s", name)); + } + + static string[] getInstalledTZNames() @trusted + { + auto timezones = appender!(string[])(); + + scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); + + foreach (tzKeyName; baseKey.keyNames) + timezones.put(tzKeyName); + sort(timezones.data); + + return timezones.data; + } + + @safe unittest + { + import std.exception : assertNotThrown; + import std.stdio : writefln; + static void testWTZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + + WindowsTimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + + foreach (tzName; tzNames) + assertNotThrown!DateTimeException(testWTZSuccess(tzName)); + } + + + private: + + static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) @trusted nothrow + { + try + { + if (tzInfo.DaylightDate.wMonth == 0) + return false; + + auto utcDateTime = cast(DateTime) SysTime(stdTime, UTC()); + + //The limits of what SystemTimeToTzSpecificLocalTime will accept. + if (utcDateTime.year < 1601) + { + if (utcDateTime.month == Month.feb && utcDateTime.day == 29) + utcDateTime.day = 28; + utcDateTime.year = 1601; + } + else if (utcDateTime.year > 30_827) + { + if (utcDateTime.month == Month.feb && utcDateTime.day == 29) + utcDateTime.day = 28; + utcDateTime.year = 30_827; + } + + //SystemTimeToTzSpecificLocalTime doesn't act correctly at the + //beginning or end of the year (bleh). Unless some bizarre time + //zone changes DST on January 1st or December 31st, this should + //fix the problem. + if (utcDateTime.month == Month.jan) + { + if (utcDateTime.day == 1) + utcDateTime.day = 2; + } + else if (utcDateTime.month == Month.dec && utcDateTime.day == 31) + utcDateTime.day = 30; + + SYSTEMTIME utcTime = void; + SYSTEMTIME otherTime = void; + + utcTime.wYear = utcDateTime.year; + utcTime.wMonth = utcDateTime.month; + utcTime.wDay = utcDateTime.day; + utcTime.wHour = utcDateTime.hour; + utcTime.wMinute = utcDateTime.minute; + utcTime.wSecond = utcDateTime.second; + utcTime.wMilliseconds = 0; + + immutable result = SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*) tzInfo, + &utcTime, + &otherTime); + assert(result); + + immutable otherDateTime = DateTime(otherTime.wYear, + otherTime.wMonth, + otherTime.wDay, + otherTime.wHour, + otherTime.wMinute, + otherTime.wSecond); + immutable diff = utcDateTime - otherDateTime; + immutable minutes = diff.total!"minutes" - tzInfo.Bias; + + if (minutes == tzInfo.DaylightBias) + return true; + + assert(minutes == tzInfo.StandardBias); + + return false; + } + catch (Exception e) + assert(0, "DateTime's constructor threw."); + } + + @system unittest + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + foreach (year; [1600, 1601, 30_827, 30_828]) + WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(year, 1, 1)).stdTime); + } + + + static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) @safe nothrow + { + if (hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) + return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); + + return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); + } + + + static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) @trusted nothrow + { + if (hasDST) + { + try + { + bool dstInEffectForLocalDateTime(DateTime localDateTime) + { + // The limits of what SystemTimeToTzSpecificLocalTime will accept. + if (localDateTime.year < 1601) + { + if (localDateTime.month == Month.feb && localDateTime.day == 29) + localDateTime.day = 28; + + localDateTime.year = 1601; + } + else if (localDateTime.year > 30_827) + { + if (localDateTime.month == Month.feb && localDateTime.day == 29) + localDateTime.day = 28; + + localDateTime.year = 30_827; + } + + // SystemTimeToTzSpecificLocalTime doesn't act correctly at the + // beginning or end of the year (bleh). Unless some bizarre time + // zone changes DST on January 1st or December 31st, this should + // fix the problem. + if (localDateTime.month == Month.jan) + { + if (localDateTime.day == 1) + localDateTime.day = 2; + } + else if (localDateTime.month == Month.dec && localDateTime.day == 31) + localDateTime.day = 30; + + SYSTEMTIME utcTime = void; + SYSTEMTIME localTime = void; + + localTime.wYear = localDateTime.year; + localTime.wMonth = localDateTime.month; + localTime.wDay = localDateTime.day; + localTime.wHour = localDateTime.hour; + localTime.wMinute = localDateTime.minute; + localTime.wSecond = localDateTime.second; + localTime.wMilliseconds = 0; + + immutable result = TzSpecificLocalTimeToSystemTime(cast(TIME_ZONE_INFORMATION*) tzInfo, + &localTime, + &utcTime); + assert(result); + + immutable utcDateTime = DateTime(utcTime.wYear, + utcTime.wMonth, + utcTime.wDay, + utcTime.wHour, + utcTime.wMinute, + utcTime.wSecond); + + immutable diff = localDateTime - utcDateTime; + immutable minutes = -tzInfo.Bias - diff.total!"minutes"; + + if (minutes == tzInfo.DaylightBias) + return true; + + assert(minutes == tzInfo.StandardBias); + + return false; + } + + auto localDateTime = cast(DateTime) SysTime(adjTime, UTC()); + auto localDateTimeBefore = localDateTime - dur!"hours"(1); + auto localDateTimeAfter = localDateTime + dur!"hours"(1); + + auto dstInEffectNow = dstInEffectForLocalDateTime(localDateTime); + auto dstInEffectBefore = dstInEffectForLocalDateTime(localDateTimeBefore); + auto dstInEffectAfter = dstInEffectForLocalDateTime(localDateTimeAfter); + + bool isDST; + + if (dstInEffectBefore && dstInEffectNow && dstInEffectAfter) + isDST = true; + else if (!dstInEffectBefore && !dstInEffectNow && !dstInEffectAfter) + isDST = false; + else if (!dstInEffectBefore && dstInEffectAfter) + isDST = false; + else if (dstInEffectBefore && !dstInEffectAfter) + isDST = dstInEffectNow; + else + assert(0, "Bad Logic."); + + if (isDST) + return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); + } + catch (Exception e) + assert(0, "SysTime's constructor threw."); + } + + return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); + } + + + this(string name, TIME_ZONE_INFORMATION tzInfo) @trusted immutable pure + { + super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); + _tzInfo = tzInfo; + } + + + TIME_ZONE_INFORMATION _tzInfo; + } +} + + +version(StdDdoc) +{ + /++ + $(BLUE This function is Posix-Only.) + + Sets the local time zone on Posix systems with the TZ + Database name by setting the TZ environment variable. + + Unfortunately, there is no way to do it on Windows using the TZ + Database name, so this function only exists on Posix systems. + +/ + void setTZEnvVar(string tzDatabaseName) @safe nothrow; + + + /++ + $(BLUE This function is Posix-Only.) + + Clears the TZ environment variable. + +/ + void clearTZEnvVar() @safe nothrow; +} +else version(Posix) +{ + void setTZEnvVar(string tzDatabaseName) @trusted nothrow + { + import core.stdc.time : tzset; + import core.sys.posix.stdlib : setenv; + import std.internal.cstring : tempCString; + import std.path : asNormalizedPath, chainPath; + + version(Android) + auto value = asNormalizedPath(tzDatabaseName); + else + auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName)); + setenv("TZ", value.tempCString(), 1); + tzset(); + } + + + void clearTZEnvVar() @trusted nothrow + { + import core.stdc.time : tzset; + import core.sys.posix.stdlib : unsetenv; + + unsetenv("TZ"); + tzset(); + } +} + + +/++ + Provides the conversions between the IANA time zone database time zone names + (which POSIX systems use) and the time zone names that Windows uses. + + Windows uses a different set of time zone names than the IANA time zone + database does, and how they correspond to one another changes over time + (particularly when Microsoft updates Windows). + $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) + provides the current conversions (which may or may not match up with what's + on a particular Windows box depending on how up-to-date it is), and + parseTZConversions reads in those conversions from windowsZones.xml so that + a D program can use those conversions. + + However, it should be noted that the time zone information on Windows is + frequently less accurate than that in the IANA time zone database, and if + someone really wants accurate time zone information, they should use the + IANA time zone database files with $(LREF PosixTimeZone) on Windows rather + than $(LREF WindowsTimeZone), whereas $(LREF WindowsTimeZone) makes more + sense when trying to match what Windows will think the time is in a specific + time zone. + + Also, the IANA time zone database has a lot more time zones than Windows + does. + + Params: + windowsZonesXMLFileText The text from + $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) + + Throws: + Exception if there is an error while parsing the given XML. + +-------------------- + // Parse the conversions from a local file. + auto text = std.file.readText("path/to/windowsZones.xml"); + auto conversions = parseTZConversions(text); + + // Alternatively, grab the XML file from the web at runtime + // and parse it so that it's guaranteed to be up-to-date, though + // that has the downside that the code needs to worry about the + // site being down or unicode.org changing the URL. + auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"; + auto conversions2 = parseTZConversions(std.net.curl.get(url)); +-------------------- + +/ +struct TZConversions +{ + /++ + The key is the Windows time zone name, and the value is a list of + IANA TZ database names which are close (currently only ever one, but + it allows for multiple in case it's ever necessary). + +/ + string[][string] toWindows; + + /++ + The key is the IANA time zone database name, and the value is a list of + Windows time zone names which are close (usually only one, but it could + be multiple). + +/ + string[][string] fromWindows; +} + +/++ ditto +/ +TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure +{ + // This is a bit hacky, since it doesn't properly read XML, but it avoids + // needing to pull in std.xml (which we're theoretically replacing at some + // point anyway). + import std.algorithm.iteration : uniq; + import std.algorithm.searching : find; + import std.algorithm.sorting : sort; + import std.array : array, split; + import std.string : lineSplitter; + + string[][string] win2Nix; + string[][string] nix2Win; + + immutable f1 = ` + + line = line.find(f1); + if (line.empty) + continue; + line = line[f1.length .. $]; + auto next = line.find('"'); + enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); + auto win = line[0 .. $ - next.length]; + line = next.find(f2); + enforce(!line.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); + line = line[f2.length .. $]; + next = line.find('"'); + enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); + auto nixes = line[0 .. $ - next.length].split(); + + if (auto n = win in win2Nix) + *n ~= nixes; + else + win2Nix[win] = nixes; + + foreach (nix; nixes) + { + if (auto w = nix in nix2Win) + *w ~= win; + else + nix2Win[nix] = [win]; + } + } + + foreach (key, ref value; nix2Win) + value = value.sort().uniq().array(); + foreach (key, ref value; win2Nix) + value = value.sort().uniq().array(); + + return TZConversions(nix2Win, win2Nix); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : uniq; + import std.algorithm.sorting : isSorted; + + // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml + auto sampleFileText = +` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + + auto tzConversions = parseTZConversions(sampleFileText); + assert(tzConversions.toWindows.length == 15); + assert(tzConversions.toWindows["America/Anchorage"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Juneau"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Nome"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Sitka"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Yakutat"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["Etc/GMT+10"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Etc/GMT+11"] == ["UTC-11"]); + assert(tzConversions.toWindows["Etc/GMT+12"] == ["Dateline Standard Time"]); + assert(tzConversions.toWindows["Pacific/Honolulu"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Pacific/Johnston"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Pacific/Midway"] == ["UTC-11"]); + assert(tzConversions.toWindows["Pacific/Niue"] == ["UTC-11"]); + assert(tzConversions.toWindows["Pacific/Pago_Pago"] == ["UTC-11"]); + assert(tzConversions.toWindows["Pacific/Rarotonga"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Pacific/Tahiti"] == ["Hawaiian Standard Time"]); + + assert(tzConversions.fromWindows.length == 4); + assert(tzConversions.fromWindows["Alaskan Standard Time"] == + ["America/Anchorage", "America/Juneau", "America/Nome", "America/Sitka", "America/Yakutat"]); + assert(tzConversions.fromWindows["Dateline Standard Time"] == ["Etc/GMT+12"]); + assert(tzConversions.fromWindows["Hawaiian Standard Time"] == + ["Etc/GMT+10", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Rarotonga", "Pacific/Tahiti"]); + assert(tzConversions.fromWindows["UTC-11"] == + ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"]); + + foreach (key, value; tzConversions.fromWindows) + { + assert(value.isSorted, key); + assert(equal(value.uniq(), value), key); + } +} + + +// @@@DEPRECATED_2017-07@@@ +/++ + $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes + their time zones too often for us to compile the conversions into + Phobos and have them be properly up-to-date. + tzDatabaseNameToWindowsTZName will be removed in July 2017.) + + Converts the given TZ Database name to the corresponding Windows time zone + name. + + Note that in a few cases, a TZ Dabatase name corresponds to two different + Windows time zone names. So, while in most cases converting from one to the + other and back again will result in the same time zone name started + with, in a few case, it'll get a different name. + + Also, there are far more TZ Database names than Windows time zones, so some + of the more exotic TZ Database names don't have corresponding Windows time + zone names. + + Returns null if the given time zone name cannot be converted. + + See_Also: + $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, + Windows <-> TZ Database Name Conversion Table) + + Params: + tzName = The TZ Database name to convert. + +/ +deprecated("Use parseTZConversions instead") +string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc +{ + switch (tzName) + { + case "Africa/Abidjan": return "Greenwich Standard Time"; + case "Africa/Accra": return "Greenwich Standard Time"; + case "Africa/Addis_Ababa": return "E. Africa Standard Time"; + case "Africa/Algiers": return "W. Central Africa Standard Time"; + case "Africa/Asmera": return "E. Africa Standard Time"; + case "Africa/Bamako": return "Greenwich Standard Time"; + case "Africa/Bangui": return "W. Central Africa Standard Time"; + case "Africa/Banjul": return "Greenwich Standard Time"; + case "Africa/Bissau": return "Greenwich Standard Time"; + case "Africa/Blantyre": return "South Africa Standard Time"; + case "Africa/Brazzaville": return "W. Central Africa Standard Time"; + case "Africa/Bujumbura": return "South Africa Standard Time"; + case "Africa/Cairo": return "Egypt Standard Time"; + case "Africa/Casablanca": return "Morocco Standard Time"; + case "Africa/Ceuta": return "Romance Standard Time"; + case "Africa/Conakry": return "Greenwich Standard Time"; + case "Africa/Dakar": return "Greenwich Standard Time"; + case "Africa/Dar_es_Salaam": return "E. Africa Standard Time"; + case "Africa/Djibouti": return "E. Africa Standard Time"; + case "Africa/Douala": return "W. Central Africa Standard Time"; + case "Africa/El_Aaiun": return "Morocco Standard Time"; + case "Africa/Freetown": return "Greenwich Standard Time"; + case "Africa/Gaborone": return "South Africa Standard Time"; + case "Africa/Harare": return "South Africa Standard Time"; + case "Africa/Johannesburg": return "South Africa Standard Time"; + case "Africa/Juba": return "E. Africa Standard Time"; + case "Africa/Kampala": return "E. Africa Standard Time"; + case "Africa/Khartoum": return "E. Africa Standard Time"; + case "Africa/Kigali": return "South Africa Standard Time"; + case "Africa/Kinshasa": return "W. Central Africa Standard Time"; + case "Africa/Lagos": return "W. Central Africa Standard Time"; + case "Africa/Libreville": return "W. Central Africa Standard Time"; + case "Africa/Lome": return "Greenwich Standard Time"; + case "Africa/Luanda": return "W. Central Africa Standard Time"; + case "Africa/Lubumbashi": return "South Africa Standard Time"; + case "Africa/Lusaka": return "South Africa Standard Time"; + case "Africa/Malabo": return "W. Central Africa Standard Time"; + case "Africa/Maputo": return "South Africa Standard Time"; + case "Africa/Maseru": return "South Africa Standard Time"; + case "Africa/Mbabane": return "South Africa Standard Time"; + case "Africa/Mogadishu": return "E. Africa Standard Time"; + case "Africa/Monrovia": return "Greenwich Standard Time"; + case "Africa/Nairobi": return "E. Africa Standard Time"; + case "Africa/Ndjamena": return "W. Central Africa Standard Time"; + case "Africa/Niamey": return "W. Central Africa Standard Time"; + case "Africa/Nouakchott": return "Greenwich Standard Time"; + case "Africa/Ouagadougou": return "Greenwich Standard Time"; + case "Africa/Porto-Novo": return "W. Central Africa Standard Time"; + case "Africa/Sao_Tome": return "Greenwich Standard Time"; + case "Africa/Tripoli": return "Libya Standard Time"; + case "Africa/Tunis": return "W. Central Africa Standard Time"; + case "Africa/Windhoek": return "Namibia Standard Time"; + case "America/Adak": return "Aleutian Standard Time"; + case "America/Anchorage": return "Alaskan Standard Time"; + case "America/Anguilla": return "SA Western Standard Time"; + case "America/Antigua": return "SA Western Standard Time"; + case "America/Araguaina": return "SA Eastern Standard Time"; + case "America/Argentina/La_Rioja": return "Argentina Standard Time"; + case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time"; + case "America/Argentina/Salta": return "Argentina Standard Time"; + case "America/Argentina/San_Juan": return "Argentina Standard Time"; + case "America/Argentina/San_Luis": return "Argentina Standard Time"; + case "America/Argentina/Tucuman": return "Argentina Standard Time"; + case "America/Argentina/Ushuaia": return "Argentina Standard Time"; + case "America/Arguaina": return "Tocantins Standard Time"; + case "America/Aruba": return "SA Western Standard Time"; + case "America/Asuncion": return "Paraguay Standard Time"; + case "America/Bahia": return "Bahia Standard Time"; + case "America/Bahia_Banderas": return "Central Standard Time (Mexico)"; + case "America/Barbados": return "SA Western Standard Time"; + case "America/Belem": return "SA Eastern Standard Time"; + case "America/Belize": return "Central America Standard Time"; + case "America/Blanc-Sablon": return "SA Western Standard Time"; + case "America/Boa_Vista": return "SA Western Standard Time"; + case "America/Bogota": return "SA Pacific Standard Time"; + case "America/Boise": return "Mountain Standard Time"; + case "America/Buenos_Aires": return "Argentina Standard Time"; + case "America/Cambridge_Bay": return "Mountain Standard Time"; + case "America/Campo_Grande": return "Central Brazilian Standard Time"; + case "America/Cancun": return "Eastern Standard Time (Mexico)"; + case "America/Caracas": return "Venezuela Standard Time"; + case "America/Catamarca": return "Argentina Standard Time"; + case "America/Cayenne": return "SA Eastern Standard Time"; + case "America/Cayman": return "SA Pacific Standard Time"; + case "America/Chicago": return "Central Standard Time"; + case "America/Chihuahua": return "Mountain Standard Time (Mexico)"; + case "America/Coral_Harbour": return "SA Pacific Standard Time"; + case "America/Cordoba": return "Argentina Standard Time"; + case "America/Costa_Rica": return "Central America Standard Time"; + case "America/Creston": return "US Mountain Standard Time"; + case "America/Cuiaba": return "Central Brazilian Standard Time"; + case "America/Curacao": return "SA Western Standard Time"; + case "America/Danmarkshavn": return "UTC"; + case "America/Dawson": return "Pacific Standard Time"; + case "America/Dawson_Creek": return "US Mountain Standard Time"; + case "America/Denver": return "Mountain Standard Time"; + case "America/Detroit": return "Eastern Standard Time"; + case "America/Dominica": return "SA Western Standard Time"; + case "America/Edmonton": return "Mountain Standard Time"; + case "America/Eirunepe": return "SA Pacific Standard Time"; + case "America/El_Salvador": return "Central America Standard Time"; + case "America/Fortaleza": return "SA Eastern Standard Time"; + case "America/Glace_Bay": return "Atlantic Standard Time"; + case "America/Godthab": return "Greenland Standard Time"; + case "America/Goose_Bay": return "Atlantic Standard Time"; + case "America/Grand_Turk": return "Turks And Caicos Standard Time"; + case "America/Grenada": return "SA Western Standard Time"; + case "America/Guadeloupe": return "SA Western Standard Time"; + case "America/Guatemala": return "Central America Standard Time"; + case "America/Guayaquil": return "SA Pacific Standard Time"; + case "America/Guyana": return "SA Western Standard Time"; + case "America/Halifax": return "Atlantic Standard Time"; + case "America/Havana": return "Cuba Standard Time"; + case "America/Hermosillo": return "US Mountain Standard Time"; + case "America/Indiana/Knox": return "Central Standard Time"; + case "America/Indiana/Marengo": return "US Eastern Standard Time"; + case "America/Indiana/Petersburg": return "Eastern Standard Time"; + case "America/Indiana/Tell_City": return "Central Standard Time"; + case "America/Indiana/Vevay": return "US Eastern Standard Time"; + case "America/Indiana/Vincennes": return "Eastern Standard Time"; + case "America/Indiana/Winamac": return "Eastern Standard Time"; + case "America/Indianapolis": return "US Eastern Standard Time"; + case "America/Inuvik": return "Mountain Standard Time"; + case "America/Iqaluit": return "Eastern Standard Time"; + case "America/Jamaica": return "SA Pacific Standard Time"; + case "America/Jujuy": return "Argentina Standard Time"; + case "America/Juneau": return "Alaskan Standard Time"; + case "America/Kentucky/Monticello": return "Eastern Standard Time"; + case "America/Kralendijk": return "SA Western Standard Time"; + case "America/La_Paz": return "SA Western Standard Time"; + case "America/Lima": return "SA Pacific Standard Time"; + case "America/Los_Angeles": return "Pacific Standard Time"; + case "America/Louisville": return "Eastern Standard Time"; + case "America/Lower_Princes": return "SA Western Standard Time"; + case "America/Maceio": return "SA Eastern Standard Time"; + case "America/Managua": return "Central America Standard Time"; + case "America/Manaus": return "SA Western Standard Time"; + case "America/Marigot": return "SA Western Standard Time"; + case "America/Martinique": return "SA Western Standard Time"; + case "America/Matamoros": return "Central Standard Time"; + case "America/Mazatlan": return "Mountain Standard Time (Mexico)"; + case "America/Mendoza": return "Argentina Standard Time"; + case "America/Menominee": return "Central Standard Time"; + case "America/Merida": return "Central Standard Time (Mexico)"; + case "America/Mexico_City": return "Central Standard Time (Mexico)"; + case "America/Miquelon": return "Saint Pierre Standard Time"; + case "America/Moncton": return "Atlantic Standard Time"; + case "America/Monterrey": return "Central Standard Time (Mexico)"; + case "America/Montevideo": return "Montevideo Standard Time"; + case "America/Montreal": return "Eastern Standard Time"; + case "America/Montserrat": return "SA Western Standard Time"; + case "America/Nassau": return "Eastern Standard Time"; + case "America/New_York": return "Eastern Standard Time"; + case "America/Nipigon": return "Eastern Standard Time"; + case "America/Nome": return "Alaskan Standard Time"; + case "America/Noronha": return "UTC-02"; + case "America/North_Dakota/Beulah": return "Central Standard Time"; + case "America/North_Dakota/Center": return "Central Standard Time"; + case "America/North_Dakota/New_Salem": return "Central Standard Time"; + case "America/Ojinaga": return "Mountain Standard Time"; + case "America/Panama": return "SA Pacific Standard Time"; + case "America/Pangnirtung": return "Eastern Standard Time"; + case "America/Paramaribo": return "SA Eastern Standard Time"; + case "America/Phoenix": return "US Mountain Standard Time"; + case "America/Port-au-Prince": return "Haiti Standard Time"; + case "America/Port_of_Spain": return "SA Western Standard Time"; + case "America/Porto_Velho": return "SA Western Standard Time"; + case "America/Puerto_Rico": return "SA Western Standard Time"; + case "America/Rainy_River": return "Central Standard Time"; + case "America/Rankin_Inlet": return "Central Standard Time"; + case "America/Recife": return "SA Eastern Standard Time"; + case "America/Regina": return "Canada Central Standard Time"; + case "America/Resolute": return "Central Standard Time"; + case "America/Rio_Branco": return "SA Pacific Standard Time"; + case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)"; + case "America/Santarem": return "SA Eastern Standard Time"; + case "America/Santiago": return "Pacific SA Standard Time"; + case "America/Santo_Domingo": return "SA Western Standard Time"; + case "America/Sao_Paulo": return "E. South America Standard Time"; + case "America/Scoresbysund": return "Azores Standard Time"; + case "America/Sitka": return "Alaskan Standard Time"; + case "America/St_Barthelemy": return "SA Western Standard Time"; + case "America/St_Johns": return "Newfoundland Standard Time"; + case "America/St_Kitts": return "SA Western Standard Time"; + case "America/St_Lucia": return "SA Western Standard Time"; + case "America/St_Thomas": return "SA Western Standard Time"; + case "America/St_Vincent": return "SA Western Standard Time"; + case "America/Swift_Current": return "Canada Central Standard Time"; + case "America/Tegucigalpa": return "Central America Standard Time"; + case "America/Thule": return "Atlantic Standard Time"; + case "America/Thunder_Bay": return "Eastern Standard Time"; + case "America/Tijuana": return "Pacific Standard Time"; + case "America/Toronto": return "Eastern Standard Time"; + case "America/Tortola": return "SA Western Standard Time"; + case "America/Vancouver": return "Pacific Standard Time"; + case "America/Whitehorse": return "Pacific Standard Time"; + case "America/Winnipeg": return "Central Standard Time"; + case "America/Yakutat": return "Alaskan Standard Time"; + case "America/Yellowknife": return "Mountain Standard Time"; + case "Antarctica/Casey": return "W. Australia Standard Time"; + case "Antarctica/Davis": return "SE Asia Standard Time"; + case "Antarctica/DumontDUrville": return "West Pacific Standard Time"; + case "Antarctica/Macquarie": return "Central Pacific Standard Time"; + case "Antarctica/Mawson": return "West Asia Standard Time"; + case "Antarctica/McMurdo": return "New Zealand Standard Time"; + case "Antarctica/Palmer": return "Pacific SA Standard Time"; + case "Antarctica/Rothera": return "SA Eastern Standard Time"; + case "Antarctica/Syowa": return "E. Africa Standard Time"; + case "Antarctica/Vostok": return "Central Asia Standard Time"; + case "Arctic/Longyearbyen": return "W. Europe Standard Time"; + case "Asia/Aden": return "Arab Standard Time"; + case "Asia/Almaty": return "Central Asia Standard Time"; + case "Asia/Amman": return "Jordan Standard Time"; + case "Asia/Anadyr": return "Russia Time Zone 11"; + case "Asia/Aqtau": return "West Asia Standard Time"; + case "Asia/Aqtobe": return "West Asia Standard Time"; + case "Asia/Ashgabat": return "West Asia Standard Time"; + case "Asia/Baghdad": return "Arabic Standard Time"; + case "Asia/Bahrain": return "Arab Standard Time"; + case "Asia/Baku": return "Azerbaijan Standard Time"; + case "Asia/Bangkok": return "SE Asia Standard Time"; + case "Asia/Barnaul": return "Altai Standard Time"; + case "Asia/Beirut": return "Middle East Standard Time"; + case "Asia/Bishkek": return "Central Asia Standard Time"; + case "Asia/Brunei": return "Singapore Standard Time"; + case "Asia/Calcutta": return "India Standard Time"; + case "Asia/Chita": return "Transbaikal Standard Time"; + case "Asia/Choibalsan": return "Ulaanbaatar Standard Time"; + case "Asia/Colombo": return "Sri Lanka Standard Time"; + case "Asia/Damascus": return "Syria Standard Time"; + case "Asia/Dhaka": return "Bangladesh Standard Time"; + case "Asia/Dili": return "Tokyo Standard Time"; + case "Asia/Dubai": return "Arabian Standard Time"; + case "Asia/Dushanbe": return "West Asia Standard Time"; + case "Asia/Hebron": return "West Bank Standard Time"; + case "Asia/Hong_Kong": return "China Standard Time"; + case "Asia/Hovd": return "W. Mongolia Standard Time"; + case "Asia/Irkutsk": return "North Asia East Standard Time"; + case "Asia/Jakarta": return "SE Asia Standard Time"; + case "Asia/Jayapura": return "Tokyo Standard Time"; + case "Asia/Jerusalem": return "Israel Standard Time"; + case "Asia/Kabul": return "Afghanistan Standard Time"; + case "Asia/Kamchatka": return "Russia Time Zone 11"; + case "Asia/Karachi": return "Pakistan Standard Time"; + case "Asia/Katmandu": return "Nepal Standard Time"; + case "Asia/Khandyga": return "Yakutsk Standard Time"; + case "Asia/Krasnoyarsk": return "North Asia Standard Time"; + case "Asia/Kuala_Lumpur": return "Singapore Standard Time"; + case "Asia/Kuching": return "Singapore Standard Time"; + case "Asia/Kuwait": return "Arab Standard Time"; + case "Asia/Macau": return "China Standard Time"; + case "Asia/Magadan": return "Magadan Standard Time"; + case "Asia/Makassar": return "Singapore Standard Time"; + case "Asia/Manila": return "Singapore Standard Time"; + case "Asia/Muscat": return "Arabian Standard Time"; + case "Asia/Nicosia": return "GTB Standard Time"; + case "Asia/Novokuznetsk": return "North Asia Standard Time"; + case "Asia/Novosibirsk": return "N. Central Asia Standard Time"; + case "Asia/Omsk": return "N. Central Asia Standard Time"; + case "Asia/Oral": return "West Asia Standard Time"; + case "Asia/Phnom_Penh": return "SE Asia Standard Time"; + case "Asia/Pontianak": return "SE Asia Standard Time"; + case "Asia/Pyongyang": return "North Korea Standard Time"; + case "Asia/Qatar": return "Arab Standard Time"; + case "Asia/Qyzylorda": return "Central Asia Standard Time"; + case "Asia/Rangoon": return "Myanmar Standard Time"; + case "Asia/Riyadh": return "Arab Standard Time"; + case "Asia/Saigon": return "SE Asia Standard Time"; + case "Asia/Sakhalin": return "Sakhalin Standard Time"; + case "Asia/Samarkand": return "West Asia Standard Time"; + case "Asia/Seoul": return "Korea Standard Time"; + case "Asia/Shanghai": return "China Standard Time"; + case "Asia/Singapore": return "Singapore Standard Time"; + case "Asia/Srednekolymsk": return "Russia Time Zone 10"; + case "Asia/Taipei": return "Taipei Standard Time"; + case "Asia/Tashkent": return "West Asia Standard Time"; + case "Asia/Tbilisi": return "Georgian Standard Time"; + case "Asia/Tehran": return "Iran Standard Time"; + case "Asia/Thimphu": return "Bangladesh Standard Time"; + case "Asia/Tokyo": return "Tokyo Standard Time"; + case "Asia/Tomsk": return "Tomsk Standard Time"; + case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time"; + case "Asia/Urumqi": return "Central Asia Standard Time"; + case "Asia/Ust-Nera": return "Vladivostok Standard Time"; + case "Asia/Vientiane": return "SE Asia Standard Time"; + case "Asia/Vladivostok": return "Vladivostok Standard Time"; + case "Asia/Yakutsk": return "Yakutsk Standard Time"; + case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time"; + case "Asia/Yerevan": return "Caucasus Standard Time"; + case "Atlantic/Azores": return "Azores Standard Time"; + case "Atlantic/Bermuda": return "Atlantic Standard Time"; + case "Atlantic/Canary": return "GMT Standard Time"; + case "Atlantic/Cape_Verde": return "Cape Verde Standard Time"; + case "Atlantic/Faeroe": return "GMT Standard Time"; + case "Atlantic/Madeira": return "GMT Standard Time"; + case "Atlantic/Reykjavik": return "Greenwich Standard Time"; + case "Atlantic/South_Georgia": return "UTC-02"; + case "Atlantic/St_Helena": return "Greenwich Standard Time"; + case "Atlantic/Stanley": return "SA Eastern Standard Time"; + case "Australia/Adelaide": return "Cen. Australia Standard Time"; + case "Australia/Brisbane": return "E. Australia Standard Time"; + case "Australia/Broken_Hill": return "Cen. Australia Standard Time"; + case "Australia/Currie": return "Tasmania Standard Time"; + case "Australia/Darwin": return "AUS Central Standard Time"; + case "Australia/Eucla": return "Aus Central W. Standard Time"; + case "Australia/Hobart": return "Tasmania Standard Time"; + case "Australia/Lindeman": return "E. Australia Standard Time"; + case "Australia/Lord_Howe": return "Lord Howe Standard Time"; + case "Australia/Melbourne": return "AUS Eastern Standard Time"; + case "Australia/Perth": return "W. Australia Standard Time"; + case "Australia/Sydney": return "AUS Eastern Standard Time"; + case "CST6CDT": return "Central Standard Time"; + case "EST5EDT": return "Eastern Standard Time"; + case "Etc/GMT": return "UTC"; + case "Etc/GMT+1": return "Cape Verde Standard Time"; + case "Etc/GMT+10": return "Hawaiian Standard Time"; + case "Etc/GMT+11": return "UTC-11"; + case "Etc/GMT+12": return "Dateline Standard Time"; + case "Etc/GMT+2": return "UTC-02"; + case "Etc/GMT+3": return "SA Eastern Standard Time"; + case "Etc/GMT+4": return "SA Western Standard Time"; + case "Etc/GMT+5": return "SA Pacific Standard Time"; + case "Etc/GMT+6": return "Central America Standard Time"; + case "Etc/GMT+7": return "US Mountain Standard Time"; + case "Etc/GMT+8": return "UTC-08"; + case "Etc/GMT+9": return "UTC-09"; + case "Etc/GMT-1": return "W. Central Africa Standard Time"; + case "Etc/GMT-10": return "West Pacific Standard Time"; + case "Etc/GMT-11": return "Central Pacific Standard Time"; + case "Etc/GMT-12": return "UTC+12"; + case "Etc/GMT-13": return "Tonga Standard Time"; + case "Etc/GMT-14": return "Line Islands Standard Time"; + case "Etc/GMT-2": return "South Africa Standard Time"; + case "Etc/GMT-3": return "E. Africa Standard Time"; + case "Etc/GMT-4": return "Arabian Standard Time"; + case "Etc/GMT-5": return "West Asia Standard Time"; + case "Etc/GMT-6": return "Central Asia Standard Time"; + case "Etc/GMT-7": return "SE Asia Standard Time"; + case "Etc/GMT-8": return "Singapore Standard Time"; + case "Etc/GMT-9": return "Tokyo Standard Time"; + case "Europe/Amsterdam": return "W. Europe Standard Time"; + case "Europe/Andorra": return "W. Europe Standard Time"; + case "Europe/Astrakhan": return "Astrakhan Standard Time"; + case "Europe/Athens": return "GTB Standard Time"; + case "Europe/Belgrade": return "Central Europe Standard Time"; + case "Europe/Berlin": return "W. Europe Standard Time"; + case "Europe/Bratislava": return "Central Europe Standard Time"; + case "Europe/Brussels": return "Romance Standard Time"; + case "Europe/Bucharest": return "GTB Standard Time"; + case "Europe/Budapest": return "Central Europe Standard Time"; + case "Europe/Busingen": return "W. Europe Standard Time"; + case "Europe/Chisinau": return "GTB Standard Time"; + case "Europe/Copenhagen": return "Romance Standard Time"; + case "Europe/Dublin": return "GMT Standard Time"; + case "Europe/Gibraltar": return "W. Europe Standard Time"; + case "Europe/Guernsey": return "GMT Standard Time"; + case "Europe/Helsinki": return "FLE Standard Time"; + case "Europe/Isle_of_Man": return "GMT Standard Time"; + case "Europe/Istanbul": return "Turkey Standard Time"; + case "Europe/Jersey": return "GMT Standard Time"; + case "Europe/Kaliningrad": return "Kaliningrad Standard Time"; + case "Europe/Kiev": return "FLE Standard Time"; + case "Europe/Lisbon": return "GMT Standard Time"; + case "Europe/Ljubljana": return "Central Europe Standard Time"; + case "Europe/London": return "GMT Standard Time"; + case "Europe/Luxembourg": return "W. Europe Standard Time"; + case "Europe/Madrid": return "Romance Standard Time"; + case "Europe/Malta": return "W. Europe Standard Time"; + case "Europe/Mariehamn": return "FLE Standard Time"; + case "Europe/Minsk": return "Belarus Standard Time"; + case "Europe/Monaco": return "W. Europe Standard Time"; + case "Europe/Moscow": return "Russian Standard Time"; + case "Europe/Oslo": return "W. Europe Standard Time"; + case "Europe/Paris": return "Romance Standard Time"; + case "Europe/Podgorica": return "Central Europe Standard Time"; + case "Europe/Prague": return "Central Europe Standard Time"; + case "Europe/Riga": return "FLE Standard Time"; + case "Europe/Rome": return "W. Europe Standard Time"; + case "Europe/Samara": return "Russia Time Zone 3"; + case "Europe/San_Marino": return "W. Europe Standard Time"; + case "Europe/Sarajevo": return "Central European Standard Time"; + case "Europe/Simferopol": return "Russian Standard Time"; + case "Europe/Skopje": return "Central European Standard Time"; + case "Europe/Sofia": return "FLE Standard Time"; + case "Europe/Stockholm": return "W. Europe Standard Time"; + case "Europe/Tallinn": return "FLE Standard Time"; + case "Europe/Tirane": return "Central Europe Standard Time"; + case "Europe/Uzhgorod": return "FLE Standard Time"; + case "Europe/Vaduz": return "W. Europe Standard Time"; + case "Europe/Vatican": return "W. Europe Standard Time"; + case "Europe/Vienna": return "W. Europe Standard Time"; + case "Europe/Vilnius": return "FLE Standard Time"; + case "Europe/Volgograd": return "Russian Standard Time"; + case "Europe/Warsaw": return "Central European Standard Time"; + case "Europe/Zagreb": return "Central European Standard Time"; + case "Europe/Zaporozhye": return "FLE Standard Time"; + case "Europe/Zurich": return "W. Europe Standard Time"; + case "Indian/Antananarivo": return "E. Africa Standard Time"; + case "Indian/Chagos": return "Central Asia Standard Time"; + case "Indian/Christmas": return "SE Asia Standard Time"; + case "Indian/Cocos": return "Myanmar Standard Time"; + case "Indian/Comoro": return "E. Africa Standard Time"; + case "Indian/Kerguelen": return "West Asia Standard Time"; + case "Indian/Mahe": return "Mauritius Standard Time"; + case "Indian/Maldives": return "West Asia Standard Time"; + case "Indian/Mauritius": return "Mauritius Standard Time"; + case "Indian/Mayotte": return "E. Africa Standard Time"; + case "Indian/Reunion": return "Mauritius Standard Time"; + case "MST7MDT": return "Mountain Standard Time"; + case "PST8PDT": return "Pacific Standard Time"; + case "Pacific/Apia": return "Samoa Standard Time"; + case "Pacific/Auckland": return "New Zealand Standard Time"; + case "Pacific/Bougainville": return "Bougainville Standard Time"; + case "Pacific/Chatham": return "Chatham Islands Standard Time"; + case "Pacific/Easter": return "Easter Island Standard Time"; + case "Pacific/Efate": return "Central Pacific Standard Time"; + case "Pacific/Enderbury": return "Tonga Standard Time"; + case "Pacific/Fakaofo": return "Tonga Standard Time"; + case "Pacific/Fiji": return "Fiji Standard Time"; + case "Pacific/Funafuti": return "UTC+12"; + case "Pacific/Galapagos": return "Central America Standard Time"; + case "Pacific/Guadalcanal": return "Central Pacific Standard Time"; + case "Pacific/Guam": return "West Pacific Standard Time"; + case "Pacific/Honolulu": return "Hawaiian Standard Time"; + case "Pacific/Johnston": return "Hawaiian Standard Time"; + case "Pacific/Kiritimati": return "Line Islands Standard Time"; + case "Pacific/Kosrae": return "Central Pacific Standard Time"; + case "Pacific/Kwajalein": return "UTC+12"; + case "Pacific/Majuro": return "UTC+12"; + case "Pacific/Marquesas": return "Marquesas Standard Time"; + case "Pacific/Midway": return "UTC-11"; + case "Pacific/Nauru": return "UTC+12"; + case "Pacific/Niue": return "UTC-11"; + case "Pacific/Noumea": return "Central Pacific Standard Time"; + case "Pacific/Norfolk": return "Norfolk Standard Time"; + case "Pacific/Pago_Pago": return "UTC-11"; + case "Pacific/Palau": return "Tokyo Standard Time"; + case "Pacific/Ponape": return "Central Pacific Standard Time"; + case "Pacific/Port_Moresby": return "West Pacific Standard Time"; + case "Pacific/Rarotonga": return "Hawaiian Standard Time"; + case "Pacific/Saipan": return "West Pacific Standard Time"; + case "Pacific/Tahiti": return "Hawaiian Standard Time"; + case "Pacific/Tarawa": return "UTC+12"; + case "Pacific/Tongatapu": return "Tonga Standard Time"; + case "Pacific/Truk": return "West Pacific Standard Time"; + case "Pacific/Wake": return "UTC+12"; + case "Pacific/Wallis": return "UTC+12"; + default: return null; + } +} + +version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest +{ + import std.stdio : stderr; + + foreach (tzName; TimeZone.getInstalledTZNames()) + { + if (tzDatabaseNameToWindowsTZName(tzName) is null) + stderr.writeln("Missing TZName to Windows translation: ", tzName); + } +} + + +// @@@DEPRECATED_2017-07@@@ +/++ + $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes + their time zones too often for us to compile the conversions into + Phobos and have them be properly up-to-date. + windowsTZNameToTZDatabaseName will be removed in July 2017.) + + Converts the given Windows time zone name to a corresponding TZ Database + name. + + Returns null if the given time zone name cannot be converted. + + See_Also: + $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, + Windows <-> TZ Database Name Conversion Table) + + Params: + tzName = The TZ Database name to convert. + +/ +deprecated("Use parseTZConversions instead") +string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc +{ + switch (tzName) + { + case "AUS Central Standard Time": return "Australia/Darwin"; + case "AUS Eastern Standard Time": return "Australia/Sydney"; + case "Aus Central W. Standard Time": return "Australia/Eucla"; + case "Afghanistan Standard Time": return "Asia/Kabul"; + case "Haiti Standard Time": return "America/Port-au-Prince"; + case "Alaskan Standard Time": return "America/Anchorage"; + case "Aleutian Standard Time": return "America/Adak"; + case "Altai Standard Time": return "Asia/Barnaul"; + case "Arab Standard Time": return "Asia/Riyadh"; + case "Arabian Standard Time": return "Asia/Dubai"; + case "Arabic Standard Time": return "Asia/Baghdad"; + case "Argentina Standard Time": return "America/Buenos_Aires"; + case "Astrakhan Standard Time": return "Europe/Astrakhan"; + case "Atlantic Standard Time": return "America/Halifax"; + case "Azerbaijan Standard Time": return "Asia/Baku"; + case "Azores Standard Time": return "Atlantic/Azores"; + case "Bahia Standard Time": return "America/Bahia"; + case "Bangladesh Standard Time": return "Asia/Dhaka"; + case "Belarus Standard Time": return "Europe/Minsk"; + case "Bougainville Standard Time": return "Pacific/Bougainville"; + case "Canada Central Standard Time": return "America/Regina"; + case "Cape Verde Standard Time": return "Atlantic/Cape_Verde"; + case "Caucasus Standard Time": return "Asia/Yerevan"; + case "Cen. Australia Standard Time": return "Australia/Adelaide"; + case "Central America Standard Time": return "America/Guatemala"; + case "Central Asia Standard Time": return "Asia/Almaty"; + case "Central Brazilian Standard Time": return "America/Cuiaba"; + case "Central Europe Standard Time": return "Europe/Budapest"; + case "Central European Standard Time": return "Europe/Warsaw"; + case "Central Pacific Standard Time": return "Pacific/Guadalcanal"; + case "Central Standard Time": return "America/Chicago"; + case "Central Standard Time (Mexico)": return "America/Mexico_City"; + case "Chatham Islands Standard Time": return "Pacific/Chatham"; + case "China Standard Time": return "Asia/Shanghai"; + case "Cuba Standard Time": return "America/Havana"; + case "Dateline Standard Time": return "Etc/GMT+12"; + case "E. Africa Standard Time": return "Africa/Nairobi"; + case "E. Australia Standard Time": return "Australia/Brisbane"; + // This doesn't appear to be in the current stuff from MS, but the autotester + // is failing without it (probably because its time zone data hasn't been + // updated recently enough). + case "E. Europe Standard Time": return "Europe/Minsk"; + case "E. South America Standard Time": return "America/Sao_Paulo"; + case "Easter Island Standard Time": return "Pacific/Easter"; + case "Eastern Standard Time": return "America/New_York"; + case "Eastern Standard Time (Mexico)": return "America/Cancun"; + case "Egypt Standard Time": return "Africa/Cairo"; + case "Ekaterinburg Standard Time": return "Asia/Yekaterinburg"; + case "FLE Standard Time": return "Europe/Kiev"; + case "Fiji Standard Time": return "Pacific/Fiji"; + case "GMT Standard Time": return "Europe/London"; + case "GTB Standard Time": return "Europe/Athens"; + case "Georgian Standard Time": return "Asia/Tbilisi"; + case "Greenland Standard Time": return "America/Godthab"; + case "Greenwich Standard Time": return "Atlantic/Reykjavik"; + case "Hawaiian Standard Time": return "Pacific/Honolulu"; + case "India Standard Time": return "Asia/Calcutta"; + case "Iran Standard Time": return "Asia/Tehran"; + case "Israel Standard Time": return "Asia/Jerusalem"; + case "Jordan Standard Time": return "Asia/Amman"; + case "Kaliningrad Standard Time": return "Europe/Kaliningrad"; + // Same as with E. Europe Standard Time. + case "Kamchatka Standard Time": return "Asia/Kamchatka"; + case "Korea Standard Time": return "Asia/Seoul"; + case "Libya Standard Time": return "Africa/Tripoli"; + case "Line Islands Standard Time": return "Pacific/Kiritimati"; + case "Lord Howe Standard Time": return "Australia/Lord_Howe"; + case "Magadan Standard Time": return "Asia/Magadan"; + case "Marquesas Standard Time": return "Pacific/Marquesas"; + case "Mauritius Standard Time": return "Indian/Mauritius"; + // Same as with E. Europe Standard Time. + case "Mexico Standard Time": return "America/Mexico_City"; + // Same as with E. Europe Standard Time. + case "Mexico Standard Time 2": return "America/Chihuahua"; + // Same as with E. Europe Standard Time. + case "Mid-Atlantic Standard Time": return "Etc/GMT+2"; + case "Middle East Standard Time": return "Asia/Beirut"; + case "Montevideo Standard Time": return "America/Montevideo"; + case "Morocco Standard Time": return "Africa/Casablanca"; + case "Mountain Standard Time": return "America/Denver"; + case "Mountain Standard Time (Mexico)": return "America/Chihuahua"; + case "Myanmar Standard Time": return "Asia/Rangoon"; + case "N. Central Asia Standard Time": return "Asia/Novosibirsk"; + case "Namibia Standard Time": return "Africa/Windhoek"; + case "Nepal Standard Time": return "Asia/Katmandu"; + case "New Zealand Standard Time": return "Pacific/Auckland"; + case "Newfoundland Standard Time": return "America/St_Johns"; + case "Norfolk Standard Time": return "Pacific/Norfolk"; + case "North Asia East Standard Time": return "Asia/Irkutsk"; + case "North Asia Standard Time": return "Asia/Krasnoyarsk"; + case "North Korea Standard Time": return "Asia/Pyongyang"; + case "Pacific SA Standard Time": return "America/Santiago"; + case "Pacific Standard Time": return "America/Los_Angeles"; + case "Pacific Standard Time (Mexico)": return "America/Santa_Isabel"; + case "Pakistan Standard Time": return "Asia/Karachi"; + case "Paraguay Standard Time": return "America/Asuncion"; + case "Romance Standard Time": return "Europe/Paris"; + case "Russia Time Zone 10": return "Asia/Srednekolymsk"; + case "Russia Time Zone 11": return "Asia/Anadyr"; + case "Russia Time Zone 3": return "Europe/Samara"; + case "Russian Standard Time": return "Europe/Moscow"; + case "SA Eastern Standard Time": return "America/Cayenne"; + case "SA Pacific Standard Time": return "America/Bogota"; + case "SA Western Standard Time": return "America/La_Paz"; + case "SE Asia Standard Time": return "Asia/Bangkok"; + case "Sakhalin Standard Time": return "Asia/Sakhalin"; + case "Saint Pierre Standard Time": return "America/Miquelon"; + case "Samoa Standard Time": return "Pacific/Apia"; + case "Singapore Standard Time": return "Asia/Singapore"; + case "South Africa Standard Time": return "Africa/Johannesburg"; + case "Sri Lanka Standard Time": return "Asia/Colombo"; + case "Syria Standard Time": return "Asia/Damascus"; + case "Taipei Standard Time": return "Asia/Taipei"; + case "Tasmania Standard Time": return "Australia/Hobart"; + case "Tocantins Standard Time": return "America/Arguaina"; + case "Tokyo Standard Time": return "Asia/Tokyo"; + case "Tomsk Standard Time": return "Asia/Tomsk"; + case "Tonga Standard Time": return "Pacific/Tongatapu"; + case "Transbaikal Standard Time": return "Asia/Chita"; + case "Turkey Standard Time": return "Europe/Istanbul"; + case "Turks And Caicos Standard Time": return "America/Grand_Turk"; + case "US Eastern Standard Time": return "America/Indianapolis"; + case "US Mountain Standard Time": return "America/Phoenix"; + case "UTC": return "Etc/GMT"; + case "UTC+12": return "Etc/GMT-12"; + case "UTC-02": return "Etc/GMT+2"; + case "UTC-08": return "Etc/GMT+8"; + case "UTC-09": return "Etc/GMT+9"; + case "UTC-11": return "Etc/GMT+11"; + case "Ulaanbaatar Standard Time": return "Asia/Ulaanbaatar"; + case "Venezuela Standard Time": return "America/Caracas"; + case "Vladivostok Standard Time": return "Asia/Vladivostok"; + case "W. Australia Standard Time": return "Australia/Perth"; + case "W. Central Africa Standard Time": return "Africa/Lagos"; + case "W. Europe Standard Time": return "Europe/Berlin"; + case "W. Mongolia Standard Time": return "Asia/Hovd"; + case "West Asia Standard Time": return "Asia/Tashkent"; + case "West Bank Standard Time": return "Asia/Hebron"; + case "West Pacific Standard Time": return "Pacific/Port_Moresby"; + case "Yakutsk Standard Time": return "Asia/Yakutsk"; + default: return null; + } +} + +version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest +{ + import std.stdio : stderr; + + foreach (winName; WindowsTimeZone.getInstalledTZNames()) + { + if (windowsTZNameToTZDatabaseName(winName) is null) + stderr.writeln("Missing Windows to TZName translation: ", winName); + } +} + + +// This script is for regenerating tzDatabaseNameToWindowsTZName and +// windowsTZNameToTZDatabaseName from +// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml + +/+ +#!/bin/rdmd + +import std.algorithm; +import std.array; +import std.conv; +import std.datetime; +import std.exception; +import std.path; +import std.stdio; +import std.string; + +int main(string[] args) +{ + if (args.length != 4 || args[1].baseName != "windowsZones.xml") + { + stderr.writeln("genTZs.d windowsZones.xml "); + return -1; + } + + string[][string] win2Nix; + string[][string] nix2Win; + immutable f1 = ` %s", nix, wins)); + + // We'll try to eliminate multiples by favoring a conversion if it's already + // in Phobos, but if it's new, then the correct one will have to be chosen + // manually from the results. + string[] haveMultiple; + foreach (win, nixes; win2Nix) + { + if (nixes.length > 1) + haveMultiple ~= win; + } + bool[string] haveConflicts; + foreach (win; haveMultiple) + { + if (auto curr = windowsTZNameToTZDatabaseName(win)) + { + if (auto other = curr in nix2Win) + { + if ((*other)[0] == win) + { + win2Nix[win] = [curr]; + continue; + } + } + } + haveConflicts[win] = true; + writefln("Warning: %s -> %s", win, win2Nix[win]); + } + + + string[] nix2WinLines = [ + `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`, + `{`, + ` switch (tzName)`, + ` {`]; + + foreach (nix; nix2Win.keys.sort()) + nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]); + + nix2WinLines ~= [ + ` default: return null;`, + ` }`, + `}`]; + + + string[] win2NixLines = [ + `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`, + `{`, + ` switch (tzName)`, + ` {`]; + foreach (win; win2Nix.keys.sort()) + { + immutable hasMultiple = cast(bool)(win in haveConflicts); + foreach (nix; win2Nix[win]) + win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : ""); + } + + win2NixLines ~= [ + ` default: return null;`, + ` }`, + `}`]; + + + auto nix2WinFile = args[2]; + std.file.write(nix2WinFile, nix2WinLines.join("\n")); + + auto win2NixFile = args[3]; + std.file.write(win2NixFile, win2NixLines.join("\n")); + + return 0; +} ++/ diff --git a/std/demangle.d b/std/demangle.d index c90500a806b..e49bb9f5215 100644 --- a/std/demangle.d +++ b/std/demangle.d @@ -3,14 +3,12 @@ /** * Demangle D mangled names. * - * Macros: - * WIKI = Phobos/StdDemangle - * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright), + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright), * Thomas K$(UUML)hne, Frits van Bommel * Source: $(PHOBOSSRC std/_demangle.d) + * $(SCRIPT inhibitQuickIndex = 1;) */ /* * Copyright Digital Mars 2000 - 2009. diff --git a/std/digest/crc.d b/std/digest/crc.d index e92ff83af80..25ec75c9c96 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -25,12 +25,13 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of)) * module. * * Note: - * CRCs are usually printed with the MSB first. When using $(XREF digest.digest, toHexString) the result - * will be in an unexpected order. Use $(XREF digest.digest, toHexString)s optional order parameter - * to specify decreasing order for the correct result. The $(LREF crcHexString) alias can also - * be used for this purpose. + * CRCs are usually printed with the MSB first. When using + * $(REF toHexString, std,digest,digest) the result will be in an unexpected + * order. Use $(REF toHexString, std,digest,digest)'s optional order parameter + * to specify decreasing order for the correct result. The $(LREF crcHexString) + * alias can also be used for this purpose. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * * Authors: Pavel "EvilOne" Minayev, Alex Rønne Petersen, Johannes Pfau * @@ -39,9 +40,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of)) * * Source: $(PHOBOSSRC std/digest/_crc.d) * - * Macros: - * WIKI = Phobos/StdUtilDigestCRC32 - * * Standards: * Implements the 'common' IEEE CRC32 variant * (LSB-first order, Initial value uint.max, complement result) @@ -67,7 +65,7 @@ version(unittest) /// -unittest +@safe unittest { //Template API import std.digest.crc; @@ -85,7 +83,7 @@ unittest } /// -unittest +@safe unittest { //OOP API import std.digest.crc; @@ -102,46 +100,39 @@ unittest hash = crc.finish(); } -private immutable uint[256] crc32_table = -[ - 0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,0x706af48f,0xe963a535, - 0x9e6495a3,0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x09b64c2b,0x7eb17cbd, - 0xe7b82d07,0x90bf1d91,0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d, - 0x6ddde4eb,0xf4d4b551,0x83d385c7,0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, - 0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4, - 0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,0x35b5a8fa,0x42b2986c, - 0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,0x26d930ac, - 0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, - 0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab, - 0xb6662d3d,0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f, - 0x9fbfe4a5,0xe8b8d433,0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb, - 0x086d3d2d,0x91646c97,0xe6635c01,0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, - 0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950,0x8bbeb8ea, - 0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,0x4db26158,0x3ab551ce, - 0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,0x4369e96a, - 0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, - 0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409, - 0xce61e49f,0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81, - 0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a,0xead54739, - 0x9dd277af,0x04db2615,0x73dc1683,0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, - 0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,0xf00f9344,0x8708a3d2,0x1e01f268, - 0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0, - 0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,0xd6d6a3e8, - 0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, - 0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef, - 0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703, - 0x220216b9,0x5505262f,0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7, - 0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, - 0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,0x95bf4a82,0xe2b87a14,0x7bb12bae, - 0x0cb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,0x86d3d2d4,0xf1d4e242, - 0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,0x88085ae6, - 0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, - 0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d, - 0x3e6e77db,0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5, - 0x47b2cf7f,0x30b5ffe9,0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605, - 0xcdd70693,0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, - 0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d -]; +private uint[256][8] genTables(uint polynomial) +{ + uint[256][8] res = void; + + foreach (i; 0 .. 0x100) + { + uint crc = i; + foreach (_; 0 .. 8) + crc = (crc >> 1) ^ (-int(crc & 1) & polynomial); + res[0][i] = crc; + } + + foreach (i; 0 .. 0x100) + { + res[1][i] = (res[0][i] >> 8) ^ res[0][res[0][i] & 0xFF]; + res[2][i] = (res[1][i] >> 8) ^ res[0][res[1][i] & 0xFF]; + res[3][i] = (res[2][i] >> 8) ^ res[0][res[2][i] & 0xFF]; + + res[4][i] = (res[3][i] >> 8) ^ res[0][res[3][i] & 0xFF]; + res[5][i] = (res[4][i] >> 8) ^ res[0][res[4][i] & 0xFF]; + res[6][i] = (res[5][i] >> 8) ^ res[0][res[5][i] & 0xFF]; + res[7][i] = (res[6][i] >> 8) ^ res[0][res[6][i] & 0xFF]; + } + return res; +} + +private static immutable uint[256][8] crc32Tables = genTables(0xEDB88320); + +@system unittest +{ + auto tables = genTables(0xEDB88320); + assert(tables[0][0] == 0x00000000 && tables[0][$ - 1] == 0x2d02ef8d && tables[7][$ - 1] == 0x264b06e6); +} /** * Template API CRC32 implementation. @@ -156,20 +147,60 @@ struct CRC32 public: /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). */ void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc { - foreach (val; data) - _state = (_state >> 8) ^ crc32_table[cast(ubyte)_state ^ val]; + uint crc = _state; + // process eight bytes at once + while (data.length >= 8) + { + // Use byte-wise reads to support architectures without HW support + // for unaligned reads. This can be optimized by compilers to a single + // 32-bit read if unaligned reads are supported. + // DMD is not able to do this optimization though, so explicitly + // do unaligned reads for DMD's architectures. + version (X86) + enum hasLittleEndianUnalignedReads = true; + else version (X86_64) + enum hasLittleEndianUnalignedReads = true; + else + enum hasLittleEndianUnalignedReads = false; // leave decision to optimizer + static if (hasLittleEndianUnalignedReads) + { + uint one = (cast(uint*) data.ptr)[0] ^ crc; + uint two = (cast(uint*) data.ptr)[1]; + } + else + { + uint one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]) ^ crc; + uint two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]); + } + + crc = + crc32Tables[0][two >> 24] ^ + crc32Tables[1][(two >> 16) & 0xFF] ^ + crc32Tables[2][(two >> 8) & 0xFF] ^ + crc32Tables[3][two & 0xFF] ^ + crc32Tables[4][one >> 24] ^ + crc32Tables[5][(one >> 16) & 0xFF] ^ + crc32Tables[6][(one >> 8) & 0xFF] ^ + crc32Tables[7][one & 0xFF]; + + data = data[8 .. $]; + } + // remaining 1 to 7 bytes + foreach (d; data) + crc = (crc >> 8) ^ crc32Tables[0][(crc & 0xFF) ^ d]; + _state = crc; } /// - unittest + @safe unittest { CRC32 dig; - dig.put(cast(ubyte)0); //single ubyte - dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + dig.put(cast(ubyte) 0); //single ubyte + dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic ubyte[10] buf; dig.put(buf); //buffer } @@ -188,7 +219,7 @@ struct CRC32 this = CRC32.init; } /// - unittest + @safe unittest { CRC32 digest; //digest.start(); //Not necessary @@ -206,11 +237,11 @@ struct CRC32 return tmp; } /// - unittest + @safe unittest { //Simple example CRC32 hash; - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); ubyte[4] result = hash.finish(); } @@ -227,7 +258,7 @@ struct CRC32 } /// -unittest +@safe unittest { //Simple example, hashing a string using crc32Of helper function ubyte[4] hash = crc32Of("abc"); @@ -236,7 +267,7 @@ unittest } /// -unittest +@safe unittest { //Using the basic API CRC32 hash; @@ -247,13 +278,14 @@ unittest } /// -unittest +@safe unittest { //Let's use the template features: //Note: When passing a CRC32 to a function, it must be passed by reference! - void doSomething(T)(ref T hash) if(isDigest!T) + void doSomething(T)(ref T hash) + if (isDigest!T) { - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); } CRC32 crc; crc.start(); @@ -261,86 +293,105 @@ unittest assert(crcHexString(crc.finish()) == "D202EF8D"); } -unittest +@safe unittest { assert(isDigest!CRC32); } -unittest +@system unittest { ubyte[4] digest; CRC32 crc; crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[])x"bd50274c"); + assert(crc.peek() == cast(ubyte[]) x"bd50274c"); crc.start(); crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[])x"00000000"); + assert(crc.finish() == cast(ubyte[]) x"00000000"); digest = crc32Of(""); - assert(digest == cast(ubyte[])x"00000000"); + assert(digest == cast(ubyte[]) x"00000000"); //Test vector from http://rosettacode.org/wiki/CRC-32 assert(crcHexString(crc32Of("The quick brown fox jumps over the lazy dog")) == "414FA339"); digest = crc32Of("a"); - assert(digest == cast(ubyte[])x"43beb7e8"); + assert(digest == cast(ubyte[]) x"43beb7e8"); digest = crc32Of("abc"); - assert(digest == cast(ubyte[])x"c2412435"); + assert(digest == cast(ubyte[]) x"c2412435"); digest = crc32Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[])x"5f3f1a17"); + assert(digest == cast(ubyte[]) x"5f3f1a17"); digest = crc32Of("message digest"); - assert(digest == cast(ubyte[])x"7f9d1520"); + assert(digest == cast(ubyte[]) x"7f9d1520"); digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[])x"d2e6c21f"); + assert(digest == cast(ubyte[]) x"d2e6c21f"); digest = crc32Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[])x"724aa97c"); + assert(digest == cast(ubyte[]) x"724aa97c"); - assert(crcHexString(cast(ubyte[4])x"c3fcd3d7") == "D7D3FCC3"); + assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3"); } /** - * This is a convenience alias for $(XREF digest.digest, digest) using the + * This is a convenience alias for $(REF digest, std,digest,digest) using the * CRC32 implementation. + * + * Params: + * data = $(D InputRange) of $(D ElementType) implicitly convertible to + * $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays + * of any type. + * + * Returns: + * CRC32 of data */ //simple alias doesn't work here, hope this gets inlined... -auto crc32Of(T...)(T data) +ubyte[4] crc32Of(T...)(T data) { return digest!(CRC32, T)(data); } +/// +@system unittest +{ + ubyte[] data = [4,5,7,25]; + assert(data.crc32Of == [167, 180, 199, 131]); + + import std.utf : byChar; + assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]); + + ubyte[4] hash = "abc".crc32Of(); + assert(hash == digest!CRC32("ab", "c")); + + import std.range : iota; + enum ubyte S = 5, F = 66; + assert(iota(S, F).crc32Of == [59, 140, 234, 154]); +} + /** - * This is a convenience alias for $(XREF digest.digest, toHexString) producing the usual - * CRC32 string output. + * This is a convenience alias for $(REF toHexString, std,digest,digest) + * producing the usual CRC32 string output. */ public alias crcHexString = toHexString!(Order.decreasing); ///ditto public alias crcHexString = toHexString!(Order.decreasing, 16); -/// -unittest -{ - ubyte[4] hash = crc32Of("abc"); - assert(hash == digest!CRC32("abc")); //This is the same as above -} /** * OOP API CRC32 implementation. * See $(D std.digest.digest) for differences between template and OOP API. * - * This is an alias for $(XREF digest.digest, WrapperDigest)!CRC32, see - * $(XREF digest.digest, WrapperDigest) for more information. + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC32), see + * there for more information. */ alias CRC32Digest = WrapperDigest!CRC32; /// -unittest +@safe unittest { //Simple example, hashing a string using Digest.digest helper function auto crc = new CRC32Digest(); @@ -350,12 +401,12 @@ unittest } /// -unittest +@system unittest { //Let's use the OOP features: void test(Digest dig) { - dig.put(cast(ubyte)0); + dig.put(cast(ubyte) 0); } auto crc = new CRC32Digest(); test(crc); @@ -367,76 +418,76 @@ unittest } /// -unittest +@safe unittest { //Simple example auto hash = new CRC32Digest(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); ubyte[] result = hash.finish(); } /// -unittest +@system unittest { //using a supplied buffer ubyte[4] buf; auto hash = new CRC32Digest(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); ubyte[] result = hash.finish(buf[]); //The result is now in result (and in buf. If you pass a buffer which is bigger than //necessary, result will have the correct length, but buf will still have it's original //length) } -unittest +@system unittest { import std.range; auto crc = new CRC32Digest(); crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[])x"bd50274c"); + assert(crc.peek() == cast(ubyte[]) x"bd50274c"); crc.reset(); crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[])x"00000000"); + assert(crc.finish() == cast(ubyte[]) x"00000000"); crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); ubyte[20] result; auto result2 = crc.finish(result[]); - assert(result[0 .. 4] == result2 && result2 == cast(ubyte[])x"bd50274c"); + assert(result[0 .. 4] == result2 && result2 == cast(ubyte[]) x"bd50274c"); debug assertThrown!Error(crc.finish(result[0 .. 3])); assert(crc.length == 4); - assert(crc.digest("") == cast(ubyte[])x"00000000"); + assert(crc.digest("") == cast(ubyte[]) x"00000000"); - assert(crc.digest("a") == cast(ubyte[])x"43beb7e8"); + assert(crc.digest("a") == cast(ubyte[]) x"43beb7e8"); - assert(crc.digest("abc") == cast(ubyte[])x"c2412435"); + assert(crc.digest("abc") == cast(ubyte[]) x"c2412435"); assert(crc.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[])x"5f3f1a17"); + == cast(ubyte[]) x"5f3f1a17"); - assert(crc.digest("message digest") == cast(ubyte[])x"7f9d1520"); + assert(crc.digest("message digest") == cast(ubyte[]) x"7f9d1520"); assert(crc.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[])x"bd50274c"); + == cast(ubyte[]) x"bd50274c"); assert(crc.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[])x"d2e6c21f"); + == cast(ubyte[]) x"d2e6c21f"); assert(crc.digest("1234567890123456789012345678901234567890", "1234567890123456789012345678901234567890") - == cast(ubyte[])x"724aa97c"); + == cast(ubyte[]) x"724aa97c"); ubyte[] onemilliona = new ubyte[1000000]; onemilliona[] = 'a'; auto digest = crc32Of(onemilliona); - assert(digest == cast(ubyte[])x"BCBF25DC"); + assert(digest == cast(ubyte[]) x"BCBF25DC"); auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); digest = crc32Of(oneMillionRange); - assert(digest == cast(ubyte[])x"BCBF25DC"); + assert(digest == cast(ubyte[]) x"BCBF25DC"); } diff --git a/std/digest/digest.d b/std/digest/digest.d index 99282a94ecc..48cbe35da43 100644 --- a/std/digest/digest.d +++ b/std/digest/digest.d @@ -1,6 +1,7 @@ /** - * This module describes the digest APIs used in Phobos. All digests follow these APIs. - * Additionally, this module contains useful helper methods which can be used with every _digest type. + * This module describes the _digest APIs used in Phobos. All digests follow + * these APIs. Additionally, this module contains useful helper methods which + * can be used with every _digest type. * $(SCRIPT inhibitQuickIndex = 1;) @@ -9,6 +10,7 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Functions) ) $(TR $(TDNW Template API) $(TD $(MYREF isDigest) $(MYREF DigestType) $(MYREF hasPeek) + $(MYREF hasBlockSize) $(MYREF ExampleDigest) $(MYREF _digest) $(MYREF hexDigest) $(MYREF makeDigest) ) ) @@ -41,7 +43,7 @@ $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDi * In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs * directly. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: * Johannes Pfau * @@ -61,13 +63,14 @@ $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDi */ module std.digest.digest; +import std.meta : allSatisfy; import std.traits; -import std.typetuple : allSatisfy; +import std.range.primitives; public import std.ascii : LetterCase; /// -unittest +@system unittest { import std.digest.crc; @@ -83,14 +86,15 @@ unittest } /// -unittest +@system unittest { //Generating the hashes of a file, idiomatic D way import std.digest.crc, std.digest.sha, std.digest.md; import std.stdio; // Digests a file and prints the result. - void digestFile(Hash)(string filename) if(isDigest!Hash) + void digestFile(Hash)(string filename) + if (isDigest!Hash) { auto file = File(filename); auto result = digest!Hash(file.byChunk(4096 * 1024)); @@ -108,13 +112,14 @@ unittest } } /// -unittest +@system unittest { //Generating the hashes of a file using the template API import std.digest.crc, std.digest.sha, std.digest.md; import std.stdio; // Digests a file and prints the result. - void digestFile(Hash)(ref Hash hash, string filename) if(isDigest!Hash) + void digestFile(Hash)(ref Hash hash, string filename) + if (isDigest!Hash) { File file = File(filename); @@ -147,7 +152,7 @@ unittest } /// -unittest +@system unittest { import std.digest.crc, std.digest.sha, std.digest.md; import std.stdio; @@ -202,14 +207,15 @@ version(ExampleDigest) public: /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). - * The following usages of $(D put) must work for any type which passes $(LREF isDigest): - * Examples: + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). + * The following usages of $(D put) must work for any type which + * passes $(LREF isDigest): + * Example: * ---- * ExampleDigest dig; - * dig.put(cast(ubyte)0); //single ubyte - * dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + * dig.put(cast(ubyte) 0); //single ubyte + * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic * ubyte[10] buf; * dig.put(buf); //buffer * ---- @@ -250,10 +256,10 @@ version(ExampleDigest) } /// -unittest +@system unittest { //Using the OutputRange feature - import std.algorithm : copy; + import std.algorithm.mutation : copy; import std.range : repeat; import std.digest.md; @@ -284,23 +290,24 @@ template isDigest(T) is(typeof( { T dig = void; //Can define - dig.put(cast(ubyte)0, cast(ubyte)0); //varags + dig.put(cast(ubyte) 0, cast(ubyte) 0); //varags dig.start(); //has start auto value = dig.finish(); //has finish })); } /// -unittest +@system unittest { import std.digest.crc; static assert(isDigest!CRC32); } /// -unittest +@system unittest { import std.digest.crc; - void myFunction(T)() if(isDigest!T) + void myFunction(T)() + if (isDigest!T) { T dig; dig.start(); @@ -314,7 +321,7 @@ unittest */ template DigestType(T) { - static if(isDigest!T) + static if (isDigest!T) { alias DigestType = ReturnType!(typeof( @@ -328,13 +335,13 @@ template DigestType(T) } /// -unittest +@system unittest { import std.digest.crc; assert(is(DigestType!(CRC32) == ubyte[4])); } /// -unittest +@system unittest { import std.digest.crc; CRC32 dig; @@ -364,17 +371,18 @@ template hasPeek(T) } /// -unittest +@system unittest { import std.digest.crc, std.digest.md; assert(!hasPeek!(MD5)); assert(hasPeek!CRC32); } /// -unittest +@system unittest { import std.digest.crc; - void myFunction(T)() if(hasPeek!T) + void myFunction(T)() + if (hasPeek!T) { T dig; dig.start(); @@ -383,7 +391,26 @@ unittest myFunction!CRC32(); } -private template isDigestibleRange(Range) +/** + * Checks whether the digest has a $(D blockSize) member, which contains the + * digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest.hmac). + */ + +template hasBlockSize(T) +if (isDigest!T) +{ + enum bool hasBlockSize = __traits(compiles, { size_t blockSize = T.blockSize; }); +} + +/// +@system unittest +{ + import std.digest.md, std.digest.hmac; + static assert(hasBlockSize!MD5 && MD5.blockSize == 512); + static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512); +} + +package template isDigestibleRange(Range) { import std.digest.md; import std.range : isInputRange, ElementType; @@ -402,10 +429,11 @@ private template isDigestibleRange(Range) * Params: * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) */ -DigestType!Hash digest(Hash, Range)(auto ref Range range) if(!isArray!Range +DigestType!Hash digest(Hash, Range)(auto ref Range range) +if (!isArray!Range && isDigestibleRange!Range) { - import std.algorithm : copy; + import std.algorithm.mutation : copy; Hash hash; hash.start(); copy(range, &hash); @@ -413,7 +441,7 @@ DigestType!Hash digest(Hash, Range)(auto ref Range range) if(!isArray!Range } /// -unittest +@system unittest { import std.digest.md; import std.range : repeat; @@ -427,17 +455,18 @@ unittest * Params: * data= one or more arrays of any type */ -DigestType!Hash digest(Hash, T...)(scope const T data) if(allSatisfy!(isArray, typeof(data))) +DigestType!Hash digest(Hash, T...)(scope const T data) +if (allSatisfy!(isArray, typeof(data))) { Hash hash; hash.start(); - foreach(datum; data) + foreach (datum; data) hash.put(cast(const(ubyte[]))datum); return hash.finish(); } /// -unittest +@system unittest { import std.digest.md, std.digest.sha, std.digest.crc; auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog"); @@ -447,7 +476,7 @@ unittest } /// -unittest +@system unittest { import std.digest.crc; auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog"); @@ -464,13 +493,13 @@ unittest * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) */ char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range) - if(!isArray!Range && isDigestibleRange!Range) +if (!isArray!Range && isDigestibleRange!Range) { return toHexString!order(digest!Hash(range)); } /// -unittest +@system unittest { import std.digest.md; import std.range : repeat; @@ -486,19 +515,19 @@ unittest * data= one or more arrays of any type */ char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, T...)(scope const T data) - if(allSatisfy!(isArray, typeof(data))) +if (allSatisfy!(isArray, typeof(data))) { return toHexString!order(digest!Hash(data)); } /// -unittest +@system unittest { import std.digest.crc; assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339"); } /// -unittest +@system unittest { import std.digest.crc; assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339"); @@ -516,7 +545,7 @@ Hash makeDigest(Hash)() } /// -unittest +@system unittest { import std.digest.md; auto md5 = makeDigest!MD5(); @@ -540,15 +569,15 @@ interface Digest public: /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). * - * Examples: + * Example: * ---- * void test(Digest dig) * { - * dig.put(cast(ubyte)0); //single ubyte - * dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + * dig.put(cast(ubyte) 0); //single ubyte + * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic * ubyte[10] buf; * dig.put(buf); //buffer * } @@ -576,7 +605,7 @@ interface Digest */ @trusted nothrow ubyte[] finish(); ///ditto - nothrow ubyte[] finish(scope ubyte[] buf); + nothrow ubyte[] finish(ubyte[] buf); //@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549 /*in { @@ -589,17 +618,17 @@ interface Digest final @trusted nothrow ubyte[] digest(scope const(void[])[] data...) { this.reset(); - foreach(datum; data) - this.put(cast(ubyte[])datum); + foreach (datum; data) + this.put(cast(ubyte[]) datum); return this.finish(); } } /// -unittest +@system unittest { //Using the OutputRange feature - import std.algorithm : copy; + import std.algorithm.mutation : copy; import std.range : repeat; import std.digest.md; @@ -610,7 +639,7 @@ unittest } /// -unittest +@system unittest { import std.digest.md, std.digest.sha, std.digest.crc; ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog"); @@ -620,14 +649,14 @@ unittest } /// -unittest +@system unittest { import std.digest.crc; ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog"); assert(crcHexString(crc32) == "414FA339"); } -unittest +@system unittest { import std.range : isOutputRange; assert(!isDigest!(Digest)); @@ -635,12 +664,12 @@ unittest } /// -unittest +@system unittest { void test(Digest dig) { - dig.put(cast(ubyte)0); //single ubyte - dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + dig.put(cast(ubyte) 0); //single ubyte + dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic ubyte[10] buf; dig.put(buf); //buffer } @@ -669,6 +698,11 @@ enum Order : bool * The additional letterCase parameter can be used to specify the case of the output data. * By default the output is in upper case. To change it to the lower case * pass LetterCase.lower as a parameter. + * + * Note: + * The function overloads returning a string allocate their return values + * using the GC. The versions returning static arrays use pass-by-value for + * the return value, effectively avoiding dynamic allocation. */ char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper) (in ubyte[num] digest) @@ -686,9 +720,9 @@ char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase l char[num*2] result; size_t i; - static if(order == Order.increasing) + static if (order == Order.increasing) { - foreach(u; digest) + foreach (u; digest) { result[i++] = hexDigits[u >> 4]; result[i++] = hexDigits[u & 15]; @@ -697,7 +731,7 @@ char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase l else { size_t j = num - 1; - while(i < num*2) + while (i < num*2) { result[i++] = hexDigits[digest[j] >> 4]; result[i++] = hexDigits[digest[j] & 15]; @@ -708,7 +742,7 @@ char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase l } ///ditto -auto toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest) +char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest) { return toHexString!(order, num, letterCase)(digest); } @@ -729,9 +763,9 @@ string toHexString(Order order = Order.increasing, LetterCase letterCase = Lette auto result = new char[digest.length*2]; size_t i; - static if(order == Order.increasing) + static if (order == Order.increasing) { - foreach(u; digest) + foreach (u; digest) { result[i++] = hexDigits[u >> 4]; result[i++] = hexDigits[u & 15]; @@ -740,18 +774,19 @@ string toHexString(Order order = Order.increasing, LetterCase letterCase = Lette else { import std.range : retro; - foreach(u; retro(digest)) + foreach (u; retro(digest)) { result[i++] = hexDigits[u >> 4]; result[i++] = hexDigits[u & 15]; } } import std.exception : assumeUnique; - return assumeUnique(result); + // memory was just created, so casting to immutable is safe + return () @trusted { return assumeUnique(result); }(); } ///ditto -auto toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest) +string toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest) { return toHexString!(order, letterCase)(digest); } @@ -759,7 +794,7 @@ auto toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte //For more example unittests, see Digest.digest, digest /// -unittest +@safe unittest { import std.digest.crc; //Test with template API: @@ -772,7 +807,7 @@ unittest } /// -unittest +@safe unittest { import std.digest.crc; // With OOP API @@ -781,7 +816,7 @@ unittest assert(toHexString!(Order.decreasing)(crc32) == "414FA339"); } -unittest +@safe unittest { ubyte[16] data; assert(toHexString(data) == "00000000000000000000000000000000"); @@ -803,25 +838,36 @@ unittest ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "") { assert(source.length >= N, errorMsg); - return *cast(T[N]*)source.ptr; + return *cast(T[N]*) source.ptr; } -/** - * This helper is used internally in the WrapperDigest template, but it might be - * useful for other purposes as well. It returns the length (in bytes) of the hash value - * produced by T. +/* + * Returns the length (in bytes) of the hash value produced by T. */ -template digestLength(T) if(isDigest!T) +template digestLength(T) +if (isDigest!T) { enum size_t digestLength = (ReturnType!(T.finish)).length; } +@safe pure nothrow @nogc +unittest +{ + import std.digest.md : MD5; + import std.digest.sha : SHA1, SHA256, SHA512; + assert(digestLength!MD5 == 16); + assert(digestLength!SHA1 == 20); + assert(digestLength!SHA256 == 32); + assert(digestLength!SHA512 == 64); +} + /** * Wraps a template API hash struct into a Digest interface. * Modules providing digest implementations will usually provide * an alias for this template (e.g. MD5Digest, SHA1Digest, ...). */ -class WrapperDigest(T) if(isDigest!T) : Digest +class WrapperDigest(T) +if (isDigest!T) : Digest { protected: T _digest; @@ -837,8 +883,8 @@ class WrapperDigest(T) if(isDigest!T) : Digest /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). */ @trusted nothrow void put(scope const(ubyte)[] data...) { @@ -869,20 +915,20 @@ class WrapperDigest(T) if(isDigest!T) : Digest * The finish function returns the hash value. It takes an optional buffer to copy the data * into. If a buffer is passed, it must have a length at least $(LREF length) bytes. * - * Examples: + * Example: * -------- * * import std.digest.md; * ubyte[16] buf; * auto hash = new WrapperDigest!MD5(); - * hash.put(cast(ubyte)0); + * hash.put(cast(ubyte) 0); * auto result = hash.finish(buf[]); * //The result is now in result (and in buf). If you pass a buffer which is bigger than * //necessary, result will have the correct length, but buf will still have it's original * //length * -------- */ - nothrow ubyte[] finish(scope ubyte[] buf) + nothrow ubyte[] finish(ubyte[] buf) in { assert(buf.length >= this.length); @@ -912,13 +958,13 @@ class WrapperDigest(T) if(isDigest!T) : Digest * * These functions are only available if $(D hasPeek!T) is true. */ - @trusted ubyte[] peek(scope ubyte[] buf) const; + @trusted ubyte[] peek(ubyte[] buf) const; ///ditto @trusted ubyte[] peek() const; } - else static if(hasPeek!T) + else static if (hasPeek!T) { - @trusted ubyte[] peek(scope ubyte[] buf) const + @trusted ubyte[] peek(ubyte[] buf) const in { assert(buf.length >= this.length); @@ -942,25 +988,184 @@ class WrapperDigest(T) if(isDigest!T) : Digest } /// -unittest +@system unittest { import std.digest.md; //Simple example auto hash = new WrapperDigest!MD5(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); auto result = hash.finish(); } /// -unittest +@system unittest { //using a supplied buffer import std.digest.md; ubyte[16] buf; auto hash = new WrapperDigest!MD5(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); auto result = hash.finish(buf[]); //The result is now in result (and in buf). If you pass a buffer which is bigger than //necessary, result will have the correct length, but buf will still have it's original //length } + +@safe unittest +{ + // Test peek & length + import std.digest.crc; + auto hash = new WrapperDigest!CRC32(); + assert(hash.length == 4); + hash.put(cast(const(ubyte[]))"The quick brown fox jumps over the lazy dog"); + assert(hash.peek().toHexString() == "39A34F41"); + ubyte[5] buf; + assert(hash.peek(buf).toHexString() == "39A34F41"); +} + +/** + * Securely compares two digest representations while protecting against timing + * attacks. Do not use `==` to compare digest representations. + * + * The attack happens as follows: + * + * $(OL + * $(LI An attacker wants to send harmful data to your server, which + * requires a integrity HMAC SHA1 token signed with a secret.) + * $(LI The length of the token is known to be 40 characters long due to its format, + * so the attacker first sends `"0000000000000000000000000000000000000000"`, + * then `"1000000000000000000000000000000000000000"`, and so on.) + * $(LI The given HMAC token is compared with the expected token using the + * `==` string comparison, which returns `false` as soon as the first wrong + * element is found. If a wrong element is found, then a rejection is sent + * back to the sender.) + * $(LI Eventually, the attacker is able to determine the first character in + * the correct token because the sever takes slightly longer to return a + * rejection. This is due to the comparison moving on to second item in + * the two arrays, seeing they are different, and then sending the rejection.) + * $(LI It may seem like too small of a difference in time for the attacker + * to notice, but security researchers have shown that differences as + * small as $(LINK2 http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf, + * 20µs can be reliably distinguished) even with network inconsistencies.) + * $(LI Repeat the process for each character until the attacker has the whole + * correct token and the server accepts the harmful data. This can be done + * in a week with the attacker pacing the attack to 10 requests per second + * with only one client.) + * ) + * + * This function defends against this attack by always comparing every single + * item in the array if the two arrays are the same length. Therefore, this + * function is always $(BIGOH n) for ranges of the same length. + * + * This attack can also be mitigated via rate limiting and banning IPs which have too + * many rejected requests. However, this does not completely solve the problem, + * as the attacker could be in control of a bot net. To fully defend against + * the timing attack, rate limiting, banning IPs, and using this function + * should be used together. + * + * Params: + * r1 = A digest representation + * r2 = A digest representation + * Returns: + * `true` if both representations are equal, `false` otherwise + * See_Also: + * $(LINK2 https://en.wikipedia.org/wiki/Timing_attack, The Wikipedia article + * on timing attacks). + */ +bool secureEqual(R1, R2)(R1 r1, R2 r2) +if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && + (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) && + !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void)) +{ + static if (hasLength!R1 && hasLength!R2) + if (r1.length != r2.length) + return false; + + int result; + + static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && + hasLength!R1 && hasLength!R2) + { + foreach (i; 0 .. r1.length) + result |= r1[i] ^ r2[i]; + } + else static if (hasLength!R1 && hasLength!R2) + { + // Lengths are the same so we can squeeze out a bit of performance + // by not checking if r2 is empty + for (; !r1.empty; r1.popFront(), r2.popFront()) + { + result |= r1.front ^ r2.front; + } + } + else + { + // Generic case, walk both ranges + for (; !r1.empty; r1.popFront(), r2.popFront()) + { + if (r2.empty) return false; + result |= r1.front ^ r2.front; + } + if (!r2.empty) return false; + } + + return result == 0; +} + +/// +@system pure unittest +{ + import std.digest.hmac : hmac; + import std.digest.sha : SHA1; + import std.string : representation; + + // a typical HMAC data integrity verification + auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation; + auto data = "data".representation; + + string hex1 = data.hmac!SHA1(secret).toHexString; + string hex2 = data.hmac!SHA1(secret).toHexString; + string hex3 = "data1".representation.hmac!SHA1(secret).toHexString; + + assert( secureEqual(hex1, hex2)); + assert(!secureEqual(hex1, hex3)); +} + +@system pure unittest +{ + import std.internal.test.dummyrange : ReferenceInputRange; + import std.range : takeExactly; + import std.string : representation; + import std.utf : byWchar, byDchar; + + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".representation; + assert(!secureEqual(hex1, hex2)); + } + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018"w.representation; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018"d.representation; + assert(secureEqual(hex1, hex2)); + } + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byWchar; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; + assert(secureEqual(hex1, hex2)); + } + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".byWchar; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; + assert(!secureEqual(hex1, hex2)); + } + { + auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); + auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); + assert(secureEqual(hex1, hex2)); + } + { + auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); + auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 9]).takeExactly(9); + assert(!secureEqual(hex1, hex2)); + } +} diff --git a/std/digest/hmac.d b/std/digest/hmac.d new file mode 100644 index 00000000000..0e6158e0b59 --- /dev/null +++ b/std/digest/hmac.d @@ -0,0 +1,333 @@ +// Written in the D programming language. + +/** +This package implements the hash-based message authentication code (_HMAC) +algorithm as defined in $(HTTP tools.ietf.org/html/rfc2104, RFC2104). See also +the corresponding $(HTTP en.wikipedia.org/wiki/Hash-based_message_authentication_code, Wikipedia article). + +$(SCRIPT inhibitQuickIndex = 1;) + +Macros: + +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). + +Source: $(PHOBOSSRC std/digest/_hmac.d) + */ + +module std.digest.hmac; + +import std.digest.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType; +import std.meta : allSatisfy; + +/** + * Template API HMAC implementation. + * + * This implements an _HMAC over the digest H. If H doesn't provide + * information about the block size, it can be supplied explicitly using + * the second overload. + * + * This type conforms to $(REF isDigest, std,digest.digest). + */ + +version(StdDdoc) +/// Computes an HMAC over data read from stdin. +@safe unittest +{ + import std.stdio, std.digest.hmac, std.digest.sha; + import std.string : representation; + + auto secret = "secret".representation; + stdin.byChunk(4096) + .hmac!SHA1(secret) + .toHexString!(LetterCase.lower) + .writeln; +} + +template HMAC(H) +if (isDigest!H && hasBlockSize!H) +{ + alias HMAC = HMAC!(H, H.blockSize); +} + +/** + * Overload of HMAC to be used if H doesn't provide information about its + * block size. + */ + +struct HMAC(H, size_t hashBlockSize) +if (hashBlockSize % 8 == 0) +{ + enum blockSize = hashBlockSize; + + private H digest; + private ubyte[blockSize / 8] key; + + /** + * Constructs the HMAC digest using the specified secret. + */ + + this(scope const(ubyte)[] secret) + { + // if secret is too long, shorten it by computing its hash + typeof(digest.finish()) buffer = void; + if (secret.length > blockSize / 8) + { + digest.start(); + digest.put(secret); + buffer = digest.finish(); + secret = buffer[]; + } + + // if secret is too short, it will be padded with zeroes + // (the key buffer is already zero-initialized) + import std.algorithm.mutation : copy; + secret.copy(key[]); + + start(); + } + + /// + @safe pure nothrow @nogc unittest + { + import std.digest.sha, std.digest.hmac; + import std.string : representation; + auto hmac = HMAC!SHA1("My s3cR3T keY".representation); + hmac.put("Hello, world".representation); + static immutable expected = [ + 130, 32, 235, 44, 208, 141, + 150, 232, 211, 214, 162, 195, + 188, 127, 52, 89, 100, 68, 90, 216]; + assert(hmac.finish() == expected); + } + + /** + * Reinitializes the digest, making it ready for reuse. + * + * Note: + * The constructor leaves the digest in an initialized state, so that this + * method only needs to be called if an unfinished digest is to be reused. + * + * Returns: + * A reference to the digest for convenient chaining. + */ + + ref HMAC!(H, blockSize) start() return + { + ubyte[blockSize / 8] ipad = void; + foreach (immutable i; 0 .. blockSize / 8) + ipad[i] = key[i] ^ 0x36; + + digest.start(); + digest.put(ipad[]); + + return this; + } + + /// + @safe pure nothrow @nogc unittest + { + import std.digest.sha, std.digest.hmac; + import std.string : representation; + string data1 = "Hello, world", data2 = "Hola mundo"; + auto hmac = HMAC!SHA1("My s3cR3T keY".representation); + hmac.put(data1.representation); + hmac.start(); // reset digest + hmac.put(data2.representation); // start over + static immutable expected = [ + 122, 151, 232, 240, 249, 80, + 19, 178, 186, 77, 110, 23, 208, + 52, 11, 88, 34, 151, 192, 255]; + assert(hmac.finish() == expected); + } + + /** + * Feeds a piece of data into the hash computation. This method allows the + * type to be used as an $(REF OutputRange, std,range). + * + * Returns: + * A reference to the digest for convenient chaining. + */ + + ref HMAC!(H, blockSize) put(in ubyte[] data...) return + { + digest.put(data); + return this; + } + + /// + @safe pure nothrow @nogc unittest + { + import std.digest.sha, std.digest.hmac; + import std.string : representation; + string data1 = "Hello, world", data2 = "Hola mundo"; + auto hmac = HMAC!SHA1("My s3cR3T keY".representation); + hmac.put(data1.representation) + .put(data2.representation); + static immutable expected = [ + 197, 57, 52, 3, 13, 194, 13, + 36, 117, 228, 8, 11, 111, 51, + 165, 3, 123, 31, 251, 113]; + assert(hmac.finish() == expected); + } + + /** + * Resets the digest and returns the finished hash. + */ + + DigestType!H finish() + { + ubyte[blockSize / 8] opad = void; + foreach (immutable i; 0 .. blockSize / 8) + opad[i] = key[i] ^ 0x5c; + + auto tmp = digest.finish(); + + digest.start(); + digest.put(opad[]); + digest.put(tmp); + auto result = digest.finish(); + start(); // reset the digest + return result; + } + + /// + @safe pure nothrow @nogc unittest + { + import std.digest.sha, std.digest.hmac; + import std.string : representation; + string data1 = "Hello, world", data2 = "Hola mundo"; + auto hmac = HMAC!SHA1("My s3cR3T keY".representation); + auto digest = hmac.put(data1.representation) + .put(data2.representation) + .finish(); + static immutable expected = [ + 197, 57, 52, 3, 13, 194, 13, + 36, 117, 228, 8, 11, 111, 51, + 165, 3, 123, 31, 251, 113]; + assert(digest == expected); + } +} + +/// Convenience constructor for $(LREF HMAC). +template hmac(H) +if (isDigest!H && hasBlockSize!H) +{ + alias hmac = hmac!(H, H.blockSize); +} + +/// ditto +template hmac(H, size_t blockSize) +if (isDigest!H) +{ + /** + * Constructs an HMAC digest with the specified secret. + * + * Returns: + * An instance of HMAC that can be fed data as desired, and finished + * to compute the final hash when done. + */ + auto hmac(scope const(ubyte)[] secret) + { + return HMAC!(H, blockSize)(secret); + } + + /// + @safe pure nothrow @nogc unittest + { + import std.digest.sha, std.digest.hmac; + import std.string : representation; + string data1 = "Hello, world", data2 = "Hola mundo"; + auto digest = hmac!SHA1("My s3cR3T keY".representation) + .put(data1.representation) + .put(data2.representation) + .finish(); + static immutable expected = [ + 197, 57, 52, 3, 13, 194, 13, 36, + 117, 228, 8, 11, 111, 51, 165, + 3, 123, 31, 251, 113]; + assert(digest == expected); + } + + /** + * Computes an _HMAC digest over the given range of data with the + * specified secret. + * + * Returns: + * The final _HMAC hash. + */ + DigestType!H hmac(T...)(scope T data, scope const(ubyte)[] secret) + if (allSatisfy!(isDigestibleRange, typeof(data))) + { + import std.algorithm.mutation : copy; + auto hash = HMAC!(H, blockSize)(secret); + foreach (datum; data) + copy(datum, &hash); + return hash.finish(); + } + + /// + @system pure nothrow @nogc unittest + { + import std.digest.sha, std.digest.hmac; + import std.string : representation; + import std.algorithm.iteration : map; + string data = "Hello, world"; + auto digest = data.representation + .map!(a => cast(ubyte)(a+1)) + .hmac!SHA1("My s3cR3T keY".representation); + static assert(is(typeof(digest) == ubyte[20])); + static immutable expected = [ + 163, 208, 118, 179, 216, 93, + 17, 10, 84, 200, 87, 104, 244, + 111, 136, 214, 167, 210, 58, 10]; + assert(digest == expected); + } +} + +version(unittest) +{ + import std.digest.digest : toHexString, LetterCase; + alias hex = toHexString!(LetterCase.lower); +} + +@safe pure nothrow @nogc +unittest +{ + import std.digest.md : MD5; + import std.range : isOutputRange; + static assert(isOutputRange!(HMAC!MD5, ubyte)); + static assert(isDigest!(HMAC!MD5)); + static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == MD5.blockSize); +} + +@system pure nothrow +unittest +{ + import std.digest.md : MD5; + import std.digest.sha : SHA1, SHA256; + + ubyte[] nada; + assert(hmac!MD5 (nada, nada).hex == "74e6f7298a9c2d168935f58c001bad88"); + assert(hmac!SHA1 (nada, nada).hex == "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d"); + assert(hmac!SHA256(nada, nada).hex == "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"); + + import std.string : representation; + auto key = "key".representation, + long_key = ("012345678901234567890123456789012345678901" + ~"234567890123456789012345678901234567890123456789").representation, + data1 = "The quick brown fox ".representation, + data2 = "jumps over the lazy dog".representation, + data = data1 ~ data2; + + assert(data.hmac!MD5 (key).hex == "80070713463e7749b90c2dc24911e275"); + assert(data.hmac!SHA1 (key).hex == "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"); + assert(data.hmac!SHA256(key).hex == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"); + + assert(data.hmac!MD5 (long_key).hex == "e1728d68e05beae186ea768561963778"); + assert(data.hmac!SHA1 (long_key).hex == "560d3cd77316e57ab4bba0c186966200d2b37ba3"); + assert(data.hmac!SHA256(long_key).hex == "a1b0065a5d1edd93152c677e1bc1b1e3bc70d3a76619842e7f733f02b8135c04"); + + assert(hmac!MD5 (key).put(data1).put(data2).finish == data.hmac!MD5 (key)); + assert(hmac!SHA1 (key).put(data1).put(data2).finish == data.hmac!SHA1 (key)); + assert(hmac!SHA256(key).put(data1).put(data2).finish == data.hmac!SHA256(key)); +} diff --git a/std/digest/md.d b/std/digest/md.d index c2bb6f8b739..8ef7bbcb583 100644 --- a/std/digest/md.d +++ b/std/digest/md.d @@ -24,7 +24,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of)) * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone * module. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * * CTFE: * Digests do not work in CTFE @@ -38,8 +38,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of)) * * Source: $(PHOBOSSRC std/digest/_md.d) * - * Macros: - * WIKI = Phobos/StdMd5 */ /* md5.d - RSA Data Security, Inc., MD5 message-digest algorithm @@ -50,7 +48,7 @@ module std.digest.md; public import std.digest.digest; /// -unittest +@safe unittest { //Template API import std.digest.md; @@ -66,7 +64,7 @@ unittest } /// -unittest +@safe unittest { //OOP API import std.digest.md; @@ -192,14 +190,16 @@ struct MD5 version(BigEndian) { - for(size_t i = 0; i < 16; i++) + import std.bitmanip : littleEndianToNative; + + for (size_t i = 0; i < 16; i++) { x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&(*block)[i*4]); } } else { - (cast(ubyte*)x.ptr)[0 .. 64] = (cast(ubyte*)block)[0 .. 64]; + (cast(ubyte*) x.ptr)[0 .. 64] = (cast(ubyte*) block)[0 .. 64]; } //Round 1 @@ -284,16 +284,18 @@ struct MD5 } public: + enum blockSize = 512; + /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). * - * Examples: + * Example: * ---- * MD5 dig; - * dig.put(cast(ubyte)0); //single ubyte - * dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + * dig.put(cast(ubyte) 0); //single ubyte + * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic * ubyte[10] buf; * dig.put(buf); //buffer * ---- @@ -317,7 +319,7 @@ struct MD5 (&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen]; transform(&_buffer); - for(i = partLen; i + 63 < inputLen; i += 64) + for (i = partLen; i + 63 < inputLen; i += 64) { transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr)); } @@ -343,7 +345,7 @@ struct MD5 * * Generic code which deals with different Digest types should always call start though. * - * Examples: + * Example: * -------- * MD5 digest; * //digest.start(); //Not necessary @@ -389,18 +391,18 @@ struct MD5 return data; } /// - unittest + @safe unittest { //Simple example MD5 hash; hash.start(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); ubyte[16] result = hash.finish(); } } /// -unittest +@safe unittest { //Simple example, hashing a string using md5Of helper function ubyte[16] hash = md5Of("abc"); @@ -409,7 +411,7 @@ unittest } /// -unittest +@safe unittest { //Using the basic API MD5 hash; @@ -421,12 +423,13 @@ unittest } /// -unittest +@safe unittest { //Let's use the template features: - void doSomething(T)(ref T hash) if(isDigest!T) + void doSomething(T)(ref T hash) + if (isDigest!T) { - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); } MD5 md5; md5.start(); @@ -434,12 +437,12 @@ unittest assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71"); } -unittest +@safe unittest { assert(isDigest!MD5); } -unittest +@system unittest { import std.range; @@ -449,48 +452,48 @@ unittest md5.put(cast(ubyte[])"abcdef"); md5.start(); md5.put(cast(ubyte[])""); - assert(md5.finish() == cast(ubyte[])x"d41d8cd98f00b204e9800998ecf8427e"); + assert(md5.finish() == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e"); digest = md5Of(""); - assert(digest == cast(ubyte[])x"d41d8cd98f00b204e9800998ecf8427e"); + assert(digest == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e"); digest = md5Of("a"); - assert(digest == cast(ubyte[])x"0cc175b9c0f1b6a831c399e269772661"); + assert(digest == cast(ubyte[]) x"0cc175b9c0f1b6a831c399e269772661"); digest = md5Of("abc"); - assert(digest == cast(ubyte[])x"900150983cd24fb0d6963f7d28e17f72"); + assert(digest == cast(ubyte[]) x"900150983cd24fb0d6963f7d28e17f72"); digest = md5Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[])x"8215ef0796a20bcaaae116d3876c664a"); + assert(digest == cast(ubyte[]) x"8215ef0796a20bcaaae116d3876c664a"); digest = md5Of("message digest"); - assert(digest == cast(ubyte[])x"f96b697d7cb7938d525a2f31aaf161d0"); + assert(digest == cast(ubyte[]) x"f96b697d7cb7938d525a2f31aaf161d0"); digest = md5Of("abcdefghijklmnopqrstuvwxyz"); - assert(digest == cast(ubyte[])x"c3fcd3d76192e4007dfb496cca67e13b"); + assert(digest == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b"); digest = md5Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[])x"d174ab98d277d9f5a5611c2c9f419d9f"); + assert(digest == cast(ubyte[]) x"d174ab98d277d9f5a5611c2c9f419d9f"); digest = md5Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[])x"57edf4a22be3c955ac49da2e2107b67a"); + assert(digest == cast(ubyte[]) x"57edf4a22be3c955ac49da2e2107b67a"); - assert(toHexString(cast(ubyte[16])x"c3fcd3d76192e4007dfb496cca67e13b") + assert(toHexString(cast(ubyte[16]) x"c3fcd3d76192e4007dfb496cca67e13b") == "C3FCD3D76192E4007DFB496CCA67E13B"); ubyte[] onemilliona = new ubyte[1000000]; onemilliona[] = 'a'; digest = md5Of(onemilliona); - assert(digest == cast(ubyte[])x"7707D6AE4E027C70EEA2A935C2296F21"); + assert(digest == cast(ubyte[]) x"7707D6AE4E027C70EEA2A935C2296F21"); auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); digest = md5Of(oneMillionRange); - assert(digest == cast(ubyte[])x"7707D6AE4E027C70EEA2A935C2296F21"); + assert(digest == cast(ubyte[]) x"7707D6AE4E027C70EEA2A935C2296F21"); } /** - * This is a convenience alias for $(XREF digest.digest, digest) using the + * This is a convenience alias for $(REF digest, std,digest,digest) using the * MD5 implementation. */ //simple alias doesn't work here, hope this gets inlined... @@ -500,7 +503,7 @@ auto md5Of(T...)(T data) } /// -unittest +@safe unittest { ubyte[16] hash = md5Of("abc"); assert(hash == digest!MD5("abc")); @@ -510,13 +513,13 @@ unittest * OOP API MD5 implementation. * See $(D std.digest.digest) for differences between template and OOP API. * - * This is an alias for $(XREF digest.digest, WrapperDigest)!MD5, see - * $(XREF digest.digest, WrapperDigest) for more information. + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!MD5), see + * there for more information. */ alias MD5Digest = WrapperDigest!MD5; /// -unittest +@safe unittest { //Simple example, hashing a string using Digest.digest helper function auto md5 = new MD5Digest(); @@ -526,12 +529,12 @@ unittest } /// -unittest +@system unittest { //Let's use the OOP features: void test(Digest dig) { - dig.put(cast(ubyte)0); + dig.put(cast(ubyte) 0); } auto md5 = new MD5Digest(); test(md5); @@ -542,19 +545,19 @@ unittest assert(toHexString(result) == "93B885ADFE0DA089CDF634904FD59F71"); } -unittest +@system unittest { auto md5 = new MD5Digest(); md5.put(cast(ubyte[])"abcdef"); md5.reset(); md5.put(cast(ubyte[])""); - assert(md5.finish() == cast(ubyte[])x"d41d8cd98f00b204e9800998ecf8427e"); + assert(md5.finish() == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e"); md5.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); ubyte[20] result; auto result2 = md5.finish(result[]); - assert(result[0 .. 16] == result2 && result2 == cast(ubyte[])x"c3fcd3d76192e4007dfb496cca67e13b"); + assert(result[0 .. 16] == result2 && result2 == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b"); debug { @@ -564,24 +567,24 @@ unittest assert(md5.length == 16); - assert(md5.digest("") == cast(ubyte[])x"d41d8cd98f00b204e9800998ecf8427e"); + assert(md5.digest("") == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e"); - assert(md5.digest("a") == cast(ubyte[])x"0cc175b9c0f1b6a831c399e269772661"); + assert(md5.digest("a") == cast(ubyte[]) x"0cc175b9c0f1b6a831c399e269772661"); - assert(md5.digest("abc") == cast(ubyte[])x"900150983cd24fb0d6963f7d28e17f72"); + assert(md5.digest("abc") == cast(ubyte[]) x"900150983cd24fb0d6963f7d28e17f72"); assert(md5.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[])x"8215ef0796a20bcaaae116d3876c664a"); + == cast(ubyte[]) x"8215ef0796a20bcaaae116d3876c664a"); - assert(md5.digest("message digest") == cast(ubyte[])x"f96b697d7cb7938d525a2f31aaf161d0"); + assert(md5.digest("message digest") == cast(ubyte[]) x"f96b697d7cb7938d525a2f31aaf161d0"); assert(md5.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[])x"c3fcd3d76192e4007dfb496cca67e13b"); + == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b"); assert(md5.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[])x"d174ab98d277d9f5a5611c2c9f419d9f"); + == cast(ubyte[]) x"d174ab98d277d9f5a5611c2c9f419d9f"); assert(md5.digest("1234567890123456789012345678901234567890", "1234567890123456789012345678901234567890") - == cast(ubyte[])x"57edf4a22be3c955ac49da2e2107b67a"); + == cast(ubyte[]) x"57edf4a22be3c955ac49da2e2107b67a"); } diff --git a/std/digest/murmurhash.d b/std/digest/murmurhash.d new file mode 100644 index 00000000000..89b4b1cd476 --- /dev/null +++ b/std/digest/murmurhash.d @@ -0,0 +1,751 @@ +/** +Computes $(LINK2 https://en.wikipedia.org/wiki/MurmurHash, MurmurHash) hashes +of arbitrary data. MurmurHash is a non-cryptographic hash function suitable +for general hash-based lookup. It is optimized for x86 but can be used on +all architectures. + +The current version is MurmurHash3, which yields a 32-bit or 128-bit hash value. +The older MurmurHash 1 and 2 are currently not supported. + +MurmurHash3 comes in three flavors, listed in increasing order of throughput: +$(UL +$(LI $(D MurmurHash3!32) produces a 32-bit value and is optimized for 32-bit architectures) +$(LI $(D MurmurHash3!(128, 32)) produces a 128-bit value and is optimized for 32-bit architectures) +$(LI $(D MurmurHash3!(128, 64)) produces a 128-bit value and is optimized for 64-bit architectures) +) + +Note: +$(UL +$(LI $(D MurmurHash3!(128, 32)) and $(D MurmurHash3!(128, 64)) produce different values.) +$(LI The current implementation is optimized for little endian architectures. + It will exhibit different results on big endian architectures and a slightly + less uniform distribution.) +) + +This module conforms to the APIs defined in $(D std.digest.digest). + +This module publicly imports $(D std.digest.digest) and can be used as a stand-alone module. + +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: Guillaume Chatelet +References: $(LINK2 https://github.com/aappleby/smhasher, Reference implementation) +$(BR) $(LINK2 https://en.wikipedia.org/wiki/MurmurHash, Wikipedia) +*/ +/* Copyright Guillaume Chatelet 2016. + * Distributed under the Boost Software License, Version 1.0. + * (See LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +module std.digest.murmurhash; + +/// +@safe unittest +{ + // MurmurHash3!32, MurmurHash3!(128, 32) and MurmurHash3!(128, 64) implement + // the std.digest.digest Template API. + static assert(isDigest!(MurmurHash3!32)); + // The convenient digest template allows for quick hashing of any data. + ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]); +} + +/// +@safe unittest +{ + // One can also hash ubyte data piecewise by instanciating a hasher and call + // the 'put' method. + const(ubyte)[] data1 = [1, 2, 3]; + const(ubyte)[] data2 = [4, 5, 6, 7]; + // The incoming data will be buffered and hashed element by element. + MurmurHash3!32 hasher; + hasher.put(data1); + hasher.put(data2); + // The call to 'finish' ensures: + // - the remaining bits are processed + // - the hash gets finalized + auto hashed = hasher.finish(); +} + +/// +@safe unittest +{ + // Using `putElements`, `putRemainder` and `finalize` you gain full + // control over which part of the algorithm to run. + // This allows for maximum throughput but needs extra care. + + // Data type must be the same as the hasher's element type: + // - uint for MurmurHash3!32 + // - uint[4] for MurmurHash3!(128, 32) + // - ulong[2] for MurmurHash3!(128, 64) + const(uint)[] data = [1, 2, 3, 4]; + // Note the hasher starts with 'Fast'. + MurmurHash3!32 hasher; + // Push as many array of elements as you need. The less calls the better. + hasher.putElements(data); + // Put remainder bytes if needed. This method can be called only once. + hasher.putRemainder(ubyte(1), ubyte(1), ubyte(1)); + // Call finalize to incorporate data length in the hash. + hasher.finalize(); + // Finally get the hashed value. + auto hashed = hasher.getBytes(); +} + +public import std.digest.digest; + +@safe: + +/* +Performance notes: + - To help a bit with the performance when compiling with DMD some + functions have been rewritten to pass by value instead of by reference. + - GDC and LDC are on par with their C++ counterpart. + - DMD is typically between 20% to 50% of the GCC version. +*/ + +/++ + + Implements the MurmurHash3 functions. You can specify the `size` of the + + hash in bit. For 128 bit hashes you can specify whether to optimize for 32 + + or 64 bit architectures. If you don't specify the `opt` value it will select + + the fastest version of the host platform. + + + + This hasher is compatible with the `Digest` API: + + $(UL + + $(LI `void start()`) + + $(LI `void put(scope const(ubyte)[] data...)`) + + $(LI `ubyte[Element.sizeof] finish()`) + + ) + + + + It also provides a faster, low level API working with data of size + + `Element.sizeof`: + + $(UL + + $(LI `void putElements(scope const(Element[]) elements...)`) + + $(LI `void putRemainder(scope const(ubyte[]) data...)`) + + $(LI `void finalize()`) + + $(LI `Element get()`) + + $(LI `ubyte[Element.sizeof] getBytes()`) + + ) + +/ +struct MurmurHash3(uint size /* 32 or 128 */ , uint opt = size_t.sizeof == 8 ? 64 : 32) +{ + enum blockSize = size; // Number of bits of the hashed value. + size_t element_count; // The number of full elements pushed, this is used for finalization. + + static if (size == 32) + { + private enum uint c1 = 0xcc9e2d51; + private enum uint c2 = 0x1b873593; + private uint h1; + alias Element = uint; /// The element type for 32-bit implementation. + + this(uint seed) + { + h1 = seed; + } + /++ + Adds a single Element of data without increasing `element_count`. + Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`. + +/ + void putElement(uint block) pure nothrow @nogc + { + h1 = update(h1, block, 0, c1, c2, 15, 13, 0xe6546b64U); + } + + /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`. + void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc + { + assert(data.length < Element.sizeof); + assert(data.length >= 0); + element_count += data.length; + uint k1 = 0; + final switch (data.length & 3) + { + case 3: + k1 ^= data[2] << 16; + goto case; + case 2: + k1 ^= data[1] << 8; + goto case; + case 1: + k1 ^= data[0]; + h1 ^= shuffle(k1, c1, c2, 15); + goto case; + case 0: + } + } + + /// Incorporate `element_count` and finalizes the hash. + void finalize() pure nothrow @nogc + { + h1 ^= element_count; + h1 = fmix(h1); + } + + /// Returns the hash as an uint value. + Element get() pure nothrow @nogc + { + return h1; + } + + /// Returns the current hashed value as an ubyte array. + ubyte[4] getBytes() pure nothrow @nogc + { + return cast(typeof(return)) cast(uint[1])[get()]; + } + } + else static if (size == 128 && opt == 32) + { + private enum uint c1 = 0x239b961b; + private enum uint c2 = 0xab0e9789; + private enum uint c3 = 0x38b34ae5; + private enum uint c4 = 0xa1e38b93; + private uint h4, h3, h2, h1; + + alias Element = uint[4]; /// The element type for 128-bit implementation. + + this(uint seed4, uint seed3, uint seed2, uint seed1) pure nothrow @nogc + { + h4 = seed4; + h3 = seed3; + h2 = seed2; + h1 = seed1; + } + + this(uint seed) pure nothrow @nogc + { + h4 = h3 = h2 = h1 = seed; + } + + /++ + Adds a single Element of data without increasing element_count. + Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`. + +/ + void putElement(Element block) pure nothrow @nogc + { + h1 = update(h1, block[0], h2, c1, c2, 15, 19, 0x561ccd1bU); + h2 = update(h2, block[1], h3, c2, c3, 16, 17, 0x0bcaa747U); + h3 = update(h3, block[2], h4, c3, c4, 17, 15, 0x96cd1c35U); + h4 = update(h4, block[3], h1, c4, c1, 18, 13, 0x32ac3b17U); + } + + /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`. + void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc + { + assert(data.length < Element.sizeof); + assert(data.length >= 0); + element_count += data.length; + uint k1 = 0; + uint k2 = 0; + uint k3 = 0; + uint k4 = 0; + + final switch (data.length & 15) + { + case 15: + k4 ^= data[14] << 16; + goto case; + case 14: + k4 ^= data[13] << 8; + goto case; + case 13: + k4 ^= data[12] << 0; + h4 ^= shuffle(k4, c4, c1, 18); + goto case; + case 12: + k3 ^= data[11] << 24; + goto case; + case 11: + k3 ^= data[10] << 16; + goto case; + case 10: + k3 ^= data[9] << 8; + goto case; + case 9: + k3 ^= data[8] << 0; + h3 ^= shuffle(k3, c3, c4, 17); + goto case; + case 8: + k2 ^= data[7] << 24; + goto case; + case 7: + k2 ^= data[6] << 16; + goto case; + case 6: + k2 ^= data[5] << 8; + goto case; + case 5: + k2 ^= data[4] << 0; + h2 ^= shuffle(k2, c2, c3, 16); + goto case; + case 4: + k1 ^= data[3] << 24; + goto case; + case 3: + k1 ^= data[2] << 16; + goto case; + case 2: + k1 ^= data[1] << 8; + goto case; + case 1: + k1 ^= data[0] << 0; + h1 ^= shuffle(k1, c1, c2, 15); + goto case; + case 0: + } + } + + /// Incorporate `element_count` and finalizes the hash. + void finalize() pure nothrow @nogc + { + h1 ^= element_count; + h2 ^= element_count; + h3 ^= element_count; + h4 ^= element_count; + + h1 += h2; + h1 += h3; + h1 += h4; + h2 += h1; + h3 += h1; + h4 += h1; + + h1 = fmix(h1); + h2 = fmix(h2); + h3 = fmix(h3); + h4 = fmix(h4); + + h1 += h2; + h1 += h3; + h1 += h4; + h2 += h1; + h3 += h1; + h4 += h1; + } + + /// Returns the hash as an uint[4] value. + Element get() pure nothrow @nogc + { + return [h1, h2, h3, h4]; + } + + /// Returns the current hashed value as an ubyte array. + ubyte[16] getBytes() pure nothrow @nogc + { + return cast(typeof(return)) get(); + } + } + else static if (size == 128 && opt == 64) + { + private enum ulong c1 = 0x87c37b91114253d5; + private enum ulong c2 = 0x4cf5ad432745937f; + private ulong h2, h1; + + alias Element = ulong[2]; /// The element type for 128-bit implementation. + + this(ulong seed) pure nothrow @nogc + { + h2 = h1 = seed; + } + + this(ulong seed2, ulong seed1) pure nothrow @nogc + { + h2 = seed2; + h1 = seed1; + } + + /++ + Adds a single Element of data without increasing `element_count`. + Make sure to increase `element_count` by `Element.sizeof` for each call to `putElement`. + +/ + void putElement(Element block) pure nothrow @nogc + { + h1 = update(h1, block[0], h2, c1, c2, 31, 27, 0x52dce729U); + h2 = update(h2, block[1], h1, c2, c1, 33, 31, 0x38495ab5U); + } + + /// Put remainder bytes. This must be called only once after `putElement` and before `finalize`. + void putRemainder(scope const(ubyte[]) data...) pure nothrow @nogc + { + assert(data.length < Element.sizeof); + assert(data.length >= 0); + element_count += data.length; + ulong k1 = 0; + ulong k2 = 0; + final switch (data.length & 15) + { + case 15: + k2 ^= ulong(data[14]) << 48; + goto case; + case 14: + k2 ^= ulong(data[13]) << 40; + goto case; + case 13: + k2 ^= ulong(data[12]) << 32; + goto case; + case 12: + k2 ^= ulong(data[11]) << 24; + goto case; + case 11: + k2 ^= ulong(data[10]) << 16; + goto case; + case 10: + k2 ^= ulong(data[9]) << 8; + goto case; + case 9: + k2 ^= ulong(data[8]) << 0; + h2 ^= shuffle(k2, c2, c1, 33); + goto case; + case 8: + k1 ^= ulong(data[7]) << 56; + goto case; + case 7: + k1 ^= ulong(data[6]) << 48; + goto case; + case 6: + k1 ^= ulong(data[5]) << 40; + goto case; + case 5: + k1 ^= ulong(data[4]) << 32; + goto case; + case 4: + k1 ^= ulong(data[3]) << 24; + goto case; + case 3: + k1 ^= ulong(data[2]) << 16; + goto case; + case 2: + k1 ^= ulong(data[1]) << 8; + goto case; + case 1: + k1 ^= ulong(data[0]) << 0; + h1 ^= shuffle(k1, c1, c2, 31); + goto case; + case 0: + } + } + + /// Incorporate `element_count` and finalizes the hash. + void finalize() pure nothrow @nogc + { + h1 ^= element_count; + h2 ^= element_count; + + h1 += h2; + h2 += h1; + h1 = fmix(h1); + h2 = fmix(h2); + h1 += h2; + h2 += h1; + } + + /// Returns the hash as an ulong[2] value. + Element get() pure nothrow @nogc + { + return [h1, h2]; + } + + /// Returns the current hashed value as an ubyte array. + ubyte[16] getBytes() pure nothrow @nogc + { + return cast(typeof(return)) get(); + } + } + else + { + alias Element = char; // This is needed to trigger the following error message. + static assert(false, "MurmurHash3(" ~ size.stringof ~ ", " ~ opt.stringof ~ ") is not implemented"); + } + + /++ + Pushes an array of elements at once. It is more efficient to push as much data as possible in a single call. + On platforms that do not support unaligned reads (MIPS or old ARM chips), the compiler may produce slower code to ensure correctness. + +/ + void putElements(scope const(Element[]) elements...) pure nothrow @nogc + { + foreach (const block; elements) + { + putElement(block); + } + element_count += elements.length * Element.sizeof; + } + + //------------------------------------------------------------------------- + // Implementation of the Digest API. + //------------------------------------------------------------------------- + + private union BufferUnion + { + Element block; + ubyte[Element.sizeof] data; + } + + private BufferUnion buffer; + private size_t bufferSize; + + @disable this(this); + + // Initialize + void start() + { + this = this.init; + } + + /++ + Adds data to the digester. This function can be called many times in a row + after start but before finish. + +/ + void put(scope const(ubyte)[] data...) pure nothrow + { + // Buffer should never be full while entering this function. + assert(bufferSize < Element.sizeof); + + // Check if we have some leftover data in the buffer. Then fill the first block buffer. + if (bufferSize + data.length < Element.sizeof) + { + buffer.data[bufferSize .. bufferSize + data.length] = data[]; + bufferSize += data.length; + return; + } + const bufferLeeway = Element.sizeof - bufferSize; + assert(bufferLeeway <= Element.sizeof); + buffer.data[bufferSize .. $] = data[0 .. bufferLeeway]; + putElement(buffer.block); + data = data[bufferLeeway .. $]; + + // Do main work: process chunks of `Element.sizeof` bytes. + const numElements = data.length / Element.sizeof; + const remainderStart = numElements * Element.sizeof; + foreach (ref const Element block; cast(const(Element[]))(data[0 .. remainderStart])) + { + putElement(block); + } + // +1 for bufferLeeway Element. + element_count += (numElements + 1) * Element.sizeof; + data = data[remainderStart .. $]; + + // Now add remaining data to buffer. + assert(data.length < Element.sizeof); + bufferSize = data.length; + buffer.data[0 .. data.length] = data[]; + } + + /++ + Finalizes the computation of the hash and returns the computed value. + Note that $(D finish) can be called only once and that no subsequent calls + to $(D put) is allowed. + +/ + ubyte[Element.sizeof] finish() pure nothrow + { + auto tail = buffer.data[0 .. bufferSize]; + if (tail.length > 0) + { + putRemainder(tail); + } + finalize(); + return getBytes(); + } + + //------------------------------------------------------------------------- + // MurmurHash3 utils + //------------------------------------------------------------------------- + + private T rotl(T)(T x, uint y) + in + { + import std.traits : isUnsigned; + + static assert(isUnsigned!T); + debug assert(y >= 0 && y <= (T.sizeof * 8)); + } + body + { + return ((x << y) | (x >> ((T.sizeof * 8) - y))); + } + + private T shuffle(T)(T k, T c1, T c2, ubyte r1) + { + import std.traits : isUnsigned; + + static assert(isUnsigned!T); + k *= c1; + k = rotl(k, r1); + k *= c2; + return k; + } + + private T update(T)(ref T h, T k, T mixWith, T c1, T c2, ubyte r1, ubyte r2, T n) + { + import std.traits : isUnsigned; + + static assert(isUnsigned!T); + h ^= shuffle(k, c1, c2, r1); + h = rotl(h, r2); + h += mixWith; + return h * 5 + n; + } + + private uint fmix(uint h) pure nothrow @nogc + { + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; + } + + private ulong fmix(ulong k) pure nothrow @nogc + { + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return k; + } +} + +version (unittest) +{ + import std.string : representation; + + private auto hash(H, Element = H.Element)(string data) + { + H hasher; + immutable elements = data.length / Element.sizeof; + hasher.putElements(cast(const(Element)[]) data[0 .. elements * Element.sizeof]); + hasher.putRemainder(cast(const(ubyte)[]) data[elements * Element.sizeof .. $]); + hasher.finalize(); + return hasher.getBytes(); + } + + private void checkResult(H)(in string[string] groundtruth) + { + foreach (data, expectedHash; groundtruth) + { + assert(data.digest!H.toHexString() == expectedHash); + assert(data.hash!H.toHexString() == expectedHash); + H hasher; + foreach (element; data) + { + hasher.put(element); + } + assert(hasher.finish.toHexString() == expectedHash); + } + } +} + +@safe unittest +{ + // dfmt off + checkResult!(MurmurHash3!32)([ + "" : "00000000", + "a" : "B269253C", + "ab" : "5FD7BF9B", + "abc" : "FA93DDB3", + "abcd" : "6A67ED43", + "abcde" : "F69A9BE8", + "abcdef" : "85C08161", + "abcdefg" : "069B3C88", + "abcdefgh" : "C4CCDD49", + "abcdefghi" : "F0061442", + "abcdefghij" : "91779288", + "abcdefghijk" : "DF253B5F", + "abcdefghijkl" : "273D6FA3", + "abcdefghijklm" : "1B1612F2", + "abcdefghijklmn" : "F06D52F8", + "abcdefghijklmno" : "D2F7099D", + "abcdefghijklmnop" : "ED9162E7", + "abcdefghijklmnopq" : "4A5E65B6", + "abcdefghijklmnopqr" : "94A819C2", + "abcdefghijklmnopqrs" : "C15BBF85", + "abcdefghijklmnopqrst" : "9A711CBE", + "abcdefghijklmnopqrstu" : "ABE7195A", + "abcdefghijklmnopqrstuv" : "C73CB670", + "abcdefghijklmnopqrstuvw" : "1C4D1EA5", + "abcdefghijklmnopqrstuvwx" : "3939F9B0", + "abcdefghijklmnopqrstuvwxy" : "1A568338", + "abcdefghijklmnopqrstuvwxyz" : "6D034EA3"]); + // dfmt on +} + +@safe unittest +{ + // dfmt off + checkResult!(MurmurHash3!(128,32))([ + "" : "00000000000000000000000000000000", + "a" : "3C9394A71BB056551BB056551BB05655", + "ab" : "DF5184151030BE251030BE251030BE25", + "abc" : "D1C6CD75A506B0A2A506B0A2A506B0A2", + "abcd" : "AACCB6962EC6AF452EC6AF452EC6AF45", + "abcde" : "FB2E40C5BCC5245D7701725A7701725A", + "abcdef" : "0AB97CE12127AFA1F9DFBEA9F9DFBEA9", + "abcdefg" : "D941B590DE3A86092869774A2869774A", + "abcdefgh" : "3611F4AE8714B1AD92806CFA92806CFA", + "abcdefghi" : "1C8C05AD6F590622107DD2147C4194DD", + "abcdefghij" : "A72ED9F50E90379A2AAA92C77FF12F69", + "abcdefghijk" : "DDC9C8A01E111FCA2DF1FE8257975EBD", + "abcdefghijkl" : "FE038573C02482F4ADDFD42753E58CD2", + "abcdefghijklm" : "15A23AC1ECA1AEDB66351CF470DE2CD9", + "abcdefghijklmn" : "8E11EC75D71F5D60F4456F944D89D4F1", + "abcdefghijklmno" : "691D6DEEAED51A4A5714CE84A861A7AD", + "abcdefghijklmnop" : "2776D29F5612B990218BCEE445BA93D1", + "abcdefghijklmnopq" : "D3A445046F5C51642ADC6DD99D07111D", + "abcdefghijklmnopqr" : "AA5493A0DA291D966A9E7128585841D9", + "abcdefghijklmnopqrs" : "281B6A4F9C45B9BFC3B77850930F2C20", + "abcdefghijklmnopqrst" : "19342546A8216DB62873B49E545DCB1F", + "abcdefghijklmnopqrstu" : "A6C0F30D6C738620E7B9590D2E088D99", + "abcdefghijklmnopqrstuv" : "A7D421D9095CDCEA393CBBA908342384", + "abcdefghijklmnopqrstuvw" : "C3A93D572B014949317BAD7EE809158F", + "abcdefghijklmnopqrstuvwx" : "802381D77956833791F87149326E4801", + "abcdefghijklmnopqrstuvwxy" : "0AC619A5302315755A80D74ADEFAA842", + "abcdefghijklmnopqrstuvwxyz" : "1306343E662F6F666E56F6172C3DE344"]); + // dfmt on +} + +@safe unittest +{ + // dfmt off + checkResult!(MurmurHash3!(128,64))([ + "" : "00000000000000000000000000000000", + "a" : "897859F6655555855A890E51483AB5E6", + "ab" : "2E1BED16EA118B93ADD4529B01A75EE6", + "abc" : "6778AD3F3F3F96B4522DCA264174A23B", + "abcd" : "4FCD5646D6B77BB875E87360883E00F2", + "abcde" : "B8BB96F491D036208CECCF4BA0EEC7C5", + "abcdef" : "55BFA3ACBF867DE45C842133990971B0", + "abcdefg" : "99E49EC09F2FCDA6B6BB55B13AA23A1C", + "abcdefgh" : "028CEF37B00A8ACCA14069EB600D8948", + "abcdefghi" : "64793CF1CFC0470533E041B7F53DB579", + "abcdefghij" : "998C2F770D5BC1B6C91A658CDC854DA2", + "abcdefghijk" : "029D78DFB8D095A871E75A45E2317CBB", + "abcdefghijkl" : "94E17AE6B19BF38E1C62FF7232309E1F", + "abcdefghijklm" : "73FAC0A78D2848167FCCE70DFF7B652E", + "abcdefghijklmn" : "E075C3F5A794D09124336AD2276009EE", + "abcdefghijklmno" : "FB2F0C895124BE8A612A969C2D8C546A", + "abcdefghijklmnop" : "23B74C22A33CCAC41AEB31B395D63343", + "abcdefghijklmnopq" : "57A6BD887F746475E40D11A19D49DAEC", + "abcdefghijklmnopqr" : "508A7F90EC8CF0776BC7005A29A8D471", + "abcdefghijklmnopqrs" : "886D9EDE23BC901574946FB62A4D8AA6", + "abcdefghijklmnopqrst" : "F1E237F926370B314BD016572AF40996", + "abcdefghijklmnopqrstu" : "3CC9FF79E268D5C9FB3C9BE9C148CCD7", + "abcdefghijklmnopqrstuv" : "56F8ABF430E388956DA9F4A8741FDB46", + "abcdefghijklmnopqrstuvw" : "8E234F9DBA0A4840FFE9541CEBB7BE83", + "abcdefghijklmnopqrstuvwx" : "F72CDED40F96946408F22153A3CF0F79", + "abcdefghijklmnopqrstuvwxy" : "0F96072FA4CBE771DBBD9E398115EEED", + "abcdefghijklmnopqrstuvwxyz" : "A94A6F517E9D9C7429D5A7B6899CADE9"]); + // dfmt on +} + +@safe unittest +{ + // Pushing unaligned data and making sure the result is still coherent. + void testUnalignedHash(H)() + { + immutable ubyte[1025] data = 0xAC; + immutable alignedHash = digest!H(data[0 .. $ - 1]); // 0 .. 1023 + immutable unalignedHash = digest!H(data[1 .. $]); // 1 .. 1024 + assert(alignedHash == unalignedHash); + } + + testUnalignedHash!(MurmurHash3!32)(); + testUnalignedHash!(MurmurHash3!(128, 32))(); + testUnalignedHash!(MurmurHash3!(128, 64))(); +} diff --git a/std/digest/ripemd.d b/std/digest/ripemd.d index d8d50b521de..0dd472c03e4 100644 --- a/std/digest/ripemd.d +++ b/std/digest/ripemd.d @@ -24,7 +24,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of)) * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone * module. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * * CTFE: * Digests do not work in CTFE @@ -42,8 +42,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of)) * * Source: $(PHOBOSSRC std/digest/_ripemd.d) * - * Macros: - * WIKI = Phobos/StdRipemd */ module std.digest.ripemd; @@ -51,7 +49,7 @@ module std.digest.ripemd; public import std.digest.digest; /// -unittest +@safe unittest { //Template API import std.digest.md; @@ -70,7 +68,7 @@ unittest } /// -unittest +@safe unittest { //OOP API import std.digest.md; @@ -237,14 +235,16 @@ struct RIPEMD160 version(BigEndian) { - for(size_t i = 0; i < 16; i++) + import std.bitmanip : littleEndianToNative; + + for (size_t i = 0; i < 16; i++) { x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&(*block)[i*4]); } } else { - (cast(ubyte*)x.ptr)[0 .. 64] = (cast(ubyte*)block)[0 .. 64]; + (cast(ubyte*) x.ptr)[0 .. 64] = (cast(ubyte*) block)[0 .. 64]; } /* round 1 */ @@ -440,16 +440,18 @@ struct RIPEMD160 } public: + enum blockSize = 512; + /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). * - * Examples: + * Example: * ---- * RIPEMD160 dig; - * dig.put(cast(ubyte)0); //single ubyte - * dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + * dig.put(cast(ubyte) 0); //single ubyte + * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic * ubyte[10] buf; * dig.put(buf); //buffer * ---- @@ -473,7 +475,7 @@ struct RIPEMD160 (&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen]; transform(&_buffer); - for(i = partLen; i + 63 < inputLen; i += 64) + for (i = partLen; i + 63 < inputLen; i += 64) { transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr)); } @@ -499,7 +501,7 @@ struct RIPEMD160 * * Generic code which deals with different Digest types should always call start though. * - * Examples: + * Example: * -------- * RIPEMD160 digest; * //digest.start(); //Not necessary @@ -515,12 +517,12 @@ struct RIPEMD160 * Returns the finished RIPEMD160 hash. This also calls $(LREF start) to * reset the internal state. * - * Examples: + * Example: * -------- * //Simple example * RIPEMD160 hash; * hash.start(); - * hash.put(cast(ubyte)0); + * hash.put(cast(ubyte) 0); * ubyte[20] result = hash.finish(); * assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); * -------- @@ -558,7 +560,7 @@ struct RIPEMD160 } /// -unittest +@safe unittest { //Simple example, hashing a string using ripemd160Of helper function ubyte[20] hash = ripemd160Of("abc"); @@ -567,7 +569,7 @@ unittest } /// -unittest +@safe unittest { //Using the basic API RIPEMD160 hash; @@ -579,12 +581,13 @@ unittest } /// -unittest +@safe unittest { //Let's use the template features: - void doSomething(T)(ref T hash) if(isDigest!T) + void doSomething(T)(ref T hash) + if (isDigest!T) { - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); } RIPEMD160 md; md.start(); @@ -593,22 +596,22 @@ unittest } /// -unittest +@safe unittest { //Simple example RIPEMD160 hash; hash.start(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); ubyte[20] result = hash.finish(); assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); } -unittest +@safe unittest { assert(isDigest!RIPEMD160); } -unittest +@system unittest { import std.range; @@ -618,48 +621,48 @@ unittest md.put(cast(ubyte[])"abcdef"); md.start(); md.put(cast(ubyte[])""); - assert(md.finish() == cast(ubyte[])x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); + assert(md.finish() == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); digest = ripemd160Of(""); - assert(digest == cast(ubyte[])x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); + assert(digest == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); digest = ripemd160Of("a"); - assert(digest == cast(ubyte[])x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); + assert(digest == cast(ubyte[]) x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); digest = ripemd160Of("abc"); - assert(digest == cast(ubyte[])x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); + assert(digest == cast(ubyte[]) x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); digest = ripemd160Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[])x"12a053384a9c0c88e405a06c27dcf49ada62eb2b"); + assert(digest == cast(ubyte[]) x"12a053384a9c0c88e405a06c27dcf49ada62eb2b"); digest = ripemd160Of("message digest"); - assert(digest == cast(ubyte[])x"5d0689ef49d2fae572b881b123a85ffa21595f36"); + assert(digest == cast(ubyte[]) x"5d0689ef49d2fae572b881b123a85ffa21595f36"); digest = ripemd160Of("abcdefghijklmnopqrstuvwxyz"); - assert(digest == cast(ubyte[])x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); + assert(digest == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); digest = ripemd160Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[])x"b0e20b6e3116640286ed3a87a5713079b21f5189"); + assert(digest == cast(ubyte[]) x"b0e20b6e3116640286ed3a87a5713079b21f5189"); digest = ripemd160Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[])x"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); + assert(digest == cast(ubyte[]) x"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); - assert(toHexString(cast(ubyte[20])x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc") + assert(toHexString(cast(ubyte[20]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc") == "F71C27109C692C1B56BBDCEB5B9D2865B3708DBC"); ubyte[] onemilliona = new ubyte[1000000]; onemilliona[] = 'a'; digest = ripemd160Of(onemilliona); - assert(digest == cast(ubyte[])x"52783243c1697bdbe16d37f97f68f08325dc1528"); + assert(digest == cast(ubyte[]) x"52783243c1697bdbe16d37f97f68f08325dc1528"); auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); digest = ripemd160Of(oneMillionRange); - assert(digest == cast(ubyte[])x"52783243c1697bdbe16d37f97f68f08325dc1528"); + assert(digest == cast(ubyte[]) x"52783243c1697bdbe16d37f97f68f08325dc1528"); } /** - * This is a convenience alias for $(XREF digest.digest, digest) using the + * This is a convenience alias for $(REF digest, std,digest,digest) using the * RIPEMD160 implementation. */ //simple alias doesn't work here, hope this gets inlined... @@ -669,7 +672,7 @@ auto ripemd160Of(T...)(T data) } /// -unittest +@safe unittest { ubyte[20] hash = ripemd160Of("abc"); assert(hash == digest!RIPEMD160("abc")); @@ -679,13 +682,13 @@ unittest * OOP API RIPEMD160 implementation. * See $(D std.digest.digest) for differences between template and OOP API. * - * This is an alias for $(XREF digest.digest, WrapperDigest)!RIPEMD160, see - * $(XREF digest.digest, WrapperDigest) for more information. + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!RIPEMD160), + * see there for more information. */ alias RIPEMD160Digest = WrapperDigest!RIPEMD160; /// -unittest +@safe unittest { //Simple example, hashing a string using Digest.digest helper function auto md = new RIPEMD160Digest(); @@ -695,12 +698,12 @@ unittest } /// -unittest +@system unittest { //Let's use the OOP features: void test(Digest dig) { - dig.put(cast(ubyte)0); + dig.put(cast(ubyte) 0); } auto md = new RIPEMD160Digest(); test(md); @@ -711,19 +714,19 @@ unittest assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D"); } -unittest +@system unittest { auto md = new RIPEMD160Digest(); md.put(cast(ubyte[])"abcdef"); md.reset(); md.put(cast(ubyte[])""); - assert(md.finish() == cast(ubyte[])x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); + assert(md.finish() == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); md.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); ubyte[20] result; auto result2 = md.finish(result[]); - assert(result[0 .. 20] == result2 && result2 == cast(ubyte[])x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); + assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); debug { @@ -733,27 +736,27 @@ unittest assert(md.length == 20); - assert(md.digest("") == cast(ubyte[])x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); + assert(md.digest("") == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31"); - assert(md.digest("a") == cast(ubyte[])x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); + assert(md.digest("a") == cast(ubyte[]) x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); - assert(md.digest("abc") == cast(ubyte[])x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); + assert(md.digest("abc") == cast(ubyte[]) x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); assert(md.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[])x"12a053384a9c0c88e405a06c27dcf49ada62eb2b"); + == cast(ubyte[]) x"12a053384a9c0c88e405a06c27dcf49ada62eb2b"); - assert(md.digest("message digest") == cast(ubyte[])x"5d0689ef49d2fae572b881b123a85ffa21595f36"); + assert(md.digest("message digest") == cast(ubyte[]) x"5d0689ef49d2fae572b881b123a85ffa21595f36"); assert(md.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[])x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); + == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc"); assert(md.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[])x"b0e20b6e3116640286ed3a87a5713079b21f5189"); + == cast(ubyte[]) x"b0e20b6e3116640286ed3a87a5713079b21f5189"); assert(md.digest("1234567890123456789012345678901234567890", "1234567890123456789012345678901234567890") - == cast(ubyte[])x"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); + == cast(ubyte[]) x"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); assert(md.digest(new ubyte[160/8]) // 160 zero bits - == cast(ubyte[])x"5c00bd4aca04a9057c09b20b05f723f2e23deb65"); + == cast(ubyte[]) x"5c00bd4aca04a9057c09b20b05f723f2e23deb65"); } diff --git a/std/digest/sha.d b/std/digest/sha.d index 8b3137bfa13..5494e7b3186 100644 --- a/std/digest/sha.d +++ b/std/digest/sha.d @@ -29,7 +29,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of)) * This module publicly imports $(D std.digest.digest) and can be used as a stand-alone * module. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * * CTFE: * Digests do not work in CTFE @@ -48,8 +48,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of)) * * Source: $(PHOBOSSRC std/digest/_sha.d) * - * Macros: - * WIKI = Phobos/StdSha1 */ /* Copyright Kai Nacke 2012. @@ -60,7 +58,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of)) module std.digest.sha; /// -unittest +@safe unittest { //Template API import std.digest.sha; @@ -82,7 +80,7 @@ unittest } /// -unittest +@safe unittest { //OOP API import std.digest.sha; @@ -103,17 +101,14 @@ unittest hash1 = sha1.finish(); } -version(D_PIC) -{ - // Do not use (Bug9378). -} -else version(Win64) +version(Win64) { // wrong calling convention } else version(D_InlineAsm_X86) { - private version = USE_SSSE3; + version (D_PIC) {} // Bugzilla 9378 + else private version = USE_SSSE3; } else version(D_InlineAsm_X86_64) { @@ -121,8 +116,6 @@ else version(D_InlineAsm_X86_64) } version(LittleEndian) import core.bitop : bswap; -version(USE_SSSE3) import core.cpuid : hasSSSE3Support = ssse3; -version(USE_SSSE3) import std.internal.digest.sha_SSSE3 : transformSSSE3; version(unittest) @@ -196,14 +189,16 @@ private ulong rotateRight(ulong x, uint n) @safe pure nothrow @nogc * Template API SHA1/SHA2 implementation. Supports: SHA-1, SHA-224, SHA-256, * SHA-384, SHA-512, SHA-512/224 and SHA-512/256. * - * The blockSize and digestSize are in bits. However, it's likely easier to + * The hashBlockSize and digestSize are in bits. However, it's likely easier to * simply use the convenience aliases: SHA1, SHA224, SHA256, SHA384, SHA512, * SHA512_224 and SHA512_256. * * See $(D std.digest.digest) for differences between template and OOP API. */ -struct SHA(int blockSize, int digestSize) +struct SHA(uint hashBlockSize, uint digestSize) { + enum blockSize = hashBlockSize; + static assert(blockSize == 512 || blockSize == 1024, "Invalid SHA blockSize, must be 512 or 1024"); static assert(digestSize == 160 || digestSize == 224 || digestSize == 256 || digestSize == 384 || digestSize == 512, @@ -213,15 +208,25 @@ struct SHA(int blockSize, int digestSize) static assert(!(blockSize == 1024 && digestSize < 224), "Invalid SHA digestSize for a blockSize of 1024. The digestSize must be 224, 256, 384 or 512."); - static if(digestSize==160) /* SHA-1 */ + static if (digestSize == 160) /* SHA-1 */ { version(USE_SSSE3) { - private __gshared immutable pure nothrow @nogc void function(uint[5]* state, const(ubyte[64])* block) transform; + import core.cpuid : ssse3; + import std.internal.digest.sha_SSSE3 : sse3_constants=constants, transformSSSE3; - shared static this() + static void transform(uint[5]* state, const(ubyte[64])* block) pure nothrow @nogc { - transform = hasSSSE3Support ? &transformSSSE3 : &transformX86; + if (ssse3) + { + version (D_InlineAsm_X86_64) + // constants as extra argument for PIC, see Bugzilla 9378 + transformSSSE3(state, block, &sse3_constants); + else + transformSSSE3(state, block); + } + else + transformX86(state, block); } } else @@ -229,35 +234,35 @@ struct SHA(int blockSize, int digestSize) alias transform = transformX86; } } - else static if(blockSize == 512) /* SHA-224, SHA-256 */ + else static if (blockSize == 512) /* SHA-224, SHA-256 */ alias transform = transformSHA2!uint; - else static if(blockSize == 1024) /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */ + else static if (blockSize == 1024) /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */ alias transform = transformSHA2!ulong; else static assert(0); private: /* magic initialization constants - state (ABCDEFGH) */ - static if(blockSize == 512 && digestSize == 160) /* SHA-1 */ + static if (blockSize == 512 && digestSize == 160) /* SHA-1 */ { uint[5] state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; } - else static if(blockSize == 512 && digestSize == 224) /* SHA-224 */ + else static if (blockSize == 512 && digestSize == 224) /* SHA-224 */ { uint[8] state = [ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, ]; } - else static if(blockSize == 512 && digestSize == 256) /* SHA-256 */ + else static if (blockSize == 512 && digestSize == 256) /* SHA-256 */ { uint[8] state = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ]; } - else static if(blockSize == 1024 && digestSize == 224) /* SHA-512/224 */ + else static if (blockSize == 1024 && digestSize == 224) /* SHA-512/224 */ { ulong[8] state = [ 0x8C3D37C8_19544DA2, 0x73E19966_89DCD4D6, @@ -266,7 +271,7 @@ struct SHA(int blockSize, int digestSize) 0x3F9D85A8_6A1D36C8, 0x1112E6AD_91D692A1, ]; } - else static if(blockSize == 1024 && digestSize == 256) /* SHA-512/256 */ + else static if (blockSize == 1024 && digestSize == 256) /* SHA-512/256 */ { ulong[8] state = [ 0x22312194_FC2BF72C, 0x9F555FA3_C84C64C2, @@ -275,7 +280,7 @@ struct SHA(int blockSize, int digestSize) 0x2B0199FC_2C85B8AA, 0x0EB72DDC_81C52CA2, ]; } - else static if(blockSize == 1024 && digestSize == 384) /* SHA-384 */ + else static if (blockSize == 1024 && digestSize == 384) /* SHA-384 */ { ulong[8] state = [ 0xcbbb9d5d_c1059ed8, 0x629a292a_367cd507, @@ -284,7 +289,7 @@ struct SHA(int blockSize, int digestSize) 0xdb0c2e0d_64f98fa7, 0x47b5481d_befa4fa4, ]; } - else static if(blockSize == 1024 && digestSize == 512) /* SHA-512 */ + else static if (blockSize == 1024 && digestSize == 512) /* SHA-512 */ { ulong[8] state = [ 0x6a09e667_f3bcc908, 0xbb67ae85_84caa73b, @@ -297,7 +302,7 @@ struct SHA(int blockSize, int digestSize) static assert(0); /* constants */ - static if(blockSize == 512) + static if (blockSize == 512) { static immutable uint[64] constants = [ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, @@ -310,7 +315,7 @@ struct SHA(int blockSize, int digestSize) 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, ]; } - else static if(blockSize == 1024) + else static if (blockSize == 1024) { static immutable ulong[80] constants = [ 0x428a2f98_d728ae22, 0x71374491_23ef65cd, 0xb5c0fbcf_ec4d3b2f, 0xe9b5dba5_8189dbbc, @@ -631,7 +636,7 @@ struct SHA(int blockSize, int digestSize) T_SHA2_16_79!Word(62, W, C, D, E, F, G, H, A, B, constants[62]); T_SHA2_16_79!Word(63, W, B, C, D, E, F, G, H, A, constants[63]); - static if(is(Word==ulong)) + static if (is(Word == ulong)) { T_SHA2_16_79!Word(64, W, A, B, C, D, E, F, G, H, constants[64]); T_SHA2_16_79!Word(65, W, H, A, B, C, D, E, F, G, constants[65]); @@ -674,7 +679,7 @@ struct SHA(int blockSize, int digestSize) * * Generic code which deals with different Digest types should always call start though. * - * Examples: + * Example: * -------- * SHA1 digest; * //digest.start(); //Not necessary @@ -688,8 +693,8 @@ struct SHA(int blockSize, int digestSize) /** * Use this to feed the digest with data. - * Also implements the $(XREF range, OutputRange) interface for $(D ubyte) and - * $(D const(ubyte)[]). + * Also implements the $(REF isOutputRange, std,range,primitives) + * interface for $(D ubyte) and $(D const(ubyte)[]). */ void put(scope const(ubyte)[] input...) @trusted pure nothrow @nogc { @@ -698,17 +703,17 @@ struct SHA(int blockSize, int digestSize) auto inputLen = input.length; /* Compute number of bytes mod block size (64 or 128 bytes) */ - index = (cast(uint)count[0] >> 3) & (blockSizeInBytes - 1); + index = (cast(uint) count[0] >> 3) & (blockSizeInBytes - 1); /* Update number of bits */ - static if(blockSize==512) + static if (blockSize == 512) count[0] += inputLen * 8; - else static if(blockSize==1024) + else static if (blockSize == 1024) { /* ugly hack to work around lack of ucent */ auto oldCount0 = count[0]; count[0] += inputLen * 8; - if(count[0] < oldCount0) + if (count[0] < oldCount0) count[1]++; } else @@ -734,12 +739,12 @@ struct SHA(int blockSize, int digestSize) if (inputLen - i) (&buffer[index])[0 .. inputLen-i] = (&input[i])[0 .. inputLen-i]; } - /// - unittest + + @safe unittest { typeof(this) dig; - dig.put(cast(ubyte)0); //single ubyte - dig.put(cast(ubyte)0, cast(ubyte)0); //variadic + dig.put(cast(ubyte) 0); //single ubyte + dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic ubyte[10] buf; dig.put(buf); //buffer } @@ -751,7 +756,7 @@ struct SHA(int blockSize, int digestSize) */ ubyte[digestSize/8] finish() @trusted pure nothrow @nogc { - static if(blockSize==512) + static if (blockSize == 512) { ubyte[32] data = void; uint index, padLen; @@ -760,7 +765,7 @@ struct SHA(int blockSize, int digestSize) ubyte[8] bits = nativeToBigEndian(count[0]); /* Pad out to 56 mod 64. */ - index = (cast(uint)count[0] >> 3) & (64 - 1); + index = (cast(uint) count[0] >> 3) & (64 - 1); padLen = (index < 56) ? (56 - index) : (120 - index); put(padding[0 .. padLen]); @@ -773,20 +778,20 @@ struct SHA(int blockSize, int digestSize) /* Zeroize sensitive information. */ start(); - return data[0..digestSize/8]; + return data[0 .. digestSize/8]; } - else static if(blockSize==1024) + else static if (blockSize == 1024) { ubyte[64] data = void; uint index, padLen; /* Save number of bits */ ubyte[16] bits; - bits[ 0..8] = nativeToBigEndian(count[1]); - bits[8..16] = nativeToBigEndian(count[0]); + bits[ 0 .. 8] = nativeToBigEndian(count[1]); + bits[8 .. 16] = nativeToBigEndian(count[0]); /* Pad out to 112 mod 128. */ - index = (cast(uint)count[0] >> 3) & (128 - 1); + index = (cast(uint) count[0] >> 3) & (128 - 1); padLen = (index < 112) ? (112 - index) : (240 - index); put(padding[0 .. padLen]); @@ -799,18 +804,18 @@ struct SHA(int blockSize, int digestSize) /* Zeroize sensitive information. */ start(); - return data[0..digestSize/8]; + return data[0 .. digestSize/8]; } else static assert(0); } /// - unittest + @safe unittest { //Simple example SHA1 hash; hash.start(); - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); ubyte[20] result = hash.finish(); } } @@ -824,7 +829,7 @@ alias SHA512_224 = SHA!(1024, 224); /// SHA alias for SHA-512/224, hash is ubyte alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte[32] /// -unittest +@safe unittest { //Simple example, hashing a string using sha1Of helper function ubyte[20] hash = sha1Of("abc"); @@ -837,7 +842,7 @@ unittest } /// -unittest +@safe unittest { //Using the basic API SHA1 hash; @@ -849,13 +854,14 @@ unittest } /// -unittest +@safe unittest { //Let's use the template features: //Note: When passing a SHA1 to a function, it must be passed by reference! - void doSomething(T)(ref T hash) if(isDigest!T) + void doSomething(T)(ref T hash) + if (isDigest!T) { - hash.put(cast(ubyte)0); + hash.put(cast(ubyte) 0); } SHA1 sha; sha.start(); @@ -863,7 +869,7 @@ unittest assert(toHexString(sha.finish()) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F"); } -unittest +@safe unittest { assert(isDigest!SHA1); assert(isDigest!SHA224); @@ -874,9 +880,10 @@ unittest assert(isDigest!SHA512_256); } -unittest +@system unittest { import std.range; + import std.conv : hexString; ubyte[20] digest; ubyte[28] digest224; @@ -890,43 +897,45 @@ unittest sha.put(cast(ubyte[])"abcdef"); sha.start(); sha.put(cast(ubyte[])""); - assert(sha.finish() == cast(ubyte[])x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); + assert(sha.finish() == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); SHA224 sha224; sha224.put(cast(ubyte[])"abcdef"); sha224.start(); sha224.put(cast(ubyte[])""); - assert(sha224.finish() == cast(ubyte[])x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + assert(sha224.finish() == cast(ubyte[]) x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); SHA256 sha256; sha256.put(cast(ubyte[])"abcdef"); sha256.start(); sha256.put(cast(ubyte[])""); - assert(sha256.finish() == cast(ubyte[])x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + assert(sha256.finish() == cast(ubyte[]) x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); SHA384 sha384; sha384.put(cast(ubyte[])"abcdef"); sha384.start(); sha384.put(cast(ubyte[])""); - assert(sha384.finish() == cast(ubyte[])x"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); + assert(sha384.finish() == cast(ubyte[]) hexString!("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c" + ~"0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")); SHA512 sha512; sha512.put(cast(ubyte[])"abcdef"); sha512.start(); sha512.put(cast(ubyte[])""); - assert(sha512.finish() == cast(ubyte[])x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + assert(sha512.finish() == cast(ubyte[]) hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b571" + ~"5dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); SHA512_224 sha512_224; sha512_224.put(cast(ubyte[])"abcdef"); sha512_224.start(); sha512_224.put(cast(ubyte[])""); - assert(sha512_224.finish() == cast(ubyte[])x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); + assert(sha512_224.finish() == cast(ubyte[]) x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); SHA512_256 sha512_256; sha512_256.put(cast(ubyte[])"abcdef"); sha512_256.start(); sha512_256.put(cast(ubyte[])""); - assert(sha512_256.finish() == cast(ubyte[])x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); + assert(sha512_256.finish() == cast(ubyte[]) x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); digest = sha1Of (""); digest224 = sha224Of (""); @@ -935,13 +944,15 @@ unittest digest512 = sha512Of (""); digest512_224 = sha512_224Of(""); digest512_256 = sha512_256Of(""); - assert(digest == cast(ubyte[])x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - assert(digest224 == cast(ubyte[])x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); - assert(digest256 == cast(ubyte[])x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - assert(digest384 == cast(ubyte[])x"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); - assert(digest512 == cast(ubyte[])x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); - assert(digest512_224 == cast(ubyte[])x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); - assert(digest512_256 == cast(ubyte[])x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); + assert(digest == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); + assert(digest224 == cast(ubyte[]) x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + assert(digest256 == cast(ubyte[]) x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + assert(digest384 == cast(ubyte[]) hexString!("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c" + ~"0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b")); + assert(digest512 == cast(ubyte[]) hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83" + ~"f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")); + assert(digest512_224 == cast(ubyte[]) x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); + assert(digest512_256 == cast(ubyte[]) x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); digest = sha1Of ("a"); digest224 = sha224Of ("a"); @@ -950,13 +961,15 @@ unittest digest512 = sha512Of ("a"); digest512_224 = sha512_224Of("a"); digest512_256 = sha512_256Of("a"); - assert(digest == cast(ubyte[])x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); - assert(digest224 == cast(ubyte[])x"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"); - assert(digest256 == cast(ubyte[])x"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"); - assert(digest384 == cast(ubyte[])x"54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"); - assert(digest512 == cast(ubyte[])x"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"); - assert(digest512_224 == cast(ubyte[])x"d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327"); - assert(digest512_256 == cast(ubyte[])x"455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8"); + assert(digest == cast(ubyte[]) x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); + assert(digest224 == cast(ubyte[]) x"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"); + assert(digest256 == cast(ubyte[]) x"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"); + assert(digest384 == cast(ubyte[]) hexString!("54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9" + ~"cd697e85175033caa88e6d57bc35efae0b5afd3145f31")); + assert(digest512 == cast(ubyte[]) hexString!("1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05ab" + ~"c54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75")); + assert(digest512_224 == cast(ubyte[]) x"d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327"); + assert(digest512_256 == cast(ubyte[]) x"455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8"); digest = sha1Of ("abc"); digest224 = sha224Of ("abc"); @@ -965,13 +978,15 @@ unittest digest512 = sha512Of ("abc"); digest512_224 = sha512_224Of("abc"); digest512_256 = sha512_256Of("abc"); - assert(digest == cast(ubyte[])x"a9993e364706816aba3e25717850c26c9cd0d89d"); - assert(digest224 == cast(ubyte[])x"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"); - assert(digest256 == cast(ubyte[])x"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); - assert(digest384 == cast(ubyte[])x"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"); - assert(digest512 == cast(ubyte[])x"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); - assert(digest512_224 == cast(ubyte[])x"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"); - assert(digest512_256 == cast(ubyte[])x"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"); + assert(digest == cast(ubyte[]) x"a9993e364706816aba3e25717850c26c9cd0d89d"); + assert(digest224 == cast(ubyte[]) x"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"); + assert(digest256 == cast(ubyte[]) x"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); + assert(digest384 == cast(ubyte[]) hexString!("cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a" + ~"8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7")); + assert(digest512 == cast(ubyte[]) hexString!("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9" + ~"eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f")); + assert(digest512_224 == cast(ubyte[]) x"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"); + assert(digest512_256 == cast(ubyte[]) x"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"); digest = sha1Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); digest224 = sha224Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); @@ -980,13 +995,15 @@ unittest digest512 = sha512Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); digest512_224 = sha512_224Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); digest512_256 = sha512_256Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[])x"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - assert(digest224 == cast(ubyte[])x"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"); - assert(digest256 == cast(ubyte[])x"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); - assert(digest384 == cast(ubyte[])x"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b"); - assert(digest512 == cast(ubyte[])x"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"); - assert(digest512_224 == cast(ubyte[])x"e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174"); - assert(digest512_256 == cast(ubyte[])x"bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461"); + assert(digest == cast(ubyte[]) x"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); + assert(digest224 == cast(ubyte[]) x"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"); + assert(digest256 == cast(ubyte[]) x"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); + assert(digest384 == cast(ubyte[]) hexString!("3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe" + ~"8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b")); + assert(digest512 == cast(ubyte[]) hexString!("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a827" + ~"9be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")); + assert(digest512_224 == cast(ubyte[]) x"e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174"); + assert(digest512_256 == cast(ubyte[]) x"bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461"); digest = sha1Of ("message digest"); digest224 = sha224Of ("message digest"); @@ -995,13 +1012,15 @@ unittest digest512 = sha512Of ("message digest"); digest512_224 = sha512_224Of("message digest"); digest512_256 = sha512_256Of("message digest"); - assert(digest == cast(ubyte[])x"c12252ceda8be8994d5fa0290a47231c1d16aae3"); - assert(digest224 == cast(ubyte[])x"2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb"); - assert(digest256 == cast(ubyte[])x"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); - assert(digest384 == cast(ubyte[])x"473ed35167ec1f5d8e550368a3db39be54639f828868e9454c239fc8b52e3c61dbd0d8b4de1390c256dcbb5d5fd99cd5"); - assert(digest512 == cast(ubyte[])x"107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"); - assert(digest512_224 == cast(ubyte[])x"ad1a4db188fe57064f4f24609d2a83cd0afb9b398eb2fcaeaae2c564"); - assert(digest512_256 == cast(ubyte[])x"0cf471fd17ed69d990daf3433c89b16d63dec1bb9cb42a6094604ee5d7b4e9fb"); + assert(digest == cast(ubyte[]) x"c12252ceda8be8994d5fa0290a47231c1d16aae3"); + assert(digest224 == cast(ubyte[]) x"2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb"); + assert(digest256 == cast(ubyte[]) x"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); + assert(digest384 == cast(ubyte[]) hexString!("473ed35167ec1f5d8e550368a3db39be54639f828868e9454c" + ~"239fc8b52e3c61dbd0d8b4de1390c256dcbb5d5fd99cd5")); + assert(digest512 == cast(ubyte[]) hexString!("107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c134" + ~"92ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c")); + assert(digest512_224 == cast(ubyte[]) x"ad1a4db188fe57064f4f24609d2a83cd0afb9b398eb2fcaeaae2c564"); + assert(digest512_256 == cast(ubyte[]) x"0cf471fd17ed69d990daf3433c89b16d63dec1bb9cb42a6094604ee5d7b4e9fb"); digest = sha1Of ("abcdefghijklmnopqrstuvwxyz"); digest224 = sha224Of ("abcdefghijklmnopqrstuvwxyz"); @@ -1010,13 +1029,15 @@ unittest digest512 = sha512Of ("abcdefghijklmnopqrstuvwxyz"); digest512_224 = sha512_224Of("abcdefghijklmnopqrstuvwxyz"); digest512_256 = sha512_256Of("abcdefghijklmnopqrstuvwxyz"); - assert(digest == cast(ubyte[])x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); - assert(digest224 == cast(ubyte[])x"45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2"); - assert(digest256 == cast(ubyte[])x"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73"); - assert(digest384 == cast(ubyte[])x"feb67349df3db6f5924815d6c3dc133f091809213731fe5c7b5f4999e463479ff2877f5f2936fa63bb43784b12f3ebb4"); - assert(digest512 == cast(ubyte[])x"4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1"); - assert(digest512_224 == cast(ubyte[])x"ff83148aa07ec30655c1b40aff86141c0215fe2a54f767d3f38743d8"); - assert(digest512_256 == cast(ubyte[])x"fc3189443f9c268f626aea08a756abe7b726b05f701cb08222312ccfd6710a26"); + assert(digest == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); + assert(digest224 == cast(ubyte[]) x"45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2"); + assert(digest256 == cast(ubyte[]) x"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73"); + assert(digest384 == cast(ubyte[]) hexString!("feb67349df3db6f5924815d6c3dc133f091809213731fe5c7b5" + ~"f4999e463479ff2877f5f2936fa63bb43784b12f3ebb4")); + assert(digest512 == cast(ubyte[]) hexString!("4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034" + ~"898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1")); + assert(digest512_224 == cast(ubyte[]) x"ff83148aa07ec30655c1b40aff86141c0215fe2a54f767d3f38743d8"); + assert(digest512_256 == cast(ubyte[]) x"fc3189443f9c268f626aea08a756abe7b726b05f701cb08222312ccfd6710a26"); digest = sha1Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); digest224 = sha224Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); @@ -1025,13 +1046,15 @@ unittest digest512 = sha512Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); digest512_224 = sha512_224Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); digest512_256 = sha512_256Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[])x"761c457bf73b14d27e9e9265c46f4b4dda11f940"); - assert(digest224 == cast(ubyte[])x"bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9"); - assert(digest256 == cast(ubyte[])x"db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0"); - assert(digest384 == cast(ubyte[])x"1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039c1464ee8732f11a5341a6f41e0c202294736ed64db1a84"); - assert(digest512 == cast(ubyte[])x"1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894"); - assert(digest512_224 == cast(ubyte[])x"a8b4b9174b99ffc67d6f49be9981587b96441051e16e6dd036b140d3"); - assert(digest512_256 == cast(ubyte[])x"cdf1cc0effe26ecc0c13758f7b4a48e000615df241284185c39eb05d355bb9c8"); + assert(digest == cast(ubyte[]) x"761c457bf73b14d27e9e9265c46f4b4dda11f940"); + assert(digest224 == cast(ubyte[]) x"bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9"); + assert(digest256 == cast(ubyte[]) x"db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0"); + assert(digest384 == cast(ubyte[]) hexString!("1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039" + ~"c1464ee8732f11a5341a6f41e0c202294736ed64db1a84")); + assert(digest512 == cast(ubyte[]) hexString!("1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f" + ~"536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894")); + assert(digest512_224 == cast(ubyte[]) x"a8b4b9174b99ffc67d6f49be9981587b96441051e16e6dd036b140d3"); + assert(digest512_256 == cast(ubyte[]) x"cdf1cc0effe26ecc0c13758f7b4a48e000615df241284185c39eb05d355bb9c8"); digest = sha1Of ("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); @@ -1047,13 +1070,15 @@ unittest "1234567890123456789012345678901234567890"); digest512_256 = sha512_256Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[])x"50abf5706a150990a08b2c5ea40fa0e585554732"); - assert(digest224 == cast(ubyte[])x"b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e"); - assert(digest256 == cast(ubyte[])x"f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e"); - assert(digest384 == cast(ubyte[])x"b12932b0627d1c060942f5447764155655bd4da0c9afa6dd9b9ef53129af1b8fb0195996d2de9ca0df9d821ffee67026"); - assert(digest512 == cast(ubyte[])x"72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d1914042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843"); - assert(digest512_224 == cast(ubyte[])x"ae988faaa47e401a45f704d1272d99702458fea2ddc6582827556dd2"); - assert(digest512_256 == cast(ubyte[])x"2c9fdbc0c90bdd87612ee8455474f9044850241dc105b1e8b94b8ddf5fac9148"); + assert(digest == cast(ubyte[]) x"50abf5706a150990a08b2c5ea40fa0e585554732"); + assert(digest224 == cast(ubyte[]) x"b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e"); + assert(digest256 == cast(ubyte[]) x"f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e"); + assert(digest384 == cast(ubyte[]) hexString!("b12932b0627d1c060942f5447764155655bd4da0c9afa6dd9b" + ~"9ef53129af1b8fb0195996d2de9ca0df9d821ffee67026")); + assert(digest512 == cast(ubyte[]) hexString!("72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d191" + ~"4042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843")); + assert(digest512_224 == cast(ubyte[]) x"ae988faaa47e401a45f704d1272d99702458fea2ddc6582827556dd2"); + assert(digest512_256 == cast(ubyte[]) x"2c9fdbc0c90bdd87612ee8455474f9044850241dc105b1e8b94b8ddf5fac9148"); ubyte[] onemilliona = new ubyte[1000000]; onemilliona[] = 'a'; @@ -1064,13 +1089,15 @@ unittest digest512 = sha512Of(onemilliona); digest512_224 = sha512_224Of(onemilliona); digest512_256 = sha512_256Of(onemilliona); - assert(digest == cast(ubyte[])x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); - assert(digest224 == cast(ubyte[])x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); - assert(digest256 == cast(ubyte[])x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); - assert(digest384 == cast(ubyte[])x"9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"); - assert(digest512 == cast(ubyte[])x"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); - assert(digest512_224 == cast(ubyte[])x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); - assert(digest512_256 == cast(ubyte[])x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); + assert(digest == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + assert(digest224 == cast(ubyte[]) x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); + assert(digest256 == cast(ubyte[]) x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + assert(digest384 == cast(ubyte[]) hexString!("9d0e1809716474cb086e834e310a4a1ced149e9c00f2485279" + ~"72cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985")); + assert(digest512 == cast(ubyte[]) hexString!("e718483d0ce769644e2e42c7bc15b4638e1f98b13b20442856" + ~"32a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b")); + assert(digest512_224 == cast(ubyte[]) x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); + assert(digest512_256 == cast(ubyte[]) x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); digest = sha1Of(oneMillionRange); @@ -1080,20 +1107,22 @@ unittest digest512 = sha512Of(oneMillionRange); digest512_224 = sha512_224Of(oneMillionRange); digest512_256 = sha512_256Of(oneMillionRange); - assert(digest == cast(ubyte[])x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); - assert(digest224 == cast(ubyte[])x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); - assert(digest256 == cast(ubyte[])x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); - assert(digest384 == cast(ubyte[])x"9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"); - assert(digest512 == cast(ubyte[])x"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); - assert(digest512_224 == cast(ubyte[])x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); - assert(digest512_256 == cast(ubyte[])x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); - - assert(toHexString(cast(ubyte[20])x"a9993e364706816aba3e25717850c26c9cd0d89d") + assert(digest == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + assert(digest224 == cast(ubyte[]) x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); + assert(digest256 == cast(ubyte[]) x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + assert(digest384 == cast(ubyte[]) hexString!("9d0e1809716474cb086e834e310a4a1ced149e9c00f2485279" + ~"72cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985")); + assert(digest512 == cast(ubyte[]) hexString!("e718483d0ce769644e2e42c7bc15b4638e1f98b13b20442856" + ~"32a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b")); + assert(digest512_224 == cast(ubyte[]) x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); + assert(digest512_256 == cast(ubyte[]) x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); + + assert(toHexString(cast(ubyte[20]) x"a9993e364706816aba3e25717850c26c9cd0d89d") == "A9993E364706816ABA3E25717850C26C9CD0D89D"); } /** - * These are convenience aliases for $(XREF digest.digest, digest) using the + * These are convenience aliases for $(REF digest, std,digest,digest) using the * SHA implementation. */ //simple alias doesn't work here, hope this gets inlined... @@ -1133,7 +1162,7 @@ auto sha512_256Of(T...)(T data) } /// -unittest +@safe unittest { ubyte[20] hash = sha1Of("abc"); assert(hash == digest!SHA1("abc")); @@ -1157,7 +1186,7 @@ unittest assert(hash512_256 == digest!SHA512_256("abc")); } -unittest +@safe unittest { string a = "Mary has ", b = "a little lamb"; int[] c = [ 1, 2, 3, 4, 5 ]; @@ -1172,8 +1201,8 @@ unittest * OOP API SHA1 and SHA2 implementations. * See $(D std.digest.digest) for differences between template and OOP API. * - * This is an alias for $(XREF digest.digest, WrapperDigest)!SHA1, see - * $(XREF digest.digest, WrapperDigest) for more information. + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!SHA1), see + * there for more information. */ alias SHA1Digest = WrapperDigest!SHA1; alias SHA224Digest = WrapperDigest!SHA224; ///ditto @@ -1184,7 +1213,7 @@ alias SHA512_224Digest = WrapperDigest!SHA512_224; ///ditto alias SHA512_256Digest = WrapperDigest!SHA512_256; ///ditto /// -unittest +@safe unittest { //Simple example, hashing a string using Digest.digest helper function auto sha = new SHA1Digest(); @@ -1200,12 +1229,12 @@ unittest } /// -unittest +@system unittest { //Let's use the OOP features: void test(Digest dig) { - dig.put(cast(ubyte)0); + dig.put(cast(ubyte) 0); } auto sha = new SHA1Digest(); test(sha); @@ -1216,47 +1245,47 @@ unittest assert(toHexString(result) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F"); } -unittest +@system unittest { auto sha = new SHA1Digest(); sha.put(cast(ubyte[])"abcdef"); sha.reset(); sha.put(cast(ubyte[])""); - assert(sha.finish() == cast(ubyte[])x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); + assert(sha.finish() == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); sha.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); ubyte[22] result; auto result2 = sha.finish(result[]); - assert(result[0 .. 20] == result2 && result2 == cast(ubyte[])x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); + assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); debug assertThrown!Error(sha.finish(result[0 .. 15])); assert(sha.length == 20); - assert(sha.digest("") == cast(ubyte[])x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); + assert(sha.digest("") == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - assert(sha.digest("a") == cast(ubyte[])x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); + assert(sha.digest("a") == cast(ubyte[]) x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); - assert(sha.digest("abc") == cast(ubyte[])x"a9993e364706816aba3e25717850c26c9cd0d89d"); + assert(sha.digest("abc") == cast(ubyte[]) x"a9993e364706816aba3e25717850c26c9cd0d89d"); assert(sha.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") - == cast(ubyte[])x"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); + == cast(ubyte[]) x"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - assert(sha.digest("message digest") == cast(ubyte[])x"c12252ceda8be8994d5fa0290a47231c1d16aae3"); + assert(sha.digest("message digest") == cast(ubyte[]) x"c12252ceda8be8994d5fa0290a47231c1d16aae3"); assert(sha.digest("abcdefghijklmnopqrstuvwxyz") - == cast(ubyte[])x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); + == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); assert(sha.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - == cast(ubyte[])x"761c457bf73b14d27e9e9265c46f4b4dda11f940"); + == cast(ubyte[]) x"761c457bf73b14d27e9e9265c46f4b4dda11f940"); assert(sha.digest("1234567890123456789012345678901234567890", "1234567890123456789012345678901234567890") - == cast(ubyte[])x"50abf5706a150990a08b2c5ea40fa0e585554732"); + == cast(ubyte[]) x"50abf5706a150990a08b2c5ea40fa0e585554732"); ubyte[] onemilliona = new ubyte[1000000]; onemilliona[] = 'a'; - assert(sha.digest(onemilliona) == cast(ubyte[])x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + assert(sha.digest(onemilliona) == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); } diff --git a/std/encoding.d b/std/encoding.d index 43e25b36555..3acb4199e58 100644 --- a/std/encoding.d +++ b/std/encoding.d @@ -8,38 +8,90 @@ for arbitrary _encoding and decoding of characters, arbitrary transcoding between strings of different type, as well as validation and sanitization. Encodings currently supported are UTF-8, UTF-16, UTF-32, ASCII, ISO-8859-1 -(also known as LATIN-1), and WINDOWS-1252. - -$(UL -$(LI The type $(D AsciiChar) represents an ASCII character.) -$(LI The type $(D AsciiString) represents an ASCII string.) -$(LI The type $(D Latin1Char) represents an ISO-8859-1 character.) -$(LI The type $(D Latin1String) represents an ISO-8859-1 string.) -$(LI The type $(D Windows1252Char) represents a Windows-1252 character.) -$(LI The type $(D Windows1252String) represents a Windows-1252 string.)) +(also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250 and WINDOWS-1252. + +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Decode) $(TD + $(LREF codePoints) + $(LREF decode) + $(LREF decodeReverse) + $(LREF safeDecode) +)) +$(TR $(TD Conversion) $(TD + $(LREF codeUnits) + $(LREF sanitize) + $(LREF transcode) +)) +$(TR $(TD Classification) $(TD + $(LREF canEncode) + $(LREF isValid) + $(LREF isValidCodePoint) + $(LREF isValidCodeUnit) +)) +$(TR $(TD BOM) $(TD + $(LREF BOM) + $(LREF BOMSeq) + $(LREF getBOM) + $(LREF utfBOM) +)) +$(TR $(TD Length & Index) $(TD + $(LREF firstSequence) + $(LREF encodedLength) + $(LREF index) + $(LREF lastSequence) + $(LREF validLength) +)) +$(TR $(TD Encoding schemes) $(TD + $(LREF encodingName) + $(LREF EncodingScheme) + $(LREF EncodingSchemeASCII) + $(LREF EncodingSchemeLatin1) + $(LREF EncodingSchemeLatin2) + $(LREF EncodingSchemeUtf16Native) + $(LREF EncodingSchemeUtf32Native) + $(LREF EncodingSchemeUtf8) + $(LREF EncodingSchemeWindows1250) + $(LREF EncodingSchemeWindows1252) +)) +$(TR $(TD Representation) $(TD + $(LREF AsciiChar) + $(LREF AsciiString) + $(LREF Latin1Char) + $(LREF Latin1String) + $(LREF Latin2Char) + $(LREF Latin2String) + $(LREF Windows1250Char) + $(LREF Windows1250String) + $(LREF Windows1252Char) + $(LREF Windows1252String) +)) +$(TR $(TD Exceptions) $(TD + $(LREF INVALID_SEQUENCE) + $(LREF EncodingException) +)) +) For cases where the _encoding is not known at compile-time, but is -known at run-time, we provide the abstract class $(D EncodingScheme) -and its subclasses. To construct a run-time encoder/decoder, one does -e.g. +known at run-time, the abstract class $(LREF EncodingScheme) +and its subclasses is provided. To construct a run-time encoder/decoder, +one does e.g. ---------------------------------------------------- - auto e = EncodingScheme.create("utf-8"); +auto e = EncodingScheme.create("utf-8"); ---------------------------------------------------- -This library supplies $(D EncodingScheme) subclasses for ASCII, -ISO-8859-1 (also known as LATIN-1), WINDOWS-1252, UTF-8, and (on -little-endian architectures) UTF-16LE and UTF-32LE; or (on big-endian -architectures) UTF-16BE and UTF-32BE. +This library supplies $(LREF EncodingScheme) subclasses for ASCII, +ISO-8859-1 (also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250, +WINDOWS-1252, UTF-8, and (on little-endian architectures) UTF-16LE and +UTF-32LE; or (on big-endian architectures) UTF-16BE and UTF-32BE. -This library provides a mechanism whereby other modules may add $(D +This library provides a mechanism whereby other modules may add $(LREF EncodingScheme) subclasses for any other _encoding. -Macros: - WIKI=Phobos/StdEncoding - Copyright: Copyright Janice Caron 2008 - 2009. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Janice Caron Source: $(PHOBOSSRC std/_encoding.d) */ @@ -52,9 +104,11 @@ Distributed under the Boost Software License, Version 1.0. module std.encoding; import std.traits; +import std.typecons; import std.range.primitives; +import std.internal.encodinginit; -unittest +@system unittest { static ubyte[][] validStrings = [ @@ -225,36 +279,36 @@ unittest ]; // Make sure everything that should be valid, is - foreach(a;validStrings) + foreach (a;validStrings) { - string s = cast(string)a; + string s = cast(string) a; assert(isValid(s),"Failed to validate: "~makeReadable(s)); } // Make sure everything that shouldn't be valid, isn't - foreach(a;invalidStrings) + foreach (a;invalidStrings) { - string s = cast(string)a; + string s = cast(string) a; assert(!isValid(s),"Incorrectly validated: "~makeReadable(s)); } // Make sure we can sanitize everything bad assert(invalidStrings.length == sanitizedStrings.length); - for(int i=0; i m_charMapEnd && c < 0x100)) return true; + if (c >= 0xFFFD) return false; + + auto idx = 0; + while (idx < bstMap.length) + { + if (bstMap[idx][0] == c) return true; + idx = bstMap[idx][0] > c ? 2 * idx + 1 : 2 * idx + 2; // next BST index + } + + return false; + } + + bool isValidCodeUnit(E c) @safe pure @nogc nothrow + { + if (c < m_charMapStart || c > m_charMapEnd) return true; + return charMap[c-m_charMapStart] != 0xFFFD; + } + + size_t encodedLength(dchar c) @safe pure @nogc nothrow + in + { + assert(canEncode(c)); + } + body + { + return 1; + } + + void encodeViaWrite()(dchar c) + { + if (c < m_charMapStart || (c > m_charMapEnd && c < 0x100)) {} + else if (c >= 0xFFFD) { c = '?'; } + else + { + auto idx = 0; + while (idx < bstMap.length) + { + if (bstMap[idx][0] == c) + { + write(cast(E) bstMap[idx][1]); + return; + } + idx = bstMap[idx][0] > c ? 2 * idx + 1 : 2 * idx + 2; // next BST index + } + c = '?'; + } + write(cast(E) c); + } + + void skipViaRead()() + { + read(); + } + + dchar decodeViaRead()() + { + E c = read(); + return (c >= m_charMapStart && c <= m_charMapEnd) ? charMap[c-m_charMapStart] : c; + } + + dchar safeDecodeViaRead()() + { + immutable E c = read(); + immutable d = (c >= m_charMapStart && c <= m_charMapEnd) ? charMap[c-m_charMapStart] : c; + return d == 0xFFFD ? INVALID_SEQUENCE : d; + } + + dchar decodeReverseViaRead()() + { + E c = read(); + return (c >= m_charMapStart && c <= m_charMapEnd) ? charMap[c-m_charMapStart] : c; + } + + @property EString replacementSequence() @safe pure @nogc nothrow + { + return cast(EString)("?"); + } + + mixin EncoderFunctions; +} + //============================================================================= // ASCII //============================================================================= @@ -653,22 +800,22 @@ template EncoderInstance(CharType : AsciiChar) alias E = AsciiChar; alias EString = AsciiString; - @property string encodingName() + @property string encodingName() @safe pure nothrow @nogc { return "ASCII"; } - bool canEncode(dchar c) + bool canEncode(dchar c) @safe pure nothrow @nogc { return c < 0x80; } - bool isValidCodeUnit(AsciiChar c) + bool isValidCodeUnit(AsciiChar c) @safe pure nothrow @nogc { return c < 0x80; } - size_t encodedLength(dchar c) + size_t encodedLength(dchar c) @safe pure nothrow @nogc in { assert(canEncode(c)); @@ -687,7 +834,7 @@ template EncoderInstance(CharType : AsciiChar) void encodeViaWrite()(dchar c) { if (!canEncode(c)) c = '?'; - write(cast(AsciiChar)c); + write(cast(AsciiChar) c); } void skipViaRead()() @@ -702,7 +849,7 @@ template EncoderInstance(CharType : AsciiChar) dchar safeDecodeViaRead()() { - dchar c = read(); + immutable c = read(); return canEncode(c) ? c : INVALID_SEQUENCE; } @@ -711,7 +858,7 @@ template EncoderInstance(CharType : AsciiChar) return read(); } - @property EString replacementSequence() + @property EString replacementSequence() @safe pure nothrow @nogc { return cast(EString)("?"); } @@ -729,42 +876,42 @@ enum Latin1Char : ubyte { init } Defines an Latin1-encoded string (as an array of $(D immutable(Latin1Char))). */ -alias Latin1String = immutable(Latin1Char)[]; /// +alias Latin1String = immutable(Latin1Char)[]; template EncoderInstance(CharType : Latin1Char) { alias E = Latin1Char; alias EString = Latin1String; - @property string encodingName() + @property string encodingName() @safe pure nothrow @nogc { return "ISO-8859-1"; } - bool canEncode(dchar c) + bool canEncode(dchar c) @safe pure nothrow @nogc { return c < 0x100; } - bool isValidCodeUnit(Latin1Char c) + bool isValidCodeUnit(Latin1Char c) @safe pure nothrow @nogc { return true; } - size_t encodedLength(dchar c) + size_t encodedLength(dchar c) @safe pure nothrow @nogc in { assert(canEncode(c)); } body { - return 1; + return 1; } void encodeViaWrite()(dchar c) { if (!canEncode(c)) c = '?'; - write(cast(Latin1Char)c); + write(cast(Latin1Char) c); } void skipViaRead()() @@ -787,7 +934,7 @@ template EncoderInstance(CharType : Latin1Char) return read(); } - @property EString replacementSequence() + @property EString replacementSequence() @safe pure nothrow @nogc { return cast(EString)("?"); } @@ -796,108 +943,225 @@ template EncoderInstance(CharType : Latin1Char) } //============================================================================= -// WINDOWS-1252 +// ISO-8859-2 //============================================================================= -/** Defines a Windows1252-encoded character. */ -enum Windows1252Char : ubyte { init } +/// Defines a Latin2-encoded character. +enum Latin2Char : ubyte { init } + /** -Defines an Windows1252-encoded string (as an array of $(D -immutable(Windows1252Char))). + * Defines an Latin2-encoded string (as an array of $(D + * immutable(Latin2Char))). */ -alias Windows1252String = immutable(Windows1252Char)[]; /// +alias Latin2String = immutable(Latin2Char)[]; + +private template EncoderInstance(CharType : Latin2Char) +{ + import std.typecons : Tuple, tuple; + + alias E = Latin2Char; + alias EString = Latin2String; + + @property string encodingName() @safe pure nothrow @nogc + { + return "ISO-8859-2"; + } + + private static immutable dchar m_charMapStart = 0xa1; + private static immutable dchar m_charMapEnd = 0xff; + + private immutable wstring charMap = + "\u0104\u02D8\u0141\u00A4\u013D\u015A\u00A7\u00A8"~ + "\u0160\u015E\u0164\u0179\u00AD\u017D\u017B\u00B0"~ + "\u0105\u02DB\u0142\u00B4\u013E\u015B\u02C7\u00B8"~ + "\u0161\u015F\u0165\u017A\u02DD\u017E\u017C\u0154"~ + "\u00C1\u00C2\u0102\u00C4\u0139\u0106\u00C7\u010C"~ + "\u00C9\u0118\u00CB\u011A\u00CD\u00CE\u010E\u0110"~ + "\u0143\u0147\u00D3\u00D4\u0150\u00D6\u00D7\u0158"~ + "\u016E\u00DA\u0170\u00DC\u00DD\u0162\u00DF\u0155"~ + "\u00E1\u00E2\u0103\u00E4\u013A\u0107\u00E7\u010D"~ + "\u00E9\u0119\u00EB\u011B\u00ED\u00EE\u010F\u0111"~ + "\u0144\u0148\u00F3\u00F4\u0151\u00F6\u00F7\u0159"~ + "\u016F\u00FA\u0171\u00FC\u00FD\u0163\u02D9"; + + private immutable Tuple!(wchar, char)[] bstMap = [ + tuple('\u0148','\xF2'), tuple('\u00F3','\xF3'), tuple('\u0165','\xBB'), + tuple('\u00D3','\xD3'), tuple('\u010F','\xEF'), tuple('\u015B','\xB6'), + tuple('\u017C','\xBF'), tuple('\u00C1','\xC1'), tuple('\u00E1','\xE1'), + tuple('\u0103','\xE3'), tuple('\u013A','\xE5'), tuple('\u0155','\xE0'), + tuple('\u0161','\xB9'), tuple('\u0171','\xFB'), tuple('\u02D8','\xA2'), + tuple('\u00AD','\xAD'), tuple('\u00C9','\xC9'), tuple('\u00DA','\xDA'), + tuple('\u00E9','\xE9'), tuple('\u00FA','\xFA'), tuple('\u0107','\xE6'), + tuple('\u0119','\xEA'), tuple('\u0142','\xB3'), tuple('\u0151','\xF5'), + tuple('\u0159','\xF8'), tuple('\u015F','\xBA'), tuple('\u0163','\xFE'), + tuple('\u016F','\xF9'), tuple('\u017A','\xBC'), tuple('\u017E','\xBE'), + tuple('\u02DB','\xB2'), tuple('\u00A7','\xA7'), tuple('\u00B4','\xB4'), + tuple('\u00C4','\xC4'), tuple('\u00CD','\xCD'), tuple('\u00D6','\xD6'), + tuple('\u00DD','\xDD'), tuple('\u00E4','\xE4'), tuple('\u00ED','\xED'), + tuple('\u00F6','\xF6'), tuple('\u00FD','\xFD'), tuple('\u0105','\xB1'), + tuple('\u010D','\xE8'), tuple('\u0111','\xF0'), tuple('\u011B','\xEC'), + tuple('\u013E','\xB5'), tuple('\u0144','\xF1'), tuple('\u0150','\xD5'), + tuple('\u0154','\xC0'), tuple('\u0158','\xD8'), tuple('\u015A','\xA6'), + tuple('\u015E','\xAA'), tuple('\u0160','\xA9'), tuple('\u0162','\xDE'), + tuple('\u0164','\xAB'), tuple('\u016E','\xD9'), tuple('\u0170','\xDB'), + tuple('\u0179','\xAC'), tuple('\u017B','\xAF'), tuple('\u017D','\xAE'), + tuple('\u02C7','\xB7'), tuple('\u02D9','\xFF'), tuple('\u02DD','\xBD'), + tuple('\u00A4','\xA4'), tuple('\u00A8','\xA8'), tuple('\u00B0','\xB0'), + tuple('\u00B8','\xB8'), tuple('\u00C2','\xC2'), tuple('\u00C7','\xC7'), + tuple('\u00CB','\xCB'), tuple('\u00CE','\xCE'), tuple('\u00D4','\xD4'), + tuple('\u00D7','\xD7'), tuple('\u00DC','\xDC'), tuple('\u00DF','\xDF'), + tuple('\u00E2','\xE2'), tuple('\u00E7','\xE7'), tuple('\u00EB','\xEB'), + tuple('\u00EE','\xEE'), tuple('\u00F4','\xF4'), tuple('\u00F7','\xF7'), + tuple('\u00FC','\xFC'), tuple('\u0102','\xC3'), tuple('\u0104','\xA1'), + tuple('\u0106','\xC6'), tuple('\u010C','\xC8'), tuple('\u010E','\xCF'), + tuple('\u0110','\xD0'), tuple('\u0118','\xCA'), tuple('\u011A','\xCC'), + tuple('\u0139','\xC5'), tuple('\u013D','\xA5'), tuple('\u0141','\xA3'), + tuple('\u0143','\xD1'), tuple('\u0147','\xD2') + ]; -template EncoderInstance(CharType : Windows1252Char) -{ - alias E = Windows1252Char; - alias EString = Windows1252String; + mixin GenericEncoder!(); +} - @property string encodingName() - { - return "windows-1252"; - } +//============================================================================= +// WINDOWS-1250 +//============================================================================= - immutable wstring charMap = - "\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021"~ - "\u02C6\u2030\u0160\u2039\u0152\uFFFD\u017D\uFFFD"~ - "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2103\u2014"~ - "\u02DC\u2122\u0161\u203A\u0153\uFFFD\u017E\u0178" - ; +/// Defines a Windows1250-encoded character. +enum Windows1250Char : ubyte { init } - bool canEncode(dchar c) - { - if (c < 0x80 || (c >= 0xA0 && c < 0x100)) return true; - if (c >= 0xFFFD) return false; - foreach(wchar d;charMap) { if (c == d) return true; } - return false; - } +/** + * Defines an Windows1250-encoded string (as an array of $(D + * immutable(Windows1250Char))). + */ +alias Windows1250String = immutable(Windows1250Char)[]; + +private template EncoderInstance(CharType : Windows1250Char) +{ + import std.typecons : Tuple, tuple; + + alias E = Windows1250Char; + alias EString = Windows1250String; + + @property string encodingName() @safe pure nothrow @nogc + { + return "windows-1250"; + } + + private static immutable dchar m_charMapStart = 0x80; + private static immutable dchar m_charMapEnd = 0xff; + + private immutable wstring charMap = + "\u20AC\uFFFD\u201A\uFFFD\u201E\u2026\u2020\u2021"~ + "\uFFFD\u2030\u0160\u2039\u015A\u0164\u017D\u0179"~ + "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014"~ + "\uFFFD\u2122\u0161\u203A\u015B\u0165\u017E\u017A"~ + "\u00A0\u02C7\u02D8\u0141\u00A4\u0104\u00A6\u00A7"~ + "\u00A8\u00A9\u015E\u00AB\u00AC\u00AD\u00AE\u017B"~ + "\u00B0\u00B1\u02DB\u0142\u00B4\u00B5\u00B6\u00B7"~ + "\u00B8\u0105\u015F\u00BB\u013D\u02DD\u013E\u017C"~ + "\u0154\u00C1\u00C2\u0102\u00C4\u0139\u0106\u00C7"~ + "\u010C\u00C9\u0118\u00CB\u011A\u00CD\u00CE\u010E"~ + "\u0110\u0143\u0147\u00D3\u00D4\u0150\u00D6\u00D7"~ + "\u0158\u016E\u00DA\u0170\u00DC\u00DD\u0162\u00DF"~ + "\u0155\u00E1\u00E2\u0103\u00E4\u013A\u0107\u00E7"~ + "\u010D\u00E9\u0119\u00EB\u011B\u00ED\u00EE\u010F"~ + "\u0111\u0144\u0148\u00F3\u00F4\u0151\u00F6\u00F7"~ + "\u0159\u016F\u00FA\u0171\u00FC\u00FD\u0163\u02D9"; + + private immutable Tuple!(wchar, char)[] bstMap = [ + tuple('\u011A','\xCC'), tuple('\u00DC','\xDC'), tuple('\u0179','\x8F'), + tuple('\u00B7','\xB7'), tuple('\u00FC','\xFC'), tuple('\u0158','\xD8'), + tuple('\u201C','\x93'), tuple('\u00AC','\xAC'), tuple('\u00CB','\xCB'), + tuple('\u00EB','\xEB'), tuple('\u010C','\xC8'), tuple('\u0143','\xD1'), + tuple('\u0162','\xDE'), tuple('\u02D9','\xFF'), tuple('\u2039','\x8B'), + tuple('\u00A7','\xA7'), tuple('\u00B1','\xB1'), tuple('\u00C2','\xC2'), + tuple('\u00D4','\xD4'), tuple('\u00E2','\xE2'), tuple('\u00F4','\xF4'), + tuple('\u0104','\xA5'), tuple('\u0110','\xD0'), tuple('\u013D','\xBC'), + tuple('\u0150','\xD5'), tuple('\u015E','\xAA'), tuple('\u016E','\xD9'), + tuple('\u017D','\x8E'), tuple('\u2014','\x97'), tuple('\u2021','\x87'), + tuple('\u20AC','\x80'), tuple('\u00A4','\xA4'), tuple('\u00A9','\xA9'), + tuple('\u00AE','\xAE'), tuple('\u00B5','\xB5'), tuple('\u00BB','\xBB'), + tuple('\u00C7','\xC7'), tuple('\u00CE','\xCE'), tuple('\u00D7','\xD7'), + tuple('\u00DF','\xDF'), tuple('\u00E7','\xE7'), tuple('\u00EE','\xEE'), + tuple('\u00F7','\xF7'), tuple('\u0102','\xC3'), tuple('\u0106','\xC6'), + tuple('\u010E','\xCF'), tuple('\u0118','\xCA'), tuple('\u0139','\xC5'), + tuple('\u0141','\xA3'), tuple('\u0147','\xD2'), tuple('\u0154','\xC0'), + tuple('\u015A','\x8C'), tuple('\u0160','\x8A'), tuple('\u0164','\x8D'), + tuple('\u0170','\xDB'), tuple('\u017B','\xAF'), tuple('\u02C7','\xA1'), + tuple('\u02DD','\xBD'), tuple('\u2019','\x92'), tuple('\u201E','\x84'), + tuple('\u2026','\x85'), tuple('\u203A','\x9B'), tuple('\u2122','\x99'), + tuple('\u00A0','\xA0'), tuple('\u00A6','\xA6'), tuple('\u00A8','\xA8'), + tuple('\u00AB','\xAB'), tuple('\u00AD','\xAD'), tuple('\u00B0','\xB0'), + tuple('\u00B4','\xB4'), tuple('\u00B6','\xB6'), tuple('\u00B8','\xB8'), + tuple('\u00C1','\xC1'), tuple('\u00C4','\xC4'), tuple('\u00C9','\xC9'), + tuple('\u00CD','\xCD'), tuple('\u00D3','\xD3'), tuple('\u00D6','\xD6'), + tuple('\u00DA','\xDA'), tuple('\u00DD','\xDD'), tuple('\u00E1','\xE1'), + tuple('\u00E4','\xE4'), tuple('\u00E9','\xE9'), tuple('\u00ED','\xED'), + tuple('\u00F3','\xF3'), tuple('\u00F6','\xF6'), tuple('\u00FA','\xFA'), + tuple('\u00FD','\xFD'), tuple('\u0103','\xE3'), tuple('\u0105','\xB9'), + tuple('\u0107','\xE6'), tuple('\u010D','\xE8'), tuple('\u010F','\xEF'), + tuple('\u0111','\xF0'), tuple('\u0119','\xEA'), tuple('\u011B','\xEC'), + tuple('\u013A','\xE5'), tuple('\u013E','\xBE'), tuple('\u0142','\xB3'), + tuple('\u0144','\xF1'), tuple('\u0148','\xF2'), tuple('\u0151','\xF5'), + tuple('\u0155','\xE0'), tuple('\u0159','\xF8'), tuple('\u015B','\x9C'), + tuple('\u015F','\xBA'), tuple('\u0161','\x9A'), tuple('\u0163','\xFE'), + tuple('\u0165','\x9D'), tuple('\u016F','\xF9'), tuple('\u0171','\xFB'), + tuple('\u017A','\x9F'), tuple('\u017C','\xBF'), tuple('\u017E','\x9E'), + tuple('\u02D8','\xA2'), tuple('\u02DB','\xB2'), tuple('\u2013','\x96'), + tuple('\u2018','\x91'), tuple('\u201A','\x82'), tuple('\u201D','\x94'), + tuple('\u2020','\x86'), tuple('\u2022','\x95'), tuple('\u2030','\x89') + ]; - bool isValidCodeUnit(Windows1252Char c) - { - if (c < 0x80 || c >= 0xA0) return true; - return (charMap[c-0x80] != 0xFFFD); - } + mixin GenericEncoder!(); +} - size_t encodedLength(dchar c) - in - { - assert(canEncode(c)); - } - body - { - return 1; - } +//============================================================================= +// WINDOWS-1252 +//============================================================================= - void encodeViaWrite()(dchar c) - { - if (c < 0x80 || (c >= 0xA0 && c < 0x100)) {} - else if (c >= 0xFFFD) { c = '?'; } - else - { - ptrdiff_t n = -1; - foreach (i, wchar d; charMap) - { - if (c == d) - { - n = i; - break; - } - } - c = n == -1 ? '?' : 0x80 + cast(dchar) n; - } - write(cast(Windows1252Char)c); - } +/// Defines a Windows1252-encoded character. +enum Windows1252Char : ubyte { init } - void skipViaRead()() - { - read(); - } +/** + * Defines an Windows1252-encoded string (as an array of $(D + * immutable(Windows1252Char))). + */ +alias Windows1252String = immutable(Windows1252Char)[]; - dchar decodeViaRead()() - { - Windows1252Char c = read(); - return (c >= 0x80 && c < 0xA0) ? charMap[c-0x80] : c; - } +template EncoderInstance(CharType : Windows1252Char) +{ + import std.typecons : Tuple, tuple; - dchar safeDecodeViaRead()() - { - Windows1252Char c = read(); - dchar d = (c >= 0x80 && c < 0xA0) ? charMap[c-0x80] : c; - return d == 0xFFFD ? INVALID_SEQUENCE : d; - } + alias E = Windows1252Char; + alias EString = Windows1252String; - dchar decodeReverseViaRead()() + @property string encodingName() @safe pure nothrow @nogc { - Windows1252Char c = read(); - return (c >= 0x80 && c < 0xA0) ? charMap[c-0x80] : c; + return "windows-1252"; } - @property EString replacementSequence() - { - return cast(EString)("?"); - } + private static immutable dchar m_charMapStart = 0x80; + private static immutable dchar m_charMapEnd = 0x9f; - mixin EncoderFunctions; + private immutable wstring charMap = + "\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021"~ + "\u02C6\u2030\u0160\u2039\u0152\uFFFD\u017D\uFFFD"~ + "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2013\u2014"~ + "\u02DC\u2122\u0161\u203A\u0153\uFFFD\u017E\u0178"; + + private immutable Tuple!(wchar, char)[] bstMap = [ + tuple('\u201C','\x93'), tuple('\u0192','\x83'), tuple('\u2039','\x8B'), + tuple('\u0161','\x9A'), tuple('\u2014','\x97'), tuple('\u2021','\x87'), + tuple('\u20AC','\x80'), tuple('\u0153','\x9C'), tuple('\u017D','\x8E'), + tuple('\u02DC','\x98'), tuple('\u2019','\x92'), tuple('\u201E','\x84'), + tuple('\u2026','\x85'), tuple('\u203A','\x9B'), tuple('\u2122','\x99'), + tuple('\u0152','\x8C'), tuple('\u0160','\x8A'), tuple('\u0178','\x9F'), + tuple('\u017E','\x9E'), tuple('\u02C6','\x88'), tuple('\u2013','\x96'), + tuple('\u2018','\x91'), tuple('\u201A','\x82'), tuple('\u201D','\x94'), + tuple('\u2020','\x86'), tuple('\u2022','\x95'), tuple('\u2030','\x89') + ]; + + mixin GenericEncoder!(); } //============================================================================= @@ -909,17 +1173,17 @@ template EncoderInstance(CharType : char) alias E = char; alias EString = immutable(char)[]; - @property string encodingName() + @property string encodingName() @safe pure nothrow @nogc { return "UTF-8"; } - bool canEncode(dchar c) + bool canEncode(dchar c) @safe pure nothrow @nogc { return isValidCodePoint(c); } - bool isValidCodeUnit(char c) + bool isValidCodeUnit(char c) @safe pure nothrow @nogc { return (c < 0xC0 || (c >= 0xC2 && c < 0xF5)); } @@ -936,7 +1200,7 @@ template EncoderInstance(CharType : char) 3,3,3,3,3,3,3,3,4,4,4,4,5,5,6,0, ]; - private int tails(char c) + private int tails(char c) @safe pure nothrow @nogc in { assert(c >= 0x80); @@ -946,7 +1210,7 @@ template EncoderInstance(CharType : char) return tailTable[c-0x80]; } - size_t encodedLength(dchar c) + size_t encodedLength(dchar c) @safe pure nothrow @nogc in { assert(canEncode(c)); @@ -963,7 +1227,7 @@ template EncoderInstance(CharType : char) { if (c < 0x80) { - write(cast(char)c); + write(cast(char) c); } else if (c < 0x800) { @@ -1018,7 +1282,7 @@ template EncoderInstance(CharType : char) if (!canRead) return INVALID_SEQUENCE; size_t d = peek(); - bool err = + immutable err = ( (c < 0xC2) // fail overlong 2-byte sequences || (c > 0xF4) // fail overlong 4-6-byte sequences @@ -1051,14 +1315,14 @@ template EncoderInstance(CharType : char) shift += 6; auto d = read(); size_t n = tails(cast(char) d); - size_t mask = n == 0 ? 0x3F : (1 << (6 - n)) - 1; + immutable mask = n == 0 ? 0x3F : (1 << (6 - n)) - 1; c += ((d & mask) << shift); if (n != 0) break; } return c; } - @property EString replacementSequence() + @property EString replacementSequence() @safe pure nothrow @nogc { return "\uFFFD"; } @@ -1075,36 +1339,36 @@ template EncoderInstance(CharType : wchar) alias E = wchar; alias EString = immutable(wchar)[]; - @property string encodingName() + @property string encodingName() @safe pure nothrow @nogc { return "UTF-16"; } - bool canEncode(dchar c) + bool canEncode(dchar c) @safe pure nothrow @nogc { return isValidCodePoint(c); } - bool isValidCodeUnit(wchar c) + bool isValidCodeUnit(wchar c) @safe pure nothrow @nogc { return true; } - size_t encodedLength(dchar c) + size_t encodedLength(dchar c) @safe pure nothrow @nogc in { assert(canEncode(c)); } body { - return (c < 0x10000) ? 1 : 2; + return (c < 0x10000) ? 1 : 2; } void encodeViaWrite()(dchar c) { if (c < 0x10000) { - write(cast(wchar)c); + write(cast(wchar) c); } else { @@ -1116,7 +1380,7 @@ template EncoderInstance(CharType : wchar) void skipViaRead()() { - wchar c = read(); + immutable c = read(); if (c < 0xD800 || c >= 0xE000) return; read(); } @@ -1124,7 +1388,7 @@ template EncoderInstance(CharType : wchar) dchar decodeViaRead()() { wchar c = read(); - if (c < 0xD800 || c >= 0xE000) return cast(dchar)c; + if (c < 0xD800 || c >= 0xE000) return cast(dchar) c; wchar d = read(); c &= 0x3FF; d &= 0x3FF; @@ -1134,7 +1398,7 @@ template EncoderInstance(CharType : wchar) dchar safeDecodeViaRead()() { wchar c = read(); - if (c < 0xD800 || c >= 0xE000) return cast(dchar)c; + if (c < 0xD800 || c >= 0xE000) return cast(dchar) c; if (c >= 0xDC00) return INVALID_SEQUENCE; if (!canRead) return INVALID_SEQUENCE; wchar d = peek(); @@ -1148,14 +1412,14 @@ template EncoderInstance(CharType : wchar) dchar decodeReverseViaRead()() { wchar c = read(); - if (c < 0xD800 || c >= 0xE000) return cast(dchar)c; + if (c < 0xD800 || c >= 0xE000) return cast(dchar) c; wchar d = read(); c &= 0x3FF; d &= 0x3FF; return 0x10000 + (d << 10) + c; } - @property EString replacementSequence() + @property EString replacementSequence() @safe pure nothrow @nogc { return "\uFFFD"w; } @@ -1172,29 +1436,29 @@ template EncoderInstance(CharType : dchar) alias E = dchar; alias EString = immutable(dchar)[]; - @property string encodingName() + @property string encodingName() @safe pure nothrow @nogc { return "UTF-32"; } - bool canEncode(dchar c) + bool canEncode(dchar c) @safe pure @nogc nothrow { return isValidCodePoint(c); } - bool isValidCodeUnit(dchar c) + bool isValidCodeUnit(dchar c) @safe pure @nogc nothrow { return isValidCodePoint(c); } - size_t encodedLength(dchar c) + size_t encodedLength(dchar c) @safe pure @nogc nothrow in { assert(canEncode(c)); } body { - return 1; + return 1; } void encodeViaWrite()(dchar c) @@ -1209,21 +1473,21 @@ template EncoderInstance(CharType : dchar) dchar decodeViaRead()() { - return cast(dchar)read(); + return cast(dchar) read(); } dchar safeDecodeViaRead()() { - dchar c = read(); + immutable c = read(); return isValidCodePoint(c) ? c : INVALID_SEQUENCE; } dchar decodeReverseViaRead()() { - return cast(dchar)read(); + return cast(dchar) read(); } - @property EString replacementSequence() + @property EString replacementSequence() @safe pure nothrow @nogc { return "\uFFFD"d; } @@ -1244,12 +1508,13 @@ Returns true if c is a valid code point Supersedes: This function supersedes $(D std.utf.startsValidDchar()). - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code point to be tested */ -bool isValidCodePoint(dchar c) +bool isValidCodePoint(dchar c) @safe pure nothrow @nogc { return c < 0xD800 || (c >= 0xE000 && c < 0x110000); } @@ -1260,7 +1525,8 @@ bool isValidCodePoint(dchar c) The type of encoding cannot be deduced. Therefore, it is necessary to explicitly specify the encoding type. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 */ @property string encodingName(T)() { @@ -1268,13 +1534,15 @@ bool isValidCodePoint(dchar c) } /// -unittest +@safe unittest { assert(encodingName!(char) == "UTF-8"); assert(encodingName!(wchar) == "UTF-16"); assert(encodingName!(dchar) == "UTF-32"); assert(encodingName!(AsciiChar) == "ASCII"); assert(encodingName!(Latin1Char) == "ISO-8859-1"); + assert(encodingName!(Latin2Char) == "ISO-8859-2"); + assert(encodingName!(Windows1250Char) == "windows-1250"); assert(encodingName!(Windows1252Char) == "windows-1252"); } @@ -1285,7 +1553,8 @@ unittest The type of encoding cannot be deduced. Therefore, it is necessary to explicitly specify the encoding type. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 */ bool canEncode(E)(dchar c) { @@ -1293,15 +1562,32 @@ bool canEncode(E)(dchar c) } /// -unittest +@safe pure unittest { - assert( canEncode!(Latin1Char)('A')); + assert( canEncode!(Latin1Char)('A')); + assert( canEncode!(Latin2Char)('A')); assert(!canEncode!(AsciiChar)('\u00A0')); assert( canEncode!(Latin1Char)('\u00A0')); + assert( canEncode!(Latin2Char)('\u00A0')); + assert( canEncode!(Windows1250Char)('\u20AC')); + assert(!canEncode!(Windows1250Char)('\u20AD')); + assert(!canEncode!(Windows1250Char)('\uFFFD')); assert( canEncode!(Windows1252Char)('\u20AC')); assert(!canEncode!(Windows1252Char)('\u20AD')); assert(!canEncode!(Windows1252Char)('\uFFFD')); - assert(!canEncode!(char)(cast(dchar)0x110000)); + assert(!canEncode!(char)(cast(dchar) 0x110000)); +} + +/// How to check an entire string +@safe pure unittest +{ + import std.algorithm.searching : find; + import std.utf : byDchar; + + assert("The quick brown fox" + .byDchar + .find!(x => !canEncode!AsciiChar(x)) + .empty); } /** @@ -1309,7 +1595,8 @@ unittest not be legal in ASCII, because ASCII code units must always be in the range 0x00 to 0x7F. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code unit to be tested @@ -1320,15 +1607,17 @@ bool isValidCodeUnit(E)(E c) } /// -unittest +@system pure unittest { - assert(!isValidCodeUnit(cast(char)0xC0)); - assert(!isValidCodeUnit(cast(char)0xFF)); - assert( isValidCodeUnit(cast(wchar)0xD800)); - assert(!isValidCodeUnit(cast(dchar)0xD800)); - assert(!isValidCodeUnit(cast(AsciiChar)0xA0)); - assert( isValidCodeUnit(cast(Windows1252Char)0x80)); - assert(!isValidCodeUnit(cast(Windows1252Char)0x81)); + assert(!isValidCodeUnit(cast(char) 0xC0)); + assert(!isValidCodeUnit(cast(char) 0xFF)); + assert( isValidCodeUnit(cast(wchar) 0xD800)); + assert(!isValidCodeUnit(cast(dchar) 0xD800)); + assert(!isValidCodeUnit(cast(AsciiChar) 0xA0)); + assert( isValidCodeUnit(cast(Windows1250Char) 0x80)); + assert(!isValidCodeUnit(cast(Windows1250Char) 0x81)); + assert( isValidCodeUnit(cast(Windows1252Char) 0x80)); + assert(!isValidCodeUnit(cast(Windows1252Char) 0x81)); } /** @@ -1339,7 +1628,8 @@ unittest function returns a bool indicating whether the input was valid or not, whereas the older function would throw an exception. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string to be tested @@ -1350,7 +1640,7 @@ bool isValid(E)(const(E)[] s) } /// -unittest +@system pure unittest { assert( isValid("\u20AC100")); assert(!isValid(cast(char[3])[167, 133, 175])); @@ -1360,7 +1650,8 @@ unittest Returns the length of the longest possible substring, starting from the first code unit, which is validly encoded. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string to be tested @@ -1388,7 +1679,8 @@ size_t validLength(E)(const(E)[] s) character repertoire contains it, otherwise invalid sequences will be replaced with '?'. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string to be sanitized @@ -1406,7 +1698,7 @@ immutable(E)[] sanitize(E)(immutable(E)[] s) const(E)[] t = s[n..$]; while (t.length != 0) { - dchar c = EncoderInstance!(E).safeDecode(t); + immutable c = EncoderInstance!(E).safeDecode(t); assert(c == INVALID_SEQUENCE); len += repSeq.length; t = t[validLength(t)..$]; @@ -1414,40 +1706,41 @@ immutable(E)[] sanitize(E)(immutable(E)[] s) // Now do the write E[] array = new E[len]; - array[0..n] = s[0..n]; + array[0 .. n] = s[0 .. n]; size_t offset = n; t = s[n..$]; while (t.length != 0) { - dchar c = EncoderInstance!(E).safeDecode(t); + immutable c = EncoderInstance!(E).safeDecode(t); assert(c == INVALID_SEQUENCE); - array[offset..offset+repSeq.length] = repSeq[]; + array[offset .. offset+repSeq.length] = repSeq[]; offset += repSeq.length; n = validLength(t); - array[offset..offset+n] = t[0..n]; + array[offset .. offset+n] = t[0 .. n]; offset += n; t = t[n..$]; } - return cast(immutable(E)[])array[0..offset]; + return cast(immutable(E)[])array[0 .. offset]; } /// -unittest +@system pure unittest { assert(sanitize("hello \xF0\x80world") == "hello \xEF\xBF\xBDworld"); } /** -Returns the length of the first encoded sequence. + Returns the length of the first encoded sequence. -The input to this function MUST be validly encoded. -This is enforced by the function's in-contract. + The input to this function MUST be validly encoded. + This is enforced by the function's in-contract. -Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 -Params: -s = the string to be sliced + Params: + s = the string to be sliced */ size_t firstSequence(E)(const(E)[] s) in @@ -1464,7 +1757,7 @@ body } /// -unittest +@system pure unittest { assert(firstSequence("\u20AC1000") == "\u20AC".length); assert(firstSequence("hel") == "h".length); @@ -1476,7 +1769,8 @@ unittest The input to this function MUST be validly encoded. This is enforced by the function's in-contract. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string to be sliced @@ -1495,7 +1789,7 @@ body } /// -unittest +@system pure unittest { assert(lastSequence("1000\u20AC") == "\u20AC".length); assert(lastSequence("hellö") == "ö".length); @@ -1510,7 +1804,8 @@ unittest Supersedes: This function supersedes std.utf.toUTFindex(). - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string to be counted @@ -1530,7 +1825,7 @@ body } /// -unittest +@system pure unittest { assert(index("\u20AC100",1) == 3); assert(index("hällo",2) == 3); @@ -1549,7 +1844,8 @@ unittest This function supersedes std.utf.decode(), however, note that the function codePoints() supersedes it more conveniently. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string whose first code point is to be decoded @@ -1575,7 +1871,8 @@ body The input to this function MUST be validly encoded. This is enforced by the function's in-contract. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string whose first code point is to be decoded @@ -1601,7 +1898,8 @@ body If an invalid sequence is found at the start of the string, this function will remove it, and return the value INVALID_SEQUENCE. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string whose first code point is to be decoded @@ -1625,7 +1923,8 @@ body The type of the output cannot be deduced. Therefore, it is necessary to explicitly specify the encoding as a template parameter. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code point to be encoded @@ -1656,7 +1955,8 @@ body This function supersedes std.utf.encode(), however, note that the function codeUnits() supersedes it more conveniently. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code point to be encoded @@ -1688,7 +1988,8 @@ body This function supersedes std.utf.encode(), however, note that the function codeUnits() supersedes it more conveniently. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code point to be encoded @@ -1709,37 +2010,6 @@ body return array.length - t.length; } -// /** -// * Encodes a single code point into a Buffer. -// * -// * This function encodes a single code point into one or more code units -// * The code units are stored in a growable buffer. -// * -// * The input to this function MUST be a valid code point. -// * This is enforced by the function's in-contract. -// * -// * The type of the output cannot be deduced. Therefore, it is necessary to -// * explicitly specify the encoding as a template parameter. -// * -// * Supersedes: -// * This function supersedes std.utf.encode(), however, note that the -// * function codeUnits() supersedes it more conveniently. -// * -// * Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 -// * -// * Params: -// * c = the code point to be encoded -// */ -// deprecated void encode(E)(dchar c, ref Buffer!(E) buffer) -// in -// { -// assert(isValidCodePoint(c)); -// } -// body -// { -// EncoderInstance!(E).encode(c,buffer); -// } - /* Encodes $(D c) in units of type $(D E) and writes the result to the output range $(D R). Returns the number of $(D E)s written. @@ -1802,7 +2072,7 @@ if (isNativeOutputRange!(R, E)) } } -unittest +@safe pure unittest { import std.array; Appender!(char[]) r; @@ -1827,7 +2097,8 @@ unittest This function supersedes std.utf.encode(), however, note that the function codeUnits() supersedes it more conveniently. - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code point to be encoded @@ -1878,21 +2149,22 @@ size_t encode(Tgt, Src, R)(in Src[] s, R range) Supersedes: This function supersedes std.utf.decode(). - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = the string to be decoded - Examples: + Example: -------------------------------------------------------- string s = "hello world"; - foreach(c;codePoints(s)) + foreach (c;codePoints(s)) { // do something with c (which will always be a dchar) } -------------------------------------------------------- - Note that, currently, foreach(c:codePoints(s)) is superior to foreach(c;s) + Note that, currently, foreach (c:codePoints(s)) is superior to foreach (c;s) in that the latter will fall over on encountering U+FFFF. */ CodePoints!(E) codePoints(E)(immutable(E)[] s) @@ -1906,13 +2178,13 @@ body } /// -unittest +@system unittest { string s = "hello"; string t; - foreach(c;codePoints(s)) + foreach (c;codePoints(s)) { - t ~= cast(char)c; + t ~= cast(char) c; } assert(s == t); } @@ -1930,7 +2202,8 @@ unittest Supersedes: This function supersedes std.utf.encode(). - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: c = the code point to be encoded @@ -1946,10 +2219,10 @@ body } /// -unittest +@system unittest { char[] a; - foreach(c;codeUnits!(char)(cast(dchar)'\u20AC')) + foreach (c;codeUnits!(char)(cast(dchar)'\u20AC')) { a ~= c; } @@ -1967,7 +2240,8 @@ unittest std.utf.toUTF32() (but note that to!() supersedes it more conveniently). - Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 + Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250, + WINDOWS-1252 Params: s = Source string. $(B Must) be validly encoded. @@ -1975,30 +2249,30 @@ unittest r = Destination string See_Also: - $(XREF conv, to) + $(REF to, std,conv) */ -void transcode(Src,Dst)(immutable(Src)[] s,out immutable(Dst)[] r) +void transcode(Src, Dst)(Src[] s, out Dst[] r) in { assert(isValid(s)); } body { - static if(is(Src==Dst)) + static if (is(Src == Dst) && is(Src == immutable)) { r = s; } - else static if(is(Src==AsciiChar)) + else static if (is(Unqual!Src == AsciiChar)) { - transcode!(char,Dst)(cast(string)s,r); + transcode(cast(const(char)[])s, r); } else { - static if(is(Dst == wchar)) + static if (is(Unqual!Dst == wchar)) { immutable minReservePlace = 2; } - else static if(is(Dst == dchar)) + else static if (is(Unqual!Dst == dchar)) { immutable minReservePlace = 1; } @@ -2007,27 +2281,26 @@ body immutable minReservePlace = 6; } - Dst[] buffer = new Dst[s.length]; - Dst[] tmpBuffer = buffer; - const(Src)[] t = s; + auto buffer = new Unqual!Dst[s.length]; + auto tmpBuffer = buffer; - while (t.length != 0) + while (s.length != 0) { - if(tmpBuffer.length < minReservePlace) + if (tmpBuffer.length < minReservePlace) { size_t prevLength = buffer.length; - buffer.length += t.length + minReservePlace; + buffer.length += s.length + minReservePlace; tmpBuffer = buffer[prevLength - tmpBuffer.length .. $]; } - EncoderInstance!(Dst).encode(decode(t), tmpBuffer); + EncoderInstance!(Unqual!Dst).encode(decode(s), tmpBuffer); } - r = cast(immutable)buffer[0 .. buffer.length - tmpBuffer.length]; + r = cast(Dst[]) buffer[0 .. buffer.length - tmpBuffer.length]; } } /// -unittest +@system pure unittest { wstring ws; // transcode from UTF-8 to UTF-16 @@ -2040,18 +2313,19 @@ unittest assert(ws == "hello world"); } -unittest +@system pure unittest { + import std.meta; import std.range; - import std.typetuple; { import std.conv : to; string asciiCharString = to!string(iota(0, 128, 1)); - alias Types = TypeTuple!(string, Latin1String, AsciiString, Windows1252String, dstring, wstring); - foreach(S; Types) - foreach(D; Types) + alias Types = AliasSeq!(string, Latin1String, Latin2String, AsciiString, + Windows1250String, Windows1252String, dstring, wstring); + foreach (S; Types) + foreach (D; Types) { string str; S sStr; @@ -2064,9 +2338,9 @@ unittest } { string czechChars = "PříliÅ¡ žluÅ¥ouÄký kůň úpÄ›l Äábelské ódy."; - alias Types = TypeTuple!(string, dstring, wstring); - foreach(S; Types) - foreach(D; Types) + alias Types = AliasSeq!(string, dstring, wstring); + foreach (S; Types) + foreach (D; Types) { string str; S sStr; @@ -2079,14 +2353,52 @@ unittest } } +@system unittest // mutable/const input/output +{ + import std.meta : AliasSeq; + + foreach (O; AliasSeq!(Latin1Char, const Latin1Char, immutable Latin1Char)) + { + O[] output; + + char[] mutableInput = "äbc".dup; + transcode(mutableInput, output); + assert(output == [0xE4, 'b', 'c']); + + const char[] constInput = "öbc"; + transcode(constInput, output); + assert(output == [0xF6, 'b', 'c']); + + immutable char[] immutInput = "übc"; + transcode(immutInput, output); + assert(output == [0xFC, 'b', 'c']); + } + + // Make sure that const/mutable input is copied. + foreach (C; AliasSeq!(char, const char)) + { + C[] input = "foo".dup; + C[] output; + transcode(input, output); + assert(input == output); + assert(input !is output); + } + + // But immutable input should not be copied. + string input = "foo"; + string output; + transcode(input, output); + assert(input is output); +} + //============================================================================= /** The base class for exceptions thrown by this module */ -class EncodingException : Exception { this(string msg) { super(msg); } } +class EncodingException : Exception { this(string msg) @safe pure { super(msg); } } class UnrecognizedEncodingException : EncodingException { - private this(string msg) { super(msg); } + private this(string msg) @safe pure { super(msg); } } /** Abstract base class of all encoding schemes */ @@ -2100,25 +2412,38 @@ abstract class EncodingScheme * This function allows user-defined subclasses of EncodingScheme to * be declared in other modules. * - * Examples: + * Params: + * Klass = The subclass of EncodingScheme to register. + * + * Example: * ---------------------------------------------- * class Amiga1251 : EncodingScheme * { * shared static this() * { - * EncodingScheme.register("path.to.Amiga1251"); + * EncodingScheme.register!Amiga1251; * } * } * ---------------------------------------------- */ + static void register(Klass:EncodingScheme)() + { + scope scheme = new Klass(); + foreach (encodingName;scheme.names()) + { + supported[toLower(encodingName)] = () => new Klass(); + } + } + + deprecated("Please pass the EncodingScheme subclass as template argument instead.") static void register(string className) { - auto scheme = cast(EncodingScheme)ClassInfo.find(className).create(); + auto scheme = cast(EncodingScheme) ClassInfo.find(className).create(); if (scheme is null) throw new EncodingException("Unable to create class "~className); - foreach(encodingName;scheme.names()) + foreach (encodingName;scheme.names()) { - supported[toLower(encodingName)] = className; + supportedFactories[toLower(encodingName)] = className; } } @@ -2129,18 +2454,23 @@ abstract class EncodingScheme * This function is only aware of EncodingSchemes which have been * registered with the register() function. * - * Examples: + * Example: * --------------------------------------------------- * auto scheme = EncodingScheme.create("Amiga-1251"); * --------------------------------------------------- */ static EncodingScheme create(string encodingName) { - auto p = toLower(encodingName) in supported; + encodingName = toLower(encodingName); + + if (auto p = encodingName in supported) + return (*p)(); + + auto p = encodingName in supportedFactories; if (p is null) throw new EncodingException("Unrecognized Encoding: "~encodingName); string className = *p; - auto scheme = cast(EncodingScheme)ClassInfo.find(className).create(); + auto scheme = cast(EncodingScheme) ClassInfo.find(className).create(); if (scheme is null) throw new EncodingException("Unable to create class "~className); return scheme; } @@ -2243,8 +2573,7 @@ abstract class EncodingScheme { while (s.length != 0) { - dchar d = safeDecode(s); - if (d == INVALID_SEQUENCE) + if (safeDecode(s) == INVALID_SEQUENCE) return false; } return true; @@ -2257,7 +2586,7 @@ abstract class EncodingScheme * Params: * s = the array to be tested */ - size_t validLength(const(ubyte)[] s) + size_t validLength()(const(ubyte)[] s) { const(ubyte)[] r = s; const(ubyte)[] t = s; @@ -2281,7 +2610,7 @@ abstract class EncodingScheme * Params: * s = the string to be sanitized */ - immutable(ubyte)[] sanitize(immutable(ubyte)[] s) + immutable(ubyte)[] sanitize()(immutable(ubyte)[] s) { auto n = validLength(s); if (n == s.length) return s; @@ -2294,7 +2623,7 @@ abstract class EncodingScheme const(ubyte)[] t = s[n..$]; while (t.length != 0) { - dchar c = safeDecode(t); + immutable c = safeDecode(t); assert(c == INVALID_SEQUENCE); len += repSeq.length; t = t[validLength(t)..$]; @@ -2302,22 +2631,22 @@ abstract class EncodingScheme // Now do the write ubyte[] array = new ubyte[len]; - array[0..n] = s[0..n]; + array[0 .. n] = s[0 .. n]; auto offset = n; t = s[n..$]; while (t.length != 0) { - dchar c = safeDecode(t); + immutable c = safeDecode(t); assert(c == INVALID_SEQUENCE); - array[offset..offset+repSeq.length] = repSeq[]; + array[offset .. offset+repSeq.length] = repSeq[]; offset += repSeq.length; n = validLength(t); - array[offset..offset+n] = t[0..n]; + array[offset .. offset+n] = t[0 .. n]; offset += n; t = t[n..$]; } - return cast(immutable(ubyte)[])array[0..offset]; + return cast(immutable(ubyte)[])array[0 .. offset]; } /** @@ -2329,7 +2658,7 @@ abstract class EncodingScheme * Params: * s = the array to be sliced */ - size_t firstSequence(const(ubyte)[] s) + size_t firstSequence()(const(ubyte)[] s) in { assert(s.length != 0); @@ -2352,7 +2681,7 @@ abstract class EncodingScheme * Params: * s = the string to be counted */ - size_t count(const(ubyte)[] s) + size_t count()(const(ubyte)[] s) in { assert(isValid(s)); @@ -2378,7 +2707,7 @@ abstract class EncodingScheme * s = the string to be counted * n = the current code point index */ - ptrdiff_t index(const(ubyte)[] s, size_t n) + ptrdiff_t index()(const(ubyte)[] s, size_t n) in { assert(isValid(s)); @@ -2391,7 +2720,8 @@ abstract class EncodingScheme return t.length - s.length; } - __gshared string[string] supported; + __gshared EncodingScheme function()[string] supported; + __gshared string[string] supportedFactories; } /** @@ -2412,18 +2742,18 @@ abstract class EncodingScheme */ class EncodingSchemeASCII : EncodingScheme { + /* // moved to std.internal.phobosinit shared static this() { EncodingScheme.register("std.encoding.EncodingSchemeASCII"); - } + }*/ const { - override string[] names() + override string[] names() @safe pure nothrow { return [ - cast(string) "ANSI_X3.4-1968", "ANSI_X3.4-1986", "ASCII", @@ -2438,28 +2768,28 @@ class EncodingSchemeASCII : EncodingScheme ]; } - override string toString() + override string toString() @safe pure nothrow @nogc { return "ASCII"; } - override bool canEncode(dchar c) + override bool canEncode(dchar c) @safe pure nothrow @nogc { return std.encoding.canEncode!(AsciiChar)(c); } - override size_t encodedLength(dchar c) + override size_t encodedLength(dchar c) @safe pure nothrow @nogc { - return std.encoding.encodedLength!(AsciiChar)(c); + return std.encoding.encodedLength!(AsciiChar)(c); } - override size_t encode(dchar c, ubyte[] buffer) + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc { - auto r = cast(AsciiChar[])buffer; + auto r = cast(AsciiChar[]) buffer; return std.encoding.encode(c,r); } - override dchar decode(ref const(ubyte)[] s) + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(AsciiChar)[]) s; dchar c = std.encoding.decode(t); @@ -2467,7 +2797,7 @@ class EncodingSchemeASCII : EncodingScheme return c; } - override dchar safeDecode(ref const(ubyte)[] s) + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(AsciiChar)[]) s; dchar c = std.encoding.safeDecode(t); @@ -2475,7 +2805,7 @@ class EncodingSchemeASCII : EncodingScheme return c; } - override @property immutable(ubyte)[] replacementSequence() + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc { return cast(immutable(ubyte)[])"?"; } @@ -2498,18 +2828,18 @@ class EncodingSchemeASCII : EncodingScheme */ class EncodingSchemeLatin1 : EncodingScheme { + /* // moved to std.internal.phobosinit shared static this() { EncodingScheme.register("std.encoding.EncodingSchemeLatin1"); - } + }*/ const { - override string[] names() + override string[] names() @safe pure nothrow { return [ - cast(string) "CP819", "IBM819", "ISO-8859-1", @@ -2522,28 +2852,28 @@ class EncodingSchemeLatin1 : EncodingScheme ]; } - override string toString() + override string toString() @safe pure nothrow @nogc { return "ISO-8859-1"; } - override bool canEncode(dchar c) + override bool canEncode(dchar c) @safe pure nothrow @nogc { return std.encoding.canEncode!(Latin1Char)(c); } - override size_t encodedLength(dchar c) + override size_t encodedLength(dchar c) @safe pure nothrow @nogc { - return std.encoding.encodedLength!(Latin1Char)(c); + return std.encoding.encodedLength!(Latin1Char)(c); } - override size_t encode(dchar c, ubyte[] buffer) + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc { - auto r = cast(Latin1Char[])buffer; + auto r = cast(Latin1Char[]) buffer; return std.encoding.encode(c,r); } - override dchar decode(ref const(ubyte)[] s) + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(Latin1Char)[]) s; dchar c = std.encoding.decode(t); @@ -2551,7 +2881,7 @@ class EncodingSchemeLatin1 : EncodingScheme return c; } - override dchar safeDecode(ref const(ubyte)[] s) + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(Latin1Char)[]) s; dchar c = std.encoding.safeDecode(t); @@ -2559,7 +2889,151 @@ class EncodingSchemeLatin1 : EncodingScheme return c; } - override @property immutable(ubyte)[] replacementSequence() + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc + { + return cast(immutable(ubyte)[])"?"; + } + } +} + +/** + EncodingScheme to handle Latin-2 + + This scheme recognises the following names: + "Latin 2", + "ISO-8859-2", + "ISO_8859-2", + "ISO_8859-2:1999", + "Windows-28592" + */ +class EncodingSchemeLatin2 : EncodingScheme +{ + /* // moved to std.internal.phobosinit + shared static this() + { + EncodingScheme.register("std.encoding.EncodingSchemeLatin2"); + }*/ + + const + { + override string[] names() @safe pure nothrow + { + return + [ + "Latin 2", + "ISO-8859-2", + "ISO_8859-2", + "ISO_8859-2:1999", + "windows-28592" + ]; + } + + override string toString() @safe pure nothrow @nogc + { + return "ISO-8859-2"; + } + + override bool canEncode(dchar c) @safe pure nothrow @nogc + { + return std.encoding.canEncode!(Latin2Char)(c); + } + + override size_t encodedLength(dchar c) @safe pure nothrow @nogc + { + return std.encoding.encodedLength!(Latin2Char)(c); + } + + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc + { + auto r = cast(Latin2Char[]) buffer; + return std.encoding.encode(c,r); + } + + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc + { + auto t = cast(const(Latin2Char)[]) s; + dchar c = std.encoding.decode(t); + s = s[$-t.length..$]; + return c; + } + + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc + { + auto t = cast(const(Latin2Char)[]) s; + dchar c = std.encoding.safeDecode(t); + s = s[$-t.length..$]; + return c; + } + + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc + { + return cast(immutable(ubyte)[])"?"; + } + } +} + +/** + EncodingScheme to handle Windows-1250 + + This scheme recognises the following names: + "windows-1250" + */ +class EncodingSchemeWindows1250 : EncodingScheme +{ + /* // moved to std.internal.phobosinit + shared static this() + { + EncodingScheme.register("std.encoding.EncodingSchemeWindows1250"); + }*/ + + const + { + override string[] names() @safe pure nothrow + { + return + [ + "windows-1250" + ]; + } + + override string toString() @safe pure nothrow @nogc + { + return "windows-1250"; + } + + override bool canEncode(dchar c) @safe pure nothrow @nogc + { + return std.encoding.canEncode!(Windows1250Char)(c); + } + + override size_t encodedLength(dchar c) @safe pure nothrow @nogc + { + return std.encoding.encodedLength!(Windows1250Char)(c); + } + + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc + { + auto r = cast(Windows1250Char[]) buffer; + return std.encoding.encode(c,r); + } + + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc + { + auto t = cast(const(Windows1250Char)[]) s; + dchar c = std.encoding.decode(t); + s = s[$-t.length..$]; + return c; + } + + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc + { + auto t = cast(const(Windows1250Char)[]) s; + dchar c = std.encoding.safeDecode(t); + s = s[$-t.length..$]; + return c; + } + + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc { return cast(immutable(ubyte)[])"?"; } @@ -2574,44 +3048,44 @@ class EncodingSchemeLatin1 : EncodingScheme */ class EncodingSchemeWindows1252 : EncodingScheme { + /* // moved to std.internal.phobosinit shared static this() { EncodingScheme.register("std.encoding.EncodingSchemeWindows1252"); - } + }*/ const { - override string[] names() + override string[] names() @safe pure nothrow { return [ - cast(string) "windows-1252" ]; } - override string toString() + override string toString() @safe pure nothrow @nogc { return "windows-1252"; } - override bool canEncode(dchar c) + override bool canEncode(dchar c) @safe pure nothrow @nogc { return std.encoding.canEncode!(Windows1252Char)(c); } - override size_t encodedLength(dchar c) + override size_t encodedLength(dchar c) @safe pure nothrow @nogc { - return std.encoding.encodedLength!(Windows1252Char)(c); + return std.encoding.encodedLength!(Windows1252Char)(c); } - override size_t encode(dchar c, ubyte[] buffer) + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc { - auto r = cast(Windows1252Char[])buffer; + auto r = cast(Windows1252Char[]) buffer; return std.encoding.encode(c,r); } - override dchar decode(ref const(ubyte)[] s) + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(Windows1252Char)[]) s; dchar c = std.encoding.decode(t); @@ -2619,7 +3093,7 @@ class EncodingSchemeWindows1252 : EncodingScheme return c; } - override dchar safeDecode(ref const(ubyte)[] s) + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(Windows1252Char)[]) s; dchar c = std.encoding.safeDecode(t); @@ -2627,7 +3101,7 @@ class EncodingSchemeWindows1252 : EncodingScheme return c; } - override @property immutable(ubyte)[] replacementSequence() + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc { return cast(immutable(ubyte)[])"?"; } @@ -2642,44 +3116,44 @@ class EncodingSchemeWindows1252 : EncodingScheme */ class EncodingSchemeUtf8 : EncodingScheme { + /* // moved to std.internal.phobosinit shared static this() { EncodingScheme.register("std.encoding.EncodingSchemeUtf8"); - } + }*/ const { - override string[] names() + override string[] names() @safe pure nothrow { return [ - cast(string) "UTF-8" ]; } - override string toString() + override string toString() @safe pure nothrow @nogc { return "UTF-8"; } - override bool canEncode(dchar c) + override bool canEncode(dchar c) @safe pure nothrow @nogc { return std.encoding.canEncode!(char)(c); } - override size_t encodedLength(dchar c) + override size_t encodedLength(dchar c) @safe pure nothrow @nogc { - return std.encoding.encodedLength!(char)(c); + return std.encoding.encodedLength!(char)(c); } - override size_t encode(dchar c, ubyte[] buffer) + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc { - auto r = cast(char[])buffer; + auto r = cast(char[]) buffer; return std.encoding.encode(c,r); } - override dchar decode(ref const(ubyte)[] s) + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(char)[]) s; dchar c = std.encoding.decode(t); @@ -2687,7 +3161,7 @@ class EncodingSchemeUtf8 : EncodingScheme return c; } - override dchar safeDecode(ref const(ubyte)[] s) + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc { auto t = cast(const(char)[]) s; dchar c = std.encoding.safeDecode(t); @@ -2695,7 +3169,7 @@ class EncodingSchemeUtf8 : EncodingScheme return c; } - override @property immutable(ubyte)[] replacementSequence() + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc { return cast(immutable(ubyte)[])"\uFFFD"; } @@ -2711,43 +3185,44 @@ class EncodingSchemeUtf8 : EncodingScheme */ class EncodingSchemeUtf16Native : EncodingScheme { + /* // moved to std.internal.phobosinit shared static this() { EncodingScheme.register("std.encoding.EncodingSchemeUtf16Native"); - } + }*/ const { version(LittleEndian) { enum string NAME = "UTF-16LE"; } version(BigEndian) { enum string NAME = "UTF-16BE"; } - override string[] names() + override string[] names() @safe pure nothrow { return [ NAME ]; } - override string toString() + override string toString() @safe pure nothrow @nogc { return NAME; } - override bool canEncode(dchar c) + override bool canEncode(dchar c) @safe pure nothrow @nogc { return std.encoding.canEncode!(wchar)(c); } - override size_t encodedLength(dchar c) + override size_t encodedLength(dchar c) @safe pure nothrow @nogc { - return std.encoding.encodedLength!(wchar)(c); + return std.encoding.encodedLength!(wchar)(c); } - override size_t encode(dchar c, ubyte[] buffer) + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc { - auto r = cast(wchar[])buffer; + auto r = cast(wchar[]) buffer; return wchar.sizeof * std.encoding.encode(c,r); } - override dchar decode(ref const(ubyte)[] s) + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc in { assert((s.length & 1) == 0); @@ -2760,7 +3235,7 @@ class EncodingSchemeUtf16Native : EncodingScheme return c; } - override dchar safeDecode(ref const(ubyte)[] s) + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc in { assert((s.length & 1) == 0); @@ -2773,13 +3248,13 @@ class EncodingSchemeUtf16Native : EncodingScheme return c; } - override @property immutable(ubyte)[] replacementSequence() + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc { return cast(immutable(ubyte)[])"\uFFFD"w; } } } -unittest +@system unittest { version(LittleEndian) { @@ -2806,43 +3281,44 @@ unittest */ class EncodingSchemeUtf32Native : EncodingScheme { + /* // moved to std.internal.phobosinit shared static this() { EncodingScheme.register("std.encoding.EncodingSchemeUtf32Native"); - } + }*/ const { version(LittleEndian) { enum string NAME = "UTF-32LE"; } version(BigEndian) { enum string NAME = "UTF-32BE"; } - override string[] names() + override string[] names() @safe pure nothrow { return [ NAME ]; } - override string toString() + override string toString() @safe pure nothrow @nogc { return NAME; } - override bool canEncode(dchar c) + override bool canEncode(dchar c) @safe pure nothrow @nogc { return std.encoding.canEncode!(dchar)(c); } - override size_t encodedLength(dchar c) + override size_t encodedLength(dchar c) @safe pure nothrow @nogc { - return std.encoding.encodedLength!(dchar)(c); + return std.encoding.encodedLength!(dchar)(c); } - override size_t encode(dchar c, ubyte[] buffer) + override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc { - auto r = cast(dchar[])buffer; + auto r = cast(dchar[]) buffer; return dchar.sizeof * std.encoding.encode(c,r); } - override dchar decode(ref const(ubyte)[] s) + override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc in { assert((s.length & 3) == 0); @@ -2855,7 +3331,7 @@ class EncodingSchemeUtf32Native : EncodingScheme return c; } - override dchar safeDecode(ref const(ubyte)[] s) + override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc in { assert((s.length & 3) == 0); @@ -2868,13 +3344,13 @@ class EncodingSchemeUtf32Native : EncodingScheme return c; } - override @property immutable(ubyte)[] replacementSequence() + override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc { return cast(immutable(ubyte)[])"\uFFFD"d; } } } -unittest +@system unittest { version(LittleEndian) { @@ -2892,6 +3368,20 @@ unittest assert(ub.length == 8); } + +// shared static this() called from encodinginit to break ctor cycle +extern(C) void std_encoding_shared_static_this() +{ + EncodingScheme.register!EncodingSchemeASCII; + EncodingScheme.register!EncodingSchemeLatin1; + EncodingScheme.register!EncodingSchemeLatin2; + EncodingScheme.register!EncodingSchemeWindows1250; + EncodingScheme.register!EncodingSchemeWindows1252; + EncodingScheme.register!EncodingSchemeUtf8; + EncodingScheme.register!EncodingSchemeUtf16Native; + EncodingScheme.register!EncodingSchemeUtf32Native; +} + //============================================================================= @@ -2900,19 +3390,19 @@ version(unittest) { void transcodeReverse(Src,Dst)(immutable(Src)[] s, out immutable(Dst)[] r) { - static if(is(Src==Dst)) + static if (is(Src == Dst)) { return s; } - else static if(is(Src==AsciiChar)) + else static if (is(Src == AsciiChar)) { - transcodeReverse!(char,Dst)(cast(string)s,r); + transcodeReverse!(char,Dst)(cast(string) s,r); } else { - foreach_reverse(d;codePoints(s)) + foreach_reverse (d;codePoints(s)) { - foreach_reverse(c;codeUnits!(Dst)(d)) + foreach_reverse (c;codeUnits!(Dst)(d)) { r = c ~ r; } @@ -2923,7 +3413,7 @@ version(unittest) string makeReadable(string s) { string r = "\""; - foreach(char c;s) + foreach (char c;s) { if (c >= 0x20 && c < 0x80) { @@ -2943,7 +3433,7 @@ version(unittest) string makeReadable(wstring s) { string r = "\""; - foreach(wchar c;s) + foreach (wchar c;s) { if (c >= 0x20 && c < 0x80) { @@ -2965,7 +3455,7 @@ version(unittest) string makeReadable(dstring s) { string r = "\""; - foreach(dchar c; s) + foreach (dchar c; s) { if (c >= 0x20 && c < 0x80) { @@ -2999,3 +3489,173 @@ version(unittest) return "0123456789ABCDEF"[n & 0xF]; } } + +/** Definitions of common Byte Order Marks. +The elements of the $(D enum) can used as indices into $(D bomTable) to get +matching $(D BOMSeq). +*/ +enum BOM +{ + none = 0, /// no BOM was found + utf32be = 1, /// [0x00, 0x00, 0xFE, 0xFF] + utf32le = 2, /// [0xFF, 0xFE, 0x00, 0x00] + utf7 = 3, /* [0x2B, 0x2F, 0x76, 0x38] + [0x2B, 0x2F, 0x76, 0x39], + [0x2B, 0x2F, 0x76, 0x2B], + [0x2B, 0x2F, 0x76, 0x2F], + [0x2B, 0x2F, 0x76, 0x38, 0x2D] + */ + utf1 = 8, /// [0xF7, 0x64, 0x4C] + utfebcdic = 9, /// [0xDD, 0x73, 0x66, 0x73] + scsu = 10, /// [0x0E, 0xFE, 0xFF] + bocu1 = 11, /// [0xFB, 0xEE, 0x28] + gb18030 = 12, /// [0x84, 0x31, 0x95, 0x33] + utf8 = 13, /// [0xEF, 0xBB, 0xBF] + utf16be = 14, /// [0xFE, 0xFF] + utf16le = 15 /// [0xFF, 0xFE] +} + +/// The type stored inside $(D bomTable). +alias BOMSeq = Tuple!(BOM, "schema", ubyte[], "sequence"); + +/** Mapping of a byte sequence to $(B Byte Order Mark (BOM)) +*/ +immutable bomTable = [ + BOMSeq(BOM.none, null), + BOMSeq(BOM.utf32be, cast(ubyte[])([0x00, 0x00, 0xFE, 0xFF])), + BOMSeq(BOM.utf32le, cast(ubyte[])([0xFF, 0xFE, 0x00, 0x00])), + BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x39])), + BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x2B])), + BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x2F])), + BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x38, 0x2D])), + BOMSeq(BOM.utf7, cast(ubyte[])([0x2B, 0x2F, 0x76, 0x38])), + BOMSeq(BOM.utf1, cast(ubyte[])([0xF7, 0x64, 0x4C])), + BOMSeq(BOM.utfebcdic, cast(ubyte[])([0xDD, 0x73, 0x66, 0x73])), + BOMSeq(BOM.scsu, cast(ubyte[])([0x0E, 0xFE, 0xFF])), + BOMSeq(BOM.bocu1, cast(ubyte[])([0xFB, 0xEE, 0x28])), + BOMSeq(BOM.gb18030, cast(ubyte[])([0x84, 0x31, 0x95, 0x33])), + BOMSeq(BOM.utf8, cast(ubyte[])([0xEF, 0xBB, 0xBF])), + BOMSeq(BOM.utf16be, cast(ubyte[])([0xFE, 0xFF])), + BOMSeq(BOM.utf16le, cast(ubyte[])([0xFF, 0xFE])) +]; + +/** Returns a $(D BOMSeq) for a given $(D input). +If no $(D BOM) is present the $(D BOMSeq) for $(D BOM.none) is +returned. The $(D BOM) sequence at the beginning of the range will +not be comsumed from the passed range. If you pass a reference type +range make sure that $(D save) creates a deep copy. + +Params: + input = The sequence to check for the $(D BOM) + +Returns: + the found $(D BOMSeq) corresponding to the passed $(D input). +*/ +immutable(BOMSeq) getBOM(Range)(Range input) +if (isForwardRange!Range && is(Unqual!(ElementType!Range) == ubyte)) +{ + import std.algorithm.searching : startsWith; + foreach (it; bomTable[1 .. $]) + { + if (startsWith(input.save, it.sequence)) + { + return it; + } + } + + return bomTable[0]; +} + +/// +@system unittest +{ + import std.format : format; + + auto ts = dchar(0x0000FEFF) ~ "Hello World"d; + + auto entry = getBOM(cast(ubyte[]) ts); + version(BigEndian) + { + assert(entry.schema == BOM.utf32be, format("%s", entry.schema)); + } + else + { + assert(entry.schema == BOM.utf32le, format("%s", entry.schema)); + } +} + +@system unittest +{ + import std.format : format; + + foreach (idx, it; bomTable) + { + auto s = it[1] ~ cast(ubyte[])"hello world"; + auto i = getBOM(s); + assert(i[0] == bomTable[idx][0]); + + if (idx < 4 || idx > 7) // get around the multiple utf7 bom's + { + assert(i[0] == BOM.init + idx); + assert(i[1] == it[1]); + } + } +} + +@safe pure unittest +{ + struct BOMInputRange + { + ubyte[] arr; + + @property ubyte front() + { + return this.arr.front; + } + + @property bool empty() + { + return this.arr.empty; + } + + void popFront() + { + this.arr = this.arr[1 .. $]; + } + + @property typeof(this) save() + { + return this; + } + } + + static assert( isInputRange!BOMInputRange); + static assert(!isArray!BOMInputRange); + + ubyte[] dummyEnd = [0,0,0,0]; + + foreach (idx, it; bomTable[1 .. $]) + { + { + auto ir = BOMInputRange(it.sequence.dup); + + auto b = getBOM(ir); + assert(b.schema == it.schema); + assert(ir.arr == it.sequence); + } + + { + auto noBom = it.sequence[0 .. 1].dup ~ dummyEnd; + size_t oldLen = noBom.length; + assert(oldLen - 4 < it.sequence.length); + + auto ir = BOMInputRange(noBom.dup); + auto b = getBOM(ir); + assert(b.schema == BOM.none); + assert(noBom.length == oldLen); + } + } +} + +/** Constant defining a fully decoded BOM */ +enum dchar utfBOM = 0xfeff; diff --git a/std/exception.d b/std/exception.d index 7e9995e54bf..44854b87599 100644 --- a/std/exception.d +++ b/std/exception.d @@ -4,6 +4,36 @@ This module defines functions related to exceptions and general error handling. It also defines functions intended to aid in unit testing. +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Assumptions) $(TD + $(LREF assertNotThrown) + $(LREF assertThrown) + $(LREF assumeUnique) + $(LREF assumeWontThrow) + $(LREF mayPointTo) +)) +$(TR $(TD Enforce) $(TD + $(LREF doesPointTo) + $(LREF enforce) + $(LREF enforceEx) + $(LREF errnoEnforce) +)) +$(TR $(TD Handlers) $(TD + $(LREF collectException) + $(LREF collectExceptionMsg) + $(LREF ifThrown) + $(LREF handle) +)) +$(TR $(TD Other) $(TD + $(LREF basicExceptionCtors) + $(LREF emptyExceptionMsg) + $(LREF ErrnoException) + $(LREF RangePrimitive) +)) +) + Synopsis of some of std.exception's functions: -------------------- string synopsis() @@ -33,23 +63,17 @@ } -------------------- - Macros: - WIKI = Phobos/StdException - Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-. - License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) - Authors: $(WEB erdani.org, Andrei Alexandrescu) and Jonathan M Davis + License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) + Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis Source: $(PHOBOSSRC std/_exception.d) +/ module std.exception; -import std.range; +import std.range.primitives; import std.traits; -import core.stdc.errno; -import core.stdc.string; - /++ Asserts that the given expression does $(I not) throw the given type of $(D Throwable). If a $(D Throwable) of the given type is thrown, @@ -70,8 +94,11 @@ import core.stdc.string; Throws: $(D AssertError) if the given $(D Throwable) is thrown. + + Returns: + the result of `expression`. +/ -void assertNotThrown(T : Throwable = Exception, E) +auto assertNotThrown(T : Throwable = Exception, E) (lazy E expression, string msg = null, string file = __FILE__, @@ -80,7 +107,7 @@ void assertNotThrown(T : Throwable = Exception, E) import core.exception : AssertError; try { - expression(); + return expression(); } catch (T t) { @@ -90,7 +117,7 @@ void assertNotThrown(T : Throwable = Exception, E) } } /// -unittest +@system unittest { import core.exception : AssertError; @@ -104,7 +131,7 @@ unittest enforce!StringException(false, "Error!"))) == `assertNotThrown failed: StringException was thrown: Error!`); } -unittest +@system unittest { import core.exception : AssertError; import std.string; @@ -121,34 +148,34 @@ unittest `assertNotThrown failed: StringException was thrown.`); } -unittest +@system unittest { import core.exception : AssertError; void throwEx(Throwable t) { throw t; } - void nothrowEx() { } + bool nothrowEx() { return true; } try { - assertNotThrown!Exception(nothrowEx()); + assert(assertNotThrown!Exception(nothrowEx())); } catch (AssertError) assert(0); try { - assertNotThrown!Exception(nothrowEx(), "It's a message"); + assert(assertNotThrown!Exception(nothrowEx(), "It's a message")); } catch (AssertError) assert(0); try { - assertNotThrown!AssertError(nothrowEx()); + assert(assertNotThrown!AssertError(nothrowEx())); } catch (AssertError) assert(0); try { - assertNotThrown!AssertError(nothrowEx(), "It's a message"); + assert(assertNotThrown!AssertError(nothrowEx(), "It's a message")); } catch (AssertError) assert(0); @@ -233,7 +260,7 @@ void assertThrown(T : Throwable = Exception, E) file, line); } /// -unittest +@system unittest { import core.exception : AssertError; import std.string; @@ -248,7 +275,7 @@ unittest `assertThrown failed: No StringException was thrown.`); } -unittest +@system unittest { import core.exception : AssertError; @@ -266,7 +293,7 @@ unittest assertThrown!Exception(throwEx(new Exception("It's an Exception")), "It's a message"); } - catch(AssertError) assert(0); + catch (AssertError) assert(0); try { @@ -288,7 +315,7 @@ unittest bool thrown = false; try assertThrown!Exception(nothrowEx()); - catch(AssertError) + catch (AssertError) thrown = true; assert(thrown); @@ -298,7 +325,7 @@ unittest bool thrown = false; try assertThrown!Exception(nothrowEx(), "It's a message"); - catch(AssertError) + catch (AssertError) thrown = true; assert(thrown); @@ -308,7 +335,7 @@ unittest bool thrown = false; try assertThrown!AssertError(nothrowEx()); - catch(AssertError) + catch (AssertError) thrown = true; assert(thrown); @@ -318,7 +345,7 @@ unittest bool thrown = false; try assertThrown!AssertError(nothrowEx(), "It's a message"); - catch(AssertError) + catch (AssertError) thrown = true; assert(thrown); @@ -327,7 +354,16 @@ unittest /++ - If $(D !!value) is true, $(D value) is returned. Otherwise, + Enforces that the given value is true. + + Params: + value = The value to test. + E = Exception type to throw if the value evalues to false. + msg = The error message to put in the exception if it is thrown. + file = The source file of the caller. + line = The line number of the caller. + + Returns: $(D value), if `cast(bool) value` is true. Otherwise, $(D new Exception(msg)) is thrown. Note: @@ -345,38 +381,33 @@ unittest enforce(line.length, "Expected a non-empty line."); -------------------- +/ -T enforce(E : Throwable = Exception, T)(T value, lazy const(char)[] msg = null, string file = __FILE__, size_t line = __LINE__) - if (is(typeof({ if (!value) {} }))) +T enforce(E : Throwable = Exception, T)(T value, lazy const(char)[] msg = null, +string file = __FILE__, size_t line = __LINE__) +if (is(typeof({ if (!value) {} }))) { if (!value) bailOut!E(file, line, msg); return value; } /++ - $(RED Deprecated. If passing the file or line number explicitly, please use - the overload of enforce which takes them as function arguments. Taking - them as template arguments causes unnecessary template bloat. This - overload will be removed in June 2015.) - +/ -deprecated("Use the overload of enforce that takes file and line as function arguments.") -T enforce(T, string file, size_t line = __LINE__) - (T value, lazy const(char)[] msg = null) - if (is(typeof({ if (!value) {} }))) -{ - if (!value) bailOut(file, line, msg); - return value; -} + Enforces that the given value is true. -/++ - If $(D !!value) is true, $(D value) is returned. Otherwise, the given + Params: + value = The value to test. + dg = The delegate to be called if the value evaluates to false. + file = The source file of the caller. + line = The line number of the caller. + + Returns: $(D value), if `cast(bool) value` is true. Otherwise, the given delegate is called. - The whole safety and purity are inferred from $(D Dg)'s safety and purity. + The safety and purity of this function are inferred from $(D Dg)'s safety + and purity. +/ T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__) (T value, scope Dg dg) - if (isSomeFunction!Dg && is(typeof( dg() )) && - is(typeof({ if (!value) {} }))) +if (isSomeFunction!Dg && is(typeof( dg() )) && + is(typeof({ if (!value) {} }))) { if (!value) dg(); return value; @@ -386,7 +417,7 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha { static if (is(typeof(new E(string.init, string.init, size_t.init)))) { - throw new E(msg.ptr ? msg.idup : "Enforcement failed", file, line); + throw new E(msg ? msg.idup : "Enforcement failed", file, line); } else static if (is(typeof(new E(string.init, size_t.init)))) { @@ -394,29 +425,29 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha } else { - static assert("Expected this(string, string, size_t) or this(string, size_t)" ~ + static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~ " constructor for " ~ __traits(identifier, E)); } } -unittest +@safe unittest { - assert (enforce(123) == 123); + assert(enforce(123) == 123); try { enforce(false, "error"); - assert (false); + assert(false); } catch (Exception e) { - assert (e.msg == "error"); - assert (e.file == __FILE__); - assert (e.line == __LINE__-7); + assert(e.msg == "error"); + assert(e.file == __FILE__); + assert(e.line == __LINE__-7); } } -unittest +@safe unittest { // Issue 10510 extern(C) void cFoo() { } @@ -424,15 +455,15 @@ unittest } // purity and safety inference test -unittest +@system unittest { - import std.typetuple; + import std.meta : AliasSeq; - foreach (EncloseSafe; TypeTuple!(false, true)) - foreach (EnclosePure; TypeTuple!(false, true)) + foreach (EncloseSafe; AliasSeq!(false, true)) + foreach (EnclosePure; AliasSeq!(false, true)) { - foreach (BodySafe; TypeTuple!(false, true)) - foreach (BodyPure; TypeTuple!(false, true)) + foreach (BodySafe; AliasSeq!(false, true)) + foreach (BodyPure; AliasSeq!(false, true)) { enum code = "delegate void() " ~ @@ -459,14 +490,14 @@ unittest } // Test for bugzilla 8637 -unittest +@system unittest { struct S { static int g; ~this() {} // impure & unsafe destructor bool opCast(T:bool)() { - int* p = cast(int*)0; // unsafe operation + int* p = cast(int*) 0; // unsafe operation int n = g; // impure operation return true; } @@ -492,25 +523,26 @@ unittest enforce!E2(s); } -deprecated unittest +@safe unittest { - struct S + // Issue 14685 + + class E : Exception { - static int g; - ~this() {} // impure & unsafe destructor - bool opCast(T:bool)() { - int* p = cast(int*)0; // unsafe operation - int n = g; // impure operation - return true; - } + this() { super("Not found"); } } - S s; - - enforce!(S, __FILE__, __LINE__)(s, ""); + static assert(!__traits(compiles, { enforce!E(false); })); } /++ - If $(D !!value) is true, $(D value) is returned. Otherwise, $(D ex) is thrown. + Enforces that the given value is true. + + Params: + value = The value to test. + ex = The exception to throw if the value evaluates to false. + + Returns: $(D value), if `cast(bool) value` is true. Otherwise, $(D ex) is + thrown. Example: -------------------- @@ -525,16 +557,24 @@ T enforce(T)(T value, lazy Throwable ex) return value; } -unittest +@safe unittest { assertNotThrown(enforce(true, new Exception("this should not be thrown"))); assertThrown(enforce(false, new Exception("this should be thrown"))); } /++ - If $(D !!value) is true, $(D value) is returned. Otherwise, - $(D new ErrnoException(msg)) is thrown. $(D ErrnoException) assumes that the - last operation set $(D errno) to an error code. + Enforces that the given value is true, throwing an `ErrnoException` if it + is not. + + Params: + value = The value to test. + msg = The message to include in the `ErrnoException` if it is thrown. + + Returns: $(D value), if `cast(bool) value` is true. Otherwise, + $(D new ErrnoException(msg)) is thrown. It is assumed that the last + operation set $(D errno) to an error code corresponding with the failed + condition. Example: -------------------- @@ -552,7 +592,7 @@ T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__) /++ - If $(D !!value) is $(D true), $(D value) is returned. Otherwise, + If $(D !value) is $(D false), $(D value) is returned. Otherwise, $(D new E(msg, file, line)) is thrown. Or if $(D E) doesn't take a message and can be constructed with $(D new E(file, line)), then $(D new E(file, line)) will be thrown. @@ -567,7 +607,7 @@ T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__) -------------------- +/ template enforceEx(E : Throwable) - if (is(typeof(new E("", __FILE__, __LINE__)))) +if (is(typeof(new E("", __FILE__, __LINE__)))) { /++ Ditto +/ T enforceEx(T)(T value, lazy string msg = "", string file = __FILE__, size_t line = __LINE__) @@ -579,7 +619,7 @@ template enforceEx(E : Throwable) /++ Ditto +/ template enforceEx(E : Throwable) - if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LINE__)))) +if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LINE__)))) { /++ Ditto +/ T enforceEx(T)(T value, string file = __FILE__, size_t line = __LINE__) @@ -589,7 +629,7 @@ template enforceEx(E : Throwable) } } -unittest +@system unittest { import std.array : empty; import core.exception : OutOfMemoryError; @@ -632,7 +672,7 @@ unittest static assert(!is(typeof(enforceEx!int(true)))); } -unittest +@safe unittest { alias enf = enforceEx!Exception; assertNotThrown(enf(true)); @@ -670,7 +710,7 @@ T collectException(T = Exception, E)(lazy E expression, ref E result) return null; } /// -unittest +@system unittest { int b; int foo() { throw new Exception("blah"); } @@ -710,7 +750,7 @@ T collectException(T : Throwable = Exception, E)(lazy E expression) return null; } -unittest +@safe unittest { int foo() { throw new Exception("blah"); } assert(collectException(foo())); @@ -742,13 +782,13 @@ string collectExceptionMsg(T = Exception, E)(lazy E expression) { expression(); - return cast(string)null; + return cast(string) null; } - catch(T e) + catch (T e) return e.msg.empty ? emptyExceptionMsg : e.msg; } /// -unittest +@safe unittest { void throwFunc() { throw new Exception("My Message."); } assert(collectExceptionMsg(throwFunc()) == "My Message."); @@ -782,6 +822,11 @@ enum emptyExceptionMsg = ""; * Typically, $(D assumeUnique) is used to return arrays from * functions that have allocated and built them. * + * Params: + * array = The array to cast to immutable. + * + * Returns: The immutable array. + * * Example: * * ---- @@ -847,7 +892,7 @@ enum emptyExceptionMsg = ""; * * For more on infering uniqueness see the $(B unique) and * $(B lent) keywords in the - * $(WEB archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava) + * $(HTTP archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava) * language. * * The downside of using $(D assumeUnique)'s @@ -868,14 +913,7 @@ immutable(T)[] assumeUnique(T)(ref T[] array) pure nothrow array = null; return result; } - -unittest -{ - int[] arr = new int[1]; - auto arr1 = assumeUnique(arr); - assert(is(typeof(arr1) == immutable(int)[]) && arr == null); -} - +/// ditto immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow { auto result = cast(immutable(T[U])) array; @@ -883,8 +921,16 @@ immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow return result; } +@system unittest +{ + // @system due to assumeUnique + int[] arr = new int[1]; + auto arr1 = assumeUnique(arr); + assert(is(typeof(arr1) == immutable(int)[]) && arr == null); +} + // @@@BUG@@@ -version(none) unittest +version(none) @system unittest { int[string] arr = ["a":1]; auto arr1 = assumeUnique(arr); @@ -905,6 +951,16 @@ version(none) unittest * subclass $(D Exception) may be thrown even from $(D nothrow) functions, * since they are considered to be serious runtime problems that cannot be * recovered from.) + * + * Params: + * expr = The expression asserted not to throw. + * msg = The message to include in the `AssertError` if the assumption turns + * out to be false. + * file = The source file name of the caller. + * line = The line number of the caller. + * + * Returns: + * The value of `expr`, if any. */ T assumeWontThrow(T)(lazy T expr, string msg = null, @@ -916,7 +972,7 @@ T assumeWontThrow(T)(lazy T expr, { return expr; } - catch(Exception e) + catch (Exception e) { import std.range.primitives : empty; immutable tail = msg.empty ? "." : ": " ~ msg; @@ -926,7 +982,7 @@ T assumeWontThrow(T)(lazy T expr, } /// -unittest +@safe unittest { import std.math : sqrt; @@ -935,7 +991,7 @@ unittest { if (x < 0) throw new Exception("Tried to take root of negative number"); - return cast(int)sqrt(cast(double)x); + return cast(int) sqrt(cast(double) x); } // This function never throws. @@ -951,7 +1007,7 @@ unittest assert(computeLength(3, 4) == 5); } -unittest +@system unittest { import core.exception : AssertError; @@ -967,16 +1023,21 @@ unittest } /** -The "pointsTo" functions, $(D doesPointTo) and $(D mayPointTo). +Checks whether a given source object contains pointers or references to a given +target object. + +Params: + source = The source object + target = The target object -Returns $(D true) if $(D source)'s representation embeds a pointer +Returns: $(D true) if $(D source)'s representation embeds a pointer that points to $(D target)'s representation or somewhere inside it. If $(D source) is or contains a dynamic array, then, then these functions will check if there is overlap between the dynamic array and $(D target)'s representation. -If $(D source) is a class, then pointsTo will handle it as a pointer. +If $(D source) is a class, then it will be handled as a pointer. If $(D target) is a pointer, a dynamic array or a class, then these functions will only check if $(D source) points to $(D target), $(I not) what $(D target) references. @@ -994,14 +1055,14 @@ $(D source) does not point to $(D target). It may produce false positives, but n false negatives. This function should be prefered for defensively choosing a code path. -Note: Evaluating $(D pointsTo(x, x)) checks whether $(D x) has +Note: Evaluating $(D doesPointTo(x, x)) checks whether $(D x) has internal pointers. This should only be done as an assertive test, as the language is free to assume objects don't have internal pointers (TDPL 7.1.3.5). */ bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow - if (__traits(isRef, source) || isDynamicArray!S || - isPointer!S || is(S == class)) +if (__traits(isRef, source) || isDynamicArray!S || + isPointer!S || is(S == class)) { static if (isPointer!S || is(S == class) || is(S == interface)) { @@ -1026,7 +1087,7 @@ bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) else static if (isDynamicArray!S) { import std.array : overlap; - return overlap(cast(void[])source, cast(void[])(&target)[0 .. 1]).length != 0; + return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0; } else { @@ -1034,9 +1095,17 @@ bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) } } +// for shared objects +/// ditto +bool doesPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow +{ + return doesPointTo!(shared S, shared T, void)(source, target); +} + +/// ditto bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow - if (__traits(isRef, source) || isDynamicArray!S || - isPointer!S || is(S == class)) +if (__traits(isRef, source) || isDynamicArray!S || + isPointer!S || is(S == class)) { static if (isPointer!S || is(S == class) || is(S == interface)) { @@ -1060,7 +1129,7 @@ bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) else static if (isDynamicArray!S) { import std.array : overlap; - return overlap(cast(void[])source, cast(void[])(&target)[0 .. 1]).length != 0; + return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0; } else { @@ -1069,74 +1138,14 @@ bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) } // for shared objects -bool doesPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow -{ - return doesPointTo!(shared S, shared T, void)(source, target); -} +/// ditto bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow { return mayPointTo!(shared S, shared T, void)(source, target); } -deprecated ("pointsTo is ambiguous. Please use either of doesPointTo or mayPointTo") -alias pointsTo = doesPointTo; - -/+ -Returns true if the field at index $(D i) in ($D T) shares its address with another field. - -Note: This does not merelly check if the field is a member of an union, but also that -it is not a single child. -+/ -package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof); -private bool isUnionAliasedImpl(T)(size_t offset) -{ - int count = 0; - foreach (i, U; typeof(T.tupleof)) - if (T.tupleof[i].offsetof == offset) - ++count; - return count >= 2; -} -// -unittest -{ - static struct S - { - int a0; //Not aliased - union - { - int a1; //Not aliased - } - union - { - int a2; //Aliased - int a3; //Aliased - } - union A4 - { - int b0; //Not aliased - }; - A4 a4; - union A5 - { - int b0; //Aliased - int b1; //Aliased - }; - A5 a5; - } - - static assert(!isUnionAliased!(S, 0)); //a0; - static assert(!isUnionAliased!(S, 1)); //a1; - static assert( isUnionAliased!(S, 2)); //a2; - static assert( isUnionAliased!(S, 3)); //a3; - static assert(!isUnionAliased!(S, 4)); //a4; - static assert(!isUnionAliased!(S.A4, 0)); //a4.b0; - static assert(!isUnionAliased!(S, 5)); //a5; - static assert( isUnionAliased!(S.A5, 0)); //a5.b0; - static assert( isUnionAliased!(S.A5, 1)); //a5.b1; -} - /// Pointers -unittest +@system unittest { int i = 0; int* p = null; @@ -1146,7 +1155,7 @@ unittest } /// Structs and Unions -unittest +@system unittest { struct S { @@ -1156,18 +1165,18 @@ unittest int i; auto s = S(0, &i); - //structs and unions "own" their members - //pointsTo will answer true if one of the members pointsTo. + // structs and unions "own" their members + // pointsTo will answer true if one of the members pointsTo. assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed. assert( s.p.doesPointTo(i)); //i is pointed by s.p. assert( s .doesPointTo(i)); //which means i is pointed by s itself. - //Unions will behave exactly the same. Points to will check each "member" - //individually, even if they share the same memory + // Unions will behave exactly the same. Points to will check each "member" + // individually, even if they share the same memory } /// Arrays (dynamic and static) -unittest +@system unittest { int i; int[] slice = [0, 1, 2, 3, 4]; @@ -1175,20 +1184,23 @@ unittest int*[] slicep = [&i]; int*[1] arrp = [&i]; - //A slice points to all of its members: + // A slice points to all of its members: assert( slice.doesPointTo(slice[3])); - assert(!slice[0 .. 2].doesPointTo(slice[3])); //Object 3 is outside of the slice [0 .. 2] + assert(!slice[0 .. 2].doesPointTo(slice[3])); // Object 3 is outside of the + // slice [0 .. 2] - //Note that a slice will not take into account what its members point to. + // Note that a slice will not take into account what its members point to. assert( slicep[0].doesPointTo(i)); assert(!slicep .doesPointTo(i)); - //static arrays are objects that own their members, just like structs: - assert(!arr.doesPointTo(arr[0])); //arr[0] is just a member of arr, so not pointed. - assert( arrp[0].doesPointTo(i)); //i is pointed by arrp[0]. - assert( arrp .doesPointTo(i)); //which means i is pointed by arrp itslef. + // static arrays are objects that own their members, just like structs: + assert(!arr.doesPointTo(arr[0])); // arr[0] is just a member of arr, so not + // pointed. + assert( arrp[0].doesPointTo(i)); // i is pointed by arrp[0]. + assert( arrp .doesPointTo(i)); // which means i is pointed by arrp + // itself. - //Notice the difference between static and dynamic arrays: + // Notice the difference between static and dynamic arrays: assert(!arr .doesPointTo(arr[0])); assert( arr[].doesPointTo(arr[0])); assert( arrp .doesPointTo(i)); @@ -1196,7 +1208,7 @@ unittest } /// Classes -unittest +@system unittest { class C { @@ -1206,26 +1218,30 @@ unittest int i; C a = new C(&i); C b = a; - //Classes are a bit particular, as they are treated like simple pointers - //to a class payload. - assert( a.p.doesPointTo(i)); //a.p points to i. - assert(!a .doesPointTo(i)); //Yet a itself does not point i. + + // Classes are a bit particular, as they are treated like simple pointers + // to a class payload. + assert( a.p.doesPointTo(i)); // a.p points to i. + assert(!a .doesPointTo(i)); // Yet a itself does not point i. //To check the class payload itself, iterate on its members: () { - foreach (index, _; FieldTypeTuple!C) + import std.traits : Fields; + + foreach (index, _; Fields!C) if (doesPointTo(a.tupleof[index], i)) return; assert(0); }(); - //To check if a class points a specific payload, a direct memmory check can be done: + // To check if a class points a specific payload, a direct memmory check + // can be done: auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a; - assert(b.doesPointTo(*aLoc)); //b points to where a is pointing + assert(b.doesPointTo(*aLoc)); // b points to where a is pointing } -unittest +@system unittest { struct S1 { int a; S1 * b; } S1 a1; @@ -1255,7 +1271,7 @@ unittest static struct NoCopy { this(this) { assert(0); } } static struct Holder { NoCopy a, b, c; } Holder h; - cast(void)doesPointTo(h, h); + cast(void) doesPointTo(h, h); } shared S3 sh3; @@ -1271,11 +1287,11 @@ unittest //But they do point their elements foreach (i; 0 .. 4) assert(doesPointTo(darr, darr[i])); - assert(doesPointTo(darr[0..3], darr[2])); - assert(!doesPointTo(darr[0..3], darr[3])); + assert(doesPointTo(darr[0 .. 3], darr[2])); + assert(!doesPointTo(darr[0 .. 3], darr[3])); } -unittest +@system unittest { //tests with static arrays //Static arrays themselves are just objects, and don't really *point* to anything. @@ -1324,7 +1340,7 @@ unittest } -unittest //Unions +@system unittest //Unions { int i; union U //Named union @@ -1363,7 +1379,7 @@ unittest //Unions assert( mayPointTo(s, i)); } -unittest //Classes +@system unittest //Classes { int i; static class A @@ -1375,7 +1391,7 @@ unittest //Classes a.p = &i; assert(!doesPointTo(a, i)); //a does not point to i } -unittest //alias this test +@safe unittest //alias this test { static int i; static int j; @@ -1389,10 +1405,10 @@ unittest //alias this test S s = S(&j); assert(!doesPointTo(s, i)); assert( doesPointTo(s, j)); - assert( doesPointTo(cast(int*)s, i)); - assert(!doesPointTo(cast(int*)s, j)); + assert( doesPointTo(cast(int*) s, i)); + assert(!doesPointTo(cast(int*) s, j)); } -unittest //more alias this opCast +@safe unittest //more alias this opCast { void* p; class A @@ -1408,6 +1424,60 @@ unittest //more alias this opCast assert(!mayPointTo(A.init, p)); } +/+ +Returns true if the field at index $(D i) in ($D T) shares its address with another field. + +Note: This does not merelly check if the field is a member of an union, but also that +it is not a single child. ++/ +package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof); +private bool isUnionAliasedImpl(T)(size_t offset) +{ + int count = 0; + foreach (i, U; typeof(T.tupleof)) + if (T.tupleof[i].offsetof == offset) + ++count; + return count >= 2; +} +// +@safe unittest +{ + static struct S + { + int a0; //Not aliased + union + { + int a1; //Not aliased + } + union + { + int a2; //Aliased + int a3; //Aliased + } + union A4 + { + int b0; //Not aliased + } + A4 a4; + union A5 + { + int b0; //Aliased + int b1; //Aliased + } + A5 a5; + } + + static assert(!isUnionAliased!(S, 0)); //a0; + static assert(!isUnionAliased!(S, 1)); //a1; + static assert( isUnionAliased!(S, 2)); //a2; + static assert( isUnionAliased!(S, 3)); //a3; + static assert(!isUnionAliased!(S, 4)); //a4; + static assert(!isUnionAliased!(S.A4, 0)); //a4.b0; + static assert(!isUnionAliased!(S, 5)); //a5; + static assert( isUnionAliased!(S.A5, 0)); //a5.b0; + static assert( isUnionAliased!(S.A5, 1)); //a5.b1; +} + /********************* * Thrown if errors that set $(D errno) occur. */ @@ -1417,17 +1487,45 @@ class ErrnoException : Exception private uint _errno; this(string msg, string file = null, size_t line = 0) @trusted { - _errno = .errno; - version (linux) + import core.stdc.errno : errno; + this(msg, errno, file, line); + } + this(string msg, int errno, string file = null, size_t line = 0) @trusted + { + import core.stdc.string : strlen; + + _errno = errno; + version (CRuntime_Glibc) { + import core.stdc.string : strerror_r; char[1024] buf = void; - auto s = core.stdc.string.strerror_r(errno, buf.ptr, buf.length); + auto s = strerror_r(errno, buf.ptr, buf.length); } else { - auto s = core.stdc.string.strerror(errno); + import core.stdc.string : strerror; + auto s = strerror(errno); } - super(msg ~ " (" ~ s[0..s.strlen].idup ~ ")", file, line); + super(msg ~ " (" ~ s[0 .. s.strlen].idup ~ ")", file, line); + } + + @system unittest + { + import core.stdc.errno : errno, EAGAIN; + + auto old = errno; + scope(exit) errno = old; + + errno = EAGAIN; + auto ex = new ErrnoException("oh no"); + assert(ex.errno == EAGAIN); + } + + @system unittest + { + import core.stdc.errno : EAGAIN; + auto ex = new ErrnoException("oh no", EAGAIN); + assert(ex.errno == EAGAIN); } } @@ -1444,7 +1542,11 @@ class ErrnoException : Exception expression = The expression to run and return its result. errorHandler = The handler to run if the expression throwed. - Examples: + Returns: + expression, if it does not throw. Otherwise, returns the result of + errorHandler. + + Example: -------------------- //Revert to a default value upon an error: assert("x".to!int().ifThrown(0) == 0); @@ -1458,8 +1560,8 @@ class ErrnoException : Exception //Chaining multiple calls to ifThrown to attempt multiple things in a row: string s="true"; assert(s.to!int(). - ifThrown(cast(int)s.to!double()). - ifThrown(cast(int)s.to!bool()) + ifThrown(cast(int) s.to!double()). + ifThrown(cast(int) s.to!bool()) == 1); //Respond differently to different types of errors @@ -1473,7 +1575,7 @@ class ErrnoException : Exception be implicitly casted to, and that type will be the type of the compound expression. - Examples: + Example: -------------------- //null and new Object have a common type(Object). static assert(is(typeof(null.ifThrown(new Object())) == Object)); @@ -1495,12 +1597,17 @@ class ErrnoException : Exception CommonType!(T1, T2) ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1 expression, lazy scope T2 errorHandler) { static assert(!is(typeof(return) == void), - "The error handler's return value(" ~ T2.stringof ~ ") does not have a common type with the expression(" ~ T1.stringof ~ ")."); + "The error handler's return value(" + ~ T2.stringof ~ + ") does not have a common type with the expression(" + ~ T1.stringof ~ + ")." + ); try { return expression(); } - catch(E) + catch (E) { return errorHandler(); } @@ -1511,12 +1618,17 @@ CommonType!(T1, T2) ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1 ex CommonType!(T1, T2) ifThrown(E : Throwable, T1, T2)(lazy scope T1 expression, scope T2 delegate(E) errorHandler) { static assert(!is(typeof(return) == void), - "The error handler's return value(" ~ T2.stringof ~ ") does not have a common type with the expression(" ~ T1.stringof ~ ")."); + "The error handler's return value(" + ~ T2.stringof ~ + ") does not have a common type with the expression(" + ~ T1.stringof ~ + ")." + ); try { return expression(); } - catch(E e) + catch (E e) { return errorHandler(e); } @@ -1527,19 +1639,24 @@ CommonType!(T1, T2) ifThrown(E : Throwable, T1, T2)(lazy scope T1 expression, sc CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate(Exception) errorHandler) { static assert(!is(typeof(return) == void), - "The error handler's return value(" ~ T2.stringof ~ ") does not have a common type with the expression(" ~ T1.stringof ~ ")."); + "The error handler's return value(" + ~ T2.stringof ~ + ") does not have a common type with the expression(" + ~ T1.stringof ~ + ")." + ); try { return expression(); } - catch(Exception e) + catch (Exception e) { return errorHandler(e); } } //Verify Examples -unittest +@system unittest { import std.string; import std.conv; @@ -1549,8 +1666,8 @@ unittest //Chaining multiple calls to ifThrown to attempt multiple things in a row: string s="true"; assert(s.to!int(). - ifThrown(cast(int)s.to!double()). - ifThrown(cast(int)s.to!bool()) + ifThrown(cast(int) s.to!double()). + ifThrown(cast(int) s.to!bool()) == 1); //Respond differently to different types of errors @@ -1571,7 +1688,7 @@ unittest assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException"); } -unittest +@system unittest { import std.string; import std.conv; @@ -1611,8 +1728,8 @@ unittest version(unittest) package @property void assertCTFEable(alias dg)() { - static assert({ cast(void)dg(); return true; }()); - cast(void)dg(); + static assert({ cast(void) dg(); return true; }()); + cast(void) dg(); } /** This $(D enum) is used to select the primitives of the range to handle by the @@ -1663,13 +1780,13 @@ Returns: A wrapper $(D struct) that preserves the range interface of $(D input). opSlice: Infinite ranges with slicing support must return an instance of -$(XREF range, Take) when sliced with a specific lower and upper -bound (see $(XREF range_primitives, hasSlicing)); $(D handle) deals with this -by $(D take)ing 0 from the return value of the handler function and returning -that when an exception is caught. +$(REF Take, std,range) when sliced with a specific lower and upper +bound (see $(REF hasSlicing, std,range,primitives)); $(D handle) deals with +this by $(D take)ing 0 from the return value of the handler function and +returning that when an exception is caught. */ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Range input) - if (isInputRange!Range) +if (isInputRange!Range) { static struct Handler { @@ -1685,7 +1802,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return typeof(this)(range.save); } - catch(E exception) + catch (E exception) { return typeof(this)(handler(exception, this.range)); } @@ -1709,7 +1826,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return this.range.empty; } - catch(E exception) + catch (E exception) { return handler(exception, this.range); } @@ -1727,7 +1844,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return this.range.front; } - catch(E exception) + catch (E exception) { return handler(exception, this.range); } @@ -1744,7 +1861,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { this.range.popFront(); } - catch(E exception) + catch (E exception) { handler(exception, this.range); } @@ -1763,7 +1880,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return this.range.back; } - catch(E exception) + catch (E exception) { return handler(exception, this.range); } @@ -1780,7 +1897,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { this.range.popBack(); } - catch(E exception) + catch (E exception) { handler(exception, this.range); } @@ -1800,7 +1917,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return this.range[index]; } - catch(E exception) + catch (E exception) { static if (__traits(compiles, handler(exception, this.range, index))) return handler(exception, this.range, index); @@ -1823,7 +1940,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return this.range.length; } - catch(E exception) + catch (E exception) { return handler(exception, this.range); } @@ -1845,7 +1962,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return typeof(this)(this.range[lower .. upper]); } - catch(E exception) + catch (E exception) { return typeof(this)(handler(exception, this.range)); } @@ -1856,6 +1973,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran } else static if (is(typeof(Range.init[size_t.init .. $]))) { + import std.range : Take, takeExactly; static struct DollarToken {} enum opDollar = DollarToken.init; @@ -1867,7 +1985,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return typeof(this)(this.range[lower .. $]); } - catch(E exception) + catch (E exception) { return typeof(this)(handler(exception, this.range)); } @@ -1884,7 +2002,7 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran { return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1); } - catch(E exception) + catch (E exception) { return takeExactly(typeof(this)(handler(exception, this.range)), 0); } @@ -1902,7 +2020,8 @@ auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Ran /// pure @safe unittest { - import std.algorithm : equal, map, splitter; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map, splitter; import std.conv : to, ConvException; auto s = "12,1337z32,54,2,7,9,1z,6,8"; @@ -1919,7 +2038,7 @@ pure @safe unittest /// pure @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : retro; import std.utf : UTFException; @@ -2046,6 +2165,7 @@ pure nothrow @safe unittest static struct Infinite { + import std.range : Take; pure @safe: enum bool empty = false; int front() { assert(false); } @@ -2071,3 +2191,109 @@ pure nothrow @safe unittest auto infSlice = infinite[0 .. $]; // this would throw otherwise } + + +/++ + Convenience mixin for trivially sub-classing exceptions + + Even trivially sub-classing an exception involves writing boilerplate code + for the constructor to: 1$(RPAREN) correctly pass in the source file and line number + the exception was thrown from; 2$(RPAREN) be usable with $(LREF enforce) which + expects exception constructors to take arguments in a fixed order. This + mixin provides that boilerplate code. + + Note however that you need to mark the $(B mixin) line with at least a + minimal (i.e. just $(B ///)) DDoc comment if you want the mixed-in + constructors to be documented in the newly created Exception subclass. + + $(RED Current limitation): Due to + $(LINK2 https://issues.dlang.org/show_bug.cgi?id=11500, bug #11500), + currently the constructors specified in this mixin cannot be overloaded with + any other custom constructors. Thus this mixin can currently only be used + when no such custom constructors need to be explicitly specified. + +/ +mixin template basicExceptionCtors() +{ + /++ + Params: + msg = The message for the exception. + file = The file where the exception occurred. + line = The line number where the exception occurred. + next = The previous exception in the chain of exceptions, if any. + +/ + this(string msg, string file = __FILE__, size_t line = __LINE__, + Throwable next = null) @nogc @safe pure nothrow + { + super(msg, file, line, next); + } + + /++ + Params: + msg = The message for the exception. + next = The previous exception in the chain of exceptions. + file = The file where the exception occurred. + line = The line number where the exception occurred. + +/ + this(string msg, Throwable next, string file = __FILE__, + size_t line = __LINE__) @nogc @safe pure nothrow + { + super(msg, file, line, next); + } +} + +/// +@safe unittest +{ + class MeaCulpa: Exception + { + /// + mixin basicExceptionCtors; + } + + try + throw new MeaCulpa("test"); + catch (MeaCulpa e) + { + assert(e.msg == "test"); + assert(e.file == __FILE__); + assert(e.line == __LINE__ - 5); + } +} + +@safe pure nothrow unittest +{ + class TestException : Exception { mixin basicExceptionCtors; } + auto e = new Exception("msg"); + auto te1 = new TestException("foo"); + auto te2 = new TestException("foo", e); +} + +@safe unittest +{ + class TestException : Exception { mixin basicExceptionCtors; } + auto e = new Exception("!!!"); + + auto te1 = new TestException("message", "file", 42, e); + assert(te1.msg == "message"); + assert(te1.file == "file"); + assert(te1.line == 42); + assert(te1.next is e); + + auto te2 = new TestException("message", e, "file", 42); + assert(te2.msg == "message"); + assert(te2.file == "file"); + assert(te2.line == 42); + assert(te2.next is e); + + auto te3 = new TestException("foo"); + assert(te3.msg == "foo"); + assert(te3.file == __FILE__); + assert(te3.line == __LINE__ - 3); + assert(te3.next is null); + + auto te4 = new TestException("foo", e); + assert(te4.msg == "foo"); + assert(te4.file == __FILE__); + assert(te4.line == __LINE__ - 3); + assert(te4.next is e); +} diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d new file mode 100644 index 00000000000..761840dc54a --- /dev/null +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -0,0 +1,441 @@ +/// +module std.experimental.allocator.building_blocks.affix_allocator; + +/** + +Allocator that adds some extra data before (of type $(D Prefix)) and/or after +(of type $(D Suffix)) any allocation made with its parent allocator. This is +useful for uses where additional allocation-related information is needed, such +as mutexes, reference counts, or walls for debugging memory corruption errors. + +If $(D Prefix) is not $(D void), $(D Allocator) must guarantee an alignment at +least as large as $(D Prefix.alignof). + +Suffixes are slower to get at because of alignment rounding, so prefixes should +be preferred. However, small prefixes blunt the alignment so if a large +alignment with a small affix is needed, suffixes should be chosen. + +The following methods are defined if $(D Allocator) defines them, and forward to it: $(D deallocateAll), $(D empty), $(D owns). + */ +struct AffixAllocator(Allocator, Prefix, Suffix = void) +{ + import std.conv : emplace; + import std.experimental.allocator.common : stateSize, forwardToMember, + roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf, + hasStaticallyKnownAlignment; + import std.experimental.allocator : IAllocator, theAllocator; + import std.traits : hasMember; + import std.algorithm.comparison : min; + import std.typecons : Ternary; + import std.math : isPowerOf2; + + static if (hasStaticallyKnownAlignment!Allocator) + { + static assert( + !stateSize!Prefix || Allocator.alignment >= Prefix.alignof, + "AffixAllocator does not work with allocators offering a smaller" + ~ " alignment than the prefix alignment."); + } + static assert(alignment % Suffix.alignof == 0, + "This restriction could be relaxed in the future."); + + /** + If $(D Prefix) is $(D void), the alignment is that of the parent. Otherwise, the alignment is the same as the $(D Prefix)'s alignment. + */ + static if (hasStaticallyKnownAlignment!Allocator) + { + enum uint alignment = isPowerOf2(stateSize!Prefix) + ? min(stateSize!Prefix, Allocator.alignment) + : (stateSize!Prefix ? Prefix.alignof : Allocator.alignment); + } + else static if (is(Prefix == void)) + { + enum uint alignment = platformAlignment; + } + else + { + enum uint alignment = Prefix.alignof; + } + + /** + If the parent allocator $(D Allocator) is stateful, an instance of it is + stored as a member. Otherwise, $(D AffixAllocator) uses + `Allocator.instance`. In either case, the name $(D _parent) is uniformly + used for accessing the parent allocator. + */ + static if (stateSize!Allocator) + { + Allocator _parent; + static if (is(Allocator == IAllocator)) + { + Allocator parent() + { + if (_parent is null) _parent = theAllocator; + assert(alignment <= _parent.alignment); + return _parent; + } + } + else + { + alias parent = _parent; + } + } + else + { + alias parent = Allocator.instance; + } + + private template Impl() + { + + size_t goodAllocSize(size_t s) + { + import std.experimental.allocator.common : goodAllocSize; + auto a = actualAllocationSize(s); + return roundUpToMultipleOf(parent.goodAllocSize(a) + - stateSize!Prefix - stateSize!Suffix, + this.alignment); + } + + private size_t actualAllocationSize(size_t s) const + { + assert(s > 0); + static if (!stateSize!Suffix) + { + return s + stateSize!Prefix; + } + else + { + return + roundUpToMultipleOf(s + stateSize!Prefix, Suffix.alignof) + + stateSize!Suffix; + } + } + + private void[] actualAllocation(void[] b) const + { + assert(b !is null); + return (b.ptr - stateSize!Prefix) + [0 .. actualAllocationSize(b.length)]; + } + + void[] allocate(size_t bytes) + { + if (!bytes) return null; + auto result = parent.allocate(actualAllocationSize(bytes)); + if (result is null) return null; + static if (stateSize!Prefix) + { + assert(result.ptr.alignedAt(Prefix.alignof)); + emplace!Prefix(cast(Prefix*) result.ptr); + } + static if (stateSize!Suffix) + { + auto suffixP = result.ptr + result.length - Suffix.sizeof; + assert(suffixP.alignedAt(Suffix.alignof)); + emplace!Suffix(cast(Suffix*)(suffixP)); + } + return result[stateSize!Prefix .. stateSize!Prefix + bytes]; + } + + static if (hasMember!(Allocator, "allocateAll")) + void[] allocateAll() + { + auto result = parent.allocateAll(); + if (result is null) return null; + if (result.length < actualAllocationSize(1)) + { + deallocate(result); + return null; + } + static if (stateSize!Prefix) + { + assert(result.length > stateSize!Prefix); + emplace!Prefix(cast(Prefix*) result.ptr); + result = result[stateSize!Prefix .. $]; + } + static if (stateSize!Suffix) + { + assert(result.length > stateSize!Suffix); + // Ehm, find a properly aligned place for the suffix + auto p = (result.ptr + result.length - stateSize!Suffix) + .alignDownTo(Suffix.alignof); + assert(p > result.ptr); + emplace!Suffix(cast(Suffix*) p); + result = result[0 .. p - result.ptr]; + } + return result; + } + + static if (hasMember!(Allocator, "owns")) + Ternary owns(void[] b) + { + if (b is null) return Ternary.no; + return parent.owns(actualAllocation(b)); + } + + static if (hasMember!(Allocator, "resolveInternalPointer")) + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + void[] p1; + Ternary r = parent.resolveInternalPointer(p, p1); + if (r != Ternary.yes || p1 is null) + return r; + p1 = p1[stateSize!Prefix .. $]; + auto p2 = (p1.ptr + p1.length - stateSize!Suffix) + .alignDownTo(Suffix.alignof); + result = p1[0 .. p2 - p1.ptr]; + return Ternary.yes; + } + + static if (!stateSize!Suffix && hasMember!(Allocator, "expand")) + bool expand(ref void[] b, size_t delta) + { + if (!b.ptr) return delta == 0; + auto t = actualAllocation(b); + const result = parent.expand(t, delta); + if (!result) return false; + b = b.ptr[0 .. b.length + delta]; + return true; + } + + static if (hasMember!(Allocator, "reallocate")) + bool reallocate(ref void[] b, size_t s) + { + if (b is null) + { + b = allocate(s); + return b.length == s; + } + auto t = actualAllocation(b); + const result = parent.reallocate(t, actualAllocationSize(s)); + if (!result) return false; // no harm done + b = t.ptr[stateSize!Prefix .. stateSize!Prefix + s]; + return true; + } + + static if (hasMember!(Allocator, "deallocate")) + bool deallocate(void[] b) + { + if (!b.ptr) return true; + return parent.deallocate(actualAllocation(b)); + } + + /* The following methods are defined if $(D ParentAllocator) defines + them, and forward to it: $(D deallocateAll), $(D empty).*/ + mixin(forwardToMember("parent", + "deallocateAll", "empty")); + + // Computes suffix type given buffer type + private template Payload2Affix(Payload, Affix) + { + static if (is(Payload[] : void[])) + alias Payload2Affix = Affix; + else static if (is(Payload[] : shared(void)[])) + alias Payload2Affix = shared Affix; + else static if (is(Payload[] : immutable(void)[])) + alias Payload2Affix = shared Affix; + else static if (is(Payload[] : const(shared(void))[])) + alias Payload2Affix = shared Affix; + else static if (is(Payload[] : const(void)[])) + alias Payload2Affix = const Affix; + else + static assert(0, "Internal error for type " ~ Payload.stringof); + } + + // Extra functions + static if (stateSize!Prefix) + { + static auto ref prefix(T)(T[] b) + { + assert(b.ptr && b.ptr.alignedAt(Prefix.alignof)); + return (cast(Payload2Affix!(T, Prefix)*) b.ptr)[-1]; + } + } + static if (stateSize!Suffix) + auto ref suffix(T)(T[] b) + { + assert(b.ptr); + auto p = b.ptr - stateSize!Prefix + + actualAllocationSize(b.length); + assert(p && p.alignedAt(Suffix.alignof)); + return (cast(Payload2Affix!(T, Suffix)*) p)[-1]; + } + } + + version (StdDdoc) + { + /** + Standard allocator methods. Each is defined if and only if the parent + allocator defines the homonym method (except for $(D goodAllocSize), + which may use the global default). Also, the methods will be $(D + shared) if the parent allocator defines them as such. + */ + size_t goodAllocSize(size_t); + /// Ditto + void[] allocate(size_t); + /// Ditto + Ternary owns(void[]); + /// Ditto + bool expand(ref void[] b, size_t delta); + /// Ditto + bool reallocate(ref void[] b, size_t s); + /// Ditto + bool deallocate(void[] b); + /// Ditto + bool deallocateAll(); + /// Ditto + Ternary empty(); + + /** + The `instance` singleton is defined if and only if the parent allocator + has no state and defines its own `it` object. + */ + static AffixAllocator instance; + + /** + Affix access functions offering references to the affixes of a + block `b` previously allocated with this allocator. `b` may not be null. + They are defined if and only if the corresponding affix is not `void`. + + The qualifiers of the affix are not always the same as the qualifiers + of the argument. This is because the affixes are not part of the data + itself, but instead are just $(I associated) with the data and known + to the allocator. The table below documents the type of `preffix(b)` and + `affix(b)` depending on the type of `b`. + + $(BOOKTABLE Result of `prefix`/`suffix` depending on argument (`U` is + any unqualified type, `Affix` is `Prefix` or `Suffix`), + $(TR $(TH Argument$(NBSP)Type) $(TH Return) $(TH Comments)) + + $(TR $(TD `shared(U)[]`) $(TD `ref shared Affix`) + $(TD Data is shared across threads and the affix follows suit.)) + + $(TR $(TD `immutable(U)[]`) $(TD `ref shared Affix`) + $(TD Although the data is immutable, the allocator "knows" the + underlying memory is mutable, so `immutable` is elided for the affix + which is independent from the data itself. However, the result is + `shared` because `immutable` is implicitly shareable so multiple + threads may access and manipulate the affix for the same data.)) + + $(TR $(TD `const(shared(U))[]`) $(TD `ref shared Affix`) + $(TD The data is always shareable across threads. Even if the data + is `const`, the affix is modifiable by the same reasoning as for + `immutable`.)) + + $(TR $(TD `const(U)[]`) $(TD `ref const Affix`) + $(TD The input may have originated from `U[]` or `immutable(U)[]`, + so it may be actually shared or not. Returning an unqualified affix + may result in race conditions, whereas returning a `shared` affix + may result in inadvertent sharing of mutable thread-local data + across multiple threads. So the returned type is conservatively + `ref const`.)) + + $(TR $(TD `U[]`) $(TD `ref Affix`) + $(TD Unqualified data has unqualified affixes.)) + ) + + Precondition: `b !is null` and `b` must have been allocated with + this allocator. + */ + static ref auto prefix(T)(T[] b); + /// Ditto + ref auto suffix(T)(T[] b); + } + else static if (is(typeof(Allocator.instance) == shared)) + { + static shared AffixAllocator instance; + shared { mixin Impl!(); } + } + else + { + mixin Impl!(); + static if (stateSize!Allocator == 0) + static __gshared AffixAllocator instance; + } +} + +/// +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + // One word before and after each allocation. + alias A = AffixAllocator!(Mallocator, size_t, size_t); + auto b = A.instance.allocate(11); + A.instance.prefix(b) = 0xCAFE_BABE; + A.instance.suffix(b) = 0xDEAD_BEEF; + assert(A.instance.prefix(b) == 0xCAFE_BABE + && A.instance.suffix(b) == 0xDEAD_BEEF); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator : theAllocator, IAllocator; + + // One word before and after each allocation. + auto A = AffixAllocator!(IAllocator, size_t, size_t)(theAllocator); + auto a = A.allocate(11); + A.prefix(a) = 0xCAFE_BABE; + A.suffix(a) = 0xDEAD_BEEF; + assert(A.prefix(a) == 0xCAFE_BABE + && A.suffix(a) == 0xDEAD_BEEF); + + // One word before and after each allocation. + auto B = AffixAllocator!(IAllocator, size_t, size_t)(); + auto b = B.allocate(11); + B.prefix(b) = 0xCAFE_BABE; + B.suffix(b) = 0xDEAD_BEEF; + assert(B.prefix(b) == 0xCAFE_BABE + && B.suffix(b) == 0xDEAD_BEEF); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks.bitmapped_block + : BitmappedBlock; + import std.experimental.allocator.common : testAllocator; + testAllocator!({ + auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) + (BitmappedBlock!128(new ubyte[128 * 4096])); + return a; + }); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + alias A = AffixAllocator!(Mallocator, size_t); + auto b = A.instance.allocate(10); + A.instance.prefix(b) = 10; + assert(A.instance.prefix(b) == 10); + + import std.experimental.allocator.building_blocks.null_allocator + : NullAllocator; + alias B = AffixAllocator!(NullAllocator, size_t); + b = B.instance.allocate(100); + assert(b is null); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator; + import std.experimental.allocator; + import std.typecons : Ternary; + alias MyAllocator = AffixAllocator!(GCAllocator, uint); + auto a = MyAllocator.instance.makeArray!(shared int)(100); + static assert(is(typeof(&MyAllocator.instance.prefix(a)) == shared(uint)*)); + auto b = MyAllocator.instance.makeArray!(shared const int)(100); + static assert(is(typeof(&MyAllocator.instance.prefix(b)) == shared(uint)*)); + auto c = MyAllocator.instance.makeArray!(immutable int)(100); + static assert(is(typeof(&MyAllocator.instance.prefix(c)) == shared(uint)*)); + auto d = MyAllocator.instance.makeArray!(int)(100); + static assert(is(typeof(&MyAllocator.instance.prefix(d)) == uint*)); + auto e = MyAllocator.instance.makeArray!(const int)(100); + static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*)); + + void[] p; + assert(MyAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); + Ternary r = MyAllocator.instance.resolveInternalPointer(d.ptr, p); + assert(p.ptr is d.ptr && p.length >= d.length); +} diff --git a/std/experimental/allocator/building_blocks/allocator_list.d b/std/experimental/allocator/building_blocks/allocator_list.d new file mode 100644 index 00000000000..bd212fbb4f2 --- /dev/null +++ b/std/experimental/allocator/building_blocks/allocator_list.d @@ -0,0 +1,640 @@ +/// +module std.experimental.allocator.building_blocks.allocator_list; + +import std.experimental.allocator.common; +import std.experimental.allocator.building_blocks.null_allocator; +import std.experimental.allocator.gc_allocator; +version(unittest) import std.stdio; + +// Turn this on for debugging +// debug = allocator_list; + +/** + +Given an $(LINK2 https://en.wikipedia.org/wiki/Factory_(object-oriented_programming), +object factory) of type `Factory` or a factory function +`factoryFunction`, and optionally also `BookkeepingAllocator` as a supplemental +allocator for bookkeeping, `AllocatorList` creates an allocator that lazily +creates as many allocators are needed for satisfying client allocation requests. + +An embedded list builds a most-recently-used strategy: the most recent +allocators used in calls to either `allocate`, `owns` (successful calls +only), or `deallocate` are tried for new allocations in order of their most +recent use. Thus, although core operations take in theory $(BIGOH k) time for +$(D k) allocators in current use, in many workloads the factor is sublinear. +Details of the actual strategy may change in future releases. + +`AllocatorList` is primarily intended for coarse-grained handling of +allocators, i.e. the number of allocators in the list is expected to be +relatively small compared to the number of allocations handled by each +allocator. However, the per-allocator overhead is small so using +`AllocatorList` with a large number of allocators should be satisfactory as long +as the most-recently-used strategy is fast enough for the application. + +`AllocatorList` makes an effort to return allocated memory back when no +longer used. It does so by destroying empty allocators. However, in order to +avoid thrashing (excessive creation/destruction of allocators under certain use +patterns), it keeps unused allocators for a while. + +Params: +factoryFunction = A function or template function (including function literals). +New allocators are created by calling `factoryFunction(n)` with strictly +positive numbers `n`. Delegates that capture their enviroment are not created +amid concerns regarding garbage creation for the environment. When the factory +needs state, a `Factory` object should be used. + +BookkeepingAllocator = Allocator used for storing bookkeeping data. The size of +bookkeeping data is proportional to the number of allocators. If $(D +BookkeepingAllocator) is $(D NullAllocator), then $(D AllocatorList) is +"ouroboros-style", i.e. it keeps the bookkeeping data in memory obtained from +the allocators themselves. Note that for ouroboros-style management, the size +$(D n) passed to $(D make) will be occasionally different from the size +requested by client code. + +Factory = Type of a factory object that returns new allocators on a need +basis. For an object $(D sweatshop) of type $(D Factory), `sweatshop(n)` should +return an allocator able to allocate at least `n` bytes (i.e. `Factory` must +define `opCall(size_t)` to return an allocator object). Usually the capacity of +allocators created should be much larger than $(D n) such that an allocator can +be used for many subsequent allocations. $(D n) is passed only to ensure the +minimum necessary for the next allocation. The factory object is allowed to hold +state, which will be stored inside `AllocatorList` as a direct `public` member +called `factory`. + +*/ +struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) +{ + import std.traits : hasMember; + import std.conv : emplace; + import std.typecons : Ternary; + import std.experimental.allocator.building_blocks.stats_collector + : StatsCollector, Options; + + private enum ouroboros = is(BookkeepingAllocator == NullAllocator); + + /** + Alias for `typeof(Factory()(1))`, i.e. the type of the individual + allocators. + */ + alias Allocator = typeof(Factory.init(1)); + // Allocator used internally + private alias SAllocator = StatsCollector!(Allocator, Options.bytesUsed); + + private static struct Node + { + // Allocator in this node + SAllocator a; + Node* next; + + @disable this(this); + + // Is this node unused? + void setUnused() { next = &this; } + bool unused() const { return next is &this; } + + // Just forward everything to the allocator + alias a this; + } + + /** + If $(D BookkeepingAllocator) is not $(D NullAllocator), $(D bkalloc) is + defined and accessible. + */ + + // State is stored in an array, but it has a list threaded through it by + // means of "nextIdx". + + // state + static if (!ouroboros) + { + static if (stateSize!BookkeepingAllocator) BookkeepingAllocator bkalloc; + else alias bkalloc = BookkeepingAllocator.instance; + } + static if (stateSize!Factory) + { + Factory factory; + } + private Node[] allocators; + private Node* root; + + static if (stateSize!Factory) + { + private auto make(size_t n) { return factory(n); } + } + else + { + private auto make(size_t n) { Factory f; return f(n); } + } + + /** + Constructs an `AllocatorList` given a factory object. This constructor is + defined only if `Factory` has state. + */ + static if (stateSize!Factory) + this(ref Factory plant) + { + factory = plant; + } + /// Ditto + static if (stateSize!Factory) + this(Factory plant) + { + factory = plant; + } + + static if (hasMember!(Allocator, "deallocateAll") + && hasMember!(Allocator, "owns")) + ~this() + { + deallocateAll; + } + + /** + The alignment offered. + */ + enum uint alignment = Allocator.alignment; + + /** + Allocate a block of size $(D s). First tries to allocate from the existing + list of already-created allocators. If neither can satisfy the request, + creates a new allocator by calling $(D make(s)) and delegates the request + to it. However, if the allocation fresh off a newly created allocator + fails, subsequent calls to $(D allocate) will not cause more calls to $(D + make). + */ + void[] allocate(size_t s) + { + for (auto p = &root, n = *p; n; p = &n.next, n = *p) + { + auto result = n.allocate(s); + if (result.length != s) continue; + // Bring to front if not already + if (root != n) + { + *p = n.next; + n.next = root; + root = n; + } + return result; + } + // Can't allocate from the current pool. Check if we just added a new + // allocator, in that case it won't do any good to add yet another. + if (root && root.empty == Ternary.yes) + { + // no can do + return null; + } + // Add a new allocator + if (auto a = addAllocator(s)) + { + auto result = a.allocate(s); + assert(owns(result) == Ternary.yes || !result.ptr); + return result; + } + return null; + } + + private void moveAllocators(void[] newPlace) + { + assert(newPlace.ptr.alignedAt(Node.alignof)); + assert(newPlace.length % Node.sizeof == 0); + auto newAllocators = cast(Node[]) newPlace; + assert(allocators.length <= newAllocators.length); + + // Move allocators + foreach (i, ref e; allocators) + { + if (e.unused) + { + newAllocators[i].setUnused; + continue; + } + import core.stdc.string : memcpy; + memcpy(&newAllocators[i].a, &e.a, e.a.sizeof); + if (e.next) + { + newAllocators[i].next = newAllocators.ptr + + (e.next - allocators.ptr); + } + else + { + newAllocators[i].next = null; + } + } + + // Mark the unused portion as unused + foreach (i; allocators.length .. newAllocators.length) + { + newAllocators[i].setUnused; + } + auto toFree = allocators; + + // Change state + root = newAllocators.ptr + (root - allocators.ptr); + allocators = newAllocators; + + // Free the olden buffer + static if (ouroboros) + { + static if (hasMember!(Allocator, "deallocate") + && hasMember!(Allocator, "owns")) + deallocate(toFree); + } + else + { + bkalloc.deallocate(toFree); + } + } + + static if (ouroboros) + private Node* addAllocator(size_t atLeastBytes) + { + void[] t = allocators; + static if (hasMember!(Allocator, "expand") + && hasMember!(Allocator, "owns")) + { + immutable bool expanded = t && this.expand(t, Node.sizeof); + } + else + { + enum expanded = false; + } + if (expanded) + { + import std.c.string : memcpy; + assert(t.length % Node.sizeof == 0); + assert(t.ptr.alignedAt(Node.alignof)); + allocators = cast(Node[]) t; + allocators[$ - 1].setUnused; + auto newAlloc = SAllocator(make(atLeastBytes)); + memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); + emplace(&newAlloc); + } + else + { + immutable toAlloc = (allocators.length + 1) * Node.sizeof + + atLeastBytes + 128; + auto newAlloc = SAllocator(make(toAlloc)); + auto newPlace = newAlloc.allocate( + (allocators.length + 1) * Node.sizeof); + if (!newPlace) return null; + moveAllocators(newPlace); + import core.stdc.string : memcpy; + memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); + emplace(&newAlloc); + assert(allocators[$ - 1].owns(allocators) == Ternary.yes); + } + // Insert as new root + if (root != &allocators[$ - 1]) + { + allocators[$ - 1].next = root; + root = &allocators[$ - 1]; + } + else + { + // This is the first one + root.next = null; + } + assert(!root.unused); + return root; + } + + static if (!ouroboros) + private Node* addAllocator(size_t atLeastBytes) + { + void[] t = allocators; + static if (hasMember!(BookkeepingAllocator, "expand")) + immutable bool expanded = bkalloc.expand(t, Node.sizeof); + else + immutable bool expanded = false; + if (expanded) + { + assert(t.length % Node.sizeof == 0); + assert(t.ptr.alignedAt(Node.alignof)); + allocators = cast(Node[]) t; + allocators[$ - 1].setUnused; + } + else + { + // Could not expand, create a new block + t = bkalloc.allocate((allocators.length + 1) * Node.sizeof); + assert(t.length % Node.sizeof == 0); + if (!t.ptr) return null; + moveAllocators(t); + } + assert(allocators[$ - 1].unused); + auto newAlloc = SAllocator(make(atLeastBytes)); + import core.stdc.string : memcpy; + memcpy(&allocators[$ - 1].a, &newAlloc, newAlloc.sizeof); + emplace(&newAlloc); + // Creation succeeded, insert as root + if (allocators.length == 1) + allocators[$ - 1].next = null; + else + allocators[$ - 1].next = root; + assert(allocators[$ - 1].a.bytesUsed == 0); + root = &allocators[$ - 1]; + return root; + } + + /** + Defined only if `Allocator` defines `owns`. Tries each allocator in + turn, in most-recently-used order. If the owner is found, it is moved to + the front of the list as a side effect under the assumption it will be used + soon. + + Returns: `Ternary.yes` if one allocator was found to return `Ternary.yes`, + `Ternary.no` if all component allocators returned `Ternary.no`, and + `Ternary.unknown` if no allocator returned `Ternary.yes` and at least one + returned `Ternary.unknown`. + */ + static if (hasMember!(Allocator, "owns")) + Ternary owns(void[] b) + { + auto result = Ternary.no; + for (auto p = &root, n = *p; n; p = &n.next, n = *p) + { + immutable t = n.owns(b); + if (t != Ternary.yes) + { + if (t == Ternary.unknown) result = t; + continue; + } + // Move the owner to front, speculating it'll be used + if (n != root) + { + *p = n.next; + n.next = root; + root = n; + } + return Ternary.yes; + } + return result; + } + + /** + Defined only if $(D Allocator.expand) is defined. Finds the owner of $(D b) + and calls $(D expand) for it. The owner is not brought to the head of the + list. + */ + static if (hasMember!(Allocator, "expand") + && hasMember!(Allocator, "owns")) + bool expand(ref void[] b, size_t delta) + { + if (!b.ptr) return delta == 0; + for (auto p = &root, n = *p; n; p = &n.next, n = *p) + { + if (n.owns(b) == Ternary.yes) return n.expand(b, delta); + } + return false; + } + + /** + Defined only if $(D Allocator.reallocate) is defined. Finds the owner of + $(D b) and calls $(D reallocate) for it. If that fails, calls the global + $(D reallocate), which allocates a new block and moves memory. + */ + static if (hasMember!(Allocator, "reallocate")) + bool reallocate(ref void[] b, size_t s) + { + // First attempt to reallocate within the existing node + if (!b.ptr) + { + b = allocate(s); + return b.length == s; + } + for (auto p = &root, n = *p; n; p = &n.next, n = *p) + { + if (n.owns(b) == Ternary.yes) return n.reallocate(b, s); + } + // Failed, but we may find new memory in a new node. + return .reallocate(this, b, s); + } + + /** + Defined if $(D Allocator.deallocate) and $(D Allocator.owns) are defined. + */ + static if (hasMember!(Allocator, "deallocate") + && hasMember!(Allocator, "owns")) + bool deallocate(void[] b) + { + if (!b.ptr) return true; + assert(allocators.length); + assert(owns(b) == Ternary.yes); + bool result; + for (auto p = &root, n = *p; ; p = &n.next, n = *p) + { + assert(n); + if (n.owns(b) != Ternary.yes) continue; + result = n.deallocate(b); + // Bring to front + if (n != root) + { + *p = n.next; + n.next = root; + root = n; + } + if (n.empty != Ternary.yes) return result; + break; + } + // Hmmm... should we return this allocator back to the wild? Let's + // decide if there are TWO empty allocators we can release ONE. This + // is to avoid thrashing. + // Note that loop starts from the second element. + for (auto p = &root.next, n = *p; n; p = &n.next, n = *p) + { + if (n.unused || n.empty != Ternary.yes) continue; + // Used and empty baby, nuke it! + n.a.destroy; + *p = n.next; + n.setUnused; + break; + } + return result; + } + + /** + Defined only if $(D Allocator.owns) and $(D Allocator.deallocateAll) are + defined. + */ + static if (ouroboros && hasMember!(Allocator, "deallocateAll") + && hasMember!(Allocator, "owns")) + bool deallocateAll() + { + Node* special; + foreach (ref n; allocators) + { + if (n.unused) continue; + if (n.owns(allocators) == Ternary.yes) + { + special = &n; + continue; + } + n.a.deallocateAll; + n.a.destroy; + } + assert(special || !allocators.ptr); + if (special) + { + special.deallocate(allocators); + } + allocators = null; + root = null; + return true; + } + + static if (!ouroboros && hasMember!(Allocator, "deallocateAll") + && hasMember!(Allocator, "owns")) + bool deallocateAll() + { + foreach (ref n; allocators) + { + if (n.unused) continue; + n.a.deallocateAll; + n.a.destroy; + } + bkalloc.deallocate(allocators); + allocators = null; + root = null; + return true; + } + + /** + Returns `Ternary.yes` if no allocators are currently active, + `Ternary.no` otherwise. This methods never returns `Ternary.unknown`. + */ + Ternary empty() const + { + return Ternary(!allocators.length); + } +} + +/// Ditto +template AllocatorList(alias factoryFunction, + BookkeepingAllocator = GCAllocator) +{ + alias A = typeof(factoryFunction(1)); + static assert( + // is a template function (including literals) + is(typeof({A function(size_t) @system x = factoryFunction!size_t;})) + || + // or a function (including literals) + is(typeof({A function(size_t) @system x = factoryFunction;})) + , + "Only function names and function literals that take size_t" + ~ " and return an allocator are accepted, not " + ~ typeof(factoryFunction).stringof + ); + static struct Factory + { + A opCall(size_t n) { return factoryFunction(n); } + } + alias AllocatorList = .AllocatorList!(Factory, BookkeepingAllocator); +} + +/// +version(Posix) @system unittest +{ + import std.algorithm.comparison : max; + import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.building_blocks.free_list : ContiguousFreeList; + import std.experimental.allocator.building_blocks.segregator : Segregator; + import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + + // Ouroboros allocator list based upon 4MB regions, fetched directly from + // mmap. All memory is released upon destruction. + alias A1 = AllocatorList!((n) => Region!MmapAllocator(max(n, 1024 * 4096)), + NullAllocator); + + // Allocator list based upon 4MB regions, fetched from the garbage + // collector. All memory is released upon destruction. + alias A2 = AllocatorList!((n) => Region!GCAllocator(max(n, 1024 * 4096))); + + // Ouroboros allocator list based upon 4MB regions, fetched from the garbage + // collector. Memory is left to the collector. + alias A3 = AllocatorList!( + (n) => Region!NullAllocator(new ubyte[max(n, 1024 * 4096)]), + NullAllocator); + + // Allocator list that creates one freelist for all objects + alias A4 = + Segregator!( + 64, AllocatorList!( + (n) => ContiguousFreeList!(NullAllocator, 0, 64)( + cast(ubyte[])(GCAllocator.instance.allocate(4096)))), + GCAllocator); + + A4 a; + auto small = a.allocate(64); + assert(small); + a.deallocate(small); + auto b1 = a.allocate(1024 * 8192); + assert(b1 !is null); // still works due to overdimensioning + b1 = a.allocate(1024 * 10); + assert(b1.length == 1024 * 10); +} + +@system unittest +{ + // Create an allocator based upon 4MB regions, fetched from the GC heap. + import std.algorithm.comparison : max; + import std.experimental.allocator.building_blocks.region : Region; + AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]), + NullAllocator) a; + const b1 = a.allocate(1024 * 8192); + assert(b1 !is null); // still works due to overdimensioning + const b2 = a.allocate(1024 * 10); + assert(b2.length == 1024 * 10); + a.deallocateAll(); +} + +@system unittest +{ + // Create an allocator based upon 4MB regions, fetched from the GC heap. + import std.algorithm.comparison : max; + import std.experimental.allocator.building_blocks.region : Region; + AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a; + auto b1 = a.allocate(1024 * 8192); + assert(b1 !is null); // still works due to overdimensioning + b1 = a.allocate(1024 * 10); + assert(b1.length == 1024 * 10); + a.deallocateAll(); +} + +@system unittest +{ + import std.algorithm.comparison : max; + import std.experimental.allocator.building_blocks.region : Region; + import std.typecons : Ternary; + AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a; + auto b1 = a.allocate(1024 * 8192); + assert(b1 !is null); + b1 = a.allocate(1024 * 10); + assert(b1.length == 1024 * 10); + a.allocate(1024 * 4095); + a.deallocateAll(); + assert(a.empty == Ternary.yes); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks.region : Region; + enum bs = GCAllocator.alignment; + AllocatorList!((n) => Region!GCAllocator(256 * bs)) a; + auto b1 = a.allocate(192 * bs); + assert(b1.length == 192 * bs); + assert(a.allocators.length == 1); + auto b2 = a.allocate(64 * bs); + assert(b2.length == 64 * bs); + assert(a.allocators.length == 1); + auto b3 = a.allocate(192 * bs); + assert(b3.length == 192 * bs); + assert(a.allocators.length == 2); + a.deallocate(b1); + b1 = a.allocate(64 * bs); + assert(b1.length == 64 * bs); + assert(a.allocators.length == 2); + a.deallocateAll(); +} diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d new file mode 100644 index 00000000000..a0ded14ab68 --- /dev/null +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -0,0 +1,1423 @@ +/// +module std.experimental.allocator.building_blocks.bitmapped_block; + +import std.experimental.allocator.common; +import std.experimental.allocator.building_blocks.null_allocator; + +/** + +$(D BitmappedBlock) implements a simple heap consisting of one contiguous area +of memory organized in blocks, each of size $(D theBlockSize). A block is a unit +of allocation. A bitmap serves as bookkeeping data, more precisely one bit per +block indicating whether that block is currently allocated or not. + +Passing $(D NullAllocator) as $(D ParentAllocator) (the default) means user code +manages allocation of the memory block from the outside; in that case +$(D BitmappedBlock) must be constructed with a $(D void[]) preallocated block and +has no responsibility regarding the lifetime of its support underlying storage. +If another allocator type is passed, $(D BitmappedBlock) defines a destructor that +uses the parent allocator to release the memory block. That makes the combination of $(D AllocatorList), $(D BitmappedBlock), and a back-end allocator such as $(D MmapAllocator) a simple and scalable solution for memory allocation. + +There are advantages to storing bookkeeping data separated from the payload +(as opposed to e.g. using $(D AffixAllocator) to store metadata together with +each allocation). The layout is more compact (overhead is one bit per block), +searching for a free block during allocation enjoys better cache locality, and +deallocation does not touch memory around the payload being deallocated (which +is often cold). + +Allocation requests are handled on a first-fit basis. Although linear in +complexity, allocation is in practice fast because of the compact bookkeeping +representation, use of simple and fast bitwise routines, and caching of the +first available block position. A known issue with this general approach is +fragmentation, partially mitigated by coalescing. Since $(D BitmappedBlock) does +not need to maintain the allocated size, freeing memory implicitly coalesces +free blocks together. Also, tuning $(D blockSize) has a considerable impact on +both internal and external fragmentation. + +The size of each block can be selected either during compilation or at run +time. Statically-known block sizes are frequent in practice and yield slightly +better performance. To choose a block size statically, pass it as the $(D +blockSize) parameter as in $(D BitmappedBlock!(Allocator, 4096)). To choose a block +size parameter, use $(D BitmappedBlock!(Allocator, chooseAtRuntime)) and pass the +block size to the constructor. + +*/ +struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment, + ParentAllocator = NullAllocator) +{ + import std.typecons : tuple, Tuple; + import std.traits : hasMember; + import std.conv : text; + import std.typecons : Ternary; + + @system unittest + { + import std.experimental.allocator.mallocator : AlignedMallocator; + import std.algorithm.comparison : max; + auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + max(theAlignment, cast(uint) size_t.sizeof))); + scope(exit) AlignedMallocator.instance.deallocate(m); + testAllocator!(() => BitmappedBlock(m)); + } + static assert(theBlockSize > 0 && theAlignment.isGoodStaticAlignment); + static assert(theBlockSize == chooseAtRuntime + || theBlockSize % theAlignment == 0, + "Block size must be a multiple of the alignment"); + + /** + If $(D blockSize == chooseAtRuntime), $(D BitmappedBlock) offers a read/write + property $(D blockSize). It must be set before any use of the allocator. + Otherwise (i.e. $(D theBlockSize) is a legit constant), $(D blockSize) is + an alias for $(D theBlockSize). Whether constant or variable, must also be + a multiple of $(D alignment). This constraint is $(D assert)ed statically + and dynamically. + */ + static if (theBlockSize != chooseAtRuntime) + { + alias blockSize = theBlockSize; + } + else + { + @property uint blockSize() { return _blockSize; } + @property void blockSize(uint s) + { + assert(!_control && s % alignment == 0); + _blockSize = s; + } + private uint _blockSize; + } + + static if (is(ParentAllocator == NullAllocator)) + { + private enum parentAlignment = platformAlignment; + } + else + { + private alias parentAlignment = ParentAllocator.alignment; + static assert(parentAlignment >= ulong.alignof); + } + + /** + The _alignment offered is user-configurable statically through parameter + $(D theAlignment), defaulted to $(D platformAlignment). + */ + alias alignment = theAlignment; + + // state { + /** + The _parent allocator. Depending on whether $(D ParentAllocator) holds state + or not, this is a member variable or an alias for + `ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) + { + ParentAllocator parent; + } + else + { + alias parent = ParentAllocator.instance; + } + private uint _blocks; + private BitVector _control; + private void[] _payload; + private size_t _startIdx; + // } + + private size_t totalAllocation(size_t capacity) + { + auto blocks = capacity.divideRoundUp(blockSize); + auto leadingUlongs = blocks.divideRoundUp(64); + import std.algorithm.comparison : min; + immutable initialAlignment = min(parentAlignment, + 1U << trailingZeros(leadingUlongs * 8)); + auto maxSlack = alignment <= initialAlignment + ? 0 + : alignment - initialAlignment; + //writeln(maxSlack); + return leadingUlongs * 8 + maxSlack + blockSize * blocks; + } + + /** + Constructs a block allocator given a hunk of memory, or a desired capacity + in bytes. + + $(UL + $(LI If $(D ParentAllocator) is $(D NullAllocator), only the constructor + taking $(D data) is defined and the user is responsible for freeing $(D + data) if desired.) + $(LI Otherwise, both constructors are defined. The $(D data)-based + constructor assumes memory has been allocated with the parent allocator. + The $(D capacity)-based constructor uses $(D ParentAllocator) to allocate + an appropriate contiguous hunk of memory. Regardless of the constructor + used, the destructor releases the memory by using $(D + ParentAllocator.deallocate).) + ) + */ + this(ubyte[] data) + { + immutable a = data.ptr.effectiveAlignment; + assert(a >= size_t.alignof || !data.ptr, + "Data must be aligned properly"); + + immutable ulong totalBits = data.length * 8; + immutable ulong bitsPerBlock = blockSize * 8 + 1; + // Get a first estimate + import std.conv : to; + _blocks = to!uint(totalBits / bitsPerBlock); + + // Reality is a bit more complicated, iterate until a good number of + // blocks found. + for (; _blocks; --_blocks) + { + immutable controlWords = _blocks.divideRoundUp(64); + auto payload = data[controlWords * 8 .. $].roundStartToMultipleOf( + alignment); + if (payload.length < _blocks * blockSize) + { + // Overestimated + continue; + } + _control = BitVector((cast(ulong*) data.ptr)[0 .. controlWords]); + _control[] = 0; + _payload = payload; + break; + } + } + + /// Ditto + static if (!is(ParentAllocator == NullAllocator)) + this(size_t capacity) + { + size_t toAllocate = totalAllocation(capacity); + auto data = cast(ubyte[])(parent.allocate(toAllocate)); + this(data); + assert(_blocks * blockSize >= capacity); + } + + /** + If $(D ParentAllocator) is not $(D NullAllocator) and defines $(D + deallocate), the destructor is defined to deallocate the block held. + */ + static if (!is(ParentAllocator == NullAllocator) + && hasMember!(ParentAllocator, "deallocate")) + ~this() + { + auto start = _control.rep.ptr, end = _payload.ptr + _payload.length; + parent.deallocate(start[0 .. end - start]); + } + + /* + Adjusts the memoized _startIdx to the leftmost control word that has at + least one zero bit. Assumes all control words to the left of $(D + _control[_startIdx]) are already occupied. + */ + private void adjustStartIdx() + { + while (_startIdx < _control.rep.length + && _control.rep[_startIdx] == ulong.max) + { + ++_startIdx; + } + } + + /* + Returns the blocks corresponding to the control bits starting at word index + wordIdx and bit index msbIdx (MSB=0) for a total of howManyBlocks. + */ + private void[] blocksFor(size_t wordIdx, uint msbIdx, size_t howManyBlocks) + { + assert(msbIdx <= 63); + const start = (wordIdx * 64 + msbIdx) * blockSize; + const end = start + blockSize * howManyBlocks; + if (end <= _payload.length) return _payload[start .. end]; + // This could happen if we have more control bits than available memory. + // That's possible because the control bits are rounded up to fit in + // 64-bit words. + return null; + } + + /** + Returns the actual bytes allocated when $(D n) bytes are requested, i.e. + $(D n.roundUpToMultipleOf(blockSize)). + */ + size_t goodAllocSize(size_t n) + { + return n.roundUpToMultipleOf(blockSize); + } + + /** + Allocates $(D s) bytes of memory and returns it, or $(D null) if memory + could not be allocated. + + The following information might be of help with choosing the appropriate + block size. Actual allocation occurs in sizes multiple of the block size. + Allocating one block is the fastest because only one 0 bit needs to be + found in the metadata. Allocating 2 through 64 blocks is the next cheapest + because it affects a maximum of two $(D ulong)s in the metadata. + Allocations greater than 64 blocks require a multiword search through the + metadata. + */ + @trusted void[] allocate(const size_t s) + { + const blocks = s.divideRoundUp(blockSize); + void[] result = void; + + switcharoo: + switch (blocks) + { + case 1: + // inline code here for speed + // find the next available block + foreach (i; _startIdx .. _control.rep.length) + { + const w = _control.rep[i]; + if (w == ulong.max) continue; + uint j = leadingOnes(w); + assert(j < 64); + assert((_control.rep[i] & ((1UL << 63) >> j)) == 0); + _control.rep[i] |= (1UL << 63) >> j; + if (i == _startIdx) + { + adjustStartIdx(); + } + result = blocksFor(i, j, 1); + break switcharoo; + } + goto case 0; // fall through + case 0: + return null; + case 2: .. case 64: + result = smallAlloc(cast(uint) blocks); + break; + default: + result = hugeAlloc(blocks); + break; + } + return result.ptr ? result.ptr[0 .. s] : null; + } + + /** + Allocates a block with specified alignment $(D a). The alignment must be a + power of 2. If $(D a <= alignment), function forwards to $(D allocate). + Otherwise, it attempts to overallocate and then adjust the result for + proper alignment. In the worst case the slack memory is around two blocks. + */ + void[] alignedAllocate(size_t n, uint a) + { + import std.math : isPowerOf2; + assert(a.isPowerOf2); + if (a <= alignment) return allocate(n); + + // Overallocate to make sure we can get an aligned block + auto b = allocate((n + a - alignment).roundUpToMultipleOf(blockSize)); + if (!b.ptr) return null; + auto result = b.roundStartToMultipleOf(a); + assert(result.length >= n); + result = result.ptr[0 .. n]; // final result + + // Free any blocks that might be slack at the beginning + auto slackHeadingBlocks = (result.ptr - b.ptr) / blockSize; + if (slackHeadingBlocks) + { + deallocate(b[0 .. slackHeadingBlocks * blockSize]); + } + + // Free any blocks that might be slack at the end + auto slackTrailingBlocks = ((b.ptr + b.length) + - (result.ptr + result.length)) / blockSize; + if (slackTrailingBlocks) + { + deallocate(b[$ - slackTrailingBlocks * blockSize .. $]); + } + + return result; + } + + /** + If the $(D BitmappedBlock) object is empty (has no active allocation), allocates + all memory within and returns a slice to it. Otherwise, returns $(D null) + (i.e. no attempt is made to allocate the largest available block). + */ + void[] allocateAll() + { + if (empty != Ternary.yes) return null; + _control[] = 1; + return _payload; + } + + /** + Returns `Ternary.yes` if `b` belongs to the `BitmappedBlock` object, + `Ternary.no` otherwise. Never returns `Ternary.unkown`. (This + method is somewhat tolerant in that accepts an interior slice.) + */ + Ternary owns(void[] b) const + { + //if (!b.ptr) return Ternary.no; + assert(b.ptr !is null || b.length == 0, "Corrupt block."); + return Ternary(b.ptr >= _payload.ptr + && b.ptr + b.length <= _payload.ptr + _payload.length); + } + + /* + Tries to allocate "blocks" blocks at the exact position indicated by the + position wordIdx/msbIdx (msbIdx counts from MSB, i.e. MSB has index 0). If + it succeeds, fills "result" with the result and returns tuple(size_t.max, + 0). Otherwise, returns a tuple with the next position to search. + */ + private Tuple!(size_t, uint) allocateAt(size_t wordIdx, uint msbIdx, + size_t blocks, ref void[] result) + { + assert(blocks > 0); + assert(wordIdx < _control.rep.length); + assert(msbIdx <= 63); + if (msbIdx + blocks <= 64) + { + // Allocation should fit this control word + if (setBitsIfZero(_control.rep[wordIdx], + cast(uint) (64 - msbIdx - blocks), 63 - msbIdx)) + { + // Success + result = blocksFor(wordIdx, msbIdx, blocks); + return tuple(size_t.max, 0u); + } + // Can't allocate, make a suggestion + return msbIdx + blocks == 64 + ? tuple(wordIdx + 1, 0u) + : tuple(wordIdx, cast(uint) (msbIdx + blocks)); + } + // Allocation spans two control words or more + immutable mask = ulong.max >> msbIdx; + if (_control.rep[wordIdx] & mask) + { + // We can't allocate the rest of this control word, + // return a suggestion. + return tuple(wordIdx + 1, 0u); + } + // We can allocate the rest of this control word, but we first need to + // make sure we can allocate the tail. + if (wordIdx + 1 == _control.rep.length) + { + // No more memory + return tuple(_control.rep.length, 0u); + } + auto hint = allocateAt(wordIdx + 1, 0, blocks - 64 + msbIdx, result); + if (hint[0] == size_t.max) + { + // We did it! + _control.rep[wordIdx] |= mask; + result = blocksFor(wordIdx, msbIdx, blocks); + return tuple(size_t.max, 0u); + } + // Failed, return a suggestion that skips this whole run. + return hint; + } + + /* Allocates as many blocks as possible at the end of the blocks indicated + by wordIdx. Returns the number of blocks allocated. */ + private uint allocateAtTail(size_t wordIdx) + { + assert(wordIdx < _control.rep.length); + const available = trailingZeros(_control.rep[wordIdx]); + _control.rep[wordIdx] |= ulong.max >> available; + return available; + } + + private void[] smallAlloc(uint blocks) + { + assert(blocks >= 2 && blocks <= 64, text(blocks)); + foreach (i; _startIdx .. _control.rep.length) + { + // Test within the current 64-bit word + const v = _control.rep[i]; + if (v == ulong.max) continue; + auto j = findContigOnes(~v, blocks); + if (j < 64) + { + // yay, found stuff + setBits(_control.rep[i], 64 - j - blocks, 63 - j); + return blocksFor(i, j, blocks); + } + // Next, try allocations that cross a word + auto available = trailingZeros(v); + if (available == 0) continue; + if (i + 1 >= _control.rep.length) break; + assert(available < blocks); // otherwise we should have found it + auto needed = blocks - available; + assert(needed > 0 && needed < 64); + if (allocateAtFront(i + 1, needed)) + { + // yay, found a block crossing two words + _control.rep[i] |= (1UL << available) - 1; + return blocksFor(i, 64 - available, blocks); + } + } + return null; + } + + private void[] hugeAlloc(size_t blocks) + { + assert(blocks > 64); + if (_startIdx == _control._rep.length) + { + assert(_control.allAre1); + return null; + } + auto i = _control.findZeros(blocks, _startIdx * 64); + if (i == i.max) return null; + // Allocate those bits + _control[i .. i + blocks] = 1; + return _payload[cast(size_t) (i * blockSize) + .. cast(size_t) ((i + blocks) * blockSize)]; + } + + // Rounds sizeInBytes to a multiple of blockSize. + private size_t bytes2blocks(size_t sizeInBytes) + { + return (sizeInBytes + blockSize - 1) / blockSize; + } + + /* Allocates given blocks at the beginning blocks indicated by wordIdx. + Returns true if allocation was possible, false otherwise. */ + private bool allocateAtFront(size_t wordIdx, uint blocks) + { + assert(wordIdx < _control.rep.length && blocks >= 1 && blocks <= 64); + const mask = (1UL << (64 - blocks)) - 1; + if (_control.rep[wordIdx] > mask) return false; + // yay, works + _control.rep[wordIdx] |= ~mask; + return true; + } + + /** + Expands an allocated block in place. + */ + @trusted bool expand(ref void[] b, immutable size_t delta) + { + // Dispose with trivial corner cases + if (delta == 0) return true; + if (b is null) return false; + + /* To simplify matters, refuse to expand buffers that don't start at a block start (this may be the case for blocks allocated with alignedAllocate). + */ + if ((b.ptr - _payload.ptr) % blockSize) return false; + + const blocksOld = bytes2blocks(b.length); + const blocksNew = bytes2blocks(b.length + delta); + assert(blocksOld <= blocksNew); + + // Possibly we have enough slack at the end of the block! + if (blocksOld == blocksNew) + { + b = b.ptr[0 .. b.length + delta]; + return true; + } + + assert((b.ptr - _payload.ptr) % blockSize == 0); + const blockIdx = (b.ptr - _payload.ptr) / blockSize; + const blockIdxAfter = blockIdx + blocksOld; + + // Try the maximum + const wordIdx = blockIdxAfter / 64, + msbIdx = cast(uint) (blockIdxAfter % 64); + void[] p; + auto hint = allocateAt(wordIdx, msbIdx, blocksNew - blocksOld, p); + if (hint[0] != size_t.max) + { + return false; + } + // Expansion successful + assert(p.ptr == b.ptr + blocksOld * blockSize, + text(p.ptr, " != ", b.ptr + blocksOld * blockSize)); + b = b.ptr[0 .. b.length + delta]; + return true; + } + + /** + Reallocates a previously-allocated block. Contractions occur in place. + */ + @system bool reallocate(ref void[] b, size_t newSize) + { + if (!b.ptr) + { + b = allocate(newSize); + return b.length == newSize; + } + if (newSize == 0) + { + deallocate(b); + b = null; + return true; + } + if (newSize < b.length) + { + // Shrink. Will shrink in place by deallocating the trailing part. + auto newCapacity = bytes2blocks(newSize) * blockSize; + deallocate(b[newCapacity .. $]); + b = b[0 .. newSize]; + return true; + } + // Go the slow route + return .reallocate(this, b, newSize); + } + + /** + Reallocates a block previously allocated with $(D alignedAllocate). Contractions do not occur in place. + */ + @system bool alignedReallocate(ref void[] b, size_t newSize, uint a) + { + if (newSize == 0) + { + deallocate(b); + b = null; + return true; + } + // Go the slow route + return .alignedReallocate(this, b, newSize, a); + } + + /** + Deallocates a block previously allocated with this allocator. + */ + bool deallocate(void[] b) + { + if (b is null) return true; + + // Locate position + immutable pos = b.ptr - _payload.ptr; + immutable blockIdx = pos / blockSize; + + // Adjust pointer, might be inside a block due to alignedAllocate + auto begin = _payload.ptr + blockIdx * blockSize, + end = b.ptr + b.length; + b = begin[0 .. end - begin]; + // Round up size to multiple of block size + auto blocks = b.length.divideRoundUp(blockSize); + + // Get into details + auto wordIdx = blockIdx / 64, msbIdx = cast(uint) (blockIdx % 64); + if (_startIdx > wordIdx) _startIdx = wordIdx; + + // Three stages: heading bits, full words, leftover bits + if (msbIdx) + { + if (blocks + msbIdx <= 64) + { + resetBits(_control.rep[wordIdx], + cast(uint) (64 - msbIdx - blocks), + 63 - msbIdx); + return true; + } + else + { + _control.rep[wordIdx] &= ulong.max << 64 - msbIdx; + blocks -= 64 - msbIdx; + ++wordIdx; + msbIdx = 0; + } + } + + // Stage 2: reset one word at a time + for (; blocks >= 64; blocks -= 64) + { + _control.rep[wordIdx++] = 0; + } + + // Stage 3: deal with leftover bits, if any + assert(wordIdx <= _control.rep.length); + if (blocks) + { + _control.rep[wordIdx] &= ulong.max >> blocks; + } + return true; + } + + /** + Forcibly deallocates all memory allocated by this allocator, making it + available for further allocations. Does not return memory to $(D + ParentAllocator). + */ + bool deallocateAll() + { + _control[] = 0; + _startIdx = 0; + return true; + } + + /** + Returns `Ternary.yes` if no memory is currently allocated with this + allocator, otherwise `Ternary.no`. This method never returns + `Ternary.unknown`. + */ + Ternary empty() + { + return Ternary(_control.allAre0()); + } + + void dump() + { + import std.stdio : writefln, writeln; + writefln("%s @ %s {", typeid(this), cast(void*) _control._rep.ptr); + scope(exit) writeln("}"); + assert(_payload.length == blockSize * _blocks); + assert(_control.length >= _blocks); + writefln(" _startIdx=%s; blockSize=%s; blocks=%s", + _startIdx, blockSize, _blocks); + if (!_control.length) return; + uint blockCount = 1; + bool inAllocatedStore = _control[0]; + void* start = _payload.ptr; + for (size_t i = 1;; ++i) + { + if (i >= _blocks || _control[i] != inAllocatedStore) + { + writefln(" %s block at 0x%s, length: %s (%s*%s)", + inAllocatedStore ? "Busy" : "Free", + cast(void*) start, + blockCount * blockSize, + blockCount, blockSize); + if (i >= _blocks) break; + assert(i < _control.length); + inAllocatedStore = _control[i]; + start = _payload.ptr + blockCount * blockSize; + blockCount = 1; + } + else + { + ++blockCount; + } + } + } +} + +/// +@system unittest +{ + // Create a block allocator on top of a 10KB stack region. + import std.experimental.allocator.building_blocks.region : InSituRegion; + import std.traits : hasMember; + InSituRegion!(10_240, 64) r; + auto a = BitmappedBlock!(64, 64)(cast(ubyte[])(r.allocateAll())); + static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); + const b = a.allocate(100); + assert(b.length == 100); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + testAllocator!(() => BitmappedBlock!(64, 8, GCAllocator)(1024 * 64)); +} + +@system unittest +{ + static void testAllocateAll(size_t bs)(uint blocks, uint blocksAtATime) + { + import std.algorithm.comparison : min; + assert(bs); + import std.experimental.allocator.gc_allocator : GCAllocator; + auto a = BitmappedBlock!(bs, min(bs, platformAlignment))( + cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + + blocks) / 8)) + ); + import std.conv : text; + assert(blocks >= a._blocks, text(blocks, " < ", a._blocks)); + blocks = a._blocks; + + // test allocation of 0 bytes + auto x = a.allocate(0); + assert(x is null); + // test allocation of 1 byte + x = a.allocate(1); + assert(x.length == 1 || blocks == 0, + text(x.ptr, " ", x.length, " ", a)); + a.deallocateAll(); + + bool twice = true; + + begin: + foreach (i; 0 .. blocks / blocksAtATime) + { + auto b = a.allocate(bs * blocksAtATime); + assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); + } + assert(a.allocate(bs * blocksAtATime) is null); + assert(a.allocate(1) is null); + + // Now deallocate all and do it again! + a.deallocateAll(); + + // Test deallocation + + auto v = new void[][blocks / blocksAtATime]; + foreach (i; 0 .. blocks / blocksAtATime) + { + auto b = a.allocate(bs * blocksAtATime); + assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); + v[i] = b; + } + assert(a.allocate(bs * blocksAtATime) is null); + assert(a.allocate(1) is null); + + foreach (i; 0 .. blocks / blocksAtATime) + { + a.deallocate(v[i]); + } + + foreach (i; 0 .. blocks / blocksAtATime) + { + auto b = a.allocate(bs * blocksAtATime); + assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); + v[i] = b; + } + + foreach (i; 0 .. v.length) + { + a.deallocate(v[i]); + } + + if (twice) + { + twice = false; + goto begin; + } + + a.deallocateAll; + + // test expansion + if (blocks >= blocksAtATime) + { + foreach (i; 0 .. blocks / blocksAtATime - 1) + { + auto b = a.allocate(bs * blocksAtATime); + assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); + (cast(ubyte[]) b)[] = 0xff; + a.expand(b, blocksAtATime * bs) + || assert(0, text(i)); + (cast(ubyte[]) b)[] = 0xfe; + assert(b.length == bs * blocksAtATime * 2, text(i, ": ", b.length)); + a.reallocate(b, blocksAtATime * bs) || assert(0); + assert(b.length == bs * blocksAtATime, text(i, ": ", b.length)); + } + } + } + + testAllocateAll!(1)(0, 1); + testAllocateAll!(1)(8, 1); + testAllocateAll!(4096)(128, 1); + + testAllocateAll!(1)(0, 2); + testAllocateAll!(1)(128, 2); + testAllocateAll!(4096)(128, 2); + + testAllocateAll!(1)(0, 4); + testAllocateAll!(1)(128, 4); + testAllocateAll!(4096)(128, 4); + + testAllocateAll!(1)(0, 3); + testAllocateAll!(1)(24, 3); + testAllocateAll!(3008)(100, 1); + testAllocateAll!(3008)(100, 3); + + testAllocateAll!(1)(0, 128); + testAllocateAll!(1)(128 * 1, 128); + testAllocateAll!(128 * 20)(13 * 128, 128); +} + +// Test totalAllocation +@safe unittest +{ + BitmappedBlock!(8, 8, NullAllocator) h1; + assert(h1.totalAllocation(1) >= 8); + assert(h1.totalAllocation(64) >= 64); + assert(h1.totalAllocation(8 * 64) >= 8 * 64); + assert(h1.totalAllocation(8 * 63) >= 8 * 63); + assert(h1.totalAllocation(8 * 64 + 1) >= 8 * 65); + + BitmappedBlock!(64, 8, NullAllocator) h2; + assert(h2.totalAllocation(1) >= 64); + assert(h2.totalAllocation(64 * 64) >= 64 * 64); + + BitmappedBlock!(4096, 4096, NullAllocator) h3; + assert(h3.totalAllocation(1) >= 4096); + assert(h3.totalAllocation(64 * 4096) >= 64 * 4096); + assert(h3.totalAllocation(64 * 4096 + 1) >= 65 * 4096); +} + +// BitmappedBlockWithInternalPointers +/** + +A $(D BitmappedBlock) with additional structure for supporting $(D +resolveInternalPointer). To that end, $(D BitmappedBlockWithInternalPointers) adds a +bitmap (one bit per block) that marks object starts. The bitmap itself has +variable size and is allocated together with regular allocations. + +The time complexity of $(D resolveInternalPointer) is $(BIGOH k), where $(D k) +is the size of the object within which the internal pointer is looked up. + +*/ +struct BitmappedBlockWithInternalPointers( + size_t theBlockSize, uint theAlignment = platformAlignment, + ParentAllocator = NullAllocator) +{ + import std.conv : text; + import std.typecons : Ternary; + @system unittest + { + import std.experimental.allocator.mallocator : AlignedMallocator; + auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + theAlignment)); + scope(exit) AlignedMallocator.instance.deallocate(m); + testAllocator!(() => BitmappedBlockWithInternalPointers(m)); + } + + // state { + private BitmappedBlock!(theBlockSize, theAlignment, NullAllocator) _heap; + private BitVector _allocStart; + // } + + /** + Constructors accepting desired capacity or a preallocated buffer, similar + in semantics to those of $(D BitmappedBlock). + */ + this(ubyte[] data) + { + _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data); + } + + /// Ditto + static if (!is(ParentAllocator == NullAllocator)) + this(size_t capacity) + { + // Add room for the _allocStart vector + _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator) + (capacity + capacity.divideRoundUp(64)); + } + + // Makes sure there's enough room for _allocStart + private bool ensureRoomForAllocStart(size_t len) + { + if (_allocStart.length >= len) return true; + // Must ensure there's room + immutable oldLength = _allocStart.rep.length; + immutable bits = len.roundUpToMultipleOf(64); + void[] b = _allocStart.rep; + if (!_heap.reallocate(b, bits / 8)) return false; + assert(b.length * 8 == bits, text(b.length * 8, " != ", bits)); + _allocStart = BitVector(cast(ulong[]) b); + assert(_allocStart.rep.length * 64 == bits); + _allocStart.rep[oldLength .. $] = ulong.max; + return true; + } + + /** + Allocator primitives. + */ + alias alignment = theAlignment; + + /// Ditto + size_t goodAllocSize(size_t n) + { + return n.roundUpToMultipleOf(_heap.blockSize); + } + + /// Ditto + void[] allocate(size_t bytes) + { + auto r = _heap.allocate(bytes); + if (!r.ptr) return r; + immutable block = (r.ptr - _heap._payload.ptr) / _heap.blockSize; + immutable blocks = + (r.length + _heap.blockSize - 1) / _heap.blockSize; + if (!ensureRoomForAllocStart(block + blocks)) + { + // Failed, free r and bailout + _heap.deallocate(r); + return null; + } + assert(block < _allocStart.length); + assert(block + blocks <= _allocStart.length); + // Mark the _allocStart bits + assert(blocks > 0); + _allocStart[block] = 1; + _allocStart[block + 1 .. block + blocks] = 0; + assert(block + blocks == _allocStart.length + || _allocStart[block + blocks] == 1); + return r; + } + + /// Ditto + void[] allocateAll() + { + auto r = _heap.allocateAll(); + if (!r.ptr) return r; + // Carve space at the end for _allocStart + auto p = alignDownTo(r.ptr + r.length - 8, ulong.alignof); + r = r[0 .. p - r.ptr]; + // Initialize _allocStart + _allocStart = BitVector(cast(ulong[]) p[0 .. 8]); + _allocStart[] = 0; + immutable block = (r.ptr - _heap._payload.ptr) / _heap.blockSize; + assert(block < _allocStart.length); + _allocStart[block] = 1; + return r; + } + + /// Ditto + bool expand(ref void[] b, size_t bytes) + { + if (!bytes) return true; + if (b is null) return false; + immutable oldBlocks = + (b.length + _heap.blockSize - 1) / _heap.blockSize; + assert(oldBlocks); + immutable newBlocks = + (b.length + bytes + _heap.blockSize - 1) / _heap.blockSize; + assert(newBlocks >= oldBlocks); + immutable block = (b.ptr - _heap._payload.ptr) / _heap.blockSize; + assert(_allocStart[block]); + if (!ensureRoomForAllocStart(block + newBlocks) + || !_heap.expand(b, bytes)) + { + return false; + } + // Zero only the expanded bits + _allocStart[block + oldBlocks .. block + newBlocks] = 0; + assert(_allocStart[block]); + return true; + } + + /// Ditto + bool deallocate(void[] b) + { + // No need to touch _allocStart here - except for the first bit, it's + // meaningless in freed memory. The first bit is already 1. + return _heap.deallocate(b); + // TODO: one smart thing to do is reduce memory occupied by + // _allocStart if we're freeing the rightmost block. + } + + /// Ditto + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + if (p < _heap._payload.ptr + || p >= _heap._payload.ptr + _heap._payload.length) + { + return Ternary.no; + } + // Find block start + auto block = (p - _heap._payload.ptr) / _heap.blockSize; + if (block >= _allocStart.length) return Ternary.no; + // Within an allocation, must find the 1 just to the left of it + auto i = _allocStart.find1Backward(block); + if (i == i.max) return Ternary.no; + auto j = _allocStart.find1(i + 1); + result = _heap._payload.ptr[cast(size_t) (_heap.blockSize * i) + .. cast(size_t) (_heap.blockSize * j)]; + return Ternary.yes; + } + + /// Ditto + Ternary empty() + { + return _heap.empty; + } + + // Currently unused + private void markAllAsUnused() + { + // Mark all deallocated memory with 1 so we minimize damage created by + // false pointers. TODO: improve speed. + foreach (i, ref e; _allocStart.rep) + { + // Set to 1 all bits in _allocStart[i] that were 0 in control, and + // leave the others unchanged. + // (0, 0) => 1; (0, 1) => 0; (1, 0) => 1; (1, 1) => 1 + e |= ~_heap._control.rep[i]; + } + // Now zero all control bits + _heap._control[] = 0; + // EXCEPT for the _allocStart block itself + markAsUsed(_allocStart.rep); + } + + // Currently unused + private bool markAsUsed(void[] b) + { + // Locate position + immutable pos = b.ptr - _heap._payload.ptr; + assert(pos % _heap.blockSize == 0); + auto blockIdx = pos / _heap.blockSize; + if (_heap._control[blockIdx]) return false; + // Round up size to multiple of block size + auto blocks = b.length.divideRoundUp(_heap.blockSize); + _heap._control[blockIdx .. blockIdx + blocks] = 1; + return true; + } + + // Currently unused + private void doneMarking() + { + // Nothing to do, what's free stays free. + } +} + +@system unittest +{ + import std.typecons : Ternary; + + auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); + auto b = h.allocate(123); + assert(b.length == 123); + + void[] p; + Ternary r = h.resolveInternalPointer(b.ptr + 17, p); + assert(p.ptr is b.ptr); + assert(p.length >= b.length); + b = h.allocate(4096); + + h.resolveInternalPointer(b.ptr, p); + assert(p is b); + + h.resolveInternalPointer(b.ptr + 11, p); + assert(p is b); + + void[] unchanged = p; + h.resolveInternalPointer(b.ptr - 40_970, p); + assert(p is unchanged); + + assert(h.expand(b, 1)); + assert(b.length == 4097); + h.resolveInternalPointer(b.ptr + 4096, p); + assert(p.ptr is b.ptr); +} + +/** +Returns the number of most significant ones before a zero can be found in $(D +x). If $(D x) contains no zeros (i.e. is equal to $(D ulong.max)), returns 64. +*/ +private uint leadingOnes(ulong x) +{ + uint result = 0; + while (cast(long) x < 0) + { + ++result; + x <<= 1; + } + return result; +} + +@system unittest +{ + assert(leadingOnes(0) == 0); + assert(leadingOnes(~0UL) == 64); + assert(leadingOnes(0xF000_0000_0000_0000) == 4); + assert(leadingOnes(0xE400_0000_0000_0000) == 3); + assert(leadingOnes(0xC700_0200_0000_0000) == 2); + assert(leadingOnes(0x8000_0030_0000_0000) == 1); + assert(leadingOnes(0x2000_0000_0000_0000) == 0); +} + +/** +Finds a run of contiguous ones in $(D x) of length at least $(D n). +*/ +private uint findContigOnes(ulong x, uint n) +{ + while (n > 1) + { + immutable s = n >> 1; + x &= x << s; + n -= s; + } + return leadingOnes(~x); +} + +@system unittest +{ + assert(findContigOnes(0x0000_0000_0000_0300, 2) == 54); + + assert(findContigOnes(~0UL, 1) == 0); + assert(findContigOnes(~0UL, 2) == 0); + assert(findContigOnes(~0UL, 32) == 0); + assert(findContigOnes(~0UL, 64) == 0); + assert(findContigOnes(0UL, 1) == 64); + + assert(findContigOnes(0x4000_0000_0000_0000, 1) == 1); + assert(findContigOnes(0x0000_0F00_0000_0000, 4) == 20); +} + +/* +Unconditionally sets the bits from lsb through msb in w to zero. +*/ +private void setBits(ref ulong w, uint lsb, uint msb) +{ + assert(lsb <= msb && msb < 64); + const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); + w |= mask; +} + +@system unittest +{ + ulong w; + w = 0; setBits(w, 0, 63); assert(w == ulong.max); + w = 0; setBits(w, 1, 63); assert(w == ulong.max - 1); + w = 6; setBits(w, 0, 1); assert(w == 7); + w = 6; setBits(w, 3, 3); assert(w == 14); +} + +/* Are bits from lsb through msb in w zero? If so, make then 1 +and return the resulting w. Otherwise, just return 0. +*/ +private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) +{ + assert(lsb <= msb && msb < 64); + const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); + if (w & mask) return false; + w |= mask; + return true; +} + +// Assigns bits in w from lsb through msb to zero. +private void resetBits(ref ulong w, uint lsb, uint msb) +{ + assert(lsb <= msb && msb < 64); + const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); + w &= ~mask; +} + +/* +Bit disposition is MSB=0 (leftmost, big endian). +*/ +private struct BitVector +{ + ulong[] _rep; + + auto rep() { return _rep; } + + this(ulong[] data) { _rep = data; } + + void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } + + void opSliceAssign(bool b, ulong x, ulong y) + { + assert(x <= y && y <= _rep.length * 64); + if (x == y) return; + --y; + assert(x / 64 <= size_t.max); + immutable i1 = cast(size_t) (x / 64); + immutable uint b1 = 63 - x % 64; + assert(y / 64 <= size_t.max); + immutable i2 = cast(size_t) (y / 64); + immutable uint b2 = 63 - y % 64; + assert(i1 <= i2 && i2 < _rep.length); + if (i1 == i2) + { + // Inside the same word + assert(b1 >= b2); + if (b) setBits(_rep[i1], b2, b1); + else resetBits(_rep[i1], b2, b1); + } + else + { + // Spans multiple words + assert(i1 < i2); + if (b) setBits(_rep[i1], 0, b1); + else resetBits(_rep[i1], 0, b1); + _rep[i1 + 1 .. i2] = b; + if (b) setBits(_rep[i2], b2, 63); + else resetBits(_rep[i2], b2, 63); + } + } + + bool opIndex(ulong x) + { + assert(x < length); + return (_rep[cast(size_t) (x / 64)] + & (0x8000_0000_0000_0000UL >> (x % 64))) != 0; + } + + void opIndexAssign(bool b, ulong x) + { + assert(x / 64 <= size_t.max); + immutable i = cast(size_t) (x / 64); + immutable j = 0x8000_0000_0000_0000UL >> (x % 64); + if (b) _rep[i] |= j; + else _rep[i] &= ~j; + } + + ulong length() const + { + return _rep.length * 64; + } + + /* Returns the index of the first 1 to the right of i (including i itself), + or length if not found. + */ + ulong find1(ulong i) + { + assert(i < length); + assert(i / 64 <= size_t.max); + auto w = cast(size_t) (i / 64); + immutable b = i % 64; // 0 through 63, 0 when i == 0 + immutable mask = ulong.max >> b; + if (auto current = _rep[w] & mask) + { + // Great, found + return w * 64 + leadingOnes(~current); + } + // The current word doesn't have the solution, find the leftmost 1 + // going to the right. + for (++w; w < _rep.length; ++w) + { + if (auto current = _rep[w]) + { + return w * 64 + leadingOnes(~current); + } + } + return length; + } + + /* Returns the index of the first 1 to the left of i (including i itself), + or ulong.max if not found. + */ + ulong find1Backward(ulong i) + { + assert(i < length); + auto w = cast(size_t) (i / 64); + immutable b = 63 - (i % 64); // 0 through 63, 63 when i == 0 + immutable mask = ~((1UL << b) - 1); + assert(mask != 0); + // First, let's see if the current word has a bit larger than ours. + if (auto currentWord = _rep[w] & mask) + { + // Great, this word contains the result. + return w * 64 + 63 - currentWord.trailingZeros; + } + // The current word doesn't have the solution, find the rightmost 1 + // going to the left. + while (w >= 1) + { + --w; + if (auto currentWord = _rep[w]) + return w * 64 + (63 - currentWord.trailingZeros); + } + return ulong.max; + } + + /// Are all bits zero? + bool allAre0() const + { + foreach (w; _rep) if (w) return false; + return true; + } + + /// Are all bits one? + bool allAre1() const + { + foreach (w; _rep) if (w != ulong.max) return false; + return true; + } + + ulong findZeros(immutable size_t howMany, ulong start) + { + assert(start < length); + assert(howMany > 64); + auto i = cast(size_t) (start / 64); + while (_rep[i] & 1) + { + // No trailing zeros in this word, try the next one + if (++i == _rep.length) return ulong.max; + start = i * 64; + } + // Adjust start to have only trailing zeros after it + auto prefixLength = 64; + while (_rep[i] & (ulong.max >> (64 - prefixLength))) + { + assert(prefixLength > 0); + --prefixLength; + ++start; + } + + assert(howMany > prefixLength); + auto needed = howMany - prefixLength; + for (++i; needed >= 64; needed -= 64, ++i) + { + if (i >= _rep.length) return ulong.max; + if (_rep[i] != 0) return findZeros(howMany, i * 64); + } + // Leftover < 64 bits + assert(needed < 64); + if (!needed) return start; + if (i >= _rep.length) return ulong.max; + if (leadingOnes(~_rep[i]) >= needed) return start; + return findZeros(howMany, i * 64); + } +} + +@system unittest +{ + auto v = BitVector(new ulong[10]); + assert(v.length == 640); + + v[] = 0; + v[53] = 1; + assert(v[52] == 0); + assert(v[53] == 1); + assert(v[54] == 0); + + v[] = 0; + v[53 .. 55] = 1; + assert(v[52] == 0); + assert(v[53] == 1); + assert(v[54] == 1); + assert(v[55] == 0); + + v[] = 0; + v[2 .. 65] = 1; + assert(v.rep[0] == 0x3FFF_FFFF_FFFF_FFFF); + assert(v.rep[1] == 0x8000_0000_0000_0000); + assert(v.rep[2] == 0); + + v[] = 0; + assert(v.find1Backward(0) == ulong.max); + assert(v.find1Backward(43) == ulong.max); + assert(v.find1Backward(83) == ulong.max); + + v[0] = 1; + assert(v.find1Backward(0) == 0); + assert(v.find1Backward(43) == 0); + import std.conv : text; + assert(v.find1Backward(83) == 0, text(v.find1Backward(83))); + + v[0] = 0; + v[101] = 1; + assert(v.find1Backward(0) == ulong.max); + assert(v.find1Backward(43) == ulong.max); + assert(v.find1Backward(83) == ulong.max); + assert(v.find1Backward(100) == ulong.max); + assert(v.find1Backward(101) == 101); + assert(v.find1Backward(553) == 101); + + v[0 .. v.length] = 0; + v[v.length .. v.length] = 0; + v[0 .. 0] = 0; + + v[] = 0; + assert(v.find1(0) == v.length); + v[139] = 1; + assert(v.find1(0) == 139); + assert(v.find1(100) == 139); + assert(v.find1(138) == 139); + assert(v.find1(139) == 139); + assert(v.find1(140) == v.length); + + v[] = 0; + assert(v.findZeros(100, 0) == 0); + foreach (i; 0 .. 500) + assert(v.findZeros(100, i) == i, text(v.findZeros(100, i), " != ", i)); + assert(v.findZeros(540, 99) == 99); + assert(v.findZeros(99, 540) == 540); + assert(v.findZeros(540, 100) == 100); + assert(v.findZeros(640, 0) == 0); + assert(v.findZeros(641, 1) == ulong.max); + assert(v.findZeros(641, 100) == ulong.max); +} diff --git a/std/experimental/allocator/building_blocks/bucketizer.d b/std/experimental/allocator/building_blocks/bucketizer.d new file mode 100644 index 00000000000..e63c9274c59 --- /dev/null +++ b/std/experimental/allocator/building_blocks/bucketizer.d @@ -0,0 +1,241 @@ +/// +module std.experimental.allocator.building_blocks.bucketizer; + +/** + +A $(D Bucketizer) uses distinct allocators for handling allocations of sizes in +the intervals $(D [min, min + step - 1]), $(D [min + step, min + 2 * step - 1]), +$(D [min + 2 * step, min + 3 * step - 1]), $(D ...), $(D [max - step + 1, max]). + +$(D Bucketizer) holds a fixed-size array of allocators and dispatches calls to +them appropriately. The size of the array is $(D (max + 1 - min) / step), which +must be an exact division. + +Allocations for sizes smaller than $(D min) or larger than $(D max) are illegal +for $(D Bucketizer). To handle them separately, $(D Segregator) may be of use. + +*/ +struct Bucketizer(Allocator, size_t min, size_t max, size_t step) +{ + import std.traits : hasMember; + import common = std.experimental.allocator.common : roundUpToMultipleOf; + import std.typecons : Ternary; + + static assert((max - (min - 1)) % step == 0, + "Invalid limits when instantiating " ~ Bucketizer.stringof); + + // state + /** + The array of allocators is publicly available for e.g. initialization and + inspection. + */ + Allocator[(max + 1 - min) / step] buckets; + + private Allocator* allocatorFor(size_t n) + { + const i = (n - min) / step; + return i < buckets.length ? buckets.ptr + i : null; + } + + /** + The alignment offered is the same as $(D Allocator.alignment). + */ + enum uint alignment = Allocator.alignment; + + /** + Rounds up to the maximum size of the bucket in which $(D bytes) falls. + */ + size_t goodAllocSize(size_t bytes) const + { + // round up bytes such that bytes - min + 1 is a multiple of step + assert(bytes >= min); + const min_1 = min - 1; + return min_1 + roundUpToMultipleOf(bytes - min_1, step); + } + + /** + Directs the call to either one of the $(D buckets) allocators. + */ + void[] allocate(size_t bytes) + { + if (!bytes) return null; + if (auto a = allocatorFor(bytes)) + { + const actual = goodAllocSize(bytes); + auto result = a.allocate(actual); + return result.ptr ? result.ptr[0 .. bytes] : null; + } + return null; + } + + /** + Directs the call to either one of the $(D buckets) allocators. Defined only + if `Allocator` defines `alignedAllocate`. + */ + static if (hasMember!(Allocator, "alignedAllocate")) + void[] alignedAllocate(size_t bytes, uint a) + { + if (!bytes) return null; + if (auto a = allocatorFor(b.length)) + { + const actual = goodAllocSize(bytes); + auto result = a.alignedAllocate(actual); + return result.ptr ? result.ptr[0 .. bytes] : null; + } + return null; + } + + /** + This method allows expansion within the respective bucket range. It succeeds + if both $(D b.length) and $(D b.length + delta) fall in a range of the form + $(D [min + k * step, min + (k + 1) * step - 1]). + */ + bool expand(ref void[] b, size_t delta) + { + if (!b.ptr) return delta == 0; + assert(b.length >= min && b.length <= max); + const available = goodAllocSize(b.length); + const desired = b.length + delta; + if (available < desired) return false; + b = b.ptr[0 .. desired]; + return true; + } + + /** + This method allows reallocation within the respective bucket range. If both + $(D b.length) and $(D size) fall in a range of the form $(D [min + k * + step, min + (k + 1) * step - 1]), then reallocation is in place. Otherwise, + reallocation with moving is attempted. + */ + bool reallocate(ref void[] b, size_t size) + { + if (size == 0) + { + deallocate(b); + b = null; + return true; + } + if (size >= b.length) + { + return expand(b, size - b.length); + } + assert(b.length >= min && b.length <= max); + if (goodAllocSize(size) == goodAllocSize(b.length)) + { + b = b.ptr[0 .. size]; + return true; + } + // Move cross buckets + return common.reallocate(this, b, size); + } + + /** + Similar to `reallocate`, with alignment. Defined only if `Allocator` + defines `alignedReallocate`. + */ + static if (hasMember!(Allocator, "alignedReallocate")) + bool alignedReallocate(ref void[] b, size_t size, uint a) + { + if (size == 0) + { + deallocate(b); + b = null; + return true; + } + if (size >= b.length) + { + return expand(b, size - b.length); + } + assert(b.length >= min && b.length <= max); + if (goodAllocSize(size) == goodAllocSize(b.length)) + { + b = b.ptr[0 .. size]; + return true; + } + // Move cross buckets + return .alignedReallocate(this, b, size, a); + } + + /** + Defined only if `Allocator` defines `owns`. Finds the owner of `b` and forwards the call to it. + */ + static if (hasMember!(Allocator, "owns")) + Ternary owns(void[] b) + { + if (!b.ptr) return Ternary.no; + if (auto a = allocatorFor(b.length)) + { + const actual = goodAllocSize(b.length); + return a.owns(b.ptr[0 .. actual]); + } + return Ternary.no; + } + + /** + This method is only defined if $(D Allocator) defines $(D deallocate). + */ + static if (hasMember!(Allocator, "deallocate")) + bool deallocate(void[] b) + { + if (!b.ptr) return true; + if (auto a = allocatorFor(b.length)) + { + a.deallocate(b.ptr[0 .. goodAllocSize(b.length)]); + } + return true; + } + + /** + This method is only defined if all allocators involved define $(D + deallocateAll), and calls it for each bucket in turn. Returns `true` if all + allocators could deallocate all. + */ + static if (hasMember!(Allocator, "deallocateAll")) + bool deallocateAll() + { + bool result = true; + foreach (ref a; buckets) + { + if (!a.deallocateAll()) result = false; + } + return result; + } + + /** + This method is only defined if all allocators involved define $(D + resolveInternalPointer), and tries it for each bucket in turn. + */ + static if (hasMember!(Allocator, "resolveInternalPointer")) + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + foreach (ref a; buckets) + { + Ternary r = a.resolveInternalPointer(p, result); + if (r == Ternary.yes) return r; + } + return Ternary.no; + } +} + +/// +@system unittest +{ + import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.common : unbounded; + import std.typecons : Ternary; + import std.algorithm.comparison : max; + Bucketizer!( + FreeList!( + AllocatorList!( + (size_t n) => Region!Mallocator(max(n, 1024 * 1024))), + 0, unbounded), + 65, 512, 64) a; + auto b = a.allocate(400); + assert(b.length == 400); + assert(a.owns(b) == Ternary.yes); + void[] p; + a.deallocate(b); +} diff --git a/std/experimental/allocator/building_blocks/fallback_allocator.d b/std/experimental/allocator/building_blocks/fallback_allocator.d new file mode 100644 index 00000000000..f3f099dfaf0 --- /dev/null +++ b/std/experimental/allocator/building_blocks/fallback_allocator.d @@ -0,0 +1,355 @@ +/// +module std.experimental.allocator.building_blocks.fallback_allocator; + +import std.experimental.allocator.common; + +/** +$(D FallbackAllocator) is the allocator equivalent of an "or" operator in +algebra. An allocation request is first attempted with the $(D Primary) +allocator. If that returns $(D null), the request is forwarded to the $(D +Fallback) allocator. All other requests are dispatched appropriately to one of +the two allocators. + +In order to work, $(D FallbackAllocator) requires that $(D Primary) defines the +$(D owns) method. This is needed in order to decide which allocator was +responsible for a given allocation. + +$(D FallbackAllocator) is useful for fast, special-purpose allocators backed up +by general-purpose allocators. The example below features a stack region backed +up by the $(D GCAllocator). +*/ +struct FallbackAllocator(Primary, Fallback) +{ + import std.algorithm.comparison : min; + import std.traits : hasMember; + import std.typecons : Ternary; + + @system unittest + { + testAllocator!(() => FallbackAllocator()); + } + + /// The primary allocator. + static if (stateSize!Primary) Primary primary; + else alias primary = Primary.instance; + + /// The fallback allocator. + static if (stateSize!Fallback) Fallback fallback; + else alias fallback = Fallback.instance; + + /** + If both $(D Primary) and $(D Fallback) are stateless, $(D FallbackAllocator) + defines a static instance called `instance`. + */ + static if (!stateSize!Primary && !stateSize!Fallback) + { + static FallbackAllocator instance; + } + + /** + The alignment offered is the minimum of the two allocators' alignment. + */ + enum uint alignment = min(Primary.alignment, Fallback.alignment); + + /** + Allocates memory trying the primary allocator first. If it returns $(D + null), the fallback allocator is tried. + */ + void[] allocate(size_t s) + { + auto result = primary.allocate(s); + return result.length == s ? result : fallback.allocate(s); + } + + /** + $(D FallbackAllocator) offers $(D alignedAllocate) iff at least one of the + allocators also offers it. It attempts to allocate using either or both. + */ + static if (hasMember!(Primary, "alignedAllocate") + || hasMember!(Fallback, "alignedAllocate")) + void[] alignedAllocate(size_t s, uint a) + { + static if (hasMember!(Primary, "alignedAllocate")) + {{ + auto result = primary.alignedAllocate(s, a); + if (result.length == s) return result; + }} + static if (hasMember!(Fallback, "alignedAllocate")) + {{ + auto result = fallback.alignedAllocate(s, a); + if (result.length == s) return result; + }} + return null; + } + + /** + + $(D expand) is defined if and only if at least one of the allocators + defines $(D expand). It works as follows. If $(D primary.owns(b)), then the + request is forwarded to $(D primary.expand) if it is defined, or fails + (returning $(D false)) otherwise. If $(D primary) does not own $(D b), then + the request is forwarded to $(D fallback.expand) if it is defined, or fails + (returning $(D false)) otherwise. + + */ + static if (hasMember!(Primary, "owns") + && (hasMember!(Primary, "expand") || hasMember!(Fallback, "expand"))) + bool expand(ref void[] b, size_t delta) + { + if (!delta) return true; + if (!b.ptr) return false; + if (primary.owns(b) == Ternary.yes) + { + static if (hasMember!(Primary, "expand")) + return primary.expand(b, delta); + else + return false; + } + static if (hasMember!(Fallback, "expand")) + return fallback.expand(b, delta); + else + return false; + } + + /** + + $(D reallocate) works as follows. If $(D primary.owns(b)), then $(D + primary.reallocate(b, newSize)) is attempted. If it fails, an attempt is + made to move the allocation from $(D primary) to $(D fallback). + + If $(D primary) does not own $(D b), then $(D fallback.reallocate(b, + newSize)) is attempted. If that fails, an attempt is made to move the + allocation from $(D fallback) to $(D primary). + + */ + static if (hasMember!(Primary, "owns")) + bool reallocate(ref void[] b, size_t newSize) + { + bool crossAllocatorMove(From, To)(ref From from, ref To to) + { + auto b1 = to.allocate(newSize); + if (b1.length != newSize) return false; + if (b.length < newSize) b1[0 .. b.length] = b[]; + else b1[] = b[0 .. newSize]; + static if (hasMember!(From, "deallocate")) + from.deallocate(b); + b = b1; + return true; + } + + if (b is null || primary.owns(b) == Ternary.yes) + { + return primary.reallocate(b, newSize) + // Move from primary to fallback + || crossAllocatorMove(primary, fallback); + } + return fallback.reallocate(b, newSize) + // Interesting. Move from fallback to primary. + || crossAllocatorMove(fallback, primary); + } + + static if (hasMember!(Primary, "owns") + && (hasMember!(Primary, "alignedAllocate") + || hasMember!(Fallback, "alignedAllocate"))) + bool alignedReallocate(ref void[] b, size_t newSize, uint a) + { + bool crossAllocatorMove(From, To)(ref From from, ref To to) + { + static if (!hasMember!(To, "alignedAllocate")) + { + return false; + } + else + { + auto b1 = to.alignedAllocate(newSize, a); + if (b1.length != newSize) return false; + if (b.length < newSize) b1[0 .. b.length] = b[]; + else b1[] = b[0 .. newSize]; + static if (hasMember!(From, "deallocate")) + from.deallocate(b); + b = b1; + return true; + } + } + + static if (hasMember!(Primary, "alignedAllocate")) + { + if (b is null || primary.owns(b) == Ternary.yes) + { + return primary.alignedReallocate(b, newSize, a) + || crossAllocatorMove(primary, fallback); + } + } + static if (hasMember!(Fallback, "alignedAllocate")) + { + return fallback.alignedReallocate(b, newSize, a) + || crossAllocatorMove(fallback, primary); + } + else + { + return false; + } + } + + /** + $(D owns) is defined if and only if both allocators define $(D owns). + Returns $(D primary.owns(b) | fallback.owns(b)). + */ + static if (hasMember!(Primary, "owns") && hasMember!(Fallback, "owns")) + Ternary owns(void[] b) + { + return primary.owns(b) | fallback.owns(b); + } + + /** + $(D resolveInternalPointer) is defined if and only if both allocators + define it. + */ + static if (hasMember!(Primary, "resolveInternalPointer") + && hasMember!(Fallback, "resolveInternalPointer")) + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + Ternary r = primary.resolveInternalPointer(p, result); + return r == Ternary.no ? fallback.resolveInternalPointer(p, result) : r; + } + + /** + $(D deallocate) is defined if and only if at least one of the allocators + define $(D deallocate). It works as follows. If $(D primary.owns(b)), + then the request is forwarded to $(D primary.deallocate) if it is defined, + or is a no-op otherwise. If $(D primary) does not own $(D b), then the + request is forwarded to $(D fallback.deallocate) if it is defined, or is a + no-op otherwise. + */ + static if (hasMember!(Primary, "owns") && + (hasMember!(Primary, "deallocate") + || hasMember!(Fallback, "deallocate"))) + bool deallocate(void[] b) + { + if (primary.owns(b) == Ternary.yes) + { + static if (hasMember!(Primary, "deallocate")) + return primary.deallocate(b); + else + return false; + } + else + { + static if (hasMember!(Fallback, "deallocate")) + return fallback.deallocate(b); + else + return false; + } + } + + /** + $(D empty) is defined if both allocators also define it. + + Returns: $(D primary.empty & fallback.empty) + */ + static if (hasMember!(Primary, "empty") && hasMember!(Fallback, "empty")) + Ternary empty() + { + return primary.empty & fallback.empty; + } +} + +@system unittest +{ + import std.experimental.allocator.building_blocks.region : InSituRegion; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.conv : text; + import std.typecons : Ternary; + FallbackAllocator!(InSituRegion!16_384, GCAllocator) a; + // This allocation uses the stack + auto b1 = a.allocate(1024); + assert(b1.length == 1024, text(b1.length)); + assert(a.primary.owns(b1) == Ternary.yes); + // This large allocation will go to the Mallocator + auto b2 = a.allocate(1024 * 1024); + assert(a.primary.owns(b2) == Ternary.no); + a.deallocate(b1); + a.deallocate(b2); +} + +/* +Forwards an argument from one function to another +*/ +private auto ref forward(alias arg)() +{ + static if (__traits(isRef, arg)) + { + return arg; + } + else + { + import std.algorithm.mutation : move; + return move(arg); + } +} + +@safe unittest +{ + void fun(T)(auto ref T, string) { /* ... */ } + void gun(T...)(auto ref T args) + { + fun(forward!(args[0]), forward!(args[1])); + } + gun(42, "hello"); + int x; + gun(x, "hello"); +} + +@safe unittest +{ + static void checkByRef(T)(auto ref T value) + { + static assert(__traits(isRef, value)); + } + + static void checkByVal(T)(auto ref T value) + { + static assert(!__traits(isRef, value)); + } + + static void test1(ref int a) { checkByRef(forward!a); } + static void test2(int a) { checkByVal(forward!a); } + static void test3() { int a; checkByVal(forward!a); } +} + +/** +Convenience function that uses type deduction to return the appropriate +$(D FallbackAllocator) instance. To initialize with allocators that don't have +state, use their $(D it) static member. +*/ +FallbackAllocator!(Primary, Fallback) +fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f) +{ + alias R = FallbackAllocator!(Primary, Fallback); + + static if (stateSize!Primary) + static if (stateSize!Fallback) + return R(forward!p, forward!f); + else + return R(forward!p); + else + static if (stateSize!Fallback) + return R(forward!f); + else + return R(); +} + +/// +@system unittest +{ + import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + auto a = fallbackAllocator(Region!GCAllocator(1024), GCAllocator.instance); + auto b1 = a.allocate(1020); + assert(b1.length == 1020); + assert(a.primary.owns(b1) == Ternary.yes); + auto b2 = a.allocate(10); + assert(b2.length == 10); + assert(a.primary.owns(b2) == Ternary.no); +} diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d new file mode 100644 index 00000000000..136e9b44db4 --- /dev/null +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -0,0 +1,1123 @@ +/// +module std.experimental.allocator.building_blocks.free_list; + +import std.experimental.allocator.common; +import std.typecons : Flag, Yes, No; + +/** + +$(HTTP en.wikipedia.org/wiki/Free_list, Free list allocator), stackable on top of +another allocator. Allocation requests between $(D min) and $(D max) bytes are +rounded up to $(D max) and served from a singly-linked list of buffers +deallocated in the past. All other allocations are directed to $(D +ParentAllocator). Due to the simplicity of free list management, allocations +from the free list are fast. + +One instantiation is of particular interest: $(D FreeList!(0, unbounded)) puts +every deallocation in the freelist, and subsequently serves any allocation from +the freelist (if not empty). There is no checking of size matching, which would +be incorrect for a freestanding allocator but is both correct and fast when an +owning allocator on top of the free list allocator (such as $(D Segregator)) is +already in charge of handling size checking. + +The following methods are defined if $(D ParentAllocator) defines them, and +forward to it: $(D expand), $(D owns), $(D reallocate). + +*/ +struct FreeList(ParentAllocator, + size_t minSize, size_t maxSize = minSize, + Flag!"adaptive" adaptive = No.adaptive) +{ + import std.conv : text; + import std.exception : enforce; + import std.traits : hasMember; + import std.typecons : Ternary; + + static assert(minSize != unbounded, "Use minSize = 0 for no low bound."); + static assert(maxSize >= (void*).sizeof, + "Maximum size must accommodate a pointer."); + + private enum unchecked = minSize == 0 && maxSize == unbounded; + + private enum hasTolerance = !unchecked && (minSize != maxSize + || maxSize == chooseAtRuntime); + + static if (minSize == chooseAtRuntime) + { + /** + Returns the smallest allocation size eligible for allocation from the + freelist. (If $(D minSize != chooseAtRuntime), this is simply an alias + for $(D minSize).) + */ + @property size_t min() const + { + assert(_min != chooseAtRuntime); + return _min; + } + /** + If $(D FreeList) has been instantiated with $(D minSize == + chooseAtRuntime), then the $(D min) property is writable. Setting it + must precede any allocation. + + Params: + low = new value for $(D min) + + Precondition: $(D low <= max), or $(D maxSize == chooseAtRuntime) and + $(D max) has not yet been initialized. Also, no allocation has been + yet done with this allocator. + + Postcondition: $(D min == low) + */ + @property void min(size_t low) + { + assert(low <= max || max == chooseAtRuntime); + minimize; + _min = low; + } + } + else + { + alias min = minSize; + } + + static if (maxSize == chooseAtRuntime) + { + /** + Returns the largest allocation size eligible for allocation from the + freelist. (If $(D maxSize != chooseAtRuntime), this is simply an alias + for $(D maxSize).) All allocation requests for sizes greater than or + equal to $(D min) and less than or equal to $(D max) are rounded to $(D + max) and forwarded to the parent allocator. When the block fitting the + same constraint gets deallocated, it is put in the freelist with the + allocated size assumed to be $(D max). + */ + @property size_t max() const { return _max; } + + /** + If $(D FreeList) has been instantiated with $(D maxSize == + chooseAtRuntime), then the $(D max) property is writable. Setting it + must precede any allocation. + + Params: + high = new value for $(D max) + + Precondition: $(D high >= min), or $(D minSize == chooseAtRuntime) and + $(D min) has not yet been initialized. Also $(D high >= (void*).sizeof). Also, no allocation has been yet done with this allocator. + + Postcondition: $(D max == high) + */ + @property void max(size_t high) + { + assert((high >= min || min == chooseAtRuntime) + && high >= (void*).sizeof); + minimize; + _max = high; + } + + /// + @safe unittest + { + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.common : chooseAtRuntime; + + FreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; + a.min = 64; + a.max = 128; + assert(a.min == 64); + assert(a.max == 128); + } + } + else + { + alias max = maxSize; + } + + private bool tooSmall(size_t n) const + { + static if (minSize == 0) return false; + else return n < min; + } + + private bool tooLarge(size_t n) const + { + static if (maxSize == unbounded) return false; + else return n > max; + } + + private bool freeListEligible(size_t n) const + { + static if (unchecked) + { + return true; + } + else + { + static if (minSize == 0) + { + if (!n) return false; + } + static if (minSize == maxSize && minSize != chooseAtRuntime) + return n == maxSize; + else + return !tooSmall(n) && !tooLarge(n); + } + } + + static if (!unchecked) + private void[] blockFor(Node* p) + { + assert(p); + return (cast(void*) p)[0 .. max]; + } + + // statistics + static if (adaptive == Yes.adaptive) + { + private enum double windowLength = 1000.0; + private enum double tooFewMisses = 0.01; + private double probMiss = 1.0; // start with a high miss probability + private uint accumSamples, accumMisses; + + void updateStats() + { + assert(accumSamples >= accumMisses); + /* + Given that for the past windowLength samples we saw misses with + estimated probability probMiss, and assuming the new sample wasMiss or + not, what's the new estimated probMiss? + */ + probMiss = (probMiss * windowLength + accumMisses) + / (windowLength + accumSamples); + assert(probMiss <= 1.0); + accumSamples = 0; + accumMisses = 0; + // If probability to miss is under x%, yank one off the freelist + static if (!unchecked) + { + if (probMiss < tooFewMisses && _root) + { + auto b = blockFor(_root); + _root = _root.next; + parent.deallocate(b); + } + } + } + } + + private struct Node { Node* next; } + static assert(ParentAllocator.alignment >= Node.alignof); + + // state + /** + The parent allocator. Depending on whether $(D ParentAllocator) holds state + or not, this is a member variable or an alias for + `ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) ParentAllocator parent; + else alias parent = ParentAllocator.instance; + private Node* root; + static if (minSize == chooseAtRuntime) private size_t _min = chooseAtRuntime; + static if (maxSize == chooseAtRuntime) private size_t _max = chooseAtRuntime; + + /** + Alignment offered. + */ + alias alignment = ParentAllocator.alignment; + + /** + If $(D maxSize == unbounded), returns $(D parent.goodAllocSize(bytes)). + Otherwise, returns $(D max) for sizes in the interval $(D [min, max]), and + $(D parent.goodAllocSize(bytes)) otherwise. + + Precondition: + If set at runtime, $(D min) and/or $(D max) must be initialized + appropriately. + + Postcondition: + $(D result >= bytes) + */ + size_t goodAllocSize(size_t bytes) + { + assert(minSize != chooseAtRuntime && maxSize != chooseAtRuntime); + static if (maxSize != unbounded) + { + if (freeListEligible(bytes)) + { + assert(parent.goodAllocSize(max) == max, + text("Wrongly configured freelist: maximum should be ", + parent.goodAllocSize(max), " instead of ", max)); + return max; + } + } + return parent.goodAllocSize(bytes); + } + + private void[] allocateEligible(size_t bytes) + { + assert(bytes); + if (root) + { + // faster + auto result = (cast(ubyte*) root)[0 .. bytes]; + root = root.next; + return result; + } + // slower + static if (hasTolerance) + { + immutable toAllocate = max; + } + else + { + alias toAllocate = bytes; + } + assert(toAllocate == max || max == unbounded); + auto result = parent.allocate(toAllocate); + static if (hasTolerance) + { + if (result) result = result.ptr[0 .. bytes]; + } + static if (adaptive == Yes.adaptive) + { + ++accumMisses; + updateStats; + } + return result; + } + + /** + Allocates memory either off of the free list or from the parent allocator. + If $(D n) is within $(D [min, max]) or if the free list is unchecked + ($(D minSize == 0 && maxSize == size_t.max)), then the free list is + consulted first. If not empty (hit), the block at the front of the free + list is removed from the list and returned. Otherwise (miss), a new block + of $(D max) bytes is allocated, truncated to $(D n) bytes, and returned. + + Params: + n = number of bytes to allocate + + Returns: + The allocated block, or $(D null). + + Precondition: + If set at runtime, $(D min) and/or $(D max) must be initialized + appropriately. + + Postcondition: $(D result.length == bytes || result is null) + */ + void[] allocate(size_t n) + { + static if (adaptive == Yes.adaptive) ++accumSamples; + assert(n < size_t.max / 2); + // fast path + if (freeListEligible(n)) + { + return allocateEligible(n); + } + // slower + static if (adaptive == Yes.adaptive) + { + updateStats; + } + return parent.allocate(n); + } + + // Forwarding methods + mixin(forwardToMember("parent", + "expand", "owns", "reallocate")); + + /** + If $(D block.length) is within $(D [min, max]) or if the free list is + unchecked ($(D minSize == 0 && maxSize == size_t.max)), then inserts the + block at the front of the free list. For all others, forwards to $(D + parent.deallocate) if $(D Parent.deallocate) is defined. + + Params: + block = Block to deallocate. + + Precondition: + If set at runtime, $(D min) and/or $(D max) must be initialized + appropriately. The block must have been allocated with this + freelist, and no dynamic changing of $(D min) or $(D max) is allowed to + occur between allocation and deallocation. + */ + bool deallocate(void[] block) + { + if (freeListEligible(block.length)) + { + if (min == 0) + { + // In this case a null pointer might have made it this far. + if (block is null) return true; + } + auto t = root; + root = cast(Node*) block.ptr; + root.next = t; + return true; + } + static if (hasMember!(ParentAllocator, "deallocate")) + return parent.deallocate(block); + else + return false; + } + + /** + Defined only if $(D ParentAllocator) defines $(D deallocateAll). If so, + forwards to it and resets the freelist. + */ + static if (hasMember!(ParentAllocator, "deallocateAll")) + bool deallocateAll() + { + root = null; + return parent.deallocateAll(); + } + + /** + Nonstandard function that minimizes the memory usage of the freelist by + freeing each element in turn. Defined only if $(D ParentAllocator) defines + $(D deallocate). + */ + static if (hasMember!(ParentAllocator, "deallocate") && !unchecked) + void minimize() + { + while (root) + { + auto nuke = blockFor(root); + root = root.next; + parent.deallocate(nuke); + } + } +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + FreeList!(GCAllocator, 0, 8) fl; + assert(fl.root is null); + auto b1 = fl.allocate(7); + fl.allocate(8); + assert(fl.root is null); + fl.deallocate(b1); + assert(fl.root !is null); + fl.allocate(8); + assert(fl.root is null); +} + +/** +Free list built on top of exactly one contiguous block of memory. The block is +assumed to have been allocated with $(D ParentAllocator), and is released in +$(D ContiguousFreeList)'s destructor (unless $(D ParentAllocator) is $(D +NullAllocator)). + +$(D ContiguousFreeList) has most advantages of $(D FreeList) but fewer +disadvantages. It has better cache locality because items are closer to one +another. It imposes less fragmentation on its parent allocator. + +The disadvantages of $(D ContiguousFreeList) over $(D FreeList) are its pay +upfront model (as opposed to $(D FreeList)'s pay-as-you-go approach), and a +hard limit on the number of nodes in the list. Thus, a large number of long- +lived objects may occupy the entire block, making it unavailable for serving +allocations from the free list. However, an absolute cap on the free list size +may be beneficial. + +The options $(D minSize == unbounded) and $(D maxSize == unbounded) are not +available for $(D ContiguousFreeList). +*/ +struct ContiguousFreeList(ParentAllocator, + size_t minSize, size_t maxSize = minSize) +{ + import std.experimental.allocator.building_blocks.null_allocator + : NullAllocator; + import std.experimental.allocator.building_blocks.stats_collector + : StatsCollector, Options; + import std.traits : hasMember; + import std.typecons : Ternary; + + alias Impl = FreeList!(NullAllocator, minSize, maxSize); + enum unchecked = minSize == 0 && maxSize == unbounded; + alias Node = Impl.Node; + + alias SParent = StatsCollector!(ParentAllocator, Options.bytesUsed); + + // state + /** + The parent allocator. Depending on whether $(D ParentAllocator) holds state + or not, this is a member variable or an alias for + `ParentAllocator.instance`. + */ + SParent parent; + FreeList!(NullAllocator, minSize, maxSize) fl; + void[] support; + size_t allocated; + + /// Alignment offered. + enum uint alignment = (void*).alignof; + + private void initialize(ubyte[] buffer, size_t itemSize = fl.max) + { + assert(itemSize != unbounded && itemSize != chooseAtRuntime); + assert(buffer.ptr.alignedAt(alignment)); + immutable available = buffer.length / itemSize; + if (available == 0) return; + support = buffer; + fl.root = cast(Node*) buffer.ptr; + auto past = cast(Node*) (buffer.ptr + available * itemSize); + for (auto n = fl.root; ; ) + { + auto next = cast(Node*) (cast(ubyte*) n + itemSize); + if (next == past) + { + n.next = null; + break; + } + assert(next < past); + assert(n < next); + n.next = next; + n = next; + } + } + + /** + Constructors setting up the memory structured as a free list. + + Params: + buffer = Buffer to structure as a free list. If $(D ParentAllocator) is not + $(D NullAllocator), the buffer is assumed to be allocated by $(D parent) + and will be freed in the destructor. + parent = Parent allocator. For construction from stateless allocators, use + their `instance` static member. + bytes = Bytes (not items) to be allocated for the free list. Memory will be + allocated during construction and deallocated in the destructor. + max = Maximum size eligible for freelisting. Construction with this + parameter is defined only if $(D maxSize == chooseAtRuntime) or $(D maxSize + == unbounded). + min = Minimum size eligible for freelisting. Construction with this + parameter is defined only if $(D minSize == chooseAtRuntime). If this + condition is met and no $(D min) parameter is present, $(D min) is + initialized with $(D max). + */ + static if (!stateSize!ParentAllocator) + this(ubyte[] buffer) + { + initialize(buffer); + } + + /// ditto + static if (stateSize!ParentAllocator) + this(ParentAllocator parent, ubyte[] buffer) + { + initialize(buffer); + this.parent = SParent(parent); + } + + /// ditto + static if (!stateSize!ParentAllocator) + this(size_t bytes) + { + initialize(cast(ubyte[])(ParentAllocator.instance.allocate(bytes))); + } + + /// ditto + static if (stateSize!ParentAllocator) + this(ParentAllocator parent, size_t bytes) + { + initialize(cast(ubyte[])(parent.allocate(bytes))); + this.parent = SParent(parent); + } + + /// ditto + static if (!stateSize!ParentAllocator + && (maxSize == chooseAtRuntime || maxSize == unbounded)) + this(size_t bytes, size_t max) + { + static if (maxSize == chooseAtRuntime) fl.max = max; + static if (minSize == chooseAtRuntime) fl.min = max; + initialize(cast(ubyte[])(parent.allocate(bytes)), max); + } + + /// ditto + static if (stateSize!ParentAllocator + && (maxSize == chooseAtRuntime || maxSize == unbounded)) + this(ParentAllocator parent, size_t bytes, size_t max) + { + static if (maxSize == chooseAtRuntime) fl.max = max; + static if (minSize == chooseAtRuntime) fl.min = max; + initialize(cast(ubyte[])(parent.allocate(bytes)), max); + this.parent = SParent(parent); + } + + /// ditto + static if (!stateSize!ParentAllocator + && (maxSize == chooseAtRuntime || maxSize == unbounded) + && minSize == chooseAtRuntime) + this(size_t bytes, size_t min, size_t max) + { + static if (maxSize == chooseAtRuntime) fl.max = max; + fl.min = min; + initialize(cast(ubyte[])(parent.allocate(bytes)), max); + static if (stateSize!ParentAllocator) + this.parent = SParent(parent); + } + + /// ditto + static if (stateSize!ParentAllocator + && (maxSize == chooseAtRuntime || maxSize == unbounded) + && minSize == chooseAtRuntime) + this(ParentAllocator parent, size_t bytes, size_t min, size_t max) + { + static if (maxSize == chooseAtRuntime) fl.max = max; + fl.min = min; + initialize(cast(ubyte[])(parent.allocate(bytes)), max); + static if (stateSize!ParentAllocator) + this.parent = SParent(parent); + } + + /** + If $(D n) is eligible for freelisting, returns $(D max). Otherwise, returns + $(D parent.goodAllocSize(n)). + + Precondition: + If set at runtime, $(D min) and/or $(D max) must be initialized + appropriately. + + Postcondition: + $(D result >= bytes) + */ + size_t goodAllocSize(size_t n) + { + if (fl.freeListEligible(n)) return fl.max; + return parent.goodAllocSize(n); + } + + /** + Allocate $(D n) bytes of memory. If $(D n) is eligible for freelist and the + freelist is not empty, pops the memory off the free list. In all other + cases, uses the parent allocator. + */ + void[] allocate(size_t n) + { + auto result = fl.allocate(n); + if (result) + { + // Only case we care about: eligible sizes allocated from us + ++allocated; + return result; + } + // All others, allocate from parent + return parent.allocate(n); + } + + /** + Defined if `ParentAllocator` defines it. Checks whether the block + belongs to this allocator. + */ + static if (hasMember!(SParent, "owns") || unchecked) + Ternary owns(void[] b) + { + if (support.ptr <= b.ptr && b.ptr < support.ptr + support.length) + return Ternary.yes; + static if (unchecked) + return Ternary.no; + else + return parent.owns(b); + } + + /** + Deallocates $(D b). If it's of eligible size, it's put on the free list. + Otherwise, it's returned to $(D parent). + + Precondition: $(D b) has been allocated with this allocator, or is $(D + null). + */ + bool deallocate(void[] b) + { + if (support.ptr <= b.ptr && b.ptr < support.ptr + support.length) + { + // we own this guy + import std.conv : text; + assert(fl.freeListEligible(b.length), text(b.length)); + assert(allocated); + --allocated; + // Put manually in the freelist + auto t = fl.root; + fl.root = cast(Node*) b.ptr; + fl.root.next = t; + return true; + } + return parent.deallocate(b); + } + + /** + Deallocates everything from the parent. + */ + static if (hasMember!(ParentAllocator, "deallocateAll") + && stateSize!ParentAllocator) + bool deallocateAll() + { + bool result = fl.deallocateAll && parent.deallocateAll; + allocated = 0; + return result; + } + + /** + Returns `Ternary.yes` if no memory is currently allocated with this + allocator, `Ternary.no` otherwise. This method never returns + `Ternary.unknown`. + */ + Ternary empty() + { + return Ternary(allocated == 0 && parent.bytesUsed == 0); + } +} + +/// +@safe unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + + import std.experimental.allocator.common : unbounded; + + alias ScalableFreeList = AllocatorList!((n) => + ContiguousFreeList!(GCAllocator, 0, unbounded)(4096) + ); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks.null_allocator + : NullAllocator; + import std.typecons : Ternary; + alias A = ContiguousFreeList!(NullAllocator, 0, 64); + auto a = A(new ubyte[1024]); + + assert(a.empty == Ternary.yes); + + assert(a.goodAllocSize(15) == 64); + assert(a.goodAllocSize(65) == NullAllocator.instance.goodAllocSize(65)); + + auto b = a.allocate(100); + assert(a.empty == Ternary.yes); + assert(b.length == 0); + a.deallocate(b); + b = a.allocate(64); + assert(a.empty == Ternary.no); + assert(b.length == 64); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + a.deallocate(b); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + alias A = ContiguousFreeList!(Region!GCAllocator, 0, 64); + auto a = A(Region!GCAllocator(1024 * 4), 1024); + + assert(a.empty == Ternary.yes); + + assert(a.goodAllocSize(15) == 64); + assert(a.goodAllocSize(65) == a.parent.goodAllocSize(65)); + + auto b = a.allocate(100); + assert(a.empty == Ternary.no); + assert(a.allocated == 0); + assert(b.length == 100); + a.deallocate(b); + assert(a.empty == Ternary.yes); + b = a.allocate(64); + assert(a.empty == Ternary.no); + assert(b.length == 64); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + a.deallocate(b); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + alias A = ContiguousFreeList!(GCAllocator, 64, 64); + auto a = A(1024); + const b = a.allocate(100); + assert(b.length == 100); +} + +/** +FreeList shared across threads. Allocation and deallocation are lock-free. The +parameters have the same semantics as for $(D FreeList). + +$(D expand) is defined to forward to $(ParentAllocator.expand) (it must be also +$(D shared)). +*/ +struct SharedFreeList(ParentAllocator, + size_t minSize, size_t maxSize = minSize, size_t approxMaxNodes = unbounded) +{ + import std.conv : text; + import std.exception : enforce; + import std.traits : hasMember; + + static assert(approxMaxNodes, "approxMaxNodes must not be null."); + static assert(minSize != unbounded, "Use minSize = 0 for no low bound."); + static assert(maxSize >= (void*).sizeof, + "Maximum size must accommodate a pointer."); + + import core.atomic : atomicOp, cas; + import core.internal.spinlock : SpinLock; + + static if (minSize != chooseAtRuntime) + { + alias min = minSize; + } + else + { + private shared size_t _min = chooseAtRuntime; + @property size_t min() const shared + { + assert(_min != chooseAtRuntime); + return _min; + } + @property void min(size_t x) shared + { + enforce(x <= max); + enforce(cas(&_min, chooseAtRuntime, x), + "SharedFreeList.min must be initialized exactly once."); + } + static if (maxSize == chooseAtRuntime) + { + // Both bounds can be set, provide one function for setting both in + // one shot. + void setBounds(size_t low, size_t high) shared + { + enforce(low <= high && high >= (void*).sizeof); + enforce(cas(&_min, chooseAtRuntime, low), + "SharedFreeList.min must be initialized exactly once."); + enforce(cas(&_max, chooseAtRuntime, high), + "SharedFreeList.max must be initialized exactly once."); + } + } + } + + private bool tooSmall(size_t n) const shared + { + static if (minSize == 0) return false; + else static if (minSize == chooseAtRuntime) return n < _min; + else return n < minSize; + } + + static if (maxSize != chooseAtRuntime) + { + alias max = maxSize; + } + else + { + private shared size_t _max = chooseAtRuntime; + @property size_t max() const shared { return _max; } + @property void max(size_t x) shared + { + enforce(x >= _min && x >= (void*).sizeof); + enforce(cas(&_max, chooseAtRuntime, x), + "SharedFreeList.max must be initialized exactly once."); + } + } + + private bool tooLarge(size_t n) const shared + { + static if (maxSize == unbounded) return false; + else static if (maxSize == chooseAtRuntime) return n > _max; + else return n > maxSize; + } + + private bool freeListEligible(size_t n) const shared + { + static if (minSize == maxSize && minSize != chooseAtRuntime) + return n == maxSize; + else return !tooSmall(n) && !tooLarge(n); + } + + static if (approxMaxNodes != chooseAtRuntime) + { + alias approxMaxLength = approxMaxNodes; + } + else + { + private shared size_t _approxMaxLength = chooseAtRuntime; + @property size_t approxMaxLength() const shared { return _approxMaxLength; } + @property void approxMaxLength(size_t x) shared { _approxMaxLength = enforce(x); } + } + + static if (approxMaxNodes != unbounded) + { + private shared size_t nodes; + private void incNodes() shared + { + atomicOp!("+=")(nodes, 1); + } + private void decNodes() shared + { + assert(nodes); + atomicOp!("-=")(nodes, 1); + } + private void resetNodes() shared + { + nodes = 0; + } + private bool nodesFull() shared + { + return nodes >= approxMaxLength; + } + } + else + { + private static void incNodes() { } + private static void decNodes() { } + private static void resetNodes() { } + private enum bool nodesFull = false; + } + + version (StdDdoc) + { + /** + Properties for getting (and possibly setting) the bounds. Setting bounds + is allowed only once , and before any allocation takes place. Otherwise, + the primitives have the same semantics as those of $(D FreeList). + */ + @property size_t min(); + /// Ditto + @property void min(size_t newMinSize); + /// Ditto + @property size_t max(); + /// Ditto + @property void max(size_t newMaxSize); + /// Ditto + void setBounds(size_t newMin, size_t newMax); + /// + @safe unittest + { + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.common : chooseAtRuntime; + + shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; + // Set the maxSize first so setting the minSize doesn't throw + a.max = 128; + a.min = 64; + a.setBounds(64, 128); // equivalent + assert(a.max == 128); + assert(a.min == 64); + } + + /** + Properties for getting (and possibly setting) the approximate maximum length of a shared freelist. + */ + @property size_t approxMaxLength() const shared; + /// ditto + @property void approxMaxLength(size_t x) shared; + /// + @safe unittest + { + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.common : chooseAtRuntime; + + shared SharedFreeList!(Mallocator, 50, 50, chooseAtRuntime) a; + // Set the maxSize first so setting the minSize doesn't throw + a.approxMaxLength = 128; + assert(a.approxMaxLength == 128); + a.approxMaxLength = 1024; + assert(a.approxMaxLength == 1024); + a.approxMaxLength = 1; + assert(a.approxMaxLength == 1); + } + } + + /** + The parent allocator. Depending on whether $(D ParentAllocator) holds state + or not, this is a member variable or an alias for + `ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) shared ParentAllocator parent; + else alias parent = ParentAllocator.instance; + + mixin(forwardToMember("parent", "expand")); + + private SpinLock lock; + + private struct Node { Node* next; } + static assert(ParentAllocator.alignment >= Node.alignof); + private Node* _root; + + /// Standard primitives. + enum uint alignment = ParentAllocator.alignment; + + /// Ditto + size_t goodAllocSize(size_t bytes) shared + { + if (freeListEligible(bytes)) return maxSize == unbounded ? bytes : max; + return parent.goodAllocSize(bytes); + } + + /// Ditto + static if (hasMember!(ParentAllocator, "owns")) + Ternary owns(void[] b) shared const + { + return parent.owns(b); + } + + /// Ditto + static if (hasMember!(ParentAllocator, "reallocate")) + bool reallocate(ref void[] b, size_t s) shared + { + return parent.reallocate(b, s); + } + + /// Ditto + void[] allocate(size_t bytes) shared + { + assert(bytes < size_t.max / 2); + if (!freeListEligible(bytes)) return parent.allocate(bytes); + if (maxSize != unbounded) bytes = max; + + // Try to pop off the freelist + lock.lock(); + if (!_root) + { + lock.unlock(); + return allocateFresh(bytes); + } + else + { + auto oldRoot = _root; + _root = _root.next; + decNodes(); + lock.unlock(); + return (cast(ubyte*) oldRoot)[0 .. bytes]; + } + } + + private void[] allocateFresh(const size_t bytes) shared + { + assert(bytes == max || max == unbounded); + return parent.allocate(bytes); + } + + /// Ditto + bool deallocate(void[] b) shared + { + if (!nodesFull && freeListEligible(b.length)) + { + auto newRoot = cast(shared Node*) b.ptr; + lock.lock(); + newRoot.next = _root; + _root = newRoot; + incNodes(); + lock.unlock(); + return true; + } + static if (hasMember!(ParentAllocator, "deallocate")) + return parent.deallocate(b); + else + return false; + } + + /// Ditto + bool deallocateAll() shared + { + bool result = false; + lock.lock(); + scope(exit) lock.unlock(); + static if (hasMember!(ParentAllocator, "deallocateAll")) + { + result = parent.deallocateAll(); + } + else static if (hasMember!(ParentAllocator, "deallocate")) + { + result = true; + for (auto n = _root; n;) + { + auto tmp = n.next; + if (!parent.deallocate((cast(ubyte*) n)[0 .. max])) + result = false; + n = tmp; + } + } + _root = null; + resetNodes(); + return result; + } +} + +@system unittest +{ + import std.algorithm.comparison : equal; + import std.range : repeat; + import std.experimental.allocator.mallocator : Mallocator; + import core.thread : ThreadGroup; + + static shared SharedFreeList!(Mallocator, 64, 128, 10) a; + + assert(a.goodAllocSize(1) == platformAlignment); + + auto b = a.allocate(96); + a.deallocate(b); + + void fun() + { + auto b = cast(size_t[]) a.allocate(96); + b[] = cast(size_t) &b; + + assert(b.equal(repeat(cast(size_t) &b, b.length))); + a.deallocate(b); + } + + auto tg = new ThreadGroup; + foreach (i; 0 .. 20) + { + tg.create(&fun); + } + + tg.joinAll(); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + static shared SharedFreeList!(Mallocator, 64, 128, 10) a; + auto b = a.allocate(100); + a.deallocate(b); + assert(a.nodes == 1); + b = []; + a.deallocateAll(); + assert(a.nodes == 0); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; + auto c = a.allocate(64); + assert(a.reallocate(c, 96)); + assert(c.length == 96); + a.deallocate(c); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime, chooseAtRuntime) a; + a.allocate(64); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + shared SharedFreeList!(Mallocator, 30, 40) a; + a.allocate(64); +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + shared SharedFreeList!(Mallocator, 30, 40, chooseAtRuntime) a; + a.allocate(64); +} diff --git a/std/experimental/allocator/building_blocks/free_tree.d b/std/experimental/allocator/building_blocks/free_tree.d new file mode 100644 index 00000000000..6b64659297e --- /dev/null +++ b/std/experimental/allocator/building_blocks/free_tree.d @@ -0,0 +1,487 @@ +/// +module std.experimental.allocator.building_blocks.free_tree; + +import std.experimental.allocator.common; + +//debug = std_experimental_allocator_free_tree; + +/** + +The Free Tree allocator, stackable on top of any other allocator, bears +similarity with the free list allocator. Instead of a singly-linked list of +previously freed blocks, it maintains a binary search tree. This allows the +Free Tree allocator to manage blocks of arbitrary lengths and search them +efficiently. + +Common uses of $(D FreeTree) include: + +$(UL +$(LI Adding $(D deallocate) capability to an allocator that lacks it (such as simple regions).) +$(LI Getting the benefits of multiple adaptable freelists that do not need to +be tuned for one specific size but insted automatically adapts itself to +frequently used sizes.) +) + +The free tree has special handling of duplicates (a singly-linked list per +node) in anticipation of large number of duplicates. Allocation time from the +free tree is expected to be $(BIGOH log n) where $(D n) is the number of +distinct sizes (not total nodes) kept in the free tree. + +Allocation requests first search the tree for a buffer of suitable size +deallocated in the past. If a match is found, the node is removed from the tree +and the memory is returned. Otherwise, the allocation is directed to $(D +ParentAllocator). If at this point $(D ParentAllocator) also fails to allocate, +$(D FreeTree) frees everything and then tries the parent allocator again. + +Upon deallocation, the deallocated block is inserted in the internally +maintained free tree (not returned to the parent). The free tree is not kept +balanced. Instead, it has a last-in-first-out flavor because newly inserted +blocks are rotated to the root of the tree. That way allocations are cache +friendly and also frequently used sizes are more likely to be found quickly, +whereas seldom used sizes migrate to the leaves of the tree. + +$(D FreeTree) rounds up small allocations to at least $(D 4 * size_t.sizeof), +which on 64-bit system is one cache line size. If very small objects need to +be efficiently allocated, the $(D FreeTree) should be fronted with an +appropriate small object allocator. + +The following methods are defined if $(D ParentAllocator) defines them, and forward to it: $(D allocateAll), $(D expand), $(D owns), $(D reallocate). +*/ +struct FreeTree(ParentAllocator) +{ + static assert(ParentAllocator.alignment % size_t.alignof == 0, + "FreeTree must be on top of a word-aligned allocator"); + + import std.algorithm.comparison : min, max; + import std.algorithm.mutation : swap; + import std.traits : hasMember; + + // State + static if (stateSize!ParentAllocator) private ParentAllocator parent; + else private alias parent = ParentAllocator.instance; + private Node* root; // that's the entire added state + + private struct Node + { + Node*[2] kid; + Node* sibling; + size_t size; + ref Node* left() { return kid[0]; } + ref Node* right() { return kid[1]; } + } + + // Removes "which" from the tree, returns the memory it occupied + private void[] remove(ref Node* which) + { + assert(which); + assert(!which.sibling); + auto result = (cast(ubyte*) which)[0 .. which.size]; + if (!which.right) which = which.left; + else if (!which.left) which = which.right; + else + { + // result has two kids + static bool toggler; + // Crude randomization: alternate left/right choices + toggler = !toggler; + auto newRoot = which.kid[toggler], orphan = which.kid[!toggler]; + which = newRoot; + for (Node* n = void; (n = newRoot.kid[!toggler]) !is null; ) + { + newRoot = n; + } + newRoot.kid[!toggler] = orphan; + } + return result; + } + + private void[] findAndRemove(ref Node* n, size_t s) + { + if (!n) return null; + if (s == n.size) + { + if (auto sis = n.sibling) + { + // Nice, give away one from the freelist + auto result = (cast(ubyte*) sis)[0 .. sis.size]; + n.sibling = sis.sibling; + return result; + } + return remove(n); + } + return findAndRemove(n.kid[s > n.size], s); + } + + debug(std_experimental_allocator_free_tree) + private void dump() + { + import std.stdio : writef, writefln, writeln; + writeln(typeof(this).stringof, "@", &this, " {"); + scope(exit) writeln("}"); + + if (!root) return; + + static void recurse(Node* n, uint indent = 4) + { + if (!n) + { + writefln("%*s(null)", indent, ""); + return; + } + for (auto sis = n; sis; sis = sis.sibling) + { + writef("%*s%x (%s bytes) ", indent, "", + cast(void*) n, n.size); + } + writeln; + if (!n.left && !n.right) return; + recurse(n.left, indent + 4); + recurse(n.right, indent + 4); + } + recurse(root); + } + + private string formatSizes() + { + string result = "("; + void recurse(Node* n) + { + if (!n) + { + result ~= "_"; + return; + } + import std.conv : to; + result ~= to!string(n.size); + for (auto sis = n.sibling; sis; sis = sis.sibling) + { + result ~= "+moar"; + } + if (n.left || n.right) + { + result ~= " ("; + recurse(n.left); + result ~= ' '; + recurse(n.right); + result ~= ")"; + } + } + recurse(root); + return result ~= ")"; + } + + private static void rotate(ref Node* parent, bool toRight) + { + assert(parent); + auto opposing = parent.kid[!toRight]; + if (!opposing) return; + parent.kid[!toRight] = opposing.kid[toRight]; + opposing.kid[toRight] = parent; + parent = opposing; + } + + // Inserts which into the tree, making it the new root + private void insertAsRoot(Node* which) + { + assert(which); + debug(std_experimental_allocator_free_tree) + { + assertValid; + scope(exit) assertValid; + } + + static void recurse(ref Node* where, Node* which) + { + if (!where) + { + where = which; + which.left = null; + which.right = null; + which.sibling = null; + return; + } + if (which.size == where.size) + { + // Special handling of duplicates + which.sibling = where.sibling; + where.sibling = which; + which.left = null; + which.right = null; + return; + } + bool goRight = which.size > where.size; + recurse(where.kid[goRight], which); + rotate(where, !goRight); + } + recurse(root, which); + } + + private void assertValid() + { + debug(std_experimental_allocator_free_tree) + { + static bool isBST(Node* n, size_t lb = 0, size_t ub = size_t.max) + { + if (!n) return true; + for (auto sis = n.sibling; sis; sis = sis.sibling) + { + assert(n.size == sis.size); + assert(sis.left is null); + assert(sis.right is null); + } + return lb < n.size && n.size <= ub + && isBST(n.left, lb, min(ub, n.size)) + && isBST(n.right, max(lb, n.size), ub); + } + if (isBST(root)) return; + dump; + assert(0); + } + } + + /** + The $(D FreeTree) is word aligned. + */ + enum uint alignment = size_t.alignof; + + /** + The $(D FreeTree) allocator is noncopyable. + */ + this(this) @disable; + + /** + The destructor of $(D FreeTree) releases all memory back to the parent + allocator. + */ + static if (hasMember!(ParentAllocator, "deallocate")) + ~this() + { + clear; + } + + /** + Returns $(D parent.goodAllocSize(max(Node.sizeof, s))). + */ + static if (stateSize!ParentAllocator) + size_t goodAllocSize(size_t s) + { + return parent.goodAllocSize(max(Node.sizeof, s)); + } + else + static size_t goodAllocSize(size_t s) + { + return parent.goodAllocSize(max(Node.sizeof, s)); + } + + /** + + Allocates $(D n) bytes of memory. First consults the free tree, and returns + from it if a suitably sized block is found. Otherwise, the parent allocator + is tried. If allocation from the parent succeeds, the allocated block is + returned. Otherwise, the free tree tries an alternate strategy: If $(D + ParentAllocator) defines $(D deallocate), $(D FreeTree) releases all of its + contents and tries again. + + TODO: Splitting and coalescing should be implemented if $(D ParentAllocator) does not defined $(D deallocate). + + */ + void[] allocate(size_t n) + { + assertValid; + if (n == 0) return null; + + immutable s = goodAllocSize(n); + + // Consult the free tree. + auto result = findAndRemove(root, s); + if (result.ptr) return result.ptr[0 .. n]; + + // No block found, try the parent allocator. + result = parent.allocate(s); + if (result.ptr) return result.ptr[0 .. n]; + + // Parent ran out of juice, desperation mode on + static if (hasMember!(ParentAllocator, "deallocate")) + { + clear; + // Try parent allocator again. + result = parent.allocate(s); + if (result.ptr) return result.ptr[0 .. n]; + return null; + } + else + { + // TODO: get smart here + return null; + } + } + + // Forwarding methods + mixin(forwardToMember("parent", + "allocateAll", "expand", "owns", "reallocate")); + + /** Places $(D b) into the free tree. */ + bool deallocate(void[] b) + { + if (!b.ptr) return true; + auto which = cast(Node*) b.ptr; + which.size = goodAllocSize(b.length); + // deliberately don't initialize which.left and which.right + assert(which.size >= Node.sizeof); + insertAsRoot(which); + return true; + } + + @system unittest // test a few simple configurations + { + import std.experimental.allocator.gc_allocator; + FreeTree!GCAllocator a; + auto b1 = a.allocate(10000); + auto b2 = a.allocate(20000); + auto b3 = a.allocate(30000); + assert(b1.ptr && b2.ptr && b3.ptr); + a.deallocate(b1); + a.deallocate(b3); + a.deallocate(b2); + assert(a.formatSizes == "(20480 (12288 32768))", a.formatSizes); + + b1 = a.allocate(10000); + assert(a.formatSizes == "(20480 (_ 32768))", a.formatSizes); + b1 = a.allocate(30000); + assert(a.formatSizes == "(20480)", a.formatSizes); + b1 = a.allocate(20000); + assert(a.formatSizes == "(_)", a.formatSizes); + } + + @system unittest // build a complex free tree + { + import std.experimental.allocator.gc_allocator, std.range; + FreeTree!GCAllocator a; + uint[] sizes = [3008,704,1856,576,1632,672,832,1856,1120,2656,1216,672, + 448,992,2400,1376,2688,2656,736,1440]; + void[][] allocs; + foreach (s; sizes) + allocs ~= a.allocate(s); + foreach_reverse (b; allocs) + { + assert(b.ptr); + a.deallocate(b); + } + a.assertValid; + allocs = null; + foreach (s; sizes) + allocs ~= a.allocate(s); + assert(a.root is null); + a.assertValid; + } + + /** Defined if $(D ParentAllocator.deallocate) exists, and returns to it + all memory held in the free tree. */ + static if (hasMember!(ParentAllocator, "deallocate")) + void clear() + { + void recurse(Node* n) + { + if (!n) return; + recurse(n.left); + recurse(n.right); + parent.deallocate((cast(ubyte*) n)[0 .. n.size]); + } + recurse(root); + root = null; + } + + /** + + Defined if $(D ParentAllocator.deallocateAll) exists, and forwards to it. + Also nullifies the free tree (it's assumed the parent frees all memory + stil managed by the free tree). + + */ + static if (hasMember!(ParentAllocator, "deallocateAll")) + bool deallocateAll() + { + // This is easy, just nuke the root and deallocate all from the + // parent + root = null; + return parent.deallocateAll; + } +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator; + testAllocator!(() => FreeTree!GCAllocator()); +} + +@system unittest // issue 16506 +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + + static void f(ParentAllocator)(size_t sz) + { + static FreeTree!ParentAllocator myAlloc; + byte[] _payload = cast(byte[]) myAlloc.allocate(sz); + assert(_payload, "_payload is null"); + _payload[] = 0; + myAlloc.deallocate(_payload); + } + + f!Mallocator(33); + f!Mallocator(43); + f!GCAllocator(1); +} + +@system unittest // issue 16507 +{ + static struct MyAllocator + { + byte dummy; + static bool alive = true; + void[] allocate(size_t s) { return new byte[](s); } + bool deallocate(void[] ) { if (alive) assert(false); return true; } + enum alignment = size_t.sizeof; + } + + FreeTree!MyAllocator ft; + void[] x = ft.allocate(1); + ft.deallocate(x); + ft.allocate(1000); + MyAllocator.alive = false; +} + +@system unittest // "desperation mode" +{ + uint myDeallocCounter = 0; + + struct MyAllocator + { + byte[] allocation; + void[] allocate(size_t s) + { + if (allocation.ptr) return null; + allocation = new byte[](s); + return allocation; + } + bool deallocate(void[] ) + { + ++myDeallocCounter; + allocation = null; + return true; + } + enum alignment = size_t.sizeof; + } + + FreeTree!MyAllocator ft; + void[] x = ft.allocate(1); + ft.deallocate(x); + assert(myDeallocCounter == 0); + x = ft.allocate(1000); // Triggers "desperation mode". + assert(myDeallocCounter == 1); + assert(x.ptr); + void[] y = ft.allocate(1000); /* Triggers "desperation mode" but there's + nothing to deallocate so MyAllocator can't deliver. */ + assert(myDeallocCounter == 1); + assert(y.ptr is null); +} diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d new file mode 100644 index 00000000000..849892e8265 --- /dev/null +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -0,0 +1,882 @@ +/// +module std.experimental.allocator.building_blocks.kernighan_ritchie; +import std.experimental.allocator.building_blocks.null_allocator; + +//debug = KRRegion; +debug(KRRegion) import std.stdio; +version(unittest) import std.conv : text; + +// KRRegion +/** +$(D KRRegion) draws inspiration from the $(MREF_ALTTEXT region allocation +strategy, std,experimental,allocator,building_blocks,region) and also the +$(HTTP stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book, +famed allocator) described by Brian Kernighan and Dennis Ritchie in section 8.7 +of the book $(HTTP amazon.com/exec/obidos/ASIN/0131103628/classicempire, "The C +Programming Language"), Second Edition, Prentice Hall, 1988. + +$(H4 `KRRegion` = `Region` + Kernighan-Ritchie Allocator) + +Initially, `KRRegion` starts in "region" mode: allocations are served from +the memory chunk in a region fashion. Thus, as long as there is enough memory +left, $(D KRRegion.allocate) has the performance profile of a region allocator. +Deallocation inserts (in $(BIGOH 1) time) the deallocated blocks in an +unstructured freelist, which is not read in region mode. + +Once the region cannot serve an $(D allocate) request, $(D KRRegion) switches +to "free list" mode. It sorts the list of previously deallocated blocks by +address and serves allocation requests off that free list. The allocation and +deallocation follow the pattern described by Kernighan and Ritchie. + +The recommended use of `KRRegion` is as a $(I region with deallocation). If the +`KRRegion` is dimensioned appropriately, it could often not enter free list +mode during its lifetime. Thus it is as fast as a simple region, whilst +offering deallocation at a small cost. When the region memory is exhausted, +the previously deallocated memory is still usable, at a performance cost. If +the region is not excessively large and fragmented, the linear allocation and +deallocation cost may still be compensated for by the good locality +characteristics. + +If the chunk of memory managed is large, it may be desirable to switch +management to free list from the beginning. That way, memory may be used in a +more compact manner than region mode. To force free list mode, call $(D +switchToFreeList) shortly after construction or when deemed appropriate. + +The smallest size that can be allocated is two words (16 bytes on 64-bit +systems, 8 bytes on 32-bit systems). This is because the free list management +needs two words (one for the length, the other for the next pointer in the +singly-linked list). + +The $(D ParentAllocator) type parameter is the type of the allocator used to +allocate the memory chunk underlying the $(D KRRegion) object. Choosing the +default ($(D NullAllocator)) means the user is responsible for passing a buffer +at construction (and for deallocating it if necessary). Otherwise, $(D KRRegion) +automatically deallocates the buffer during destruction. For that reason, if +$(D ParentAllocator) is not $(D NullAllocator), then $(D KRRegion) is not +copyable. + +$(H4 Implementation Details) + +In free list mode, $(D KRRegion) embeds a free blocks list onto the chunk of +memory. The free list is circular, coalesced, and sorted by address at all +times. Allocations and deallocations take time proportional to the number of +previously deallocated blocks. (In practice the cost may be lower, e.g. if +memory is deallocated in reverse order of allocation, all operations take +constant time.) Memory utilization is good (small control structure and no +per-allocation overhead). The disadvantages of freelist mode include proneness +to fragmentation, a minimum allocation size of two words, and linear worst-case +allocation and deallocation times. + +Similarities of `KRRegion` (in free list mode) with the +Kernighan-Ritchie allocator: + +$(UL +$(LI Free blocks have variable size and are linked in a singly-linked list.) +$(LI The freelist is maintained in increasing address order, which makes +coalescing easy.) +$(LI The strategy for finding the next available block is first fit.) +$(LI The free list is circular, with the last node pointing back to the first.) +$(LI Coalescing is carried during deallocation.) +) + +Differences from the Kernighan-Ritchie allocator: + +$(UL +$(LI Once the chunk is exhausted, the Kernighan-Ritchie allocator allocates +another chunk using operating system primitives. For better composability, $(D +KRRegion) just gets full (returns $(D null) on new allocation requests). The +decision to allocate more blocks is deferred to a higher-level entity. For an +example, see the example below using $(D AllocatorList) in conjunction with $(D +KRRegion).) +$(LI Allocated blocks do not hold a size prefix. This is because in D the size +information is available in client code at deallocation time.) +) + +*/ +struct KRRegion(ParentAllocator = NullAllocator) +{ + import std.experimental.allocator.common : stateSize, alignedAt; + import std.traits : hasMember; + import std.typecons : Ternary; + + private static struct Node + { + import std.typecons : tuple, Tuple; + + Node* next; + size_t size; + + this(this) @disable; + + void[] payload() inout + { + return (cast(ubyte*) &this)[0 .. size]; + } + + bool adjacent(in Node* right) const + { + assert(right); + auto p = payload; + return p.ptr < right && right < p.ptr + p.length + Node.sizeof; + } + + bool coalesce(void* memoryEnd = null) + { + // Coalesce the last node before the memory end with any possible gap + if (memoryEnd + && memoryEnd < payload.ptr + payload.length + Node.sizeof) + { + size += memoryEnd - (payload.ptr + payload.length); + return true; + } + + if (!adjacent(next)) return false; + size = (cast(ubyte*) next + next.size) - cast(ubyte*) &this; + next = next.next; + return true; + } + + Tuple!(void[], Node*) allocateHere(size_t bytes) + { + assert(bytes >= Node.sizeof); + assert(bytes % Node.alignof == 0); + assert(next); + assert(!adjacent(next)); + if (size < bytes) return typeof(return)(); + assert(size >= bytes); + immutable leftover = size - bytes; + + if (leftover >= Node.sizeof) + { + // There's room for another node + auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes); + newNode.size = leftover; + newNode.next = next == &this ? newNode : next; + assert(next); + return tuple(payload, newNode); + } + + // No slack space, just return next node + return tuple(payload, next == &this ? null : next); + } + } + + // state + /** + If $(D ParentAllocator) holds state, $(D parent) is a public member of type + $(D KRRegion). Otherwise, $(D parent) is an $(D alias) for + `ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) ParentAllocator parent; + else alias parent = ParentAllocator.instance; + private void[] payload; + private Node* root; + private bool regionMode = true; + + auto byNodePtr() + { + static struct Range + { + Node* start, current; + @property bool empty() { return !current; } + @property Node* front() { return current; } + void popFront() + { + assert(current && current.next); + current = current.next; + if (current == start) current = null; + } + @property Range save() { return this; } + } + import std.range : isForwardRange; + static assert(isForwardRange!Range); + return Range(root, root); + } + + string toString() + { + import std.format : format; + string s = "KRRegion@"; + s ~= format("%s-%s(0x%s[%s] %s", &this, &this + 1, + payload.ptr, payload.length, + regionMode ? "(region)" : "(freelist)"); + + Node* lastNode = null; + if (!regionMode) + { + foreach (node; byNodePtr) + { + s ~= format(", %sfree(0x%s[%s])", + lastNode && lastNode.adjacent(node) ? "+" : "", + cast(void*) node, node.size); + lastNode = node; + } + } + else + { + for (auto node = root; node; node = node.next) + { + s ~= format(", %sfree(0x%s[%s])", + lastNode && lastNode.adjacent(node) ? "+" : "", + cast(void*) node, node.size); + lastNode = node; + } + } + + s ~= ')'; + return s; + } + + private void assertValid(string s) + { + assert(!regionMode); + if (!payload.ptr) + { + assert(!root, s); + return; + } + if (!root) + { + return; + } + assert(root >= payload.ptr, s); + assert(root < payload.ptr + payload.length, s); + + // Check that the list terminates + size_t n; + foreach (node; byNodePtr) + { + assert(node.next); + assert(!node.adjacent(node.next)); + assert(n++ < payload.length / Node.sizeof, s); + } + } + + private Node* sortFreelist(Node* root) + { + // Find a monotonic run + auto last = root; + for (;;) + { + if (!last.next) return root; + if (last > last.next) break; + assert(last < last.next); + last = last.next; + } + auto tail = last.next; + last.next = null; + tail = sortFreelist(tail); + return merge(root, tail); + } + + private Node* merge(Node* left, Node* right) + { + assert(left != right); + if (!left) return right; + if (!right) return left; + if (left < right) + { + auto result = left; + result.next = merge(left.next, right); + return result; + } + auto result = right; + result.next = merge(left, right.next); + return result; + } + + private void coalesceAndMakeCircular() + { + for (auto n = root;;) + { + assert(!n.next || n < n.next); + if (!n.next) + { + // Convert to circular + n.next = root; + break; + } + if (n.coalesce) continue; // possibly another coalesce + n = n.next; + } + } + + /** + Create a $(D KRRegion). If $(D ParentAllocator) is not $(D NullAllocator), + $(D KRRegion)'s destructor will call $(D parent.deallocate). + + Params: + b = Block of memory to serve as support for the allocator. Memory must be + larger than two words and word-aligned. + n = Capacity desired. This constructor is defined only if $(D + ParentAllocator) is not $(D NullAllocator). + */ + this(ubyte[] b) + { + if (b.length < Node.sizeof) + { + // Init as empty + assert(root is null); + assert(payload is null); + return; + } + assert(b.length >= Node.sizeof); + assert(b.ptr.alignedAt(Node.alignof)); + assert(b.length >= 2 * Node.sizeof); + payload = b; + root = cast(Node*) b.ptr; + // Initialize the free list with all list + assert(regionMode); + root.next = null; + root.size = b.length; + debug(KRRegion) writefln("KRRegion@%s: init with %s[%s]", &this, + b.ptr, b.length); + } + + /// Ditto + static if (!is(ParentAllocator == NullAllocator)) + this(size_t n) + { + assert(n > Node.sizeof); + this(cast(ubyte[])(parent.allocate(n))); + } + + /// Ditto + static if (!is(ParentAllocator == NullAllocator) + && hasMember!(ParentAllocator, "deallocate")) + ~this() + { + parent.deallocate(payload); + } + + /** + Forces free list mode. If already in free list mode, does nothing. + Otherwise, sorts the free list accumulated so far and switches strategy for + future allocations to KR style. + */ + void switchToFreeList() + { + if (!regionMode) return; + regionMode = false; + if (!root) return; + root = sortFreelist(root); + coalesceAndMakeCircular; + } + + /* + Noncopyable + */ + @disable this(this); + + /** + Word-level alignment. + */ + enum alignment = Node.alignof; + + /** + Allocates $(D n) bytes. Allocation searches the list of available blocks + until a free block with $(D n) or more bytes is found (first fit strategy). + The block is split (if larger) and returned. + + Params: n = number of bytes to _allocate + + Returns: A word-aligned buffer of $(D n) bytes, or $(D null). + */ + void[] allocate(size_t n) + { + if (!n || !root) return null; + const actualBytes = goodAllocSize(n); + + // Try the region first + if (regionMode) + { + // Only look at the head of the freelist + if (root.size >= actualBytes) + { + // Enough room for allocation + void* result = root; + immutable balance = root.size - actualBytes; + if (balance >= Node.sizeof) + { + auto newRoot = cast(Node*) (result + actualBytes); + newRoot.next = root.next; + newRoot.size = balance; + root = newRoot; + } + else + { + root = null; + switchToFreeList; + } + return result[0 .. n]; + } + + // Not enough memory, switch to freelist mode and fall through + switchToFreeList; + } + + // Try to allocate from next after the iterating node + for (auto pnode = root;;) + { + assert(!pnode.adjacent(pnode.next)); + auto k = pnode.next.allocateHere(actualBytes); + if (k[0] !is null) + { + // awes + assert(k[0].length >= n); + if (root == pnode.next) root = k[1]; + pnode.next = k[1]; + return k[0][0 .. n]; + } + + pnode = pnode.next; + if (pnode == root) break; + } + return null; + } + + /** + Deallocates $(D b), which is assumed to have been previously allocated with + this allocator. Deallocation performs a linear search in the free list to + preserve its sorting order. It follows that blocks with higher addresses in + allocators with many free blocks are slower to deallocate. + + Params: b = block to be deallocated + */ + bool deallocate(void[] b) + { + debug(KRRegion) writefln("KRRegion@%s: deallocate(%s[%s])", &this, + b.ptr, b.length); + if (!b.ptr) return true; + assert(owns(b) == Ternary.yes); + assert(b.ptr.alignedAt(Node.alignof)); + + // Insert back in the freelist, keeping it sorted by address. Do not + // coalesce at this time. Instead, do it lazily during allocation. + auto n = cast(Node*) b.ptr; + n.size = goodAllocSize(b.length); + auto memoryEnd = payload.ptr + payload.length; + + if (regionMode) + { + assert(root); + // Insert right after root + n.next = root.next; + root.next = n; + return true; + } + + if (!root) + { + // What a sight for sore eyes + root = n; + root.next = root; + + // If the first block freed is the last one allocated, + // maybe there's a gap after it. + root.coalesce(memoryEnd); + return true; + } + + version(assert) foreach (test; byNodePtr) + { + assert(test != n); + } + // Linear search + auto pnode = root; + do + { + assert(pnode && pnode.next); + assert(pnode != n); + assert(pnode.next != n); + if (pnode < pnode.next) + { + if (pnode >= n || n >= pnode.next) continue; + // Insert in between pnode and pnode.next + n.next = pnode.next; + pnode.next = n; + n.coalesce; + pnode.coalesce; + root = pnode; + return true; + } + else if (pnode < n) + { + // Insert at the end of the list + // Add any possible gap at the end of n to the length of n + n.next = pnode.next; + pnode.next = n; + n.coalesce(memoryEnd); + pnode.coalesce; + root = pnode; + return true; + } + else if (n < pnode.next) + { + // Insert at the front of the list + n.next = pnode.next; + pnode.next = n; + n.coalesce; + root = n; + return true; + } + } + while ((pnode = pnode.next) != root); + assert(0, "Wrong parameter passed to deallocate"); + } + + /** + Allocates all memory available to this allocator. If the allocator is empty, + returns the entire available block of memory. Otherwise, it still performs + a best-effort allocation: if there is no fragmentation (e.g. $(D allocate) + has been used but not $(D deallocate)), allocates and returns the only + available block of memory. + + The operation takes time proportional to the number of adjacent free blocks + at the front of the free list. These blocks get coalesced, whether + $(D allocateAll) succeeds or fails due to fragmentation. + */ + void[] allocateAll() + { + if (regionMode) switchToFreeList; + if (root && root.next == root) + return allocate(root.size); + return null; + } + + /// + @system unittest + { + import std.experimental.allocator.gc_allocator : GCAllocator; + auto alloc = KRRegion!GCAllocator(1024 * 64); + const b1 = alloc.allocate(2048); + assert(b1.length == 2048); + const b2 = alloc.allocateAll; + assert(b2.length == 1024 * 62); + } + + /** + Deallocates all memory currently allocated, making the allocator ready for + other allocations. This is a $(BIGOH 1) operation. + */ + bool deallocateAll() + { + debug(KRRegion) assertValid("deallocateAll"); + debug(KRRegion) scope(exit) assertValid("deallocateAll"); + root = cast(Node*) payload.ptr; + // Initialize the free list with all list + if (root) + { + root.next = root; + root.size = payload.length; + } + return true; + } + + /** + Checks whether the allocator is responsible for the allocation of $(D b). + It does a simple $(BIGOH 1) range check. $(D b) should be a buffer either + allocated with $(D this) or obtained through other means. + */ + Ternary owns(void[] b) + { + debug(KRRegion) assertValid("owns"); + debug(KRRegion) scope(exit) assertValid("owns"); + return Ternary(b.ptr >= payload.ptr + && b.ptr < payload.ptr + payload.length); + } + + /** + Adjusts $(D n) to a size suitable for allocation (two words or larger, + word-aligned). + */ + static size_t goodAllocSize(size_t n) + { + import std.experimental.allocator.common : roundUpToMultipleOf; + return n <= Node.sizeof + ? Node.sizeof : n.roundUpToMultipleOf(alignment); + } + + /** + Returns: `Ternary.yes` if the allocator is empty, `Ternary.no` otherwise. + Never returns `Ternary.unknown`. + */ + Ternary empty() + { + return Ternary(root && root.size == payload.length); + } +} + +/** +$(D KRRegion) is preferable to $(D Region) as a front for a general-purpose +allocator if $(D deallocate) is needed, yet the actual deallocation traffic is +relatively low. The example below shows a $(D KRRegion) using stack storage +fronting the GC allocator. +*/ +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.fallback_allocator + : fallbackAllocator; + import std.typecons : Ternary; + // KRRegion fronting a general-purpose allocator + ubyte[1024 * 128] buf; + auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance); + auto b = alloc.allocate(100); + assert(b.length == 100); + assert(alloc.primary.owns(b) == Ternary.yes); +} + +/** +The code below defines a scalable allocator consisting of 1 MB (or larger) +blocks fetched from the garbage-collected heap. Each block is organized as a +KR-style heap. More blocks are allocated and freed on a need basis. + +This is the closest example to the allocator introduced in the K$(AMP)R book. +It should perform slightly better because instead of searching through one +large free list, it searches through several shorter lists in LRU order. Also, +it actually returns memory to the operating system when possible. +*/ +@system unittest +{ + import std.algorithm.comparison : max; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc; +} + +@system unittest +{ + import std.algorithm.comparison : max; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + /* + Create a scalable allocator consisting of 1 MB (or larger) blocks fetched + from the garbage-collected heap. Each block is organized as a KR-style + heap. More blocks are allocated and freed on a need basis. + */ + AllocatorList!(n => KRRegion!Mallocator(max(n * 16, 1024 * 1024)), + NullAllocator) alloc; + void[][50] array; + foreach (i; 0 .. array.length) + { + auto length = i * 10_000 + 1; + array[i] = alloc.allocate(length); + assert(array[i].ptr); + assert(array[i].length == length); + } + import std.random : randomShuffle; + randomShuffle(array[]); + foreach (i; 0 .. array.length) + { + assert(array[i].ptr); + assert(alloc.owns(array[i]) == Ternary.yes); + alloc.deallocate(array[i]); + } +} + +@system unittest +{ + import std.algorithm.comparison : max; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + /* + Create a scalable allocator consisting of 1 MB (or larger) blocks fetched + from the garbage-collected heap. Each block is organized as a KR-style + heap. More blocks are allocated and freed on a need basis. + */ + AllocatorList!((n) { + auto result = KRRegion!MmapAllocator(max(n * 2, 1024 * 1024)); + return result; + }) alloc; + void[][99] array; + foreach (i; 0 .. array.length) + { + auto length = i * 10_000 + 1; + array[i] = alloc.allocate(length); + assert(array[i].ptr); + foreach (j; 0 .. i) + { + assert(array[i].ptr != array[j].ptr); + } + assert(array[i].length == length); + } + import std.random : randomShuffle; + randomShuffle(array[]); + foreach (i; 0 .. array.length) + { + assert(alloc.owns(array[i]) == Ternary.yes); + alloc.deallocate(array[i]); + } +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + import std.algorithm.comparison : max; + import std.experimental.allocator.common : testAllocator; + testAllocator!(() => AllocatorList!( + n => KRRegion!GCAllocator(max(n * 16, 1024 * 1024)))()); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + + auto alloc = KRRegion!GCAllocator(1024 * 1024); + + void[][] array; + foreach (i; 1 .. 4) + { + array ~= alloc.allocate(i); + assert(array[$ - 1].length == i); + } + alloc.deallocate(array[1]); + alloc.deallocate(array[0]); + alloc.deallocate(array[2]); + assert(alloc.allocateAll().length == 1024 * 1024); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.typecons : Ternary; + auto alloc = KRRegion!()( + cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); + const store = alloc.allocate(KRRegion!().sizeof); + auto p = cast(KRRegion!()* ) store.ptr; + import std.conv : emplace; + import std.algorithm.mutation : move; + import core.stdc.string : memcpy; + + memcpy(p, &alloc, alloc.sizeof); + emplace(&alloc); + + void[][100] array; + foreach (i; 0 .. array.length) + { + auto length = 100 * i + 1; + array[i] = p.allocate(length); + assert(array[i].length == length, text(array[i].length)); + assert(p.owns(array[i]) == Ternary.yes); + } + import std.random : randomShuffle; + randomShuffle(array[]); + foreach (i; 0 .. array.length) + { + assert(p.owns(array[i]) == Ternary.yes); + p.deallocate(array[i]); + } + auto b = p.allocateAll(); + assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length)); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + auto alloc = KRRegion!()( + cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); + auto p = alloc.allocateAll(); + assert(p.length == 1024 * 1024); + alloc.deallocateAll(); + p = alloc.allocateAll(); + assert(p.length == 1024 * 1024); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks; + import std.random; + import std.typecons : Ternary; + + // Both sequences must work on either system + + // A sequence of allocs which generates the error described in issue 16564 + // that is a gap at the end of buf from the perspective of the allocator + + // for 64 bit systems (leftover balance = 8 bytes < 16) + int[] sizes64 = [18904, 2008, 74904, 224, 111904, 1904, 52288, 8]; + + // for 32 bit systems (leftover balance < 8) + int[] sizes32 = [81412, 107068, 49892, 23768]; + + + void test(int[] sizes) + { + ubyte[256 * 1024] buf; + auto a = KRRegion!()(buf); + + void[][] bufs; + + foreach (size; sizes) + { + bufs ~= a.allocate(size); + } + + foreach (b; bufs.randomCover) + { + a.deallocate(b); + } + + assert(a.empty == Ternary.yes); + } + + test(sizes64); + test(sizes32); +} + +@system unittest +{ + import std.experimental.allocator.building_blocks; + import std.random; + import std.typecons : Ternary; + + // For 64 bits, we allocate in multiples of 8, but the minimum alloc size is 16. + // This can create gaps. + // This test is an example of such a case. The gap is formed between the block + // allocated for the second value in sizes and the third. There is also a gap + // at the very end. (total lost 2 * word) + + int[] sizes64 = [2008, 18904, 74904, 224, 111904, 1904, 52288, 8]; + int[] sizes32 = [81412, 107068, 49892, 23768]; + + int word64 = 8; + int word32 = 4; + + void test(int[] sizes, int word) + { + ubyte[256 * 1024] buf; + auto a = KRRegion!()(buf); + + void[][] bufs; + + foreach (size; sizes) + { + bufs ~= a.allocate(size); + } + + a.deallocate(bufs[1]); + bufs ~= a.allocate(sizes[1] - word); + + a.deallocate(bufs[0]); + foreach (i; 2 .. bufs.length) + { + a.deallocate(bufs[i]); + } + + assert(a.empty == Ternary.yes); + } + + test(sizes64, word64); + test(sizes32, word32); +} diff --git a/std/experimental/allocator/building_blocks/null_allocator.d b/std/experimental/allocator/building_blocks/null_allocator.d new file mode 100644 index 00000000000..68bab708a87 --- /dev/null +++ b/std/experimental/allocator/building_blocks/null_allocator.d @@ -0,0 +1,85 @@ +/// +module std.experimental.allocator.building_blocks.null_allocator; + +/** +$(D NullAllocator) is an emphatically empty implementation of the allocator +interface. Although it has no direct use, it is useful as a "terminator" in +composite allocators. +*/ +struct NullAllocator +{ + import std.typecons : Ternary; + /** + $(D NullAllocator) advertises a relatively large _alignment equal to 64 KB. + This is because $(D NullAllocator) never actually needs to honor this + alignment and because composite allocators using $(D NullAllocator) + shouldn't be unnecessarily constrained. + */ + enum uint alignment = 64 * 1024; + // /// Returns $(D n). + //size_t goodAllocSize(size_t n) shared const + //{ return .goodAllocSize(this, n); } + /// Always returns $(D null). + void[] allocate(size_t) shared { return null; } + /// Always returns $(D null). + void[] alignedAllocate(size_t, uint) shared { return null; } + /// Always returns $(D null). + void[] allocateAll() shared { return null; } + /** + These methods return $(D false). + Precondition: $(D b is null). This is because there is no other possible + legitimate input. + */ + bool expand(ref void[] b, size_t s) shared + { assert(b is null); return s == 0; } + /// Ditto + bool reallocate(ref void[] b, size_t) shared + { assert(b is null); return false; } + /// Ditto + bool alignedReallocate(ref void[] b, size_t, uint) shared + { assert(b is null); return false; } + /// Returns $(D Ternary.no). + Ternary owns(void[]) shared const { return Ternary.no; } + /** + Returns $(D Ternary.no). + */ + Ternary resolveInternalPointer(const void*, ref void[]) shared const + { return Ternary.no; } + /** + No-op. + Precondition: $(D b is null) + */ + bool deallocate(void[] b) shared { assert(b is null); return true; } + /** + No-op. + */ + bool deallocateAll() shared { return true; } + /** + Returns $(D Ternary.yes). + */ + Ternary empty() shared const { return Ternary.yes; } + /** + Returns the $(D shared) global instance of the $(D NullAllocator). + */ + static shared NullAllocator instance; +} + +@system unittest +{ + assert(NullAllocator.instance.alignedAllocate(100, 0) is null); + assert(NullAllocator.instance.allocateAll() is null); + auto b = NullAllocator.instance.allocate(100); + assert(b is null); + assert(NullAllocator.instance.expand(b, 0)); + assert(!NullAllocator.instance.expand(b, 42)); + assert(!NullAllocator.instance.reallocate(b, 42)); + assert(!NullAllocator.instance.alignedReallocate(b, 42, 0)); + NullAllocator.instance.deallocate(b); + NullAllocator.instance.deallocateAll(); + + import std.typecons : Ternary; + assert(NullAllocator.instance.empty() == Ternary.yes); + assert(NullAllocator.instance.owns(null) == Ternary.no); + void[] p; + assert(NullAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); +} diff --git a/std/experimental/allocator/building_blocks/package.d b/std/experimental/allocator/building_blocks/package.d new file mode 100644 index 00000000000..c92116befb5 --- /dev/null +++ b/std/experimental/allocator/building_blocks/package.d @@ -0,0 +1,313 @@ +/** +$(H2 Assembling Your Own Allocator) + +In addition to defining the interfaces above, this package also implements +untyped composable memory allocators. They are $(I untyped) because they deal +exclusively in $(D void[]) and have no notion of what type the memory allocated +would be destined for. They are $(I composable) because the included allocators +are building blocks that can be assembled in complex nontrivial allocators. + +$(P Unlike the allocators for the C and C++ programming languages, which manage +the allocated size internally, these allocators require that the client +maintains (or knows $(I a priori)) the allocation size for each piece of memory +allocated. Put simply, the client must pass the allocated size upon +deallocation. Storing the size in the _allocator has significant negative +performance implications, and is virtually always redundant because client code +needs knowledge of the allocated size in order to avoid buffer overruns. (See +more discussion in a $(HTTP open- +std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html, proposal) for sized +deallocation in C++.) For this reason, allocators herein traffic in $(D void[]) +as opposed to $(D void*).) + +$(P In order to be usable as an _allocator, a type should implement the +following methods with their respective semantics. Only $(D alignment) and $(D +allocate) are required. If any of the other methods is missing, the _allocator +is assumed to not have that capability (for example some allocators do not offer +manual deallocation of memory). Allocators should NOT implement +unsupported methods to always fail. For example, an allocator that lacks the +capability to implement `alignedAllocate` should not define it at all (as +opposed to defining it to always return `null` or throw an exception). The +missing implementation statically informs other components about the +allocator's capabilities and allows them to make design decisions accordingly.) + +$(BOOKTABLE , +$(TR $(TH Method name) $(TH Semantics)) + +$(TR $(TDC uint alignment;, $(POST $(RES) > 0)) $(TD Returns the minimum +alignment of all data returned by the allocator. An allocator may implement $(D +alignment) as a statically-known $(D enum) value only. Applications that need +dynamically-chosen alignment values should use the $(D alignedAllocate) and $(D +alignedReallocate) APIs.)) + +$(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators +customarily allocate memory in discretely-sized chunks. Therefore, a request for +$(D n) bytes may result in a larger allocation. The extra memory allocated goes +unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation). +The function $(D goodAllocSize(n)) returns the actual number of bytes that would +be allocated upon a request for $(D n) bytes. This module defines a default +implementation that returns $(D n) rounded up to a multiple of the allocator's +alignment.)) + +$(TR $(TDC void[] allocate(size_t s);, $(POST $(RES) is null || $(RES).length == +s)) $(TD If $(D s == 0), the call may return any empty slice (including $(D +null)). Otherwise, the call allocates $(D s) bytes of memory and returns the +allocated block, or $(D null) if the request could not be satisfied.)) + +$(TR $(TDC void[] alignedAllocate(size_t s, uint a);, $(POST $(RES) is null || +$(RES).length == s)) $(TD Similar to `allocate`, with the additional +guarantee that the memory returned is aligned to at least `a` bytes. `a` +must be a power of 2.)) + +$(TR $(TDC void[] allocateAll();) $(TD Offers all of allocator's memory to the +caller, so it's usually defined by fixed-size allocators. If the allocator is +currently NOT managing any memory, then $(D allocateAll()) shall allocate and +return all memory available to the allocator, and subsequent calls to all +allocation primitives should not succeed (e.g. $(D allocate) shall return $(D +null) etc). Otherwise, $(D allocateAll) only works on a best-effort basis, and +the allocator is allowed to return $(D null) even if does have available memory. +Memory allocated with $(D allocateAll) is not otherwise special (e.g. can be +reallocated or deallocated with the usual primitives, if defined).)) + +$(TR $(TDC bool expand(ref void[] b, size_t delta);, $(POST !$(RES) || b.length +== $(I old)(b).length + delta)) $(TD Expands $(D b) by $(D delta) bytes. If $(D +delta == 0), succeeds without changing $(D b). If $(D b is null), returns +`false` (the null pointer cannot be expanded in place). Otherwise, $(D +b) must be a buffer previously allocated with the same allocator. If expansion +was successful, $(D expand) changes $(D b)'s length to $(D b.length + delta) and +returns $(D true). Upon failure, the call effects no change upon the allocator +object, leaves $(D b) unchanged, and returns $(D false).)) + +$(TR $(TDC bool reallocate(ref void[] b, size_t s);, $(POST !$(RES) || b.length +== s)) $(TD Reallocates $(D b) to size $(D s), possibly moving memory around. +$(D b) must be $(D null) or a buffer allocated with the same allocator. If +reallocation was successful, $(D reallocate) changes $(D b) appropriately and +returns $(D true). Upon failure, the call effects no change upon the allocator +object, leaves $(D b) unchanged, and returns $(D false). An allocator should +implement $(D reallocate) if it can derive some advantage from doing so; +otherwise, this module defines a $(D reallocate) free function implemented in +terms of $(D expand), $(D allocate), and $(D deallocate).)) + +$(TR $(TDC bool alignedReallocate(ref void[] b,$(BR) size_t s, uint a);, $(POST +!$(RES) || b.length == s)) $(TD Similar to $(D reallocate), but guarantees the +reallocated memory is aligned at $(D a) bytes. The buffer must have been +originated with a call to $(D alignedAllocate). $(D a) must be a power of 2 +greater than $(D (void*).sizeof). An allocator should implement $(D +alignedReallocate) if it can derive some advantage from doing so; otherwise, +this module defines a $(D alignedReallocate) free function implemented in terms +of $(D expand), $(D alignedAllocate), and $(D deallocate).)) + +$(TR $(TDC Ternary owns(void[] b);) $(TD Returns `Ternary.yes` if `b` has been +allocated with this allocator. An allocator should define this method only if it +can decide on ownership precisely and fast (in constant time, logarithmic time, +or linear time with a low multiplication factor). Traditional allocators such as +the C heap do not define such functionality. If $(D b is null), the allocator +shall return `Ternary.no`, i.e. no allocator owns the `null` slice.)) + +$(TR $(TDC Ternary resolveInternalPointer(void* p, ref void[] result);) $(TD If +`p` is a pointer somewhere inside a block allocated with this allocator, +`result` holds a pointer to the beginning of the allocated block and returns +`Ternary.yes`. Otherwise, `result` holds `null` and returns `Ternary.no`. +If the pointer points immediately after an allocated block, the result is +implementation defined.)) + +$(TR $(TDC bool deallocate(void[] b);) $(TD If $(D b is null), does +nothing and returns `true`. Otherwise, deallocates memory previously allocated +with this allocator and returns `true` if successful, `false` otherwise. An +implementation that would not support deallocation (i.e. would always return +`false` should not define this primitive at all.))) + +$(TR $(TDC bool deallocateAll();, $(POST empty)) $(TD Deallocates all memory +allocated with this allocator. If an allocator implements this method, it must +specify whether its destructor calls it, too.)) + +$(TR $(TDC Ternary empty();) $(TD Returns `Ternary.yes` if and only if the +allocator holds no memory (i.e. no allocation has occurred, or all allocations +have been deallocated).)) + +$(TR $(TDC static Allocator instance;, $(POST instance $(I is a valid) +Allocator $(I object))) $(TD Some allocators are $(I monostate), i.e. have only +an instance and hold only global state. (Notable examples are C's own +`malloc`-based allocator and D's garbage-collected heap.) Such allocators must +define a static $(D instance) instance that serves as the symbolic placeholder +for the global instance of the allocator. An allocator should not hold state +and define `instance` simultaneously. Depending on whether the allocator is +thread-safe or not, this instance may be $(D shared).)) +) + +$(H2 Sample Assembly) + +The example below features an _allocator modeled after $(HTTP goo.gl/m7329l, +jemalloc), which uses a battery of free-list allocators spaced so as to keep +internal fragmentation to a minimum. The $(D FList) definitions specify no +bounds for the freelist because the $(D Segregator) does all size selection in +advance. + +Sizes through 3584 bytes are handled via freelists of staggered sizes. Sizes +from 3585 bytes through 4072 KB are handled by a $(D BitmappedBlock) with a +block size of 4 KB. Sizes above that are passed direct to the $(D Mallocator). + +---- + alias FList = FreeList!(GCAllocator, 0, unbounded); + alias A = Segregator!( + 8, FreeList!(GCAllocator, 0, 8), + 128, Bucketizer!(FList, 1, 128, 16), + 256, Bucketizer!(FList, 129, 256, 32), + 512, Bucketizer!(FList, 257, 512, 64), + 1024, Bucketizer!(FList, 513, 1024, 128), + 2048, Bucketizer!(FList, 1025, 2048, 256), + 3584, Bucketizer!(FList, 2049, 3584, 512), + 4072 * 1024, AllocatorList!( + () => BitmappedBlock!(GCAllocator, 4096)(4072 * 1024)), + GCAllocator + ); + A tuMalloc; + auto b = tuMalloc.allocate(500); + assert(b.length == 500); + auto c = tuMalloc.allocate(113); + assert(c.length == 113); + assert(tuMalloc.expand(c, 14)); + tuMalloc.deallocate(b); + tuMalloc.deallocate(c); +---- + +$(H2 Allocating memory for sharing across threads) + +One allocation pattern used in multithreaded applications is to share memory +across threads, and to deallocate blocks in a different thread than the one that +allocated it. + +All allocators in this module accept and return $(D void[]) (as opposed to +$(D shared void[])). This is because at the time of allocation, deallocation, or +reallocation, the memory is effectively not $(D shared) (if it were, it would +reveal a bug at the application level). + +The issue remains of calling $(D a.deallocate(b)) from a different thread than +the one that allocated $(D b). It follows that both threads must have access to +the same instance $(D a) of the respective allocator type. By definition of D, +this is possible only if $(D a) has the $(D shared) qualifier. It follows that +the allocator type must implement $(D allocate) and $(D deallocate) as $(D +shared) methods. That way, the allocator commits to allowing usable $(D shared) +instances. + +Conversely, allocating memory with one non-$(D shared) allocator, passing it +across threads (by casting the obtained buffer to $(D shared)), and later +deallocating it in a different thread (either with a different allocator object +or with the same allocator object after casting it to $(D shared)) is illegal. + +$(H2 Building Blocks) + +$(P The table below gives a synopsis of predefined allocator building blocks, +with their respective modules. Either `import` the needed modules individually, +or `import` `std.experimental.building_blocks`, which imports them all +`public`ly. The building blocks can be assembled in unbounded ways and also +combined with your own. For a collection of typical and useful preassembled +allocators and for inspiration in defining more such assemblies, refer to +$(MREF std,experimental,allocator,showcase).) + +$(BOOKTABLE, +$(TR $(TH Allocator$(BR)) $(TH Description)) + +$(TR $(TDC2 NullAllocator, null_allocator) $(TD Very good at doing absolutely nothing. A good +starting point for defining other allocators or for studying the API.)) + +$(TR $(TDC3 GCAllocator, gc_allocator) $(TD The system-provided garbage-collector allocator. +This should be the default fallback allocator tapping into system memory. It +offers manual $(D free) and dutifully collects litter.)) + +$(TR $(TDC3 Mallocator, mallocator) $(TD The C heap _allocator, a.k.a. $(D +malloc)/$(D realloc)/$(D free). Use sparingly and only for code that is unlikely +to leak.)) + +$(TR $(TDC3 AlignedMallocator, mallocator) $(TD Interface to OS-specific _allocators that +support specifying alignment: +$(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, $(D posix_memalign)) +on Posix and $(HTTP msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx, +$(D __aligned_xxx)) on Windows.)) + +$(TR $(TDC2 AffixAllocator, affix_allocator) $(TD Allocator that allows and manages allocating +extra prefix and/or a suffix bytes for each block allocated.)) + +$(TR $(TDC2 BitmappedBlock, bitmapped_block) $(TD Organizes one contiguous chunk of memory in +equal-size blocks and tracks allocation status at the cost of one bit per +block.)) + +$(TR $(TDC2 FallbackAllocator, fallback_allocator) $(TD Allocator that combines two other allocators + - primary and fallback. Allocation requests are first tried with primary, and + upon failure are passed to the fallback. Useful for small and fast allocators + fronting general-purpose ones.)) + +$(TR $(TDC2 FreeList, free_list) $(TD Allocator that implements a $(HTTP +wikipedia.org/wiki/Free_list, free list) on top of any other allocator. The +preferred size, tolerance, and maximum elements are configurable at compile- and +run time.)) + +$(TR $(TDC2 SharedFreeList, free_list) $(TD Same features as $(D FreeList), but packaged as +a $(D shared) structure that is accessible to several threads.)) + +$(TR $(TDC2 FreeTree, free_tree) $(TD Allocator similar to $(D FreeList) that uses a +binary search tree to adaptively store not one, but many free lists.)) + +$(TR $(TDC2 Region, region) $(TD Region allocator organizes a chunk of memory as a +simple bump-the-pointer allocator.)) + +$(TR $(TDC2 InSituRegion, region) $(TD Region holding its own allocation, most often on +the stack. Has statically-determined size.)) + +$(TR $(TDC2 SbrkRegion, region) $(TD Region using $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, +sbrk)) for allocating memory.)) + +$(TR $(TDC3 MmapAllocator, mmap_allocator) $(TD Allocator using + $(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) directly.)) + +$(TR $(TDC2 StatsCollector, stats_collector) $(TD Collect statistics about any other +allocator.)) + +$(TR $(TDC2 Quantizer, quantizer) $(TD Allocates in coarse-grained quantas, thus +improving performance of reallocations by often reallocating in place. The drawback is higher memory consumption because of allocated and unused memory.)) + +$(TR $(TDC2 AllocatorList, allocator_list) $(TD Given an allocator factory, lazily creates as +many allocators as needed to satisfy allocation requests. The allocators are +stored in a linked list. Requests for allocation are satisfied by searching the +list in a linear manner.)) + +$(TR $(TDC2 Segregator, segregator) $(TD Segregates allocation requests by size +and dispatches them to distinct allocators.)) + +$(TR $(TDC2 Bucketizer, bucketizer) $(TD Divides allocation sizes in discrete buckets and +uses an array of allocators, one per bucket, to satisfy requests.)) + +$(COMMENT $(TR $(TDC2 InternalPointersTree) $(TD Adds support for resolving internal +pointers on top of another allocator.))) +) + +Macros: +MYREF2 = $(REF_SHORT $1, std,experimental,allocator,building_blocks,$2) +MYREF3 = $(REF_SHORT $1, std,experimental,allocator,$2) +TDC = $(TDNW $(D $1)$+) +TDC2 = $(TDNW $(D $(MYREF2 $1,$+))$(BR)$(SMALL +$(D std.experimental.allocator.building_blocks.$2))) +TDC3 = $(TDNW $(D $(MYREF3 $1,$+))$(BR)$(SMALL +$(D std.experimental.allocator.$2))) +RES = $(I result) +POST = $(BR)$(SMALL $(I Post:) $(BLUE $(D $0))) +*/ + +module std.experimental.allocator.building_blocks; + +public import + std.experimental.allocator.building_blocks.affix_allocator, + std.experimental.allocator.building_blocks.allocator_list, + std.experimental.allocator.building_blocks.bucketizer, + std.experimental.allocator.building_blocks.fallback_allocator, + std.experimental.allocator.building_blocks.free_list, + std.experimental.allocator.building_blocks.free_tree, + std.experimental.allocator.gc_allocator, + std.experimental.allocator.building_blocks.bitmapped_block, + std.experimental.allocator.building_blocks.kernighan_ritchie, + std.experimental.allocator.mallocator, + std.experimental.allocator.mmap_allocator, + std.experimental.allocator.building_blocks.null_allocator, + std.experimental.allocator.building_blocks.quantizer, + std.experimental.allocator.building_blocks.region, + std.experimental.allocator.building_blocks.segregator, + std.experimental.allocator.building_blocks.stats_collector; diff --git a/std/experimental/allocator/building_blocks/quantizer.d b/std/experimental/allocator/building_blocks/quantizer.d new file mode 100644 index 00000000000..38a061d7454 --- /dev/null +++ b/std/experimental/allocator/building_blocks/quantizer.d @@ -0,0 +1,234 @@ +/// +module std.experimental.allocator.building_blocks.quantizer; + +import std.experimental.allocator.common; + +/** +This allocator sits on top of $(D ParentAllocator) and quantizes allocation +sizes, usually from arbitrary positive numbers to a small set of round numbers +(e.g. powers of two, page sizes etc). This technique is commonly used to: + +$(UL +$(LI Preallocate more memory than requested such that later on, when +reallocation is needed (e.g. to grow an array), expansion can be done quickly +in place. Reallocation to smaller sizes is also fast (in-place) when the new +size requested is within the same quantum as the existing size. Code that's +reallocation-heavy can therefore benefit from fronting a generic allocator +with a $(D Quantizer). These advantages are present even if +$(D ParentAllocator) does not support reallocation at all.) +$(LI Improve behavior of allocators sensitive to allocation sizes, such as $(D +FreeList) and $(D FreeTree). Rounding allocation requests up makes for smaller +free lists/trees at the cost of slack memory (internal fragmentation).) +) + +The following methods are forwarded to the parent allocator if present: +$(D allocateAll), $(D owns), $(D deallocateAll), $(D empty). + +Preconditions: $(D roundingFunction) must satisfy three constraints. These are +not enforced (save for the use of $(D assert)) for the sake of efficiency. +$(OL +$(LI $(D roundingFunction(n) >= n) for all $(D n) of type $(D size_t);) +$(LI $(D roundingFunction) must be monotonically increasing, i.e. $(D +roundingFunction(n1) <= roundingFunction(n2)) for all $(D n1 < n2);) +$(LI $(D roundingFunction) must be $(D pure), i.e. always return the same +value for a given $(D n).) +) +*/ +struct Quantizer(ParentAllocator, alias roundingFunction) +{ + import std.traits : hasMember; + + /** + The parent allocator. Depending on whether $(D ParentAllocator) holds state + or not, this is a member variable or an alias for + `ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) + { + ParentAllocator parent; + } + else + { + alias parent = ParentAllocator.instance; + static __gshared Quantizer instance; + } + + /** + Returns $(D roundingFunction(n)). + */ + size_t goodAllocSize(size_t n) + { + auto result = roundingFunction(n); + assert(result >= n); + return result; + } + + /** + Alignment is identical to that of the parent. + */ + enum alignment = ParentAllocator.alignment; + + /** + Gets a larger buffer $(D buf) by calling + $(D parent.allocate(goodAllocSize(n))). If $(D buf) is $(D null), returns + $(D null). Otherwise, returns $(D buf[0 .. n]). + */ + void[] allocate(size_t n) + { + auto result = parent.allocate(goodAllocSize(n)); + return result.ptr ? result.ptr[0 .. n] : null; + } + + /** + Defined only if $(D parent.alignedAllocate) exists and works similarly to + $(D allocate) by forwarding to + $(D parent.alignedAllocate(goodAllocSize(n), a)). + */ + static if (hasMember!(ParentAllocator, "alignedAllocate")) + void[] alignedAllocate(size_t n, uint) + { + auto result = parent.alignedAllocate(goodAllocSize(n)); + return result.ptr ? result.ptr[0 .. n] : null; + } + + /** + First checks whether there's enough slack memory preallocated for $(D b) + by evaluating $(D b.length + delta <= goodAllocSize(b.length)). If that's + the case, expands $(D b) in place. Otherwise, attempts to use + $(D parent.expand) appropriately if present. + */ + bool expand(ref void[] b, size_t delta) + { + if (!b.ptr) return delta == 0; + immutable allocated = goodAllocSize(b.length), + needed = b.length + delta, + neededAllocation = goodAllocSize(needed); + assert(b.length <= allocated); + assert(needed <= neededAllocation); + assert(allocated <= neededAllocation); + // Second test needed because expand must work for null pointers, too. + if (allocated == neededAllocation) + { + // Nice! + b = b.ptr[0 .. needed]; + return true; + } + // Hail Mary + static if (hasMember!(ParentAllocator, "expand")) + { + // Expand to the appropriate quantum + auto original = b.ptr[0 .. allocated]; + assert(goodAllocSize(needed) >= allocated); + if (!parent.expand(original, neededAllocation - allocated)) + return false; + // Dial back the size + b = original.ptr[0 .. needed]; + return true; + } + else + { + return false; + } + } + + /** + Expands or shrinks allocated block to an allocated size of $(D + goodAllocSize(s)). Expansion occurs in place under the conditions required + by $(D expand). Shrinking occurs in place if $(D goodAllocSize(b.length) + == goodAllocSize(s)). + */ + bool reallocate(ref void[] b, size_t s) + { + if (!b.ptr) + { + b = allocate(s); + return b.length == s; + } + if (s >= b.length && expand(b, s - b.length)) return true; + immutable toAllocate = goodAllocSize(s), + allocated = goodAllocSize(b.length); + // Are the lengths within the same quantum? + if (allocated == toAllocate) + { + // Reallocation (whether up or down) will be done in place + b = b.ptr[0 .. s]; + return true; + } + // Defer to parent (or global) with quantized size + auto original = b.ptr[0 .. allocated]; + if (!parent.reallocate(original, toAllocate)) return false; + b = original.ptr[0 .. s]; + return true; + } + + /** + Defined only if $(D ParentAllocator.alignedAllocate) exists. Expansion + occurs in place under the conditions required by $(D expand). Shrinking + occurs in place if $(D goodAllocSize(b.length) == goodAllocSize(s)). + */ + static if (hasMember!(ParentAllocator, "alignedAllocate")) + bool alignedReallocate(ref void[] b, size_t s, uint a) + { + if (!b.ptr) + { + b = alignedAllocate(s); + return b.length == s; + } + if (s >= b.length && expand(b, s - b.length)) return true; + immutable toAllocate = goodAllocSize(s), + allocated = goodAllocSize(b.length); + // Are the lengths within the same quantum? + if (allocated == toAllocate) + { + assert(b.ptr); // code above must have caught this + // Reallocation (whether up or down) will be done in place + b = b.ptr[0 .. s]; + return true; + } + // Defer to parent (or global) with quantized size + auto original = b.ptr[0 .. allocated]; + if (!parent.alignedReallocate(original, toAllocate, a)) return false; + b = original.ptr[0 .. s]; + return true; + } + + /** + Defined if $(D ParentAllocator.deallocate) exists and forwards to + $(D parent.deallocate(b.ptr[0 .. goodAllocSize(b.length)])). + */ + static if (hasMember!(ParentAllocator, "deallocate")) + bool deallocate(void[] b) + { + if (!b.ptr) return true; + return parent.deallocate(b.ptr[0 .. goodAllocSize(b.length)]); + } + + // Forwarding methods + mixin(forwardToMember("parent", + "allocateAll", "owns", "deallocateAll", "empty")); +} + +/// +@system unittest +{ + import std.experimental.allocator.building_blocks.free_tree : FreeTree; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.common : roundUpToMultipleOf; + + // Quantize small allocations to a multiple of cache line, large ones to a + // multiple of page size + alias MyAlloc = Quantizer!( + FreeTree!GCAllocator, + n => n.roundUpToMultipleOf(n <= 16_384 ? 64 : 4096)); + MyAlloc alloc; + const buf = alloc.allocate(256); + assert(buf.ptr); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + alias MyAlloc = Quantizer!(GCAllocator, + (size_t n) => n.roundUpToMultipleOf(64)); + testAllocator!(() => MyAlloc()); +} diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d new file mode 100644 index 00000000000..3809b9dd7e4 --- /dev/null +++ b/std/experimental/allocator/building_blocks/region.d @@ -0,0 +1,784 @@ +/// +module std.experimental.allocator.building_blocks.region; + +import std.experimental.allocator.common; +import std.experimental.allocator.building_blocks.null_allocator; +import std.typecons : Flag, Yes, No; + +/** +A $(D Region) allocator allocates memory straight from one contiguous chunk. +There is no deallocation, and once the region is full, allocation requests +return $(D null). Therefore, $(D Region)s are often used (a) in conjunction with +more sophisticated allocators; or (b) for batch-style very fast allocations +that deallocate everything at once. + +The region only stores three pointers, corresponding to the current position in +the store and the limits. One allocation entails rounding up the allocation +size for alignment purposes, bumping the current pointer, and comparing it +against the limit. + +If $(D ParentAllocator) is different from $(D NullAllocator), $(D Region) +deallocates the chunk of memory during destruction. + +The $(D minAlign) parameter establishes alignment. If $(D minAlign > 1), the +sizes of all allocation requests are rounded up to a multiple of $(D minAlign). +Applications aiming at maximum speed may want to choose $(D minAlign = 1) and +control alignment externally. + +*/ +struct Region(ParentAllocator = NullAllocator, + uint minAlign = platformAlignment, + Flag!"growDownwards" growDownwards = No.growDownwards) +{ + static assert(minAlign.isGoodStaticAlignment); + static assert(ParentAllocator.alignment >= minAlign); + + import std.traits : hasMember; + import std.typecons : Ternary; + + // state + /** + The _parent allocator. Depending on whether $(D ParentAllocator) holds state + or not, this is a member variable or an alias for + `ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) + { + ParentAllocator parent; + } + else + { + alias parent = ParentAllocator.instance; + } + private void* _current, _begin, _end; + + /** + Constructs a region backed by a user-provided store. Assumes $(D store) is + aligned at $(D minAlign). Also assumes the memory was allocated with $(D + ParentAllocator) (if different from $(D NullAllocator)). + + Params: + store = User-provided store backing up the region. $(D store) must be + aligned at $(D minAlign) (enforced with $(D assert)). If $(D + ParentAllocator) is different from $(D NullAllocator), memory is assumed to + have been allocated with $(D ParentAllocator). + n = Bytes to allocate using $(D ParentAllocator). This constructor is only + defined If $(D ParentAllocator) is different from $(D NullAllocator). If + $(D parent.allocate(n)) returns $(D null), the region will be initialized + as empty (correctly initialized but unable to allocate). + */ + this(ubyte[] store) + { + store = cast(ubyte[])(store.roundUpToAlignment(alignment)); + store = store[0 .. $.roundDownToAlignment(alignment)]; + assert(store.ptr.alignedAt(minAlign)); + assert(store.length % minAlign == 0); + _begin = store.ptr; + _end = store.ptr + store.length; + static if (growDownwards) + _current = _end; + else + _current = store.ptr; + } + + /// Ditto + static if (!is(ParentAllocator == NullAllocator)) + this(size_t n) + { + this(cast(ubyte[])(parent.allocate(n.roundUpToAlignment(alignment)))); + } + + /* + TODO: The postblit of $(D BasicRegion) should be disabled because such objects + should not be copied around naively. + */ + + /** + If `ParentAllocator` is not `NullAllocator` and defines `deallocate`, the region defines a destructor that uses `ParentAllocator.delete` to free the + memory chunk. + */ + static if (!is(ParentAllocator == NullAllocator) + && hasMember!(ParentAllocator, "deallocate")) + ~this() + { + parent.deallocate(_begin[0 .. _end - _begin]); + } + + + /** + Alignment offered. + */ + alias alignment = minAlign; + + /** + Allocates $(D n) bytes of memory. The shortest path involves an alignment + adjustment (if $(D alignment > 1)), an increment, and a comparison. + + Params: + n = number of bytes to allocate + + Returns: + A properly-aligned buffer of size $(D n) or $(D null) if request could not + be satisfied. + */ + void[] allocate(size_t n) + { + static if (growDownwards) + { + if (available < n) return null; + static if (minAlign > 1) + const rounded = n.roundUpToAlignment(alignment); + else + alias rounded = n; + assert(available >= rounded); + auto result = (_current - rounded)[0 .. n]; + assert(result.ptr >= _begin); + _current = result.ptr; + assert(owns(result) == Ternary.yes); + return result; + } + else + { + auto result = _current[0 .. n]; + static if (minAlign > 1) + const rounded = n.roundUpToAlignment(alignment); + else + alias rounded = n; + _current += rounded; + if (_current <= _end) return result; + // Slow path, backtrack + _current -= rounded; + return null; + } + } + + /** + Allocates $(D n) bytes of memory aligned at alignment $(D a). + + Params: + n = number of bytes to allocate + a = alignment for the allocated block + + Returns: + Either a suitable block of $(D n) bytes aligned at $(D a), or $(D null). + */ + void[] alignedAllocate(size_t n, uint a) + { + import std.math : isPowerOf2; + assert(a.isPowerOf2); + static if (growDownwards) + { + const available = _current - _begin; + if (available < n) return null; + auto result = (_current - n).alignDownTo(a)[0 .. n]; + if (result.ptr >= _begin) + { + _current = result.ptr; + return result; + } + } + else + { + // Just bump the pointer to the next good allocation + auto save = _current; + _current = _current.alignUpTo(a); + auto result = allocate(n); + if (result.ptr) + { + assert(result.length == n); + return result; + } + // Failed, rollback + _current = save; + } + return null; + } + + /// Allocates and returns all memory available to this region. + void[] allocateAll() + { + static if (growDownwards) + { + auto result = _begin[0 .. available]; + _current = _begin; + } + else + { + auto result = _current[0 .. available]; + _current = _end; + } + return result; + } + + /** + Expands an allocated block in place. Expansion will succeed only if the + block is the last allocated. Defined only if `growDownwards` is + `No.growDownwards`. + */ + static if (growDownwards == No.growDownwards) + bool expand(ref void[] b, size_t delta) + { + assert(owns(b) == Ternary.yes || b.ptr is null); + assert(b.ptr + b.length <= _current || b.ptr is null); + if (!b.ptr) return delta == 0; + auto newLength = b.length + delta; + if (_current < b.ptr + b.length + alignment) + { + // This was the last allocation! Allocate some more and we're done. + if (this.goodAllocSize(b.length) == this.goodAllocSize(newLength) + || allocate(delta).length == delta) + { + b = b.ptr[0 .. newLength]; + assert(_current < b.ptr + b.length + alignment); + return true; + } + } + return false; + } + + /** + Deallocates $(D b). This works only if $(D b) was obtained as the last call + to $(D allocate); otherwise (i.e. another allocation has occurred since) it + does nothing. This semantics is tricky and therefore $(D deallocate) is + defined only if $(D Region) is instantiated with $(D Yes.defineDeallocate) + as the third template argument. + + Params: + b = Block previously obtained by a call to $(D allocate) against this + allocator ($(D null) is allowed). + */ + bool deallocate(void[] b) + { + assert(owns(b) == Ternary.yes || b.ptr is null); + static if (growDownwards) + { + if (b.ptr == _current) + { + _current += this.goodAllocSize(b.length); + return true; + } + } + else + { + if (b.ptr + this.goodAllocSize(b.length) == _current) + { + assert(b.ptr !is null || _current is null); + _current = b.ptr; + return true; + } + } + return false; + } + + /** + Deallocates all memory allocated by this region, which can be subsequently + reused for new allocations. + */ + bool deallocateAll() + { + static if (growDownwards) + { + _current = _end; + } + else + { + _current = _begin; + } + return true; + } + + /** + Queries whether $(D b) has been allocated with this region. + + Params: + b = Arbitrary block of memory ($(D null) is allowed; $(D owns(null)) + returns $(D false)). + + Returns: + $(D true) if $(D b) has been allocated with this region, $(D false) + otherwise. + */ + Ternary owns(void[] b) const + { + return Ternary(b.ptr >= _begin && b.ptr + b.length <= _end); + } + + /** + Returns `Ternary.yes` if no memory has been allocated in this region, + `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) + */ + Ternary empty() const + { + return Ternary(_current == _begin); + } + + /// Nonstandard property that returns bytes available for allocation. + size_t available() const + { + static if (growDownwards) + { + return _current - _begin; + } + else + { + return _end - _current; + } + } +} + +/// +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + import std.algorithm.comparison : max; + // Create a scalable list of regions. Each gets at least 1MB at a time by + // using malloc. + auto batchAllocator = AllocatorList!( + (size_t n) => Region!Mallocator(max(n, 1024 * 1024)) + )(); + auto b = batchAllocator.allocate(101); + assert(b.length == 101); + // This will cause a second allocation + b = batchAllocator.allocate(2 * 1024 * 1024); + assert(b.length == 2 * 1024 * 1024); + // Destructor will free the memory +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + // Create a 64 KB region allocated with malloc + auto reg = Region!(Mallocator, Mallocator.alignment, + Yes.growDownwards)(1024 * 64); + const b = reg.allocate(101); + assert(b.length == 101); + // Destructor will free the memory +} + +/** + +$(D InSituRegion) is a convenient region that carries its storage within itself +(in the form of a statically-sized array). + +The first template argument is the size of the region and the second is the +needed alignment. Depending on the alignment requested and platform details, +the actual available storage may be smaller than the compile-time parameter. To +make sure that at least $(D n) bytes are available in the region, use +$(D InSituRegion!(n + a - 1, a)). + +Given that the most frequent use of `InSituRegion` is as a stack allocator, it +allocates starting at the end on systems where stack grows downwards, such that +hot memory is used first. + +*/ +struct InSituRegion(size_t size, size_t minAlign = platformAlignment) +{ + import std.algorithm.comparison : max; + import std.conv : to; + import std.traits : hasMember; + import std.typecons : Ternary; + + static assert(minAlign.isGoodStaticAlignment); + static assert(size >= minAlign); + + version (X86) enum growDownwards = Yes.growDownwards; + else version (X86_64) enum growDownwards = Yes.growDownwards; + else version (ARM) enum growDownwards = Yes.growDownwards; + else version (AArch64) enum growDownwards = Yes.growDownwards; + else version (PPC) enum growDownwards = Yes.growDownwards; + else version (PPC64) enum growDownwards = Yes.growDownwards; + else version (MIPS) enum growDownwards = Yes.growDownwards; + else version (MIPS64) enum growDownwards = Yes.growDownwards; + else version (SPARC) enum growDownwards = Yes.growDownwards; + else version (SystemZ) enum growDownwards = Yes.growDownwards; + else static assert(0, "Dunno how the stack grows on this architecture."); + + @disable this(this); + + // state { + private Region!(NullAllocator, minAlign, growDownwards) _impl; + union + { + private ubyte[size] _store = void; + private double _forAlignmentOnly1 = void; + } + // } + + /** + An alias for $(D minAlign), which must be a valid alignment (nonzero power + of 2). The start of the region and all allocation requests will be rounded + up to a multiple of the alignment. + + ---- + InSituRegion!(4096) a1; + assert(a1.alignment == platformAlignment); + InSituRegion!(4096, 64) a2; + assert(a2.alignment == 64); + ---- + */ + alias alignment = minAlign; + + private void lazyInit() + { + assert(!_impl._current); + _impl = typeof(_impl)(_store); + assert(_impl._current.alignedAt(alignment)); + } + + /** + Allocates $(D bytes) and returns them, or $(D null) if the region cannot + accommodate the request. For efficiency reasons, if $(D bytes == 0) the + function returns an empty non-null slice. + */ + void[] allocate(size_t n) + { + // Fast path + entry: + auto result = _impl.allocate(n); + if (result.length == n) return result; + // Slow path + if (_impl._current) return null; // no more room + lazyInit; + assert(_impl._current); + goto entry; + } + + /** + As above, but the memory allocated is aligned at $(D a) bytes. + */ + void[] alignedAllocate(size_t n, uint a) + { + // Fast path + entry: + auto result = _impl.alignedAllocate(n, a); + if (result.length == n) return result; + // Slow path + if (_impl._current) return null; // no more room + lazyInit; + assert(_impl._current); + goto entry; + } + + /** + Deallocates $(D b). This works only if $(D b) was obtained as the last call + to $(D allocate); otherwise (i.e. another allocation has occurred since) it + does nothing. This semantics is tricky and therefore $(D deallocate) is + defined only if $(D Region) is instantiated with $(D Yes.defineDeallocate) + as the third template argument. + + Params: + b = Block previously obtained by a call to $(D allocate) against this + allocator ($(D null) is allowed). + */ + bool deallocate(void[] b) + { + if (!_impl._current) return b is null; + return _impl.deallocate(b); + } + + /** + Returns `Ternary.yes` if `b` is the result of a previous allocation, + `Ternary.no` otherwise. + */ + Ternary owns(void[] b) + { + if (!_impl._current) return Ternary.no; + return _impl.owns(b); + } + + /** + Expands an allocated block in place. Expansion will succeed only if the + block is the last allocated. + */ + static if (hasMember!(typeof(_impl), "expand")) + bool expand(ref void[] b, size_t delta) + { + if (!_impl._current) lazyInit; + return _impl.expand(b, delta); + } + + /** + Deallocates all memory allocated with this allocator. + */ + bool deallocateAll() + { + // We don't care to lazily init the region + return _impl.deallocateAll; + } + + /** + Allocates all memory available with this allocator. + */ + void[] allocateAll() + { + if (!_impl._current) lazyInit; + return _impl.allocateAll; + } + + /** + Nonstandard function that returns the bytes available for allocation. + */ + size_t available() + { + if (!_impl._current) lazyInit; + return _impl.available; + } +} + +/// +@system unittest +{ + // 128KB region, allocated to x86's cache line + InSituRegion!(128 * 1024, 16) r1; + auto a1 = r1.allocate(101); + assert(a1.length == 101); + + // 128KB region, with fallback to the garbage collector. + import std.experimental.allocator.building_blocks.fallback_allocator + : FallbackAllocator; + import std.experimental.allocator.building_blocks.free_list + : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.bitmapped_block + : BitmappedBlock; + FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2; + const a2 = r2.allocate(102); + assert(a2.length == 102); + + // Reap with GC fallback. + InSituRegion!(128 * 1024, 8) tmp3; + FallbackAllocator!(BitmappedBlock!(64, 8), GCAllocator) r3; + r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[])(tmp3.allocateAll())); + const a3 = r3.allocate(103); + assert(a3.length == 103); + + // Reap/GC with a freelist for small objects up to 16 bytes. + InSituRegion!(128 * 1024, 64) tmp4; + FreeList!(FallbackAllocator!(BitmappedBlock!(64, 64), GCAllocator), 0, 16) r4; + r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[])(tmp4.allocateAll())); + const a4 = r4.allocate(104); + assert(a4.length == 104); +} + +@system unittest +{ + InSituRegion!(4096, 1) r1; + auto a = r1.allocate(2001); + assert(a.length == 2001); + import std.conv : text; + assert(r1.available == 2095, text(r1.available)); + + InSituRegion!(65_536, 1024*4) r2; + assert(r2.available <= 65_536); + a = r2.allocate(2001); + assert(a.length == 2001); +} + +private extern(C) void* sbrk(long); +private extern(C) int brk(shared void*); + +/** + +Allocator backed by $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, sbrk)) +for Posix systems. Due to the fact that $(D sbrk) is not thread-safe +$(HTTP lifecs.likai.org/2010/02/sbrk-is-not-thread-safe.html, by design), +$(D SbrkRegion) uses a mutex internally. This implies +that uncontrolled calls to $(D brk) and $(D sbrk) may affect the workings of $(D +SbrkRegion) adversely. + +*/ +version(Posix) struct SbrkRegion(uint minAlign = platformAlignment) +{ + private import core.sys.posix.pthread : pthread_mutex_init, pthread_mutex_destroy, + pthread_mutex_t, pthread_mutex_lock, pthread_mutex_unlock, + PTHREAD_MUTEX_INITIALIZER; + private static shared pthread_mutex_t sbrkMutex = PTHREAD_MUTEX_INITIALIZER; + import std.typecons : Ternary; + + static assert(minAlign.isGoodStaticAlignment); + static assert(size_t.sizeof == (void*).sizeof); + private shared void* _brkInitial, _brkCurrent; + + /** + Instance shared by all callers. + */ + static shared SbrkRegion instance; + + /** + Standard allocator primitives. + */ + enum uint alignment = minAlign; + + /// Ditto + void[] allocate(size_t bytes) shared + { + static if (minAlign > 1) + const rounded = bytes.roundUpToMultipleOf(alignment); + else + alias rounded = bytes; + pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); + scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 + || assert(0); + // Assume sbrk returns the old break. Most online documentation confirms + // that, except for http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf, + // which claims the returned value is not portable. + auto p = sbrk(rounded); + if (p == cast(void*) -1) + { + return null; + } + if (!_brkInitial) + { + _brkInitial = cast(shared) p; + assert(cast(size_t) _brkInitial % minAlign == 0, + "Too large alignment chosen for " ~ typeof(this).stringof); + } + _brkCurrent = cast(shared) (p + rounded); + return p[0 .. bytes]; + } + + /// Ditto + void[] alignedAllocate(size_t bytes, uint a) shared + { + pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); + scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 + || assert(0); + if (!_brkInitial) + { + // This is one extra call, but it'll happen only once. + _brkInitial = cast(shared) sbrk(0); + assert(cast(size_t) _brkInitial % minAlign == 0, + "Too large alignment chosen for " ~ typeof(this).stringof); + (_brkInitial != cast(void*) -1) || assert(0); + _brkCurrent = _brkInitial; + } + immutable size_t delta = cast(shared void*) roundUpToMultipleOf( + cast(size_t) _brkCurrent, a) - _brkCurrent; + // Still must make sure the total size is aligned to the allocator's + // alignment. + immutable rounded = (bytes + delta).roundUpToMultipleOf(alignment); + + auto p = sbrk(rounded); + if (p == cast(void*) -1) + { + return null; + } + _brkCurrent = cast(shared) (p + rounded); + return p[delta .. delta + bytes]; + } + + /** + + The $(D expand) method may only succeed if the argument is the last block + allocated. In that case, $(D expand) attempts to push the break pointer to + the right. + + */ + bool expand(ref void[] b, size_t delta) shared + { + if (b is null) return delta == 0; + assert(_brkInitial && _brkCurrent); // otherwise where did b come from? + pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); + scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 + || assert(0); + if (_brkCurrent != b.ptr + b.length) return false; + // Great, can expand the last block + static if (minAlign > 1) + const rounded = delta.roundUpToMultipleOf(alignment); + else + alias rounded = bytes; + auto p = sbrk(rounded); + if (p == cast(void*) -1) + { + return false; + } + _brkCurrent = cast(shared) (p + rounded); + b = b.ptr[0 .. b.length + delta]; + return true; + } + + /// Ditto + Ternary owns(void[] b) shared + { + // No need to lock here. + assert(!_brkCurrent || b.ptr + b.length <= _brkCurrent); + return Ternary(_brkInitial && b.ptr >= _brkInitial); + } + + /** + + The $(D deallocate) method only works (and returns $(D true)) on systems + that support reducing the break address (i.e. accept calls to $(D sbrk) + with negative offsets). OSX does not accept such. In addition the argument + must be the last block allocated. + + */ + bool deallocate(void[] b) shared + { + static if (minAlign > 1) + const rounded = b.length.roundUpToMultipleOf(alignment); + else + const rounded = b.length; + pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); + scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 + || assert(0); + if (_brkCurrent != b.ptr + rounded) return false; + assert(b.ptr >= _brkInitial); + if (sbrk(-rounded) == cast(void*) -1) + return false; + _brkCurrent = cast(shared) b.ptr; + return true; + } + + /** + The $(D deallocateAll) method only works (and returns $(D true)) on systems + that support reducing the break address (i.e. accept calls to $(D sbrk) + with negative offsets). OSX does not accept such. + */ + bool deallocateAll() shared + { + pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); + scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 + || assert(0); + return !_brkInitial || brk(_brkInitial) == 0; + } + + /// Standard allocator API. + Ternary empty() + { + // Also works when they're both null. + return Ternary(_brkCurrent == _brkInitial); + } +} + +version(Posix) @system unittest +{ + // Let's test the assumption that sbrk(n) returns the old address + const p1 = sbrk(0); + const p2 = sbrk(4096); + assert(p1 == p2); + const p3 = sbrk(0); + assert(p3 == p2 + 4096); + // Try to reset brk, but don't make a fuss if it doesn't work + sbrk(-4096); +} + +version(Posix) @system unittest +{ + import std.typecons : Ternary; + alias alloc = SbrkRegion!(8).instance; + auto a = alloc.alignedAllocate(2001, 4096); + assert(a.length == 2001); + auto b = alloc.allocate(2001); + assert(b.length == 2001); + assert(alloc.owns(a) == Ternary.yes); + assert(alloc.owns(b) == Ternary.yes); + // reducing the brk does not work on OSX + version(OSX) {} else + { + assert(alloc.deallocate(b)); + assert(alloc.deallocateAll); + } +} diff --git a/std/experimental/allocator/building_blocks/scoped_allocator.d b/std/experimental/allocator/building_blocks/scoped_allocator.d new file mode 100644 index 00000000000..998a05f2894 --- /dev/null +++ b/std/experimental/allocator/building_blocks/scoped_allocator.d @@ -0,0 +1,221 @@ +/// +module std.experimental.allocator.building_blocks.scoped_allocator; + +import std.experimental.allocator.common; + +/** + +$(D ScopedAllocator) delegates all allocation requests to $(D ParentAllocator). +When destroyed, the $(D ScopedAllocator) object automatically calls $(D +deallocate) for all memory allocated through its lifetime. (The $(D +deallocateAll) function is also implemented with the same semantics.) + +$(D deallocate) is also supported, which is where most implementation effort +and overhead of $(D ScopedAllocator) go. If $(D deallocate) is not needed, a +simpler design combining $(D AllocatorList) with $(D Region) is recommended. + +*/ +struct ScopedAllocator(ParentAllocator) +{ + @system unittest + { + testAllocator!(() => ScopedAllocator()); + } + + private import std.experimental.allocator.building_blocks.affix_allocator + : AffixAllocator; + private import std.traits : hasMember; + import std.typecons : Ternary; + + private struct Node + { + Node* prev; + Node* next; + size_t length; + } + + alias Allocator = AffixAllocator!(ParentAllocator, Node); + + // state + /** + If $(D ParentAllocator) is stateful, $(D parent) is a property giving access + to an $(D AffixAllocator!ParentAllocator). Otherwise, $(D parent) is an alias for `AffixAllocator!ParentAllocator.instance`. + */ + static if (stateSize!ParentAllocator) + { + Allocator parent; + } + else + { + alias parent = Allocator.instance; + } + private Node* root; + + /** + $(D ScopedAllocator) is not copyable. + */ + @disable this(this); + + /** + $(D ScopedAllocator)'s destructor releases all memory allocated during its + lifetime. + */ + ~this() + { + deallocateAll; + } + + /// Alignment offered + enum alignment = Allocator.alignment; + + /** + Forwards to $(D parent.goodAllocSize) (which accounts for the management + overhead). + */ + size_t goodAllocSize(size_t n) + { + return parent.goodAllocSize(n); + } + + /** + Allocates memory. For management it actually allocates extra memory from + the parent. + */ + void[] allocate(size_t n) + { + auto b = parent.allocate(n); + if (!b.ptr) return b; + Node* toInsert = & parent.prefix(b); + toInsert.prev = null; + toInsert.next = root; + toInsert.length = n; + assert(!root || !root.prev); + if (root) root.prev = toInsert; + root = toInsert; + return b; + } + + /** + Forwards to $(D parent.expand(b, delta)). + */ + static if (hasMember!(Allocator, "expand")) + bool expand(ref void[] b, size_t delta) + { + auto result = parent.expand(b, delta); + if (result && b.ptr) + { + parent.prefix(b).length = b.length; + } + return result; + } + + /** + Reallocates $(D b) to new size $(D s). + */ + bool reallocate(ref void[] b, size_t s) + { + // Remove from list + if (b.ptr) + { + Node* n = & parent.prefix(b); + if (n.prev) n.prev.next = n.next; + else root = n.next; + if (n.next) n.next.prev = n.prev; + } + auto result = parent.reallocate(b, s); + // Add back to list + if (b.ptr) + { + Node* n = & parent.prefix(b); + n.prev = null; + n.next = root; + n.length = s; + if (root) root.prev = n; + root = n; + } + return result; + } + + /** + Forwards to $(D parent.owns(b)). + */ + static if (hasMember!(Allocator, "owns")) + Ternary owns(void[] b) + { + return parent.owns(b); + } + + /** + Deallocates $(D b). + */ + static if (hasMember!(Allocator, "deallocate")) + bool deallocate(void[] b) + { + // Remove from list + if (b.ptr) + { + Node* n = & parent.prefix(b); + if (n.prev) n.prev.next = n.next; + else root = n.next; + if (n.next) n.next.prev = n.prev; + } + return parent.deallocate(b); + } + + /** + Deallocates all memory allocated. + */ + bool deallocateAll() + { + bool result = true; + for (auto n = root; n; ) + { + void* p = n + 1; + auto length = n.length; + n = n.next; + if (!parent.deallocate(p[0 .. length])) + result = false; + } + root = null; + return result; + } + + /** + Returns `Ternary.yes` if this allocator is not responsible for any memory, + `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) + */ + Ternary empty() const + { + return Ternary(root is null); + } +} + +/// +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + import std.typecons : Ternary; + ScopedAllocator!Mallocator alloc; + assert(alloc.empty == Ternary.yes); + const b = alloc.allocate(10); + assert(b.length == 10); + assert(alloc.empty == Ternary.no); +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + testAllocator!(() => ScopedAllocator!GCAllocator()); +} + +@system unittest // https://issues.dlang.org/show_bug.cgi?id=16046 +{ + import std.exception; + import std.experimental.allocator; + import std.experimental.allocator.mallocator; + ScopedAllocator!Mallocator alloc; + auto foo = alloc.make!int(1).enforce; + auto bar = alloc.make!int(2).enforce; + alloc.dispose(foo); + alloc.dispose(bar); // segfault here +} diff --git a/std/experimental/allocator/building_blocks/segregator.d b/std/experimental/allocator/building_blocks/segregator.d new file mode 100644 index 00000000000..2d8ecffb2c8 --- /dev/null +++ b/std/experimental/allocator/building_blocks/segregator.d @@ -0,0 +1,361 @@ +/// +module std.experimental.allocator.building_blocks.segregator; + +import std.experimental.allocator.common; + +/** +Dispatches allocations (and deallocations) between two allocators ($(D +SmallAllocator) and $(D LargeAllocator)) depending on the size allocated, as +follows. All allocations smaller than or equal to $(D threshold) will be +dispatched to $(D SmallAllocator). The others will go to $(D LargeAllocator). + +If both allocators are $(D shared), the $(D Segregator) will also offer $(D +shared) methods. +*/ +struct Segregator(size_t threshold, SmallAllocator, LargeAllocator) +{ + import std.algorithm.comparison : min; + import std.traits : hasMember; + import std.typecons : Ternary; + + static if (stateSize!SmallAllocator) private SmallAllocator _small; + else private alias _small = SmallAllocator.instance; + static if (stateSize!LargeAllocator) private LargeAllocator _large; + else private alias _large = LargeAllocator.instance; + + version (StdDdoc) + { + /** + The alignment offered is the minimum of the two allocators' alignment. + */ + enum uint alignment; + /** + This method is defined only if at least one of the allocators defines + it. The good allocation size is obtained from $(D SmallAllocator) if $(D + s <= threshold), or $(D LargeAllocator) otherwise. (If one of the + allocators does not define $(D goodAllocSize), the default + implementation in this module applies.) + */ + static size_t goodAllocSize(size_t s); + /** + The memory is obtained from $(D SmallAllocator) if $(D s <= threshold), + or $(D LargeAllocator) otherwise. + */ + void[] allocate(size_t); + /** + This method is defined if both allocators define it, and forwards to + $(D SmallAllocator) or $(D LargeAllocator) appropriately. + */ + void[] alignedAllocate(size_t, uint); + /** + This method is defined only if at least one of the allocators defines + it. If $(D SmallAllocator) defines $(D expand) and $(D b.length + + delta <= threshold), the call is forwarded to $(D SmallAllocator). If $( + LargeAllocator) defines $(D expand) and $(D b.length > threshold), the + call is forwarded to $(D LargeAllocator). Otherwise, the call returns + $(D false). + */ + bool expand(ref void[] b, size_t delta); + /** + This method is defined only if at least one of the allocators defines + it. If $(D SmallAllocator) defines $(D reallocate) and $(D b.length <= + threshold && s <= threshold), the call is forwarded to $(D + SmallAllocator). If $(D LargeAllocator) defines $(D expand) and $(D + b.length > threshold && s > threshold), the call is forwarded to $(D + LargeAllocator). Otherwise, the call returns $(D false). + */ + bool reallocate(ref void[] b, size_t s); + /** + This method is defined only if at least one of the allocators defines + it, and work similarly to $(D reallocate). + */ + bool alignedReallocate(ref void[] b, size_t s); + /** + This method is defined only if both allocators define it. The call is + forwarded to $(D SmallAllocator) if $(D b.length <= threshold), or $(D + LargeAllocator) otherwise. + */ + Ternary owns(void[] b); + /** + This function is defined only if both allocators define it, and forwards + appropriately depending on $(D b.length). + */ + bool deallocate(void[] b); + /** + This function is defined only if both allocators define it, and calls + $(D deallocateAll) for them in turn. + */ + bool deallocateAll(); + /** + This function is defined only if both allocators define it, and returns + the conjunction of $(D empty) calls for the two. + */ + Ternary empty(); + } + + /** + Composite allocators involving nested instantiations of $(D Segregator) make + it difficult to access individual sub-allocators stored within. $(D + allocatorForSize) simplifies the task by supplying the allocator nested + inside a $(D Segregator) that is responsible for a specific size $(D s). + + Example: + ---- + alias A = Segregator!(300, + Segregator!(200, A1, A2), + A3); + A a; + static assert(typeof(a.allocatorForSize!10) == A1); + static assert(typeof(a.allocatorForSize!250) == A2); + static assert(typeof(a.allocatorForSize!301) == A3); + ---- + */ + ref auto allocatorForSize(size_t s)() + { + static if (s <= threshold) + static if (is(SmallAllocator == Segregator!(Args), Args...)) + return _small.allocatorForSize!s; + else return _small; + else + static if (is(LargeAllocator == Segregator!(Args), Args...)) + return _large.allocatorForSize!s; + else return _large; + } + + enum uint alignment = min(SmallAllocator.alignment, + LargeAllocator.alignment); + + private template Impl() + { + size_t goodAllocSize(size_t s) + { + return s <= threshold + ? _small.goodAllocSize(s) + : _large.goodAllocSize(s); + } + + void[] allocate(size_t s) + { + return s <= threshold ? _small.allocate(s) : _large.allocate(s); + } + + static if (hasMember!(SmallAllocator, "alignedAllocate") + && hasMember!(LargeAllocator, "alignedAllocate")) + void[] alignedAllocate(size_t s, uint a) + { + return s <= threshold + ? _small.alignedAllocate(s, a) + : _large.alignedAllocate(s, a); + } + + static if (hasMember!(SmallAllocator, "expand") + || hasMember!(LargeAllocator, "expand")) + bool expand(ref void[] b, size_t delta) + { + if (!delta) return true; + if (b.length + delta <= threshold) + { + // Old and new allocations handled by _small + static if (hasMember!(SmallAllocator, "expand")) + return _small.expand(b, delta); + else + return false; + } + if (b.length > threshold) + { + // Old and new allocations handled by _large + static if (hasMember!(LargeAllocator, "expand")) + return _large.expand(b, delta); + else + return false; + } + // Oops, cross-allocator transgression + return false; + } + + static if (hasMember!(SmallAllocator, "reallocate") + || hasMember!(LargeAllocator, "reallocate")) + bool reallocate(ref void[] b, size_t s) + { + static if (hasMember!(SmallAllocator, "reallocate")) + if (b.length <= threshold && s <= threshold) + { + // Old and new allocations handled by _small + return _small.reallocate(b, s); + } + static if (hasMember!(LargeAllocator, "reallocate")) + if (b.length > threshold && s > threshold) + { + // Old and new allocations handled by _large + return _large.reallocate(b, s); + } + // Cross-allocator transgression + return .reallocate(this, b, s); + } + + static if (hasMember!(SmallAllocator, "alignedReallocate") + || hasMember!(LargeAllocator, "alignedReallocate")) + bool reallocate(ref void[] b, size_t s) + { + static if (hasMember!(SmallAllocator, "alignedReallocate")) + if (b.length <= threshold && s <= threshold) + { + // Old and new allocations handled by _small + return _small.alignedReallocate(b, s); + } + static if (hasMember!(LargeAllocator, "alignedReallocate")) + if (b.length > threshold && s > threshold) + { + // Old and new allocations handled by _large + return _large.alignedReallocate(b, s); + } + // Cross-allocator transgression + return .alignedReallocate(this, b, s); + } + + static if (hasMember!(SmallAllocator, "owns") + && hasMember!(LargeAllocator, "owns")) + Ternary owns(void[] b) + { + return Ternary(b.length <= threshold + ? _small.owns(b) : _large.owns(b)); + } + + static if (hasMember!(SmallAllocator, "deallocate") + && hasMember!(LargeAllocator, "deallocate")) + bool deallocate(void[] data) + { + return data.length <= threshold + ? _small.deallocate(data) + : _large.deallocate(data); + } + + static if (hasMember!(SmallAllocator, "deallocateAll") + && hasMember!(LargeAllocator, "deallocateAll")) + bool deallocateAll() + { + // Use & insted of && to evaluate both + return _small.deallocateAll() & _large.deallocateAll(); + } + + static if (hasMember!(SmallAllocator, "empty") + && hasMember!(LargeAllocator, "empty")) + Ternary empty() + { + return _small.empty && _large.empty; + } + + static if (hasMember!(SmallAllocator, "resolveInternalPointer") + && hasMember!(LargeAllocator, "resolveInternalPointer")) + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + Ternary r = _small.resolveInternalPointer(p, result); + return r == Ternary.no ? _large.resolveInternalPointer(p, result) : r; + } + } + + private enum sharedMethods = + !stateSize!SmallAllocator + && !stateSize!LargeAllocator + && is(typeof(SmallAllocator.instance) == shared) + && is(typeof(LargeAllocator.instance) == shared); + + static if (sharedMethods) + { + static shared Segregator instance; + shared { mixin Impl!(); } + } + else + { + static if (!stateSize!SmallAllocator && !stateSize!LargeAllocator) + static __gshared Segregator instance; + mixin Impl!(); + } +} + +/// +@system unittest +{ + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + alias A = + Segregator!( + 1024 * 4, + Segregator!( + 128, FreeList!(Mallocator, 0, 128), + GCAllocator), + Segregator!( + 1024 * 1024, Mallocator, + GCAllocator) + ); + A a; + auto b = a.allocate(200); + assert(b.length == 200); + a.deallocate(b); +} + +/** +A $(D Segregator) with more than three arguments expands to a composition of +elemental $(D Segregator)s, as illustrated by the following example: + +---- +alias A = + Segregator!( + n1, A1, + n2, A2, + n3, A3, + A4 + ); +---- + +With this definition, allocation requests for $(D n1) bytes or less are directed +to $(D A1); requests between $(D n1 + 1) and $(D n2) bytes (inclusive) are +directed to $(D A2); requests between $(D n2 + 1) and $(D n3) bytes (inclusive) +are directed to $(D A3); and requests for more than $(D n3) bytes are directed +to $(D A4). If some particular range should not be handled, $(D NullAllocator) +may be used appropriately. + +*/ +template Segregator(Args...) +if (Args.length > 3) +{ + // Binary search + private enum cutPoint = ((Args.length - 2) / 4) * 2; + static if (cutPoint >= 2) + { + alias Segregator = .Segregator!( + Args[cutPoint], + .Segregator!(Args[0 .. cutPoint], Args[cutPoint + 1]), + .Segregator!(Args[cutPoint + 2 .. $]) + ); + } + else + { + // Favor small sizes + alias Segregator = .Segregator!( + Args[0], + Args[1], + .Segregator!(Args[2 .. $]) + ); + } +} + +/// +@system unittest +{ + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + alias A = + Segregator!( + 128, FreeList!(Mallocator, 0, 128), + 1024 * 4, GCAllocator, + 1024 * 1024, Mallocator, + GCAllocator + ); + A a; + auto b = a.allocate(201); + assert(b.length == 201); + a.deallocate(b); +} diff --git a/std/experimental/allocator/building_blocks/stats_collector.d b/std/experimental/allocator/building_blocks/stats_collector.d new file mode 100644 index 00000000000..327c76b3618 --- /dev/null +++ b/std/experimental/allocator/building_blocks/stats_collector.d @@ -0,0 +1,735 @@ +// Written in the D programming language. +/** +Allocator that collects useful statistics about allocations, both global and per +calling point. The statistics collected can be configured statically by choosing +combinations of `Options` appropriately. + +Example: +---- +import std.experimental.allocator.gc_allocator : GCAllocator; +import std.experimental.allocator.building_blocks.free_list : FreeList; +alias Allocator = StatsCollector!(GCAllocator, Options.bytesUsed); +---- +*/ +module std.experimental.allocator.building_blocks.stats_collector; + +import std.experimental.allocator.common; + +/** +_Options for $(D StatsCollector) defined below. Each enables during +compilation one specific counter, statistic, or other piece of information. +*/ +enum Options : ulong +{ + /** + Counts the number of calls to $(D owns). + */ + numOwns = 1u << 0, + /** + Counts the number of calls to $(D allocate). All calls are counted, + including requests for zero bytes or failed requests. + */ + numAllocate = 1u << 1, + /** + Counts the number of calls to $(D allocate) that succeeded, i.e. they + returned a block as large as requested. (N.B. requests for zero bytes count + as successful.) + */ + numAllocateOK = 1u << 2, + /** + Counts the number of calls to $(D expand), regardless of arguments or + result. + */ + numExpand = 1u << 3, + /** + Counts the number of calls to $(D expand) that resulted in a successful + expansion. + */ + numExpandOK = 1u << 4, + /** + Counts the number of calls to $(D reallocate), regardless of arguments or + result. + */ + numReallocate = 1u << 5, + /** + Counts the number of calls to $(D reallocate) that succeeded. + (Reallocations to zero bytes count as successful.) + */ + numReallocateOK = 1u << 6, + /** + Counts the number of calls to $(D reallocate) that resulted in an in-place + reallocation (no memory moved). If this number is close to the total number + of reallocations, that indicates the allocator finds room at the current + block's end in a large fraction of the cases, but also that internal + fragmentation may be high (the size of the unit of allocation is large + compared to the typical allocation size of the application). + */ + numReallocateInPlace = 1u << 7, + /** + Counts the number of calls to $(D deallocate). + */ + numDeallocate = 1u << 8, + /** + Counts the number of calls to $(D deallocateAll). + */ + numDeallocateAll = 1u << 9, + /** + Chooses all $(D numXxx) flags. + */ + numAll = (1u << 10) - 1, + /** + Tracks bytes currently allocated by this allocator. This number goes up + and down as memory is allocated and deallocated, and is zero if the + allocator currently has no active allocation. + */ + bytesUsed = 1u << 10, + /** + Tracks total cumulative bytes allocated by means of $(D allocate), + $(D expand), and $(D reallocate) (when resulting in an expansion). This + number always grows and indicates allocation traffic. To compute bytes + deallocated cumulatively, subtract $(D bytesUsed) from $(D bytesAllocated). + */ + bytesAllocated = 1u << 11, + /** + Tracks the sum of all $(D delta) values in calls of the form + $(D expand(b, delta)) that succeed (return $(D true)). + */ + bytesExpanded = 1u << 12, + /** + Tracks the sum of all $(D b.length - s) with $(D b.length > s) in calls of + the form $(D realloc(b, s)) that succeed (return $(D true)). In per-call + statistics, also unambiguously counts the bytes deallocated with + $(D deallocate). + */ + bytesContracted = 1u << 13, + /** + Tracks the sum of all bytes moved as a result of calls to $(D realloc) that + were unable to reallocate in place. A large number (relative to $(D + bytesAllocated)) indicates that the application should use larger + preallocations. + */ + bytesMoved = 1u << 14, + /** + Tracks the sum of all bytes NOT moved as result of calls to $(D realloc) + that managed to reallocate in place. A large number (relative to $(D + bytesAllocated)) indicates that the application is expansion-intensive and + is saving a good amount of moves. However, if this number is relatively + small and $(D bytesSlack) is high, it means the application is + overallocating for little benefit. + */ + bytesNotMoved = 1u << 15, + /** + Measures the sum of extra bytes allocated beyond the bytes requested, i.e. + the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current + effective number of slack bytes, and it goes up and down with time. + */ + bytesSlack = 1u << 16, + /** + Measures the maximum bytes allocated over the time. This is useful for + dimensioning allocators. + */ + bytesHighTide = 1u << 17, + /** + Chooses all $(D byteXxx) flags. + */ + bytesAll = ((1u << 18) - 1) & ~numAll, + /** + Combines all flags above. + */ + all = (1u << 18) - 1 +} + +/** + +Allocator that collects extra data about allocations. Since each piece of +information adds size and time overhead, statistics can be individually enabled +or disabled through compile-time $(D flags). + +All stats of the form $(D numXxx) record counts of events occurring, such as +calls to functions and specific results. The stats of the form $(D bytesXxx) +collect cumulative sizes. + +In addition, the data $(D callerSize), $(D callerModule), $(D callerFile), $(D +callerLine), and $(D callerTime) is associated with each specific allocation. +This data prefixes each allocation. + +*/ +struct StatsCollector(Allocator, ulong flags = Options.all, + ulong perCallFlags = 0) +{ +private: + import std.traits : hasMember, Signed; + import std.typecons : Ternary; + + static string define(string type, string[] names...) + { + string result; + foreach (v; names) + result ~= "static if (flags & Options."~v~") {" + ~ "private "~type~" _"~v~";" + ~ "public const("~type~") "~v~"() const { return _"~v~"; }" + ~ "}"; + return result; + } + + void add(string counter)(Signed!size_t n) + { + mixin("static if (flags & Options." ~ counter + ~ ") _" ~ counter ~ " += n;"); + static if (counter == "bytesUsed" && (flags & Options.bytesHighTide)) + { + if (bytesHighTide < bytesUsed ) _bytesHighTide = bytesUsed; + } + } + + void up(string counter)() { add!counter(1); } + void down(string counter)() { add!counter(-1); } + + version (StdDdoc) + { + /** + Read-only properties enabled by the homonym $(D flags) chosen by the + user. + + Example: + ---- + StatsCollector!(Mallocator, + Options.bytesUsed | Options.bytesAllocated) a; + auto d1 = a.allocate(10); + auto d2 = a.allocate(11); + a.deallocate(d1); + assert(a.bytesAllocated == 21); + assert(a.bytesUsed == 11); + a.deallocate(d2); + assert(a.bytesAllocated == 21); + assert(a.bytesUsed == 0); + ---- + */ + @property ulong numOwns() const; + /// Ditto + @property ulong numAllocate() const; + /// Ditto + @property ulong numAllocateOK() const; + /// Ditto + @property ulong numExpand() const; + /// Ditto + @property ulong numExpandOK() const; + /// Ditto + @property ulong numReallocate() const; + /// Ditto + @property ulong numReallocateOK() const; + /// Ditto + @property ulong numReallocateInPlace() const; + /// Ditto + @property ulong numDeallocate() const; + /// Ditto + @property ulong numDeallocateAll() const; + /// Ditto + @property ulong bytesUsed() const; + /// Ditto + @property ulong bytesAllocated() const; + /// Ditto + @property ulong bytesExpanded() const; + /// Ditto + @property ulong bytesContracted() const; + /// Ditto + @property ulong bytesMoved() const; + /// Ditto + @property ulong bytesNotMoved() const; + /// Ditto + @property ulong bytesSlack() const; + /// Ditto + @property ulong bytesHighTide() const; + } + +public: + /** + The parent allocator is publicly accessible either as a direct member if it + holds state, or as an alias to `Allocator.instance` otherwise. One may use + it for making calls that won't count toward statistics collection. + */ + static if (stateSize!Allocator) Allocator parent; + else alias parent = Allocator.instance; + +private: + // Per-allocator state + mixin(define("ulong", + "numOwns", + "numAllocate", + "numAllocateOK", + "numExpand", + "numExpandOK", + "numReallocate", + "numReallocateOK", + "numReallocateInPlace", + "numDeallocate", + "numDeallocateAll", + "bytesUsed", + "bytesAllocated", + "bytesExpanded", + "bytesContracted", + "bytesMoved", + "bytesNotMoved", + "bytesSlack", + "bytesHighTide", + )); + +public: + + /// Alignment offered is equal to $(D Allocator.alignment). + alias alignment = Allocator.alignment; + + /** + Increments $(D numOwns) (per instance and and per call) and forwards to $(D + parent.owns(b)). + */ + static if (hasMember!(Allocator, "owns")) + { + static if ((perCallFlags & Options.numOwns) == 0) + Ternary owns(void[] b) + { return ownsImpl(b); } + else + Ternary owns(string f = __FILE, uint n = line)(void[] b) + { return ownsImpl!(f, n)(b); } + } + + private Ternary ownsImpl(string f = null, uint n = 0)(void[] b) + { + up!"numOwns"; + addPerCall!(f, n, "numOwns")(1); + return parent.owns(b); + } + + /** + Forwards to $(D parent.allocate). Affects per instance: $(D numAllocate), + $(D bytesUsed), $(D bytesAllocated), $(D bytesSlack), $(D numAllocateOK), + and $(D bytesHighTide). Affects per call: $(D numAllocate), $(D + numAllocateOK), and $(D bytesAllocated). + */ + static if (!(perCallFlags + & (Options.numAllocate | Options.numAllocateOK + | Options.bytesAllocated))) + { + void[] allocate(size_t n) + { return allocateImpl(n); } + } + else + { + void[] allocate(string f = __FILE__, ulong n = __LINE__) + (size_t bytes) + { return allocateImpl!(f, n)(bytes); } + } + + private void[] allocateImpl(string f = null, ulong n = 0)(size_t bytes) + { + auto result = parent.allocate(bytes); + add!"bytesUsed"(result.length); + add!"bytesAllocated"(result.length); + immutable slack = this.goodAllocSize(result.length) - result.length; + add!"bytesSlack"(slack); + up!"numAllocate"; + add!"numAllocateOK"(result.length == bytes); // allocating 0 bytes is OK + addPerCall!(f, n, "numAllocate", "numAllocateOK", "bytesAllocated") + (1, result.length == bytes, result.length); + return result; + } + + /** + Defined whether or not $(D Allocator.expand) is defined. Affects + per instance: $(D numExpand), $(D numExpandOK), $(D bytesExpanded), + $(D bytesSlack), $(D bytesAllocated), and $(D bytesUsed). Affects per call: + $(D numExpand), $(D numExpandOK), $(D bytesExpanded), and + $(D bytesAllocated). + */ + static if (!(perCallFlags + & (Options.numExpand | Options.numExpandOK | Options.bytesExpanded))) + { + bool expand(ref void[] b, size_t delta) + { return expandImpl(b, delta); } + } + else + { + bool expand(string f = __FILE__, uint n = __LINE__) + (ref void[] b, size_t delta) + { return expandImpl!(f, n)(b, delta); } + } + + private bool expandImpl(string f = null, uint n = 0)(ref void[] b, size_t s) + { + up!"numExpand"; + Signed!size_t slack = 0; + static if (!hasMember!(Allocator, "expand")) + { + auto result = s == 0; + } + else + { + immutable bytesSlackB4 = this.goodAllocSize(b.length) - b.length; + auto result = parent.expand(b, s); + if (result) + { + up!"numExpandOK"; + add!"bytesUsed"(s); + add!"bytesAllocated"(s); + add!"bytesExpanded"(s); + slack = Signed!size_t(this.goodAllocSize(b.length) - b.length + - bytesSlackB4); + add!"bytesSlack"(slack); + } + } + immutable xtra = result ? s : 0; + addPerCall!(f, n, "numExpand", "numExpandOK", "bytesExpanded", + "bytesAllocated") + (1, result, xtra, xtra); + return result; + } + + /** + Defined whether or not $(D Allocator.reallocate) is defined. Affects + per instance: $(D numReallocate), $(D numReallocateOK), $(D + numReallocateInPlace), $(D bytesNotMoved), $(D bytesAllocated), $(D + bytesSlack), $(D bytesExpanded), and $(D bytesContracted). Affects per call: + $(D numReallocate), $(D numReallocateOK), $(D numReallocateInPlace), + $(D bytesNotMoved), $(D bytesExpanded), $(D bytesContracted), and + $(D bytesMoved). + */ + static if (!(perCallFlags + & (Options.numReallocate | Options.numReallocateOK + | Options.numReallocateInPlace | Options.bytesNotMoved + | Options.bytesExpanded | Options.bytesContracted + | Options.bytesMoved))) + { + bool reallocate(ref void[] b, size_t s) + { return reallocateImpl(b, s); } + } + else + { + bool reallocate(string f = __FILE__, ulong n = __LINE__) + (ref void[] b, size_t s) + { return reallocateImpl!(f, n)(b, s); } + } + + private bool reallocateImpl(string f = null, uint n = 0) + (ref void[] b, size_t s) + { + up!"numReallocate"; + const bytesSlackB4 = this.goodAllocSize(b.length) - b.length; + const oldB = b.ptr; + const oldLength = b.length; + + const result = parent.reallocate(b, s); + + Signed!size_t slack = 0; + bool wasInPlace = false; + Signed!size_t delta = 0; + + if (result) + { + up!"numReallocateOK"; + slack = (this.goodAllocSize(b.length) - b.length) - bytesSlackB4; + add!"bytesSlack"(slack); + add!"bytesUsed"(Signed!size_t(b.length - oldLength)); + if (oldB == b.ptr) + { + // This was an in-place reallocation, yay + wasInPlace = true; + up!"numReallocateInPlace"; + add!"bytesNotMoved"(oldLength); + delta = b.length - oldLength; + if (delta >= 0) + { + // Expansion + add!"bytesAllocated"(delta); + add!"bytesExpanded"(delta); + } + else + { + // Contraction + add!"bytesContracted"(-delta); + } + } + else + { + // This was a allocate-move-deallocate cycle + add!"bytesAllocated"(b.length); + add!"bytesMoved"(oldLength); + } + } + addPerCall!(f, n, "numReallocate", "numReallocateOK", + "numReallocateInPlace", "bytesNotMoved", + "bytesExpanded", "bytesContracted", "bytesMoved") + (1, result, wasInPlace, wasInPlace ? oldLength : 0, + delta >= 0 ? delta : 0, delta < 0 ? -delta : 0, + wasInPlace ? 0 : oldLength); + return result; + } + + /** + Defined whether or not $(D Allocator.deallocate) is defined. Affects + per instance: $(D numDeallocate), $(D bytesUsed), and $(D bytesSlack). + Affects per call: $(D numDeallocate) and $(D bytesContracted). + */ + static if (!(perCallFlags & + (Options.numDeallocate | Options.bytesContracted))) + bool deallocate(void[] b) + { return deallocateImpl(b); } + else + bool deallocate(string f = __FILE__, uint n = __LINE__)(void[] b) + { return deallocateImpl!(f, n)(b); } + + private bool deallocateImpl(string f = null, uint n = 0)(void[] b) + { + up!"numDeallocate"; + add!"bytesUsed"(-Signed!size_t(b.length)); + add!"bytesSlack"(-(this.goodAllocSize(b.length) - b.length)); + addPerCall!(f, n, "numDeallocate", "bytesContracted")(1, b.length); + static if (hasMember!(Allocator, "deallocate")) + return parent.deallocate(b); + else + return false; + } + + static if (hasMember!(Allocator, "deallocateAll")) + { + /** + Defined only if $(D Allocator.deallocateAll) is defined. Affects + per instance and per call $(D numDeallocateAll). + */ + static if (!(perCallFlags & Options.numDeallocateAll)) + bool deallocateAll() + { return deallocateAllImpl(); } + else + bool deallocateAll(string f = __FILE__, uint n = __LINE__)() + { return deallocateAllImpl!(f, n)(); } + + private bool deallocateAllImpl(string f = null, uint n = 0)() + { + up!"numDeallocateAll"; + addPerCall!(f, n, "numDeallocateAll")(1); + static if ((flags & Options.bytesUsed)) + _bytesUsed = 0; + return parent.deallocateAll(); + } + } + + /** + Defined only if $(D Options.bytesUsed) is defined. Returns $(D bytesUsed == + 0). + */ + static if (flags & Options.bytesUsed) + Ternary empty() + { + return Ternary(_bytesUsed == 0); + } + + /** + Reports per instance statistics to $(D output) (e.g. $(D stdout)). The + format is simple: one kind and value per line, separated by a colon, e.g. + $(D bytesAllocated:7395404) + */ + void reportStatistics(R)(auto ref R output) + { + import std.traits : EnumMembers; + import std.conv : to; + foreach (e; EnumMembers!Options) + { + static if ((flags & e) && e != Options.numAll + && e != Options.bytesAll && e != Options.all) + output.write(e.to!string, ":", mixin(e.to!string), '\n'); + } + } + + static if (perCallFlags) + { + /** + Defined if $(D perCallFlags) is nonzero. + */ + struct PerCallStatistics + { + /// The file and line of the call. + string file; + /// Ditto + uint line; + /// The options corresponding to the statistics collected. + Options[] opts; + /// The values of the statistics. Has the same length as $(D opts). + ulong[] values; + // Next in the chain. + private PerCallStatistics* next; + + /** + Format to a string such as: + $(D mymodule.d(655): [numAllocate:21, numAllocateOK:21, bytesAllocated:324202]). + */ + string toString() const + { + import std.conv : text, to; + auto result = text(file, "(", line, "): ["); + foreach (i, opt; opts) + { + if (i) result ~= ", "; + result ~= opt.to!string; + result ~= ':'; + result ~= values[i].to!string; + } + return result ~= "]"; + } + } + private static PerCallStatistics* root; + + /** + Defined if $(D perCallFlags) is nonzero. Iterates all monitored + file/line instances. The order of iteration is not meaningful (items + are inserted at the front of a list upon the first call), so + preprocessing the statistics after collection might be appropriate. + */ + static auto byFileLine() + { + static struct Voldemort + { + PerCallStatistics* current; + bool empty() { return !current; } + ref PerCallStatistics front() { return *current; } + void popFront() { current = current.next; } + auto save() { return this; } + } + return Voldemort(root); + } + + /** + Defined if $(D perCallFlags) is nonzero. Outputs (e.g. to a $(D File)) + a simple report of the collected per-call statistics. + */ + static void reportPerCallStatistics(R)(auto ref R output) + { + output.write("Stats for: ", StatsCollector.stringof, '\n'); + foreach (ref stat; byFileLine) + { + output.write(stat, '\n'); + } + } + + private PerCallStatistics* statsAt(string f, uint n, opts...)() + { + import std.range : repeat; + import std.array : array; + + static PerCallStatistics s = { f, n, [ opts ], + repeat(0UL, opts.length).array }; + static bool inserted; + + if (!inserted) + { + // Insert as root + s.next = root; + root = &s; + inserted = true; + } + return &s; + } + + private void addPerCall(string f, uint n, names...)(ulong[] values...) + { + import std.array : join; + enum uint mask = mixin("Options."~[names].join("|Options.")); + static if (perCallFlags & mask) + { + // Per allocation info + auto ps = mixin("statsAt!(f, n," + ~ "Options."~[names].join(", Options.") + ~")"); + foreach (i; 0 .. names.length) + { + ps.values[i] += values[i]; + } + } + } + } + else + { + private void addPerCall(string f, uint n, names...)(ulong[]...) + { + } + } +} + +/// +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.free_list : FreeList; + alias Allocator = StatsCollector!(GCAllocator, Options.all, Options.all); + + Allocator alloc; + auto b = alloc.allocate(10); + alloc.reallocate(b, 20); + alloc.deallocate(b); + + import std.file : deleteme, remove; + import std.stdio : File; + import std.range : walkLength; + + auto f = deleteme ~ "-dlang.std.experimental.allocator.stats_collector.txt"; + scope(exit) remove(f); + Allocator.reportPerCallStatistics(File(f, "w")); + alloc.reportStatistics(File(f, "a")); + assert(File(f).byLine.walkLength == 22); +} + +@system unittest +{ + void test(Allocator)() + { + import std.range : walkLength; + import std.stdio : writeln; + Allocator a; + auto b1 = a.allocate(100); + assert(a.numAllocate == 1); + assert(a.expand(b1, 0)); + assert(a.reallocate(b1, b1.length + 1)); + auto b2 = a.allocate(101); + assert(a.numAllocate == 2); + assert(a.bytesAllocated == 202); + assert(a.bytesUsed == 202); + auto b3 = a.allocate(202); + assert(a.numAllocate == 3); + assert(a.bytesAllocated == 404); + + a.deallocate(b2); + assert(a.numDeallocate == 1); + a.deallocate(b1); + assert(a.numDeallocate == 2); + a.deallocate(b3); + assert(a.numDeallocate == 3); + assert(a.numAllocate == a.numDeallocate); + assert(a.bytesUsed == 0); + } + + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.free_list : FreeList; + test!(StatsCollector!(GCAllocator, Options.all, Options.all)); + test!(StatsCollector!(FreeList!(GCAllocator, 128), Options.all, + Options.all)); +} + +@system unittest +{ + void test(Allocator)() + { + import std.range : walkLength; + import std.stdio : writeln; + Allocator a; + auto b1 = a.allocate(100); + assert(a.expand(b1, 0)); + assert(a.reallocate(b1, b1.length + 1)); + auto b2 = a.allocate(101); + auto b3 = a.allocate(202); + + a.deallocate(b2); + a.deallocate(b1); + a.deallocate(b3); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.free_list : FreeList; + test!(StatsCollector!(GCAllocator, 0, 0)); +} diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d new file mode 100644 index 00000000000..c1d2540b0c8 --- /dev/null +++ b/std/experimental/allocator/common.d @@ -0,0 +1,683 @@ +/** +Utility and ancillary artifacts of `std.experimental.allocator`. This module +shouldn't be used directly; its functionality will be migrated into more +appropriate parts of `std`. + +Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`) +*/ +module std.experimental.allocator.common; +import std.algorithm.comparison, std.traits; + +/** +Returns the size in bytes of the state that needs to be allocated to hold an +object of type $(D T). $(D stateSize!T) is zero for $(D struct)s that are not +nested and have no nonstatic member variables. + */ +template stateSize(T) +{ + static if (is(T == class) || is(T == interface)) + enum stateSize = __traits(classInstanceSize, T); + else static if (is(T == struct) || is(T == union)) + enum stateSize = Fields!T.length || isNested!T ? T.sizeof : 0; + else static if (is(T == void)) + enum size_t stateSize = 0; + else + enum stateSize = T.sizeof; +} + +@safe @nogc nothrow pure +unittest +{ + static assert(stateSize!void == 0); + struct A {} + static assert(stateSize!A == 0); + struct B { int x; } + static assert(stateSize!B == 4); + interface I1 {} + //static assert(stateSize!I1 == 2 * size_t.sizeof); + class C1 {} + static assert(stateSize!C1 == 3 * size_t.sizeof); + class C2 { char c; } + static assert(stateSize!C2 == 4 * size_t.sizeof); + static class C3 { char c; } + static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof); +} + +/** +Returns `true` if the `Allocator` has the alignment known at compile time; +otherwise it returns `false`. + */ +template hasStaticallyKnownAlignment(Allocator) +{ + enum hasStaticallyKnownAlignment = __traits(compiles, + {enum x = Allocator.alignment;}); +} + +/** +$(D chooseAtRuntime) is a compile-time constant of type $(D size_t) that several +parameterized structures in this module recognize to mean deferral to runtime of +the exact value. For example, $(D BitmappedBlock!(Allocator, 4096)) (described in +detail below) defines a block allocator with block size of 4096 bytes, whereas +$(D BitmappedBlock!(Allocator, chooseAtRuntime)) defines a block allocator that has a +field storing the block size, initialized by the user. +*/ +enum chooseAtRuntime = size_t.max - 1; + +/** +$(D unbounded) is a compile-time constant of type $(D size_t) that several +parameterized structures in this module recognize to mean "infinite" bounds for +the parameter. For example, $(D Freelist) (described in detail below) accepts a +$(D maxNodes) parameter limiting the number of freelist items. If $(D unbounded) +is passed for $(D maxNodes), then there is no limit and no checking for the +number of nodes. +*/ +enum unbounded = size_t.max; + +/** +The alignment that is guaranteed to accommodate any D object allocation on the +current platform. +*/ +enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof); + +/** +The default good size allocation is deduced as $(D n) rounded up to the +allocator's alignment. +*/ +size_t goodAllocSize(A)(auto ref A a, size_t n) +{ + return n.roundUpToMultipleOf(a.alignment); +} + +/** +Returns s rounded up to a multiple of base. +*/ +@safe @nogc nothrow pure +package size_t roundUpToMultipleOf(size_t s, uint base) +{ + assert(base); + auto rem = s % base; + return rem ? s + base - rem : s; +} + +@safe @nogc nothrow pure +unittest +{ + assert(10.roundUpToMultipleOf(11) == 11); + assert(11.roundUpToMultipleOf(11) == 11); + assert(12.roundUpToMultipleOf(11) == 22); + assert(118.roundUpToMultipleOf(11) == 121); +} + +/** +Returns `n` rounded up to a multiple of alignment, which must be a power of 2. +*/ +@safe @nogc nothrow pure +package size_t roundUpToAlignment(size_t n, uint alignment) +{ + import std.math : isPowerOf2; + assert(alignment.isPowerOf2); + immutable uint slack = cast(uint) n & (alignment - 1); + const result = slack + ? n + alignment - slack + : n; + assert(result >= n); + return result; +} + +@safe @nogc nothrow pure +unittest +{ + assert(10.roundUpToAlignment(4) == 12); + assert(11.roundUpToAlignment(2) == 12); + assert(12.roundUpToAlignment(8) == 16); + assert(118.roundUpToAlignment(64) == 128); +} + +/** +Returns `n` rounded down to a multiple of alignment, which must be a power of 2. +*/ +@safe @nogc nothrow pure +package size_t roundDownToAlignment(size_t n, uint alignment) +{ + import std.math : isPowerOf2; + assert(alignment.isPowerOf2); + return n & ~size_t(alignment - 1); +} + +@safe @nogc nothrow pure +unittest +{ + assert(10.roundDownToAlignment(4) == 8); + assert(11.roundDownToAlignment(2) == 10); + assert(12.roundDownToAlignment(8) == 8); + assert(63.roundDownToAlignment(64) == 0); +} + +/** +Advances the beginning of `b` to start at alignment `a`. The resulting buffer +may therefore be shorter. Returns the adjusted buffer, or null if obtaining a +non-empty buffer is impossible. +*/ +@nogc nothrow pure +package void[] roundUpToAlignment(void[] b, uint a) +{ + auto e = b.ptr + b.length; + auto p = cast(void*) roundUpToAlignment(cast(size_t) b.ptr, a); + if (e <= p) return null; + return p[0 .. e - p]; +} + +@nogc nothrow pure +@system unittest +{ + void[] empty; + assert(roundUpToAlignment(empty, 4) == null); + char[128] buf; + // At least one pointer inside buf is 128-aligned + assert(roundUpToAlignment(buf, 128) !is null); +} + +/** +Like `a / b` but rounds the result up, not down. +*/ +@safe @nogc nothrow pure +package size_t divideRoundUp(size_t a, size_t b) +{ + assert(b); + return (a + b - 1) / b; +} + +/** +Returns `s` rounded up to a multiple of `base`. +*/ +@nogc nothrow pure +package void[] roundStartToMultipleOf(void[] s, uint base) +{ + assert(base); + auto p = cast(void*) roundUpToMultipleOf( + cast(size_t) s.ptr, base); + auto end = s.ptr + s.length; + return p[0 .. end - p]; +} + +nothrow pure +@system unittest +{ + void[] p; + assert(roundStartToMultipleOf(p, 16) is null); + p = new ulong[10]; + assert(roundStartToMultipleOf(p, 16) is p); +} + +/** +Returns $(D s) rounded up to the nearest power of 2. +*/ +@safe @nogc nothrow pure +package size_t roundUpToPowerOf2(size_t s) +{ + import std.meta : AliasSeq; + assert(s <= (size_t.max >> 1) + 1); + --s; + static if (size_t.sizeof == 4) + alias Shifts = AliasSeq!(1, 2, 4, 8, 16); + else + alias Shifts = AliasSeq!(1, 2, 4, 8, 16, 32); + foreach (i; Shifts) + { + s |= s >> i; + } + return s + 1; +} + +@safe @nogc nothrow pure +unittest +{ + assert(0.roundUpToPowerOf2 == 0); + assert(1.roundUpToPowerOf2 == 1); + assert(2.roundUpToPowerOf2 == 2); + assert(3.roundUpToPowerOf2 == 4); + assert(7.roundUpToPowerOf2 == 8); + assert(8.roundUpToPowerOf2 == 8); + assert(10.roundUpToPowerOf2 == 16); + assert(11.roundUpToPowerOf2 == 16); + assert(12.roundUpToPowerOf2 == 16); + assert(118.roundUpToPowerOf2 == 128); + assert((size_t.max >> 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); + assert(((size_t.max >> 1) + 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); +} + +/** +Returns the number of trailing zeros of $(D x). +*/ +@safe @nogc nothrow pure +package uint trailingZeros(ulong x) +{ + uint result; + while (result < 64 && !(x & (1UL << result))) + { + ++result; + } + return result; +} + +@safe @nogc nothrow pure +unittest +{ + assert(trailingZeros(0) == 64); + assert(trailingZeros(1) == 0); + assert(trailingZeros(2) == 1); + assert(trailingZeros(3) == 0); + assert(trailingZeros(4) == 2); +} + +/** +Returns `true` if `ptr` is aligned at `alignment`. +*/ +@nogc nothrow pure +package bool alignedAt(T)(T* ptr, uint alignment) +{ + return cast(size_t) ptr % alignment == 0; +} + +/** +Returns the effective alignment of `ptr`, i.e. the largest power of two that is +a divisor of `ptr`. +*/ +@nogc nothrow pure +package uint effectiveAlignment(void* ptr) +{ + return 1U << trailingZeros(cast(size_t) ptr); +} + +@nogc nothrow pure +@system unittest +{ + int x; + assert(effectiveAlignment(&x) >= int.alignof); +} + +/** +Aligns a pointer down to a specified alignment. The resulting pointer is less +than or equal to the given pointer. +*/ +@nogc nothrow pure +package void* alignDownTo(void* ptr, uint alignment) +{ + import std.math : isPowerOf2; + assert(alignment.isPowerOf2); + return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL)); +} + +/** +Aligns a pointer up to a specified alignment. The resulting pointer is greater +than or equal to the given pointer. +*/ +@nogc nothrow pure +package void* alignUpTo(void* ptr, uint alignment) +{ + import std.math : isPowerOf2; + assert(alignment.isPowerOf2); + immutable uint slack = cast(size_t) ptr & (alignment - 1U); + return slack ? ptr + alignment - slack : ptr; +} + +@safe @nogc nothrow pure +package bool isGoodStaticAlignment(uint x) +{ + import std.math : isPowerOf2; + return x.isPowerOf2; +} + +@safe @nogc nothrow pure +package bool isGoodDynamicAlignment(uint x) +{ + import std.math : isPowerOf2; + return x.isPowerOf2 && x >= (void*).sizeof; +} + +/** +The default $(D reallocate) function first attempts to use $(D expand). If $(D +Allocator.expand) is not defined or returns $(D false), $(D reallocate) +allocates a new block of memory of appropriate size and copies data from the old +block to the new block. Finally, if $(D Allocator) defines $(D deallocate), $(D +reallocate) uses it to free the old memory block. + +$(D reallocate) does not attempt to use $(D Allocator.reallocate) even if +defined. This is deliberate so allocators may use it internally within their own +implementation of $(D reallocate). + +*/ +bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s) +{ + if (b.length == s) return true; + static if (hasMember!(Allocator, "expand")) + { + if (b.length <= s && a.expand(b, s - b.length)) return true; + } + auto newB = a.allocate(s); + if (newB.length != s) return false; + if (newB.length <= b.length) newB[] = b[0 .. newB.length]; + else newB[0 .. b.length] = b[]; + static if (hasMember!(Allocator, "deallocate")) + a.deallocate(b); + b = newB; + return true; +} + +/** + +The default $(D alignedReallocate) function first attempts to use $(D expand). +If $(D Allocator.expand) is not defined or returns $(D false), $(D +alignedReallocate) allocates a new block of memory of appropriate size and +copies data from the old block to the new block. Finally, if $(D Allocator) +defines $(D deallocate), $(D alignedReallocate) uses it to free the old memory +block. + +$(D alignedReallocate) does not attempt to use $(D Allocator.reallocate) even if +defined. This is deliberate so allocators may use it internally within their own +implementation of $(D reallocate). + +*/ +bool alignedReallocate(Allocator)(ref Allocator alloc, + ref void[] b, size_t s, uint a) +{ + static if (hasMember!(Allocator, "expand")) + { + if (b.length <= s && b.ptr.alignedAt(a) + && alloc.expand(b, s - b.length)) return true; + } + else + { + if (b.length == s) return true; + } + auto newB = alloc.alignedAllocate(s, a); + if (newB.length <= b.length) newB[] = b[0 .. newB.length]; + else newB[0 .. b.length] = b[]; + static if (hasMember!(Allocator, "deallocate")) + alloc.deallocate(b); + b = newB; + return true; +} + +/** +Forwards each of the methods in `funs` (if defined) to `member`. +*/ +/*package*/ string forwardToMember(string member, string[] funs...) +{ + string result = " import std.traits : hasMember, Parameters;\n"; + foreach (fun; funs) + { + result ~= " + static if (hasMember!(typeof("~member~"), `"~fun~"`)) + auto ref "~fun~"(Parameters!(typeof("~member~"."~fun~")) args) + { + return "~member~"."~fun~"(args); + }\n"; + } + return result; +} + +version(unittest) +{ + import std.experimental.allocator : IAllocator, ISharedAllocator; + + package void testAllocator(alias make)() + { + import std.conv : text; + import std.stdio : writeln, stderr; + import std.math : isPowerOf2; + import std.typecons : Ternary; + alias A = typeof(make()); + scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); + + auto a = make(); + + // Test alignment + static assert(A.alignment.isPowerOf2); + + // Test goodAllocSize + assert(a.goodAllocSize(1) >= A.alignment, + text(a.goodAllocSize(1), " < ", A.alignment)); + assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(A.alignment)); + assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(A.alignment)); + + // Test allocate + assert(a.allocate(0) is null); + + auto b1 = a.allocate(1); + assert(b1.length == 1); + auto b2 = a.allocate(2); + assert(b2.length == 2); + assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); + + // Test alignedAllocate + static if (hasMember!(A, "alignedAllocate")) + {{ + auto b3 = a.alignedAllocate(1, 256); + assert(b3.length <= 1); + assert(b3.ptr.alignedAt(256)); + assert(a.alignedReallocate(b3, 2, 512)); + assert(b3.ptr.alignedAt(512)); + static if (hasMember!(A, "alignedDeallocate")) + { + a.alignedDeallocate(b3); + } + }} + else + { + static assert(!hasMember!(A, "alignedDeallocate")); + // This seems to be a bug in the compiler: + //static assert(!hasMember!(A, "alignedReallocate"), A.stringof); + } + + static if (hasMember!(A, "allocateAll")) + {{ + auto aa = make(); + if (aa.allocateAll().ptr) + { + // Can't get any more memory + assert(!aa.allocate(1).ptr); + } + auto ab = make(); + const b4 = ab.allocateAll(); + assert(b4.length); + // Can't get any more memory + assert(!ab.allocate(1).ptr); + }} + + static if (hasMember!(A, "expand")) + {{ + assert(a.expand(b1, 0)); + auto len = b1.length; + if (a.expand(b1, 102)) + { + assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); + } + auto aa = make(); + void[] b5 = null; + assert(aa.expand(b5, 0)); + assert(b5 is null); + assert(!aa.expand(b5, 1)); + assert(b5.length == 0); + }} + + void[] b6 = null; + assert(a.reallocate(b6, 0)); + assert(b6.length == 0); + assert(a.reallocate(b6, 1)); + assert(b6.length == 1, text(b6.length)); + assert(a.reallocate(b6, 2)); + assert(b6.length == 2); + + // Test owns + static if (hasMember!(A, "owns")) + {{ + assert(a.owns(null) == Ternary.no); + assert(a.owns(b1) == Ternary.yes); + assert(a.owns(b2) == Ternary.yes); + assert(a.owns(b6) == Ternary.yes); + }} + + static if (hasMember!(A, "resolveInternalPointer")) + {{ + void[] p; + assert(a.resolveInternalPointer(null, p) == Ternary.no); + Ternary r = a.resolveInternalPointer(b1.ptr, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b2.ptr, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b6.ptr, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + static int[10] b7 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); + int[3] b8 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); + }} + } + + package void testAllocatorObject(AllocInterface)(AllocInterface a) + if (is(AllocInterface : IAllocator) + || is (AllocInterface : shared ISharedAllocator)) + { + import std.conv : text; + import std.stdio : writeln, stderr; + import std.math : isPowerOf2; + import std.typecons : Ternary; + scope(failure) stderr.writeln("testAllocatorObject failed for ", + AllocInterface.stringof); + + assert(a); + + // Test alignment + assert(a.alignment.isPowerOf2); + + // Test goodAllocSize + assert(a.goodAllocSize(1) >= a.alignment, + text(a.goodAllocSize(1), " < ", a.alignment)); + assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(a.alignment)); + assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(a.alignment)); + + // Test empty + assert(a.empty != Ternary.no); + + // Test allocate + assert(a.allocate(0) is null); + + auto b1 = a.allocate(1); + assert(b1.length == 1); + auto b2 = a.allocate(2); + assert(b2.length == 2); + assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); + + // Test alignedAllocate + { + // If not implemented it will return null, so those should pass + auto b3 = a.alignedAllocate(1, 256); + assert(b3.length <= 1); + assert(b3.ptr.alignedAt(256)); + if (a.alignedReallocate(b3, 1, 256)) + { + // If it is false, then the wrapped allocator did not implement + // this + assert(a.alignedReallocate(b3, 2, 512)); + assert(b3.ptr.alignedAt(512)); + } + } + + // Test allocateAll + { + auto aa = a.allocateAll(); + if (aa.ptr) + { + // Can't get any more memory + assert(!a.allocate(1).ptr); + a.deallocate(aa); + } + const b4 = a.allocateAll(); + if (b4.ptr) + { + // Can't get any more memory + assert(!a.allocate(1).ptr); + } + } + + // Test expand + { + assert(a.expand(b1, 0)); + auto len = b1.length; + if (a.expand(b1, 102)) + { + assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); + } + } + + void[] b6 = null; + assert(a.reallocate(b6, 0)); + assert(b6.length == 0); + assert(a.reallocate(b6, 1)); + assert(b6.length == 1, text(b6.length)); + assert(a.reallocate(b6, 2)); + assert(b6.length == 2); + + // Test owns + { + if (a.owns(null) != Ternary.unknown) + { + assert(a.owns(null) == Ternary.no); + assert(a.owns(b1) == Ternary.yes); + assert(a.owns(b2) == Ternary.yes); + assert(a.owns(b6) == Ternary.yes); + } + } + + // Test resolveInternalPointer + { + void[] p; + if (a.resolveInternalPointer(null, p) != Ternary.unknown) + { + assert(a.resolveInternalPointer(null, p) == Ternary.no); + Ternary r = a.resolveInternalPointer(b1.ptr, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b2.ptr, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b6.ptr, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + static int[10] b7 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); + int[3] b8 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); + } + } + + // Test deallocateAll + { + if (a.deallocateAll()) + { + if (a.empty != Ternary.unknown) + { + assert(a.empty == Ternary.yes); + } + } + } + } +} diff --git a/std/experimental/allocator/gc_allocator.d b/std/experimental/allocator/gc_allocator.d new file mode 100644 index 00000000000..41894568f03 --- /dev/null +++ b/std/experimental/allocator/gc_allocator.d @@ -0,0 +1,167 @@ +/// +module std.experimental.allocator.gc_allocator; +import std.experimental.allocator.common; + +/** +D's built-in garbage-collected allocator. + */ +struct GCAllocator +{ + import core.memory : GC; + import std.typecons : Ternary; + @system unittest { testAllocator!(() => GCAllocator.instance); } + + /** + The alignment is a static constant equal to $(D platformAlignment), which + ensures proper alignment for any D data type. + */ + enum uint alignment = platformAlignment; + + /** + Standard allocator methods per the semantics defined above. The $(D + deallocate) and $(D reallocate) methods are $(D @system) because they may + move memory around, leaving dangling pointers in user code. + */ + pure nothrow @trusted void[] allocate(size_t bytes) shared + { + if (!bytes) return null; + auto p = GC.malloc(bytes); + return p ? p[0 .. bytes] : null; + } + + /// Ditto + @system bool expand(ref void[] b, size_t delta) shared + { + if (delta == 0) return true; + if (b is null) return false; + immutable curLength = GC.sizeOf(b.ptr); + assert(curLength != 0); // we have a valid GC pointer here + immutable desired = b.length + delta; + if (desired > curLength) // check to see if the current block can't hold the data + { + immutable sizeRequest = desired - curLength; + immutable newSize = GC.extend(b.ptr, sizeRequest, sizeRequest); + if (newSize == 0) + { + // expansion unsuccessful + return false; + } + assert(newSize >= desired); + } + b = b.ptr[0 .. desired]; + return true; + } + + /// Ditto + pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared + { + import core.exception : OutOfMemoryError; + try + { + auto p = cast(ubyte*) GC.realloc(b.ptr, newSize); + b = p[0 .. newSize]; + } + catch (OutOfMemoryError) + { + // leave the block in place, tell caller + return false; + } + return true; + } + + /// Ditto + pure nothrow + Ternary resolveInternalPointer(const void* p, ref void[] result) shared + { + auto r = GC.addrOf(cast(void*) p); + if (!r) return Ternary.no; + result = r[0 .. GC.sizeOf(r)]; + return Ternary.yes; + } + + /// Ditto + pure nothrow @system bool deallocate(void[] b) shared + { + GC.free(b.ptr); + return true; + } + + /// Ditto + size_t goodAllocSize(size_t n) shared + { + if (n == 0) + return 0; + if (n <= 16) + return 16; + + import core.bitop : bsr; + + auto largestBit = bsr(n-1) + 1; + if (largestBit <= 12) // 4096 or less + return size_t(1) << largestBit; + + // larger, we use a multiple of 4096. + return ((n + 4095) / 4096) * 4096; + } + + /** + Returns the global instance of this allocator type. The garbage collected + allocator is thread-safe, therefore all of its methods and `instance` itself + are $(D shared). + */ + + static shared GCAllocator instance; + + // Leave it undocummented for now. + nothrow @trusted void collect() shared + { + GC.collect(); + } +} + +/// +@system unittest +{ + auto buffer = GCAllocator.instance.allocate(1024 * 1024 * 4); + // deallocate upon scope's end (alternatively: leave it to collection) + scope(exit) GCAllocator.instance.deallocate(buffer); + //... +} + +@system unittest +{ + auto b = GCAllocator.instance.allocate(10_000); + assert(GCAllocator.instance.expand(b, 1)); +} + +@system unittest +{ + import core.memory : GC; + import std.typecons : Ternary; + + // test allocation sizes + assert(GCAllocator.instance.goodAllocSize(1) == 16); + for (size_t s = 16; s <= 8192; s *= 2) + { + assert(GCAllocator.instance.goodAllocSize(s) == s); + assert(GCAllocator.instance.goodAllocSize(s - (s / 2) + 1) == s); + + auto buffer = GCAllocator.instance.allocate(s); + scope(exit) GCAllocator.instance.deallocate(buffer); + + void[] p; + assert(GCAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); + Ternary r = GCAllocator.instance.resolveInternalPointer(buffer.ptr, p); + assert(p.ptr is buffer.ptr && p.length >= buffer.length); + + assert(GC.sizeOf(buffer.ptr) == s); + + auto buffer2 = GCAllocator.instance.allocate(s - (s / 2) + 1); + scope(exit) GCAllocator.instance.deallocate(buffer2); + + assert(GC.sizeOf(buffer2.ptr) == s); + } + + // anything above a page is simply rounded up to next page + assert(GCAllocator.instance.goodAllocSize(4096 * 4 + 1) == 4096 * 5); +} diff --git a/std/experimental/allocator/mallocator.d b/std/experimental/allocator/mallocator.d new file mode 100644 index 00000000000..5dfdc999966 --- /dev/null +++ b/std/experimental/allocator/mallocator.d @@ -0,0 +1,387 @@ +/// +module std.experimental.allocator.mallocator; +import std.experimental.allocator.common; + +/** + The C heap allocator. + */ +struct Mallocator +{ + @system unittest { testAllocator!(() => Mallocator.instance); } + + /** + The alignment is a static constant equal to $(D platformAlignment), which + ensures proper alignment for any D data type. + */ + enum uint alignment = platformAlignment; + + /** + Standard allocator methods per the semantics defined above. The + $(D deallocate) and $(D reallocate) methods are $(D @system) because they + may move memory around, leaving dangling pointers in user code. Somewhat + paradoxically, $(D malloc) is $(D @safe) but that's only useful to safe + programs that can afford to leak memory allocated. + */ + @trusted @nogc nothrow + void[] allocate(size_t bytes) shared + { + import core.stdc.stdlib : malloc; + if (!bytes) return null; + auto p = malloc(bytes); + return p ? p[0 .. bytes] : null; + } + + /// Ditto + @system @nogc nothrow + bool deallocate(void[] b) shared + { + import core.stdc.stdlib : free; + free(b.ptr); + return true; + } + + /// Ditto + @system @nogc nothrow + bool reallocate(ref void[] b, size_t s) shared + { + import core.stdc.stdlib : realloc; + if (!s) + { + // fuzzy area in the C standard, see http://goo.gl/ZpWeSE + // so just deallocate and nullify the pointer + deallocate(b); + b = null; + return true; + } + auto p = cast(ubyte*) realloc(b.ptr, s); + if (!p) return false; + b = p[0 .. s]; + return true; + } + + /** + Returns the global instance of this allocator type. The C heap allocator is + thread-safe, therefore all of its methods and `it` itself are + $(D shared). + */ + static shared Mallocator instance; +} + +/// +@nogc nothrow +@system unittest +{ + auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4); + scope(exit) Mallocator.instance.deallocate(buffer); + //... +} + +@nogc nothrow +@system unittest +{ + @nogc nothrow + static void test(A)() + { + int* p = null; + p = cast(int*) A.instance.allocate(int.sizeof); + scope(exit) A.instance.deallocate(p[0 .. int.sizeof]); + *p = 42; + assert(*p == 42); + } + test!Mallocator(); +} + +@nogc nothrow +@system unittest +{ + static void test(A)() + { + import std.experimental.allocator : make; + Object p = null; + p = A.instance.make!Object(); + assert(p !is null); + } + + test!Mallocator(); +} + +version (Posix) +@nogc nothrow +private extern(C) int posix_memalign(void**, size_t, size_t); + +version (Windows) +{ + // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx + // functions family (snn.lib) + version(CRuntime_DigitalMars) + { + // Helper to cast the infos written before the aligned pointer + // this header keeps track of the size (required to realloc) and of + // the base ptr (required to free). + private struct AlignInfo + { + void* basePtr; + size_t size; + + @nogc nothrow + static AlignInfo* opCall(void* ptr) + { + return cast(AlignInfo*) (ptr - AlignInfo.sizeof); + } + } + + @nogc nothrow + private void* _aligned_malloc(size_t size, size_t alignment) + { + import std.c.stdlib : malloc; + size_t offset = alignment + size_t.sizeof * 2 - 1; + + // unaligned chunk + void* basePtr = malloc(size + offset); + if (!basePtr) return null; + + // get aligned location within the chunk + void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset) + & ~(alignment - 1)); + + // write the header before the aligned pointer + AlignInfo* head = AlignInfo(alignedPtr); + head.basePtr = basePtr; + head.size = size; + + return alignedPtr; + } + + @nogc nothrow + private void* _aligned_realloc(void* ptr, size_t size, size_t alignment) + { + import std.c.stdlib : free; + import std.c.string : memcpy; + + if (!ptr) return _aligned_malloc(size, alignment); + + // gets the header from the exising pointer + AlignInfo* head = AlignInfo(ptr); + + // gets a new aligned pointer + void* alignedPtr = _aligned_malloc(size, alignment); + if (!alignedPtr) + { + //to https://msdn.microsoft.com/en-us/library/ms235462.aspx + //see Return value: in this case the original block is unchanged + return null; + } + + // copy exising data + memcpy(alignedPtr, ptr, head.size); + free(head.basePtr); + + return alignedPtr; + } + + @nogc nothrow + private void _aligned_free(void *ptr) + { + import std.c.stdlib : free; + if (!ptr) return; + AlignInfo* head = AlignInfo(ptr); + free(head.basePtr); + } + + } + // DMD Win 64 bit, uses microsoft standard C library which implements them + else + { + @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); + @nogc nothrow private extern(C) void _aligned_free(void *memblock); + @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); + } +} + +/** + Aligned allocator using OS-specific primitives, under a uniform API. + */ +struct AlignedMallocator +{ + @system unittest { testAllocator!(() => typeof(this).instance); } + + /** + The default alignment is $(D platformAlignment). + */ + enum uint alignment = platformAlignment; + + /** + Forwards to $(D alignedAllocate(bytes, platformAlignment)). + */ + @trusted @nogc nothrow + void[] allocate(size_t bytes) shared + { + if (!bytes) return null; + return alignedAllocate(bytes, alignment); + } + + /** + Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, + $(D posix_memalign)) on Posix and + $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, + $(D __aligned_malloc)) on Windows. + */ + version(Posix) + @trusted @nogc nothrow + void[] alignedAllocate(size_t bytes, uint a) shared + { + import core.stdc.errno : ENOMEM, EINVAL; + assert(a.isGoodDynamicAlignment); + void* result; + auto code = posix_memalign(&result, a, bytes); + if (code == ENOMEM) + return null; + + else if (code == EINVAL) + { + assert(0, "AlignedMallocator.alignment is not a power of two " + ~"multiple of (void*).sizeof, according to posix_memalign!"); + } + else if (code != 0) + assert(0, "posix_memalign returned an unknown code!"); + + else + return result[0 .. bytes]; + } + else version(Windows) + @trusted @nogc nothrow + void[] alignedAllocate(size_t bytes, uint a) shared + { + auto result = _aligned_malloc(bytes, a); + return result ? result[0 .. bytes] : null; + } + else static assert(0); + + /** + Calls $(D free(b.ptr)) on Posix and + $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, + $(D __aligned_free(b.ptr))) on Windows. + */ + version (Posix) + @system @nogc nothrow + bool deallocate(void[] b) shared + { + import core.stdc.stdlib : free; + free(b.ptr); + return true; + } + else version (Windows) + @system @nogc nothrow + bool deallocate(void[] b) shared + { + _aligned_free(b.ptr); + return true; + } + else static assert(0); + + /** + On Posix, forwards to $(D realloc). On Windows, forwards to + $(D alignedReallocate(b, newSize, platformAlignment)). + */ + version (Posix) + @system @nogc nothrow + bool reallocate(ref void[] b, size_t newSize) shared + { + return Mallocator.instance.reallocate(b, newSize); + } + version (Windows) + @system @nogc nothrow + bool reallocate(ref void[] b, size_t newSize) shared + { + return alignedReallocate(b, newSize, alignment); + } + + /** + On Posix, uses $(D alignedAllocate) and copies data around because there is + no realloc for aligned memory. On Windows, calls + $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx, + $(D __aligned_realloc(b.ptr, newSize, a))). + */ + version (Windows) + @system @nogc nothrow + bool alignedReallocate(ref void[] b, size_t s, uint a) shared + { + if (!s) + { + deallocate(b); + b = null; + return true; + } + auto p = cast(ubyte*) _aligned_realloc(b.ptr, s, a); + if (!p) return false; + b = p[0 .. s]; + return true; + } + + /** + Returns the global instance of this allocator type. The C heap allocator is + thread-safe, therefore all of its methods and `instance` itself are + $(D shared). + */ + static shared AlignedMallocator instance; +} + +/// +@nogc nothrow +@system unittest +{ + auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, + 128); + scope(exit) AlignedMallocator.instance.deallocate(buffer); + //... +} + +version(unittest) version(CRuntime_DigitalMars) +@nogc nothrow +size_t addr(ref void* ptr) { return cast(size_t) ptr; } + +version(CRuntime_DigitalMars) +@nogc nothrow +@system unittest +{ + void* m; + + m = _aligned_malloc(16, 0x10); + if (m) + { + assert((m.addr & 0xF) == 0); + _aligned_free(m); + } + + m = _aligned_malloc(16, 0x100); + if (m) + { + assert((m.addr & 0xFF) == 0); + _aligned_free(m); + } + + m = _aligned_malloc(16, 0x1000); + if (m) + { + assert((m.addr & 0xFFF) == 0); + _aligned_free(m); + } + + m = _aligned_malloc(16, 0x10); + if (m) + { + assert((cast(size_t) m & 0xF) == 0); + m = _aligned_realloc(m, 32, 0x10000); + if (m) assert((m.addr & 0xFFFF) == 0); + _aligned_free(m); + } + + m = _aligned_malloc(8, 0x10); + if (m) + { + *cast(ulong*) m = 0X01234567_89ABCDEF; + m = _aligned_realloc(m, 0x800, 0x1000); + if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF); + _aligned_free(m); + } +} diff --git a/std/experimental/allocator/mmap_allocator.d b/std/experimental/allocator/mmap_allocator.d new file mode 100644 index 00000000000..45986524c04 --- /dev/null +++ b/std/experimental/allocator/mmap_allocator.d @@ -0,0 +1,79 @@ +/// +module std.experimental.allocator.mmap_allocator; + +// MmapAllocator +/** + +Allocator (currently defined only for Posix and Windows) using +$(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) +and $(D $(LUCKY munmap)) directly (or their Windows equivalents). There is no +additional structure: each call to $(D allocate(s)) issues a call to +$(D mmap(null, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), +and each call to $(D deallocate(b)) issues $(D munmap(b.ptr, b.length)). +So $(D MmapAllocator) is usually intended for allocating large chunks to be +managed by fine-granular allocators. + +*/ +struct MmapAllocator +{ + /// The one shared instance. + static shared MmapAllocator instance; + + /** + Alignment is page-size and hardcoded to 4096 (even though on certain systems + it could be larger). + */ + enum size_t alignment = 4096; + + version(Posix) + { + /// Allocator API. + void[] allocate(size_t bytes) shared + { + import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ, + PROT_WRITE, MAP_PRIVATE, MAP_FAILED; + if (!bytes) return null; + auto p = mmap(null, bytes, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (p is MAP_FAILED) return null; + return p[0 .. bytes]; + } + + /// Ditto + bool deallocate(void[] b) shared + { + import core.sys.posix.sys.mman : munmap; + if (b.ptr) munmap(b.ptr, b.length) == 0 || assert(0); + return true; + } + } + else version(Windows) + { + import core.sys.windows.windows : VirtualAlloc, VirtualFree, MEM_COMMIT, + PAGE_READWRITE, MEM_RELEASE; + + /// Allocator API. + void[] allocate(size_t bytes) shared + { + if (!bytes) return null; + auto p = VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE); + if (p == null) + return null; + return p[0 .. bytes]; + } + + /// Ditto + bool deallocate(void[] b) shared + { + return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0; + } + } +} + +@system unittest +{ + alias alloc = MmapAllocator.instance; + auto p = alloc.allocate(100); + assert(p.length == 100); + alloc.deallocate(p); +} diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d new file mode 100644 index 00000000000..d4724830490 --- /dev/null +++ b/std/experimental/allocator/package.d @@ -0,0 +1,3030 @@ +// Written in the D programming language. +/** + +High-level interface for allocators. Implements bundled allocation/creation +and destruction/deallocation of data including `struct`s and `class`es, +and also array primitives related to allocation. This module is the entry point +for both making use of allocators and for their documentation. + +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Make) $(TD + $(LREF make) + $(LREF makeArray) + $(LREF makeMultidimensionalArray) +)) +$(TR $(TD Dispose) $(TD + $(LREF dispose) + $(LREF disposeMultidimensionalArray) +)) +$(TR $(TD Modify) $(TD + $(LREF expandArray) + $(LREF shrinkArray) +)) +$(TR $(TD Global) $(TD + $(LREF processAllocator) + $(LREF theAllocator) +)) +$(TR $(TD Class interface) $(TD + $(LREF allocatorObject) + $(LREF CAllocatorImpl) + $(LREF IAllocator) +)) +) + +Synopsis: +--- +// Allocate an int, initialize it with 42 +int* p = theAllocator.make!int(42); +assert(*p == 42); +// Destroy and deallocate it +theAllocator.dispose(p); + +// Allocate using the global process allocator +p = processAllocator.make!int(100); +assert(*p == 100); +// Destroy and deallocate +processAllocator.dispose(p); + +// Create an array of 50 doubles initialized to -1.0 +double[] arr = theAllocator.makeArray!double(50, -1.0); +// Append two zeros to it +theAllocator.expandArray(arr, 2, 0.0); +// On second thought, take that back +theAllocator.shrinkArray(arr, 2); +// Destroy and deallocate +theAllocator.dispose(arr); +--- + +$(H2 Layered Structure) + +D's allocators have a layered structure in both implementation and documentation: + +$(OL +$(LI A high-level, dynamically-typed layer (described further down in this +module). It consists of an interface called $(LREF IAllocator), which concret; +allocators need to implement. The interface primitives themselves are oblivious +to the type of the objects being allocated; they only deal in `void[]`, by +necessity of the interface being dynamic (as opposed to type-parameterized). +Each thread has a current allocator it uses by default, which is a thread-local +variable $(LREF theAllocator) of type $(LREF IAllocator). The process has a +global _allocator called $(LREF processAllocator), also of type $(LREF +IAllocator). When a new thread is created, $(LREF processAllocator) is copied +into $(LREF theAllocator). An application can change the objects to which these +references point. By default, at application startup, $(LREF processAllocator) +refers to an object that uses D's garbage collected heap. This layer also +include high-level functions such as $(LREF make) and $(LREF dispose) that +comfortably allocate/create and respectively destroy/deallocate objects. This +layer is all needed for most casual uses of allocation primitives.) + +$(LI A mid-level, statically-typed layer for assembling several allocators into +one. It uses properties of the type of the objects being created to route +allocation requests to possibly specialized allocators. This layer is relatively +thin and implemented and documented in the $(MREF +std,experimental,_allocator,typed) module. It allows an interested user to e.g. +use different allocators for arrays versus fixed-sized objects, to the end of +better overall performance.) + +$(LI A low-level collection of highly generic $(I heap building blocks)$(MDASH) +Lego-like pieces that can be used to assemble application-specific allocators. +The real allocation smarts are occurring at this level. This layer is of +interest to advanced applications that want to configure their own allocators. +A good illustration of typical uses of these building blocks is module $(MREF +std,experimental,_allocator,showcase) which defines a collection of frequently- +used preassembled allocator objects. The implementation and documentation entry +point is $(MREF std,experimental,_allocator,building_blocks). By design, the +primitives of the static interface have the same signatures as the $(LREF +IAllocator) primitives but are for the most part optional and driven by static +introspection. The parameterized class $(LREF CAllocatorImpl) offers an +immediate and useful means to package a static low-level _allocator into an +implementation of $(LREF IAllocator).) + +$(LI Core _allocator objects that interface with D's garbage collected heap +($(MREF std,experimental,_allocator,gc_allocator)), the C `malloc` family +($(MREF std,experimental,_allocator,mallocator)), and the OS ($(MREF +std,experimental,_allocator,mmap_allocator)). Most custom allocators would +ultimately obtain memory from one of these core allocators.) +) + +$(H2 Idiomatic Use of $(D std.experimental._allocator)) + +As of this time, $(D std.experimental._allocator) is not integrated with D's +built-in operators that allocate memory, such as `new`, array literals, or +array concatenation operators. That means $(D std.experimental._allocator) is +opt-in$(MDASH)applications need to make explicit use of it. + +For casual creation and disposal of dynamically-allocated objects, use $(LREF +make), $(LREF dispose), and the array-specific functions $(LREF makeArray), +$(LREF expandArray), and $(LREF shrinkArray). These use by default D's garbage +collected heap, but open the application to better configuration options. These +primitives work either with `theAllocator` but also with any allocator obtained +by combining heap building blocks. For example: + +---- +void fun(size_t n) +{ + // Use the current allocator + int[] a1 = theAllocator.makeArray!int(n); + scope(exit) theAllocator.dispose(a1); + ... +} +---- + +To experiment with alternative allocators, set $(LREF theAllocator) for the +current thread. For example, consider an application that allocates many 8-byte +objects. These are not well supported by the default _allocator, so a +$(MREF_ALTTEXT free list _allocator, +std,experimental,_allocator,building_blocks,free_list) would be recommended. +To install one in `main`, the application would use: + +---- +void main() +{ + import std.experimental.allocator.building_blocks.free_list + : FreeList; + theAllocator = allocatorObject(FreeList!8()); + ... +} +---- + +$(H3 Saving the `IAllocator` Reference For Later Use) + +As with any global resource, setting `theAllocator` and `processAllocator` +should not be done often and casually. In particular, allocating memory with +one allocator and deallocating with another causes undefined behavior. +Typically, these variables are set during application initialization phase and +last through the application. + +To avoid this, long-lived objects that need to perform allocations, +reallocations, and deallocations relatively often may want to store a reference +to the _allocator object they use throughout their lifetime. Then, instead of +using `theAllocator` for internal allocation-related tasks, they'd use the +internally held reference. For example, consider a user-defined hash table: + +---- +struct HashTable +{ + private IAllocator _allocator; + this(size_t buckets, IAllocator allocator = theAllocator) { + this._allocator = allocator; + ... + } + // Getter and setter + IAllocator allocator() { return _allocator; } + void allocator(IAllocator a) { assert(empty); _allocator = a; } +} +---- + +Following initialization, the `HashTable` object would consistently use its +$(D _allocator) object for acquiring memory. Furthermore, setting +$(D HashTable._allocator) to point to a different _allocator should be legal but +only if the object is empty; otherwise, the object wouldn't be able to +deallocate its existing state. + +$(H3 Using Allocators without `IAllocator`) + +Allocators assembled from the heap building blocks don't need to go through +`IAllocator` to be usable. They have the same primitives as `IAllocator` and +they work with $(LREF make), $(LREF makeArray), $(LREF dispose) etc. So it +suffice to create allocator objects wherever fit and use them appropriately: + +---- +void fun(size_t n) +{ + // Use a stack-installed allocator for up to 64KB + StackFront!65536 myAllocator; + int[] a2 = myAllocator.makeArray!int(n); + scope(exit) myAllocator.dispose(a2); + ... +} +---- + +In this case, `myAllocator` does not obey the `IAllocator` interface, but +implements its primitives so it can work with `makeArray` by means of duck +typing. + +One important thing to note about this setup is that statically-typed assembled +allocators are almost always faster than allocators that go through +`IAllocator`. An important rule of thumb is: "assemble allocator first, adapt +to `IAllocator` after". A good allocator implements intricate logic by means of +template assembly, and gets wrapped with `IAllocator` (usually by means of +$(LREF allocatorObject)) only once, at client level. + +Copyright: Andrei Alexandrescu 2013-. + +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). + +Authors: $(HTTP erdani.com, Andrei Alexandrescu) + +Source: $(PHOBOSSRC std/experimental/_allocator) + +*/ + +module std.experimental.allocator; + +public import std.experimental.allocator.common, + std.experimental.allocator.typed; + +// Example in the synopsis above +@system unittest +{ + import std.algorithm.comparison : min, max; + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.building_blocks.segregator : Segregator; + import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + import std.experimental.allocator.building_blocks.bitmapped_block + : BitmappedBlock; + + alias FList = FreeList!(GCAllocator, 0, unbounded); + alias A = Segregator!( + 8, FreeList!(GCAllocator, 0, 8), + 128, Bucketizer!(FList, 1, 128, 16), + 256, Bucketizer!(FList, 129, 256, 32), + 512, Bucketizer!(FList, 257, 512, 64), + 1024, Bucketizer!(FList, 513, 1024, 128), + 2048, Bucketizer!(FList, 1025, 2048, 256), + 3584, Bucketizer!(FList, 2049, 3584, 512), + 4072 * 1024, AllocatorList!( + (n) => BitmappedBlock!(4096)( + cast(ubyte[])(GCAllocator.instance.allocate( + max(n, 4072 * 1024))))), + GCAllocator + ); + A tuMalloc; + auto b = tuMalloc.allocate(500); + assert(b.length == 500); + auto c = tuMalloc.allocate(113); + assert(c.length == 113); + assert(tuMalloc.expand(c, 14)); + tuMalloc.deallocate(b); + tuMalloc.deallocate(c); +} + +import std.range.primitives; +import std.traits; +import std.typecons; + +/** +Dynamic allocator interface. Code that defines allocators ultimately implements +this interface. This should be used wherever a uniform type is required for +encapsulating various allocator implementations. + +Composition of allocators is not recommended at this level due to +inflexibility of dynamic interfaces and inefficiencies caused by cascaded +multiple calls. Instead, compose allocators using the static interface defined +in $(A std_experimental_allocator_building_blocks.html, +`std.experimental.allocator.building_blocks`), then adapt the composed +allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below). + +Methods returning $(D Ternary) return $(D Ternary.yes) upon success, +$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not +implemented by the allocator instance. +*/ +interface IAllocator +{ + /** + Returns the alignment offered. + */ + @property uint alignment(); + + /** + Returns the good allocation size that guarantees zero internal + fragmentation. + */ + size_t goodAllocSize(size_t s); + + /** + Allocates `n` bytes of memory. + */ + void[] allocate(size_t, TypeInfo ti = null); + + /** + Allocates `n` bytes of memory with specified alignment `a`. Implementations + that do not support this primitive should always return `null`. + */ + void[] alignedAllocate(size_t n, uint a); + + /** + Allocates and returns all memory available to this allocator. + Implementations that do not support this primitive should always return + `null`. + */ + void[] allocateAll(); + + /** + Expands a memory block in place and returns `true` if successful. + Implementations that don't support this primitive should always return + `false`. + */ + bool expand(ref void[], size_t); + + /// Reallocates a memory block. + bool reallocate(ref void[], size_t); + + /// Reallocates a memory block with specified alignment. + bool alignedReallocate(ref void[] b, size_t size, uint alignment); + + /** + Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if + the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership + cannot be determined. Implementations that don't support this primitive + should always return `Ternary.unknown`. + */ + Ternary owns(void[] b); + + /** + Resolves an internal pointer to the full block allocated. Implementations + that don't support this primitive should always return `Ternary.unknown`. + */ + Ternary resolveInternalPointer(const void* p, ref void[] result); + + /** + Deallocates a memory block. Implementations that don't support this + primitive should always return `false`. A simple way to check that an + allocator supports deallocation is to call $(D deallocate(null)). + */ + bool deallocate(void[] b); + + /** + Deallocates all memory. Implementations that don't support this primitive + should always return `false`. + */ + bool deallocateAll(); + + /** + Returns $(D Ternary.yes) if no memory is currently allocated from this + allocator, $(D Ternary.no) if some allocations are currently active, or + $(D Ternary.unknown) if not supported. + */ + Ternary empty(); +} + +/** +Dynamic shared allocator interface. Code that defines allocators shareable +across threads ultimately implements this interface. This should be used +wherever a uniform type is required for encapsulating various allocator +implementations. + +Composition of allocators is not recommended at this level due to +inflexibility of dynamic interfaces and inefficiencies caused by cascaded +multiple calls. Instead, compose allocators using the static interface defined +in $(A std_experimental_allocator_building_blocks.html, +`std.experimental.allocator.building_blocks`), then adapt the composed +allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below). + +Methods returning $(D Ternary) return $(D Ternary.yes) upon success, +$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not +implemented by the allocator instance. +*/ +interface ISharedAllocator +{ + /** + Returns the alignment offered. + */ + @property uint alignment() shared; + + /** + Returns the good allocation size that guarantees zero internal + fragmentation. + */ + size_t goodAllocSize(size_t s) shared; + + /** + Allocates `n` bytes of memory. + */ + void[] allocate(size_t, TypeInfo ti = null) shared; + + /** + Allocates `n` bytes of memory with specified alignment `a`. Implementations + that do not support this primitive should always return `null`. + */ + void[] alignedAllocate(size_t n, uint a) shared; + + /** + Allocates and returns all memory available to this allocator. + Implementations that do not support this primitive should always return + `null`. + */ + void[] allocateAll() shared; + + /** + Expands a memory block in place and returns `true` if successful. + Implementations that don't support this primitive should always return + `false`. + */ + bool expand(ref void[], size_t) shared; + + /// Reallocates a memory block. + bool reallocate(ref void[], size_t) shared; + + /// Reallocates a memory block with specified alignment. + bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared; + + /** + Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if + the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership + cannot be determined. Implementations that don't support this primitive + should always return `Ternary.unknown`. + */ + Ternary owns(void[] b) shared; + + /** + Resolves an internal pointer to the full block allocated. Implementations + that don't support this primitive should always return `Ternary.unknown`. + */ + Ternary resolveInternalPointer(const void* p, ref void[] result) shared; + + /** + Deallocates a memory block. Implementations that don't support this + primitive should always return `false`. A simple way to check that an + allocator supports deallocation is to call $(D deallocate(null)). + */ + bool deallocate(void[] b) shared; + + /** + Deallocates all memory. Implementations that don't support this primitive + should always return `false`. + */ + bool deallocateAll() shared; + + /** + Returns $(D Ternary.yes) if no memory is currently allocated from this + allocator, $(D Ternary.no) if some allocations are currently active, or + $(D Ternary.unknown) if not supported. + */ + Ternary empty() shared; +} + +shared ISharedAllocator _processAllocator; +IAllocator _threadAllocator; + +shared static this() +{ + assert(!_processAllocator); + import std.experimental.allocator.gc_allocator : GCAllocator; + _processAllocator = sharedAllocatorObject(GCAllocator.instance); +} + +static this() +{ + /* + Forwards the `_threadAllocator` calls to the `_processAllocator` + */ + static class ThreadAllocator : IAllocator + { + override @property uint alignment() + { + return _processAllocator.alignment(); + } + + override size_t goodAllocSize(size_t s) + { + return _processAllocator.goodAllocSize(s); + } + + override void[] allocate(size_t n, TypeInfo ti = null) + { + return _processAllocator.allocate(n, ti); + } + + override void[] alignedAllocate(size_t n, uint a) + { + return _processAllocator.alignedAllocate(n, a); + } + + override void[] allocateAll() + { + return _processAllocator.allocateAll(); + } + + override bool expand(ref void[] b, size_t size) + { + return _processAllocator.expand(b, size); + } + + override bool reallocate(ref void[] b, size_t size) + { + return _processAllocator.reallocate(b, size); + } + + override bool alignedReallocate(ref void[] b, size_t size, uint alignment) + { + return _processAllocator.alignedReallocate(b, size, alignment); + } + + override Ternary owns(void[] b) + { + return _processAllocator.owns(b); + } + + override Ternary resolveInternalPointer(const void* p, ref void[] result) + { + return _processAllocator.resolveInternalPointer(p, result); + } + + override bool deallocate(void[] b) + { + return _processAllocator.deallocate(b); + } + + override bool deallocateAll() + { + return _processAllocator.deallocateAll(); + } + + override Ternary empty() + { + return _processAllocator.empty(); + } + } + + assert(!_threadAllocator); + import std.conv : emplace; + static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState; + _threadAllocator = emplace!(ThreadAllocator)(_threadAllocatorState[]); +} + +/** +Gets/sets the allocator for the current thread. This is the default allocator +that should be used for allocating thread-local memory. For allocating memory +to be shared across threads, use $(D processAllocator) (below). By default, +$(D theAllocator) ultimately fetches memory from $(D processAllocator), which +in turn uses the garbage collected heap. +*/ +nothrow @safe @nogc @property IAllocator theAllocator() +{ + return _threadAllocator; +} + +/// Ditto +nothrow @safe @nogc @property void theAllocator(IAllocator a) +{ + assert(a); + _threadAllocator = a; +} + +/// +@system unittest +{ + // Install a new allocator that is faster for 128-byte allocations. + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; + auto oldAllocator = theAllocator; + scope(exit) theAllocator = oldAllocator; + theAllocator = allocatorObject(FreeList!(GCAllocator, 128)()); + // Use the now changed allocator to allocate an array + const ubyte[] arr = theAllocator.makeArray!ubyte(128); + assert(arr.ptr); + //... +} + +/** +Gets/sets the allocator for the current process. This allocator must be used +for allocating memory shared across threads. Objects created using this +allocator can be cast to $(D shared). +*/ +@property shared(ISharedAllocator) processAllocator() +{ + return _processAllocator; +} + +/// Ditto +@property void processAllocator(shared ISharedAllocator a) +{ + assert(a); + _processAllocator = a; +} + +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.building_blocks.free_list : SharedFreeList; + import std.exception : assertThrown; + import core.exception : AssertError; + + assert(processAllocator); + assert(theAllocator); + + testAllocatorObject(processAllocator); + testAllocatorObject(theAllocator); + + shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL; + shared ISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL); + assert(sharedFLObj); + testAllocatorObject(sharedFLObj); + + // Test processAllocator setter + shared ISharedAllocator oldProcessAllocator = processAllocator; + processAllocator = sharedFLObj; + assert(processAllocator is sharedFLObj); + + testAllocatorObject(processAllocator); + testAllocatorObject(theAllocator); + assertThrown!AssertError(processAllocator = null); + + // Restore initial processAllocator state + processAllocator = oldProcessAllocator; + assert(processAllocator is oldProcessAllocator); + + shared ISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL); + testAllocatorObject(indirectShFLObj); + + IAllocator indirectMallocator = allocatorObject(&Mallocator.instance); + testAllocatorObject(indirectMallocator); +} + +/** +Dynamically allocates (using $(D alloc)) and then creates in the memory +allocated an object of type $(D T), using $(D args) (if any) for its +initialization. Initialization occurs in the memory allocated and is otherwise +semantically the same as $(D T(args)). +(Note that using $(D alloc.make!(T[])) creates a pointer to an (empty) array +of $(D T)s, not an array. To use an allocator to allocate and initialize an +array, use $(D alloc.makeArray!T) described below.) + +Params: +T = Type of the object being created. +alloc = The allocator used for getting the needed memory. It may be an object +implementing the static interface for allocators, or an $(D IAllocator) +reference. +args = Optional arguments used for initializing the created object. If not +present, the object is default constructed. + +Returns: If $(D T) is a class type, returns a reference to the created $(D T) +object. Otherwise, returns a $(D T*) pointing to the created object. In all +cases, returns $(D null) if allocation failed. + +Throws: If $(D T)'s constructor throws, deallocates the allocated memory and +propagates the exception. +*/ +auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args) +{ + import std.algorithm.comparison : max; + import std.conv : emplace, emplaceRef; + auto m = alloc.allocate(max(stateSize!T, 1)); + if (!m.ptr) return null; + + // make can only be @safe if emplace or emplaceRef is `pure` + auto construct() + { + static if (is(T == class)) return emplace!T(m, args); + else + { + // Assume cast is safe as allocation succeeded for `stateSize!T` + auto p = () @trusted { return cast(T*) m.ptr; }(); + emplaceRef(*p, args); + return p; + } + } + + scope(failure) + { + static if (is(typeof(() pure { return construct(); }))) + { + // Assume deallocation is safe because: + // 1) in case of failure, `m` is the only reference to this memory + // 2) `m` is known to originate from `alloc` + () @trusted { alloc.deallocate(m); }(); + } + else + { + alloc.deallocate(m); + } + } + + return construct(); +} + +/// +@system unittest +{ + // Dynamically allocate one integer + const int* p1 = theAllocator.make!int; + // It's implicitly initialized with its .init value + assert(*p1 == 0); + // Dynamically allocate one double, initialize to 42.5 + const double* p2 = theAllocator.make!double(42.5); + assert(*p2 == 42.5); + + // Dynamically allocate a struct + static struct Point + { + int x, y, z; + } + // Use the generated constructor taking field values in order + const Point* p = theAllocator.make!Point(1, 2); + assert(p.x == 1 && p.y == 2 && p.z == 0); + + // Dynamically allocate a class object + static class Customer + { + uint id = uint.max; + this() {} + this(uint id) { this.id = id; } + // ... + } + Customer cust = theAllocator.make!Customer; + assert(cust.id == uint.max); // default initialized + cust = theAllocator.make!Customer(42); + assert(cust.id == 42); + + // explicit passing of outer pointer + static class Outer + { + int x = 3; + class Inner + { + auto getX() { return x; } + } + } + auto outer = theAllocator.make!Outer(); + auto inner = theAllocator.make!(Outer.Inner)(outer); + assert(outer.x == inner.getX); +} + +@system unittest // bugzilla 15639 & 15772 +{ + abstract class Foo {} + class Bar: Foo {} + static assert(!is(typeof(theAllocator.make!Foo))); + static assert( is(typeof(theAllocator.make!Bar))); +} + +@system unittest +{ + void test(Allocator)(auto ref Allocator alloc) + { + const int* a = alloc.make!int(10); + assert(*a == 10); + + struct A + { + int x; + string y; + double z; + } + + A* b = alloc.make!A(42); + assert(b.x == 42); + assert(b.y is null); + import std.math : isNaN; + assert(b.z.isNaN); + + b = alloc.make!A(43, "44", 45); + assert(b.x == 43); + assert(b.y == "44"); + assert(b.z == 45); + + static class B + { + int x; + string y; + double z; + this(int _x, string _y = null, double _z = double.init) + { + x = _x; + y = _y; + z = _z; + } + } + + B c = alloc.make!B(42); + assert(c.x == 42); + assert(c.y is null); + assert(c.z.isNaN); + + c = alloc.make!B(43, "44", 45); + assert(c.x == 43); + assert(c.y == "44"); + assert(c.z == 45); + + const parray = alloc.make!(int[]); + assert((*parray).empty); + } + + import std.experimental.allocator.gc_allocator : GCAllocator; + test(GCAllocator.instance); + test(theAllocator); +} + +// Attribute propagation +nothrow @safe @nogc unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + alias alloc = Mallocator.instance; + + void test(T, Args...)(auto ref Args args) + { + auto k = alloc.make!T(args); + () @trusted { alloc.dispose(k); }(); + } + + test!int; + test!(int*); + test!int(0); + test!(int*)(null); +} + +// should be pure with the GCAllocator +/*pure nothrow*/ @safe unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + + alias alloc = GCAllocator.instance; + + void test(T, Args...)(auto ref Args args) + { + auto k = alloc.make!T(args); + (a) @trusted { a.dispose(k); }(alloc); + } + + test!int(); + test!(int*); + test!int(0); + test!(int*)(null); +} + +// Verify that making an object by calling an impure constructor is not @safe +nothrow @safe @nogc unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + static struct Pure { this(int) pure nothrow @nogc @safe {} } + + cast(void) Mallocator.instance.make!Pure(0); + + static int g = 0; + static struct Impure { this(int) nothrow @nogc @safe { + g++; + } } + static assert(!__traits(compiles, cast(void) Mallocator.instance.make!Impure(0))); +} + +// test failure with a pure, failing struct +@safe unittest +{ + import std.exception : assertThrown, enforce; + + // this struct can't be initialized + struct InvalidStruct + { + this(int b) + { + enforce(1 == 2); + } + } + import std.experimental.allocator.mallocator : Mallocator; + assertThrown(make!InvalidStruct(Mallocator.instance, 42)); +} + +// test failure with an impure, failing struct +@system unittest +{ + import std.exception : assertThrown, enforce; + static int g; + struct InvalidImpureStruct + { + this(int b) + { + g++; + enforce(1 == 2); + } + } + import std.experimental.allocator.mallocator : Mallocator; + assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42)); +} + +private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow +{ + import core.stdc.string : memcpy; + import std.algorithm.comparison : min; + if (!array.length) return; + memcpy(array.ptr, &filler, T.sizeof); + // Fill the array from the initialized portion of itself exponentially. + for (size_t offset = T.sizeof; offset < array.length; ) + { + size_t extent = min(offset, array.length - offset); + memcpy(array.ptr + offset, array.ptr, extent); + offset += extent; + } +} + +@system unittest +{ + int[] a; + fillWithMemcpy(a, 42); + assert(a.length == 0); + a = [ 1, 2, 3, 4, 5 ]; + fillWithMemcpy(a, 42); + assert(a == [ 42, 42, 42, 42, 42]); +} + +private T[] uninitializedFillDefault(T)(T[] array) nothrow +{ + T t = T.init; + fillWithMemcpy(array, t); + return array; +} + +pure nothrow @nogc +@system unittest +{ + static struct S { int x = 42; @disable this(this); } + + int[5] expected = [42, 42, 42, 42, 42]; + S[5] arr = void; + uninitializedFillDefault(arr); + assert((cast(int*) arr.ptr)[0 .. arr.length] == expected); +} + +@system unittest +{ + int[] a = [1, 2, 4]; + uninitializedFillDefault(a); + assert(a == [0, 0, 0]); +} + +/** +Create an array of $(D T) with $(D length) elements using $(D alloc). The array is either default-initialized, filled with copies of $(D init), or initialized with values fetched from `range`. + +Params: +T = element type of the array being created +alloc = the allocator used for getting memory +length = length of the newly created array +init = element used for filling the array +range = range used for initializing the array elements + +Returns: +The newly-created array, or $(D null) if either $(D length) was $(D 0) or +allocation failed. + +Throws: +The first two overloads throw only if `alloc`'s primitives do. The +overloads that involve copy initialization deallocate memory and propagate the +exception if the copy operation throws. +*/ +T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length) +{ + if (!length) return null; + auto m = alloc.allocate(T.sizeof * length); + if (!m.ptr) return null; + alias U = Unqual!T; + return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }(); +} + +@system unittest +{ + void test1(A)(auto ref A alloc) + { + int[] a = alloc.makeArray!int(0); + assert(a.length == 0 && a.ptr is null); + a = alloc.makeArray!int(5); + assert(a.length == 5); + static immutable cheatsheet = [0, 0, 0, 0, 0]; + assert(a == cheatsheet); + } + + void test2(A)(auto ref A alloc) + { + static struct S { int x = 42; @disable this(this); } + S[] arr = alloc.makeArray!S(5); + assert(arr.length == 5); + int[] arrInt = () @trusted { return (cast(int*) arr.ptr)[0 .. 5]; }(); + static immutable res = [42, 42, 42, 42, 42]; + assert(arrInt == res); + } + + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + (alloc) /*pure nothrow*/ @safe { test1(alloc); test2(alloc);} (GCAllocator.instance); + (alloc) nothrow @safe @nogc { test1(alloc); test2(alloc);} (Mallocator.instance); + test2(theAllocator); +} + +@system unittest +{ + import std.algorithm.comparison : equal; + auto a = theAllocator.makeArray!(shared int)(5); + static assert(is(typeof(a) == shared(int)[])); + assert(a.length == 5); + assert(a.equal([0, 0, 0, 0, 0])); + + auto b = theAllocator.makeArray!(const int)(5); + static assert(is(typeof(b) == const(int)[])); + assert(b.length == 5); + assert(b.equal([0, 0, 0, 0, 0])); + + auto c = theAllocator.makeArray!(immutable int)(5); + static assert(is(typeof(c) == immutable(int)[])); + assert(c.length == 5); + assert(c.equal([0, 0, 0, 0, 0])); +} + +private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T || + is(typeof(() pure { T.init.__xpostblit(); })); + +private enum hasPureDtor(T) = !hasElaborateDestructor!T || + is(typeof(() pure { T.init.__xdtor(); })); + +// `true` when postblit and destructor of T cannot escape references to itself +private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T; + +/// Ditto +T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, + auto ref T init) +{ + if (!length) return null; + auto m = alloc.allocate(T.sizeof * length); + if (!m.ptr) return null; + auto result = () @trusted { return cast(T[]) m; } (); + import std.traits : hasElaborateCopyConstructor; + static if (hasElaborateCopyConstructor!T) + { + scope(failure) + { + static if (canSafelyDeallocPostRewind!T) + () @trusted { alloc.deallocate(m); } (); + else + alloc.deallocate(m); + } + + size_t i = 0; + static if (hasElaborateDestructor!T) + { + scope (failure) + { + foreach (j; 0 .. i) + { + destroy(result[j]); + } + } + } + import std.conv : emplace; + for (; i < length; ++i) + { + emplace!T(&result[i], init); + } + } + else + { + alias U = Unqual!T; + () @trusted { fillWithMemcpy(cast(U[]) result, *(cast(U*) &init)); }(); + } + return result; +} + +/// +@system unittest +{ + import std.algorithm.comparison : equal; + static void test(T)() + { + T[] a = theAllocator.makeArray!T(2); + assert(a.equal([0, 0])); + a = theAllocator.makeArray!T(3, 42); + assert(a.equal([42, 42, 42])); + import std.range : only; + a = theAllocator.makeArray!T(only(42, 43, 44)); + assert(a.equal([42, 43, 44])); + } + test!int(); + test!(shared int)(); + test!(const int)(); + test!(immutable int)(); +} + +@system unittest +{ + void test(A)(auto ref A alloc) + { + long[] a = alloc.makeArray!long(0, 42); + assert(a.length == 0 && a.ptr is null); + a = alloc.makeArray!long(5, 42); + assert(a.length == 5); + assert(a == [ 42, 42, 42, 42, 42 ]); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + (alloc) /*pure nothrow*/ @safe { test(alloc); } (GCAllocator.instance); + test(theAllocator); +} + +// test failure with a pure, failing struct +@safe unittest +{ + import std.exception : assertThrown, enforce; + + struct NoCopy + { + @disable this(); + + this(int b){} + + // can't be copied + this(this) + { + enforce(1 == 2); + } + } + import std.experimental.allocator.mallocator : Mallocator; + assertThrown(makeArray!NoCopy(Mallocator.instance, 10, NoCopy(42))); +} + +// test failure with an impure, failing struct +@system unittest +{ + import std.exception : assertThrown, enforce; + + static int i = 0; + struct Singleton + { + @disable this(); + + this(int b){} + + // can't be copied + this(this) + { + enforce(i++ == 0); + } + + ~this() + { + i--; + } + } + import std.experimental.allocator.mallocator : Mallocator; + assertThrown(makeArray!Singleton(Mallocator.instance, 10, Singleton(42))); +} + +/// Ditto +Unqual!(ElementEncodingType!R)[] makeArray(Allocator, R)(auto ref Allocator alloc, R range) +if (isInputRange!R && !isInfinite!R) +{ + alias T = Unqual!(ElementEncodingType!R); + return makeArray!(T, Allocator, R)(alloc, range); +} + +/// Ditto +T[] makeArray(T, Allocator, R)(auto ref Allocator alloc, R range) +if (isInputRange!R && !isInfinite!R) +{ + static if (isForwardRange!R || hasLength!R) + { + static if (hasLength!R || isNarrowString!R) + immutable length = range.length; + else + immutable length = range.save.walkLength; + + if (!length) return null; + auto m = alloc.allocate(T.sizeof * length); + if (!m.ptr) return null; + auto result = () @trusted { return cast(T[]) m; } (); + + size_t i = 0; + scope (failure) + { + foreach (j; 0 .. i) + { + auto p = () @trusted { return cast(Unqual!T*) &result[j]; }(); + destroy(p); + } + + static if (canSafelyDeallocPostRewind!T) + () @trusted { alloc.deallocate(m); } (); + else + alloc.deallocate(m); + } + + import std.conv : emplaceRef; + static if (isNarrowString!R || isRandomAccessRange!R) + { + foreach (j; 0 .. range.length) + { + emplaceRef!T(result[i++], range[j]); + } + } + else + { + for (; !range.empty; range.popFront, ++i) + { + emplaceRef!T(result[i], range.front); + } + } + + return result; + } + else + { + // Estimated size + size_t estimated = 8; + auto m = alloc.allocate(T.sizeof * estimated); + if (!m.ptr) return null; + auto result = () @trusted { return cast(T[]) m; } (); + + size_t initialized = 0; + void bailout() + { + foreach (i; 0 .. initialized + 1) + { + destroy(result[i]); + } + + static if (canSafelyDeallocPostRewind!T) + () @trusted { alloc.deallocate(m); } (); + else + alloc.deallocate(m); + } + scope (failure) bailout; + + for (; !range.empty; range.popFront, ++initialized) + { + if (initialized == estimated) + { + // Need to reallocate + static if (hasPurePostblit!T) + auto success = () @trusted { return alloc.reallocate(m, T.sizeof * (estimated *= 2)); } (); + else + auto success = alloc.reallocate(m, T.sizeof * (estimated *= 2)); + if (!success) + { + bailout; + return null; + } + result = () @trusted { return cast(T[]) m; } (); + } + import std.conv : emplaceRef; + emplaceRef(result[initialized], range.front); + } + + if (initialized < estimated) + { + // Try to shrink memory, no harm if not possible + static if (hasPurePostblit!T) + auto success = () @trusted { return alloc.reallocate(m, T.sizeof * initialized); } (); + else + auto success = alloc.reallocate(m, T.sizeof * initialized); + if (success) + result = () @trusted { return cast(T[]) m; } (); + } + + return result[0 .. initialized]; + } +} + +@system unittest +{ + void test(A)(auto ref A alloc) + { + long[] a = alloc.makeArray!long((int[]).init); + assert(a.length == 0 && a.ptr is null); + a = alloc.makeArray!long([5, 42]); + assert(a.length == 2); + assert(a == [ 5, 42]); + + // we can also infer the type + auto b = alloc.makeArray([4.0, 2.0]); + static assert(is(typeof(b) == double[])); + assert(b == [4.0, 2.0]); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); + test(theAllocator); +} + +// infer types for strings +@system unittest +{ + void test(A)(auto ref A alloc) + { + auto c = alloc.makeArray("fooπ😜"); + static assert(is(typeof(c) == char[])); + assert(c == "fooπ😜"); + + auto d = alloc.makeArray("fooπ😜"d); + static assert(is(typeof(d) == dchar[])); + assert(d == "fooπ😜"); + + auto w = alloc.makeArray("fooπ😜"w); + static assert(is(typeof(w) == wchar[])); + assert(w == "fooπ😜"); + } + + import std.experimental.allocator.gc_allocator : GCAllocator; + (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); + test(theAllocator); +} + +/*pure*/ nothrow @safe unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.range : iota; + foreach (DummyType; AllDummyRanges) + { + (alloc) pure nothrow @safe + { + DummyType d; + auto arr = alloc.makeArray(d); + assert(arr.length == 10); + assert(arr.equal(iota(1, 11))); + } (GCAllocator.instance); + } +} + +// test failure with a pure, failing struct +@safe unittest +{ + import std.exception : assertThrown, enforce; + + struct NoCopy + { + int b; + + @disable this(); + + this(int b) + { + this.b = b; + } + + // can't be copied + this(this) + { + enforce(b < 3, "there can only be three elements"); + } + } + import std.experimental.allocator.mallocator : Mallocator; + auto arr = [NoCopy(1), NoCopy(2), NoCopy(3)]; + assertThrown(makeArray!NoCopy(Mallocator.instance, arr)); + + struct NoCopyRange + { + static j = 0; + bool empty() + { + return j > 5; + } + + auto front() + { + return NoCopy(j); + } + + void popFront() + { + j++; + } + } + assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange())); +} + +// test failure with an impure, failing struct +@system unittest +{ + import std.exception : assertThrown, enforce; + + static i = 0; + static maxElements = 2; + struct NoCopy + { + int val; + @disable this(); + + this(int b){ + this.val = i++; + } + + // can't be copied + this(this) + { + enforce(i++ < maxElements, "there can only be four elements"); + } + } + + import std.experimental.allocator.mallocator : Mallocator; + auto arr = [NoCopy(1), NoCopy(2)]; + assertThrown(makeArray!NoCopy(Mallocator.instance, arr)); + + // allow more copies and thus force reallocation + i = 0; + maxElements = 30; + static j = 0; + + struct NoCopyRange + { + bool empty() + { + return j > 100; + } + + auto front() + { + return NoCopy(1); + } + + void popFront() + { + j++; + } + } + assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange())); + + maxElements = 300; + auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange()); + + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.range : iota; + assert(arr2.map!`a.val`.equal(iota(32, 204, 2))); +} + +version(unittest) +{ + private struct ForcedInputRange + { + int[]* array; + pure nothrow @safe @nogc: + bool empty() { return !array || (*array).empty; } + ref int front() { return (*array)[0]; } + void popFront() { *array = (*array)[1 .. $]; } + } +} + +@system unittest +{ + import std.array : array; + import std.range : iota; + int[] arr = iota(10).array; + + void test(A)(auto ref A alloc) + { + ForcedInputRange r; + long[] a = alloc.makeArray!long(r); + assert(a.length == 0 && a.ptr is null); + auto arr2 = arr; + r.array = () @trusted { return &arr2; } (); + a = alloc.makeArray!long(r); + assert(a.length == 10); + assert(a == iota(10).array); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); + test(theAllocator); +} + +/** +Grows $(D array) by appending $(D delta) more elements. The needed memory is +allocated using $(D alloc). The extra elements added are either default- +initialized, filled with copies of $(D init), or initialized with values +fetched from `range`. + +Params: +T = element type of the array being created +alloc = the allocator used for getting memory +array = a reference to the array being grown +delta = number of elements to add (upon success the new length of $(D array) is +$(D array.length + delta)) +init = element used for filling the array +range = range used for initializing the array elements + +Returns: +$(D true) upon success, $(D false) if memory could not be allocated. In the +latter case $(D array) is left unaffected. + +Throws: +The first two overloads throw only if `alloc`'s primitives do. The +overloads that involve copy initialization deallocate memory and propagate the +exception if the copy operation throws. +*/ +bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array, + size_t delta) +{ + if (!delta) return true; + if (array is null) return false; + immutable oldLength = array.length; + void[] buf = array; + if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false; + array = cast(T[]) buf; + array[oldLength .. $].uninitializedFillDefault; + return true; +} + +@system unittest +{ + void test(A)(auto ref A alloc) + { + auto arr = alloc.makeArray!int([1, 2, 3]); + assert(alloc.expandArray(arr, 3)); + assert(arr == [1, 2, 3, 0, 0, 0]); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + test(GCAllocator.instance); + test(theAllocator); +} + +/// Ditto +bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array, + size_t delta, auto ref T init) +{ + if (!delta) return true; + if (array is null) return false; + void[] buf = array; + if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false; + immutable oldLength = array.length; + array = cast(T[]) buf; + scope(failure) array[oldLength .. $].uninitializedFillDefault; + import std.algorithm.mutation : uninitializedFill; + array[oldLength .. $].uninitializedFill(init); + return true; +} + +@system unittest +{ + void test(A)(auto ref A alloc) + { + auto arr = alloc.makeArray!int([1, 2, 3]); + assert(alloc.expandArray(arr, 3, 1)); + assert(arr == [1, 2, 3, 1, 1, 1]); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + test(GCAllocator.instance); + test(theAllocator); +} + +/// Ditto +bool expandArray(T, Allocator, R)(auto ref Allocator alloc, ref T[] array, + R range) +if (isInputRange!R) +{ + if (array is null) return false; + static if (isForwardRange!R) + { + immutable delta = walkLength(range.save); + if (!delta) return true; + immutable oldLength = array.length; + + // Reallocate support memory + void[] buf = array; + if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) + { + return false; + } + array = cast(T[]) buf; + // At this point we're committed to the new length. + + auto toFill = array[oldLength .. $]; + scope (failure) + { + // Fill the remainder with default-constructed data + toFill.uninitializedFillDefault; + } + + for (; !range.empty; range.popFront, toFill.popFront) + { + assert(!toFill.empty); + import std.conv : emplace; + emplace!T(&toFill.front, range.front); + } + assert(toFill.empty); + } + else + { + scope(failure) + { + // The last element didn't make it, fill with default + array[$ - 1 .. $].uninitializedFillDefault; + } + void[] buf = array; + for (; !range.empty; range.popFront) + { + if (!alloc.reallocate(buf, buf.length + T.sizeof)) + { + array = cast(T[]) buf; + return false; + } + import std.conv : emplace; + emplace!T(buf[$ - T.sizeof .. $], range.front); + } + + array = cast(T[]) buf; + } + return true; +} + +/// +@system unittest +{ + auto arr = theAllocator.makeArray!int([1, 2, 3]); + assert(theAllocator.expandArray(arr, 2)); + assert(arr == [1, 2, 3, 0, 0]); + import std.range : only; + assert(theAllocator.expandArray(arr, only(4, 5))); + assert(arr == [1, 2, 3, 0, 0, 4, 5]); +} + +@system unittest +{ + auto arr = theAllocator.makeArray!int([1, 2, 3]); + ForcedInputRange r; + int[] b = [ 1, 2, 3, 4 ]; + auto temp = b; + r.array = &temp; + assert(theAllocator.expandArray(arr, r)); + assert(arr == [1, 2, 3, 1, 2, 3, 4]); +} + +/** +Shrinks an array by $(D delta) elements. + +If $(D array.length < delta), does nothing and returns `false`. Otherwise, +destroys the last $(D array.length - delta) elements in the array and then +reallocates the array's buffer. If reallocation fails, fills the array with +default-initialized data. + +Params: +T = element type of the array being created +alloc = the allocator used for getting memory +array = a reference to the array being shrunk +delta = number of elements to remove (upon success the new length of $(D array) is $(D array.length - delta)) + +Returns: +`true` upon success, `false` if memory could not be reallocated. In the latter +case, the slice $(D array[$ - delta .. $]) is left with default-initialized +elements. + +Throws: +The first two overloads throw only if `alloc`'s primitives do. The +overloads that involve copy initialization deallocate memory and propagate the +exception if the copy operation throws. +*/ +bool shrinkArray(T, Allocator)(auto ref Allocator alloc, + ref T[] array, size_t delta) +{ + if (delta > array.length) return false; + + // Destroy elements. If a destructor throws, fill the already destroyed + // stuff with the default initializer. + { + size_t destroyed; + scope(failure) + { + array[$ - delta .. $][0 .. destroyed].uninitializedFillDefault; + } + foreach (ref e; array[$ - delta .. $]) + { + e.destroy; + ++destroyed; + } + } + + if (delta == array.length) + { + alloc.deallocate(array); + array = null; + return true; + } + + void[] buf = array; + if (!alloc.reallocate(buf, buf.length - T.sizeof * delta)) + { + // urgh, at least fill back with default + array[$ - delta .. $].uninitializedFillDefault; + return false; + } + array = cast(T[]) buf; + return true; +} + +/// +@system unittest +{ + int[] a = theAllocator.makeArray!int(100, 42); + assert(a.length == 100); + assert(theAllocator.shrinkArray(a, 98)); + assert(a.length == 2); + assert(a == [42, 42]); +} + +@system unittest +{ + void test(A)(auto ref A alloc) + { + long[] a = alloc.makeArray!long((int[]).init); + assert(a.length == 0 && a.ptr is null); + a = alloc.makeArray!long(100, 42); + assert(alloc.shrinkArray(a, 98)); + assert(a.length == 2); + assert(a == [ 42, 42]); + } + import std.experimental.allocator.gc_allocator : GCAllocator; + test(GCAllocator.instance); + test(theAllocator); +} + +/** + +Destroys and then deallocates (using $(D alloc)) the object pointed to by a +pointer, the class object referred to by a $(D class) or $(D interface) +reference, or an entire array. It is assumed the respective entities had been +allocated with the same allocator. + +*/ +void dispose(A, T)(auto ref A alloc, T* p) +{ + static if (hasElaborateDestructor!T) + { + destroy(*p); + } + alloc.deallocate((cast(void*) p)[0 .. T.sizeof]); +} + +/// Ditto +void dispose(A, T)(auto ref A alloc, T p) +if (is(T == class) || is(T == interface)) +{ + if (!p) return; + static if (is(T == interface)) + { + version(Windows) + { + import core.sys.windows.unknwn : IUnknown; + static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " + ~ __PRETTY_FUNCTION__); + } + auto ob = cast(Object) p; + } + else + alias ob = p; + auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length]; + destroy(p); + alloc.deallocate(support); +} + +/// Ditto +void dispose(A, T)(auto ref A alloc, T[] array) +{ + static if (hasElaborateDestructor!(typeof(array[0]))) + { + foreach (ref e; array) + { + destroy(e); + } + } + alloc.deallocate(array); +} + +@system unittest +{ + static int x; + static interface I + { + void method(); + } + static class A : I + { + int y; + override void method() { x = 21; } + ~this() { x = 42; } + } + static class B : A + { + } + auto a = theAllocator.make!A; + a.method(); + assert(x == 21); + theAllocator.dispose(a); + assert(x == 42); + + B b = theAllocator.make!B; + b.method(); + assert(x == 21); + theAllocator.dispose(b); + assert(x == 42); + + I i = theAllocator.make!B; + i.method(); + assert(x == 21); + theAllocator.dispose(i); + assert(x == 42); + + int[] arr = theAllocator.makeArray!int(43); + theAllocator.dispose(arr); +} + +@system unittest //bugzilla 15721 +{ + import std.experimental.allocator.mallocator : Mallocator; + + interface Foo {} + class Bar: Foo {} + + Bar bar; + Foo foo; + bar = Mallocator.instance.make!Bar; + foo = cast(Foo) bar; + Mallocator.instance.dispose(foo); +} + +/** +Allocates a multidimensional array of elements of type T. + +Params: +N = number of dimensions +T = element type of an element of the multidimensional arrat +alloc = the allocator used for getting memory +lengths = static array containing the size of each dimension + +Returns: +An N-dimensional array with individual elements of type T. +*/ +auto makeMultidimensionalArray(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...) +{ + static if (N == 1) + { + return makeArray!T(alloc, lengths[0]); + } + else + { + alias E = typeof(makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $])); + auto ret = makeArray!E(alloc, lengths[0]); + foreach (ref e; ret) + e = makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]); + return ret; + } +} + +/// +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + + auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6); + + // deallocate when exiting scope + scope(exit) + { + Mallocator.instance.disposeMultidimensionalArray(mArray); + } + + assert(mArray.length == 2); + foreach (lvl2Array; mArray) + { + assert(lvl2Array.length == 3); + foreach (lvl3Array; lvl2Array) + assert(lvl3Array.length == 6); + } +} + +/** +Destroys and then deallocates a multidimensional array, assuming it was +created with makeMultidimensionalArray and the same allocator was used. + +Params: +T = element type of an element of the multidimensional array +alloc = the allocator used for getting memory +array = the multidimensional array that is to be deallocated +*/ +void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, T[] array) +{ + static if (isArray!T) + { + foreach (ref e; array) + disposeMultidimensionalArray(alloc, e); + } + + dispose(alloc, array); +} + +/// +@system unittest +{ + struct TestAllocator + { + import std.experimental.allocator.common : platformAlignment; + import std.experimental.allocator.mallocator : Mallocator; + + alias allocator = Mallocator.instance; + + private static struct ByteRange + { + void* ptr; + size_t length; + } + + private ByteRange[] _allocations; + + enum uint alignment = platformAlignment; + + void[] allocate(size_t numBytes) + { + auto ret = allocator.allocate(numBytes); + _allocations ~= ByteRange(ret.ptr, ret.length); + return ret; + } + + bool deallocate(void[] bytes) + { + import std.algorithm.mutation : remove; + import std.algorithm.searching : canFind; + + bool pred(ByteRange other) + { return other.ptr == bytes.ptr && other.length == bytes.length; } + + assert(_allocations.canFind!pred); + + _allocations = _allocations.remove!pred; + return allocator.deallocate(bytes); + } + + ~this() + { + assert(!_allocations.length); + } + } + + TestAllocator allocator; + + auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2); + + allocator.disposeMultidimensionalArray(mArray); +} + +/** + +Returns a dynamically-typed $(D CAllocator) built around a given statically- +typed allocator $(D a) of type $(D A). Passing a pointer to the allocator +creates a dynamic allocator around the allocator pointed to by the pointer, +without attempting to copy or move it. Passing the allocator by value or +reference behaves as follows. + +$(UL +$(LI If $(D A) has no state, the resulting object is allocated in static +shared storage.) +$(LI If $(D A) has state and is copyable, the result will store a copy of it +within. The result itself is allocated in its own statically-typed allocator.) +$(LI If $(D A) has state and is not copyable, the result will move the +passed-in argument into the result. The result itself is allocated in its own +statically-typed allocator.) +) + +*/ +CAllocatorImpl!A allocatorObject(A)(auto ref A a) +if (!isPointer!A) +{ + import std.conv : emplace; + static if (stateSize!A == 0) + { + enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof); + static __gshared ulong[s] state; + static __gshared CAllocatorImpl!A result; + if (!result) + { + // Don't care about a few races + result = emplace!(CAllocatorImpl!A)(state[]); + } + assert(result); + return result; + } + else static if (is(typeof({ A b = a; A c = b; }))) // copyable + { + auto state = a.allocate(stateSize!(CAllocatorImpl!A)); + import std.traits : hasMember; + static if (hasMember!(A, "deallocate")) + { + scope(failure) a.deallocate(state); + } + return cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state); + } + else // the allocator object is not copyable + { + // This is sensitive... create on the stack and then move + enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof); + ulong[s] state; + import std.algorithm.mutation : move; + emplace!(CAllocatorImpl!A)(state[], move(a)); + auto dynState = a.allocate(stateSize!(CAllocatorImpl!A)); + // Bitblast the object in its final destination + dynState[] = state[]; + return cast(CAllocatorImpl!A) dynState.ptr; + } +} + +/// Ditto +CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa) +{ + assert(pa); + import std.conv : emplace; + auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect))); + import std.traits : hasMember; + static if (hasMember!(A, "deallocate")) + { + scope(failure) pa.deallocate(state); + } + return emplace!(CAllocatorImpl!(A, Yes.indirect)) + (state, pa); +} + +/// +@system unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + IAllocator a = allocatorObject(Mallocator.instance); + auto b = a.allocate(100); + assert(b.length == 100); + assert(a.deallocate(b)); + + // The in-situ region must be used by pointer + import std.experimental.allocator.building_blocks.region : InSituRegion; + auto r = InSituRegion!1024(); + a = allocatorObject(&r); + b = a.allocate(200); + assert(b.length == 200); + // In-situ regions can deallocate the last allocation + assert(a.deallocate(b)); +} + +/** + +Returns a dynamically-typed $(D CSharedAllocator) built around a given statically- +typed allocator $(D a) of type $(D A). Passing a pointer to the allocator +creates a dynamic allocator around the allocator pointed to by the pointer, +without attempting to copy or move it. Passing the allocator by value or +reference behaves as follows. + +$(UL +$(LI If $(D A) has no state, the resulting object is allocated in static +shared storage.) +$(LI If $(D A) has state and is copyable, the result will store a copy of it +within. The result itself is allocated in its own statically-typed allocator.) +$(LI If $(D A) has state and is not copyable, the result will move the +passed-in argument into the result. The result itself is allocated in its own +statically-typed allocator.) +) + +*/ +shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a) +if (!isPointer!A) +{ + import std.conv : emplace; + static if (stateSize!A == 0) + { + enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof); + static __gshared ulong[s] state; + static shared CSharedAllocatorImpl!A result; + if (!result) + { + // Don't care about a few races + result = cast(shared + CSharedAllocatorImpl!A)(emplace!(CSharedAllocatorImpl!A)(state[])); + } + assert(result); + return result; + } + else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable + { + auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A)); + import std.traits : hasMember; + static if (hasMember!(A, "deallocate")) + { + scope(failure) a.deallocate(state); + } + return emplace!(shared CSharedAllocatorImpl!A)(state); + } + else // the allocator object is not copyable + { + assert(0, "Not yet implemented"); + } +} + +/// Ditto +shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa) +{ + assert(pa); + import std.conv : emplace; + auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect))); + import std.traits : hasMember; + static if (hasMember!(A, "deallocate")) + { + scope(failure) pa.deallocate(state); + } + return emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa); +} + + +/** + +Implementation of `IAllocator` using `Allocator`. This adapts a +statically-built allocator type to `IAllocator` that is directly usable by +non-templated code. + +Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator). +*/ +class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) + : IAllocator +{ + import std.traits : hasMember; + + /** + The implementation is available as a public member. + */ + static if (indirect) + { + private Allocator* pimpl; + ref Allocator impl() + { + return *pimpl; + } + this(Allocator* pa) + { + pimpl = pa; + } + } + else + { + static if (stateSize!Allocator) Allocator impl; + else alias impl = Allocator.instance; + } + + /// Returns `impl.alignment`. + override @property uint alignment() + { + return impl.alignment; + } + + /** + Returns `impl.goodAllocSize(s)`. + */ + override size_t goodAllocSize(size_t s) + { + return impl.goodAllocSize(s); + } + + /** + Returns `impl.allocate(s)`. + */ + override void[] allocate(size_t s, TypeInfo ti = null) + { + return impl.allocate(s); + } + + /** + If `impl.alignedAllocate` exists, calls it and returns the result. + Otherwise, always returns `null`. + */ + override void[] alignedAllocate(size_t s, uint a) + { + static if (hasMember!(Allocator, "alignedAllocate")) + return impl.alignedAllocate(s, a); + else + return null; + } + + /** + If `Allocator` implements `owns`, forwards to it. Otherwise, returns + `Ternary.unknown`. + */ + override Ternary owns(void[] b) + { + static if (hasMember!(Allocator, "owns")) return impl.owns(b); + else return Ternary.unknown; + } + + /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. + override bool expand(ref void[] b, size_t s) + { + static if (hasMember!(Allocator, "expand")) + return impl.expand(b, s); + else + return s == 0; + } + + /// Returns $(D impl.reallocate(b, s)). + override bool reallocate(ref void[] b, size_t s) + { + return impl.reallocate(b, s); + } + + /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. + bool alignedReallocate(ref void[] b, size_t s, uint a) + { + static if (!hasMember!(Allocator, "alignedAllocate")) + { + return false; + } + else + { + return impl.alignedReallocate(b, s, a); + } + } + + // Undocumented for now + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + static if (hasMember!(Allocator, "resolveInternalPointer")) + { + return impl.resolveInternalPointer(p, result); + } + else + { + return Ternary.unknown; + } + } + + /** + If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards + the call. + */ + override bool deallocate(void[] b) + { + static if (hasMember!(Allocator, "deallocate")) + { + return impl.deallocate(b); + } + else + { + return false; + } + } + + /** + Calls `impl.deallocateAll()` and returns the result if defined, + otherwise returns `false`. + */ + override bool deallocateAll() + { + static if (hasMember!(Allocator, "deallocateAll")) + { + return impl.deallocateAll(); + } + else + { + return false; + } + } + + /** + Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. + */ + override Ternary empty() + { + static if (hasMember!(Allocator, "empty")) + { + return Ternary(impl.empty); + } + else + { + return Ternary.unknown; + } + } + + /** + Returns `impl.allocateAll()` if present, `null` otherwise. + */ + override void[] allocateAll() + { + static if (hasMember!(Allocator, "allocateAll")) + { + return impl.allocateAll(); + } + else + { + return null; + } + } +} + +/** + +Implementation of `ISharedAllocator` using `Allocator`. This adapts a +statically-built, shareable across threads, allocator type to `ISharedAllocator` +that is directly usable by non-templated code. + +Usually `CSharedAllocatorImpl` is used indirectly by calling +$(LREF processAllocator). +*/ +class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) + : ISharedAllocator +{ + import std.traits : hasMember; + + /** + The implementation is available as a public member. + */ + static if (indirect) + { + private shared Allocator* pimpl; + ref Allocator impl() shared + { + return *pimpl; + } + this(Allocator* pa) shared + { + pimpl = pa; + } + } + else + { + static if (stateSize!Allocator) shared Allocator impl; + else alias impl = Allocator.instance; + } + + /// Returns `impl.alignment`. + override @property uint alignment() shared + { + return impl.alignment; + } + + /** + Returns `impl.goodAllocSize(s)`. + */ + override size_t goodAllocSize(size_t s) shared + { + return impl.goodAllocSize(s); + } + + /** + Returns `impl.allocate(s)`. + */ + override void[] allocate(size_t s, TypeInfo ti = null) shared + { + return impl.allocate(s); + } + + /** + If `impl.alignedAllocate` exists, calls it and returns the result. + Otherwise, always returns `null`. + */ + override void[] alignedAllocate(size_t s, uint a) shared + { + static if (hasMember!(Allocator, "alignedAllocate")) + return impl.alignedAllocate(s, a); + else + return null; + } + + /** + If `Allocator` implements `owns`, forwards to it. Otherwise, returns + `Ternary.unknown`. + */ + override Ternary owns(void[] b) shared + { + static if (hasMember!(Allocator, "owns")) return impl.owns(b); + else return Ternary.unknown; + } + + /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. + override bool expand(ref void[] b, size_t s) shared + { + static if (hasMember!(Allocator, "expand")) + return impl.expand(b, s); + else + return s == 0; + } + + /// Returns $(D impl.reallocate(b, s)). + override bool reallocate(ref void[] b, size_t s) shared + { + return impl.reallocate(b, s); + } + + /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. + bool alignedReallocate(ref void[] b, size_t s, uint a) shared + { + static if (!hasMember!(Allocator, "alignedAllocate")) + { + return false; + } + else + { + return impl.alignedReallocate(b, s, a); + } + } + + // Undocumented for now + Ternary resolveInternalPointer(const void* p, ref void[] result) shared + { + static if (hasMember!(Allocator, "resolveInternalPointer")) + { + return impl.resolveInternalPointer(p, result); + } + else + { + return Ternary.unknown; + } + } + + /** + If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards + the call. + */ + override bool deallocate(void[] b) shared + { + static if (hasMember!(Allocator, "deallocate")) + { + return impl.deallocate(b); + } + else + { + return false; + } + } + + /** + Calls `impl.deallocateAll()` and returns the result if defined, + otherwise returns `false`. + */ + override bool deallocateAll() shared + { + static if (hasMember!(Allocator, "deallocateAll")) + { + return impl.deallocateAll(); + } + else + { + return false; + } + } + + /** + Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. + */ + override Ternary empty() shared + { + static if (hasMember!(Allocator, "empty")) + { + return Ternary(impl.empty); + } + else + { + return Ternary.unknown; + } + } + + /** + Returns `impl.allocateAll()` if present, `null` otherwise. + */ + override void[] allocateAll() shared + { + static if (hasMember!(Allocator, "allocateAll")) + { + return impl.allocateAll(); + } + else + { + return null; + } + } +} + + +// Example in intro above +@system unittest +{ + // Allocate an int, initialize it with 42 + int* p = theAllocator.make!int(42); + assert(*p == 42); + + // Destroy and deallocate it + theAllocator.dispose(p); + + // Allocate using the global process allocator + p = processAllocator.make!int(100); + assert(*p == 100); + + // Destroy and deallocate + processAllocator.dispose(p); + + // Create an array of 50 doubles initialized to -1.0 + double[] arr = theAllocator.makeArray!double(50, -1.0); + + // Check internal pointer + void[] result; + assert(theAllocator.resolveInternalPointer(null, result) == Ternary.no); + Ternary r = theAllocator.resolveInternalPointer(arr.ptr, result); + assert(result.ptr is arr.ptr && result.length >= arr.length); + + // Append two zeros to it + theAllocator.expandArray(arr, 2, 0.0); + // On second thought, take that back + theAllocator.shrinkArray(arr, 2); + // Destroy and deallocate + theAllocator.dispose(arr); +} + +__EOF__ + +/** + +Stores an allocator object in thread-local storage (i.e. non-$(D shared) D +global). $(D ThreadLocal!A) is a subtype of $(D A) so it appears to implement +$(D A)'s allocator primitives. + +$(D A) must hold state, otherwise $(D ThreadLocal!A) refuses instantiation. This +means e.g. $(D ThreadLocal!Mallocator) does not work because $(D Mallocator)'s +state is not stored as members of $(D Mallocator), but instead is hidden in the +C library implementation. + +*/ +struct ThreadLocal(A) +{ + static assert(stateSize!A, + typeof(A).stringof + ~ " does not have state so it cannot be used with ThreadLocal"); + + /** + The allocator instance. + */ + static A instance; + + /** + `ThreadLocal!A` is a subtype of `A` so it appears to implement `A`'s + allocator primitives. + */ + alias instance this; + + /** + `ThreadLocal` disables all constructors. The intended usage is + `ThreadLocal!A.instance`. + */ + @disable this(); + /// Ditto + @disable this(this); +} + +/// +unittest +{ + static assert(!is(ThreadLocal!Mallocator)); + static assert(!is(ThreadLocal!GCAllocator)); + alias ThreadLocal!(FreeList!(GCAllocator, 0, 8)) Allocator; + auto b = Allocator.instance.allocate(5); + static assert(hasMember!(Allocator, "allocate")); +} + +/* +(Not public.) + +A binary search tree that uses no allocation of its own. Instead, it relies on +user code to allocate nodes externally. Then $(D EmbeddedTree)'s primitives wire +the nodes appropriately. + +Warning: currently $(D EmbeddedTree) is not using rebalancing, so it may +degenerate. A red-black tree implementation storing the color with one of the +pointers is planned for the future. +*/ +private struct EmbeddedTree(T, alias less) +{ + static struct Node + { + T payload; + Node* left, right; + } + + private Node* root; + + private Node* insert(Node* n, ref Node* backref) + { + backref = n; + n.left = n.right = null; + return n; + } + + Node* find(Node* data) + { + for (auto n = root; n; ) + { + if (less(data, n)) + { + n = n.left; + } + else if (less(n, data)) + { + n = n.right; + } + else + { + return n; + } + } + return null; + } + + Node* insert(Node* data) + { + if (!root) + { + root = data; + data.left = data.right = null; + return root; + } + auto n = root; + for (;;) + { + if (less(data, n)) + { + if (!n.left) + { + // Found insertion point + return insert(data, n.left); + } + n = n.left; + } + else if (less(n, data)) + { + if (!n.right) + { + // Found insertion point + return insert(data, n.right); + } + n = n.right; + } + else + { + // Found + return n; + } + if (!n) return null; + } + } + + Node* remove(Node* data) + { + auto n = root; + Node* parent = null; + for (;;) + { + if (!n) return null; + if (less(data, n)) + { + parent = n; + n = n.left; + } + else if (less(n, data)) + { + parent = n; + n = n.right; + } + else + { + // Found + remove(n, parent); + return n; + } + } + } + + private void remove(Node* n, Node* parent) + { + assert(n); + assert(!parent || parent.left == n || parent.right == n); + Node** referrer = parent + ? (parent.left == n ? &parent.left : &parent.right) + : &root; + if (!n.left) + { + *referrer = n.right; + } + else if (!n.right) + { + *referrer = n.left; + } + else + { + // Find the leftmost child in the right subtree + auto leftmost = n.right; + Node** leftmostReferrer = &n.right; + while (leftmost.left) + { + leftmostReferrer = &leftmost.left; + leftmost = leftmost.left; + } + // Unlink leftmost from there + *leftmostReferrer = leftmost.right; + // Link leftmost in lieu of n + leftmost.left = n.left; + leftmost.right = n.right; + *referrer = leftmost; + } + } + + Ternary empty() const + { + return Ternary(!root); + } + + void dump() + { + writeln(typeid(this), " @ ", cast(void*) &this); + dump(root, 3); + } + + void dump(Node* r, uint indent) + { + write(repeat(' ', indent).array); + if (!r) + { + writeln("(null)"); + return; + } + writeln(r.payload, " @ ", cast(void*) r); + dump(r.left, indent + 3); + dump(r.right, indent + 3); + } + + void assertSane() + { + static bool isBST(Node* r, Node* lb, Node* ub) + { + if (!r) return true; + if (lb && !less(lb, r)) return false; + if (ub && !less(r, ub)) return false; + return isBST(r.left, lb, r) && + isBST(r.right, r, ub); + } + if (isBST(root, null, null)) return; + dump; + assert(0); + } +} + +unittest +{ + alias a = GCAllocator.instance; + alias Tree = EmbeddedTree!(int, (a, b) => a.payload < b.payload); + Tree t; + assert(t.empty); + int[] vals = [ 6, 3, 9, 1, 0, 2, 8, 11 ]; + foreach (v; vals) + { + auto n = new Tree.Node(v, null, null); + assert(t.insert(n)); + assert(n); + t.assertSane; + } + assert(!t.empty); + foreach (v; vals) + { + Tree.Node n = { v }; + assert(t.remove(&n)); + t.assertSane; + } + assert(t.empty); +} + +/* + +$(D InternalPointersTree) adds a primitive on top of another allocator: calling +$(D resolveInternalPointer(p)) returns the block within which the internal +pointer $(D p) lies. Pointers right after the end of allocated blocks are also +considered internal. + +The implementation stores three additional words with each allocation (one for +the block size and two for search management). + +*/ +private struct InternalPointersTree(Allocator) +{ + alias Tree = EmbeddedTree!(size_t, + (a, b) => cast(void*) a + a.payload < cast(void*) b); + alias Parent = AffixAllocator!(Allocator, Tree.Node); + + // Own state + private Tree blockMap; + + alias alignment = Parent.alignment; + + /** + The implementation is available as a public member. + */ + static if (stateSize!Parent) Parent parent; + else alias parent = Parent.instance; + + /// Allocator API. + void[] allocate(size_t bytes) + { + auto r = parent.allocate(bytes); + if (!r.ptr) return r; + Tree.Node* n = &parent.prefix(r); + n.payload = bytes; + blockMap.insert(n) || assert(0); + return r; + } + + /// Ditto + bool deallocate(void[] b) + { + if (!b.ptr) return; + Tree.Node* n = &parent.prefix(b); + blockMap.remove(n) || assert(false); + parent.deallocate(b); + return true; + } + + /// Ditto + static if (hasMember!(Allocator, "reallocate")) + bool reallocate(ref void[] b, size_t s) + { + auto n = &parent.prefix(b); + assert(n.payload == b.length); + blockMap.remove(n) || assert(0); + if (!parent.reallocate(b, s)) + { + // Failed, must reinsert the same node in the tree + assert(n.payload == b.length); + blockMap.insert(n) || assert(0); + return false; + } + // Insert the new node + n = &parent.prefix(b); + n.payload = s; + blockMap.insert(n) || assert(0); + return true; + } + + /// Ditto + Ternary owns(void[] b) + { + void[] result; + return resolveInternalPointer(b.ptr, result); + } + + /// Ditto + Ternary empty() + { + return Ternary(blockMap.empty); + } + + /** Returns the block inside which $(D p) resides, or $(D null) if the + pointer does not belong. + */ + Ternary resolveInternalPointer(const void* p, ref void[] result) + { + // Must define a custom find + Tree.Node* find() + { + for (auto n = blockMap.root; n; ) + { + if (p < n) + { + n = n.left; + } + else if (p > (cast(void*) (n + 1)) + n.payload) + { + n = n.right; + } + else + { + return n; + } + } + return null; + } + + auto n = find(); + if (!n) return Ternary.no; + result = (cast(void*) (n + 1))[0 .. n.payload]; + return Ternary.yes; + } +} + +unittest +{ + InternalPointersTree!(Mallocator) a; + int[] vals = [ 6, 3, 9, 1, 2, 8, 11 ]; + void[][] allox; + foreach (v; vals) + { + allox ~= a.allocate(v); + } + a.blockMap.assertSane; + + foreach (b; allox) + { + void[] p; + Ternary r = a.resolveInternalPointer(b.ptr, p); + assert(p.ptr is b.ptr && p.length >= b.length); + r = a.resolveInternalPointer(b.ptr + b.length, p); + assert(p.ptr is b.ptr && p.length >= b.length); + r = a.resolveInternalPointer(b.ptr + b.length / 2, p); + assert(p.ptr is b.ptr && p.length >= b.length); + auto bogus = new void[b.length]; + assert(a.resolveInternalPointer(bogus.ptr, p) == Ternary.no); + } + + foreach (b; allox.randomCover) + { + a.deallocate(b); + } + + assert(a.empty); +} + +//version (std_allocator_benchmark) +unittest +{ + static void testSpeed(A)() + { + static if (stateSize!A) A a; + else alias a = A.instance; + + void[][128] bufs; + + import std.random; + foreach (i; 0 .. 100_000) + { + auto j = uniform(0, bufs.length); + switch (uniform(0, 2)) + { + case 0: + a.deallocate(bufs[j]); + bufs[j] = a.allocate(uniform(0, 4096)); + break; + case 1: + a.deallocate(bufs[j]); + bufs[j] = null; + break; + default: + assert(0); + } + } + } + + alias FList = FreeList!(GCAllocator, 0, unbounded); + alias A = Segregator!( + 8, FreeList!(GCAllocator, 0, 8), + 128, Bucketizer!(FList, 1, 128, 16), + 256, Bucketizer!(FList, 129, 256, 32), + 512, Bucketizer!(FList, 257, 512, 64), + 1024, Bucketizer!(FList, 513, 1024, 128), + 2048, Bucketizer!(FList, 1025, 2048, 256), + 3584, Bucketizer!(FList, 2049, 3584, 512), + 4072 * 1024, AllocatorList!( + (size_t n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate( + max(n, 4072 * 1024)))), + GCAllocator + ); + + import std.datetime, std.experimental.allocator.null_allocator; + if (false) writeln(benchmark!( + testSpeed!NullAllocator, + testSpeed!Mallocator, + testSpeed!GCAllocator, + testSpeed!(ThreadLocal!A), + testSpeed!(A), + )(20)[].map!(t => t.to!("seconds", double))); +} + +unittest +{ + auto a = allocatorObject(Mallocator.instance); + auto b = a.allocate(100); + assert(b.length == 100); + + FreeList!(GCAllocator, 0, 8) fl; + auto sa = allocatorObject(fl); + b = a.allocate(101); + assert(b.length == 101); + + FallbackAllocator!(InSituRegion!(10240, 64), GCAllocator) fb; + // Doesn't work yet... + //a = allocatorObject(fb); + //b = a.allocate(102); + //assert(b.length == 102); +} + +/// +unittest +{ + /// Define an allocator bound to the built-in GC. + IAllocator alloc = allocatorObject(GCAllocator.instance); + auto b = alloc.allocate(42); + assert(b.length == 42); + assert(alloc.deallocate(b) == Ternary.yes); + + // Define an elaborate allocator and bind it to the class API. + // Note that the same variable "alloc" is used. + alias FList = FreeList!(GCAllocator, 0, unbounded); + alias A = ThreadLocal!( + Segregator!( + 8, FreeList!(GCAllocator, 0, 8), + 128, Bucketizer!(FList, 1, 128, 16), + 256, Bucketizer!(FList, 129, 256, 32), + 512, Bucketizer!(FList, 257, 512, 64), + 1024, Bucketizer!(FList, 513, 1024, 128), + 2048, Bucketizer!(FList, 1025, 2048, 256), + 3584, Bucketizer!(FList, 2049, 3584, 512), + 4072 * 1024, AllocatorList!( + (n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate( + max(n, 4072 * 1024)))), + GCAllocator + ) + ); + + auto alloc2 = allocatorObject(A.instance); + b = alloc.allocate(101); + assert(alloc.deallocate(b) == Ternary.yes); +} diff --git a/std/experimental/allocator/showcase.d b/std/experimental/allocator/showcase.d new file mode 100644 index 00000000000..84bad447671 --- /dev/null +++ b/std/experimental/allocator/showcase.d @@ -0,0 +1,92 @@ +/** + +Collection of typical and useful prebuilt allocators using the given +components. User code would typically import this module and use its +facilities, or import individual heap building blocks and assemble them. + +*/ +module std.experimental.allocator.showcase; + +import std.experimental.allocator.building_blocks.fallback_allocator, + std.experimental.allocator.gc_allocator, + std.experimental.allocator.building_blocks.region; +import std.traits : hasMember; + +/** + +Allocator that uses stack allocation for up to $(D stackSize) bytes and +then falls back to $(D Allocator). Defined as: + +---- +alias StackFront(size_t stackSize, Allocator) = + FallbackAllocator!( + InSituRegion!(stackSize, Allocator.alignment, + hasMember!(Allocator, "deallocate") + ? Yes.defineDeallocate + : No.defineDeallocate), + Allocator); +---- + +Choosing `stackSize` is as always a compromise. Too small a size exhausts the +stack storage after a few allocations, after which there are no gains over the +backup allocator. Too large a size increases the stack consumed by the thread +and may end up worse off because it explores cold portions of the stack. + +*/ +alias StackFront(size_t stackSize, Allocator = GCAllocator) = + FallbackAllocator!( + InSituRegion!(stackSize, Allocator.alignment), + Allocator); + +/// +@system unittest +{ + StackFront!4096 a; + auto b = a.allocate(4000); + assert(b.length == 4000); + auto c = a.allocate(4000); + assert(c.length == 4000); + a.deallocate(b); + a.deallocate(c); +} + +/** +Creates a scalable `AllocatorList` of `Regions`, each having at least +`bytesPerRegion` bytes. Allocation is very fast. This allocator does not offer +`deallocate` but does free all regions in its destructor. It is recommended for +short-lived batch applications that count on never running out of memory. +*/ +auto mmapRegionList(size_t bytesPerRegion) +{ + static struct Factory + { + size_t bytesPerRegion; + private import std.algorithm.comparison : max; + private import std.experimental.allocator.building_blocks.region + : Region; + private import std.experimental.allocator.mmap_allocator + : MmapAllocator; + this(size_t n) + { + bytesPerRegion = n; + } + auto opCall(size_t n) + { + return Region!MmapAllocator(max(n, bytesPerRegion)); + } + } + import std.experimental.allocator.building_blocks.allocator_list + : AllocatorList; + import std.experimental.allocator.building_blocks.null_allocator + : NullAllocator; + auto shop = Factory(bytesPerRegion); + return AllocatorList!(Factory, NullAllocator)(shop); +} + +/// +@system unittest +{ + auto alloc = mmapRegionList(1024 * 1024); + const b = alloc.allocate(100); + assert(b.length == 100); +} diff --git a/std/experimental/allocator/typed.d b/std/experimental/allocator/typed.d new file mode 100644 index 00000000000..754c9359f0e --- /dev/null +++ b/std/experimental/allocator/typed.d @@ -0,0 +1,423 @@ +/** +This module defines `TypedAllocator`, a statically-typed allocator that +aggregates multiple untyped allocators and uses them depending on the static +properties of the types allocated. For example, distinct allocators may be used +for thread-local vs. thread-shared data, or for fixed-size data (`struct`, +`class` objects) vs. resizable data (arrays). + +Macros: +T2=$(TR $(D $1) $(TD $(ARGS $+))) +*/ + +module std.experimental.allocator.typed; + +import std.experimental.allocator; +import std.experimental.allocator.common; +import std.traits : isPointer, hasElaborateDestructor; +import std.typecons : Flag, Yes, No; +import std.range : isInputRange, isForwardRange, walkLength, save, empty, + front, popFront; + +/** +Allocation-related flags dictated by type characteristics. `TypedAllocator` +deduces these flags from the type being allocated and uses the appropriate +allocator accordingly. +*/ +enum AllocFlag : uint +{ + init = 0, + /** + Fixed-size allocation (unlikely to get reallocated later). Examples: `int`, + `double`, any `struct` or `class` type. By default it is assumed that the + allocation is variable-size, i.e. susceptible to later reallocation + (for example all array types). This flag is advisory, i.e. in-place resizing + may be attempted for `fixedSize` allocations and may succeed. The flag is + just a hint to the compiler it may use allocation strategies that work well + with objects of fixed size. + */ + fixedSize = 1, + /** + The type being allocated embeds no pointers. Examples: `int`, `int[]`, $(D + Tuple!(int, float)). The implicit conservative assumption is that the type + has members with indirections so it needs to be scanned if garbage + collected. Example of types with pointers: `int*[]`, $(D Tuple!(int, + string)). + */ + hasNoIndirections = 4, + /** + By default it is conservatively assumed that allocated memory may be `cast` + to `shared`, passed across threads, and deallocated in a different thread + than the one that allocated it. If that's not the case, there are two + options. First, `immutableShared` means the memory is allocated for + `immutable` data and will be deallocated in the same thread it was + allocated in. Second, `threadLocal` means the memory is not to be shared + across threads at all. The two flags cannot be simultaneously present. + */ + immutableShared = 8, + /// ditto + threadLocal = 16, +} + +/** +`TypedAllocator` acts like a chassis on which several specialized allocators +can be assembled. To let the system make a choice about a particular kind of +allocation, use `Default` for the respective parameters. + +There is a hierarchy of allocation kinds. When an allocator is implemented for +a given combination of flags, it is used. Otherwise, the next down the list is +chosen. + +$(BOOKTABLE , + +$(TR $(TH `AllocFlag` combination) $(TH Description)) + +$(T2 AllocFlag.threadLocal |$(NBSP)AllocFlag.hasNoIndirections +|$(NBSP)AllocFlag.fixedSize, +This is the most specific allocation policy: the memory being allocated is +thread local, has no indirections at all, and will not be reallocated. Examples +of types fitting this description: `int`, `double`, $(D Tuple!(int, long)), but +not $(D Tuple!(int, string)), which contains an indirection.) + +$(T2 AllocFlag.threadLocal |$(NBSP)AllocFlag.hasNoIndirections, +As above, but may be reallocated later. Examples of types fitting this +description are $(D int[]), $(D double[]), $(D Tuple!(int, long)[]), but not +$(D Tuple!(int, string)[]), which contains an indirection.) + +$(T2 AllocFlag.threadLocal, +As above, but may embed indirections. Examples of types fitting this +description are $(D int*[]), $(D Object[]), $(D Tuple!(int, string)[]).) + +$(T2 AllocFlag.immutableShared |$(NBSP)AllocFlag.hasNoIndirections +|$(NBSP)AllocFlag.fixedSize, +The type being allocated is `immutable` and has no pointers. The thread that +allocated it must also deallocate it. Example: `immutable(int)`.) + +$(T2 AllocFlag.immutableShared |$(NBSP)AllocFlag.hasNoIndirections, +As above, but the type may be appended to in the future. Example: `string`.) + +$(T2 AllocFlag.immutableShared, +As above, but the type may embed references. Example: `immutable(Object)[]`.) + +$(T2 AllocFlag.hasNoIndirections |$(NBSP)AllocFlag.fixedSize, +The type being allocated may be shared across threads, embeds no indirections, +and has fixed size.) + +$(T2 AllocFlag.hasNoIndirections, +The type being allocated may be shared across threads, may embed indirections, +and has variable size.) + +$(T2 AllocFlag.fixedSize, +The type being allocated may be shared across threads, may embed indirections, +and has fixed size.) + +$(T2 0, The most conservative/general allocation: memory may be shared, +deallocated in a different thread, may or may not be resized, and may embed +references.) +) + +Params: +PrimaryAllocator = The default allocator. +Policies = Zero or more pairs consisting of an `AllocFlag` and an allocator +type. +*/ +struct TypedAllocator(PrimaryAllocator, Policies...) +{ + import std.typecons : Tuple; + import std.meta : AliasSeq; + import std.algorithm.sorting : isSorted; + + static assert(Policies.length == 0 || isSorted([Stride2!Policies])); + + private template Stride2(T...) + { + static if (T.length >= 2) + { + alias Stride2 = AliasSeq!(T[0], Stride2!(T[2 .. $])); + } + else + { + alias Stride2 = AliasSeq!(T[0 .. $]); + } + } + + // state + static if (stateSize!PrimaryAllocator) private PrimaryAllocator primary; + else alias primary = PrimaryAllocator.instance; + static if (Policies.length > 0) + private Tuple!(Stride2!(Policies[1 .. $])) extras; + + private static bool match(uint have, uint want) + { + enum uint maskAway = + ~(AllocFlag.immutableShared | AllocFlag.threadLocal); + // Do we offer thread local? + if (have & AllocFlag.threadLocal) + { + if (want & AllocFlag.threadLocal) + return match(have & maskAway, want & maskAway); + return false; + } + if (have & AllocFlag.immutableShared) + { + // Okay to ask for either thread local or immutable shared + if (want & (AllocFlag.threadLocal + | AllocFlag.immutableShared)) + return match(have & maskAway, want & maskAway); + return false; + } + // From here on we have full-blown thread sharing. + if (have & AllocFlag.hasNoIndirections) + { + if (want & AllocFlag.hasNoIndirections) + return match(have & ~AllocFlag.hasNoIndirections, + want & ~AllocFlag.hasNoIndirections); + return false; + } + // Fixed size or variable size both match. + return true; + } + + /** + Given `flags` as a combination of `AllocFlag` values, or a type `T`, returns + the allocator that's a closest fit in capabilities. + */ + auto ref allocatorFor(uint flags)() + { + static if (Policies.length == 0 || !match(Policies[0], flags)) + { + return primary; + } + else static if (Policies.length && match(Policies[$ - 2], flags)) + { + return extras[$ - 1]; + } + else + { + foreach (i, choice; Stride2!Policies) + { + static if (!match(choice, flags)) + { + return extras[i - 1]; + } + } + assert(0); + } + } + + /// ditto + auto ref allocatorFor(T)() + { + static if (is(T == void[])) + { + return primary; + } + else + { + return allocatorFor!(type2flags!T)(); + } + } + + /** + Given a type `T`, returns its allocation-related flags as a combination of + `AllocFlag` values. + */ + static uint type2flags(T)() + { + uint result; + static if (is(T == immutable)) + result |= AllocFlag.immutableShared; + else static if (is(T == shared)) + result |= AllocFlag.forSharing; + static if (!is(T == U[], U)) + result |= AllocFlag.fixedSize; + import std.traits : hasIndirections; + static if (!hasIndirections!T) + result |= AllocFlag.hasNoIndirections; + return result; + } + + /** + Dynamically allocates (using the appropriate allocator chosen with + `allocatorFor!T`) and then creates in the memory allocated an object of + type `T`, using `args` (if any) for its initialization. Initialization + occurs in the memory allocated and is otherwise semantically the same as + `T(args)`. (Note that using `make!(T[])` creates a pointer to an + (empty) array of `T`s, not an array. To allocate and initialize an + array, use `makeArray!T` described below.) + + Params: + T = Type of the object being created. + args = Optional arguments used for initializing the created object. If not + present, the object is default constructed. + + Returns: If `T` is a class type, returns a reference to the created `T` + object. Otherwise, returns a `T*` pointing to the created object. In all + cases, returns `null` if allocation failed. + + Throws: If `T`'s constructor throws, deallocates the allocated memory and + propagates the exception. + */ + auto make(T, A...)(auto ref A args) + { + return .make!T(allocatorFor!T, args); + } + + /** + Create an array of `T` with `length` elements. The array is either + default-initialized, filled with copies of `init`, or initialized with + values fetched from `range`. + + Params: + T = element type of the array being created + length = length of the newly created array + init = element used for filling the array + range = range used for initializing the array elements + + Returns: + The newly-created array, or `null` if either `length` was `0` or + allocation failed. + + Throws: + The first two overloads throw only if the used allocator's primitives do. + The overloads that involve copy initialization deallocate memory and propagate the exception if the copy operation throws. + */ + T[] makeArray(T)(size_t length) + { + return .makeArray!T(allocatorFor!(T[]), length); + } + + /// Ditto + T[] makeArray(T)(size_t length, auto ref T init) + { + return .makeArray!T(allocatorFor!(T[]), init, length); + } + + /// Ditto + T[] makeArray(T, R)(R range) + if (isInputRange!R) + { + return .makeArray!T(allocatorFor!(T[]), range); + } + + /** + Grows `array` by appending `delta` more elements. The needed memory is + allocated using the same allocator that was used for the array type. The + extra elements added are either default-initialized, filled with copies of + `init`, or initialized with values fetched from `range`. + + Params: + T = element type of the array being created + array = a reference to the array being grown + delta = number of elements to add (upon success the new length of `array` + is $(D array.length + delta)) + init = element used for filling the array + range = range used for initializing the array elements + + Returns: + `true` upon success, `false` if memory could not be allocated. In the + latter case `array` is left unaffected. + + Throws: + The first two overloads throw only if the used allocator's primitives do. + The overloads that involve copy initialization deallocate memory and + propagate the exception if the copy operation throws. + */ + bool expandArray(T)(ref T[] array, size_t delta) + { + return .expandArray(allocatorFor!(T[]), array, delta); + } + /// Ditto + bool expandArray(T)(T[] array, size_t delta, auto ref T init) + { + return .expandArray(allocatorFor!(T[]), array, delta, init); + } + /// Ditto + bool expandArray(T, R)(ref T[] array, R range) + if (isInputRange!R) + { + return .expandArray(allocatorFor!(T[]), array, range); + } + + /** + Shrinks an array by `delta` elements using `allocatorFor!(T[])`. + + If $(D arr.length < delta), does nothing and returns `false`. Otherwise, + destroys the last $(D arr.length - delta) elements in the array and then + reallocates the array's buffer. If reallocation fails, fills the array with + default-initialized data. + + Params: + T = element type of the array being created + arr = a reference to the array being shrunk + delta = number of elements to remove (upon success the new length of + `arr` is $(D arr.length - delta)) + + Returns: + `true` upon success, `false` if memory could not be reallocated. In the + latter case $(D arr[$ - delta .. $]) is left with default-initialized + elements. + + Throws: + The first two overloads throw only if the used allocator's primitives do. + The overloads that involve copy initialization deallocate memory and + propagate the exception if the copy operation throws. + */ + bool shrinkArray(T)(ref T[] arr, size_t delta) + { + return .shrinkArray(allocatorFor!(T[]), arr, delta); + } + + /** + Destroys and then deallocates (using `allocatorFor!T`) the object pointed + to by a pointer, the class object referred to by a `class` or `interface` + reference, or an entire array. It is assumed the respective entities had + been allocated with the same allocator. + */ + void dispose(T)(T* p) + { + return .dispose(allocatorFor!T, p); + } + /// Ditto + void dispose(T)(T p) + if (is(T == class) || is(T == interface)) + { + return .dispose(allocatorFor!T, p); + } + /// Ditto + void dispose(T)(T[] array) + { + return .dispose(allocatorFor!(T[]), array); + } +} + +/// +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + alias MyAllocator = TypedAllocator!(GCAllocator, + AllocFlag.fixedSize | AllocFlag.threadLocal, Mallocator, + AllocFlag.fixedSize | AllocFlag.threadLocal + | AllocFlag.hasNoIndirections, + MmapAllocator, + ); + MyAllocator a; + auto b = &a.allocatorFor!0(); + static assert(is(typeof(*b) == shared GCAllocator)); + enum f1 = AllocFlag.fixedSize | AllocFlag.threadLocal; + auto c = &a.allocatorFor!f1(); + static assert(is(typeof(*c) == Mallocator)); + enum f2 = AllocFlag.fixedSize | AllocFlag.threadLocal; + static assert(is(typeof(a.allocatorFor!f2()) == Mallocator)); + // Partial match + enum f3 = AllocFlag.threadLocal; + static assert(is(typeof(a.allocatorFor!f3()) == Mallocator)); + + int* p = a.make!int; + scope(exit) a.dispose(p); + int[] arr = a.makeArray!int(42); + scope(exit) a.dispose(arr); + assert(a.expandArray(arr, 3)); + assert(a.shrinkArray(arr, 4)); +} diff --git a/std/experimental/checkedint.d b/std/experimental/checkedint.d new file mode 100644 index 00000000000..a1701a8419e --- /dev/null +++ b/std/experimental/checkedint.d @@ -0,0 +1,3060 @@ +/** +$(SCRIPT inhibitQuickIndex = 1;) + +This module defines facilities for efficient checking of integral operations +against overflow, casting with loss of precision, unexpected change of sign, +etc. The checking (and possibly correction) can be done at operation level, for +example $(LREF opChecked)$(D !"+"(x, y, overflow)) adds two integrals `x` and +`y` and sets `overflow` to `true` if an overflow occurred. The flag `overflow` +(a `bool` passed by reference) is not touched if the operation succeeded, so the +same flag can be reused for a sequence of operations and tested at the end. + +Issuing individual checked operations is flexible and efficient but often +tedious. The $(LREF Checked) facility offers encapsulated integral wrappers that +do all checking internally and have configurable behavior upon erroneous +results. For example, `Checked!int` is a type that behaves like `int` but aborts +execution immediately whenever involved in an operation that produces the +arithmetically wrong result. The accompanying convenience function $(LREF +checked) uses type deduction to convert a value `x` of integral type `T` to +`Checked!T` by means of `checked(x)`. For example: + +--- +void main() +{ + import std.experimental.checkedint, std.stdio; + writeln((checked(5) + 7).get); // 12 + writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow +} +--- + +Similarly, $(D checked(-1) > uint(0)) aborts execution (even though the built-in +comparison $(D int(-1) > uint(0)) is surprisingly true due to language's +conversion rules modeled after C). Thus, `Checked!int` is a virtually drop-in +replacement for `int` useable in debug builds, to be replaced by `int` in +release mode if efficiency demands it. + +`Checked` has customizable behavior with the help of a second type parameter, +`Hook`. Depending on what methods `Hook` defines, core operations on the +underlying integral may be verified for overflow or completely redefined. If +`Hook` defines no method at all and carries no state, there is no change in +behavior, i.e. $(D Checked!(int, void)) is a wrapper around `int` that adds no +customization at all. + +This module provides a few predefined hooks (below) that add useful behavior to +`Checked`: + +$(BOOKTABLE , + $(TR $(TD $(LREF Abort)) $(TD + fails every incorrect operation with a message to $(REF + stderr, std, stdio) followed by a call to `assert(0)`. It is the default + second parameter, i.e. `Checked!short` is the same as + $(D Checked!(short, Abort)). + )) + $(TR $(TD $(LREF Warn)) $(TD + prints incorrect operations to $(REF stderr, std, stdio) + but otherwise preserves the built-in behavior. + )) + $(TR $(TD $(LREF ProperCompare)) $(TD + fixes the comparison operators `==`, `!=`, `<`, `<=`, `>`, and `>=` + to return correct results in all circumstances, + at a slight cost in efficiency. For example, + $(D Checked!(uint, ProperCompare)(1) > -1) is `true`, + which is not the case for the built-in comparison. Also, comparing + numbers for equality with floating-point numbers only passes if the + integral can be converted to the floating-point number precisely, + so as to preserve transitivity of equality. + )) + $(TR $(TD $(LREF WithNaN)) $(TD + reserves a special "Not a Number" (NaN) value akin to the homonym value + reserved for floating-point values. Once a $(D Checked!(X, WithNaN)) + gets this special value, it preserves and propagates it until + reassigned. $(LREF isNaN) can be used to query whether the object + is not a number. + )) + $(TR $(TD $(LREF Saturate)) $(TD + implements saturating arithmetic, i.e. $(D Checked!(int, Saturate)) + "stops" at `int.max` for all operations that would cause an `int` to + overflow toward infinity, and at `int.min` for all operations that would + correspondingly overflow toward negative infinity. + )) +) + + +These policies may be used alone, e.g. $(D Checked!(uint, WithNaN)) defines a +`uint`-like type that reaches a stable NaN state for all erroneous operations. +They may also be "stacked" on top of each other, owing to the property that a +checked integral emulates an actual integral, which means another checked +integral can be built on top of it. Some combinations of interest include: + +$(BOOKTABLE , + $(TR $(TD $(D Checked!(Checked!int, ProperCompare)))) + $(TR $(TD +defines an `int` with fixed +comparison operators that will fail with `assert(0)` upon overflow. (Recall that +`Abort` is the default policy.) The order in which policies are combined is +important because the outermost policy (`ProperCompare` in this case) has the +first crack at intercepting an operator. The converse combination $(D +Checked!(Checked!(int, ProperCompare))) is meaningless because `Abort` will +intercept comparison and will fail without giving `ProperCompare` a chance to +intervene. + )) + $(TR $(TD)) + $(TR $(TDNW $(D Checked!(Checked!(int, ProperCompare), WithNaN)))) + $(TR $(TD +defines an `int`-like +type that supports a NaN value. For values that are not NaN, comparison works +properly. Again the composition order is important; $(D Checked!(Checked!(int, +WithNaN), ProperCompare)) does not have good semantics because `ProperCompare` +intercepts comparisons before the numbers involved are tested for NaN. + )) +) + +The hook's members are looked up statically in a Design by Introspection manner +and are all optional. The table below illustrates the members that a hook type +may define and their influence over the behavior of the `Checked` type using it. +In the table, `hook` is an alias for `Hook` if the type `Hook` does not +introduce any state, or an object of type `Hook` otherwise. + +$(TABLE , +$(TR $(TH `Hook` member) $(TH Semantics in $(D Checked!(T, Hook))) +) +$(TR $(TD `defaultValue`) $(TD If defined, `Hook.defaultValue!T` is used as the +default initializer of the payload.) +) +$(TR $(TD `min`) $(TD If defined, `Hook.min!T` is used as the minimum value of +the payload.) +) +$(TR $(TD `max`) $(TD If defined, `Hook.max!T` is used as the maximum value of +the payload.) +) +$(TR $(TD `hookOpCast`) $(TD If defined, `hook.hookOpCast!U(get)` is forwarded +to unconditionally when the payload is to be cast to type `U`.) +) +$(TR $(TD `onBadCast`) $(TD If defined and `hookOpCast` is $(I not) defined, +`onBadCast!U(get)` is forwarded to when the payload is to be cast to type `U` +and the cast would lose information or force a change of sign.) +) +$(TR $(TD `hookOpEquals`) $(TD If defined, $(D hook.hookOpEquals(get, rhs)) is +forwarded to unconditionally when the payload is compared for equality against +value `rhs` of integral, floating point, or Boolean type.) +) +$(TR $(TD `hookOpCmp`) $(TD If defined, $(D hook.hookOpCmp(get, rhs)) is +forwarded to unconditionally when the payload is compared for ordering against +value `rhs` of integral, floating point, or Boolean type.) +) +$(TR $(TD `hookOpUnary`) $(TD If defined, `hook.hookOpUnary!op(get)` (where `op` +is the operator symbol) is forwarded to for unary operators `-` and `~`. In +addition, for unary operators `++` and `--`, `hook.hookOpUnary!op(payload)` is +called, where `payload` is a reference to the value wrapped by `Checked` so the +hook can change it.) +) +$(TR $(TD `hookOpBinary`) $(TD If defined, $(D hook.hookOpBinary!op(get, rhs)) +(where `op` is the operator symbol and `rhs` is the right-hand side operand) is +forwarded to unconditionally for binary operators `+`, `-`, `*`, `/`, `%`, +`^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) +) +$(TR $(TD `hookOpBinaryRight`) $(TD If defined, $(D +hook.hookOpBinaryRight!op(lhs, get)) (where `op` is the operator symbol and +`lhs` is the left-hand side operand) is forwarded to unconditionally for binary +operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) +) +$(TR $(TD `onOverflow`) $(TD If defined, `hook.onOverflow!op(get)` is forwarded +to for unary operators that overflow but only if `hookOpUnary` is not defined. +Unary `~` does not overflow; unary `-` overflows only when the most negative +value of a signed type is negated, and the result of the hook call is returned. +When the increment or decrement operators overflow, the payload is assigned the +result of `hook.onOverflow!op(get)`. When a binary operator overflows, the +result of $(D hook.onOverflow!op(get, rhs)) is returned, but only if `Hook` does +not define `hookOpBinary`.) +) +$(TR $(TD `hookOpOpAssign`) $(TD If defined, $(D hook.hookOpOpAssign!op(payload, +rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side +operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, +`^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.) +) +$(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound)) +(where `value` is the value being assigned) is forwarded to when the result of +binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, +and `>>>=` is smaller than the smallest value representable by `T`.) +) +$(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound)) +(where `value` is the value being assigned) is forwarded to when the result of +binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, +and `>>>=` is larger than the largest value representable by `T`.) +) +) + +*/ +module std.experimental.checkedint; +import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual; + +/// +@system unittest +{ + int[] concatAndAdd(int[] a, int[] b, int offset) + { + // Aborts on overflow on size computation + auto r = new int[(checked(a.length) + b.length).get]; + // Aborts on overflow on element computation + foreach (i; 0 .. a.length) + r[i] = (a[i] + checked(offset)).get; + foreach (i; 0 .. b.length) + r[i + a.length] = (b[i] + checked(offset)).get; + return r; + } + assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]); +} + +/** +Checked integral type wraps an integral `T` and customizes its behavior with the +help of a `Hook` type. The type wrapped must be one of the predefined integrals +(unqualified), or another instance of `Checked`. +*/ +struct Checked(T, Hook = Abort) +if (isIntegral!T || is(T == Checked!(U, H), U, H)) +{ + import std.algorithm.comparison : among; + import std.traits : hasMember; + import std.experimental.allocator.common : stateSize; + + /** + The type of the integral subject to checking. + */ + alias Representation = T; + + // state { + static if (hasMember!(Hook, "defaultValue")) + private T payload = Hook.defaultValue!T; + else + private T payload; + /** + `hook` is a member variable if it has state, or an alias for `Hook` + otherwise. + */ + static if (stateSize!Hook > 0) Hook hook; + else alias hook = Hook; + // } state + + // get + /** + Returns a copy of the underlying value. + */ + auto get() inout { return payload; } + /// + @safe unittest + { + auto x = checked(ubyte(42)); + static assert(is(typeof(x.get()) == ubyte)); + assert(x.get == 42); + const y = checked(ubyte(42)); + static assert(is(typeof(y.get()) == const ubyte)); + assert(y.get == 42); + } + + /** + Defines the minimum and maximum. These values are hookable by defining + `Hook.min` and/or `Hook.max`. + */ + static if (hasMember!(Hook, "min")) + { + enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T); + /// + @system unittest + { + assert(Checked!short.min == -32768); + assert(Checked!(short, WithNaN).min == -32767); + assert(Checked!(uint, WithNaN).max == uint.max - 1); + } + } + else + enum Checked!(T, Hook) min = Checked(T.min); + /// ditto + static if (hasMember!(Hook, "max")) + enum Checked!(T, Hook) max = Checked(Hook.max!T); + else + enum Checked!(T, Hook) max = Checked(T.max); + + /** + Constructor taking a value properly convertible to the underlying type. `U` + may be either an integral that can be converted to `T` without a loss, or + another `Checked` instance whose representation may be in turn converted to + `T` without a loss. + */ + this(U)(U rhs) + if (valueConvertible!(U, T) || + !isIntegral!T && is(typeof(T(rhs))) || + is(U == Checked!(V, W), V, W) && + is(typeof(Checked!(T, Hook)(rhs.get)))) + { + static if (isIntegral!U) + payload = rhs; + else + payload = rhs.payload; + } + /// + @system unittest + { + auto a = checked(42L); + assert(a == 42); + auto b = Checked!long(4242); // convert 4242 to long + assert(b == 4242); + } + + /** + Assignment operator. Has the same constraints as the constructor. + */ + void opAssign(U)(U rhs) if (is(typeof(Checked!(T, Hook)(rhs)))) + { + static if (isIntegral!U) + payload = rhs; + else + payload = rhs.payload; + } + /// + @system unittest + { + Checked!long a; + a = 42L; + assert(a == 42); + a = 4242; + assert(a == 4242); + } + + // opCast + /** + Casting operator to integral, `bool`, or floating point type. If `Hook` + defines `hookOpCast`, the call immediately returns + `hook.hookOpCast!U(get)`. Otherwise, casting to `bool` yields $(D + get != 0) and casting to another integral that can represent all + values of `T` returns `get` promoted to `U`. + + If a cast to a floating-point type is requested and `Hook` defines + `onBadCast`, the cast is verified by ensuring $(D get == cast(T) + U(get)). If that is not `true`, `hook.onBadCast!U(get)` is returned. + + If a cast to an integral type is requested and `Hook` defines `onBadCast`, + the cast is verified by ensuring `get` and $(D cast(U) + get) are the same arithmetic number. (Note that `int(-1)` and + `uint(1)` are different values arithmetically although they have the same + bitwise representation and compare equal by language rules.) If the numbers + are not arithmetically equal, `hook.onBadCast!U(get)` is + returned. + + */ + U opCast(U, this _)() + if (isIntegral!U || isFloatingPoint!U || is(U == bool)) + { + static if (hasMember!(Hook, "hookOpCast")) + { + return hook.hookOpCast!U(payload); + } + else static if (is(U == bool)) + { + return payload != 0; + } + else static if (valueConvertible!(T, U)) + { + return payload; + } + // may lose bits or precision + else static if (!hasMember!(Hook, "onBadCast")) + { + return cast(U) payload; + } + else + { + if (isUnsigned!T || !isUnsigned!U || + T.sizeof > U.sizeof || payload >= 0) + { + auto result = cast(U) payload; + // If signedness is different, we need additional checks + if (result == payload && + (!isUnsigned!T || isUnsigned!U || result >= 0)) + return result; + } + return hook.onBadCast!U(payload); + } + } + /// + @system unittest + { + assert(cast(uint) checked(42) == 42); + assert(cast(uint) checked!WithNaN(-42) == uint.max); + } + + // opEquals + /** + Compares `this` against `rhs` for equality. If `Hook` defines + `hookOpEquals`, the function forwards to $(D + hook.hookOpEquals(get, rhs)). Otherwise, the result of the + built-in operation $(D get == rhs) is returned. + + If `U` is also an instance of `Checked`, both hooks (left- and right-hand + side) are introspected for the method `hookOpEquals`. If both define it, + priority is given to the left-hand side. + + */ + bool opEquals(U, this _)(U rhs) + if (isIntegral!U || isFloatingPoint!U || is(U == bool) || + is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload))) + { + static if (is(U == Checked!(V, W), V, W)) + { + alias R = typeof(payload + rhs.payload); + static if (is(Hook == W)) + { + // Use the lhs hook if there + return this == rhs.payload; + } + else static if (valueConvertible!(T, R) && valueConvertible!(V, R)) + { + return payload == rhs.payload; + } + else static if (hasMember!(Hook, "hookOpEquals")) + { + return hook.hookOpEquals(payload, rhs.payload); + } + else static if (hasMember!(W, "hookOpEquals")) + { + return rhs.hook.hookOpEquals(rhs.payload, payload); + } + else + { + return payload == rhs.payload; + } + } + else static if (hasMember!(Hook, "hookOpEquals")) + return hook.hookOpEquals(payload, rhs); + else static if (isIntegral!U || isFloatingPoint!U || is(U == bool)) + return payload == rhs; + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + static struct MyHook + { + static bool thereWereErrors; + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + if (lhs != rhs) return false; + static if (isUnsigned!L && !isUnsigned!R) + { + if (lhs > 0 && rhs < 0) thereWereErrors = true; + } + else static if (isUnsigned!R && !isUnsigned!L) + if (lhs < 0 && rhs > 0) thereWereErrors = true; + // Preserve built-in behavior. + return true; + } + } + auto a = checked!MyHook(-42); + assert(a == uint(-42)); + assert(MyHook.thereWereErrors); + MyHook.thereWereErrors = false; + assert(checked!MyHook(uint(-42)) == -42); + assert(MyHook.thereWereErrors); + static struct MyHook2 + { + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + return lhs == rhs; + } + } + MyHook.thereWereErrors = false; + assert(checked!MyHook2(uint(-42)) == a); + // Hook on left hand side takes precedence, so no errors + assert(!MyHook.thereWereErrors); + } + + // opCmp + /** + + Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`, + the function forwards to $(D hook.hookOpCmp(get, rhs)). Otherwise, the + result of the built-in comparison operation is returned. + + If `U` is also an instance of `Checked`, both hooks (left- and right-hand + side) are introspected for the method `hookOpCmp`. If both define it, + priority is given to the left-hand side. + + */ + auto opCmp(U, this _)(const U rhs) //const pure @safe nothrow @nogc + if (isIntegral!U || isFloatingPoint!U || is(U == bool)) + { + static if (hasMember!(Hook, "hookOpCmp")) + { + return hook.hookOpCmp(payload, rhs); + } + else static if (valueConvertible!(T, U) || valueConvertible!(U, T)) + { + return payload < rhs ? -1 : payload > rhs; + } + else static if (isFloatingPoint!U) + { + U lhs = payload; + return lhs < rhs ? U(-1.0) + : lhs > rhs ? U(1.0) + : lhs == rhs ? U(0.0) : U.init; + } + else + { + return payload < rhs ? -1 : payload > rhs; + } + } + + /// ditto + auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs) + { + alias R = typeof(payload + rhs.payload); + static if (valueConvertible!(T, R) && valueConvertible!(U, R)) + { + return payload < rhs.payload ? -1 : payload > rhs.payload; + } + else static if (is(Hook == Hook1)) + { + // Use the lhs hook + return this.opCmp(rhs.payload); + } + else static if (hasMember!(Hook, "hookOpCmp")) + { + return hook.hookOpCmp(get, rhs.get); + } + else static if (hasMember!(Hook1, "hookOpCmp")) + { + return -rhs.hook.hookOpCmp(rhs.payload, get); + } + else + { + return payload < rhs.payload ? -1 : payload > rhs.payload; + } + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + static struct MyHook + { + static bool thereWereErrors; + static int hookOpCmp(L, R)(L lhs, R rhs) + { + static if (isUnsigned!L && !isUnsigned!R) + { + if (rhs < 0 && rhs >= lhs) + thereWereErrors = true; + } + else static if (isUnsigned!R && !isUnsigned!L) + { + if (lhs < 0 && lhs >= rhs) + thereWereErrors = true; + } + // Preserve built-in behavior. + return lhs < rhs ? -1 : lhs > rhs; + } + } + auto a = checked!MyHook(-42); + assert(a > uint(42)); + assert(MyHook.thereWereErrors); + static struct MyHook2 + { + static int hookOpCmp(L, R)(L lhs, R rhs) + { + // Default behavior + return lhs < rhs ? -1 : lhs > rhs; + } + } + MyHook.thereWereErrors = false; + assert(Checked!(uint, MyHook2)(uint(-42)) <= a); + //assert(Checked!(uint, MyHook2)(uint(-42)) >= a); + // Hook on left hand side takes precedence, so no errors + assert(!MyHook.thereWereErrors); + assert(a <= Checked!(uint, MyHook2)(uint(-42))); + assert(MyHook.thereWereErrors); + } + + // For coverage + static if (is(T == int) && is(Hook == void)) @system unittest + { + assert(checked(42) <= checked!void(42)); + assert(checked!void(42) <= checked(42u)); + assert(checked!void(42) <= checked!(void*)(42u)); + } + + // opUnary + /** + + Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not + overridable and always has built-in behavior (returns `this`). For the + others, if `Hook` defines `hookOpUnary`, `opUnary` forwards to $(D + Checked!(typeof(hook.hookOpUnary!op(get)), + Hook)(hook.hookOpUnary!op(get))). + + If `Hook` does not define `hookOpUnary` but defines `onOverflow`, `opUnary` + forwards to `hook.onOverflow!op(get)` in case an overflow occurs. + For `++` and `--`, the payload is assigned from the result of the call to + `onOverflow`. + + Note that unary `-` is considered to overflow if `T` is a signed integral of + 32 or 64 bits and is equal to the most negative value. This is because that + value has no positive negation. + + */ + auto opUnary(string op, this _)() + if (op == "+" || op == "-" || op == "~") + { + static if (op == "+") + return Checked(this); // "+" is not hookable + else static if (hasMember!(Hook, "hookOpUnary")) + { + auto r = hook.hookOpUnary!op(payload); + return Checked!(typeof(r), Hook)(r); + } + else static if (op == "-" && isIntegral!T && T.sizeof >= 4 && + !isUnsigned!T && hasMember!(Hook, "onOverflow")) + { + static assert(is(typeof(-payload) == typeof(payload))); + bool overflow; + import core.checkedint : negs; + auto r = negs(payload, overflow); + if (overflow) r = hook.onOverflow!op(payload); + return Checked(r); + } + else + return Checked(mixin(op ~ "payload")); + } + + /// ditto + ref Checked opUnary(string op)() return + if (op == "++" || op == "--") + { + static if (hasMember!(Hook, "hookOpUnary")) + hook.hookOpUnary!op(payload); + else static if (hasMember!(Hook, "onOverflow")) + { + static if (op == "++") + { + if (payload == max.payload) + payload = hook.onOverflow!"++"(payload); + else + ++payload; + } + else + { + if (payload == min.payload) + payload = hook.onOverflow!"--"(payload); + else + --payload; + } + } + else + mixin(op ~ "payload;"); + return this; + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + static struct MyHook + { + static bool thereWereErrors; + static L hookOpUnary(string x, L)(L lhs) + { + if (x == "-" && lhs == -lhs) thereWereErrors = true; + return -lhs; + } + } + auto a = checked!MyHook(long.min); + assert(a == -a); + assert(MyHook.thereWereErrors); + auto b = checked!void(42); + assert(++b == 43); + } + + // opBinary + /** + + Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, + and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to $(D + Checked!(typeof(hook.hookOpBinary!op(get, rhs)), + Hook)(hook.hookOpBinary!op(get, rhs))). + + If `Hook` does not define `hookOpBinary` but defines `onOverflow`, + `opBinary` forwards to `hook.onOverflow!op(get, rhs)` in case an + overflow occurs. + + If two `Checked` instances are involved in a binary operation and both + define `hookOpBinary`, the left-hand side hook has priority. If both define + `onOverflow`, a compile-time error occurs. + + */ + auto opBinary(string op, Rhs)(const Rhs rhs) + if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) + { + return opBinaryImpl!(op, Rhs, typeof(this))(rhs); + } + + /// ditto + auto opBinary(string op, Rhs)(const Rhs rhs) const + if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) + { + return opBinaryImpl!(op, Rhs, typeof(this))(rhs); + } + + private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs) + { + alias R = typeof(mixin("payload" ~ op ~ "rhs")); + static assert(is(typeof(mixin("payload" ~ op ~ "rhs")) == R)); + static if (isIntegral!R) alias Result = Checked!(R, Hook); + else alias Result = R; + + static if (hasMember!(Hook, "hookOpBinary")) + { + auto r = hook.hookOpBinary!op(payload, rhs); + return Checked!(typeof(r), Hook)(r); + } + else static if (is(Rhs == bool)) + { + return mixin("this" ~ op ~ "ubyte(rhs)"); + } + else static if (isFloatingPoint!Rhs) + { + return mixin("payload" ~ op ~ "rhs"); + } + else static if (hasMember!(Hook, "onOverflow")) + { + bool overflow; + auto r = opChecked!op(payload, rhs, overflow); + if (overflow) r = hook.onOverflow!op(payload, rhs); + return Result(r); + } + else + { + // Default is built-in behavior + return Result(mixin("payload" ~ op ~ "rhs")); + } + } + + /// ditto + auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) + { + return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); + } + + /// ditto + auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) const + { + return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); + } + + private + auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs) + { + alias R = typeof(get + rhs.payload); + static if (valueConvertible!(T, R) && valueConvertible!(U, R) || + is(Hook == Hook1)) + { + // Delegate to lhs + return mixin("this" ~ op ~ "rhs.payload"); + } + else static if (hasMember!(Hook, "hookOpBinary")) + { + return hook.hookOpBinary!op(payload, rhs); + } + else static if (hasMember!(Hook1, "hookOpBinary")) + { + // Delegate to rhs + return mixin("this.payload" ~ op ~ "rhs"); + } + else static if (hasMember!(Hook, "onOverflow") && + !hasMember!(Hook1, "onOverflow")) + { + // Delegate to lhs + return mixin("this" ~ op ~ "rhs.payload"); + } + else static if (hasMember!(Hook1, "onOverflow") && + !hasMember!(Hook, "onOverflow")) + { + // Delegate to rhs + return mixin("this.payload" ~ op ~ "rhs"); + } + else + { + static assert(0, "Conflict between lhs and rhs hooks," ~ + " use .get on one side to disambiguate."); + } + } + + static if (is(T == int) && is(Hook == void)) @system unittest + { + const a = checked(42); + assert(a + 1 == 43); + assert(a + checked(uint(42)) == 84); + assert(checked(42) + checked!void(42u) == 84); + assert(checked!void(42) + checked(42u) == 84); + + static struct MyHook + { + static uint tally; + static auto hookOpBinary(string x, L, R)(L lhs, R rhs) + { + ++tally; + return mixin("lhs" ~ x ~ "rhs"); + } + } + assert(checked!MyHook(42) + checked(42u) == 84); + assert(checked!void(42) + checked!MyHook(42u) == 84); + assert(MyHook.tally == 2); + } + + // opBinaryRight + /** + + Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, + `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on + the left-hand side, and a `Checked` instance is on the right-hand side. + + */ + auto opBinaryRight(string op, Lhs)(const Lhs lhs) + if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) + { + return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); + } + + /// ditto + auto opBinaryRight(string op, Lhs)(const Lhs lhs) const + if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) + { + return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); + } + + private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs) + { + static if (hasMember!(Hook, "hookOpBinaryRight")) + { + auto r = hook.hookOpBinaryRight!op(lhs, payload); + return Checked!(typeof(r), Hook)(r); + } + else static if (hasMember!(Hook, "hookOpBinary")) + { + auto r = hook.hookOpBinary!op(lhs, payload); + return Checked!(typeof(r), Hook)(r); + } + else static if (is(Lhs == bool)) + { + return mixin("ubyte(lhs)" ~ op ~ "this"); + } + else static if (isFloatingPoint!Lhs) + { + return mixin("lhs" ~ op ~ "payload"); + } + else static if (hasMember!(Hook, "onOverflow")) + { + bool overflow; + auto r = opChecked!op(lhs, T(payload), overflow); + if (overflow) r = hook.onOverflow!op(42); + return Checked!(typeof(r), Hook)(r); + } + else + { + // Default is built-in behavior + auto r = mixin("lhs" ~ op ~ "T(payload)"); + return Checked!(typeof(r), Hook)(r); + } + } + + static if (is(T == int) && is(Hook == void)) @system unittest + { + assert(1 + checked(1) == 2); + static uint tally; + static struct MyHook + { + static auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) + { + ++tally; + return mixin("lhs" ~ x ~ "rhs"); + } + } + assert(1 + checked!MyHook(1) == 2); + assert(tally == 1); + + immutable x1 = checked(1); + assert(1 + x1 == 2); + immutable x2 = checked!MyHook(1); + assert(1 + x2 == 2); + assert(tally == 2); + } + + // opOpAssign + /** + + Defines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, + `<<=`, `>>=`, and `>>>=`. + + If `Hook` defines `hookOpOpAssign`, `opOpAssign` forwards to + `hook.hookOpOpAssign!op(payload, rhs)`, where `payload` is a reference to + the internally held data so the hook can change it. + + Otherwise, the operator first evaluates $(D auto result = + opBinary!op(payload, rhs).payload), which is subject to the hooks in + `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if + `Hook` defines `onLowerBound`, the payload is assigned from $(D + hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T, + Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned + from $(D hook.onUpperBound(result, min)). + + In all other cases, the built-in behavior is carried out. + + Params: + op = The operator involved (without the `"="`, e.g. `"+"` for `"+="` etc) + rhs = The right-hand side of the operator (left-hand side is `this`) + + Returns: A reference to `this`. + */ + ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return + if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) + { + static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T)); + + static if (hasMember!(Hook, "hookOpOpAssign")) + { + hook.hookOpOpAssign!op(payload, rhs); + } + else + { + alias R = typeof(get + rhs); + auto r = opBinary!op(rhs).get; + import std.conv : unsigned; + + static if (ProperCompare.hookOpCmp(R.min, min.get) < 0 && + hasMember!(Hook, "onLowerBound")) + { + if (ProperCompare.hookOpCmp(r, min.get) < 0) + { + // Example: Checked!uint(1) += int(-3) + payload = hook.onLowerBound(r, min.get); + return this; + } + } + static if (ProperCompare.hookOpCmp(max.get, R.max) < 0 && + hasMember!(Hook, "onUpperBound")) + { + if (ProperCompare.hookOpCmp(r, max.get) > 0) + { + // Example: Checked!uint(1) += long(uint.max) + payload = hook.onUpperBound(r, max.get); + return this; + } + } + payload = cast(T) r; + } + return this; + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + static struct MyHook + { + static bool thereWereErrors; + static T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + thereWereErrors = true; + return bound; + } + static T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + thereWereErrors = true; + return bound; + } + } + auto x = checked!MyHook(byte.min); + x -= 1; + assert(MyHook.thereWereErrors); + MyHook.thereWereErrors = false; + x = byte.max; + x += 1; + assert(MyHook.thereWereErrors); + } +} + +/** + +Convenience function that turns an integral into the corresponding `Checked` +instance by using template argument deduction. The hook type may be specified +(by default `Abort`). + +*/ +Checked!(T, Hook) checked(Hook = Abort, T)(const T value) +if (is(typeof(Checked!(T, Hook)(value)))) +{ + return Checked!(T, Hook)(value); +} + +/// +@system unittest +{ + static assert(is(typeof(checked(42)) == Checked!int)); + assert(checked(42) == Checked!int(42)); + static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN))); + assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42)); +} + +// get +@safe unittest +{ + void test(T)() + { + assert(Checked!(T, void)(ubyte(22)).get == 22); + } + test!ubyte; + test!(const ubyte); + test!(immutable ubyte); +} + +// Abort +/** + +Force all integral errors to fail by printing an error message to `stderr` and +then abort the program. `Abort` is the default second argument for `Checked`. + +*/ +struct Abort +{ +static: + /** + + Called automatically upon a bad cast (one that loses precision or attempts + to convert a negative value to an unsigned type). The source type is `Src` + and the destination type is `Dst`. + + Params: + src = The source of the cast + + Returns: Nominally the result is the desired value of the cast operation, + which will be forwarded as the result of the cast. For `Abort`, the + function never returns because it aborts the program. + + */ + Dst onBadCast(Dst, Src)(Src src) + { + Warn.onBadCast!Dst(src); + assert(0); + } + + /** + + Called automatically upon a bounds error. + + Params: + rhs = The right-hand side value in the assignment, after the operator has + been evaluated + bound = The value of the bound being violated + + Returns: Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Abort`, the function never returns because + it aborts the program. + + */ + T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + Warn.onLowerBound(rhs, bound); + assert(0); + } + /// ditto + T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + Warn.onUpperBound(rhs, bound); + assert(0); + } + + /** + + Called automatically upon a comparison for equality. In case of a erroneous + comparison (one that would make a signed negative value appear equal to an + unsigned positive value), this hook issues `assert(0)` which terminates the + application. + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: Upon a correct comparison, returns the result of the comparison. + Otherwise, the function terminates the application so it never returns. + + */ + static bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + Warn.hookOpEquals(lhs, rhs); + assert(0); + } + return result; + } + + /** + + Called automatically upon a comparison for ordering using one of the + operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. + it would make a signed negative value appear greater than or equal to an + unsigned positive value), then application is terminated with `assert(0)`. + Otherwise, the three-state result is returned (positive if $(D lhs > rhs), + negative if $(D lhs < rhs), `0` otherwise). + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: For correct comparisons, returns a positive integer if $(D lhs > + rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. Upon + a mistaken comparison such as $(D int(-1) < uint(0)), the function never + returns because it aborts the program. + + */ + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"cmp"(lhs, rhs, error); + if (error) + { + Warn.hookOpCmp(lhs, rhs); + assert(0); + } + return result; + } + + /** + + Called automatically upon an overflow during a unary or binary operation. + + Params: + x = The operator, e.g. `-` + lhs = The left-hand side (or sole) argument + rhs = The right-hand side type involved in the operator + + Returns: Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Abort`, the function never returns because + it aborts the program. + + */ + typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) + { + Warn.onOverflow!x(lhs); + assert(0); + } + /// ditto + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + Warn.onOverflow!x(lhs, rhs); + assert(0); + } +} + +@system unittest +{ + void test(T)() + { + Checked!(int, Abort) x; + x = 42; + auto x1 = cast(T) x; + assert(x1 == 42); + //x1 += long(int.max); + } + test!short; + test!(const short); + test!(immutable short); +} + + +// Throw +/** + +Force all integral errors to fail by throwing an exception of type +`Throw.CheckFailure`. The message coming with the error is similar to the one +printed by `Warn`. + +*/ +struct Throw +{ + /** + Exception type thrown upon any failure. + */ + static class CheckFailure : Exception + { + this(T...)(string f, T vals) + { + import std.format : format; + super(format(f, vals)); + } + } + + /** + + Called automatically upon a bad cast (one that loses precision or attempts + to convert a negative value to an unsigned type). The source type is `Src` + and the destination type is `Dst`. + + Params: + src = The source of the cast + + Returns: Nominally the result is the desired value of the cast operation, + which will be forwarded as the result of the cast. For `Throw`, the + function never returns because it throws an exception. + + */ + static Dst onBadCast(Dst, Src)(Src src) + { + throw new CheckFailure("Erroneous cast: cast(%s) %s(%s)", + Dst.stringof, Src.stringof, src); + } + + /** + + Called automatically upon a bounds error. + + Params: + rhs = The right-hand side value in the assignment, after the operator has + been evaluated + bound = The value of the bound being violated + + Returns: Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Throw`, the function never returns because + it throws. + + */ + static T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + throw new CheckFailure("Lower bound error: %s(%s) < %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + } + /// ditto + static T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + throw new CheckFailure("Upper bound error: %s(%s) > %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + } + + /** + + Called automatically upon a comparison for equality. Throws upon an + erroneous comparison (one that would make a signed negative value appear + equal to an unsigned positive value). + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: The result of the comparison. + + Throws: `CheckFailure` if the comparison is mathematically erroneous. + + */ + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + throw new CheckFailure("Erroneous comparison: %s(%s) == %s(%s)", + L.stringof, lhs, R.stringof, rhs); + } + return result; + } + + /** + + Called automatically upon a comparison for ordering using one of the + operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. + it would make a signed negative value appear greater than or equal to an + unsigned positive value), throws a `Throw.CheckFailure` exception. + Otherwise, the three-state result is returned (positive if $(D lhs > rhs), + negative if $(D lhs < rhs), `0` otherwise). + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: For correct comparisons, returns a positive integer if $(D lhs > + rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. + + Throws: Upon a mistaken comparison such as $(D int(-1) < uint(0)), the + function never returns because it throws a `Throw.CheckedFailure` exception. + + */ + static int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"cmp"(lhs, rhs, error); + if (error) + { + throw new CheckFailure("Erroneous ordering comparison: %s(%s) and %s(%s)", + Lhs.stringof, lhs, Rhs.stringof, rhs); + } + return result; + } + + /** + + Called automatically upon an overflow during a unary or binary operation. + + Params: + x = The operator, e.g. `-` + lhs = The left-hand side (or sole) argument + rhs = The right-hand side type involved in the operator + + Returns: Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Throw`, the function never returns because + it throws an exception. + + */ + static typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) + { + throw new CheckFailure("Overflow on unary operator: %s%s(%s)", + x, Lhs.stringof, lhs); + } + /// ditto + static typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + throw new CheckFailure("Overflow on binary operator: %s(%s) %s %s(%s)", + Lhs.stringof, lhs, x, Rhs.stringof, rhs); + } +} + +/// +@safe unittest +{ + void test(T)() + { + Checked!(int, Throw) x; + x = 42; + auto x1 = cast(T) x; + assert(x1 == 42); + x = T.max + 1; + import std.exception : assertThrown, assertNotThrown; + assertThrown(cast(T) x); + x = x.max; + assertThrown(x += 42); + assertThrown(x += 42L); + x = x.min; + assertThrown(-x); + assertThrown(x -= 42); + assertThrown(x -= 42L); + x = -1; + assertNotThrown(x == -1); + assertThrown(x == uint(-1)); + assertNotThrown(x <= -1); + assertThrown(x <= uint(-1)); + } + test!short; + test!(const short); + test!(immutable short); +} + +// Warn +/** +Hook that prints to `stderr` a trace of all integral errors, without affecting +default behavior. +*/ +struct Warn +{ + import std.stdio : stderr; +static: + /** + + Called automatically upon a bad cast from `src` to type `Dst` (one that + loses precision or attempts to convert a negative value to an unsigned + type). + + Params: + src = The source of the cast + Dst = The target type of the cast + + Returns: `cast(Dst) src` + + */ + Dst onBadCast(Dst, Src)(Src src) + { + stderr.writefln("Erroneous cast: cast(%s) %s(%s)", + Dst.stringof, Src.stringof, src); + return cast(Dst) src; + } + + /** + + Called automatically upon a bad `opOpAssign` call (one that loses precision + or attempts to convert a negative value to an unsigned type). + + Params: + rhs = The right-hand side value in the assignment, after the operator has + been evaluated + bound = The bound being violated + + Returns: `cast(Lhs) rhs` + */ + Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + stderr.writefln("Lower bound error: %s(%s) < %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + return cast(T) rhs; + } + /// ditto + T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + stderr.writefln("Upper bound error: %s(%s) > %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + return cast(T) rhs; + } + + /** + + Called automatically upon a comparison for equality. In case of an Erroneous + comparison (one that would make a signed negative value appear equal to an + unsigned positive value), writes a warning message to `stderr` as a side + effect. + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: In all cases the function returns the built-in result of $(D lhs == + rhs). + + */ + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + stderr.writefln("Erroneous comparison: %s(%s) == %s(%s)", + Lhs.stringof, lhs, Rhs.stringof, rhs); + return lhs == rhs; + } + return result; + } + + /// + @system unittest + { + auto x = checked!Warn(-42); + // Passes + assert(x == -42); + // Passes but prints a warning + // assert(x == uint(-42)); + } + + /** + + Called automatically upon a comparison for ordering using one of the + operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. + it would make a signed negative value appear greater than or equal to an + unsigned positive value), then a warning message is printed to `stderr`. + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: In all cases, returns $(D lhs < rhs ? -1 : lhs > rhs). The result + is not autocorrected in case of an erroneous comparison. + + */ + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"cmp"(lhs, rhs, error); + if (error) + { + stderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)", + Lhs.stringof, lhs, Rhs.stringof, rhs); + return lhs < rhs ? -1 : lhs > rhs; + } + return result; + } + + /// + @system unittest + { + auto x = checked!Warn(-42); + // Passes + assert(x <= -42); + // Passes but prints a warning + // assert(x <= uint(-42)); + } + + /** + + Called automatically upon an overflow during a unary or binary operation. + + Params: + x = The operator involved + Lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + Rhs = The right-hand side type involved in the operator + + Returns: $(D mixin(x ~ "lhs")) for unary, $(D mixin("lhs" ~ x ~ "rhs")) for + binary + + */ + typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) + { + stderr.writefln("Overflow on unary operator: %s%s(%s)", + x, Lhs.stringof, lhs); + return mixin(x ~ "lhs"); + } + /// ditto + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + stderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)", + Lhs.stringof, lhs, x, Rhs.stringof, rhs); + return mixin("lhs" ~ x ~ "rhs"); + } +} + +/// +@system unittest +{ + auto x = checked!Warn(42); + short x1 = cast(short) x; + //x += long(int.max); + auto y = checked!Warn(cast(const int) 42); + short y1 = cast(const byte) y; +} + +// ProperCompare +/** + +Hook that provides arithmetically correct comparisons for equality and ordering. +Comparing an object of type $(D Checked!(X, ProperCompare)) against another +integral (for equality or ordering) ensures that no surprising conversions from +signed to unsigned integral occur before the comparison. Using $(D Checked!(X, +ProperCompare)) on either side of a comparison for equality against a +floating-point number makes sure the integral can be properly converted to the +floating point type, thus making sure equality is transitive. + +*/ +struct ProperCompare +{ + /** + Hook for `==` and `!=` that ensures comparison against integral values has + the behavior expected by the usual arithmetic rules. The built-in semantics + yield surprising behavior when comparing signed values against unsigned + values for equality, for example $(D uint.max == -1) or $(D -1_294_967_296 == + 3_000_000_000u). The call $(D hookOpEquals(x, y)) returns `true` if and only + if `x` and `y` represent the same arithmetic number. + + If one of the numbers is an integral and the other is a floating-point + number, $(D hookOpEquals(x, y)) returns `true` if and only if the integral + can be converted exactly (without approximation) to the floating-point + number. This is in order to preserve transitivity of equality: if $(D + hookOpEquals(x, y)) and $(D hookOpEquals(y, z)) then $(D hookOpEquals(y, + z)), in case `x`, `y`, and `z` are a mix of integral and floating-point + numbers. + + Params: + lhs = The left-hand side of the comparison for equality + rhs = The right-hand side of the comparison for equality + + Returns: + The result of the comparison, `true` if the values are equal + */ + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + alias C = typeof(lhs + rhs); + static if (isFloatingPoint!C) + { + static if (!isFloatingPoint!L) + { + return hookOpEquals(rhs, lhs); + } + else static if (!isFloatingPoint!R) + { + static assert(isFloatingPoint!L && !isFloatingPoint!R); + auto rhs1 = C(rhs); + return lhs == rhs1 && cast(R) rhs1 == rhs; + } + else + return lhs == rhs; + } + else + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + // Only possible error is a wrong "true" + return false; + } + return result; + } + } + + /** + Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral + values has the behavior expected by the usual arithmetic rules. The built-in + semantics yield surprising behavior when comparing signed values against + unsigned values, for example $(D 0u < -1). The call $(D hookOpCmp(x, y)) + returns `-1` if and only if `x` is smaller than `y` in abstract arithmetic + sense. + + If one of the numbers is an integral and the other is a floating-point + number, $(D hookOpEquals(x, y)) returns a floating-point number that is `-1` + if `x < y`, `0` if `x == y`, `1` if `x > y`, and `NaN` if the floating-point + number is `NaN`. + + Params: + lhs = The left-hand side of the comparison for ordering + rhs = The right-hand side of the comparison for ordering + + Returns: + The result of the comparison (negative if $(D lhs < rhs), positive if $(D + lhs > rhs), `0` if the values are equal) + */ + static auto hookOpCmp(L, R)(L lhs, R rhs) + { + alias C = typeof(lhs + rhs); + static if (isFloatingPoint!C) + { + return lhs < rhs + ? C(-1) + : lhs > rhs ? C(1) : lhs == rhs ? C(0) : C.init; + } + else + { + static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) + { + static assert(isUnsigned!C); + static assert(isUnsigned!L != isUnsigned!R); + if (!isUnsigned!L && lhs < 0) + return -1; + if (!isUnsigned!R && rhs < 0) + return 1; + } + return lhs < rhs ? -1 : lhs > rhs; + } + } +} + +/// +@safe unittest +{ + alias opEqualsProper = ProperCompare.hookOpEquals; + assert(opEqualsProper(42, 42)); + assert(opEqualsProper(42.0, 42.0)); + assert(opEqualsProper(42u, 42)); + assert(opEqualsProper(42, 42u)); + assert(-1 == 4294967295u); + assert(!opEqualsProper(-1, 4294967295u)); + assert(!opEqualsProper(const uint(-1), -1)); + assert(!opEqualsProper(uint(-1), -1.0)); + assert(3_000_000_000U == -1_294_967_296); + assert(!opEqualsProper(3_000_000_000U, -1_294_967_296)); +} + +@safe unittest +{ + alias opCmpProper = ProperCompare.hookOpCmp; + assert(opCmpProper(42, 42) == 0); + assert(opCmpProper(42, 42.0) == 0); + assert(opCmpProper(41, 42.0) < 0); + assert(opCmpProper(42, 41.0) > 0); + import std.math : isNaN; + assert(isNaN(opCmpProper(41, double.init))); + assert(opCmpProper(42u, 42) == 0); + assert(opCmpProper(42, 42u) == 0); + assert(opCmpProper(-1, uint(-1)) < 0); + assert(opCmpProper(uint(-1), -1) > 0); + assert(opCmpProper(-1.0, -1) == 0); +} + +@safe unittest +{ + auto x1 = Checked!(uint, ProperCompare)(42u); + assert(x1.get < -1); + assert(x1 > -1); +} + +// WithNaN +/** + +Hook that reserves a special value as a "Not a Number" representative. For +signed integrals, the reserved value is `T.min`. For signed integrals, the +reserved value is `T.max`. + +The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must +be taken that all variables are explicitly initialized. Any arithmetic and logic +operation involving at least on NaN becomes NaN itself. All of $(D a == b), $(D +a < b), $(D a > b), $(D a <= b), $(D a >= b) yield `false` if at least one of +`a` and `b` is NaN. + +*/ +struct WithNaN +{ +static: + /** + The default value used for values not explicitly initialized. It is the NaN + value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals. + */ + enum T defaultValue(T) = T.min == 0 ? T.max : T.min; + /** + The maximum value representable is $(D T.max) for signed integrals, $(D + T.max - 1) for unsigned integrals. The minimum value representable is $(D + T.min + 1) for signed integrals, $(D 0) for unsigned integrals. + */ + enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max); + /// ditto + enum T min(T) = cast(T) (T.min == 0 ? T(0) : T.min + 1); + + /** + If `rhs` is `WithNaN.defaultValue!Rhs`, returns + `WithNaN.defaultValue!Lhs`. Otherwise, returns $(D cast(Lhs) rhs). + + Params: + rhs = the value being cast (`Rhs` is the first argument to `Checked`) + Lhs = the target type of the cast + + Returns: The result of the cast operation. + */ + Lhs hookOpCast(Lhs, Rhs)(Rhs rhs) + { + static if (is(Lhs == bool)) + { + return rhs != defaultValue!Rhs && rhs != 0; + } + else static if (valueConvertible!(Rhs, Lhs)) + { + return rhs != defaultValue!Rhs ? Lhs(rhs) : defaultValue!Lhs; + } + else + { + // Not value convertible, only viable option is rhs fits within the + // bounds of Lhs + static if (ProperCompare.hookOpCmp(Rhs.min, Lhs.min) < 0) + { + // Example: hookOpCast!short(int(42)), hookOpCast!uint(int(42)) + if (ProperCompare.hookOpCmp(rhs, Lhs.min) < 0) + return defaultValue!Lhs; + } + static if (ProperCompare.hookOpCmp(Rhs.max, Lhs.max) > 0) + { + // Example: hookOpCast!int(uint(42)) + if (ProperCompare.hookOpCmp(rhs, Lhs.max) > 0) + return defaultValue!Lhs; + } + return cast(Lhs) rhs; + } + } + + /// + @safe unittest + { + auto x = checked!WithNaN(422); + assert((cast(ubyte) x) == 255); + x = checked!WithNaN(-422); + assert((cast(byte) x) == -128); + assert(cast(short) x == -422); + assert(cast(bool) x); + x = x.init; // set back to NaN + assert(x != true); + assert(x != false); + } + + /** + + Returns `false` if $(D lhs == WithNaN.defaultValue!Lhs), $(D lhs == rhs) + otherwise. + + Params: + lhs = The left-hand side of the comparison (`Lhs` is the first argument to + `Checked`) + rhs = The right-hand side of the comparison + + Returns: `lhs != WithNaN.defaultValue!Lhs && lhs == rhs` + */ + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + return lhs != defaultValue!Lhs && lhs == rhs; + } + + /** + + If $(D lhs == WithNaN.defaultValue!Lhs), returns `double.init`. Otherwise, + has the same semantics as the default comparison. + + Params: + lhs = The left-hand side of the comparison (`Lhs` is the first argument to + `Checked`) + rhs = The right-hand side of the comparison + + Returns: `double.init` if `lhs == WitnNaN.defaultValue!Lhs`, `-1.0` if $(D + lhs < rhs), `0.0` if $(D lhs == rhs), `1.0` if $(D lhs > rhs). + + */ + double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + if (lhs == defaultValue!Lhs) return double.init; + return lhs < rhs + ? -1.0 + : lhs > rhs ? 1.0 : lhs == rhs ? 0.0 : double.init; + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + assert(!(x < 0) && !(x > 0) && !(x == 0)); + x = 1; + assert(x > 0 && !(x < 0) && !(x == 0)); + } + + /** + Defines hooks for unary operators `-`, `~`, `++`, and `--`. + + For `-` and `~`, if $(D v == WithNaN.defaultValue!T), returns + `WithNaN.defaultValue!T`. Otherwise, the semantics is the same as for the + built-in operator. + + For `++` and `--`, if $(D v == WithNaN.defaultValue!Lhs) or the operation + would result in an overflow, sets `v` to `WithNaN.defaultValue!T`. + Otherwise, the semantics is the same as for the built-in operator. + + Params: + x = The operator symbol + v = The left-hand side of the comparison (`T` is the first argument to + `Checked`) + + Returns: $(UL $(LI For $(D x == "-" || x == "~"): If $(D v == + WithNaN.defaultValue!T), the function returns `WithNaN.defaultValue!T`. + Otherwise it returns the normal result of the operator.) $(LI For $(D x == + "++" || x == "--"): The function returns `void`.)) + + */ + auto hookOpUnary(string x, T)(ref T v) + { + static if (x == "-" || x == "~") + { + return v != defaultValue!T ? mixin(x ~ "v") : v; + } + else static if (x == "++") + { + static if (defaultValue!T == T.min) + { + if (v != defaultValue!T) + { + if (v == T.max) v = defaultValue!T; + else ++v; + } + } + else + { + static assert(defaultValue!T == T.max); + if (v != defaultValue!T) ++v; + } + } + else static if (x == "--") + { + if (v != defaultValue!T) --v; + } + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + ++x; + assert(x.isNaN); + x = 1; + assert(!x.isNaN); + x = -x; + ++x; + assert(!x.isNaN); + } + + @safe unittest // for coverage + { + Checked!(uint, WithNaN) y; + ++y; + assert(y.isNaN); + } + + /** + Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, + `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the + left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns + $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the + operand. Otherwise, evaluates the operand. If evaluation does not overflow, + returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + + rhs))). + + Params: + x = The operator symbol + lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) + rhs = The right-hand side operand + + Returns: If $(D lhs != WithNaN.defaultValue!Lhs) and the operator does not + overflow, the function returns the same result as the built-in operator. In + all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). + */ + auto hookOpBinary(string x, L, R)(L lhs, R rhs) + { + alias Result = typeof(lhs + rhs); + if (lhs != defaultValue!L) + { + bool error; + auto result = opChecked!x(lhs, rhs, error); + if (!error) return result; + } + return defaultValue!Result; + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + assert((x + 1).isNaN); + x = 100; + assert(!(x + 1).isNaN); + } + + /** + Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, + `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the + right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns + $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the + operand. Otherwise, evaluates the operand. If evaluation does not overflow, + returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + + rhs))). + + Params: + x = The operator symbol + lhs = The left-hand side operand + rhs = The right-hand side operand (`Rhs` is the first argument to `Checked`) + + Returns: If $(D rhs != WithNaN.defaultValue!Rhs) and the operator does not + overflow, the function returns the same result as the built-in operator. In + all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). + */ + auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) + { + alias Result = typeof(lhs + rhs); + if (rhs != defaultValue!R) + { + bool error; + auto result = opChecked!x(lhs, rhs, error); + if (!error) return result; + } + return defaultValue!Result; + } + /// + @safe unittest + { + Checked!(int, WithNaN) x; + assert((1 + x).isNaN); + x = 100; + assert(!(1 + x).isNaN); + } + + /** + + Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, + `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked` + object is the left-hand side operand. If $(D lhs == + WithNaN.defaultValue!Lhs), no action is carried. Otherwise, evaluates the + operand. If evaluation does not overflow and fits in `Lhs` without loss of + information or change of sign, sets `lhs` to the result. Otherwise, sets + `lhs` to `WithNaN.defaultValue!Lhs`. + + Params: + x = The operator symbol (without the `=`) + lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) + rhs = The right-hand side operand + + Returns: `void` + */ + void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs) + { + if (lhs == defaultValue!L) + return; + bool error; + auto temp = opChecked!x(lhs, rhs, error); + lhs = error + ? defaultValue!L + : hookOpCast!L(temp); + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + x += 4; + assert(x.isNaN); + x = 0; + x += 4; + assert(!x.isNaN); + x += int.max; + assert(x.isNaN); + } +} + +/// +@safe unittest +{ + auto x1 = Checked!(int, WithNaN)(); + assert(x1.isNaN); + assert(x1.get == int.min); + assert(x1 != x1); + assert(!(x1 < x1)); + assert(!(x1 > x1)); + assert(!(x1 == x1)); + ++x1; + assert(x1.isNaN); + assert(x1.get == int.min); + --x1; + assert(x1.isNaN); + assert(x1.get == int.min); + x1 = 42; + assert(!x1.isNaN); + assert(x1 == x1); + assert(x1 <= x1); + assert(x1 >= x1); + static assert(x1.min == int.min + 1); + x1 += long(int.max); +} + +/** +Queries whether a $(D Checked!(T, WithNaN)) object is not a number (NaN). + +Params: x = the `Checked` instance queried + +Returns: `true` if `x` is a NaN, `false` otherwise +*/ +bool isNaN(T)(const Checked!(T, WithNaN) x) +{ + return x.get == x.init.get; +} + +/// +@safe unittest +{ + auto x1 = Checked!(int, WithNaN)(); + assert(x1.isNaN); + x1 = 1; + assert(!x1.isNaN); + x1 = x1.init; + assert(x1.isNaN); +} + +@safe unittest +{ + void test1(T)() + { + auto x1 = Checked!(T, WithNaN)(); + assert(x1.isNaN); + assert(x1.get == int.min); + assert(x1 != x1); + assert(!(x1 < x1)); + assert(!(x1 > x1)); + assert(!(x1 == x1)); + assert(x1.get == int.min); + auto x2 = Checked!(T, WithNaN)(42); + assert(!x2.isNaN); + assert(x2 == x2); + assert(x2 <= x2); + assert(x2 >= x2); + static assert(x2.min == T.min + 1); + } + test1!int; + test1!(const int); + test1!(immutable int); + + void test2(T)() + { + auto x1 = Checked!(T, WithNaN)(); + assert(x1.get == T.min); + assert(x1 != x1); + assert(!(x1 < x1)); + assert(!(x1 > x1)); + assert(!(x1 == x1)); + ++x1; + assert(x1.get == T.min); + --x1; + assert(x1.get == T.min); + x1 = 42; + assert(x1 == x1); + assert(x1 <= x1); + assert(x1 >= x1); + static assert(x1.min == T.min + 1); + x1 += long(T.max); + } + test2!int; +} + +@safe unittest +{ + alias Smart(T) = Checked!(Checked!(T, ProperCompare), WithNaN); + Smart!int x1; + assert(x1 != x1); + x1 = -1; + assert(x1 < 1u); + auto x2 = Smart!(const int)(42); +} + +// Saturate +/** + +Hook that implements $(I saturation), i.e. any arithmetic operation that would +overflow leaves the result at its extreme value (`min` or `max` depending on the +direction of the overflow). + +Saturation is not sticky; if a value reaches its saturation value, another +operation may take it back to normal range. + +*/ +struct Saturate +{ +static: + /** + + Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, + and `>>>=`. This hook is called if the result of the binary operation does + not fit in `Lhs` without loss of information or a change in sign. + + Params: + Rhs = The right-hand side type in the assignment, after the operation has + been computed + bound = The bound being violated + + Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise. + + */ + T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + return bound; + } + /// ditto + T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + return bound; + } + /// + @safe unittest + { + auto x = checked!Saturate(short(100)); + x += 33000; + assert(x == short.max); + x -= 70000; + assert(x == short.min); + } + + /** + + Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`, + `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`. + + For unary `-`, `onOverflow` is called if $(D lhs == Lhs.min) and `Lhs` is a + signed type. The function returns `Lhs.max`. + + For binary operators, the result is as follows: $(UL $(LI `Lhs.max` if the + result overflows in the positive direction, on division by `0`, or on + shifting right by a negative value) $(LI `Lhs.min` if the result overflows + in the negative direction) $(LI `0` if `lhs` is being shifted left by a + negative value, or shifted right by a large positive value)) + + Params: + x = The operator involved in the `opAssign` operation + Lhs = The left-hand side of the operator (`Lhs` is the first argument to + `Checked`) + Rhs = The right-hand side type in the operator + + Returns: The saturated result of the operator. + + */ + typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) + { + static assert(x == "-" || x == "++" || x == "--"); + return x == "--" ? Lhs.min : Lhs.max; + } + /// ditto + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + static if (x == "+") + return rhs >= 0 ? Lhs.max : Lhs.min; + else static if (x == "*") + return (lhs >= 0) == (rhs >= 0) ? Lhs.max : Lhs.min; + else static if (x == "^^") + return lhs > 0 || !(rhs & 1) ? Lhs.max : Lhs.min; + else static if (x == "-") + return rhs >= 0 ? Lhs.min : Lhs.max; + else static if (x == "/" || x == "%") + return Lhs.max; + else static if (x == "<<") + return rhs >= 0 ? Lhs.max : 0; + else static if (x == ">>" || x == ">>>") + return rhs >= 0 ? 0 : Lhs.max; + else + static assert(false); + } + /// + @safe unittest + { + assert(checked!Saturate(int.max) + 1 == int.max); + assert(checked!Saturate(100) ^^ 10 == int.max); + assert(checked!Saturate(-100) ^^ 10 == int.max); + assert(checked!Saturate(100) / 0 == int.max); + assert(checked!Saturate(100) << -1 == 0); + assert(checked!Saturate(100) << 33 == int.max); + assert(checked!Saturate(100) >> -1 == int.max); + assert(checked!Saturate(100) >> 33 == 0); + } +} + +/// +@safe unittest +{ + auto x = checked!Saturate(int.max); + ++x; + assert(x == int.max); + --x; + assert(x == int.max - 1); + x = int.min; + assert(-x == int.max); + x -= 42; + assert(x == int.min); + assert(x * -2 == int.max); +} + +/* +Yields `true` if `T1` is "value convertible" (by C's "value preserving" rule, +see $(HTTP c-faq.com/expr/preservingrules.html)) to `T2`, where the two are +integral types. That is, all of values in `T1` are also in `T2`. For example +`int` is value convertible to `long` but not to `uint` or `ulong`. +*/ +private enum valueConvertible(T1, T2) = isIntegral!T1 && isIntegral!T2 && + is(T1 : T2) && ( + isUnsigned!T1 == isUnsigned!T2 || // same signedness + !isUnsigned!T2 && T2.sizeof > T1.sizeof // safely convertible + ); + +/** + +Defines binary operations with overflow checking for any two integral types. +The result type obeys the language rules (even when they may be +counterintuitive), and `overflow` is set if an overflow occurs (including +inadvertent change of signedness, e.g. `-1` is converted to `uint`). +Conceptually the behavior is: + +$(OL $(LI Perform the operation in infinite precision) +$(LI If the infinite-precision result fits in the result type, return it and +do not touch `overflow`) +$(LI Otherwise, set `overflow` to `true` and return an unspecified value) +) + +The implementation exploits properties of types and operations to minimize +additional work. + +Params: +x = The binary operator involved, e.g. `/` +lhs = The left-hand side of the operator +rhs = The right-hand side of the operator +error = The error indicator (assigned `true` in case there's an error) + +Returns: +The result of the operation, which is the same as the built-in operator +*/ +typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()"))) +opChecked(string x, L, R)(const L lhs, const R rhs, ref bool error) +if (isIntegral!L && isIntegral!R) +{ + static if (x == "cmp") + alias Result = int; + else + alias Result = typeof(mixin("L() " ~ x ~ " R()")); + + import core.checkedint : addu, adds, subs, muls, subu, mulu; + import std.algorithm.comparison : among; + static if (x == "==") + { + alias C = typeof(lhs + rhs); + static if (valueConvertible!(L, C) && valueConvertible!(R, C)) + { + // Values are converted to R before comparison, cool. + return lhs == rhs; + } + else + { + static assert(isUnsigned!C); + static assert(isUnsigned!L != isUnsigned!R); + if (lhs != rhs) return false; + // R(lhs) and R(rhs) have the same bit pattern, yet may be + // different due to signedness change. + static if (!isUnsigned!R) + { + if (rhs >= 0) + return true; + } + else + { + if (lhs >= 0) + return true; + } + error = true; + return true; + } + } + else static if (x == "cmp") + { + alias C = typeof(lhs + rhs); + static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) + { + static assert(isUnsigned!C); + static assert(isUnsigned!L != isUnsigned!R); + if (!isUnsigned!L && lhs < 0) + { + error = true; + return -1; + } + if (!isUnsigned!R && rhs < 0) + { + error = true; + return 1; + } + } + return lhs < rhs ? -1 : lhs > rhs; + } + else static if (x.among("<<", ">>", ">>>")) + { + // Handle shift separately from all others. The test below covers + // negative rhs as well. + import std.conv : unsigned; + if (unsigned(rhs) > 8 * Result.sizeof) goto fail; + return mixin("lhs" ~ x ~ "rhs"); + } + else static if (x.among("&", "|", "^")) + { + // Nothing to check + return mixin("lhs" ~ x ~ "rhs"); + } + else static if (x == "^^") + { + // Exponentiation is weird, handle separately + return pow(lhs, rhs, error); + } + else static if (valueConvertible!(L, Result) && + valueConvertible!(R, Result)) + { + static if (L.sizeof < Result.sizeof && R.sizeof < Result.sizeof && + x.among("+", "-", "*")) + { + // No checks - both are value converted and result is in range + return mixin("lhs" ~ x ~ "rhs"); + } + else static if (x == "+") + { + static if (isUnsigned!Result) alias impl = addu; + else alias impl = adds; + return impl(Result(lhs), Result(rhs), error); + } + else static if (x == "-") + { + static if (isUnsigned!Result) alias impl = subu; + else alias impl = subs; + return impl(Result(lhs), Result(rhs), error); + } + else static if (x == "*") + { + static if (!isUnsigned!L && !isUnsigned!R && + is(L == Result)) + { + if (lhs == Result.min && rhs == -1) goto fail; + } + static if (isUnsigned!Result) alias impl = mulu; + else alias impl = muls; + return impl(Result(lhs), Result(rhs), error); + } + else static if (x == "/" || x == "%") + { + static if (!isUnsigned!L && !isUnsigned!R && + is(L == Result) && x == "/") + { + if (lhs == Result.min && rhs == -1) goto fail; + } + if (rhs == 0) goto fail; + return mixin("lhs" ~ x ~ "rhs"); + } + else static assert(0, x); + } + else // Mixed signs + { + static assert(isUnsigned!Result); + static assert(isUnsigned!L != isUnsigned!R); + static if (x == "+") + { + static if (!isUnsigned!L) + { + if (lhs < 0) + return subu(Result(rhs), Result(-lhs), error); + } + else static if (!isUnsigned!R) + { + if (rhs < 0) + return subu(Result(lhs), Result(-rhs), error); + } + return addu(Result(lhs), Result(rhs), error); + } + else static if (x == "-") + { + static if (!isUnsigned!L) + { + if (lhs < 0) goto fail; + } + else static if (!isUnsigned!R) + { + if (rhs < 0) + return addu(Result(lhs), Result(-rhs), error); + } + return subu(Result(lhs), Result(rhs), error); + } + else static if (x == "*") + { + static if (!isUnsigned!L) + { + if (lhs < 0) goto fail; + } + else static if (!isUnsigned!R) + { + if (rhs < 0) goto fail; + } + return mulu(Result(lhs), Result(rhs), error); + } + else static if (x == "/" || x == "%") + { + static if (!isUnsigned!L) + { + if (lhs < 0 || rhs == 0) goto fail; + } + else static if (!isUnsigned!R) + { + if (rhs <= 0) goto fail; + } + return mixin("Result(lhs)" ~ x ~ "Result(rhs)"); + } + else static assert(0, x); + } + debug assert(false); +fail: + error = true; + return Result(0); +} + +/// +@safe unittest +{ + bool overflow; + assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow); + assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow); + assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow); + assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow); + assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow); +} + +/// +@safe unittest +{ + bool overflow; + assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow); + assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow); + assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow); + assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow); +} + +@safe unittest +{ + bool overflow; + assert(opChecked!"*"(2, 3, overflow) == 6 && !overflow); + assert(opChecked!"*"(2, 3u, overflow) == 6 && !overflow); + assert(opChecked!"*"(1u, -1, overflow) == 0 && overflow); + //assert(mul(-1, 1u, overflow) == uint.max - 1 && overflow); +} + +@safe unittest +{ + bool overflow; + assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); + assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); + assert(opChecked!"/"(6u, 3, overflow) == 2 && !overflow); + assert(opChecked!"/"(6, 3u, overflow) == 2 && !overflow); + assert(opChecked!"/"(11, 0, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"/"(6u, 0, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"/"(-6, 2u, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"/"(-6, 0u, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"cmp"(0u, -6, overflow) == 1 && overflow); + overflow = false; + assert(opChecked!"|"(1, 2, overflow) == 3 && !overflow); +} + +/* +Exponentiation function used by the implementation of operator `^^`. +*/ +private pure @safe nothrow @nogc +auto pow(L, R)(const L lhs, const R rhs, ref bool overflow) +if (isIntegral!L && isIntegral!R) +{ + if (rhs <= 1) + { + if (rhs == 0) return 1; + static if (!isUnsigned!R) + return rhs == 1 + ? lhs + : (rhs == -1 && (lhs == 1 || lhs == -1)) ? lhs : 0; + else + return lhs; + } + + typeof(lhs ^^ rhs) b = void; + static if (!isUnsigned!L && isUnsigned!(typeof(b))) + { + // Need to worry about mixed-sign stuff + if (lhs < 0) + { + if (rhs & 1) + { + if (lhs < 0) overflow = true; + return 0; + } + b = -lhs; + } + else + { + b = lhs; + } + } + else + { + b = lhs; + } + if (b == 1) return 1; + if (b == -1) return (rhs & 1) ? -1 : 1; + if (rhs > 63) + { + overflow = true; + return 0; + } + + assert((b > 1 || b < -1) && rhs > 1); + return powImpl(b, cast(uint) rhs, overflow); +} + +// Inspiration: http://www.stepanovpapers.com/PAM.pdf +pure @safe nothrow @nogc +private T powImpl(T)(T b, uint e, ref bool overflow) +if (isIntegral!T && T.sizeof >= 4) +{ + assert(e > 1); + + import core.checkedint : muls, mulu; + static if (isUnsigned!T) alias mul = mulu; + else alias mul = muls; + + T r = b; + --e; + // Loop invariant: r * (b ^^ e) is the actual result + for (;; e /= 2) + { + if (e % 2) + { + r = mul(r, b, overflow); + if (e == 1) break; + } + b = mul(b, b, overflow); + } + return r; +} + +@safe unittest +{ + static void testPow(T)(T x, uint e) + { + bool overflow; + assert(opChecked!"^^"(T(0), 0, overflow) == 1); + assert(opChecked!"^^"(-2, T(0), overflow) == 1); + assert(opChecked!"^^"(-2, T(1), overflow) == -2); + assert(opChecked!"^^"(-1, -1, overflow) == -1); + assert(opChecked!"^^"(-2, 1, overflow) == -2); + assert(opChecked!"^^"(-2, -1, overflow) == 0); + assert(opChecked!"^^"(-2, 4u, overflow) == 16); + assert(!overflow); + assert(opChecked!"^^"(-2, 3u, overflow) == 0); + assert(overflow); + overflow = false; + assert(opChecked!"^^"(3, 64u, overflow) == 0); + assert(overflow); + overflow = false; + foreach (uint i; 0 .. e) + { + assert(opChecked!"^^"(x, i, overflow) == x ^^ i); + assert(!overflow); + } + assert(opChecked!"^^"(x, e, overflow) == x ^^ e); + assert(overflow); + } + + testPow!int(3, 21); + testPow!uint(3, 21); + testPow!long(3, 40); + testPow!ulong(3, 41); +} + +version(unittest) private struct CountOverflows +{ + uint calls; + auto onOverflow(string op, Lhs)(Lhs lhs) + { + ++calls; + return mixin(op ~ "lhs"); + } + auto onOverflow(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return mixin("lhs" ~ op ~ "rhs"); + } + T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + ++calls; + return cast(T) rhs; + } + T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + ++calls; + return cast(T) rhs; + } +} + +version(unittest) private struct CountOpBinary +{ + uint calls; + auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return mixin("lhs" ~ op ~ "rhs"); + } +} + +// opBinary +@nogc nothrow pure @safe unittest +{ + auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142); + assert(x + y == 184); + assert(x + 100 == 142); + assert(y - x == 100); + assert(200 - x == 158); + assert(y * x == 142 * 42); + assert(x / 1 == 42); + assert(x % 20 == 2); + + auto x1 = Checked!(int, CountOverflows)(42); + assert(x1 + 0 == 42); + assert(x1 + false == 42); + assert(is(typeof(x1 + 0.5) == double)); + assert(x1 + 0.5 == 42.5); + assert(x1.hook.calls == 0); + assert(x1 + int.max == int.max + 42); + assert(x1.hook.calls == 1); + assert(x1 * 2 == 84); + assert(x1.hook.calls == 1); + assert(x1 / 2 == 21); + assert(x1.hook.calls == 1); + assert(x1 % 20 == 2); + assert(x1.hook.calls == 1); + assert(x1 << 2 == 42 << 2); + assert(x1.hook.calls == 1); + assert(x1 << 42 == x1.get << x1.get); + assert(x1.hook.calls == 2); + x1 = int.min; + assert(x1 - 1 == int.max); + assert(x1.hook.calls == 3); + + auto x2 = Checked!(int, CountOpBinary)(42); + assert(x2 + 1 == 43); + assert(x2.hook.calls == 1); + + auto x3 = Checked!(uint, CountOverflows)(42u); + assert(x3 + 1 == 43); + assert(x3.hook.calls == 0); + assert(x3 - 1 == 41); + assert(x3.hook.calls == 0); + assert(x3 + (-42) == 0); + assert(x3.hook.calls == 0); + assert(x3 - (-42) == 84); + assert(x3.hook.calls == 0); + assert(x3 * 2 == 84); + assert(x3.hook.calls == 0); + assert(x3 * -2 == -84); + assert(x3.hook.calls == 1); + assert(x3 / 2 == 21); + assert(x3.hook.calls == 1); + assert(x3 / -2 == 0); + assert(x3.hook.calls == 2); + assert(x3 ^^ 2 == 42 * 42); + assert(x3.hook.calls == 2); + + auto x4 = Checked!(int, CountOverflows)(42); + assert(x4 + 1 == 43); + assert(x4.hook.calls == 0); + assert(x4 + 1u == 43); + assert(x4.hook.calls == 0); + assert(x4 - 1 == 41); + assert(x4.hook.calls == 0); + assert(x4 * 2 == 84); + assert(x4.hook.calls == 0); + x4 = -2; + assert(x4 + 2u == 0); + assert(x4.hook.calls == 0); + assert(x4 * 2u == -4); + assert(x4.hook.calls == 1); + + auto x5 = Checked!(int, CountOverflows)(3); + assert(x5 ^^ 0 == 1); + assert(x5 ^^ 1 == 3); + assert(x5 ^^ 2 == 9); + assert(x5 ^^ 3 == 27); + assert(x5 ^^ 4 == 81); + assert(x5 ^^ 5 == 81 * 3); + assert(x5 ^^ 6 == 81 * 9); +} + +// opBinaryRight +@nogc nothrow pure @safe unittest +{ + auto x1 = Checked!(int, CountOverflows)(42); + assert(1 + x1 == 43); + assert(true + x1 == 43); + assert(0.5 + x1 == 42.5); + auto x2 = Checked!(int, void)(42); + assert(x1 + x2 == 84); + assert(x2 + x1 == 84); +} + +// opOpAssign +@safe unittest +{ + auto x1 = Checked!(int, CountOverflows)(3); + assert((x1 += 2) == 5); + x1 *= 2_000_000_000L; + assert(x1.hook.calls == 1); + x1 *= -2_000_000_000L; + assert(x1.hook.calls == 2); + + auto x2 = Checked!(ushort, CountOverflows)(ushort(3)); + assert((x2 += 2) == 5); + assert(x2.hook.calls == 0); + assert((x2 += ushort.max) == cast(ushort) (ushort(5) + ushort.max)); + assert(x2.hook.calls == 1); + + auto x3 = Checked!(uint, CountOverflows)(3u); + x3 *= ulong(2_000_000_000); + assert(x3.hook.calls == 1); +} + +// opAssign +@safe unittest +{ + Checked!(int, void) x; + x = 42; + assert(x.get == 42); + x = x; + assert(x.get == 42); + x = short(43); + assert(x.get == 43); + x = ushort(44); + assert(x.get == 44); +} + +@safe unittest +{ + static assert(!is(typeof(Checked!(short, void)(ushort(42))))); + static assert(!is(typeof(Checked!(int, void)(long(42))))); + static assert(!is(typeof(Checked!(int, void)(ulong(42))))); + assert(Checked!(short, void)(short(42)).get == 42); + assert(Checked!(int, void)(ushort(42)).get == 42); +} + +// opCast +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof(cast(float) Checked!(int, void)(42)) == float)); + assert(cast(float) Checked!(int, void)(42) == 42); + + assert(is(typeof(cast(long) Checked!(int, void)(42)) == long)); + assert(cast(long) Checked!(int, void)(42) == 42); + static assert(is(typeof(cast(long) Checked!(uint, void)(42u)) == long)); + assert(cast(long) Checked!(uint, void)(42u) == 42); + + auto x = Checked!(int, void)(42); + if (x) {} else assert(0); + x = 0; + if (x) assert(0); + + static struct Hook1 + { + uint calls; + Dst hookOpCast(Dst, Src)(Src value) + { + ++calls; + return 42; + } + } + auto y = Checked!(long, Hook1)(long.max); + assert(cast(int) y == 42); + assert(cast(uint) y == 42); + assert(y.hook.calls == 2); + + static struct Hook2 + { + uint calls; + Dst onBadCast(Dst, Src)(Src value) + { + ++calls; + return 42; + } + } + auto x1 = Checked!(uint, Hook2)(100u); + assert(cast(ushort) x1 == 100); + assert(cast(short) x1 == 100); + assert(cast(float) x1 == 100); + assert(cast(double) x1 == 100); + assert(cast(real) x1 == 100); + assert(x1.hook.calls == 0); + assert(cast(int) x1 == 100); + assert(x1.hook.calls == 0); + x1 = uint.max; + assert(cast(int) x1 == 42); + assert(x1.hook.calls == 1); + + auto x2 = Checked!(int, Hook2)(-100); + assert(cast(short) x2 == -100); + assert(cast(ushort) x2 == 42); + assert(cast(uint) x2 == 42); + assert(cast(ulong) x2 == 42); + assert(x2.hook.calls == 3); +} + +// opEquals +@nogc nothrow pure @safe unittest +{ + assert(Checked!(int, void)(42) == 42L); + assert(42UL == Checked!(int, void)(42)); + + static struct Hook1 + { + uint calls; + bool hookOpEquals(Lhs, Rhs)(const Lhs lhs, const Rhs rhs) + { + ++calls; + return lhs != rhs; + } + } + auto x1 = Checked!(int, Hook1)(100); + assert(x1 != Checked!(long, Hook1)(100)); + assert(x1.hook.calls == 1); + assert(x1 != 100u); + assert(x1.hook.calls == 2); + + static struct Hook2 + { + uint calls; + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return false; + } + } + auto x2 = Checked!(int, Hook2)(-100); + assert(x2 != x1); + // For coverage: lhs has no hookOpEquals, rhs does + assert(Checked!(uint, void)(100u) != x2); + // For coverage: different types, neither has a hookOpEquals + assert(Checked!(uint, void)(100u) == Checked!(int, void*)(100)); + assert(x2.hook.calls == 0); + assert(x2 != -100); + assert(x2.hook.calls == 1); + assert(x2 != cast(uint) -100); + assert(x2.hook.calls == 2); + x2 = 100; + assert(x2 != cast(uint) 100); + assert(x2.hook.calls == 3); + x2 = -100; + + auto x3 = Checked!(uint, Hook2)(100u); + assert(x3 != 100); + x3 = uint.max; + assert(x3 != -1); + + assert(x2 != x3); +} + +// opCmp +@nogc nothrow pure @safe unittest +{ + Checked!(int, void) x; + assert(x <= x); + assert(x < 45); + assert(x < 45u); + assert(x > -45); + assert(x < 44.2); + assert(x > -44.2); + assert(!(x < double.init)); + assert(!(x > double.init)); + assert(!(x <= double.init)); + assert(!(x >= double.init)); + + static struct Hook1 + { + uint calls; + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return 0; + } + } + auto x1 = Checked!(int, Hook1)(42); + assert(!(x1 < 43u)); + assert(!(43u < x1)); + assert(x1.hook.calls == 2); + + static struct Hook2 + { + uint calls; + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return ProperCompare.hookOpCmp(lhs, rhs); + } + } + auto x2 = Checked!(int, Hook2)(-42); + assert(x2 < 43u); + assert(43u > x2); + assert(x2.hook.calls == 2); + x2 = 42; + assert(x2 > 41u); + + auto x3 = Checked!(uint, Hook2)(42u); + assert(x3 > 41); + assert(x3 > -41); +} + +// opUnary +@nogc nothrow pure @safe unittest +{ + auto x = Checked!(int, void)(42); + assert(x == +x); + static assert(is(typeof(-x) == typeof(x))); + assert(-x == Checked!(int, void)(-42)); + static assert(is(typeof(~x) == typeof(x))); + assert(~x == Checked!(int, void)(~42)); + assert(++x == 43); + assert(--x == 42); + + static struct Hook1 + { + uint calls; + auto hookOpUnary(string op, T)(T value) if (op == "-") + { + ++calls; + return T(42); + } + auto hookOpUnary(string op, T)(T value) if (op == "~") + { + ++calls; + return T(43); + } + } + auto x1 = Checked!(int, Hook1)(100); + assert(is(typeof(-x1) == typeof(x1))); + assert(-x1 == Checked!(int, Hook1)(42)); + assert(is(typeof(~x1) == typeof(x1))); + assert(~x1 == Checked!(int, Hook1)(43)); + assert(x1.hook.calls == 2); + + static struct Hook2 + { + uint calls; + void hookOpUnary(string op, T)(ref T value) if (op == "++") + { + ++calls; + --value; + } + void hookOpUnary(string op, T)(ref T value) if (op == "--") + { + ++calls; + ++value; + } + } + auto x2 = Checked!(int, Hook2)(100); + assert(++x2 == 99); + assert(x2 == 99); + assert(--x2 == 100); + assert(x2 == 100); + + auto x3 = Checked!(int, CountOverflows)(int.max - 1); + assert(++x3 == int.max); + assert(x3.hook.calls == 0); + assert(++x3 == int.min); + assert(x3.hook.calls == 1); + assert(-x3 == int.min); + assert(x3.hook.calls == 2); + + x3 = int.min + 1; + assert(--x3 == int.min); + assert(x3.hook.calls == 2); + assert(--x3 == int.max); + assert(x3.hook.calls == 3); +} + +// +@nogc nothrow pure @safe unittest +{ + Checked!(int, void) x; + assert(x == x); + assert(x == +x); + assert(x == -x); + ++x; + assert(x == 1); + x++; + assert(x == 2); + + x = 42; + assert(x == 42); + const short _short = 43; + x = _short; + assert(x == _short); + ushort _ushort = 44; + x = _ushort; + assert(x == _ushort); + assert(x == 44.0); + assert(x != 44.1); + assert(x < 45); + assert(x < 44.2); + assert(x > -45); + assert(x > -44.2); + + assert(cast(long) x == 44); + assert(cast(short) x == 44); + + const Checked!(uint, void) y; + assert(y <= y); + assert(y == 0); + assert(y < x); + x = -1; + assert(x > y); +} diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 07a5b555050..e8e8d4c0531 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -1,16 +1,9 @@ +/// module std.experimental.logger.core; -import std.array; -import std.stdio; -import std.conv; import std.datetime; -import std.string; -import std.range; +import std.range.primitives; import std.traits; -import std.exception; -import std.concurrency; -import std.format; -import core.atomic; import core.sync.mutex : Mutex; import std.experimental.logger.filelogger; @@ -121,20 +114,22 @@ If this module exists and it contains a $(D LogLevel) called logLevel this $(D LogLevel) will be used. This parent lookup is continued until there is no parent module. Then the moduleLogLevel is $(D LogLevel.all). */ -template moduleLogLevel(string moduleName) if (!moduleName.length) +template moduleLogLevel(string moduleName) +if (!moduleName.length) { // default enum moduleLogLevel = LogLevel.all; } /// -unittest +@system unittest { static assert(moduleLogLevel!"" == LogLevel.all); } /// ditto -template moduleLogLevel(string moduleName) if (moduleName.length) +template moduleLogLevel(string moduleName) +if (moduleName.length) { import std.string : format; mixin(q{ @@ -153,7 +148,7 @@ template moduleLogLevel(string moduleName) if (moduleName.length) } /// -unittest +@system unittest { static assert(moduleLogLevel!"not.amodule.path" == LogLevel.all); } @@ -168,16 +163,19 @@ private string parentOf(string mod) /* This function formates a $(D SysTime) into an $(D OutputRange). The $(D SysTime) is formatted similar to -$(LREF std.datatime.DateTime.toISOExtString) expect the fractional second part. -The sub second part is the upper three digest of the microsecond. +$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part. +The fractional second part is in milliseconds and is always 3 digits. */ void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time) - if (isOutputRange!(OutputRange,string)) +if (isOutputRange!(OutputRange,string)) { - auto fsec = time.fracSec.usecs / 1000; + import std.format : formattedWrite; + + const auto dt = cast(DateTime) time; + const auto fsec = time.fracSecs.total!"msecs"; formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", - time.year, time.month, time.day, time.hour, time.minute, time.second, + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec); } @@ -192,7 +190,7 @@ Params: condition = The condition must be $(D true) for the data to be logged. args = The data that should be logged. -Examples: +Example: -------------------- log(LogLevel.warning, true, "Hello World", 3.1415); -------------------- @@ -200,8 +198,8 @@ log(LogLevel.warning, true, "Hello World", 3.1415); void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy A args) @safe - if (args.length != 1) + lazy bool condition, lazy A args) +if (args.length != 1) { static if (isLoggingActive) { @@ -237,7 +235,7 @@ Params: ll = The $(D LogLevel) used by this log call. args = The data that should be logged. -Examples: +Example: -------------------- log(LogLevel.warning, "Hello World", 3.1415); -------------------- @@ -245,7 +243,7 @@ log(LogLevel.warning, "Hello World", 3.1415); void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) - if (args.length > 1 && !is(Unqual!(A[0]) : bool)) +if (args.length > 1 && !is(Unqual!(A[0]) : bool)) { static if (isLoggingActive) { @@ -282,7 +280,7 @@ Params: condition = The condition must be $(D true) for the data to be logged. args = The data that should be logged. -Examples: +Example: -------------------- log(true, "Hello World", 3.1415); -------------------- @@ -290,7 +288,7 @@ log(true, "Hello World", 3.1415); void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - if (args.length != 1) +if (args.length != 1) { static if (isLoggingActive) { @@ -319,7 +317,7 @@ $(D sharedLog) must be greater or equal to the $(D defaultLogLevel). Params: args = The data that should be logged. -Examples: +Example: -------------------- log("Hello World", 3.1415); -------------------- @@ -327,9 +325,9 @@ log("Hello World", 3.1415); void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 1 && !is(Unqual!(A[0]) : bool) - && !is(Unqual!(A[0]) == LogLevel)) - || args.length == 0) +if ((args.length > 1 && !is(Unqual!(A[0]) : bool) + && !is(Unqual!(A[0]) == LogLevel)) + || args.length == 0) { static if (isLoggingActive) { @@ -361,7 +359,7 @@ Params: msg = The $(D printf)-style string. args = The data that should be logged. -Examples: +Example: -------------------- logf(LogLevel.warning, true, "Hello World %f", 3.1415); -------------------- @@ -393,7 +391,7 @@ Params: msg = The $(D printf)-style string. args = The data that should be logged. -Examples: +Example: -------------------- logf(LogLevel.warning, "Hello World %f", 3.1415); -------------------- @@ -424,7 +422,7 @@ Params: msg = The $(D printf)-style string. args = The data that should be logged. -Examples: +Example: -------------------- logf(true, "Hello World %f", 3.1415); -------------------- @@ -450,7 +448,7 @@ Params: msg = The $(D printf)-style string. args = The data that should be logged. -Examples: +Example: -------------------- logf("Hello World %f", 3.1415); -------------------- @@ -478,7 +476,7 @@ template defaultLogFunction(LogLevel ll) void defaultLogFunction(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__, A...)(lazy A args) @safe + string moduleName = __MODULE__, A...)(lazy A args) if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) @@ -492,7 +490,6 @@ template defaultLogFunction(LogLevel ll) string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - @safe { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) { @@ -502,42 +499,27 @@ template defaultLogFunction(LogLevel ll) } } -/** This function logs data to the $(D stdThreadLocalLog). +/** This function logs data to the $(D stdThreadLocalLog), optionally depending +on a condition. In order for the resulting log message to be logged the $(D LogLevel) must be greater or equal than the $(D LogLevel) of the $(D stdThreadLocalLog) and must be greater or equal than the global $(D LogLevel). Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel) of the $(D stdSharedLogger). +If a condition is given, it must evaluate to $(D true). Params: + condition = The condition must be $(D true) for the data to be logged. args = The data that should be logged. -Examples: +Example: -------------------- trace(1337, "is number"); info(1337, "is number"); error(1337, "is number"); critical(1337, "is number"); fatal(1337, "is number"); --------------------- - -The second version of the function logs data to the $(D stdThreadLocalLog) depending -on a condition. - -In order for the resulting log message to be logged the $(D LogLevel) must -be greater or equal than the $(D LogLevel) of the $(D stdThreadLocalLog) and -must be greater or equal than the global $(D LogLevel) additionally the -condition passed must be $(D true). -Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel) -of the $(D stdSharedLogger). - -Params: - condition = The condition must be $(D true) for the data to be logged. - args = The data that should be logged. - -Examples: --------------------- trace(true, 1337, "is number"); info(false, 1337, "is number"); error(true, 1337, "is number"); @@ -569,7 +551,6 @@ template defaultLogFunctionf(LogLevel ll) string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - @safe { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) { @@ -582,7 +563,7 @@ template defaultLogFunctionf(LogLevel ll) string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) @safe + lazy string msg, lazy A args) { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) { @@ -605,7 +586,7 @@ Params: msg = The $(D printf)-style string. args = The data that should be logged. -Examples: +Example: -------------------- tracef("is number %d", 1); infof("is number %d", 2); @@ -628,7 +609,7 @@ Params: msg = The $(D printf)-style string. args = The data that should be logged. -Examples: +Example: -------------------- tracef(false, "is number %d", 1); infof(false, "is number %d", 2); @@ -682,11 +663,11 @@ private void formatString(A...)(MsgRange oRange, A args) foreach (arg; args) { - std.format.formattedWrite(oRange, "%s", arg); + formattedWrite(oRange, "%s", arg); } } -unittest +@system unittest { void dummy() @safe { @@ -731,6 +712,9 @@ flexibility. */ abstract class Logger { + import std.array : appender, Appender; + import std.concurrency : thisTid, Tid; + /** LogEntry is a aggregation combining all information associated with a log message. This aggregation will be passed to the method writeLogMsg. @@ -760,12 +744,14 @@ abstract class Logger Logger logger; } - /** This constructor takes a name of type $(D string), and a $(D LogLevel). + /** + Every subclass of `Logger` has to call this constructor from their + constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal + handler will throw an `Error` if a log call is made with level + `LogLevel.fatal`. - Every subclass of $(D Logger) has to call this constructor from their - constructor. It sets the $(D LogLevel), the name of the $(D Logger), and - creates a fatal handler. The fatal handler will throw an $(D Error) if a - log call is made with a $(D LogLevel) $(D LogLevel.fatal). + Params: + lv = `LogLevel` to use for this `Logger` instance. */ this(LogLevel lv) @safe { @@ -773,8 +759,8 @@ abstract class Logger this.fatalHandler_ = delegate() { throw new Error("A fatal log message was logged"); }; - // TODO: remove lambda hack after relevant druntime PR gets merged - this.mutex = () @trusted { return new Mutex(); } (); + + this.mutex = new Mutex(); } /** A custom logger must implement this method in order to work in a @@ -911,17 +897,11 @@ abstract class Logger */ void forwardMsg(ref LogEntry payload) @trusted { - //writeln(payload); static if (isLoggingActive) synchronized (mutex) { - //writeln(__LINE__, " ",payload, this.logLevel_, " ", globalLogLevel); - //writeln(isLoggingEnabled(payload.logLevel, this.logLevel_, - // globalLogLevel), payload.logLevel >= this.logLevel_, - // payload.logLevel >= globalLogLevel); if (isLoggingEnabled(payload.logLevel, this.logLevel_, globalLogLevel)) { - //writeln(__LINE__, " ",payload); this.writeLogMsg(payload); if (payload.logLevel == LogLevel.fatal) @@ -950,7 +930,7 @@ abstract class Logger Params: args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.trace(1337, "is number"); @@ -997,7 +977,7 @@ abstract class Logger condition = The condition must be $(D true) for the data to be logged. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.trace(true, 1337, "is number"); @@ -1011,7 +991,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, - lazy A args) @safe + lazy A args) { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) synchronized (mutex) @@ -1046,7 +1026,7 @@ abstract class Logger msg = The $(D printf)-style string. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stderr); s.tracef(true, "is number %d", 1); @@ -1060,11 +1040,13 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) @safe + lazy string msg, lazy A args) { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) synchronized (mutex) { + import std.format : formattedWrite; + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) { @@ -1093,7 +1075,7 @@ abstract class Logger msg = The $(D printf)-style string. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stderr); s.tracef("is number %d", 1); @@ -1107,11 +1089,12 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - @safe { static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) synchronized (mutex) { + import std.format : formattedWrite; + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) { this.beginLogMsg(file, line, funcName, prettyFuncName, @@ -1167,17 +1150,17 @@ abstract class Logger Returns: The logger used by the logging function as reference. - Examples: + Example: -------------------- auto l = new StdioLogger(); l.log(1337); -------------------- */ - final void log(int line = __LINE__, string file = __FILE__, + void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy A args) @safe + lazy bool condition, lazy A args) if (args.length != 1) { static if (isLoggingActive) synchronized (mutex) @@ -1199,10 +1182,10 @@ abstract class Logger } /// Ditto - final void log(T, string moduleName = __MODULE__)(const LogLevel ll, + void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy bool condition, lazy T args, int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, - string prettyFuncName = __PRETTY_FUNCTION__) @safe + string prettyFuncName = __PRETTY_FUNCTION__) { static if (isLoggingActive) synchronized (mutex) { @@ -1233,7 +1216,7 @@ abstract class Logger ll = The specific $(D LogLevel) used for logging the log message. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.log(LogLevel.trace, 1337, "is number"); @@ -1243,11 +1226,10 @@ abstract class Logger s.log(LogLevel.fatal, 1337, "is number"); -------------------- */ - final void log(int line = __LINE__, string file = __FILE__, + void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) - @safe if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) { static if (isLoggingActive) synchronized (mutex) @@ -1269,10 +1251,10 @@ abstract class Logger } /// Ditto - final void log(T)(const LogLevel ll, lazy T args, int line = __LINE__, + void log(T)(const LogLevel ll, lazy T args, int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) @safe + string moduleName = __MODULE__) { static if (isLoggingActive) synchronized (mutex) { @@ -1303,7 +1285,7 @@ abstract class Logger condition = The condition must be $(D true) for the data to be logged. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.log(true, 1337, "is number"); @@ -1313,11 +1295,10 @@ abstract class Logger s.log(false, 1337, "is number"); -------------------- */ - final void log(int line = __LINE__, string file = __FILE__, + void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - @safe if (args.length != 1) { static if (isLoggingActive) synchronized (mutex) @@ -1340,10 +1321,10 @@ abstract class Logger } /// Ditto - final void log(T)(lazy bool condition, lazy T args, int line = __LINE__, + void log(T)(lazy bool condition, lazy T args, int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) @safe + string moduleName = __MODULE__) { static if (isLoggingActive) synchronized (mutex) { @@ -1373,7 +1354,7 @@ abstract class Logger Params: args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.log(1337, "is number"); @@ -1383,11 +1364,10 @@ abstract class Logger s.log(1337, "is number"); -------------------- */ - final void log(int line = __LINE__, string file = __FILE__, + void log(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - @safe if ((args.length > 1 && !is(Unqual!(A[0]) : bool) && !is(Unqual!(A[0]) == LogLevel)) @@ -1412,10 +1392,10 @@ abstract class Logger } /// Ditto - final void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, + void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, - string moduleName = __MODULE__) @safe + string moduleName = __MODULE__) { static if (isLoggingActive) synchronized (mutex) { @@ -1448,7 +1428,7 @@ abstract class Logger msg = The format string used for this log call. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number"); @@ -1458,14 +1438,16 @@ abstract class Logger s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number"); -------------------- */ - final void logf(int line = __LINE__, string file = __FILE__, + void logf(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy bool condition, lazy string msg, lazy A args) @safe + lazy bool condition, lazy string msg, lazy A args) { static if (isLoggingActive) synchronized (mutex) { + import std.format : formattedWrite; + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) { this.beginLogMsg(file, line, funcName, prettyFuncName, @@ -1494,7 +1476,7 @@ abstract class Logger msg = The format string used for this log call. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.logf(LogLevel.trace, "%d %s", 1337, "is number"); @@ -1504,14 +1486,16 @@ abstract class Logger s.logf(LogLevel.fatal, "%d %s", 1337, "is number"); -------------------- */ - final void logf(int line = __LINE__, string file = __FILE__, + void logf(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, - lazy string msg, lazy A args) @safe + lazy string msg, lazy A args) { static if (isLoggingActive) synchronized (mutex) { + import std.format : formattedWrite; + if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) { this.beginLogMsg(file, line, funcName, prettyFuncName, @@ -1541,7 +1525,7 @@ abstract class Logger msg = The format string used for this log call. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.logf(true ,"%d %s", 1337, "is number"); @@ -1551,19 +1535,19 @@ abstract class Logger s.logf(true ,"%d %s", 1337, "is number"); -------------------- */ - final void logf(int line = __LINE__, string file = __FILE__, + void logf(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, - lazy string msg, lazy A args) @safe + lazy string msg, lazy A args) { static if (isLoggingActive) synchronized (mutex) { - //writeln(msg, args, " ", line); + import std.format : formattedWrite; + if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, condition)) { - //writeln(msg, args, " ", line); this.beginLogMsg(file, line, funcName, prettyFuncName, moduleName, this.logLevel_, thisTid, Clock.currTime, this); @@ -1588,7 +1572,7 @@ abstract class Logger msg = The format string used for this log call. args = The data that should be logged. - Examples: + Example: -------------------- auto s = new FileLogger(stdout); s.logf("%d %s", 1337, "is number"); @@ -1598,14 +1582,15 @@ abstract class Logger s.logf("%d %s", 1337, "is number"); -------------------- */ - final void logf(int line = __LINE__, string file = __FILE__, + void logf(int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) - @safe { static if (isLoggingActive) synchronized (mutex) { + import std.format : formattedWrite; + if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel)) { @@ -1643,7 +1628,10 @@ private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; */ private @property Logger defaultSharedLoggerImpl() @trusted { - static __gshared ubyte[__traits(classInstanceSize, FileLogger)] _buffer; + import std.conv : emplace; + import std.stdio : stderr; + + static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer; synchronized (stdSharedLoggerMutex) { @@ -1673,6 +1661,9 @@ While getting and setting $(D sharedLog) is thread-safe, it has to be considered that the returned reference is only a current snapshot and in the following code, you must make sure no other thread reassigns to it between reading and writing $(D sharedLog). + +$(D sharedLog) is only thread-safe if the the used $(D Logger) is thread-safe. +The default $(D Logger) is thread-safe. ------------- if (sharedLog !is myLogger) sharedLog = new myLogger; @@ -1682,6 +1673,7 @@ if (sharedLog !is myLogger) { static auto trustedLoad(ref shared Logger logger) @trusted { + import core.atomic : atomicLoad, MemoryOrder; return atomicLoad!(MemoryOrder.acq)(logger); } @@ -1700,6 +1692,7 @@ if (sharedLog !is myLogger) /// Ditto @property void sharedLog(Logger logger) @trusted { + import core.atomic : atomicStore, MemoryOrder; atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger); } @@ -1771,7 +1764,9 @@ private Logger stdLoggerDefaultThreadLogger; */ private @property Logger stdThreadLocalLogImpl() @trusted { - static ubyte[__traits(classInstanceSize, StdForwardLogger)] _buffer; + import std.conv : emplace; + + static void*[(__traits(classInstanceSize, StdForwardLogger) - 1) / (void*).sizeof + 1] _buffer; auto buffer = cast(ubyte[]) _buffer; @@ -1810,27 +1805,17 @@ functions. } /// Ditto -unittest +@system unittest { + import std.experimental.logger.filelogger : FileLogger; + import std.file : deleteme, remove; Logger l = stdThreadLocalLog; - stdThreadLocalLog = new FileLogger("someFile.log"); + stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log"); + scope(exit) remove(deleteme ~ "-someFile.log"); + auto tempLog = stdThreadLocalLog; stdThreadLocalLog = l; -} - -version (unittest) -{ - import std.array; - import std.ascii; - import std.random; - - @trusted package string randomString(size_t upto) - { - auto app = Appender!string(); - foreach (_ ; 0 .. upto) - app.put(letters[uniform(0, letters.length)]); - return app.data; - } + destroy(tempLog); } @safe unittest @@ -1841,40 +1826,37 @@ version (unittest) globalLogLevel = ll; } -version (unittest) +package class TestLogger : Logger { - package class TestLogger : Logger - { - int line = -1; - string file = null; - string func = null; - string prettyFunc = null; - string msg = null; - LogLevel lvl; - - this(const LogLevel lv = LogLevel.info) @safe - { - super(lv); - } + int line = -1; + string file = null; + string func = null; + string prettyFunc = null; + string msg = null; + LogLevel lvl; - override protected void writeLogMsg(ref LogEntry payload) @safe - { - this.line = payload.line; - this.file = payload.file; - this.func = payload.funcName; - this.prettyFunc = payload.prettyFuncName; - this.lvl = payload.logLevel; - this.msg = payload.msg; - } + this(const LogLevel lv = LogLevel.all) @safe + { + super(lv); } - private void testFuncNames(Logger logger) @safe + override protected void writeLogMsg(ref LogEntry payload) @safe { - string s = "I'm here"; - logger.log(s); + this.line = payload.line; + this.file = payload.file; + this.func = payload.funcName; + this.prettyFunc = payload.prettyFuncName; + this.lvl = payload.logLevel; + this.msg = payload.msg; } } +version(unittest) private void testFuncNames(Logger logger) @safe +{ + string s = "I'm here"; + logger.log(s); +} + @safe unittest { auto tl1 = new TestLogger(); @@ -1966,6 +1948,10 @@ version (unittest) @safe unittest { + import std.conv : to; + import std.exception : assertThrown, assertNotThrown; + import std.format : format; + auto l = new TestLogger(LogLevel.all); string msg = "Hello Logger World"; l.log(msg); @@ -2102,10 +2088,13 @@ version (unittest) assert(l.logLevel == LogLevel.all); } -unittest // default logger +@system unittest // default logger { - import std.file : remove; - string filename = randomString(32) ~ ".tempLogFile"; + import std.file : deleteme, exists, remove; + import std.stdio : File; + import std.string : indexOf; + + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; FileLogger l = new FileLogger(filename); auto oldunspecificLogger = sharedLog; sharedLog = l; @@ -2113,6 +2102,7 @@ unittest // default logger scope(exit) { remove(filename); + assert(!exists(filename)); sharedLog = oldunspecificLogger; globalLogLevel = LogLevel.all; } @@ -2138,11 +2128,13 @@ unittest // default logger file.close(); } -unittest +@system unittest { - import std.file : remove; - import core.memory : destroy; - string filename = randomString(32) ~ ".tempLogFile"; + import std.file : deleteme, remove; + import std.stdio : File; + import std.string : indexOf; + + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; auto oldunspecificLogger = sharedLog; scope(exit) @@ -2173,6 +2165,8 @@ unittest @safe unittest { + import std.conv : to; + auto tl = new TestLogger(LogLevel.all); int l = __LINE__; tl.info("a"); @@ -2189,6 +2183,10 @@ unittest // testing possible log conditions @safe unittest { + import std.conv : to; + import std.string : indexOf; + import std.format : format; + auto oldunspecificLogger = sharedLog; auto mem = new TestLogger; @@ -2432,6 +2430,10 @@ unittest // more testing @safe unittest { + import std.conv : to; + import std.format : format; + import std.string : indexOf; + auto oldunspecificLogger = sharedLog; auto mem = new TestLogger; @@ -2916,6 +2918,8 @@ unittest // Issue #5 @safe unittest { + import std.string : indexOf; + auto oldunspecificLogger = sharedLog; scope(exit) @@ -2935,6 +2939,8 @@ unittest @safe unittest { import std.experimental.logger.multilogger : MultiLogger; + import std.string : indexOf; + stdThreadLocalLog.logLevel = LogLevel.all; auto oldunspecificLogger = sharedLog; @@ -2959,7 +2965,7 @@ unittest assert(tl.msg.indexOf("error") == 0); } -unittest +@system unittest { import std.exception : assertThrown; auto tl = new TestLogger(); @@ -2967,7 +2973,7 @@ unittest } // log objects with non-safe toString -unittest +@system unittest { struct Test { @@ -2985,18 +2991,20 @@ unittest // Workaround for atomics not allowed in @safe code private auto trustedLoad(T)(ref shared T value) @trusted { + import core.atomic : atomicLoad, MemoryOrder; return atomicLoad!(MemoryOrder.acq)(value); } // ditto private void trustedStore(T)(ref shared T dst, ref T src) @trusted { + import core.atomic : atomicStore, MemoryOrder; atomicStore!(MemoryOrder.rel)(dst, src); } // check that thread-local logging does not propagate // to shared logger -unittest +@system unittest { import std.concurrency, core.atomic, core.thread; @@ -3041,7 +3049,7 @@ unittest sharedLog = new IgnoredLog; Thread[] spawned; - foreach (i; 0..4) + foreach (i; 0 .. 4) { spawned ~= new Thread({ stdThreadLocalLog = new TestLog; @@ -3053,17 +3061,47 @@ unittest foreach (t; spawned) t.join(); - assert (atomicOp!"=="(logged_count, 4)); + assert(atomicOp!"=="(logged_count, 4)); } @safe unittest { - auto dl = cast(FileLogger)sharedLog; + auto dl = cast(FileLogger) sharedLog; assert(dl !is null); assert(dl.logLevel == LogLevel.all); assert(globalLogLevel == LogLevel.all); - auto tl = cast(StdForwardLogger)stdThreadLocalLog; + auto tl = cast(StdForwardLogger) stdThreadLocalLog; assert(tl !is null); stdThreadLocalLog.logLevel = LogLevel.all; } + +// Issue 14940 +@safe unittest +{ + import std.typecons : Nullable; + + Nullable!int a = 1; + auto l = new TestLogger(); + l.infof("log: %s", a); + assert(l.msg == "log: 1"); +} + +// Ensure @system toString methods work +@system unittest +{ + enum SystemToStringMsg = "SystemToString"; + static struct SystemToString + { + string toString() @system + { + return SystemToStringMsg; + } + } + + auto tl = new TestLogger(); + + SystemToString sts; + tl.logf("%s", sts); + assert(tl.msg == SystemToStringMsg); +} diff --git a/std/experimental/logger/filelogger.d b/std/experimental/logger/filelogger.d index 270e962f475..79d5baaab43 100644 --- a/std/experimental/logger/filelogger.d +++ b/std/experimental/logger/filelogger.d @@ -1,3 +1,4 @@ +/// module std.experimental.logger.filelogger; import std.stdio; @@ -19,17 +20,16 @@ class FileLogger : Logger fn = The filename of the output file of the $(D FileLogger). If that file can not be opened for writting an exception will be thrown. lv = The $(D LogLevel) for the $(D FileLogger). By default the - $(D LogLevel) for $(D FileLogger) is $(D LogLevel.info). + $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all). Example: ------------- - auto l1 = new FileLogger("logFile", "loggerName"); - auto l2 = new FileLogger("logFile", "loggerName", LogLevel.fatal); + auto l1 = new FileLogger("logFile"); + auto l2 = new FileLogger("logFile", LogLevel.fatal); ------------- */ - this(in string fn, const LogLevel lv = LogLevel.info) @safe + this(in string fn, const LogLevel lv = LogLevel.all) @safe { - import std.exception : enforce; super(lv); this.filename = fn; this.file_.open(this.filename, "a"); @@ -45,16 +45,16 @@ class FileLogger : Logger Params: file = The file used for logging. lv = The $(D LogLevel) for the $(D FileLogger). By default the - $(D LogLevel) for $(D FileLogger) is $(D LogLevel.info). + $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all). Example: ------------- auto file = File("logFile.log", "w"); - auto l1 = new FileLogger(file, "LoggerName"); - auto l2 = new FileLogger(file, "LoggerName", LogLevel.fatal); + auto l1 = new FileLogger(file); + auto l2 = new FileLogger(file, LogLevel.fatal); ------------- */ - this(File file, const LogLevel lv = LogLevel.info) @safe + this(File file, const LogLevel lv = LogLevel.all) @safe { super(lv); this.file_ = file; @@ -129,13 +129,13 @@ class FileLogger : Logger private string filename; } -unittest +@system unittest { - import std.file : remove; + import std.file : deleteme, remove; import std.array : empty; import std.string : indexOf; - string filename = randomString(32) ~ ".tempLogFile"; + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; auto l = new FileLogger(filename); scope(exit) @@ -158,13 +158,13 @@ unittest assert(readLine.indexOf(notWritten) == -1, readLine); } -unittest +@system unittest { - import std.file : remove; + import std.file : deleteme, remove; import std.array : empty; import std.string : indexOf; - string filename = randomString(32) ~ ".tempLogFile"; + string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; auto file = File(filename, "w"); auto l = new FileLogger(file); @@ -191,12 +191,12 @@ unittest @safe unittest { - auto dl = cast(FileLogger)sharedLog; + auto dl = cast(FileLogger) sharedLog; assert(dl !is null); assert(dl.logLevel == LogLevel.all); assert(globalLogLevel == LogLevel.all); - auto tl = cast(StdForwardLogger)stdThreadLocalLog; + auto tl = cast(StdForwardLogger) stdThreadLocalLog; assert(tl !is null); stdThreadLocalLog.logLevel = LogLevel.all; } diff --git a/std/experimental/logger/multilogger.d b/std/experimental/logger/multilogger.d index 3a61293970d..58ac051bcc1 100644 --- a/std/experimental/logger/multilogger.d +++ b/std/experimental/logger/multilogger.d @@ -1,3 +1,4 @@ +/// module std.experimental.logger.multilogger; import std.experimental.logger.core; @@ -13,10 +14,10 @@ struct MultiLoggerEntry } /** MultiLogger logs to multiple $(D Logger). The $(D Logger)s are stored in an -$(D Logger[]) in there order of insertion. +$(D Logger[]) in their order of insertion. Every data logged to this $(D MultiLogger) will be distributed to all the $(D -Logger)s inserted into inserted it. This $(D MultiLogger) implementation can +Logger)s inserted into it. This $(D MultiLogger) implementation can hold multiple $(D Logger)s with the same name. If the method $(D removeLogger) is used to remove a $(D Logger) only the first occurrence with that name will be removed. @@ -27,19 +28,19 @@ class MultiLogger : Logger Params: lv = The $(D LogLevel) for the $(D MultiLogger). By default the - $(D LogLevel) for $(D MultiLogger) is $(D LogLevel.info). + $(D LogLevel) for $(D MultiLogger) is $(D LogLevel.all). Example: ------------- auto l1 = new MultiLogger(LogLevel.trace); ------------- */ - this(const LogLevel lv = LogLevel.info) @safe + this(const LogLevel lv = LogLevel.all) @safe { super(lv); } - /** This member holds all $(D Logger) stored in the $(D MultiLogger). + /** This member holds all $(D Logger)s stored in the $(D MultiLogger). When inheriting from $(D MultiLogger) this member can be used to gain access to the stored $(D Logger). @@ -68,7 +69,7 @@ class MultiLogger : Logger */ Logger removeLogger(in char[] toRemove) @safe { - import std.algorithm : copy; + import std.algorithm.mutation : copy; import std.range.primitives : back, popBack; for (size_t i = 0; i < this.logger.length; ++i) { @@ -135,15 +136,16 @@ class MultiLogger : Logger assert(n0.msg == "Hello TestLogger"); assert(n0.line == line); assert(n1.msg == "Hello TestLogger"); - assert(n0.line == line); + assert(n1.line == line); } // Issue #16 -unittest +@system unittest { + import std.file : deleteme; import std.stdio : File; import std.string : indexOf; - auto logName = randomString(32) ~ ".log"; + string logName = deleteme ~ __FUNCTION__ ~ ".log"; auto logFileOutput = File(logName, "w"); scope(exit) { @@ -184,12 +186,12 @@ unittest @safe unittest { - auto dl = cast(FileLogger)sharedLog; + auto dl = cast(FileLogger) sharedLog; assert(dl !is null); assert(dl.logLevel == LogLevel.all); assert(globalLogLevel == LogLevel.all); - auto tl = cast(StdForwardLogger)stdThreadLocalLog; + auto tl = cast(StdForwardLogger) stdThreadLocalLog; assert(tl !is null); stdThreadLocalLog.logLevel = LogLevel.all; } diff --git a/std/experimental/logger/nulllogger.d b/std/experimental/logger/nulllogger.d index bfa7981144e..fa511be2b3f 100644 --- a/std/experimental/logger/nulllogger.d +++ b/std/experimental/logger/nulllogger.d @@ -1,3 +1,4 @@ +/// module std.experimental.logger.nulllogger; import std.experimental.logger.core; @@ -14,9 +15,9 @@ class NullLogger : Logger Params: lv = The $(D LogLevel) for the $(D NullLogger). By default the $(D LogLevel) - for $(D NullLogger) is $(D LogLevel.info). + for $(D NullLogger) is $(D LogLevel.all). */ - this(const LogLevel lv = LogLevel.info) @safe + this(const LogLevel lv = LogLevel.all) @safe { super(lv); this.fatalHandler = delegate() {}; @@ -30,6 +31,8 @@ class NullLogger : Logger /// @safe unittest { + import std.experimental.logger.nulllogger : LogLevel; + auto nl1 = new NullLogger(LogLevel.all); nl1.info("You will never read this."); nl1.fatal("You will never read this, either and it will not throw"); diff --git a/std/experimental/logger/package.d b/std/experimental/logger/package.d index f46e64c5c37..d0d141d1dba 100644 --- a/std/experimental/logger/package.d +++ b/std/experimental/logger/package.d @@ -3,7 +3,7 @@ Implements logging facilities. Copyright: Copyright Robert "burner" Schadek 2013 -- License: Boost License 1.0. -Authors: $(WEB http://www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek) +Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek) $(H3 Basic Logging) @@ -20,7 +20,7 @@ void main() { } ------------- This will print a message to the $(D stderr) device. The message will contain -the filename, the linenumber, the name of the surrounding function, the time +the filename, the line number, the name of the surrounding function, the time and the message. More complex log call can go along the lines like: @@ -48,15 +48,17 @@ Individual $(D Logger) and the global log functions share commonly named functions to log data. The names of the functions are as follows: -$(LI $(D log)) -$(LI $(D trace)) -$(LI $(D info)) -$(LI $(D warning)) -$(LI $(D critical)) -$(LI $(D fatal)) +$(UL + $(LI $(D log)) + $(LI $(D trace)) + $(LI $(D info)) + $(LI $(D warning)) + $(LI $(D critical)) + $(LI $(D fatal)) +) The default $(D Logger) will by default log to $(D stderr) and has a default $(D LogLevel) of $(D LogLevel.all). The default Logger can be accessed by -using the property called $(D sharedLog). This property a reference to the +using the property called $(D sharedLog). This property is a reference to the current default $(D Logger). This reference can be used to assign a new default $(D Logger). ------------- @@ -68,11 +70,11 @@ required $(D Logger). $(H3 Logging Fundamentals) $(H4 LogLevel) -The $(D LogLevel) of an log call can be defined in two ways. The first is by -calling $(D log) and passing the $(D LogLevel) explicit as the first argument. +The $(D LogLevel) of a log call can be defined in two ways. The first is by +calling $(D log) and passing the $(D LogLevel) explicitly as the first argument. The second way of setting the $(D LogLevel) of a log call, is by calling either $(D trace), $(D info), $(D warning), -$(D critical), or $(D fatal). The log call will than have the respective +$(D critical), or $(D fatal). The log call will then have the respective $(D LogLevel). If no $(D LogLevel) is defined the log call will use the current $(D LogLevel) of the used $(D Logger). If data is logged with $(D LogLevel) $(D fatal) by default an $(D Error) will be thrown. @@ -90,29 +92,30 @@ $(D bool). $(H4 Filtering Log Messages) Messages are logged if the $(D LogLevel) of the log message is greater than or -equal to than the $(D LogLevel) of the used $(D Logger) and additionally if the -$(D LogLevel) of the log message is greater equal to the global $(D LogLevel). +equal to the $(D LogLevel) of the used $(D Logger) and additionally if the +$(D LogLevel) of the log message is greater than or equal to the global $(D LogLevel). If a condition is passed into the log call, this condition must be true. The global $(D LogLevel) is accessible by using $(D globalLogLevel). -To assign the $(D LogLevel) of a $(D Logger) use the $(D logLevel) property of +To assign a $(D LogLevel) of a $(D Logger) use the $(D logLevel) property of the logger. -$(H4 Printf Sytle Logging) +$(H4 Printf Style Logging) If $(D printf)-style logging is needed add a $(B f) to the logging call, such as -$(D myLogger.infof("Hello %s", "world");) or $(fatalf("errno %d", 1337)) -The additional $(B f) enables $(D printf)-style logging for call combinations of -explicit $(D LogLevel) and conditional logging functions and methods. +$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)). +The additional $(B f) appended to the function name enables $(D printf)-style +logging for all combinations of explicit $(D LogLevel) and conditional +logging functions and methods. $(H4 Thread Local Redirection) Calls to the free standing log functions are not directly forwarded to the global $(D Logger) $(D sharedLog). Actually, a thread local $(D Logger) of -type $(D StdForwardLogger) process the log call and then, by default, forward +type $(D StdForwardLogger) processes the log call and then, by default, forwards the created $(D Logger.LogEntry) to the $(D sharedLog) $(D Logger). -The thread local $(D Logger) is accessable by the $(D stdThreadLocalLog) +The thread local $(D Logger) is accessible by the $(D stdThreadLocalLog) property. This property allows to assign user defined $(D Logger). The default $(D LogLevel) of the $(D stdThreadLocalLog) $(D Logger) is $(D LogLevel.all) -and it will therefore forward all messaged to the $(D sharedLog) $(D Logger). +and it will therefore forward all messages to the $(D sharedLog) $(D Logger). The $(D LogLevel) of the $(D stdThreadLocalLog) can be used to filter log calls before they reach the $(D sharedLog) $(D Logger). @@ -123,9 +126,9 @@ method. ------------- class MyCustomLogger : Logger { - this(string newName, LogLevel lv) @safe + this(LogLevel lv) @safe { - super(newName, lv); + super(lv); } override void writeLogMsg(ref LogEntry payload) @@ -134,13 +137,13 @@ class MyCustomLogger : Logger } } -auto logger = new MyCustomLogger(); -logger.log("Awesome log message"); +auto logger = new MyCustomLogger(LogLevel.info); +logger.log("Awesome log message with LogLevel.info"); ------------- To gain more precise control over the logging process, additionally to -overwriting the $(D writeLogMsg) method the methods $(D beginLogMsg), -$(D logMsgPart) and $(D finishLogMsg) can be overwritten. +overriding the $(D writeLogMsg) method the methods $(D beginLogMsg), +$(D logMsgPart) and $(D finishLogMsg) can be overridden. $(H3 Compile Time Disabling of $(D Logger)) In order to disable logging at compile time, pass $(D StdLoggerDisableLogging) as a diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d new file mode 100644 index 00000000000..6b965439bc7 --- /dev/null +++ b/std/experimental/typecons.d @@ -0,0 +1,1078 @@ +// Written in the D programming language. + +/** +This module implements experimental additions/modifications to $(MREF std, _typecons). + +Use this module to test out new functionality for $(REF wrap, std, _typecons) +which allows for a struct to be wrapped against an interface; the +implementation in $(MREF std, _typecons) only allows for classes to use the wrap +functionality. + +Source: $(PHOBOSSRC std/experimental/_typecons.d) + +Copyright: Copyright the respective authors, 2008- +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu), + $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski), + Don Clugston, + Shin Fujishiro, + Kenji Hara + */ +module std.experimental.typecons; + +import std.meta; // : AliasSeq, allSatisfy; +import std.traits; + +import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, + isImplicitlyConvertible, mixinAll, staticIota, + GetOverloadedMethods; + +private +{ + pragma(mangle, "_d_toObject") + extern(C) pure nothrow Object typecons_d_toObject(void* p); +} + +/* + * Avoids opCast operator overloading. + */ +private template dynamicCast(T) +if (is(T == class) || is(T == interface)) +{ + @trusted + T dynamicCast(S)(inout S source) + if (is(S == class) || is(S == interface)) + { + static if (is(Unqual!S : Unqual!T)) + { + import std.traits : QualifierOf; + alias Qual = QualifierOf!S; // SharedOf or MutableOf + alias TmpT = Qual!(Unqual!T); + inout(TmpT) tmp = source; // bypass opCast by implicit conversion + return *cast(T*)(&tmp); // + variable pointer cast + dereference + } + else + { + return cast(T) typecons_d_toObject(*cast(void**)(&source)); + } + } +} + +@system unittest +{ + class C { @disable opCast(T)() {} } + auto c = new C; + static assert(!__traits(compiles, cast(Object) c)); + auto o = dynamicCast!Object(c); + assert(c is o); + + interface I { @disable opCast(T)() {} Object instance(); } + interface J { @disable opCast(T)() {} Object instance(); } + class D : I, J { Object instance() { return this; } } + I i = new D(); + static assert(!__traits(compiles, cast(J) i)); + J j = dynamicCast!J(i); + assert(i.instance() is j.instance()); +} + +/* + * Determines if the `Source` type satisfies all interface requirements of + * `Targets`. + */ +private template implementsInterface(Source, Targets...) +if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) +{ + import std.meta : staticMap; + + // strict upcast + bool implementsInterface()() + if (Targets.length == 1 && is(Source : Targets[0])) + { + return true; + } + // structural upcast + template implementsInterface() + if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets)) + { + auto implementsInterface() + { + return hasRequiredMethods!(); + } + + // list of FuncInfo + alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!Targets); + // list of function symbols + alias SourceMembers = GetOverloadedMethods!Source; + + // Check whether all of SourceMembers satisfy covariance target in + // TargetMembers + template hasRequiredMethods(size_t i = 0) + { + static if (i >= TargetMembers.length) + enum hasRequiredMethods = true; + else + { + enum foundFunc = findCovariantFunction!(TargetMembers[i], Source, SourceMembers); + static if (foundFunc == -1) + pragma(msg, "Could not locate matching function for: " ~ TargetMembers[i].stringof); + enum hasRequiredMethods = + foundFunc != -1 && + hasRequiredMethods!(i + 1); + } + } + } +} +// ditto +private template implementsInterface(Source, Targets...) +if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets)) +{ + import std.meta : staticMap; + + alias implementsInterface = .implementsInterface!(Source, staticMap!(Unqual, Targets)); +} + +@safe unittest +{ + interface Foo { + void foo(); + } + interface Bar { + void bar(); + } + interface FooBar : Foo, Bar { + void foobar(); + } + + struct A { + void foo() {} + } + struct B { + void bar() {} + void foobar() {} + } + class C { + void foo() {} + void bar() {} + } + struct D { + void foo() {} + void bar() {} + void foobar() {} + } + // Implements interface + static assert(implementsInterface!(A, Foo)); + static assert(implementsInterface!(A, const(Foo))); + static assert(implementsInterface!(A, immutable(Foo))); + // Doesn't implement interface + static assert(!implementsInterface!(B, Foo)); + static assert(implementsInterface!(B, Bar)); + // Implements both interfaces + static assert(implementsInterface!(C, Foo)); + static assert(implementsInterface!(C, Bar)); + static assert(implementsInterface!(C, Foo, Bar)); + static assert(implementsInterface!(C, Foo, const(Bar))); + static assert(!implementsInterface!(A, Foo, Bar)); + static assert(!implementsInterface!(A, Foo, immutable(Bar))); + // Implements inherited + static assert(implementsInterface!(D, FooBar)); + static assert(!implementsInterface!(B, FooBar)); +} + +private enum isInterface(ConceptType) = is(ConceptType == interface); + +/// +template wrap(Targets...) +if (Targets.length >= 1 && allSatisfy!(isInterface, Targets)) +{ + import std.meta : ApplyLeft, staticMap; + + version(StdDdoc) + { + /** + * Wrap src in an anonymous class implementing $(D_PARAM Targets). + * + * wrap creates an internal wrapper class which implements the + * interfaces in `Targets` using the methods of `src`, then returns a + * GC-allocated instance of it. + * + * $(D_PARAM Source) can be either a `class` or a `struct`, but it must + * $(I structurally conform) with all the $(D_PARAM Targets) + * interfaces; i.e. it must provide concrete methods with compatible + * signatures of those in $(D_PARAM Targets). + * + * If $(D_PARAM Source) is a `struct` then wrapping/unwrapping will + * create a copy; it is not possible to affect the original `struct` + * through the wrapper. + * + * The returned object additionally supports $(LREF unwrap). + * + * Note: + * If $(D_PARAM Targets) has only one entry and $(D_PARAM Source) is a + * class which explicitly implements it, wrap simply returns src + * upcasted to `Targets[0]`. + * + * Bugs: + * wrap does not support interfaces which take their own type as either + * a parameter type or return type in any of its methods. + * + * See_Also: $(LREF unwrap) for examples + */ + auto wrap(Source)(inout Source src) + if (implementsInterface!(Source, Targets)); + } + + static if (!allSatisfy!(isMutable, Targets)) + alias wrap = .wrap!(staticMap!(Unqual, Targets)); + else + { + // strict upcast + auto wrap(Source)(inout Source src) + if (Targets.length == 1 && is(Source : Targets[0])) + { + alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]); + return dynamicCast!(inout T)(src); + } + + // structural upcast + template wrap(Source) + if (!allSatisfy!(ApplyLeft!(isImplicitlyConvertible, Source), Targets)) + { + auto wrap(inout Source src) + { + static assert(implementsInterface!(Source, Targets), + "Source "~Source.stringof~ + " does not have structural conformance to "~ + Targets.stringof); + + alias T = Select!(is(Source == shared), shared Impl, Impl); + return new inout T(src); + } + + // list of FuncInfo + alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!(Targets)); + // list of function symbols + alias SourceMembers = GetOverloadedMethods!Source; + + static if (is(Source == class) || is(Source == interface)) + alias StructuralType = Object; + else static if (is(Source == struct)) + alias StructuralType = Source; + + // Check whether all of SourceMembers satisfy covariance target in TargetMembers + // Internal wrapper class + final class Impl : Structural!StructuralType, Targets + { + private: + Source _wrap_source; + + this( inout Source s) inout @safe pure nothrow { _wrap_source = s; } + this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; } + + static if (is(Source == class) || is(Source == interface)) + { + // BUG: making private should work with NVI. + protected inout(Object) _wrap_getSource() inout @safe + { + return dynamicCast!(inout Object)(_wrap_source); + } + } + else + { + // BUG: making private should work with NVI. + protected inout(Source) _wrap_getSource() inout @safe + { + return _wrap_source; + } + } + + import std.conv : to; + import std.functional : forward; + template generateFun(size_t i) + { + enum name = TargetMembers[i].name; + enum fa = functionAttributes!(TargetMembers[i].type); + static args(int num)() + { + string r; + bool first = true; + foreach (i; staticIota!(0, num)) + { + import std.conv : to; + r ~= (first ? "" : ", ") ~ " a" ~ (i+1).to!string; + first = false; + } + return r; + } + static if (fa & FunctionAttribute.property) + { + static if (Parameters!(TargetMembers[i].type).length == 0) + enum fbody = "_wrap_source."~name; + else + enum fbody = "_wrap_source."~name~" = a1"; + } + else + { + enum fbody = "_wrap_source."~name~"("~args!(Parameters!(TargetMembers[i].type).length)~")"; + } + enum generateFun = + "override "~wrapperSignature!(TargetMembers[i]) ~ + "{ return "~fbody~"; }"; + } + + public: + mixin mixinAll!( + staticMap!(generateFun, staticIota!(0, TargetMembers.length))); + } + } + } +} + +// Build a signature that matches the provided function +// Each argument will be provided a name in the form a# +private template wrapperSignature(alias fun) +{ + enum name = fun.name; + enum fa = functionAttributes!(fun.type); + static @property stc() + { + string r; + if (fa & FunctionAttribute.property) r ~= "@property "; + if (fa & FunctionAttribute.ref_) r ~= "ref "; + if (fa & FunctionAttribute.pure_) r ~= "pure "; + if (fa & FunctionAttribute.nothrow_) r ~= "nothrow "; + if (fa & FunctionAttribute.trusted) r ~= "@trusted "; + if (fa & FunctionAttribute.safe) r ~= "@safe "; + return r; + } + static @property mod() + { + alias type = AliasSeq!(fun.type)[0]; + string r; + static if (is(type == immutable)) r ~= " immutable"; + else + { + static if (is(type == shared)) r ~= " shared"; + static if (is(type == const)) r ~= " const"; + else static if (is(type == inout)) r ~= " inout"; + //else --> mutable + } + return r; + } + alias param = Parameters!(fun.type); + static @property wrapperParameters() + { + string r; + bool first = true; + foreach (i, p; param) + { + import std.conv : to; + r ~= (first ? "" : ", ") ~ p.stringof ~ " a" ~ (i+1).to!string; + first = false; + } + return r; + } + + enum wrapperSignature = + stc~ReturnType!(fun.type).stringof ~ " " + ~ name~"("~wrapperParameters~")"~mod; +} + +@safe unittest +{ + interface M + { + void f1(); + void f2(string[] args, int count); + void f3(string[] args, int count) pure const; + } + + alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!M); + static assert(wrapperSignature!(TargetMembers[0]) == "void f1()" + , wrapperSignature!(TargetMembers[0])); + + static assert(wrapperSignature!(TargetMembers[1]) == "void f2(string[] a1, int a2)" + , wrapperSignature!(TargetMembers[1])); + + static assert(wrapperSignature!(TargetMembers[2]) == "pure void f3(string[] a1, int a2) const" + , wrapperSignature!(TargetMembers[2])); +} + +// Internal class to support dynamic cross-casting +private interface Structural(T) +{ + inout(T) _wrap_getSource() inout @safe pure nothrow; +} + +private string unwrapExceptionText(Source, Target)() +{ + return Target.stringof~ " not wrapped into "~ Source.stringof; +} + +version(StdDdoc) +{ + /** + * Extract object previously wrapped by $(LREF wrap). + * + * Params: + * Target = type of wrapped object + * src = wrapper object returned by $(LREF wrap) + * + * Returns: the wrapped object, or null if src is not a wrapper created + * by $(LREF wrap) and $(D_PARAM Target) is a class + * + * Throws: $(REF ConvException, std, conv) when attempting to extract a + * struct which is not the wrapped type + * + * See_also: $(LREF wrap) + */ + public inout(Target) unwrap(Target, Source)(inout Source src); +} + +/// +@system unittest +{ + interface Quack + { + int quack(); + @property int height(); + } + interface Flyer + { + @property int height(); + } + class Duck : Quack + { + int quack() { return 1; } + @property int height() { return 10; } + } + class Human + { + int quack() { return 2; } + @property int height() { return 20; } + } + struct HumanStructure + { + int quack() { return 3; } + @property int height() { return 30; } + } + + Duck d1 = new Duck(); + Human h1 = new Human(); + HumanStructure hs1; + + interface Refreshable + { + int refresh(); + } + // does not have structural conformance + static assert(!__traits(compiles, d1.wrap!Refreshable)); + static assert(!__traits(compiles, h1.wrap!Refreshable)); + static assert(!__traits(compiles, hs1.wrap!Refreshable)); + + // strict upcast + Quack qd = d1.wrap!Quack; + assert(qd is d1); + assert(qd.quack() == 1); // calls Duck.quack + // strict downcast + Duck d2 = qd.unwrap!Duck; + assert(d2 is d1); + + // structural upcast + Quack qh = h1.wrap!Quack; + Quack qhs = hs1.wrap!Quack; + assert(qh.quack() == 2); // calls Human.quack + assert(qhs.quack() == 3); // calls HumanStructure.quack + // structural downcast + Human h2 = qh.unwrap!Human; + HumanStructure hs2 = qhs.unwrap!HumanStructure; + assert(h2 is h1); + assert(hs2 is hs1); + + // structural upcast (two steps) + Quack qx = h1.wrap!Quack; // Human -> Quack + Quack qxs = hs1.wrap!Quack; // HumanStructure -> Quack + Flyer fx = qx.wrap!Flyer; // Quack -> Flyer + Flyer fxs = qxs.wrap!Flyer; // Quack -> Flyer + assert(fx.height == 20); // calls Human.height + assert(fxs.height == 30); // calls HumanStructure.height + // strucural downcast (two steps) + Quack qy = fx.unwrap!Quack; // Flyer -> Quack + Quack qys = fxs.unwrap!Quack; // Flyer -> Quack + Human hy = qy.unwrap!Human; // Quack -> Human + HumanStructure hys = qys.unwrap!HumanStructure; // Quack -> HumanStructure + assert(hy is h1); + assert(hys is hs1); + // strucural downcast (one step) + Human hz = fx.unwrap!Human; // Flyer -> Human + HumanStructure hzs = fxs.unwrap!HumanStructure; // Flyer -> HumanStructure + assert(hz is h1); + assert(hzs is hs1); +} + +/// +@system unittest +{ + import std.traits : functionAttributes, FunctionAttribute; + interface A { int run(); } + interface B { int stop(); @property int status(); } + class X + { + int run() { return 1; } + int stop() { return 2; } + @property int status() { return 3; } + } + + auto x = new X(); + auto ab = x.wrap!(A, B); + A a = ab; + B b = ab; + assert(a.run() == 1); + assert(b.stop() == 2); + assert(b.status == 3); + static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property); +} + +template unwrap(Target) +{ + static if (!isMutable!Target) + alias unwrap = .unwrap!(Unqual!Target); + else + { + // strict downcast + auto unwrap(Source)(inout Source src) + if (is(Target : Source)) + { + alias T = Select!(is(Source == shared), shared Target, Target); + return dynamicCast!(inout T)(src); + } + + // structural downcast for struct target + auto unwrap(Source)(inout Source src) + if (is(Target == struct)) + { + alias T = Select!(is(Source == shared), shared Target, Target); + auto upCastSource = dynamicCast!Object(src); // remove qualifier + do + { + if (auto a = dynamicCast!(Structural!Object)(upCastSource)) + { + upCastSource = a._wrap_getSource(); + } + else if (auto a = dynamicCast!(Structural!T)(upCastSource)) + { + return a._wrap_getSource(); + } + else + { + static if (hasMember!(Source, "_wrap_getSource")) + return unwrap!Target(src._wrap_getSource()); + else + break; + } + } while (upCastSource); + import std.conv : ConvException; + throw new ConvException(unwrapExceptionText!(Source,Target)); + } + // structural downcast for class target + auto unwrap(Source)(inout Source src) + if (!is(Target : Source) && !is(Target == struct)) + { + alias T = Select!(is(Source == shared), shared Target, Target); + Object upCastSource = dynamicCast!(Object)(src); // remove qualifier + do + { + // Unwrap classes + if (auto a = dynamicCast!(Structural!Object)(upCastSource)) + { + if (auto d = dynamicCast!(inout T)(upCastSource = a._wrap_getSource())) + return d; + } + // Unwrap a structure of type T + else if (auto a = dynamicCast!(Structural!T)(upCastSource)) + { + return a._wrap_getSource(); + } + // Unwrap class that already inherited from interface + else if (auto d = dynamicCast!(inout T)(upCastSource)) + { + return d; + } + // Recurse to find the struct Target within a wrapped tree + else + { + static if (hasMember!(Source, "_wrap_getSource")) + return unwrap!Target(src._wrap_getSource()); + else + break; + } + } while (upCastSource); + return null; + } + } +} + +@system unittest +{ + // Validate const/immutable + class A + { + int draw() { return 1; } + int draw(int v) { return v; } + + int draw() const { return 2; } + int draw() shared { return 3; } + int draw() shared const { return 4; } + int draw() immutable { return 5; } + } + interface Drawable + { + int draw(); + int draw() const; + int draw() shared; + int draw() shared const; + int draw() immutable; + } + interface Drawable2 + { + int draw(int v); + } + + auto ma = new A(); + auto sa = new shared A(); + auto ia = new immutable A(); + { + Drawable md = ma.wrap!Drawable; + const Drawable cd = ma.wrap!Drawable; + shared Drawable sd = sa.wrap!Drawable; + shared const Drawable scd = sa.wrap!Drawable; + immutable Drawable id = ia.wrap!Drawable; + assert( md.draw() == 1); + assert( cd.draw() == 2); + assert( sd.draw() == 3); + assert(scd.draw() == 4); + assert( id.draw() == 5); + } + { + Drawable2 d = ma.wrap!Drawable2; + static assert(!__traits(compiles, d.draw())); + assert(d.draw(10) == 10); + } +} +@system unittest +{ + // Bugzilla 10377 + import std.range, std.algorithm; + + interface MyInputRange(T) + { + @property T front(); + void popFront(); + @property bool empty(); + } + + //auto o = iota(0,10,1).inputRangeObject(); + //pragma(msg, __traits(allMembers, typeof(o))); + auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)(); + assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); +} +@system unittest +{ + // Bugzilla 10536 + interface Interface + { + int foo(); + } + class Pluggable + { + int foo() { return 1; } + @disable void opCast(T, this X)(); // ! + } + + Interface i = new Pluggable().wrap!Interface; + assert(i.foo() == 1); +} +@system unittest +{ + // Enhancement 10538 + interface Interface + { + int foo(); + int bar(int); + } + class Pluggable + { + int opDispatch(string name, A...)(A args) { return 100; } + } + + Interface i = wrap!Interface(new Pluggable()); + assert(i.foo() == 100); + assert(i.bar(10) == 100); +} + +// Concat all Targets function members into one tuple +private template ConcatInterfaceMembers(Targets...) +{ + static if (Targets.length == 0) + alias ConcatInterfaceMembers = AliasSeq!(); + else static if (Targets.length == 1) + alias ConcatInterfaceMembers + = AliasSeq!(GetOverloadedMethods!(Targets[0])); + else + alias ConcatInterfaceMembers = AliasSeq!( + GetOverloadedMethods!(Targets[0]), + ConcatInterfaceMembers!(Targets[1..$])); +} +// Remove duplicated functions based on the identifier name and function type covariance +private template UniqMembers(members...) +{ + template FuncInfo(string s, F) + { + enum name = s; + alias type = F; + } + + static if (members.length == 0) + alias UniqMembers = AliasSeq!(); + else + { + alias func = members[0]; + enum name = __traits(identifier, func); + alias type = FunctionTypeOf!func; + template check(size_t i, mem...) + { + static if (i >= mem.length) + enum ptrdiff_t check = -1; + else static if + (__traits(identifier, func) == __traits(identifier, mem[i]) && + !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void)) + { + enum ptrdiff_t check = i; + } + else + enum ptrdiff_t check = check!(i + 1, mem); + } + enum ptrdiff_t x = 1 + check!(0, members[1 .. $]); + static if (x >= 1) + { + alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x])); + alias remain = UniqMembers!(members[1 .. x], members[x + 1 .. $]); + + static if (remain.length >= 1 && remain[0].name == name && + !is(DerivedFunctionType!(typex, remain[0].type) == void)) + { + alias F = DerivedFunctionType!(typex, remain[0].type); + alias UniqMembers = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]); + } + else + alias UniqMembers = AliasSeq!(FuncInfo!(name, typex), remain); + } + else + { + alias UniqMembers = AliasSeq!(FuncInfo!(name, type), UniqMembers!(members[1 .. $])); + } + } +} + +// find a function from Fs that has same identifier and covariant type with f +private template findCovariantFunction(alias finfo, Source, Fs...) +{ + template check(size_t i = 0) + { + static if (i >= Fs.length) + enum ptrdiff_t check = -1; + else + { + enum ptrdiff_t check = + (finfo.name == __traits(identifier, Fs[i])) && + isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type) + ? i : check!(i + 1); + } + } + enum x = check!(); + static if (x == -1 && is(typeof(Source.opDispatch))) + { + alias Params = Parameters!(finfo.type); + enum ptrdiff_t findCovariantFunction = + is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) || + is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) || + is(typeof(( immutable Source).init.opDispatch!(finfo.name)(Params.init))) || + is(typeof(( shared Source).init.opDispatch!(finfo.name)(Params.init))) || + is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init))) + ? ptrdiff_t.max : -1; + } + else + enum ptrdiff_t findCovariantFunction = x; +} + +/** +Type constructor for final (aka head-const) variables. + +Final variables cannot be directly mutated or rebound, but references +reached through the variable are typed with their original mutability. +It is equivalent to `final` variables in D1 and Java, as well as +`readonly` variables in C#. + +When `T` is a `const` or `immutable` type, `Final` aliases +to `T`. +*/ +template Final(T) +{ +static if (is(T == const) || is(T == immutable)) + alias Final = T; +else +{ + struct Final + { + import std.typecons : Proxy; + + private T final_value; + mixin Proxy!final_value; + + /** + * Construction is forwarded to the underlying type. + */ + this(T other) + { + this.final_value = other; + } + + /// Ditto + this(Args...)(auto ref Args args) + if (__traits(compiles, T(args))) + { + static assert((!is(T == struct) && !is(T == union)) || !isNested!T, + "Non-static nested type " ~ fullyQualifiedName!T ~ " must be " ~ + "constructed explicitly at the call-site (e.g. auto s = " ~ + "makeFinal(" ~ T.stringof ~ "(...));)"); + this.final_value = T(args); + } + + // Attaching function attributes gives less noisy error messages + pure nothrow @safe @nogc + { + /++ + + All operators, including member access, are forwarded to the + + underlying value of type `T` except for these mutating operators, + + which are disabled. + +/ + void opAssign(Other)(Other other) + { + static assert(0, typeof(this).stringof ~ + " cannot be reassigned."); + } + + /// Ditto + void opOpAssign(string op, Other)(Other other) + { + static assert(0, typeof(this).stringof ~ + " cannot be reassigned."); + } + + /// Ditto + void opUnary(string op : "--")() + { + static assert(0, typeof(this).stringof ~ + " cannot be mutated."); + } + + /// Ditto + void opUnary(string op : "++")() + { + static assert(0, typeof(this).stringof ~ + " cannot be mutated."); + } + } + + /** + * + * `Final!T` implicitly converts to an rvalue of type `T` through + * `AliasThis`. + */ + inout(T) final_get() inout + { + return final_value; + } + + /// Ditto + alias final_get this; + + /// Ditto + auto ref opUnary(string op)() + if (__traits(compiles, mixin(op ~ "T.init"))) + { + return mixin(op ~ "this.final_value"); + } + } +} +} + +/// Ditto +Final!T makeFinal(T)(T t) +{ + return Final!T(t); +} + +/// `Final` can be used to create class references which cannot be rebound: +pure nothrow @safe unittest +{ + static class A + { + int i; + + this(int i) pure nothrow @nogc @safe + { + this.i = i; + } + } + + auto a = makeFinal(new A(42)); + assert(a.i == 42); + + //a = new A(24); // Reassignment is illegal, + a.i = 24; // But fields are still mutable. + + assert(a.i == 24); +} + +/// `Final` can also be used to create read-only data fields without using transitive immutability: +pure nothrow @safe unittest +{ + static class A + { + int i; + + this(int i) pure nothrow @nogc @safe + { + this.i = i; + } + } + + static class B + { + Final!A a; + + this(A a) pure nothrow @nogc @safe + { + this.a = a; // Construction, thus allowed. + } + } + + auto b = new B(new A(42)); + assert(b.a.i == 42); + + // b.a = new A(24); // Reassignment is illegal, + b.a.i = 24; // but `a` is still mutable. + + assert(b.a.i == 24); +} + +pure nothrow @safe unittest +{ + static class A { int i; } + static assert(!is(Final!A == A)); + static assert(is(Final!(const A) == const A)); + static assert(is(Final!(immutable A) == immutable A)); + + Final!A a = new A; + static assert(!__traits(compiles, a = new A)); + + static void foo(ref A a) pure nothrow @safe @nogc {} + static assert(!__traits(compiles, foo(a))); + + assert(a.i == 0); + a.i = 42; + assert(a.i == 42); + + Final!int i = 42; + static assert(!__traits(compiles, i = 24)); + static assert(!__traits(compiles, --i)); + static assert(!__traits(compiles, ++i)); + assert(i == 42); + int iCopy = i; + assert(iCopy == 42); + iCopy = -i; // non-mutating unary operators must work + assert(iCopy == -42); + + static struct S + { + int i; + + pure nothrow @safe @nogc: + this(int i){} + this(string s){} + this(int i, string s, float f){ this.i = i; } + } + + Final!S sint = 42; + Final!S sstr = "foo"; + static assert(!__traits(compiles, sint = sstr)); + + auto sboth = Final!S(42, "foo", 3.14); + assert(sboth.i == 42); + + sboth.i = 24; + assert(sboth.i == 24); + + struct NestedS + { + int i; + int get() pure nothrow @safe @nogc { return sboth.i + i; } + } + + // Nested structs must be constructed at the call-site + static assert(!__traits(compiles, Final!NestedS(6))); + auto s = makeFinal(NestedS(6)); + assert(s.i == 6); + assert(s.get == 30); + + class NestedC + { + int i; + + pure nothrow @safe @nogc: + this(int i) { this.i = i; } + int get() { return sboth.i + i; } + } + + auto c = makeFinal(new NestedC(6)); + assert(c.i == 6); + assert(c.get == 30); +} + +pure nothrow @safe unittest +{ + auto arr = makeFinal([1, 2, 3]); + static assert(!__traits(compiles, arr = null)); + static assert(!__traits(compiles, arr ~= 4)); + assert((arr ~ 4) == [1, 2, 3, 4]); +} + +// issue 17270 +pure nothrow @nogc @system unittest +{ + int i = 1; + Final!(int*) fp = &i; + assert(*fp == 1); + static assert(!__traits(compiles, + fp = &i // direct assignment + )); + static assert(is(typeof(*fp) == int)); + *fp = 2; // indirect assignment + assert(*fp == 2); + int* p = fp; + assert(*p == 2); +} + +pure nothrow @system unittest +{ + Final!(int[]) arr; + // static assert(!__traits(compiles, + // arr.length = 10; // bug! + // )); + static assert(!__traits(compiles, + arr.ptr = null + )); + static assert(!__traits(compiles, + arr.ptr++ + )); +} \ No newline at end of file diff --git a/std/file.d b/std/file.d index f49b74e748b..09d611a8141 100644 --- a/std/file.d +++ b/std/file.d @@ -4,15 +4,75 @@ Utilities for manipulating files and scanning directories. Functions in this module handle files as a unit, e.g., read or write one _file at a time. For opening files and manipulating them via handles refer -to module $(LINK2 std_stdio.html,$(D std.stdio)). +to module $(MREF std, stdio). + +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD General) $(TD + $(LREF exists) + $(LREF isDir) + $(LREF isFile) + $(LREF isSymlink) + $(LREF rename) + $(LREF thisExePath) +)) +$(TR $(TD Directories) $(TD + $(LREF chdir) + $(LREF dirEntries) + $(LREF getcwd) + $(LREF mkdir) + $(LREF mkdirRecurse) + $(LREF rmdir) + $(LREF rmdirRecurse) + $(LREF tempDir) +)) +$(TR $(TD Files) $(TD + $(LREF append) + $(LREF copy) + $(LREF read) + $(LREF readText) + $(LREF remove) + $(LREF slurp) + $(LREF write) +)) +$(TR $(TD Symlinks) $(TD + $(LREF symlink) + $(LREF readLink) +)) +$(TR $(TD Attributes) $(TD + $(LREF attrIsDir) + $(LREF attrIsFile) + $(LREF attrIsSymlink) + $(LREF getAttributes) + $(LREF getLinkAttributes) + $(LREF getSize) + $(LREF setAttributes) +)) +$(TR $(TD Timestamp) $(TD + $(LREF getTimes) + $(LREF getTimesWin) + $(LREF setTimes) + $(LREF timeLastModified) +)) +$(TR $(TD Other) $(TD + $(LREF DirEntry) + $(LREF FileException) + $(LREF PreserveAttributes) + $(LREF SpanMode) +)) +) -Macros: -WIKI = Phobos/StdFile Copyright: Copyright Digital Mars 2007 - 2011. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB digitalmars.com, Walter Bright), - $(WEB erdani.org, Andrei Alexandrescu), +See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an +introduction to working with files in D, module +$(MREF std, stdio) for opening files and manipulating them via handles, +and module $(MREF std, path) for manipulating path strings. + +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP digitalmars.com, Walter Bright), + $(HTTP erdani.org, Andrei Alexandrescu), Jonathan M Davis Source: $(PHOBOSSRC std/_file.d) */ @@ -20,14 +80,11 @@ module std.file; import core.stdc.stdlib, core.stdc.string, core.stdc.errno; -import std.conv; import std.datetime; -import std.exception; -import std.path; +import std.meta; import std.range.primitives; import std.traits; import std.typecons; -import std.typetuple; import std.internal.cstring; version (Windows) @@ -42,52 +99,55 @@ else version (Posix) else static assert(false, "Module " ~ .stringof ~ " not implemented for this OS."); -version (unittest) +// Character type used for operating system filesystem APIs +version (Windows) { - @property string deleteme() @safe - { - import std.process : thisProcessID; - static _deleteme = "deleteme.dmd.unittest.pid"; - static _first = true; + private alias FSChar = wchar; +} +else version (Posix) +{ + private alias FSChar = char; +} +else + static assert(0); - if(_first) - { - _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID); - _first = false; - } +// Purposefully not documented. Use at your own risk +@property string deleteme() @safe +{ + import std.conv : to; + import std.path : buildPath; + import std.process : thisProcessID; - return _deleteme; - } + static _deleteme = "deleteme.dmd.unittest.pid"; + static _first = true; - version(Android) - { - enum system_directory = "/system/etc"; - enum system_file = "/system/etc/hosts"; - } - else version(Posix) + if (_first) { - enum system_directory = "/usr/include"; - enum system_file = "/usr/include/assert.h"; + _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID); + _first = false; } -} + return _deleteme; +} -// @@@@ TEMPORARY - THIS SHOULD BE IN THE CORE @@@ -// {{{ -version (Windows) +version (unittest) private struct TestAliasedString { - enum FILE_ATTRIBUTE_REPARSE_POINT = 0x400; + string get() @safe @nogc pure nothrow { return _s; } + alias get this; + @disable this(this); + string _s; +} - // Required by tempPath(): - private extern(Windows) DWORD GetTempPathW(DWORD nBufferLength, - LPWSTR lpBuffer); - // Required by rename(): - enum MOVEFILE_REPLACE_EXISTING = 1; - private extern(Windows) DWORD MoveFileExW(LPCWSTR lpExistingFileName, - LPCWSTR lpNewFileName, - DWORD dwFlags); +version(Android) +{ + package enum system_directory = "/system/etc"; + package enum system_file = "/system/etc/hosts"; +} +else version(Posix) +{ + package enum system_directory = "/usr/include"; + package enum system_file = "/usr/include/assert.h"; } -// }}} /++ @@ -95,6 +155,8 @@ version (Windows) +/ class FileException : Exception { + import std.conv : text, to; + /++ OS error code. +/ @@ -111,7 +173,7 @@ class FileException : Exception +/ this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure { - if(msg.empty) + if (msg.empty) super(name.idup, file, line); else super(text(name, ": ", msg), file, line); @@ -152,18 +214,62 @@ class FileException : Exception private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__) { - if (!condition) + if (condition) + return condition; + version (Windows) { - version (Windows) - { throw new FileException(name, .GetLastError(), file, line); - } - else version (Posix) - { + } + else version (Posix) + { throw new FileException(name, .errno, file, line); - } } - return condition; +} + +version (Windows) +@trusted +private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez, + string file = __FILE__, size_t line = __LINE__) +{ + if (condition) + return condition; + if (!name) + { + import core.stdc.wchar_ : wcslen; + import std.conv : to; + + auto len = namez ? wcslen(namez) : 0; + name = to!string(namez[0 .. len]); + } + throw new FileException(name, .GetLastError(), file, line); +} + +version (Posix) +@trusted +private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez, + string file = __FILE__, size_t line = __LINE__) +{ + if (condition) + return condition; + if (!name) + { + import core.stdc.string : strlen; + + auto len = namez ? strlen(namez) : 0; + name = namez[0 .. len].idup; + } + throw new FileException(name, .errno, file, line); +} + +@safe unittest +{ + // issue 17102 + try + { + cenforce(false, null, null, + __FILE__, __LINE__); + } + catch (FileException) {} } /* ********************************** @@ -175,27 +281,59 @@ Read entire contents of file $(D name) and returns it as an untyped array. If the file size is larger than $(D upTo), only $(D upTo) bytes are read. -Example: +Params: + name = string or range of characters representing the file _name + upTo = if present, the maximum number of bytes to read ----- -import std.file, std.stdio; -void main() +Returns: Untyped array of bytes _read. + +Throws: $(LREF FileException) on error. + */ + +void[] read(R)(R name, size_t upTo = size_t.max) +if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R && + !isConvertibleToString!R) { - auto bytes = cast(ubyte[]) read("filename", 5); - if (bytes.length == 5) - writefln("The fifth byte of the file is 0x%x", bytes[4]); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + return readImpl(name, name.tempCString!FSChar(), upTo); + else + return readImpl(null, name.tempCString!FSChar(), upTo); } ----- -Returns: Untyped array of bytes _read. +/// +@safe unittest +{ + import std.utf : byChar; + scope(exit) + { + assert(exists(deleteme)); + remove(deleteme); + } -Throws: $(D FileException) on error. - */ -version (Posix) void[] read(in char[] name, size_t upTo = size_t.max) @trusted + write(deleteme, "1234"); // deleteme is the name of a temporary file + assert(read(deleteme, 2) == "12"); + assert(read(deleteme.byChar) == "1234"); + assert((cast(const(ubyte)[])read(deleteme)).length == 4); +} + +/// ditto +void[] read(R)(auto ref R name, size_t upTo = size_t.max) +if (isConvertibleToString!R) { - import std.algorithm : min; + return read!(StringTypeOf!R)(name, upTo); +} + +@safe unittest +{ + static assert(__traits(compiles, read(TestAliasedString(null)))); +} + +version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @trusted +{ + import std.algorithm.comparison : min; import std.array : uninitializedArray; import core.memory : GC; + import std.conv : to; // A few internal configuration parameters { enum size_t @@ -205,28 +343,29 @@ version (Posix) void[] read(in char[] name, size_t upTo = size_t.max) @trusted maxSlackMemoryAllowed = 1024; // } - immutable fd = core.sys.posix.fcntl.open(name.tempCString, + immutable fd = core.sys.posix.fcntl.open(namez, core.sys.posix.fcntl.O_RDONLY); cenforce(fd != -1, name); scope(exit) core.sys.posix.unistd.close(fd); stat_t statbuf = void; - cenforce(fstat(fd, &statbuf) == 0, name); + cenforce(fstat(fd, &statbuf) == 0, name, namez); - immutable initialAlloc = to!size_t(statbuf.st_size + immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size ? min(statbuf.st_size + 1, maxInitialAlloc) - : minInitialAlloc); + : minInitialAlloc)); void[] result = uninitializedArray!(ubyte[])(initialAlloc); - scope(failure) delete result; + scope(failure) GC.free(result.ptr); size_t size = 0; for (;;) { immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size, min(result.length, upTo) - size); - cenforce(actual != -1, name); + cenforce(actual != -1, name, namez); if (actual == 0) break; size += actual; + if (size >= upTo) break; if (size < result.length) continue; immutable newAlloc = size + sizeIncrement; result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)[0 .. newAlloc]; @@ -237,20 +376,17 @@ version (Posix) void[] read(in char[] name, size_t upTo = size_t.max) @trusted : result[0 .. size]; } -version (Windows) void[] read(in char[] name, size_t upTo = size_t.max) @safe + +version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe { - import std.algorithm : min; + import std.algorithm.comparison : min; import std.array : uninitializedArray; - static trustedRef(T)(ref T buf) @trusted - { - return &buf; - } - - static trustedCreateFileW(in char[] fileName, DWORD dwDesiredAccess, DWORD dwShareMode, + import core.memory : GC; + static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted { - return CreateFileW(fileName.tempCStringW(), dwDesiredAccess, dwShareMode, + return CreateFileW(namez, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); @@ -259,44 +395,53 @@ version (Windows) void[] read(in char[] name, size_t upTo = size_t.max) @safe { return CloseHandle(hObject); } - static trustedGetFileSize(HANDLE hFile, DWORD *lpFileSizeHigh) @trusted + static trustedGetFileSize(HANDLE hFile, out ulong fileSize) @trusted { - return GetFileSize(hFile, lpFileSizeHigh); + DWORD sizeHigh; + DWORD sizeLow = GetFileSize(hFile, &sizeHigh); + const bool result = sizeLow != INVALID_FILE_SIZE; + if (result) + fileSize = makeUlong(sizeLow, sizeHigh); + return result; } - static trustedReadFile(HANDLE hFile, void *lpBuffer, DWORD nNumberOfBytesToRead, - DWORD *lpNumberOfBytesRead, OVERLAPPED *lpOverlapped) @trusted + static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) @trusted { - return ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, - lpNumberOfBytesRead, lpOverlapped); + // Read by chunks of size < 4GB (Windows API limit) + ulong totalNumRead = 0; + while (totalNumRead != nNumberOfBytesToRead) + { + const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000); + DWORD numRead = void; + const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null); + if (result == 0 || numRead != chunkSize) + return false; + totalNumRead += chunkSize; + } + return true; } alias defaults = - TypeTuple!(GENERIC_READ, + AliasSeq!(GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, HANDLE.init); - auto h = trustedCreateFileW(name, defaults); + auto h = trustedCreateFileW(namez, defaults); - cenforce(h != INVALID_HANDLE_VALUE, name); - scope(exit) cenforce(trustedCloseHandle(h), name); - auto size = trustedGetFileSize(h, null); - cenforce(size != INVALID_FILE_SIZE, name); - size = min(upTo, size); + cenforce(h != INVALID_HANDLE_VALUE, name, namez); + scope(exit) cenforce(trustedCloseHandle(h), name, namez); + ulong fileSize = void; + cenforce(trustedGetFileSize(h, fileSize), name, namez); + size_t size = min(upTo, fileSize); auto buf = uninitializedArray!(ubyte[])(size); - scope(failure) delete buf; - DWORD numread = void; - cenforce(trustedReadFile(h,buf.ptr, size, trustedRef(numread), null) != 0 - && numread == size, name); - return buf[0 .. size]; -} + scope(failure) + { + () @trusted { GC.free(buf.ptr); } (); + } -@safe unittest -{ - write(deleteme, "1234"); - scope(exit) { assert(exists(deleteme)); remove(deleteme); } - assert(read(deleteme, 2) == "12"); - assert(read(deleteme) == "1234"); + if (size) + cenforce(trustedReadFile(h, &buf[0], size), name, namez); + return buf[0 .. size]; } version (linux) @safe unittest @@ -317,196 +462,402 @@ version (linux) @safe unittest } /******************************************** -Read and validates (using $(XREF utf, validate)) a text file. $(D S) +Read and validates (using $(REF validate, std,utf)) a text file. $(D S) can be a type of array of characters of any width and constancy. No width conversion is performed; if the width of the characters in file $(D name) is different from the width of elements of $(D S), validation will fail. +Params: + name = string or range of characters representing the file _name + Returns: Array of characters read. Throws: $(D FileException) on file error, $(D UTFException) on UTF decoding error. - -Example: - ----- -enforce(system("echo abc>deleteme") == 0); -scope(exit) remove("deleteme"); -enforce(chomp(readText("deleteme")) == "abc"); ----- */ -S readText(S = string)(in char[] name) @safe if (isSomeString!S) +S readText(S = string, R)(R name) +if (isSomeString!S && + (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) && + !isConvertibleToString!R) { import std.utf : validate; - static auto trustedCast(void[] buf) @trusted { return cast(S)buf; } + static auto trustedCast(void[] buf) @trusted { return cast(S) buf; } auto result = trustedCast(read(name)); validate(result); return result; } +/// @safe unittest { - import std.string; - write(deleteme, "abc\n"); - scope(exit) { assert(exists(deleteme)); remove(deleteme); } - enforce(chomp(readText(deleteme)) == "abc"); + import std.exception : enforce; + write(deleteme, "abc"); // deleteme is the name of a temporary file + scope(exit) remove(deleteme); + string content = readText(deleteme); + enforce(content == "abc"); +} + +/// ditto +S readText(S = string, R)(auto ref R name) +if (isConvertibleToString!R) +{ + return readText!(S, StringTypeOf!R)(name); +} + +@safe unittest +{ + static assert(__traits(compiles, readText(TestAliasedString(null)))); } /********************************************* Write $(D buffer) to file $(D name). + +Creates the file if it does not already exist. + +Params: + name = string or range of characters representing the file _name + buffer = data to be written to file + Throws: $(D FileException) on error. -Example: +See_also: $(REF toFile, std,stdio) + */ +void write(R)(R name, const void[] buffer) +if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) && + !isConvertibleToString!R) +{ + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + writeImpl(name, name.tempCString!FSChar(), buffer, false); + else + writeImpl(null, name.tempCString!FSChar(), buffer, false); +} ----- -import std.file; -void main() +/// +@system unittest { + scope(exit) + { + assert(exists(deleteme)); + remove(deleteme); + } + int[] a = [ 0, 1, 1, 2, 3, 5, 8 ]; - write("filename", a); - assert(cast(int[]) read("filename") == a); + write(deleteme, a); // deleteme is the name of a temporary file + assert(cast(int[]) read(deleteme) == a); } ----- - */ -void write(in char[] name, const void[] buffer) @trusted + +/// ditto +void write(R)(auto ref R name, const void[] buffer) +if (isConvertibleToString!R) { - version(Windows) - { - alias defaults = - TypeTuple!(GENERIC_WRITE, 0, null, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - HANDLE.init); - auto h = CreateFileW(name.tempCStringW(), defaults); + write!(StringTypeOf!R)(name, buffer); +} - cenforce(h != INVALID_HANDLE_VALUE, name); - scope(exit) cenforce(CloseHandle(h), name); - DWORD numwritten; - cenforce(WriteFile(h, buffer.ptr, to!DWORD(buffer.length), &numwritten, null) != 0 - && buffer.length == numwritten, - name); - } - else version(Posix) - return writeImpl(name, buffer, O_CREAT | O_WRONLY | O_TRUNC); +@safe unittest +{ + static assert(__traits(compiles, write(TestAliasedString(null), null))); } /********************************************* Appends $(D buffer) to file $(D name). -Throws: $(D FileException) on error. -Example: +Creates the file if it does not already exist. + +Params: + name = string or range of characters representing the file _name + buffer = data to be appended to file + +Throws: $(D FileException) on error. + */ +void append(R)(R name, const void[] buffer) +if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) && + !isConvertibleToString!R) +{ + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + writeImpl(name, name.tempCString!FSChar(), buffer, true); + else + writeImpl(null, name.tempCString!FSChar(), buffer, true); +} ----- -import std.file; -void main() +/// +@system unittest { + scope(exit) + { + assert(exists(deleteme)); + remove(deleteme); + } + int[] a = [ 0, 1, 1, 2, 3, 5, 8 ]; - write("filename", a); + write(deleteme, a); // deleteme is the name of a temporary file int[] b = [ 13, 21 ]; - append("filename", b); - assert(cast(int[]) read("filename") == a ~ b); + append(deleteme, b); + assert(cast(int[]) read(deleteme) == a ~ b); } ----- - */ -void append(in char[] name, in void[] buffer) @trusted -{ - version(Windows) - { - alias defaults = - TypeTuple!(GENERIC_WRITE,0,null,OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,HANDLE.init); - auto h = CreateFileW(name.tempCStringW(), defaults); +/// ditto +void append(R)(auto ref R name, const void[] buffer) +if (isConvertibleToString!R) +{ + append!(StringTypeOf!R)(name, buffer); +} - cenforce(h != INVALID_HANDLE_VALUE, name); - scope(exit) cenforce(CloseHandle(h), name); - DWORD numwritten; - cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER - && WriteFile(h,buffer.ptr,to!DWORD(buffer.length),&numwritten,null) != 0 - && buffer.length == numwritten, - name); - } - else version(Posix) - return writeImpl(name, buffer, O_APPEND | O_WRONLY | O_CREAT); +@safe unittest +{ + static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3]))); } // Posix implementation helper for write and append -version(Posix) private void writeImpl(in char[] name, - in void[] buffer, in uint mode) @trusted +version(Posix) private void writeImpl(const(char)[] name, const(FSChar)* namez, + in void[] buffer, bool append) @trusted { - immutable fd = core.sys.posix.fcntl.open(name.tempCString(), - mode, octal!666); - cenforce(fd != -1, name); + import std.conv : octal; + + // append or write + auto mode = append ? O_CREAT | O_WRONLY | O_APPEND + : O_CREAT | O_WRONLY | O_TRUNC; + + immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666); + cenforce(fd != -1, name, namez); { scope(failure) core.sys.posix.unistd.close(fd); + immutable size = buffer.length; - cenforce( - core.sys.posix.unistd.write(fd, buffer.ptr, size) == size, - name); + size_t sum, cnt = void; + while (sum != size) + { + cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30; + const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt); + if (numwritten != cnt) + break; + sum += numwritten; + } + cenforce(sum == size, name, namez); } - cenforce(core.sys.posix.unistd.close(fd) == 0, name); + cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez); +} + +// Windows implementation helper for write and append + +version(Windows) private void writeImpl(const(char)[] name, const(FSChar)* namez, + in void[] buffer, bool append) @trusted +{ + HANDLE h; + if (append) + { + alias defaults = + AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + HANDLE.init); + + h = CreateFileW(namez, defaults); + cenforce(h != INVALID_HANDLE_VALUE, name, namez); + cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER, + name, namez); + } + else // write + { + alias defaults = + AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + HANDLE.init); + + h = CreateFileW(namez, defaults); + cenforce(h != INVALID_HANDLE_VALUE, name, namez); + } + immutable size = buffer.length; + size_t sum, cnt = void; + DWORD numwritten = void; + while (sum != size) + { + cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30; + WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null); + if (numwritten != cnt) + break; + sum += numwritten; + } + cenforce(sum == size && CloseHandle(h), name, namez); } /*************************************************** * Rename file $(D from) to $(D to). * If the target file exists, it is overwritten. + * Params: + * from = string or range of characters representing the existing file name + * to = string or range of characters representing the target file name * Throws: $(D FileException) on error. */ -void rename(in char[] from, in char[] to) @trusted +void rename(RF, RT)(RF from, RT to) +if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF) + && !isConvertibleToString!RF && + (isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) || isSomeString!RT) + && !isConvertibleToString!RT) +{ + // Place outside of @trusted block + auto fromz = from.tempCString!FSChar(); + auto toz = to.tempCString!FSChar(); + + static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char)) + alias f = from; + else + enum string f = null; + + static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char)) + alias t = to; + else + enum string t = null; + + renameImpl(f, t, fromz, toz); +} + +/// ditto +void rename(RF, RT)(auto ref RF from, auto ref RT to) +if (isConvertibleToString!RF || isConvertibleToString!RT) +{ + import std.meta : staticMap; + alias Types = staticMap!(convertToString, RF, RT); + rename!Types(from, to); +} + +@safe unittest +{ + static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null)))); + static assert(__traits(compiles, rename("", TestAliasedString(null)))); + static assert(__traits(compiles, rename(TestAliasedString(null), ""))); + import std.utf : byChar; + static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar))); +} + +private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz) @trusted { version(Windows) { - enforce(MoveFileExW(from.tempCStringW(), to.tempCStringW(), MOVEFILE_REPLACE_EXISTING), + import std.exception : enforce; + + const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING); + if (!result) + { + import core.stdc.wchar_ : wcslen; + import std.conv : to, text; + + if (!f) + f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]); + + if (!t) + t = to!(typeof(t))(toz[0 .. wcslen(toz)]); + + enforce(false, new FileException( - text("Attempting to rename file ", from, " to ", - to))); + text("Attempting to rename file ", f, " to ", t))); + } } else version(Posix) { - import core.stdc.stdio; + static import core.stdc.stdio; - cenforce(core.stdc.stdio.rename(from.tempCString(), to.tempCString()) == 0, to); + cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz); } } @safe unittest { + import std.utf : byWchar; + auto t1 = deleteme, t2 = deleteme~"2"; scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); write(t1, "1"); rename(t1, t2); assert(readText(t2) == "1"); write(t1, "2"); - rename(t1, t2); + rename(t1, t2.byWchar); assert(readText(t2) == "2"); } /*************************************************** Delete file $(D name). + +Params: + name = string or range of characters representing the file name + Throws: $(D FileException) on error. */ -void remove(in char[] name) @trusted +void remove(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) +{ + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + removeImpl(name, name.tempCString!FSChar()); + else + removeImpl(null, name.tempCString!FSChar()); +} + +/// ditto +void remove(R)(auto ref R name) +if (isConvertibleToString!R) +{ + remove!(StringTypeOf!R)(name); +} + +@safe unittest +{ + static assert(__traits(compiles, remove(TestAliasedString("foo")))); +} + +private void removeImpl(const(char)[] name, const(FSChar)* namez) @trusted { version(Windows) { - cenforce(DeleteFileW(name.tempCStringW()), name); + cenforce(DeleteFileW(namez), name, namez); } else version(Posix) { - import core.stdc.stdio; + static import core.stdc.stdio; - cenforce(core.stdc.stdio.remove(name.tempCString()) == 0, + if (!name) + { + import core.stdc.string : strlen; + auto len = strlen(namez); + name = namez[0 .. len]; + } + cenforce(core.stdc.stdio.remove(namez) == 0, "Failed to remove file " ~ name); } } -version(Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(in char[] name) @trusted +version(Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)) { - WIN32_FILE_ATTRIBUTE_DATA fad; - enforce(GetFileAttributesExW(name.tempCStringW(), GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad), new FileException(name.idup)); + auto namez = name.tempCString!FSChar(); + + WIN32_FILE_ATTRIBUTE_DATA fad = void; + + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + { + static void getFA(const(char)[] name, const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted + { + import std.exception : enforce; + enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad), + new FileException(name.idup)); + } + getFA(name, namez, fad); + } + else + { + static void getFA(const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted + { + import core.stdc.wchar_ : wcslen; + import std.conv : to; + import std.exception : enforce; + + enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad), + new FileException(namez[0 .. wcslen(namez)].to!string)); + } + getFA(namez, fad); + } return fad; } @@ -521,9 +872,14 @@ version(Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure n /*************************************************** Get size of file $(D name) in bytes. +Params: + name = string or range of characters representing the file name + Throws: $(D FileException) on error (e.g., file not found). */ -ulong getSize(in char[] name) @safe +ulong getSize(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { @@ -532,20 +888,34 @@ ulong getSize(in char[] name) @safe } else version(Posix) { - static auto trustedStat(in char[] path, stat_t* buf) @trusted - { - return stat(path.tempCString(), buf); - } - static stat_t* ptrOfLocalVariable(return ref stat_t buf) @trusted + auto namez = name.tempCString(); + + static trustedStat(const(FSChar)* namez, out stat_t buf) @trusted { - return &buf; + return stat(namez, &buf); } + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; stat_t statbuf = void; - cenforce(trustedStat(name, ptrOfLocalVariable(statbuf)) == 0, name); + cenforce(trustedStat(namez, statbuf) == 0, names, namez); return statbuf.st_size; } } +/// ditto +ulong getSize(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return getSize!(StringTypeOf!R)(name); +} + +@safe unittest +{ + static assert(__traits(compiles, getSize(TestAliasedString("foo")))); +} + @safe unittest { // create a file of size 1 @@ -554,10 +924,33 @@ ulong getSize(in char[] name) @safe assert(getSize(deleteme) == 1); // create a file of size 3 write(deleteme, "abc"); - assert(getSize(deleteme) == 3); + import std.utf : byChar; + assert(getSize(deleteme.byChar) == 3); } +// Reads a time field from a stat_t with full precision. +version(Posix) +private SysTime statTimeToStdTime(char which)(ref stat_t statbuf) +{ + auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`); + long stdTime = unixTimeToStdTime(unixTime); + + static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`)))) + stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100; + else + static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`)))) + stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100; + else + static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`)))) + stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100; + else + static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`)))) + stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100; + + return SysTime(stdTime); +} + /++ Get the access and modified times of file or folder $(D name). @@ -569,9 +962,11 @@ ulong getSize(in char[] name) @safe Throws: $(D FileException) on error. +/ -void getTimes(in char[] name, +void getTimes(R)(R name, out SysTime accessTime, - out SysTime modificationTime) @safe + out SysTime modificationTime) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { @@ -583,20 +978,41 @@ void getTimes(in char[] name, } else version(Posix) { - static auto trustedStat(in char[] path, ref stat_t buf) @trusted + auto namez = name.tempCString(); + + static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted { - return stat(path.tempCString(), &buf); + return stat(namez, &buf); } stat_t statbuf = void; - cenforce(trustedStat(name, statbuf) == 0, name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedStat(namez, statbuf) == 0, names, namez); - accessTime = SysTime(unixTimeToStdTime(statbuf.st_atime)); - modificationTime = SysTime(unixTimeToStdTime(statbuf.st_mtime)); + accessTime = statTimeToStdTime!'a'(statbuf); + modificationTime = statTimeToStdTime!'m'(statbuf); } } -unittest +/// ditto +void getTimes(R)(auto ref R name, + out SysTime accessTime, + out SysTime modificationTime) +if (isConvertibleToString!R) +{ + return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime); +} + +@safe unittest +{ + SysTime atime, mtime; + static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime))); +} + +@system unittest { import std.stdio : writefln; @@ -651,41 +1067,60 @@ unittest } -/++ - $(BLUE This function is Windows-Only.) +version(StdDdoc) +{ + /++ + $(BLUE This function is Windows-Only.) - Get creation/access/modified times of file $(D name). + Get creation/access/modified times of file $(D name). - This is the same as $(D getTimes) except that it also gives you the file - creation time - which isn't possible on Posix systems. + This is the same as $(D getTimes) except that it also gives you the file + creation time - which isn't possible on Posix systems. - Params: - name = File name to get times for. - fileCreationTime = Time the file was created. - fileAccessTime = Time the file was last accessed. - fileModificationTime = Time the file was last modified. + Params: + name = File name to get times for. + fileCreationTime = Time the file was created. + fileAccessTime = Time the file was last accessed. + fileModificationTime = Time the file was last modified. - Throws: - $(D FileException) on error. - +/ -version(StdDdoc) void getTimesWin(in char[] name, - out SysTime fileCreationTime, - out SysTime fileAccessTime, - out SysTime fileModificationTime) @safe; -else version(Windows) void getTimesWin(in char[] name, - out SysTime fileCreationTime, - out SysTime fileAccessTime, - out SysTime fileModificationTime) @safe + Throws: + $(D FileException) on error. + +/ + void getTimesWin(R)(R name, + out SysTime fileCreationTime, + out SysTime fileAccessTime, + out SysTime fileModificationTime) + if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R); +} +else version(Windows) { - with (getFileAttributesWin(name)) + void getTimesWin(R)(R name, + out SysTime fileCreationTime, + out SysTime fileAccessTime, + out SysTime fileModificationTime) + if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) + { + with (getFileAttributesWin(name)) + { + fileCreationTime = std.datetime.FILETIMEToSysTime(&ftCreationTime); + fileAccessTime = std.datetime.FILETIMEToSysTime(&ftLastAccessTime); + fileModificationTime = std.datetime.FILETIMEToSysTime(&ftLastWriteTime); + } + } + + void getTimesWin(R)(auto ref R name, + out SysTime fileCreationTime, + out SysTime fileAccessTime, + out SysTime fileModificationTime) + if (isConvertibleToString!R) { - fileCreationTime = std.datetime.FILETIMEToSysTime(&ftCreationTime); - fileAccessTime = std.datetime.FILETIMEToSysTime(&ftLastAccessTime); - fileModificationTime = std.datetime.FILETIMEToSysTime(&ftLastWriteTime); + getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime); } } -version(Windows) unittest +version(Windows) @system unittest { import std.stdio : writefln; auto currTime = Clock.currTime(); @@ -748,6 +1183,11 @@ version(Windows) unittest assert(accessTime1 <= accessTime2); assert(modificationTime1 <= modificationTime2); } + + { + SysTime ctime, atime, mtime; + static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime))); + } } @@ -762,17 +1202,20 @@ version(Windows) unittest Throws: $(D FileException) on error. +/ -void setTimes(in char[] name, +void setTimes(R)(R name, SysTime accessTime, - SysTime modificationTime) @safe + SysTime modificationTime) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { - static auto trustedCreateFileW(in char[] fileName, DWORD dwDesiredAccess, DWORD dwShareMode, + auto namez = name.tempCString!FSChar(); + static auto trustedCreateFileW(const(FSChar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted { - return CreateFileW(fileName.tempCStringW(), dwDesiredAccess, dwShareMode, + return CreateFileW(namez, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); @@ -790,39 +1233,83 @@ void setTimes(in char[] name, const ta = SysTimeToFILETIME(accessTime); const tm = SysTimeToFILETIME(modificationTime); alias defaults = - TypeTuple!(GENERIC_WRITE, - 0, - null, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | - FILE_ATTRIBUTE_DIRECTORY | - FILE_FLAG_BACKUP_SEMANTICS, - HANDLE.init); - auto h = trustedCreateFileW(name, defaults); - - cenforce(h != INVALID_HANDLE_VALUE, name); + AliasSeq!(GENERIC_WRITE, + 0, + null, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_DIRECTORY | + FILE_FLAG_BACKUP_SEMANTICS, + HANDLE.init); + auto h = trustedCreateFileW(namez, defaults); + + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(h != INVALID_HANDLE_VALUE, names, namez); scope(exit) - cenforce(trustedCloseHandle(h), name); + cenforce(trustedCloseHandle(h), names, namez); - cenforce(trustedSetFileTime(h, null, ta, tm), name); + cenforce(trustedSetFileTime(h, null, ta, tm), names, namez); } else version(Posix) { - static auto trustedUtimes(in char[] path, const ref timeval[2] times) @trusted + auto namez = name.tempCString!FSChar(); + static if (is(typeof(&utimensat))) { - return utimes(path.tempCString(), times); + static auto trustedUtimensat(int fd, const(FSChar)* namez, const ref timespec[2] times, int flags) @trusted + { + return utimensat(fd, namez, times, flags); + } + timespec[2] t = void; + + t[0] = accessTime.toTimeSpec(); + t[1] = modificationTime.toTimeSpec(); + + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedUtimensat(AT_FDCWD, namez, t, 0) == 0, names, namez); } - timeval[2] t = void; + else + { + static auto trustedUtimes(const(FSChar)* namez, const ref timeval[2] times) @trusted + { + return utimes(namez, times); + } + timeval[2] t = void; - t[0] = accessTime.toTimeVal(); - t[1] = modificationTime.toTimeVal(); + t[0] = accessTime.toTimeVal(); + t[1] = modificationTime.toTimeVal(); - cenforce(trustedUtimes(name, t) == 0, name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedUtimes(namez, t) == 0, names, namez); + } } } -unittest +/// ditto +void setTimes(R)(auto ref R name, + SysTime accessTime, + SysTime modificationTime) +if (isConvertibleToString!R) +{ + setTimes!(StringTypeOf!R)(name, accessTime, modificationTime); +} + +@safe unittest +{ + if (false) // Test instatiation + setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init); +} + +@system unittest { import std.stdio : File; string newdir = deleteme ~ r".dir"; @@ -832,19 +1319,26 @@ unittest if (!exists(dir)) mkdirRecurse(dir); { auto f = File(file, "w"); } - foreach (path; [file, dir]) // test file and dir + void testTimes(int hnsecValue) { - SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30)); - SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30)); - setTimes(path, atime, mtime); - - SysTime atime_res; - SysTime mtime_res; - getTimes(path, atime_res, mtime_res); - assert(atime == atime_res); - assert(mtime == mtime_res); + foreach (path; [file, dir]) // test file and dir + { + SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue)); + SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue)); + setTimes(path, atime, mtime); + + SysTime atime_res; + SysTime mtime_res; + getTimes(path, atime_res, mtime_res); + assert(atime == atime_res); + assert(mtime == mtime_res); + } } + testTimes(0); + version (linux) + testTimes(123_456_7); + rmdirRecurse(newdir); } @@ -854,7 +1348,9 @@ unittest Throws: $(D FileException) if the given file does not exist. +/ -SysTime timeLastModified(in char[] name) @safe +SysTime timeLastModified(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { @@ -867,25 +1363,41 @@ SysTime timeLastModified(in char[] name) @safe } else version(Posix) { - static auto trustedStat(in char[] path, ref stat_t buf) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted { - return stat(path.tempCString(), &buf); + return stat(namez, &buf); } stat_t statbuf = void; - cenforce(trustedStat(name, statbuf) == 0, name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedStat(namez, statbuf) == 0, names, namez); - return SysTime(unixTimeToStdTime(statbuf.st_mtime)); + return statTimeToStdTime!'m'(statbuf); } } - +/// ditto +SysTime timeLastModified(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return timeLastModified!(StringTypeOf!R)(name); +} + +@safe unittest +{ + static assert(__traits(compiles, timeLastModified(TestAliasedString("foo")))); +} + /++ Returns the time that the given file was last modified. If the file does not exist, returns $(D returnIfMissing). A frequent usage pattern occurs in build automation tools such as - $(WEB gnu.org/software/make, make) or $(WEB + $(HTTP gnu.org/software/make, make) or $(HTTP en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D target) must be rebuilt from file $(D source) (i.e., $(D target) is older than $(D source) or does not exist), use the comparison @@ -898,9 +1410,9 @@ SysTime timeLastModified(in char[] name) @safe name = The name of the file to get the modification time for. returnIfMissing = The time to return if the given file does not exist. -Examples: +Example: -------------------- -if(timeLastModified(source) >= timeLastModified(target, SysTime.min)) +if (timeLastModified(source) >= timeLastModified(target, SysTime.min)) { // must (re)build } @@ -910,11 +1422,12 @@ else } -------------------- +/ -SysTime timeLastModified(in char[] name, SysTime returnIfMissing) @safe +SysTime timeLastModified(R)(R name, SysTime returnIfMissing) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)) { version(Windows) { - if(!exists(name)) + if (!exists(name)) return returnIfMissing; SysTime dummy; @@ -926,22 +1439,23 @@ SysTime timeLastModified(in char[] name, SysTime returnIfMissing) @safe } else version(Posix) { - static auto trustedStat(in char[] path, ref stat_t buf) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted { - return stat(path.tempCString(), &buf); + return stat(namez, &buf); } stat_t statbuf = void; - return trustedStat(name, statbuf) != 0 ? + return trustedStat(namez, statbuf) != 0 ? returnIfMissing : - SysTime(unixTimeToStdTime(statbuf.st_mtime)); + statTimeToStdTime!'m'(statbuf); } } -unittest +@safe unittest { //std.process.system("echo a > deleteme") == 0 || assert(false); - if(exists(deleteme)) + if (exists(deleteme)) remove(deleteme); write(deleteme, "a\n"); @@ -958,16 +1472,63 @@ unittest } -/++ - Returns whether the given file (or directory) exists. - +/ -bool exists(in char[] name) @trusted nothrow @nogc +// Tests sub-second precision of querying file times. +// Should pass on most modern systems running on modern filesystems. +// Exceptions: +// - FreeBSD, where one would need to first set the +// vfs.timestamp_precision sysctl to a value greater than zero. +// - OS X, where the native filesystem (HFS+) stores filesystem +// timestamps with 1-second precision. +version (FreeBSD) {} else +version (OSX) {} else +@system unittest +{ + import core.thread; + + if (exists(deleteme)) + remove(deleteme); + + SysTime lastTime; + foreach (n; 0 .. 3) + { + write(deleteme, "a"); + auto time = timeLastModified(deleteme); + remove(deleteme); + assert(time != lastTime); + lastTime = time; + Thread.sleep(10.msecs); + } +} + + +/** + * Determine whether the given file (or directory) exists. + * Params: + * name = string or range of characters representing the file name + * Returns: + * true if the filename specified as input exists + */ +bool exists(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) +{ + return existsImpl(name.tempCString!FSChar()); +} + +/// ditto +bool exists(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return exists!(StringTypeOf!R)(name); +} + +private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc { version(Windows) { -// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ -// fileio/base/getfileattributes.asp - return GetFileAttributesW(name.tempCStringW()) != 0xFFFFFFFF; + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ + // fileio/base/getfileattributes.asp + return GetFileAttributesW(namez) != 0xFFFFFFFF; } else version(Posix) { @@ -992,8 +1553,10 @@ bool exists(in char[] name) @trusted nothrow @nogc */ stat_t statbuf = void; - return lstat(name.tempCString(), &statbuf) == 0; + return lstat(namez, &statbuf) == 0; } + else + static assert(0); } @safe unittest @@ -1005,16 +1568,21 @@ bool exists(in char[] name) @trusted nothrow @nogc assert(exists(deleteme)); } +@safe unittest // Bugzilla 16573 +{ + enum S : string { foo = "foo" } + assert(__traits(compiles, S.foo.exists)); +} /++ Returns the attributes of the given file. Note that the file attributes on Windows and Posix systems are - completely different. On Windows, they're what is returned by $(WEB - msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, + completely different. On Windows, they're what is returned by + $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes), whereas on Posix systems, they're the $(LUCKY st_mode) value which is part of the $(D stat struct) gotten by - calling the $(WEB en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat)) + calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat)) function. On Posix systems, if the given file is a symbolic link, then @@ -1026,34 +1594,57 @@ bool exists(in char[] name) @trusted nothrow @nogc Throws: $(D FileException) on error. +/ -uint getAttributes(in char[] name) @safe +uint getAttributes(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { - static auto trustedGetFileAttributesW(in char[] fileName) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedGetFileAttributesW(const(FSChar)* namez) @trusted { - return GetFileAttributesW(fileName.tempCStringW()); + return GetFileAttributesW(namez); } - immutable result = trustedGetFileAttributesW(name); + immutable result = trustedGetFileAttributesW(namez); - cenforce(result != INVALID_FILE_ATTRIBUTES, name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez); return result; } else version(Posix) { - static auto trustedStat(in char[] path, ref stat_t buf) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted { - return stat(path.tempCString(), &buf); + return stat(namez, &buf); } stat_t statbuf = void; - cenforce(trustedStat(name, statbuf) == 0, name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedStat(namez, statbuf) == 0, names, namez); return statbuf.st_mode; } } +/// ditto +uint getAttributes(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return getAttributes!(StringTypeOf!R)(name); +} + +@safe unittest +{ + static assert(__traits(compiles, getAttributes(TestAliasedString(null)))); +} /++ If the given file is a symbolic link, then this returns the attributes of the @@ -1068,10 +1659,15 @@ uint getAttributes(in char[] name) @safe Params: name = The file to get the symbolic link attributes of. + Returns: + the attributes + Throws: $(D FileException) on error. +/ -uint getLinkAttributes(in char[] name) @safe +uint getLinkAttributes(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { @@ -1079,44 +1675,87 @@ uint getLinkAttributes(in char[] name) @safe } else version(Posix) { - static auto trustedLstat(in char[] path, ref stat_t buf) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted { - return lstat(path.tempCString(), &buf); + return lstat(namez, &buf); } stat_t lstatbuf = void; - cenforce(trustedLstat(name, lstatbuf) == 0, name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez); return lstatbuf.st_mode; } } +/// ditto +uint getLinkAttributes(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return getLinkAttributes!(StringTypeOf!R)(name); +} + +@safe unittest +{ + static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null)))); +} /++ Set the attributes of the given file. + Params: + name = the file name + attributes = the attributes to set the file to + Throws: $(D FileException) if the given file does not exist. +/ -void setAttributes(in char[] name, uint attributes) @safe +void setAttributes(R)(R name, uint attributes) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version (Windows) { - static auto trustedSetFileAttributesW(in char[] fileName, uint dwFileAttributes) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedSetFileAttributesW(const(FSChar)* namez, uint dwFileAttributes) @trusted { - return SetFileAttributesW(fileName.tempCStringW(), dwFileAttributes); + return SetFileAttributesW(namez, dwFileAttributes); } - cenforce(trustedSetFileAttributesW(name, attributes), name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(trustedSetFileAttributesW(namez, attributes), names, namez); } else version (Posix) { - static auto trustedChmod(in char[] path, mode_t mode) @trusted + auto namez = name.tempCString!FSChar(); + static auto trustedChmod(const(FSChar)* namez, mode_t mode) @trusted { - return chmod(path.tempCString(), mode); + return chmod(namez, mode); } assert(attributes <= mode_t.max); - cenforce(!trustedChmod(name, cast(mode_t)attributes), name); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias names = name; + else + string names = null; + cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez); } } +/// ditto +void setAttributes(R)(auto ref R name, uint attributes) +if (isConvertibleToString!R) +{ + return setAttributes!(StringTypeOf!R)(name, attributes); +} + +@safe unittest +{ + static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0))); +} /++ Returns whether the given file is a directory. @@ -1124,16 +1763,21 @@ void setAttributes(in char[] name, uint attributes) @safe Params: name = The path to the file. + Returns: + true if the name specifies a directory + Throws: $(D FileException) if the given file does not exist. -Examples: +Example: -------------------- assert(!"/etc/fonts/fonts.conf".isDir); assert("/usr/share/include".isDir); -------------------- +/ -@property bool isDir(in char[] name) @safe +@property bool isDir(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) { @@ -1145,26 +1789,52 @@ assert("/usr/share/include".isDir); } } +/// ditto +@property bool isDir(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return name.isDir!(StringTypeOf!R); +} + +@safe unittest +{ + static assert(__traits(compiles, TestAliasedString(null).isDir)); +} + @safe unittest { version(Windows) { - if("C:\\Program Files\\".exists) + if ("C:\\Program Files\\".exists) assert("C:\\Program Files\\".isDir); - if("C:\\Windows\\system.ini".exists) + if ("C:\\Windows\\system.ini".exists) assert(!"C:\\Windows\\system.ini".isDir); } else version(Posix) { - if(system_directory.exists) + if (system_directory.exists) assert(system_directory.isDir); - if(system_file.exists) + if (system_file.exists) assert(!system_file.isDir); } } +@system unittest +{ + version(Windows) + enum dir = "C:\\Program Files\\"; + else version(Posix) + enum dir = system_directory; + + if (dir.exists) + { + DirEntry de = DirEntry(dir); + assert(de.isDir); + assert(DirEntry(dir).isDir); + } +} /++ Returns whether the given file attributes are for a directory. @@ -1172,7 +1842,10 @@ assert("/usr/share/include".isDir); Params: attributes = The file attributes. -Examples: + Returns: + true if attibutes specifies a directory + +Example: -------------------- assert(!attrIsDir(getAttributes("/etc/fonts/fonts.conf"))); assert(!attrIsDir(getLinkAttributes("/etc/fonts/fonts.conf"))); @@ -1194,13 +1867,13 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc { version(Windows) { - if("C:\\Program Files\\".exists) + if ("C:\\Program Files\\".exists) { assert(attrIsDir(getAttributes("C:\\Program Files\\"))); assert(attrIsDir(getLinkAttributes("C:\\Program Files\\"))); } - if("C:\\Windows\\system.ini".exists) + if ("C:\\Windows\\system.ini".exists) { assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini"))); assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini"))); @@ -1208,13 +1881,13 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc } else version(Posix) { - if(system_directory.exists) + if (system_directory.exists) { assert(attrIsDir(getAttributes(system_directory))); assert(attrIsDir(getLinkAttributes(system_directory))); } - if(system_file.exists) + if (system_file.exists) { assert(!attrIsDir(getAttributes(system_file))); assert(!attrIsDir(getLinkAttributes(system_file))); @@ -1241,16 +1914,21 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc Params: name = The path to the file. + Returns: + true if name specifies a file + Throws: $(D FileException) if the given file does not exist. -Examples: +Example: -------------------- assert("/etc/fonts/fonts.conf".isFile); assert(!"/usr/share/include".isFile); -------------------- +/ -@property bool isFile(in char[] name) @safe +@property bool isFile(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) return !name.isDir; @@ -1258,22 +1936,40 @@ assert(!"/usr/share/include".isFile); return (getAttributes(name) & S_IFMT) == S_IFREG; } +/// ditto +@property bool isFile(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return isFile!(StringTypeOf!R)(name); +} + +@system unittest // bugzilla 15658 +{ + DirEntry e = DirEntry("."); + static assert(is(typeof(isFile(e)))); +} + +@safe unittest +{ + static assert(__traits(compiles, TestAliasedString(null).isFile)); +} + @safe unittest { version(Windows) { - if("C:\\Program Files\\".exists) + if ("C:\\Program Files\\".exists) assert(!"C:\\Program Files\\".isFile); - if("C:\\Windows\\system.ini".exists) + if ("C:\\Windows\\system.ini".exists) assert("C:\\Windows\\system.ini".isFile); } else version(Posix) { - if(system_directory.exists) + if (system_directory.exists) assert(!system_directory.isFile); - if(system_file.exists) + if (system_file.exists) assert(system_file.isFile); } } @@ -1296,7 +1992,10 @@ assert(!"/usr/share/include".isFile); Params: attributes = The file attributes. -Examples: + Returns: + true if the given file attributes are for a file + +Example: -------------------- assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf"))); assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf"))); @@ -1318,13 +2017,13 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc { version(Windows) { - if("C:\\Program Files\\".exists) + if ("C:\\Program Files\\".exists) { assert(!attrIsFile(getAttributes("C:\\Program Files\\"))); assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\"))); } - if("C:\\Windows\\system.ini".exists) + if ("C:\\Windows\\system.ini".exists) { assert(attrIsFile(getAttributes("C:\\Windows\\system.ini"))); assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini"))); @@ -1332,13 +2031,13 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc } else version(Posix) { - if(system_directory.exists) + if (system_directory.exists) { assert(!attrIsFile(getAttributes(system_directory))); assert(!attrIsFile(getLinkAttributes(system_directory))); } - if(system_file.exists) + if (system_file.exists) { assert(attrIsFile(getAttributes(system_file))); assert(attrIsFile(getLinkAttributes(system_file))); @@ -1356,10 +2055,15 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc Params: name = The path to the file. + Returns: + true if name is a symbolic link + Throws: $(D FileException) if the given file does not exist. +/ -@property bool isSymlink(in char[] name) @safe +@property bool isSymlink(R)(R name) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { version(Windows) return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0; @@ -1367,18 +2071,30 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc return (getLinkAttributes(name) & S_IFMT) == S_IFLNK; } -unittest +/// ditto +@property bool isSymlink(R)(auto ref R name) +if (isConvertibleToString!R) +{ + return name.isSymlink!(StringTypeOf!R); +} + +@safe unittest +{ + static assert(__traits(compiles, TestAliasedString(null).isSymlink)); +} + +@system unittest { version(Windows) { - if("C:\\Program Files\\".exists) + if ("C:\\Program Files\\".exists) assert(!"C:\\Program Files\\".isSymlink); - if("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists) + if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists) assert("C:\\Documents and Settings\\".isSymlink); enum fakeSymFile = "C:\\Windows\\system.ini"; - if(fakeSymFile.exists) + if (fakeSymFile.exists) { assert(!fakeSymFile.isSymlink); @@ -1396,12 +2112,12 @@ unittest } else version(Posix) { - if(system_directory.exists) + if (system_directory.exists) { assert(!system_directory.isSymlink); immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if(symfile.exists) symfile.remove(); + scope(exit) if (symfile.exists) symfile.remove(); core.sys.posix.unistd.symlink(system_directory, symfile.ptr); @@ -1416,12 +2132,12 @@ unittest assert(!attrIsFile(getLinkAttributes(symfile))); } - if(system_file.exists) + if (system_file.exists) { assert(!system_file.isSymlink); immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if(symfile.exists) symfile.remove(); + scope(exit) if (symfile.exists) symfile.remove(); core.sys.posix.unistd.symlink(system_file, symfile.ptr); @@ -1450,7 +2166,10 @@ unittest Params: attributes = The file attributes. -Examples: + Returns: + true if attributes are for a symbolic link + +Example: -------------------- core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink"); @@ -1471,24 +2190,44 @@ bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc * Change directory to $(D pathname). * Throws: $(D FileException) on error. */ -void chdir(in char[] pathname) @safe +void chdir(R)(R pathname) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { + // Place outside of @trusted block + auto pathz = pathname.tempCString!FSChar(); + version(Windows) { - static auto trustedSetCurrentDirectoryW(in char[] path) @trusted + static auto trustedChdir(const(FSChar)* pathz) @trusted { - return SetCurrentDirectoryW(path.tempCStringW()); + return SetCurrentDirectoryW(pathz); } - cenforce(trustedSetCurrentDirectoryW(pathname), pathname); } else version(Posix) { - static auto trustedChdir(in char[] path) @trusted + static auto trustedChdir(const(FSChar)* pathz) @trusted { - return core.sys.posix.unistd.chdir(path.tempCString()); + return core.sys.posix.unistd.chdir(pathz) == 0; } - cenforce(trustedChdir(pathname) == 0, pathname); } + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias pathStr = pathname; + else + string pathStr = null; + cenforce(trustedChdir(pathz), pathStr, pathz); +} + +/// ditto +void chdir(R)(auto ref R pathname) +if (isConvertibleToString!R) +{ + return chdir!(StringTypeOf!R)(pathname); +} + +@safe unittest +{ + static assert(__traits(compiles, chdir(TestAliasedString(null)))); } /**************************************************** @@ -1497,42 +2236,75 @@ Make directory $(D pathname). Throws: $(D FileException) on Posix or $(D WindowsException) on Windows if an error occured. */ -void mkdir(in char[] pathname) @safe +void mkdir(R)(R pathname) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { + // Place outside of @trusted block + const pathz = pathname.tempCString!FSChar(); + version(Windows) { - static auto trustedCreateDirectoryW(in char[] path) @trusted + static auto trustedCreateDirectoryW(const(FSChar)* pathz) @trusted { - return CreateDirectoryW(path.tempCStringW(), null); + return CreateDirectoryW(pathz, null); } - wenforce(trustedCreateDirectoryW(pathname), pathname); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias pathStr = pathname; + else + string pathStr = null; + wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz); } else version(Posix) { - static auto trustedMkdir(in char[] path, mode_t mode) @trusted + import std.conv : octal; + + static auto trustedMkdir(const(FSChar)* pathz, mode_t mode) @trusted { - return core.sys.posix.sys.stat.mkdir(path.tempCString(), mode); + return core.sys.posix.sys.stat.mkdir(pathz, mode); } - cenforce(trustedMkdir(pathname, octal!777) == 0, pathname); + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias pathStr = pathname; + else + string pathStr = null; + cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz); } } +/// ditto +void mkdir(R)(auto ref R pathname) +if (isConvertibleToString!R) +{ + return mkdir!(StringTypeOf!R)(pathname); +} + +@safe unittest +{ + import std.path : mkdir; + static assert(__traits(compiles, mkdir(TestAliasedString(null)))); +} + // Same as mkdir but ignores "already exists" errors. // Returns: "true" if the directory was created, // "false" if it already existed. -private bool ensureDirExists(in char[] pathname) +private bool ensureDirExists()(in char[] pathname) { + import std.exception : enforce; + const pathz = pathname.tempCString!FSChar(); + version(Windows) { - if (CreateDirectoryW(pathname.tempCStringW(), null)) + if (() @trusted { return CreateDirectoryW(pathz, null); }()) return true; cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup); } else version(Posix) { - if (core.sys.posix.sys.stat.mkdir(pathname.tempCString(), octal!777) == 0) + import std.conv : octal; + + if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0) return true; - cenforce(errno == EEXIST, pathname); + cenforce(errno == EEXIST || errno == EISDIR, pathname); } enforce(pathname.isDir, new FileException(pathname.idup)); return false; @@ -1541,11 +2313,16 @@ private bool ensureDirExists(in char[] pathname) /**************************************************** * Make directory and all parent directories as needed. * + * Does nothing if the directory specified by + * $(D pathname) already exists. + * * Throws: $(D FileException) on error. */ -void mkdirRecurse(in char[] pathname) +void mkdirRecurse(in char[] pathname) @safe { + import std.path : dirName, baseName; + const left = dirName(pathname); if (left.length != pathname.length && !exists(left)) { @@ -1557,11 +2334,14 @@ void mkdirRecurse(in char[] pathname) } } -unittest +@safe unittest { + import std.exception : assertThrown; { + import std.path : buildPath, buildNormalizedPath; + immutable basepath = deleteme ~ "_dir"; - scope(exit) rmdirRecurse(basepath); + scope(exit) () @trusted { rmdirRecurse(basepath); }(); auto path = buildPath(basepath, "a", "..", "b"); mkdirRecurse(path); @@ -1596,7 +2376,7 @@ unittest mkdirRecurse(path); assert(basepath.exists && basepath.isDir); - scope(exit) rmdirRecurse(basepath); + scope(exit) () @trusted { rmdirRecurse(basepath); }(); assert(path.exists && path.isDir); } } @@ -1604,56 +2384,101 @@ unittest /**************************************************** Remove directory $(D pathname). +Params: + pathname = Range or string specifying the directory name + Throws: $(D FileException) on error. */ -void rmdir(in char[] pathname) +void rmdir(R)(R pathname) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && + !isConvertibleToString!R) { + // Place outside of @trusted block + auto pathz = pathname.tempCString!FSChar(); + version(Windows) { - cenforce(RemoveDirectoryW(pathname.tempCStringW()), - pathname); + static auto trustedRmdir(const(FSChar)* pathz) @trusted + { + return RemoveDirectoryW(pathz); + } } else version(Posix) { - cenforce(core.sys.posix.unistd.rmdir(pathname.tempCString()) == 0, - pathname); + static auto trustedRmdir(const(FSChar)* pathz) @trusted + { + return core.sys.posix.unistd.rmdir(pathz) == 0; + } } + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias pathStr = pathname; + else + string pathStr = null; + cenforce(trustedRmdir(pathz), pathStr, pathz); +} + +/// ditto +void rmdir(R)(auto ref R pathname) +if (isConvertibleToString!R) +{ + rmdir!(StringTypeOf!R)(pathname); +} + +@safe unittest +{ + static assert(__traits(compiles, rmdir(TestAliasedString(null)))); } /++ $(BLUE This function is Posix-Only.) - Creates a symlink. + Creates a symbolic _link (_symlink). Params: - original = The file to link from. - link = The symlink to create. - - Note: - Relative paths are relative to the current working directory, - not the files being linked to or from. + original = The file that is being linked. This is the target path that's + stored in the _symlink. A relative path is relative to the created + _symlink. + link = The _symlink to create. A relative path is relative to the + current working directory. Throws: - $(D FileException) on error (which includes if the symlink already + $(D FileException) on error (which includes if the _symlink already exists). +/ -version(StdDdoc) void symlink(C1, C2)(const(C1)[] original, const(C2)[] link) @safe; -else version(Posix) void symlink(C1, C2)(const(C1)[] original, const(C2)[] link) @safe +version(StdDdoc) void symlink(RO, RL)(RO original, RL link) +if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) || + isConvertibleToString!RO) && + (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) || + isConvertibleToString!RL)); +else version(Posix) void symlink(RO, RL)(RO original, RL link) +if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) || + isConvertibleToString!RO) && + (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) || + isConvertibleToString!RL)) { - static auto trustedSymlink(const(C1)[] path1, const(C2)[] path2) @trusted + static if (isConvertibleToString!RO || isConvertibleToString!RL) { - return core.sys.posix.unistd.symlink(path1.tempCString(), - path2.tempCString()); + import std.meta : staticMap; + alias Types = staticMap!(convertToString, RO, RL); + symlink!Types(original, link); + } + else + { + import std.conv : text; + auto oz = original.tempCString(); + auto lz = link.tempCString(); + alias posixSymlink = core.sys.posix.unistd.symlink; + immutable int result = () @trusted { return posixSymlink(oz, lz); } (); + cenforce(result == 0, text(link)); } - cenforce(trustedSymlink(original, link) == 0, link); } version(Posix) @safe unittest { - if(system_directory.exists) + if (system_directory.exists) { immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if(symfile.exists) symfile.remove(); + scope(exit) if (symfile.exists) symfile.remove(); symlink(system_directory, symfile); @@ -1669,12 +2494,12 @@ version(Posix) @safe unittest assert(!attrIsFile(getLinkAttributes(symfile))); } - if(system_file.exists) + if (system_file.exists) { assert(!system_file.isSymlink); immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if(symfile.exists) symfile.remove(); + scope(exit) if (symfile.exists) symfile.remove(); symlink(system_file, symfile); @@ -1691,6 +2516,12 @@ version(Posix) @safe unittest } } +version(Posix) @safe unittest +{ + static assert(__traits(compiles, + symlink(TestAliasedString(null), TestAliasedString(null)))); +} + /++ $(BLUE This function is Posix-Only.) @@ -1703,55 +2534,70 @@ version(Posix) @safe unittest Throws: $(D FileException) on error. +/ -version(StdDdoc) string readLink(C)(const(C)[] link) @safe; -else version(Posix) string readLink(C)(const(C)[] link) @safe +version(StdDdoc) string readLink(R)(R link) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || + isConvertibleToString!R); +else version(Posix) string readLink(R)(R link) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || + isConvertibleToString!R) { - static auto trustedReadlink(const(C)[] path, char[] buf) @trusted + static if (isConvertibleToString!R) { - return core.sys.posix.unistd.readlink(path.tempCString(), buf.ptr, buf.length); + return readLink!(convertToString!R)(link); } - static auto trustedAssumeUnique(ref C[] array) @trusted + else { - return assumeUnique(array); - } - - enum bufferLen = 2048; - enum maxCodeUnits = 6; - char[bufferLen] buffer; - auto size = trustedReadlink(link, buffer); - cenforce(size != -1, link); - - if(size <= bufferLen - maxCodeUnits) - return to!string(buffer[0 .. size]); - - auto dynamicBuffer = new char[](bufferLen * 3 / 2); + import std.conv : to; + import std.exception : assumeUnique; + alias posixReadlink = core.sys.posix.unistd.readlink; + enum bufferLen = 2048; + enum maxCodeUnits = 6; + char[bufferLen] buffer; + const linkz = link.tempCString(); + auto size = () @trusted { + return posixReadlink(linkz, buffer.ptr, buffer.length); + } (); + cenforce(size != -1, to!string(link)); + + if (size <= bufferLen - maxCodeUnits) + return to!string(buffer[0 .. size]); + + auto dynamicBuffer = new char[](bufferLen * 3 / 2); + + foreach (i; 0 .. 10) + { + size = () @trusted { + return posixReadlink(linkz, dynamicBuffer.ptr, + dynamicBuffer.length); + } (); + cenforce(size != -1, to!string(link)); - foreach(i; 0 .. 10) - { - size = trustedReadlink(link, dynamicBuffer); - cenforce(size != -1, link); + if (size <= dynamicBuffer.length - maxCodeUnits) + { + dynamicBuffer.length = size; + return () @trusted { + return assumeUnique(dynamicBuffer); + } (); + } - if(size <= dynamicBuffer.length - maxCodeUnits) - { - dynamicBuffer.length = size; - return trustedAssumeUnique(dynamicBuffer); + dynamicBuffer.length = dynamicBuffer.length * 3 / 2; } - dynamicBuffer.length = dynamicBuffer.length * 3 / 2; + throw new FileException(to!string(link), "Path is too long to read."); } - - throw new FileException(to!string(link), "Path is too long to read."); } version(Posix) @safe unittest { + import std.exception : assertThrown; import std.string; - foreach(file; [system_directory, system_file]) + + foreach (file; [system_directory, system_file]) { - if(file.exists) + if (file.exists) { immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if(symfile.exists) symfile.remove(); + scope(exit) if (symfile.exists) symfile.remove(); symlink(file, symfile); assert(readLink(symfile) == file, format("Failed file: %s", file)); @@ -1761,6 +2607,27 @@ version(Posix) @safe unittest assertThrown!FileException(readLink("/doesnotexist")); } +version(Posix) @safe unittest +{ + static assert(__traits(compiles, readLink(TestAliasedString("foo")))); +} + +version(Posix) @system unittest // input range of dchars +{ + mkdirRecurse(deleteme); + scope(exit) if (deleteme.exists) rmdirRecurse(deleteme); + write(deleteme ~ "/f", ""); + import std.range.interfaces : InputRange, inputRangeObject; + import std.utf : byChar; + immutable string link = deleteme ~ "/l"; + symlink("f", link); + InputRange!dchar linkr = inputRangeObject(link); + alias R = typeof(linkr); + static assert(isInputRange!R); + static assert(!isForwardRange!R); + assert(readLink(linkr) == "f"); +} + /**************************************************** * Get the current working directory. @@ -1768,6 +2635,7 @@ version(Posix) @safe unittest */ version(Windows) string getcwd() { + import std.conv : to; import std.utf : toUTF8; /* GetCurrentDirectory's return value: 1. function succeeds: the number of characters that are written to @@ -1780,7 +2648,7 @@ version(Windows) string getcwd() immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr), "getcwd"); // we can do it because toUTFX always produces a fresh string - if(n < buffW.length) + if (n < buffW.length) { return toUTF8(buffW[0 .. n]); } @@ -1793,6 +2661,16 @@ version(Windows) string getcwd() return toUTF8(ptr[0 .. n2]); } } +else version (Solaris) string getcwd() +{ + /* BUF_SIZE >= PATH_MAX */ + enum BUF_SIZE = 4096; + /* The user should be able to specify any size buffer > 0 */ + auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE), + "cannot get cwd"); + scope(exit) core.stdc.stdlib.free(p); + return p[0 .. core.stdc.string.strlen(p)].idup; +} else version (Posix) string getcwd() { auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0), @@ -1801,7 +2679,7 @@ else version (Posix) string getcwd() return p[0 .. core.stdc.string.strlen(p)].idup; } -unittest +@system unittest { auto s = getcwd(); assert(s.length); @@ -1812,18 +2690,23 @@ version (OSX) else version (FreeBSD) private extern (C) int sysctl (const int* name, uint namelen, void* oldp, size_t* oldlenp, const void* newp, size_t newlen); +else version (NetBSD) + private extern (C) int sysctl (const int* name, uint namelen, void* oldp, + size_t* oldlenp, const void* newp, size_t newlen); /** * Returns the full path of the current executable. * * Throws: - * $(XREF object, Exception) + * $(REF Exception, std,object) */ @trusted string thisExePath () { version (OSX) { import core.sys.posix.stdlib : realpath; + import std.conv : to; + import std.exception : errnoEnforce; uint size; @@ -1848,6 +2731,9 @@ else version (FreeBSD) } else version (Windows) { + import std.conv : to; + import std.exception : enforce; + wchar[MAX_PATH] buf; wchar[] buffer = buf[]; @@ -1862,6 +2748,7 @@ else version (FreeBSD) } else version (FreeBSD) { + import std.exception : errnoEnforce, assumeUnique; enum { CTL_KERN = 1, @@ -1881,6 +2768,10 @@ else version (FreeBSD) return buffer.assumeUnique; } + else version (NetBSD) + { + return readLink("/proc/self/exe"); + } else version (Solaris) { import core.sys.posix.unistd : getpid; @@ -1889,16 +2780,13 @@ else version (FreeBSD) // Only Solaris 10 and later return readLink(format("/proc/%d/path/a.out", getpid())); } - else version (Android) - { - return readLink("/proc/self/exe"); - } else static assert(0, "thisExePath is not supported on this platform"); } @safe unittest { + import std.path : isAbsolute; auto path = thisExePath(); assert(path.exists); @@ -1936,7 +2824,7 @@ version(StdDdoc) /++ Returns the path to the file represented by this $(D DirEntry). -Examples: +Example: -------------------- auto de1 = DirEntry("/etc/fonts/fonts.conf"); assert(de1.name == "/etc/fonts/fonts.conf"); @@ -1952,7 +2840,7 @@ assert(de2.name == "/usr/share/include"); Returns whether the file represented by this $(D DirEntry) is a directory. -Examples: +Example: -------------------- auto de1 = DirEntry("/etc/fonts/fonts.conf"); assert(!de1.isDir); @@ -1978,7 +2866,7 @@ assert(de2.isDir); information about a special file (see the stat man page for more details). -Examples: +Example: -------------------- auto de1 = DirEntry("/etc/fonts/fonts.conf"); assert(de1.isFile); @@ -2035,7 +2923,7 @@ assert(!de2.isFile); Note that the file attributes on Windows and Posix systems are completely different. On, Windows, they're what is returned by $(D GetFileAttributes) - $(WEB msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes) + $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes) Whereas, an Posix systems, they're the $(D st_mode) value which is part of the $(D stat) struct gotten by calling $(D stat). @@ -2078,7 +2966,7 @@ else version(Windows) this(string path) { - if(!path.exists()) + if (!path.exists()) throw new FileException(path, "File does not exist"); _name = path; @@ -2096,11 +2984,12 @@ else version(Windows) private this(string path, in WIN32_FIND_DATAW *fd) { import core.stdc.wchar_ : wcslen; + import std.path : buildPath; size_t clength = wcslen(fd.cFileName.ptr); _name = toUTF8(fd.cFileName[0 .. clength]); _name = buildPath(path, toUTF8(fd.cFileName[0 .. clength])); - _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow; + _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow; _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime); _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime); _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime); @@ -2180,7 +3069,7 @@ else version(Posix) this(string path) { - if(!path.exists) + if (!path.exists) throw new FileException(path, "File does not exist"); _name = path; @@ -2192,6 +3081,8 @@ else version(Posix) private this(string path, core.sys.posix.dirent.dirent* fd) { + import std.path : buildPath; + immutable len = core.stdc.string.strlen(fd.d_name.ptr); _name = buildPath(path, fd.d_name[0 .. len]); @@ -2208,7 +3099,7 @@ else version(Posix) //cost of calling lstat). static if (__traits(compiles, fd.d_type != DT_UNKNOWN)) { - if(fd.d_type != DT_UNKNOWN) + if (fd.d_type != DT_UNKNOWN) { _dType = fd.d_type; _dTypeSet = true; @@ -2259,21 +3150,21 @@ else version(Posix) { _ensureStatDone(); - return SysTime(unixTimeToStdTime(_statBuf.st_ctime)); + return statTimeToStdTime!'c'(_statBuf); } @property SysTime timeLastAccessed() { _ensureStatDone(); - return SysTime(unixTimeToStdTime(_statBuf.st_ctime)); + return statTimeToStdTime!'a'(_statBuf); } @property SysTime timeLastModified() { _ensureStatDone(); - return SysTime(unixTimeToStdTime(_statBuf.st_mtime)); + return statTimeToStdTime!'m'(_statBuf); } @property uint attributes() @@ -2304,11 +3195,13 @@ else version(Posix) +/ void _ensureStatDone() @safe { + import std.exception : enforce; + static auto trustedStat(in char[] path, stat_t* buf) @trusted { return stat(path.tempCString(), buf); } - if(_didStat) + if (_didStat) return; enforce(trustedStat(_name, &_statBuf) == 0, @@ -2326,10 +3219,10 @@ else version(Posix) +/ void _ensureStatOrLStatDone() { - if(_didStat) + if (_didStat) return; - if( stat(_name.tempCString(), &_statBuf) != 0 ) + if ( stat(_name.tempCString(), &_statBuf) != 0 ) { _ensureLStatDone(); @@ -2348,7 +3241,9 @@ else version(Posix) +/ void _ensureLStatDone() { - if(_didLStat) + import std.exception : enforce; + + if (_didLStat) return; stat_t statbuf = void; @@ -2374,11 +3269,11 @@ else version(Posix) } } -unittest +@system unittest { version(Windows) { - if("C:\\Program Files\\".exists) + if ("C:\\Program Files\\".exists) { auto de = DirEntry("C:\\Program Files\\"); assert(!de.isFile); @@ -2386,13 +3281,13 @@ unittest assert(!de.isSymlink); } - if("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists) + if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists) { auto de = DirEntry("C:\\Documents and Settings\\"); assert(de.isSymlink); } - if("C:\\Windows\\system.ini".exists) + if ("C:\\Windows\\system.ini".exists) { auto de = DirEntry("C:\\Windows\\system.ini"); assert(de.isFile); @@ -2402,7 +3297,9 @@ unittest } else version(Posix) { - if(system_directory.exists) + import std.exception : assertThrown; + + if (system_directory.exists) { { auto de = DirEntry(system_directory); @@ -2412,7 +3309,7 @@ unittest } immutable symfile = deleteme ~ "_slink\0"; - scope(exit) if(symfile.exists) symfile.remove(); + scope(exit) if (symfile.exists) symfile.remove(); core.sys.posix.unistd.symlink(system_directory, symfile.ptr); @@ -2444,7 +3341,7 @@ unittest } } - if(system_file.exists) + if (system_file.exists) { auto de = DirEntry(system_file); assert(de.isFile); @@ -2458,102 +3355,172 @@ alias PreserveAttributes = Flag!"preserveAttributes"; version (StdDdoc) { - /// Defaults to PreserveAttributes.yes on Windows, and the opposite on all other platforms. + /// Defaults to $(D Yes.preserveAttributes) on Windows, and the opposite on all other platforms. PreserveAttributes preserveAttributesDefault; } else version(Windows) { - enum preserveAttributesDefault = PreserveAttributes.yes; + enum preserveAttributesDefault = Yes.preserveAttributes; } else { - enum preserveAttributesDefault = PreserveAttributes.no; + enum preserveAttributesDefault = No.preserveAttributes; } /*************************************************** Copy file $(D from) to file $(D to). File timestamps are preserved. -File attributes are preserved, if $(D preserve) equals $(D PreserveAttributes.yes). -On Windows only $(D PreserveAttributes.yes) (the default on Windows) is supported. +File attributes are preserved, if $(D preserve) equals $(D Yes.preserveAttributes). +On Windows only $(D Yes.preserveAttributes) (the default on Windows) is supported. If the target file exists, it is overwritten. +Params: + from = string or range of characters representing the existing file name + to = string or range of characters representing the target file name + preserve = whether to preserve the file attributes + Throws: $(D FileException) on error. */ -void copy(in char[] from, in char[] to, PreserveAttributes preserve = preserveAttributesDefault) +void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault) +if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF && + isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) && !isConvertibleToString!RT) +{ + // Place outside of @trusted block + auto fromz = from.tempCString!FSChar(); + auto toz = to.tempCString!FSChar(); + + static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char)) + alias f = from; + else + enum string f = null; + + static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char)) + alias t = to; + else + enum string t = null; + + copyImpl(f, t, fromz, toz, preserve); +} + +/// ditto +void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault) +if (isConvertibleToString!RF || isConvertibleToString!RT) +{ + import std.meta : staticMap; + alias Types = staticMap!(convertToString, RF, RT); + copy!Types(from, to, preserve); +} + +@safe unittest // issue 15319 +{ + assert(__traits(compiles, copy("from.txt", "to.txt"))); +} + +private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz, + PreserveAttributes preserve) @trusted { version(Windows) { assert(preserve == Yes.preserve); - immutable result = CopyFileW(from.tempCStringW(), to.tempCStringW(), false); + immutable result = CopyFileW(fromz, toz, false); if (!result) - throw new FileException(to.idup); + { + import core.stdc.wchar_ : wcslen; + import std.conv : to; + + if (!t) + t = to!(typeof(t))(toz[0 .. wcslen(toz)]); + + throw new FileException(t); + } } else version(Posix) { - import core.stdc.stdio; + static import core.stdc.stdio; + import std.conv : to, octal; - immutable fd = core.sys.posix.fcntl.open(from.tempCString(), O_RDONLY); - cenforce(fd != -1, from); - scope(exit) core.sys.posix.unistd.close(fd); + immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY); + cenforce(fdr != -1, f, fromz); + scope(exit) core.sys.posix.unistd.close(fdr); - stat_t statbuf = void; - cenforce(fstat(fd, &statbuf) == 0, from); - //cenforce(core.sys.posix.sys.stat.fstat(fd, &statbuf) == 0, from); + stat_t statbufr = void; + cenforce(fstat(fdr, &statbufr) == 0, f, fromz); + //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz); + + immutable fdw = core.sys.posix.fcntl.open(toz, + O_CREAT | O_WRONLY, octal!666); + cenforce(fdw != -1, t, toz); + { + scope(failure) core.sys.posix.unistd.close(fdw); + + stat_t statbufw = void; + cenforce(fstat(fdw, &statbufw) == 0, t, toz); + if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino) + throw new FileException(t, "Source and destination are the same file"); + } - auto tozTmp = to.tempCString(); - immutable fdw = core.sys.posix.fcntl.open(tozTmp, - O_CREAT | O_WRONLY | O_TRUNC, octal!666); - cenforce(fdw != -1, from); - scope(failure) core.stdc.stdio.remove(tozTmp); + scope(failure) core.stdc.stdio.remove(toz); { scope(failure) core.sys.posix.unistd.close(fdw); + cenforce(ftruncate(fdw, 0) == 0, t, toz); + auto BUFSIZ = 4096u * 16; auto buf = core.stdc.stdlib.malloc(BUFSIZ); if (!buf) { BUFSIZ = 4096; buf = core.stdc.stdlib.malloc(BUFSIZ); - buf || assert(false, "Out of memory in std.file.copy"); + if (!buf) + { + import core.exception : onOutOfMemoryError; + onOutOfMemoryError(); + } } scope(exit) core.stdc.stdlib.free(buf); - for (auto size = statbuf.st_size; size; ) + for (auto size = statbufr.st_size; size; ) { immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size; cenforce( - core.sys.posix.unistd.read(fd, buf, toxfer) == toxfer + core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer, - from); + f, fromz); assert(size >= toxfer); size -= toxfer; } if (preserve) - cenforce(fchmod(fdw, statbuf.st_mode) == 0, from); + cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz); } - cenforce(core.sys.posix.unistd.close(fdw) != -1, from); + cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz); utimbuf utim = void; - utim.actime = cast(time_t)statbuf.st_atime; - utim.modtime = cast(time_t)statbuf.st_mtime; + utim.actime = cast(time_t) statbufr.st_atime; + utim.modtime = cast(time_t) statbufr.st_mtime; - cenforce(utime(tozTmp, &utim) != -1, from); + cenforce(utime(toz, &utim) != -1, f, fromz); } } -unittest +@safe unittest { + import std.algorithm, std.file; // issue 14817 auto t1 = deleteme, t2 = deleteme~"2"; scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); - write(t1, "1"); + write(t1, "11"); copy(t1, t2); - assert(readText(t2) == "1"); + assert(readText(t2) == "11"); write(t1, "2"); copy(t1, t2); assert(readText(t2) == "2"); + + import std.utf : byChar; + copy(t1.byChar, t2.byChar); + assert(readText(t2.byChar) == "2"); } -version(Posix) unittest //issue 11434 +@safe version(Posix) @safe unittest //issue 11434 { + import std.conv : octal; auto t1 = deleteme, t2 = deleteme~"2"; scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove(); write(t1, "1"); @@ -2563,6 +3530,16 @@ version(Posix) unittest //issue 11434 assert(getAttributes(t2) == octal!100767); } +@safe unittest // issue 15865 +{ + import std.exception : assertThrown; + auto t = deleteme; + write(t, "a"); + scope(exit) t.remove(); + assertThrown!FileException(copy(t, t)); + assert(readText(t) == "a"); +} + /++ Remove directory and all of its content and subdirectories, recursively. @@ -2575,7 +3552,7 @@ void rmdirRecurse(in char[] pathname) { //No references to pathname will be kept after rmdirRecurse, //so the cast is safe - rmdirRecurse(DirEntry(cast(string)pathname)); + rmdirRecurse(DirEntry(cast(string) pathname)); } /++ @@ -2588,7 +3565,7 @@ void rmdirRecurse(in char[] pathname) +/ void rmdirRecurse(ref DirEntry de) { - if(!de.isDir) + if (!de.isDir) throw new FileException(de.name, "Not a directory"); if (de.isSymlink) @@ -2601,7 +3578,7 @@ void rmdirRecurse(ref DirEntry de) else { // all children, recursively depth-first - foreach(DirEntry e; dirEntries(de.name, SpanMode.depth, false)) + foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false)) { attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name); } @@ -2621,16 +3598,19 @@ void rmdirRecurse(DirEntry de) rmdirRecurse(de); } -version(Windows) unittest +version(Windows) @system unittest { + import std.exception : enforce; auto d = deleteme ~ r".dir\a\b\c\d\e\f\g"; mkdirRecurse(d); rmdirRecurse(deleteme ~ ".dir"); enforce(!exists(deleteme ~ ".dir")); } -version(Posix) unittest +version(Posix) @system unittest { + import std.exception : enforce, collectException; + import std.process : executeShell; collectException(rmdirRecurse(deleteme)); auto d = deleteme~"/a/b/c/d/e/f/g"; enforce(collectException(mkdir(d))); @@ -2646,17 +3626,17 @@ version(Posix) unittest mkdirRecurse(d); version(Android) string link_cmd = "ln -s "; else string link_cmd = "ln -sf "; - std.process.executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link"); + executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link"); rmdirRecurse(deleteme); enforce(!exists(deleteme)); } -unittest +@system unittest { void[] buf; buf = new void[10]; - (cast(byte[])buf)[] = 3; + (cast(byte[]) buf)[] = 3; string unit_file = deleteme ~ "-unittest_write.tmp"; if (exists(unit_file)) remove(unit_file); write(unit_file, buf); @@ -2725,9 +3705,11 @@ private struct DirIteratorImpl bool stepIn(string directory) { - string search_pattern = buildPath(directory, "*.*"); + import std.path : chainPath; + + auto search_pattern = chainPath(directory, "*.*"); WIN32_FIND_DATAW findinfo; - HANDLE h = FindFirstFileW(search_pattern.tempCStringW(), &findinfo); + HANDLE h = FindFirstFileW(search_pattern.tempCString!FSChar(), &findinfo); cenforce(h != INVALID_HANDLE_VALUE, directory); _stack.put(DirHandle(directory, h)); return toNext(false, &findinfo); @@ -2735,7 +3717,7 @@ private struct DirIteratorImpl bool next() { - if(_stack.data.empty) + if (_stack.data.empty) return false; WIN32_FIND_DATAW findinfo; return toNext(true, &findinfo); @@ -2745,17 +3727,17 @@ private struct DirIteratorImpl { import core.stdc.wchar_ : wcscmp; - if(fetch) + if (fetch) { - if(FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE) + if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE) { popDirStack(); return false; } } - while( wcscmp(findinfo.cFileName.ptr, ".") == 0 + while ( wcscmp(findinfo.cFileName.ptr, ".") == 0 || wcscmp(findinfo.cFileName.ptr, "..") == 0) - if(FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE) + if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE) { popDirStack(); return false; @@ -2773,7 +3755,7 @@ private struct DirIteratorImpl void releaseDirStack() { - foreach( d; _stack.data) + foreach ( d; _stack.data) FindClose(d.h); } @@ -2792,19 +3774,20 @@ private struct DirIteratorImpl bool stepIn(string directory) { - auto h = cenforce(opendir(directory.tempCString()), directory); + auto h = directory.length ? opendir(directory.tempCString()) : opendir("."); + cenforce(h, directory); _stack.put(DirHandle(directory, h)); return next(); } bool next() { - if(_stack.data.empty) + if (_stack.data.empty) return false; - for(dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; ) + for (dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; ) { // Skip "." and ".." - if(core.stdc.string.strcmp(fdata.d_name.ptr, ".") && + if (core.stdc.string.strcmp(fdata.d_name.ptr, ".") && core.stdc.string.strcmp(fdata.d_name.ptr, "..") ) { _cur = DirEntry(_stack.data[$-1].dirpath, fdata); @@ -2824,7 +3807,7 @@ private struct DirIteratorImpl void releaseDirStack() { - foreach( d; _stack.data) + foreach ( d; _stack.data) closedir(d.h); } @@ -2834,20 +3817,29 @@ private struct DirIteratorImpl } } - this(string pathname, SpanMode mode, bool followSymlink) + this(R)(R pathname, SpanMode mode, bool followSymlink) + if (isInputRange!R && isSomeChar!(ElementEncodingType!R)) { _mode = mode; _followSymlink = followSymlink; _stack = appender(cast(DirHandle[])[]); - if(_mode == SpanMode.depth) + if (_mode == SpanMode.depth) _stashed = appender(cast(DirEntry[])[]); - if(stepIn(pathname)) + + static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) + alias pathnameStr = pathname; + else + { + import std.array : array; + string pathnameStr = pathname.array; + } + if (stepIn(pathnameStr)) { - if(_mode == SpanMode.depth) - while(mayStepIn()) + if (_mode == SpanMode.depth) + while (mayStepIn()) { auto thisDir = _cur; - if(stepIn(_cur.name)) + if (stepIn(_cur.name)) { pushExtra(thisDir); } @@ -2860,15 +3852,15 @@ private struct DirIteratorImpl @property DirEntry front(){ return _cur; } void popFront() { - switch(_mode) + switch (_mode) { case SpanMode.depth: - if(next()) + if (next()) { - while(mayStepIn()) + while (mayStepIn()) { auto thisDir = _cur; - if(stepIn(_cur.name)) + if (stepIn(_cur.name)) { pushExtra(thisDir); } @@ -2876,17 +3868,17 @@ private struct DirIteratorImpl break; } } - else if(hasExtra()) + else if (hasExtra()) _cur = popExtra(); break; case SpanMode.breadth: - if(mayStepIn()) + if (mayStepIn()) { - if(!stepIn(_cur.name)) - while(!empty && !next()){} + if (!stepIn(_cur.name)) + while (!empty && !next()){} } else - while(!empty && !next()){} + while (!empty && !next()){} break; default: next(); @@ -2917,12 +3909,13 @@ public: Returns an input range of DirEntry that lazily iterates a given directory, also provides two ways of foreach iteration. The iteration variable can be of type $(D_PARAM string) if only the name is needed, or $(D_PARAM DirEntry) - if additional details are needed. The span mode dictates the how the - directory is traversed. The name of the each directory entry iterated + if additional details are needed. The span mode dictates how the + directory is traversed. The name of each iterated directory entry contains the absolute path. Params: path = The directory to iterate over. + If empty, the current directory will be iterated. mode = Whether the directory's sub-directories should be iterated over depth-first ($(D_PARAM depth)), breadth-first ($(D_PARAM breadth)), or not at all ($(D_PARAM shallow)). @@ -2933,29 +3926,33 @@ public: Throws: $(D FileException) if the directory does not exist. -Examples: +Example: -------------------- // Iterate a directory in depth foreach (string name; dirEntries("destroy/me", SpanMode.depth)) { - remove(name); + remove(name); } -// Iterate a directory in breadth -foreach (string name; dirEntries(".", SpanMode.breadth)) + +// Iterate the current directory in breadth +foreach (string name; dirEntries("", SpanMode.breadth)) { - writeln(name); + writeln(name); } + // Iterate a directory and get detailed info about it foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth)) { - writeln(e.name, "\t", e.size); + writeln(e.name, "\t", e.size); } + // Iterate over all *.d files in current directory and all its subdirectories -auto dFiles = filter!`endsWith(a.name,".d")`(dirEntries(".",SpanMode.depth)); -foreach(d; dFiles) +auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d")); +foreach (d; dFiles) writeln(d.name); + // Hook it up with std.parallelism to compile them all in parallel: -foreach(d; parallel(dFiles, 1)) //passes by 1 file to each thread +foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread { string cmd = "dmd -c " ~ d.name; writeln(cmd); @@ -2969,7 +3966,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true) } /// Duplicate functionality of D1's $(D std.file.listdir()): -unittest +@safe unittest { string[] listdir(string pathname) { @@ -2993,11 +3990,17 @@ unittest } } -unittest +@system unittest { - import std.algorithm; - import std.range; - import std.process; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.algorithm.searching : startsWith; + import std.array : array; + import std.conv : to; + import std.path : dirEntries, buildPath, absolutePath; + import std.process : thisProcessID; + import std.range.primitives : walkLength; + version(Android) string testdir = deleteme; // This has to be an absolute path when // called from a shared library on Android, @@ -3012,10 +4015,11 @@ unittest // testing range interface size_t equalEntries(string relpath, SpanMode mode) { + import std.exception : enforce; auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode))); assert(walkLength(dirEntries(relpath, mode)) == len); assert(equal( - map!(a => std.path.absolutePath(a.name))(dirEntries(relpath, mode)), + map!(a => absolutePath(a.name))(dirEntries(relpath, mode)), map!(a => a.name)(dirEntries(absolutePath(relpath), mode)))); return len; } @@ -3050,7 +4054,10 @@ unittest // issue 11392 auto dFiles = dirEntries(testdir, SpanMode.shallow); - foreach(d; dFiles){} + foreach (d; dFiles){} + + // issue 15146 + dirEntries("", SpanMode.shallow).walkLength(); } /++ @@ -3060,7 +4067,7 @@ unittest path = The directory to iterate over. pattern = String with wildcards, such as $(RED "*.d"). The supported wildcard strings are described under - $(XREF _path, globMatch). + $(REF globMatch, std,_path). mode = Whether the directory's sub-directories should be iterated over depth-first ($(D_PARAM depth)), breadth-first ($(D_PARAM breadth)), or not at all ($(D_PARAM shallow)). @@ -3071,32 +4078,26 @@ unittest Throws: $(D FileException) if the directory does not exist. -Examples: +Example: -------------------- // Iterate over all D source files in current directory and all its // subdirectories -auto dFiles = dirEntries(".","*.{d,di}",SpanMode.depth); -foreach(d; dFiles) +auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth); +foreach (d; dFiles) writeln(d.name); -------------------- +/ auto dirEntries(string path, string pattern, SpanMode mode, bool followSymlink = true) { - import std.algorithm : filter; + import std.algorithm.iteration : filter; + import std.path : globMatch, baseName; + bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); } return filter!f(DirIterator(path, mode, followSymlink)); } -// Explicitly undocumented. It will be removed in July 2015. -deprecated("Please use DirEntry constructor directly instead.") -DirEntry dirEntry(in char[] name) -{ - return DirEntry(name.idup); -} - - -unittest +@system unittest { import std.stdio : writefln; immutable dpath = deleteme ~ "_dir"; @@ -3174,21 +4175,26 @@ unittest /** -Reads an entire file into an array. - -Example: ----- -// Load file; each line is an int followed by comma, whitespace and a -// double. -auto a = slurp!(int, double)("filename", "%s, %s"); ----- - -Bugs: -$(D slurp) expects file names to be encoded in $(B CP_ACP) on $(I Windows) -instead of UTF-8 (as it internally uses $(XREF stdio, File), -see $(BUGZILLA 7648)) thus must not be used in $(I Windows) -or cross-platform applications other than with an immediate ASCII string as -a file name to prevent accidental changes to result in incorrect behavior. + * Reads a file line by line and parses the line into a single value or a + * $(REF Tuple, std,typecons) of values depending on the length of `Types`. + * The lines are parsed using the specified format string. The format string is + * passed to $(REF formattedRead, std,format), and therefore must conform to the + * format string specification outlined in $(MREF format). + * + * Params: + * Types = the types that each of the elements in the line should be returned as + * filename = the name of the file to read + * format = the format string to use when reading + * + * Returns: + * If only one type is passed, then an array of that type. Otherwise, an + * array of $(REF Tuple, std,typecons)s. + * + * Throws: + * `Exception` if the format string is malformed. Also, throws `Exception` + * if any of the lines in the file are not fully consumed by the call + * to $(REF formattedRead, std,format). Meaning that no empty lines or lines + * with extra characters are allowed. */ Select!(Types.length == 1, Types[0][], Tuple!(Types)[]) slurp(Types...)(string filename, in char[] format) @@ -3196,7 +4202,9 @@ slurp(Types...)(string filename, in char[] format) import std.stdio : File; import std.format : formattedRead; import std.array : appender; - typeof(return) result; + import std.conv : text; + import std.exception : enforce; + auto app = appender!(typeof(return))(); ElementType!(typeof(return)) toAdd; auto f = File(filename); @@ -3212,12 +4220,19 @@ slurp(Types...)(string filename, in char[] format) return app.data; } -unittest +/// +@system unittest { - // Tuple!(int, double)[] x; - // auto app = appender(&x); - write(deleteme, "12 12.25\n345 1.125"); - scope(exit) { assert(exists(deleteme)); remove(deleteme); } + scope(exit) + { + assert(exists(deleteme)); + remove(deleteme); + } + + write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file + + // Load file; each line is an int followed by comma, whitespace and a + // double. auto a = slurp!(int, double)(deleteme, "%s %s"); assert(a.length == 2); assert(a[0] == tuple(12, 12.25)); @@ -3267,6 +4282,11 @@ string tempDir() @trusted DWORD len = GetTempPathW(buf.length, buf.ptr); if (len) cache = toUTF8(buf[0 .. len]); } + else version(Android) + { + // Don't check for a global temporary directory as + // Android doesn't have one. + } else version(Posix) { import std.process : environment; @@ -3286,7 +4306,7 @@ string tempDir() @trusted "/var/tmp", "/usr/tmp"); } - else static assert (false, "Unsupported platform"); + else static assert(false, "Unsupported platform"); if (cache is null) cache = getcwd(); } diff --git a/std/format.d b/std/format.d index bc10004964f..273d5c57f78 100644 --- a/std/format.d +++ b/std/format.d @@ -14,16 +14,16 @@ $(BOOKTABLE , $(TR $(TH Function Name) $(TH Description) ) - $(TR $(TD $(D $(LREF formattedRead))) + $(TR $(TD $(LREF formattedRead)) $(TD Reads values according to the _format string from an InputRange. )) - $(TR $(TD $(D $(LREF formattedWrite))) + $(TR $(TD $(LREF formattedWrite)) $(TD Formats its arguments according to the _format string and puts them to an OutputRange. )) ) - Please see the documentation of function $(D $(LREF formattedWrite)) for a + Please see the documentation of function $(LREF formattedWrite) for a description of the _format string. Two functions have been added for convenience: @@ -31,27 +31,24 @@ $(TR $(TH Function Name) $(TH Description) $(BOOKTABLE , $(TR $(TH Function Name) $(TH Description) ) - $(TR $(TD $(D $(LREF _format))) + $(TR $(TD $(LREF _format)) $(TD Returns a GC-allocated string with the formatting result. )) - $(TR $(TD $(D $(LREF sformat))) + $(TR $(TD $(LREF sformat)) $(TD Puts the formatting result into a preallocated array. )) ) - These two functions are publicly imported by $(LINK2 std_string.html, - std.string) to be easily available. + These two functions are publicly imported by $(MREF std, string) + to be easily available. - The functions $(D $(LREF formatValue)) and $(D $(LREF unformatValue)) are + The functions $(LREF formatValue) and $(LREF unformatValue) are used for the plumbing. - - Macros: WIKI = Phobos/StdFormat - Copyright: Copyright Digital Mars 2000-2013. - License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). + License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(WEB walterbright.com, Walter Bright), $(WEB erdani.com, + Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com, Andrei Alexandrescu), and Kenji Hara Source: $(PHOBOSSRC std/_format.d) @@ -62,25 +59,10 @@ module std.format; import core.vararg; import std.exception; +import std.meta; import std.range.primitives; import std.traits; -import std.typetuple; - -version(CRuntime_DigitalMars) -{ - version = DigitalMarsC; -} -version (DigitalMarsC) -{ - // This is DMC's internal floating point formatting function - extern (C) - { - extern shared char* function(int c, int flags, int precision, - in real* pdval, - char* buf, size_t* psl, int width) __pfloatfmt; - } -} /********************************************************************** * Signals a mismatch between a format and its corresponding argument. @@ -107,10 +89,10 @@ private alias enforceFmt = enforceEx!FormatException; Interprets variadic argument list $(D args), formats them according to $(D fmt), and sends the resulting characters to $(D w). The encoding of the output is the same as $(D Char). The type $(D Writer) - must satisfy $(XREF range,isOutputRange!(Writer, Char)). + must satisfy $(D $(REF isOutputRange, std,range,primitives)!(Writer, Char)). The variadic arguments are normally consumed in order. POSIX-style - $(WEB opengroup.org/onlinepubs/009695399/functions/printf.html, + $(HTTP opengroup.org/onlinepubs/009695399/functions/printf.html, positional parameter syntax) is also supported. Each argument is formatted into a sequence of chars according to the format specification, and the characters are passed to $(D w). As many @@ -134,7 +116,7 @@ private alias enforceFmt = enforceEx!FormatException; Params: w = Output is sent to this writer. Typical output writers include - $(XREF array,Appender!string) and $(XREF stdio,LockingTextWriter). + $(REF Appender!string, std,array) and $(REF LockingTextWriter, std,stdio). fmt = Format string. @@ -158,7 +140,7 @@ $(I FormatString): $(I FormatStringItem)* $(I FormatStringItem): $(B '%%') - $(B '%') $(I Position) $(I Flags) $(I Width) $(I Precision) $(I FormatChar) + $(B '%') $(I Position) $(I Flags) $(I Width) $(I Separator) $(I Precision) $(I FormatChar) $(B '%$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)') $(I OtherCharacterExceptPercent) $(I Position): @@ -175,6 +157,14 @@ $(I Width): $(I empty) $(I Integer) $(B '*') +$(I Separator): + $(I empty) + $(B ',') + $(B ',') $(B '?') + $(B ',') $(B '*') $(B '?') + $(B ',') $(I Integer) $(B '?') + $(B ',') $(B '*') + $(B ',') $(I Integer) $(I Precision): $(I empty) $(B '.') @@ -186,7 +176,7 @@ $(I Integer): $(I Digit): $(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9') $(I FormatChar): - $(B 's')|$(B 'c')|$(B 'b')|$(B 'd')|$(B 'o')|$(B 'x')|$(B 'X')|$(B 'e')|$(B 'E')|$(B 'f')|$(B 'F')|$(B 'g')|$(B 'G')|$(B 'a')|$(B 'A') + $(B 's')|$(B 'c')|$(B 'b')|$(B 'd')|$(B 'o')|$(B 'x')|$(B 'X')|$(B 'e')|$(B 'E')|$(B 'f')|$(B 'F')|$(B 'g')|$(B 'G')|$(B 'a')|$(B 'A')|$(B '|') ) $(BOOKTABLE Flags affect formatting depending on the specifier as @@ -218,68 +208,80 @@ $(I FormatChar): $(TR $(TD $(B ' ')) $(TD numeric) $(TD Prefix positive numbers in a signed conversion with a space.))) -
-
$(I Width) -
+ $(DL + $(DT $(I Width)) + $(DD Specifies the minimum field width. If the width is a $(B *), an additional argument of type $(B int), preceding the actual argument, is taken as the width. If the width is negative, it is as if the $(B -) was given - as a $(I Flags) character. + as a $(I Flags) character.) -
$(I Precision) -
Gives the precision for numeric conversions. + $(DT $(I Precision)) + $(DD Gives the precision for numeric conversions. If the precision is a $(B *), an additional argument of type $(B int), preceding the actual argument, is taken as the precision. - If it is negative, it is as if there was no $(I Precision) specifier. - -
$(I FormatChar) -
-
-
$(B 's') -
The corresponding argument is formatted in a manner consistent + If it is negative, it is as if there was no $(I Precision) specifier.) + + $(DT $(I Separator)) + $(DD Inserts the separator symbols ',' every $(I X) digits, from right + to left, into numeric values to increase readability. + The fractional part of floating point values inserts the separator + from left to right. + Entering an integer after the ',' allows to specify $(I X). + If a '*' is placed after the ',' then $(I X) is specified by an + additional parameter to the format function. + Adding a '?' after the ',' or $(I X) specifier allows to specify + the separator character as an additional parameter. + ) + + $(DT $(I FormatChar)) + $(DD + $(DL + $(DT $(B 's')) + $(DD The corresponding argument is formatted in a manner consistent with its type: -
-
$(B bool) -
The result is 'true' or 'false'. -
integral types -
The $(B %d) format is used. -
floating point types -
The $(B %g) format is used. -
string types -
The result is the string converted to UTF-8. + $(DL + $(DT $(B bool)) + $(DD The result is $(D "true") or $(D "false").) + $(DT integral types) + $(DD The $(B %d) format is used.) + $(DT floating point types) + $(DD The $(B %g) format is used.) + $(DT string types) + $(DD The result is the string converted to UTF-8. A $(I Precision) specifies the maximum number of characters - to use in the result. -
structs -
If the struct defines a $(B toString()) method the result is + to use in the result.) + $(DT structs) + $(DD If the struct defines a $(B toString()) method the result is the string returned from this function. Otherwise the result is StructName(field0, field1, ...) where fieldn is the nth element formatted with the default - format. -
classes derived from $(B Object) -
The result is the string returned from the class instance's + format.) + $(DT classes derived from $(B Object)) + $(DD The result is the string returned from the class instance's $(B .toString()) method. A $(I Precision) specifies the maximum number of characters - to use in the result. -
unions -
If the union defines a $(B toString()) method the result is + to use in the result.) + $(DT unions) + $(DD If the union defines a $(B toString()) method the result is the string returned from this function. Otherwise the result is - the name of the union, without its contents. -
non-string static and dynamic arrays -
The result is [s0, s1, ...] + the name of the union, without its contents.) + $(DT non-string static and dynamic arrays) + $(DD The result is [s0, s1, ...] where sn is the nth element - formatted with the default format. -
associative arrays -
The result is the equivalent of what the initializer + formatted with the default format.) + $(DT associative arrays) + $(DD The result is the equivalent of what the initializer would look like for the contents of the associative array, - e.g.: ["red" : 10, "blue" : 20]. -
+ e.g.: ["red" : 10, "blue" : 20].) + )) -
$(B 'c') -
The corresponding argument must be a character type. + $(DT $(B 'c')) + $(DD The corresponding argument must be a character type.) -
$(B 'b','d','o','x','X') -
The corresponding argument must be an integral type + $(DT $(B 'b','d','o','x','X')) + $(DD The corresponding argument must be an integral type and is formatted as an integer. If the argument is a signed type and the $(I FormatChar) is $(B d) it is converted to a signed string of characters, otherwise it is treated as @@ -291,26 +293,26 @@ $(I FormatChar): If there are fewer resulting digits than the $(I Precision), leading zeros are used as necessary. If the $(I Precision) is 0 and the number is 0, no digits - result. + result.) -
$(B 'e','E') -
A floating point number is formatted as one digit before + $(DT $(B 'e','E')) + $(DD A floating point number is formatted as one digit before the decimal point, $(I Precision) digits after, the $(I FormatChar), ±, followed by at least a two digit exponent: $(I d.dddddd)e$(I ±dd). If there is no $(I Precision), six digits are generated after the decimal point. - If the $(I Precision) is 0, no decimal point is generated. + If the $(I Precision) is 0, no decimal point is generated.) -
$(B 'f','F') -
A floating point number is formatted in decimal notation. + $(DT $(B 'f','F')) + $(DD A floating point number is formatted in decimal notation. The $(I Precision) specifies the number of digits generated after the decimal point. It defaults to six. At least one digit is generated before the decimal point. If the $(I Precision) - is zero, no decimal point is generated. + is zero, no decimal point is generated.) -
$(B 'g','G') -
A floating point number is formatted in either $(B e) or + $(DT $(B 'g','G')) + $(DD A floating point number is formatted in either $(B e) or $(B f) format for $(B g); $(B E) or $(B F) format for $(B G). The $(B f) format is used if the exponent for an $(B e) format @@ -318,10 +320,10 @@ $(I FormatChar): The $(I Precision) specifies the number of significant digits, and defaults to six. Trailing zeros are elided after the decimal point, if the fractional - part is zero then no decimal point is generated. + part is zero then no decimal point is generated.) -
$(B 'a','A') -
A floating point number is formatted in hexadecimal + $(DT $(B 'a','A')) + $(DD A floating point number is formatted in hexadecimal exponential notation 0x$(I h.hhhhhh)p$(I ±d). There is one hexadecimal digit before the decimal point, and as many after as specified by the $(I Precision). @@ -333,9 +335,9 @@ $(I FormatChar): $(I h.hhhhhh)*2$(I ±d). The exponent for zero is zero. The hexadecimal digits, x and p are in upper case if the - $(I FormatChar) is upper case. -
-
+ $(I FormatChar) is upper case.) + )) + ) Floating point NaN's are formatted as $(B nan) if the $(I FormatChar) is lower case, or $(B NAN) if upper. @@ -343,23 +345,6 @@ $(I FormatChar): $(B infinity) if the $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper. - Examples: - ------------------------- - import std.array; - import std.format; - - void main() - { - auto writer = appender!string(); - formattedWrite(writer, "%s is the ultimate %s.", 42, "answer"); - assert(writer.data == "42 is the ultimate answer."); - // Clear the writer - writer = appender!string(); - formattedWrite(writer, "Date: %2$s %1$s", "October", 5); - assert(writer.data == "Date: 5 October"); - } - ------------------------ - The positional and non-positional styles can be mixed in the same format string. (POSIX leaves this behavior undefined.) The internal counter for non-positional parameters tracks the next parameter after @@ -376,10 +361,10 @@ $(I FormatChar): } ------------------------- The output is: -
+$(CONSOLE
 My items are 1 2 3.
 My items are 1, 2, 3.
-
+) The trailing end of the sub-format string following the specifier for each item is interpreted as the array delimiter, and is therefore omitted @@ -395,9 +380,9 @@ My items are 1, 2, 3. } ------------------------- which gives the output: -
+$(CONSOLE
 My items are -1-, -2-, -3-.
-
+) These compound format specifiers may be nested in the case of a nested array argument: @@ -419,7 +404,7 @@ My items are -1-, -2-, -3-. } ------------------------- The output is: -
+$(CONSOLE
 1 2 3
 4 5 6
 7 8 9
@@ -431,7 +416,7 @@ My items are -1-, -2-, -3-.
 [[1 2 3]
  [4 5 6]
  [7 8 9]]
-
+) Inside a compound format specifier, strings and characters are escaped automatically. To avoid this behavior, add $(B '-') flag to @@ -447,17 +432,42 @@ My items are -1-, -2-, -3-. } ------------------------- which gives the output: -
+$(CONSOLE
 My friends are ["John", "Nancy"].
 My friends are "John", "Nancy".
 My friends are John, Nancy.
-
+) */ +uint formattedWrite(alias fmt, Writer, A...)(Writer w, A args) +if (isSomeString!(typeof(fmt))) +{ + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return .formattedWrite(w, fmt, args); +} + +/// The format string can be checked at compile-time (see $(LREF format) for details): +@safe pure unittest +{ + import std.array : appender; + import std.format : formattedWrite; + + auto writer = appender!string(); + writer.formattedWrite!"%s is the ultimate %s."(42, "answer"); + assert(writer.data == "42 is the ultimate answer."); + + // Clear the writer + writer = appender!string(); + formattedWrite(writer, "Date: %2$s %1$s", "October", 5); + assert(writer.data == "Date: 5 October"); +} + +/// ditto uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) { import std.conv : text, to; - alias FPfmt = void function(Writer, const(void)*, ref FormatSpec!Char) @safe pure nothrow; + alias FPfmt = void function(Writer, scope const(void)*, const ref FormatSpec!Char) @safe pure nothrow; auto spec = FormatSpec!Char(fmt); @@ -488,6 +498,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) text("Orphan format specifier: %", spec.spec)); break; } + if (spec.width == spec.DYNAMIC) { auto width = to!(typeof(spec.width))(getNthInt(currentArg, args)); @@ -513,6 +524,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) } spec.width = width; } + if (spec.precision == spec.DYNAMIC) { auto precision = to!(typeof(spec.precision))( @@ -534,17 +546,45 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) // else negative precision is same as no precision else spec.precision = spec.UNSPECIFIED; } + + if (spec.separators == spec.DYNAMIC) + { + auto separators = to!(typeof(spec.width))(getNthInt(currentArg, args)); + spec.separators = separators; + ++currentArg; + } + + if (spec.separatorCharPos == spec.DYNAMIC) + { + auto separatorChar = getNth!(isSomeChar,dchar)(currentArg, args); + spec.separatorChar = separatorChar; + ++currentArg; + } + + if (currentArg == A.length && !spec.indexStart) + { + // leftover spec? + enforceFmt(fmt.length == 0, + text("Orphan format specifier: %", spec.spec)); + break; + } + // Format! if (spec.indexStart > 0) { // using positional parameters! - foreach (i; spec.indexStart - 1 .. spec.indexEnd) + + // Make the conditional compilation of this loop explicit, to avoid "statement not reachable" warnings. + static if (A.length > 0) { - if (funs.length <= i) break; - if (__ctfe) - formatNth(w, spec, i, args); - else - funs[i](w, argsAddresses[i], spec); + foreach (i; spec.indexStart - 1 .. spec.indexEnd) + { + if (A.length <= i) break; + if (__ctfe) + formatNth(w, spec, i, args); + else + funs[i](w, argsAddresses[i], spec); + } } if (currentArg < spec.indexEnd) currentArg = spec.indexEnd; } @@ -560,6 +600,20 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) return currentArg; } +/// +@safe unittest +{ + assert(format("%,d", 1000) == "1,000"); + assert(format("%,f", 1234567.891011) == "1,234,567.891,011"); + assert(format("%,?d", '?', 1000) == "1?000"); + assert(format("%,1d", 1000) == "1,0,0,0", format("%,1d", 1000)); + assert(format("%,*d", 4, -12345) == "-1,2345"); + assert(format("%,*?d", 4, '_', -12345) == "-1_2345"); + assert(format("%,6?d", '_', -12345678) == "-12_345678"); + assert(format("%12,3.3f", 1234.5678) == " 1,234.568", "'" ~ + format("%12,3.3f", 1234.5678) ~ "'"); +} + @safe pure unittest { import std.array; @@ -582,8 +636,20 @@ Returns: On success, the function returns the number of variables filled. This count can match the expected number of readings or fewer, even zero, if a matching failure happens. + +Throws: + An `Exception` if `S.length == 0` and `fmt` has format specifiers. */ -uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, S args) +uint formattedRead(alias fmt, R, S...)(ref R r, auto ref S args) +if (isSomeString!(typeof(fmt))) +{ + alias e = checkFormatException!(fmt, S); + static assert(!e, e.msg); + return .formattedRead(r, fmt, args); +} + +/// ditto +uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, auto ref S args) { import std.typecons : isTuple; @@ -591,11 +657,13 @@ uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, S args) static if (!S.length) { spec.readUpToNextSpec(r); - enforce(spec.trailing.empty); + enforce(spec.trailing.empty, "Trailing characters in formattedRead format string"); return 0; } else { + enum hasPointer = isPointer!(typeof(args[0])); + // The function below accounts for '*' == fields meant to be // read and skipped void skipUnstoredFields() @@ -615,278 +683,568 @@ uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, S args) // Input is empty, nothing to read return 0; } - alias A = typeof(*args[0]); + static if (hasPointer) + alias A = typeof(*args[0]); + else + alias A = typeof(args[0]); + static if (isTuple!A) { foreach (i, T; A.Types) { - (*args[0])[i] = unformatValue!(T)(r, spec); + static if (hasPointer) + (*args[0])[i] = unformatValue!(T)(r, spec); + else + args[0][i] = unformatValue!(T)(r, spec); skipUnstoredFields(); } } else { - *args[0] = unformatValue!(A)(r, spec); + static if (hasPointer) + *args[0] = unformatValue!(A)(r, spec); + else + args[0] = unformatValue!(A)(r, spec); } return 1 + formattedRead(r, spec.trailing, args[1 .. $]); } } -/// -unittest +/// The format string can be checked at compile-time (see $(LREF format) for details): +@safe pure unittest { string s = "hello!124:34.5"; string a; int b; double c; - formattedRead(s, "%s!%s:%s", &a, &b, &c); + s.formattedRead!"%s!%s:%s"(a, b, c); assert(a == "hello" && b == 124 && c == 34.5); } -unittest +@safe unittest { import std.math; string s = " 1.2 3.4 "; double x, y, z; - assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2); + assert(formattedRead(s, " %s %s %s ", x, y, z) == 2); assert(s.empty); assert(approxEqual(x, 1.2)); assert(approxEqual(y, 3.4)); assert(isNaN(z)); } -template FormatSpec(Char) - if (!is(Unqual!Char == Char)) +// for backwards compatibility +@system pure unittest { - alias FormatSpec = FormatSpec!(Unqual!Char); + string s = "hello!124:34.5"; + string a; + int b; + double c; + formattedRead(s, "%s!%s:%s", &a, &b, &c); + assert(a == "hello" && b == 124 && c == 34.5); + + // mix pointers and auto-ref + s = "world!200:42.2"; + formattedRead(s, "%s!%s:%s", a, &b, &c); + assert(a == "world" && b == 200 && c == 42.2); + + s = "world1!201:42.3"; + formattedRead(s, "%s!%s:%s", &a, &b, c); + assert(a == "world1" && b == 201 && c == 42.3); + + s = "world2!202:42.4"; + formattedRead(s, "%s!%s:%s", a, b, &c); + assert(a == "world2" && b == 202 && c == 42.4); } -/** - * A General handler for $(D printf) style format specifiers. Used for building more - * specific formatting functions. - */ -struct FormatSpec(Char) - if (is(Unqual!Char == Char)) +// for backwards compatibility +@system pure unittest { - import std.ascii : isDigit; - import std.algorithm : startsWith; - import std.conv : parse, text, to; - - /** - Minimum _width, default $(D 0). - */ - int width = 0; + import std.math; + string s = " 1.2 3.4 "; + double x, y, z; + assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2); + assert(s.empty); + assert(approxEqual(x, 1.2)); + assert(approxEqual(y, 3.4)); + assert(isNaN(z)); +} - /** - Precision. Its semantics depends on the argument type. For - floating point numbers, _precision dictates the number of - decimals printed. - */ - int precision = UNSPECIFIED; +@system pure unittest +{ + string line; - /** - Special value for width and precision. $(D DYNAMIC) width or - precision means that they were specified with $(D '*') in the - format string and are passed at runtime through the varargs. - */ - enum int DYNAMIC = int.max; + bool f1; - /** - Special value for precision, meaning the format specifier - contained no explicit precision. - */ - enum int UNSPECIFIED = DYNAMIC - 1; + line = "true"; + formattedRead(line, "%s", &f1); + assert(f1); - /** - The actual format specifier, $(D 's') by default. - */ - char spec = 's'; + line = "TrUE"; + formattedRead(line, "%s", &f1); + assert(f1); - /** - Index of the argument for positional parameters, from $(D 1) to - $(D ubyte.max). ($(D 0) means not used). - */ - ubyte indexStart; + line = "false"; + formattedRead(line, "%s", &f1); + assert(!f1); - /** - Index of the last argument for positional parameter range, from - $(D 1) to $(D ubyte.max). ($(D 0) means not used). - */ - ubyte indexEnd; + line = "fALsE"; + formattedRead(line, "%s", &f1); + assert(!f1); - version(StdDdoc) - { - /** - The format specifier contained a $(D '-') ($(D printf) - compatibility). - */ - bool flDash; + line = "1"; + formattedRead(line, "%d", &f1); + assert(f1); - /** - The format specifier contained a $(D '0') ($(D printf) - compatibility). - */ - bool flZero; + line = "-1"; + formattedRead(line, "%d", &f1); + assert(f1); - /** - The format specifier contained a $(D ' ') ($(D printf) - compatibility). - */ - bool flSpace; + line = "0"; + formattedRead(line, "%d", &f1); + assert(!f1); - /** - The format specifier contained a $(D '+') ($(D printf) - compatibility). - */ - bool flPlus; + line = "-0"; + formattedRead(line, "%d", &f1); + assert(!f1); +} - /** - The format specifier contained a $(D '#') ($(D printf) - compatibility). - */ - bool flHash; +@system pure unittest +{ + union B + { + char[int.sizeof] untyped; + int typed; + } + B b; + b.typed = 5; + char[] input = b.untyped[]; + int witness; + formattedRead(input, "%r", &witness); + assert(witness == b.typed); +} - // Fake field to allow compilation - ubyte allFlags; - } - else +@system pure unittest +{ + union A { - union - { - import std.bitmanip : bitfields; - mixin(bitfields!( - bool, "flDash", 1, - bool, "flZero", 1, - bool, "flSpace", 1, - bool, "flPlus", 1, - bool, "flHash", 1, - ubyte, "", 3)); - ubyte allFlags; - } + char[float.sizeof] untyped; + float typed; } + A a; + a.typed = 5.5; + char[] input = a.untyped[]; + float witness; + formattedRead(input, "%r", &witness); + assert(witness == a.typed); +} - /** - In case of a compound format specifier starting with $(D - "%$(LPAREN)") and ending with $(D "%$(RPAREN)"), $(D _nested) - contains the string contained within the two separators. - */ - const(Char)[] nested; - - /** - In case of a compound format specifier, $(D _sep) contains the - string positioning after $(D "%|"). - */ - const(Char)[] sep; +@system pure unittest +{ + import std.typecons; + char[] line = "1 2".dup; + int a, b; + formattedRead(line, "%s %s", &a, &b); + assert(a == 1 && b == 2); - /** - $(D _trailing) contains the rest of the format string. - */ - const(Char)[] trailing; + line = "10 2 3".dup; + formattedRead(line, "%d ", &a); + assert(a == 10); + assert(line == "2 3"); - /* - This string is inserted before each sequence (e.g. array) - formatted (by default $(D "[")). - */ - enum immutable(Char)[] seqBefore = "["; + Tuple!(int, float) t; + line = "1 2.125".dup; + formattedRead(line, "%d %g", &t); + assert(t[0] == 1 && t[1] == 2.125); - /* - This string is inserted after each sequence formatted (by - default $(D "]")). - */ - enum immutable(Char)[] seqAfter = "]"; + line = "1 7643 2.125".dup; + formattedRead(line, "%s %*u %s", &t); + assert(t[0] == 1 && t[1] == 2.125); +} - /* - This string is inserted after each element keys of a sequence (by - default $(D ":")). +@system pure unittest +{ + string line; + + char c1, c2; + + line = "abc"; + formattedRead(line, "%s%c", &c1, &c2); + assert(c1 == 'a' && c2 == 'b'); + assert(line == "c"); +} + +@system pure unittest +{ + string line; + + line = "[1,2,3]"; + int[] s1; + formattedRead(line, "%s", &s1); + assert(s1 == [1,2,3]); +} + +@system pure unittest +{ + string line; + + line = "[1,2,3]"; + int[] s1; + formattedRead(line, "[%(%s,%)]", &s1); + assert(s1 == [1,2,3]); + + line = `["hello", "world"]`; + string[] s2; + formattedRead(line, "[%(%s, %)]", &s2); + assert(s2 == ["hello", "world"]); + + line = "123 456"; + int[] s3; + formattedRead(line, "%(%s %)", &s3); + assert(s3 == [123, 456]); + + line = "h,e,l,l,o; w,o,r,l,d"; + string[] s4; + formattedRead(line, "%(%(%c,%); %)", &s4); + assert(s4 == ["hello", "world"]); +} + +@system pure unittest +{ + string line; + + int[4] sa1; + line = `[1,2,3,4]`; + formattedRead(line, "%s", &sa1); + assert(sa1 == [1,2,3,4]); + + int[4] sa2; + line = `[1,2,3]`; + assertThrown(formattedRead(line, "%s", &sa2)); + + int[4] sa3; + line = `[1,2,3,4,5]`; + assertThrown(formattedRead(line, "%s", &sa3)); +} + +@system pure unittest +{ + string input; + + int[4] sa1; + input = `[1,2,3,4]`; + formattedRead(input, "[%(%s,%)]", &sa1); + assert(sa1 == [1,2,3,4]); + + int[4] sa2; + input = `[1,2,3]`; + assertThrown(formattedRead(input, "[%(%s,%)]", &sa2)); +} + +@system pure unittest +{ + string line; + + string s1, s2; + + line = "hello, world"; + formattedRead(line, "%s", &s1); + assert(s1 == "hello, world", s1); + + line = "hello, world;yah"; + formattedRead(line, "%s;%s", &s1, &s2); + assert(s1 == "hello, world", s1); + assert(s2 == "yah", s2); + + line = `['h','e','l','l','o']`; + string s3; + formattedRead(line, "[%(%s,%)]", &s3); + assert(s3 == "hello"); + + line = `"hello"`; + string s4; + formattedRead(line, "\"%(%c%)\"", &s4); + assert(s4 == "hello"); +} + +@system pure unittest +{ + string line; + + string[int] aa1; + line = `[1:"hello", 2:"world"]`; + formattedRead(line, "%s", &aa1); + assert(aa1 == [1:"hello", 2:"world"]); + + int[string] aa2; + line = `{"hello"=1; "world"=2}`; + formattedRead(line, "{%(%s=%s; %)}", &aa2); + assert(aa2 == ["hello":1, "world":2]); + + int[string] aa3; + line = `{[hello=1]; [world=2]}`; + formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3); + assert(aa3 == ["hello":1, "world":2]); +} + +template FormatSpec(Char) +if (!is(Unqual!Char == Char)) +{ + alias FormatSpec = FormatSpec!(Unqual!Char); +} + +/** + * A General handler for $(D printf) style format specifiers. Used for building more + * specific formatting functions. + */ +struct FormatSpec(Char) +if (is(Unqual!Char == Char)) +{ + import std.ascii : isDigit, isPunctuation, isAlpha; + import std.algorithm.searching : startsWith; + import std.conv : parse, text, to; + + /** + Minimum _width, default $(D 0). */ - enum immutable(Char)[] keySeparator = ":"; + int width = 0; - /* - This string is inserted in between elements of a sequence (by - default $(D ", ")). + /** + Precision. Its semantics depends on the argument type. For + floating point numbers, _precision dictates the number of + decimals printed. */ - enum immutable(Char)[] seqSeparator = ", "; + int precision = UNSPECIFIED; /** - Construct a new $(D FormatSpec) using the format string $(D fmt), no - processing is done until needed. + Separator. Its value defines how many digits are printed between + $(D SeparatorChar). + */ + int separators = UNSPECIFIED; + + /** + Separator. Its value defines how many digits are printed between + $(D SeparatorChar). + */ + int separatorCharPos = UNSPECIFIED; + + /** + SeparatorChar. The character that is inserted every $(D separators) character. + */ + dchar separatorChar = ','; + + /** + Special value for width and precision. $(D DYNAMIC) width or + precision means that they were specified with $(D '*') in the + format string and are passed at runtime through the varargs. */ - this(in Char[] fmt) @safe pure - { - trailing = fmt; - } + enum int DYNAMIC = int.max; - bool writeUpToNextSpec(OutputRange)(OutputRange writer) - { - if (trailing.empty) - return false; - for (size_t i = 0; i < trailing.length; ++i) - { - if (trailing[i] != '%') continue; - put(writer, trailing[0 .. i]); - trailing = trailing[i .. $]; - enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`); - trailing = trailing[1 .. $]; + /** + Special value for precision, meaning the format specifier + contained no explicit precision. + */ + enum int UNSPECIFIED = DYNAMIC - 1; - if (trailing[0] != '%') - { - // Spec found. Fill up the spec, and bailout - fillUp(); - return true; - } - // Doubled! Reset and Keep going - i = 0; - } - // no format spec found - put(writer, trailing); - trailing = null; - return false; - } + /** + The actual format specifier, $(D 's') by default. + */ + char spec = 's'; - unittest - { - import std.array; - auto w = appender!(char[])(); - auto f = FormatSpec("abc%sdef%sghi"); - f.writeUpToNextSpec(w); - assert(w.data == "abc", w.data); - assert(f.trailing == "def%sghi", text(f.trailing)); - f.writeUpToNextSpec(w); - assert(w.data == "abcdef", w.data); - assert(f.trailing == "ghi"); - // test with embedded %%s - f = FormatSpec("ab%%cd%%ef%sg%%h%sij"); - w.clear(); - f.writeUpToNextSpec(w); - assert(w.data == "ab%cd%ef" && f.trailing == "g%%h%sij", w.data); - f.writeUpToNextSpec(w); - assert(w.data == "ab%cd%efg%h" && f.trailing == "ij"); - // bug4775 - f = FormatSpec("%%%s"); - w.clear(); - f.writeUpToNextSpec(w); - assert(w.data == "%" && f.trailing == ""); - f = FormatSpec("%%%%%s%%"); - w.clear(); - while (f.writeUpToNextSpec(w)) continue; - assert(w.data == "%%%"); + /** + Index of the argument for positional parameters, from $(D 1) to + $(D ubyte.max). ($(D 0) means not used). + */ + ubyte indexStart; - f = FormatSpec("a%%b%%c%"); - w.clear(); - assertThrown!FormatException(f.writeUpToNextSpec(w)); - assert(w.data == "a%b%c" && f.trailing == "%"); - } + /** + Index of the last argument for positional parameter range, from + $(D 1) to $(D ubyte.max). ($(D 0) means not used). + */ + ubyte indexEnd; - private void fillUp() + version(StdDdoc) { - // Reset content - if (__ctfe) - { - flDash = false; + /** + The format specifier contained a $(D '-') ($(D printf) + compatibility). + */ + bool flDash; + + /** + The format specifier contained a $(D '0') ($(D printf) + compatibility). + */ + bool flZero; + + /** + The format specifier contained a $(D ' ') ($(D printf) + compatibility). + */ + bool flSpace; + + /** + The format specifier contained a $(D '+') ($(D printf) + compatibility). + */ + bool flPlus; + + /** + The format specifier contained a $(D '#') ($(D printf) + compatibility). + */ + bool flHash; + + /** + The format specifier contained a $(D ',') + */ + bool flSeparator; + + // Fake field to allow compilation + ubyte allFlags; + } + else + { + union + { + import std.bitmanip : bitfields; + mixin(bitfields!( + bool, "flDash", 1, + bool, "flZero", 1, + bool, "flSpace", 1, + bool, "flPlus", 1, + bool, "flHash", 1, + bool, "flSeparator", 1, + ubyte, "", 2)); + ubyte allFlags; + } + } + + /** + In case of a compound format specifier starting with $(D + "%$(LPAREN)") and ending with $(D "%$(RPAREN)"), $(D _nested) + contains the string contained within the two separators. + */ + const(Char)[] nested; + + /** + In case of a compound format specifier, $(D _sep) contains the + string positioning after $(D "%|"). + `sep is null` means no separator else `sep.empty` means 0 length + separator. + */ + const(Char)[] sep; + + /** + $(D _trailing) contains the rest of the format string. + */ + const(Char)[] trailing; + + /* + This string is inserted before each sequence (e.g. array) + formatted (by default $(D "[")). + */ + enum immutable(Char)[] seqBefore = "["; + + /* + This string is inserted after each sequence formatted (by + default $(D "]")). + */ + enum immutable(Char)[] seqAfter = "]"; + + /* + This string is inserted after each element keys of a sequence (by + default $(D ":")). + */ + enum immutable(Char)[] keySeparator = ":"; + + /* + This string is inserted in between elements of a sequence (by + default $(D ", ")). + */ + enum immutable(Char)[] seqSeparator = ", "; + + /** + Construct a new $(D FormatSpec) using the format string $(D fmt), no + processing is done until needed. + */ + this(in Char[] fmt) @safe pure + { + trailing = fmt; + } + + bool writeUpToNextSpec(OutputRange)(OutputRange writer) + { + if (trailing.empty) + return false; + for (size_t i = 0; i < trailing.length; ++i) + { + if (trailing[i] != '%') continue; + put(writer, trailing[0 .. i]); + trailing = trailing[i .. $]; + enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`); + trailing = trailing[1 .. $]; + + if (trailing[0] != '%') + { + // Spec found. Fill up the spec, and bailout + fillUp(); + return true; + } + // Doubled! Reset and Keep going + i = 0; + } + // no format spec found + put(writer, trailing); + trailing = null; + return false; + } + + @safe unittest + { + import std.array; + auto w = appender!(char[])(); + auto f = FormatSpec("abc%sdef%sghi"); + f.writeUpToNextSpec(w); + assert(w.data == "abc", w.data); + assert(f.trailing == "def%sghi", text(f.trailing)); + f.writeUpToNextSpec(w); + assert(w.data == "abcdef", w.data); + assert(f.trailing == "ghi"); + // test with embedded %%s + f = FormatSpec("ab%%cd%%ef%sg%%h%sij"); + w.clear(); + f.writeUpToNextSpec(w); + assert(w.data == "ab%cd%ef" && f.trailing == "g%%h%sij", w.data); + f.writeUpToNextSpec(w); + assert(w.data == "ab%cd%efg%h" && f.trailing == "ij"); + // bug4775 + f = FormatSpec("%%%s"); + w.clear(); + f.writeUpToNextSpec(w); + assert(w.data == "%" && f.trailing == ""); + f = FormatSpec("%%%%%s%%"); + w.clear(); + while (f.writeUpToNextSpec(w)) continue; + assert(w.data == "%%%"); + + f = FormatSpec("a%%b%%c%"); + w.clear(); + assertThrown!FormatException(f.writeUpToNextSpec(w)); + assert(w.data == "a%b%c" && f.trailing == "%"); + } + + private void fillUp() + { + // Reset content + if (__ctfe) + { + flDash = false; flZero = false; flSpace = false; flPlus = false; flHash = false; + flSeparator = false; } else { @@ -955,7 +1313,7 @@ struct FormatSpec(Char) else { nested = trailing[i + 1 .. j - 1]; - sep = null; // use null (issue 12135) + sep = null; // no separator } //this = FormatSpec(innerTrailingSpec); spec = '('; @@ -989,7 +1347,7 @@ struct FormatSpec(Char) const widthOrArgIndex = parse!uint(tmp); enforceFmt(tmp.length, text("Incorrect format specifier %", trailing[i .. $])); - i = tmp.ptr - trailing.ptr; + i = arrayPtrDiff(tmp, trailing); if (tmp.startsWith('$')) { // index of the form %n$ @@ -1009,7 +1367,7 @@ struct FormatSpec(Char) { indexEnd = parse!(typeof(indexEnd))(tmp); } - i = tmp.ptr - trailing.ptr; + i = arrayPtrDiff(tmp, trailing); enforceFmt(trailing[i++] == '$', "$ expected"); } @@ -1018,6 +1376,36 @@ struct FormatSpec(Char) // width width = to!int(widthOrArgIndex); } + break; + case ',': + // Precision + ++i; + flSeparator = true; + + if (trailing[i] == '*') + { + ++i; + // read result + separators = DYNAMIC; + } + else if (isDigit(trailing[i])) + { + auto tmp = trailing[i .. $]; + separators = parse!int(tmp); + i = arrayPtrDiff(tmp, trailing); + } + else + { + // "," was specified, but nothing after it + separators = 3; + } + + if (trailing[i] == '?') + { + separatorCharPos = DYNAMIC; + ++i; + } + break; case '.': // Precision @@ -1043,5179 +1431,3996 @@ struct FormatSpec(Char) { // negative precision, as good as 0 precision = 0; - auto tmp = trailing[i .. $]; - parse!int(tmp); // skip digits - i = tmp.ptr - trailing.ptr; - } - else if (isDigit(trailing[i])) - { - auto tmp = trailing[i .. $]; - precision = parse!int(tmp); - i = tmp.ptr - trailing.ptr; - } - else - { - // "." was specified, but nothing after it - precision = 0; - } - break; - default: - // this is the format char - spec = cast(char) trailing[i++]; - trailing = trailing[i .. $]; - return; - } // end switch - } // end for - throw new Exception(text("Incorrect format specifier: ", trailing)); - } - - //-------------------------------------------------------------------------- - private bool readUpToNextSpec(R)(ref R r) - { - import std.ascii : isLower; - - // Reset content - if (__ctfe) - { - flDash = false; - flZero = false; - flSpace = false; - flPlus = false; - flHash = false; - } - else - { - allFlags = 0; - } - width = 0; - precision = UNSPECIFIED; - nested = null; - // Parse the spec - while (trailing.length) - { - if (*trailing.ptr == '%') - { - if (trailing.length > 1 && trailing.ptr[1] == '%') - { - assert(!r.empty); - // Require a '%' - if (r.front != '%') break; - trailing = trailing[2 .. $]; - r.popFront(); - } - else - { - enforce(isLower(trailing[1]) || trailing[1] == '*' || - trailing[1] == '(', - text("'%", trailing[1], - "' not supported with formatted read")); - trailing = trailing[1 .. $]; - fillUp(); - return true; - } - } - else - { - if (trailing.ptr[0] == ' ') - { - while (!r.empty && std.ascii.isWhite(r.front)) r.popFront(); - //r = std.algorithm.find!(not!(std.ascii.isWhite))(r); - } - else - { - enforce(!r.empty, - text("parseToFormatSpec: Cannot find character `", - trailing.ptr[0], "' in the input string.")); - if (r.front != trailing.front) break; - r.popFront(); - } - trailing = trailing[std.utf.stride(trailing, 0) .. $]; - } - } - return false; - } - - private string getCurFmtStr() const - { - import std.array : appender; - auto w = appender!string(); - auto f = FormatSpec!Char("%s"); // for stringnize - - put(w, '%'); - if (indexStart != 0) - { - formatValue(w, indexStart, f); - put(w, '$'); - } - if (flDash) put(w, '-'); - if (flZero) put(w, '0'); - if (flSpace) put(w, ' '); - if (flPlus) put(w, '+'); - if (flHash) put(w, '#'); - if (width != 0) - formatValue(w, width, f); - if (precision != FormatSpec!Char.UNSPECIFIED) - { - put(w, '.'); - formatValue(w, precision, f); - } - put(w, spec); - return w.data; - } - - unittest - { - // issue 5237 - import std.array; - auto w = appender!string(); - auto f = FormatSpec!char("%.16f"); - f.writeUpToNextSpec(w); // dummy eating - assert(f.spec == 'f'); - auto fmt = f.getCurFmtStr(); - assert(fmt == "%.16f"); - } - - private const(Char)[] headUpToNextSpec() - { - import std.array : appender; - auto w = appender!(typeof(return))(); - auto tr = trailing; - - while (tr.length) - { - if (*tr.ptr == '%') - { - if (tr.length > 1 && tr.ptr[1] == '%') - { - tr = tr[2 .. $]; - w.put('%'); - } - else - break; - } - else - { - w.put(tr.front); - tr.popFront(); - } - } - return w.data; - } - - string toString() - { - return text("address = ", cast(void*) &this, - "\nwidth = ", width, - "\nprecision = ", precision, - "\nspec = ", spec, - "\nindexStart = ", indexStart, - "\nindexEnd = ", indexEnd, - "\nflDash = ", flDash, - "\nflZero = ", flZero, - "\nflSpace = ", flSpace, - "\nflPlus = ", flPlus, - "\nflHash = ", flHash, - "\nnested = ", nested, - "\ntrailing = ", trailing, "\n"); - } -} - -/// -@safe pure unittest -{ - import std.array; - auto a = appender!(string)(); - auto fmt = "Number: %2.4e\nString: %s"; - auto f = FormatSpec!char(fmt); - - f.writeUpToNextSpec(a); - - assert(a.data == "Number: "); - assert(f.trailing == "\nString: %s"); - assert(f.spec == 'e'); - assert(f.width == 2); - assert(f.precision == 4); - - f.writeUpToNextSpec(a); - - assert(a.data == "Number: \nString: "); - assert(f.trailing == ""); - assert(f.spec == 's'); -} - -// Issue 14059 -unittest -{ - import std.array : appender; - auto a = appender!(string)(); - - auto f = FormatSpec!char("%-(%s%"); - assertThrown(f.writeUpToNextSpec(a)); - - f = FormatSpec!char("%(%-"); - assertThrown(f.writeUpToNextSpec(a)); -} - -/** -Helper function that returns a $(D FormatSpec) for a single specifier given -in $(D fmt) - -Params: - fmt = A format specifier - -Returns: - A $(D FormatSpec) with the specifier parsed. - -Enforces giving only one specifier to the function. - */ -FormatSpec!Char singleSpec(Char)(Char[] fmt) -{ - import std.conv : text; - enforce(fmt.length >= 2, new Exception("fmt must be at least 2 characters long")); - enforce(fmt.front == '%', new Exception("fmt must start with a '%' character")); - - static struct DummyOutputRange { - void put(C)(C[] buf) {} // eat elements - } - auto a = DummyOutputRange(); - auto spec = FormatSpec!Char(fmt); - //dummy write - spec.writeUpToNextSpec(a); - - enforce(spec.trailing.empty, - new Exception(text("Trailing characters in fmt string: '", spec.trailing))); - - return spec; -} - -/// -unittest -{ - auto spec = singleSpec("%2.3e"); - - assert(spec.trailing == ""); - assert(spec.spec == 'e'); - assert(spec.width == 2); - assert(spec.precision == 3); - - assertThrown(singleSpec("")); - assertThrown(singleSpec("2.3e")); - assertThrown(singleSpec("%2.3eTest")); -} - -/** -$(D bool)s are formatted as "true" or "false" with %s and as "1" or -"0" with integral-specific format specs. - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - BooleanTypeOf!T val = obj; - - if (f.spec == 's') - { - string s = val ? "true" : "false"; - if (!f.flDash) - { - // right align - if (f.width > s.length) - foreach (i ; 0 .. f.width - s.length) put(w, ' '); - put(w, s); - } - else - { - // left align - put(w, s); - if (f.width > s.length) - foreach (i ; 0 .. f.width - s.length) put(w, ' '); - } - } - else - formatValue(w, cast(int) val, f); -} - -/// -unittest -{ - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatValue(w, true, spec); - - assert(w.data == "true"); -} - -@safe pure unittest -{ - assertCTFEable!( - { - formatTest( false, "false" ); - formatTest( true, "true" ); - }); -} -unittest -{ - class C1 { bool val; alias val this; this(bool v){ val = v; } } - class C2 { bool val; alias val this; this(bool v){ val = v; } - override string toString() const { return "C"; } } - formatTest( new C1(false), "false" ); - formatTest( new C1(true), "true" ); - formatTest( new C2(false), "C" ); - formatTest( new C2(true), "C" ); - - struct S1 { bool val; alias val this; } - struct S2 { bool val; alias val this; - string toString() const { return "S"; } } - formatTest( S1(false), "false" ); - formatTest( S1(true), "true" ); - formatTest( S2(false), "S" ); - formatTest( S2(true), "S" ); -} - -unittest -{ - string t1 = format("[%6s] [%6s] [%-6s]", true, false, true); - assert(t1 == "[ true] [ false] [true ]"); - - string t2 = format("[%3s] [%-2s]", true, false); - assert(t2 == "[true] [false]"); -} - -/** -$(D null) literal is formatted as $(D "null"). - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char)) -{ - enforceFmt(f.spec == 's', - "null"); - - put(w, "null"); -} - -/// -unittest -{ - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatValue(w, null, spec); - - assert(w.data == "null"); -} - -@safe pure unittest -{ - assertCTFEable!( - { - formatTest( null, "null" ); - }); -} - -/** -Integrals are formatted like $(D printf) does. - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - import std.system : Endian; - alias U = IntegralTypeOf!T; - U val = obj; // Extracting alias this may be impure/system/may-throw - - if (f.spec == 'r') - { - // raw write, skip all else and write the thing - auto raw = (ref val)@trusted{ - return (cast(const char*) &val)[0 .. val.sizeof]; - }(val); - if (std.system.endian == Endian.littleEndian && f.flPlus - || std.system.endian == Endian.bigEndian && f.flDash) - { - // must swap bytes - foreach_reverse (c; raw) - put(w, c); - } - else - { - foreach (c; raw) - put(w, c); - } - return; - } - - uint base = - f.spec == 'x' || f.spec == 'X' ? 16 : - f.spec == 'o' ? 8 : - f.spec == 'b' ? 2 : - f.spec == 's' || f.spec == 'd' || f.spec == 'u' ? 10 : - 0; - enforceFmt(base > 0, - "integral"); - - // Forward on to formatIntegral to handle both U and const(U) - // Saves duplication of code for both versions. - static if (isSigned!U) - formatIntegral(w, cast( long) val, f, base, Unsigned!U.max); - else - formatIntegral(w, cast(ulong) val, f, base, U.max); -} - -/// -unittest -{ - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%d"); - formatValue(w, 1337, spec); - - assert(w.data == "1337"); -} - -private void formatIntegral(Writer, T, Char)(Writer w, const(T) val, ref FormatSpec!Char f, uint base, ulong mask) -{ - FormatSpec!Char fs = f; // fs is copy for change its values. - T arg = val; - - bool negative = (base == 10 && arg < 0); - if (negative) - { - arg = -arg; - } - - // All unsigned integral types should fit in ulong. - formatUnsigned(w, (cast(ulong) arg) & mask, fs, base, negative); -} - -private void formatUnsigned(Writer, Char)(Writer w, ulong arg, ref FormatSpec!Char fs, uint base, bool negative) -{ - if (fs.precision == fs.UNSPECIFIED) - { - // default precision for integrals is 1 - fs.precision = 1; - } - else - { - // if a precision is specified, the '0' flag is ignored. - fs.flZero = false; - } - - char leftPad = void; - if (!fs.flDash && !fs.flZero) - leftPad = ' '; - else if (!fs.flDash && fs.flZero) - leftPad = '0'; - else - leftPad = 0; - - // figure out sign and continue in unsigned mode - char forcedPrefix = void; - if (fs.flPlus) forcedPrefix = '+'; - else if (fs.flSpace) forcedPrefix = ' '; - else forcedPrefix = 0; - if (base != 10) - { - // non-10 bases are always unsigned - forcedPrefix = 0; - } - else if (negative) - { - // argument is signed - forcedPrefix = '-'; - } - // fill the digits - char[64] buffer; // 64 bits in base 2 at most - char[] digits; - { - uint i = buffer.length; - auto n = arg; - do - { - --i; - buffer[i] = cast(char) (n % base); - n /= base; - if (buffer[i] < 10) buffer[i] += '0'; - else buffer[i] += (fs.spec == 'x' ? 'a' : 'A') - 10; - } while (n); - digits = buffer[i .. $]; // got the digits without the sign - } - // adjust precision to print a '0' for octal if alternate format is on - if (base == 8 && fs.flHash - && (fs.precision <= digits.length)) // too low precision - { - //fs.precision = digits.length + (arg != 0); - forcedPrefix = '0'; - } - // write left pad; write sign; write 0x or 0X; write digits; - // write right pad - // Writing left pad - ptrdiff_t spacesToPrint = - fs.width // start with the minimum width - - digits.length // take away digits to print - - (forcedPrefix != 0) // take away the sign if any - - (base == 16 && fs.flHash && arg ? 2 : 0); // 0x or 0X - const ptrdiff_t delta = fs.precision - digits.length; - if (delta > 0) spacesToPrint -= delta; - if (spacesToPrint > 0) // need to do some padding - { - if (leftPad == '0') - { - // pad with zeros - - fs.precision = - cast(typeof(fs.precision)) (spacesToPrint + digits.length); - //to!(typeof(fs.precision))(spacesToPrint + digits.length); - } - else if (leftPad) foreach (i ; 0 .. spacesToPrint) put(w, ' '); - } - // write sign - if (forcedPrefix) put(w, forcedPrefix); - // write 0x or 0X - if (base == 16 && fs.flHash && arg) { - // @@@ overcome bug in dmd; - //w.write(fs.spec == 'x' ? "0x" : "0X"); //crashes the compiler - put(w, '0'); - put(w, fs.spec == 'x' ? 'x' : 'X'); // x or X - } - // write the digits - if (arg || fs.precision) - { - ptrdiff_t zerosToPrint = fs.precision - digits.length; - foreach (i ; 0 .. zerosToPrint) put(w, '0'); - put(w, digits); - } - // write the spaces to the right if left-align - if (!leftPad) foreach (i ; 0 .. spacesToPrint) put(w, ' '); -} - -@safe pure unittest -{ - assertCTFEable!( - { - formatTest( 10, "10" ); - }); -} -unittest -{ - class C1 { long val; alias val this; this(long v){ val = v; } } - class C2 { long val; alias val this; this(long v){ val = v; } - override string toString() const { return "C"; } } - formatTest( new C1(10), "10" ); - formatTest( new C2(10), "C" ); - - struct S1 { long val; alias val this; } - struct S2 { long val; alias val this; - string toString() const { return "S"; } } - formatTest( S1(10), "10" ); - formatTest( S2(10), "S" ); -} - -// bugzilla 9117 -unittest -{ - static struct Frop {} - - static struct Foo - { - int n = 0; - alias n this; - T opCast(T) () if (is(T == Frop)) - { - return Frop(); - } - string toString() - { - return "Foo"; - } + auto tmp = trailing[i .. $]; + parse!int(tmp); // skip digits + i = arrayPtrDiff(tmp, trailing); + } + else if (isDigit(trailing[i])) + { + auto tmp = trailing[i .. $]; + precision = parse!int(tmp); + i = arrayPtrDiff(tmp, trailing); + } + else + { + // "." was specified, but nothing after it + precision = 0; + } + break; + default: + // this is the format char + spec = cast(char) trailing[i++]; + trailing = trailing[i .. $]; + return; + } // end switch + } // end for + throw new Exception(text("Incorrect format specifier: ", trailing)); } - static struct Bar + //-------------------------------------------------------------------------- + private bool readUpToNextSpec(R)(ref R r) { - Foo foo; - alias foo this; - string toString() - { - return "Bar"; - } - } + import std.ascii : isLower, isWhite; + import std.utf : stride; - const(char)[] result; - void put(const char[] s){ result ~= s; } - - Foo foo; - formattedWrite(&put, "%s", foo); // OK - assert(result == "Foo"); - - result = null; - - Bar bar; - formattedWrite(&put, "%s", bar); // NG - assert(result == "Bar"); -} - -/** -Floating-point values are formatted like $(D printf) does. - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - import core.stdc.stdio : snprintf; - import std.system : Endian; - import std.algorithm : find, min; - FormatSpec!Char fs = f; // fs is copy for change its values. - FloatingPointTypeOf!T val = obj; - - if (fs.spec == 'r') - { - // raw write, skip all else and write the thing - auto raw = (ref val)@trusted{ - return (cast(const char*) &val)[0 .. val.sizeof]; - }(val); - if (std.system.endian == Endian.littleEndian && f.flPlus - || std.system.endian == Endian.bigEndian && f.flDash) + // Reset content + if (__ctfe) { - // must swap bytes - foreach_reverse (c; raw) - put(w, c); + flDash = false; + flZero = false; + flSpace = false; + flPlus = false; + flHash = false; + flSeparator = false; } else { - foreach (c; raw) - put(w, c); + allFlags = 0; } - return; - } - enforceFmt(find("fgFGaAeEs", fs.spec).length, - "floating"); - - version (CRuntime_Microsoft) - { - import std.math : isNaN, isInfinity; - double tval = val; // convert early to get "inf" in case of overflow - string s; - if (isNaN(tval)) - s = "nan"; // snprintf writes 1.#QNAN - else if (isInfinity(tval)) - s = val >= 0 ? "inf" : "-inf"; // snprintf writes 1.#INF - - if (s.length > 0) + width = 0; + precision = UNSPECIFIED; + nested = null; + // Parse the spec + while (trailing.length) { - version(none) - { - return formatValue(w, s, f); - } - else // FIXME:workaroun - { - s = s[0 .. f.precision < $ ? f.precision : $]; - if (!f.flDash) + const c = trailing[0]; + if (c == '%' && trailing.length > 1) { - // right align - if (f.width > s.length) - foreach (j ; 0 .. f.width - s.length) put(w, ' '); - put(w, s); + const c2 = trailing[1]; + if (c2 == '%') + { + assert(!r.empty); + // Require a '%' + if (r.front != '%') break; + trailing = trailing[2 .. $]; + r.popFront(); + } + else + { + enforce(isLower(c2) || c2 == '*' || + c2 == '(', + text("'%", c2, + "' not supported with formatted read")); + trailing = trailing[1 .. $]; + fillUp(); + return true; + } } else { - // left align - put(w, s); - if (f.width > s.length) - foreach (j ; 0 .. f.width - s.length) put(w, ' '); - } - return; - } - } - } - else - alias val tval; - if (fs.spec == 's') fs.spec = 'g'; - char[1 /*%*/ + 5 /*flags*/ + 3 /*width.prec*/ + 2 /*format*/ - + 1 /*\0*/] sprintfSpec = void; - sprintfSpec[0] = '%'; - uint i = 1; - if (fs.flDash) sprintfSpec[i++] = '-'; - if (fs.flPlus) sprintfSpec[i++] = '+'; - if (fs.flZero) sprintfSpec[i++] = '0'; - if (fs.flSpace) sprintfSpec[i++] = ' '; - if (fs.flHash) sprintfSpec[i++] = '#'; - sprintfSpec[i .. i + 3] = "*.*"; - i += 3; - if (is(Unqual!(typeof(val)) == real)) sprintfSpec[i++] = 'L'; - sprintfSpec[i++] = fs.spec; - sprintfSpec[i] = 0; - //printf("format: '%s'; geeba: %g\n", sprintfSpec.ptr, val); - char[512] buf; - - immutable n = ()@trusted{ - return snprintf(buf.ptr, buf.length, - sprintfSpec.ptr, - fs.width, - // negative precision is same as no precision specified - fs.precision == fs.UNSPECIFIED ? -1 : fs.precision, - tval); - }(); - - enforceFmt(n >= 0, - "floating point formatting failure"); - put(w, buf[0 .. min(n, buf.length-1)]); -} - -/// -unittest -{ - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%.1f"); - formatValue(w, 1337.7, spec); - - assert(w.data == "1337.7"); -} - -@safe /*pure*/ unittest // formatting floating point values is now impure -{ - import std.conv : to; - foreach (T; TypeTuple!(float, double, real)) - { - formatTest( to!( T)(5.5), "5.5" ); - formatTest( to!( const T)(5.5), "5.5" ); - formatTest( to!(immutable T)(5.5), "5.5" ); - - // bionic doesn't support lower-case string formatting of nan yet - version(Android) { formatTest( T.nan, "NaN" ); } - else { formatTest( T.nan, "nan" ); } - } -} - -unittest -{ - formatTest( 2.25, "2.25" ); - - class C1 { double val; alias val this; this(double v){ val = v; } } - class C2 { double val; alias val this; this(double v){ val = v; } - override string toString() const { return "C"; } } - formatTest( new C1(2.25), "2.25" ); - formatTest( new C2(2.25), "C" ); - - struct S1 { double val; alias val this; } - struct S2 { double val; alias val this; - string toString() const { return "S"; } } - formatTest( S1(2.25), "2.25" ); - formatTest( S2(2.25), "S" ); -} - -/* -Formatting a $(D creal) is deprecated but still kept around for a while. - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(Unqual!T : creal) && !is(T == enum) && !hasToString!(T, Char)) -{ - creal val = obj; - - formatValue(w, val.re, f); - if (val.im >= 0) - { - put(w, '+'); - } - formatValue(w, val.im, f); - put(w, 'i'); -} - -@safe /*pure*/ unittest // formatting floating point values is now impure -{ - import std.conv : to; - foreach (T; TypeTuple!(cfloat, cdouble, creal)) - { - formatTest( to!( T)(1 + 1i), "1+1i" ); - formatTest( to!( const T)(1 + 1i), "1+1i" ); - formatTest( to!(immutable T)(1 + 1i), "1+1i" ); - } - foreach (T; TypeTuple!(cfloat, cdouble, creal)) - { - formatTest( to!( T)(0 - 3i), "0-3i" ); - formatTest( to!( const T)(0 - 3i), "0-3i" ); - formatTest( to!(immutable T)(0 - 3i), "0-3i" ); - } -} - -unittest -{ - formatTest( 3+2.25i, "3+2.25i" ); - - class C1 { cdouble val; alias val this; this(cdouble v){ val = v; } } - class C2 { cdouble val; alias val this; this(cdouble v){ val = v; } - override string toString() const { return "C"; } } - formatTest( new C1(3+2.25i), "3+2.25i" ); - formatTest( new C2(3+2.25i), "C" ); - - struct S1 { cdouble val; alias val this; } - struct S2 { cdouble val; alias val this; - string toString() const { return "S"; } } - formatTest( S1(3+2.25i), "3+2.25i" ); - formatTest( S2(3+2.25i), "S" ); -} - -/* - Formatting an $(D ireal) is deprecated but still kept around for a while. + if (c == ' ') + { + while (!r.empty && isWhite(r.front)) r.popFront(); + //r = std.algorithm.find!(not!(isWhite))(r); + } + else + { + enforce(!r.empty, + text("parseToFormatSpec: Cannot find character '", + c, "' in the input string.")); + if (r.front != trailing.front) break; + r.popFront(); + } + trailing = trailing[stride(trailing, 0) .. $]; + } + } + return false; + } -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(Unqual!T : ireal) && !is(T == enum) && !hasToString!(T, Char)) -{ - ireal val = obj; + private string getCurFmtStr() const + { + import std.array : appender; + auto w = appender!string(); + auto f = FormatSpec!Char("%s"); // for stringnize - formatValue(w, val.im, f); - put(w, 'i'); -} + put(w, '%'); + if (indexStart != 0) + { + formatValue(w, indexStart, f); + put(w, '$'); + } + if (flDash) put(w, '-'); + if (flZero) put(w, '0'); + if (flSpace) put(w, ' '); + if (flPlus) put(w, '+'); + if (flHash) put(w, '#'); + if (flSeparator) put(w, ','); + if (width != 0) + formatValue(w, width, f); + if (precision != FormatSpec!Char.UNSPECIFIED) + { + put(w, '.'); + formatValue(w, precision, f); + } + put(w, spec); + return w.data; + } -@safe /*pure*/ unittest // formatting floating point values is now impure -{ - import std.conv : to; - foreach (T; TypeTuple!(ifloat, idouble, ireal)) + @safe unittest { - formatTest( to!( T)(1i), "1i" ); - formatTest( to!( const T)(1i), "1i" ); - formatTest( to!(immutable T)(1i), "1i" ); + // issue 5237 + import std.array; + auto w = appender!string(); + auto f = FormatSpec!char("%.16f"); + f.writeUpToNextSpec(w); // dummy eating + assert(f.spec == 'f'); + auto fmt = f.getCurFmtStr(); + assert(fmt == "%.16f"); } -} - -unittest -{ - formatTest( 2.25i, "2.25i" ); - - class C1 { idouble val; alias val this; this(idouble v){ val = v; } } - class C2 { idouble val; alias val this; this(idouble v){ val = v; } - override string toString() const { return "C"; } } - formatTest( new C1(2.25i), "2.25i" ); - formatTest( new C2(2.25i), "C" ); - - struct S1 { idouble val; alias val this; } - struct S2 { idouble val; alias val this; - string toString() const { return "S"; } } - formatTest( S1(2.25i), "2.25i" ); - formatTest( S2(2.25i), "S" ); -} - -/** -Individual characters ($(D char), $(D wchar), or $(D dchar)) are formatted as -Unicode characters with %s and as integers with integral-specific format -specs. - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - CharTypeOf!T val = obj; - if (f.spec == 's' || f.spec == 'c') + private const(Char)[] headUpToNextSpec() { - put(w, val); + import std.array : appender; + auto w = appender!(typeof(return))(); + auto tr = trailing; + + while (tr.length) + { + if (tr[0] == '%') + { + if (tr.length > 1 && tr[1] == '%') + { + tr = tr[2 .. $]; + w.put('%'); + } + else + break; + } + else + { + w.put(tr.front); + tr.popFront(); + } + } + return w.data; } - else + + string toString() { - alias U = TypeTuple!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2]; - formatValue(w, cast(U) val, f); + return text("address = ", cast(void*) &this, + "\nwidth = ", width, + "\nprecision = ", precision, + "\nspec = ", spec, + "\nindexStart = ", indexStart, + "\nindexEnd = ", indexEnd, + "\nflDash = ", flDash, + "\nflZero = ", flZero, + "\nflSpace = ", flSpace, + "\nflPlus = ", flPlus, + "\nflHash = ", flHash, + "\nflSeparator = ", flSeparator, + "\nnested = ", nested, + "\ntrailing = ", trailing, "\n"); } } /// -unittest -{ - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%c"); - formatValue(w, 'a', spec); - - assert(w.data == "a"); -} - @safe pure unittest { - assertCTFEable!( - { - formatTest( 'c', "c" ); - }); -} - -unittest -{ - class C1 { char val; alias val this; this(char v){ val = v; } } - class C2 { char val; alias val this; this(char v){ val = v; } - override string toString() const { return "C"; } } - formatTest( new C1('c'), "c" ); - formatTest( new C2('c'), "C" ); - - struct S1 { char val; alias val this; } - struct S2 { char val; alias val this; - string toString() const { return "S"; } } - formatTest( S1('c'), "c" ); - formatTest( S2('c'), "S" ); -} + import std.array; + auto a = appender!(string)(); + auto fmt = "Number: %2.4e\nString: %s"; + auto f = FormatSpec!char(fmt); -@safe pure unittest -{ - //Little Endian - formatTest( "%-r", cast( char)'c', ['c' ] ); - formatTest( "%-r", cast(wchar)'c', ['c', 0 ] ); - formatTest( "%-r", cast(dchar)'c', ['c', 0, 0, 0] ); - formatTest( "%-r", '本', ['\x2c', '\x67'] ); + f.writeUpToNextSpec(a); - //Big Endian - formatTest( "%+r", cast( char)'c', [ 'c'] ); - formatTest( "%+r", cast(wchar)'c', [0, 'c'] ); - formatTest( "%+r", cast(dchar)'c', [0, 0, 0, 'c'] ); - formatTest( "%+r", '本', ['\x67', '\x2c'] ); -} + assert(a.data == "Number: "); + assert(f.trailing == "\nString: %s"); + assert(f.spec == 'e'); + assert(f.width == 2); + assert(f.precision == 4); -/** -Strings are formatted like $(D printf) does. + f.writeUpToNextSpec(a); -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - Unqual!(StringTypeOf!T) val = obj; // for `alias this`, see bug5371 - formatRange(w, val, f); + assert(a.data == "Number: \nString: "); + assert(f.trailing == ""); + assert(f.spec == 's'); } -/// -unittest +// Issue 14059 +@safe unittest { import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatValue(w, "hello", spec); + auto a = appender!(string)(); - assert(w.data == "hello"); -} + auto f = FormatSpec!char("%-(%s%"); // %)") + assertThrown(f.writeUpToNextSpec(a)); -unittest -{ - formatTest( "abc", "abc" ); + f = FormatSpec!char("%(%-"); // %)") + assertThrown(f.writeUpToNextSpec(a)); } -unittest +@safe unittest { - // Test for bug 5371 for classes - class C1 { const string var; alias var this; this(string s){ var = s; } } - class C2 { string var; alias var this; this(string s){ var = s; } } - formatTest( new C1("c1"), "c1" ); - formatTest( new C2("c2"), "c2" ); - - // Test for bug 5371 for structs - struct S1 { const string var; alias var this; } - struct S2 { string var; alias var this; } - formatTest( S1("s1"), "s1" ); - formatTest( S2("s2"), "s2" ); -} + import std.array : appender; + auto a = appender!(string)(); -unittest -{ - class C3 { string val; alias val this; this(string s){ val = s; } - override string toString() const { return "C"; } } - formatTest( new C3("c3"), "C" ); + auto f = FormatSpec!char("%,d"); + f.writeUpToNextSpec(a); - struct S3 { string val; alias val this; - string toString() const { return "S"; } } - formatTest( S3("s3"), "S" ); -} + assert(f.spec == 'd', format("%s", f.spec)); + assert(f.precision == FormatSpec!char.UNSPECIFIED); + assert(f.separators == 3); -@safe pure unittest -{ - //Little Endian - formatTest( "%-r", "ab"c, ['a' , 'b' ] ); - formatTest( "%-r", "ab"w, ['a', 0 , 'b', 0 ] ); - formatTest( "%-r", "ab"d, ['a', 0, 0, 0, 'b', 0, 0, 0] ); - formatTest( "%-r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] ); - formatTest( "%-r", "日本語"w, ['\xe5', '\x65', '\x2c', '\x67', '\x9e', '\x8a' ] ); - formatTest( "%-r", "日本語"d, ['\xe5', '\x65', '\x00', '\x00', '\x2c', '\x67', '\x00', '\x00', '\x9e', '\x8a', '\x00', '\x00'] ); + f = FormatSpec!char("%5,10f"); + f.writeUpToNextSpec(a); + assert(f.spec == 'f', format("%s", f.spec)); + assert(f.separators == 10); + assert(f.width == 5); - //Big Endian - formatTest( "%+r", "ab"c, [ 'a', 'b'] ); - formatTest( "%+r", "ab"w, [ 0, 'a', 0, 'b'] ); - formatTest( "%+r", "ab"d, [0, 0, 0, 'a', 0, 0, 0, 'b'] ); - formatTest( "%+r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] ); - formatTest( "%+r", "日本語"w, [ '\x65', '\xe5', '\x67', '\x2c', '\x8a', '\x9e'] ); - formatTest( "%+r", "日本語"d, ['\x00', '\x00', '\x65', '\xe5', '\x00', '\x00', '\x67', '\x2c', '\x00', '\x00', '\x8a', '\x9e'] ); + f = FormatSpec!char("%5,10.4f"); + f.writeUpToNextSpec(a); + assert(f.spec == 'f', format("%s", f.spec)); + assert(f.separators == 10); + assert(f.width == 5); + assert(f.precision == 4); } /** -Static-size arrays are formatted as dynamic arrays. - -Params: - w = The $(D OutputRange) to write to. - obj = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, auto ref T obj, ref FormatSpec!Char f) -if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) -{ - formatValue(w, obj[], f); -} +Helper function that returns a $(D FormatSpec) for a single specifier given +in $(D fmt). -/// -unittest +Params: + fmt = A format specifier. + +Returns: + A $(D FormatSpec) with the specifier parsed. +Throws: + An `Exception` when more than one specifier is given or the specifier + is malformed. + */ +FormatSpec!Char singleSpec(Char)(Char[] fmt) { - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); - char[2] two = ['a', 'b']; - formatValue(w, two, spec); + import std.conv : text; + enforce(fmt.length >= 2, "fmt must be at least 2 characters long"); + enforce(fmt.front == '%', "fmt must start with a '%' character"); - assert(w.data == "ab"); + static struct DummyOutputRange { + void put(C)(C[] buf) {} // eat elements + } + auto a = DummyOutputRange(); + auto spec = FormatSpec!Char(fmt); + //dummy write + spec.writeUpToNextSpec(a); + + enforce(spec.trailing.empty, + text("Trailing characters in fmt string: '", spec.trailing)); + + return spec; } -unittest // Test for issue 8310 +/// +@safe pure unittest { - import std.array : appender; - FormatSpec!char f; - auto w = appender!string(); + import std.exception : assertThrown; + auto spec = singleSpec("%2.3e"); - char[2] two = ['a', 'b']; - formatValue(w, two, f); + assert(spec.trailing == ""); + assert(spec.spec == 'e'); + assert(spec.width == 2); + assert(spec.precision == 3); - char[2] getTwo(){ return two; } - formatValue(w, getTwo(), f); + assertThrown(singleSpec("")); + assertThrown(singleSpec("2.3e")); + assertThrown(singleSpec("%2.3eTest")); } /** -Dynamic arrays are formatted as input ranges. - -Specializations: - $(UL $(LI $(D void[]) is formatted like $(D ubyte[]).) - $(LI Const array is converted to input range by removing its qualifier.)) +$(D bool)s are formatted as "true" or "false" with %s and as "1" or +"0" with integral-specific format specs. Params: w = The $(D OutputRange) to write to. obj = The value to write. f = The $(D FormatSpec) defining how to write the value. */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { - static if (is(const(ArrayTypeOf!T) == const(void[]))) - { - formatValue(w, cast(const ubyte[])obj, f); - } - else static if (!isInputRange!T) + BooleanTypeOf!T val = obj; + + if (f.spec == 's') { - alias U = Unqual!(ArrayTypeOf!T); - static assert(isInputRange!U); - U val = obj; - formatValue(w, val, f); + string s = val ? "true" : "false"; + if (!f.flDash) + { + // right align + if (f.width > s.length) + foreach (i ; 0 .. f.width - s.length) put(w, ' '); + put(w, s); + } + else + { + // left align + put(w, s); + if (f.width > s.length) + foreach (i ; 0 .. f.width - s.length) put(w, ' '); + } } else - { - formatRange(w, obj, f); - } + formatValue(w, cast(int) val, f); } /// -unittest +@safe pure unittest { import std.array : appender; auto w = appender!string(); auto spec = singleSpec("%s"); - auto two = [1, 2]; - formatValue(w, two, spec); + formatValue(w, true, spec); - assert(w.data == "[1, 2]"); + assert(w.data == "true"); } -// alias this, input range I/F, and toString() -unittest +@safe pure unittest { - struct S(int flags) - { - int[] arr; - static if (flags & 1) - alias arr this; - - static if (flags & 2) - { - @property bool empty() const { return arr.length == 0; } - @property int front() const { return arr[0] * 2; } - void popFront() { arr = arr[1..$]; } - } - - static if (flags & 4) - string toString() const { return "S"; } - } - formatTest(S!0b000([0, 1, 2]), "S!0([0, 1, 2])"); - formatTest(S!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628 - formatTest(S!0b010([0, 1, 2]), "[0, 2, 4]"); - formatTest(S!0b011([0, 1, 2]), "[0, 2, 4]"); - formatTest(S!0b100([0, 1, 2]), "S"); - formatTest(S!0b101([0, 1, 2]), "S"); // Test for bug 7628 - formatTest(S!0b110([0, 1, 2]), "S"); - formatTest(S!0b111([0, 1, 2]), "S"); - - class C(uint flags) + assertCTFEable!( { - int[] arr; - static if (flags & 1) - alias arr this; - - this(int[] a) { arr = a; } - - static if (flags & 2) - { - @property bool empty() const { return arr.length == 0; } - @property int front() const { return arr[0] * 2; } - void popFront() { arr = arr[1..$]; } - } - - static if (flags & 4) - override string toString() const { return "C"; } - } - formatTest(new C!0b000([0, 1, 2]), (new C!0b000([])).toString()); - formatTest(new C!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628 - formatTest(new C!0b010([0, 1, 2]), "[0, 2, 4]"); - formatTest(new C!0b011([0, 1, 2]), "[0, 2, 4]"); - formatTest(new C!0b100([0, 1, 2]), "C"); - formatTest(new C!0b101([0, 1, 2]), "C"); // Test for bug 7628 - formatTest(new C!0b110([0, 1, 2]), "C"); - formatTest(new C!0b111([0, 1, 2]), "C"); + formatTest( false, "false" ); + formatTest( true, "true" ); + }); } - -unittest +@system unittest { - // void[] - void[] val0; - formatTest( val0, "[]" ); - - void[] val = cast(void[])cast(ubyte[])[1, 2, 3]; - formatTest( val, "[1, 2, 3]" ); - - void[0] sval0 = []; - formatTest( sval0, "[]"); + class C1 { bool val; alias val this; this(bool v){ val = v; } } + class C2 { bool val; alias val this; this(bool v){ val = v; } + override string toString() const { return "C"; } } + formatTest( new C1(false), "false" ); + formatTest( new C1(true), "true" ); + formatTest( new C2(false), "C" ); + formatTest( new C2(true), "C" ); - void[3] sval = cast(void[3])cast(ubyte[3])[1, 2, 3]; - formatTest( sval, "[1, 2, 3]" ); + struct S1 { bool val; alias val this; } + struct S2 { bool val; alias val this; + string toString() const { return "S"; } } + formatTest( S1(false), "false" ); + formatTest( S1(true), "true" ); + formatTest( S2(false), "S" ); + formatTest( S2(true), "S" ); } -unittest +@safe pure unittest { - // const(T[]) -> const(T)[] - const short[] a = [1, 2, 3]; - formatTest( a, "[1, 2, 3]" ); + string t1 = format("[%6s] [%6s] [%-6s]", true, false, true); + assert(t1 == "[ true] [ false] [true ]"); - struct S { const(int[]) arr; alias arr this; } - auto s = S([1,2,3]); - formatTest( s, "[1, 2, 3]" ); + string t2 = format("[%3s] [%-2s]", true, false); + assert(t2 == "[true] [false]"); } -unittest +/** +$(D null) literal is formatted as $(D "null"). + +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char)) { - // 6640 - struct Range - { - string value; - @property bool empty() const { return !value.length; } - @property dchar front() const { return value.front; } - void popFront() { value.popFront(); } + enforceFmt(f.spec == 's', + "null literal cannot match %" ~ f.spec); - @property size_t length() const { return value.length; } - } - immutable table = - [ - ["[%s]", "[string]"], - ["[%10s]", "[ string]"], - ["[%-10s]", "[string ]"], - ["[%(%02x %)]", "[73 74 72 69 6e 67]"], - ["[%(%c %)]", "[s t r i n g]"], - ]; - foreach (e; table) - { - formatTest(e[0], "string", e[1]); - formatTest(e[0], Range("string"), e[1]); - } + put(w, "null"); } -unittest +/// +@safe pure unittest { - // string literal from valid UTF sequence is encoding free. - foreach (StrType; TypeTuple!(string, wstring, dstring)) - { - // Valid and printable (ASCII) - formatTest( [cast(StrType)"hello"], - `["hello"]` ); + import std.array : appender; + auto w = appender!string(); + auto spec = singleSpec("%s"); + formatValue(w, null, spec); - // 1 character escape sequences (' is not escaped in strings) - formatTest( [cast(StrType)"\"'\0\\\a\b\f\n\r\t\v"], - `["\"'\0\\\a\b\f\n\r\t\v"]` ); + assert(w.data == "null"); +} - // 1 character optional escape sequences - formatTest( [cast(StrType)"\'\?"], - `["'?"]` ); +@safe pure unittest +{ + assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p'); - // Valid and non-printable code point (<= U+FF) - formatTest( [cast(StrType)"\x10\x1F\x20test"], - `["\x10\x1F test"]` ); + assertCTFEable!( + { + formatTest( null, "null" ); + }); +} - // Valid and non-printable code point (<= U+FFFF) - formatTest( [cast(StrType)"\u200B..\u200F"], - `["\u200B..\u200F"]` ); +/** +Integrals are formatted like $(D printf) does. - // Valid and non-printable code point (<= U+10FFFF) - formatTest( [cast(StrType)"\U000E0020..\U000E007F"], - `["\U000E0020..\U000E007F"]` ); - } +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) +{ + alias U = IntegralTypeOf!T; + U val = obj; // Extracting alias this may be impure/system/may-throw - // invalid UTF sequence needs hex-string literal postfix (c/w/d) + if (f.spec == 'r') { - // U+FFFF with UTF-8 (Invalid code point for interchange) - formatTest( [cast(string)[0xEF, 0xBF, 0xBF]], - `[x"EF BF BF"c]` ); + // raw write, skip all else and write the thing + auto raw = (ref val)@trusted{ + return (cast(const char*) &val)[0 .. val.sizeof]; + }(val); + if (needToSwapEndianess(f)) + { + foreach_reverse (c; raw) + put(w, c); + } + else + { + foreach (c; raw) + put(w, c); + } + return; + } - // U+FFFF with UTF-16 (Invalid code point for interchange) - formatTest( [cast(wstring)[0xFFFF]], - `[x"FFFF"w]` ); + immutable uint base = + f.spec == 'x' || f.spec == 'X' ? 16 : + f.spec == 'o' ? 8 : + f.spec == 'b' ? 2 : + f.spec == 's' || f.spec == 'd' || f.spec == 'u' ? 10 : + 0; + enforceFmt(base > 0, + "incompatible format character for integral argument: %" ~ f.spec); - // U+FFFF with UTF-32 (Invalid code point for interchange) - formatTest( [cast(dstring)[0xFFFF]], - `[x"FFFF"d]` ); - } + // Forward on to formatIntegral to handle both U and const(U) + // Saves duplication of code for both versions. + static if (is(ucent) && (is(U == cent) || is(U == ucent))) + alias C = U; + else static if (isSigned!U) + alias C = long; + else + alias C = ulong; + formatIntegral(w, cast(C) val, f, base, Unsigned!U.max); } -unittest +/// +@safe pure unittest { - // nested range formatting with array of string - formatTest( "%({%(%02x %)}%| %)", ["test", "msg"], - `{74 65 73 74} {6d 73 67}` ); + import std.array : appender; + auto w = appender!string(); + auto spec = singleSpec("%d"); + formatValue(w, 1337, spec); + + assert(w.data == "1337"); } -unittest +private void formatIntegral(Writer, T, Char)(Writer w, const(T) val, const ref FormatSpec!Char fs, + uint base, ulong mask) { - // stop auto escaping inside range formatting - auto arr = ["hello", "world"]; - formatTest( "%(%s, %)", arr, `"hello", "world"` ); - formatTest( "%-(%s, %)", arr, `hello, world` ); + T arg = val; - auto aa1 = [1:"hello", 2:"world"]; - formatTest( "%(%s:%s, %)", aa1, [`1:"hello", 2:"world"`, `2:"world", 1:"hello"`] ); - formatTest( "%-(%s:%s, %)", aa1, [`1:hello, 2:world`, `2:world, 1:hello`] ); + immutable negative = (base == 10 && arg < 0); + if (negative) + { + arg = -arg; + } - auto aa2 = [1:["ab", "cd"], 2:["ef", "gh"]]; - formatTest( "%-(%s:%s, %)", aa2, [`1:["ab", "cd"], 2:["ef", "gh"]`, `2:["ef", "gh"], 1:["ab", "cd"]`] ); - formatTest( "%-(%s:%(%s%), %)", aa2, [`1:"ab""cd", 2:"ef""gh"`, `2:"ef""gh", 1:"ab""cd"`] ); - formatTest( "%-(%s:%-(%s%)%|, %)", aa2, [`1:abcd, 2:efgh`, `2:efgh, 1:abcd`] ); + // All unsigned integral types should fit in ulong. + static if (is(ucent) && is(typeof(arg) == ucent)) + formatUnsigned(w, (cast(ucent) arg) & mask, fs, base, negative); + else + formatUnsigned(w, (cast(ulong) arg) & mask, fs, base, negative); } -// input range formatting -private void formatRange(Writer, T, Char)(ref Writer w, ref T val, ref FormatSpec!Char f) -if (isInputRange!T) +private void formatUnsigned(Writer, T, Char)(Writer w, T arg, const ref FormatSpec!Char fs, uint base, bool negative) { - import std.conv : text; + /* Write string: + * leftpad prefix1 prefix2 zerofill digits rightpad + */ - // Formatting character ranges like string - if (f.spec == 's') + /* Convert arg to digits[]. + * Note that 0 becomes an empty digits[] + */ + char[64] buffer = void; // 64 bits in base 2 at most + char[] digits; + if (arg < base && base <= 10 && arg) { - alias E = ElementType!T; - - static if (!is(E == enum) && is(CharTypeOf!E)) + // Most numbers are a single digit - avoid expensive divide + buffer[0] = cast(char)(arg + '0'); + digits = buffer[0 .. 1]; + } + else + { + size_t i = buffer.length; + while (arg) { - static if (is(StringTypeOf!T)) - { - auto s = val[0 .. f.precision < $ ? f.precision : $]; - if (!f.flDash) - { - // right align - if (f.width > s.length) - foreach (i ; 0 .. f.width - s.length) put(w, ' '); - put(w, s); - } - else - { - // left align - put(w, s); - if (f.width > s.length) - foreach (i ; 0 .. f.width - s.length) put(w, ' '); - } - } + --i; + char c = cast(char) (arg % base); + arg /= base; + if (c < 10) + buffer[i] = cast(char)(c + '0'); else - { - if (!f.flDash) - { - static if (hasLength!T) - { - // right align - auto len = val.length; - } - else static if (isForwardRange!T && !isInfinite!T) - { - auto len = walkLength(val.save); - } - else - { - enforce(f.width == 0, "Cannot right-align a range without length"); - size_t len = 0; - } - if (f.precision != f.UNSPECIFIED && len > f.precision) - len = f.precision; + buffer[i] = cast(char)(c + (fs.spec == 'x' ? 'a' - 10 : 'A' - 10)); + } + digits = buffer[i .. $]; // got the digits without the sign + } - if (f.width > len) - foreach (i ; 0 .. f.width - len) - put(w, ' '); - if (f.precision == f.UNSPECIFIED) - put(w, val); - else - { - size_t printed = 0; - for (; !val.empty && printed < f.precision; val.popFront(), ++printed) - put(w, val.front); - } - } - else - { - size_t printed = void; - // left align - if (f.precision == f.UNSPECIFIED) - { - static if (hasLength!T) - { - printed = val.length; - put(w, val); - } - else - { - printed = 0; - for (; !val.empty; val.popFront(), ++printed) - put(w, val.front); - } - } - else - { - printed = 0; - for (; !val.empty && printed < f.precision; val.popFront(), ++printed) - put(w, val.front); - } + immutable precision = (fs.precision == fs.UNSPECIFIED) ? 1 : fs.precision; - if (f.width > printed) - foreach (i ; 0 .. f.width - printed) - put(w, ' '); - } - } - } - else - { - put(w, f.seqBefore); - if (!val.empty) - { - formatElement(w, val.front, f); - val.popFront(); - for (size_t i; !val.empty; val.popFront(), ++i) - { - put(w, f.seqSeparator); - formatElement(w, val.front, f); - } - } - static if (!isInfinite!T) put(w, f.seqAfter); - } - } - else if (f.spec == 'r') + char padChar = 0; + if (!fs.flDash) { - static if (is(DynamicArrayTypeOf!T)) - { - alias ARR = DynamicArrayTypeOf!T; - foreach (e ; cast(ARR)val) - { - formatValue(w, e, f); - } - } - else - { - for (size_t i; !val.empty; val.popFront(), ++i) - { - formatValue(w, val.front, f); - } - } + padChar = (fs.flZero && fs.precision == fs.UNSPECIFIED) ? '0' : ' '; } - else if (f.spec == '(') + + // Compute prefix1 and prefix2 + char prefix1 = 0; + char prefix2 = 0; + if (base == 10) { - if (val.empty) - return; - // Nested specifier is to be used - for (;;) - { - auto fmt = FormatSpec!Char(f.nested); - fmt.writeUpToNextSpec(w); - if (f.flDash) - formatValue(w, val.front, fmt); - else - formatElement(w, val.front, fmt); - if (f.sep.ptr) - { - put(w, fmt.trailing); - val.popFront(); - if (val.empty) - break; - put(w, f.sep); - } - else - { - val.popFront(); - if (val.empty) - break; - put(w, fmt.trailing); - } - } + if (negative) + prefix1 = '-'; + else if (fs.flPlus) + prefix1 = '+'; + else if (fs.flSpace) + prefix1 = ' '; } - else - throw new Exception(text("Incorrect format specifier for range: %", f.spec)); -} - -// character formatting with ecaping -private void formatChar(Writer)(Writer w, in dchar c, in char quote) -{ - import std.uni : isGraphical; - - if (std.uni.isGraphical(c)) + else if (base == 16 && fs.flHash && digits.length) { - if (c == quote || c == '\\') - { - put(w, '\\'); - put(w, c); - } - else - put(w, c); + prefix1 = '0'; + prefix2 = fs.spec == 'x' ? 'x' : 'X'; } - else if (c <= 0xFF) + // adjust precision to print a '0' for octal if alternate format is on + else if (base == 8 && fs.flHash && + (precision <= 1 || precision <= digits.length) && // too low precision + digits.length > 0) + prefix1 = '0'; + + size_t zerofill = precision > digits.length ? precision - digits.length : 0; + size_t leftpad = 0; + size_t rightpad = 0; + + immutable ptrdiff_t spacesToPrint = + fs.width - ( + (prefix1 != 0) + + (prefix2 != 0) + + zerofill + + digits.length + + ((fs.flSeparator != 0) * (digits.length / fs.separators)) + ); + if (spacesToPrint > 0) // need to do some padding { - put(w, '\\'); - switch (c) - { - case '\0': put(w, '0'); break; - case '\a': put(w, 'a'); break; - case '\b': put(w, 'b'); break; - case '\f': put(w, 'f'); break; - case '\n': put(w, 'n'); break; - case '\r': put(w, 'r'); break; - case '\t': put(w, 't'); break; - case '\v': put(w, 'v'); break; - default: - formattedWrite(w, "x%02X", cast(uint)c); - } + if (padChar == '0') + zerofill += spacesToPrint; + else if (padChar) + leftpad = spacesToPrint; + else + rightpad = spacesToPrint; } - else if (c <= 0xFFFF) - formattedWrite(w, "\\u%04X", cast(uint)c); - else - formattedWrite(w, "\\U%08X", cast(uint)c); -} -// undocumented because of deprecation -// string elements are formatted like UTF-8 string literals. -void formatElement(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (is(StringTypeOf!T) && !is(T == enum)) -{ - import std.utf : UTFException; - import std.array : appender; + // Print + foreach (i ; 0 .. leftpad) + put(w, ' '); - StringTypeOf!T str = val; // bug 8015 + if (prefix1) put(w, prefix1); + if (prefix2) put(w, prefix2); + + foreach (i ; 0 .. zerofill) + put(w, '0'); - if (f.spec == 's') + if (fs.flSeparator) { - try + for (size_t j = 0; j < digits.length; ++j) { - // ignore other specifications and quote - auto app = appender!(typeof(val[0])[])(); - put(app, '\"'); - for (size_t i = 0; i < str.length; ) + if (j != 0 && (digits.length - j) % fs.separators == 0) { - auto c = std.utf.decode(str, i); - // \uFFFE and \uFFFF are considered valid by isValidDchar, - // so need checking for interchange. - if (c == 0xFFFE || c == 0xFFFF) - goto LinvalidSeq; - formatChar(app, c, '"'); + put(w, fs.separatorChar); } - put(app, '\"'); - put(w, app.data); - return; - } - catch (UTFException) - { - } - - // If val contains invalid UTF sequence, formatted like HexString literal - LinvalidSeq: - static if (is(typeof(str[0]) : const(char))) - { - enum postfix = 'c'; - alias IntArr = const(ubyte)[]; - } - else static if (is(typeof(str[0]) : const(wchar))) - { - enum postfix = 'w'; - alias IntArr = const(ushort)[]; - } - else static if (is(typeof(str[0]) : const(dchar))) - { - enum postfix = 'd'; - alias IntArr = const(uint)[]; + put(w, digits[j]); } - formattedWrite(w, "x\"%(%02X %)\"%s", cast(IntArr)str, postfix); } else - formatValue(w, str, f); + { + put(w, digits); + } + + foreach (i ; 0 .. rightpad) + put(w, ' '); } -unittest +@safe pure unittest { - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatElement(w, "Hello World", spec); + assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c'); - assert(w.data == "\"Hello World\""); + assertCTFEable!( + { + formatTest(9, "9"); + formatTest( 10, "10" ); + }); } -unittest +@system unittest { - // Test for bug 8015 - import std.typecons; - - struct MyStruct { - string str; - @property string toStr() { - return str; - } - alias toStr this; - } + class C1 { long val; alias val this; this(long v){ val = v; } } + class C2 { long val; alias val this; this(long v){ val = v; } + override string toString() const { return "C"; } } + formatTest( new C1(10), "10" ); + formatTest( new C2(10), "C" ); - Tuple!(MyStruct) t; + struct S1 { long val; alias val this; } + struct S2 { long val; alias val this; + string toString() const { return "S"; } } + formatTest( S1(10), "10" ); + formatTest( S2(10), "S" ); } -// undocumented because of deprecation -// Character elements are formatted like UTF-8 character literals. -void formatElement(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (is(CharTypeOf!T) && !is(T == enum)) +// bugzilla 9117 +@safe unittest { - if (f.spec == 's') + static struct Frop {} + + static struct Foo { - put(w, '\''); - formatChar(w, val, '\''); - put(w, '\''); + int n = 0; + alias n this; + T opCast(T) () if (is(T == Frop)) + { + return Frop(); + } + string toString() + { + return "Foo"; + } } - else - formatValue(w, val, f); -} -/// -unittest -{ - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); - formatElement(w, "H", spec); + static struct Bar + { + Foo foo; + alias foo this; + string toString() + { + return "Bar"; + } + } - assert(w.data == "\"H\"", w.data); -} + const(char)[] result; + void put(const char[] s){ result ~= s; } -// undocumented -// Maybe T is noncopyable struct, so receive it by 'auto ref'. -void formatElement(Writer, T, Char)(Writer w, auto ref T val, ref FormatSpec!Char f) -if (!is(StringTypeOf!T) && !is(CharTypeOf!T) || is(T == enum)) -{ - formatValue(w, val, f); + Foo foo; + formattedWrite(&put, "%s", foo); // OK + assert(result == "Foo"); + + result = null; + + Bar bar; + formattedWrite(&put, "%s", bar); // NG + assert(result == "Bar"); + + result = null; + + int i = 9; + formattedWrite(&put, "%s", 9); + assert(result == "9"); } +private enum ctfpMessage = "Cannot format floating point types at compile-time"; + /** - Associative arrays are formatted by using $(D ':') and $(D ", ") as - separators, and enclosed by $(D '[') and $(D ']'). +Floating-point values are formatted like $(D printf) does. Params: w = The $(D OutputRange) to write to. obj = The value to write. f = The $(D FormatSpec) defining how to write the value. */ -void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { - AssocArrayTypeOf!T val = obj; + import std.algorithm.searching : find; + import std.algorithm.comparison : min; + import std.string : indexOf, indexOfAny, indexOfNeither; - enforceFmt(f.spec == 's' || f.spec == '(', - "associative"); + FormatSpec!Char fs = f; // fs is copy for change its values. + FloatingPointTypeOf!T val = obj; - enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator; - auto fmtSpec = f.spec == '(' ? f.nested : defSpec; + if (fs.spec == 'r') + { + // raw write, skip all else and write the thing + auto raw = (ref val)@trusted{ + return (cast(const char*) &val)[0 .. val.sizeof]; + }(val); + if (needToSwapEndianess(f)) + { + foreach_reverse (c; raw) + put(w, c); + } + else + { + foreach (c; raw) + put(w, c); + } + return; + } + enforceFmt(find("fgFGaAeEs", fs.spec).length, + "incompatible format character for floating point argument: %" ~ fs.spec); + enforceFmt(!__ctfe, ctfpMessage); - size_t i = 0, end = val.length; + version (CRuntime_Microsoft) + { + import std.math : isNaN, isInfinity; + immutable double tval = val; // convert early to get "inf" in case of overflow + string s; + if (isNaN(tval)) + s = "nan"; // snprintf writes 1.#QNAN + else if (isInfinity(tval)) + s = val >= 0 ? "inf" : "-inf"; // snprintf writes 1.#INF - if (f.spec == 's') - put(w, f.seqBefore); - foreach (k, ref v; val) + if (s.length > 0) + { + version(none) + { + return formatValue(w, s, f); + } + else // FIXME:workaround + { + s = s[0 .. f.precision < $ ? f.precision : $]; + if (!f.flDash) + { + // right align + if (f.width > s.length) + foreach (j ; 0 .. f.width - s.length) put(w, ' '); + put(w, s); + } + else + { + // left align + put(w, s); + if (f.width > s.length) + foreach (j ; 0 .. f.width - s.length) put(w, ' '); + } + return; + } + } + } + else + alias tval = val; + if (fs.spec == 's') fs.spec = 'g'; + char[1 /*%*/ + 5 /*flags*/ + 3 /*width.prec*/ + 2 /*format*/ + + 1 /*\0*/] sprintfSpec = void; + sprintfSpec[0] = '%'; + uint i = 1; + if (fs.flDash) sprintfSpec[i++] = '-'; + if (fs.flPlus) sprintfSpec[i++] = '+'; + if (fs.flZero) sprintfSpec[i++] = '0'; + if (fs.flSpace) sprintfSpec[i++] = ' '; + if (fs.flHash) sprintfSpec[i++] = '#'; + sprintfSpec[i .. i + 3] = "*.*"; + i += 3; + if (is(Unqual!(typeof(val)) == real)) sprintfSpec[i++] = 'L'; + sprintfSpec[i++] = fs.spec; + sprintfSpec[i] = 0; + //printf("format: '%s'; geeba: %g\n", sprintfSpec.ptr, val); + char[512] buf = void; + + immutable n = ()@trusted{ + import core.stdc.stdio : snprintf; + return snprintf(buf.ptr, buf.length, + sprintfSpec.ptr, + fs.width, + // negative precision is same as no precision specified + fs.precision == fs.UNSPECIFIED ? -1 : fs.precision, + tval); + }(); + + enforceFmt(n >= 0, + "floating point formatting failure"); + + auto len = min(n, buf.length-1); + ptrdiff_t dot = buf[0 .. len].indexOf('.'); + if (fs.flSeparator && dot != -1) { - auto fmt = FormatSpec!Char(fmtSpec); - fmt.writeUpToNextSpec(w); - if (f.flDash) + ptrdiff_t firstDigit = buf[0 .. len].indexOfAny("0123456789"); + ptrdiff_t ePos = buf[0 .. len].indexOf('e'); + size_t j; + + ptrdiff_t firstLen = dot - firstDigit; + + size_t separatorScoreCnt = firstLen / fs.separators; + + size_t afterDotIdx; + if (ePos != -1) { - formatValue(w, k, fmt); - fmt.writeUpToNextSpec(w); - formatValue(w, v, fmt); + afterDotIdx = ePos; } else { - formatElement(w, k, fmt); - fmt.writeUpToNextSpec(w); - formatElement(w, v, fmt); + afterDotIdx = len; } - if (f.sep !is null) + + if (dot != -1) { - fmt.writeUpToNextSpec(w); - if (++i != end) - put(w, f.sep); + ptrdiff_t mantissaLen = afterDotIdx - (dot + 1); + separatorScoreCnt += (mantissaLen > 0) ? (mantissaLen - 1) / fs.separators : 0; + } + + // plus, minus prefix + ptrdiff_t digitsBegin = buf[0 .. separatorScoreCnt].indexOfNeither(" "); + if (digitsBegin == -1) + { + digitsBegin = separatorScoreCnt; + } + put(w, buf[digitsBegin .. firstDigit]); + + // digits until dot with separator + for (j = 0; j < firstLen; ++j) + { + if (j > 0 && (firstLen - j) % fs.separators == 0) + { + put(w, fs.separatorChar); + } + put(w, buf[j + firstDigit]); + } + put(w, '.'); + + // digits after dot + for (j = dot + 1; j < afterDotIdx; ++j) + { + auto realJ = (j - (dot + 1)); + if (realJ != 0 && realJ % fs.separators == 0) + { + put(w, fs.separatorChar); + } + put(w, buf[j]); } - else + + // rest + if (ePos != -1) { - if (++i != end) - fmt.writeUpToNextSpec(w); + put(w, buf[afterDotIdx .. len]); } } - if (f.spec == 's') - put(w, f.seqAfter); + else + { + put(w, buf[0 .. len]); + } } /// -unittest +@safe unittest { import std.array : appender; auto w = appender!string(); - auto spec = singleSpec("%s"); - auto aa = ["H":"W"]; - formatElement(w, aa, spec); + auto spec = singleSpec("%.1f"); + formatValue(w, 1337.7, spec); - assert(w.data == "[\"H\":\"W\"]", w.data); + assert(w.data == "1337.7"); } -unittest +@safe /*pure*/ unittest // formatting floating point values is now impure { - int[string] aa0; - formatTest( aa0, `[]` ); + import std.conv : to; - // elements escaping - formatTest( ["aaa":1, "bbb":2], - [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`] ); - formatTest( ['c':"str"], - `['c':"str"]` ); - formatTest( ['"':"\"", '\'':"'"], - [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`] ); + assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd'); - // range formatting for AA - auto aa3 = [1:"hello", 2:"world"]; - // escape - formatTest( "{%(%s:%s $ %)}", aa3, - [`{1:"hello" $ 2:"world"}`, `{2:"world" $ 1:"hello"}`]); - // use range formatting for key and value, and use %| - formatTest( "{%([%04d->%(%c.%)]%| $ %)}", aa3, - [`{[0001->h.e.l.l.o] $ [0002->w.o.r.l.d]}`, `{[0002->w.o.r.l.d] $ [0001->h.e.l.l.o]}`] ); + foreach (T; AliasSeq!(float, double, real)) + { + formatTest( to!( T)(5.5), "5.5" ); + formatTest( to!( const T)(5.5), "5.5" ); + formatTest( to!(immutable T)(5.5), "5.5" ); - // issue 12135 - formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>"); - formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>"); + formatTest( T.nan, "nan" ); + } } -unittest +@system unittest { - class C1 { int[char] val; alias val this; this(int[char] v){ val = v; } } - class C2 { int[char] val; alias val this; this(int[char] v){ val = v; } + formatTest( 2.25, "2.25" ); + + class C1 { double val; alias val this; this(double v){ val = v; } } + class C2 { double val; alias val this; this(double v){ val = v; } override string toString() const { return "C"; } } - formatTest( new C1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] ); - formatTest( new C2(['c':1, 'd':2]), "C" ); + formatTest( new C1(2.25), "2.25" ); + formatTest( new C2(2.25), "C" ); - struct S1 { int[char] val; alias val this; } - struct S2 { int[char] val; alias val this; + struct S1 { double val; alias val this; } + struct S2 { double val; alias val this; string toString() const { return "S"; } } - formatTest( S1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] ); - formatTest( S2(['c':1, 'd':2]), "S" ); + formatTest( S1(2.25), "2.25" ); + formatTest( S2(2.25), "S" ); } -unittest // Issue 8921 -{ - enum E : char { A = 'a', B = 'b', C = 'c' } - E[3] e = [E.A, E.B, E.C]; - formatTest(e, "[A, B, C]"); - - E[] e2 = [E.A, E.B, E.C]; - formatTest(e2, "[A, B, C]"); -} +/* +Formatting a $(D creal) is deprecated but still kept around for a while. -template hasToString(T, Char) +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(Unqual!T : creal) && !is(T == enum) && !hasToString!(T, Char)) { - static if(isPointer!T && !isAggregateType!T) - { - // X* does not have toString, even if X is aggregate type has toString. - enum hasToString = 0; - } - else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((const(char)[] s){}, f); }))) - { - enum hasToString = 4; - } - else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}, "%s"); }))) - { - enum hasToString = 3; - } - else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}); }))) - { - enum hasToString = 2; - } - else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S) - { - enum hasToString = 1; - } - else + immutable creal val = obj; + + formatValue(w, val.re, f); + if (val.im >= 0) { - enum hasToString = 0; + put(w, '+'); } + formatValue(w, val.im, f); + put(w, 'i'); } -// object formatting with toString -private void formatObject(Writer, T, Char)(ref Writer w, ref T val, ref FormatSpec!Char f) -if (hasToString!(T, Char)) +@safe /*pure*/ unittest // formatting floating point values is now impure { - static if (is(typeof(val.toString((const(char)[] s){}, f)))) - { - val.toString((const(char)[] s) { put(w, s); }, f); - } - else static if (is(typeof(val.toString((const(char)[] s){}, "%s")))) - { - val.toString((const(char)[] s) { put(w, s); }, f.getCurFmtStr()); - } - else static if (is(typeof(val.toString((const(char)[] s){})))) - { - val.toString((const(char)[] s) { put(w, s); }); - } - else static if (is(typeof(val.toString()) S) && isSomeString!S) + import std.conv : to; + foreach (T; AliasSeq!(cfloat, cdouble, creal)) { - put(w, val.toString()); + formatTest( to!( T)(1 + 1i), "1+1i" ); + formatTest( to!( const T)(1 + 1i), "1+1i" ); + formatTest( to!(immutable T)(1 + 1i), "1+1i" ); } - else - static assert(0); -} - -void enforceValidFormatSpec(T, Char)(ref FormatSpec!Char f) -{ - static if (!isInputRange!T && hasToString!(T, Char) != 4) + foreach (T; AliasSeq!(cfloat, cdouble, creal)) { - enforceFmt(f.spec == 's', - "Expected '%s' format specifier for type '" ~ T.stringof ~ "'"); + formatTest( to!( T)(0 - 3i), "0-3i" ); + formatTest( to!( const T)(0 - 3i), "0-3i" ); + formatTest( to!(immutable T)(0 - 3i), "0-3i" ); } } -unittest +@system unittest { - static interface IF1 { } - class CIF1 : IF1 { } - static struct SF1 { } - static union UF1 { } - static class CF1 { } - - static interface IF2 { string toString(); } - static class CIF2 : IF2 { override string toString() { return ""; } } - static struct SF2 { string toString() { return ""; } } - static union UF2 { string toString() { return ""; } } - static class CF2 { override string toString() { return ""; } } + formatTest( 3+2.25i, "3+2.25i" ); - static interface IK1 { void toString(scope void delegate(const(char)[]) sink, - FormatSpec!char) const; } - static class CIK1 : IK1 { override void toString(scope void delegate(const(char)[]) sink, - FormatSpec!char) const { sink("CIK1"); } } - static struct KS1 { void toString(scope void delegate(const(char)[]) sink, - FormatSpec!char) const { sink("KS1"); } } + class C1 { cdouble val; alias val this; this(cdouble v){ val = v; } } + class C2 { cdouble val; alias val this; this(cdouble v){ val = v; } + override string toString() const { return "C"; } } + formatTest( new C1(3+2.25i), "3+2.25i" ); + formatTest( new C2(3+2.25i), "C" ); - static union KU1 { void toString(scope void delegate(const(char)[]) sink, - FormatSpec!char) const { sink("KU1"); } } + struct S1 { cdouble val; alias val this; } + struct S2 { cdouble val; alias val this; + string toString() const { return "S"; } } + formatTest( S1(3+2.25i), "3+2.25i" ); + formatTest( S2(3+2.25i), "S" ); +} - static class KC1 { void toString(scope void delegate(const(char)[]) sink, - FormatSpec!char) const { sink("KC1"); } } +/* + Formatting an $(D ireal) is deprecated but still kept around for a while. - IF1 cif1 = new CIF1; - assertThrown!FormatException(format("%f", cif1)); - assertThrown!FormatException(format("%f", SF1())); - assertThrown!FormatException(format("%f", UF1())); - assertThrown!FormatException(format("%f", new CF1())); +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(Unqual!T : ireal) && !is(T == enum) && !hasToString!(T, Char)) +{ + immutable ireal val = obj; - IF2 cif2 = new CIF2; - assertThrown!FormatException(format("%f", cif2)); - assertThrown!FormatException(format("%f", SF2())); - assertThrown!FormatException(format("%f", UF2())); - assertThrown!FormatException(format("%f", new CF2())); + formatValue(w, val.im, f); + put(w, 'i'); +} - IK1 cik1 = new CIK1; - assert(format("%f", cik1) == "CIK1"); - assert(format("%f", KS1()) == "KS1"); - assert(format("%f", KU1()) == "KU1"); - assert(format("%f", new KC1()) == "KC1"); +@safe /*pure*/ unittest // formatting floating point values is now impure +{ + import std.conv : to; + foreach (T; AliasSeq!(ifloat, idouble, ireal)) + { + formatTest( to!( T)(1i), "1i" ); + formatTest( to!( const T)(1i), "1i" ); + formatTest( to!(immutable T)(1i), "1i" ); + } } -/** - Aggregates ($(D struct), $(D union), $(D class), and $(D interface)) are - basically formatted by calling $(D toString). - $(D toString) should have one of the following signatures: +@system unittest +{ + formatTest( 2.25i, "2.25i" ); ---- -const void toString(scope void delegate(const(char)[]) sink, FormatSpec fmt); -const void toString(scope void delegate(const(char)[]) sink, string fmt); -const void toString(scope void delegate(const(char)[]) sink); -const string toString(); ---- + class C1 { idouble val; alias val this; this(idouble v){ val = v; } } + class C2 { idouble val; alias val this; this(idouble v){ val = v; } + override string toString() const { return "C"; } } + formatTest( new C1(2.25i), "2.25i" ); + formatTest( new C2(2.25i), "C" ); - For the class objects which have input range interface, - $(UL $(LI If the instance $(D toString) has overridden - $(D Object.toString), it is used.) - $(LI Otherwise, the objects are formatted as input range.)) + struct S1 { idouble val; alias val this; } + struct S2 { idouble val; alias val this; + string toString() const { return "S"; } } + formatTest( S1(2.25i), "2.25i" ); + formatTest( S2(2.25i), "S" ); +} - For the struct and union objects which does not have $(D toString), - $(UL $(LI If they have range interface, formatted as input range.) - $(LI Otherwise, they are formatted like $(D Type(field1, filed2, ...)).)) +/** +Individual characters ($(D char), $(D wchar), or $(D dchar)) are formatted as +Unicode characters with %s and as integers with integral-specific format +specs. - Otherwise, are formatted just as their type name. +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. */ -void formatValue(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (is(T == class) && !is(T == enum)) +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { - enforceValidFormatSpec!(T, Char)(f); - // TODO: Change this once toString() works for shared objects. - static assert(!is(T == shared), "unable to format shared objects"); + CharTypeOf!T val = obj; - if (val is null) - put(w, "null"); + if (f.spec == 's' || f.spec == 'c') + { + put(w, val); + } else { - static if (hasToString!(T, Char) > 1 || (!isInputRange!T && !is(BuiltinTypeOf!T))) - { - formatObject!(Writer, T, Char)(w, val, f); - } - else - { - //string delegate() dg = &val.toString; - Object o = val; // workaround - string delegate() dg = &o.toString; - if (dg.funcptr != &Object.toString) // toString is overridden - { - formatObject(w, val, f); - } - else static if (isInputRange!T) - { - formatRange(w, val, f); - } - else static if (is(BuiltinTypeOf!T X)) - { - X x = val; - formatValue(w, x, f); - } - else - { - formatObject(w, val, f); - } - } + alias U = AliasSeq!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2]; + formatValue(w, cast(U) val, f); } } -/++ - $(D formatValue) allows to reuse existing format specifiers: - +/ -unittest +/// +@safe pure unittest { - import std.format; + import std.array : appender; + auto w = appender!string(); + auto spec = singleSpec("%c"); + formatValue(w, 'a', spec); - struct Point - { - int x, y; + assert(w.data == "a"); +} - void toString(scope void delegate(const(char)[]) sink, - FormatSpec!char fmt) const - { - sink("("); - sink.formatValue(x, fmt); - sink(","); - sink.formatValue(y, fmt); - sink(")"); - } - } +@safe pure unittest +{ + assertCTFEable!( + { + formatTest( 'c', "c" ); + }); +} - auto p = Point(16,11); - assert(format("%03d", p) == "(016,011)"); - assert(format("%02x", p) == "(10,0b)"); +@system unittest +{ + class C1 { char val; alias val this; this(char v){ val = v; } } + class C2 { char val; alias val this; this(char v){ val = v; } + override string toString() const { return "C"; } } + formatTest( new C1('c'), "c" ); + formatTest( new C2('c'), "C" ); + + struct S1 { char val; alias val this; } + struct S2 { char val; alias val this; + string toString() const { return "S"; } } + formatTest( S1('c'), "c" ); + formatTest( S2('c'), "S" ); } -/++ - The following code compares the use of $(D formatValue) and $(D formattedWrite). - +/ -unittest +@safe unittest { - import std.format; - import std.array : appender; + //Little Endian + formatTest( "%-r", cast( char)'c', ['c' ] ); + formatTest( "%-r", cast(wchar)'c', ['c', 0 ] ); + formatTest( "%-r", cast(dchar)'c', ['c', 0, 0, 0] ); + formatTest( "%-r", '本', ['\x2c', '\x67'] ); - auto writer1 = appender!string(); - writer1.formattedWrite("%08b", 42); + //Big Endian + formatTest( "%+r", cast( char)'c', [ 'c'] ); + formatTest( "%+r", cast(wchar)'c', [0, 'c'] ); + formatTest( "%+r", cast(dchar)'c', [0, 0, 0, 'c'] ); + formatTest( "%+r", '本', ['\x67', '\x2c'] ); +} - auto writer2 = appender!string(); - auto f = singleSpec("%08b"); - writer2.formatValue(42, f); +/** +Strings are formatted like $(D printf) does. - assert(writer1.data == writer2.data && writer1.data == "00101010"); +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) +{ + Unqual!(StringTypeOf!T) val = obj; // for `alias this`, see bug5371 + formatRange(w, val, f); } -unittest +/// +@safe pure unittest { import std.array : appender; - import std.range.interfaces; - // class range (issue 5154) - auto c = inputRangeObject([1,2,3,4]); - formatTest( c, "[1, 2, 3, 4]" ); - assert(c.empty); - c = null; - formatTest( c, "null" ); + auto w = appender!string(); + auto spec = singleSpec("%s"); + formatValue(w, "hello", spec); + + assert(w.data == "hello"); } -unittest +@safe unittest { - // 5354 - // If the class has both range I/F and custom toString, the use of custom - // toString routine is prioritized. + formatTest( "abc", "abc" ); +} - // Enable the use of custom toString that gets a sink delegate - // for class formatting. +@system unittest +{ + // Test for bug 5371 for classes + class C1 { const string var; alias var this; this(string s){ var = s; } } + class C2 { string var; alias var this; this(string s){ var = s; } } + formatTest( new C1("c1"), "c1" ); + formatTest( new C2("c2"), "c2" ); - enum inputRangeCode = - q{ - int[] arr; - this(int[] a){ arr = a; } - @property int front() const { return arr[0]; } - @property bool empty() const { return arr.length == 0; } - void popFront(){ arr = arr[1..$]; } - }; + // Test for bug 5371 for structs + struct S1 { const string var; alias var this; } + struct S2 { string var; alias var this; } + formatTest( S1("s1"), "s1" ); + formatTest( S2("s2"), "s2" ); +} - class C1 - { - mixin(inputRangeCode); - void toString(scope void delegate(const(char)[]) dg, ref FormatSpec!char f) const { dg("[012]"); } - } - class C2 - { - mixin(inputRangeCode); - void toString(scope void delegate(const(char)[]) dg, string f) const { dg("[012]"); } - } - class C3 - { - mixin(inputRangeCode); - void toString(scope void delegate(const(char)[]) dg) const { dg("[012]"); } - } - class C4 - { - mixin(inputRangeCode); - override string toString() const { return "[012]"; } - } - class C5 - { - mixin(inputRangeCode); - } +@system unittest +{ + class C3 { string val; alias val this; this(string s){ val = s; } + override string toString() const { return "C"; } } + formatTest( new C3("c3"), "C" ); - formatTest( new C1([0, 1, 2]), "[012]" ); - formatTest( new C2([0, 1, 2]), "[012]" ); - formatTest( new C3([0, 1, 2]), "[012]" ); - formatTest( new C4([0, 1, 2]), "[012]" ); - formatTest( new C5([0, 1, 2]), "[0, 1, 2]" ); + struct S3 { string val; alias val this; + string toString() const { return "S"; } } + formatTest( S3("s3"), "S" ); } -/// ditto -void formatValue(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum)) +@safe pure unittest { - enforceValidFormatSpec!(T, Char)(f); - if (val is null) - put(w, "null"); - else - { - static if (hasToString!(T, Char)) - { - formatObject(w, val, f); - } - else static if (isInputRange!T) - { - formatRange(w, val, f); - } - else - { - version (Windows) - { - import core.sys.windows.com : IUnknown; - static if (is(T : IUnknown)) - { - formatValue(w, *cast(void**)&val, f); - } - else - { - formatValue(w, cast(Object)val, f); - } - } - else - { - formatValue(w, cast(Object)val, f); - } - } - } + //Little Endian + formatTest( "%-r", "ab"c, ['a' , 'b' ] ); + formatTest( "%-r", "ab"w, ['a', 0 , 'b', 0 ] ); + formatTest( "%-r", "ab"d, ['a', 0, 0, 0, 'b', 0, 0, 0] ); + formatTest( "%-r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] ); + formatTest( "%-r", "日本語"w, ['\xe5', '\x65', '\x2c', '\x67', '\x9e', '\x8a']); + formatTest( "%-r", "日本語"d, ['\xe5', '\x65', '\x00', '\x00', '\x2c', '\x67', + '\x00', '\x00', '\x9e', '\x8a', '\x00', '\x00'] ); + + //Big Endian + formatTest( "%+r", "ab"c, [ 'a', 'b'] ); + formatTest( "%+r", "ab"w, [ 0, 'a', 0, 'b'] ); + formatTest( "%+r", "ab"d, [0, 0, 0, 'a', 0, 0, 0, 'b'] ); + formatTest( "%+r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] ); + formatTest( "%+r", "日本語"w, ['\x65', '\xe5', '\x67', '\x2c', '\x8a', '\x9e'] ); + formatTest( "%+r", "日本語"d, ['\x00', '\x00', '\x65', '\xe5', '\x00', '\x00', + '\x67', '\x2c', '\x00', '\x00', '\x8a', '\x9e'] ); +} + +/** +Static-size arrays are formatted as dynamic arrays. + +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, auto ref T obj, const ref FormatSpec!Char f) +if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) +{ + formatValue(w, obj[], f); +} + +/// +@safe pure unittest +{ + import std.array : appender; + auto w = appender!string(); + auto spec = singleSpec("%s"); + char[2] two = ['a', 'b']; + formatValue(w, two, spec); + + assert(w.data == "ab"); } -unittest +@safe unittest // Test for issue 8310 { - // interface - import std.range.interfaces; - InputRange!int i = inputRangeObject([1,2,3,4]); - formatTest( i, "[1, 2, 3, 4]" ); - assert(i.empty); - i = null; - formatTest( i, "null" ); - - // interface (downcast to Object) - interface Whatever {} - class C : Whatever - { - override @property string toString() const { return "ab"; } - } - Whatever val = new C; - formatTest( val, "ab" ); + import std.array : appender; + FormatSpec!char f; + auto w = appender!string(); - // Issue 11175 - version (Windows) - { - import core.sys.windows.windows : HRESULT; - import core.sys.windows.com : IUnknown, IID; + char[2] two = ['a', 'b']; + formatValue(w, two, f); - interface IUnknown2 : IUnknown { } + char[2] getTwo(){ return two; } + formatValue(w, getTwo(), f); +} - class D : IUnknown2 - { - extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; } - extern(Windows) uint AddRef() { return 0; } - extern(Windows) uint Release() { return 0; } - } +/** +Dynamic arrays are formatted as input ranges. - IUnknown2 d = new D; - string expected = format("%X", cast(void*)d); - formatTest(d, expected); - } -} +Specializations: + $(UL $(LI $(D void[]) is formatted like $(D ubyte[]).) + $(LI Const array is converted to input range by removing its qualifier.)) -/// ditto -// Maybe T is noncopyable struct, so receive it by 'auto ref'. -void formatValue(Writer, T, Char)(Writer w, auto ref T val, ref FormatSpec!Char f) -if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum)) +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { - enforceValidFormatSpec!(T, Char)(f); - static if (hasToString!(T, Char)) - { - formatObject(w, val, f); - } - else static if (isInputRange!T) + static if (is(const(ArrayTypeOf!T) == const(void[]))) { - formatRange(w, val, f); + formatValue(w, cast(const ubyte[]) obj, f); } - else static if (is(T == struct)) + else static if (!isInputRange!T) { - enum left = T.stringof~"("; - enum separator = ", "; - enum right = ")"; - - put(w, left); - foreach (i, e; val.tupleof) - { - static if (0 < i && val.tupleof[i-1].offsetof == val.tupleof[i].offsetof) - { - static if (i == val.tupleof.length - 1 || val.tupleof[i].offsetof != val.tupleof[i+1].offsetof) - put(w, separator~val.tupleof[i].stringof[4..$]~"}"); - else - put(w, separator~val.tupleof[i].stringof[4..$]); - } - else - { - static if (i+1 < val.tupleof.length && val.tupleof[i].offsetof == val.tupleof[i+1].offsetof) - put(w, (i > 0 ? separator : "")~"#{overlap "~val.tupleof[i].stringof[4..$]); - else - { - static if (i > 0) - put(w, separator); - formatElement(w, e, f); - } - } - } - put(w, right); + alias U = Unqual!(ArrayTypeOf!T); + static assert(isInputRange!U); + U val = obj; + formatValue(w, val, f); } else { - put(w, T.stringof); + formatRange(w, obj, f); } } -unittest +/// +@safe pure unittest { - // bug 4638 - struct U8 { string toString() const { return "blah"; } } - struct U16 { wstring toString() const { return "blah"; } } - struct U32 { dstring toString() const { return "blah"; } } - formatTest( U8(), "blah" ); - formatTest( U16(), "blah" ); - formatTest( U32(), "blah" ); -} + import std.array : appender; + auto w = appender!string(); + auto spec = singleSpec("%s"); + auto two = [1, 2]; + formatValue(w, two, spec); -unittest -{ - // 3890 - struct Int{ int n; } - struct Pair{ string s; Int i; } - formatTest( Pair("hello", Int(5)), - `Pair("hello", Int(5))` ); + assert(w.data == "[1, 2]"); } -unittest +// alias this, input range I/F, and toString() +@system unittest { - // union formatting without toString - union U1 + struct S(int flags) { - int n; - string s; + int[] arr; + static if (flags & 1) + alias arr this; + + static if (flags & 2) + { + @property bool empty() const { return arr.length == 0; } + @property int front() const { return arr[0] * 2; } + void popFront() { arr = arr[1..$]; } + } + + static if (flags & 4) + string toString() const { return "S"; } } - U1 u1; - formatTest( u1, "U1" ); + formatTest(S!0b000([0, 1, 2]), "S!0([0, 1, 2])"); + formatTest(S!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628 + formatTest(S!0b010([0, 1, 2]), "[0, 2, 4]"); + formatTest(S!0b011([0, 1, 2]), "[0, 2, 4]"); + formatTest(S!0b100([0, 1, 2]), "S"); + formatTest(S!0b101([0, 1, 2]), "S"); // Test for bug 7628 + formatTest(S!0b110([0, 1, 2]), "S"); + formatTest(S!0b111([0, 1, 2]), "S"); - // union formatting with toString - union U2 + class C(uint flags) { - int n; - string s; - string toString() const { return s; } + int[] arr; + static if (flags & 1) + alias arr this; + + this(int[] a) { arr = a; } + + static if (flags & 2) + { + @property bool empty() const { return arr.length == 0; } + @property int front() const { return arr[0] * 2; } + void popFront() { arr = arr[1..$]; } + } + + static if (flags & 4) + override string toString() const { return "C"; } } - U2 u2; - u2.s = "hello"; - formatTest( u2, "hello" ); + formatTest(new C!0b000([0, 1, 2]), (new C!0b000([])).toString()); + formatTest(new C!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628 + formatTest(new C!0b010([0, 1, 2]), "[0, 2, 4]"); + formatTest(new C!0b011([0, 1, 2]), "[0, 2, 4]"); + formatTest(new C!0b100([0, 1, 2]), "C"); + formatTest(new C!0b101([0, 1, 2]), "C"); // Test for bug 7628 + formatTest(new C!0b110([0, 1, 2]), "C"); + formatTest(new C!0b111([0, 1, 2]), "C"); } -unittest +@system unittest { - import std.array; - // 7230 - static struct Bug7230 - { - string s = "hello"; - union { - string a; - int b; - double c; - } - long x = 10; - } + // void[] + void[] val0; + formatTest( val0, "[]" ); - Bug7230 bug; - bug.b = 123; + void[] val = cast(void[]) cast(ubyte[])[1, 2, 3]; + formatTest( val, "[1, 2, 3]" ); - FormatSpec!char f; - auto w = appender!(char[])(); - formatValue(w, bug, f); - assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`); + void[0] sval0 = []; + formatTest( sval0, "[]"); + + void[3] sval = cast(void[3]) cast(ubyte[3])[1, 2, 3]; + formatTest( sval, "[1, 2, 3]" ); } -unittest +@safe unittest { - import std.array; - static struct S{ @disable this(this); } - S s; + // const(T[]) -> const(T)[] + const short[] a = [1, 2, 3]; + formatTest( a, "[1, 2, 3]" ); - FormatSpec!char f; - auto w = appender!string(); - formatValue(w, s, f); - assert(w.data == "S()"); + struct S { const(int[]) arr; alias arr this; } + auto s = S([1,2,3]); + formatTest( s, "[1, 2, 3]" ); } -/** -$(D enum) is formatted like its base value. - -Params: - w = The $(D OutputRange) to write to. - val = The value to write. - f = The $(D FormatSpec) defining how to write the value. - */ -void formatValue(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (is(T == enum)) +@safe unittest { - if (f.spec == 's') + // 6640 + struct Range { - foreach (i, e; EnumMembers!T) - { - if (val == e) - { - formatValue(w, __traits(allMembers, T)[i], f); - return; - } - } + @safe: + string value; + @property bool empty() const { return !value.length; } + @property dchar front() const { return value.front; } + void popFront() { value.popFront(); } - // val is not a member of T, output cast(T)rawValue instead. - put(w, "cast(" ~ T.stringof ~ ")"); - static assert(!is(OriginalType!T == T)); + @property size_t length() const { return value.length; } + } + immutable table = + [ + ["[%s]", "[string]"], + ["[%10s]", "[ string]"], + ["[%-10s]", "[string ]"], + ["[%(%02x %)]", "[73 74 72 69 6e 67]"], + ["[%(%c %)]", "[s t r i n g]"], + ]; + foreach (e; table) + { + formatTest(e[0], "string", e[1]); + formatTest(e[0], Range("string"), e[1]); } - formatValue(w, cast(OriginalType!T)val, f); } -/// -unittest +@system unittest { - import std.array : appender; - auto w = appender!string(); - auto spec = singleSpec("%s"); + // string literal from valid UTF sequence is encoding free. + foreach (StrType; AliasSeq!(string, wstring, dstring)) + { + // Valid and printable (ASCII) + formatTest( [cast(StrType)"hello"], + `["hello"]` ); - enum A { first, second, third } + // 1 character escape sequences (' is not escaped in strings) + formatTest( [cast(StrType)"\"'\0\\\a\b\f\n\r\t\v"], + `["\"'\0\\\a\b\f\n\r\t\v"]` ); - formatElement(w, A.second, spec); + // 1 character optional escape sequences + formatTest( [cast(StrType)"\'\?"], + `["'?"]` ); - assert(w.data == "second"); -} + // Valid and non-printable code point (<= U+FF) + formatTest( [cast(StrType)"\x10\x1F\x20test"], + `["\x10\x1F test"]` ); -unittest -{ - enum A { first, second, third } - formatTest( A.second, "second" ); - formatTest( cast(A)72, "cast(A)72" ); -} -unittest -{ - enum A : string { one = "uno", two = "dos", three = "tres" } - formatTest( A.three, "three" ); - formatTest( cast(A)"mill\ón", "cast(A)mill\ón" ); + // Valid and non-printable code point (<= U+FFFF) + formatTest( [cast(StrType)"\u200B..\u200F"], + `["\u200B..\u200F"]` ); + + // Valid and non-printable code point (<= U+10FFFF) + formatTest( [cast(StrType)"\U000E0020..\U000E007F"], + `["\U000E0020..\U000E007F"]` ); + } + + // invalid UTF sequence needs hex-string literal postfix (c/w/d) + { + // U+FFFF with UTF-8 (Invalid code point for interchange) + formatTest( [cast(string)[0xEF, 0xBF, 0xBF]], + `[x"EF BF BF"c]` ); + + // U+FFFF with UTF-16 (Invalid code point for interchange) + formatTest( [cast(wstring)[0xFFFF]], + `[x"FFFF"w]` ); + + // U+FFFF with UTF-32 (Invalid code point for interchange) + formatTest( [cast(dstring)[0xFFFF]], + `[x"FFFF"d]` ); + } } -unittest + +@safe unittest { - enum A : bool { no, yes } - formatTest( A.yes, "yes" ); - formatTest( A.no, "no" ); + // nested range formatting with array of string + formatTest( "%({%(%02x %)}%| %)", ["test", "msg"], + `{74 65 73 74} {6d 73 67}` ); } -unittest + +@safe unittest { - // Test for bug 6892 - enum Foo { A = 10 } - formatTest("%s", Foo.A, "A"); - formatTest(">%4s<", Foo.A, "> A<"); - formatTest("%04d", Foo.A, "0010"); - formatTest("%+2u", Foo.A, "+10"); - formatTest("%02x", Foo.A, "0a"); - formatTest("%3o", Foo.A, " 12"); - formatTest("%b", Foo.A, "1010"); + // stop auto escaping inside range formatting + auto arr = ["hello", "world"]; + formatTest( "%(%s, %)", arr, `"hello", "world"` ); + formatTest( "%-(%s, %)", arr, `hello, world` ); + + auto aa1 = [1:"hello", 2:"world"]; + formatTest( "%(%s:%s, %)", aa1, [`1:"hello", 2:"world"`, `2:"world", 1:"hello"`] ); + formatTest( "%-(%s:%s, %)", aa1, [`1:hello, 2:world`, `2:world, 1:hello`] ); + + auto aa2 = [1:["ab", "cd"], 2:["ef", "gh"]]; + formatTest( "%-(%s:%s, %)", aa2, [`1:["ab", "cd"], 2:["ef", "gh"]`, `2:["ef", "gh"], 1:["ab", "cd"]`] ); + formatTest( "%-(%s:%(%s%), %)", aa2, [`1:"ab""cd", 2:"ef""gh"`, `2:"ef""gh", 1:"ab""cd"`] ); + formatTest( "%-(%s:%-(%s%)%|, %)", aa2, [`1:abcd, 2:efgh`, `2:efgh, 1:abcd`] ); } -/** - Pointers are formatted as hex integers. - */ -void formatValue(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (isPointer!T && !is(T == enum) && !hasToString!(T, Char)) +// input range formatting +private void formatRange(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f) +if (isInputRange!T) { - static if (isInputRange!T) + import std.conv : text; + + // Formatting character ranges like string + if (f.spec == 's') { - if (val !is null) + alias E = ElementType!T; + + static if (!is(E == enum) && is(CharTypeOf!E)) { - formatRange(w, *val, f); - return; - } - } + static if (is(StringTypeOf!T)) + { + auto s = val[0 .. f.precision < $ ? f.precision : $]; + if (!f.flDash) + { + // right align + if (f.width > s.length) + foreach (i ; 0 .. f.width - s.length) put(w, ' '); + put(w, s); + } + else + { + // left align + put(w, s); + if (f.width > s.length) + foreach (i ; 0 .. f.width - s.length) put(w, ' '); + } + } + else + { + if (!f.flDash) + { + static if (hasLength!T) + { + // right align + auto len = val.length; + } + else static if (isForwardRange!T && !isInfinite!T) + { + auto len = walkLength(val.save); + } + else + { + enforce(f.width == 0, "Cannot right-align a range without length"); + size_t len = 0; + } + if (f.precision != f.UNSPECIFIED && len > f.precision) + len = f.precision; - static if (is(typeof({ shared const void* p = val; }))) - alias SharedOf(T) = shared(T); - else - alias SharedOf(T) = T; + if (f.width > len) + foreach (i ; 0 .. f.width - len) + put(w, ' '); + if (f.precision == f.UNSPECIFIED) + put(w, val); + else + { + size_t printed = 0; + for (; !val.empty && printed < f.precision; val.popFront(), ++printed) + put(w, val.front); + } + } + else + { + size_t printed = void; - const SharedOf!(void*) p = val; - const pnum = ()@trusted{ return cast(ulong) p; }(); + // left align + if (f.precision == f.UNSPECIFIED) + { + static if (hasLength!T) + { + printed = val.length; + put(w, val); + } + else + { + printed = 0; + for (; !val.empty; val.popFront(), ++printed) + put(w, val.front); + } + } + else + { + printed = 0; + for (; !val.empty && printed < f.precision; val.popFront(), ++printed) + put(w, val.front); + } - if (f.spec == 's') + if (f.width > printed) + foreach (i ; 0 .. f.width - printed) + put(w, ' '); + } + } + } + else + { + put(w, f.seqBefore); + if (!val.empty) + { + formatElement(w, val.front, f); + val.popFront(); + for (size_t i; !val.empty; val.popFront(), ++i) + { + put(w, f.seqSeparator); + formatElement(w, val.front, f); + } + } + static if (!isInfinite!T) put(w, f.seqAfter); + } + } + else if (f.spec == 'r') { - if (p is null) + static if (is(DynamicArrayTypeOf!T)) { - put(w, "null"); - return; + alias ARR = DynamicArrayTypeOf!T; + foreach (e ; cast(ARR) val) + { + formatValue(w, e, f); + } + } + else + { + for (size_t i; !val.empty; val.popFront(), ++i) + { + formatValue(w, val.front, f); + } + } + } + else if (f.spec == '(') + { + if (val.empty) + return; + // Nested specifier is to be used + for (;;) + { + auto fmt = FormatSpec!Char(f.nested); + fmt.writeUpToNextSpec(w); + if (f.flDash) + formatValue(w, val.front, fmt); + else + formatElement(w, val.front, fmt); + if (f.sep !is null) + { + put(w, fmt.trailing); + val.popFront(); + if (val.empty) + break; + put(w, f.sep); + } + else + { + val.popFront(); + if (val.empty) + break; + put(w, fmt.trailing); + } } - FormatSpec!Char fs = f; // fs is copy for change its values. - fs.spec = 'X'; - formatValue(w, pnum, fs); } else - { - enforceFmt(f.spec == 'X' || f.spec == 'x', - "Expected one of %s, %x or %X for pointer type."); - formatValue(w, pnum, f); - } + throw new Exception(text("Incorrect format specifier for range: %", f.spec)); } @safe pure unittest { - // pointer - import std.range; - auto r = retro([1,2,3,4]); - auto p = ()@trusted{ auto p = &r; return p; }(); - formatTest( p, "[4, 3, 2, 1]" ); - assert(p.empty); - p = null; - formatTest( p, "null" ); - - auto q = ()@trusted{ return cast(void*)0xFFEECCAA; }(); - formatTest( q, "FFEECCAA" ); + assert(collectExceptionMsg(format("%d", "hi")).back == 'd'); } -pure unittest +// character formatting with ecaping +private void formatChar(Writer)(Writer w, in dchar c, in char quote) { - // Test for issue 7869 - struct S + import std.uni : isGraphical; + + string fmt; + if (isGraphical(c)) { - string toString() const { return ""; } + if (c == quote || c == '\\') + put(w, '\\'); + put(w, c); + return; } - S* p = null; - formatTest( p, "null" ); - - S* q = cast(S*)0xFFEECCAA; - formatTest( q, "FFEECCAA" ); -} - -unittest -{ - // Test for issue 8186 - class B + else if (c <= 0xFF) { - int*a; - this(){ a = new int; } - alias a this; + if (c < 0x20) + { + foreach (i, k; "\n\r\t\a\b\f\v\0") + { + if (c == k) + { + put(w, '\\'); + put(w, "nrtabfv0"[i]); + return; + } + } + } + fmt = "\\x%02X"; } - formatTest( B.init, "null" ); -} - -pure unittest -{ - // Test for issue 9336 - shared int i; - format("%s", &i); -} - -pure unittest -{ - // Test for issue 11778 - int* p = null; - assertThrown(format("%d", p)); - assertThrown(format("%04d", p + 2)); -} - -pure unittest -{ - // Test for issue 12505 - void* p = null; - formatTest( "%08X", p, "00000000" ); -} + else if (c <= 0xFFFF) + fmt = "\\u%04X"; + else + fmt = "\\U%08X"; -/** - Delegates are formatted by 'ReturnType delegate(Parameters) FunctionAttributes' - */ -void formatValue(Writer, T, Char)(Writer w, scope T, ref FormatSpec!Char f) - if (isDelegate!T) -{ - formatValue(w, T.stringof, f); + formattedWrite(w, fmt, cast(uint) c); } -/// -unittest +// undocumented because of deprecation +// string elements are formatted like UTF-8 string literals. +void formatElement(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) +if (is(StringTypeOf!T) && !is(T == enum)) { - import std.conv : to; + import std.utf : UTFException; + import std.array : appender; - int i; + StringTypeOf!T str = val; // bug 8015 - int foo(short k) @nogc + if (f.spec == 's') { - return i + k; - } + try + { + // ignore other specifications and quote + auto app = appender!(typeof(val[0])[])(); + put(app, '\"'); + for (size_t i = 0; i < str.length; ) + { + import std.utf : decode; - int delegate(short) @nogc bar() nothrow - { - return &foo; - } + auto c = decode(str, i); + // \uFFFE and \uFFFF are considered valid by isValidDchar, + // so need checking for interchange. + if (c == 0xFFFE || c == 0xFFFF) + goto LinvalidSeq; + formatChar(app, c, '"'); + } + put(app, '\"'); + put(w, app.data); + return; + } + catch (UTFException) + { + } - assert(to!string(&bar) == "int delegate(short) @nogc delegate() nothrow"); + // If val contains invalid UTF sequence, formatted like HexString literal + LinvalidSeq: + static if (is(typeof(str[0]) : const(char))) + { + enum postfix = 'c'; + alias IntArr = const(ubyte)[]; + } + else static if (is(typeof(str[0]) : const(wchar))) + { + enum postfix = 'w'; + alias IntArr = const(ushort)[]; + } + else static if (is(typeof(str[0]) : const(dchar))) + { + enum postfix = 'd'; + alias IntArr = const(uint)[]; + } + formattedWrite(w, "x\"%(%02X %)\"%s", cast(IntArr) str, postfix); + } + else + formatValue(w, str, f); } -unittest +@safe pure unittest { - void func() {} - formatTest( &func, "void delegate()" ); -} + import std.array : appender; + auto w = appender!string(); + auto spec = singleSpec("%s"); + formatElement(w, "Hello World", spec); -/* - Formats an object of type 'D' according to 'f' and writes it to - 'w'. The pointer 'arg' is assumed to point to an object of type - 'D'. The untyped signature is for the sake of taking this function's - address. - */ -private void formatGeneric(Writer, D, Char)(Writer w, const(void)* arg, ref FormatSpec!Char f) -{ - formatValue(w, *cast(D*) arg, f); + assert(w.data == "\"Hello World\""); } -private void formatNth(Writer, Char, A...)(Writer w, ref FormatSpec!Char f, size_t index, A args) +@safe unittest { - import std.conv : to; - static string gencode(size_t count)() - { - string result; - foreach (n; 0 .. count) - { - auto num = to!string(n); - result ~= - "case "~num~":"~ - " formatValue(w, args["~num~"], f);"~ - " break;"; - } - return result; - } - - switch (index) - { - mixin(gencode!(A.length)()); + // Test for bug 8015 + import std.typecons; - default: - assert(0, "n = "~cast(char)(index + '0')); + struct MyStruct { + string str; + @property string toStr() { + return str; + } + alias toStr this; } -} -pure unittest -{ - int[] a = [ 1, 3, 2 ]; - formatTest( "testing %(%s & %) embedded", a, - "testing 1 & 3 & 2 embedded"); - formatTest( "testing %((%s) %)) wyda3", a, - "testing (1) (3) (2) wyda3" ); - - int[0] empt = []; - formatTest( "(%s)", empt, - "([])" ); + Tuple!(MyStruct) t; } -//------------------------------------------------------------------------------ -// Fix for issue 1591 -private int getNthInt(A...)(uint index, A args) +// undocumented because of deprecation +// Character elements are formatted like UTF-8 character literals. +void formatElement(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) +if (is(CharTypeOf!T) && !is(T == enum)) { - import std.conv : to; - static if (A.length) + if (f.spec == 's') { - if (index) - { - return getNthInt(index - 1, args[1 .. $]); - } - static if (isIntegral!(typeof(args[0]))) - { - return to!int(args[0]); - } - else - { - throw new FormatException("int expected"); - } + put(w, '\''); + formatChar(w, val, '\''); + put(w, '\''); } else - { - throw new FormatException("int expected"); - } + formatValue(w, val, f); } -/* ======================== Unit Tests ====================================== */ - -version(unittest) -void formatTest(T)(T val, string expected, size_t ln = __LINE__, string fn = __FILE__) +/// +@safe unittest { - import core.exception; import std.array : appender; - import std.conv : text; - FormatSpec!char f; auto w = appender!string(); - formatValue(w, val, f); - enforce!AssertError( - w.data == expected, - text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln); + auto spec = singleSpec("%s"); + formatElement(w, "H", spec); + + assert(w.data == "\"H\"", w.data); } -version(unittest) -void formatTest(T)(string fmt, T val, string expected, size_t ln = __LINE__, string fn = __FILE__) +// undocumented +// Maybe T is noncopyable struct, so receive it by 'auto ref'. +void formatElement(Writer, T, Char)(Writer w, auto ref T val, const ref FormatSpec!Char f) +if (!is(StringTypeOf!T) && !is(CharTypeOf!T) || is(T == enum)) { - import core.exception; - import std.array : appender; - import std.conv : text; - auto w = appender!string(); - formattedWrite(w, fmt, val); - enforce!AssertError( - w.data == expected, - text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln); + formatValue(w, val, f); } -version(unittest) -void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) -{ - import core.exception; - import std.conv : text; - import std.array : appender; - FormatSpec!char f; - auto w = appender!string(); - formatValue(w, val, f); - foreach(cur; expected) +/** + Associative arrays are formatted by using $(D ':') and $(D ", ") as + separators, and enclosed by $(D '[') and $(D ']'). + +Params: + w = The $(D OutputRange) to write to. + obj = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) +if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) +{ + AssocArrayTypeOf!T val = obj; + + enforceFmt(f.spec == 's' || f.spec == '(', + "incompatible format character for associative array argument: %" ~ f.spec); + + enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator; + auto fmtSpec = f.spec == '(' ? f.nested : defSpec; + + size_t i = 0; + immutable end = val.length; + + if (f.spec == 's') + put(w, f.seqBefore); + foreach (k, ref v; val) { - if(w.data == cur) return; + auto fmt = FormatSpec!Char(fmtSpec); + fmt.writeUpToNextSpec(w); + if (f.flDash) + { + formatValue(w, k, fmt); + fmt.writeUpToNextSpec(w); + formatValue(w, v, fmt); + } + else + { + formatElement(w, k, fmt); + fmt.writeUpToNextSpec(w); + formatElement(w, v, fmt); + } + if (f.sep !is null) + { + fmt.writeUpToNextSpec(w); + if (++i != end) + put(w, f.sep); + } + else + { + if (++i != end) + fmt.writeUpToNextSpec(w); + } } - enforce!AssertError( - false, - text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln); + if (f.spec == 's') + put(w, f.seqAfter); } -version(unittest) -void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) +/// +@safe pure unittest { - import core.exception; - import std.conv : text; import std.array : appender; auto w = appender!string(); - formattedWrite(w, fmt, val); - foreach(cur; expected) - { - if(w.data == cur) return; - } - enforce!AssertError( - false, - text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln); + auto spec = singleSpec("%s"); + auto aa = ["H":"W"]; + formatElement(w, aa, spec); + + assert(w.data == "[\"H\":\"W\"]", w.data); } -@safe /*pure*/ unittest // formatting floating point values is now impure +@safe unittest { - import std.array; + assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd'); - auto stream = appender!string(); - formattedWrite(stream, "%s", 1.1); - assert(stream.data == "1.1", stream.data); -} + int[string] aa0; + formatTest( aa0, `[]` ); -pure unittest -{ - import std.algorithm; - import std.array; + // elements escaping + formatTest( ["aaa":1, "bbb":2], + [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`] ); + formatTest( ['c':"str"], + `['c':"str"]` ); + formatTest( ['"':"\"", '\'':"'"], + [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`] ); - auto stream = appender!string(); - formattedWrite(stream, "%s", map!"a*a"([2, 3, 5])); - assert(stream.data == "[4, 9, 25]", stream.data); + // range formatting for AA + auto aa3 = [1:"hello", 2:"world"]; + // escape + formatTest( "{%(%s:%s $ %)}", aa3, + [`{1:"hello" $ 2:"world"}`, `{2:"world" $ 1:"hello"}`]); + // use range formatting for key and value, and use %| + formatTest( "{%([%04d->%(%c.%)]%| $ %)}", aa3, + [`{[0001->h.e.l.l.o] $ [0002->w.o.r.l.d]}`, `{[0002->w.o.r.l.d] $ [0001->h.e.l.l.o]}`] ); - // Test shared data. - stream = appender!string(); - shared int s = 6; - formattedWrite(stream, "%s", s); - assert(stream.data == "6"); + // issue 12135 + formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>"); + formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>"); } -pure unittest +@system unittest { - import std.array; - auto stream = appender!string(); - formattedWrite(stream, "%u", 42); - assert(stream.data == "42", stream.data); -} + class C1 { int[char] val; alias val this; this(int[char] v){ val = v; } } + class C2 { int[char] val; alias val this; this(int[char] v){ val = v; } + override string toString() const { return "C"; } } + formatTest( new C1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] ); + formatTest( new C2(['c':1, 'd':2]), "C" ); -pure unittest -{ - // testing raw writes - import std.array; - auto w = appender!(char[])(); - uint a = 0x02030405; - formattedWrite(w, "%+r", a); - assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3 - && w.data[2] == 4 && w.data[3] == 5); - w.clear(); - formattedWrite(w, "%-r", a); - assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4 - && w.data[2] == 3 && w.data[3] == 2); + struct S1 { int[char] val; alias val this; } + struct S2 { int[char] val; alias val this; + string toString() const { return "S"; } } + formatTest( S1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] ); + formatTest( S2(['c':1, 'd':2]), "S" ); } -pure unittest +@safe unittest // Issue 8921 { - // testing positional parameters - import std.array; - auto w = appender!(char[])(); - formattedWrite(w, - "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated", - 42, 0); - assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated", - w.data); - w.clear(); - formattedWrite(w, "asd%s", 23); - assert(w.data == "asd23", w.data); - w.clear(); - formattedWrite(w, "%s%s", 23, 45); - assert(w.data == "2345", w.data); + enum E : char { A = 'a', B = 'b', C = 'c' } + E[3] e = [E.A, E.B, E.C]; + formatTest(e, "[A, B, C]"); + + E[] e2 = [E.A, E.B, E.C]; + formatTest(e2, "[A, B, C]"); } -unittest +template hasToString(T, Char) { - import std.conv : text, octal; - import std.array; - - debug(format) printf("std.format.format.unittest\n"); - - auto stream = appender!(char[])(); - //goto here; - - formattedWrite(stream, - "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo"); - assert(stream.data == "hello world! true 57 ", - stream.data); - - stream.clear(); - formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan); - // core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); - - /* The host C library is used to format floats. C99 doesn't - * specify what the hex digit before the decimal point is for - * %A. */ - - version (linux) + static if (isPointer!T && !isAggregateType!T) { - assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", - stream.data); + // X* does not have toString, even if X is aggregate type has toString. + enum hasToString = 0; } - else version (OSX) + else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((const(char)[] s){}, f); }))) { - assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", - stream.data); + enum hasToString = 4; } - else version (MinGW) + else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}, "%s"); }))) { - assert(stream.data == "1.67 -0XA.3D70A3D70A3D8P-3 nan", - stream.data); + enum hasToString = 3; } - else version (CRuntime_Microsoft) + else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}); }))) { - assert(stream.data == "1.67 -0X1.47AE14P+0 nan", - stream.data); + enum hasToString = 2; } - else version (Android) + else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S) { - // bionic doesn't support hex formatting of floating point numbers - // or lower-case string formatting of nan yet, but it was committed - // recently (April 2014): - // https://code.google.com/p/android/issues/detail?id=64886 + enum hasToString = 1; } else { - assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", - stream.data); - } - stream.clear(); - - formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF); - assert(stream.data == "1234af AFAFAFAF"); - stream.clear(); - - formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF); - assert(stream.data == "100100011010010101111 25753727657"); - stream.clear(); - - formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF); - assert(stream.data == "1193135 2947526575"); - stream.clear(); - - // formattedWrite(stream, "%s", 1.2 + 3.4i); - // assert(stream.data == "1.2+3.4i"); - // stream.clear(); - - formattedWrite(stream, "%a %A", 1.32, 6.78f); - //formattedWrite(stream, "%x %X", 1.32); - version (CRuntime_Microsoft) - assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2"); - else version (Android) - { - // bionic doesn't support hex formatting of floating point numbers, - // but it was committed recently (April 2014): - // https://code.google.com/p/android/issues/detail?id=64886 + enum hasToString = 0; } - else - assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2"); - stream.clear(); - - formattedWrite(stream, "%#06.*f",2,12.345); - assert(stream.data == "012.35"); - stream.clear(); - - formattedWrite(stream, "%#0*.*f",6,2,12.345); - assert(stream.data == "012.35"); - stream.clear(); - - const real constreal = 1; - formattedWrite(stream, "%g",constreal); - assert(stream.data == "1"); - stream.clear(); - - formattedWrite(stream, "%7.4g:", 12.678); - assert(stream.data == " 12.68:"); - stream.clear(); - - formattedWrite(stream, "%7.4g:", 12.678L); - assert(stream.data == " 12.68:"); - stream.clear(); - - formattedWrite(stream, "%04f|%05d|%#05x|%#5x",-4.0,-10,1,1); - assert(stream.data == "-4.000000|-0010|0x001| 0x1", - stream.data); - stream.clear(); - - int i; - string s; - - i = -10; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); - assert(stream.data == "-10|-10|-10|-10|-10.0000"); - stream.clear(); +} - i = -5; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); - assert(stream.data == "-5| -5|-05|-5|-5.0000"); - stream.clear(); +// object formatting with toString +private void formatObject(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f) +if (hasToString!(T, Char)) +{ + static if (is(typeof(val.toString((const(char)[] s){}, f)))) + { + val.toString((const(char)[] s) { put(w, s); }, f); + } + else static if (is(typeof(val.toString((const(char)[] s){}, "%s")))) + { + val.toString((const(char)[] s) { put(w, s); }, f.getCurFmtStr()); + } + else static if (is(typeof(val.toString((const(char)[] s){})))) + { + val.toString((const(char)[] s) { put(w, s); }); + } + else static if (is(typeof(val.toString()) S) && isSomeString!S) + { + put(w, val.toString()); + } + else + static assert(0); +} - i = 0; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); - assert(stream.data == "0| 0|000|0|0.0000"); - stream.clear(); +void enforceValidFormatSpec(T, Char)(const ref FormatSpec!Char f) +{ + static if (!isInputRange!T && hasToString!(T, Char) != 4) + { + enforceFmt(f.spec == 's', + "Expected '%s' format specifier for type '" ~ T.stringof ~ "'"); + } +} - i = 5; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); - assert(stream.data == "5| 5|005|5|5.0000"); - stream.clear(); +@system unittest +{ + static interface IF1 { } + class CIF1 : IF1 { } + static struct SF1 { } + static union UF1 { } + static class CF1 { } - i = 10; - formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); - assert(stream.data == "10| 10|010|10|10.0000"); - stream.clear(); + static interface IF2 { string toString(); } + static class CIF2 : IF2 { override string toString() { return ""; } } + static struct SF2 { string toString() { return ""; } } + static union UF2 { string toString() { return ""; } } + static class CF2 { override string toString() { return ""; } } - formattedWrite(stream, "%.0d", 0); - assert(stream.data == ""); - stream.clear(); + static interface IK1 { void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char) const; } + static class CIK1 : IK1 { override void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char) const { sink("CIK1"); } } + static struct KS1 { void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char) const { sink("KS1"); } } - formattedWrite(stream, "%.g", .34); - assert(stream.data == "0.3"); - stream.clear(); + static union KU1 { void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char) const { sink("KU1"); } } - stream.clear(); formattedWrite(stream, "%.0g", .34); - assert(stream.data == "0.3"); + static class KC1 { void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char) const { sink("KC1"); } } - stream.clear(); formattedWrite(stream, "%.2g", .34); - assert(stream.data == "0.34"); + IF1 cif1 = new CIF1; + assertThrown!FormatException(format("%f", cif1)); + assertThrown!FormatException(format("%f", SF1())); + assertThrown!FormatException(format("%f", UF1())); + assertThrown!FormatException(format("%f", new CF1())); - stream.clear(); formattedWrite(stream, "%0.0008f", 1e-08); - assert(stream.data == "0.00000001"); + IF2 cif2 = new CIF2; + assertThrown!FormatException(format("%f", cif2)); + assertThrown!FormatException(format("%f", SF2())); + assertThrown!FormatException(format("%f", UF2())); + assertThrown!FormatException(format("%f", new CF2())); - stream.clear(); formattedWrite(stream, "%0.0008f", 1e-05); - assert(stream.data == "0.00001000"); + IK1 cik1 = new CIK1; + assert(format("%f", cik1) == "CIK1"); + assert(format("%f", KS1()) == "KS1"); + assert(format("%f", KU1()) == "KU1"); + assert(format("%f", new KC1()) == "KC1"); +} - //return; - //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); +/** + Aggregates ($(D struct), $(D union), $(D class), and $(D interface)) are + basically formatted by calling $(D toString). + $(D toString) should have one of the following signatures: - s = "helloworld"; - string r; - stream.clear(); formattedWrite(stream, "%.2s", s[0..5]); - assert(stream.data == "he"); - stream.clear(); formattedWrite(stream, "%.20s", s[0..5]); - assert(stream.data == "hello"); - stream.clear(); formattedWrite(stream, "%8s", s[0..5]); - assert(stream.data == " hello"); +--- +const void toString(scope void delegate(const(char)[]) sink, FormatSpec fmt); +const void toString(scope void delegate(const(char)[]) sink, string fmt); +const void toString(scope void delegate(const(char)[]) sink); +const string toString(); +--- - byte[] arrbyte = new byte[4]; - arrbyte[0] = 100; - arrbyte[1] = -99; - arrbyte[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrbyte); - assert(stream.data == "[100, -99, 0, 0]", stream.data); + For the class objects which have input range interface, + $(UL $(LI If the instance $(D toString) has overridden + $(D Object.toString), it is used.) + $(LI Otherwise, the objects are formatted as input range.)) - ubyte[] arrubyte = new ubyte[4]; - arrubyte[0] = 100; - arrubyte[1] = 200; - arrubyte[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrubyte); - assert(stream.data == "[100, 200, 0, 0]", stream.data); + For the struct and union objects which does not have $(D toString), + $(UL $(LI If they have range interface, formatted as input range.) + $(LI Otherwise, they are formatted like $(D Type(field1, filed2, ...)).)) - short[] arrshort = new short[4]; - arrshort[0] = 100; - arrshort[1] = -999; - arrshort[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrshort); - assert(stream.data == "[100, -999, 0, 0]"); - stream.clear(); formattedWrite(stream, "%s",arrshort); - assert(stream.data == "[100, -999, 0, 0]"); + Otherwise, are formatted just as their type name. + */ +void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) +if (is(T == class) && !is(T == enum)) +{ + enforceValidFormatSpec!(T, Char)(f); + // TODO: Change this once toString() works for shared objects. + static assert(!is(T == shared), "unable to format shared objects"); - ushort[] arrushort = new ushort[4]; - arrushort[0] = 100; - arrushort[1] = 20_000; - arrushort[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrushort); - assert(stream.data == "[100, 20000, 0, 0]"); + if (val is null) + put(w, "null"); + else + { + static if (hasToString!(T, Char) > 1 || (!isInputRange!T && !is(BuiltinTypeOf!T))) + { + formatObject!(Writer, T, Char)(w, val, f); + } + else + { + //string delegate() dg = &val.toString; + Object o = val; // workaround + string delegate() dg = &o.toString; + if (dg.funcptr != &Object.toString) // toString is overridden + { + formatObject(w, val, f); + } + else static if (isInputRange!T) + { + formatRange(w, val, f); + } + else static if (is(BuiltinTypeOf!T X)) + { + X x = val; + formatValue(w, x, f); + } + else + { + formatObject(w, val, f); + } + } + } +} - int[] arrint = new int[4]; - arrint[0] = 100; - arrint[1] = -999; - arrint[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrint); - assert(stream.data == "[100, -999, 0, 0]"); - stream.clear(); formattedWrite(stream, "%s",arrint); - assert(stream.data == "[100, -999, 0, 0]"); +/++ + $(D formatValue) allows to reuse existing format specifiers: + +/ +@system unittest +{ + import std.format; - long[] arrlong = new long[4]; - arrlong[0] = 100; - arrlong[1] = -999; - arrlong[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrlong); - assert(stream.data == "[100, -999, 0, 0]"); - stream.clear(); formattedWrite(stream, "%s",arrlong); - assert(stream.data == "[100, -999, 0, 0]"); + struct Point + { + int x, y; - ulong[] arrulong = new ulong[4]; - arrulong[0] = 100; - arrulong[1] = 999; - arrulong[3] = 0; - stream.clear(); formattedWrite(stream, "%s", arrulong); - assert(stream.data == "[100, 999, 0, 0]"); + void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char fmt) const + { + sink("("); + sink.formatValue(x, fmt); + sink(","); + sink.formatValue(y, fmt); + sink(")"); + } + } - string[] arr2 = new string[4]; - arr2[0] = "hello"; - arr2[1] = "world"; - arr2[3] = "foo"; - stream.clear(); formattedWrite(stream, "%s", arr2); - assert(stream.data == `["hello", "world", "", "foo"]`, stream.data); + auto p = Point(16,11); + assert(format("%03d", p) == "(016,011)"); + assert(format("%02x", p) == "(10,0b)"); +} - stream.clear(); formattedWrite(stream, "%.8d", 7); - assert(stream.data == "00000007"); +/++ + The following code compares the use of $(D formatValue) and $(D formattedWrite). + +/ +@safe pure unittest +{ + import std.format; + import std.array : appender; - stream.clear(); formattedWrite(stream, "%.8x", 10); - assert(stream.data == "0000000a"); + auto writer1 = appender!string(); + writer1.formattedWrite("%08b", 42); - stream.clear(); formattedWrite(stream, "%-3d", 7); - assert(stream.data == "7 "); + auto writer2 = appender!string(); + auto f = singleSpec("%08b"); + writer2.formatValue(42, f); - stream.clear(); formattedWrite(stream, "%*d", -3, 7); - assert(stream.data == "7 "); + assert(writer1.data == writer2.data && writer1.data == "00101010"); +} - stream.clear(); formattedWrite(stream, "%.*d", -3, 7); - //writeln(stream.data); - assert(stream.data == "7"); +@system unittest +{ + import std.array : appender; + import std.range.interfaces; + // class range (issue 5154) + auto c = inputRangeObject([1,2,3,4]); + formatTest( c, "[1, 2, 3, 4]" ); + assert(c.empty); + c = null; + formatTest( c, "null" ); +} - stream.clear(); formattedWrite(stream, "%s", "abc"c); - assert(stream.data == "abc"); - stream.clear(); formattedWrite(stream, "%s", "def"w); - assert(stream.data == "def", text(stream.data.length)); - stream.clear(); formattedWrite(stream, "%s", "ghi"d); - assert(stream.data == "ghi"); +@system unittest +{ + // 5354 + // If the class has both range I/F and custom toString, the use of custom + // toString routine is prioritized. -here: - void* p = cast(void*)0xDEADBEEF; - stream.clear(); formattedWrite(stream, "%s", p); - assert(stream.data == "DEADBEEF", stream.data); + // Enable the use of custom toString that gets a sink delegate + // for class formatting. - stream.clear(); formattedWrite(stream, "%#x", 0xabcd); - assert(stream.data == "0xabcd"); - stream.clear(); formattedWrite(stream, "%#X", 0xABCD); - assert(stream.data == "0XABCD"); + enum inputRangeCode = + q{ + int[] arr; + this(int[] a){ arr = a; } + @property int front() const { return arr[0]; } + @property bool empty() const { return arr.length == 0; } + void popFront(){ arr = arr[1..$]; } + }; - stream.clear(); formattedWrite(stream, "%#o", octal!12345); - assert(stream.data == "012345"); - stream.clear(); formattedWrite(stream, "%o", 9); - assert(stream.data == "11"); + class C1 + { + mixin(inputRangeCode); + void toString(scope void delegate(const(char)[]) dg, const ref FormatSpec!char f) const { dg("[012]"); } + } + class C2 + { + mixin(inputRangeCode); + void toString(scope void delegate(const(char)[]) dg, string f) const { dg("[012]"); } + } + class C3 + { + mixin(inputRangeCode); + void toString(scope void delegate(const(char)[]) dg) const { dg("[012]"); } + } + class C4 + { + mixin(inputRangeCode); + override string toString() const { return "[012]"; } + } + class C5 + { + mixin(inputRangeCode); + } - stream.clear(); formattedWrite(stream, "%+d", 123); - assert(stream.data == "+123"); - stream.clear(); formattedWrite(stream, "%+d", -123); - assert(stream.data == "-123"); - stream.clear(); formattedWrite(stream, "% d", 123); - assert(stream.data == " 123"); - stream.clear(); formattedWrite(stream, "% d", -123); - assert(stream.data == "-123"); + formatTest( new C1([0, 1, 2]), "[012]" ); + formatTest( new C2([0, 1, 2]), "[012]" ); + formatTest( new C3([0, 1, 2]), "[012]" ); + formatTest( new C4([0, 1, 2]), "[012]" ); + formatTest( new C5([0, 1, 2]), "[0, 1, 2]" ); +} - stream.clear(); formattedWrite(stream, "%%"); - assert(stream.data == "%"); +/// ditto +void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) +if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum)) +{ + enforceValidFormatSpec!(T, Char)(f); + if (val is null) + put(w, "null"); + else + { + static if (hasToString!(T, Char)) + { + formatObject(w, val, f); + } + else static if (isInputRange!T) + { + formatRange(w, val, f); + } + else + { + version (Windows) + { + import core.sys.windows.com : IUnknown; + static if (is(T : IUnknown)) + { + formatValue(w, *cast(void**)&val, f); + } + else + { + formatValue(w, cast(Object) val, f); + } + } + else + { + formatValue(w, cast(Object) val, f); + } + } + } +} - stream.clear(); formattedWrite(stream, "%d", true); - assert(stream.data == "1"); - stream.clear(); formattedWrite(stream, "%d", false); - assert(stream.data == "0"); +@system unittest +{ + // interface + import std.range.interfaces; + InputRange!int i = inputRangeObject([1,2,3,4]); + formatTest( i, "[1, 2, 3, 4]" ); + assert(i.empty); + i = null; + formatTest( i, "null" ); - stream.clear(); formattedWrite(stream, "%d", 'a'); - assert(stream.data == "97", stream.data); - wchar wc = 'a'; - stream.clear(); formattedWrite(stream, "%d", wc); - assert(stream.data == "97"); - dchar dc = 'a'; - stream.clear(); formattedWrite(stream, "%d", dc); - assert(stream.data == "97"); + // interface (downcast to Object) + interface Whatever {} + class C : Whatever + { + override @property string toString() const { return "ab"; } + } + Whatever val = new C; + formatTest( val, "ab" ); - byte b = byte.max; - stream.clear(); formattedWrite(stream, "%x", b); - assert(stream.data == "7f"); - stream.clear(); formattedWrite(stream, "%x", ++b); - assert(stream.data == "80"); - stream.clear(); formattedWrite(stream, "%x", ++b); - assert(stream.data == "81"); + // Issue 11175 + version (Windows) + { + import core.sys.windows.windows : HRESULT; + import core.sys.windows.com : IUnknown, IID; - short sh = short.max; - stream.clear(); formattedWrite(stream, "%x", sh); - assert(stream.data == "7fff"); - stream.clear(); formattedWrite(stream, "%x", ++sh); - assert(stream.data == "8000"); - stream.clear(); formattedWrite(stream, "%x", ++sh); - assert(stream.data == "8001"); + interface IUnknown2 : IUnknown { } - i = int.max; - stream.clear(); formattedWrite(stream, "%x", i); - assert(stream.data == "7fffffff"); - stream.clear(); formattedWrite(stream, "%x", ++i); - assert(stream.data == "80000000"); - stream.clear(); formattedWrite(stream, "%x", ++i); - assert(stream.data == "80000001"); + class D : IUnknown2 + { + extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; } + extern(Windows) uint AddRef() { return 0; } + extern(Windows) uint Release() { return 0; } + } - stream.clear(); formattedWrite(stream, "%x", 10); - assert(stream.data == "a"); - stream.clear(); formattedWrite(stream, "%X", 10); - assert(stream.data == "A"); - stream.clear(); formattedWrite(stream, "%x", 15); - assert(stream.data == "f"); - stream.clear(); formattedWrite(stream, "%X", 15); - assert(stream.data == "F"); + IUnknown2 d = new D; + string expected = format("%X", cast(void*) d); + formatTest(d, expected); + } +} - Object c = null; - stream.clear(); formattedWrite(stream, "%s", c); - assert(stream.data == "null"); +/// ditto +// Maybe T is noncopyable struct, so receive it by 'auto ref'. +void formatValue(Writer, T, Char)(Writer w, auto ref T val, const ref FormatSpec!Char f) +if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum)) +{ + enforceValidFormatSpec!(T, Char)(f); + static if (hasToString!(T, Char)) + { + formatObject(w, val, f); + } + else static if (isInputRange!T) + { + formatRange(w, val, f); + } + else static if (is(T == struct)) + { + enum left = T.stringof~"("; + enum separator = ", "; + enum right = ")"; - enum TestEnum + put(w, left); + foreach (i, e; val.tupleof) + { + static if (0 < i && val.tupleof[i-1].offsetof == val.tupleof[i].offsetof) + { + static if (i == val.tupleof.length - 1 || val.tupleof[i].offsetof != val.tupleof[i+1].offsetof) + put(w, separator~val.tupleof[i].stringof[4..$]~"}"); + else + put(w, separator~val.tupleof[i].stringof[4..$]); + } + else + { + static if (i+1 < val.tupleof.length && val.tupleof[i].offsetof == val.tupleof[i+1].offsetof) + put(w, (i > 0 ? separator : "")~"#{overlap "~val.tupleof[i].stringof[4..$]); + else + { + static if (i > 0) + put(w, separator); + formatElement(w, e, f); + } + } + } + put(w, right); + } + else { - Value1, Value2 + put(w, T.stringof); } - stream.clear(); formattedWrite(stream, "%s", TestEnum.Value2); - assert(stream.data == "Value2", stream.data); - stream.clear(); formattedWrite(stream, "%s", cast(TestEnum)5); - assert(stream.data == "cast(TestEnum)5", stream.data); +} - //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); - //stream.clear(); formattedWrite(stream, "%s", aa.values); - //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); - //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]"); - //stream.clear(); formattedWrite(stream, "%s", aa); - //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); +@safe unittest +{ + // bug 4638 + struct U8 { string toString() const { return "blah"; } } + struct U16 { wstring toString() const { return "blah"; } } + struct U32 { dstring toString() const { return "blah"; } } + formatTest( U8(), "blah" ); + formatTest( U16(), "blah" ); + formatTest( U32(), "blah" ); +} + +@safe unittest +{ + // 3890 + struct Int{ int n; } + struct Pair{ string s; Int i; } + formatTest( Pair("hello", Int(5)), + `Pair("hello", Int(5))` ); +} - static const dchar[] ds = ['a','b']; - for (int j = 0; j < ds.length; ++j) +@system unittest +{ + // union formatting without toString + union U1 { - stream.clear(); formattedWrite(stream, " %d", ds[j]); - if (j == 0) - assert(stream.data == " 97"); - else - assert(stream.data == " 98"); + int n; + string s; } + U1 u1; + formatTest( u1, "U1" ); - stream.clear(); formattedWrite(stream, "%.-3d", 7); - assert(stream.data == "7", ">" ~ stream.data ~ "<"); - + // union formatting with toString + union U2 + { + int n; + string s; + string toString() const { return s; } + } + U2 u2; + u2.s = "hello"; + formatTest( u2, "hello" ); +} - // systematic test - const string[] flags = [ "-", "+", "#", "0", " ", "" ]; - const string[] widths = [ "", "0", "4", "20" ]; - const string[] precs = [ "", ".", ".0", ".4", ".20" ]; - const string formats = "sdoxXeEfFgGaA"; - /+ - foreach (flag1; flags) - foreach (flag2; flags) - foreach (flag3; flags) - foreach (flag4; flags) - foreach (flag5; flags) - foreach (width; widths) - foreach (prec; precs) - foreach (format; formats) - { - stream.clear(); - auto fmt = "%" ~ flag1 ~ flag2 ~ flag3 - ~ flag4 ~ flag5 ~ width ~ prec ~ format - ~ '\0'; - fmt = fmt[0 .. $ - 1]; // keep it zero-term - char buf[256]; - buf[0] = 0; - switch (format) - { - case 's': - formattedWrite(stream, fmt, "wyda"); - snprintf(buf.ptr, buf.length, fmt.ptr, - "wyda\0".ptr); - break; - case 'd': - formattedWrite(stream, fmt, 456); - snprintf(buf.ptr, buf.length, fmt.ptr, - 456); - break; - case 'o': - formattedWrite(stream, fmt, 345); - snprintf(buf.ptr, buf.length, fmt.ptr, - 345); - break; - case 'x': - formattedWrite(stream, fmt, 63546); - snprintf(buf.ptr, buf.length, fmt.ptr, - 63546); - break; - case 'X': - formattedWrite(stream, fmt, 12566); - snprintf(buf.ptr, buf.length, fmt.ptr, - 12566); - break; - case 'e': - formattedWrite(stream, fmt, 3245.345234); - snprintf(buf.ptr, buf.length, fmt.ptr, - 3245.345234); - break; - case 'E': - formattedWrite(stream, fmt, 3245.2345234); - snprintf(buf.ptr, buf.length, fmt.ptr, - 3245.2345234); - break; - case 'f': - formattedWrite(stream, fmt, 3245234.645675); - snprintf(buf.ptr, buf.length, fmt.ptr, - 3245234.645675); - break; - case 'F': - formattedWrite(stream, fmt, 213412.43); - snprintf(buf.ptr, buf.length, fmt.ptr, - 213412.43); - break; - case 'g': - formattedWrite(stream, fmt, 234134.34); - snprintf(buf.ptr, buf.length, fmt.ptr, - 234134.34); - break; - case 'G': - formattedWrite(stream, fmt, 23141234.4321); - snprintf(buf.ptr, buf.length, fmt.ptr, - 23141234.4321); - break; - case 'a': - formattedWrite(stream, fmt, 21341234.2134123); - snprintf(buf.ptr, buf.length, fmt.ptr, - 21341234.2134123); - break; - case 'A': - formattedWrite(stream, fmt, 1092384098.45234); - snprintf(buf.ptr, buf.length, fmt.ptr, - 1092384098.45234); - break; - default: - break; - } - auto exp = buf[0 .. strlen(buf.ptr)]; - if (stream.data != exp) - { - writeln("Format: \"", fmt, '"'); - writeln("Expected: >", exp, "<"); - writeln("Actual: >", stream.data, - "<"); - assert(false); - } - }+/ -} - -unittest +@system unittest { import std.array; - import std.stdio; - - immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); - if (false) writeln(aa.keys); - assert(aa[3] == "hello"); - assert(aa[4] == "betty"); - // if (false) - // { - // writeln(aa.values[0]); - // writeln(aa.values[1]); - // writefln("%s", typeid(typeof(aa.values))); - // writefln("%s", aa[3]); - // writefln("%s", aa[4]); - // writefln("%s", aa.values); - // //writefln("%s", aa); - // wstring a = "abcd"; - // writefln(a); - // dstring b = "abcd"; - // writefln(b); - // } - - auto stream = appender!(char[])(); - alias AllNumerics = - TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, - float, double, real); - foreach (T; AllNumerics) + // 7230 + static struct Bug7230 { - T value = 1; - stream.clear(); - formattedWrite(stream, "%s", value); - assert(stream.data == "1"); + string s = "hello"; + union { + string a; + int b; + double c; + } + long x = 10; } - //auto r = format("%s", aa.values); - stream.clear(); formattedWrite(stream, "%s", aa); - //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]", stream.data); - //r = format("%s", aa); - //assert(r == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); -} + Bug7230 bug; + bug.b = 123; -unittest -{ - string s = "hello!124:34.5"; - string a; - int b; - double c; - formattedRead(s, "%s!%s:%s", &a, &b, &c); - assert(a == "hello" && b == 124 && c == 34.5); + FormatSpec!char f; + auto w = appender!(char[])(); + formatValue(w, bug, f); + assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`); } -version(unittest) -void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__) +@safe unittest { - import core.exception; - import std.array : appender; + import std.array; + static struct S{ @disable this(this); } + S s; + + FormatSpec!char f; auto w = appender!string(); - formattedWrite(w, fmt, val); + formatValue(w, s, f); + assert(w.data == "S()"); +} - auto input = w.data; - enforce!AssertError( - input == formatted, - input, fn, ln); +/** +$(D enum) is formatted like its base value. - T val2; - formattedRead(input, fmt, &val2); - static if (isAssociativeArray!T) - if (__ctfe) +Params: + w = The $(D OutputRange) to write to. + val = The value to write. + f = The $(D FormatSpec) defining how to write the value. + */ +void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) +if (is(T == enum)) +{ + if (f.spec == 's') { - alias aa1 = val; - alias aa2 = val2; - //assert(aa1 == aa2); - - assert(aa1.length == aa2.length); - - assert(aa1.keys == aa2.keys); - - //assert(aa1.values == aa2.values); - assert(aa1.values.length == aa2.values.length); - foreach (i; 0 .. aa1.values.length) - assert(aa1.values[i] == aa2.values[i]); + foreach (i, e; EnumMembers!T) + { + if (val == e) + { + formatValue(w, __traits(allMembers, T)[i], f); + return; + } + } - //foreach (i, key; aa1.keys) - // assert(aa1.values[i] == aa1[key]); - //foreach (i, key; aa2.keys) - // assert(aa2.values[i] == aa2[key]); - return; + // val is not a member of T, output cast(T) rawValue instead. + put(w, "cast(" ~ T.stringof ~ ")"); + static assert(!is(OriginalType!T == T)); } - enforce!AssertError( - val == val2, - input, fn, ln); + formatValue(w, cast(OriginalType!T) val, f); } -version(unittest) -void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__) +/// +@safe pure unittest { - import core.exception; import std.array : appender; auto w = appender!string(); - formattedWrite(w, fmt, val); - - auto input = w.data; - - foreach(cur; formatted) - { - if(input == cur) return; - } - enforce!AssertError( - false, - input, - fn, - ln); - - T val2; - formattedRead(input, fmt, &val2); - static if (isAssociativeArray!T) - if (__ctfe) - { - alias aa1 = val; - alias aa2 = val2; - //assert(aa1 == aa2); - - assert(aa1.length == aa2.length); + auto spec = singleSpec("%s"); - assert(aa1.keys == aa2.keys); + enum A { first, second, third } - //assert(aa1.values == aa2.values); - assert(aa1.values.length == aa2.values.length); - foreach (i; 0 .. aa1.values.length) - assert(aa1.values[i] == aa2.values[i]); + formatElement(w, A.second, spec); - //foreach (i, key; aa1.keys) - // assert(aa1.values[i] == aa1[key]); - //foreach (i, key; aa2.keys) - // assert(aa2.values[i] == aa2[key]); - return; - } - enforce!AssertError( - val == val2, - input, fn, ln); + assert(w.data == "second"); } -unittest +@safe unittest { - void booleanTest() - { - auto b = true; - formatReflectTest(b, "%s", `true`); - formatReflectTest(b, "%b", `1`); - formatReflectTest(b, "%o", `1`); - formatReflectTest(b, "%d", `1`); - formatReflectTest(b, "%u", `1`); - formatReflectTest(b, "%x", `1`); - } + enum A { first, second, third } + formatTest( A.second, "second" ); + formatTest( cast(A) 72, "cast(A)72" ); +} +@safe unittest +{ + enum A : string { one = "uno", two = "dos", three = "tres" } + formatTest( A.three, "three" ); + formatTest( cast(A)"mill\ón", "cast(A)mill\ón" ); +} +@safe unittest +{ + enum A : bool { no, yes } + formatTest( A.yes, "yes" ); + formatTest( A.no, "no" ); +} +@safe unittest +{ + // Test for bug 6892 + enum Foo { A = 10 } + formatTest("%s", Foo.A, "A"); + formatTest(">%4s<", Foo.A, "> A<"); + formatTest("%04d", Foo.A, "0010"); + formatTest("%+2u", Foo.A, "+10"); + formatTest("%02x", Foo.A, "0a"); + formatTest("%3o", Foo.A, " 12"); + formatTest("%b", Foo.A, "1010"); +} - void integerTest() +/** + Pointers are formatted as hex integers. + */ +void formatValue(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) +if (isPointer!T && !is(T == enum) && !hasToString!(T, Char)) +{ + static if (isInputRange!T) { - auto n = 127; - formatReflectTest(n, "%s", `127`); - formatReflectTest(n, "%b", `1111111`); - formatReflectTest(n, "%o", `177`); - formatReflectTest(n, "%d", `127`); - formatReflectTest(n, "%u", `127`); - formatReflectTest(n, "%x", `7f`); + if (val !is null) + { + formatRange(w, *val, f); + return; + } } - void floatingTest() - { - auto f = 3.14; - formatReflectTest(f, "%s", `3.14`); - version (MinGW) - formatReflectTest(f, "%e", `3.140000e+000`); - else - formatReflectTest(f, "%e", `3.140000e+00`); - formatReflectTest(f, "%f", `3.140000`); - formatReflectTest(f, "%g", `3.14`); - } + static if (is(typeof({ shared const void* p = val; }))) + alias SharedOf(T) = shared(T); + else + alias SharedOf(T) = T; - void charTest() - { - auto c = 'a'; - formatReflectTest(c, "%s", `a`); - formatReflectTest(c, "%c", `a`); - formatReflectTest(c, "%b", `1100001`); - formatReflectTest(c, "%o", `141`); - formatReflectTest(c, "%d", `97`); - formatReflectTest(c, "%u", `97`); - formatReflectTest(c, "%x", `61`); - } + const SharedOf!(void*) p = val; + const pnum = ()@trusted{ return cast(ulong) p; }(); - void strTest() + if (f.spec == 's') { - auto s = "hello"; - formatReflectTest(s, "%s", `hello`); - formatReflectTest(s, "%(%c,%)", `h,e,l,l,o`); - formatReflectTest(s, "%(%s,%)", `'h','e','l','l','o'`); - formatReflectTest(s, "[%(<%c>%| $ %)]", `[ $ $ $ $ ]`); + if (p is null) + { + put(w, "null"); + return; + } + FormatSpec!Char fs = f; // fs is copy for change its values. + fs.spec = 'X'; + formatValue(w, pnum, fs); } - - void daTest() + else { - auto a = [1,2,3,4]; - formatReflectTest(a, "%s", `[1, 2, 3, 4]`); - formatReflectTest(a, "[%(%s; %)]", `[1; 2; 3; 4]`); - formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`); + enforceFmt(f.spec == 'X' || f.spec == 'x', + "Expected one of %s, %x or %X for pointer type."); + formatValue(w, pnum, f); } +} - void saTest() - { - int[4] sa = [1,2,3,4]; - formatReflectTest(sa, "%s", `[1, 2, 3, 4]`); - formatReflectTest(sa, "[%(%s; %)]", `[1; 2; 3; 4]`); - formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`); - } +@safe pure unittest +{ + // pointer + import std.range; + auto r = retro([1,2,3,4]); + auto p = ()@trusted{ auto p = &r; return p; }(); + formatTest( p, "[4, 3, 2, 1]" ); + assert(p.empty); + p = null; + formatTest( p, "null" ); - void aaTest() + auto q = ()@trusted{ return cast(void*) 0xFFEECCAA; }(); + formatTest( q, "FFEECCAA" ); +} + +@system pure unittest +{ + // Test for issue 7869 + struct S { - auto aa = [1:"hello", 2:"world"]; - formatReflectTest(aa, "%s", [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]); - formatReflectTest(aa, "[%(%s->%s, %)]", [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]); - formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]); + string toString() const { return ""; } } + S* p = null; + formatTest( p, "null" ); - import std.exception; - assertCTFEable!( - { - booleanTest(); - integerTest(); - if (!__ctfe) floatingTest(); // snprintf - charTest(); - strTest(); - daTest(); - saTest(); - aaTest(); - return true; - }); + S* q = cast(S*) 0xFFEECCAA; + formatTest( q, "FFEECCAA" ); } -//------------------------------------------------------------------------------ -private void skipData(Range, Char)(ref Range input, ref FormatSpec!Char spec) +@system unittest { - import std.ascii : isDigit; - import std.conv : text; - - switch (spec.spec) + // Test for issue 8186 + class B { - case 'c': input.popFront(); break; - case 'd': - if (input.front == '+' || input.front == '-') input.popFront(); - goto case 'u'; - case 'u': - while (!input.empty && isDigit(input.front)) input.popFront(); - break; - default: - assert(false, - text("Format specifier not understood: %", spec.spec)); + int*a; + this(){ a = new int; } + alias a this; } + formatTest( B.init, "null" ); } -private template acceptedSpecs(T) +@system pure unittest { - static if (isIntegral!T) enum acceptedSpecs = "bdosuxX"; - else static if (isFloatingPoint!T) enum acceptedSpecs = "seEfgG"; - else static if (isSomeChar!T) enum acceptedSpecs = "bcdosuxX"; // integral + 'c' - else enum acceptedSpecs = ""; + // Test for issue 9336 + shared int i; + format("%s", &i); +} + +@system pure unittest +{ + // Test for issue 11778 + int* p = null; + assertThrown(format("%d", p)); + assertThrown(format("%04d", p + 2)); +} + +@safe pure unittest +{ + // Test for issue 12505 + void* p = null; + formatTest( "%08X", p, "00000000" ); } /** - * Reads a boolean value and returns it. + Delegates are formatted by 'ReturnType delegate(Parameters) FunctionAttributes' */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && is(Unqual!T == bool)) +void formatValue(Writer, T, Char)(Writer w, scope T, const ref FormatSpec!Char f) +if (isDelegate!T) { - import std.algorithm : find; - import std.conv : parse, text; - if (spec.spec == 's') + formatValue(w, T.stringof, f); +} + +/// +@safe pure unittest +{ + import std.conv : to; + + int i; + + int foo(short k) @nogc { - return parse!T(input); + return i + k; } - enforce(find(acceptedSpecs!long, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - return unformatValue!long(input, spec) != 0; + @system int delegate(short) @nogc bar() nothrow pure + { + int* p = new int; + return &foo; + } + + assert(to!string(&bar) == "int delegate(short) @nogc delegate() pure nothrow @system"); } -pure unittest +@safe unittest { - string line; - - bool f1; - - line = "true"; - formattedRead(line, "%s", &f1); - assert(f1); + void func() @system { __gshared int x; ++x; throw new Exception("msg"); } + version (linux) formatTest( &func, "void delegate() @system" ); +} - line = "TrUE"; - formattedRead(line, "%s", &f1); - assert(f1); +/* + Formats an object of type 'D' according to 'f' and writes it to + 'w'. The pointer 'arg' is assumed to point to an object of type + 'D'. The untyped signature is for the sake of taking this function's + address. + */ +private void formatGeneric(Writer, D, Char)(Writer w, const(void)* arg, const ref FormatSpec!Char f) +{ + formatValue(w, *cast(D*) arg, f); +} - line = "false"; - formattedRead(line, "%s", &f1); - assert(!f1); +private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f, size_t index, A args) +{ + import std.conv : to; + static string gencode(size_t count)() + { + string result; + foreach (n; 0 .. count) + { + auto num = to!string(n); + result ~= + "case "~num~":"~ + " formatValue(w, args["~num~"], f);"~ + " break;"; + } + return result; + } - line = "fALsE"; - formattedRead(line, "%s", &f1); - assert(!f1); + switch (index) + { + mixin(gencode!(A.length)()); - line = "1"; - formattedRead(line, "%d", &f1); - assert(f1); + default: + assert(0, "n = "~cast(char)(index + '0')); + } +} - line = "-1"; - formattedRead(line, "%d", &f1); - assert(f1); +@safe pure unittest +{ + int[] a = [ 1, 3, 2 ]; + formatTest( "testing %(%s & %) embedded", a, + "testing 1 & 3 & 2 embedded"); + formatTest( "testing %((%s) %)) wyda3", a, + "testing (1) (3) (2) wyda3" ); - line = "0"; - formattedRead(line, "%d", &f1); - assert(!f1); + int[0] empt = []; + formatTest( "(%s)", empt, + "([])" ); +} - line = "-0"; - formattedRead(line, "%d", &f1); - assert(!f1); +//------------------------------------------------------------------------------ +// Fix for issue 1591 +private int getNthInt(A...)(uint index, A args) +{ + return getNth!(isIntegral,int)(index, args); } -/** - * Reads null literal and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && is(T == typeof(null))) +private T getNth(alias Condition, T,A...)(uint index, A args) { - import std.conv : parse, text; - enforce(spec.spec == 's', - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + import std.conv : to; - return parse!T(input); + static if (A.length) + { + if (index) + { + return getNth!(Condition,T)(index - 1, args[1 .. $]); + } + static if (Condition!(typeof(args[0]))) + { + return to!T(args[0]); + } + else + { + throw new FormatException(T.stringof ~ " expected, not " ~ + typeof(args[0]).stringof); + } + } + else + { + throw new FormatException("missing integer width/precision argument"); + } } -/** - Reads an integral value and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && isIntegral!T && !is(T == enum)) +@safe unittest { - import std.algorithm : find; - import std.conv : parse, text; - enforce(find(acceptedSpecs!T, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) + == "int expected, not double"); + assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) + == "int expected, not char"); + assert(collectExceptionMsg!FormatException(format("%.*d", 5)) + == "Orphan format specifier: %d"); + assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) + == "missing integer width/precision argument"); +} - enforce(spec.width == 0); // TODO +/* ======================== Unit Tests ====================================== */ - uint base = - spec.spec == 'x' || spec.spec == 'X' ? 16 : - spec.spec == 'o' ? 8 : - spec.spec == 'b' ? 2 : - spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0; - assert(base != 0); - return parse!T(input, base); +version(unittest) +void formatTest(T)(T val, string expected, size_t ln = __LINE__, string fn = __FILE__) +{ + import core.exception : AssertError; + import std.array : appender; + import std.conv : text; + FormatSpec!char f; + auto w = appender!string(); + formatValue(w, val, f); + enforce!AssertError( + w.data == expected, + text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln); } -/** - Reads a floating-point value and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isFloatingPoint!T && !is(T == enum)) +version(unittest) +void formatTest(T)(string fmt, T val, string expected, size_t ln = __LINE__, string fn = __FILE__) @safe +{ + import core.exception : AssertError; + import std.array : appender; + import std.conv : text; + auto w = appender!string(); + formattedWrite(w, fmt, val); + enforce!AssertError( + w.data == expected, + text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln); +} + +version(unittest) +void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) { - import std.algorithm : find; - import std.conv : parse, text; - if (spec.spec == 'r') + import core.exception : AssertError; + import std.conv : text; + import std.array : appender; + FormatSpec!char f; + auto w = appender!string(); + formatValue(w, val, f); + foreach (cur; expected) { - // raw read - //enforce(input.length >= T.sizeof); - enforce(isSomeString!Range || ElementType!(Range).sizeof == 1); - union X - { - ubyte[T.sizeof] raw; - T typed; - } - X x; - foreach (i; 0 .. T.sizeof) - { - static if (isSomeString!Range) - { - x.raw[i] = input[0]; - input = input[1 .. $]; - } - else - { - // TODO: recheck this - x.raw[i] = cast(ubyte) input.front; - input.popFront(); - } - } - return x.typed; + if (w.data == cur) return; } - enforce(find(acceptedSpecs!T, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - - return parse!T(input); + enforce!AssertError( + false, + text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln); } -version(none)unittest +version(unittest) +void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) @safe { - union A + import core.exception : AssertError; + import std.conv : text; + import std.array : appender; + auto w = appender!string(); + formattedWrite(w, fmt, val); + foreach (cur; expected) { - char[float.sizeof] untyped; - float typed; + if (w.data == cur) return; } - A a; - a.typed = 5.5; - char[] input = a.untyped[]; - float witness; - formattedRead(input, "%r", &witness); - assert(witness == a.typed); + enforce!AssertError( + false, + text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln); } -pure unittest +@safe /*pure*/ unittest // formatting floating point values is now impure { - import std.typecons; - char[] line = "1 2".dup; - int a, b; - formattedRead(line, "%s %s", &a, &b); - assert(a == 1 && b == 2); - - line = "10 2 3".dup; - formattedRead(line, "%d ", &a); - assert(a == 10); - assert(line == "2 3"); - - Tuple!(int, float) t; - line = "1 2.125".dup; - formattedRead(line, "%d %g", &t); - assert(t[0] == 1 && t[1] == 2.125); + import std.array; - line = "1 7643 2.125".dup; - formattedRead(line, "%s %*u %s", &t); - assert(t[0] == 1 && t[1] == 2.125); + auto stream = appender!string(); + formattedWrite(stream, "%s", 1.1); + assert(stream.data == "1.1", stream.data); } -/** - * Reads one character and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && isSomeChar!T && !is(T == enum)) +@safe pure unittest { - import std.algorithm : find; - import std.conv : to, text; - if (spec.spec == 's' || spec.spec == 'c') - { - auto result = to!T(input.front); - input.popFront(); - return result; - } - enforce(find(acceptedSpecs!T, spec.spec).length, - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + import std.algorithm; + import std.array; - static if (T.sizeof == 1) - return unformatValue!ubyte(input, spec); - else static if (T.sizeof == 2) - return unformatValue!ushort(input, spec); - else static if (T.sizeof == 4) - return unformatValue!uint(input, spec); - else - static assert(0); + auto stream = appender!string(); + formattedWrite(stream, "%s", map!"a*a"([2, 3, 5])); + assert(stream.data == "[4, 9, 25]", stream.data); + + // Test shared data. + stream = appender!string(); + shared int s = 6; + formattedWrite(stream, "%s", s); + assert(stream.data == "6"); } -pure unittest +@safe pure unittest { - string line; + import std.array; + auto stream = appender!string(); + formattedWrite(stream, "%u", 42); + assert(stream.data == "42", stream.data); +} - char c1, c2; +@safe pure unittest +{ + // testing raw writes + import std.array; + auto w = appender!(char[])(); + uint a = 0x02030405; + formattedWrite(w, "%+r", a); + assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3 + && w.data[2] == 4 && w.data[3] == 5); + w.clear(); + formattedWrite(w, "%-r", a); + assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4 + && w.data[2] == 3 && w.data[3] == 2); +} - line = "abc"; - formattedRead(line, "%s%c", &c1, &c2); - assert(c1 == 'a' && c2 == 'b'); - assert(line == "c"); +@safe pure unittest +{ + // testing positional parameters + import std.array; + auto w = appender!(char[])(); + formattedWrite(w, + "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated", + 42, 0); + assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated", + w.data); + w.clear(); + formattedWrite(w, "asd%s", 23); + assert(w.data == "asd23", w.data); + w.clear(); + formattedWrite(w, "%s%s", 23, 45); + assert(w.data == "2345", w.data); } -/** - Reads a string and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) +@safe unittest { - import std.conv : text; + import std.conv : text, octal; + import std.array : appender; + import std.c.stdio : snprintf; + import core.stdc.string : strlen; - if (spec.spec == '(') - { - return unformatRange!T(input, spec); - } - enforce(spec.spec == 's', - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + debug(format) printf("std.format.format.unittest\n"); - static if (isStaticArray!T) + auto stream = appender!(char[])(); + //goto here; + + formattedWrite(stream, + "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo"); + assert(stream.data == "hello world! true 57 ", + stream.data); + + stream.clear(); + formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan); + // core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); + + /* The host C library is used to format floats. C99 doesn't + * specify what the hex digit before the decimal point is for + * %A. */ + + version (CRuntime_Glibc) { - T result; - auto app = result[]; + assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", + stream.data); } - else + else version (OSX) { - import std.array : appender; - auto app = appender!T(); + assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", + stream.data); } - if (spec.trailing.empty) + else version (MinGW) { - for (; !input.empty; input.popFront()) - { - static if (isStaticArray!T) - if (app.empty) - break; - app.put(input.front); - } + assert(stream.data == "1.67 -0XA.3D70A3D70A3D8P-3 nan", + stream.data); } - else + else version (CRuntime_Microsoft) { - auto end = spec.trailing.front; - for (; !input.empty && input.front != end; input.popFront()) - { - static if (isStaticArray!T) - if (app.empty) - break; - app.put(input.front); - } + assert(stream.data == "1.67 -0X1.47AE14P+0 nan" + || stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", // MSVCRT 14+ (VS 2015) + stream.data); } - static if (isStaticArray!T) + else { - enforce(app.empty, "need more input"); - return result; + assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", + stream.data); } + stream.clear(); + + formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF); + assert(stream.data == "1234af AFAFAFAF"); + stream.clear(); + + formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF); + assert(stream.data == "100100011010010101111 25753727657"); + stream.clear(); + + formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF); + assert(stream.data == "1193135 2947526575"); + stream.clear(); + + // formattedWrite(stream, "%s", 1.2 + 3.4i); + // assert(stream.data == "1.2+3.4i"); + // stream.clear(); + + formattedWrite(stream, "%a %A", 1.32, 6.78f); + //formattedWrite(stream, "%x %X", 1.32); + version (CRuntime_Microsoft) + assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2" + || stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB860000000P+2"); // MSVCRT 14+ (VS 2015) else - return app.data; -} + assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2"); + stream.clear(); -pure unittest -{ - string line; + formattedWrite(stream, "%#06.*f",2,12.345); + assert(stream.data == "012.35"); + stream.clear(); - string s1, s2; + formattedWrite(stream, "%#0*.*f",6,2,12.345); + assert(stream.data == "012.35"); + stream.clear(); - line = "hello, world"; - formattedRead(line, "%s", &s1); - assert(s1 == "hello, world", s1); + const real constreal = 1; + formattedWrite(stream, "%g",constreal); + assert(stream.data == "1"); + stream.clear(); + + formattedWrite(stream, "%7.4g:", 12.678); + assert(stream.data == " 12.68:"); + stream.clear(); + + formattedWrite(stream, "%7.4g:", 12.678L); + assert(stream.data == " 12.68:"); + stream.clear(); + + formattedWrite(stream, "%04f|%05d|%#05x|%#5x",-4.0,-10,1,1); + assert(stream.data == "-4.000000|-0010|0x001| 0x1", + stream.data); + stream.clear(); - line = "hello, world;yah"; - formattedRead(line, "%s;%s", &s1, &s2); - assert(s1 == "hello, world", s1); - assert(s2 == "yah", s2); + int i; + string s; - line = `['h','e','l','l','o']`; - string s3; - formattedRead(line, "[%(%s,%)]", &s3); - assert(s3 == "hello"); + i = -10; + formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(stream.data == "-10|-10|-10|-10|-10.0000"); + stream.clear(); - line = `"hello"`; - string s4; - formattedRead(line, "\"%(%c%)\"", &s4); - assert(s4 == "hello"); -} + i = -5; + formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(stream.data == "-5| -5|-05|-5|-5.0000"); + stream.clear(); -/** - Reads an array (except for string types) and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && isArray!T && !is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) -{ - import std.conv : parse, text; - if (spec.spec == '(') - { - return unformatRange!T(input, spec); - } - enforce(spec.spec == 's', - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + i = 0; + formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(stream.data == "0| 0|000|0|0.0000"); + stream.clear(); - return parse!T(input); -} + i = 5; + formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(stream.data == "5| 5|005|5|5.0000"); + stream.clear(); -pure unittest -{ - string line; + i = 10; + formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i); + assert(stream.data == "10| 10|010|10|10.0000"); + stream.clear(); - line = "[1,2,3]"; - int[] s1; - formattedRead(line, "%s", &s1); - assert(s1 == [1,2,3]); -} + formattedWrite(stream, "%.0d", 0); + assert(stream.data == ""); + stream.clear(); -pure unittest -{ - string line; + formattedWrite(stream, "%.g", .34); + assert(stream.data == "0.3"); + stream.clear(); - line = "[1,2,3]"; - int[] s1; - formattedRead(line, "[%(%s,%)]", &s1); - assert(s1 == [1,2,3]); + stream.clear(); formattedWrite(stream, "%.0g", .34); + assert(stream.data == "0.3"); - line = `["hello", "world"]`; - string[] s2; - formattedRead(line, "[%(%s, %)]", &s2); - assert(s2 == ["hello", "world"]); + stream.clear(); formattedWrite(stream, "%.2g", .34); + assert(stream.data == "0.34"); - line = "123 456"; - int[] s3; - formattedRead(line, "%(%s %)", &s3); - assert(s3 == [123, 456]); + stream.clear(); formattedWrite(stream, "%0.0008f", 1e-08); + assert(stream.data == "0.00000001"); - line = "h,e,l,l,o; w,o,r,l,d"; - string[] s4; - formattedRead(line, "%(%(%c,%); %)", &s4); - assert(s4 == ["hello", "world"]); -} + stream.clear(); formattedWrite(stream, "%0.0008f", 1e-05); + assert(stream.data == "0.00001000"); -pure unittest -{ - string line; + //return; + //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); - int[4] sa1; - line = `[1,2,3,4]`; - formattedRead(line, "%s", &sa1); - assert(sa1 == [1,2,3,4]); + s = "helloworld"; + string r; + stream.clear(); formattedWrite(stream, "%.2s", s[0 .. 5]); + assert(stream.data == "he"); + stream.clear(); formattedWrite(stream, "%.20s", s[0 .. 5]); + assert(stream.data == "hello"); + stream.clear(); formattedWrite(stream, "%8s", s[0 .. 5]); + assert(stream.data == " hello"); - int[4] sa2; - line = `[1,2,3]`; - assertThrown(formattedRead(line, "%s", &sa2)); + byte[] arrbyte = new byte[4]; + arrbyte[0] = 100; + arrbyte[1] = -99; + arrbyte[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrbyte); + assert(stream.data == "[100, -99, 0, 0]", stream.data); - int[4] sa3; - line = `[1,2,3,4,5]`; - assertThrown(formattedRead(line, "%s", &sa3)); -} + ubyte[] arrubyte = new ubyte[4]; + arrubyte[0] = 100; + arrubyte[1] = 200; + arrubyte[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrubyte); + assert(stream.data == "[100, 200, 0, 0]", stream.data); -pure unittest -{ - string input; + short[] arrshort = new short[4]; + arrshort[0] = 100; + arrshort[1] = -999; + arrshort[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrshort); + assert(stream.data == "[100, -999, 0, 0]"); + stream.clear(); formattedWrite(stream, "%s",arrshort); + assert(stream.data == "[100, -999, 0, 0]"); - int[4] sa1; - input = `[1,2,3,4]`; - formattedRead(input, "[%(%s,%)]", &sa1); - assert(sa1 == [1,2,3,4]); + ushort[] arrushort = new ushort[4]; + arrushort[0] = 100; + arrushort[1] = 20_000; + arrushort[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrushort); + assert(stream.data == "[100, 20000, 0, 0]"); - int[4] sa2; - input = `[1,2,3]`; - assertThrown(formattedRead(input, "[%(%s,%)]", &sa2)); -} + int[] arrint = new int[4]; + arrint[0] = 100; + arrint[1] = -999; + arrint[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrint); + assert(stream.data == "[100, -999, 0, 0]"); + stream.clear(); formattedWrite(stream, "%s",arrint); + assert(stream.data == "[100, -999, 0, 0]"); -pure unittest -{ - // 7241 - string input = "a"; - auto spec = FormatSpec!char("%s"); - spec.readUpToNextSpec(input); - auto result = unformatValue!(dchar[1])(input, spec); - assert(result[0] == 'a'); -} + long[] arrlong = new long[4]; + arrlong[0] = 100; + arrlong[1] = -999; + arrlong[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrlong); + assert(stream.data == "[100, -999, 0, 0]"); + stream.clear(); formattedWrite(stream, "%s",arrlong); + assert(stream.data == "[100, -999, 0, 0]"); -/** - * Reads an associative array and returns it. - */ -T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && isAssociativeArray!T && !is(T == enum)) -{ - import std.conv : parse, text; - if (spec.spec == '(') - { - return unformatRange!T(input, spec); - } - enforce(spec.spec == 's', - text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + ulong[] arrulong = new ulong[4]; + arrulong[0] = 100; + arrulong[1] = 999; + arrulong[3] = 0; + stream.clear(); formattedWrite(stream, "%s", arrulong); + assert(stream.data == "[100, 999, 0, 0]"); - return parse!T(input); -} + string[] arr2 = new string[4]; + arr2[0] = "hello"; + arr2[1] = "world"; + arr2[3] = "foo"; + stream.clear(); formattedWrite(stream, "%s", arr2); + assert(stream.data == `["hello", "world", "", "foo"]`, stream.data); -pure unittest -{ - string line; + stream.clear(); formattedWrite(stream, "%.8d", 7); + assert(stream.data == "00000007"); - string[int] aa1; - line = `[1:"hello", 2:"world"]`; - formattedRead(line, "%s", &aa1); - assert(aa1 == [1:"hello", 2:"world"]); + stream.clear(); formattedWrite(stream, "%.8x", 10); + assert(stream.data == "0000000a"); - int[string] aa2; - line = `{"hello"=1; "world"=2}`; - formattedRead(line, "{%(%s=%s; %)}", &aa2); - assert(aa2 == ["hello":1, "world":2]); + stream.clear(); formattedWrite(stream, "%-3d", 7); + assert(stream.data == "7 "); - int[string] aa3; - line = `{[hello=1]; [world=2]}`; - formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3); - assert(aa3 == ["hello":1, "world":2]); -} + stream.clear(); formattedWrite(stream, "%*d", -3, 7); + assert(stream.data == "7 "); -//debug = unformatRange; + stream.clear(); formattedWrite(stream, "%.*d", -3, 7); + //writeln(stream.data); + assert(stream.data == "7"); -private T unformatRange(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) -in -{ - assert(spec.spec == '('); -} -body -{ - debug (unformatRange) printf("unformatRange:\n"); + stream.clear(); formattedWrite(stream, "%s", "abc"c); + assert(stream.data == "abc"); + stream.clear(); formattedWrite(stream, "%s", "def"w); + assert(stream.data == "def", text(stream.data.length)); + stream.clear(); formattedWrite(stream, "%s", "ghi"d); + assert(stream.data == "ghi"); - T result; - static if (isStaticArray!T) - { - size_t i; - } +here: + @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; } + stream.clear(); formattedWrite(stream, "%s", deadBeef()); + assert(stream.data == "DEADBEEF", stream.data); - const(Char)[] cont = spec.trailing; - for (size_t j = 0; j < spec.trailing.length; ++j) - { - if (spec.trailing[j] == '%') - { - cont = spec.trailing[0 .. j]; - break; - } - } - debug (unformatRange) printf("\t"); - debug (unformatRange) if (!input.empty) printf("input.front = %c, ", input.front); - debug (unformatRange) printf("cont = %.*s\n", cont); + stream.clear(); formattedWrite(stream, "%#x", 0xabcd); + assert(stream.data == "0xabcd"); + stream.clear(); formattedWrite(stream, "%#X", 0xABCD); + assert(stream.data == "0XABCD"); - bool checkEnd() - { - return input.empty || !cont.empty && input.front == cont.front; - } + stream.clear(); formattedWrite(stream, "%#o", octal!12345); + assert(stream.data == "012345"); + stream.clear(); formattedWrite(stream, "%o", 9); + assert(stream.data == "11"); - if (!checkEnd()) - { - for (;;) - { - auto fmt = FormatSpec!Char(spec.nested); - fmt.readUpToNextSpec(input); - enforce(!input.empty); + stream.clear(); formattedWrite(stream, "%+d", 123); + assert(stream.data == "+123"); + stream.clear(); formattedWrite(stream, "%+d", -123); + assert(stream.data == "-123"); + stream.clear(); formattedWrite(stream, "% d", 123); + assert(stream.data == " 123"); + stream.clear(); formattedWrite(stream, "% d", -123); + assert(stream.data == "-123"); - debug (unformatRange) printf("\t) spec = %c, front = %c ", fmt.spec, input.front); - static if (isStaticArray!T) - { - result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt); - } - else static if (isDynamicArray!T) - { - result ~= unformatElement!(ElementType!T)(input, fmt); - } - else static if (isAssociativeArray!T) - { - auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt); - fmt.readUpToNextSpec(input); // eat key separator + stream.clear(); formattedWrite(stream, "%%"); + assert(stream.data == "%"); - result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt); - } - debug (unformatRange) { - if (input.empty) printf("-> front = [empty] "); - else printf("-> front = %c ", input.front); - } + stream.clear(); formattedWrite(stream, "%d", true); + assert(stream.data == "1"); + stream.clear(); formattedWrite(stream, "%d", false); + assert(stream.data == "0"); - static if (isStaticArray!T) - { - debug (unformatRange) printf("i = %u < %u\n", i, T.length); - enforce(i <= T.length); - } + stream.clear(); formattedWrite(stream, "%d", 'a'); + assert(stream.data == "97", stream.data); + wchar wc = 'a'; + stream.clear(); formattedWrite(stream, "%d", wc); + assert(stream.data == "97"); + dchar dc = 'a'; + stream.clear(); formattedWrite(stream, "%d", dc); + assert(stream.data == "97"); - if (spec.sep != null) - fmt.readUpToNextSpec(input); - auto sep = spec.sep != null ? spec.sep - : fmt.trailing; - debug (unformatRange) { - if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, sep); - else printf("\n"); - } + byte b = byte.max; + stream.clear(); formattedWrite(stream, "%x", b); + assert(stream.data == "7f"); + stream.clear(); formattedWrite(stream, "%x", ++b); + assert(stream.data == "80"); + stream.clear(); formattedWrite(stream, "%x", ++b); + assert(stream.data == "81"); - if (checkEnd()) - break; + short sh = short.max; + stream.clear(); formattedWrite(stream, "%x", sh); + assert(stream.data == "7fff"); + stream.clear(); formattedWrite(stream, "%x", ++sh); + assert(stream.data == "8000"); + stream.clear(); formattedWrite(stream, "%x", ++sh); + assert(stream.data == "8001"); - if (!sep.empty && input.front == sep.front) - { - while (!sep.empty) - { - enforce(!input.empty); - enforce(input.front == sep.front); - input.popFront(); - sep.popFront(); - } - debug (unformatRange) printf("input.front = %c\n", input.front); - } - } - } - static if (isStaticArray!T) + i = int.max; + stream.clear(); formattedWrite(stream, "%x", i); + assert(stream.data == "7fffffff"); + stream.clear(); formattedWrite(stream, "%x", ++i); + assert(stream.data == "80000000"); + stream.clear(); formattedWrite(stream, "%x", ++i); + assert(stream.data == "80000001"); + + stream.clear(); formattedWrite(stream, "%x", 10); + assert(stream.data == "a"); + stream.clear(); formattedWrite(stream, "%X", 10); + assert(stream.data == "A"); + stream.clear(); formattedWrite(stream, "%x", 15); + assert(stream.data == "f"); + stream.clear(); formattedWrite(stream, "%X", 15); + assert(stream.data == "F"); + + @trusted void ObjectTest() { - enforce(i == T.length); + Object c = null; + stream.clear(); formattedWrite(stream, "%s", c); + assert(stream.data == "null"); } - return result; -} + ObjectTest(); -// Undocumented -T unformatElement(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range) -{ - import std.conv : parseElement; - static if (isSomeString!T) + enum TestEnum { - if (spec.spec == 's') - { - return parseElement!T(input); - } + Value1, Value2 } - else static if (isSomeChar!T) + stream.clear(); formattedWrite(stream, "%s", TestEnum.Value2); + assert(stream.data == "Value2", stream.data); + stream.clear(); formattedWrite(stream, "%s", cast(TestEnum) 5); + assert(stream.data == "cast(TestEnum)5", stream.data); + + //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); + //stream.clear(); formattedWrite(stream, "%s", aa.values); + //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr); + //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]"); + //stream.clear(); formattedWrite(stream, "%s", aa); + //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]"); + + static const dchar[] ds = ['a','b']; + for (int j = 0; j < ds.length; ++j) { - if (spec.spec == 's') - { - return parseElement!T(input); - } + stream.clear(); formattedWrite(stream, " %d", ds[j]); + if (j == 0) + assert(stream.data == " 97"); + else + assert(stream.data == " 98"); } - return unformatValue!T(input, spec); + stream.clear(); formattedWrite(stream, "%.-3d", 7); + assert(stream.data == "7", ">" ~ stream.data ~ "<"); } - -// Legacy implementation - -enum Mangle : char +@safe unittest { - Tvoid = 'v', - Tbool = 'b', - Tbyte = 'g', - Tubyte = 'h', - Tshort = 's', - Tushort = 't', - Tint = 'i', - Tuint = 'k', - Tlong = 'l', - Tulong = 'm', - Tfloat = 'f', - Tdouble = 'd', - Treal = 'e', - - Tifloat = 'o', - Tidouble = 'p', - Tireal = 'j', - Tcfloat = 'q', - Tcdouble = 'r', - Tcreal = 'c', + import std.array; + import std.stdio; - Tchar = 'a', - Twchar = 'u', - Tdchar = 'w', + immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]); + assert(aa[3] == "hello"); + assert(aa[4] == "betty"); - Tarray = 'A', - Tsarray = 'G', - Taarray = 'H', - Tpointer = 'P', - Tfunction = 'F', - Tident = 'I', - Tclass = 'C', - Tstruct = 'S', - Tenum = 'E', - Ttypedef = 'T', - Tdelegate = 'D', + auto stream = appender!(char[])(); + alias AllNumerics = + AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, + float, double, real); + foreach (T; AllNumerics) + { + T value = 1; + stream.clear(); + formattedWrite(stream, "%s", value); + assert(stream.data == "1"); + } - Tconst = 'x', - Timmutable = 'y', + stream.clear(); + formattedWrite(stream, "%s", aa); } -// return the TypeInfo for a primitive type and null otherwise. This -// is required since for arrays of ints we only have the mangled char -// to work from. If arrays always subclassed TypeInfo_Array this -// routine could go away. -private TypeInfo primitiveTypeInfo(Mangle m) +@system unittest { - // BUG: should fix this in static this() to avoid double checked locking bug - __gshared TypeInfo[Mangle] dic; - if (!dic.length) { - dic = [ - Mangle.Tvoid : typeid(void), - Mangle.Tbool : typeid(bool), - Mangle.Tbyte : typeid(byte), - Mangle.Tubyte : typeid(ubyte), - Mangle.Tshort : typeid(short), - Mangle.Tushort : typeid(ushort), - Mangle.Tint : typeid(int), - Mangle.Tuint : typeid(uint), - Mangle.Tlong : typeid(long), - Mangle.Tulong : typeid(ulong), - Mangle.Tfloat : typeid(float), - Mangle.Tdouble : typeid(double), - Mangle.Treal : typeid(real), - Mangle.Tifloat : typeid(ifloat), - Mangle.Tidouble : typeid(idouble), - Mangle.Tireal : typeid(ireal), - Mangle.Tcfloat : typeid(cfloat), - Mangle.Tcdouble : typeid(cdouble), - Mangle.Tcreal : typeid(creal), - Mangle.Tchar : typeid(char), - Mangle.Twchar : typeid(wchar), - Mangle.Tdchar : typeid(dchar) - ]; - } - auto p = m in dic; - return p ? *p : null; + string s = "hello!124:34.5"; + string a; + int b; + double c; + formattedRead(s, "%s!%s:%s", &a, &b, &c); + assert(a == "hello" && b == 124 && c == 34.5); } -// This stuff has been removed from the docs and is planned for deprecation. -/* - * Interprets variadic argument list pointed to by argptr whose types - * are given by arguments[], formats them according to embedded format - * strings in the variadic argument list, and sends the resulting - * characters to putc. - * - * The variadic arguments are consumed in order. Each is formatted - * into a sequence of chars, using the default format specification - * for its type, and the characters are sequentially passed to putc. - * If a $(D char[]), $(D wchar[]), or $(D dchar[]) argument is - * encountered, it is interpreted as a format string. As many - * arguments as specified in the format string are consumed and - * formatted according to the format specifications in that string and - * passed to putc. If there are too few remaining arguments, a - * $(D FormatException) is thrown. If there are more remaining arguments than - * needed by the format specification, the default processing of - * arguments resumes until they are all consumed. - * - * Params: - * putc = Output is sent do this delegate, character by character. - * arguments = Array of $(D TypeInfo)s, one for each argument to be formatted. - * argptr = Points to variadic argument list. - * - * Throws: - * Mismatched arguments and formats result in a $(D FormatException) being thrown. - * - * Format_String: - * $(I Format strings) - * consist of characters interspersed with - * $(I format specifications). Characters are simply copied - * to the output (such as putc) after any necessary conversion - * to the corresponding UTF-8 sequence. - * - * A $(I format specification) starts with a '%' character, - * and has the following grammar: +version(unittest) +void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__) +{ + import core.exception : AssertError; + import std.array : appender; + auto w = appender!string(); + formattedWrite(w, fmt, val); + + auto input = w.data; + enforce!AssertError( + input == formatted, + input, fn, ln); + + T val2; + formattedRead(input, fmt, &val2); + static if (isAssociativeArray!T) + if (__ctfe) + { + alias aa1 = val; + alias aa2 = val2; + assert(aa1 == aa2); + + assert(aa1.length == aa2.length); -
-$(I FormatSpecification):
-    $(B '%%')
-    $(B '%') $(I Flags) $(I Width) $(I Precision) $(I FormatChar)
+        assert(aa1.keys == aa2.keys);
 
-$(I Flags):
-    $(I empty)
-    $(B '-') $(I Flags)
-    $(B '+') $(I Flags)
-    $(B '#') $(I Flags)
-    $(B '0') $(I Flags)
-    $(B ' ') $(I Flags)
+        assert(aa1.values == aa2.values);
+        assert(aa1.values.length == aa2.values.length);
+        foreach (i; 0 .. aa1.values.length)
+            assert(aa1.values[i] == aa2.values[i]);
 
-$(I Width):
-    $(I empty)
-    $(I Integer)
-    $(B '*')
+        foreach (i, key; aa1.keys)
+            assert(aa1.values[i] == aa1[key]);
+        foreach (i, key; aa2.keys)
+            assert(aa2.values[i] == aa2[key]);
+        return;
+    }
+    enforce!AssertError(
+            val == val2,
+            input, fn, ln);
+}
 
-$(I Precision):
-    $(I empty)
-    $(B '.')
-    $(B '.') $(I Integer)
-    $(B '.*')
+version(unittest)
+void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__)
+{
+    import core.exception : AssertError;
+    import std.array : appender;
+    auto w = appender!string();
+    formattedWrite(w, fmt, val);
 
-$(I Integer):
-    $(I Digit)
-    $(I Digit) $(I Integer)
+    auto input = w.data;
 
-$(I Digit):
-    $(B '0')
-    $(B '1')
-    $(B '2')
-    $(B '3')
-    $(B '4')
-    $(B '5')
-    $(B '6')
-    $(B '7')
-    $(B '8')
-    $(B '9')
+    foreach (cur; formatted)
+    {
+        if (input == cur) return;
+    }
+    enforce!AssertError(
+            false,
+            input,
+            fn,
+            ln);
 
-$(I FormatChar):
-    $(B 's')
-    $(B 'b')
-    $(B 'd')
-    $(B 'o')
-    $(B 'x')
-    $(B 'X')
-    $(B 'e')
-    $(B 'E')
-    $(B 'f')
-    $(B 'F')
-    $(B 'g')
-    $(B 'G')
-    $(B 'a')
-    $(B 'A')
-
-
-
$(I Flags) -
-
$(B '-') -
- Left justify the result in the field. - It overrides any $(B 0) flag. - -
$(B '+') -
Prefix positive numbers in a signed conversion with a $(B +). - It overrides any $(I space) flag. - -
$(B '#') -
Use alternative formatting: -
-
For $(B 'o'): -
Add to precision as necessary so that the first digit - of the octal formatting is a '0', even if both the argument - and the $(I Precision) are zero. -
For $(B 'x') ($(B 'X')): -
If non-zero, prefix result with $(B 0x) ($(B 0X)). -
For floating point formatting: -
Always insert the decimal point. -
For $(B 'g') ($(B 'G')): -
Do not elide trailing zeros. -
- -
$(B '0') -
For integer and floating point formatting when not nan or - infinity, use leading zeros - to pad rather than spaces. - Ignore if there's a $(I Precision). - -
$(B ' ') -
Prefix positive numbers in a signed conversion with a space. -
- -
$(I Width) -
- Specifies the minimum field width. - If the width is a $(B *), the next argument, which must be - of type $(B int), is taken as the width. - If the width is negative, it is as if the $(B -) was given - as a $(I Flags) character. - -
$(I Precision) -
Gives the precision for numeric conversions. - If the precision is a $(B *), the next argument, which must be - of type $(B int), is taken as the precision. If it is negative, - it is as if there was no $(I Precision). - -
$(I FormatChar) -
-
-
$(B 's') -
The corresponding argument is formatted in a manner consistent - with its type: -
-
$(B bool) -
The result is 'true' or 'false'. -
integral types -
The $(B %d) format is used. -
floating point types -
The $(B %g) format is used. -
string types -
The result is the string converted to UTF-8. - A $(I Precision) specifies the maximum number of characters - to use in the result. -
classes derived from $(B Object) -
The result is the string returned from the class instance's - $(B .toString()) method. - A $(I Precision) specifies the maximum number of characters - to use in the result. -
non-string static and dynamic arrays -
The result is [s0, s1, ...] - where sk is the kth element - formatted with the default format. -
- -
$(B 'b','d','o','x','X') -
The corresponding argument must be an integral type - and is formatted as an integer. If the argument is a signed type - and the $(I FormatChar) is $(B d) it is converted to - a signed string of characters, otherwise it is treated as - unsigned. An argument of type $(B bool) is formatted as '1' - or '0'. The base used is binary for $(B b), octal for $(B o), - decimal - for $(B d), and hexadecimal for $(B x) or $(B X). - $(B x) formats using lower case letters, $(B X) uppercase. - If there are fewer resulting digits than the $(I Precision), - leading zeros are used as necessary. - If the $(I Precision) is 0 and the number is 0, no digits - result. - -
$(B 'e','E') -
A floating point number is formatted as one digit before - the decimal point, $(I Precision) digits after, the $(I FormatChar), - ±, followed by at least a two digit exponent: $(I d.dddddd)e$(I ±dd). - If there is no $(I Precision), six - digits are generated after the decimal point. - If the $(I Precision) is 0, no decimal point is generated. - -
$(B 'f','F') -
A floating point number is formatted in decimal notation. - The $(I Precision) specifies the number of digits generated - after the decimal point. It defaults to six. At least one digit - is generated before the decimal point. If the $(I Precision) - is zero, no decimal point is generated. - -
$(B 'g','G') -
A floating point number is formatted in either $(B e) or - $(B f) format for $(B g); $(B E) or $(B F) format for - $(B G). - The $(B f) format is used if the exponent for an $(B e) format - is greater than -5 and less than the $(I Precision). - The $(I Precision) specifies the number of significant - digits, and defaults to six. - Trailing zeros are elided after the decimal point, if the fractional - part is zero then no decimal point is generated. - -
$(B 'a','A') -
A floating point number is formatted in hexadecimal - exponential notation 0x$(I h.hhhhhh)p$(I ±d). - There is one hexadecimal digit before the decimal point, and as - many after as specified by the $(I Precision). - If the $(I Precision) is zero, no decimal point is generated. - If there is no $(I Precision), as many hexadecimal digits as - necessary to exactly represent the mantissa are generated. - The exponent is written in as few digits as possible, - but at least one, is in decimal, and represents a power of 2 as in - $(I h.hhhhhh)*2$(I ±d). - The exponent for zero is zero. - The hexadecimal digits, x and p are in upper case if the - $(I FormatChar) is upper case. -
+ T val2; + formattedRead(input, fmt, &val2); + static if (isAssociativeArray!T) + if (__ctfe) + { + alias aa1 = val; + alias aa2 = val2; + assert(aa1 == aa2); - Floating point NaN's are formatted as $(B nan) if the - $(I FormatChar) is lower case, or $(B NAN) if upper. - Floating point infinities are formatted as $(B inf) or - $(B infinity) if the - $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper. -
+ assert(aa1.length == aa2.length); -Example: + assert(aa1.keys == aa2.keys); -------------------------- -import core.stdc.stdio; -import std.format; + assert(aa1.values == aa2.values); + assert(aa1.values.length == aa2.values.length); + foreach (i; 0 .. aa1.values.length) + assert(aa1.values[i] == aa2.values[i]); -void myPrint(...) -{ - void putc(dchar c) - { - fputc(c, stdout); + foreach (i, key; aa1.keys) + assert(aa1.values[i] == aa1[key]); + foreach (i, key; aa2.keys) + assert(aa2.values[i] == aa2[key]); + return; } - - std.format.doFormat(&putc, _arguments, _argptr); + enforce!AssertError( + val == val2, + input, fn, ln); } -void main() +@system unittest { - int x = 27; + void booleanTest() + { + auto b = true; + formatReflectTest(b, "%s", `true`); + formatReflectTest(b, "%b", `1`); + formatReflectTest(b, "%o", `1`); + formatReflectTest(b, "%d", `1`); + formatReflectTest(b, "%u", `1`); + formatReflectTest(b, "%x", `1`); + } - // prints 'The answer is 27:6' - myPrint("The answer is %s:", x, 6); -} ------------------------- - */ -void doFormat()(scope void delegate(dchar) putc, TypeInfo[] arguments, va_list ap) -{ - import std.utf : toUCSindex, isValidDchar, UTFException, toUTF8; - import core.stdc.string : strlen; - import core.stdc.stdlib : alloca, malloc, realloc, free; - import core.stdc.stdio : snprintf; - - size_t bufLength = 1024; - void* argBuffer = malloc(bufLength); - scope(exit) free(argBuffer); - - size_t bufUsed = 0; - foreach(ti; arguments) - { - // Ensure the required alignment - bufUsed += ti.talign - 1; - bufUsed -= (cast(size_t)argBuffer + bufUsed) & (ti.talign - 1); - auto pos = bufUsed; - // Align to next word boundary - bufUsed += ti.tsize + size_t.sizeof - 1; - bufUsed -= (cast(size_t)argBuffer + bufUsed) & (size_t.sizeof - 1); - // Resize buffer if necessary - while (bufUsed > bufLength) - { - bufLength *= 2; - argBuffer = realloc(argBuffer, bufLength); - } - // Copy argument into buffer - va_arg(ap, ti, argBuffer + pos); + void integerTest() + { + auto n = 127; + formatReflectTest(n, "%s", `127`); + formatReflectTest(n, "%b", `1111111`); + formatReflectTest(n, "%o", `177`); + formatReflectTest(n, "%d", `127`); + formatReflectTest(n, "%u", `127`); + formatReflectTest(n, "%x", `7f`); } - auto argptr = argBuffer; - void* skipArg(TypeInfo ti) + void floatingTest() { - // Ensure the required alignment - argptr += ti.talign - 1; - argptr -= cast(size_t)argptr & (ti.talign - 1); - auto p = argptr; - // Align to next word boundary - argptr += ti.tsize + size_t.sizeof - 1; - argptr -= cast(size_t)argptr & (size_t.sizeof - 1); - return p; + auto f = 3.14; + formatReflectTest(f, "%s", `3.14`); + version (MinGW) + formatReflectTest(f, "%e", `3.140000e+000`); + else + formatReflectTest(f, "%e", `3.140000e+00`); + formatReflectTest(f, "%f", `3.140000`); + formatReflectTest(f, "%g", `3.14`); } - auto getArg(T)() + + void charTest() { - return *cast(T*)skipArg(typeid(T)); + auto c = 'a'; + formatReflectTest(c, "%s", `a`); + formatReflectTest(c, "%c", `a`); + formatReflectTest(c, "%b", `1100001`); + formatReflectTest(c, "%o", `141`); + formatReflectTest(c, "%d", `97`); + formatReflectTest(c, "%u", `97`); + formatReflectTest(c, "%x", `61`); } - TypeInfo ti; - Mangle m; - uint flags; - int field_width; - int precision; + void strTest() + { + auto s = "hello"; + formatReflectTest(s, "%s", `hello`); + formatReflectTest(s, "%(%c,%)", `h,e,l,l,o`); + formatReflectTest(s, "%(%s,%)", `'h','e','l','l','o'`); + formatReflectTest(s, "[%(<%c>%| $ %)]", `[ $ $ $ $ ]`); + } - enum : uint + void daTest() { - FLdash = 1, - FLplus = 2, - FLspace = 4, - FLhash = 8, - FLlngdbl = 0x20, - FL0pad = 0x40, - FLprecision = 0x80, + auto a = [1,2,3,4]; + formatReflectTest(a, "%s", `[1, 2, 3, 4]`); + formatReflectTest(a, "[%(%s; %)]", `[1; 2; 3; 4]`); + formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`); } - static TypeInfo skipCI(TypeInfo valti) + void saTest() { - for (;;) - { - if (typeid(valti).name.length == 18 && - typeid(valti).name[9..18] == "Invariant") - valti = (cast(TypeInfo_Invariant)valti).next; - else if (typeid(valti).name.length == 14 && - typeid(valti).name[9..14] == "Const") - valti = (cast(TypeInfo_Const)valti).next; - else - break; - } + int[4] sa = [1,2,3,4]; + formatReflectTest(sa, "%s", `[1, 2, 3, 4]`); + formatReflectTest(sa, "[%(%s; %)]", `[1; 2; 3; 4]`); + formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`); + } - return valti; + void aaTest() + { + auto aa = [1:"hello", 2:"world"]; + formatReflectTest(aa, "%s", [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]); + formatReflectTest(aa, "[%(%s->%s, %)]", [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]); + formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]); } - void formatArg(char fc) + import std.exception; + assertCTFEable!( { - bool vbit; - ulong vnumber; - char vchar; - dchar vdchar; - Object vobject; - real vreal; - creal vcreal; - Mangle m2; - int signed = 0; - uint base = 10; - int uc; - char[ulong.sizeof * 8] tmpbuf; // long enough to print long in binary - const(char)* prefix = ""; - string s; + booleanTest(); + integerTest(); + if (!__ctfe) floatingTest(); // snprintf + charTest(); + strTest(); + daTest(); + saTest(); + aaTest(); + return true; + }); +} - void putstr(const char[] s) - { - //printf("putstr: s = %.*s, flags = x%x\n", s.length, s.ptr, flags); - ptrdiff_t padding = field_width - - (strlen(prefix) + toUCSindex(s, s.length)); - ptrdiff_t prepad = 0; - ptrdiff_t postpad = 0; - if (padding > 0) - { - if (flags & FLdash) - postpad = padding; - else - prepad = padding; - } +//------------------------------------------------------------------------------ +private void skipData(Range, Char)(ref Range input, const ref FormatSpec!Char spec) +{ + import std.ascii : isDigit; + import std.conv : text; - if (flags & FL0pad) - { - while (*prefix) - putc(*prefix++); - while (prepad--) - putc('0'); - } - else - { - while (prepad--) - putc(' '); - while (*prefix) - putc(*prefix++); - } + switch (spec.spec) + { + case 'c': input.popFront(); break; + case 'd': + if (input.front == '+' || input.front == '-') input.popFront(); + goto case 'u'; + case 'u': + while (!input.empty && isDigit(input.front)) input.popFront(); + break; + default: + assert(false, + text("Format specifier not understood: %", spec.spec)); + } +} - foreach (dchar c; s) - putc(c); +private template acceptedSpecs(T) +{ + static if (isIntegral!T) enum acceptedSpecs = "bdosuxX"; + else static if (isFloatingPoint!T) enum acceptedSpecs = "seEfgG"; + else static if (isSomeChar!T) enum acceptedSpecs = "bcdosuxX"; // integral + 'c' + else enum acceptedSpecs = ""; +} - while (postpad--) - putc(' '); - } +/** + * Reads a value from the given _input range according to spec + * and returns it as type `T`. + * + * Params: + * T = the type to return + * input = the _input range to read from + * spec = the `FormatSpec` to use when reading from `input` + * Returns: + * A value from `input` of type `T` + * Throws: + * An `Exception` if `spec` cannot read a type `T` + * See_Also: + * $(REF parse, std, conv) and $(REF to, std, conv) + */ +T unformatValue(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +{ + return unformatValueImpl!T(input, spec); +} - void putreal(real v) - { - //printf("putreal %Lg\n", vreal); +/// Booleans +@safe pure unittest +{ + auto str = "false"; + auto spec = singleSpec("%s"); + assert(unformatValue!bool(str, spec) == false); - switch (fc) - { - case 's': - fc = 'g'; - break; + str = "1"; + spec = singleSpec("%d"); + assert(unformatValue!bool(str, spec)); +} - case 'f', 'F', 'e', 'E', 'g', 'G', 'a', 'A': - break; +/// Null values +@safe pure unittest +{ + auto str = "null"; + auto spec = singleSpec("%s"); + assert(str.unformatValue!(typeof(null))(spec) == null); +} - default: - //printf("fc = '%c'\n", fc); - Lerror: - throw new FormatException("floating"); - } - version (DigitalMarsC) - { - uint sl; - char[] fbuf = tmpbuf; - if (!(flags & FLprecision)) - precision = 6; - while (1) - { - sl = fbuf.length; - prefix = (*__pfloatfmt)(fc, flags | FLlngdbl, - precision, &v, cast(char*)fbuf, &sl, field_width); - if (sl != -1) - break; - sl = fbuf.length * 2; - fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; - } - putstr(fbuf[0 .. sl]); - } - else - { - ptrdiff_t sl; - char[] fbuf = tmpbuf; - char[12] format; - format[0] = '%'; - int i = 1; - if (flags & FLdash) - format[i++] = '-'; - if (flags & FLplus) - format[i++] = '+'; - if (flags & FLspace) - format[i++] = ' '; - if (flags & FLhash) - format[i++] = '#'; - if (flags & FL0pad) - format[i++] = '0'; - format[i + 0] = '*'; - format[i + 1] = '.'; - format[i + 2] = '*'; - format[i + 3] = 'L'; - format[i + 4] = fc; - format[i + 5] = 0; - if (!(flags & FLprecision)) - precision = -1; - while (1) - { - sl = fbuf.length; - int n; - version (CRuntime_Microsoft) - { - import std.math : isNaN, isInfinity; - if (isNaN(v)) // snprintf writes 1.#QNAN - n = snprintf(fbuf.ptr, sl, "nan"); - else if(isInfinity(v)) // snprintf writes 1.#INF - n = snprintf(fbuf.ptr, sl, v < 0 ? "-inf" : "inf"); - else - n = snprintf(fbuf.ptr, sl, format.ptr, field_width, - precision, cast(double)v); - } - else - n = snprintf(fbuf.ptr, sl, format.ptr, field_width, - precision, v); - //printf("format = '%s', n = %d\n", cast(char*)format, n); - if (n >= 0 && n < sl) - { sl = n; - break; - } - if (n < 0) - sl = sl * 2; - else - sl = n + 1; - fbuf = (cast(char*)alloca(sl * char.sizeof))[0 .. sl]; - } - putstr(fbuf[0 .. sl]); - } - return; - } +/// Integrals +@safe pure unittest +{ + auto str = "123"; + auto spec = singleSpec("%s"); + assert(str.unformatValue!int(spec) == 123); + + str = "ABC"; + spec = singleSpec("%X"); + assert(str.unformatValue!int(spec) == 2748); + + str = "11610"; + spec = singleSpec("%o"); + assert(str.unformatValue!int(spec) == 5000); +} + +/// Floating point numbers +@safe pure unittest +{ + import std.math : approxEqual; + + auto str = "123.456"; + auto spec = singleSpec("%s"); + assert(str.unformatValue!double(spec).approxEqual(123.456)); +} - static Mangle getMan(TypeInfo ti) - { - auto m = cast(Mangle)typeid(ti).name[9]; - if (typeid(ti).name.length == 20 && - typeid(ti).name[9..20] == "StaticArray") - m = cast(Mangle)'G'; - return m; - } +/// Character input ranges +@safe pure unittest +{ + auto str = "aaa"; + auto spec = singleSpec("%s"); + assert(str.unformatValue!char(spec) == 'a'); - /* p = pointer to the first element in the array - * len = number of elements in the array - * valti = type of the elements - */ - void putArray(void* p, size_t len, TypeInfo valti) - { - //printf("\nputArray(len = %u), tsize = %u\n", len, valti.tsize); - putc('['); - valti = skipCI(valti); - size_t tsize = valti.tsize; - auto argptrSave = argptr; - auto tiSave = ti; - auto mSave = m; - ti = valti; - //printf("\n%.*s\n", typeid(valti).name.length, typeid(valti).name.ptr); - m = getMan(valti); - while (len--) - { - //doFormat(putc, (&valti)[0 .. 1], p); - argptr = p; - formatArg('s'); - p += tsize; - if (len > 0) putc(','); - } - m = mSave; - ti = tiSave; - argptr = argptrSave; - putc(']'); - } + // Using a numerical format spec reads a Unicode value from a string + str = "65"; + spec = singleSpec("%d"); + assert(str.unformatValue!char(spec) == 'A'); - void putAArray(ubyte[long] vaa, TypeInfo valti, TypeInfo keyti) - { - putc('['); - bool comma=false; - auto argptrSave = argptr; - auto tiSave = ti; - auto mSave = m; - valti = skipCI(valti); - keyti = skipCI(keyti); - foreach(ref fakevalue; vaa) - { - if (comma) putc(','); - comma = true; - void *pkey = &fakevalue; - version (D_LP64) - pkey -= (long.sizeof + 15) & ~(15); - else - pkey -= (long.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1); + str = "41"; + spec = singleSpec("%x"); + assert(str.unformatValue!char(spec) == 'A'); - // the key comes before the value - auto keysize = keyti.tsize; - version (D_LP64) - auto keysizet = (keysize + 15) & ~(15); - else - auto keysizet = (keysize + size_t.sizeof - 1) & ~(size_t.sizeof - 1); + str = "10003"; + spec = singleSpec("%d"); + assert(str.unformatValue!dchar(spec) == '✓'); +} - void* pvalue = pkey + keysizet; +/// Arrays and static arrays +@safe pure unittest +{ + string str = "aaa"; + auto spec = singleSpec("%s"); + assert(str.unformatValue!(dchar[])(spec) == "aaa"d); - //doFormat(putc, (&keyti)[0..1], pkey); - m = getMan(keyti); - argptr = pkey; + str = "aaa"; + spec = singleSpec("%s"); + dchar[3] ret = ['a', 'a', 'a']; + assert(str.unformatValue!(dchar[3])(spec) == ret); - ti = keyti; - formatArg('s'); + str = "[1, 2, 3, 4]"; + spec = singleSpec("%s"); + assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]); - putc(':'); - //doFormat(putc, (&valti)[0..1], pvalue); - m = getMan(valti); - argptr = pvalue; + str = "[1, 2, 3, 4]"; + spec = singleSpec("%s"); + int[4] ret2 = [1, 2, 3, 4]; + assert(str.unformatValue!(int[4])(spec) == ret2); +} - ti = valti; - formatArg('s'); - } - m = mSave; - ti = tiSave; - argptr = argptrSave; - putc(']'); - } +/// Associative arrays +@safe pure unittest +{ + auto str = `["one": 1, "two": 2]`; + auto spec = singleSpec("%s"); + assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]); +} - //printf("formatArg(fc = '%c', m = '%c')\n", fc, m); - int mi; - switch (m) - { - case Mangle.Tbool: - vbit = getArg!(bool)(); - if (fc != 's') - { vnumber = vbit; - goto Lnumber; - } - putstr(vbit ? "true" : "false"); - return; +@safe pure unittest +{ + // 7241 + string input = "a"; + auto spec = FormatSpec!char("%s"); + spec.readUpToNextSpec(input); + auto result = unformatValue!(dchar[1])(input, spec); + assert(result[0] == 'a'); +} - case Mangle.Tchar: - vchar = getArg!(char)(); - if (fc != 's') - { vnumber = vchar; - goto Lnumber; - } - L2: - putstr((&vchar)[0 .. 1]); - return; +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && is(Unqual!T == bool)) +{ + import std.algorithm.searching : find; + import std.conv : parse, text; - case Mangle.Twchar: - vdchar = getArg!(wchar)(); - goto L1; + if (spec.spec == 's') return parse!T(input); - case Mangle.Tdchar: - vdchar = getArg!(dchar)(); - L1: - if (fc != 's') - { vnumber = vdchar; - goto Lnumber; - } - if (vdchar <= 0x7F) - { vchar = cast(char)vdchar; - goto L2; - } - else - { if (!isValidDchar(vdchar)) - throw new UTFException("invalid dchar in format"); - char[4] vbuf; - putstr(toUTF8(vbuf, vdchar)); - } - return; + enforce(find(acceptedSpecs!long, spec.spec).length, + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - case Mangle.Tbyte: - signed = 1; - vnumber = getArg!(byte)(); - goto Lnumber; - - case Mangle.Tubyte: - vnumber = getArg!(ubyte)(); - goto Lnumber; - - case Mangle.Tshort: - signed = 1; - vnumber = getArg!(short)(); - goto Lnumber; - - case Mangle.Tushort: - vnumber = getArg!(ushort)(); - goto Lnumber; - - case Mangle.Tint: - signed = 1; - vnumber = getArg!(int)(); - goto Lnumber; - - case Mangle.Tuint: - Luint: - vnumber = getArg!(uint)(); - goto Lnumber; - - case Mangle.Tlong: - signed = 1; - vnumber = cast(ulong)getArg!(long)(); - goto Lnumber; - - case Mangle.Tulong: - Lulong: - vnumber = getArg!(ulong)(); - goto Lnumber; - - case Mangle.Tclass: - vobject = getArg!(Object)(); - if (vobject is null) - s = "null"; - else - s = vobject.toString(); - goto Lputstr; - - case Mangle.Tpointer: - vnumber = cast(ulong)getArg!(void*)(); - if (fc != 'x') uc = 1; - flags |= FL0pad; - if (!(flags & FLprecision)) - { flags |= FLprecision; - precision = (void*).sizeof; - } - base = 16; - goto Lnumber; - - case Mangle.Tfloat: - case Mangle.Tifloat: - if (fc == 'x' || fc == 'X') - goto Luint; - vreal = getArg!(float)(); - goto Lreal; - - case Mangle.Tdouble: - case Mangle.Tidouble: - if (fc == 'x' || fc == 'X') - goto Lulong; - vreal = getArg!(double)(); - goto Lreal; - - case Mangle.Treal: - case Mangle.Tireal: - vreal = getArg!(real)(); - goto Lreal; - - case Mangle.Tcfloat: - vcreal = getArg!(cfloat)(); - goto Lcomplex; - - case Mangle.Tcdouble: - vcreal = getArg!(cdouble)(); - goto Lcomplex; - - case Mangle.Tcreal: - vcreal = getArg!(creal)(); - goto Lcomplex; - - case Mangle.Tsarray: - putArray(argptr, (cast(TypeInfo_StaticArray)ti).len, (cast(TypeInfo_StaticArray)ti).next); - return; + return unformatValue!long(input, spec) != 0; +} - case Mangle.Tarray: - mi = 10; - if (typeid(ti).name.length == 14 && - typeid(ti).name[9..14] == "Array") - { // array of non-primitive types - TypeInfo tn = (cast(TypeInfo_Array)ti).next; - tn = skipCI(tn); - switch (cast(Mangle)typeid(tn).name[9]) - { - case Mangle.Tchar: goto LarrayChar; - case Mangle.Twchar: goto LarrayWchar; - case Mangle.Tdchar: goto LarrayDchar; - default: - break; - } - void[] va = getArg!(void[])(); - putArray(va.ptr, va.length, tn); - return; - } - if (typeid(ti).name.length == 25 && - typeid(ti).name[9..25] == "AssociativeArray") - { // associative array - ubyte[long] vaa = getArg!(ubyte[long])(); - putAArray(vaa, - (cast(TypeInfo_AssociativeArray)ti).next, - (cast(TypeInfo_AssociativeArray)ti).key); - return; - } +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && is(T == typeof(null))) +{ + import std.conv : parse, text; + enforce(spec.spec == 's', + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - while (1) - { - m2 = cast(Mangle)typeid(ti).name[mi]; - switch (m2) - { - case Mangle.Tchar: - LarrayChar: - s = getArg!(string)(); - goto Lputstr; - - case Mangle.Twchar: - LarrayWchar: - wchar[] sw = getArg!(wchar[])(); - s = toUTF8(sw); - goto Lputstr; - - case Mangle.Tdchar: - LarrayDchar: - s = toUTF8(getArg!(dstring)()); - Lputstr: - if (fc != 's') - throw new FormatException("string"); - if (flags & FLprecision && precision < s.length) - s = s[0 .. precision]; - putstr(s); - break; + return parse!T(input); +} - case Mangle.Tconst: - case Mangle.Timmutable: - mi++; - continue; +/// ditto +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range)) +{ - default: - TypeInfo ti2 = primitiveTypeInfo(m2); - if (!ti2) - goto Lerror; - void[] va = getArg!(void[])(); - putArray(va.ptr, va.length, ti2); - } - return; - } - assert(0); + import std.algorithm.searching : find; + import std.conv : parse, text; - case Mangle.Ttypedef: - ti = (cast(TypeInfo_Typedef)ti).base; - m = cast(Mangle)typeid(ti).name[9]; - formatArg(fc); - return; + if (spec.spec == 'r') + { + static if (is(Unqual!(ElementEncodingType!Range) == char) + || is(Unqual!(ElementEncodingType!Range) == byte) + || is(Unqual!(ElementEncodingType!Range) == ubyte)) + return rawRead!T(input); + else + throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes."); + } - case Mangle.Tenum: - ti = (cast(TypeInfo_Enum)ti).base; - m = cast(Mangle)typeid(ti).name[9]; - formatArg(fc); - return; + enforce(find(acceptedSpecs!T, spec.spec).length, + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - case Mangle.Tstruct: - { TypeInfo_Struct tis = cast(TypeInfo_Struct)ti; - if (tis.xtoString is null) - throw new FormatException("Can't convert " ~ tis.toString() - ~ " to string: \"string toString()\" not defined"); - s = tis.xtoString(skipArg(tis)); - goto Lputstr; - } + enforce(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO - default: - goto Lerror; - } + immutable uint base = + spec.spec == 'x' || spec.spec == 'X' ? 16 : + spec.spec == 'o' ? 8 : + spec.spec == 'b' ? 2 : + spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0; + assert(base != 0); - Lnumber: - switch (fc) - { - case 's': - case 'd': - if (signed) - { if (cast(long)vnumber < 0) - { prefix = "-"; - vnumber = -vnumber; - } - else if (flags & FLplus) - prefix = "+"; - else if (flags & FLspace) - prefix = " "; - } - break; + return parse!T(input, base); - case 'b': - signed = 0; - base = 2; - break; +} - case 'o': - signed = 0; - base = 8; - break; +/// ditto +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range + && isSomeChar!(ElementType!Range)&& !is(Range == enum)) +{ + import std.algorithm.searching : find; + import std.conv : parse, text; - case 'X': - uc = 1; - if (flags & FLhash && vnumber) - prefix = "0X"; - signed = 0; - base = 16; - break; + if (spec.spec == 'r') + { + static if (is(Unqual!(ElementEncodingType!Range) == char) + || is(Unqual!(ElementEncodingType!Range) == byte) + || is(Unqual!(ElementEncodingType!Range) == ubyte)) + return rawRead!T(input); + else + throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes."); + } - case 'x': - if (flags & FLhash && vnumber) - prefix = "0x"; - signed = 0; - base = 16; - break; + enforce(find(acceptedSpecs!T, spec.spec).length, + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - default: - goto Lerror; - } + return parse!T(input); +} - if (!signed) - { - switch (m) - { - case Mangle.Tbyte: - vnumber &= 0xFF; - break; +/// ditto +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range)) +{ + import std.algorithm.searching : find; + import std.conv : to, text; + if (spec.spec == 's' || spec.spec == 'c') + { + auto result = to!T(input.front); + input.popFront(); + return result; + } + enforce(find(acceptedSpecs!T, spec.spec).length, + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - case Mangle.Tshort: - vnumber &= 0xFFFF; - break; + static if (T.sizeof == 1) + return unformatValue!ubyte(input, spec); + else static if (T.sizeof == 2) + return unformatValue!ushort(input, spec); + else static if (T.sizeof == 4) + return unformatValue!uint(input, spec); + else + static assert(0); +} - case Mangle.Tint: - vnumber &= 0xFFFFFFFF; - break; +/// ditto +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) +{ + import std.conv : text; + + if (spec.spec == '(') + { + return unformatRange!T(input, spec); + } + enforce(spec.spec == 's', + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - default: + static if (isStaticArray!T) + { + T result; + auto app = result[]; + } + else + { + import std.array : appender; + auto app = appender!T(); + } + if (spec.trailing.empty) + { + for (; !input.empty; input.popFront()) + { + static if (isStaticArray!T) + if (app.empty) break; - } + app.put(input.front); + } + } + else + { + immutable end = spec.trailing.front; + for (; !input.empty && input.front != end; input.popFront()) + { + static if (isStaticArray!T) + if (app.empty) + break; + app.put(input.front); } + } + static if (isStaticArray!T) + { + enforce(app.empty, "need more input"); + return result; + } + else + return app.data; +} + +/// ditto +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && isArray!T && !is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) +{ + import std.conv : parse, text; + if (spec.spec == '(') + { + return unformatRange!T(input, spec); + } + enforce(spec.spec == 's', + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); + + return parse!T(input); +} + +/// ditto +private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range && isAssociativeArray!T && !is(T == enum)) +{ + import std.conv : parse, text; + if (spec.spec == '(') + { + return unformatRange!T(input, spec); + } + enforce(spec.spec == 's', + text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof)); - if (flags & FLprecision && fc != 'p') - flags &= ~FL0pad; + return parse!T(input); +} - if (vnumber < base) +/** + * Function that performs raw reading. Used by unformatValue + * for integral and float types. + */ +private T rawRead(T, Range)(ref Range input) +if (is(Unqual!(ElementEncodingType!Range) == char) + || is(Unqual!(ElementEncodingType!Range) == byte) + || is(Unqual!(ElementEncodingType!Range) == ubyte)) +{ + union X + { + ubyte[T.sizeof] raw; + T typed; + } + X x; + foreach (i; 0 .. T.sizeof) + { + static if (isSomeString!Range) { - if (vnumber == 0 && precision == 0 && flags & FLprecision && - !(fc == 'o' && flags & FLhash)) - { - putstr(null); - return; - } - if (precision == 0 || !(flags & FLprecision)) - { vchar = cast(char)('0' + vnumber); - if (vnumber < 10) - vchar = cast(char)('0' + vnumber); - else - vchar = cast(char)((uc ? 'A' - 10 : 'a' - 10) + vnumber); - goto L2; - } + x.raw[i] = input[0]; + input = input[1 .. $]; } - + else { - ptrdiff_t n = tmpbuf.length; - char c; - int hexoffset = uc ? ('A' - ('9' + 1)) : ('a' - ('9' + 1)); - - while (vnumber) - { - c = cast(char)((vnumber % base) + '0'); - if (c > '9') - c += hexoffset; - vnumber /= base; - tmpbuf[--n] = c; - } - if (tmpbuf.length - n < precision && precision < tmpbuf.length) - { - ptrdiff_t m = tmpbuf.length - precision; - tmpbuf[m .. n] = '0'; - n = m; - } - else if (flags & FLhash && fc == 'o') - prefix = "0"; - putstr(tmpbuf[n .. tmpbuf.length]); - return; + // TODO: recheck this + x.raw[i] = input.front; + input.popFront(); } + } + return x.typed; +} - Lreal: - putreal(vreal); - return; +//debug = unformatRange; + +private T unformatRange(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +in +{ + assert(spec.spec == '('); +} +body +{ + debug (unformatRange) printf("unformatRange:\n"); - Lcomplex: - putreal(vcreal.re); - if (vcreal.im >= 0) + T result; + static if (isStaticArray!T) + { + size_t i; + } + + const(Char)[] cont = spec.trailing; + for (size_t j = 0; j < spec.trailing.length; ++j) + { + if (spec.trailing[j] == '%') { - putc('+'); + cont = spec.trailing[0 .. j]; + break; } - putreal(vcreal.im); - putc('i'); - return; + } + debug (unformatRange) printf("\t"); + debug (unformatRange) if (!input.empty) printf("input.front = %c, ", input.front); + debug (unformatRange) printf("cont = %.*s\n", cont); - Lerror: - throw new FormatException("formatArg"); + bool checkEnd() + { + return input.empty || !cont.empty && input.front == cont.front; } - for (int j = 0; j < arguments.length; ) + if (!checkEnd()) { - ti = arguments[j++]; - //printf("arg[%d]: '%.*s' %d\n", j, typeid(ti).name.length, typeid(ti).name.ptr, typeid(ti).name.length); - //ti.print(); - - flags = 0; - precision = 0; - field_width = 0; - - ti = skipCI(ti); - int mi = 9; - do + for (;;) { - if (typeid(ti).name.length <= mi) - goto Lerror; - m = cast(Mangle)typeid(ti).name[mi++]; - } while (m == Mangle.Tconst || m == Mangle.Timmutable); + auto fmt = FormatSpec!Char(spec.nested); + fmt.readUpToNextSpec(input); + enforce(!input.empty, "Unexpected end of input when parsing range"); - if (m == Mangle.Tarray) - { - if (typeid(ti).name.length == 14 && - typeid(ti).name[9..14] == "Array") + debug (unformatRange) printf("\t) spec = %c, front = %c ", fmt.spec, input.front); + static if (isStaticArray!T) { - TypeInfo tn = (cast(TypeInfo_Array)ti).next; - tn = skipCI(tn); - switch (cast(Mangle)typeid(tn).name[9]) - { - case Mangle.Tchar: - case Mangle.Twchar: - case Mangle.Tdchar: - ti = tn; - mi = 9; - break; - default: - break; - } + result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt); } - L1: - Mangle m2 = cast(Mangle)typeid(ti).name[mi]; - string fmt; // format string - wstring wfmt; - dstring dfmt; - - /* For performance reasons, this code takes advantage of the - * fact that most format strings will be ASCII, and that the - * format specifiers are always ASCII. This means we only need - * to deal with UTF in a couple of isolated spots. - */ - - switch (m2) + else static if (isDynamicArray!T) { - case Mangle.Tchar: - fmt = getArg!(string)(); - break; - - case Mangle.Twchar: - wfmt = getArg!(wstring)(); - fmt = toUTF8(wfmt); - break; - - case Mangle.Tdchar: - dfmt = getArg!(dstring)(); - fmt = toUTF8(dfmt); - break; - - case Mangle.Tconst: - case Mangle.Timmutable: - mi++; - goto L1; + result ~= unformatElement!(ElementType!T)(input, fmt); + } + else static if (isAssociativeArray!T) + { + auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt); + fmt.readUpToNextSpec(input); // eat key separator - default: - formatArg('s'); - continue; + result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt); + } + debug (unformatRange) { + if (input.empty) printf("-> front = [empty] "); + else printf("-> front = %c ", input.front); } - for (size_t i = 0; i < fmt.length; ) - { dchar c = fmt[i++]; + static if (isStaticArray!T) + { + debug (unformatRange) printf("i = %u < %u\n", i, T.length); + enforce(i <= T.length, "Too many format specifiers for static array of length %d".format(T.length)); + } - dchar getFmtChar() - { // Valid format specifier characters will never be UTF - if (i == fmt.length) - throw new FormatException("invalid specifier"); - return fmt[i++]; - } + if (spec.sep !is null) + fmt.readUpToNextSpec(input); + auto sep = spec.sep !is null ? spec.sep + : fmt.trailing; + debug (unformatRange) { + if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, sep); + else printf("\n"); + } - int getFmtInt() - { int n; + if (checkEnd()) + break; - while (1) - { - n = n * 10 + (c - '0'); - if (n < 0) // overflow - throw new FormatException("int overflow"); - c = getFmtChar(); - if (c < '0' || c > '9') - break; - } - return n; + if (!sep.empty && input.front == sep.front) + { + while (!sep.empty) + { + enforce(!input.empty, "Unexpected end of input when parsing range separator"); + enforce(input.front == sep.front, "Unexpected character when parsing range separator"); + input.popFront(); + sep.popFront(); } + debug (unformatRange) printf("input.front = %c\n", input.front); + } + } + } + static if (isStaticArray!T) + { + enforce(i == T.length, "Too few (%d) format specifiers for static array of length %d".format(i, T.length)); + } + return result; +} - int getFmtStar() - { Mangle m; - TypeInfo ti; - - if (j == arguments.length) - throw new FormatException("too few arguments"); - ti = arguments[j++]; - m = cast(Mangle)typeid(ti).name[9]; - if (m != Mangle.Tint) - throw new FormatException("int argument expected"); - return getArg!(int)(); - } +// Undocumented +T unformatElement(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec) +if (isInputRange!Range) +{ + import std.conv : parseElement; + static if (isSomeString!T) + { + if (spec.spec == 's') + { + return parseElement!T(input); + } + } + else static if (isSomeChar!T) + { + if (spec.spec == 's') + { + return parseElement!T(input); + } + } - if (c != '%') - { - if (c > 0x7F) // if UTF sequence - { - i--; // back up and decode UTF sequence - c = std.utf.decode(fmt, i); - } - Lputc: - putc(c); - continue; - } + return unformatValue!T(input, spec); +} - // Get flags {-+ #} - flags = 0; - while (1) - { - c = getFmtChar(); - switch (c) - { - case '-': flags |= FLdash; continue; - case '+': flags |= FLplus; continue; - case ' ': flags |= FLspace; continue; - case '#': flags |= FLhash; continue; - case '0': flags |= FL0pad; continue; - case '%': if (flags == 0) - goto Lputc; - break; +// Legacy implementation - default: break; - } - break; - } +enum Mangle : char +{ + Tvoid = 'v', + Tbool = 'b', + Tbyte = 'g', + Tubyte = 'h', + Tshort = 's', + Tushort = 't', + Tint = 'i', + Tuint = 'k', + Tlong = 'l', + Tulong = 'm', + Tfloat = 'f', + Tdouble = 'd', + Treal = 'e', - // Get field width - field_width = 0; - if (c == '*') - { - field_width = getFmtStar(); - if (field_width < 0) - { flags |= FLdash; - field_width = -field_width; - } + Tifloat = 'o', + Tidouble = 'p', + Tireal = 'j', + Tcfloat = 'q', + Tcdouble = 'r', + Tcreal = 'c', - c = getFmtChar(); - } - else if (c >= '0' && c <= '9') - field_width = getFmtInt(); - - if (flags & FLplus) - flags &= ~FLspace; - if (flags & FLdash) - flags &= ~FL0pad; - - // Get precision - precision = 0; - if (c == '.') - { flags |= FLprecision; - //flags &= ~FL0pad; - - c = getFmtChar(); - if (c == '*') - { - precision = getFmtStar(); - if (precision < 0) - { precision = 0; - flags &= ~FLprecision; - } + Tchar = 'a', + Twchar = 'u', + Tdchar = 'w', - c = getFmtChar(); - } - else if (c >= '0' && c <= '9') - precision = getFmtInt(); - } + Tarray = 'A', + Tsarray = 'G', + Taarray = 'H', + Tpointer = 'P', + Tfunction = 'F', + Tident = 'I', + Tclass = 'C', + Tstruct = 'S', + Tenum = 'E', + Ttypedef = 'T', + Tdelegate = 'D', - if (j == arguments.length) - goto Lerror; - ti = arguments[j++]; - ti = skipCI(ti); - mi = 9; - do - { - m = cast(Mangle)typeid(ti).name[mi++]; - } while (m == Mangle.Tconst || m == Mangle.Timmutable); + Tconst = 'x', + Timmutable = 'y', +} - if (c > 0x7F) // if UTF sequence - goto Lerror; // format specifiers can't be UTF - formatArg(cast(char)c); - } - } - else - { - formatArg('s'); - } +// return the TypeInfo for a primitive type and null otherwise. This +// is required since for arrays of ints we only have the mangled char +// to work from. If arrays always subclassed TypeInfo_Array this +// routine could go away. +private TypeInfo primitiveTypeInfo(Mangle m) +{ + // BUG: should fix this in static this() to avoid double checked locking bug + __gshared TypeInfo[Mangle] dic; + if (!dic.length) + { + dic = [ + Mangle.Tvoid : typeid(void), + Mangle.Tbool : typeid(bool), + Mangle.Tbyte : typeid(byte), + Mangle.Tubyte : typeid(ubyte), + Mangle.Tshort : typeid(short), + Mangle.Tushort : typeid(ushort), + Mangle.Tint : typeid(int), + Mangle.Tuint : typeid(uint), + Mangle.Tlong : typeid(long), + Mangle.Tulong : typeid(ulong), + Mangle.Tfloat : typeid(float), + Mangle.Tdouble : typeid(double), + Mangle.Treal : typeid(real), + Mangle.Tifloat : typeid(ifloat), + Mangle.Tidouble : typeid(idouble), + Mangle.Tireal : typeid(ireal), + Mangle.Tcfloat : typeid(cfloat), + Mangle.Tcdouble : typeid(cdouble), + Mangle.Tcreal : typeid(creal), + Mangle.Tchar : typeid(char), + Mangle.Twchar : typeid(wchar), + Mangle.Tdchar : typeid(dchar) + ]; } - return; + auto p = m in dic; + return p ? *p : null; +} + +private bool needToSwapEndianess(Char)(const ref FormatSpec!Char f) +{ + import std.system : endian, Endian; - Lerror: - throw new FormatException(); + return endian == Endian.littleEndian && f.flPlus + || endian == Endian.bigEndian && f.flDash; } /* ======================== Unit Tests ====================================== */ -unittest +@system unittest { import std.conv : octal; @@ -6240,14 +5445,8 @@ unittest version (MinGW) assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s); else version (CRuntime_Microsoft) - assert(s == "1.67 -0X1.47AE14P+0 nan", s); - else version (Android) - { - // bionic doesn't support hex formatting of floating point numbers - // or lower-case string formatting of nan yet, but it was committed - // recently (April 2014): - // https://code.google.com/p/android/issues/detail?id=64886 - } + assert(s == "1.67 -0X1.47AE14P+0 nan" + || s == "1.67 -0X1.47AE147AE147BP+0 nan", s); // MSVCRT 14+ (VS 2015) else assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s); @@ -6329,11 +5528,11 @@ unittest s = "helloworld"; string r; - r = format("%.2s", s[0..5]); + r = format("%.2s", s[0 .. 5]); assert(r == "he"); - r = format("%.20s", s[0..5]); + r = format("%.20s", s[0 .. 5]); assert(r == "hello"); - r = format("%8s", s[0..5]); + r = format("%8s", s[0 .. 5]); assert(r == " hello"); byte[] arrbyte = new byte[4]; @@ -6408,12 +5607,17 @@ unittest r = format("abc"c); assert(r == "abc"); - r = format("def"w); - assert(r == "def"); - r = format("ghi"d); - assert(r == "ghi"); - void* p = cast(void*)0xDEADBEEF; + //format() returns the same type as inputted. + wstring wr; + wr = format("def"w); + assert(wr == "def"w); + + dstring dr; + dr = format("ghi"d); + assert(dr == "ghi"d); + + void* p = cast(void*) 0xDEADBEEF; r = format("%s", p); assert(r == "DEADBEEF"); @@ -6426,6 +5630,8 @@ unittest assert(r == "012345"); r = format("%o", 9); assert(r == "11"); + r = format("%#o", 0); // issue 15663 + assert(r == "0"); r = format("%+d", 123); assert(r == "+123"); @@ -6520,7 +5726,7 @@ unittest assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4"); } -unittest +@safe unittest { // bugzilla 3479 import std.array; @@ -6529,7 +5735,7 @@ unittest assert(stream.data == "000000000010", stream.data); } -unittest +@safe unittest { // bug 6893 import std.array; @@ -6539,17 +5745,48 @@ unittest assert(stream.data == "C"); } +// Used to check format strings are compatible with argument types +package static const checkFormatException(alias fmt, Args...) = +{ + try + .format(fmt, Args.init); + catch (Exception e) + return (e.msg == ctfpMessage) ? null : e; + return null; +}(); + /***************************************************** * Format arguments into a string. * - * Params: fmt = Format string. For detailed specification, see $(XREF _format,formattedWrite). - * args = Variadic list of arguments to format into returned string. + * Params: fmt = Format string. For detailed specification, see $(LREF formattedWrite). + * args = Variadic list of arguments to _format into returned string. */ -string format(Char, Args...)(in Char[] fmt, Args args) +typeof(fmt) format(alias fmt, Args...)(Args args) +if (isSomeString!(typeof(fmt))) +{ + alias e = checkFormatException!(fmt, Args); + static assert(!e, e.msg); + return .format(fmt, args); +} + +/// Type checking can be done when fmt is known at compile-time: +@safe unittest +{ + auto s = format!"%s is %s"("Pi", 3.14); + assert(s == "Pi is 3.14"); + + static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg + static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg + static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg +} + +/// ditto +immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) +if (isSomeChar!Char) { import std.format : formattedWrite, FormatException; import std.array : appender; - auto w = appender!string(); + auto w = appender!(immutable(Char)[]); auto n = formattedWrite(w, fmt, args); version (all) { @@ -6563,7 +5800,7 @@ string format(Char, Args...)(in Char[] fmt, Args args) return w.data; } -unittest +@safe pure unittest { import std.format; import core.exception; @@ -6585,33 +5822,45 @@ unittest assert(format("hel%slo%s%s%s", "world", -138, 'c', true) == "helworldlo-138ctrue"); }); + + assert(is(typeof(format("happy")) == string)); + assert(is(typeof(format("happy"w)) == wstring)); + assert(is(typeof(format("happy"d)) == dstring)); +} + +// https://issues.dlang.org/show_bug.cgi?id=16661 +@safe unittest +{ + assert(format("%.2f"d, 0.4) == "0.40"); + assert("%02d"d.format(1) == "01"d); } /***************************************************** * Format arguments into buffer $(I buf) which must be large - * enough to hold the result. Throws RangeError if it is not. - * Returns: The slice of $(D buf) containing the formatted string. + * enough to hold the result. * - * $(RED sformat's current implementation has been replaced with $(LREF xsformat)'s - * implementation in November 2012. - * This is seamless for most code, but it makes it so that the only - * argument that can be a format string is the first one, so any - * code which used multiple format strings has broken. Please change - * your calls to sformat accordingly. + * Returns: + * The slice of `buf` containing the formatted string. * - * e.g.: - * ---- - * sformat(buf, "key = %s", key, ", value = %s", value) - * ---- - * needs to be rewritten as: - * ---- - * sformat(buf, "key = %s, value = %s", key, value) - * ---- - * ) + * Throws: + * A `RangeError` if `buf` isn't large enough to hold the + * formatted string. + * + * A $(LREF FormatException) if the length of `args` is different + * than the number of format specifiers in `fmt`. */ +char[] sformat(alias fmt, Args...)(char[] buf, Args args) +if (isSomeString!(typeof(fmt))) +{ + alias e = checkFormatException!(fmt, Args); + static assert(!e, e.msg); + return .sformat(buf, fmt, args); +} + +/// ditto char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { - import core.exception : onRangeError; + import core.exception : RangeError; import std.utf : encode; import std.format : formattedWrite, FormatException; @@ -6625,7 +5874,7 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) auto n = encode(enc, c); if (buf.length < i + n) - onRangeError("std.string.sformat", 0); + throw new RangeError(__FILE__, __LINE__); buf[i .. i + n] = enc[0 .. n]; i += n; @@ -6633,7 +5882,7 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) void put(const(char)[] s) { if (buf.length < i + s.length) - onRangeError("std.string.sformat", 0); + throw new RangeError(__FILE__, __LINE__); buf[i .. i + s.length] = s[]; i += s.length; @@ -6656,13 +5905,24 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) // with formattedWrite import std.conv : text; import std.exception : enforce; - enforce(n == args.length, new FormatException( - text("Orphan format arguments: args[", n, "..", args.length, "]"))); + enforce!FormatException( + n == args.length, + text("Orphan format arguments: args[", n, " .. ", args.length, "]") + ); } return buf[0 .. i]; } -unittest +/// The format string can be checked at compile-time (see $(LREF format) for details): +@system unittest +{ + char[10] buf; + + assert(buf[].sformat!"foo%s"('C') == "fooC"); + assert(sformat(buf[], "%s foo", "bar") == "bar foo"); +} + +@system unittest { import core.exception; import std.format; @@ -6688,3 +5948,97 @@ unittest assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d"); }); } + +/***************************** + * The .ptr is unsafe because it could be dereferenced and the length of the array may be 0. + * Returns: + * the difference between the starts of the arrays + */ +@trusted private pure nothrow @nogc + ptrdiff_t arrayPtrDiff(T)(const T[] array1, const T[] array2) +{ + return array1.ptr - array2.ptr; +} + +@safe unittest +{ + assertCTFEable!({ + auto tmp = format("%,d", 1000); + assert(tmp == "1,000", "'" ~ tmp ~ "'"); + + tmp = format("%,?d", 'z', 1234567); + assert(tmp == "1z234z567", "'" ~ tmp ~ "'"); + + tmp = format("%10,?d", 'z', 1234567); + assert(tmp == " 1z234z567", "'" ~ tmp ~ "'"); + + tmp = format("%11,2?d", 'z', 1234567); + assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'"); + + tmp = format("%11,*?d", 2, 'z', 1234567); + assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'"); + + tmp = format("%11,*d", 2, 1234567); + assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'"); + + tmp = format("%11,2d", 1234567); + assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'"); + }); +} + +@safe unittest +{ + auto tmp = format("%,f", 1000.0); + assert(tmp == "1,000.000,000", "'" ~ tmp ~ "'"); + + tmp = format("%,f", 1234567.891011); + assert(tmp == "1,234,567.891,011", "'" ~ tmp ~ "'"); + + tmp = format("%,f", -1234567.891011); + assert(tmp == "-1,234,567.891,011", "'" ~ tmp ~ "'"); + + tmp = format("%,2f", 1234567.891011); + assert(tmp == "1,23,45,67.89,10,11", "'" ~ tmp ~ "'"); + + tmp = format("%18,f", 1234567.891011); + assert(tmp == " 1,234,567.891,011", "'" ~ tmp ~ "'"); + + tmp = format("%18,?f", '.', 1234567.891011); + assert(tmp == " 1.234.567.891.011", "'" ~ tmp ~ "'"); + + tmp = format("%,?.3f", 'ä', 1234567.891011); + assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'"); + + tmp = format("%,*?.3f", 1, 'ä', 1234567.891011); + assert(tmp == "1ä2ä3ä4ä5ä6ä7.8ä9ä1", "'" ~ tmp ~ "'"); + + tmp = format("%,4?.3f", '_', 1234567.891011); + assert(tmp == "123_4567.891", "'" ~ tmp ~ "'"); + + tmp = format("%12,3.3f", 1234.5678); + assert(tmp == " 1,234.568", "'" ~ tmp ~ "'"); + + tmp = format("%,e", 3.141592653589793238462); + assert(tmp == "3.141,593e+00", "'" ~ tmp ~ "'"); + + tmp = format("%15,e", 3.141592653589793238462); + assert(tmp == " 3.141,593e+00", "'" ~ tmp ~ "'"); + + tmp = format("%15,e", -3.141592653589793238462); + assert(tmp == " -3.141,593e+00", "'" ~ tmp ~ "'"); + + tmp = format("%.4,*e", 2, 3.141592653589793238462); + assert(tmp == "3.14,16e+00", "'" ~ tmp ~ "'"); + + tmp = format("%13.4,*e", 2, 3.141592653589793238462); + assert(tmp == " 3.14,16e+00", "'" ~ tmp ~ "'"); + + tmp = format("%,.0f", 3.14); + assert(tmp == "3", "'" ~ tmp ~ "'"); + + tmp = format("%3,g", 1_000_000.123456); + assert(tmp == "1e+06", "'" ~ tmp ~ "'"); + + tmp = format("%19,?f", '.', -1234567.891011); + assert(tmp == " -1.234.567.891.011", "'" ~ tmp ~ "'"); +} diff --git a/std/functional.d b/std/functional.d index f6be980cae4..a0f4c096c0c 100644 --- a/std/functional.d +++ b/std/functional.d @@ -5,56 +5,52 @@ Functions that manipulate other functions. This module provides functions for compile time function composition. These functions are helpful when constructing predicates for the algorithms in -$(LINK2 std_algorithm.html, std.algorithm) or $(LINK2 std_range.html, -std.range). +$(MREF std, algorithm) or $(MREF std, range). +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , $(TR $(TH Function Name) $(TH Description) ) - $(TR $(TD $(D $(LREF adjoin))) + $(TR $(TD $(LREF adjoin)) $(TD Joins a couple of functions into one that executes the original functions independently and returns a tuple with all the results. )) - $(TR $(TD $(D $(LREF compose)), $(D $(LREF pipe))) + $(TR $(TD $(LREF compose)), $(LREF pipe) $(TD Join a couple of functions into one that executes the original functions one after the other, using one function's result for the next function's argument. )) - $(TR $(TD $(D $(LREF forward))) + $(TR $(TD $(LREF forward)) $(TD Forwards function arguments while saving ref-ness. )) - $(TR $(TD $(D $(LREF lessThan)), $(D $(LREF greaterThan)), $(D $(LREF equalTo))) + $(TR $(TD $(LREF lessThan)), $(LREF greaterThan)), $(D $(LREF equalTo) $(TD Ready-made predicate functions to compare two values. )) - $(TR $(TD $(D $(LREF memoize))) - $(TD Creates a function that caches its result for fast re-evalation. + $(TR $(TD $(LREF memoize)) + $(TD Creates a function that caches its result for fast re-evaluation. )) - $(TR $(TD $(D $(LREF not))) + $(TR $(TD $(LREF not)) $(TD Creates a function that negates another. )) - $(TR $(TD $(D $(LREF partial))) + $(TR $(TD $(LREF partial)) $(TD Creates a function that binds the first argument of a given function to a given value. )) - $(TR $(TD $(D $(LREF reverseArgs)), $(D $(LREF binaryReverseArgs))) + $(TR $(TD $(LREF reverseArgs)), $(LREF binaryReverseArgs) $(TD Predicate that reverses the order of its arguments. )) - $(TR $(TD $(D $(LREF toDelegate))) + $(TR $(TD $(LREF toDelegate)) $(TD Converts a callable to a delegate. )) - $(TR $(TD $(D $(LREF unaryFun)), $(D $(LREF binaryFun))) + $(TR $(TD $(LREF unaryFun)), $(LREF binaryFun) $(TD Create a unary or binary function from a string. Most often used when defining algorithms on ranges. )) ) -Macros: - -WIKI = Phobos/StdFunctional - Copyright: Copyright Andrei Alexandrescu 2008 - 2009. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.org, Andrei Alexandrescu) +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu) Source: $(PHOBOSSRC std/_functional.d) */ /* @@ -65,7 +61,8 @@ Distributed under the Boost Software License, Version 1.0. */ module std.functional; -import std.traits, std.typetuple; +import std.meta; // AliasSeq, Reverse +import std.traits; // isCallable, Parameters private template needOpCallAlias(alias fun) @@ -87,10 +84,8 @@ private template needOpCallAlias(alias fun) */ static if (is(typeof(fun.opCall) == function)) { - import std.traits : ParameterTypeTuple; - enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { - return fun(ParameterTypeTuple!fun.init); + return fun(Parameters!fun.init); }); } else @@ -110,7 +105,7 @@ template unaryFun(alias fun, string parmName = "a") { static if (!fun._ctfeMatchUnary(parmName)) { - import std.traits, std.typecons, std.typetuple; + import std.traits, std.typecons, std.meta; import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; } auto unaryFun(ElementType)(auto ref ElementType __a) @@ -131,21 +126,14 @@ template unaryFun(alias fun, string parmName = "a") } /// -unittest +@safe unittest { // Strings are compiled into functions: alias isEven = unaryFun!("(a & 1) == 0"); assert(isEven(2) && !isEven(1)); } -/+ Undocumented, will be removed December 2014+/ -deprecated("Parameter byRef is obsolete. Please call unaryFun!(fun, parmName) directly.") -template unaryFun(alias fun, bool byRef, string parmName = "a") -{ - alias unaryFun = unaryFun!(fun, parmName); -} - -unittest +@safe unittest { static int f1(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f1)(1)) == int)); @@ -157,7 +145,7 @@ unittest //assert(unaryFun!("return a + 1;")(41) == 42); int num = 41; - assert(unaryFun!("a + 1", true)(num) == 42); + assert(unaryFun!"a + 1"(num) == 42); // Issue 9906 struct Seen @@ -202,7 +190,7 @@ template binaryFun(alias fun, string parm1Name = "a", { static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) { - import std.traits, std.typecons, std.typetuple; + import std.traits, std.typecons, std.meta; import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; } auto binaryFun(ElementType1, ElementType2) @@ -225,7 +213,7 @@ template binaryFun(alias fun, string parm1Name = "a", } /// -unittest +@safe unittest { alias less = binaryFun!("a < b"); assert(less(1, 2) && !less(2, 1)); @@ -233,7 +221,7 @@ unittest assert(!greater("1", "2") && greater("2", "1")); } -unittest +@safe unittest { static int f1(int a, string b) { return a + 1; } static assert(is(typeof(binaryFun!(f1)(1, "2")) == int)); @@ -267,7 +255,7 @@ unittest static assert(!is(typeof(binaryFun!FuncObj))); } -// skip all ASCII chars except a..z, A..Z, 0..9, '_' and '.'. +// skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'. private uint _ctfeSkipOp(ref string op) { if (!__ctfe) assert(false); @@ -276,7 +264,7 @@ private uint _ctfeSkipOp(ref string op) while (op.length) { immutable front = op[0]; - if(front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.')) + if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.')) op = op[1..$]; else break; @@ -293,7 +281,7 @@ private uint _ctfeSkipInteger(ref string op) while (op.length) { immutable front = op[0]; - if(front.isDigit()) + if (front.isDigit()) op = op[1..$]; else break; @@ -305,7 +293,7 @@ private uint _ctfeSkipInteger(ref string op) private uint _ctfeSkipName(ref string op, string name) { if (!__ctfe) assert(false); - if (op.length >= name.length && op[0..name.length] == name) + if (op.length >= name.length && op[0 .. name.length] == name) { op = op[name.length..$]; return 1; @@ -317,7 +305,6 @@ private uint _ctfeSkipName(ref string op, string name) private uint _ctfeMatchUnary(string fun, string name) { if (!__ctfe) assert(false); - import std.stdio; fun._ctfeSkipOp(); for (;;) { @@ -329,7 +316,7 @@ private uint _ctfeMatchUnary(string fun, string name) } else if (h == 1) { - if(!fun._ctfeSkipOp()) + if (!fun._ctfeSkipOp()) break; } else @@ -338,7 +325,7 @@ private uint _ctfeMatchUnary(string fun, string name) return fun.length == 0; } -unittest +@safe unittest { static assert(!_ctfeMatchUnary("sqrt(Ñ‘)", "Ñ‘")); static assert(!_ctfeMatchUnary("Ñ‘.sqrt", "Ñ‘")); @@ -348,9 +335,9 @@ unittest static assert(_ctfeMatchUnary("a+a", "a")); static assert(_ctfeMatchUnary("a + 10", "a")); static assert(_ctfeMatchUnary("4 == a", "a")); - static assert(_ctfeMatchUnary("2==a", "a")); + static assert(_ctfeMatchUnary("2 == a", "a")); static assert(_ctfeMatchUnary("1 != a", "a")); - static assert(_ctfeMatchUnary("a!=4", "a")); + static assert(_ctfeMatchUnary("a != 4", "a")); static assert(_ctfeMatchUnary("a< 1", "a")); static assert(_ctfeMatchUnary("434 < a", "a")); static assert(_ctfeMatchUnary("132 > a", "a")); @@ -376,7 +363,7 @@ private uint _ctfeMatchBinary(string fun, string name1, string name2) } else if (h == 1) { - if(!fun._ctfeSkipOp()) + if (!fun._ctfeSkipOp()) break; } else @@ -385,7 +372,8 @@ private uint _ctfeMatchBinary(string fun, string name1, string name2) return fun.length == 0; } -unittest { +@safe unittest +{ static assert(!_ctfeMatchBinary("sqrt(Ñ‘)", "Ñ‘", "b")); static assert(!_ctfeMatchBinary("Ñ‘.sqrt", "Ñ‘", "b")); @@ -395,9 +383,9 @@ unittest { static assert(_ctfeMatchBinary("a+a", "a", "b")); static assert(_ctfeMatchBinary("a + 10", "a", "b")); static assert(_ctfeMatchBinary("4 == a", "a", "b")); - static assert(_ctfeMatchBinary("2==a", "a", "b")); + static assert(_ctfeMatchBinary("2 == a", "a", "b")); static assert(_ctfeMatchBinary("1 != a", "a", "b")); - static assert(_ctfeMatchBinary("a!=4", "a", "b")); + static assert(_ctfeMatchBinary("a != 4", "a", "b")); static assert(_ctfeMatchBinary("a< 1", "a", "b")); static assert(_ctfeMatchBinary("434 < a", "a", "b")); static assert(_ctfeMatchBinary("132 > a", "a", "b")); @@ -415,9 +403,9 @@ unittest { static assert(_ctfeMatchBinary("a+b", "b", "a")); static assert(_ctfeMatchBinary("a + b", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); - static assert(_ctfeMatchBinary("b==a", "b", "a")); + static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b != a", "b", "a")); - static assert(_ctfeMatchBinary("a!=b", "b", "a")); + static assert(_ctfeMatchBinary("a != b", "b", "a")); static assert(_ctfeMatchBinary("a< b", "b", "a")); static assert(_ctfeMatchBinary("b < a", "b", "a")); static assert(_ctfeMatchBinary("b > a", "b", "a")); @@ -430,17 +418,20 @@ unittest { //undocumented template safeOp(string S) - if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") +if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") { + import std.traits : isIntegral; private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure if (isIntegral!ElementType1 && isIntegral!ElementType2) { + import std.traits : CommonType; alias T = CommonType!(ElementType1, ElementType2); - return mixin("cast(T)a "~S~" cast(T)b"); + return mixin("cast(T)a "~S~" cast(T) b"); } bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b) { + import std.traits : mostNegative; static if (isIntegral!T0 && isIntegral!T1 && (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) { @@ -461,7 +452,7 @@ template safeOp(string S) } else { - static assert (is(typeof(mixin("a "~S~" b"))), + static assert(is(typeof(mixin("a "~S~" b"))), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); immutable result = mixin("a "~S~" b"); @@ -470,9 +461,9 @@ template safeOp(string S) } } -unittest //check user defined types +@safe unittest //check user defined types { - import std.algorithm : equal; + import std.algorithm.comparison : equal; struct Foo { int a; @@ -490,6 +481,7 @@ unittest //check user defined types */ alias lessThan = safeOp!"<"; +/// pure @safe @nogc nothrow unittest { assert(lessThan(2, 3)); @@ -510,7 +502,8 @@ pure @safe @nogc nothrow unittest */ alias greaterThan = safeOp!">"; -unittest +/// +@safe unittest { assert(!greaterThan(2, 3)); assert(!greaterThan(2U, 3U)); @@ -530,7 +523,8 @@ unittest */ alias equalTo = safeOp!"=="; -unittest +/// +@safe unittest { assert(equalTo(0U, 0)); assert(equalTo(0, 0U)); @@ -549,7 +543,8 @@ template reverseArgs(alias pred) } } -unittest +/// +@safe unittest { alias gt = reverseArgs!(binaryFun!("a < b")); assert(gt(2, 1) && !gt(1, 1)); @@ -559,15 +554,27 @@ unittest foo(4, 5); alias zyx = reverseArgs!(foo); assert(zyx(5, 4) == foo(4, 5)); +} +/// +@safe unittest +{ int abc(int a, int b, int c) { return a * b + c; } alias cba = reverseArgs!abc; assert(abc(91, 17, 32) == cba(32, 17, 91)); +} +/// +@safe unittest +{ int a(int a) { return a * 2; } alias _a = reverseArgs!a; assert(a(2) == _a(2)); +} +/// +@safe unittest +{ int b() { return 4; } alias _b = reverseArgs!b; assert(b() == _b()); @@ -586,10 +593,16 @@ template binaryReverseArgs(alias pred) } } -unittest +/// +@safe unittest { alias gt = binaryReverseArgs!(binaryFun!("a < b")); assert(gt(2, 1) && !gt(1, 1)); +} + +/// +@safe unittest +{ int x = 42; bool xyz(int a, int b) { return a * x < b / x; } auto foo = &xyz; @@ -617,16 +630,16 @@ template not(alias pred) } /// -unittest +@safe unittest { + import std.algorithm.searching : find; import std.functional; - import std.algorithm : find; import std.uni : isWhite; string a = " Hello, world!"; assert(find!(not!isWhite)(a) == "Hello, world!"); } -unittest +@safe unittest { assert(not!"a != 5"(5)); assert(not!"a != b"(5, 5)); @@ -640,24 +653,13 @@ unittest /** $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg). - -Example: - ----- -int fun(int a, int b) { return a + b; } -alias partial!(fun, 5) fun5; -assert(fun5(6) == 11); ----- - -Note that in most cases you'd use an alias instead of a value -assignment. Using an alias allows you to partially evaluate template -functions without committing to a particular type of the function. */ template partial(alias fun, alias arg) { static if (is(typeof(fun) == delegate) || is(typeof(fun) == function)) { - ReturnType!fun partial(ParameterTypeTuple!fun[1..$] args2) + import std.traits : ReturnType; + ReturnType!fun partial(Parameters!fun[1..$] args2) { return fun(arg, args2); } @@ -676,7 +678,7 @@ template partial(alias fun, alias arg) { string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~ "(" ~ arg.stringof; - foreach(T; Ts) + foreach (T; Ts) msg ~= ", " ~ T.stringof; msg ~= ")."; return msg; @@ -687,15 +689,19 @@ template partial(alias fun, alias arg) } } -/** -Deprecated alias for $(D partial), kept for backwards compatibility - */ - -deprecated("Please use std.functional.partial instead") -alias curry = partial; +/// +@safe unittest +{ + int fun(int a, int b) { return a + b; } + alias fun5 = partial!(fun, 5); + assert(fun5(6) == 11); + // Note that in most cases you'd use an alias instead of a value + // assignment. Using an alias allows you to partially evaluate template + // functions without committing to a particular type of the function. +} // tests for partially evaluating callables -unittest +@safe unittest { static int f1(int a, int b) { return a + b; } assert(partial!(f1, 5)(6) == 11); @@ -725,7 +731,7 @@ unittest } // tests for partially evaluating templated/overloaded callables -unittest +@safe unittest { static auto add(A, B)(A x, B y) { @@ -776,19 +782,13 @@ unittest assert(funThreeArgs1(2, 3) == 6); static assert(!is(typeof(funThreeArgs1(1)))); - // @@ dmd BUG 6600 @@ - // breaks completely unrelated unittest for toDelegate - // static assert(is(typeof(dg_pure_nothrow) == int delegate() pure nothrow)); - version (none) - { - auto dg2 = &funOneArg1!(); - assert(dg2() == 1); - } + auto dg2 = &funOneArg1!(); + assert(dg2() == 1); } /** Takes multiple functions and adjoins them together. The result is a -$(XREF typecons, Tuple) with one element per passed-in function. Upon +$(REF Tuple, std,typecons) with one element per passed-in function. Upon invocation, the returned tuple is the adjoined results of all functions. @@ -796,12 +796,14 @@ Note: In the special case where only a single function is provided ($(D F.length == 1)), adjoin simply aliases to the single passed function ($(D F[0])). */ -template adjoin(F...) if (F.length == 1) +template adjoin(F...) +if (F.length == 1) { alias adjoin = F[0]; } /// ditto -template adjoin(F...) if (F.length > 1) +template adjoin(F...) +if (F.length > 1) { auto adjoin(V...)(auto ref V a) { @@ -824,9 +826,9 @@ template adjoin(F...) if (F.length > 1) } /// -unittest +@safe unittest { - import std.functional, std.typecons; + import std.functional, std.typecons : Tuple; static bool f1(int a) { return a != 0; } static int f2(int a) { return a / 2; } auto x = adjoin!(f1, f2)(5); @@ -834,9 +836,9 @@ unittest assert(x[0] == true && x[1] == 2); } -unittest +@safe unittest { - import std.typecons; + import std.typecons : Tuple; static bool F1(int a) { return a != 0; } auto x1 = adjoin!(F1)(5); static int F2(int a) { return a / 2; } @@ -851,7 +853,7 @@ unittest alias eff4 = adjoin!(F4); static struct S { - bool delegate(int) store; + bool delegate(int) @safe store; int fun() { return 42 + store(5); } } S s; @@ -860,9 +862,9 @@ unittest assert(x4 == 43); } -unittest +@safe unittest { - import std.typetuple : staticMap; + import std.meta : staticMap; import std.typecons : Tuple, tuple; alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a"); alias afun = adjoin!funs; @@ -879,60 +881,14 @@ unittest enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); } -// /*private*/ template NaryFun(string fun, string letter, V...) -// { -// static if (V.length == 0) -// { -// enum args = ""; -// } -// else -// { -// enum args = V[0].stringof~" "~letter~"; " -// ~NaryFun!(fun, [letter[0] + 1], V[1..$]).args; -// enum code = args ~ "return "~fun~";"; -// } -// alias Result = void; -// } - -// unittest -// { -// writeln(NaryFun!("a * b * 2", "a", int, double).code); -// } - -// /** -// naryFun -// */ -// template naryFun(string fun) -// { -// //NaryFun!(fun, "a", V).Result -// int naryFun(V...)(V values) -// { -// enum string code = NaryFun!(fun, "a", V).code; -// mixin(code); -// } -// } - -// unittest -// { -// alias test = naryFun!("a + b"); -// test(1, 2); -// } - /** Composes passed-in functions $(D fun[0], fun[1], ...) returning a function $(D f(x)) that in turn returns $(D fun[0](fun[1](...(x)))...). Each function can be a regular functions, a delegate, or a string. - Example: - ----- -// First split a string in whitespace-separated tokens and then -// convert each token into an integer -assert(compose!(map!(to!(int)), split)("1 2 3") == [1, 2, 3]); ----- + See_Also: $(LREF pipe) */ - template compose(fun...) { static if (fun.length == 1) @@ -958,6 +914,19 @@ template compose(fun...) } } +/// +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.array : split; + import std.conv : to; + + // First split a string in whitespace-separated tokens and then + // convert each token into an integer + assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3])); +} + /** Pipes functions in sequence. Offers the same functionality as $(D compose), but with functions specified in reverse order. This may @@ -972,10 +941,12 @@ template compose(fun...) // integer int[] a = pipe!(readText, split, map!(to!(int)))("file.txt"); ---- + + See_Also: $(LREF compose) */ alias pipe(fun...) = compose!(Reverse!(fun)); -unittest +@safe unittest { import std.conv : to; string foo(int a) { return to!(string)(a); } @@ -989,16 +960,15 @@ unittest assert(compose!(baz, bar)("1") == 2.5); - // @@@BUG@@@ - //assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5); + assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5); } /** - * $(LUCKY Memoizes) a function so as to avoid repeated - * computation. The memoization structure is a hash table keyed by a + * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as + * to avoid repeated computation. The memoization structure is a hash table keyed by a * tuple of the function's arguments. There is a speed gain if the * function is repeatedly called with the same arguments and is more - * expensive than a hash table lookup. For more information on memoization, refer to $(WEB docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter). + * expensive than a hash table lookup. For more information on memoization, refer to $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter). Example: ---- @@ -1022,11 +992,12 @@ is useful to memoize an impure function, too. */ template memoize(alias fun) { - // alias Args = ParameterTypeTuple!fun; // Bugzilla 13580 + import std.traits : ReturnType; + // alias Args = Parameters!fun; // Bugzilla 13580 - ReturnType!fun memoize(ParameterTypeTuple!fun args) + ReturnType!fun memoize(Parameters!fun args) { - alias Args = ParameterTypeTuple!fun; + alias Args = Parameters!fun; import std.typecons : Tuple; static ReturnType!fun[Tuple!Args] memo; @@ -1040,22 +1011,28 @@ template memoize(alias fun) /// ditto template memoize(alias fun, uint maxSize) { - // alias Args = ParameterTypeTuple!fun; // Bugzilla 13580 - ReturnType!fun memoize(ParameterTypeTuple!fun args) + import std.traits : ReturnType; + // alias Args = Parameters!fun; // Bugzilla 13580 + ReturnType!fun memoize(Parameters!fun args) { + import std.traits : hasIndirections; import std.typecons : tuple; - static struct Value { ParameterTypeTuple!fun args; ReturnType!fun res; } + static struct Value { Parameters!fun args; ReturnType!fun res; } static Value[] memo; static size_t[] initialized; if (!memo.length) { - import core.memory; + import core.memory : GC; + + // Ensure no allocation overflows + static assert(maxSize < size_t.max / Value.sizeof); + static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1)); enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN); - memo = (cast(Value*)GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; + memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); - initialized = (cast(size_t*)GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; + initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; } import core.bitop : bt, bts; @@ -1075,7 +1052,7 @@ template memoize(alias fun, uint maxSize) else if (memo[idx1].args == args) return memo[idx1].res; // FNV prime - immutable idx2 = (hash * 16777619) % maxSize; + immutable idx2 = (hash * 16_777_619) % maxSize; if (!bt(initialized.ptr, idx2)) { emplace(&memo[idx2], memo[idx1]); @@ -1095,21 +1072,21 @@ template memoize(alias fun, uint maxSize) * To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call. * For example, to transform the exponential-time Fibonacci implementation into a linear-time computation: */ -unittest +@safe unittest { - ulong fib(ulong n) + ulong fib(ulong n) @safe { - return n < 2 ? 1 : memoize!fib(n - 2) + memoize!fib(n - 1); + return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1); } - assert(fib(10) == 89); + assert(fib(10) == 55); } /** * To improve the speed of the factorial function, */ -unittest +@safe unittest { - ulong fact(ulong n) + ulong fact(ulong n) @safe { return n < 2 ? 1 : n * memoize!fact(n - 1); } @@ -1120,9 +1097,9 @@ unittest * This memoizes all values of $(D fact) up to the largest argument. To only cache the final * result, move $(D memoize) outside the function as shown below. */ -unittest +@safe unittest { - ulong factImpl(ulong n) + ulong factImpl(ulong n) @safe { return n < 2 ? 1 : n * factImpl(n - 1); } @@ -1134,7 +1111,7 @@ unittest * When the $(D maxSize) parameter is specified, memoize will used * a fixed size hash table to limit the number of cached entries. */ -unittest +@system unittest // not @safe due to memoize { ulong fact(ulong n) { @@ -1146,9 +1123,9 @@ unittest assert(fact(10) == 3628800); } -unittest +@system unittest // not @safe due to memoize { - import core.math; + import core.math : sqrt; alias msqrt = memoize!(function double(double x) { return sqrt(x); }); auto y = msqrt(2.0); assert(y == msqrt(2.0)); @@ -1161,7 +1138,7 @@ unittest //alias mfib = memoize!fib; - static ulong fib(ulong n) + static ulong fib(ulong n) @safe { alias mfib = memoize!fib; return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1); @@ -1170,7 +1147,7 @@ unittest auto z = fib(10); assert(z == 89); - static ulong fact(ulong n) + static ulong fact(ulong n) @safe { alias mfact = memoize!fact; return n < 2 ? 1 : n * mfact(n - 1); @@ -1186,7 +1163,7 @@ unittest return 1 + mLen2(s[1 .. $]); } - int _func(int x) { return 1; } + int _func(int x) @safe { return 1; } alias func = memoize!(_func, 10); assert(func(int.init) == 1); assert(func(int.init) == 1); @@ -1194,7 +1171,7 @@ unittest private struct DelegateFaker(F) { - import std.typecons; + import std.typecons : FuncInfo, MemberFunctionGenerator; // for @safe static F castToF(THIS)(THIS x) @trusted @@ -1207,7 +1184,7 @@ private struct DelegateFaker(F) *-------------------- * struct DelegateFaker(F) { * extern(linkage) - * [ref] ReturnType!F doIt(ParameterTypeTuple!F args) [@attributes] + * [ref] ReturnType!F doIt(Parameters!F args) [@attributes] * { * auto fp = cast(F) &this; * return fp(args); @@ -1227,7 +1204,7 @@ private struct DelegateFaker(F) template generateFunctionBody(unused...) { enum generateFunctionBody = - // [ref] ReturnType doIt(ParameterTypeTuple args) @attributes + // [ref] ReturnType doIt(Parameters args) @attributes q{ // When this function gets called, the this pointer isn't // really a this pointer (no instance even really exists), but @@ -1243,7 +1220,7 @@ private struct DelegateFaker(F) alias FuncInfo_doIt = FuncInfo!(F); // Generate the member function doIt(). - mixin( std.typecons.MemberFunctionGenerator!(GeneratingPolicy!()) + mixin( MemberFunctionGenerator!(GeneratingPolicy!()) .generateFunction!("FuncInfo_doIt", "doIt", F) ); } @@ -1251,7 +1228,7 @@ private struct DelegateFaker(F) * Convert a callable to a delegate with the same parameter list and * return type, avoiding heap allocations and use of auxiliary storage. * - * Examples: + * Example: * ---- * void doStuff() { * writeln("Hello, world."); @@ -1271,7 +1248,8 @@ private struct DelegateFaker(F) * $(LI Ignores C-style / D-style variadic arguments.) * ) */ -auto toDelegate(F)(auto ref F fp) if (isCallable!(F)) +auto toDelegate(F)(auto ref F fp) +if (isCallable!(F)) { static if (is(F == delegate)) { @@ -1313,7 +1291,22 @@ auto toDelegate(F)(auto ref F fp) if (isCallable!(F)) } } -unittest { +/// +@system unittest +{ + static int inc(ref uint num) { + num++; + return 8675309; + } + + uint myNum = 0; + auto incMyNumDel = toDelegate(&inc); + auto returnVal = incMyNumDel(myNum); + assert(myNum == 1); +} + +@system unittest // not @safe due to toDelegate +{ static int inc(ref uint num) { num++; return 8675309; @@ -1321,7 +1314,7 @@ unittest { uint myNum = 0; auto incMyNumDel = toDelegate(&inc); - static assert(is(typeof(incMyNumDel) == int delegate(ref uint))); + int delegate(ref uint) dg = incMyNumDel; auto returnVal = incMyNumDel(myNum); assert(myNum == 1); @@ -1360,37 +1353,29 @@ unittest { static int func_trusted() @trusted { return 5; } static int func_system() @system { return 6; } static int func_pure_nothrow() pure nothrow { return 7; } - static int func_pure_nothrow_safe() pure @safe { return 8; } + static int func_pure_nothrow_safe() pure nothrow @safe { return 8; } auto dg_ref = toDelegate(&func_ref); - auto dg_pure = toDelegate(&func_pure); - auto dg_nothrow = toDelegate(&func_nothrow); - auto dg_property = toDelegate(&func_property); - auto dg_safe = toDelegate(&func_safe); - auto dg_trusted = toDelegate(&func_trusted); - auto dg_system = toDelegate(&func_system); - auto dg_pure_nothrow = toDelegate(&func_pure_nothrow); - auto dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe); + int delegate() pure dg_pure = toDelegate(&func_pure); + int delegate() nothrow dg_nothrow = toDelegate(&func_nothrow); + int delegate() @property dg_property = toDelegate(&func_property); + int delegate() @safe dg_safe = toDelegate(&func_safe); + int delegate() @trusted dg_trusted = toDelegate(&func_trusted); + int delegate() @system dg_system = toDelegate(&func_system); + int delegate() pure nothrow dg_pure_nothrow = toDelegate(&func_pure_nothrow); + int delegate() @safe pure nothrow dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe); //static assert(is(typeof(dg_ref) == ref int delegate())); // [BUG@DMD] - static assert(is(typeof(dg_pure) == int delegate() pure)); - static assert(is(typeof(dg_nothrow) == int delegate() nothrow)); - static assert(is(typeof(dg_property) == int delegate() @property)); - //static assert(is(typeof(dg_safe) == int delegate() @safe)); - static assert(is(typeof(dg_trusted) == int delegate() @trusted)); - static assert(is(typeof(dg_system) == int delegate() @system)); - static assert(is(typeof(dg_pure_nothrow) == int delegate() pure nothrow)); - //static assert(is(typeof(dg_pure_nothrow_safe) == int delegate() @safe pure nothrow)); assert(dg_ref() == refvar); assert(dg_pure() == 1); assert(dg_nothrow() == 2); assert(dg_property() == 3); - //assert(dg_safe() == 4); + assert(dg_safe() == 4); assert(dg_trusted() == 5); assert(dg_system() == 6); assert(dg_pure_nothrow() == 7); - //assert(dg_pure_nothrow_safe() == 8); + assert(dg_pure_nothrow_safe() == 8); } /* test for linkage */ { @@ -1410,21 +1395,19 @@ Forwards function arguments with saving ref-ness. */ template forward(args...) { - import std.typetuple; - static if (args.length) { - import std.algorithm.mutation : move; + import std.algorithm.mutation : move; alias arg = args[0]; static if (__traits(isRef, arg)) alias fwd = arg; else @property fwd()(){ return move(arg); } - alias forward = TypeTuple!(fwd, forward!(args[1..$])); + alias forward = AliasSeq!(fwd, forward!(args[1..$])); } else - alias forward = TypeTuple!(); + alias forward = AliasSeq!(); } /// @@ -1445,7 +1428,7 @@ template forward(args...) /// @safe unittest { - void foo(int n, ref string s) { s = null; foreach (i; 0..n) s ~= "Hello"; } + void foo(int n, ref string s) { s = null; foreach (i; 0 .. n) s ~= "Hello"; } // forwards all arguments which are bound to parameter tuple void bar(Args...)(auto ref Args args) { return foo(forward!args); } @@ -1502,4 +1485,3 @@ template forward(args...) int value = 3; auto x2 = bar(value); // case of OK } - diff --git a/std/getopt.d b/std/getopt.d index c7cb446d2d3..98d4eb62ebd 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -10,14 +10,10 @@ supported in the form of long options introduced by a double dash with the more traditional single-letter approach, is provided but not enabled by default. -Macros: - -WIKI = Phobos/StdGetopt - -Copyright: Copyright Andrei Alexandrescu 2008 - 2009. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.org, Andrei Alexandrescu) -Credits: This module and its documentation are inspired by Perl's $(WEB +Copyright: Copyright Andrei Alexandrescu 2008 - 2015. +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu) +Credits: This module and its documentation are inspired by Perl's $(HTTP perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of D's $(D getopt) is simpler than its Perl counterpart because $(D getopt) infers the expected parameter types from the static types of @@ -25,7 +21,7 @@ Credits: This module and its documentation are inspired by Perl's $(WEB Source: $(PHOBOSSRC std/_getopt.d) */ /* - Copyright Andrei Alexandrescu 2008 - 2009. + Copyright Andrei Alexandrescu 2008 - 2015. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -33,23 +29,27 @@ Distributed under the Boost Software License, Version 1.0. module std.getopt; import std.traits; +import std.exception; // basicExceptionCtors /** - * Thrown on one of the following conditions: - * - An unrecognized command-line argument is passed - * and $(D std.getopt.config.passThrough) was not present. - */ +Thrown on one of the following conditions: +$(UL + $(LI An unrecognized command-line argument is passed, and + $(D std.getopt.config.passThrough) was not present.) + $(LI A command-line option was not found, and + $(D std.getopt.config.required) was present.) +) +*/ class GetOptException : Exception { - @safe pure nothrow - this(string msg, string file = __FILE__, size_t line = __LINE__) - { - super(msg, file, line); - } + mixin basicExceptionCtors; } +static assert(is(typeof(new GetOptException("message")))); +static assert(is(typeof(new GetOptException("message", Exception.init)))); + /** - Parse and remove command line options from an string array. + Parse and remove command line options from a string array. Synopsis: @@ -83,7 +83,7 @@ void main(string[] args) The $(D getopt) function takes a reference to the command line (as received by $(D main)) as its first argument, and an unbounded number of pairs of strings and pointers. Each string is an - option meant to "fill" the value pointed-to by the pointer to its + option meant to "fill" the value referenced by the pointer to its right (the "bound" pointer). The option string in the call to $(D getopt) should not start with a dash. @@ -94,9 +94,9 @@ void main(string[] args) options are not touched, so a common idiom is to initialize options to their defaults and then invoke $(D getopt). If a command-line argument is recognized as an option with a parameter and - the parameter cannot be parsed properly (e.g. a number is expected + the parameter cannot be parsed properly (e.g., a number is expected but not present), a $(D ConvException) exception is thrown. - If $(D std.getopt.config.passThrough) was not passed to getopt + If $(D std.getopt.config.passThrough) was not passed to $(D getopt) and an unrecognized command-line argument is found, a $(D GetOptException) is thrown. @@ -144,7 +144,7 @@ void main(string[] args) Invoking the program with "--paranoid --paranoid --paranoid" will set $(D paranoid) to 3. Note that an incremental option never expects a parameter, - e.g. in the command line "--paranoid 42 --paranoid", the "42" does not set + e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set $(D paranoid) to 42; instead, $(D paranoid) is set to 2 and "42" is not considered as part of the normal program arguments. ) @@ -271,9 +271,10 @@ void main(string[] args) list. --------- -void main(string[] args) +int main(string[] args) { uint verbosityLevel = 1; + bool handlerFailed = false; void myHandler(string option, string value) { switch (value) @@ -282,20 +283,20 @@ void main(string[] args) case "verbose": verbosityLevel = 2; break; case "shouting": verbosityLevel = verbosityLevel.max; break; default : - stderr.writeln("Dunno how verbose you want me to be by saying ", - value); - exit(1); + stderr.writeln("Unknown verbosity level ", value); + handlerFailed = true; + break; } } getopt(args, "verbosity", &myHandler); + return handlerFailed ? 1 : 0; } --------- ) )) ) -$(B Options with multiple names) - +Options_with_multiple_names: Sometimes option synonyms are desirable, e.g. "--verbose", "--loquacious", and "--garrulous" should have the same effect. Such alternate option names can be included in the option specification, @@ -306,8 +307,7 @@ bool verbose; getopt(args, "verbose|loquacious|garrulous", &verbose); --------- -$(B Case) - +Case: By default options are case-insensitive. You can change that behavior by passing $(D getopt) the $(D caseSensitive) directive like this: @@ -319,8 +319,9 @@ getopt(args, "bar", &bar); --------- -In the example above, "--foo", "--bar", "--FOo", "--bAr" etc. are recognized. -The directive is active til the end of $(D getopt), or until the +In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar", +"--FOo", "--bAr", etc. are rejected. +The directive is active until the end of $(D getopt), or until the converse directive $(D caseInsensitive) is encountered: --------- @@ -338,8 +339,7 @@ etc. because the directive $(D std.getopt.config.caseInsensitive) turned sensitivity off before option "bar" was parsed. -$(B "Short" versus "long" options) - +Short_versus_long_options: Traditionally, programs accepted single-letter options preceded by only one dash (e.g. $(D -t)). $(D getopt) accepts such parameters seamlessly. When used with a double-dash (e.g. $(D --t)), a @@ -359,8 +359,7 @@ and $(D -timeout=5) will be not accepted. For more details about short options, refer also to the next section. -$(B Bundling) - +Bundling: Single-letter options can be bundled together, i.e. "-abc" is the same as $(D "-a -b -c"). By default, this option is turned off. You can turn it on with the $(D std.getopt.config.bundling) directive: @@ -376,10 +375,9 @@ getopt(args, In case you want to only enable bundling for some of the parameters, bundling can be turned off with $(D std.getopt.config.noBundling). -$(B Required) - +Required: An option can be marked as required. If that option is not present in the -arguments an exceptin will be thrown. +arguments an exception will be thrown. --------- bool foo, bar; @@ -389,11 +387,10 @@ getopt(args, "bar|b", &bar); --------- -Only the option direclty following $(D std.getopt.config.required) is +Only the option directly following $(D std.getopt.config.required) is required. -$(B Passing unrecognized options through) - +Passing_unrecognized_options_through: If an application needs to do its own processing of whichever arguments $(D getopt) did not understand, it can pass the $(D std.getopt.config.passThrough) directive to $(D getopt): @@ -409,21 +406,21 @@ getopt(args, An unrecognized option such as "--baz" will be found untouched in $(D args) after $(D getopt) returns. -$(D Help Information Generation) - -If an option string is followed by another string, this string serves as an -description for this option. The function $(D getopt) returns a struct of type +Help_Information_Generation: +If an option string is followed by another string, this string serves as a +description for this option. The $(D getopt) function returns a struct of type $(D GetoptResult). This return value contains information about all passed options -as well a bool indicating if information about these options where required by -the passed arguments. +as well a $(D bool GetoptResult.helpWanted) flag indicating whether information +about these options was requested. The $(D getopt) function always adds an option for +`--help|-h` to set the flag if the option is seen on the command line. -$(B Options Terminator) - -A lonesome double-dash terminates $(D getopt) gathering. It is used to -separate program options from other parameters (e.g. options to be passed +Options_Terminator: +A lone double-dash terminates $(D getopt) gathering. It is used to +separate program options from other parameters (e.g., options to be passed to another program). Invoking the example above with $(D "--foo -- --bar") parses foo but leaves "--bar" in $(D args). The double-dash itself is -removed from the argument array. +removed from the argument array unless the $(D std.getopt.config.keepEndOfOptions) +directive is given. */ GetoptResult getopt(T...)(ref string[] args, T opts) { @@ -433,13 +430,20 @@ GetoptResult getopt(T...)(ref string[] args, T opts) configuration cfg; GetoptResult rslt; - getoptImpl(args, cfg, rslt, opts); + GetOptException excep; + void[][string] visitedLongOpts, visitedShortOpts; + getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts); + + if (!rslt.helpWanted && excep !is null) + { + throw excep; + } return rslt; } /// -unittest +@system unittest { auto args = ["prog", "--foo", "-b"]; @@ -462,45 +466,42 @@ unittest string and its bound pointer. */ enum config { - /// Turns case sensitivity on + /// Turn case sensitivity on caseSensitive, - /// Turns case sensitivity off + /// Turn case sensitivity off (default) caseInsensitive, - /// Turns bundling on + /// Turn bundling on bundling, - /// Turns bundling off + /// Turn bundling off (default) noBundling, /// Pass unrecognized arguments through passThrough, - /// Signal unrecognized arguments as errors + /// Signal unrecognized arguments as errors (default) noPassThrough, /// Stop at first argument that does not look like an option stopOnFirstNonOption, /// Do not erase the endOfOptions separator from args keepEndOfOptions, - /// Makes the next option a required option + /// Make the next option a required option required } /** The result of the $(D getopt) function. -The $(D GetoptResult) contains two members. The first member is a boolean with -the name $(D helpWanted). The second member is an array of $(D Option). The -array is accessable by the name $(D options). +$(D helpWanted) is set if the option `--help` or `-h` was passed to the option parser. */ struct GetoptResult { bool helpWanted; /// Flag indicating if help was requested Option[] options; /// All possible options } -/** The result of the $(D getoptHelp) function. +/** Information about an option. */ struct Option { string optShort; /// The short symbol for this option string optLong; /// The long symbol for this option string help; /// The description of this option - bool required; /// If a option is required, not passing it will result in - /// an error. + bool required; /// If a option is required, not passing it will result in an error } private pure Option splitAndGet(string opt) @trusted nothrow @@ -515,18 +516,174 @@ private pure Option splitAndGet(string opt) @trusted nothrow ret.optLong = "--" ~ (sp[0].length > sp[1].length ? sp[0] : sp[1]); } - else + else if (sp[0].length > 1) { ret.optLong = "--" ~ sp[0]; } + else + { + ret.optShort = "-" ~ sp[0]; + } return ret; } +@safe unittest +{ + auto oshort = splitAndGet("f"); + assert(oshort.optShort == "-f"); + assert(oshort.optLong == ""); + + auto olong = splitAndGet("foo"); + assert(olong.optShort == ""); + assert(olong.optLong == "--foo"); + + auto oshortlong = splitAndGet("f|foo"); + assert(oshortlong.optShort == "-f"); + assert(oshortlong.optLong == "--foo"); + + auto olongshort = splitAndGet("foo|f"); + assert(olongshort.optShort == "-f"); + assert(olongshort.optLong == "--foo"); +} + +/* +This function verifies that the variadic parameters passed in getOpt +follow this pattern: + + [config override], option, [description], receiver, + + - config override: a config value, optional + - option: a string or a char + - description: a string, optional + - receiver: a pointer or a callable +*/ +private template optionValidator(A...) +{ + import std.typecons : staticIota; + import std.format : format; + + enum fmt = "getopt validator: %s (at position %d)"; + enum isReceiver(T) = isPointer!T || (is(T == function)) || (is(T == delegate)); + enum isOptionStr(T) = isSomeString!T || isSomeChar!T; + + auto validator() + { + string msg; + static if (A.length > 0) + { + static if (isReceiver!(A[0])) + { + msg = format(fmt, "first argument must be a string or a config", 0); + } + else static if (!isOptionStr!(A[0]) && !is(A[0] == config)) + { + msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0); + } + else foreach (i; staticIota!(1, A.length)) + { + static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) && + !(is(A[i] == config))) + { + msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i); + break; + } + else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1])) + { + msg = format(fmt, "a receiver can not be preceeded by a receiver", i); + break; + } + else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1]) + && isSomeString!(A[i-2])) + { + msg = format(fmt, "a string can not be preceeded by two strings", i); + break; + } + } + static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config)) + { + msg = format(fmt, "last argument must be a receiver or a config", + A.length -1); + } + } + return msg; + } + enum message = validator; + alias optionValidator = message; +} + +@safe pure unittest +{ + alias P = void*; + alias S = string; + alias A = char; + alias C = config; + alias F = void function(); + + static assert(optionValidator!(S,P) == ""); + static assert(optionValidator!(S,F) == ""); + static assert(optionValidator!(A,P) == ""); + static assert(optionValidator!(A,F) == ""); + + static assert(optionValidator!(C,S,P) == ""); + static assert(optionValidator!(C,S,F) == ""); + static assert(optionValidator!(C,A,P) == ""); + static assert(optionValidator!(C,A,F) == ""); + + static assert(optionValidator!(C,S,S,P) == ""); + static assert(optionValidator!(C,S,S,F) == ""); + static assert(optionValidator!(C,A,S,P) == ""); + static assert(optionValidator!(C,A,S,F) == ""); + + static assert(optionValidator!(C,S,S,P) == ""); + static assert(optionValidator!(C,S,S,P,C,S,F) == ""); + static assert(optionValidator!(C,S,P,C,S,S,F) == ""); + + static assert(optionValidator!(C,A,P,A,S,F) == ""); + static assert(optionValidator!(C,A,P,C,A,S,F) == ""); + + static assert(optionValidator!(P,S,S) != ""); + static assert(optionValidator!(P,P,S) != ""); + static assert(optionValidator!(P,F,S,P) != ""); + static assert(optionValidator!(C,C,S) != ""); + static assert(optionValidator!(S,S,P,S,S,P,S) != ""); + static assert(optionValidator!(S,S,P,P) != ""); + static assert(optionValidator!(S,S,S,P) != ""); + + static assert(optionValidator!(C,A,S,P,C,A,F) == ""); + static assert(optionValidator!(C,A,P,C,A,S,F) == ""); +} + +@system unittest // bugzilla 15914 +{ + bool opt; + string[] args = ["program", "-a"]; + getopt(args, config.passThrough, 'a', &opt); + assert(opt); + opt = false; + args = ["program", "-a"]; + getopt(args, 'a', &opt); + assert(opt); + opt = false; + args = ["program", "-a"]; + getopt(args, 'a', "help string", &opt); + assert(opt); + opt = false; + args = ["program", "-a"]; + getopt(args, config.caseSensitive, 'a', "help string", &opt); + assert(opt); + + assertThrown(getopt(args, "", "forgot to put a string", &opt)); +} + private void getoptImpl(T...)(ref string[] args, ref configuration cfg, - ref GetoptResult rslt, T opts) + ref GetoptResult rslt, ref GetOptException excep, + void[][string] visitedLongOpts, void[][string] visitedShortOpts, T opts) { - import std.algorithm : remove; + enum validationMessage = optionValidator!T; + static assert(validationMessage == "", validationMessage); + + import std.algorithm.mutation : remove; import std.conv : to; static if (opts.length) { @@ -534,15 +691,38 @@ private void getoptImpl(T...)(ref string[] args, ref configuration cfg, { // it's a configuration flag, act on it setConfig(cfg, opts[0]); - return getoptImpl(args, cfg, rslt, opts[1 .. $]); + return getoptImpl(args, cfg, rslt, excep, visitedLongOpts, + visitedShortOpts, opts[1 .. $]); } else { // it's an option string auto option = to!string(opts[0]); + if (option.length == 0) + { + excep = new GetOptException("An option name may not be an empty string", excep); + return; + } Option optionHelp = splitAndGet(option); optionHelp.required = cfg.required; + if (optionHelp.optLong.length) + { + assert(optionHelp.optLong !in visitedLongOpts, + "Long option " ~ optionHelp.optLong ~ " is multiply defined"); + + visitedLongOpts[optionHelp.optLong] = []; + } + + if (optionHelp.optShort.length) + { + assert(optionHelp.optShort !in visitedShortOpts, + "Short option " ~ optionHelp.optShort + ~ " is multiply defined"); + + visitedShortOpts[optionHelp.optShort] = []; + } + static if (is(typeof(opts[1]) : string)) { auto receiver = opts[2]; @@ -569,12 +749,13 @@ private void getoptImpl(T...)(ref string[] args, ref configuration cfg, if (cfg.required && !optWasHandled) { - throw new GetOptException("Required option " ~ option ~ - "was not supplied"); + excep = new GetOptException("Required option " + ~ option ~ " was not supplied", excep); } cfg.required = false; - return getoptImpl(args, cfg, rslt, opts[lowSliceIdx .. $]); + getoptImpl(args, cfg, rslt, excep, visitedLongOpts, + visitedShortOpts, opts[lowSliceIdx .. $]); } } else @@ -605,7 +786,7 @@ private void getoptImpl(T...)(ref string[] args, ref configuration cfg, } if (!cfg.passThrough) { - throw new GetOptException("Unrecognized option "~a); + throw new GetOptException("Unrecognized option "~a, excep); } ++i; } @@ -619,9 +800,9 @@ private void getoptImpl(T...)(ref string[] args, ref configuration cfg, } private bool handleOption(R)(string option, R receiver, ref string[] args, - ref configuration cfg, bool incremental) + ref configuration cfg, bool incremental) { - import std.algorithm : map, splitter; + import std.algorithm.iteration : map, splitter; import std.ascii : isAlpha; import std.conv : text, to; // Scan arguments looking for a match for this option @@ -646,6 +827,8 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, // e.g. -j100 to work as "pass argument 100 to option -j". if (!isAlpha(c)) { + if (c == '=') + j++; expanded ~= a[j + 1 .. $]; break; } @@ -671,16 +854,16 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, static if (is(typeof(*receiver) == bool)) { - // parse '--b=true/false' if (val.length) { + // parse '--b=true/false' *receiver = to!(typeof(*receiver))(val); - break; } - - // no argument means set it to true - *receiver = true; - break; + else + { + // no argument means set it to true + *receiver = true; + } } else { @@ -781,10 +964,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, setHash(receiver, val.splitter(arraySep)); } else - { - static assert(false, "Dunno how to deal with type " ~ - typeof(receiver).stringof); - } + static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof); } } @@ -792,7 +972,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, } // 5316 - arrays with arraySep -unittest +@system unittest { import std.conv; @@ -805,7 +985,7 @@ unittest assert(names == ["foo", "bar", "baz"], to!string(names)); names = names.init; - args = ["program.name", "-n" "foo,bar,baz"]; + args = ["program.name", "-n", "foo,bar,baz"]; getopt(args, "name|n", &names); assert(names == ["foo", "bar", "baz"], to!string(names)); @@ -821,7 +1001,7 @@ unittest } // 5316 - associative arrays with arraySep -unittest +@system unittest { import std.conv; @@ -880,7 +1060,7 @@ dchar assignChar = '='; */ string arraySep = ""; -enum autoIncrementChar = '+'; +private enum autoIncrementChar = '+'; private struct configuration { @@ -896,7 +1076,7 @@ private struct configuration } private bool optMatch(string arg, string optPattern, ref string value, - configuration cfg) + configuration cfg) @safe { import std.uni : toUpper; import std.string : indexOf; @@ -919,6 +1099,13 @@ private bool optMatch(string arg, string optPattern, ref string value, } else { + if (!isLong && eqPos == 1) + { + // argument looks like -o=value + value = arg[2 .. $]; + arg = arg[0 .. 1]; + } + else if (!isLong && !cfg.bundling) { // argument looks like -ovalue and there's no bundling @@ -949,9 +1136,9 @@ private bool optMatch(string arg, string optPattern, ref string value, return false; } -private void setConfig(ref configuration cfg, config option) +private void setConfig(ref configuration cfg, config option) @safe pure nothrow @nogc { - switch (option) + final switch (option) { case config.caseSensitive: cfg.caseSensitive = true; break; case config.caseInsensitive: cfg.caseSensitive = false; break; @@ -964,11 +1151,10 @@ private void setConfig(ref configuration cfg, config option) cfg.stopOnFirstNonOption = true; break; case config.keepEndOfOptions: cfg.keepEndOfOptions = true; break; - default: assert(false); } } -unittest +@system unittest { import std.conv; import std.math; @@ -1150,7 +1336,17 @@ unittest catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); } } -unittest +@safe unittest // @safe std.getopt.config option use +{ + long x = 0; + string[] args = ["program", "--inc-x", "--inc-x"]; + getopt(args, + std.getopt.config.caseSensitive, + "inc-x", "Add one to x", delegate void() { x++; }); + assert(x == 2); +} + +@system unittest { // From bugzilla 2142 bool f_linenum, f_filename; @@ -1167,7 +1363,7 @@ unittest assert(f_filename); } -unittest +@system unittest { // From bugzilla 6887 string[] p; @@ -1177,7 +1373,7 @@ unittest assert(p[0] == "a"); } -unittest +@system unittest { // From bugzilla 6888 int[string] foo; @@ -1186,7 +1382,7 @@ unittest assert(foo == ["a":1]); } -unittest +@system unittest { // From bugzilla 9583 int opt; @@ -1195,13 +1391,14 @@ unittest assert(args == ["prog", "--a", "--b", "--c"]); } -unittest +@system unittest { string foo, bar; auto args = ["prog", "-thello", "-dbar=baz"]; getopt(args, "t", &foo, "d", &bar); assert(foo == "hello"); assert(bar == "bar=baz"); + // From bugzilla 5762 string a; args = ["prog", "-a-0x12"]; @@ -1210,14 +1407,26 @@ unittest args = ["prog", "--addr=-0x12"]; getopt(args, config.bundling, "a|addr", &a); assert(a == "-0x12"); + // From https://d.puremagic.com/issues/show_bug.cgi?id=11764 args = ["main", "-test"]; bool opt; args.getopt(config.passThrough, "opt", &opt); assert(args == ["main", "-test"]); + + // From https://issues.dlang.org/show_bug.cgi?id=15220 + args = ["main", "-o=str"]; + string o; + args.getopt("o", &o); + assert(o == "str"); + + args = ["main", "-o=str"]; + o = null; + args.getopt(config.bundling, "o", &o); + assert(o == "str"); } -unittest // 5228 +@system unittest // 5228 { import std.exception; import std.conv; @@ -1230,7 +1439,7 @@ unittest // 5228 assertThrown!ConvException(getopt(args, "abc", &abc)); } -unittest // From bugzilla 7693 +@system unittest // From bugzilla 7693 { import std.exception; @@ -1250,7 +1459,7 @@ unittest // From bugzilla 7693 assertNotThrown(getopt(args, "foo", &foo)); } -unittest // same bug as 7693 only for bool +@system unittest // same bug as 7693 only for bool { import std.exception; @@ -1262,7 +1471,7 @@ unittest // same bug as 7693 only for bool assert(foo); } -unittest +@system unittest { bool foo; auto args = ["prog", "--foo"]; @@ -1270,30 +1479,30 @@ unittest assert(foo); } -unittest +@system unittest { bool foo; bool bar; auto args = ["prog", "--foo", "-b"]; - getopt(args, config.caseInsensitive,"foo|f" "Some foo", &foo, + getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", &bar); assert(foo); assert(bar); } -unittest +@system unittest { bool foo; bool bar; auto args = ["prog", "-b", "--foo", "-z"]; - getopt(args, config.caseInsensitive, config.required, "foo|f" "Some foo", + getopt(args, config.caseInsensitive, config.required, "foo|f", "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", &bar, config.passThrough); assert(foo); assert(bar); } -unittest +@system unittest { import std.exception; @@ -1305,7 +1514,7 @@ unittest config.passThrough)); } -unittest +@system unittest { import std.exception; @@ -1319,7 +1528,7 @@ unittest assert(!bar); } -unittest +@system unittest { bool foo; auto args = ["prog", "-f"]; @@ -1328,7 +1537,7 @@ unittest assert(!r.helpWanted); } -unittest // implicit help option without config.passThrough +@safe unittest // implicit help option without config.passThrough { string[] args = ["program", "--help"]; auto r = getopt(args); @@ -1336,7 +1545,7 @@ unittest // implicit help option without config.passThrough } // Issue 13316 - std.getopt: implicit help option breaks the next argument -unittest +@system unittest { string[] args = ["program", "--help", "--", "something"]; getopt(args); @@ -1353,7 +1562,7 @@ unittest } // Issue 13317 - std.getopt: endOfOptions broken when it doesn't look like an option -unittest +@system unittest { auto endOfOptionsBackup = endOfOptions; scope(exit) endOfOptions = endOfOptionsBackup; @@ -1365,19 +1574,21 @@ unittest assert(args == ["program", "--option"]); } -/** This function prints the passed $(D Option) and text in an aligned manner. +/** This function prints the passed $(D Option)s and text in an aligned manner on $(D stdout). -The passed text will be printed first, followed by a newline. Than the short +The passed text will be printed first, followed by a newline, then the short and long version of every option will be printed. The short and long version -will be aligned to the longest option of every $(D Option) passed. If a help -message is present it will be printed after the long version of the -$(D Option). +will be aligned to the longest option of every $(D Option) passed. If the option +is required, then "Required:" will be printed after the long version of the +$(D Option). If a help message is present it will be printed next. The format is +illustrated by this code: ------------ -foreach(it; opt) +foreach (it; opt) { - writefln("%*s %*s %s", lengthOfLongestShortOption, it.optShort, - lengthOfLongestLongOption, it.optLong, it.help); + writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort, + lengthOfLongestLongOption, it.optLong, + it.required ? " Required: " : " ", it.help); } ------------ @@ -1393,18 +1604,18 @@ void defaultGetoptPrinter(string text, Option[] opt) } /** This function writes the passed text and $(D Option) into an output range -in the manner, described in the documentation of function +in the manner described in the documentation of function $(D defaultGetoptPrinter). Params: output = The output range used to write the help information. - text = The text to printed at the beginning of the help output. + text = The text to print at the beginning of the help output. opt = The $(D Option) extracted from the $(D getopt) parameter. */ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) { import std.format : formattedWrite; - import std.algorithm : min, max; + import std.algorithm.comparison : min, max; output.formattedWrite("%s\n", text); @@ -1418,8 +1629,6 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) hasRequired = hasRequired || it.required; } - size_t argLength = ls + ll + 2; - string re = " Required: "; foreach (it; opt) @@ -1429,7 +1638,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) } } -unittest +@system unittest { import std.conv; @@ -1458,7 +1667,7 @@ unittest assert(wanted == helpMsg); } -unittest +@system unittest { import std.conv; import std.string; @@ -1483,6 +1692,123 @@ unittest assert(helpMsg.indexOf("Help") != -1); string wanted = "Some Text\n-f --foo Required: Help\n-h --help " - " This help information.\n"; + ~ " This help information.\n"; assert(wanted == helpMsg, helpMsg ~ wanted); } + +@system unittest // Issue 14724 +{ + bool a; + auto args = ["prog", "--help"]; + GetoptResult rslt; + try + { + rslt = getopt(args, config.required, "foo|f", "bool a", &a); + } + catch (Exception e) + { + enum errorMsg = "If the request for help was passed required options" ~ + "must not be set."; + assert(false, errorMsg); + } + + assert(rslt.helpWanted); +} + +// throw on duplicate options +@system unittest +{ + import core.exception; + auto args = ["prog", "--abc", "1"]; + int abc, def; + assertThrown!AssertError(getopt(args, "abc", &abc, "abc", &abc)); + assertThrown!AssertError(getopt(args, "abc|a", &abc, "def|a", &def)); + assertNotThrown!AssertError(getopt(args, "abc", &abc, "def", &def)); +} + +@system unittest // Issue 17327 repeated option use +{ + long num = 0; + + string[] args = ["program", "--num", "3"]; + getopt(args, "n|num", &num); + assert(num == 3); + + args = ["program", "--num", "3", "--num", "5"]; + getopt(args, "n|num", &num); + assert(num == 5); + + args = ["program", "--n", "3", "--num", "5", "-n", "-7"]; + getopt(args, "n|num", &num); + assert(num == -7); + + void add1() { num++; } + void add2(string option) { num += 2; } + void addN(string option, string value) + { + import std.conv : to; + num += value.to!long; + } + + num = 0; + args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"]; + getopt(args, + "add1", "Add 1 to num", &add1, + "add2", "Add 2 to num", &add2, + "add", "Add N to num", &addN,); + assert(num == 21); + + bool flag = false; + args = ["program", "--flag"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(flag); + + flag = false; + args = ["program", "-f", "-f"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(flag); + + flag = false; + args = ["program", "--flag=true", "--flag=false"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(!flag); + + flag = false; + args = ["program", "--flag=true", "--flag=false", "-f"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(flag); +} + +@safe unittest // Delegates as callbacks +{ + alias TwoArgOptionHandler = void delegate(string option, string value) @safe; + + TwoArgOptionHandler makeAddNHandler(ref long dest) + { + void addN(ref long dest, string n) + { + import std.conv : to; + dest += n.to!long; + } + + return (option, value) => addN(dest, value); + } + + long x = 0; + long y = 0; + + string[] args = + ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10", + "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"]; + + getopt(args, + "x-plus-1", "Add one to x", delegate void() { x += 1; }, + "x-plus-5", "Add five to x", delegate void(string option) { x += 5; }, + "x-plus-n", "Add NUM to x", makeAddNHandler(x), + "y-plus-7", "Add seven to y", delegate void() { y += 7; }, + "y-plus-3", "Add three to y", delegate void(string option) { y += 3; }, + "y-plus-n", "Add NUM to x", makeAddNHandler(y),); + + assert(x == 17); + assert(y == 50); +} diff --git a/std/internal/cstring.d b/std/internal/cstring.d index 344210a402c..626ea9a0f7f 100644 --- a/std/internal/cstring.d +++ b/std/internal/cstring.d @@ -1,30 +1,9 @@ -/** +/** Helper functions for working with $(I C strings). This module is intended to provide fast, safe and garbage free way to work with $(I C strings). -Examples: ---- -version(Posix): - -import core.stdc.stdlib: free; -import core.sys.posix.stdlib: setenv; -import std.exception: enforce; - -void setEnvironment(in char[] name, in char[] value) -{ enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); } ---- ---- -version(Windows): - -import core.sys.windows.windows: SetEnvironmentVariableW; -import std.exception: enforce; - -void setEnvironment(in char[] name, in char[] value) -{ enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); } ---- - Copyright: Denis Shelomovskij 2013-2014 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). @@ -36,28 +15,59 @@ COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, $(D core.$1.$2)) */ module std.internal.cstring; +/// +@safe unittest +{ + version(Posix) + { + import core.stdc.stdlib : free; + import core.sys.posix.stdlib : setenv; + import std.exception : enforce; + + void setEnvironment(in char[] name, in char[] value) + { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); } + } + + version(Windows) + { + import core.sys.windows.windows : SetEnvironmentVariableW; + import std.exception : enforce; + + void setEnvironment(in char[] name, in char[] value) + { enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); } + } +} import std.traits; +import std.range; version(unittest) -@property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc -if(isSomeChar!C) +@property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc @trusted +if (isSomeChar!C) in { assert(cstr); } body { size_t length = 0; - while(cstr[length]) + while (cstr[length]) ++length; return cstr[0 .. length]; } /** -Creates temporary $(I C string) with copy of passed text. +Creates temporary 0-terminated $(I C string) with copy of passed text. -Returned object is implicitly convertible to $(D const To*) and +Params: + To = character type of returned C string + str = string or input range to be converted + +Returns: + +The value returned is implicitly convertible to $(D const To*) and has two properties: $(D ptr) to access $(I C string) as $(D const To*) and $(D buffPtr) to access it as $(D To*). +The value returned can be indexed by [] to access it as an array. + The temporary $(I C string) is valid unless returned object is destroyed. Thus if returned object is assigned to a variable the temporary is valid unless the variable goes out of scope. If returned object isn't @@ -75,62 +85,142 @@ $(D strlen(str.tempCString()))). Incorrect usage of this function may lead to memory corruption. See $(RED WARNING) in $(B Examples) section. */ -auto tempCString(To = char, From)(in From[] str) nothrow @nogc -if(isSomeChar!To && isSomeChar!From) + +auto tempCString(To = char, From)(From str) +if (isSomeChar!To && (isInputRange!From || isSomeString!From) && + isSomeChar!(ElementEncodingType!From)) { - import core.checkedint : addu; - import core.exception : onOutOfMemoryError; - enum useStack = cast(To*) -1; + alias CF = Unqual!(ElementEncodingType!From); + + enum To* useStack = () @trusted { return cast(To*) size_t.max; }(); static struct Res { + @trusted: nothrow @nogc: @disable this(); @disable this(this); alias ptr this; - @property inout(To)* buffPtr() inout @safe pure - { return _ptr == useStack ? _buff.ptr : _ptr; } + @property inout(To)* buffPtr() inout pure + { + return _ptr == useStack ? _buff.ptr : _ptr; + } + + @property const(To)* ptr() const pure + { + return buffPtr; + } - @property const(To)* ptr() const @safe pure - { return buffPtr; } + const(To)[] opIndex() const pure + { + return buffPtr[0 .. _length]; + } ~this() - { if(_ptr != useStack) rawFree(_ptr); } + { + if (_ptr != useStack) + { + import core.stdc.stdlib : free; + free(_ptr); + } + } private: To* _ptr; - To[256] _buff; - } + size_t _length; // length of the string + version (unittest) + { + enum buffLength = 16 / To.sizeof; // smaller size to trigger reallocations + } + else + { + enum buffLength = 256 / To.sizeof; // production size + } - // TODO: Don't stack allocate uninitialized array to - // not confuse unprecise GC. + To[buffLength] _buff; // the 'small string optimization' - Res res = void; - if(!str.ptr) - { - res._ptr = null; - return res; + static Res trustedVoidInit() { Res res = void; return res; } } + Res res = Res.trustedVoidInit(); // expensive to fill _buff[] + // Note: res._ptr can't point to res._buff as structs are movable. - bool overflow = false; - const totalCount = addu(maxLength!To(str), 1, overflow); - if(overflow) - onOutOfMemoryError(); - const needAllocate = totalCount > res._buff.length; - To[] arr = copyEncoded(str, needAllocate ? - allocate!To(totalCount)[0 .. $ - 1] : res._buff[0 .. totalCount - 1]); - *(arr.ptr + arr.length) = '\0'; - res._ptr = needAllocate ? arr.ptr : useStack; + To[] p; + bool p_is_onstack = true; + size_t i; + + static To[] trustedRealloc(To[] buf, size_t i, To[] res, size_t strLength, bool res_is_onstack) + @trusted @nogc nothrow + { + pragma(inline, false); // because it's rarely called + + import core.exception : onOutOfMemoryError; + import core.stdc.string : memcpy; + import core.stdc.stdlib : malloc, realloc; + + if (res_is_onstack) + { + size_t newlen = res.length * 3 / 2; + if (newlen <= strLength) + newlen = strLength + 1; // +1 for terminating 0 + auto ptr = cast(To*) malloc(newlen * To.sizeof); + if (!ptr) + onOutOfMemoryError(); + memcpy(ptr, res.ptr, i * To.sizeof); + return ptr[0 .. newlen]; + } + else + { + if (buf.length >= size_t.max / (2 * To.sizeof)) + onOutOfMemoryError(); + const newlen = buf.length * 3 / 2; + auto ptr = cast(To*) realloc(buf.ptr, newlen * To.sizeof); + if (!ptr) + onOutOfMemoryError(); + return ptr[0 .. newlen]; + } + } + + size_t strLength; + static if (hasLength!From) + { + strLength = str.length; + } + import std.utf : byUTF; + static if (isSomeString!From) + { + auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF + if (r is null) // Bugzilla 14980 + { + res._ptr = null; + return res; + } + } + else + alias r = str; + To[] q = res._buff; + foreach (const c; byUTF!(Unqual!To)(r)) + { + if (i + 1 == q.length) + { + p = trustedRealloc(p, i, res._buff, strLength, p_is_onstack); + p_is_onstack = false; + q = p; + } + q[i++] = c; + } + q[i] = 0; + res._length = i; + res._ptr = p_is_onstack ? useStack : &p[0]; return res; } /// -nothrow @nogc unittest +nothrow @nogc @system unittest { import core.stdc.string; @@ -151,183 +241,27 @@ nothrow @nogc unittest // both primary expressions are ended. } -nothrow @nogc unittest +@safe nothrow @nogc unittest { assert("abc".tempCString().asArray == "abc"); assert("abc"d.tempCString().ptr.asArray == "abc"); assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w); -} -// Test for Issue 13367: ensure there is no memory corruption -nothrow @nogc unittest -{ - @property str(C)() { C[300] arr = 'a'; return arr; } - assert(str!char.tempCString!wchar().asArray == str!wchar); - assert(str!char.tempCString!dchar().asArray == str!dchar); + import std.utf : byChar, byWchar; + char[300] abc = 'a'; + assert(tempCString(abc[].byChar).buffPtr.asArray == abc); + assert(tempCString(abc[].byWchar).buffPtr.asArray == abc); + assert(tempCString(abc[].byChar)[] == abc); } -version(Windows) - alias tempCStringW = tempCString!(wchar, char); - - -private: - - -// Helper UTF functions. -// ---------------------------------------------------------------------------------------------------- - -/** -Returns maximum possible length of string conversion -to another Unicode Transformation Format result. -*/ -size_t maxLength(To, From)(in size_t length) pure nothrow @nogc -if(isSomeChar!To && isSomeChar!From) -{ - static if (To.sizeof >= From.sizeof) - enum k = 1; // worst case: every code unit represents a character - else static if (To.sizeof == 1 && From.sizeof == 2) - enum k = 3; // worst case: every wchar in top of BMP - else static if (To.sizeof == 1 && From.sizeof == 4) - enum k = 4; // worst case: every dchar not in BMP - else static if (To.sizeof == 2 && From.sizeof == 4) - enum k = 2; // worst case: every dchar not in BMP - else - static assert(0); - return length * k; -} - -/// ditto -size_t maxLength(To, From)(in From[] str) pure nothrow @nogc -{ return maxLength!(To, From)(str.length); } - -pure nothrow @nogc unittest -{ - assert(maxLength!char("abc") == 3); - assert(maxLength!dchar("abc") == 3); - assert(maxLength!char("abc"w) == 9); - assert(maxLength!char("abc"d) == 12); - assert(maxLength!wchar("abc"d) == 6); -} - - -/** -Copies text from $(D source) to $(D buff) performing conversion -to different Unicode Transformation Format if needed. - -$(D buff) must be large enough to hold the result. - -Returns: -Slice of the provided buffer $(D buff) with the copy of $(D source). -*/ -To[] copyEncoded(To, From)(in From[] source, To[] buff) @trusted nothrow @nogc -if(isSomeChar!To && isSomeChar!From) -{ - static if(is(Unqual!To == Unqual!From)) - { - return buff[0 .. source.length] = source[]; - } - else - { - import std.utf : byChar, byWchar, byDchar; - alias GenericTuple(Args...) = Args; - alias byFunc = GenericTuple!(byChar, byWchar, null, byDchar)[To.sizeof - 1]; - - To* ptr = buff.ptr; - const To* last = ptr + buff.length; - foreach(const c; byFunc(source)) - { - assert(ptr != last); - *ptr++ = c; - } - return buff[0 .. ptr - buff.ptr]; - } -} - -/// -pure nothrow @nogc unittest +// Bugzilla 14980 +nothrow @nogc @safe unittest { - const str = "abc-ЭЮЯ"; - wchar[100] wsbuff; - assert(copyEncoded(str, wsbuff) == "abc-ЭЮЯ"w); + const(char[]) str = null; + auto res = tempCString(str); + const char* ptr = res; + assert(ptr is null); } -pure nothrow @nogc unittest -{ - wchar[100] wsbuff; - assert(copyEncoded("abc-ЭЮЯ"w, wsbuff) == "abc-ЭЮЯ"w); -} - -pure unittest -{ - import std.range; - import std.utf : toUTF16, toUTF32; - - const str = "abc-ЭЮЯ"; - char[100] sbuff; - - { - wchar[100] wsbuff; - const strW = toUTF16(str); - assert(copyEncoded(str, wsbuff[0 .. strW.length]) == strW); - assert(copyEncoded(strW, sbuff[0 .. str.length]) == str); - } - { - dchar[100] dsbuff; - const strD = toUTF32(str); - assert(copyEncoded(str, dsbuff[0 .. walkLength(str)]) == strD); - assert(copyEncoded(strD, sbuff[0 .. str.length]) == str); - } -} - - -// Helper functions for memory allocation & freeing. -// ---------------------------------------------------------------------------------------------------- - -// WARNING: Alignment is implementation defined in C so the value '4' -// relies on undocumented but common behaviour. -// FIXME: This value should be checked and adjusted on every C runtime. -enum mallocAlignment = 4; - -// NOTE: `allocate`/`rawFree` simply wraps C allocation functions for now -// but it may be changed in future so one shouldn't rely on that. - -T[] allocate(T)(in size_t count) nothrow @nogc -if(T.alignof <= mallocAlignment) -in { assert(count); } -body -{ - import core.exception : onOutOfMemoryError; - import core.checkedint: mulu; - - bool overflow = false; - const buffBytes = mulu(T.sizeof, count, overflow); - if(overflow) - onOutOfMemoryError(); - - auto ptr = cast(T*) tryRawAllocate(buffBytes); - if(!ptr) - onOutOfMemoryError(); - - return ptr[0 .. count]; -} - -void* tryRawAllocate(in size_t count) nothrow @nogc -in { assert(count); } -body -{ - import core.stdc.stdlib: malloc; - // Workaround snn @@@BUG11646@@@ - version(DigitalMars) version(Win32) - if(count > 0xD5550000) return null; - - // FIXME: `malloc` must be checked on every C runtime for - // possible bugs and workarounded if necessary. - - return malloc(count); -} - -void rawFree(void* ptr) nothrow @nogc -{ - import core.stdc.stdlib: free; - free(ptr); -} +version(Windows) + alias tempCStringW = tempCString!(wchar, const(char)[]); diff --git a/std/internal/digest/sha_SSSE3.d b/std/internal/digest/sha_SSSE3.d index 36b2414fc73..547b208bddf 100644 --- a/std/internal/digest/sha_SSSE3.d +++ b/std/internal/digest/sha_SSSE3.d @@ -15,14 +15,14 @@ */ module std.internal.digest.sha_SSSE3; -version(D_PIC) +version(D_InlineAsm_X86) { - // Do not use (Bug9378). -} -else version(D_InlineAsm_X86) -{ - private version = USE_SSSE3; - private version = _32Bit; + version (D_PIC) {} // Bugzilla 9378 + else + { + private version = USE_SSSE3; + private version = _32Bit; + } } else version(D_InlineAsm_X86_64) { @@ -108,6 +108,7 @@ version(USE_SSSE3) private immutable string SP = "RSP"; private immutable string BUFFER_PTR = "R9"; private immutable string STATE_PTR = "R8"; + private immutable string CONSTANTS_PTR = "R10"; // Registers for temporary results (XMM10 and XMM11 are also used temporary) private immutable string W_TMP = "XMM8"; @@ -120,15 +121,11 @@ version(USE_SSSE3) private immutable string X_CONSTANT = "XMM13"; } - /* The control words for the byte shuffle instruction. */ - align(16) private immutable uint[4] bswap_shufb_ctl = - [ - 0x0001_0203, 0x0405_0607, 0x0809_0a0b, 0x0c0d_0e0f - ]; - - /* The round constants. */ - align(16) private immutable uint[16] constants = + /* The control words for the byte shuffle instruction and the round constants. */ + align(16) public immutable uint[20] constants = [ + // The control words for the byte shuffle instruction. + 0x0001_0203, 0x0405_0607, 0x0809_0a0b, 0x0c0d_0e0f, // Constants for round 0-19 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999, // Constants for round 20-39 @@ -142,17 +139,32 @@ version(USE_SSSE3) /** Simple version to produce numbers < 100 as string. */ private nothrow pure string to_string(uint i) { + if (i < 10) + return "0123456789"[i .. i + 1]; + assert(i < 100); - string s; - if (i >= 10) - s ~= cast(char)('0' + (i / 10) % 10); - return s ~ cast(char)('0' + i % 10); + char[2] s; + s[0] = cast(char)(i / 10 + '0'); + s[1] = cast(char)(i % 10 + '0'); + return s.idup; + } + + /** Returns the reference to the byte shuffle control word. */ + private nothrow pure string bswap_shufb_ctl() + { + version (_64Bit) + return "["~CONSTANTS_PTR~"]"; + else + return "[constants]"; } /** Returns the reference to constant used in round i. */ private nothrow pure string constant(uint i) { - return "[constants + 16*"~to_string(i/20)~"]"; + version (_64Bit) + return "16 + 16*"~to_string(i/20)~"["~CONSTANTS_PTR~"]"; + else + return "[constants + 16 + 16*"~to_string(i/20)~"]"; } /** Returns the XMM register number used in round i */ @@ -161,13 +173,13 @@ version(USE_SSSE3) return (i/4)&7; } - /** Returns reference to storage of vector W[i..i+4]. */ + /** Returns reference to storage of vector W[i .. i+4]. */ private nothrow pure string WiV(uint i) { return "["~SP~" + WI_PTR + "~to_string((i/4)&7)~"*16]"; } - /** Returns reference to storage of vector (W + K)[i..i+4]. */ + /** Returns reference to storage of vector (W + K)[i .. i+4]. */ private nothrow pure string WiKiV(uint i) { return "["~SP~" + WI_PLUS_KI_PTR + "~to_string((i/4)&3)~"*16]"; @@ -212,20 +224,20 @@ version(USE_SSSE3) */ private nothrow pure string[] weave(string[] seq1, string[] seq2, uint dist = 1) { - import std.algorithm : min; - string[] res = []; auto i1 = 0, i2 = 0; while (i1 < seq1.length || i2 < seq2.length) { if (i2 < seq2.length) { - res ~= seq2[i2..i2+1]; + res ~= seq2[i2 .. i2+1]; i2 += 1; } if (i1 < seq1.length) { - res ~= seq1[i1..std.algorithm.min(i1+dist,$)]; + import std.algorithm.comparison : min; + + res ~= seq1[i1 .. min(i1+dist, $)]; i1 += dist; } } @@ -301,9 +313,9 @@ version(USE_SSSE3) { if (i == 0) { - return swt3264(["movdqa "~X_SHUFFLECTL~",[bswap_shufb_ctl]", + return swt3264(["movdqa "~X_SHUFFLECTL~","~bswap_shufb_ctl(), "movdqa "~X_CONSTANT~","~constant(i)], - ["movdqa "~X_SHUFFLECTL~",[bswap_shufb_ctl]", + ["movdqa "~X_SHUFFLECTL~","~bswap_shufb_ctl(), "movdqa "~X_CONSTANT~","~constant(i)]); } version(_64Bit) @@ -586,8 +598,9 @@ version(USE_SSSE3) { /* * Parameters: - * RSI contains pointer to state - * RDI contains pointer to input buffer + * RDX contains pointer to state + * RSI contains pointer to input buffer + * RDI contains pointer to constants * * Stack layout as follows: * +----------------+ @@ -607,8 +620,9 @@ version(USE_SSSE3) "push RBP", "push RBX", // Save parameters - "mov "~STATE_PTR~", RSI", //pointer to state - "mov "~BUFFER_PTR~", RDI", //pointer to buffer + "mov "~STATE_PTR~", RDX", //pointer to state + "mov "~BUFFER_PTR~", RSI", //pointer to buffer + "mov "~CONSTANTS_PTR~", RDI", //pointer to constants to avoid absolute addressing // Align stack "sub RSP, 4*16+8", ]; @@ -640,10 +654,17 @@ version(USE_SSSE3) } } + // constants as extra argument for PIC, see Bugzilla 9378 + import std.meta : AliasSeq; + version (_64Bit) + alias ExtraArgs = AliasSeq!(typeof(&constants)); + else + alias ExtraArgs = AliasSeq!(); + /** * */ - public void transformSSSE3(uint[5]* state, const(ubyte[64])* buffer) pure nothrow @nogc + public void transformSSSE3(uint[5]* state, const(ubyte[64])* buffer, ExtraArgs) pure nothrow @nogc { mixin(wrap(["naked;"] ~ prologue())); // Precalc first 4*16=64 bytes diff --git a/std/internal/encodinginit.d b/std/internal/encodinginit.d new file mode 100644 index 00000000000..837e4649287 --- /dev/null +++ b/std/internal/encodinginit.d @@ -0,0 +1,19 @@ +// Written in the D programming language. + +/++ + The purpose of this module is to perform static construction away from the + normal modules to eliminate cyclic construction errors. + + Copyright: Copyright 2011 - 2016 + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Martin Nowak, Steven Schveighoffer + Source: $(PHOBOSSRC std/internal/_encodinginit.d) + +/ +module std.internal.encodinginit; + +extern(C) void std_encoding_shared_static_this(); + +shared static this() +{ + std_encoding_shared_static_this(); +} diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index c4f646dd9b5..7591a7ea8ac 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -47,18 +47,19 @@ alias multibyteSub = multibyteAddSub!('-'); private import core.cpuid; -private import std.traits : Unqual; +private import std.traits; +private import std.range.primitives; +public import std.ascii : LetterCase; shared static this() { CACHELIMIT = core.cpuid.datacache[0].size*1024/2; - FASTDIVLIMIT = 100; } private: // Limits for when to switch between algorithms. immutable size_t CACHELIMIT; // Half the size of the data cache. -immutable size_t FASTDIVLIMIT; // crossover to recursive division +enum size_t FASTDIVLIMIT = 100; // crossover to recursive division // These constants are used by shift operations @@ -75,9 +76,10 @@ else static if (BigDigit.sizeof == long.sizeof) else static assert(0, "Unsupported BigDigit size"); private import std.exception : assumeUnique; -private import std.traits:isIntegral; +private import std.traits : isIntegral; enum BigDigitBits = BigDigit.sizeof*8; -template maxBigDigits(T) if (isIntegral!T) +template maxBigDigits(T) +if (isIntegral!T) { enum maxBigDigits = (T.sizeof+BigDigit.sizeof-1)/BigDigit.sizeof; } @@ -98,11 +100,14 @@ private: { assert( data.length >= 1 && (data.length == 1 || data[$-1] != 0 )); } + immutable(BigDigit) [] data = ZERO; + this(immutable(BigDigit) [] x) pure nothrow @nogc @safe { data = x; } + package(std) // used from: std.bigint this(T)(T x) pure nothrow @safe if (isIntegral!T) { opAssign(x); @@ -137,13 +142,13 @@ public: } } - // The value at (cast(ulong[])data)[n] + // The value at (cast(ulong[]) data)[n] ulong peekUlong(int n) pure nothrow const @safe @nogc { static if (BigDigit.sizeof == int.sizeof) { if (data.length == n*2 + 1) return data[n*2]; - return data[n*2] + ((cast(ulong)data[n*2 + 1]) << 32 ); + return data[n*2] + ((cast(ulong) data[n*2 + 1]) << 32 ); } else static if (BigDigit.sizeof == long.sizeof) { @@ -158,8 +163,8 @@ public: } else { - ulong x = data[n >> 1]; - return (n & 1) ? cast(uint)(x >> 32) : cast(uint)x; + immutable x = data[n >> 1]; + return (n & 1) ? cast(uint)(x >> 32) : cast(uint) x; } } public: @@ -208,7 +213,7 @@ public: } /// - int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe if(is (Tulong == ulong)) + int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe if (is (Tulong == ulong)) { if (data.length > maxBigDigits!Tulong) return 1; @@ -220,7 +225,7 @@ public: if (data.length >= i+1) { // Since ZERO is [0], so we cannot simply return 1 here, as - // data[i] would be 0 for i==0 in that case. + // data[i] would be 0 for i == 0 in that case. return (data[i] > 0) ? 1 : 0; } else @@ -245,9 +250,9 @@ public: return false; uint ylo = cast(uint)(y & 0xFFFF_FFFF); uint yhi = cast(uint)(y >> 32); - if (data.length==2 && data[1]!=yhi) + if (data.length == 2 && data[1]!=yhi) return false; - if (data.length==1 && yhi!=0) + if (data.length == 1 && yhi != 0) return false; return (data[0] == ylo); } @@ -265,7 +270,7 @@ public: // the extra bytes are added to the start of the string char [] toDecimalString(int frontExtraBytes) const pure nothrow { - auto predictlength = 20+20*(data.length/2); // just over 19 + immutable predictlength = 20+20*(data.length/2); // just over 19 char [] buff = new char[frontExtraBytes + predictlength]; ptrdiff_t sofar = biguintToDecimal(buff, data.dup); return buff[sofar-frontExtraBytes..$]; @@ -279,7 +284,8 @@ public: * Separator characters do not contribute to the minPadding. */ char [] toHexString(int frontExtraBytes, char separator = 0, - int minPadding=0, char padChar = '0') const pure nothrow @safe + int minPadding=0, char padChar = '0', + LetterCase letterCase = LetterCase.upper) const pure nothrow @safe { // Calculate number of extra padding bytes size_t extraPad = (minPadding > data.length * 2 * BigDigit.sizeof) @@ -290,10 +296,10 @@ public: // Calculate number of separator bytes size_t mainSeparatorBytes = separator ? (lenBytes / 8) - 1 : 0; - size_t totalSeparatorBytes = separator ? ((extraPad + lenBytes + 7) / 8) - 1: 0; + immutable totalSeparatorBytes = separator ? ((extraPad + lenBytes + 7) / 8) - 1: 0; char [] buff = new char[lenBytes + extraPad + totalSeparatorBytes + frontExtraBytes]; - biguintToHex(buff[$ - lenBytes - mainSeparatorBytes .. $], data, separator); + biguintToHex(buff[$ - lenBytes - mainSeparatorBytes .. $], data, separator, letterCase); if (extraPad > 0) { if (separator) @@ -340,36 +346,65 @@ public: return buff[z-frontExtraBytes..$]; } + /** + * Convert to an octal string. + */ + char[] toOctalString() const + { + auto predictLength = 1 + data.length*BigDigitBits / 3; + char[] buff = new char[predictLength]; + size_t firstNonZero = biguintToOctal(buff, data); + return buff[firstNonZero .. $]; + } + // return false if invalid character found - bool fromHexString(const(char)[] s) pure nothrow @safe + bool fromHexString(Range)(Range s) if ( + isBidirectionalRange!Range && isSomeChar!(ElementType!Range)) { + import std.range : walkLength; + //Strip leading zeros - int firstNonZero = 0; - while ((firstNonZero < s.length - 1) && - (s[firstNonZero]=='0' || s[firstNonZero]=='_')) - { - ++firstNonZero; - } - auto len = (s.length - firstNonZero + 15)/4; - auto tmp = new BigDigit[len+1]; - uint part = 0; - uint sofar = 0; - uint partcount = 0; - assert(s.length>0); - for (ptrdiff_t i = s.length - 1; i>=firstNonZero; --i) - { - assert(i>=0); - char c = s[i]; - if (s[i]=='_') continue; - uint x = (c>='0' && c<='9') ? c - '0' - : (c>='A' && c<='F') ? c - 'A' + 10 - : (c>='a' && c<='f') ? c - 'a' + 10 - : 100; - if (x==100) return false; + while (!s.empty && s.front == '0') + s.popFront; + + if (s.empty) + { + data = ZERO; + return true; + } + + immutable len = (s.save.walkLength + 15) / 4; + auto tmp = new BigDigit[len + 1]; + uint part, sofar, partcount; + + foreach_reverse (character; s) + { + if (character == '_') + continue; + + uint x; + if (character >= '0' && character <= '9') + { + x = character - '0'; + } + else if (character >= 'A' && character <= 'F') + { + x = character - 'A' + 10; + } + else if (character >= 'a' && character <= 'f') + { + x = character - 'a' + 10; + } + else + { + return false; + } + part >>= 4; - part |= (x<<(32-4)); + part |= (x << (32 - 4)); ++partcount; - if (partcount==8) + + if (partcount == 8) { tmp[sofar] = part; ++sofar; @@ -392,26 +427,26 @@ public: } // return true if OK; false if erroneous characters found - // FIXME: actually throws `ConvException` on error. - bool fromDecimalString(const(char)[] s) pure @trusted + bool fromDecimalString(Range)(Range s) if ( + isForwardRange!Range && isSomeChar!(ElementType!Range)) { - //Strip leading zeros - int firstNonZero = 0; - while ((firstNonZero < s.length) && - (s[firstNonZero]=='0' || s[firstNonZero]=='_')) + import std.range : walkLength; + + while (!s.empty && s.front == '0') { - ++firstNonZero; + s.popFront; } - if (firstNonZero == s.length && s.length >= 1) + + if (s.empty) { data = ZERO; return true; } - auto predictlength = (18*2 + 2*(s.length-firstNonZero)) / 19; - auto tmp = new BigDigit[predictlength]; - uint hi = biguintFromDecimal(tmp, s[firstNonZero..$]); - tmp.length = hi; + auto predict_length = (18 * 2 + 2 * s.save.walkLength) / 19; + auto tmp = new BigDigit[predict_length]; + + tmp.length = biguintFromDecimal(tmp, s); data = trustedAssumeUnique(tmp); return true; @@ -425,10 +460,10 @@ public: BigUint opShr(Tulong)(Tulong y) pure nothrow const if (is (Tulong == ulong)) { assert(y>0); - uint bits = cast(uint)y & BIGDIGITSHIFTMASK; - if ((y>>LG2BIGDIGITBITS) >= data.length) return BigUint(ZERO); + uint bits = cast(uint) y & BIGDIGITSHIFTMASK; + if ((y >> LG2BIGDIGITBITS) >= data.length) return BigUint(ZERO); uint words = cast(uint)(y >> LG2BIGDIGITBITS); - if (bits==0) + if (bits == 0) { return BigUint(data[words..$]); } @@ -449,20 +484,20 @@ public: { assert(y>0); if (isZero()) return this; - uint bits = cast(uint)y & BIGDIGITSHIFTMASK; - assert ((y>>LG2BIGDIGITBITS) < cast(ulong)(uint.max)); + uint bits = cast(uint) y & BIGDIGITSHIFTMASK; + assert((y >> LG2BIGDIGITBITS) < cast(ulong)(uint.max)); uint words = cast(uint)(y >> LG2BIGDIGITBITS); BigDigit [] result = new BigDigit[data.length + words+1]; - result[0..words] = 0; - if (bits==0) + result[0 .. words] = 0; + if (bits == 0) { - result[words..words+data.length] = data[]; - return BigUint(trustedAssumeUnique(result[0..words+data.length])); + result[words .. words+data.length] = data[]; + return BigUint(trustedAssumeUnique(result[0 .. words+data.length])); } else { - uint c = multibyteShl(result[words..words+data.length], data, bits); - if (c==0) return BigUint(trustedAssumeUnique(result[0..words+data.length])); + immutable c = multibyteShl(result[words .. words+data.length], data, bits); + if (c == 0) return BigUint(trustedAssumeUnique(result[0 .. words+data.length])); result[$-1] = c; return BigUint(trustedAssumeUnique(result)); } @@ -484,7 +519,7 @@ public: { // could change sign! ulong xx = x.data[0]; if (x.data.length > 1) - xx += (cast(ulong)x.data[1]) << 32; + xx += (cast(ulong) x.data[1]) << 32; ulong d; if (xx <= y) { @@ -503,7 +538,7 @@ public: } if (d > uint.max) { - r.data = [cast(uint)(d & 0xFFFF_FFFF), cast(uint)(d>>32)]; + r.data = [cast(uint)(d & 0xFFFF_FFFF), cast(uint)(d >> 32)]; } else { @@ -546,14 +581,14 @@ public: // y must not be zero. static BigUint mulInt(T = ulong)(BigUint x, T y) pure nothrow { - if (y==0 || x == 0) return BigUint(ZERO); + if (y == 0 || x == 0) return BigUint(ZERO); uint hi = cast(uint)(y >>> 32); uint lo = cast(uint)(y & 0xFFFF_FFFF); - uint [] result = new BigDigit[x.data.length+1+(hi!=0)]; - result[x.data.length] = multibyteMul(result[0..x.data.length], x.data, lo, 0); - if (hi!=0) + uint [] result = new BigDigit[x.data.length+1+(hi != 0)]; + result[x.data.length] = multibyteMul(result[0 .. x.data.length], x.data, lo, 0); + if (hi != 0) { - result[x.data.length+1] = multibyteMulAdd!('+')(result[1..x.data.length+1], + result[x.data.length+1] = multibyteMulAdd!('+')(result[1 .. x.data.length+1], x.data, hi, 0); } return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); @@ -563,7 +598,7 @@ public: */ static BigUint mul(BigUint x, BigUint y) pure nothrow { - if (y==0 || x == 0) + if (y == 0 || x == 0) return BigUint(ZERO); auto len = x.data.length + y.data.length; BigDigit [] result = new BigDigit[len]; @@ -591,10 +626,10 @@ public: uint [] result = new BigDigit[x.data.length]; if ((y&(-y))==y) { - assert(y!=0, "BigUint division by zero"); + assert(y != 0, "BigUint division by zero"); // perfect power of 2 uint b = 0; - for (;y!=1; y>>=1) + for (;y != 1; y>>=1) { ++b; } @@ -603,7 +638,7 @@ public: else { result[] = x.data[]; - uint rem = multibyteDivAssign(result, y, 0); + cast(void) multibyteDivAssign(result, y, 0); } return BigUint(removeLeadingZeros(trustedAssumeUnique(result))); } @@ -612,7 +647,7 @@ public: if ( is(Unqual!T == ulong) ) { if (y <= uint.max) - return divInt!uint(x, cast(uint)y); + return divInt!uint(x, cast(uint) y); if (x.data.length < 2) return BigUint(ZERO); uint hi = cast(uint)(y >>> 32); @@ -626,8 +661,9 @@ public: // return x % y static uint modInt(T)(BigUint x, T y_) pure if ( is(Unqual!T == uint) ) { + import core.memory : GC; uint y = y_; - assert(y!=0); + assert(y != 0); if ((y&(-y)) == y) { // perfect power of 2 return x.data[0] & (y-1); @@ -637,8 +673,8 @@ public: // horribly inefficient - malloc, copy, & store are unnecessary. uint [] wasteful = new BigDigit[x.data.length]; wasteful[] = x.data[]; - uint rem = multibyteDivAssign(wasteful, y, 0); - delete wasteful; + immutable rem = multibyteDivAssign(wasteful, y, 0); + () @trusted { GC.free(wasteful.ptr); } (); return rem; } } @@ -676,7 +712,7 @@ public: auto d1 = includeSign(x.data, y.uintLength, xSign); auto d2 = includeSign(y.data, x.uintLength, ySign); - foreach (i; 0..d1.length) + foreach (i; 0 .. d1.length) { mixin("d1[i] " ~ op ~ "= d2[i];"); } @@ -700,9 +736,9 @@ public: static BigUint pow(BigUint x, ulong y) pure nothrow { // Deal with the degenerate cases first. - if (y==0) return BigUint(ONE); - if (y==1) return x; - if (x==0 || x==1) return x; + if (y == 0) return BigUint(ONE); + if (y == 1) return x; + if (x == 0 || x == 1) return x; BigUint result; @@ -716,7 +752,7 @@ public: // If true, then x0 is that digit // and the result will be (x0 ^^ y) * (2^^(firstnonzero*y*BigDigitBits)) BigDigit x0 = x.data[firstnonzero]; - assert(x0 !=0); + assert(x0 != 0); // Length of the non-zero portion size_t nonzerolength = x.data.length - firstnonzero; ulong y0; @@ -727,10 +763,10 @@ public: ++evenbits; } - if ((x.data.length- firstnonzero == 2)) + if (x.data.length- firstnonzero == 2) { // Check for a single digit straddling a digit boundary - BigDigit x1 = x.data[firstnonzero+1]; + const BigDigit x1 = x.data[firstnonzero+1]; if ((x1 >> evenbits) == 0) { x0 |= (x1 << (BigDigit.sizeof * 8 - evenbits)); @@ -758,10 +794,10 @@ public: result = 1UL; return result << (evenbits + firstnonzero * 8 * BigDigit.sizeof) * y; } - int p = highestPowerBelowUintMax(x0); + immutable p = highestPowerBelowUintMax(x0); if (y <= p) { // Just do it with pow - result = cast(ulong)intpow(x0, y); + result = cast(ulong) intpow(x0, y); if (evenbits + firstnonzero == 0) return result; return result << (evenbits + firstnonzero * 8 * BigDigit.sizeof) * y; @@ -791,7 +827,7 @@ public: // Note that the divisions must be rounded up. // Estimated length in BigDigits - ulong estimatelength = singledigit + immutable estimatelength = singledigit ? 1 + y0 + ((evenbits*y + BigDigit.sizeof * 8 - 1) / (BigDigit.sizeof *8)) + firstnonzero*y : x.data.length * y; // Imprecise check for overflow. Makes the extreme cases easier to debug @@ -800,19 +836,19 @@ public: assert(0, "Overflow in BigInt.pow"); // The result buffer includes space for all the trailing zeros - BigDigit [] resultBuffer = new BigDigit[cast(size_t)estimatelength]; + BigDigit [] resultBuffer = new BigDigit[cast(size_t) estimatelength]; // Do all the powers of 2! size_t result_start = cast(size_t)( firstnonzero * y + (singledigit ? ((evenbits * y) >> LG2BIGDIGITBITS) : 0)); - resultBuffer[0..result_start] = 0; + resultBuffer[0 .. result_start] = 0; BigDigit [] t1 = resultBuffer[result_start..$]; BigDigit [] r1; if (singledigit) { - r1 = t1[0..1]; + r1 = t1[0 .. 1]; r1[0] = x0; y = y0; } @@ -820,26 +856,25 @@ public: { // It's not worth right shifting by evenbits unless we also shrink the length after each // multiply or squaring operation. That might still be worthwhile for large y. - r1 = t1[0..x.data.length - firstnonzero]; + r1 = t1[0 .. x.data.length - firstnonzero]; r1[0..$] = x.data[firstnonzero..$]; } if (y>1) { // Set r1 = r1 ^^ y. // The secondary buffer only needs space for the multiplication results - BigDigit [] secondaryBuffer = new BigDigit[resultBuffer.length - result_start]; - BigDigit [] t2 = secondaryBuffer; + BigDigit [] t2 = new BigDigit[resultBuffer.length - result_start]; BigDigit [] r2; int shifts = 63; // num bits in a long - while(!(y & 0x8000_0000_0000_0000L)) + while (!(y & 0x8000_0000_0000_0000L)) { y <<= 1; --shifts; } y <<=1; - while(y!=0) + while (y != 0) { // For each bit of y: Set r1 = r1 * r1 // If the bit is 1, set r1 = r1 * x @@ -880,9 +915,9 @@ public: } } - if (finalMultiplier!=1) + if (finalMultiplier != 1) { - BigDigit carry = multibyteMul(r1, r1, finalMultiplier, 0); + const BigDigit carry = multibyteMul(r1, r1, finalMultiplier, 0); if (carry) { r1 = t1[0 .. r1.length + 1]; @@ -891,14 +926,14 @@ public: } if (evenshiftbits) { - BigDigit carry = multibyteShl(r1, r1, evenshiftbits); - if (carry!=0) + const BigDigit carry = multibyteShl(r1, r1, evenshiftbits); + if (carry != 0) { r1 = t1[0 .. r1.length + 1]; r1[$ - 1] = carry; } } - while(r1[$ - 1]==0) + while (r1[$ - 1]==0) { r1=r1[0 .. $ - 1]; } @@ -931,16 +966,16 @@ public: inout(BigDigit) [] removeLeadingZeros(inout(BigDigit) [] x) pure nothrow @safe { size_t k = x.length; - while(k>1 && x[k - 1]==0) --k; + while (k>1 && x[k - 1]==0) --k; return x[0 .. k]; } -pure unittest +pure @system unittest { BigUint r = BigUint([5]); BigUint t = BigUint([7]); BigUint s = BigUint.mod(r, t); - assert(s==5); + assert(s == 5); } @@ -957,7 +992,7 @@ pure unittest // Pow tests -pure unittest +pure @system unittest { BigUint r, s; r.fromHexString("80000000_00000001"); @@ -999,7 +1034,28 @@ pure unittest assert(r.toHexString(0, '_', 7, ' ') == " 0"); assert(r.toHexString(0, '#', 9) == "0#00000000"); assert(r.toHexString(0, 0, 9) == "000000000"); +} +// +@safe pure unittest +{ + BigUint r; + r.fromHexString("1_E1178E81_00000000"); + assert(r.toHexString(0, '_', 0, '0', LetterCase.lower) == "1_e1178e81_00000000"); + assert(r.toHexString(0, '_', 20, '0', LetterCase.lower) == "0001_e1178e81_00000000"); + assert(r.toHexString(0, '_', 16+8, '0', LetterCase.lower) == "00000001_e1178e81_00000000"); + assert(r.toHexString(0, '_', 16+9, '0', LetterCase.lower) == "0_00000001_e1178e81_00000000"); + assert(r.toHexString(0, '_', 16+8+8, '0', LetterCase.lower) == "00000000_00000001_e1178e81_00000000"); + assert(r.toHexString(0, '_', 16+8+8+1, '0', LetterCase.lower) == "0_00000000_00000001_e1178e81_00000000"); + assert(r.toHexString(0, '_', 16+8+8+1, ' ', LetterCase.lower) == " 1_e1178e81_00000000"); + assert(r.toHexString(0, 0, 16+8+8+1, '0', LetterCase.lower) == "00000000000000001e1178e8100000000"); + r = 0UL; + assert(r.toHexString(0, '_', 0, '0', LetterCase.lower) == "0"); + assert(r.toHexString(0, '_', 7, '0', LetterCase.lower) == "0000000"); + assert(r.toHexString(0, '_', 7, ' ', LetterCase.lower) == " 0"); + assert(r.toHexString(0, '#', 9, '0', LetterCase.lower) == "0#00000000"); + assert(r.toHexString(0, 'Z', 9, '0', LetterCase.lower) == "0Z00000000"); + assert(r.toHexString(0, 0, 9, '0', LetterCase.lower) == "000000000"); } @@ -1007,15 +1063,13 @@ private: void twosComplement(const(BigDigit) [] x, BigDigit[] result) pure nothrow @safe { - foreach (i; 0..x.length) + foreach (i; 0 .. x.length) { result[i] = ~x[i]; } result[x.length..$] = BigDigit.max; - bool sgn = false; - - foreach (i; 0..result.length) + foreach (i; 0 .. result.length) { if (result[i] == BigDigit.max) { @@ -1041,7 +1095,7 @@ pure nothrow @safe } else { - result[0..x.length] = x; + result[0 .. x.length] = x; } return result; } @@ -1067,7 +1121,8 @@ T intpow(T)(T x, ulong n) pure nothrow @safe default: p = 1; - while (1){ + while (1) + { if (n & 1) p *= x; n >>= 1; @@ -1092,7 +1147,7 @@ int highestPowerBelowUintMax(uint x) pure nothrow @safe if (x<85) return 5; if (x<256) return 4; if (x<1626) return 3; - if (x<65536) return 2; + if (x<65_536) return 2; return 1; } @@ -1112,8 +1167,8 @@ int highestPowerBelowUlongMax(uint x) pure nothrow @safe if (x<566) return 7; if (x<1626) return 6; if (x<7132) return 5; - if (x<65536) return 4; - if (x<2642246) return 3; + if (x<65_536) return 4; + if (x<2_642_246) return 3; return 2; } @@ -1123,7 +1178,7 @@ version(unittest) int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe { int pwr = 1; - for (ulong q = x;x*q < cast(ulong)uint.max; ) + for (ulong q = x;x*q < cast(ulong) uint.max; ) { q*=x; ++pwr; } @@ -1155,12 +1210,12 @@ pure nothrow BigDigit [] result = new BigDigit[last+1]; if (x[last] < y[last]) { // we know result is negative - multibyteSub(result[0..last+1], y[0..last+1], x[0..last+1], 0); + multibyteSub(result[0 .. last+1], y[0 .. last+1], x[0 .. last+1], 0); *negative = true; } else { // positive or zero result - multibyteSub(result[0..last+1], x[0..last+1], y[0..last+1], 0); + multibyteSub(result[0 .. last+1], x[0 .. last+1], y[0 .. last+1], 0); *negative = false; } while (result.length > 1 && result[$-1] == 0) @@ -1186,7 +1241,7 @@ pure nothrow BigDigit [] result = new BigDigit[large.length]; - BigDigit carry = multibyteSub(result[0..small.length], large[0..small.length], small, 0); + BigDigit carry = multibyteSub(result[0 .. small.length], large[0 .. small.length], small, 0); result[small.length..$] = large[small.length..$]; if (carry) { @@ -1216,7 +1271,7 @@ BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure nothrow // create result. add 1 in case it overflows BigDigit [] result = new BigDigit[x.length + 1]; - BigDigit carry = multibyteAdd(result[0..y.length], x[0..y.length], y, 0); + BigDigit carry = multibyteAdd(result[0 .. y.length], x[0 .. y.length], y, 0); if (x.length != y.length) { result[y.length..$-1]= x[y.length..$]; @@ -1238,16 +1293,16 @@ BigDigit [] addInt(const BigDigit[] x, ulong y) pure nothrow uint hi = cast(uint)(y >>> 32); uint lo = cast(uint)(y& 0xFFFF_FFFF); auto len = x.length; - if (x.length < 2 && hi!=0) ++len; + if (x.length < 2 && hi != 0) ++len; BigDigit [] result = new BigDigit[len+1]; - result[0..x.length] = x[]; - if (x.length < 2 && hi!=0) + result[0 .. x.length] = x[]; + if (x.length < 2 && hi != 0) { result[1]=hi; hi=0; } uint carry = multibyteIncrementAssign!('+')(result[0..$-1], lo); - if (hi!=0) carry += multibyteIncrementAssign!('+')(result[1..$-1], hi); + if (hi != 0) carry += multibyteIncrementAssign!('+')(result[1..$-1], hi); if (carry) { result[$-1] = carry; @@ -1287,6 +1342,7 @@ BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) pure nothrow { + import core.memory : GC; assert( result.length == x.length + y.length ); assert( y.length > 0 ); assert( x.length >= y.length); @@ -1295,7 +1351,7 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) // Small multiplier, we'll just use the asm classic multiply. if (y.length == 1) { // Trivial case, no cache effects to worry about - result[x.length] = multibyteMul(result[0..x.length], x, y[0], 0); + result[x.length] = multibyteMul(result[0 .. x.length], x, y[0], 0); return; } @@ -1307,14 +1363,14 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) // We make the first chunk shorter, if necessary, to ensure this. auto chunksize = CACHELIMIT / y.length; - auto residual = x.length % chunksize; + immutable residual = x.length % chunksize; if (residual < y.length) { chunksize -= y.length; } // Use schoolbook multiply. - mulSimple(result[0 .. chunksize + y.length], x[0..chunksize], y); + mulSimple(result[0 .. chunksize + y.length], x[0 .. chunksize], y); auto done = chunksize; while (done < x.length) @@ -1322,15 +1378,15 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) // result[done .. done+ylength] already has a value. chunksize = (done + (CACHELIMIT / y.length) < x.length) ? (CACHELIMIT / y.length) : x.length - done; BigDigit [KARATSUBALIMIT] partial; - partial[0..y.length] = result[done..done+y.length]; - mulSimple(result[done..done+chunksize+y.length], x[done..done+chunksize], y); - addAssignSimple(result[done..done+chunksize + y.length], partial[0..y.length]); + partial[0 .. y.length] = result[done .. done+y.length]; + mulSimple(result[done .. done+chunksize+y.length], x[done .. done+chunksize], y); + addAssignSimple(result[done .. done+chunksize + y.length], partial[0 .. y.length]); done += chunksize; } return; } - auto half = (x.length >> 1) + (x.length & 1); + immutable half = (x.length >> 1) + (x.length & 1); if (2*y.length*y.length <= x.length*x.length) { // UNBALANCED MULTIPLY @@ -1352,7 +1408,7 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) // in the existing chunks. // Make all the chunks a tiny bit bigger // (We're padding y with zeros) - chunksize += extra / cast(double)numchunks; + chunksize += extra / numchunks; extra = x.length - chunksize*numchunks; // there will probably be a few left over. // Every chunk will either have size chunksize, or chunksize+1. @@ -1373,7 +1429,6 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(maxchunk) + y.length]; BigDigit [] partial = scratchbuff[$ - y.length .. $]; size_t done; // how much of X have we done so far? - double residual = 0; if (paddingY) { // If the first chunk is bigger, do it first. We're padding y. @@ -1384,11 +1439,11 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) } else { // We're padding X. Begin with the extra bit. - mulKaratsuba(result[0 .. y.length + extra], y, x[0..extra], scratchbuff); + mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff); done = extra; extra = 0; } - auto basechunksize = chunksize; + immutable basechunksize = chunksize; while (done < x.length) { chunksize = basechunksize + (extra > 0 ? 1 : 0); @@ -1399,14 +1454,14 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) addAssignSimple(result[done .. done + y.length + chunksize], partial); done += chunksize; } - delete scratchbuff; + () @trusted { GC.free(scratchbuff.ptr); } (); } else { // Balanced. Use Karatsuba directly. BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(x.length)]; mulKaratsuba(result, x, y, scratchbuff); - delete scratchbuff; + () @trusted { GC.free(scratchbuff.ptr); } (); } } @@ -1417,14 +1472,15 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) */ void squareInternal(BigDigit[] result, const BigDigit[] x) pure nothrow { + import core.memory : GC; // Squaring is potentially half a multiply, plus add the squares of // the diagonal elements. assert(result.length == 2*x.length); if (x.length <= KARATSUBASQUARELIMIT) { - if (x.length==1) + if (x.length == 1) { - result[1] = multibyteMul(result[0..1], x, x[0], 0); + result[1] = multibyteMul(result[0 .. 1], x, x[0], 0); return; } return squareSimple(result, x); @@ -1432,7 +1488,7 @@ void squareInternal(BigDigit[] result, const BigDigit[] x) pure nothrow // The nice thing about squaring is that it always stays balanced BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(x.length)]; squareKaratsuba(result, x, scratchbuff); - delete scratchbuff; + () @trusted { GC.free(scratchbuff.ptr); } (); } @@ -1442,6 +1498,7 @@ import core.bitop : bsr; void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [] u, const BigDigit [] v) pure nothrow { + import core.memory : GC; assert(quotient.length == u.length - v.length + 1); assert(remainder == null || remainder.length == v.length); assert(v.length > 1); @@ -1455,7 +1512,7 @@ void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [ BigDigit [] un = new BigDigit[u.length + 1]; // How much to left shift v, so that its MSB is set. uint s = BIGDIGITSHIFTMASK - bsr(v[$-1]); - if (s!=0) + if (s != 0) { multibyteShl(vn, v, s); un[$-1] = multibyteShl(un[0..$-1], u, s); @@ -1478,14 +1535,13 @@ void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [ // Unnormalize remainder, if required. if (remainder != null) { - if (s == 0) remainder[] = un[0..vn.length]; - else multibyteShr(remainder, un[0..vn.length+1], s); + if (s == 0) remainder[] = un[0 .. vn.length]; + else multibyteShr(remainder, un[0 .. vn.length+1], s); } - delete un; - delete vn; + () @trusted { GC.free(un.ptr); GC.free(vn.ptr); } (); } -pure unittest +pure @system unittest { immutable(uint) [] u = [0, 0xFFFF_FFFE, 0x8000_0000]; immutable(uint) [] v = [0xFFFF_FFFF, 0x8000_0000]; @@ -1507,13 +1563,13 @@ private: // every 8 digits. // buff.length must be data.length*8 if separator is zero, // or data.length*9 if separator is non-zero. It will be completely filled. -char [] biguintToHex(char [] buff, const BigDigit [] data, char separator=0) - pure nothrow @safe +char [] biguintToHex(char [] buff, const BigDigit [] data, char separator=0, + LetterCase letterCase = LetterCase.upper) pure nothrow @safe { int x=0; - for (ptrdiff_t i=data.length - 1; i>=0; --i) + for (ptrdiff_t i=data.length - 1; i >= 0; --i) { - toHexZeroPadded(buff[x..x+8], data[i]); + toHexZeroPadded(buff[x .. x+8], data[i], letterCase); x+=8; if (separator) { @@ -1524,6 +1580,70 @@ char [] biguintToHex(char [] buff, const BigDigit [] data, char separator=0) return buff; } +/** + * Convert a big uint into an octal string. + * + * Params: + * buff = The destination buffer for the octal string. Must be large enough to + * store the result, including leading zeroes, which is + * ceil(data.length * BigDigitBits / 3) characters. + * The buffer is filled from back to front, starting from `buff[$-1]`. + * data = The biguint to be converted. + * + * Returns: The index of the leading non-zero digit in `buff`. Will be + * `buff.length - 1` if the entire big uint is zero. + */ +size_t biguintToOctal(char[] buff, const(BigDigit)[] data) + pure nothrow @safe @nogc +{ + ubyte carry = 0; + int shift = 0; + size_t penPos = buff.length - 1; + size_t lastNonZero = buff.length - 1; + + pragma(inline) void output(uint digit) @nogc nothrow + { + if (digit != 0) + lastNonZero = penPos; + buff[penPos--] = cast(char)('0' + digit); + } + + foreach (bigdigit; data) + { + if (shift < 0) + { + // Some bits were carried over from previous word. + assert(shift > -3); + output(((bigdigit << -shift) | carry) & 0b111); + shift += 3; + assert(shift > 0); + } + + while (shift <= BigDigitBits - 3) + { + output((bigdigit >>> shift) & 0b111); + shift += 3; + } + + if (shift < BigDigitBits) + { + // Some bits need to be carried forward. + carry = (bigdigit >>> shift) & 0b11; + } + shift -= BigDigitBits; + assert(shift >= -2 && shift <= 0); + } + + if (shift < 0) + { + // Last word had bits that haven't been output yet. + assert(shift > -3); + output(carry); + } + + return lastNonZero; +} + /** Convert a big uint into a decimal string. * * Params: @@ -1542,7 +1662,7 @@ size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow // Might be better to divide by (10^38/2^32) since that gives 38 digits for // the price of 3 divisions and a shr; this version only gives 27 digits // for 3 divisions. - while(data.length>1) + while (data.length>1) { uint rem = multibyteDivAssign(data, 10_0000_0000, 0); itoaZeroPadded(buff[sofar-9 .. sofar], rem); @@ -1555,7 +1675,7 @@ size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow itoaZeroPadded(buff[sofar-10 .. sofar], data[0]); sofar -= 10; // and strip off the leading zeros - while(sofar!= buff.length-1 && buff[sofar] == '0') + while (sofar != buff.length-1 && buff[sofar] == '0') sofar++; return sofar; } @@ -1565,7 +1685,7 @@ size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow * Params: * data The biguint to be receive the result. Must be large enough to * store the result. - * s The decimal string. May contain _ or 0..9 + * s The decimal string. May contain _ or 0 .. 9 * * The required length for the destination buffer is slightly less than * 1 + s.length/log2(10) = 1 + s.length/3.3219. @@ -1573,10 +1693,15 @@ size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow * Returns: * the highest index of data which was used. */ -int biguintFromDecimal(BigDigit [] data, const(char)[] s) pure +int biguintFromDecimal(Range)(BigDigit[] data, Range s) +if ( + isInputRange!Range && + isSomeChar!(ElementType!Range) && + !isInfinite!Range) in { - assert((data.length >= 2) || (data.length == 1 && s.length == 1)); + static if (hasLength!Range) + assert((data.length >= 2) || (data.length == 1 && s.length == 1)); } body { @@ -1591,7 +1716,7 @@ body // TODO: This is inefficient for very large strings (it is O(n^^2)). // We should take advantage of fast multiplication once the numbers exceed // Karatsuba size. - uint lo = 0; // number of powers of digits, 0..18 + uint lo = 0; // number of powers of digits, 0 .. 18 uint x = 0; ulong y = 0; uint hi = 0; // number of base 1e19 digits @@ -1599,14 +1724,15 @@ body if (data.length > 1) data[1] = 0; - for (int i= (s[0]=='-' || s[0]=='+')? 1 : 0; i '9') + + if (character < '0' || character > '9') throw new ConvException("invalid digit"); x *= 10; - x += s[i] - '0'; + x += character - '0'; ++lo; if (lo == 9) { @@ -1627,16 +1753,16 @@ body // Multiply existing number by 10^19, then add y1. if (hi>0) { - data[hi] = multibyteMul(data[0..hi], data[0..hi], 1220703125*2u, 0); // 5^13*2 = 0x9184_E72A + data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 1_220_703_125*2u, 0); // 5^13*2 = 0x9184_E72A ++hi; - data[hi] = multibyteMul(data[0..hi], data[0..hi], 15625*262144u, 0); // 5^6*2^18 = 0xF424_0000 + data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 15_625*262_144u, 0); // 5^6*2^18 = 0xF424_0000 ++hi; } else hi = 2; - uint c = multibyteIncrementAssign!('+')(data[0..hi], cast(uint)(y&0xFFFF_FFFF)); - c += multibyteIncrementAssign!('+')(data[1..hi], cast(uint)(y>>32)); - if (c!=0) + uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF)); + c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32)); + if (c != 0) { data[hi]=c; ++hi; @@ -1646,10 +1772,10 @@ body } } // Now set y = all remaining digits. - if (lo>=18) + if (lo >= 18) { } - else if (lo>=9) + else if (lo >= 9) { for (int k=9; k0) { - uint c = multibyteMul(data[0..hi], data[0..hi], 10, 0); - if (c!=0) + immutable c = multibyteMul(data[0 .. hi], data[0 .. hi], 10, 0); + if (c != 0) { data[hi]=c; ++hi; } --lo; } - uint c = multibyteIncrementAssign!('+')(data[0..hi], cast(uint)(y&0xFFFF_FFFF)); + uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF)); if (y > 0xFFFF_FFFFL) { - c += multibyteIncrementAssign!('+')(data[1..hi], cast(uint)(y>>32)); + c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32)); } - if (c!=0) + if (c != 0) { data[hi]=c; ++hi; @@ -1719,7 +1845,7 @@ in } body { - result[left.length] = multibyteMul(result[0..left.length], left, right[0], 0); + result[left.length] = multibyteMul(result[0 .. left.length], left, right[0], 0); multibyteMultiplyAccumulate(result[1..$], left, right[1..$]); } @@ -1749,11 +1875,11 @@ in } body { - uint carry = multibyteAdd(result[0..right.length], - left[0..right.length], right, 0); + uint carry = multibyteAdd(result[0 .. right.length], + left[0 .. right.length], right, 0); if (right.length < left.length) { - result[right.length..left.length] = left[right.length .. $]; + result[right.length .. left.length] = left[right.length .. $]; carry = multibyteIncrementAssign!('+')(result[right.length..$], carry); } return carry; @@ -1771,13 +1897,13 @@ in } body { - BigDigit carry = multibyteSub(result[0..right.length], - left[0..right.length], right, 0); + BigDigit carry = multibyteSub(result[0 .. right.length], + left[0 .. right.length], right, 0); if (right.length < left.length) { - result[right.length..left.length] = left[right.length .. $]; + result[right.length .. left.length] = left[right.length .. $]; carry = multibyteIncrementAssign!('-')(result[right.length..$], carry); - } //else if (result.length==left.length+1) { result[$-1] = carry; carry=0; } + } //else if (result.length == left.length+1) { result[$-1] = carry; carry=0; } return carry; } @@ -1789,7 +1915,7 @@ BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure nothrow { assert(result.length >= right.length); - uint c = multibyteSub(result[0..right.length], result[0..right.length], right, 0); + uint c = multibyteSub(result[0 .. right.length], result[0 .. right.length], right, 0); if (c && result.length > right.length) c = multibyteIncrementAssign!('-')(result[right.length .. $], c); return c; @@ -1801,7 +1927,7 @@ BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure nothrow { assert(result.length >= right.length); - uint c = multibyteAdd(result[0..right.length], result[0..right.length], right, 0); + uint c = multibyteAdd(result[0 .. right.length], result[0 .. right.length], right, 0); if (c && result.length > right.length) c = multibyteIncrementAssign!('+')(result[right.length .. $], c); return c; @@ -1824,16 +1950,16 @@ bool less(const(BigDigit)[] x, const(BigDigit)[] y) pure nothrow { assert(x.length >= y.length); auto k = x.length-1; - while(x[k]==0 && k>=y.length) + while (x[k]==0 && k >= y.length) --k; - if (k>=y.length) + if (k >= y.length) return false; while (k>0 && x[k]==y[k]) --k; return x[k] < y[k]; } -// Set result = abs(x-y), return true if result is negative(x> 1) + (x1.length & 1); - bool ysmaller = (quarter >= y1.length); - mulKaratsuba(resultHigh[0..quarter+y1.length], ysmaller ? x1[0..quarter] : y1, - ysmaller ? y1 : x1[0..quarter], newscratchbuff); + immutable ysmaller = (quarter >= y1.length); + mulKaratsuba(resultHigh[0 .. quarter+y1.length], ysmaller ? x1[0 .. quarter] : y1, + ysmaller ? y1 : x1[0 .. quarter], newscratchbuff); // Save the part which will be overwritten. - bool ysmaller2 = ((x1.length - quarter) >= y1.length); - newscratchbuff[0..y1.length] = resultHigh[quarter..quarter + y1.length]; + immutable ysmaller2 = ((x1.length - quarter) >= y1.length); + newscratchbuff[0 .. y1.length] = resultHigh[quarter .. quarter + y1.length]; mulKaratsuba(resultHigh[quarter..$], ysmaller2 ? x1[quarter..$] : y1, ysmaller2 ? y1 : x1[quarter..$], newscratchbuff[y1.length..$]); - resultHigh[quarter..$].addAssignSimple(newscratchbuff[0..y1.length]); + resultHigh[quarter..$].addAssignSimple(newscratchbuff[0 .. y1.length]); } } else @@ -1978,14 +2104,14 @@ void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x, R3 = bHi + carry_from_R2 It might actually be quicker to do it in two full-length additions: - newscratchbuff[2*half] = addSimple(newscratchbuff[0..2*half], result[0..2*half], result[2*half..$]); - addAssignSimple(result[half..$], newscratchbuff[0..2*half+1]); + newscratchbuff[2*half] = addSimple(newscratchbuff[0 .. 2*half], result[0 .. 2*half], result[2*half..$]); + addAssignSimple(result[half..$], newscratchbuff[0 .. 2*half+1]); */ - BigDigit[] R1 = result[half..half*2]; - BigDigit[] R2 = result[half*2..half*3]; + BigDigit[] R1 = result[half .. half*2]; + BigDigit[] R2 = result[half*2 .. half*3]; BigDigit[] R3 = result[half*3..$]; BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1 - BigDigit c2 = multibyteAdd(R1, R2, result[0..half], 0); // c2:R1 = R2 + R1 + R0 + BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0 BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3 if (c1+c2) multibyteIncrementAssign!('+')(result[half*2..$], c1+c2); @@ -2016,7 +2142,7 @@ void squareKaratsuba(BigDigit [] result, const BigDigit [] x, BigDigit [] newscratchbuff = scratchbuff[half*2 .. $]; // initially use result to store temporaries BigDigit [] xdiff= result[0 .. half]; - BigDigit [] ydiff = result[half .. half*2]; + const BigDigit [] ydiff = result[half .. half*2]; // First, we calculate mid. We don't need its sign inplaceSub(xdiff, x0, x1); @@ -2032,11 +2158,11 @@ void squareKaratsuba(BigDigit [] result, const BigDigit [] x, R2 = aHI + bLO + aHI + carry_from_R1 R3 = bHi + carry_from_R2 */ - BigDigit[] R1 = result[half..half*2]; - BigDigit[] R2 = result[half*2..half*3]; + BigDigit[] R1 = result[half .. half*2]; + BigDigit[] R2 = result[half*2 .. half*3]; BigDigit[] R3 = result[half*3..$]; BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1 - BigDigit c2 = multibyteAdd(R1, R2, result[0..half], 0); // c2:R1 = R2 + R1 + R0 + BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0 BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3 if (c1+c2) multibyteIncrementAssign!('+')(result[half*2..$], c1+c2); if (c1+c3) multibyteIncrementAssign!('+')(R3, c1+c3); @@ -2051,7 +2177,7 @@ void squareKaratsuba(BigDigit [] result, const BigDigit [] x, * Given u and v, calculates quotient = u / v, u = u % v. * v must be normalized (ie, the MSB of v must be 1). * The most significant words of quotient and u may be zero. - * u[0..v.length] holds the remainder. + * u[0 .. v.length] holds the remainder. */ void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) pure nothrow @@ -2106,11 +2232,11 @@ div3by2done: ; else { // version(InlineAsm) ulong uu = (cast(ulong)(u[j + v.length]) << 32) | u[j + v.length - 1]; - ulong bigqhat = uu / vhi; + immutable bigqhat = uu / vhi; ulong rhat = uu - bigqhat * vhi; - qhat = cast(uint)bigqhat; + qhat = cast(uint) bigqhat; again: - if (cast(ulong)qhat * vlo > ((rhat << 32) + ulo)) + if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo)) { --qhat; rhat += vhi; @@ -2120,13 +2246,13 @@ again: } // version(InlineAsm) } // Multiply and subtract. - uint carry = multibyteMulAdd!('-')(u[j..j + v.length], v, qhat, 0); + uint carry = multibyteMulAdd!('-')(u[j .. j + v.length], v, qhat, 0); if (u[j+v.length] < carry) { // If we subtracted too much, add back --qhat; - carry -= multibyteAdd(u[j..j + v.length],u[j..j + v.length], v, 0); + carry -= multibyteAdd(u[j .. j + v.length],u[j .. j + v.length], v, 0); } quotient[j] = qhat; u[j + v.length] = u[j + v.length] - carry; @@ -2136,24 +2262,40 @@ again: private: // TODO: Replace with a library call -void itoaZeroPadded(char[] output, uint value, int radix = 10) - pure nothrow @safe +void itoaZeroPadded(char[] output, uint value) + pure nothrow @safe @nogc { - ptrdiff_t x = output.length - 1; - for( ; x >= 0; --x) + for (auto i = output.length; i--;) { - output[x]= cast(char)(value % radix + '0'); - value /= radix; + if (value < 10) + { + output[i] = cast(char)(value + '0'); + value = 0; + } + else + { + output[i] = cast(char)(value % 10 + '0'); + value /= 10; + } } } -void toHexZeroPadded(char[] output, uint value) pure nothrow @safe +void toHexZeroPadded(char[] output, uint value, + LetterCase letterCase = LetterCase.upper) pure nothrow @safe { ptrdiff_t x = output.length - 1; - static immutable string hexDigits = "0123456789ABCDEF"; - for( ; x>=0; --x) + static immutable string upperHexDigits = "0123456789ABCDEF"; + static immutable string lowerHexDigits = "0123456789abcdef"; + for ( ; x >= 0; --x) { - output[x] = hexDigits[value & 0xF]; + if (letterCase == LetterCase.upper) + { + output[x] = upperHexDigits[value & 0xF]; + } + else + { + output[x] = lowerHexDigits[value & 0xF]; + } value >>= 4; } } @@ -2185,7 +2327,7 @@ int firstNonZeroDigit(const BigDigit [] x) pure nothrow @nogc @safe } return k; } -import core.stdc.stdio; + /* Calculate quotient and remainder of u / v using fast recursive division. v must be normalised, and must be at least half as long as u. @@ -2193,7 +2335,7 @@ import core.stdc.stdio; scratch is temporary storage space, length must be >= quotient + 1. Returns: - u[0..v.length] is the remainder. u[v.length..$] is corrupted. + u[0 .. v.length] is the remainder. u[v.length..$] is corrupted. Implements algorithm 1.8 from MCA. This algorithm has an annoying special case. After the first recursion, the @@ -2249,19 +2391,19 @@ body scratch, mayOverflow); // quotient[k..$] is our guess at the high quotient. - // u[2*k.. 2.*k + v.length - k = k + v.length] is the high part of the - // first remainder. u[0..2*k] is the low part. + // u[2*k .. 2.*k + v.length - k = k + v.length] is the high part of the + // first remainder. u[0 .. 2*k] is the low part. // Calculate the full first remainder to be // remainder - highQuotient * lowDivisor // reducing highQuotient until the remainder is positive. - // The low part of the remainder, u[0..k], cannot be altered by this. + // The low part of the remainder, u[0 .. k], cannot be altered by this. adjustRemainder(quotient[k .. $], u[k .. k + v.length], v, k, scratch[0 .. quotient.length], mayOverflow); // RECURSION 2: Calculate the low half of the quotient - // The full first remainder is now in u[0..k + v.length]. + // The full first remainder is now in u[0 .. k + v.length]. if (u[k + v.length - 1] & 0x8000_0000) { @@ -2290,15 +2432,15 @@ body recursiveDivMod(quotient[0 .. k], u[k .. k + v.length], v[k .. $], scratch, false); - // high remainder is in u[k..k+(v.length-k)] == u[k .. v.length] + // high remainder is in u[k .. k+(v.length-k)] == u[k .. v.length] adjustRemainder(quotient[0 .. k], u[0 .. v.length], v, k, scratch[0 .. 2 * k]); } } -// rem -= quot * v[0..k]. -// If would make rem negative, decrease quot until rem is >=0. +// rem -= quot * v[0 .. k]. +// If would make rem negative, decrease quot until rem is >= 0. // Needs (quot.length * k) scratch space to store the result of the multiply. void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v, ptrdiff_t k, @@ -2311,7 +2453,7 @@ void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v, carry = scratch[$-1] + subAssignSimple(rem, scratch[0..$-1]); else carry = subAssignSimple(rem, scratch); - while(carry) + while (carry) { multibyteIncrementAssign!('-')(quot, 1); // quot-- carry -= multibyteAdd(rem, rem, v, 0); @@ -2322,6 +2464,7 @@ void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v, void blockDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) pure nothrow { + import core.memory : GC; assert(quotient.length == u.length - v.length); assert(v.length > 1); assert(u.length >= v.length); @@ -2333,15 +2476,15 @@ pure nothrow auto m = u.length - v.length; while (m > v.length) { - bool mayOverflow = (u[m + v.length -1 ] & 0x8000_0000)!=0; + immutable mayOverflow = (u[m + v.length -1 ] & 0x8000_0000)!=0; BigDigit saveq; if (mayOverflow) { u[m + v.length] = 0; saveq = quotient[m]; } - recursiveDivMod(quotient[m-v.length..m + (mayOverflow? 1: 0)], - u[m - v.length..m + v.length + (mayOverflow? 1: 0)], v, scratch, mayOverflow); + recursiveDivMod(quotient[m-v.length .. m + (mayOverflow? 1: 0)], + u[m - v.length .. m + v.length + (mayOverflow? 1: 0)], v, scratch, mayOverflow); if (mayOverflow) { assert(quotient[m] == 0); @@ -2349,17 +2492,14 @@ pure nothrow } m -= v.length; } - recursiveDivMod(quotient[0..m], u[0..m + v.length], v, scratch); - delete scratch; + recursiveDivMod(quotient[0 .. m], u[0 .. m + v.length], v, scratch); + () @trusted { GC.free(scratch.ptr); } (); } -version(unittest) +@system unittest { import core.stdc.stdio; -} -unittest -{ void printBiguint(const uint [] data) { char [] buff = biguintToHex(new char[data.length*9], data, '_'); @@ -2387,7 +2527,45 @@ unittest uint [] r1 = r.dup; uint [] q1 = q.dup; blockDivMod(q, b, a); - r = b[0..a.length]; + r = b[0 .. a.length]; assert(r[] == r1[]); assert(q[] == q1[]); } + +// biguintToOctal +@safe unittest +{ + enum bufSize = 5 * BigDigitBits / 3 + 1; + auto buf = new char[bufSize]; + size_t i; + BigDigit[] data = [ 342391 ]; + + // Basic functionality with single word + i = biguintToOctal(buf, data); + assert(i == bufSize - 7 && buf[i .. $] == "1234567"); + + // Test carrying bits between words + data = [ 0x77053977, 0x39770539, 0x00000005 ]; + i = biguintToOctal(buf, data); + assert(i == bufSize - 23 && buf[i .. $] == "12345670123456701234567"); + + // Test carried bits in the last word + data = [ 0x80000000 ]; + i = biguintToOctal(buf, data); + assert(buf[i .. $] == "20000000000"); + + // Test boundary between 3rd and 4th word where the number of bits is + // divisible by 3 and no bits should be carried. + // + // The 0xC0000000's are for "poisoning" the carry to be non-zero when the + // rollover happens, so that if any bugs happen in wrongly adding the carry + // to the next word, non-zero bits will show up in the output. + data = [ 0xC0000000, 0xC0000000, 0xC0000000, 0x00000010 ]; + i = biguintToOctal(buf, data); + assert(buf[i .. $] == "2060000000001400000000030000000000"); + + // Boundary case: 0 + data = [ 0 ]; + i = biguintToOctal(buf, data); + assert(buf[i .. $] == "0"); +} diff --git a/std/internal/math/biguintnoasm.d b/std/internal/math/biguintnoasm.d index 035aad4b998..ff06808d8f6 100644 --- a/std/internal/math/biguintnoasm.d +++ b/std/internal/math/biguintnoasm.d @@ -24,8 +24,8 @@ public: alias BigDigit = uint; // A Bignum is an array of BigDigits. // Limits for when to switch between multiplication algorithms. -enum : int { KARATSUBALIMIT = 10 }; // Minimum value for which Karatsuba is worthwhile. -enum : int { KARATSUBASQUARELIMIT=12 }; // Minimum value for which square Karatsuba is worthwhile +enum int KARATSUBALIMIT = 10; // Minimum value for which Karatsuba is worthwhile. +enum int KARATSUBASQUARELIMIT = 12; // Minimum value for which square Karatsuba is worthwhile /** Multi-byte addition or subtraction @@ -41,14 +41,14 @@ uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1, for (size_t i = 0; i < src2.length; ++i) { static if (op=='+') c = c + src1[i] + src2[i]; - else c = cast(ulong)src1[i] - src2[i] - c; - dest[i] = cast(uint)c; + else c = cast(ulong) src1[i] - src2[i] - c; + dest[i] = cast(uint) c; c = (c > 0xFFFF_FFFF); } - return cast(uint)c; + return cast(uint) c; } -unittest +@safe unittest { uint [] a = new uint[40]; uint [] b = new uint[40]; @@ -56,15 +56,15 @@ unittest for (size_t i = 0; i < a.length; ++i) { if (i&1) a[i]=cast(uint)(0x8000_0000 + i); - else a[i]=cast(uint)i; + else a[i]=cast(uint) i; b[i]= 0x8000_0003; } c[19]=0x3333_3333; - uint carry = multibyteAddSub!('+')(c[0..18], b[0..18], a[0..18], 0); + uint carry = multibyteAddSub!('+')(c[0 .. 18], b[0 .. 18], a[0 .. 18], 0); assert(c[0]==0x8000_0003); assert(c[1]==4); assert(c[19]==0x3333_3333); // check for overrun - assert(carry==1); + assert(carry == 1); for (size_t i = 0; i < a.length; ++i) { a[i] = b[i] = c[i] = 0; @@ -74,7 +74,7 @@ unittest a[10]=0x1D950C84; b[10]=0x1D950C84; a[5] =0x44444444; - carry = multibyteAddSub!('-')(a[0..12], a[0..12], b[0..12], 0); + carry = multibyteAddSub!('-')(a[0 .. 12], a[0 .. 12], b[0 .. 12], 0); assert(a[11] == 0); for (size_t i = 0; i < 10; ++i) if (i != 5) @@ -88,7 +88,7 @@ unittest } a[q-2]=0x040000; b[q-2]=0x040000; - carry = multibyteAddSub!('-')(a[0..q], a[0..q], b[0..q], 0); + carry = multibyteAddSub!('-')(a[0 .. q], a[0 .. q], b[0 .. q], 0); assert(a[q-2]==0); } } @@ -106,8 +106,8 @@ uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) { ulong c = carry; c += dest[0]; - dest[0] = cast(uint)c; - if (c<=0xFFFF_FFFF) + dest[0] = cast(uint) c; + if (c <= 0xFFFF_FFFF) return 0; for (size_t i = 1; i < dest.length; ++i) @@ -122,8 +122,8 @@ uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) { ulong c = carry; c = dest[0] - c; - dest[0] = cast(uint)c; - if (c<=0xFFFF_FFFF) + dest[0] = cast(uint) c; + if (c <= 0xFFFF_FFFF) return 0; for (size_t i = 1; i < dest.length; ++i) { @@ -136,7 +136,7 @@ uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) } /** dest[] = src[] << numbits - * numbits must be in the range 1..31 + * numbits must be in the range 1 .. 31 */ uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) pure @nogc @safe @@ -145,29 +145,29 @@ uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) for (size_t i = 0; i < dest.length; ++i) { c += (cast(ulong)(src[i]) << numbits); - dest[i] = cast(uint)c; + dest[i] = cast(uint) c; c >>>= 32; } - return cast(uint)c; + return cast(uint) c; } /** dest[] = src[] >> numbits - * numbits must be in the range 1..31 + * numbits must be in the range 1 .. 31 */ void multibyteShr(uint [] dest, const(uint) [] src, uint numbits) pure @nogc @safe { ulong c = 0; - for(ptrdiff_t i = dest.length; i!=0; --i) + for (ptrdiff_t i = dest.length; i != 0; --i) { c += (src[i-1] >>numbits) + (cast(ulong)(src[i-1]) << (64 - numbits)); - dest[i-1] = cast(uint)c; + dest[i-1] = cast(uint) c; c >>>= 32; } } -unittest +@safe unittest { uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; @@ -182,7 +182,7 @@ unittest aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteShl(aa[1..4], aa[1..$], 4); + multibyteShl(aa[1 .. 4], aa[1..$], 4); assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); } @@ -195,53 +195,53 @@ uint multibyteMul(uint[] dest, const(uint)[] src, uint multiplier, uint carry) { assert(dest.length == src.length); ulong c = carry; - for(size_t i = 0; i < src.length; ++i) + for (size_t i = 0; i < src.length; ++i) { c += cast(ulong)(src[i]) * multiplier; - dest[i] = cast(uint)c; + dest[i] = cast(uint) c; c>>=32; } - return cast(uint)c; + return cast(uint) c; } -unittest +@safe unittest { uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteMul(aa[1..4], aa[1..4], 16, 0); + multibyteMul(aa[1 .. 4], aa[1 .. 4], 16, 0); assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); } /** - * dest[] += src[] * multiplier + carry(0..FFFF_FFFF). - * Returns carry out of MSB (0..FFFF_FFFF). + * dest[] += src[] * multiplier + carry(0 .. FFFF_FFFF). + * Returns carry out of MSB (0 .. FFFF_FFFF). */ uint multibyteMulAdd(char op)(uint [] dest, const(uint)[] src, uint multiplier, uint carry) pure @nogc @safe { assert(dest.length == src.length); ulong c = carry; - for(size_t i = 0; i < src.length; ++i) + for (size_t i = 0; i < src.length; ++i) { - static if(op=='+') + static if (op=='+') { c += cast(ulong)(multiplier) * src[i] + dest[i]; - dest[i] = cast(uint)c; + dest[i] = cast(uint) c; c >>= 32; } else { - c += cast(ulong)multiplier * src[i]; - ulong t = cast(ulong)dest[i] - cast(uint)c; - dest[i] = cast(uint)t; - c = cast(uint)((c>>32) - (t>>32)); + c += cast(ulong) multiplier * src[i]; + ulong t = cast(ulong) dest[i] - cast(uint) c; + dest[i] = cast(uint) t; + c = cast(uint)((c >> 32) - (t >> 32)); } } - return cast(uint)c; + return cast(uint) c; } -unittest +@safe unittest { uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, @@ -257,13 +257,14 @@ unittest /** - Sets result = result[0..left.length] + left * right + Sets result = result[0 .. left.length] + left * right It is defined in this way to allow cache-efficient multiplication. This function is equivalent to: ---- - for (size_t i = 0; i< right.length; ++i) { - dest[left.length + i] = multibyteMulAdd(dest[i..left.length+i], + for (size_t i = 0; i< right.length; ++i) + { + dest[left.length + i] = multibyteMulAdd(dest[i .. left.length+i], left, right[i], 0); } ---- @@ -273,29 +274,29 @@ void multibyteMultiplyAccumulate(uint [] dest, const(uint)[] left, const(uint) { for (size_t i = 0; i < right.length; ++i) { - dest[left.length + i] = multibyteMulAdd!('+')(dest[i..left.length+i], + dest[left.length + i] = multibyteMulAdd!('+')(dest[i .. left.length+i], left, right[i], 0); } } /** dest[] /= divisor. - * overflow is the initial remainder, and must be in the range 0..divisor-1. + * overflow is the initial remainder, and must be in the range 0 .. divisor-1. */ uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) pure @nogc @safe { - ulong c = cast(ulong)overflow; - for(ptrdiff_t i = dest.length-1; i>= 0; --i) + ulong c = cast(ulong) overflow; + for (ptrdiff_t i = dest.length-1; i >= 0; --i) { - c = (c<<32) + cast(ulong)(dest[i]); + c = (c << 32) + cast(ulong)(dest[i]); uint q = cast(uint)(c/divisor); c -= divisor * q; dest[i] = q; } - return cast(uint)c; + return cast(uint) c; } -unittest +@safe unittest { uint [] aa = new uint[101]; for (uint i = 0; i < aa.length; ++i) @@ -309,18 +310,18 @@ unittest assert(r == 0x33FF_7461); } -// Set dest[2*i..2*i+1]+=src[i]*src[i] +// Set dest[2*i .. 2*i+1]+=src[i]*src[i] void multibyteAddDiagonalSquares(uint[] dest, const(uint)[] src) pure @nogc @safe { ulong c = 0; - for(size_t i = 0; i < src.length; ++i) + for (size_t i = 0; i < src.length; ++i) { // At this point, c is 0 or 1, since FFFF*FFFF+FFFF_FFFF = 1_0000_0000. c += cast(ulong)(src[i]) * src[i] + dest[2*i]; - dest[2*i] = cast(uint)c; + dest[2*i] = cast(uint) c; c = (c>>=32) + dest[2*i+1]; - dest[2*i+1] = cast(uint)c; + dest[2*i+1] = cast(uint) c; c >>= 32; } } @@ -336,9 +337,9 @@ void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) if (x.length == 3) { ulong c = cast(ulong)(x[$-1]) * x[$-2] + dest[2*x.length-3]; - dest[2*x.length - 3] = cast(uint)c; + dest[2*x.length - 3] = cast(uint) c; c >>= 32; - dest[2*x.length - 2] = cast(uint)c; + dest[2*x.length - 2] = cast(uint) c; } return; } @@ -349,15 +350,15 @@ void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) } // Unroll the last two entries, to reduce loop overhead: ulong c = cast(ulong)(x[$-3]) * x[$-2] + dest[2*x.length-5]; - dest[2*x.length-5] = cast(uint)c; + dest[2*x.length-5] = cast(uint) c; c >>= 32; c += cast(ulong)(x[$-3]) * x[$-1] + dest[2*x.length-4]; - dest[2*x.length-4] = cast(uint)c; + dest[2*x.length-4] = cast(uint) c; c >>= 32; c += cast(ulong)(x[$-1]) * x[$-2]; - dest[2*x.length-3] = cast(uint)c; + dest[2*x.length-3] = cast(uint) c; c >>= 32; - dest[2*x.length-2] = cast(uint)c; + dest[2*x.length-2] = cast(uint) c; } void multibyteSquare(BigDigit[] result, const(BigDigit) [] x) pure @nogc @safe diff --git a/std/internal/math/biguintx86.d b/std/internal/math/biguintx86.d index 31f2b90c6b8..2f49340c8c5 100644 --- a/std/internal/math/biguintx86.d +++ b/std/internal/math/biguintx86.d @@ -60,7 +60,8 @@ nothrow: (b) compiler bugs prevent the use of .ptr when a frame pointer is used. */ -version(D_InlineAsm_X86) { +version(D_InlineAsm_X86) +{ private: @@ -72,13 +73,16 @@ private: string indexedLoopUnroll(int n, string s) pure @safe { string u; - for (int i = 0; i9 ? ""~ cast(char)('0'+i/10) : "") ~ cast(char)('0' + i%10); int last = 0; - for (int j = 0; j> numbits - * numbits must be in the range 1..31 + * numbits must be in the range 1 .. 31 * This version uses MMX. */ uint multibyteShl(uint [] dest, const uint [] src, uint numbits) pure @@ -468,12 +473,12 @@ L_length1: } /** dest[#] = src[#] >> numbits - * numbits must be in the range 1..31 + * numbits must be in the range 1 .. 31 */ void multibyteShrNoMMX(uint [] dest, const uint [] src, uint numbits) pure { // Timing: Optimal for P6 family. - // 2.0 cycles/int on PPro..PM (limited by execution port p0) + // 2.0 cycles/int on PPro .. PM (limited by execution port p0) // Terrible performance on AMD64, which has 7 cycles for SHRD!! enum { LASTPARAM = 4*4 } // 3* pushes + return address. asm pure nothrow { @@ -517,7 +522,7 @@ L_last: } } -unittest +@system unittest { uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; @@ -538,21 +543,21 @@ unittest aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - uint r = multibyteShl(aa[2..4], aa[2..4], 4); + uint r = multibyteShl(aa[2 .. 4], aa[2 .. 4], 4); assert(aa[0] == 0xF0FF_FFFF && aa[1]==0x1222_2223 && aa[2]==0x5555_5560 && aa[3]==0x9999_99A4 && aa[4]==0xBCCC_CCCD); - assert(r==8); + assert(r == 8); aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - r = multibyteShl(aa[1..4], aa[1..4], 4); + r = multibyteShl(aa[1 .. 4], aa[1 .. 4], 4); assert(aa[0] == 0xF0FF_FFFF && aa[2]==0x5555_5561); assert(aa[3]==0x9999_99A4 && aa[4]==0xBCCC_CCCD); - assert(r==8); + assert(r == 8); assert(aa[1]==0x2222_2230); aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - r = multibyteShl(aa[0..4], aa[1..5], 31); + r = multibyteShl(aa[0 .. 4], aa[1 .. 5], 31); } /** dest[#] = src[#] * multiplier + carry. @@ -619,11 +624,12 @@ L_odd: } } -unittest +@system unittest { uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; - multibyteMul(aa[1..4], aa[1..4], 16, 0); - assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); + multibyteMul(aa[1 .. 4], aa[1 .. 4], 16, 0); + assert(aa[0] == 0xF0FF_FFFF && aa[1] == 0x2222_2230 && + aa[2]==0x5555_5561 && aa[3]==0x9999_99A4 && aa[4]==0x0BCCC_CCCD); } // The inner multiply-and-add loop, together with the Even entry point. @@ -719,9 +725,9 @@ string asmMulAdd_enter_odd(string OP, string M_ADDRESS) pure /** - * dest[#] += src[#] * multiplier OP carry(0..FFFF_FFFF). + * dest[#] += src[#] * multiplier OP carry(0 .. FFFF_FFFF). * where op == '+' or '-' - * Returns carry out of MSB (0..FFFF_FFFF). + * Returns carry out of MSB (0 .. FFFF_FFFF). */ uint multibyteMulAdd(char op)(uint [] dest, const uint [] src, uint multiplier, uint carry) pure { @@ -740,9 +746,12 @@ uint multibyteMulAdd(char op)(uint [] dest, const uint [] src, uint // ESI = src enum string OP = (op=='+')? "add" : "sub"; - version(D_PIC) { + version(D_PIC) + { enum { zero = 0 } - } else { + } + else + { // use p2 (load unit) instead of the overworked p0 or p1 (ALU units) // when initializing registers to zero. __gshared int zero = 0; @@ -786,7 +795,7 @@ L_enter_odd: mixin("asm pure nothrow {" ~ asmMulAdd_enter_odd(OP, "ESP+LASTPARAM") ~ "}"); } -unittest +@system unittest { uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE]; @@ -798,13 +807,14 @@ unittest } /** - Sets result[#] = result[0..left.length] + left[#] * right[#] + Sets result[#] = result[0 .. left.length] + left[#] * right[#] It is defined in this way to allow cache-efficient multiplication. This function is equivalent to: ---- - for (int i = 0; i< right.length; ++i) { - dest[left.length + i] = multibyteMulAdd(dest[i..left.length+i], + for (int i = 0; i< right.length; ++i) + { + dest[left.length + i] = multibyteMulAdd(dest[i .. left.length+i], left, right[i], 0); } ---- @@ -820,9 +830,12 @@ void multibyteMultiplyAccumulate(uint [] dest, const uint[] left, // ESI = end of left. never changes // [ESP] = M = right[i] = multiplier for this pass through the loop. // right.length is changed into dest.ptr+dest.length - version(D_PIC) { + version(D_PIC) + { enum { zero = 0 } - } else { + } + else + { // use p2 (load unit) instead of the overworked p0 or p1 (ALU units) // when initializing registers to zero. __gshared int zero = 0; @@ -887,7 +900,7 @@ L_enter_odd: } /** dest[#] /= divisor. - * overflow is the initial remainder, and must be in the range 0..divisor-1. + * overflow is the initial remainder, and must be in the range 0 .. divisor-1. * divisor must not be a power of 2 (use right shift for that case; * A division by zero will occur if divisor is a power of 2). * Returns the final remainder @@ -922,7 +935,7 @@ uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) pure // Loop from msb to lsb lea EDI, [EDI + 4*EBX]; - mov EBP, EAX; // rem is the input remainder, in 0..divisor-1 + mov EBP, EAX; // rem is the input remainder, in 0 .. divisor-1 // Build the pseudo-inverse of divisor k: 2^64/k // First determine the shift in ecx to get the max number of bits in kinv xor ECX, ECX; @@ -978,7 +991,7 @@ Ld: inc ESI; // Adjust quotient and remainder on single word Lb: cmp EBP, [ESP + LASTPARAM+LOCALS]; //divisor; - jc Lc; // rem in 0..divisor-1, OK + jc Lc; // rem in 0 .. divisor-1, OK sub EBP, [ESP + LASTPARAM+LOCALS]; //divisor; inc ESI; jmp Lb; @@ -1002,17 +1015,17 @@ Lc: } } -unittest +@system unittest { uint [] aa = new uint[101]; for (int i=0; i>= 32; c += cast(ulong)(x[$-3]) * x[$-1] + dest[$-4]; - dest[$-4] = cast(uint)c; + dest[$-4] = cast(uint) c; c >>= 32; length2: c += cast(ulong)(x[$-2]) * x[$-1]; - dest[$-3] = cast(uint)c; + dest[$-3] = cast(uint) c; c >>= 32; - dest[$-2] = cast(uint)c; + dest[$-2] = cast(uint) c; } //dest += src[0]*src[1...$] + src[1]*src[2..$] + ... + src[$-3]*src[$-2..$]+ src[$-2]*src[$-1] @@ -1100,9 +1114,12 @@ void multibyteTriangleAccumulateAsm(uint[] dest, const uint[] src) pure // ESI = end of src. never changes // [ESP] = M = src[i] = multiplier for this pass through the loop. // dest.length is changed into dest.ptr+dest.length - version(D_PIC) { + version(D_PIC) + { enum { zero = 0 } - } else { + } + else + { // use p2 (load unit) instead of the overworked p0 or p1 (ALU units) // when initializing registers to zero. __gshared int zero = 0; @@ -1176,7 +1193,7 @@ length_is_3: // now EDX: EAX = c + x[$-3] * x[$-1] add [EDI-1*4], EAX; // ECX:dest[$-4] += (EDX:EAX) adc ECX, EDX; // ECX holds dest[$-3], it acts as carry for the last row -// do length==2 +// do length == 2 mov EAX, [ESI - 4*2]; mul EAX, [ESI - 4*1]; add ECX, EAX; @@ -1195,10 +1212,10 @@ L_enter_odd: mixin("asm pure nothrow {" ~ asmMulAdd_enter_odd("add", "ESP") ~ "}"); } -unittest +@system unittest { uint [] aa = new uint[200]; - uint [] a = aa[0..100]; + uint [] a = aa[0 .. 100]; uint [] b = new uint [100]; aa[] = 761; a[] = 0; @@ -1206,8 +1223,8 @@ unittest a[3] = 6; b[0]=1; b[1] = 17; - b[50..100]=78; - multibyteTriangleAccumulateAsm(a, b[0..50]); + b[50 .. 100]=78; + multibyteTriangleAccumulateAsm(a, b[0 .. 50]); uint [] c = new uint[100]; c[] = 0; c[1] = 17; @@ -1222,7 +1239,7 @@ unittest b[2]= 0xdaa797ed; b[3] = 0; - multibyteTriangleAccumulateAsm(a[0..8], b[0..4]); + multibyteTriangleAccumulateAsm(a[0 .. 8], b[0 .. 4]); assert(a[1]==0x3a600964); assert(a[2]==0x339974f6); assert(a[3]==0x46736fce); @@ -1231,7 +1248,7 @@ unittest b[3] = 0xe93ff9f4; b[4] = 0x184f03; a[]=0; - multibyteTriangleAccumulateAsm(a[0..14], b[0..7]); + multibyteTriangleAccumulateAsm(a[0 .. 14], b[0 .. 7]); assert(a[3]==0x79fff5c2); assert(a[4]==0xcf384241); assert(a[5]== 0x4a17fc8); @@ -1241,9 +1258,10 @@ unittest void multibyteSquare(BigDigit[] result, const BigDigit [] x) pure { - if (x.length < 4) { + if (x.length < 4) + { // Special cases, not worth doing triangular. - result[x.length] = multibyteMul(result[0..x.length], x, x[0], 0); + result[x.length] = multibyteMul(result[0 .. x.length], x, x[0], 0); multibyteMultiplyAccumulate(result[1..$], x, x[1..$]); return; } @@ -1258,7 +1276,8 @@ void multibyteSquare(BigDigit[] result, const BigDigit [] x) pure multibyteAddDiagonalSquares(result, x); } -version(BignumPerformanceTest) { +version(BignumPerformanceTest) +{ import core.stdc.stdio; int clock() { asm { push EBX; xor EAX, EAX; cpuid; pop EBX; rdtsc; } } @@ -1274,44 +1293,44 @@ void testPerformance() pure // The value for division is quite inconsistent. for (int i=0; i$2 * BIGSUM = $(BIG Σ $2$(SMALL $1)) * CHOOSE = $(BIG () $(SMALL $1)$(SMALL $2) $(BIG )) - * TABLE_SV = + * TABLE_SV =
* * $0
Special Values
* SVH = $(TR $(TH $1) $(TH $2)) @@ -124,7 +124,8 @@ real erfc(real a) real z = -a * a; - if (z < -MAXLOG){ + if (z < -MAXLOG) + { // mtherr( "erfcl", UNDERFLOW ); if (a < 0) return 2.0; else return 0.0; @@ -135,13 +136,14 @@ real erfc(real a) real y = 1.0/x; - if( x < 8.0 ) y = z * rationalPoly(y, P, Q); + if ( x < 8.0 ) y = z * rationalPoly(y, P, Q); else y = z * y * rationalPoly(y * y, R, S); if (a < 0.0L) y = 2.0L - y; - if (y == 0.0) { + if (y == 0.0) + { // mtherr( "erfcl", UNDERFLOW ); if (a < 0) return 2.0; else return 0.0; @@ -161,9 +163,12 @@ real erfce(real x) { real y = 1.0/x; - if (x < 8.0) { + if (x < 8.0) + { return rationalPoly( y, P, Q); - } else { + } + else + { return y * rationalPoly(y*y, R, S); } } @@ -204,7 +209,8 @@ real erf(real x) return x * rationalPoly(z, T, U); } -unittest { +@safe unittest +{ // High resolution test points. enum real erfc0_250 = 0.723663330078125 + 1.0279753638067014931732235184287934646022E-5; enum real erfc0_375 = 0.5958709716796875 + 1.2118885490201676174914080878232469565953E-5; @@ -217,6 +223,10 @@ unittest { enum real erf0_875 = (1-0.215911865234375) - 1.3073705765341685464282101150637224028267E-5; + static bool isNaNWithPayload(real x, ulong payload) @safe pure nothrow @nogc + { + return isNaN(x) && getNaNPayload(x) == payload; + } assert(feqrel(erfc(0.250L), erfc0_250 )>=real.mant_dig-1); assert(feqrel(erfc(0.375L), erfc0_375 )>=real.mant_dig-0); @@ -234,8 +244,8 @@ unittest { assert(isIdentical(erf(-0.0),-0.0)); assert(erf(real.infinity) == 1.0); assert(erf(-real.infinity) == -1.0); - assert(isIdentical(erf(NaN(0xDEF)),NaN(0xDEF))); - assert(isIdentical(erfc(NaN(0xDEF)),NaN(0xDEF))); + assert(isNaNWithPayload(erf(NaN(0xDEF)), 0xDEF)); + assert(isNaNWithPayload(erfc(NaN(0xDEF)), 0xDEF)); assert(isIdentical(erfc(real.infinity),0.0)); assert(erfc(-real.infinity) == 2.0); assert(erfc(0) == 1.0); @@ -262,7 +272,7 @@ real expx2(real x, int sign) Cephes Math Library Release 2.9: June, 2000 Copyright 2000 by Stephen L. Moshier */ - const real M = 32768.0; + const real M = 32_768.0; const real MINV = 3.0517578125e-5L; x = abs(x); @@ -279,7 +289,8 @@ real expx2(real x, int sign) real u = m * m; real u1 = 2 * m * f + f * f; - if (sign < 0) { + if (sign < 0) + { u = -u; u1 = -u1; } @@ -319,20 +330,22 @@ real normalDistributionImpl(real a) real x = a * SQRT1_2; real z = abs(x); - if( z < 1.0 ) + if ( z < 1.0 ) return 0.5L + 0.5L * erf(x); - else { + else + { real y = 0.5L * erfce(z); /* Multiply by exp(-x^2 / 2) */ z = expx2(a, -1); y = y * sqrt(z); - if( x > 0.0L ) + if ( x > 0.0L ) y = 1.0L - y; return y; } } -unittest { +@safe unittest +{ assert(fabs(normalDistributionImpl(1L) - (0.841344746068543))< 0.0000000000000005); assert(isIdentical(normalDistributionImpl(NaN(0x325)), NaN(0x325))); } @@ -352,7 +365,7 @@ assert(isIdentical(normalDistributionImpl(NaN(0x325)), NaN(0x325))); */ real normalDistributionInvImpl(real p) in { - assert(p>=0.0L && p<=1.0L, "Domain error"); + assert(p >= 0.0L && p <= 1.0L, "Domain error"); } body { @@ -406,24 +419,26 @@ static immutable real[8] Q3 = 0x1.e05268dd3c07989ep-3, 0x1.239c6aff14afbf82p+1, 1.0 ]; - if(p<=0.0L || p>=1.0L) + if (p <= 0.0L || p >= 1.0L) { if (p == 0.0L) return -real.infinity; - if( p == 1.0L ) + if ( p == 1.0L ) return real.infinity; return real.nan; // domain error } int code = 1; real y = p; - if( y > (1.0L - EXP_2) ) { + if ( y > (1.0L - EXP_2) ) + { y = 1.0L - y; code = 0; } real x, z, y2, x0, x1; - if ( y > EXP_2 ) { + if ( y > EXP_2 ) + { y = y - 0.5L; y2 = y * y; x = y + y * (y2 * rationalPoly( y2, P0, Q0)); @@ -433,22 +448,29 @@ static immutable real[8] Q3 = x = sqrt( -2.0L * log(y) ); x0 = x - log(x)/x; z = 1.0L/x; - if ( x < 8.0L ) { + if ( x < 8.0L ) + { x1 = z * rationalPoly( z, P1, Q1); - } else if( x < 32.0L ) { + } + else if ( x < 32.0L ) + { x1 = z * rationalPoly( z, P2, Q2); - } else { + } + else + { x1 = z * rationalPoly( z, P3, Q3); } x = x0 - x1; - if ( code != 0 ) { + if ( code != 0 ) + { x = -x; } return x; } -unittest { +@safe unittest +{ // TODO: Use verified test points. // The values below are from Excel 2003. assert(fabs(normalDistributionInvImpl(0.001) - (-3.09023230616779))< 0.00000000000005); diff --git a/std/internal/math/gammafunction.d b/std/internal/math/gammafunction.d index 3f1b997e8d1..557f14ad10d 100644 --- a/std/internal/math/gammafunction.d +++ b/std/internal/math/gammafunction.d @@ -1,14 +1,14 @@ /** * Implementation of the gamma and beta functions, and their integrals. * - * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). * Copyright: Based on the CEPHES math library, which is * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). * Authors: Stephen L. Moshier (original C code). Conversion to D by Don Clugston * * Macros: - * TABLE_SV = + * TABLE_SV =
* * $0
Special Values
* SVH = $(TR $(TH $1) $(TH $2)) @@ -102,16 +102,34 @@ real gammaStirling(real x) real w = 1.0L/x; real y = exp(x); - if ( x > 1024.0L ) { + if ( x > 1024.0L ) + { // For large x, use rational coefficients from the analytical expansion. w = poly(w, LargeStirlingCoeffs); // Avoid overflow in pow() real v = pow( x, 0.5L * x - 0.25L ); y = v * (v / y); } - else { + else + { w = 1.0L + w * poly( w, SmallStirlingCoeffs); - y = pow( x, x - 0.5L ) / y; + static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) + { + // Avoid overflow in pow() for 64-bit reals + if (x > 143.0) + { + real v = pow( x, 0.5 * x - 0.25 ); + y = v * (v / y); + } + else + { + y = pow( x, x - 0.5 ) / y; + } + } + else + { + y = pow( x, x - 0.5L ) / y; + } } y = SQRT2PI * y * w; return y; @@ -199,10 +217,10 @@ real igammaTemmeLarge(real a, real x) ]; // avoid nans when one of the arguments is inf: - if(x == real.infinity && a != real.infinity) + if (x == real.infinity && a != real.infinity) return 0; - if(x != real.infinity && a == real.infinity) + if (x != real.infinity && a == real.infinity) return 1; real sigma = (x - a) / a; @@ -210,16 +228,16 @@ real igammaTemmeLarge(real a, real x) real y = a * phi; real z = sqrt(2 * phi); - if(x < a) + if (x < a) z = -z; real[13] workspace; - foreach(i; 0 .. coef.length) + foreach (i; 0 .. coef.length) workspace[i] = poly(z, coef[i]); real result = poly(1 / a, workspace); result *= exp(-y) / sqrt(2 * PI * a); - if(x < a) + if (x < a) result = -result; result += erfc(sqrt(y)) / 2; @@ -231,7 +249,12 @@ real igammaTemmeLarge(real a, real x) public: /// The maximum value of x for which gamma(x) < real.infinity. -enum real MAXGAMMA = 1755.5483429L; +static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) + enum real MAXGAMMA = 1755.5483429L; +else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) + enum real MAXGAMMA = 171.6243769L; +else + static assert(0, "missing MAXGAMMA for other real types"); /***************************************************** @@ -270,16 +293,18 @@ real gamma(real x) if (isNaN(x)) return x; if (x == -x.infinity) return real.nan; if ( fabs(x) > MAXGAMMA ) return real.infinity; - if (x==0) return 1.0 / x; // +- infinity depending on sign of x, create an exception. + if (x == 0) return 1.0 / x; // +- infinity depending on sign of x, create an exception. q = fabs(x); - if ( q > 13.0L ) { + if ( q > 13.0L ) + { // Large arguments are handled by Stirling's // formula. Large negative arguments are made positive using // the reflection formula. - if ( x < 0.0L ) { + if ( x < 0.0L ) + { if (x < -1/real.epsilon) { // Large negatives lose all precision @@ -293,7 +318,8 @@ real gamma(real x) if ( (intpart & 1) == 0 ) sgngam = -1; z = q - p; - if ( z > 0.5L ) { + if ( z > 0.5L ) + { p += 1.0L; z = q - p; } @@ -301,7 +327,9 @@ real gamma(real x) z = fabs(z) * gammaStirling(q); if ( z <= PI/real.max ) return sgngam * real.infinity; return sgngam * PI/z; - } else { + } + else + { return gammaStirling(x); } } @@ -311,30 +339,38 @@ real gamma(real x) // interval (2,3). z = 1.0L; - while ( x >= 3.0L ) { + while ( x >= 3.0L ) + { x -= 1.0L; z *= x; } - while ( x < -0.03125L ) { + while ( x < -0.03125L ) + { z /= x; x += 1.0L; } - if ( x <= 0.03125L ) { + if ( x <= 0.03125L ) + { if ( x == 0.0L ) return real.nan; - else { - if ( x < 0.0L ) { + else + { + if ( x < 0.0L ) + { x = -x; return z / (x * poly( x, GammaSmallNegCoeffs )); - } else { + } + else + { return z / (x * poly( x, GammaSmallCoeffs )); } } } - while ( x < 2.0L ) { + while ( x < 2.0L ) + { z /= x; x += 1.0L; } @@ -344,10 +380,12 @@ real gamma(real x) return z * poly( x, GammaNumeratorCoeffs ) / poly( x, GammaDenominatorCoeffs ); } -unittest { +@safe unittest +{ // gamma(n) = factorial(n-1) if n is an integer. real fact = 1.0L; - for (int i=1; fact= real.mant_dig-15); @@ -412,7 +450,7 @@ real logGamma(real x) if (isNaN(x)) return x; if (fabs(x) == x.infinity) return x.infinity; - if( x < -34.0L ) + if ( x < -34.0L ) { q = -x; w = logGamma(q); @@ -424,7 +462,8 @@ real logGamma(real x) if ( (intpart & 1) == 0 ) sgngam = -1; z = q - p; - if ( z > 0.5L ) { + if ( z > 0.5L ) + { p += 1.0L; z = p - q; } @@ -436,18 +475,20 @@ real logGamma(real x) return z; } - if( x < 13.0L ) + if ( x < 13.0L ) { z = 1.0L; nx = floor( x + 0.5L ); f = x - nx; - while ( x >= 3.0L ) { + while ( x >= 3.0L ) + { nx -= 1.0L; x = nx + f; z *= x; } - while ( x < 2.0L ) { - if( fabs(x) <= 0.03125 ) + while ( x < 2.0L ) + { + if ( fabs(x) <= 0.03125 ) { if ( x == 0.0L ) return real.infinity; @@ -472,7 +513,7 @@ real logGamma(real x) } // const real MAXLGM = 1.04848146839019521116e+4928L; - // if( x > MAXLGM ) return sgngaml * real.infinity; + // if ( x > MAXLGM ) return sgngaml * real.infinity; const real LOGSQRT2PI = 0.91893853320467274178L; // log( sqrt( 2*pi ) ) @@ -483,7 +524,8 @@ real logGamma(real x) return q ; } -unittest { +@safe unittest +{ assert(isIdentical(logGamma(NaN(0xDEF)), NaN(0xDEF))); assert(logGamma(real.infinity) == real.infinity); assert(logGamma(-1.0) == real.infinity); @@ -514,22 +556,38 @@ unittest { -3.5L, -1.30902099609375L + 1.43111007079536392848E-5L, 1.38887092635952890151E0L ]; // TODO: test derivatives as well. - for (int i=0; i real.mant_dig-5); - if (testpoints[i] real.mant_dig-5); } } assert(logGamma(-50.2) == log(fabs(gamma(-50.2)))); assert(logGamma(-0.008) == log(fabs(gamma(-0.008)))); assert(feqrel(logGamma(-38.8),log(fabs(gamma(-38.8)))) > real.mant_dig-4); - assert(feqrel(logGamma(1500.0L),log(gamma(1500.0L))) > real.mant_dig-2); + static if (real.mant_dig >= 64) // incl. 80-bit reals + assert(feqrel(logGamma(1500.0L),log(gamma(1500.0L))) > real.mant_dig-2); + else static if (real.mant_dig >= 53) // incl. 64-bit reals + assert(feqrel(logGamma(150.0L),log(gamma(150.0L))) > real.mant_dig-2); } private { -enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) -enum real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min*real.epsilon) = log(smallest denormal) +static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) +{ + enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) + enum real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min_normal*real.epsilon) = log(smallest denormal) +} +else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) +{ + enum real MAXLOG = 0x1.62e42fefa39efp+9L; // log(real.max) + enum real MINLOG = -0x1.74385446d71c3p+9L; // log(real.min_normal*real.epsilon) = log(smallest denormal) +} +else + static assert(0, "missing MAXLOG and MINLOG for other real types"); + enum real BETA_BIG = 9.223372036854775808e18L; enum real BETA_BIGINV = 1.084202172485504434007e-19L; } @@ -562,13 +620,15 @@ real betaIncomplete(real aa, real bb, real xx ) if ( isNaN(bb) ) return bb; return real.nan; // domain error } - if (!(xx>0 && xx<1.0)) { + if (!(xx>0 && xx<1.0)) + { if (isNaN(xx)) return xx; if ( xx == 0.0L ) return 0.0; if ( xx == 1.0L ) return 1.0; return real.nan; // domain error } - if ( (bb * xx) <= 1.0L && xx <= 0.95L) { + if ( (bb * xx) <= 1.0L && xx <= 0.95L) + { return betaDistPowerSeries(aa, bb, xx); } real x; @@ -578,21 +638,25 @@ real betaIncomplete(real aa, real bb, real xx ) int flag = 0; /* Reverse a and b if x is greater than the mean. */ - if( xx > (aa/(aa+bb)) ) { + if ( xx > (aa/(aa+bb)) ) + { // here x > aa/(aa+bb) and (bb*x>1 or x>0.95) flag = 1; a = bb; b = aa; xc = xx; x = 1.0L - xx; - } else { + } + else + { a = aa; b = bb; xc = 1.0L - xx; x = xx; } - if( flag == 1 && (b * x) <= 1.0L && x <= 0.95L) { + if ( flag == 1 && (b * x) <= 1.0L && x <= 0.95L) + { // here xx > aa/(aa+bb) and ((bb*xx>1) or xx>0.95) and (aa*(1-xx)<=1) and xx > 0.05 return 1.0 - betaDistPowerSeries(a, b, x); // note loss of precision } @@ -602,9 +666,12 @@ real betaIncomplete(real aa, real bb, real xx ) // One is for x * (a+b+2) < (a+1), // the other is for x * (a+b+2) > (a+1). real y = x * (a+b-2.0L) - (a-1.0L); - if( y < 0.0L ) { + if ( y < 0.0L ) + { w = betaDistExpansion1( a, b, x ); - } else { + } + else + { w = betaDistExpansion2( a, b, x ) / xc; } @@ -614,13 +681,16 @@ real betaIncomplete(real aa, real bb, real xx ) y = a * log(x); real t = b * log(xc); - if ( (a+b) < MAXGAMMA && fabs(y) < MAXLOG && fabs(t) < MAXLOG ) { + if ( (a+b) < MAXGAMMA && fabs(y) < MAXLOG && fabs(t) < MAXLOG ) + { t = pow(xc,b); t *= pow(x,a); t /= a; t *= w; t *= gamma(a+b) / (gamma(a) * gamma(b)); - } else { + } + else + { /* Resort to logarithms. */ y += t + logGamma(a+b) - logGamma(a) - logGamma(b); y += log(w/a); @@ -629,16 +699,21 @@ real betaIncomplete(real aa, real bb, real xx ) /+ // There seems to be a bug in Cephes at this point. // Problems occur for y > MAXLOG, not y < MINLOG. - if( y < MINLOG ) { + if ( y < MINLOG ) + { t = 0.0L; - } else { + } + else + { t = exp(y); } +/ } - if( flag == 1 ) { + if ( flag == 1 ) + { /+ // CEPHES includes this code, but I think it is erroneous. - if( t <= real.epsilon ) { + if ( t <= real.epsilon ) + { t = 1.0L - real.epsilon; } else +/ @@ -663,15 +738,16 @@ real betaIncompleteInv(real aa, real bb, real yy0 ) if (isNaN(yy0)) return yy0; if (isNaN(aa)) return aa; if (isNaN(bb)) return bb; - if( yy0 <= 0.0L ) + if ( yy0 <= 0.0L ) return 0.0L; - if( yy0 >= 1.0L ) + if ( yy0 >= 1.0L ) return 1.0L; x0 = 0.0L; yl = 0.0L; x1 = 1.0L; yh = 1.0L; - if( aa <= 1.0L || bb <= 1.0L ) { + if ( aa <= 1.0L || bb <= 1.0L ) + { dithresh = 1.0e-7L; rflg = 0; a = aa; @@ -681,7 +757,9 @@ real betaIncompleteInv(real aa, real bb, real yy0 ) y = betaIncomplete( a, b, x ); nflg = 0; goto ihalve; - } else { + } + else + { nflg = 0; dithresh = 1.0e-4L; } @@ -690,13 +768,16 @@ real betaIncompleteInv(real aa, real bb, real yy0 ) yp = -normalDistributionInvImpl( yy0 ); - if( yy0 > 0.5L ) { + if ( yy0 > 0.5L ) + { rflg = 1; a = bb; b = aa; y0 = 1.0L - yy0; yp = -yp; - } else { + } + else + { rflg = 0; a = aa; b = bb; @@ -709,14 +790,15 @@ real betaIncompleteInv(real aa, real bb, real yy0 ) - ( 1.0L/(2.0L * b - 1.0L) - 1.0L/(2.0L * a - 1.0L) ) * (lgm + (5.0L/6.0L) - 2.0L/(3.0L * x)); d = 2.0L * d; - if( d < MINLOG ) { + if ( d < MINLOG ) + { x = 1.0L; goto under; } x = a/( a + b * exp(d) ); y = betaIncomplete( a, b, x ); yp = (y - y0)/y0; - if( fabs(yp) < 0.2 ) + if ( fabs(yp) < 0.2 ) goto newt; /* Resort to interval halving if not close enough. */ @@ -724,46 +806,56 @@ ihalve: dir = 0; di = 0.5L; - for( i=0; i<400; i++ ) { - if( i != 0 ) { + for ( i=0; i<400; i++ ) + { + if ( i != 0 ) + { x = x0 + di * (x1 - x0); - if( x == 1.0L ) { + if ( x == 1.0L ) + { x = 1.0L - real.epsilon; } - if( x == 0.0L ) { + if ( x == 0.0L ) + { di = 0.5; x = x0 + di * (x1 - x0); - if( x == 0.0 ) + if ( x == 0.0 ) goto under; } y = betaIncomplete( a, b, x ); yp = (x1 - x0)/(x1 + x0); - if( fabs(yp) < dithresh ) + if ( fabs(yp) < dithresh ) goto newt; yp = (y-y0)/y0; - if( fabs(yp) < dithresh ) + if ( fabs(yp) < dithresh ) goto newt; } - if( y < y0 ) { + if ( y < y0 ) + { x0 = x; yl = y; - if( dir < 0 ) { + if ( dir < 0 ) + { dir = 0; di = 0.5L; - } else if( dir > 3 ) + } else if ( dir > 3 ) di = 1.0L - (1.0L - di) * (1.0L - di); - else if( dir > 1 ) + else if ( dir > 1 ) di = 0.5L * di + 0.5L; else di = (y0 - y)/(yh - yl); dir += 1; - if( x0 > 0.95L ) { - if( rflg == 1 ) { + if ( x0 > 0.95L ) + { + if ( rflg == 1 ) + { rflg = 0; a = aa; b = bb; y0 = yy0; - } else { + } + else + { rflg = 1; a = bb; b = aa; @@ -777,32 +869,38 @@ ihalve: yh = 1.0; goto ihalve; } - } else { + } + else + { x1 = x; - if( rflg == 1 && x1 < real.epsilon ) { + if ( rflg == 1 && x1 < real.epsilon ) + { x = 0.0L; goto done; } yh = y; - if( dir > 0 ) { + if ( dir > 0 ) + { dir = 0; di = 0.5L; } - else if( dir < -3 ) + else if ( dir < -3 ) di = di * di; - else if( dir < -1 ) + else if ( dir < -1 ) di = 0.5L * di; else di = (y - y0)/(yh - yl); dir -= 1; } } - if( x0 >= 1.0L ) { + if ( x0 >= 1.0L ) + { // partial loss of precision x = 1.0L - real.epsilon; goto done; } - if( x <= 0.0L ) { + if ( x <= 0.0L ) + { under: // underflow has occurred x = real.min_normal * real.min_normal; @@ -811,50 +909,63 @@ under: newt: - if ( nflg ) { + if ( nflg ) + { goto done; } nflg = 1; lgm = logGamma(a+b) - logGamma(a) - logGamma(b); - for( i=0; i<15; i++ ) { + for ( i=0; i<15; i++ ) + { /* Compute the function at this point. */ if ( i != 0 ) y = betaIncomplete(a,b,x); - if ( y < yl ) { + if ( y < yl ) + { x = x0; y = yl; - } else if( y > yh ) { + } + else if ( y > yh ) + { x = x1; y = yh; - } else if( y < y0 ) { + } + else if ( y < y0 ) + { x0 = x; yl = y; - } else { + } + else + { x1 = x; yh = y; } - if( x == 1.0L || x == 0.0L ) + if ( x == 1.0L || x == 0.0L ) break; /* Compute the derivative of the function at this point. */ d = (a - 1.0L) * log(x) + (b - 1.0L) * log(1.0L - x) + lgm; - if ( d < MINLOG ) { + if ( d < MINLOG ) + { goto done; } - if ( d > MAXLOG ) { + if ( d > MAXLOG ) + { break; } d = exp(d); /* Compute the step to the next approximation of x. */ d = (y - y0)/d; xt = x - d; - if ( xt <= x0 ) { + if ( xt <= x0 ) + { y = (x - x0) / (x1 - x0); xt = x0 + 0.5L * y * (x - x0); - if( xt <= 0.0L ) + if ( xt <= 0.0L ) break; } - if ( xt >= x1 ) { + if ( xt >= x1 ) + { y = (x1 - x) / (x1 - x0); xt = x1 - 0.5L * y * (x1 - x); if ( xt >= 1.0L ) @@ -869,8 +980,9 @@ newt: goto ihalve; done: - if ( rflg ) { - if( x <= real.epsilon ) + if ( rflg ) + { + if ( x <= real.epsilon ) x = 1.0L - real.epsilon; else x = 1.0L - x; @@ -878,7 +990,7 @@ done: return x; } -unittest { // also tested by the normal distribution +@safe unittest { // also tested by the normal distribution // check NaN propagation assert(isIdentical(betaIncomplete(NaN(0xABC),2,3), NaN(0xABC))); assert(isIdentical(betaIncomplete(7,NaN(0xABC),3), NaN(0xABC))); @@ -898,12 +1010,15 @@ unittest { // also tested by the normal distribution // Test against Mathematica betaRegularized[z,a,b] // These arbitrary points are chosen to give good code coverage. assert(feqrel(betaIncomplete(8, 10, 0.2), 0.010_934_315_234_099_2L) >= real.mant_dig - 5); - assert(feqrel(betaIncomplete(2, 2.5, 0.9),0.989_722_597_604_452_767_171_003_59L) >= real.mant_dig - 1 ); - assert(feqrel(betaIncomplete(1000, 800, 0.5), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 13 ); - assert(feqrel(betaIncomplete(0.0001, 10000, 0.0001),0.999978059362107134278786L) >= real.mant_dig - 18 ); + assert(feqrel(betaIncomplete(2, 2.5, 0.9), 0.989_722_597_604_452_767_171_003_59L) >= real.mant_dig - 1); + static if (real.mant_dig >= 64) // incl. 80-bit reals + assert(feqrel(betaIncomplete(1000, 800, 0.5), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 13); + else + assert(feqrel(betaIncomplete(1000, 800, 0.5), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 14); + assert(feqrel(betaIncomplete(0.0001, 10000, 0.0001), 0.999978059362107134278786L) >= real.mant_dig - 18); assert(betaIncomplete(0.01, 327726.7, 0.545113) == 1.0); assert(feqrel(betaIncompleteInv(8, 10, 0.010_934_315_234_099_2L), 0.2L) >= real.mant_dig - 2); - assert(feqrel(betaIncomplete(0.01, 498.437, 0.0121433),0.99999664562033077636065L) >= real.mant_dig - 1); + assert(feqrel(betaIncomplete(0.01, 498.437, 0.0121433), 0.99999664562033077636065L) >= real.mant_dig - 1); assert(feqrel(betaIncompleteInv(5, 10, 0.2000002972865658842), 0.229121208190918L) >= real.mant_dig - 3); assert(feqrel(betaIncompleteInv(4, 7, 0.8000002209179505L), 0.483657360076904L) >= real.mant_dig - 3); @@ -913,30 +1028,35 @@ unittest { // also tested by the normal distribution // Extensive testing failed to increase the coverage. It seems likely that about // half the code in this function is unnecessary; there is potential for // significant improvement over the original CEPHES code. - - assert(betaIncompleteInv(0.01, 8e-48, 5.45464e-20)==1-real.epsilon); - assert(betaIncompleteInv(0.01, 8e-48, 9e-26)==1-real.epsilon); - - // Beware: a one-bit change in pow() changes almost all digits in the result! - assert(feqrel(betaIncompleteInv(0x1.b3d151fbba0eb18p+1, 1.2265e-19, 2.44859e-18),0x1.c0110c8531d0952cp-1L) > 10); - // This next case uncovered a one-bit difference in the FYL2X instruction - // between Intel and AMD processors. This difference gets magnified by 2^^38. - // WolframAlpha crashes attempting to calculate this. - assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41, 4.6713e18, 0.0813601), - 0x1.f97749d90c7adba8p-63L) >= real.mant_dig - 39); - real a1 = 3.40483; - assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113)== 0x1.ba8c08108aaf5d14p-109); - real b1 = 2.82847e-25; - assert(feqrel(betaIncompleteInv(0.01, b1, 9e-26), 0x1.549696104490aa9p-830L) >= real.mant_dig-10); - - // --- Problematic cases --- - // This is a situation where the series expansion fails to converge - assert( isNaN(betaIncompleteInv(0.12167, 4.0640301659679627772e19L, 0.0813601))); - // This next result is almost certainly erroneous. - // Mathematica states: "(cannot be determined by current methods)" - assert(betaIncomplete(1.16251e20, 2.18e39, 5.45e-20)==-real.infinity); - // WolframAlpha gives no result for this, though indicates that it approximately 1.0 - 1.3e-9 - assert(1- betaIncomplete(0.01, 328222, 4.0375e-5) == 0x1.5f62926b4p-30); + static if (real.mant_dig == 64) // 80-bit reals + { + assert(betaIncompleteInv(0.01, 8e-48, 5.45464e-20) == 1-real.epsilon); + assert(betaIncompleteInv(0.01, 8e-48, 9e-26) == 1-real.epsilon); + + // Beware: a one-bit change in pow() changes almost all digits in the result! + assert(feqrel( + betaIncompleteInv(0x1.b3d151fbba0eb18p+1, 1.2265e-19, 2.44859e-18), + 0x1.c0110c8531d0952cp-1L + ) > 10); + // This next case uncovered a one-bit difference in the FYL2X instruction + // between Intel and AMD processors. This difference gets magnified by 2^^38. + // WolframAlpha crashes attempting to calculate this. + assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41, 4.6713e18, 0.0813601), + 0x1.f97749d90c7adba8p-63L) >= real.mant_dig - 39); + real a1 = 3.40483; + assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113) == 0x1.ba8c08108aaf5d14p-109); + real b1 = 2.82847e-25; + assert(feqrel(betaIncompleteInv(0.01, b1, 9e-26), 0x1.549696104490aa9p-830L) >= real.mant_dig-10); + + // --- Problematic cases --- + // This is a situation where the series expansion fails to converge + assert( isNaN(betaIncompleteInv(0.12167, 4.0640301659679627772e19L, 0.0813601))); + // This next result is almost certainly erroneous. + // Mathematica states: "(cannot be determined by current methods)" + assert(betaIncomplete(1.16251e20, 2.18e39, 5.45e-20) == -real.infinity); + // WolframAlpha gives no result for this, though indicates that it approximately 1.0 - 1.3e-9 + assert(1 - betaIncomplete(0.01, 328222, 4.0375e-5) == 0x1.5f62926b4p-30); + } } @@ -969,7 +1089,8 @@ real betaDistExpansion1(real a, real b, real x ) r = 1.0L; n = 0; const real thresh = 3.0L * real.epsilon; - do { + do + { xk = -( x * k1 * k2 )/( k3 * k4 ); pk = pkm1 + pkm2 * xk; qk = qkm1 + qkm2 * xk; @@ -986,16 +1107,19 @@ real betaDistExpansion1(real a, real b, real x ) qkm2 = qkm1; qkm1 = qk; - if( qk != 0.0L ) + if ( qk != 0.0L ) r = pk/qk; - if( r != 0.0L ) { + if ( r != 0.0L ) + { t = fabs( (ans - r)/r ); ans = r; - } else { + } + else + { t = 1.0L; } - if( t < thresh ) + if ( t < thresh ) return ans; k1 += 1.0L; @@ -1007,20 +1131,22 @@ real betaDistExpansion1(real a, real b, real x ) k7 += 2.0L; k8 += 2.0L; - if( (fabs(qk) + fabs(pk)) > BETA_BIG ) { + if ( (fabs(qk) + fabs(pk)) > BETA_BIG ) + { pkm2 *= BETA_BIGINV; pkm1 *= BETA_BIGINV; qkm2 *= BETA_BIGINV; qkm1 *= BETA_BIGINV; } - if( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) { + if ( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) + { pkm2 *= BETA_BIG; pkm1 *= BETA_BIG; qkm2 *= BETA_BIG; qkm1 *= BETA_BIG; } } - while( ++n < 400 ); + while ( ++n < 400 ); // loss of precision has occurred // mtherr( "incbetl", PLOSS ); return ans; @@ -1052,8 +1178,8 @@ real betaDistExpansion2(real a, real b, real x ) r = 1.0L; int n = 0; const real thresh = 3.0L * real.epsilon; - do { - + do + { xk = -( z * k1 * k2 )/( k3 * k4 ); pk = pkm1 + pkm2 * xk; qk = qkm1 + qkm2 * xk; @@ -1070,15 +1196,16 @@ real betaDistExpansion2(real a, real b, real x ) qkm2 = qkm1; qkm1 = qk; - if( qk != 0.0L ) + if ( qk != 0.0L ) r = pk/qk; - if( r != 0.0L ) { + if ( r != 0.0L ) + { t = fabs( (ans - r)/r ); ans = r; } else t = 1.0L; - if( t < thresh ) + if ( t < thresh ) return ans; k1 += 1.0L; k2 -= 1.0L; @@ -1089,19 +1216,21 @@ real betaDistExpansion2(real a, real b, real x ) k7 += 2.0L; k8 += 2.0L; - if( (fabs(qk) + fabs(pk)) > BETA_BIG ) { + if ( (fabs(qk) + fabs(pk)) > BETA_BIG ) + { pkm2 *= BETA_BIGINV; pkm1 *= BETA_BIGINV; qkm2 *= BETA_BIGINV; qkm1 *= BETA_BIGINV; } - if( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) { + if ( (fabs(qk) < BETA_BIGINV) || (fabs(pk) < BETA_BIGINV) ) + { pkm2 *= BETA_BIG; pkm1 *= BETA_BIG; qkm2 *= BETA_BIG; qkm1 *= BETA_BIG; } - } while( ++n < 400 ); + } while ( ++n < 400 ); // loss of precision has occurred //mtherr( "incbetl", PLOSS ); return ans; @@ -1119,7 +1248,8 @@ real betaDistPowerSeries(real a, real b, real x ) real n = 2.0L; real s = 0.0L; real z = real.epsilon * ai; - while( fabs(v) > z ) { + while ( fabs(v) > z ) + { u = (n - b) * x / n; t *= u; v = t / (a + n); @@ -1130,13 +1260,17 @@ real betaDistPowerSeries(real a, real b, real x ) s += ai; u = a * log(x); - if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG ) { + if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG ) + { t = gamma(a+b)/(gamma(a)*gamma(b)); s = s * t * pow(x,a); - } else { + } + else + { t = logGamma(a+b) - logGamma(a) - logGamma(b) + u + log(s); - if( t < MINLOG ) { + if ( t < MINLOG ) + { s = 0.0L; } else s = exp(t); @@ -1176,7 +1310,7 @@ body { * k=0 | (a+k+1) * */ - if (x==0) + if (x == 0) return 0.0L; if ( (x > 1.0L) && (x > a ) ) @@ -1184,7 +1318,7 @@ body { real ax = a * log(x) - x - logGamma(a); /+ - if( ax < MINLOGL ) return 0; // underflow + if ( ax < MINLOGL ) return 0; // underflow // { mtherr( "igaml", UNDERFLOW ); return( 0.0L ); } +/ ax = exp(ax); @@ -1194,11 +1328,12 @@ body { real c = 1.0L; real ans = 1.0L; - do { + do + { r += 1.0L; c *= x/r; ans += c; - } while( c/ans > real.epsilon ); + } while ( c/ans > real.epsilon ); return ans * ax/a; } @@ -1210,7 +1345,7 @@ in { assert(a > 0); } body { - if (x==0) + if (x == 0) return 1.0L; if ( (x < 1.0L) || (x < a) ) return 1.0L - gammaIncomplete(a,x); @@ -1241,18 +1376,22 @@ body { real qkm1 = z * x; real ans = pkm1/qkm1; - do { + do + { c += 1.0L; y += 1.0L; z += 2.0L; real yc = y * c; pk = pkm1 * z - pkm2 * yc; qk = qkm1 * z - qkm2 * yc; - if( qk != 0.0L ) { + if ( qk != 0.0L ) + { real r = pk/qk; t = fabs( (ans - r)/r ); ans = r; - } else { + } + else + { t = 1.0L; } pkm2 = pkm1; @@ -1262,7 +1401,8 @@ body { const real BIG = 9.223372036854775808e18L; - if ( fabs(pk) > BIG ) { + if ( fabs(pk) > BIG ) + { pkm2 /= BIG; pkm1 /= BIG; qkm2 /= BIG; @@ -1287,11 +1427,11 @@ body { */ real gammaIncompleteComplInv(real a, real p) in { - assert(p>=0 && p<= 1); + assert(p >= 0 && p <= 1); assert(a>0); } body { - if (p==0) return real.infinity; + if (p == 0) return real.infinity; real y0 = p; const real MAXLOGL = 1.1356523406294143949492E4L; @@ -1312,16 +1452,20 @@ body { lgm = logGamma(a); - for( i=0; i<10; i++ ) { - if( x > x0 || x < x1 ) + for ( i=0; i<10; i++ ) + { + if ( x > x0 || x < x1 ) goto ihalve; y = gammaIncompleteCompl(a,x); if ( y < yl || y > yh ) goto ihalve; - if ( y < y0 ) { + if ( y < y0 ) + { x0 = x; yl = y; - } else { + } + else + { x1 = x; yh = y; } @@ -1340,13 +1484,16 @@ body { /* Resort to interval halving if Newton iteration did not converge. */ ihalve: d = 0.0625L; - if ( x0 == real.max ) { - if( x <= 0.0L ) + if ( x0 == real.max ) + { + if ( x <= 0.0L ) x = 1.0L; - while( x0 == real.max ) { + while ( x0 == real.max ) + { x = (1.0L + d) * x; y = gammaIncompleteCompl( a, x ); - if ( y < y0 ) { + if ( y < y0 ) + { x0 = x; yl = y; break; @@ -1357,7 +1504,8 @@ ihalve: d = 0.5L; dir = 0; - for( i=0; i<400; i++ ) { + for ( i=0; i<400; i++ ) + { x = x1 + d * (x0 - x1); y = gammaIncompleteCompl( a, x ); lgm = (x0 - x1)/(x1 + x0); @@ -1368,10 +1516,12 @@ ihalve: break; if ( x <= 0.0L ) break; - if ( y > y0 ) { + if ( y > y0 ) + { x1 = x; yh = y; - if ( dir < 0 ) { + if ( dir < 0 ) + { dir = 0; d = 0.5L; } else if ( dir > 1 ) @@ -1379,10 +1529,13 @@ ihalve: else d = (y0 - yl)/(yh - yl); dir += 1; - } else { + } + else + { x0 = x; yl = y; - if ( dir > 0 ) { + if ( dir > 0 ) + { dir = 0; d = 0.5L; } else if ( dir < -1 ) @@ -1393,13 +1546,14 @@ ihalve: } } /+ - if( x == 0.0L ) + if ( x == 0.0L ) mtherr( "igamil", UNDERFLOW ); +/ return x; } -unittest { +@safe unittest +{ //Values from Excel's GammaInv(1-p, x, 1) assert(fabs(gammaIncompleteComplInv(1, 0.5) - 0.693147188044814) < 0.00000005); assert(fabs(gammaIncompleteComplInv(12, 0.99) - 5.42818075054289) < 0.00000005); @@ -1418,7 +1572,10 @@ assert(gammaIncompleteComplInv(3, 0)==real.infinity); // Fixed a bug that caused gammaIncompleteCompl to return a wrong value when // x was larger than a, but not by much, and both were large: // The value is from WolframAlpha (Gamma[100000, 100001, inf] / Gamma[100000]) -assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109) < 0.000000000005); +static if (real.mant_dig >= 64) // incl. 80-bit reals + assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109) < 0.000000000005); +else + assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109) < 0.00000005); } @@ -1449,36 +1606,44 @@ real digamma(real x) negative = 0; nz = 0.0; - if ( x <= 0.0 ) { + if ( x <= 0.0 ) + { negative = 1; q = x; p = floor(q); - if( p == q ) { + if ( p == q ) + { return real.nan; // singularity. } /* Remove the zeros of tan(PI x) * by subtracting the nearest integer from x */ nz = q - p; - if ( nz != 0.5 ) { - if ( nz > 0.5 ) { + if ( nz != 0.5 ) + { + if ( nz > 0.5 ) + { p += 1.0; nz = q - p; } nz = PI/tan(PI*nz); - } else { + } + else + { nz = 0.0; } x = 1.0 - x; } // check for small positive integer - if ((x <= 13.0) && (x == floor(x)) ) { + if ((x <= 13.0) && (x == floor(x)) ) + { y = 0.0; n = lrint(x); // DAC: CEPHES bugfix. Cephes did this in reverse order, which // created a larger roundoff error. - for (i=n-1; i>0; --i) { + for (i=n-1; i>0; --i) + { y+=1.0L/i; } y -= EULERGAMMA; @@ -1487,12 +1652,14 @@ real digamma(real x) s = x; w = 0.0; - while ( s < 10.0 ) { + while ( s < 10.0 ) + { w += 1.0/s; s += 1.0; } - if ( s < 1.0e17 ) { + if ( s < 1.0e17 ) + { z = 1.0/(s * s); y = z * poly(z, Bn_n); } else @@ -1501,13 +1668,15 @@ real digamma(real x) y = log(s) - 0.5L/s - y - w; done: - if ( negative ) { + if ( negative ) + { y -= nz; } return y; } -unittest { +@safe unittest +{ // Exact values assert(digamma(1.0)== -EULERGAMMA); assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA) >= real.mant_dig-7); @@ -1516,9 +1685,11 @@ unittest { assert(feqrel(digamma(2.5), -EULERGAMMA - 2*LN2 + 2.0 + 2.0L/3) >= real.mant_dig-9); assert(isIdentical(digamma(NaN(0xABC)), NaN(0xABC))); - for (int k=1; k<40; ++k) { + for (int k=1; k<40; ++k) + { real y=0; - for (int u=k; u>=1; --u) { + for (int u=k; u >= 1; --u) + { y += 1.0L/u; } assert(feqrel(digamma(k+1.0), -EULERGAMMA + y) >= real.mant_dig-2); @@ -1547,13 +1718,15 @@ real logmdigamma(real x) real s = x; real w = 0.0; - while ( s < 10.0 ) { + while ( s < 10.0 ) + { w += 1.0/s; s += 1.0; } real y; - if ( s < 1.0e17 ) { + if ( s < 1.0e17 ) + { immutable real z = 1.0/(s * s); y = z * poly(z, Bn_n); } else @@ -1562,32 +1735,35 @@ real logmdigamma(real x) return x == s ? y + 0.5L/s : (log(x/s) + 0.5L/s + y + w); } -unittest { +@safe unittest +{ assert(logmdigamma(-5.0).isNaN()); assert(isIdentical(logmdigamma(NaN(0xABC)), NaN(0xABC))); assert(logmdigamma(0.0) == real.infinity); - for(auto x = 0.01; x < 1.0; x += 0.1) + for (auto x = 0.01; x < 1.0; x += 0.1) assert(approxEqual(digamma(x), log(x) - logmdigamma(x))); - for(auto x = 1.0; x < 15.0; x += 1.0) + for (auto x = 1.0; x < 15.0; x += 1.0) assert(approxEqual(digamma(x), log(x) - logmdigamma(x))); } /** Inverse of the Log Minus Digamma function - * + * * Returns x such $(D log(x) - digamma(x) == y). * * References: * 1. Abramowitz, M., and Stegun, I. A. (1970). * Handbook of mathematical functions. Dover, New York, * pages 258-259, equation 6.3.18. - * + * * Authors: Ilya Yaroshenko */ real logmdigammaInverse(real y) { - import std.numeric: findRoot; - enum maxY = logmdigamma(real.min_normal); - static assert(maxY > 0 && maxY <= real.max); + import std.numeric : findRoot; + // FIXME: should be returned back to enum. + // Fix requires CTFEable `log` on non-x86 targets (check both LDC and GDC). + immutable maxY = logmdigamma(real.min_normal); + assert(maxY > 0 && maxY <= real.max); if (y >= maxY) { @@ -1612,7 +1788,8 @@ real logmdigammaInverse(real y) return y; //NaN } -unittest { +@safe unittest +{ import std.typecons; //WolframAlpha, 22.02.2015 immutable Tuple!(real, real)[5] testData = [ @@ -1623,7 +1800,7 @@ unittest { tuple(1017.644138623741168814449776695062817947092468536L, 1.0L/1024), ]; foreach (test; testData) - assert(approxEqual(logmdigammaInverse(test[0]), test[1], 1e-15, 0)); + assert(approxEqual(logmdigammaInverse(test[0]), test[1], 2e-15, 0)); assert(approxEqual(logmdigamma(logmdigammaInverse(1)), 1, 1e-15, 0)); assert(approxEqual(logmdigamma(logmdigammaInverse(real.min_normal)), real.min_normal, 1e-15, 0)); diff --git a/std/internal/processinit.d b/std/internal/processinit.d index 79df0e767d9..df241089aa0 100644 --- a/std/internal/processinit.d +++ b/std/internal/processinit.d @@ -5,9 +5,9 @@ std.process in order to eliminate cyclic construction errors. Copyright: Copyright 2011 - - License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis and Kato Shoichi - Source: $(PHOBOSSRC std/_datetime.d) + Source: $(PHOBOSSRC std/internal/_processinit.d) +/ module std.internal.processinit; diff --git a/std/internal/scopebuffer.d b/std/internal/scopebuffer.d index f8fd2d8f96e..007e277e352 100644 --- a/std/internal/scopebuffer.d +++ b/std/internal/scopebuffer.d @@ -15,18 +15,18 @@ private import std.traits; /************************************** * ScopeBuffer encapsulates using a local array as a temporary buffer. - * It is initialized with the local array that should be large enough for - * most uses. If the need exceeds the size, ScopeBuffer will resize it - * using malloc() and friends. + * It is initialized with a local array that should be large enough for + * most uses. If the need exceeds that size, ScopeBuffer will reallocate + * the data using its `realloc` function. * - * ScopeBuffer cannot contain more than (uint.max-16)/2 elements. + * ScopeBuffer cannot contain more than `(uint.max-16)/2` elements. * - * ScopeBuffer is an OutputRange. + * ScopeBuffer is an Output Range. * - * Since ScopeBuffer potentially stores elements of type T in malloc'd memory, + * Since ScopeBuffer may store elements of type `T` in `malloc`'d memory, * those elements are not scanned when the GC collects. This can cause - * memory corruption. Do not use ScopeBuffer when elements of type T point - * to the GC heap. + * memory corruption. Do not use ScopeBuffer when elements of type `T` point + * to the GC heap, except when a `realloc` function is provided which supports this. * * Example: --- @@ -43,14 +43,14 @@ void main() textbuf.put('x'); textbuf.put("abc"); assert(textbuf.length == 5); - assert(textbuf[1..3] == "xa"); + assert(textbuf[1 .. 3] == "xa"); assert(textbuf[3] == 'b'); // Can shrink it textbuf.length = 3; - assert(textbuf[0..textbuf.length] == "axa"); + assert(textbuf[0 .. textbuf.length] == "axa"); assert(textbuf[textbuf.length - 1] == 'a'); - assert(textbuf[1..3] == "xa"); + assert(textbuf[1 .. 3] == "xa"); textbuf.put('z'); assert(textbuf[] == "axaz"); @@ -80,7 +80,7 @@ string cat(string s1, string s2) * $(D scope(exit) textbuf.free();) for proper cleanup, and do not refer to a ScopeBuffer * instance's contents after $(D ScopeBuffer.free()) has been called. * - * The realloc parameter defaults to C's realloc(). Another can be supplied to override it. + * The `realloc` parameter defaults to C's `realloc()`. Another can be supplied to override it. * * ScopeBuffer instances may be copied, as in: --- @@ -88,15 +88,15 @@ textbuf = doSomething(textbuf, args); --- * which can be very efficent, but these must be regarded as a move rather than a copy. * Additionally, the code between passing and returning the instance must not throw - * exceptions, otherwise when ScopeBuffer.free() is called, memory may get corrupted. + * exceptions, otherwise when `ScopeBuffer.free()` is called, memory may get corrupted. */ @system struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) - if (isAssignable!T && - !hasElaborateDestructor!T && - !hasElaborateCopyConstructor!T && - !hasElaborateAssign!T) +if (isAssignable!T && + !hasElaborateDestructor!T && + !hasElaborateCopyConstructor!T && + !hasElaborateAssign!T) { import core.stdc.string : memcpy; import core.exception : onOutOfMemoryError; @@ -111,9 +111,10 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) * ubyte[10] tmpbuf = void; * auto sbuf = ScopeBuffer!ubyte(tmpbuf); * --- - * If buf was created by the same realloc passed as a parameter - * to ScopeBuffer, then the contents of ScopeBuffer can be extracted without needing - * to copy them, and ScopeBuffer.free() will not need to be called. + * Note: + * If buf was created by the same `realloc` passed as a parameter + * to `ScopeBuffer`, then the contents of `ScopeBuffer` can be extracted without needing + * to copy them, and `ScopeBuffer.free()` will not need to be called. */ this(T[] buf) in @@ -124,10 +125,10 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) body { this.buf = buf.ptr; - this.bufLen = cast(uint)buf.length; + this.bufLen = cast(uint) buf.length; } - unittest + @system unittest { ubyte[10] tmpbuf = void; auto sbuf = ScopeBuffer!ubyte(tmpbuf); @@ -135,7 +136,7 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) /************************** * Releases any memory used. - * This will invalidate any references returned by the [] operator. + * This will invalidate any references returned by the `[]` operator. * A destructor is not used, because that would make it not POD * (Plain Old Data) and it could not be placed in registers. */ @@ -149,14 +150,9 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) used = 0; } - /**************************** - * Copying of ScopeBuffer is not allowed. - */ - //@disable this(this); - /************************ * Append element c to the buffer. - * This member function makes ScopeBuffer an OutputRange. + * This member function makes `ScopeBuffer` an Output Range. */ void put(T c) { @@ -178,12 +174,12 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) * If $(D const(T)) can be converted to $(D T), then put will accept * $(D const(T)[]) as input. It will accept a $(D T[]) otherwise. */ - private alias CT = Select!(is(const(T) : T), const(T), T); + package alias CT = Select!(is(const(T) : T), const(T), T); /// ditto void put(CT[] s) { const newlen = used + s.length; - assert((cast(ulong)used + s.length) <= uint.max); + assert((cast(ulong) used + s.length) <= uint.max); const len = bufLen; if (newlen > len) { @@ -191,14 +187,14 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) resize(newlen <= len * 2 ? len * 2 : newlen); } buf[used .. newlen] = s[]; - used = cast(uint)newlen; + used = cast(uint) newlen; } /****** - * Retrieve a slice into the result. * Returns: - * A slice into the temporary buffer that is only - * valid until the next put() or ScopeBuffer goes out of scope. + * A slice into the temporary buffer. + * Warning: + * The result is only valid until the next `put()` or `ScopeBuffer` goes out of scope. */ @system inout(T)[] opSlice(size_t lower, size_t upper) inout in @@ -221,7 +217,7 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) /******* * Returns: - * the element at index i. + * The element at index i. */ ref inout(T) opIndex(size_t i) inout { @@ -231,7 +227,7 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) /*** * Returns: - * the number of elements in the ScopeBuffer + * The number of elements in the `ScopeBuffer`. */ @property size_t length() const { @@ -240,7 +236,7 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) /*** * Used to shrink the length of the buffer, - * typically to 0 so the buffer can be reused. + * typically to `0` so the buffer can be reused. * Cannot be used to extend the length of the buffer. */ @property void length(size_t i) @@ -250,7 +246,7 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) } body { - this.used = cast(uint)i; + this.used = cast(uint) i; } alias opDollar = length; @@ -279,8 +275,8 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) memcpy(newBuf, buf, used * T.sizeof); debug(ScopeBuffer) buf[0 .. bufLen] = 0; } - buf = cast(T*)newBuf; - bufLen = cast(uint)newsize; + buf = cast(T*) newBuf; + bufLen = cast(uint) newsize; /* This function is called only rarely, * inlining results in poorer register allocation. @@ -293,7 +289,7 @@ struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) } } -unittest +@system unittest { import core.stdc.stdio; import std.range; @@ -310,16 +306,16 @@ unittest textbuf.put('x'); textbuf.put("abc"); // tickle put([])'s resize assert(textbuf.length == 5); - assert(textbuf[1..3] == "xa"); + assert(textbuf[1 .. 3] == "xa"); assert(textbuf[3] == 'b'); textbuf.length = textbuf.length - 1; - assert(textbuf[0..textbuf.length] == "axab"); + assert(textbuf[0 .. textbuf.length] == "axab"); textbuf.length = 3; - assert(textbuf[0..textbuf.length] == "axa"); + assert(textbuf[0 .. textbuf.length] == "axa"); assert(textbuf[textbuf.length - 1] == 'a'); - assert(textbuf[1..3] == "xa"); + assert(textbuf[1 .. 3] == "xa"); textbuf.put(cast(dchar)'z'); assert(textbuf[] == "axaz"); @@ -336,7 +332,7 @@ unittest } -unittest +@system unittest { string cat(string s1, string s2) { @@ -354,31 +350,25 @@ unittest } // const -unittest +@system unittest { char[10] tmpbuf = void; auto textbuf = ScopeBuffer!char(tmpbuf); scope(exit) textbuf.free(); - foreach(i; 0..10) textbuf.put('w'); + foreach (i; 0 .. 10) textbuf.put('w'); const csb = textbuf; const elem = csb[3]; - const slice0 = csb[0..5]; + const slice0 = csb[0 .. 5]; const slice1 = csb[]; } /********************************* - * This is a slightly simpler way to create a ScopeBuffer instance - * that uses type deduction. + * Creates a `ScopeBuffer` instance using type deduction - see + * $(LREF .ScopeBuffer.this) for details. * Params: * tmpbuf = the initial buffer to use * Returns: - * an instance of ScopeBuffer - * Example: ---- -ubyte[10] tmpbuf = void; -auto sb = scopeBuffer(tmpbuf); -scope(exit) sp.free(); ---- + * An instance of `ScopeBuffer`. */ auto scopeBuffer(T)(T[] tmpbuf) @@ -386,14 +376,15 @@ auto scopeBuffer(T)(T[] tmpbuf) return ScopeBuffer!T(tmpbuf); } -unittest +/// +@system unittest { ubyte[10] tmpbuf = void; auto sb = scopeBuffer(tmpbuf); scope(exit) sb.free(); } -unittest +@system unittest { ScopeBuffer!(int*) b; int*[] s; diff --git a/std/internal/test/dummyrange.d b/std/internal/test/dummyrange.d index 8e97320d05d..6a6b08cb99b 100644 --- a/std/internal/test/dummyrange.d +++ b/std/internal/test/dummyrange.d @@ -4,8 +4,8 @@ Used with the dummy ranges for testing higher order ranges. */ module std.internal.test.dummyrange; +import std.meta; import std.typecons; -import std.typetuple; import std.range.primitives; enum RangeType @@ -28,23 +28,41 @@ enum ReturnBy Value } +import std.traits : isArray; + // Range that's useful for testing other higher order ranges, // can be parametrized with attributes. It just dumbs down an array of -// numbers 1..10. -struct DummyRange(ReturnBy _r, Length _l, RangeType _rt) +// numbers 1 .. 10. +struct DummyRange(ReturnBy _r, Length _l, RangeType _rt, T = uint[]) +if (isArray!T) { + private static immutable uinttestData = + [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; // These enums are so that the template params are visible outside // this instantiation. enum r = _r; enum l = _l; enum rt = _rt; - uint[] arr = [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; + static if (is(T == uint[])) + { + T arr = uinttestData; + } + else + { + T arr; + } + + alias RetType = ElementType!(T); + alias RetTypeNoAutoDecoding = ElementEncodingType!(T); void reinit() { // Workaround for DMD bug 4378 - arr = [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; + static if (is(T == uint[])) + { + arr = uinttestData.dup; + } } void popFront() @@ -57,27 +75,27 @@ struct DummyRange(ReturnBy _r, Length _l, RangeType _rt) return arr.length == 0; } - static if(r == ReturnBy.Reference) + static if (r == ReturnBy.Reference) { - @property ref inout(uint) front() inout + @property ref inout(RetType) front() inout { return arr[0]; } - - @property void front(uint val) - { - arr[0] = val; - } } else { - @property uint front() const + @property RetType front() const { return arr[0]; } + + @property void front(RetTypeNoAutoDecoding val) + { + arr[0] = val; + } } - static if(rt >= RangeType.Forward) + static if (rt >= RangeType.Forward) { @property typeof(this) save() { @@ -85,66 +103,80 @@ struct DummyRange(ReturnBy _r, Length _l, RangeType _rt) } } - static if(rt >= RangeType.Bidirectional) + static if (rt >= RangeType.Bidirectional) { void popBack() { arr = arr[0..$ - 1]; } - static if(r == ReturnBy.Reference) + static if (r == ReturnBy.Reference) { - @property ref inout(uint) back() inout + @property ref inout(RetType) back() inout { return arr[$ - 1]; } - - @property void back(uint val) - { - arr[$ - 1] = val; - } - } else { - @property uint back() const + @property RetType back() const { return arr[$ - 1]; } + + @property void back(RetTypeNoAutoDecoding val) + { + arr[$ - 1] = val; + } } } - static if(rt >= RangeType.Random) + static if (rt >= RangeType.Random) { - static if(r == ReturnBy.Reference) + static if (r == ReturnBy.Reference) { - ref inout(uint) opIndex(size_t index) inout + ref inout(RetType) opIndex(size_t index) inout { return arr[index]; } - - void opIndexAssign(uint val, size_t index) - { - arr[index] = val; - } } else { - uint opIndex(size_t index) const + RetType opIndex(size_t index) const { return arr[index]; } + + RetType opIndexAssign(RetTypeNoAutoDecoding val, size_t index) + { + return arr[index] = val; + } + + RetType opIndexOpAssign(string op)(RetTypeNoAutoDecoding value, size_t index) + { + mixin("return arr[index] " ~ op ~ "= value;"); + } + + RetType opIndexUnary(string op)(size_t index) + { + mixin("return " ~ op ~ "arr[index];"); + } } typeof(this) opSlice(size_t lower, size_t upper) { auto ret = this; - ret.arr = arr[lower..upper]; + ret.arr = arr[lower .. upper]; return ret; } + + typeof(this) opSlice() + { + return this; + } } - static if(l == Length.Yes) + static if (l == Length.Yes) { @property size_t length() const { @@ -157,7 +189,7 @@ struct DummyRange(ReturnBy _r, Length _l, RangeType _rt) enum dummyLength = 10; -alias AllDummyRanges = TypeTuple!( +alias AllDummyRanges = AliasSeq!( DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), @@ -172,6 +204,24 @@ alias AllDummyRanges = TypeTuple!( DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional) ); +template AllDummyRangesType(T) +{ + alias AllDummyRangesType = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward, T), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional, T), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward, T), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Input, T), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward, T), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional, T) + ); +} + /** Tests whether forward, bidirectional and random access properties are propagated properly from the base range(s) R to the higher order range @@ -180,11 +230,11 @@ order ranges. */ template propagatesRangeType(H, R...) { - static if(allSatisfy!(isRandomAccessRange, R)) + static if (allSatisfy!(isRandomAccessRange, R)) enum bool propagatesRangeType = isRandomAccessRange!H; - else static if(allSatisfy!(isBidirectionalRange, R)) + else static if (allSatisfy!(isBidirectionalRange, R)) enum bool propagatesRangeType = isBidirectionalRange!H; - else static if(allSatisfy!(isForwardRange, R)) + else static if (allSatisfy!(isForwardRange, R)) enum bool propagatesRangeType = isForwardRange!H; else enum bool propagatesRangeType = isInputRange!H; @@ -192,7 +242,7 @@ template propagatesRangeType(H, R...) template propagatesLength(H, R...) { - static if(allSatisfy!(hasLength, R)) + static if (allSatisfy!(hasLength, R)) enum bool propagatesLength = hasLength!H; else enum bool propagatesLength = !hasLength!H; @@ -213,16 +263,8 @@ class ReferenceInputRange(T) } /** -Reference forward range +Infinite input range */ -class ReferenceForwardRange(T) : ReferenceInputRange!T -{ - this(Range)(Range r) if (isInputRange!Range) {super(r);} - final @property ReferenceForwardRange save() - {return new ReferenceForwardRange!T(_payload);} -} - -//Infinite input range class ReferenceInfiniteInputRange(T) { this(T first = T.init) {_val = first;} @@ -232,10 +274,292 @@ class ReferenceInfiniteInputRange(T) protected T _val; } -//Infinite forward range +/** +Reference forward range +*/ +class ReferenceForwardRange(T) : ReferenceInputRange!T +{ + this(Range)(Range r) if (isInputRange!Range) {super(r);} + final @property auto save(this This)() {return new This( _payload);} +} + +/** +Infinite forward range +*/ class ReferenceInfiniteForwardRange(T) : ReferenceInfiniteInputRange!T { this(T first = T.init) {super(first);} final @property ReferenceInfiniteForwardRange save() {return new ReferenceInfiniteForwardRange!T(_val);} } + +/** +Reference bidirectional range +*/ +class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T +{ + this(Range)(Range r) if (isInputRange!Range) {super(r);} + final @property ref T back(){return _payload.back;} + final void popBack(){_payload.popBack();} +} + +@safe unittest +{ + static assert(isInputRange!(ReferenceInputRange!int)); + static assert(isInputRange!(ReferenceInfiniteInputRange!int)); + + static assert(isForwardRange!(ReferenceForwardRange!int)); + static assert(isForwardRange!(ReferenceInfiniteForwardRange!int)); + + static assert(isBidirectionalRange!(ReferenceBidirectionalRange!int)); +} + +private: + +pure struct Cmp(T) +if (is(T == uint)) +{ + static auto iota(size_t low = 1, size_t high = 11) + { + import std.range : iota; + return iota(cast(uint) low, cast(uint) high); + } + + static void initialize(ref uint[] arr) + { + import std.array : array; + arr = iota().array; + } + + static bool function(uint,uint) cmp = function(uint a, uint b) { return a == b; }; + + enum dummyValue = 1337U; + enum dummyValueRslt = 1337U * 2; +} + +pure struct Cmp(T) +if (is(T == double)) +{ + import std.math : approxEqual; + + static auto iota(size_t low = 1, size_t high = 11) + { + import std.range : iota; + return iota(cast(double) low, cast(double) high, 1.0); + } + + static void initialize(ref double[] arr) + { + import std.array : array; + arr = iota().array; + } + + alias cmp = approxEqual!(double,double); + + enum dummyValue = 1337.0; + enum dummyValueRslt = 1337.0 * 2.0; +} + +struct TestFoo +{ + int a; + + bool opEquals(const ref TestFoo other) const + { + return this.a == other.a; + } + + TestFoo opBinary(string op)(TestFoo other) + { + TestFoo ret = this; + mixin("ret.a " ~ op ~ "= other.a;"); + return ret; + } + + TestFoo opOpAssign(string op)(TestFoo other) + { + mixin("this.a " ~ op ~ "= other.a;"); + return this; + } +} + +pure struct Cmp(T) +if (is(T == TestFoo)) +{ + import std.math : approxEqual; + + static auto iota(size_t low = 1, size_t high = 11) + { + import std.range : iota; + import std.algorithm.iteration : map; + return iota(cast(int) low, cast(int) high).map!(a => TestFoo(a)); + } + + static void initialize(ref TestFoo[] arr) + { + import std.array : array; + arr = iota().array; + } + + static bool function(TestFoo,TestFoo) cmp = function(TestFoo a, TestFoo b) + { + return a.a == b.a; + }; + + @property static TestFoo dummyValue() + { + return TestFoo(1337); + } + + @property static TestFoo dummyValueRslt() + { + return TestFoo(1337 * 2); + } +} + +@system unittest +{ + import std.algorithm.comparison : equal; + import std.range : iota, retro, repeat; + import std.traits : Unqual; + + static void testInputRange(T,Cmp)() + { + T it; + Cmp.initialize(it.arr); + for (size_t numRuns = 0; numRuns < 2; ++numRuns) + { + if (numRuns == 1) + { + static if (is(Unqual!(ElementType!(T)) == uint)) + { + it.reinit(); + } + + Cmp.initialize(it.arr); + } + + assert(equal!(Cmp.cmp)(it, Cmp.iota(1, 11))); + + static if (hasLength!T) + { + assert(it.length == 10); + } + + assert(!Cmp.cmp(it.front, Cmp.dummyValue)); + auto s = it.front; + it.front = Cmp.dummyValue; + assert(Cmp.cmp(it.front, Cmp.dummyValue)); + it.front = s; + + auto cmp = Cmp.iota(1,11); + + size_t jdx = 0; + while (!it.empty && !cmp.empty) + { + static if (hasLength!T) + { + assert(it.length == 10 - jdx); + } + + assert(Cmp.cmp(it.front, cmp.front)); + it.popFront(); + cmp.popFront(); + + ++jdx; + } + + assert(it.empty); + assert(cmp.empty); + } + + } + + static void testForwardRange(T,Cmp)() + { + T it; + Cmp.initialize(it.arr); + auto s = it.save(); + s.popFront(); + assert(!Cmp.cmp(s.front, it.front)); + } + + static void testBidirectionalRange(T,Cmp)() + { + T it; + Cmp.initialize(it.arr); + assert(equal!(Cmp.cmp)(it.retro, Cmp.iota().retro)); + + auto s = it.back; + assert(!Cmp.cmp(s, Cmp.dummyValue)); + it.back = Cmp.dummyValue; + assert( Cmp.cmp(it.back, Cmp.dummyValue)); + it.back = s; + } + + static void testRandomAccessRange(T,Cmp)() + { + T it; + Cmp.initialize(it.arr); + size_t idx = 0; + foreach (jt; it) + { + assert(it[idx] == jt); + + T copy = it[idx .. $]; + auto cmp = Cmp.iota(idx + 1, it.length + 1); + assert(equal!(Cmp.cmp)(copy, cmp)); + + ++idx; + } + + { + auto copy = it; + copy.arr = it.arr.dup; + for (size_t i = 0; i < copy.length; ++i) + { + copy[i] = Cmp.dummyValue; + copy[i] += Cmp.dummyValue; + } + assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length))); + } + + static if (it.r == ReturnBy.Reference) + { + T copy; + copy.arr = it.arr.dup; + for (size_t i = 0; i < copy.length; ++i) + { + copy[i] = Cmp.dummyValue; + copy[i] += Cmp.dummyValue; + } + + assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length))); + } + } + + import std.meta : AliasSeq; + + foreach (S; AliasSeq!(uint, double, TestFoo)) + { + foreach (T; AllDummyRangesType!(S[])) + { + testInputRange!(T,Cmp!S)(); + + static if (isForwardRange!T) + { + testForwardRange!(T,Cmp!S)(); + } + + static if (isBidirectionalRange!T) + { + testBidirectionalRange!(T,Cmp!S)(); + } + + static if (isRandomAccessRange!T) + { + testRandomAccessRange!(T,Cmp!S)(); + } + } + } +} diff --git a/std/internal/test/uda.d b/std/internal/test/uda.d new file mode 100644 index 00000000000..88e3a1b5ae9 --- /dev/null +++ b/std/internal/test/uda.d @@ -0,0 +1,16 @@ +/** +For testing only. +Provides a struct with UDA's defined in an external module. +Useful for validating behavior with member privacy. +*/ +module std.internal.test.uda; + +enum Attr; + +struct HasPrivateMembers +{ + @Attr int a; + int b; + @Attr private int c; + private int d; +} diff --git a/std/internal/unicode_comp.d b/std/internal/unicode_comp.d index 966a83e0355..fa0fcb5dd78 100644 --- a/std/internal/unicode_comp.d +++ b/std/internal/unicode_comp.d @@ -1,34 +1,2984 @@ module std.internal.unicode_comp; import std.internal.unicode_tables; -@safe pure nothrow @nogc: +@safe pure nothrow @nogc package(std): -static if(size_t.sizeof == 8) { -//6976 bytes -enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 7, 6)([ 0x0, 0x20, 0x120], [ 0x100, 0x400, 0x1240], [ 0x402030202020100, 0x206020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x20001, 0x300000000, 0x5000400000000, 0x8000000070006, 0xb0000000a0009, 0xe0000000d000c, 0x11000f0010000f, 0x11000f0011000f, 0x1100000011000f, 0x11000f00120000, 0x13000000110000, 0x17001600150014, 0x1b001a00190018, 0x1d0000001c, 0x0, 0x0, 0x1e0000, 0x0, 0x0, 0x0, 0x2000000000001f, 0x2100000000, 0x22, 0x240023, 0x28002700260025, 0x2a000000000029, 0x2b000000000000, 0x0, 0x0, 0x2c000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d000000000000, 0x2f0000002e0000, 0x0, 0x0, 0x3100000030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x34003300320000, 0x0, 0x36000000000035, 0x3a003900380037, 0x3c003b00000000, 0x3d000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x40000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4200350000, 0x3a000000000043, 0x0, 0x0, 0x0, 0x0, 0x4400000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4600450000, 0x470000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0xdcdce8e6e6e6e6e6, 0xdcdcdcdcd8e8dcdc, 0xcadcdcdcdccacadc, 0xdcdcdcdcdcdcdcca, 0x1010101dcdcdcdc, 0xe6e6e6dcdcdcdc01, 0xdce6f0e6e6e6e6e6, 0xdcdce6e6e6dcdc, 0xe6dcdcdcdce6e6e6, 0xe9eaeae9e6dcdce8, 0xe6e6e6e6e6e9eaea, 0xe6e6e6e6e6e6e6e6, 0x0, 0x0, 0xe6e6e6e6e6000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6dce6e6e6e6dc00, 0xe6e6e6e6dcdee6e6, 0xdcdcdcdcdcdce6e6, 0xe6e4dee6e6dce6e6, 0x11100f0e0d0c0b0a, 0x1700161514131312, 0x1200dce600191800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6e6, 0x201f1e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f1e1d1c1b000000, 0xe6dcdce6e6222120, 0xdce6e6dce6e6e6e6, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0, 0xe6e6000000000000, 0xe60000e6e6e6e6e6, 0xe60000e6dce6e6e6, 0xdce6e6dc00e6, 0x0, 0x0, 0x0, 0x0, 0x2400, 0x0, 0x0, 0x0, 0xdce6e6dce6e6dce6, 0xe6dce6dcdce6dcdc, 0xe6dce6dce6dce6e6, 0xe6e6dc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6000000, 0xe6dce6e6, 0x0, 0x0, 0x0, 0xe6e6000000000000, 0xe6e6e6e6e600e6e6, 0xe6e6e600e6e6e6e6, 0xe6e6e6e6e600, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdcdcdc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6dce6e600000000, 0xdcdcdce6e6e6dce6, 0xe6dce6e6e61d1c1b, 0xe6e6e6e6dcdce6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x700000000, 0x0, 0x90000000000, 0xe6e6dce600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000000000, 0x5b540000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96767, 0x0, 0x6b6b6b6b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7676, 0x0, 0x7a7a7a7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdcdc, 0x0, 0x0, 0xdc00dc0000000000, 0xd800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8400828100, 0x828282820000, 0xe6e60009e6e60082, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x700000000000000, 0x90900, 0x0, 0xdc0000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e60000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900000000, 0x0, 0x0, 0x0, 0x900000000, 0x0, 0x0, 0x0, 0x90000, 0xe60000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdce6de00, 0x0, 0x0, 0xe600000000000000, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0xe6e6e60000000000, 0xdc0000e6e6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x700000000, 0x0, 0x900000000, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6dce6000000, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000000000, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x700000000000000, 0x0, 0x0, 0x0, 0xdcdcdc0100e6e6e6, 0xdcdcdcdce6e6dcdc, 0x1010101010100e6, 0xdc0000000001, 0xe600000000, 0x0, 0xe6e6e6e6e6dce6e6, 0xdcd6eae6e6dce6e6, 0xe6e6e6e6e6e6e6ca, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6, 0x0, 0x0, 0xdce6dce900000000, 0x0, 0x0, 0xe6e6e6e60101e6e6, 0xe6e6010101, 0xe60101000000e600, 0xdcdcdcdc0101e6dc, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe600000000000000, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900000000000000, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0e0dee8e4da0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe600000000000000, 0xe6e6e6e600000000, 0xe6e6e6e6e6e6, 0x0, 0x0, 0x0, 0xe600000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6, 0x0, 0x9000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900000000, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdcdcdc000000, 0x0, 0x0, 0x0, 0x0, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe60000dce6e600e6, 0xe6e60000000000e6, 0xe600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc0000000000, 0x0, 0xe600dc0000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900000000dc01e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70900, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x909000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x709000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1d8d80000000000, 0xd8d8e20000000101, 0xd8d8d8, 0xdcdcdcdcdc000000, 0xe6e6e60000dcdcdc, 0xdcdce6e6, 0x0, 0x0, 0x0, 0xe6e6e6e60000, 0x0, 0x0, 0xe6e6e60000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -enum composeIdxMask = (1<<11)-1, composeCntShift = 11; -enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)([ 0x0, 0x400], [ 0x1000, 0x2000], [ 0x3000200010000, 0x7000600050004, 0x7000700070008, 0xa000700090007, 0x70007000c000b, 0x7000700070007, 0x700070007000d, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x700070007000e, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff080208010800, 0x281618138003ffff, 0x383308328821301b, 0x285108507841383a, 0x8068485f185c3056, 0x3882407affff1078, 0x30a510a398903889, 0xffff30b648ad10ab, 0xffffffffffffffff, 0x28cf18cc80bcffff, 0x38ec08eb88da30d4, 0x290b110970fb40f3, 0x8122491919163110, 0x393c4134ffff1132, 0x3960115e994b4143, 0xffff317351691167, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff1979, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff217cffffffff, 0x984118209810980, 0xffff2185ffffffff, 0x989ffffffffffff, 0xffffffffffffffff, 0xffff0991198e218a, 0xffffffffffff0992, 0xffffffffffff2193, 0xffff2197ffffffff, 0x99f119d099c099b, 0xffff21a0ffffffff, 0x9a4ffffffffffff, 0xffffffffffffffff, 0xffff09ac19a921a5, 0xffffffffffff09ad, 0xffffffffffff21ae, 0x21b621b2ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x11bc11baffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff11c011be, 0xffffffffffffffff, 0xffffffffffffffff, 0x9c309c2ffffffff, 0xffffffffffffffff, 0xffffffff09c509c4, 0xffffffffffffffff, 0x9c909c809c709c6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x9caffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff29d029cb, 0xffffffffffffffff, 0xffffffffffffffff, 0x29d5ffffffffffff, 0xffffffffffff29da, 0x9dfffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x9e109e0ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x9e309e2ffffffff, 0xffffffff09e509e4, 0x9e709e6ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff09e8ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff39e9ffff, 0x29f4ffff21f0ffff, 0xffffffff39f9ffff, 0x2200ffffffffffff, 0xffffffff0a04ffff, 0xffffffff3205ffff, 0xffffffff2a0bffff, 0xffff0a11ffff0a10, 0xffffffff4212ffff, 0x321effff221affff, 0xffffffff4224ffff, 0x222cffffffffffff, 0xffffffff1230ffff, 0xffffffff4232ffff, 0x1a431a40323affff, 0xffff0a46ffffffff, 0xffff1247ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0a49ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xa4cffffffff124a, 0xa5212501a4dffff, 0xffff0a57ffff2253, 0xffff0a58ffffffff, 0x2259ffffffffffff, 0xa5dffffffffffff, 0xa5effffffffffff, 0xffffffff0a5fffff, 0xa62ffffffff1260, 0xa6812661a63ffff, 0xffff0a6dffff2269, 0xffff0a6effffffff, 0x226fffffffffffff, 0xa73ffffffffffff, 0xa74ffffffffffff, 0xffffffff0a75ffff, 0xffffffffffffffff, 0xffff0a76ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a780a77, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a7a0a79, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a7c0a7b, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1a7dffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0a81ffff0a80, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a82ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0a83ffffffff, 0xffffffff0a84ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff0a85, 0xffffffffffffffff, 0xa87ffffffff0a86, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1288ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1a8affffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0a8dffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xa90128effffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0a91ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xa92ffffffffffff, 0xffffffffffffffff, 0xffff1a93ffffffff, 0xffff0a96ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xa991297ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff1a9affff, 0xffffffffffff0a9d, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a9effff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xaa0ffff0a9fffff, 0xaa2ffff0aa1ffff, 0xffffffff0aa3ffff, 0xffffffff0aa4ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0aa5ffffffff, 0xaa80aa7ffff0aa6, 0xffff0aa9ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xaab0aaaffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xaad0aacffffffff, 0xffffffffffffffff, 0xaaf0aaeffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff12b212b0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0ab50ab4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0ab70ab6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xac10ac022bc22b8, 0xac50ac40ac30ac2, 0xacf0ace22ca22c6, 0xad30ad20ad10ad0, 0xffffffff12d612d4, 0xffffffffffffffff, 0xffffffff12da12d8, 0xffffffffffffffff, 0xae50ae422e022dc, 0xae90ae80ae70ae6, 0xaf30af222ee22ea, 0xaf70af60af50af4, 0xffffffff1afb1af8, 0xffffffffffffffff, 0xffffffff1b011afe, 0xffffffffffffffff, 0xffffffff13061304, 0xffffffffffffffff, 0xffffffff130a1308, 0xffffffffffffffff, 0xffffffff1b0f1b0c, 0xffffffffffffffff, 0xffffffff1b12ffff, 0xffffffffffffffff, 0xb1e0b1d23192315, 0xb220b210b200b1f, 0xb2c0b2b23272323, 0xb300b2f0b2e0b2d, 0xffffffffffff0b31, 0xffffffffffff0b32, 0xffffffffffffffff, 0xffffffffffff0b33, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b34ffffffff, 0xffffffffffffffff, 0x1b35ffffffffffff, 0xffffffffffffffff, 0xffff0b38ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b39ffffffff, 0xffffffffffffffff, 0xffff1b3affffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b3effff0b3d, 0xffffffffffff0b3f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b41ffff0b40, 0xffffffffffff0b42, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xb43ffffffffffff, 0xffffffffffffffff, 0xb45ffffffff0b44, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xb46ffffffffffff, 0xffffffff0b47ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff0b48, 0xb49ffffffffffff, 0xffffffff0b4affff, 0xffffffffffff0b4b, 0xffffffff0b4cffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0b4dffff, 0xffffffff0b4f0b4e, 0xffffffffffffffff, 0xffffffffffffffff, 0xb510b50ffffffff, 0xb530b52ffffffff, 0xb550b54ffffffff, 0xffffffff0b570b56, 0xb590b58ffffffff, 0xb5b0b5affffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b5d0b5cffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b5effffffff, 0xffffffffffffffff, 0xb61ffff0b600b5f, 0xffffffffffffffff, 0xb630b62ffffffff, 0xffffffff0b650b64, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b66ffffffff, 0xb67ffffffffffff, 0xb69ffff0b68ffff, 0xb6bffff0b6affff, 0xb6dffff0b6cffff, 0xb6fffff0b6effff, 0xb71ffff0b70ffff, 0xffffffff0b72ffff, 0xffff0b74ffff0b73, 0xffffffffffff0b75, 0x1376ffffffffffff, 0xffff1378ffffffff, 0xffffffff137affff, 0x137effffffff137c, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0b80ffff, 0xffffffffffffffff, 0xffff0b81ffffffff, 0xb82ffffffffffff, 0xb84ffff0b83ffff, 0xb86ffff0b85ffff, 0xb88ffff0b87ffff, 0xb8affff0b89ffff, 0xb8cffff0b8bffff, 0xffffffff0b8dffff, 0xffff0b8fffff0b8e, 0xffffffffffff0b90, 0x1391ffffffffffff, 0xffff1393ffffffff, 0xffffffff1395ffff, 0x1399ffffffff1397, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xb9bffffffffffff, 0xffff0b9e0b9d0b9c, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0b9fffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xba1ffff0ba0ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0ba2ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0ba40ba3ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); -@property immutable(CompEntry[]) compositionTable() +static if (size_t.sizeof == 8) { -alias CE = CompEntry; -static immutable CE[] t = [CE(0x00338, 0x0226e),CE(0x00338, 0x02260),CE(0x00338, 0x0226f),CE(0x00300, 0x000c0),CE(0x00301, 0x000c1),CE(0x00302, 0x000c2),CE(0x00303, 0x000c3),CE(0x00304, 0x00100),CE(0x00306, 0x00102),CE(0x00307, 0x00226),CE(0x00308, 0x000c4),CE(0x00309, 0x01ea2),CE(0x0030a, 0x000c5),CE(0x0030c, 0x001cd),CE(0x0030f, 0x00200),CE(0x00311, 0x00202),CE(0x00323, 0x01ea0),CE(0x00325, 0x01e00),CE(0x00328, 0x00104),CE(0x00307, 0x01e02),CE(0x00323, 0x01e04),CE(0x00331, 0x01e06),CE(0x00301, 0x00106),CE(0x00302, 0x00108),CE(0x00307, 0x0010a),CE(0x0030c, 0x0010c),CE(0x00327, 0x000c7),CE(0x00307, 0x01e0a),CE(0x0030c, 0x0010e),CE(0x00323, 0x01e0c),CE(0x00327, 0x01e10),CE(0x0032d, 0x01e12),CE(0x00331, 0x01e0e),CE(0x00300, 0x000c8),CE(0x00301, 0x000c9),CE(0x00302, 0x000ca),CE(0x00303, 0x01ebc),CE(0x00304, 0x00112),CE(0x00306, 0x00114),CE(0x00307, 0x00116),CE(0x00308, 0x000cb),CE(0x00309, 0x01eba),CE(0x0030c, 0x0011a),CE(0x0030f, 0x00204),CE(0x00311, 0x00206),CE(0x00323, 0x01eb8),CE(0x00327, 0x00228),CE(0x00328, 0x00118),CE(0x0032d, 0x01e18),CE(0x00330, 0x01e1a),CE(0x00307, 0x01e1e),CE(0x00301, 0x001f4),CE(0x00302, 0x0011c),CE(0x00304, 0x01e20),CE(0x00306, 0x0011e),CE(0x00307, 0x00120),CE(0x0030c, 0x001e6),CE(0x00327, 0x00122),CE(0x00302, 0x00124),CE(0x00307, 0x01e22),CE(0x00308, 0x01e26),CE(0x0030c, 0x0021e),CE(0x00323, 0x01e24),CE(0x00327, 0x01e28),CE(0x0032e, 0x01e2a),CE(0x00300, 0x000cc),CE(0x00301, 0x000cd),CE(0x00302, 0x000ce),CE(0x00303, 0x00128),CE(0x00304, 0x0012a),CE(0x00306, 0x0012c),CE(0x00307, 0x00130),CE(0x00308, 0x000cf),CE(0x00309, 0x01ec8),CE(0x0030c, 0x001cf),CE(0x0030f, 0x00208),CE(0x00311, 0x0020a),CE(0x00323, 0x01eca),CE(0x00328, 0x0012e),CE(0x00330, 0x01e2c),CE(0x00302, 0x00134),CE(0x00301, 0x01e30),CE(0x0030c, 0x001e8),CE(0x00323, 0x01e32),CE(0x00327, 0x00136),CE(0x00331, 0x01e34),CE(0x00301, 0x00139),CE(0x0030c, 0x0013d),CE(0x00323, 0x01e36),CE(0x00327, 0x0013b),CE(0x0032d, 0x01e3c),CE(0x00331, 0x01e3a),CE(0x00301, 0x01e3e),CE(0x00307, 0x01e40),CE(0x00323, 0x01e42),CE(0x00300, 0x001f8),CE(0x00301, 0x00143),CE(0x00303, 0x000d1),CE(0x00307, 0x01e44),CE(0x0030c, 0x00147),CE(0x00323, 0x01e46),CE(0x00327, 0x00145),CE(0x0032d, 0x01e4a),CE(0x00331, 0x01e48),CE(0x00300, 0x000d2),CE(0x00301, 0x000d3),CE(0x00302, 0x000d4),CE(0x00303, 0x000d5),CE(0x00304, 0x0014c),CE(0x00306, 0x0014e),CE(0x00307, 0x0022e),CE(0x00308, 0x000d6),CE(0x00309, 0x01ece),CE(0x0030b, 0x00150),CE(0x0030c, 0x001d1),CE(0x0030f, 0x0020c),CE(0x00311, 0x0020e),CE(0x0031b, 0x001a0),CE(0x00323, 0x01ecc),CE(0x00328, 0x001ea),CE(0x00301, 0x01e54),CE(0x00307, 0x01e56),CE(0x00301, 0x00154),CE(0x00307, 0x01e58),CE(0x0030c, 0x00158),CE(0x0030f, 0x00210),CE(0x00311, 0x00212),CE(0x00323, 0x01e5a),CE(0x00327, 0x00156),CE(0x00331, 0x01e5e),CE(0x00301, 0x0015a),CE(0x00302, 0x0015c),CE(0x00307, 0x01e60),CE(0x0030c, 0x00160),CE(0x00323, 0x01e62),CE(0x00326, 0x00218),CE(0x00327, 0x0015e),CE(0x00307, 0x01e6a),CE(0x0030c, 0x00164),CE(0x00323, 0x01e6c),CE(0x00326, 0x0021a),CE(0x00327, 0x00162),CE(0x0032d, 0x01e70),CE(0x00331, 0x01e6e),CE(0x00300, 0x000d9),CE(0x00301, 0x000da),CE(0x00302, 0x000db),CE(0x00303, 0x00168),CE(0x00304, 0x0016a),CE(0x00306, 0x0016c),CE(0x00308, 0x000dc),CE(0x00309, 0x01ee6),CE(0x0030a, 0x0016e),CE(0x0030b, 0x00170),CE(0x0030c, 0x001d3),CE(0x0030f, 0x00214),CE(0x00311, 0x00216),CE(0x0031b, 0x001af),CE(0x00323, 0x01ee4),CE(0x00324, 0x01e72),CE(0x00328, 0x00172),CE(0x0032d, 0x01e76),CE(0x00330, 0x01e74),CE(0x00303, 0x01e7c),CE(0x00323, 0x01e7e),CE(0x00300, 0x01e80),CE(0x00301, 0x01e82),CE(0x00302, 0x00174),CE(0x00307, 0x01e86),CE(0x00308, 0x01e84),CE(0x00323, 0x01e88),CE(0x00307, 0x01e8a),CE(0x00308, 0x01e8c),CE(0x00300, 0x01ef2),CE(0x00301, 0x000dd),CE(0x00302, 0x00176),CE(0x00303, 0x01ef8),CE(0x00304, 0x00232),CE(0x00307, 0x01e8e),CE(0x00308, 0x00178),CE(0x00309, 0x01ef6),CE(0x00323, 0x01ef4),CE(0x00301, 0x00179),CE(0x00302, 0x01e90),CE(0x00307, 0x0017b),CE(0x0030c, 0x0017d),CE(0x00323, 0x01e92),CE(0x00331, 0x01e94),CE(0x00300, 0x000e0),CE(0x00301, 0x000e1),CE(0x00302, 0x000e2),CE(0x00303, 0x000e3),CE(0x00304, 0x00101),CE(0x00306, 0x00103),CE(0x00307, 0x00227),CE(0x00308, 0x000e4),CE(0x00309, 0x01ea3),CE(0x0030a, 0x000e5),CE(0x0030c, 0x001ce),CE(0x0030f, 0x00201),CE(0x00311, 0x00203),CE(0x00323, 0x01ea1),CE(0x00325, 0x01e01),CE(0x00328, 0x00105),CE(0x00307, 0x01e03),CE(0x00323, 0x01e05),CE(0x00331, 0x01e07),CE(0x00301, 0x00107),CE(0x00302, 0x00109),CE(0x00307, 0x0010b),CE(0x0030c, 0x0010d),CE(0x00327, 0x000e7),CE(0x00307, 0x01e0b),CE(0x0030c, 0x0010f),CE(0x00323, 0x01e0d),CE(0x00327, 0x01e11),CE(0x0032d, 0x01e13),CE(0x00331, 0x01e0f),CE(0x00300, 0x000e8),CE(0x00301, 0x000e9),CE(0x00302, 0x000ea),CE(0x00303, 0x01ebd),CE(0x00304, 0x00113),CE(0x00306, 0x00115),CE(0x00307, 0x00117),CE(0x00308, 0x000eb),CE(0x00309, 0x01ebb),CE(0x0030c, 0x0011b),CE(0x0030f, 0x00205),CE(0x00311, 0x00207),CE(0x00323, 0x01eb9),CE(0x00327, 0x00229),CE(0x00328, 0x00119),CE(0x0032d, 0x01e19),CE(0x00330, 0x01e1b),CE(0x00307, 0x01e1f),CE(0x00301, 0x001f5),CE(0x00302, 0x0011d),CE(0x00304, 0x01e21),CE(0x00306, 0x0011f),CE(0x00307, 0x00121),CE(0x0030c, 0x001e7),CE(0x00327, 0x00123),CE(0x00302, 0x00125),CE(0x00307, 0x01e23),CE(0x00308, 0x01e27),CE(0x0030c, 0x0021f),CE(0x00323, 0x01e25),CE(0x00327, 0x01e29),CE(0x0032e, 0x01e2b),CE(0x00331, 0x01e96),CE(0x00300, 0x000ec),CE(0x00301, 0x000ed),CE(0x00302, 0x000ee),CE(0x00303, 0x00129),CE(0x00304, 0x0012b),CE(0x00306, 0x0012d),CE(0x00308, 0x000ef),CE(0x00309, 0x01ec9),CE(0x0030c, 0x001d0),CE(0x0030f, 0x00209),CE(0x00311, 0x0020b),CE(0x00323, 0x01ecb),CE(0x00328, 0x0012f),CE(0x00330, 0x01e2d),CE(0x00302, 0x00135),CE(0x0030c, 0x001f0),CE(0x00301, 0x01e31),CE(0x0030c, 0x001e9),CE(0x00323, 0x01e33),CE(0x00327, 0x00137),CE(0x00331, 0x01e35),CE(0x00301, 0x0013a),CE(0x0030c, 0x0013e),CE(0x00323, 0x01e37),CE(0x00327, 0x0013c),CE(0x0032d, 0x01e3d),CE(0x00331, 0x01e3b),CE(0x00301, 0x01e3f),CE(0x00307, 0x01e41),CE(0x00323, 0x01e43),CE(0x00300, 0x001f9),CE(0x00301, 0x00144),CE(0x00303, 0x000f1),CE(0x00307, 0x01e45),CE(0x0030c, 0x00148),CE(0x00323, 0x01e47),CE(0x00327, 0x00146),CE(0x0032d, 0x01e4b),CE(0x00331, 0x01e49),CE(0x00300, 0x000f2),CE(0x00301, 0x000f3),CE(0x00302, 0x000f4),CE(0x00303, 0x000f5),CE(0x00304, 0x0014d),CE(0x00306, 0x0014f),CE(0x00307, 0x0022f),CE(0x00308, 0x000f6),CE(0x00309, 0x01ecf),CE(0x0030b, 0x00151),CE(0x0030c, 0x001d2),CE(0x0030f, 0x0020d),CE(0x00311, 0x0020f),CE(0x0031b, 0x001a1),CE(0x00323, 0x01ecd),CE(0x00328, 0x001eb),CE(0x00301, 0x01e55),CE(0x00307, 0x01e57),CE(0x00301, 0x00155),CE(0x00307, 0x01e59),CE(0x0030c, 0x00159),CE(0x0030f, 0x00211),CE(0x00311, 0x00213),CE(0x00323, 0x01e5b),CE(0x00327, 0x00157),CE(0x00331, 0x01e5f),CE(0x00301, 0x0015b),CE(0x00302, 0x0015d),CE(0x00307, 0x01e61),CE(0x0030c, 0x00161),CE(0x00323, 0x01e63),CE(0x00326, 0x00219),CE(0x00327, 0x0015f),CE(0x00307, 0x01e6b),CE(0x00308, 0x01e97),CE(0x0030c, 0x00165),CE(0x00323, 0x01e6d),CE(0x00326, 0x0021b),CE(0x00327, 0x00163),CE(0x0032d, 0x01e71),CE(0x00331, 0x01e6f),CE(0x00300, 0x000f9),CE(0x00301, 0x000fa),CE(0x00302, 0x000fb),CE(0x00303, 0x00169),CE(0x00304, 0x0016b),CE(0x00306, 0x0016d),CE(0x00308, 0x000fc),CE(0x00309, 0x01ee7),CE(0x0030a, 0x0016f),CE(0x0030b, 0x00171),CE(0x0030c, 0x001d4),CE(0x0030f, 0x00215),CE(0x00311, 0x00217),CE(0x0031b, 0x001b0),CE(0x00323, 0x01ee5),CE(0x00324, 0x01e73),CE(0x00328, 0x00173),CE(0x0032d, 0x01e77),CE(0x00330, 0x01e75),CE(0x00303, 0x01e7d),CE(0x00323, 0x01e7f),CE(0x00300, 0x01e81),CE(0x00301, 0x01e83),CE(0x00302, 0x00175),CE(0x00307, 0x01e87),CE(0x00308, 0x01e85),CE(0x0030a, 0x01e98),CE(0x00323, 0x01e89),CE(0x00307, 0x01e8b),CE(0x00308, 0x01e8d),CE(0x00300, 0x01ef3),CE(0x00301, 0x000fd),CE(0x00302, 0x00177),CE(0x00303, 0x01ef9),CE(0x00304, 0x00233),CE(0x00307, 0x01e8f),CE(0x00308, 0x000ff),CE(0x00309, 0x01ef7),CE(0x0030a, 0x01e99),CE(0x00323, 0x01ef5),CE(0x00301, 0x0017a),CE(0x00302, 0x01e91),CE(0x00307, 0x0017c),CE(0x0030c, 0x0017e),CE(0x00323, 0x01e93),CE(0x00331, 0x01e95),CE(0x00300, 0x01fed),CE(0x00301, 0x00385),CE(0x00342, 0x01fc1),CE(0x00300, 0x01ea6),CE(0x00301, 0x01ea4),CE(0x00303, 0x01eaa),CE(0x00309, 0x01ea8),CE(0x00304, 0x001de),CE(0x00301, 0x001fa),CE(0x00301, 0x001fc),CE(0x00304, 0x001e2),CE(0x00301, 0x01e08),CE(0x00300, 0x01ec0),CE(0x00301, 0x01ebe),CE(0x00303, 0x01ec4),CE(0x00309, 0x01ec2),CE(0x00301, 0x01e2e),CE(0x00300, 0x01ed2),CE(0x00301, 0x01ed0),CE(0x00303, 0x01ed6),CE(0x00309, 0x01ed4),CE(0x00301, 0x01e4c),CE(0x00304, 0x0022c),CE(0x00308, 0x01e4e),CE(0x00304, 0x0022a),CE(0x00301, 0x001fe),CE(0x00300, 0x001db),CE(0x00301, 0x001d7),CE(0x00304, 0x001d5),CE(0x0030c, 0x001d9),CE(0x00300, 0x01ea7),CE(0x00301, 0x01ea5),CE(0x00303, 0x01eab),CE(0x00309, 0x01ea9),CE(0x00304, 0x001df),CE(0x00301, 0x001fb),CE(0x00301, 0x001fd),CE(0x00304, 0x001e3),CE(0x00301, 0x01e09),CE(0x00300, 0x01ec1),CE(0x00301, 0x01ebf),CE(0x00303, 0x01ec5),CE(0x00309, 0x01ec3),CE(0x00301, 0x01e2f),CE(0x00300, 0x01ed3),CE(0x00301, 0x01ed1),CE(0x00303, 0x01ed7),CE(0x00309, 0x01ed5),CE(0x00301, 0x01e4d),CE(0x00304, 0x0022d),CE(0x00308, 0x01e4f),CE(0x00304, 0x0022b),CE(0x00301, 0x001ff),CE(0x00300, 0x001dc),CE(0x00301, 0x001d8),CE(0x00304, 0x001d6),CE(0x0030c, 0x001da),CE(0x00300, 0x01eb0),CE(0x00301, 0x01eae),CE(0x00303, 0x01eb4),CE(0x00309, 0x01eb2),CE(0x00300, 0x01eb1),CE(0x00301, 0x01eaf),CE(0x00303, 0x01eb5),CE(0x00309, 0x01eb3),CE(0x00300, 0x01e14),CE(0x00301, 0x01e16),CE(0x00300, 0x01e15),CE(0x00301, 0x01e17),CE(0x00300, 0x01e50),CE(0x00301, 0x01e52),CE(0x00300, 0x01e51),CE(0x00301, 0x01e53),CE(0x00307, 0x01e64),CE(0x00307, 0x01e65),CE(0x00307, 0x01e66),CE(0x00307, 0x01e67),CE(0x00301, 0x01e78),CE(0x00301, 0x01e79),CE(0x00308, 0x01e7a),CE(0x00308, 0x01e7b),CE(0x00307, 0x01e9b),CE(0x00300, 0x01edc),CE(0x00301, 0x01eda),CE(0x00303, 0x01ee0),CE(0x00309, 0x01ede),CE(0x00323, 0x01ee2),CE(0x00300, 0x01edd),CE(0x00301, 0x01edb),CE(0x00303, 0x01ee1),CE(0x00309, 0x01edf),CE(0x00323, 0x01ee3),CE(0x00300, 0x01eea),CE(0x00301, 0x01ee8),CE(0x00303, 0x01eee),CE(0x00309, 0x01eec),CE(0x00323, 0x01ef0),CE(0x00300, 0x01eeb),CE(0x00301, 0x01ee9),CE(0x00303, 0x01eef),CE(0x00309, 0x01eed),CE(0x00323, 0x01ef1),CE(0x0030c, 0x001ee),CE(0x00304, 0x001ec),CE(0x00304, 0x001ed),CE(0x00304, 0x001e0),CE(0x00304, 0x001e1),CE(0x00306, 0x01e1c),CE(0x00306, 0x01e1d),CE(0x00304, 0x00230),CE(0x00304, 0x00231),CE(0x0030c, 0x001ef),CE(0x00300, 0x01fba),CE(0x00301, 0x00386),CE(0x00304, 0x01fb9),CE(0x00306, 0x01fb8),CE(0x00313, 0x01f08),CE(0x00314, 0x01f09),CE(0x00345, 0x01fbc),CE(0x00300, 0x01fc8),CE(0x00301, 0x00388),CE(0x00313, 0x01f18),CE(0x00314, 0x01f19),CE(0x00300, 0x01fca),CE(0x00301, 0x00389),CE(0x00313, 0x01f28),CE(0x00314, 0x01f29),CE(0x00345, 0x01fcc),CE(0x00300, 0x01fda),CE(0x00301, 0x0038a),CE(0x00304, 0x01fd9),CE(0x00306, 0x01fd8),CE(0x00308, 0x003aa),CE(0x00313, 0x01f38),CE(0x00314, 0x01f39),CE(0x00300, 0x01ff8),CE(0x00301, 0x0038c),CE(0x00313, 0x01f48),CE(0x00314, 0x01f49),CE(0x00314, 0x01fec),CE(0x00300, 0x01fea),CE(0x00301, 0x0038e),CE(0x00304, 0x01fe9),CE(0x00306, 0x01fe8),CE(0x00308, 0x003ab),CE(0x00314, 0x01f59),CE(0x00300, 0x01ffa),CE(0x00301, 0x0038f),CE(0x00313, 0x01f68),CE(0x00314, 0x01f69),CE(0x00345, 0x01ffc),CE(0x00345, 0x01fb4),CE(0x00345, 0x01fc4),CE(0x00300, 0x01f70),CE(0x00301, 0x003ac),CE(0x00304, 0x01fb1),CE(0x00306, 0x01fb0),CE(0x00313, 0x01f00),CE(0x00314, 0x01f01),CE(0x00342, 0x01fb6),CE(0x00345, 0x01fb3),CE(0x00300, 0x01f72),CE(0x00301, 0x003ad),CE(0x00313, 0x01f10),CE(0x00314, 0x01f11),CE(0x00300, 0x01f74),CE(0x00301, 0x003ae),CE(0x00313, 0x01f20),CE(0x00314, 0x01f21),CE(0x00342, 0x01fc6),CE(0x00345, 0x01fc3),CE(0x00300, 0x01f76),CE(0x00301, 0x003af),CE(0x00304, 0x01fd1),CE(0x00306, 0x01fd0),CE(0x00308, 0x003ca),CE(0x00313, 0x01f30),CE(0x00314, 0x01f31),CE(0x00342, 0x01fd6),CE(0x00300, 0x01f78),CE(0x00301, 0x003cc),CE(0x00313, 0x01f40),CE(0x00314, 0x01f41),CE(0x00313, 0x01fe4),CE(0x00314, 0x01fe5),CE(0x00300, 0x01f7a),CE(0x00301, 0x003cd),CE(0x00304, 0x01fe1),CE(0x00306, 0x01fe0),CE(0x00308, 0x003cb),CE(0x00313, 0x01f50),CE(0x00314, 0x01f51),CE(0x00342, 0x01fe6),CE(0x00300, 0x01f7c),CE(0x00301, 0x003ce),CE(0x00313, 0x01f60),CE(0x00314, 0x01f61),CE(0x00342, 0x01ff6),CE(0x00345, 0x01ff3),CE(0x00300, 0x01fd2),CE(0x00301, 0x00390),CE(0x00342, 0x01fd7),CE(0x00300, 0x01fe2),CE(0x00301, 0x003b0),CE(0x00342, 0x01fe7),CE(0x00345, 0x01ff4),CE(0x00301, 0x003d3),CE(0x00308, 0x003d4),CE(0x00308, 0x00407),CE(0x00306, 0x004d0),CE(0x00308, 0x004d2),CE(0x00301, 0x00403),CE(0x00300, 0x00400),CE(0x00306, 0x004d6),CE(0x00308, 0x00401),CE(0x00306, 0x004c1),CE(0x00308, 0x004dc),CE(0x00308, 0x004de),CE(0x00300, 0x0040d),CE(0x00304, 0x004e2),CE(0x00306, 0x00419),CE(0x00308, 0x004e4),CE(0x00301, 0x0040c),CE(0x00308, 0x004e6),CE(0x00304, 0x004ee),CE(0x00306, 0x0040e),CE(0x00308, 0x004f0),CE(0x0030b, 0x004f2),CE(0x00308, 0x004f4),CE(0x00308, 0x004f8),CE(0x00308, 0x004ec),CE(0x00306, 0x004d1),CE(0x00308, 0x004d3),CE(0x00301, 0x00453),CE(0x00300, 0x00450),CE(0x00306, 0x004d7),CE(0x00308, 0x00451),CE(0x00306, 0x004c2),CE(0x00308, 0x004dd),CE(0x00308, 0x004df),CE(0x00300, 0x0045d),CE(0x00304, 0x004e3),CE(0x00306, 0x00439),CE(0x00308, 0x004e5),CE(0x00301, 0x0045c),CE(0x00308, 0x004e7),CE(0x00304, 0x004ef),CE(0x00306, 0x0045e),CE(0x00308, 0x004f1),CE(0x0030b, 0x004f3),CE(0x00308, 0x004f5),CE(0x00308, 0x004f9),CE(0x00308, 0x004ed),CE(0x00308, 0x00457),CE(0x0030f, 0x00476),CE(0x0030f, 0x00477),CE(0x00308, 0x004da),CE(0x00308, 0x004db),CE(0x00308, 0x004ea),CE(0x00308, 0x004eb),CE(0x00653, 0x00622),CE(0x00654, 0x00623),CE(0x00655, 0x00625),CE(0x00654, 0x00624),CE(0x00654, 0x00626),CE(0x00654, 0x006c2),CE(0x00654, 0x006d3),CE(0x00654, 0x006c0),CE(0x0093c, 0x00929),CE(0x0093c, 0x00931),CE(0x0093c, 0x00934),CE(0x009be, 0x009cb),CE(0x009d7, 0x009cc),CE(0x00b3e, 0x00b4b),CE(0x00b56, 0x00b48),CE(0x00b57, 0x00b4c),CE(0x00bd7, 0x00b94),CE(0x00bbe, 0x00bca),CE(0x00bd7, 0x00bcc),CE(0x00bbe, 0x00bcb),CE(0x00c56, 0x00c48),CE(0x00cd5, 0x00cc0),CE(0x00cc2, 0x00cca),CE(0x00cd5, 0x00cc7),CE(0x00cd6, 0x00cc8),CE(0x00cd5, 0x00ccb),CE(0x00d3e, 0x00d4a),CE(0x00d57, 0x00d4c),CE(0x00d3e, 0x00d4b),CE(0x00dca, 0x00dda),CE(0x00dcf, 0x00ddc),CE(0x00ddf, 0x00dde),CE(0x00dca, 0x00ddd),CE(0x0102e, 0x01026),CE(0x01b35, 0x01b06),CE(0x01b35, 0x01b08),CE(0x01b35, 0x01b0a),CE(0x01b35, 0x01b0c),CE(0x01b35, 0x01b0e),CE(0x01b35, 0x01b12),CE(0x01b35, 0x01b3b),CE(0x01b35, 0x01b3d),CE(0x01b35, 0x01b40),CE(0x01b35, 0x01b41),CE(0x01b35, 0x01b43),CE(0x00304, 0x01e38),CE(0x00304, 0x01e39),CE(0x00304, 0x01e5c),CE(0x00304, 0x01e5d),CE(0x00307, 0x01e68),CE(0x00307, 0x01e69),CE(0x00302, 0x01eac),CE(0x00306, 0x01eb6),CE(0x00302, 0x01ead),CE(0x00306, 0x01eb7),CE(0x00302, 0x01ec6),CE(0x00302, 0x01ec7),CE(0x00302, 0x01ed8),CE(0x00302, 0x01ed9),CE(0x00300, 0x01f02),CE(0x00301, 0x01f04),CE(0x00342, 0x01f06),CE(0x00345, 0x01f80),CE(0x00300, 0x01f03),CE(0x00301, 0x01f05),CE(0x00342, 0x01f07),CE(0x00345, 0x01f81),CE(0x00345, 0x01f82),CE(0x00345, 0x01f83),CE(0x00345, 0x01f84),CE(0x00345, 0x01f85),CE(0x00345, 0x01f86),CE(0x00345, 0x01f87),CE(0x00300, 0x01f0a),CE(0x00301, 0x01f0c),CE(0x00342, 0x01f0e),CE(0x00345, 0x01f88),CE(0x00300, 0x01f0b),CE(0x00301, 0x01f0d),CE(0x00342, 0x01f0f),CE(0x00345, 0x01f89),CE(0x00345, 0x01f8a),CE(0x00345, 0x01f8b),CE(0x00345, 0x01f8c),CE(0x00345, 0x01f8d),CE(0x00345, 0x01f8e),CE(0x00345, 0x01f8f),CE(0x00300, 0x01f12),CE(0x00301, 0x01f14),CE(0x00300, 0x01f13),CE(0x00301, 0x01f15),CE(0x00300, 0x01f1a),CE(0x00301, 0x01f1c),CE(0x00300, 0x01f1b),CE(0x00301, 0x01f1d),CE(0x00300, 0x01f22),CE(0x00301, 0x01f24),CE(0x00342, 0x01f26),CE(0x00345, 0x01f90),CE(0x00300, 0x01f23),CE(0x00301, 0x01f25),CE(0x00342, 0x01f27),CE(0x00345, 0x01f91),CE(0x00345, 0x01f92),CE(0x00345, 0x01f93),CE(0x00345, 0x01f94),CE(0x00345, 0x01f95),CE(0x00345, 0x01f96),CE(0x00345, 0x01f97),CE(0x00300, 0x01f2a),CE(0x00301, 0x01f2c),CE(0x00342, 0x01f2e),CE(0x00345, 0x01f98),CE(0x00300, 0x01f2b),CE(0x00301, 0x01f2d),CE(0x00342, 0x01f2f),CE(0x00345, 0x01f99),CE(0x00345, 0x01f9a),CE(0x00345, 0x01f9b),CE(0x00345, 0x01f9c),CE(0x00345, 0x01f9d),CE(0x00345, 0x01f9e),CE(0x00345, 0x01f9f),CE(0x00300, 0x01f32),CE(0x00301, 0x01f34),CE(0x00342, 0x01f36),CE(0x00300, 0x01f33),CE(0x00301, 0x01f35),CE(0x00342, 0x01f37),CE(0x00300, 0x01f3a),CE(0x00301, 0x01f3c),CE(0x00342, 0x01f3e),CE(0x00300, 0x01f3b),CE(0x00301, 0x01f3d),CE(0x00342, 0x01f3f),CE(0x00300, 0x01f42),CE(0x00301, 0x01f44),CE(0x00300, 0x01f43),CE(0x00301, 0x01f45),CE(0x00300, 0x01f4a),CE(0x00301, 0x01f4c),CE(0x00300, 0x01f4b),CE(0x00301, 0x01f4d),CE(0x00300, 0x01f52),CE(0x00301, 0x01f54),CE(0x00342, 0x01f56),CE(0x00300, 0x01f53),CE(0x00301, 0x01f55),CE(0x00342, 0x01f57),CE(0x00300, 0x01f5b),CE(0x00301, 0x01f5d),CE(0x00342, 0x01f5f),CE(0x00300, 0x01f62),CE(0x00301, 0x01f64),CE(0x00342, 0x01f66),CE(0x00345, 0x01fa0),CE(0x00300, 0x01f63),CE(0x00301, 0x01f65),CE(0x00342, 0x01f67),CE(0x00345, 0x01fa1),CE(0x00345, 0x01fa2),CE(0x00345, 0x01fa3),CE(0x00345, 0x01fa4),CE(0x00345, 0x01fa5),CE(0x00345, 0x01fa6),CE(0x00345, 0x01fa7),CE(0x00300, 0x01f6a),CE(0x00301, 0x01f6c),CE(0x00342, 0x01f6e),CE(0x00345, 0x01fa8),CE(0x00300, 0x01f6b),CE(0x00301, 0x01f6d),CE(0x00342, 0x01f6f),CE(0x00345, 0x01fa9),CE(0x00345, 0x01faa),CE(0x00345, 0x01fab),CE(0x00345, 0x01fac),CE(0x00345, 0x01fad),CE(0x00345, 0x01fae),CE(0x00345, 0x01faf),CE(0x00345, 0x01fb2),CE(0x00345, 0x01fc2),CE(0x00345, 0x01ff2),CE(0x00345, 0x01fb7),CE(0x00300, 0x01fcd),CE(0x00301, 0x01fce),CE(0x00342, 0x01fcf),CE(0x00345, 0x01fc7),CE(0x00345, 0x01ff7),CE(0x00300, 0x01fdd),CE(0x00301, 0x01fde),CE(0x00342, 0x01fdf),CE(0x00338, 0x0219a),CE(0x00338, 0x0219b),CE(0x00338, 0x021ae),CE(0x00338, 0x021cd),CE(0x00338, 0x021cf),CE(0x00338, 0x021ce),CE(0x00338, 0x02204),CE(0x00338, 0x02209),CE(0x00338, 0x0220c),CE(0x00338, 0x02224),CE(0x00338, 0x02226),CE(0x00338, 0x02241),CE(0x00338, 0x02244),CE(0x00338, 0x02247),CE(0x00338, 0x02249),CE(0x00338, 0x0226d),CE(0x00338, 0x02262),CE(0x00338, 0x02270),CE(0x00338, 0x02271),CE(0x00338, 0x02274),CE(0x00338, 0x02275),CE(0x00338, 0x02278),CE(0x00338, 0x02279),CE(0x00338, 0x02280),CE(0x00338, 0x02281),CE(0x00338, 0x022e0),CE(0x00338, 0x022e1),CE(0x00338, 0x02284),CE(0x00338, 0x02285),CE(0x00338, 0x02288),CE(0x00338, 0x02289),CE(0x00338, 0x022e2),CE(0x00338, 0x022e3),CE(0x00338, 0x022ac),CE(0x00338, 0x022ad),CE(0x00338, 0x022ae),CE(0x00338, 0x022af),CE(0x00338, 0x022ea),CE(0x00338, 0x022eb),CE(0x00338, 0x022ec),CE(0x00338, 0x022ed),CE(0x03099, 0x03094),CE(0x03099, 0x0304c),CE(0x03099, 0x0304e),CE(0x03099, 0x03050),CE(0x03099, 0x03052),CE(0x03099, 0x03054),CE(0x03099, 0x03056),CE(0x03099, 0x03058),CE(0x03099, 0x0305a),CE(0x03099, 0x0305c),CE(0x03099, 0x0305e),CE(0x03099, 0x03060),CE(0x03099, 0x03062),CE(0x03099, 0x03065),CE(0x03099, 0x03067),CE(0x03099, 0x03069),CE(0x03099, 0x03070),CE(0x0309a, 0x03071),CE(0x03099, 0x03073),CE(0x0309a, 0x03074),CE(0x03099, 0x03076),CE(0x0309a, 0x03077),CE(0x03099, 0x03079),CE(0x0309a, 0x0307a),CE(0x03099, 0x0307c),CE(0x0309a, 0x0307d),CE(0x03099, 0x0309e),CE(0x03099, 0x030f4),CE(0x03099, 0x030ac),CE(0x03099, 0x030ae),CE(0x03099, 0x030b0),CE(0x03099, 0x030b2),CE(0x03099, 0x030b4),CE(0x03099, 0x030b6),CE(0x03099, 0x030b8),CE(0x03099, 0x030ba),CE(0x03099, 0x030bc),CE(0x03099, 0x030be),CE(0x03099, 0x030c0),CE(0x03099, 0x030c2),CE(0x03099, 0x030c5),CE(0x03099, 0x030c7),CE(0x03099, 0x030c9),CE(0x03099, 0x030d0),CE(0x0309a, 0x030d1),CE(0x03099, 0x030d3),CE(0x0309a, 0x030d4),CE(0x03099, 0x030d6),CE(0x0309a, 0x030d7),CE(0x03099, 0x030d9),CE(0x0309a, 0x030da),CE(0x03099, 0x030dc),CE(0x0309a, 0x030dd),CE(0x03099, 0x030f7),CE(0x03099, 0x030f8),CE(0x03099, 0x030f9),CE(0x03099, 0x030fa),CE(0x03099, 0x030fe),CE(0x110ba, 0x1109a),CE(0x110ba, 0x1109c),CE(0x110ba, 0x110ab),CE(0x11127, 0x1112e),CE(0x11127, 0x1112f),]; -return t; -} + //6976 bytes + enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 7, 6)([0x0, 0x20, + 0x120], [0x100, 0x400, 0x1240], [0x402030202020100, + 0x206020202020205, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x0, 0x0, 0x0, 0x20001, 0x300000000, + 0x5000400000000, 0x8000000070006, 0xb0000000a0009, 0xe0000000d000c, + 0x11000f0010000f, 0x11000f0011000f, 0x1100000011000f, + 0x11000f00120000, 0x13000000110000, 0x17001600150014, + 0x1b001a00190018, 0x1d0000001c, 0x0, 0x0, 0x1e0000, 0x0, 0x0, 0x0, + 0x2000000000001f, 0x2100000000, 0x22, 0x240023, 0x28002700260025, + 0x2a000000000029, 0x2b000000000000, 0x0, 0x0, 0x2c000000000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2d000000000000, 0x2f0000002e0000, 0x0, 0x0, 0x3100000030, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x34003300320000, 0x0, 0x36000000000035, 0x3a003900380037, + 0x3c003b00000000, 0x3d000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x40000000000000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x4200350000, 0x3a000000000043, 0x0, 0x0, 0x0, 0x0, 0x4400000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x4600450000, 0x470000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6e6, + 0xe6e6e6e6e6e6e6e6, 0xdcdce8e6e6e6e6e6, 0xdcdcdcdcd8e8dcdc, + 0xcadcdcdcdccacadc, 0xdcdcdcdcdcdcdcca, 0x1010101dcdcdcdc, + 0xe6e6e6dcdcdcdc01, 0xdce6f0e6e6e6e6e6, 0xdcdce6e6e6dcdc, + 0xe6dcdcdcdce6e6e6, 0xe9eaeae9e6dcdce8, 0xe6e6e6e6e6e9eaea, + 0xe6e6e6e6e6e6e6e6, 0x0, 0x0, 0xe6e6e6e6e6000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6dce6e6e6e6dc00, + 0xe6e6e6e6dcdee6e6, 0xdcdcdcdcdcdce6e6, 0xe6e4dee6e6dce6e6, + 0x11100f0e0d0c0b0a, 0x1700161514131312, 0x1200dce600191800, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6e6, + 0x201f1e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f1e1d1c1b000000, + 0xe6dcdce6e6222120, 0xdce6e6dce6e6e6e6, 0x0, 0x0, 0x23, 0x0, 0x0, + 0x0, 0xe6e6000000000000, 0xe60000e6e6e6e6e6, 0xe60000e6dce6e6e6, + 0xdce6e6dc00e6, 0x0, 0x0, 0x0, 0x0, 0x2400, 0x0, 0x0, 0x0, + 0xdce6e6dce6e6dce6, 0xe6dce6dcdce6dcdc, 0xe6dce6dce6dce6e6, + 0xe6e6dc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe6e6e6e6e6000000, 0xe6dce6e6, 0x0, 0x0, 0x0, 0xe6e6000000000000, + 0xe6e6e6e6e600e6e6, 0xe6e6e600e6e6e6e6, 0xe6e6e6e6e600, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xdcdcdc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe6dce6e600000000, 0xdcdcdce6e6e6dce6, 0xe6dce6e6e61d1c1b, + 0xe6e6e6e6dcdce6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x700000000, + 0x0, 0x90000000000, 0xe6e6dce600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x90000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000000000, + 0x5b540000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96767, + 0x0, 0x6b6b6b6b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x7676, 0x0, 0x7a7a7a7a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xdcdc, 0x0, 0x0, 0xdc00dc0000000000, 0xd800, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8400828100, 0x828282820000, + 0xe6e60009e6e60082, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xdc000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x700000000000000, 0x90900, 0x0, 0xdc0000000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e60000000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900000000, 0x0, 0x0, 0x0, + 0x900000000, 0x0, 0x0, 0x0, 0x90000, 0xe60000000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xdce6de00, 0x0, 0x0, 0xe600000000000000, 0xdc, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0xe6e6e60000000000, + 0xdc0000e6e6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x700000000, 0x0, + 0x900000000, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6dce6000000, 0xe6e6e6e6, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x7000000000000, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x700000000000000, 0x0, 0x0, 0x0, 0xdcdcdc0100e6e6e6, + 0xdcdcdcdce6e6dcdc, 0x1010101010100e6, 0xdc0000000001, + 0xe600000000, 0x0, 0xe6e6e6e6e6dce6e6, 0xdcd6eae6e6dce6e6, + 0xe6e6e6e6e6e6e6ca, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6, 0x0, 0x0, + 0xdce6dce900000000, 0x0, 0x0, 0xe6e6e6e60101e6e6, 0xe6e6010101, + 0xe60101000000e600, 0xdcdcdcdc0101e6dc, 0xe6, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xe600000000000000, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x900000000000000, 0x0, 0x0, 0x0, 0x0, + 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, + 0xe6e6e6e6e6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0e0dee8e4da0000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x80800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xe600000000000000, 0xe6e6e6e600000000, + 0xe6e6e6e6e6e6, 0x0, 0x0, 0x0, 0xe600000000000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6, 0x0, 0x9000000000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900000000, 0x0, 0x0, 0x0, + 0xe6e6e6e6e6e6e6e6, 0xe6e6e6e6e6e6e6e6, 0xe6e6, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xdcdcdc000000, 0x0, 0x0, 0x0, 0x0, 0x9000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000, 0x0, 0x9, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe60000dce6e600e6, 0xe6e60000000000e6, 0xe600, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x9000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xe6e6e6e6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xdc0000000000, 0x0, 0xe600dc0000000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x900000000dc01e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x70900, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x909000000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x709000000000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1d8d80000000000, 0xd8d8e20000000101, 0xd8d8d8, + 0xdcdcdcdcdc000000, 0xe6e6e60000dcdcdc, 0xdcdce6e6, 0x0, 0x0, 0x0, + 0xe6e6e6e60000, 0x0, 0x0, 0xe6e6e60000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11; + enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)([0x0, 0x400], + [0x1000, 0x2000], [0x3000200010000, 0x7000600050004, + 0x7000700070008, 0xa000700090007, 0x70007000c000b, 0x7000700070007, + 0x700070007000d, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x700070007000e, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff080208010800, + 0x281618138003ffff, 0x383308328821301b, 0x285108507841383a, + 0x8068485f185c3056, 0x3882407affff1078, 0x30a510a398903889, + 0xffff30b648ad10ab, 0xffffffffffffffff, 0x28cf18cc80bcffff, + 0x38ec08eb88da30d4, 0x290b110970fb40f3, 0x8122491919163110, + 0x393c4134ffff1132, 0x3960115e994b4143, 0xffff317351691167, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff1979, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff217cffffffff, + 0x984118209810980, 0xffff2185ffffffff, 0x989ffffffffffff, + 0xffffffffffffffff, 0xffff0991198e218a, 0xffffffffffff0992, + 0xffffffffffff2193, 0xffff2197ffffffff, 0x99f119d099c099b, + 0xffff21a0ffffffff, 0x9a4ffffffffffff, 0xffffffffffffffff, + 0xffff09ac19a921a5, 0xffffffffffff09ad, 0xffffffffffff21ae, + 0x21b621b2ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x11bc11baffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff11c011be, 0xffffffffffffffff, + 0xffffffffffffffff, 0x9c309c2ffffffff, 0xffffffffffffffff, + 0xffffffff09c509c4, 0xffffffffffffffff, 0x9c909c809c709c6, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x9caffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff29d029cb, 0xffffffffffffffff, + 0xffffffffffffffff, 0x29d5ffffffffffff, 0xffffffffffff29da, + 0x9dfffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x9e109e0ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x9e309e2ffffffff, 0xffffffff09e509e4, + 0x9e709e6ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff09e8ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff39e9ffff, + 0x29f4ffff21f0ffff, 0xffffffff39f9ffff, 0x2200ffffffffffff, + 0xffffffff0a04ffff, 0xffffffff3205ffff, 0xffffffff2a0bffff, + 0xffff0a11ffff0a10, 0xffffffff4212ffff, 0x321effff221affff, + 0xffffffff4224ffff, 0x222cffffffffffff, 0xffffffff1230ffff, + 0xffffffff4232ffff, 0x1a431a40323affff, 0xffff0a46ffffffff, + 0xffff1247ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0a49ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xa4cffffffff124a, 0xa5212501a4dffff, + 0xffff0a57ffff2253, 0xffff0a58ffffffff, 0x2259ffffffffffff, + 0xa5dffffffffffff, 0xa5effffffffffff, 0xffffffff0a5fffff, + 0xa62ffffffff1260, 0xa6812661a63ffff, 0xffff0a6dffff2269, + 0xffff0a6effffffff, 0x226fffffffffffff, 0xa73ffffffffffff, + 0xa74ffffffffffff, 0xffffffff0a75ffff, 0xffffffffffffffff, + 0xffff0a76ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a780a77, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff0a7a0a79, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff0a7c0a7b, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1a7dffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0a81ffff0a80, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0a82ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0a83ffffffff, 0xffffffff0a84ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffff0a85, 0xffffffffffffffff, 0xa87ffffffff0a86, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1288ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1a8affffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0a8dffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xa90128effffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0a91ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xa92ffffffffffff, 0xffffffffffffffff, + 0xffff1a93ffffffff, 0xffff0a96ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xa991297ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff1a9affff, 0xffffffffffff0a9d, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff0a9effff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xaa0ffff0a9fffff, 0xaa2ffff0aa1ffff, + 0xffffffff0aa3ffff, 0xffffffff0aa4ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0aa5ffffffff, + 0xaa80aa7ffff0aa6, 0xffff0aa9ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xaab0aaaffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xaad0aacffffffff, + 0xffffffffffffffff, 0xaaf0aaeffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff12b212b0, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0ab50ab4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff0ab70ab6, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xac10ac022bc22b8, + 0xac50ac40ac30ac2, 0xacf0ace22ca22c6, 0xad30ad20ad10ad0, + 0xffffffff12d612d4, 0xffffffffffffffff, 0xffffffff12da12d8, + 0xffffffffffffffff, 0xae50ae422e022dc, 0xae90ae80ae70ae6, + 0xaf30af222ee22ea, 0xaf70af60af50af4, 0xffffffff1afb1af8, + 0xffffffffffffffff, 0xffffffff1b011afe, 0xffffffffffffffff, + 0xffffffff13061304, 0xffffffffffffffff, 0xffffffff130a1308, + 0xffffffffffffffff, 0xffffffff1b0f1b0c, 0xffffffffffffffff, + 0xffffffff1b12ffff, 0xffffffffffffffff, 0xb1e0b1d23192315, + 0xb220b210b200b1f, 0xb2c0b2b23272323, 0xb300b2f0b2e0b2d, + 0xffffffffffff0b31, 0xffffffffffff0b32, 0xffffffffffffffff, + 0xffffffffffff0b33, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b34ffffffff, + 0xffffffffffffffff, 0x1b35ffffffffffff, 0xffffffffffffffff, + 0xffff0b38ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0b39ffffffff, 0xffffffffffffffff, 0xffff1b3affffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0b3effff0b3d, 0xffffffffffff0b3f, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b41ffff0b40, + 0xffffffffffff0b42, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xb43ffffffffffff, + 0xffffffffffffffff, 0xb45ffffffff0b44, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xb46ffffffffffff, 0xffffffff0b47ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff0b48, + 0xb49ffffffffffff, 0xffffffff0b4affff, 0xffffffffffff0b4b, + 0xffffffff0b4cffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0b4dffff, + 0xffffffff0b4f0b4e, 0xffffffffffffffff, 0xffffffffffffffff, + 0xb510b50ffffffff, 0xb530b52ffffffff, 0xb550b54ffffffff, + 0xffffffff0b570b56, 0xb590b58ffffffff, 0xb5b0b5affffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0b5d0b5cffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0b5effffffff, 0xffffffffffffffff, 0xb61ffff0b600b5f, + 0xffffffffffffffff, 0xb630b62ffffffff, 0xffffffff0b650b64, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0b66ffffffff, 0xb67ffffffffffff, 0xb69ffff0b68ffff, + 0xb6bffff0b6affff, 0xb6dffff0b6cffff, 0xb6fffff0b6effff, + 0xb71ffff0b70ffff, 0xffffffff0b72ffff, 0xffff0b74ffff0b73, + 0xffffffffffff0b75, 0x1376ffffffffffff, 0xffff1378ffffffff, + 0xffffffff137affff, 0x137effffffff137c, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff0b80ffff, 0xffffffffffffffff, + 0xffff0b81ffffffff, 0xb82ffffffffffff, 0xb84ffff0b83ffff, + 0xb86ffff0b85ffff, 0xb88ffff0b87ffff, 0xb8affff0b89ffff, + 0xb8cffff0b8bffff, 0xffffffff0b8dffff, 0xffff0b8fffff0b8e, + 0xffffffffffff0b90, 0x1391ffffffffffff, 0xffff1393ffffffff, + 0xffffffff1395ffff, 0x1399ffffffff1397, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xb9bffffffffffff, 0xffff0b9e0b9d0b9c, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff0b9fffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xba1ffff0ba0ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0ba2ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0ba40ba3ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); + @property immutable(CompEntry[]) compositionTable() + { + alias CE = CompEntry; + static immutable CE[] t = [ + CE(0x00338, 0x0226e), CE(0x00338, 0x02260), CE(0x00338, 0x0226f), + CE(0x00300, 0x000c0), CE(0x00301, 0x000c1), CE(0x00302, 0x000c2), + CE(0x00303, 0x000c3), CE(0x00304, 0x00100), CE(0x00306, 0x00102), + CE(0x00307, 0x00226), CE(0x00308, 0x000c4), CE(0x00309, 0x01ea2), + CE(0x0030a, 0x000c5), CE(0x0030c, 0x001cd), CE(0x0030f, 0x00200), + CE(0x00311, 0x00202), CE(0x00323, 0x01ea0), CE(0x00325, 0x01e00), + CE(0x00328, 0x00104), CE(0x00307, 0x01e02), CE(0x00323, 0x01e04), + CE(0x00331, 0x01e06), CE(0x00301, 0x00106), CE(0x00302, 0x00108), + CE(0x00307, 0x0010a), CE(0x0030c, 0x0010c), CE(0x00327, 0x000c7), + CE(0x00307, 0x01e0a), CE(0x0030c, 0x0010e), CE(0x00323, 0x01e0c), + CE(0x00327, 0x01e10), CE(0x0032d, 0x01e12), CE(0x00331, 0x01e0e), + CE(0x00300, 0x000c8), CE(0x00301, 0x000c9), CE(0x00302, 0x000ca), + CE(0x00303, 0x01ebc), CE(0x00304, 0x00112), CE(0x00306, 0x00114), + CE(0x00307, 0x00116), CE(0x00308, 0x000cb), CE(0x00309, 0x01eba), + CE(0x0030c, 0x0011a), CE(0x0030f, 0x00204), CE(0x00311, 0x00206), + CE(0x00323, 0x01eb8), CE(0x00327, 0x00228), CE(0x00328, 0x00118), + CE(0x0032d, 0x01e18), CE(0x00330, 0x01e1a), CE(0x00307, 0x01e1e), + CE(0x00301, 0x001f4), CE(0x00302, 0x0011c), CE(0x00304, 0x01e20), + CE(0x00306, 0x0011e), CE(0x00307, 0x00120), CE(0x0030c, 0x001e6), + CE(0x00327, 0x00122), CE(0x00302, 0x00124), CE(0x00307, 0x01e22), + CE(0x00308, 0x01e26), CE(0x0030c, 0x0021e), CE(0x00323, 0x01e24), + CE(0x00327, 0x01e28), CE(0x0032e, 0x01e2a), CE(0x00300, 0x000cc), + CE(0x00301, 0x000cd), CE(0x00302, 0x000ce), CE(0x00303, 0x00128), + CE(0x00304, 0x0012a), CE(0x00306, 0x0012c), CE(0x00307, 0x00130), + CE(0x00308, 0x000cf), CE(0x00309, 0x01ec8), CE(0x0030c, 0x001cf), + CE(0x0030f, 0x00208), CE(0x00311, 0x0020a), CE(0x00323, 0x01eca), + CE(0x00328, 0x0012e), CE(0x00330, 0x01e2c), CE(0x00302, 0x00134), + CE(0x00301, 0x01e30), CE(0x0030c, 0x001e8), CE(0x00323, 0x01e32), + CE(0x00327, 0x00136), CE(0x00331, 0x01e34), CE(0x00301, 0x00139), + CE(0x0030c, 0x0013d), CE(0x00323, 0x01e36), CE(0x00327, 0x0013b), + CE(0x0032d, 0x01e3c), CE(0x00331, 0x01e3a), CE(0x00301, 0x01e3e), + CE(0x00307, 0x01e40), CE(0x00323, 0x01e42), CE(0x00300, 0x001f8), + CE(0x00301, 0x00143), CE(0x00303, 0x000d1), CE(0x00307, 0x01e44), + CE(0x0030c, 0x00147), CE(0x00323, 0x01e46), CE(0x00327, 0x00145), + CE(0x0032d, 0x01e4a), CE(0x00331, 0x01e48), CE(0x00300, 0x000d2), + CE(0x00301, 0x000d3), CE(0x00302, 0x000d4), CE(0x00303, 0x000d5), + CE(0x00304, 0x0014c), CE(0x00306, 0x0014e), CE(0x00307, 0x0022e), + CE(0x00308, 0x000d6), CE(0x00309, 0x01ece), CE(0x0030b, 0x00150), + CE(0x0030c, 0x001d1), CE(0x0030f, 0x0020c), CE(0x00311, 0x0020e), + CE(0x0031b, 0x001a0), CE(0x00323, 0x01ecc), CE(0x00328, 0x001ea), + CE(0x00301, 0x01e54), CE(0x00307, 0x01e56), CE(0x00301, 0x00154), + CE(0x00307, 0x01e58), CE(0x0030c, 0x00158), CE(0x0030f, 0x00210), + CE(0x00311, 0x00212), CE(0x00323, 0x01e5a), CE(0x00327, 0x00156), + CE(0x00331, 0x01e5e), CE(0x00301, 0x0015a), CE(0x00302, 0x0015c), + CE(0x00307, 0x01e60), CE(0x0030c, 0x00160), CE(0x00323, 0x01e62), + CE(0x00326, 0x00218), CE(0x00327, 0x0015e), CE(0x00307, 0x01e6a), + CE(0x0030c, 0x00164), CE(0x00323, 0x01e6c), CE(0x00326, 0x0021a), + CE(0x00327, 0x00162), CE(0x0032d, 0x01e70), CE(0x00331, 0x01e6e), + CE(0x00300, 0x000d9), CE(0x00301, 0x000da), CE(0x00302, 0x000db), + CE(0x00303, 0x00168), CE(0x00304, 0x0016a), CE(0x00306, 0x0016c), + CE(0x00308, 0x000dc), CE(0x00309, 0x01ee6), CE(0x0030a, 0x0016e), + CE(0x0030b, 0x00170), CE(0x0030c, 0x001d3), CE(0x0030f, 0x00214), + CE(0x00311, 0x00216), CE(0x0031b, 0x001af), CE(0x00323, 0x01ee4), + CE(0x00324, 0x01e72), CE(0x00328, 0x00172), CE(0x0032d, 0x01e76), + CE(0x00330, 0x01e74), CE(0x00303, 0x01e7c), CE(0x00323, 0x01e7e), + CE(0x00300, 0x01e80), CE(0x00301, 0x01e82), CE(0x00302, 0x00174), + CE(0x00307, 0x01e86), CE(0x00308, 0x01e84), CE(0x00323, 0x01e88), + CE(0x00307, 0x01e8a), CE(0x00308, 0x01e8c), CE(0x00300, 0x01ef2), + CE(0x00301, 0x000dd), CE(0x00302, 0x00176), CE(0x00303, 0x01ef8), + CE(0x00304, 0x00232), CE(0x00307, 0x01e8e), CE(0x00308, 0x00178), + CE(0x00309, 0x01ef6), CE(0x00323, 0x01ef4), CE(0x00301, 0x00179), + CE(0x00302, 0x01e90), CE(0x00307, 0x0017b), CE(0x0030c, 0x0017d), + CE(0x00323, 0x01e92), CE(0x00331, 0x01e94), CE(0x00300, 0x000e0), + CE(0x00301, 0x000e1), CE(0x00302, 0x000e2), CE(0x00303, 0x000e3), + CE(0x00304, 0x00101), CE(0x00306, 0x00103), CE(0x00307, 0x00227), + CE(0x00308, 0x000e4), CE(0x00309, 0x01ea3), CE(0x0030a, 0x000e5), + CE(0x0030c, 0x001ce), CE(0x0030f, 0x00201), CE(0x00311, 0x00203), + CE(0x00323, 0x01ea1), CE(0x00325, 0x01e01), CE(0x00328, 0x00105), + CE(0x00307, 0x01e03), CE(0x00323, 0x01e05), CE(0x00331, 0x01e07), + CE(0x00301, 0x00107), CE(0x00302, 0x00109), CE(0x00307, 0x0010b), + CE(0x0030c, 0x0010d), CE(0x00327, 0x000e7), CE(0x00307, 0x01e0b), + CE(0x0030c, 0x0010f), CE(0x00323, 0x01e0d), CE(0x00327, 0x01e11), + CE(0x0032d, 0x01e13), CE(0x00331, 0x01e0f), CE(0x00300, 0x000e8), + CE(0x00301, 0x000e9), CE(0x00302, 0x000ea), CE(0x00303, 0x01ebd), + CE(0x00304, 0x00113), CE(0x00306, 0x00115), CE(0x00307, 0x00117), + CE(0x00308, 0x000eb), CE(0x00309, 0x01ebb), CE(0x0030c, 0x0011b), + CE(0x0030f, 0x00205), CE(0x00311, 0x00207), CE(0x00323, 0x01eb9), + CE(0x00327, 0x00229), CE(0x00328, 0x00119), CE(0x0032d, 0x01e19), + CE(0x00330, 0x01e1b), CE(0x00307, 0x01e1f), CE(0x00301, 0x001f5), + CE(0x00302, 0x0011d), CE(0x00304, 0x01e21), CE(0x00306, 0x0011f), + CE(0x00307, 0x00121), CE(0x0030c, 0x001e7), CE(0x00327, 0x00123), + CE(0x00302, 0x00125), CE(0x00307, 0x01e23), CE(0x00308, 0x01e27), + CE(0x0030c, 0x0021f), CE(0x00323, 0x01e25), CE(0x00327, 0x01e29), + CE(0x0032e, 0x01e2b), CE(0x00331, 0x01e96), CE(0x00300, 0x000ec), + CE(0x00301, 0x000ed), CE(0x00302, 0x000ee), CE(0x00303, 0x00129), + CE(0x00304, 0x0012b), CE(0x00306, 0x0012d), CE(0x00308, 0x000ef), + CE(0x00309, 0x01ec9), CE(0x0030c, 0x001d0), CE(0x0030f, 0x00209), + CE(0x00311, 0x0020b), CE(0x00323, 0x01ecb), CE(0x00328, 0x0012f), + CE(0x00330, 0x01e2d), CE(0x00302, 0x00135), CE(0x0030c, 0x001f0), + CE(0x00301, 0x01e31), CE(0x0030c, 0x001e9), CE(0x00323, 0x01e33), + CE(0x00327, 0x00137), CE(0x00331, 0x01e35), CE(0x00301, 0x0013a), + CE(0x0030c, 0x0013e), CE(0x00323, 0x01e37), CE(0x00327, 0x0013c), + CE(0x0032d, 0x01e3d), CE(0x00331, 0x01e3b), CE(0x00301, 0x01e3f), + CE(0x00307, 0x01e41), CE(0x00323, 0x01e43), CE(0x00300, 0x001f9), + CE(0x00301, 0x00144), CE(0x00303, 0x000f1), CE(0x00307, 0x01e45), + CE(0x0030c, 0x00148), CE(0x00323, 0x01e47), CE(0x00327, 0x00146), + CE(0x0032d, 0x01e4b), CE(0x00331, 0x01e49), CE(0x00300, 0x000f2), + CE(0x00301, 0x000f3), CE(0x00302, 0x000f4), CE(0x00303, 0x000f5), + CE(0x00304, 0x0014d), CE(0x00306, 0x0014f), CE(0x00307, 0x0022f), + CE(0x00308, 0x000f6), CE(0x00309, 0x01ecf), CE(0x0030b, 0x00151), + CE(0x0030c, 0x001d2), CE(0x0030f, 0x0020d), CE(0x00311, 0x0020f), + CE(0x0031b, 0x001a1), CE(0x00323, 0x01ecd), CE(0x00328, 0x001eb), + CE(0x00301, 0x01e55), CE(0x00307, 0x01e57), CE(0x00301, 0x00155), + CE(0x00307, 0x01e59), CE(0x0030c, 0x00159), CE(0x0030f, 0x00211), + CE(0x00311, 0x00213), CE(0x00323, 0x01e5b), CE(0x00327, 0x00157), + CE(0x00331, 0x01e5f), CE(0x00301, 0x0015b), CE(0x00302, 0x0015d), + CE(0x00307, 0x01e61), CE(0x0030c, 0x00161), CE(0x00323, 0x01e63), + CE(0x00326, 0x00219), CE(0x00327, 0x0015f), CE(0x00307, 0x01e6b), + CE(0x00308, 0x01e97), CE(0x0030c, 0x00165), CE(0x00323, 0x01e6d), + CE(0x00326, 0x0021b), CE(0x00327, 0x00163), CE(0x0032d, 0x01e71), + CE(0x00331, 0x01e6f), CE(0x00300, 0x000f9), CE(0x00301, 0x000fa), + CE(0x00302, 0x000fb), CE(0x00303, 0x00169), CE(0x00304, 0x0016b), + CE(0x00306, 0x0016d), CE(0x00308, 0x000fc), CE(0x00309, 0x01ee7), + CE(0x0030a, 0x0016f), CE(0x0030b, 0x00171), CE(0x0030c, 0x001d4), + CE(0x0030f, 0x00215), CE(0x00311, 0x00217), CE(0x0031b, 0x001b0), + CE(0x00323, 0x01ee5), CE(0x00324, 0x01e73), CE(0x00328, 0x00173), + CE(0x0032d, 0x01e77), CE(0x00330, 0x01e75), CE(0x00303, 0x01e7d), + CE(0x00323, 0x01e7f), CE(0x00300, 0x01e81), CE(0x00301, 0x01e83), + CE(0x00302, 0x00175), CE(0x00307, 0x01e87), CE(0x00308, 0x01e85), + CE(0x0030a, 0x01e98), CE(0x00323, 0x01e89), CE(0x00307, 0x01e8b), + CE(0x00308, 0x01e8d), CE(0x00300, 0x01ef3), CE(0x00301, 0x000fd), + CE(0x00302, 0x00177), CE(0x00303, 0x01ef9), CE(0x00304, 0x00233), + CE(0x00307, 0x01e8f), CE(0x00308, 0x000ff), CE(0x00309, 0x01ef7), + CE(0x0030a, 0x01e99), CE(0x00323, 0x01ef5), CE(0x00301, 0x0017a), + CE(0x00302, 0x01e91), CE(0x00307, 0x0017c), CE(0x0030c, 0x0017e), + CE(0x00323, 0x01e93), CE(0x00331, 0x01e95), CE(0x00300, 0x01fed), + CE(0x00301, 0x00385), CE(0x00342, 0x01fc1), CE(0x00300, 0x01ea6), + CE(0x00301, 0x01ea4), CE(0x00303, 0x01eaa), CE(0x00309, 0x01ea8), + CE(0x00304, 0x001de), CE(0x00301, 0x001fa), CE(0x00301, 0x001fc), + CE(0x00304, 0x001e2), CE(0x00301, 0x01e08), CE(0x00300, 0x01ec0), + CE(0x00301, 0x01ebe), CE(0x00303, 0x01ec4), CE(0x00309, 0x01ec2), + CE(0x00301, 0x01e2e), CE(0x00300, 0x01ed2), CE(0x00301, 0x01ed0), + CE(0x00303, 0x01ed6), CE(0x00309, 0x01ed4), CE(0x00301, 0x01e4c), + CE(0x00304, 0x0022c), CE(0x00308, 0x01e4e), CE(0x00304, 0x0022a), + CE(0x00301, 0x001fe), CE(0x00300, 0x001db), CE(0x00301, 0x001d7), + CE(0x00304, 0x001d5), CE(0x0030c, 0x001d9), CE(0x00300, 0x01ea7), + CE(0x00301, 0x01ea5), CE(0x00303, 0x01eab), CE(0x00309, 0x01ea9), + CE(0x00304, 0x001df), CE(0x00301, 0x001fb), CE(0x00301, 0x001fd), + CE(0x00304, 0x001e3), CE(0x00301, 0x01e09), CE(0x00300, 0x01ec1), + CE(0x00301, 0x01ebf), CE(0x00303, 0x01ec5), CE(0x00309, 0x01ec3), + CE(0x00301, 0x01e2f), CE(0x00300, 0x01ed3), CE(0x00301, 0x01ed1), + CE(0x00303, 0x01ed7), CE(0x00309, 0x01ed5), CE(0x00301, 0x01e4d), + CE(0x00304, 0x0022d), CE(0x00308, 0x01e4f), CE(0x00304, 0x0022b), + CE(0x00301, 0x001ff), CE(0x00300, 0x001dc), CE(0x00301, 0x001d8), + CE(0x00304, 0x001d6), CE(0x0030c, 0x001da), CE(0x00300, 0x01eb0), + CE(0x00301, 0x01eae), CE(0x00303, 0x01eb4), CE(0x00309, 0x01eb2), + CE(0x00300, 0x01eb1), CE(0x00301, 0x01eaf), CE(0x00303, 0x01eb5), + CE(0x00309, 0x01eb3), CE(0x00300, 0x01e14), CE(0x00301, 0x01e16), + CE(0x00300, 0x01e15), CE(0x00301, 0x01e17), CE(0x00300, 0x01e50), + CE(0x00301, 0x01e52), CE(0x00300, 0x01e51), CE(0x00301, 0x01e53), + CE(0x00307, 0x01e64), CE(0x00307, 0x01e65), CE(0x00307, 0x01e66), + CE(0x00307, 0x01e67), CE(0x00301, 0x01e78), CE(0x00301, 0x01e79), + CE(0x00308, 0x01e7a), CE(0x00308, 0x01e7b), CE(0x00307, 0x01e9b), + CE(0x00300, 0x01edc), CE(0x00301, 0x01eda), CE(0x00303, 0x01ee0), + CE(0x00309, 0x01ede), CE(0x00323, 0x01ee2), CE(0x00300, 0x01edd), + CE(0x00301, 0x01edb), CE(0x00303, 0x01ee1), CE(0x00309, 0x01edf), + CE(0x00323, 0x01ee3), CE(0x00300, 0x01eea), CE(0x00301, 0x01ee8), + CE(0x00303, 0x01eee), CE(0x00309, 0x01eec), CE(0x00323, 0x01ef0), + CE(0x00300, 0x01eeb), CE(0x00301, 0x01ee9), CE(0x00303, 0x01eef), + CE(0x00309, 0x01eed), CE(0x00323, 0x01ef1), CE(0x0030c, 0x001ee), + CE(0x00304, 0x001ec), CE(0x00304, 0x001ed), CE(0x00304, 0x001e0), + CE(0x00304, 0x001e1), CE(0x00306, 0x01e1c), CE(0x00306, 0x01e1d), + CE(0x00304, 0x00230), CE(0x00304, 0x00231), CE(0x0030c, 0x001ef), + CE(0x00300, 0x01fba), CE(0x00301, 0x00386), CE(0x00304, 0x01fb9), + CE(0x00306, 0x01fb8), CE(0x00313, 0x01f08), CE(0x00314, 0x01f09), + CE(0x00345, 0x01fbc), CE(0x00300, 0x01fc8), CE(0x00301, 0x00388), + CE(0x00313, 0x01f18), CE(0x00314, 0x01f19), CE(0x00300, 0x01fca), + CE(0x00301, 0x00389), CE(0x00313, 0x01f28), CE(0x00314, 0x01f29), + CE(0x00345, 0x01fcc), CE(0x00300, 0x01fda), CE(0x00301, 0x0038a), + CE(0x00304, 0x01fd9), CE(0x00306, 0x01fd8), CE(0x00308, 0x003aa), + CE(0x00313, 0x01f38), CE(0x00314, 0x01f39), CE(0x00300, 0x01ff8), + CE(0x00301, 0x0038c), CE(0x00313, 0x01f48), CE(0x00314, 0x01f49), + CE(0x00314, 0x01fec), CE(0x00300, 0x01fea), CE(0x00301, 0x0038e), + CE(0x00304, 0x01fe9), CE(0x00306, 0x01fe8), CE(0x00308, 0x003ab), + CE(0x00314, 0x01f59), CE(0x00300, 0x01ffa), CE(0x00301, 0x0038f), + CE(0x00313, 0x01f68), CE(0x00314, 0x01f69), CE(0x00345, 0x01ffc), + CE(0x00345, 0x01fb4), CE(0x00345, 0x01fc4), CE(0x00300, 0x01f70), + CE(0x00301, 0x003ac), CE(0x00304, 0x01fb1), CE(0x00306, 0x01fb0), + CE(0x00313, 0x01f00), CE(0x00314, 0x01f01), CE(0x00342, 0x01fb6), + CE(0x00345, 0x01fb3), CE(0x00300, 0x01f72), CE(0x00301, 0x003ad), + CE(0x00313, 0x01f10), CE(0x00314, 0x01f11), CE(0x00300, 0x01f74), + CE(0x00301, 0x003ae), CE(0x00313, 0x01f20), CE(0x00314, 0x01f21), + CE(0x00342, 0x01fc6), CE(0x00345, 0x01fc3), CE(0x00300, 0x01f76), + CE(0x00301, 0x003af), CE(0x00304, 0x01fd1), CE(0x00306, 0x01fd0), + CE(0x00308, 0x003ca), CE(0x00313, 0x01f30), CE(0x00314, 0x01f31), + CE(0x00342, 0x01fd6), CE(0x00300, 0x01f78), CE(0x00301, 0x003cc), + CE(0x00313, 0x01f40), CE(0x00314, 0x01f41), CE(0x00313, 0x01fe4), + CE(0x00314, 0x01fe5), CE(0x00300, 0x01f7a), CE(0x00301, 0x003cd), + CE(0x00304, 0x01fe1), CE(0x00306, 0x01fe0), CE(0x00308, 0x003cb), + CE(0x00313, 0x01f50), CE(0x00314, 0x01f51), CE(0x00342, 0x01fe6), + CE(0x00300, 0x01f7c), CE(0x00301, 0x003ce), CE(0x00313, 0x01f60), + CE(0x00314, 0x01f61), CE(0x00342, 0x01ff6), CE(0x00345, 0x01ff3), + CE(0x00300, 0x01fd2), CE(0x00301, 0x00390), CE(0x00342, 0x01fd7), + CE(0x00300, 0x01fe2), CE(0x00301, 0x003b0), CE(0x00342, 0x01fe7), + CE(0x00345, 0x01ff4), CE(0x00301, 0x003d3), CE(0x00308, 0x003d4), + CE(0x00308, 0x00407), CE(0x00306, 0x004d0), CE(0x00308, 0x004d2), + CE(0x00301, 0x00403), CE(0x00300, 0x00400), CE(0x00306, 0x004d6), + CE(0x00308, 0x00401), CE(0x00306, 0x004c1), CE(0x00308, 0x004dc), + CE(0x00308, 0x004de), CE(0x00300, 0x0040d), CE(0x00304, 0x004e2), + CE(0x00306, 0x00419), CE(0x00308, 0x004e4), CE(0x00301, 0x0040c), + CE(0x00308, 0x004e6), CE(0x00304, 0x004ee), CE(0x00306, 0x0040e), + CE(0x00308, 0x004f0), CE(0x0030b, 0x004f2), CE(0x00308, 0x004f4), + CE(0x00308, 0x004f8), CE(0x00308, 0x004ec), CE(0x00306, 0x004d1), + CE(0x00308, 0x004d3), CE(0x00301, 0x00453), CE(0x00300, 0x00450), + CE(0x00306, 0x004d7), CE(0x00308, 0x00451), CE(0x00306, 0x004c2), + CE(0x00308, 0x004dd), CE(0x00308, 0x004df), CE(0x00300, 0x0045d), + CE(0x00304, 0x004e3), CE(0x00306, 0x00439), CE(0x00308, 0x004e5), + CE(0x00301, 0x0045c), CE(0x00308, 0x004e7), CE(0x00304, 0x004ef), + CE(0x00306, 0x0045e), CE(0x00308, 0x004f1), CE(0x0030b, 0x004f3), + CE(0x00308, 0x004f5), CE(0x00308, 0x004f9), CE(0x00308, 0x004ed), + CE(0x00308, 0x00457), CE(0x0030f, 0x00476), CE(0x0030f, 0x00477), + CE(0x00308, 0x004da), CE(0x00308, 0x004db), CE(0x00308, 0x004ea), + CE(0x00308, 0x004eb), CE(0x00653, 0x00622), CE(0x00654, 0x00623), + CE(0x00655, 0x00625), CE(0x00654, 0x00624), CE(0x00654, 0x00626), + CE(0x00654, 0x006c2), CE(0x00654, 0x006d3), CE(0x00654, 0x006c0), + CE(0x0093c, 0x00929), CE(0x0093c, 0x00931), CE(0x0093c, 0x00934), + CE(0x009be, 0x009cb), CE(0x009d7, 0x009cc), CE(0x00b3e, 0x00b4b), + CE(0x00b56, 0x00b48), CE(0x00b57, 0x00b4c), CE(0x00bd7, 0x00b94), + CE(0x00bbe, 0x00bca), CE(0x00bd7, 0x00bcc), CE(0x00bbe, 0x00bcb), + CE(0x00c56, 0x00c48), CE(0x00cd5, 0x00cc0), CE(0x00cc2, 0x00cca), + CE(0x00cd5, 0x00cc7), CE(0x00cd6, 0x00cc8), CE(0x00cd5, 0x00ccb), + CE(0x00d3e, 0x00d4a), CE(0x00d57, 0x00d4c), CE(0x00d3e, 0x00d4b), + CE(0x00dca, 0x00dda), CE(0x00dcf, 0x00ddc), CE(0x00ddf, 0x00dde), + CE(0x00dca, 0x00ddd), CE(0x0102e, 0x01026), CE(0x01b35, 0x01b06), + CE(0x01b35, 0x01b08), CE(0x01b35, 0x01b0a), CE(0x01b35, 0x01b0c), + CE(0x01b35, 0x01b0e), CE(0x01b35, 0x01b12), CE(0x01b35, 0x01b3b), + CE(0x01b35, 0x01b3d), CE(0x01b35, 0x01b40), CE(0x01b35, 0x01b41), + CE(0x01b35, 0x01b43), CE(0x00304, 0x01e38), CE(0x00304, 0x01e39), + CE(0x00304, 0x01e5c), CE(0x00304, 0x01e5d), CE(0x00307, 0x01e68), + CE(0x00307, 0x01e69), CE(0x00302, 0x01eac), CE(0x00306, 0x01eb6), + CE(0x00302, 0x01ead), CE(0x00306, 0x01eb7), CE(0x00302, 0x01ec6), + CE(0x00302, 0x01ec7), CE(0x00302, 0x01ed8), CE(0x00302, 0x01ed9), + CE(0x00300, 0x01f02), CE(0x00301, 0x01f04), CE(0x00342, 0x01f06), + CE(0x00345, 0x01f80), CE(0x00300, 0x01f03), CE(0x00301, 0x01f05), + CE(0x00342, 0x01f07), CE(0x00345, 0x01f81), CE(0x00345, 0x01f82), + CE(0x00345, 0x01f83), CE(0x00345, 0x01f84), CE(0x00345, 0x01f85), + CE(0x00345, 0x01f86), CE(0x00345, 0x01f87), CE(0x00300, 0x01f0a), + CE(0x00301, 0x01f0c), CE(0x00342, 0x01f0e), CE(0x00345, 0x01f88), + CE(0x00300, 0x01f0b), CE(0x00301, 0x01f0d), CE(0x00342, 0x01f0f), + CE(0x00345, 0x01f89), CE(0x00345, 0x01f8a), CE(0x00345, 0x01f8b), + CE(0x00345, 0x01f8c), CE(0x00345, 0x01f8d), CE(0x00345, 0x01f8e), + CE(0x00345, 0x01f8f), CE(0x00300, 0x01f12), CE(0x00301, 0x01f14), + CE(0x00300, 0x01f13), CE(0x00301, 0x01f15), CE(0x00300, 0x01f1a), + CE(0x00301, 0x01f1c), CE(0x00300, 0x01f1b), CE(0x00301, 0x01f1d), + CE(0x00300, 0x01f22), CE(0x00301, 0x01f24), CE(0x00342, 0x01f26), + CE(0x00345, 0x01f90), CE(0x00300, 0x01f23), CE(0x00301, 0x01f25), + CE(0x00342, 0x01f27), CE(0x00345, 0x01f91), CE(0x00345, 0x01f92), + CE(0x00345, 0x01f93), CE(0x00345, 0x01f94), CE(0x00345, 0x01f95), + CE(0x00345, 0x01f96), CE(0x00345, 0x01f97), CE(0x00300, 0x01f2a), + CE(0x00301, 0x01f2c), CE(0x00342, 0x01f2e), CE(0x00345, 0x01f98), + CE(0x00300, 0x01f2b), CE(0x00301, 0x01f2d), CE(0x00342, 0x01f2f), + CE(0x00345, 0x01f99), CE(0x00345, 0x01f9a), CE(0x00345, 0x01f9b), + CE(0x00345, 0x01f9c), CE(0x00345, 0x01f9d), CE(0x00345, 0x01f9e), + CE(0x00345, 0x01f9f), CE(0x00300, 0x01f32), CE(0x00301, 0x01f34), + CE(0x00342, 0x01f36), CE(0x00300, 0x01f33), CE(0x00301, 0x01f35), + CE(0x00342, 0x01f37), CE(0x00300, 0x01f3a), CE(0x00301, 0x01f3c), + CE(0x00342, 0x01f3e), CE(0x00300, 0x01f3b), CE(0x00301, 0x01f3d), + CE(0x00342, 0x01f3f), CE(0x00300, 0x01f42), CE(0x00301, 0x01f44), + CE(0x00300, 0x01f43), CE(0x00301, 0x01f45), CE(0x00300, 0x01f4a), + CE(0x00301, 0x01f4c), CE(0x00300, 0x01f4b), CE(0x00301, 0x01f4d), + CE(0x00300, 0x01f52), CE(0x00301, 0x01f54), CE(0x00342, 0x01f56), + CE(0x00300, 0x01f53), CE(0x00301, 0x01f55), CE(0x00342, 0x01f57), + CE(0x00300, 0x01f5b), CE(0x00301, 0x01f5d), CE(0x00342, 0x01f5f), + CE(0x00300, 0x01f62), CE(0x00301, 0x01f64), CE(0x00342, 0x01f66), + CE(0x00345, 0x01fa0), CE(0x00300, 0x01f63), CE(0x00301, 0x01f65), + CE(0x00342, 0x01f67), CE(0x00345, 0x01fa1), CE(0x00345, 0x01fa2), + CE(0x00345, 0x01fa3), CE(0x00345, 0x01fa4), CE(0x00345, 0x01fa5), + CE(0x00345, 0x01fa6), CE(0x00345, 0x01fa7), CE(0x00300, 0x01f6a), + CE(0x00301, 0x01f6c), CE(0x00342, 0x01f6e), CE(0x00345, 0x01fa8), + CE(0x00300, 0x01f6b), CE(0x00301, 0x01f6d), CE(0x00342, 0x01f6f), + CE(0x00345, 0x01fa9), CE(0x00345, 0x01faa), CE(0x00345, 0x01fab), + CE(0x00345, 0x01fac), CE(0x00345, 0x01fad), CE(0x00345, 0x01fae), + CE(0x00345, 0x01faf), CE(0x00345, 0x01fb2), CE(0x00345, 0x01fc2), + CE(0x00345, 0x01ff2), CE(0x00345, 0x01fb7), CE(0x00300, 0x01fcd), + CE(0x00301, 0x01fce), CE(0x00342, 0x01fcf), CE(0x00345, 0x01fc7), + CE(0x00345, 0x01ff7), CE(0x00300, 0x01fdd), CE(0x00301, 0x01fde), + CE(0x00342, 0x01fdf), CE(0x00338, 0x0219a), CE(0x00338, 0x0219b), + CE(0x00338, 0x021ae), CE(0x00338, 0x021cd), CE(0x00338, 0x021cf), + CE(0x00338, 0x021ce), CE(0x00338, 0x02204), CE(0x00338, 0x02209), + CE(0x00338, 0x0220c), CE(0x00338, 0x02224), CE(0x00338, 0x02226), + CE(0x00338, 0x02241), CE(0x00338, 0x02244), CE(0x00338, 0x02247), + CE(0x00338, 0x02249), CE(0x00338, 0x0226d), CE(0x00338, 0x02262), + CE(0x00338, 0x02270), CE(0x00338, 0x02271), CE(0x00338, 0x02274), + CE(0x00338, 0x02275), CE(0x00338, 0x02278), CE(0x00338, 0x02279), + CE(0x00338, 0x02280), CE(0x00338, 0x02281), CE(0x00338, 0x022e0), + CE(0x00338, 0x022e1), CE(0x00338, 0x02284), CE(0x00338, 0x02285), + CE(0x00338, 0x02288), CE(0x00338, 0x02289), CE(0x00338, 0x022e2), + CE(0x00338, 0x022e3), CE(0x00338, 0x022ac), CE(0x00338, 0x022ad), + CE(0x00338, 0x022ae), CE(0x00338, 0x022af), CE(0x00338, 0x022ea), + CE(0x00338, 0x022eb), CE(0x00338, 0x022ec), CE(0x00338, 0x022ed), + CE(0x03099, 0x03094), CE(0x03099, 0x0304c), CE(0x03099, 0x0304e), + CE(0x03099, 0x03050), CE(0x03099, 0x03052), CE(0x03099, 0x03054), + CE(0x03099, 0x03056), CE(0x03099, 0x03058), CE(0x03099, 0x0305a), + CE(0x03099, 0x0305c), CE(0x03099, 0x0305e), CE(0x03099, 0x03060), + CE(0x03099, 0x03062), CE(0x03099, 0x03065), CE(0x03099, 0x03067), + CE(0x03099, 0x03069), CE(0x03099, 0x03070), CE(0x0309a, 0x03071), + CE(0x03099, 0x03073), CE(0x0309a, 0x03074), CE(0x03099, 0x03076), + CE(0x0309a, 0x03077), CE(0x03099, 0x03079), CE(0x0309a, 0x0307a), + CE(0x03099, 0x0307c), CE(0x0309a, 0x0307d), CE(0x03099, 0x0309e), + CE(0x03099, 0x030f4), CE(0x03099, 0x030ac), CE(0x03099, 0x030ae), + CE(0x03099, 0x030b0), CE(0x03099, 0x030b2), CE(0x03099, 0x030b4), + CE(0x03099, 0x030b6), CE(0x03099, 0x030b8), CE(0x03099, 0x030ba), + CE(0x03099, 0x030bc), CE(0x03099, 0x030be), CE(0x03099, 0x030c0), + CE(0x03099, 0x030c2), CE(0x03099, 0x030c5), CE(0x03099, 0x030c7), + CE(0x03099, 0x030c9), CE(0x03099, 0x030d0), CE(0x0309a, 0x030d1), + CE(0x03099, 0x030d3), CE(0x0309a, 0x030d4), CE(0x03099, 0x030d6), + CE(0x0309a, 0x030d7), CE(0x03099, 0x030d9), CE(0x0309a, 0x030da), + CE(0x03099, 0x030dc), CE(0x0309a, 0x030dd), CE(0x03099, 0x030f7), + CE(0x03099, 0x030f8), CE(0x03099, 0x030f9), CE(0x03099, 0x030fa), + CE(0x03099, 0x030fe), CE(0x110ba, 0x1109a), CE(0x110ba, 0x1109c), + CE(0x110ba, 0x110ab), CE(0x11127, 0x1112e), CE(0x11127, 0x1112f), + ]; + return t; + } } - -static if(size_t.sizeof == 4) { -//6976 bytes -enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 7, 6)([ 0x0, 0x40, 0x240], [ 0x100, 0x400, 0x1240], [ 0x2020100, 0x4020302, 0x2020205, 0x2060202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20001, 0x0, 0x0, 0x3, 0x0, 0x50004, 0x70006, 0x80000, 0xa0009, 0xb0000, 0xd000c, 0xe0000, 0x10000f, 0x11000f, 0x11000f, 0x11000f, 0x11000f, 0x110000, 0x120000, 0x11000f, 0x110000, 0x130000, 0x150014, 0x170016, 0x190018, 0x1b001a, 0x1c, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x1e0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x200000, 0x0, 0x21, 0x22, 0x0, 0x240023, 0x0, 0x260025, 0x280027, 0x29, 0x2a0000, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d0000, 0x2e0000, 0x2f0000, 0x0, 0x0, 0x0, 0x0, 0x30, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x320000, 0x340033, 0x0, 0x0, 0x35, 0x360000, 0x380037, 0x3a0039, 0x0, 0x3c003b, 0x0, 0x3d0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x350000, 0x42, 0x43, 0x3a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x450000, 0x46, 0x470000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xdcdce8e6, 0xd8e8dcdc, 0xdcdcdcdc, 0xdccacadc, 0xcadcdcdc, 0xdcdcdcca, 0xdcdcdcdc, 0xdcdcdcdc, 0x1010101, 0xdcdcdc01, 0xe6e6e6dc, 0xe6e6e6e6, 0xdce6f0e6, 0xe6e6dcdc, 0xdcdce6, 0xdce6e6e6, 0xe6dcdcdc, 0xe6dcdce8, 0xe9eaeae9, 0xe6e9eaea, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6dc00, 0xe6dce6e6, 0xdcdee6e6, 0xe6e6e6e6, 0xdcdce6e6, 0xdcdcdcdc, 0xe6dce6e6, 0xe6e4dee6, 0xd0c0b0a, 0x11100f0e, 0x14131312, 0x17001615, 0x191800, 0x1200dce6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0x201f1e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b000000, 0x1f1e1d1c, 0xe6222120, 0xe6dcdce6, 0xe6e6e6e6, 0xdce6e6dc, 0x0, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e60000, 0xe6e6e6e6, 0xe60000e6, 0xdce6e6e6, 0xe60000e6, 0xe6dc00e6, 0xdce6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6dce6, 0xdce6e6dc, 0xdce6dcdc, 0xe6dce6dc, 0xe6dce6e6, 0xe6dce6dc, 0xe6e6dc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6e6e6, 0xe6dce6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e60000, 0xe600e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e600, 0xe6e6e600, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdcdcdc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6dce6e6, 0xe6e6dce6, 0xdcdcdce6, 0xe61d1c1b, 0xe6dce6e6, 0xe6dcdce6, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x900, 0xe6dce600, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900, 0x0, 0x5b5400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96767, 0x0, 0x0, 0x0, 0x6b6b6b6b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7676, 0x0, 0x0, 0x0, 0x7a7a7a7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdcdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc00dc00, 0xd800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x828100, 0x84, 0x82820000, 0x8282, 0xe6e60082, 0xe6e60009, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000, 0x90900, 0x0, 0x0, 0x0, 0x0, 0xdc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0xe600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdce6de00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0xe6e6e600, 0xe6e6e6e6, 0xdc0000e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6e6dc, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70000, 0x0, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6, 0xdcdcdc01, 0xe6e6dcdc, 0xdcdcdcdc, 0x10100e6, 0x1010101, 0x1, 0xdc00, 0x0, 0xe6, 0x0, 0x0, 0xe6dce6e6, 0xe6e6e6e6, 0xe6dce6e6, 0xdcd6eae6, 0xe6e6e6ca, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdce6dce9, 0x0, 0x0, 0x0, 0x0, 0x101e6e6, 0xe6e6e6e6, 0xe6010101, 0xe6, 0xe600, 0xe6010100, 0x101e6dc, 0xdcdcdcdc, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe4da0000, 0xe0e0dee8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc000000, 0xdcdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e600e6, 0xe60000dc, 0xe6, 0xe6e60000, 0xe600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc00, 0x0, 0x0, 0x0, 0xe600dc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc01e6, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70900, 0x0, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9000000, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1d8d800, 0x101, 0xd8d8e200, 0xd8d8d8, 0x0, 0xdc000000, 0xdcdcdcdc, 0xdcdcdc, 0xe6e6e600, 0xdcdce6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e60000, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0xe6e60000, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -enum composeIdxMask = (1<<11)-1, composeCntShift = 11; -enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)([ 0x0, 0x800], [ 0x1000, 0x2000], [ 0x10000, 0x30002, 0x50004, 0x70006, 0x70008, 0x70007, 0x90007, 0xa0007, 0xc000b, 0x70007, 0x70007, 0x70007, 0x7000d, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x7000e, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x8010800, 0xffff0802, 0x8003ffff, 0x28161813, 0x8821301b, 0x38330832, 0x7841383a, 0x28510850, 0x185c3056, 0x8068485f, 0xffff1078, 0x3882407a, 0x98903889, 0x30a510a3, 0x48ad10ab, 0xffff30b6, 0xffffffff, 0xffffffff, 0x80bcffff, 0x28cf18cc, 0x88da30d4, 0x38ec08eb, 0x70fb40f3, 0x290b1109, 0x19163110, 0x81224919, 0xffff1132, 0x393c4134, 0x994b4143, 0x3960115e, 0x51691167, 0xffff3173, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff1979, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff217c, 0x9810980, 0x9841182, 0xffffffff, 0xffff2185, 0xffffffff, 0x989ffff, 0xffffffff, 0xffffffff, 0x198e218a, 0xffff0991, 0xffff0992, 0xffffffff, 0xffff2193, 0xffffffff, 0xffffffff, 0xffff2197, 0x99c099b, 0x99f119d, 0xffffffff, 0xffff21a0, 0xffffffff, 0x9a4ffff, 0xffffffff, 0xffffffff, 0x19a921a5, 0xffff09ac, 0xffff09ad, 0xffffffff, 0xffff21ae, 0xffffffff, 0xffffffff, 0x21b621b2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x11bc11ba, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x11c011be, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9c309c2, 0xffffffff, 0xffffffff, 0x9c509c4, 0xffffffff, 0xffffffff, 0xffffffff, 0x9c709c6, 0x9c909c8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9caffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x29d029cb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x29d5ffff, 0xffff29da, 0xffffffff, 0xffffffff, 0x9dfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9e109e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9e309e2, 0x9e509e4, 0xffffffff, 0xffffffff, 0x9e709e6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff09e8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x39e9ffff, 0xffffffff, 0x21f0ffff, 0x29f4ffff, 0x39f9ffff, 0xffffffff, 0xffffffff, 0x2200ffff, 0xa04ffff, 0xffffffff, 0x3205ffff, 0xffffffff, 0x2a0bffff, 0xffffffff, 0xffff0a10, 0xffff0a11, 0x4212ffff, 0xffffffff, 0x221affff, 0x321effff, 0x4224ffff, 0xffffffff, 0xffffffff, 0x222cffff, 0x1230ffff, 0xffffffff, 0x4232ffff, 0xffffffff, 0x323affff, 0x1a431a40, 0xffffffff, 0xffff0a46, 0xffffffff, 0xffff1247, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a49, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff124a, 0xa4cffff, 0x1a4dffff, 0xa521250, 0xffff2253, 0xffff0a57, 0xffffffff, 0xffff0a58, 0xffffffff, 0x2259ffff, 0xffffffff, 0xa5dffff, 0xffffffff, 0xa5effff, 0xa5fffff, 0xffffffff, 0xffff1260, 0xa62ffff, 0x1a63ffff, 0xa681266, 0xffff2269, 0xffff0a6d, 0xffffffff, 0xffff0a6e, 0xffffffff, 0x226fffff, 0xffffffff, 0xa73ffff, 0xffffffff, 0xa74ffff, 0xa75ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a76, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa780a77, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa7a0a79, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa7c0a7b, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1a7dffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a80, 0xffff0a81, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa82ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a83, 0xa84ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a85, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a86, 0xa87ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1288ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1a8affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa90128e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a91, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa92ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff1a93, 0xffffffff, 0xffff0a96, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa991297, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1a9affff, 0xffffffff, 0xffff0a9d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa9effff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa9fffff, 0xaa0ffff, 0xaa1ffff, 0xaa2ffff, 0xaa3ffff, 0xffffffff, 0xaa4ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0aa5, 0xffff0aa6, 0xaa80aa7, 0xffffffff, 0xffff0aa9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xaab0aaa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xaad0aac, 0xffffffff, 0xffffffff, 0xffffffff, 0xaaf0aae, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x12b212b0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xab50ab4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xab70ab6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x22bc22b8, 0xac10ac0, 0xac30ac2, 0xac50ac4, 0x22ca22c6, 0xacf0ace, 0xad10ad0, 0xad30ad2, 0x12d612d4, 0xffffffff, 0xffffffff, 0xffffffff, 0x12da12d8, 0xffffffff, 0xffffffff, 0xffffffff, 0x22e022dc, 0xae50ae4, 0xae70ae6, 0xae90ae8, 0x22ee22ea, 0xaf30af2, 0xaf50af4, 0xaf70af6, 0x1afb1af8, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b011afe, 0xffffffff, 0xffffffff, 0xffffffff, 0x13061304, 0xffffffff, 0xffffffff, 0xffffffff, 0x130a1308, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b0f1b0c, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b12ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x23192315, 0xb1e0b1d, 0xb200b1f, 0xb220b21, 0x23272323, 0xb2c0b2b, 0xb2e0b2d, 0xb300b2f, 0xffff0b31, 0xffffffff, 0xffff0b32, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b33, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b34, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b35ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b38, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b39, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff1b3a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b3d, 0xffff0b3e, 0xffff0b3f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b40, 0xffff0b41, 0xffff0b42, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb43ffff, 0xffffffff, 0xffffffff, 0xffff0b44, 0xb45ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb46ffff, 0xb47ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b48, 0xffffffff, 0xffffffff, 0xb49ffff, 0xb4affff, 0xffffffff, 0xffff0b4b, 0xffffffff, 0xb4cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb4dffff, 0xffffffff, 0xb4f0b4e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb510b50, 0xffffffff, 0xb530b52, 0xffffffff, 0xb550b54, 0xb570b56, 0xffffffff, 0xffffffff, 0xb590b58, 0xffffffff, 0xb5b0b5a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb5cffff, 0xffff0b5d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b5e, 0xffffffff, 0xffffffff, 0xb600b5f, 0xb61ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb630b62, 0xb650b64, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b66, 0xffffffff, 0xb67ffff, 0xb68ffff, 0xb69ffff, 0xb6affff, 0xb6bffff, 0xb6cffff, 0xb6dffff, 0xb6effff, 0xb6fffff, 0xb70ffff, 0xb71ffff, 0xb72ffff, 0xffffffff, 0xffff0b73, 0xffff0b74, 0xffff0b75, 0xffffffff, 0xffffffff, 0x1376ffff, 0xffffffff, 0xffff1378, 0x137affff, 0xffffffff, 0xffff137c, 0x137effff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb80ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b81, 0xffffffff, 0xb82ffff, 0xb83ffff, 0xb84ffff, 0xb85ffff, 0xb86ffff, 0xb87ffff, 0xb88ffff, 0xb89ffff, 0xb8affff, 0xb8bffff, 0xb8cffff, 0xb8dffff, 0xffffffff, 0xffff0b8e, 0xffff0b8f, 0xffff0b90, 0xffffffff, 0xffffffff, 0x1391ffff, 0xffffffff, 0xffff1393, 0x1395ffff, 0xffffffff, 0xffff1397, 0x1399ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb9bffff, 0xb9d0b9c, 0xffff0b9e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb9fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xba0ffff, 0xba1ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xba2ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xba3ffff, 0xffff0ba4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); -@property immutable(CompEntry[]) compositionTable() +static if (size_t.sizeof == 4) { -alias CE = CompEntry; -static immutable CE[] t = [CE(0x00338, 0x0226e),CE(0x00338, 0x02260),CE(0x00338, 0x0226f),CE(0x00300, 0x000c0),CE(0x00301, 0x000c1),CE(0x00302, 0x000c2),CE(0x00303, 0x000c3),CE(0x00304, 0x00100),CE(0x00306, 0x00102),CE(0x00307, 0x00226),CE(0x00308, 0x000c4),CE(0x00309, 0x01ea2),CE(0x0030a, 0x000c5),CE(0x0030c, 0x001cd),CE(0x0030f, 0x00200),CE(0x00311, 0x00202),CE(0x00323, 0x01ea0),CE(0x00325, 0x01e00),CE(0x00328, 0x00104),CE(0x00307, 0x01e02),CE(0x00323, 0x01e04),CE(0x00331, 0x01e06),CE(0x00301, 0x00106),CE(0x00302, 0x00108),CE(0x00307, 0x0010a),CE(0x0030c, 0x0010c),CE(0x00327, 0x000c7),CE(0x00307, 0x01e0a),CE(0x0030c, 0x0010e),CE(0x00323, 0x01e0c),CE(0x00327, 0x01e10),CE(0x0032d, 0x01e12),CE(0x00331, 0x01e0e),CE(0x00300, 0x000c8),CE(0x00301, 0x000c9),CE(0x00302, 0x000ca),CE(0x00303, 0x01ebc),CE(0x00304, 0x00112),CE(0x00306, 0x00114),CE(0x00307, 0x00116),CE(0x00308, 0x000cb),CE(0x00309, 0x01eba),CE(0x0030c, 0x0011a),CE(0x0030f, 0x00204),CE(0x00311, 0x00206),CE(0x00323, 0x01eb8),CE(0x00327, 0x00228),CE(0x00328, 0x00118),CE(0x0032d, 0x01e18),CE(0x00330, 0x01e1a),CE(0x00307, 0x01e1e),CE(0x00301, 0x001f4),CE(0x00302, 0x0011c),CE(0x00304, 0x01e20),CE(0x00306, 0x0011e),CE(0x00307, 0x00120),CE(0x0030c, 0x001e6),CE(0x00327, 0x00122),CE(0x00302, 0x00124),CE(0x00307, 0x01e22),CE(0x00308, 0x01e26),CE(0x0030c, 0x0021e),CE(0x00323, 0x01e24),CE(0x00327, 0x01e28),CE(0x0032e, 0x01e2a),CE(0x00300, 0x000cc),CE(0x00301, 0x000cd),CE(0x00302, 0x000ce),CE(0x00303, 0x00128),CE(0x00304, 0x0012a),CE(0x00306, 0x0012c),CE(0x00307, 0x00130),CE(0x00308, 0x000cf),CE(0x00309, 0x01ec8),CE(0x0030c, 0x001cf),CE(0x0030f, 0x00208),CE(0x00311, 0x0020a),CE(0x00323, 0x01eca),CE(0x00328, 0x0012e),CE(0x00330, 0x01e2c),CE(0x00302, 0x00134),CE(0x00301, 0x01e30),CE(0x0030c, 0x001e8),CE(0x00323, 0x01e32),CE(0x00327, 0x00136),CE(0x00331, 0x01e34),CE(0x00301, 0x00139),CE(0x0030c, 0x0013d),CE(0x00323, 0x01e36),CE(0x00327, 0x0013b),CE(0x0032d, 0x01e3c),CE(0x00331, 0x01e3a),CE(0x00301, 0x01e3e),CE(0x00307, 0x01e40),CE(0x00323, 0x01e42),CE(0x00300, 0x001f8),CE(0x00301, 0x00143),CE(0x00303, 0x000d1),CE(0x00307, 0x01e44),CE(0x0030c, 0x00147),CE(0x00323, 0x01e46),CE(0x00327, 0x00145),CE(0x0032d, 0x01e4a),CE(0x00331, 0x01e48),CE(0x00300, 0x000d2),CE(0x00301, 0x000d3),CE(0x00302, 0x000d4),CE(0x00303, 0x000d5),CE(0x00304, 0x0014c),CE(0x00306, 0x0014e),CE(0x00307, 0x0022e),CE(0x00308, 0x000d6),CE(0x00309, 0x01ece),CE(0x0030b, 0x00150),CE(0x0030c, 0x001d1),CE(0x0030f, 0x0020c),CE(0x00311, 0x0020e),CE(0x0031b, 0x001a0),CE(0x00323, 0x01ecc),CE(0x00328, 0x001ea),CE(0x00301, 0x01e54),CE(0x00307, 0x01e56),CE(0x00301, 0x00154),CE(0x00307, 0x01e58),CE(0x0030c, 0x00158),CE(0x0030f, 0x00210),CE(0x00311, 0x00212),CE(0x00323, 0x01e5a),CE(0x00327, 0x00156),CE(0x00331, 0x01e5e),CE(0x00301, 0x0015a),CE(0x00302, 0x0015c),CE(0x00307, 0x01e60),CE(0x0030c, 0x00160),CE(0x00323, 0x01e62),CE(0x00326, 0x00218),CE(0x00327, 0x0015e),CE(0x00307, 0x01e6a),CE(0x0030c, 0x00164),CE(0x00323, 0x01e6c),CE(0x00326, 0x0021a),CE(0x00327, 0x00162),CE(0x0032d, 0x01e70),CE(0x00331, 0x01e6e),CE(0x00300, 0x000d9),CE(0x00301, 0x000da),CE(0x00302, 0x000db),CE(0x00303, 0x00168),CE(0x00304, 0x0016a),CE(0x00306, 0x0016c),CE(0x00308, 0x000dc),CE(0x00309, 0x01ee6),CE(0x0030a, 0x0016e),CE(0x0030b, 0x00170),CE(0x0030c, 0x001d3),CE(0x0030f, 0x00214),CE(0x00311, 0x00216),CE(0x0031b, 0x001af),CE(0x00323, 0x01ee4),CE(0x00324, 0x01e72),CE(0x00328, 0x00172),CE(0x0032d, 0x01e76),CE(0x00330, 0x01e74),CE(0x00303, 0x01e7c),CE(0x00323, 0x01e7e),CE(0x00300, 0x01e80),CE(0x00301, 0x01e82),CE(0x00302, 0x00174),CE(0x00307, 0x01e86),CE(0x00308, 0x01e84),CE(0x00323, 0x01e88),CE(0x00307, 0x01e8a),CE(0x00308, 0x01e8c),CE(0x00300, 0x01ef2),CE(0x00301, 0x000dd),CE(0x00302, 0x00176),CE(0x00303, 0x01ef8),CE(0x00304, 0x00232),CE(0x00307, 0x01e8e),CE(0x00308, 0x00178),CE(0x00309, 0x01ef6),CE(0x00323, 0x01ef4),CE(0x00301, 0x00179),CE(0x00302, 0x01e90),CE(0x00307, 0x0017b),CE(0x0030c, 0x0017d),CE(0x00323, 0x01e92),CE(0x00331, 0x01e94),CE(0x00300, 0x000e0),CE(0x00301, 0x000e1),CE(0x00302, 0x000e2),CE(0x00303, 0x000e3),CE(0x00304, 0x00101),CE(0x00306, 0x00103),CE(0x00307, 0x00227),CE(0x00308, 0x000e4),CE(0x00309, 0x01ea3),CE(0x0030a, 0x000e5),CE(0x0030c, 0x001ce),CE(0x0030f, 0x00201),CE(0x00311, 0x00203),CE(0x00323, 0x01ea1),CE(0x00325, 0x01e01),CE(0x00328, 0x00105),CE(0x00307, 0x01e03),CE(0x00323, 0x01e05),CE(0x00331, 0x01e07),CE(0x00301, 0x00107),CE(0x00302, 0x00109),CE(0x00307, 0x0010b),CE(0x0030c, 0x0010d),CE(0x00327, 0x000e7),CE(0x00307, 0x01e0b),CE(0x0030c, 0x0010f),CE(0x00323, 0x01e0d),CE(0x00327, 0x01e11),CE(0x0032d, 0x01e13),CE(0x00331, 0x01e0f),CE(0x00300, 0x000e8),CE(0x00301, 0x000e9),CE(0x00302, 0x000ea),CE(0x00303, 0x01ebd),CE(0x00304, 0x00113),CE(0x00306, 0x00115),CE(0x00307, 0x00117),CE(0x00308, 0x000eb),CE(0x00309, 0x01ebb),CE(0x0030c, 0x0011b),CE(0x0030f, 0x00205),CE(0x00311, 0x00207),CE(0x00323, 0x01eb9),CE(0x00327, 0x00229),CE(0x00328, 0x00119),CE(0x0032d, 0x01e19),CE(0x00330, 0x01e1b),CE(0x00307, 0x01e1f),CE(0x00301, 0x001f5),CE(0x00302, 0x0011d),CE(0x00304, 0x01e21),CE(0x00306, 0x0011f),CE(0x00307, 0x00121),CE(0x0030c, 0x001e7),CE(0x00327, 0x00123),CE(0x00302, 0x00125),CE(0x00307, 0x01e23),CE(0x00308, 0x01e27),CE(0x0030c, 0x0021f),CE(0x00323, 0x01e25),CE(0x00327, 0x01e29),CE(0x0032e, 0x01e2b),CE(0x00331, 0x01e96),CE(0x00300, 0x000ec),CE(0x00301, 0x000ed),CE(0x00302, 0x000ee),CE(0x00303, 0x00129),CE(0x00304, 0x0012b),CE(0x00306, 0x0012d),CE(0x00308, 0x000ef),CE(0x00309, 0x01ec9),CE(0x0030c, 0x001d0),CE(0x0030f, 0x00209),CE(0x00311, 0x0020b),CE(0x00323, 0x01ecb),CE(0x00328, 0x0012f),CE(0x00330, 0x01e2d),CE(0x00302, 0x00135),CE(0x0030c, 0x001f0),CE(0x00301, 0x01e31),CE(0x0030c, 0x001e9),CE(0x00323, 0x01e33),CE(0x00327, 0x00137),CE(0x00331, 0x01e35),CE(0x00301, 0x0013a),CE(0x0030c, 0x0013e),CE(0x00323, 0x01e37),CE(0x00327, 0x0013c),CE(0x0032d, 0x01e3d),CE(0x00331, 0x01e3b),CE(0x00301, 0x01e3f),CE(0x00307, 0x01e41),CE(0x00323, 0x01e43),CE(0x00300, 0x001f9),CE(0x00301, 0x00144),CE(0x00303, 0x000f1),CE(0x00307, 0x01e45),CE(0x0030c, 0x00148),CE(0x00323, 0x01e47),CE(0x00327, 0x00146),CE(0x0032d, 0x01e4b),CE(0x00331, 0x01e49),CE(0x00300, 0x000f2),CE(0x00301, 0x000f3),CE(0x00302, 0x000f4),CE(0x00303, 0x000f5),CE(0x00304, 0x0014d),CE(0x00306, 0x0014f),CE(0x00307, 0x0022f),CE(0x00308, 0x000f6),CE(0x00309, 0x01ecf),CE(0x0030b, 0x00151),CE(0x0030c, 0x001d2),CE(0x0030f, 0x0020d),CE(0x00311, 0x0020f),CE(0x0031b, 0x001a1),CE(0x00323, 0x01ecd),CE(0x00328, 0x001eb),CE(0x00301, 0x01e55),CE(0x00307, 0x01e57),CE(0x00301, 0x00155),CE(0x00307, 0x01e59),CE(0x0030c, 0x00159),CE(0x0030f, 0x00211),CE(0x00311, 0x00213),CE(0x00323, 0x01e5b),CE(0x00327, 0x00157),CE(0x00331, 0x01e5f),CE(0x00301, 0x0015b),CE(0x00302, 0x0015d),CE(0x00307, 0x01e61),CE(0x0030c, 0x00161),CE(0x00323, 0x01e63),CE(0x00326, 0x00219),CE(0x00327, 0x0015f),CE(0x00307, 0x01e6b),CE(0x00308, 0x01e97),CE(0x0030c, 0x00165),CE(0x00323, 0x01e6d),CE(0x00326, 0x0021b),CE(0x00327, 0x00163),CE(0x0032d, 0x01e71),CE(0x00331, 0x01e6f),CE(0x00300, 0x000f9),CE(0x00301, 0x000fa),CE(0x00302, 0x000fb),CE(0x00303, 0x00169),CE(0x00304, 0x0016b),CE(0x00306, 0x0016d),CE(0x00308, 0x000fc),CE(0x00309, 0x01ee7),CE(0x0030a, 0x0016f),CE(0x0030b, 0x00171),CE(0x0030c, 0x001d4),CE(0x0030f, 0x00215),CE(0x00311, 0x00217),CE(0x0031b, 0x001b0),CE(0x00323, 0x01ee5),CE(0x00324, 0x01e73),CE(0x00328, 0x00173),CE(0x0032d, 0x01e77),CE(0x00330, 0x01e75),CE(0x00303, 0x01e7d),CE(0x00323, 0x01e7f),CE(0x00300, 0x01e81),CE(0x00301, 0x01e83),CE(0x00302, 0x00175),CE(0x00307, 0x01e87),CE(0x00308, 0x01e85),CE(0x0030a, 0x01e98),CE(0x00323, 0x01e89),CE(0x00307, 0x01e8b),CE(0x00308, 0x01e8d),CE(0x00300, 0x01ef3),CE(0x00301, 0x000fd),CE(0x00302, 0x00177),CE(0x00303, 0x01ef9),CE(0x00304, 0x00233),CE(0x00307, 0x01e8f),CE(0x00308, 0x000ff),CE(0x00309, 0x01ef7),CE(0x0030a, 0x01e99),CE(0x00323, 0x01ef5),CE(0x00301, 0x0017a),CE(0x00302, 0x01e91),CE(0x00307, 0x0017c),CE(0x0030c, 0x0017e),CE(0x00323, 0x01e93),CE(0x00331, 0x01e95),CE(0x00300, 0x01fed),CE(0x00301, 0x00385),CE(0x00342, 0x01fc1),CE(0x00300, 0x01ea6),CE(0x00301, 0x01ea4),CE(0x00303, 0x01eaa),CE(0x00309, 0x01ea8),CE(0x00304, 0x001de),CE(0x00301, 0x001fa),CE(0x00301, 0x001fc),CE(0x00304, 0x001e2),CE(0x00301, 0x01e08),CE(0x00300, 0x01ec0),CE(0x00301, 0x01ebe),CE(0x00303, 0x01ec4),CE(0x00309, 0x01ec2),CE(0x00301, 0x01e2e),CE(0x00300, 0x01ed2),CE(0x00301, 0x01ed0),CE(0x00303, 0x01ed6),CE(0x00309, 0x01ed4),CE(0x00301, 0x01e4c),CE(0x00304, 0x0022c),CE(0x00308, 0x01e4e),CE(0x00304, 0x0022a),CE(0x00301, 0x001fe),CE(0x00300, 0x001db),CE(0x00301, 0x001d7),CE(0x00304, 0x001d5),CE(0x0030c, 0x001d9),CE(0x00300, 0x01ea7),CE(0x00301, 0x01ea5),CE(0x00303, 0x01eab),CE(0x00309, 0x01ea9),CE(0x00304, 0x001df),CE(0x00301, 0x001fb),CE(0x00301, 0x001fd),CE(0x00304, 0x001e3),CE(0x00301, 0x01e09),CE(0x00300, 0x01ec1),CE(0x00301, 0x01ebf),CE(0x00303, 0x01ec5),CE(0x00309, 0x01ec3),CE(0x00301, 0x01e2f),CE(0x00300, 0x01ed3),CE(0x00301, 0x01ed1),CE(0x00303, 0x01ed7),CE(0x00309, 0x01ed5),CE(0x00301, 0x01e4d),CE(0x00304, 0x0022d),CE(0x00308, 0x01e4f),CE(0x00304, 0x0022b),CE(0x00301, 0x001ff),CE(0x00300, 0x001dc),CE(0x00301, 0x001d8),CE(0x00304, 0x001d6),CE(0x0030c, 0x001da),CE(0x00300, 0x01eb0),CE(0x00301, 0x01eae),CE(0x00303, 0x01eb4),CE(0x00309, 0x01eb2),CE(0x00300, 0x01eb1),CE(0x00301, 0x01eaf),CE(0x00303, 0x01eb5),CE(0x00309, 0x01eb3),CE(0x00300, 0x01e14),CE(0x00301, 0x01e16),CE(0x00300, 0x01e15),CE(0x00301, 0x01e17),CE(0x00300, 0x01e50),CE(0x00301, 0x01e52),CE(0x00300, 0x01e51),CE(0x00301, 0x01e53),CE(0x00307, 0x01e64),CE(0x00307, 0x01e65),CE(0x00307, 0x01e66),CE(0x00307, 0x01e67),CE(0x00301, 0x01e78),CE(0x00301, 0x01e79),CE(0x00308, 0x01e7a),CE(0x00308, 0x01e7b),CE(0x00307, 0x01e9b),CE(0x00300, 0x01edc),CE(0x00301, 0x01eda),CE(0x00303, 0x01ee0),CE(0x00309, 0x01ede),CE(0x00323, 0x01ee2),CE(0x00300, 0x01edd),CE(0x00301, 0x01edb),CE(0x00303, 0x01ee1),CE(0x00309, 0x01edf),CE(0x00323, 0x01ee3),CE(0x00300, 0x01eea),CE(0x00301, 0x01ee8),CE(0x00303, 0x01eee),CE(0x00309, 0x01eec),CE(0x00323, 0x01ef0),CE(0x00300, 0x01eeb),CE(0x00301, 0x01ee9),CE(0x00303, 0x01eef),CE(0x00309, 0x01eed),CE(0x00323, 0x01ef1),CE(0x0030c, 0x001ee),CE(0x00304, 0x001ec),CE(0x00304, 0x001ed),CE(0x00304, 0x001e0),CE(0x00304, 0x001e1),CE(0x00306, 0x01e1c),CE(0x00306, 0x01e1d),CE(0x00304, 0x00230),CE(0x00304, 0x00231),CE(0x0030c, 0x001ef),CE(0x00300, 0x01fba),CE(0x00301, 0x00386),CE(0x00304, 0x01fb9),CE(0x00306, 0x01fb8),CE(0x00313, 0x01f08),CE(0x00314, 0x01f09),CE(0x00345, 0x01fbc),CE(0x00300, 0x01fc8),CE(0x00301, 0x00388),CE(0x00313, 0x01f18),CE(0x00314, 0x01f19),CE(0x00300, 0x01fca),CE(0x00301, 0x00389),CE(0x00313, 0x01f28),CE(0x00314, 0x01f29),CE(0x00345, 0x01fcc),CE(0x00300, 0x01fda),CE(0x00301, 0x0038a),CE(0x00304, 0x01fd9),CE(0x00306, 0x01fd8),CE(0x00308, 0x003aa),CE(0x00313, 0x01f38),CE(0x00314, 0x01f39),CE(0x00300, 0x01ff8),CE(0x00301, 0x0038c),CE(0x00313, 0x01f48),CE(0x00314, 0x01f49),CE(0x00314, 0x01fec),CE(0x00300, 0x01fea),CE(0x00301, 0x0038e),CE(0x00304, 0x01fe9),CE(0x00306, 0x01fe8),CE(0x00308, 0x003ab),CE(0x00314, 0x01f59),CE(0x00300, 0x01ffa),CE(0x00301, 0x0038f),CE(0x00313, 0x01f68),CE(0x00314, 0x01f69),CE(0x00345, 0x01ffc),CE(0x00345, 0x01fb4),CE(0x00345, 0x01fc4),CE(0x00300, 0x01f70),CE(0x00301, 0x003ac),CE(0x00304, 0x01fb1),CE(0x00306, 0x01fb0),CE(0x00313, 0x01f00),CE(0x00314, 0x01f01),CE(0x00342, 0x01fb6),CE(0x00345, 0x01fb3),CE(0x00300, 0x01f72),CE(0x00301, 0x003ad),CE(0x00313, 0x01f10),CE(0x00314, 0x01f11),CE(0x00300, 0x01f74),CE(0x00301, 0x003ae),CE(0x00313, 0x01f20),CE(0x00314, 0x01f21),CE(0x00342, 0x01fc6),CE(0x00345, 0x01fc3),CE(0x00300, 0x01f76),CE(0x00301, 0x003af),CE(0x00304, 0x01fd1),CE(0x00306, 0x01fd0),CE(0x00308, 0x003ca),CE(0x00313, 0x01f30),CE(0x00314, 0x01f31),CE(0x00342, 0x01fd6),CE(0x00300, 0x01f78),CE(0x00301, 0x003cc),CE(0x00313, 0x01f40),CE(0x00314, 0x01f41),CE(0x00313, 0x01fe4),CE(0x00314, 0x01fe5),CE(0x00300, 0x01f7a),CE(0x00301, 0x003cd),CE(0x00304, 0x01fe1),CE(0x00306, 0x01fe0),CE(0x00308, 0x003cb),CE(0x00313, 0x01f50),CE(0x00314, 0x01f51),CE(0x00342, 0x01fe6),CE(0x00300, 0x01f7c),CE(0x00301, 0x003ce),CE(0x00313, 0x01f60),CE(0x00314, 0x01f61),CE(0x00342, 0x01ff6),CE(0x00345, 0x01ff3),CE(0x00300, 0x01fd2),CE(0x00301, 0x00390),CE(0x00342, 0x01fd7),CE(0x00300, 0x01fe2),CE(0x00301, 0x003b0),CE(0x00342, 0x01fe7),CE(0x00345, 0x01ff4),CE(0x00301, 0x003d3),CE(0x00308, 0x003d4),CE(0x00308, 0x00407),CE(0x00306, 0x004d0),CE(0x00308, 0x004d2),CE(0x00301, 0x00403),CE(0x00300, 0x00400),CE(0x00306, 0x004d6),CE(0x00308, 0x00401),CE(0x00306, 0x004c1),CE(0x00308, 0x004dc),CE(0x00308, 0x004de),CE(0x00300, 0x0040d),CE(0x00304, 0x004e2),CE(0x00306, 0x00419),CE(0x00308, 0x004e4),CE(0x00301, 0x0040c),CE(0x00308, 0x004e6),CE(0x00304, 0x004ee),CE(0x00306, 0x0040e),CE(0x00308, 0x004f0),CE(0x0030b, 0x004f2),CE(0x00308, 0x004f4),CE(0x00308, 0x004f8),CE(0x00308, 0x004ec),CE(0x00306, 0x004d1),CE(0x00308, 0x004d3),CE(0x00301, 0x00453),CE(0x00300, 0x00450),CE(0x00306, 0x004d7),CE(0x00308, 0x00451),CE(0x00306, 0x004c2),CE(0x00308, 0x004dd),CE(0x00308, 0x004df),CE(0x00300, 0x0045d),CE(0x00304, 0x004e3),CE(0x00306, 0x00439),CE(0x00308, 0x004e5),CE(0x00301, 0x0045c),CE(0x00308, 0x004e7),CE(0x00304, 0x004ef),CE(0x00306, 0x0045e),CE(0x00308, 0x004f1),CE(0x0030b, 0x004f3),CE(0x00308, 0x004f5),CE(0x00308, 0x004f9),CE(0x00308, 0x004ed),CE(0x00308, 0x00457),CE(0x0030f, 0x00476),CE(0x0030f, 0x00477),CE(0x00308, 0x004da),CE(0x00308, 0x004db),CE(0x00308, 0x004ea),CE(0x00308, 0x004eb),CE(0x00653, 0x00622),CE(0x00654, 0x00623),CE(0x00655, 0x00625),CE(0x00654, 0x00624),CE(0x00654, 0x00626),CE(0x00654, 0x006c2),CE(0x00654, 0x006d3),CE(0x00654, 0x006c0),CE(0x0093c, 0x00929),CE(0x0093c, 0x00931),CE(0x0093c, 0x00934),CE(0x009be, 0x009cb),CE(0x009d7, 0x009cc),CE(0x00b3e, 0x00b4b),CE(0x00b56, 0x00b48),CE(0x00b57, 0x00b4c),CE(0x00bd7, 0x00b94),CE(0x00bbe, 0x00bca),CE(0x00bd7, 0x00bcc),CE(0x00bbe, 0x00bcb),CE(0x00c56, 0x00c48),CE(0x00cd5, 0x00cc0),CE(0x00cc2, 0x00cca),CE(0x00cd5, 0x00cc7),CE(0x00cd6, 0x00cc8),CE(0x00cd5, 0x00ccb),CE(0x00d3e, 0x00d4a),CE(0x00d57, 0x00d4c),CE(0x00d3e, 0x00d4b),CE(0x00dca, 0x00dda),CE(0x00dcf, 0x00ddc),CE(0x00ddf, 0x00dde),CE(0x00dca, 0x00ddd),CE(0x0102e, 0x01026),CE(0x01b35, 0x01b06),CE(0x01b35, 0x01b08),CE(0x01b35, 0x01b0a),CE(0x01b35, 0x01b0c),CE(0x01b35, 0x01b0e),CE(0x01b35, 0x01b12),CE(0x01b35, 0x01b3b),CE(0x01b35, 0x01b3d),CE(0x01b35, 0x01b40),CE(0x01b35, 0x01b41),CE(0x01b35, 0x01b43),CE(0x00304, 0x01e38),CE(0x00304, 0x01e39),CE(0x00304, 0x01e5c),CE(0x00304, 0x01e5d),CE(0x00307, 0x01e68),CE(0x00307, 0x01e69),CE(0x00302, 0x01eac),CE(0x00306, 0x01eb6),CE(0x00302, 0x01ead),CE(0x00306, 0x01eb7),CE(0x00302, 0x01ec6),CE(0x00302, 0x01ec7),CE(0x00302, 0x01ed8),CE(0x00302, 0x01ed9),CE(0x00300, 0x01f02),CE(0x00301, 0x01f04),CE(0x00342, 0x01f06),CE(0x00345, 0x01f80),CE(0x00300, 0x01f03),CE(0x00301, 0x01f05),CE(0x00342, 0x01f07),CE(0x00345, 0x01f81),CE(0x00345, 0x01f82),CE(0x00345, 0x01f83),CE(0x00345, 0x01f84),CE(0x00345, 0x01f85),CE(0x00345, 0x01f86),CE(0x00345, 0x01f87),CE(0x00300, 0x01f0a),CE(0x00301, 0x01f0c),CE(0x00342, 0x01f0e),CE(0x00345, 0x01f88),CE(0x00300, 0x01f0b),CE(0x00301, 0x01f0d),CE(0x00342, 0x01f0f),CE(0x00345, 0x01f89),CE(0x00345, 0x01f8a),CE(0x00345, 0x01f8b),CE(0x00345, 0x01f8c),CE(0x00345, 0x01f8d),CE(0x00345, 0x01f8e),CE(0x00345, 0x01f8f),CE(0x00300, 0x01f12),CE(0x00301, 0x01f14),CE(0x00300, 0x01f13),CE(0x00301, 0x01f15),CE(0x00300, 0x01f1a),CE(0x00301, 0x01f1c),CE(0x00300, 0x01f1b),CE(0x00301, 0x01f1d),CE(0x00300, 0x01f22),CE(0x00301, 0x01f24),CE(0x00342, 0x01f26),CE(0x00345, 0x01f90),CE(0x00300, 0x01f23),CE(0x00301, 0x01f25),CE(0x00342, 0x01f27),CE(0x00345, 0x01f91),CE(0x00345, 0x01f92),CE(0x00345, 0x01f93),CE(0x00345, 0x01f94),CE(0x00345, 0x01f95),CE(0x00345, 0x01f96),CE(0x00345, 0x01f97),CE(0x00300, 0x01f2a),CE(0x00301, 0x01f2c),CE(0x00342, 0x01f2e),CE(0x00345, 0x01f98),CE(0x00300, 0x01f2b),CE(0x00301, 0x01f2d),CE(0x00342, 0x01f2f),CE(0x00345, 0x01f99),CE(0x00345, 0x01f9a),CE(0x00345, 0x01f9b),CE(0x00345, 0x01f9c),CE(0x00345, 0x01f9d),CE(0x00345, 0x01f9e),CE(0x00345, 0x01f9f),CE(0x00300, 0x01f32),CE(0x00301, 0x01f34),CE(0x00342, 0x01f36),CE(0x00300, 0x01f33),CE(0x00301, 0x01f35),CE(0x00342, 0x01f37),CE(0x00300, 0x01f3a),CE(0x00301, 0x01f3c),CE(0x00342, 0x01f3e),CE(0x00300, 0x01f3b),CE(0x00301, 0x01f3d),CE(0x00342, 0x01f3f),CE(0x00300, 0x01f42),CE(0x00301, 0x01f44),CE(0x00300, 0x01f43),CE(0x00301, 0x01f45),CE(0x00300, 0x01f4a),CE(0x00301, 0x01f4c),CE(0x00300, 0x01f4b),CE(0x00301, 0x01f4d),CE(0x00300, 0x01f52),CE(0x00301, 0x01f54),CE(0x00342, 0x01f56),CE(0x00300, 0x01f53),CE(0x00301, 0x01f55),CE(0x00342, 0x01f57),CE(0x00300, 0x01f5b),CE(0x00301, 0x01f5d),CE(0x00342, 0x01f5f),CE(0x00300, 0x01f62),CE(0x00301, 0x01f64),CE(0x00342, 0x01f66),CE(0x00345, 0x01fa0),CE(0x00300, 0x01f63),CE(0x00301, 0x01f65),CE(0x00342, 0x01f67),CE(0x00345, 0x01fa1),CE(0x00345, 0x01fa2),CE(0x00345, 0x01fa3),CE(0x00345, 0x01fa4),CE(0x00345, 0x01fa5),CE(0x00345, 0x01fa6),CE(0x00345, 0x01fa7),CE(0x00300, 0x01f6a),CE(0x00301, 0x01f6c),CE(0x00342, 0x01f6e),CE(0x00345, 0x01fa8),CE(0x00300, 0x01f6b),CE(0x00301, 0x01f6d),CE(0x00342, 0x01f6f),CE(0x00345, 0x01fa9),CE(0x00345, 0x01faa),CE(0x00345, 0x01fab),CE(0x00345, 0x01fac),CE(0x00345, 0x01fad),CE(0x00345, 0x01fae),CE(0x00345, 0x01faf),CE(0x00345, 0x01fb2),CE(0x00345, 0x01fc2),CE(0x00345, 0x01ff2),CE(0x00345, 0x01fb7),CE(0x00300, 0x01fcd),CE(0x00301, 0x01fce),CE(0x00342, 0x01fcf),CE(0x00345, 0x01fc7),CE(0x00345, 0x01ff7),CE(0x00300, 0x01fdd),CE(0x00301, 0x01fde),CE(0x00342, 0x01fdf),CE(0x00338, 0x0219a),CE(0x00338, 0x0219b),CE(0x00338, 0x021ae),CE(0x00338, 0x021cd),CE(0x00338, 0x021cf),CE(0x00338, 0x021ce),CE(0x00338, 0x02204),CE(0x00338, 0x02209),CE(0x00338, 0x0220c),CE(0x00338, 0x02224),CE(0x00338, 0x02226),CE(0x00338, 0x02241),CE(0x00338, 0x02244),CE(0x00338, 0x02247),CE(0x00338, 0x02249),CE(0x00338, 0x0226d),CE(0x00338, 0x02262),CE(0x00338, 0x02270),CE(0x00338, 0x02271),CE(0x00338, 0x02274),CE(0x00338, 0x02275),CE(0x00338, 0x02278),CE(0x00338, 0x02279),CE(0x00338, 0x02280),CE(0x00338, 0x02281),CE(0x00338, 0x022e0),CE(0x00338, 0x022e1),CE(0x00338, 0x02284),CE(0x00338, 0x02285),CE(0x00338, 0x02288),CE(0x00338, 0x02289),CE(0x00338, 0x022e2),CE(0x00338, 0x022e3),CE(0x00338, 0x022ac),CE(0x00338, 0x022ad),CE(0x00338, 0x022ae),CE(0x00338, 0x022af),CE(0x00338, 0x022ea),CE(0x00338, 0x022eb),CE(0x00338, 0x022ec),CE(0x00338, 0x022ed),CE(0x03099, 0x03094),CE(0x03099, 0x0304c),CE(0x03099, 0x0304e),CE(0x03099, 0x03050),CE(0x03099, 0x03052),CE(0x03099, 0x03054),CE(0x03099, 0x03056),CE(0x03099, 0x03058),CE(0x03099, 0x0305a),CE(0x03099, 0x0305c),CE(0x03099, 0x0305e),CE(0x03099, 0x03060),CE(0x03099, 0x03062),CE(0x03099, 0x03065),CE(0x03099, 0x03067),CE(0x03099, 0x03069),CE(0x03099, 0x03070),CE(0x0309a, 0x03071),CE(0x03099, 0x03073),CE(0x0309a, 0x03074),CE(0x03099, 0x03076),CE(0x0309a, 0x03077),CE(0x03099, 0x03079),CE(0x0309a, 0x0307a),CE(0x03099, 0x0307c),CE(0x0309a, 0x0307d),CE(0x03099, 0x0309e),CE(0x03099, 0x030f4),CE(0x03099, 0x030ac),CE(0x03099, 0x030ae),CE(0x03099, 0x030b0),CE(0x03099, 0x030b2),CE(0x03099, 0x030b4),CE(0x03099, 0x030b6),CE(0x03099, 0x030b8),CE(0x03099, 0x030ba),CE(0x03099, 0x030bc),CE(0x03099, 0x030be),CE(0x03099, 0x030c0),CE(0x03099, 0x030c2),CE(0x03099, 0x030c5),CE(0x03099, 0x030c7),CE(0x03099, 0x030c9),CE(0x03099, 0x030d0),CE(0x0309a, 0x030d1),CE(0x03099, 0x030d3),CE(0x0309a, 0x030d4),CE(0x03099, 0x030d6),CE(0x0309a, 0x030d7),CE(0x03099, 0x030d9),CE(0x0309a, 0x030da),CE(0x03099, 0x030dc),CE(0x0309a, 0x030dd),CE(0x03099, 0x030f7),CE(0x03099, 0x030f8),CE(0x03099, 0x030f9),CE(0x03099, 0x030fa),CE(0x03099, 0x030fe),CE(0x110ba, 0x1109a),CE(0x110ba, 0x1109c),CE(0x110ba, 0x110ab),CE(0x11127, 0x1112e),CE(0x11127, 0x1112f),]; -return t; -} + //6976 bytes + enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 7, 6)([0x0, 0x40, + 0x240], [0x100, 0x400, 0x1240], [0x2020100, 0x4020302, 0x2020205, + 0x2060202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20001, 0x0, 0x0, 0x3, + 0x0, 0x50004, 0x70006, 0x80000, 0xa0009, 0xb0000, 0xd000c, 0xe0000, + 0x10000f, 0x11000f, 0x11000f, 0x11000f, 0x11000f, 0x110000, + 0x120000, 0x11000f, 0x110000, 0x130000, 0x150014, 0x170016, + 0x190018, 0x1b001a, 0x1c, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x1e0000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x200000, 0x0, 0x21, 0x22, 0x0, + 0x240023, 0x0, 0x260025, 0x280027, 0x29, 0x2a0000, 0x0, 0x2b0000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x2d0000, 0x2e0000, 0x2f0000, 0x0, 0x0, 0x0, + 0x0, 0x30, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x320000, 0x340033, 0x0, 0x0, 0x35, + 0x360000, 0x380037, 0x3a0039, 0x0, 0x3c003b, 0x0, 0x3d0000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x350000, 0x42, 0x43, 0x3a0000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x450000, 0x46, + 0x470000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, + 0xe6e6e6e6, 0xe6e6e6e6, 0xdcdce8e6, 0xd8e8dcdc, 0xdcdcdcdc, + 0xdccacadc, 0xcadcdcdc, 0xdcdcdcca, 0xdcdcdcdc, 0xdcdcdcdc, + 0x1010101, 0xdcdcdc01, 0xe6e6e6dc, 0xe6e6e6e6, 0xdce6f0e6, + 0xe6e6dcdc, 0xdcdce6, 0xdce6e6e6, 0xe6dcdcdc, 0xe6dcdce8, + 0xe9eaeae9, 0xe6e9eaea, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0x0, + 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe6e6dc00, 0xe6dce6e6, 0xdcdee6e6, 0xe6e6e6e6, 0xdcdce6e6, + 0xdcdcdcdc, 0xe6dce6e6, 0xe6e4dee6, 0xd0c0b0a, 0x11100f0e, + 0x14131312, 0x17001615, 0x191800, 0x1200dce6, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0x201f1e, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b000000, 0x1f1e1d1c, 0xe6222120, + 0xe6dcdce6, 0xe6e6e6e6, 0xdce6e6dc, 0x0, 0x0, 0x0, 0x0, 0x23, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e60000, 0xe6e6e6e6, + 0xe60000e6, 0xdce6e6e6, 0xe60000e6, 0xe6dc00e6, 0xdce6, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xe6e6dce6, 0xdce6e6dc, 0xdce6dcdc, 0xe6dce6dc, 0xe6dce6e6, + 0xe6dce6dc, 0xe6e6dc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xe6000000, 0xe6e6e6e6, 0xe6dce6e6, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xe6e60000, 0xe600e6e6, 0xe6e6e6e6, 0xe6e6e6e6, + 0xe6e6e600, 0xe6e6e600, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xdcdcdc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6dce6e6, + 0xe6e6dce6, 0xdcdcdce6, 0xe61d1c1b, 0xe6dce6e6, 0xe6dcdce6, + 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x900, 0xe6dce600, 0xe6, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x900, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x900, 0x0, 0x5b5400, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96767, 0x0, + 0x0, 0x0, 0x6b6b6b6b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x7676, 0x0, 0x0, 0x0, 0x7a7a7a7a, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xdcdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xdc00dc00, 0xd800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x828100, 0x84, 0x82820000, 0x8282, 0xe6e60082, + 0xe6e60009, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xdc0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7000000, 0x90900, 0x0, 0x0, + 0x0, 0x0, 0xdc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e600, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x90000, 0x0, 0x0, 0xe600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe400, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdce6de00, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xe6000000, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, + 0xe6e6e600, 0xe6e6e6e6, 0xdc0000e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x9, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6e6dc, + 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x9090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x70000, 0x0, 0x0, 0x9090000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x7000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6, 0xdcdcdc01, + 0xe6e6dcdc, 0xdcdcdcdc, 0x10100e6, 0x1010101, 0x1, 0xdc00, 0x0, + 0xe6, 0x0, 0x0, 0xe6dce6e6, 0xe6e6e6e6, 0xe6dce6e6, 0xdcd6eae6, + 0xe6e6e6ca, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, + 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdce6dce9, 0x0, 0x0, 0x0, 0x0, + 0x101e6e6, 0xe6e6e6e6, 0xe6010101, 0xe6, 0xe600, 0xe6010100, + 0x101e6dc, 0xdcdcdcdc, 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0xe6e6, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, + 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe4da0000, 0xe0e0dee8, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80800, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xe6000000, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e6e6e6, 0xe6e6e6e6, 0xe6e6e6e6, + 0xe6e6e6e6, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xdc000000, 0xdcdc, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7000000, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6e600e6, + 0xe60000dc, 0xe6, 0xe6e60000, 0xe600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x900, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a0000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe6e6e6e6, 0xe6e6e6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc00, + 0x0, 0x0, 0x0, 0xe600dc00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xdc01e6, 0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70900, 0x0, 0xe6e6e6, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9000000, 0x9, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7090000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1d8d800, 0x101, 0xd8d8e200, 0xd8d8d8, 0x0, 0xdc000000, + 0xdcdcdcdc, 0xdcdcdc, 0xe6e6e600, 0xdcdce6e6, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xe6e60000, 0xe6e6, 0x0, 0x0, 0x0, 0x0, 0xe6e60000, + 0xe6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0]); + enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11; + enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)([0x0, 0x800], + [0x1000, 0x2000], [0x10000, 0x30002, 0x50004, 0x70006, 0x70008, + 0x70007, 0x90007, 0xa0007, 0xc000b, 0x70007, 0x70007, 0x70007, + 0x7000d, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x7000e, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x8010800, + 0xffff0802, 0x8003ffff, 0x28161813, 0x8821301b, 0x38330832, + 0x7841383a, 0x28510850, 0x185c3056, 0x8068485f, 0xffff1078, + 0x3882407a, 0x98903889, 0x30a510a3, 0x48ad10ab, 0xffff30b6, + 0xffffffff, 0xffffffff, 0x80bcffff, 0x28cf18cc, 0x88da30d4, + 0x38ec08eb, 0x70fb40f3, 0x290b1109, 0x19163110, 0x81224919, + 0xffff1132, 0x393c4134, 0x994b4143, 0x3960115e, 0x51691167, + 0xffff3173, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff1979, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff217c, 0x9810980, 0x9841182, 0xffffffff, + 0xffff2185, 0xffffffff, 0x989ffff, 0xffffffff, 0xffffffff, + 0x198e218a, 0xffff0991, 0xffff0992, 0xffffffff, 0xffff2193, + 0xffffffff, 0xffffffff, 0xffff2197, 0x99c099b, 0x99f119d, + 0xffffffff, 0xffff21a0, 0xffffffff, 0x9a4ffff, 0xffffffff, + 0xffffffff, 0x19a921a5, 0xffff09ac, 0xffff09ad, 0xffffffff, + 0xffff21ae, 0xffffffff, 0xffffffff, 0x21b621b2, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x11bc11ba, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x11c011be, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x9c309c2, 0xffffffff, 0xffffffff, + 0x9c509c4, 0xffffffff, 0xffffffff, 0xffffffff, 0x9c709c6, + 0x9c909c8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x9caffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x29d029cb, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x29d5ffff, + 0xffff29da, 0xffffffff, 0xffffffff, 0x9dfffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9e109e0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9e309e2, + 0x9e509e4, 0xffffffff, 0xffffffff, 0x9e709e6, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff09e8, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x39e9ffff, 0xffffffff, 0x21f0ffff, 0x29f4ffff, 0x39f9ffff, + 0xffffffff, 0xffffffff, 0x2200ffff, 0xa04ffff, 0xffffffff, + 0x3205ffff, 0xffffffff, 0x2a0bffff, 0xffffffff, 0xffff0a10, + 0xffff0a11, 0x4212ffff, 0xffffffff, 0x221affff, 0x321effff, + 0x4224ffff, 0xffffffff, 0xffffffff, 0x222cffff, 0x1230ffff, + 0xffffffff, 0x4232ffff, 0xffffffff, 0x323affff, 0x1a431a40, + 0xffffffff, 0xffff0a46, 0xffffffff, 0xffff1247, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a49, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff124a, + 0xa4cffff, 0x1a4dffff, 0xa521250, 0xffff2253, 0xffff0a57, + 0xffffffff, 0xffff0a58, 0xffffffff, 0x2259ffff, 0xffffffff, + 0xa5dffff, 0xffffffff, 0xa5effff, 0xa5fffff, 0xffffffff, + 0xffff1260, 0xa62ffff, 0x1a63ffff, 0xa681266, 0xffff2269, + 0xffff0a6d, 0xffffffff, 0xffff0a6e, 0xffffffff, 0x226fffff, + 0xffffffff, 0xa73ffff, 0xffffffff, 0xa74ffff, 0xa75ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a76, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa780a77, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xa7a0a79, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xa7c0a7b, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x1a7dffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0a80, 0xffff0a81, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xa82ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff0a83, 0xa84ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0a85, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0a86, 0xa87ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x1288ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x1a8affff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0a8d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xa90128e, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0a91, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xa92ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff1a93, + 0xffffffff, 0xffff0a96, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xa991297, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x1a9affff, 0xffffffff, 0xffff0a9d, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xa9effff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xa9fffff, 0xaa0ffff, + 0xaa1ffff, 0xaa2ffff, 0xaa3ffff, 0xffffffff, 0xaa4ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0aa5, + 0xffff0aa6, 0xaa80aa7, 0xffffffff, 0xffff0aa9, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xaab0aaa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xaad0aac, 0xffffffff, + 0xffffffff, 0xffffffff, 0xaaf0aae, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x12b212b0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xab50ab4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xab70ab6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x22bc22b8, 0xac10ac0, 0xac30ac2, 0xac50ac4, + 0x22ca22c6, 0xacf0ace, 0xad10ad0, 0xad30ad2, 0x12d612d4, + 0xffffffff, 0xffffffff, 0xffffffff, 0x12da12d8, 0xffffffff, + 0xffffffff, 0xffffffff, 0x22e022dc, 0xae50ae4, 0xae70ae6, + 0xae90ae8, 0x22ee22ea, 0xaf30af2, 0xaf50af4, 0xaf70af6, 0x1afb1af8, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1b011afe, 0xffffffff, + 0xffffffff, 0xffffffff, 0x13061304, 0xffffffff, 0xffffffff, + 0xffffffff, 0x130a1308, 0xffffffff, 0xffffffff, 0xffffffff, + 0x1b0f1b0c, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b12ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x23192315, 0xb1e0b1d, + 0xb200b1f, 0xb220b21, 0x23272323, 0xb2c0b2b, 0xb2e0b2d, 0xb300b2f, + 0xffff0b31, 0xffffffff, 0xffff0b32, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0b33, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0b34, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b35ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b38, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff0b39, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff1b3a, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff0b3d, 0xffff0b3e, 0xffff0b3f, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b40, + 0xffff0b41, 0xffff0b42, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb43ffff, + 0xffffffff, 0xffffffff, 0xffff0b44, 0xb45ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xb46ffff, 0xb47ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b48, 0xffffffff, + 0xffffffff, 0xb49ffff, 0xb4affff, 0xffffffff, 0xffff0b4b, + 0xffffffff, 0xb4cffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xb4dffff, 0xffffffff, 0xb4f0b4e, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xb510b50, 0xffffffff, 0xb530b52, 0xffffffff, 0xb550b54, 0xb570b56, + 0xffffffff, 0xffffffff, 0xb590b58, 0xffffffff, 0xb5b0b5a, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb5cffff, + 0xffff0b5d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b5e, 0xffffffff, + 0xffffffff, 0xb600b5f, 0xb61ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xb630b62, 0xb650b64, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b66, 0xffffffff, + 0xb67ffff, 0xb68ffff, 0xb69ffff, 0xb6affff, 0xb6bffff, 0xb6cffff, + 0xb6dffff, 0xb6effff, 0xb6fffff, 0xb70ffff, 0xb71ffff, 0xb72ffff, + 0xffffffff, 0xffff0b73, 0xffff0b74, 0xffff0b75, 0xffffffff, + 0xffffffff, 0x1376ffff, 0xffffffff, 0xffff1378, 0x137affff, + 0xffffffff, 0xffff137c, 0x137effff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xb80ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0b81, + 0xffffffff, 0xb82ffff, 0xb83ffff, 0xb84ffff, 0xb85ffff, 0xb86ffff, + 0xb87ffff, 0xb88ffff, 0xb89ffff, 0xb8affff, 0xb8bffff, 0xb8cffff, + 0xb8dffff, 0xffffffff, 0xffff0b8e, 0xffff0b8f, 0xffff0b90, + 0xffffffff, 0xffffffff, 0x1391ffff, 0xffffffff, 0xffff1393, + 0x1395ffff, 0xffffffff, 0xffff1397, 0x1399ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xb9bffff, 0xb9d0b9c, + 0xffff0b9e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xb9fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xba0ffff, 0xba1ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xba2ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xba3ffff, 0xffff0ba4, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff]); + @property immutable(CompEntry[]) compositionTable() + { + alias CE = CompEntry; + static immutable CE[] t = [ + CE(0x00338, 0x0226e), CE(0x00338, 0x02260), CE(0x00338, 0x0226f), + CE(0x00300, 0x000c0), CE(0x00301, 0x000c1), CE(0x00302, 0x000c2), + CE(0x00303, 0x000c3), CE(0x00304, 0x00100), CE(0x00306, 0x00102), + CE(0x00307, 0x00226), CE(0x00308, 0x000c4), CE(0x00309, 0x01ea2), + CE(0x0030a, 0x000c5), CE(0x0030c, 0x001cd), CE(0x0030f, 0x00200), + CE(0x00311, 0x00202), CE(0x00323, 0x01ea0), CE(0x00325, 0x01e00), + CE(0x00328, 0x00104), CE(0x00307, 0x01e02), CE(0x00323, 0x01e04), + CE(0x00331, 0x01e06), CE(0x00301, 0x00106), CE(0x00302, 0x00108), + CE(0x00307, 0x0010a), CE(0x0030c, 0x0010c), CE(0x00327, 0x000c7), + CE(0x00307, 0x01e0a), CE(0x0030c, 0x0010e), CE(0x00323, 0x01e0c), + CE(0x00327, 0x01e10), CE(0x0032d, 0x01e12), CE(0x00331, 0x01e0e), + CE(0x00300, 0x000c8), CE(0x00301, 0x000c9), CE(0x00302, 0x000ca), + CE(0x00303, 0x01ebc), CE(0x00304, 0x00112), CE(0x00306, 0x00114), + CE(0x00307, 0x00116), CE(0x00308, 0x000cb), CE(0x00309, 0x01eba), + CE(0x0030c, 0x0011a), CE(0x0030f, 0x00204), CE(0x00311, 0x00206), + CE(0x00323, 0x01eb8), CE(0x00327, 0x00228), CE(0x00328, 0x00118), + CE(0x0032d, 0x01e18), CE(0x00330, 0x01e1a), CE(0x00307, 0x01e1e), + CE(0x00301, 0x001f4), CE(0x00302, 0x0011c), CE(0x00304, 0x01e20), + CE(0x00306, 0x0011e), CE(0x00307, 0x00120), CE(0x0030c, 0x001e6), + CE(0x00327, 0x00122), CE(0x00302, 0x00124), CE(0x00307, 0x01e22), + CE(0x00308, 0x01e26), CE(0x0030c, 0x0021e), CE(0x00323, 0x01e24), + CE(0x00327, 0x01e28), CE(0x0032e, 0x01e2a), CE(0x00300, 0x000cc), + CE(0x00301, 0x000cd), CE(0x00302, 0x000ce), CE(0x00303, 0x00128), + CE(0x00304, 0x0012a), CE(0x00306, 0x0012c), CE(0x00307, 0x00130), + CE(0x00308, 0x000cf), CE(0x00309, 0x01ec8), CE(0x0030c, 0x001cf), + CE(0x0030f, 0x00208), CE(0x00311, 0x0020a), CE(0x00323, 0x01eca), + CE(0x00328, 0x0012e), CE(0x00330, 0x01e2c), CE(0x00302, 0x00134), + CE(0x00301, 0x01e30), CE(0x0030c, 0x001e8), CE(0x00323, 0x01e32), + CE(0x00327, 0x00136), CE(0x00331, 0x01e34), CE(0x00301, 0x00139), + CE(0x0030c, 0x0013d), CE(0x00323, 0x01e36), CE(0x00327, 0x0013b), + CE(0x0032d, 0x01e3c), CE(0x00331, 0x01e3a), CE(0x00301, 0x01e3e), + CE(0x00307, 0x01e40), CE(0x00323, 0x01e42), CE(0x00300, 0x001f8), + CE(0x00301, 0x00143), CE(0x00303, 0x000d1), CE(0x00307, 0x01e44), + CE(0x0030c, 0x00147), CE(0x00323, 0x01e46), CE(0x00327, 0x00145), + CE(0x0032d, 0x01e4a), CE(0x00331, 0x01e48), CE(0x00300, 0x000d2), + CE(0x00301, 0x000d3), CE(0x00302, 0x000d4), CE(0x00303, 0x000d5), + CE(0x00304, 0x0014c), CE(0x00306, 0x0014e), CE(0x00307, 0x0022e), + CE(0x00308, 0x000d6), CE(0x00309, 0x01ece), CE(0x0030b, 0x00150), + CE(0x0030c, 0x001d1), CE(0x0030f, 0x0020c), CE(0x00311, 0x0020e), + CE(0x0031b, 0x001a0), CE(0x00323, 0x01ecc), CE(0x00328, 0x001ea), + CE(0x00301, 0x01e54), CE(0x00307, 0x01e56), CE(0x00301, 0x00154), + CE(0x00307, 0x01e58), CE(0x0030c, 0x00158), CE(0x0030f, 0x00210), + CE(0x00311, 0x00212), CE(0x00323, 0x01e5a), CE(0x00327, 0x00156), + CE(0x00331, 0x01e5e), CE(0x00301, 0x0015a), CE(0x00302, 0x0015c), + CE(0x00307, 0x01e60), CE(0x0030c, 0x00160), CE(0x00323, 0x01e62), + CE(0x00326, 0x00218), CE(0x00327, 0x0015e), CE(0x00307, 0x01e6a), + CE(0x0030c, 0x00164), CE(0x00323, 0x01e6c), CE(0x00326, 0x0021a), + CE(0x00327, 0x00162), CE(0x0032d, 0x01e70), CE(0x00331, 0x01e6e), + CE(0x00300, 0x000d9), CE(0x00301, 0x000da), CE(0x00302, 0x000db), + CE(0x00303, 0x00168), CE(0x00304, 0x0016a), CE(0x00306, 0x0016c), + CE(0x00308, 0x000dc), CE(0x00309, 0x01ee6), CE(0x0030a, 0x0016e), + CE(0x0030b, 0x00170), CE(0x0030c, 0x001d3), CE(0x0030f, 0x00214), + CE(0x00311, 0x00216), CE(0x0031b, 0x001af), CE(0x00323, 0x01ee4), + CE(0x00324, 0x01e72), CE(0x00328, 0x00172), CE(0x0032d, 0x01e76), + CE(0x00330, 0x01e74), CE(0x00303, 0x01e7c), CE(0x00323, 0x01e7e), + CE(0x00300, 0x01e80), CE(0x00301, 0x01e82), CE(0x00302, 0x00174), + CE(0x00307, 0x01e86), CE(0x00308, 0x01e84), CE(0x00323, 0x01e88), + CE(0x00307, 0x01e8a), CE(0x00308, 0x01e8c), CE(0x00300, 0x01ef2), + CE(0x00301, 0x000dd), CE(0x00302, 0x00176), CE(0x00303, 0x01ef8), + CE(0x00304, 0x00232), CE(0x00307, 0x01e8e), CE(0x00308, 0x00178), + CE(0x00309, 0x01ef6), CE(0x00323, 0x01ef4), CE(0x00301, 0x00179), + CE(0x00302, 0x01e90), CE(0x00307, 0x0017b), CE(0x0030c, 0x0017d), + CE(0x00323, 0x01e92), CE(0x00331, 0x01e94), CE(0x00300, 0x000e0), + CE(0x00301, 0x000e1), CE(0x00302, 0x000e2), CE(0x00303, 0x000e3), + CE(0x00304, 0x00101), CE(0x00306, 0x00103), CE(0x00307, 0x00227), + CE(0x00308, 0x000e4), CE(0x00309, 0x01ea3), CE(0x0030a, 0x000e5), + CE(0x0030c, 0x001ce), CE(0x0030f, 0x00201), CE(0x00311, 0x00203), + CE(0x00323, 0x01ea1), CE(0x00325, 0x01e01), CE(0x00328, 0x00105), + CE(0x00307, 0x01e03), CE(0x00323, 0x01e05), CE(0x00331, 0x01e07), + CE(0x00301, 0x00107), CE(0x00302, 0x00109), CE(0x00307, 0x0010b), + CE(0x0030c, 0x0010d), CE(0x00327, 0x000e7), CE(0x00307, 0x01e0b), + CE(0x0030c, 0x0010f), CE(0x00323, 0x01e0d), CE(0x00327, 0x01e11), + CE(0x0032d, 0x01e13), CE(0x00331, 0x01e0f), CE(0x00300, 0x000e8), + CE(0x00301, 0x000e9), CE(0x00302, 0x000ea), CE(0x00303, 0x01ebd), + CE(0x00304, 0x00113), CE(0x00306, 0x00115), CE(0x00307, 0x00117), + CE(0x00308, 0x000eb), CE(0x00309, 0x01ebb), CE(0x0030c, 0x0011b), + CE(0x0030f, 0x00205), CE(0x00311, 0x00207), CE(0x00323, 0x01eb9), + CE(0x00327, 0x00229), CE(0x00328, 0x00119), CE(0x0032d, 0x01e19), + CE(0x00330, 0x01e1b), CE(0x00307, 0x01e1f), CE(0x00301, 0x001f5), + CE(0x00302, 0x0011d), CE(0x00304, 0x01e21), CE(0x00306, 0x0011f), + CE(0x00307, 0x00121), CE(0x0030c, 0x001e7), CE(0x00327, 0x00123), + CE(0x00302, 0x00125), CE(0x00307, 0x01e23), CE(0x00308, 0x01e27), + CE(0x0030c, 0x0021f), CE(0x00323, 0x01e25), CE(0x00327, 0x01e29), + CE(0x0032e, 0x01e2b), CE(0x00331, 0x01e96), CE(0x00300, 0x000ec), + CE(0x00301, 0x000ed), CE(0x00302, 0x000ee), CE(0x00303, 0x00129), + CE(0x00304, 0x0012b), CE(0x00306, 0x0012d), CE(0x00308, 0x000ef), + CE(0x00309, 0x01ec9), CE(0x0030c, 0x001d0), CE(0x0030f, 0x00209), + CE(0x00311, 0x0020b), CE(0x00323, 0x01ecb), CE(0x00328, 0x0012f), + CE(0x00330, 0x01e2d), CE(0x00302, 0x00135), CE(0x0030c, 0x001f0), + CE(0x00301, 0x01e31), CE(0x0030c, 0x001e9), CE(0x00323, 0x01e33), + CE(0x00327, 0x00137), CE(0x00331, 0x01e35), CE(0x00301, 0x0013a), + CE(0x0030c, 0x0013e), CE(0x00323, 0x01e37), CE(0x00327, 0x0013c), + CE(0x0032d, 0x01e3d), CE(0x00331, 0x01e3b), CE(0x00301, 0x01e3f), + CE(0x00307, 0x01e41), CE(0x00323, 0x01e43), CE(0x00300, 0x001f9), + CE(0x00301, 0x00144), CE(0x00303, 0x000f1), CE(0x00307, 0x01e45), + CE(0x0030c, 0x00148), CE(0x00323, 0x01e47), CE(0x00327, 0x00146), + CE(0x0032d, 0x01e4b), CE(0x00331, 0x01e49), CE(0x00300, 0x000f2), + CE(0x00301, 0x000f3), CE(0x00302, 0x000f4), CE(0x00303, 0x000f5), + CE(0x00304, 0x0014d), CE(0x00306, 0x0014f), CE(0x00307, 0x0022f), + CE(0x00308, 0x000f6), CE(0x00309, 0x01ecf), CE(0x0030b, 0x00151), + CE(0x0030c, 0x001d2), CE(0x0030f, 0x0020d), CE(0x00311, 0x0020f), + CE(0x0031b, 0x001a1), CE(0x00323, 0x01ecd), CE(0x00328, 0x001eb), + CE(0x00301, 0x01e55), CE(0x00307, 0x01e57), CE(0x00301, 0x00155), + CE(0x00307, 0x01e59), CE(0x0030c, 0x00159), CE(0x0030f, 0x00211), + CE(0x00311, 0x00213), CE(0x00323, 0x01e5b), CE(0x00327, 0x00157), + CE(0x00331, 0x01e5f), CE(0x00301, 0x0015b), CE(0x00302, 0x0015d), + CE(0x00307, 0x01e61), CE(0x0030c, 0x00161), CE(0x00323, 0x01e63), + CE(0x00326, 0x00219), CE(0x00327, 0x0015f), CE(0x00307, 0x01e6b), + CE(0x00308, 0x01e97), CE(0x0030c, 0x00165), CE(0x00323, 0x01e6d), + CE(0x00326, 0x0021b), CE(0x00327, 0x00163), CE(0x0032d, 0x01e71), + CE(0x00331, 0x01e6f), CE(0x00300, 0x000f9), CE(0x00301, 0x000fa), + CE(0x00302, 0x000fb), CE(0x00303, 0x00169), CE(0x00304, 0x0016b), + CE(0x00306, 0x0016d), CE(0x00308, 0x000fc), CE(0x00309, 0x01ee7), + CE(0x0030a, 0x0016f), CE(0x0030b, 0x00171), CE(0x0030c, 0x001d4), + CE(0x0030f, 0x00215), CE(0x00311, 0x00217), CE(0x0031b, 0x001b0), + CE(0x00323, 0x01ee5), CE(0x00324, 0x01e73), CE(0x00328, 0x00173), + CE(0x0032d, 0x01e77), CE(0x00330, 0x01e75), CE(0x00303, 0x01e7d), + CE(0x00323, 0x01e7f), CE(0x00300, 0x01e81), CE(0x00301, 0x01e83), + CE(0x00302, 0x00175), CE(0x00307, 0x01e87), CE(0x00308, 0x01e85), + CE(0x0030a, 0x01e98), CE(0x00323, 0x01e89), CE(0x00307, 0x01e8b), + CE(0x00308, 0x01e8d), CE(0x00300, 0x01ef3), CE(0x00301, 0x000fd), + CE(0x00302, 0x00177), CE(0x00303, 0x01ef9), CE(0x00304, 0x00233), + CE(0x00307, 0x01e8f), CE(0x00308, 0x000ff), CE(0x00309, 0x01ef7), + CE(0x0030a, 0x01e99), CE(0x00323, 0x01ef5), CE(0x00301, 0x0017a), + CE(0x00302, 0x01e91), CE(0x00307, 0x0017c), CE(0x0030c, 0x0017e), + CE(0x00323, 0x01e93), CE(0x00331, 0x01e95), CE(0x00300, 0x01fed), + CE(0x00301, 0x00385), CE(0x00342, 0x01fc1), CE(0x00300, 0x01ea6), + CE(0x00301, 0x01ea4), CE(0x00303, 0x01eaa), CE(0x00309, 0x01ea8), + CE(0x00304, 0x001de), CE(0x00301, 0x001fa), CE(0x00301, 0x001fc), + CE(0x00304, 0x001e2), CE(0x00301, 0x01e08), CE(0x00300, 0x01ec0), + CE(0x00301, 0x01ebe), CE(0x00303, 0x01ec4), CE(0x00309, 0x01ec2), + CE(0x00301, 0x01e2e), CE(0x00300, 0x01ed2), CE(0x00301, 0x01ed0), + CE(0x00303, 0x01ed6), CE(0x00309, 0x01ed4), CE(0x00301, 0x01e4c), + CE(0x00304, 0x0022c), CE(0x00308, 0x01e4e), CE(0x00304, 0x0022a), + CE(0x00301, 0x001fe), CE(0x00300, 0x001db), CE(0x00301, 0x001d7), + CE(0x00304, 0x001d5), CE(0x0030c, 0x001d9), CE(0x00300, 0x01ea7), + CE(0x00301, 0x01ea5), CE(0x00303, 0x01eab), CE(0x00309, 0x01ea9), + CE(0x00304, 0x001df), CE(0x00301, 0x001fb), CE(0x00301, 0x001fd), + CE(0x00304, 0x001e3), CE(0x00301, 0x01e09), CE(0x00300, 0x01ec1), + CE(0x00301, 0x01ebf), CE(0x00303, 0x01ec5), CE(0x00309, 0x01ec3), + CE(0x00301, 0x01e2f), CE(0x00300, 0x01ed3), CE(0x00301, 0x01ed1), + CE(0x00303, 0x01ed7), CE(0x00309, 0x01ed5), CE(0x00301, 0x01e4d), + CE(0x00304, 0x0022d), CE(0x00308, 0x01e4f), CE(0x00304, 0x0022b), + CE(0x00301, 0x001ff), CE(0x00300, 0x001dc), CE(0x00301, 0x001d8), + CE(0x00304, 0x001d6), CE(0x0030c, 0x001da), CE(0x00300, 0x01eb0), + CE(0x00301, 0x01eae), CE(0x00303, 0x01eb4), CE(0x00309, 0x01eb2), + CE(0x00300, 0x01eb1), CE(0x00301, 0x01eaf), CE(0x00303, 0x01eb5), + CE(0x00309, 0x01eb3), CE(0x00300, 0x01e14), CE(0x00301, 0x01e16), + CE(0x00300, 0x01e15), CE(0x00301, 0x01e17), CE(0x00300, 0x01e50), + CE(0x00301, 0x01e52), CE(0x00300, 0x01e51), CE(0x00301, 0x01e53), + CE(0x00307, 0x01e64), CE(0x00307, 0x01e65), CE(0x00307, 0x01e66), + CE(0x00307, 0x01e67), CE(0x00301, 0x01e78), CE(0x00301, 0x01e79), + CE(0x00308, 0x01e7a), CE(0x00308, 0x01e7b), CE(0x00307, 0x01e9b), + CE(0x00300, 0x01edc), CE(0x00301, 0x01eda), CE(0x00303, 0x01ee0), + CE(0x00309, 0x01ede), CE(0x00323, 0x01ee2), CE(0x00300, 0x01edd), + CE(0x00301, 0x01edb), CE(0x00303, 0x01ee1), CE(0x00309, 0x01edf), + CE(0x00323, 0x01ee3), CE(0x00300, 0x01eea), CE(0x00301, 0x01ee8), + CE(0x00303, 0x01eee), CE(0x00309, 0x01eec), CE(0x00323, 0x01ef0), + CE(0x00300, 0x01eeb), CE(0x00301, 0x01ee9), CE(0x00303, 0x01eef), + CE(0x00309, 0x01eed), CE(0x00323, 0x01ef1), CE(0x0030c, 0x001ee), + CE(0x00304, 0x001ec), CE(0x00304, 0x001ed), CE(0x00304, 0x001e0), + CE(0x00304, 0x001e1), CE(0x00306, 0x01e1c), CE(0x00306, 0x01e1d), + CE(0x00304, 0x00230), CE(0x00304, 0x00231), CE(0x0030c, 0x001ef), + CE(0x00300, 0x01fba), CE(0x00301, 0x00386), CE(0x00304, 0x01fb9), + CE(0x00306, 0x01fb8), CE(0x00313, 0x01f08), CE(0x00314, 0x01f09), + CE(0x00345, 0x01fbc), CE(0x00300, 0x01fc8), CE(0x00301, 0x00388), + CE(0x00313, 0x01f18), CE(0x00314, 0x01f19), CE(0x00300, 0x01fca), + CE(0x00301, 0x00389), CE(0x00313, 0x01f28), CE(0x00314, 0x01f29), + CE(0x00345, 0x01fcc), CE(0x00300, 0x01fda), CE(0x00301, 0x0038a), + CE(0x00304, 0x01fd9), CE(0x00306, 0x01fd8), CE(0x00308, 0x003aa), + CE(0x00313, 0x01f38), CE(0x00314, 0x01f39), CE(0x00300, 0x01ff8), + CE(0x00301, 0x0038c), CE(0x00313, 0x01f48), CE(0x00314, 0x01f49), + CE(0x00314, 0x01fec), CE(0x00300, 0x01fea), CE(0x00301, 0x0038e), + CE(0x00304, 0x01fe9), CE(0x00306, 0x01fe8), CE(0x00308, 0x003ab), + CE(0x00314, 0x01f59), CE(0x00300, 0x01ffa), CE(0x00301, 0x0038f), + CE(0x00313, 0x01f68), CE(0x00314, 0x01f69), CE(0x00345, 0x01ffc), + CE(0x00345, 0x01fb4), CE(0x00345, 0x01fc4), CE(0x00300, 0x01f70), + CE(0x00301, 0x003ac), CE(0x00304, 0x01fb1), CE(0x00306, 0x01fb0), + CE(0x00313, 0x01f00), CE(0x00314, 0x01f01), CE(0x00342, 0x01fb6), + CE(0x00345, 0x01fb3), CE(0x00300, 0x01f72), CE(0x00301, 0x003ad), + CE(0x00313, 0x01f10), CE(0x00314, 0x01f11), CE(0x00300, 0x01f74), + CE(0x00301, 0x003ae), CE(0x00313, 0x01f20), CE(0x00314, 0x01f21), + CE(0x00342, 0x01fc6), CE(0x00345, 0x01fc3), CE(0x00300, 0x01f76), + CE(0x00301, 0x003af), CE(0x00304, 0x01fd1), CE(0x00306, 0x01fd0), + CE(0x00308, 0x003ca), CE(0x00313, 0x01f30), CE(0x00314, 0x01f31), + CE(0x00342, 0x01fd6), CE(0x00300, 0x01f78), CE(0x00301, 0x003cc), + CE(0x00313, 0x01f40), CE(0x00314, 0x01f41), CE(0x00313, 0x01fe4), + CE(0x00314, 0x01fe5), CE(0x00300, 0x01f7a), CE(0x00301, 0x003cd), + CE(0x00304, 0x01fe1), CE(0x00306, 0x01fe0), CE(0x00308, 0x003cb), + CE(0x00313, 0x01f50), CE(0x00314, 0x01f51), CE(0x00342, 0x01fe6), + CE(0x00300, 0x01f7c), CE(0x00301, 0x003ce), CE(0x00313, 0x01f60), + CE(0x00314, 0x01f61), CE(0x00342, 0x01ff6), CE(0x00345, 0x01ff3), + CE(0x00300, 0x01fd2), CE(0x00301, 0x00390), CE(0x00342, 0x01fd7), + CE(0x00300, 0x01fe2), CE(0x00301, 0x003b0), CE(0x00342, 0x01fe7), + CE(0x00345, 0x01ff4), CE(0x00301, 0x003d3), CE(0x00308, 0x003d4), + CE(0x00308, 0x00407), CE(0x00306, 0x004d0), CE(0x00308, 0x004d2), + CE(0x00301, 0x00403), CE(0x00300, 0x00400), CE(0x00306, 0x004d6), + CE(0x00308, 0x00401), CE(0x00306, 0x004c1), CE(0x00308, 0x004dc), + CE(0x00308, 0x004de), CE(0x00300, 0x0040d), CE(0x00304, 0x004e2), + CE(0x00306, 0x00419), CE(0x00308, 0x004e4), CE(0x00301, 0x0040c), + CE(0x00308, 0x004e6), CE(0x00304, 0x004ee), CE(0x00306, 0x0040e), + CE(0x00308, 0x004f0), CE(0x0030b, 0x004f2), CE(0x00308, 0x004f4), + CE(0x00308, 0x004f8), CE(0x00308, 0x004ec), CE(0x00306, 0x004d1), + CE(0x00308, 0x004d3), CE(0x00301, 0x00453), CE(0x00300, 0x00450), + CE(0x00306, 0x004d7), CE(0x00308, 0x00451), CE(0x00306, 0x004c2), + CE(0x00308, 0x004dd), CE(0x00308, 0x004df), CE(0x00300, 0x0045d), + CE(0x00304, 0x004e3), CE(0x00306, 0x00439), CE(0x00308, 0x004e5), + CE(0x00301, 0x0045c), CE(0x00308, 0x004e7), CE(0x00304, 0x004ef), + CE(0x00306, 0x0045e), CE(0x00308, 0x004f1), CE(0x0030b, 0x004f3), + CE(0x00308, 0x004f5), CE(0x00308, 0x004f9), CE(0x00308, 0x004ed), + CE(0x00308, 0x00457), CE(0x0030f, 0x00476), CE(0x0030f, 0x00477), + CE(0x00308, 0x004da), CE(0x00308, 0x004db), CE(0x00308, 0x004ea), + CE(0x00308, 0x004eb), CE(0x00653, 0x00622), CE(0x00654, 0x00623), + CE(0x00655, 0x00625), CE(0x00654, 0x00624), CE(0x00654, 0x00626), + CE(0x00654, 0x006c2), CE(0x00654, 0x006d3), CE(0x00654, 0x006c0), + CE(0x0093c, 0x00929), CE(0x0093c, 0x00931), CE(0x0093c, 0x00934), + CE(0x009be, 0x009cb), CE(0x009d7, 0x009cc), CE(0x00b3e, 0x00b4b), + CE(0x00b56, 0x00b48), CE(0x00b57, 0x00b4c), CE(0x00bd7, 0x00b94), + CE(0x00bbe, 0x00bca), CE(0x00bd7, 0x00bcc), CE(0x00bbe, 0x00bcb), + CE(0x00c56, 0x00c48), CE(0x00cd5, 0x00cc0), CE(0x00cc2, 0x00cca), + CE(0x00cd5, 0x00cc7), CE(0x00cd6, 0x00cc8), CE(0x00cd5, 0x00ccb), + CE(0x00d3e, 0x00d4a), CE(0x00d57, 0x00d4c), CE(0x00d3e, 0x00d4b), + CE(0x00dca, 0x00dda), CE(0x00dcf, 0x00ddc), CE(0x00ddf, 0x00dde), + CE(0x00dca, 0x00ddd), CE(0x0102e, 0x01026), CE(0x01b35, 0x01b06), + CE(0x01b35, 0x01b08), CE(0x01b35, 0x01b0a), CE(0x01b35, 0x01b0c), + CE(0x01b35, 0x01b0e), CE(0x01b35, 0x01b12), CE(0x01b35, 0x01b3b), + CE(0x01b35, 0x01b3d), CE(0x01b35, 0x01b40), CE(0x01b35, 0x01b41), + CE(0x01b35, 0x01b43), CE(0x00304, 0x01e38), CE(0x00304, 0x01e39), + CE(0x00304, 0x01e5c), CE(0x00304, 0x01e5d), CE(0x00307, 0x01e68), + CE(0x00307, 0x01e69), CE(0x00302, 0x01eac), CE(0x00306, 0x01eb6), + CE(0x00302, 0x01ead), CE(0x00306, 0x01eb7), CE(0x00302, 0x01ec6), + CE(0x00302, 0x01ec7), CE(0x00302, 0x01ed8), CE(0x00302, 0x01ed9), + CE(0x00300, 0x01f02), CE(0x00301, 0x01f04), CE(0x00342, 0x01f06), + CE(0x00345, 0x01f80), CE(0x00300, 0x01f03), CE(0x00301, 0x01f05), + CE(0x00342, 0x01f07), CE(0x00345, 0x01f81), CE(0x00345, 0x01f82), + CE(0x00345, 0x01f83), CE(0x00345, 0x01f84), CE(0x00345, 0x01f85), + CE(0x00345, 0x01f86), CE(0x00345, 0x01f87), CE(0x00300, 0x01f0a), + CE(0x00301, 0x01f0c), CE(0x00342, 0x01f0e), CE(0x00345, 0x01f88), + CE(0x00300, 0x01f0b), CE(0x00301, 0x01f0d), CE(0x00342, 0x01f0f), + CE(0x00345, 0x01f89), CE(0x00345, 0x01f8a), CE(0x00345, 0x01f8b), + CE(0x00345, 0x01f8c), CE(0x00345, 0x01f8d), CE(0x00345, 0x01f8e), + CE(0x00345, 0x01f8f), CE(0x00300, 0x01f12), CE(0x00301, 0x01f14), + CE(0x00300, 0x01f13), CE(0x00301, 0x01f15), CE(0x00300, 0x01f1a), + CE(0x00301, 0x01f1c), CE(0x00300, 0x01f1b), CE(0x00301, 0x01f1d), + CE(0x00300, 0x01f22), CE(0x00301, 0x01f24), CE(0x00342, 0x01f26), + CE(0x00345, 0x01f90), CE(0x00300, 0x01f23), CE(0x00301, 0x01f25), + CE(0x00342, 0x01f27), CE(0x00345, 0x01f91), CE(0x00345, 0x01f92), + CE(0x00345, 0x01f93), CE(0x00345, 0x01f94), CE(0x00345, 0x01f95), + CE(0x00345, 0x01f96), CE(0x00345, 0x01f97), CE(0x00300, 0x01f2a), + CE(0x00301, 0x01f2c), CE(0x00342, 0x01f2e), CE(0x00345, 0x01f98), + CE(0x00300, 0x01f2b), CE(0x00301, 0x01f2d), CE(0x00342, 0x01f2f), + CE(0x00345, 0x01f99), CE(0x00345, 0x01f9a), CE(0x00345, 0x01f9b), + CE(0x00345, 0x01f9c), CE(0x00345, 0x01f9d), CE(0x00345, 0x01f9e), + CE(0x00345, 0x01f9f), CE(0x00300, 0x01f32), CE(0x00301, 0x01f34), + CE(0x00342, 0x01f36), CE(0x00300, 0x01f33), CE(0x00301, 0x01f35), + CE(0x00342, 0x01f37), CE(0x00300, 0x01f3a), CE(0x00301, 0x01f3c), + CE(0x00342, 0x01f3e), CE(0x00300, 0x01f3b), CE(0x00301, 0x01f3d), + CE(0x00342, 0x01f3f), CE(0x00300, 0x01f42), CE(0x00301, 0x01f44), + CE(0x00300, 0x01f43), CE(0x00301, 0x01f45), CE(0x00300, 0x01f4a), + CE(0x00301, 0x01f4c), CE(0x00300, 0x01f4b), CE(0x00301, 0x01f4d), + CE(0x00300, 0x01f52), CE(0x00301, 0x01f54), CE(0x00342, 0x01f56), + CE(0x00300, 0x01f53), CE(0x00301, 0x01f55), CE(0x00342, 0x01f57), + CE(0x00300, 0x01f5b), CE(0x00301, 0x01f5d), CE(0x00342, 0x01f5f), + CE(0x00300, 0x01f62), CE(0x00301, 0x01f64), CE(0x00342, 0x01f66), + CE(0x00345, 0x01fa0), CE(0x00300, 0x01f63), CE(0x00301, 0x01f65), + CE(0x00342, 0x01f67), CE(0x00345, 0x01fa1), CE(0x00345, 0x01fa2), + CE(0x00345, 0x01fa3), CE(0x00345, 0x01fa4), CE(0x00345, 0x01fa5), + CE(0x00345, 0x01fa6), CE(0x00345, 0x01fa7), CE(0x00300, 0x01f6a), + CE(0x00301, 0x01f6c), CE(0x00342, 0x01f6e), CE(0x00345, 0x01fa8), + CE(0x00300, 0x01f6b), CE(0x00301, 0x01f6d), CE(0x00342, 0x01f6f), + CE(0x00345, 0x01fa9), CE(0x00345, 0x01faa), CE(0x00345, 0x01fab), + CE(0x00345, 0x01fac), CE(0x00345, 0x01fad), CE(0x00345, 0x01fae), + CE(0x00345, 0x01faf), CE(0x00345, 0x01fb2), CE(0x00345, 0x01fc2), + CE(0x00345, 0x01ff2), CE(0x00345, 0x01fb7), CE(0x00300, 0x01fcd), + CE(0x00301, 0x01fce), CE(0x00342, 0x01fcf), CE(0x00345, 0x01fc7), + CE(0x00345, 0x01ff7), CE(0x00300, 0x01fdd), CE(0x00301, 0x01fde), + CE(0x00342, 0x01fdf), CE(0x00338, 0x0219a), CE(0x00338, 0x0219b), + CE(0x00338, 0x021ae), CE(0x00338, 0x021cd), CE(0x00338, 0x021cf), + CE(0x00338, 0x021ce), CE(0x00338, 0x02204), CE(0x00338, 0x02209), + CE(0x00338, 0x0220c), CE(0x00338, 0x02224), CE(0x00338, 0x02226), + CE(0x00338, 0x02241), CE(0x00338, 0x02244), CE(0x00338, 0x02247), + CE(0x00338, 0x02249), CE(0x00338, 0x0226d), CE(0x00338, 0x02262), + CE(0x00338, 0x02270), CE(0x00338, 0x02271), CE(0x00338, 0x02274), + CE(0x00338, 0x02275), CE(0x00338, 0x02278), CE(0x00338, 0x02279), + CE(0x00338, 0x02280), CE(0x00338, 0x02281), CE(0x00338, 0x022e0), + CE(0x00338, 0x022e1), CE(0x00338, 0x02284), CE(0x00338, 0x02285), + CE(0x00338, 0x02288), CE(0x00338, 0x02289), CE(0x00338, 0x022e2), + CE(0x00338, 0x022e3), CE(0x00338, 0x022ac), CE(0x00338, 0x022ad), + CE(0x00338, 0x022ae), CE(0x00338, 0x022af), CE(0x00338, 0x022ea), + CE(0x00338, 0x022eb), CE(0x00338, 0x022ec), CE(0x00338, 0x022ed), + CE(0x03099, 0x03094), CE(0x03099, 0x0304c), CE(0x03099, 0x0304e), + CE(0x03099, 0x03050), CE(0x03099, 0x03052), CE(0x03099, 0x03054), + CE(0x03099, 0x03056), CE(0x03099, 0x03058), CE(0x03099, 0x0305a), + CE(0x03099, 0x0305c), CE(0x03099, 0x0305e), CE(0x03099, 0x03060), + CE(0x03099, 0x03062), CE(0x03099, 0x03065), CE(0x03099, 0x03067), + CE(0x03099, 0x03069), CE(0x03099, 0x03070), CE(0x0309a, 0x03071), + CE(0x03099, 0x03073), CE(0x0309a, 0x03074), CE(0x03099, 0x03076), + CE(0x0309a, 0x03077), CE(0x03099, 0x03079), CE(0x0309a, 0x0307a), + CE(0x03099, 0x0307c), CE(0x0309a, 0x0307d), CE(0x03099, 0x0309e), + CE(0x03099, 0x030f4), CE(0x03099, 0x030ac), CE(0x03099, 0x030ae), + CE(0x03099, 0x030b0), CE(0x03099, 0x030b2), CE(0x03099, 0x030b4), + CE(0x03099, 0x030b6), CE(0x03099, 0x030b8), CE(0x03099, 0x030ba), + CE(0x03099, 0x030bc), CE(0x03099, 0x030be), CE(0x03099, 0x030c0), + CE(0x03099, 0x030c2), CE(0x03099, 0x030c5), CE(0x03099, 0x030c7), + CE(0x03099, 0x030c9), CE(0x03099, 0x030d0), CE(0x0309a, 0x030d1), + CE(0x03099, 0x030d3), CE(0x0309a, 0x030d4), CE(0x03099, 0x030d6), + CE(0x0309a, 0x030d7), CE(0x03099, 0x030d9), CE(0x0309a, 0x030da), + CE(0x03099, 0x030dc), CE(0x0309a, 0x030dd), CE(0x03099, 0x030f7), + CE(0x03099, 0x030f8), CE(0x03099, 0x030f9), CE(0x03099, 0x030fa), + CE(0x03099, 0x030fe), CE(0x110ba, 0x1109a), CE(0x110ba, 0x1109c), + CE(0x110ba, 0x110ab), CE(0x11127, 0x1112e), CE(0x11127, 0x1112f), + ]; + return t; + } } - diff --git a/std/internal/unicode_decomp.d b/std/internal/unicode_decomp.d index bfe665b7bbb..736965da72f 100644 --- a/std/internal/unicode_decomp.d +++ b/std/internal/unicode_decomp.d @@ -1,34 +1,5301 @@ module std.internal.unicode_decomp; import std.internal.unicode_tables; -@safe pure nothrow @nogc: +@safe pure nothrow @nogc package(std): -static if(size_t.sizeof == 8) { -//22656 bytes -enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)([ 0x0, 0x20, 0x2a0], [ 0x100, 0xa00, 0x21c0], [ 0x402030202020100, 0x706020202020205, 0x802020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x3000200010000, 0x7000600050004, 0xa000900080000, 0xc000b, 0xf000e000d0000, 0x11001000000000, 0x15001400130012, 0x19001800170016, 0x1b001a00000000, 0x0, 0x1c, 0x1e0000001d0000, 0x1f00000000, 0x0, 0x0, 0x0, 0x0, 0x2100200000, 0x2200000000, 0x2400230000, 0x0, 0x2500000000, 0x2700000026, 0x2800000000, 0x2900000000, 0x2a00000000, 0x2b00000000, 0x2c0000, 0x2e002d0000, 0x3100300000002f, 0x330032, 0x340000, 0x35000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3800370036, 0x0, 0x0, 0x0, 0x3b003a00390000, 0x3d003c, 0x410040003f003e, 0x45004400430042, 0x49004800470046, 0x4d004c004b004a, 0x510050004f004e, 0x530052, 0x57005600550054, 0x5a00590058, 0x5e005d005c005b, 0x6100000060005f, 0x620000, 0x0, 0x63000000000000, 0x67006600650064, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69000000000068, 0x6a00000000, 0x0, 0x0, 0x6b000000000000, 0x0, 0x6c000000000000, 0x0, 0x0, 0x6e00000000006d, 0x7200710070006f, 0x7500740073, 0x79007800770076, 0x7d007c007b007a, 0x80007f007e0000, 0x81, 0x85008400830082, 0x89008800870086, 0x8d008c008b008a, 0x910090008f008e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92000000000000, 0x93000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x97009600950094, 0x9b009a00990098, 0x9f009e009d009c, 0xa200a100a0, 0xa600a500a400a3, 0xaa00a900a800a7, 0xae00ad00ac00ab, 0xb200b100b000af, 0xb600b500b400b3, 0xba00b900b800b7, 0xbe00bd00bc00bb, 0xc200c100c000bf, 0xc600c500c400c3, 0xca00c900c800c7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc00cb, 0xcd0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf00ce00000000, 0xd100d00000, 0x0, 0x0, 0x0, 0x0, 0xd500d400d300d2, 0xd900d800d700d6, 0xdd00dc00db00da, 0xdf00d300d200de, 0xe200e100e000d5, 0xe500e400e300d9, 0xe900e800e700e6, 0xed00ec00eb00ea, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf100f000ef00ee, 0xf300f2, 0x0, 0x0, 0x0, 0x0, 0xf700f600f500f4, 0xf8, 0xfb00fa00f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff00fe00fd00fc, 0x103010201010100, 0x107010601050104, 0x10b010a01090108, 0x10c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x69200000015, 0x9000000000000, 0x30f034300000000, 0x11b20003, 0x78703140048, 0x49403c603ce, 0x58605730570056d, 0x5f8000005b005a6, 0x6580631062e062b, 0x6f906ea06e706e4, 0x7a907a6078f0000, 0x7e307bf07ac, 0x8b708b408b10000, 0x95f08cb, 0x9c209af09ac09a9, 0xa47000009ec09e2, 0xab30a8c0a890a86, 0xb550b490b460b43, 0xc5e0c5b0c410000, 0xc980c740c61, 0xd6e0d6b0d680000, 0xe1b00000e0c0d82, 0x9c8058c09c50589, 0xa3b05ec0a0a05ce, 0xa4105f20a3e05ef, 0xa6e061a0a4405f5, 0xaa2064700000000, 0xab006550aad0652, 0xab9065e0ad00675, 0xb0106a00afb069a, 0xb0a06a90b0406a3, 0xb1606ba, 0xb4f06f00b4c06ed, 0xb6b070f0b5206f3, 0xb3706d8000006f6, 0xbae072e0b730717, 0x7500bcc07430000, 0x7400bcf07460bd9, 0x78c000000000bc9, 0x7950c4d079b0c3e, 0xed70c47, 0xc8e07d90c8307ce, 0xca207ed, 0xd1d08580d070842, 0xd2b086c0d0d0848, 0xd49088a0d320873, 0xd5d08a60d380879, 0xd54089d, 0xd7808c10d7108ba, 0xd9808e10d7f08c8, 0xdc4090d0d9b08e4, 0xe0f09620de9093f, 0x97f0e290979096e, 0x8400614060d0e2f, 0xcae07f9, 0x0, 0x0, 0x8f0000000000000, 0xda7, 0x0, 0x0, 0x0, 0x0, 0x7360a670613060c, 0x78307800bb9073d, 0x70309f305b70c32, 0x8e70ca507f00b5f, 0x8d20d8d08d60d9e, 0x8ce0d9108da0d89, 0x9e505a900000d85, 0xe630e5a09de05a2, 0xb0706a600000000, 0xccc08170ba80728, 0xecc0e7b0ccf081a, 0xa64061006090b76, 0xaf80697, 0x9ef05b30c3b0789, 0xe680e5d0e600e57, 0x9f905bd09f605ba, 0xabf06640abc0661, 0xb6507090b620706, 0xcab07f60ca807f3, 0xd13084e0d10084b, 0xda408ed0da108ea, 0xd5a08a30d460887, 0xb1f06c300000000, 0x0, 0x9db059f00000000, 0xc9b07e60ac9066e, 0xc9107dc0c7b07c6, 0xe1509680c9407df, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa11073e0e9a0b0d, 0xde10eb80eb60eb4, 0x695, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4b00240012000f, 0x270006, 0xb4108400a280e96, 0xecf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b00000004001a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xed5, 0x5400000000, 0x54600000000, 0x0, 0x7410ee8001c0003, 0xfb40f630f43, 0x103c101600000fed, 0x1185, 0x0, 0x0, 0x0, 0x0, 0x0, 0x101f0fbd00000000, 0x1175111910f5108f, 0x1213, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120c117e00000000, 0x124b120311d5, 0x10161011116e10ea, 0x11ee123c101f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11f811f011ae, 0x10f00fad, 0x100d0000, 0x0, 0x12ad000012b612b0, 0x12a4000000000000, 0x0, 0x12d712c212ce, 0x0, 0x0, 0x12c80000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12ef000012f812f2, 0x132d000000000000, 0x0, 0x131b13041310, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1333133000000000, 0x0, 0x0, 0x12fb12b90000, 0x0, 0x0, 0x0, 0x12ec12aa12e912a7, 0x12f512b300000000, 0x1339133600000000, 0x130112bf12fe12bc, 0x130712c500000000, 0x131512d1130d12cb, 0x133f133c00000000, 0x131812d4132a12e6, 0x132112dd131e12da, 0x132412e0, 0x132712e3, 0x0, 0x0, 0x1342000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13e913e600000000, 0x17ca13ec178f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x185b179213ef0000, 0x1811, 0x0, 0x18520000186d, 0x0, 0x0, 0x0, 0x186a000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18820000, 0x0, 0x188b0000, 0x188e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1879187618731870, 0x18881885187f187c, 0x0, 0x0, 0x189a000000000000, 0x189d, 0x0, 0x0, 0x0, 0x1897000018941891, 0x0, 0x0, 0x0, 0x0, 0x18ac000000000000, 0x18af00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18a618a318a00000, 0x18a900000000, 0x0, 0x0, 0x18b80000000018bb, 0x18be, 0x0, 0x0, 0x0, 0x18b518b2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18c1, 0x0, 0x0, 0x0, 0x0, 0x18ca18c400000000, 0x18c7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18cd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18d0, 0x18da000000000000, 0x18d618d3000018dd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18e618e000000000, 0x18e3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18e900000000, 0x18f318ef18ec, 0x0, 0x0, 0x0, 0x0, 0x18f6000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18ff000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18fc18f9, 0x0, 0x0, 0x0, 0x1902, 0x0, 0x0, 0x0, 0x0, 0x1907000000000000, 0x0, 0x0, 0x190a0000, 0x190d00000000, 0x1910000000000000, 0x0, 0x1913, 0x0, 0x0, 0x19040000, 0x0, 0x1916000000000000, 0x1931193519190000, 0x1938193c, 0x0, 0x191c0000, 0x0, 0x0, 0x0, 0x1922000000000000, 0x0, 0x0, 0x19250000, 0x192800000000, 0x192b000000000000, 0x0, 0x192e, 0x0, 0x0, 0x191f0000, 0x0, 0x0, 0x193f00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1942, 0x0, 0x1a3800000000, 0x1a3e00001a3b, 0x1a4400001a41, 0x1a4700000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a4a000000000000, 0x1a4d0000, 0x1a5600001a531a50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d50e550568, 0x6870e75062905e6, 0x71a060706cf06ac, 0x77e07230734, 0x82c06af0e7e07a4, 0x6920770056b088d, 0x9371a590e840e82, 0xe8e0e8c0a7d0a2e, 0xb79000006020e90, 0xe8807870e7105d3, 0xba30cd31a5d1a5b, 0x86a0ea41a610a24, 0x10ee10ec10ea1a63, 0xa110ae0123e123c, 0x10ec10ea086a0a24, 0x123e123c11f0, 0x0, 0x0, 0x0, 0x1313, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe86000000000000, 0xe900e660e8a09a0, 0xe980e940e920ad9, 0x1a650ea00e9e0e9c, 0xed31a670ea20ed1, 0xeac0eaa0ea60ea8, 0xeba0eb20eb00eae, 0xec00ebe0e790ebc, 0x6110ec40ec21a5f, 0x116e0eca0ec80ec6, 0xa1305da0a0705cb, 0xa1905e00a1605dd, 0xa6b06170a4a05fb, 0xa7a06260a71061d, 0xa7706230a740620, 0xaa9064e0aa5064a, 0xad6067b0ad30678, 0xaef06840acc0671, 0xb1906bd0afe069d, 0xb1c06c00b2206c6, 0xb2806cc0b2506c9, 0xb5806fc0b6e0712, 0xbab072b0ba50725, 0xbd207490bb10731, 0xbdf07560bd5074c, 0xc1207720bdc0753, 0xc1807780c150775, 0xc4a07980c440792, 0xc50079e0c5307a1, 0xc7f07ca0c7707c2, 0xc8a07d50c8607d1, 0xcef08380cec0835, 0xd1608510d0a0845, 0xd20085b0d190854, 0xd3f08800d350876, 0xd3b087c0d2e086f, 0xd4e089a0d420883, 0xd6308ac0d5708a0, 0xdc1090a0d6008a9, 0xdc709100dca0913, 0xd7b08c40d7408bd, 0xdde09270ddb0924, 0xde6093c0de30939, 0xdec09420def0945, 0xe0109540df50948, 0xe18096b0e040957, 0xe3509850e2c097c, 0xd510b2b0e380988, 0xd3509a60e210df2, 0x0, 0x9e905ad09fc05c0, 0x9b2057609b6057a, 0x9ba057e09be0582, 0x9cf059309ff05c3, 0x9d7059b09cb058f, 0xa0305c709d30597, 0xab6065b0ac20667, 0xa9306380a9f0644, 0xa9b06400a8f0634, 0xac5066a0a97063c, 0xb68070c0b5c0700, 0xc9f07ea0cc50810, 0xc6407af0c6807b3, 0xc6c07b70c7007bb, 0xcb508000cc80813, 0xcbd08080cb107fc, 0xcc1080c0cb90804, 0xd9508de0dbe0907, 0xdaa08f30dae08f7, 0xdb208fb0db608ff, 0xe09095c0dba0903, 0xe1e09710e240974, 0xe120965, 0x0, 0x10c1109f10be109c, 0x10d310b110ca10a8, 0xf160ef40f130ef1, 0xf280f060f1f0efd, 0x110610fb110310f8, 0x110a10ff, 0xf540f490f510f46, 0xf580f4d, 0x1145112311421120, 0x11571135114e112c, 0xf8b0f690f880f66, 0xf9d0f7b0f940f72, 0x119f1190119c118d, 0x11a7119811a31194, 0xfd20fc30fcf0fc0, 0xfda0fcb0fd60fc7, 0x11e611db11e311d8, 0x11ea11df, 0xffe0ff30ffb0ff0, 0x10020ff7, 0x122d121e122a121b, 0x1235122612311222, 0x1025000010220000, 0x102d000010290000, 0x1277125512741252, 0x128912671280125e, 0x106410421061103f, 0x10761054106d104b, 0x10f510f2108f1088, 0x1175117211191112, 0x1203120011d511d2, 0x124b1244, 0x10c510a310dc10ba, 0x10d710b510ce10ac, 0xf1a0ef80f310f0f, 0xf2c0f0a0f230f01, 0x114911271160113e, 0x115b113911521130, 0xf8f0f6d0fa60f84, 0xfa10f7f0f980f76, 0x127b125912921270, 0x128d126b12841262, 0x10681046107f105d, 0x107a10581071104f, 0x10e7108b10961099, 0x10e310e000001092, 0xee80ee50eeb0eee, 0x2a1170002a0f35, 0x116b111500200051, 0x116711640000111c, 0xf630f600f430f40, 0x350031002d0faa, 0x118511811178117b, 0x118911ab00000000, 0xfb40fb10fb70fba, 0x440040003c0000, 0x1213120f12061209, 0x1217123911f511f2, 0x101610131019101c, 0x995001c0018100a, 0x129d124700000000, 0x129912960000124e, 0x103c10390fed0fea, 0x3900031083, 0x1000100010001, 0x1000100010001, 0x100010001, 0x0, 0x1a690000, 0x4e000000000000, 0x0, 0x0, 0x0, 0x2ff02fc02fa, 0x0, 0x1000000000000, 0x1a6f000000000000, 0x1a7e1a7b00001a72, 0x0, 0xc0000008f, 0x0, 0x563000000000000, 0x920560, 0x0, 0x0, 0x1a76000000000000, 0x0, 0x1000000000000, 0x0, 0x0, 0x0, 0x0, 0xae00305, 0x392038303740365, 0x1aad02f403b003a1, 0xb3b00a500a10544, 0x30f034303140305, 0x392038303740365, 0x1aad02f403b003a1, 0xa500a10544, 0xb4107870a7d0692, 0xa280b790b0d0e8c, 0x8400cd30b3b05d3, 0xba3, 0x0, 0x0, 0x83f, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe4d05e309a2099e, 0xe770a220a1e0000, 0x6ac06020e500000, 0xe6d0b0d06ac06ac, 0xa28073406cf06cf, 0x786077e0000, 0x82c083b06af0000, 0x82c082c, 0x897088f0863, 0x77c0000060a, 0x5b0071a0000060a, 0xa7d000005e305d5, 0x7230000067e0629, 0x136a136213540787, 0x68000000ae0136f, 0x10060f3a10ec11ee, 0x1aab, 0xa7d0a2e05e60000, 0x73e0ae0, 0x0, 0x3ca03c103e203da, 0x498045903d20455, 0x3de04e703d604cf, 0x3be051104eb049c, 0x6de06d406d106cf, 0x91f091b091806b2, 0x950094d068206e1, 0x72305e605e30734, 0xb3d0b330b300ae0, 0xdd60dd20dcf086a, 0xdfd0dfa0b410b40, 0x5d30a2e09a00a28, 0x0, 0x0, 0x30d0000, 0x0, 0x0, 0x0, 0x1a8d1a8600000000, 0x0, 0x0, 0x0, 0x0, 0x1a9200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a981a9b1a950000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1aa0, 0x1aa50000, 0x1aa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ab200001aaf, 0x0, 0x1ac100001ab81ab5, 0x1ac4, 0x0, 0x0, 0x0, 0x1ac80000, 0x1ace000000001acb, 0x1ad10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ad700000556, 0x0, 0x0, 0x55b054a1ad40000, 0x1add1ada, 0x1ae31ae0, 0x1ae91ae6, 0x0, 0x1aef1aec, 0x1afb1af8, 0x1b011afe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b131b101b0d1b0a, 0x0, 0x0, 0x0, 0x0, 0x1b071b041af51af2, 0x0, 0x1b191b1600000000, 0x1b1f1b1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b371b350000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x365030f03430314, 0x3a1039203830374, 0x342032f031c03b0, 0x382037303640355, 0x3f703af03a00391, 0xe600e200d900a3, 0xf600f200ee00ea, 0xb100ac00a700fa, 0xc500c000bb00b6, 0xdd00d400cf00ca, 0x368035903460319, 0x3a4039503860377, 0x3450332031f03b3, 0x385037603670358, 0x3fa03b203a30394, 0x172016e016a0166, 0x182017e017a0176, 0x192018e018a0186, 0x1a2019e019a0196, 0x1b201ae01aa01a6, 0x1c201be01ba01b6, 0x5d5056801ca01c6, 0x67e062905e605e3, 0x60706cf06ac0687, 0x77e07230734071a, 0x82c083b06af07a4, 0x6b2056b088d085e, 0x60a095a06820770, 0xa2e09a009370692, 0xb0d06020ad90a7d, 0xa280b79073e0ae0, 0xcd307870b3b05d3, 0xba308400a1105d8, 0xb410de1086a0a24, 0x30506110695, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1abc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x552054f0542, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6b2073e, 0x0, 0x0, 0x0, 0x1b2f000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x227c000000000000, 0x0, 0x0, 0x0, 0x0, 0x26b0000000000000, 0x0, 0x0, 0x0, 0x1f091f011efb1ee9, 0x1f1b1f171f131f0d, 0x1f5f1f571f4b1f21, 0x1f871f771f6f1f67, 0x1fb91fa51f8b1f89, 0x1fcd1fc71fc51fc1, 0x1feb1fe91fdd1fdb, 0x204f20451ff71fef, 0x207f207d2079206f, 0x20b420ae20982087, 0x20ce20cc20ca20c4, 0x20f820f220dc20da, 0x210f2108210020fc, 0x212f212b21292113, 0x2141213921352131, 0x21972195218b214f, 0x21e521e321d921d7, 0x32521f121ed21e9, 0x2260222303292211, 0x227a2274226e2266, 0x228422822280227e, 0x230c230622e22286, 0x231623122310230e, 0x2334233023222318, 0x235c235a23562354, 0x2376237423622360, 0x238a238823862384, 0x23aa23a823a62394, 0x23ee23de23dc23bc, 0x241c240a23fa23f6, 0x2452244c2442243e, 0x245e245c245a2456, 0x247e247a246c246a, 0x248e248a24842482, 0x2498249624922490, 0x250e250c24f224e8, 0x2530252c25282512, 0x2558255425522534, 0x25742572255c255a, 0x2592258425822578, 0x25ba25aa25982596, 0x25de25c825c425c2, 0x260025fa25e825e0, 0x261a261826142608, 0x26262624261e261c, 0x263c263a2638262a, 0x2658264e264a2648, 0x26622660265c265a, 0x2670266826662664, 0x26862684267e267c, 0x2690268e268a2688, 0x26a0269c26982692, 0x26aa26a826a626a2, 0x26b226ae, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b4900000000, 0x1fd11fcf1fcd, 0x0, 0x0, 0x0, 0x0, 0x1b8100001b7e, 0x1b8700001b84, 0x1b8d00001b8a, 0x1b9300001b90, 0x1b9900001b96, 0x1b9f00001b9c, 0x1ba500001ba20000, 0x1ba80000, 0x0, 0x1bb100001bae1bab, 0x1bba1bb700001bb4, 0x1bc01bbd0000, 0x1bc91bc6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b7b, 0x87000000000000, 0x1bcc1bd30000008a, 0x0, 0x0, 0x0, 0x1c4300001c26, 0x1c9200001bf6, 0x1caf00001c9b, 0x1cca00001cbf, 0x1cdc00001ccf, 0x1ceb00001ce1, 0x1cf700001cf20000, 0x1c100000, 0x0, 0x1d3b00001d261d1d, 0x1d611d5700001d42, 0x1d7e1d760000, 0x1caa1da1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e44000000001c01, 0x1e571e521e4d, 0x1ca11e6000000000, 0x0, 0x0, 0x0, 0x0, 0x1a10194919440000, 0x19501a141a12194b, 0x1a181a1619571955, 0x1a201a1e1a1c1a1a, 0x19661961195c19a6, 0x196f196d196819b0, 0x198e198319811977, 0x1947199d19981993, 0x19de19dc19da19d8, 0x198c19e419e219e0, 0x19ee19ec19ea19e8, 0x19f619f419f21975, 0x19fe197f19fa19f8, 0x1a2219a419a219d4, 0x1a2a1a281a261a24, 0x1a3019a81a2e1a2c, 0x19ae19ac19aa1a32, 0x19b819b619b419b2, 0x19c019be19bc19ba, 0x19c819c619c419c2, 0x1a361a3419cc19ca, 0x1a0019d219d019ce, 0x1a081a061a041a02, 0x1a0e1a0c1a0a, 0x1f171ee900000000, 0x1efd1ef120471eef, 0x1ef71f0d23641ef3, 0x1f212051208c1eeb, 0x1e901e001d701ce, 0x20d020401fb01f2, 0x245023c02330225, 0x1db01d20257024e, 0x1ff01f601ed01e4, 0x237022902110208, 0x25b025202490240, 0x21e0216022e, 0x2a0026802700260, 0x284026402880274, 0x2c402b00290026c, 0x2a402ec02b802c0, 0x2d002b402bc02ac, 0x2d402e402c80298, 0x2a8029c0278028c, 0x29402e8027c02cc, 0x2e002dc028002d8, 0x23fe21e321112021, 0x0, 0x0, 0x41c04110406082e, 0x440043904320427, 0x475046e044e0447, 0x4850482047f047c, 0x19571950194b1944, 0x196f19681961195c, 0x1993198e19831977, 0x194d1946199d1998, 0x1963195e19591952, 0x198519791971196a, 0x199f199a19951990, 0x1974197c1988, 0x20471eef1f171ee9, 0x1f5f1eed1f611f19, 0x22e203291fcd1f0f, 0x204f25c822232286, 0x2240221b223b0325, 0x23ca255e231c2007, 0x2098236823e21fab, 0x22961fdf1f4925a2, 0x208a1f731f2b262c, 0x20fa1ef31efd1ef1, 0x20b220b81fc92001, 0x1fd725681f292390, 0x48e048b04882083, 0x4b704b404b10491, 0x4c304c004bd04ba, 0x4e404cc04c904c6, 0x4d604a3034e033b, 0x5290518050304f2, 0x34d033a0327053a, 0x7390a7f0a8206b4, 0x1c0a1bff1bf11bd8, 0x1c731c411c241c1a, 0x1cbd1cad1c991c90, 0x1cdf1cda1ccd1c1e, 0x1bde1cf51cf01bfb, 0x1c8e1d111d0f1ca6, 0x1d551d391d1b1d0d, 0x1ddc1c311d9f1d74, 0x1e041e001def1c22, 0x1c351e1b1e191e11, 0x1e421c5d1e341bed, 0x1e551e501e4b, 0x1beb1be51be01bda, 0x1c0c1c041bf91bf3, 0x1c331c201c1c1c13, 0x1c2e1c291c3c1c37, 0x1c501c571c4b1c46, 0x1c6d1c661c5f1c5c, 0x1c8b1c841c7d1c61, 0x1cb21ca81ca41c95, 0x1cd61cd21cc21cb7, 0x1c811d031cfa1ce4, 0x1d291d351d171d0c, 0x1d4c1d451d201d30, 0x1d6b1d641d3e1d51, 0x1d811d951d701d5a, 0x1d8f1d8a1d9b1d85, 0x1db81da41dac1d79, 0x1dc51dbf1dbb1db2, 0x1dd61dd21dce1dca, 0x1df11de61de31dde, 0x1e0b1e061c681df5, 0x1e291e241e1f1e13, 0x1c6f1e391e361e2e, 0x3610352033f0311, 0x39d038e037f0370, 0x33e032b03bb03ac, 0x37e036f03600351, 0x3ba03ab039c038d, 0x4230418040d0402, 0x56a0a530b0f042e, 0xa590ce60c580a0f, 0x210a06db0a600a5c, 0x223d21f920892200, 0xbea11b40c260cda, 0x689075b071c0b7b, 0xc290cdd0b8c0a26, 0x6010bf611c011b7, 0x68c07640b7e068d, 0xa560bfd11c30893, 0x11c60c350aec0b94, 0xc030b970a300c00, 0xc070b9a0a340a33, 0xc1b0b9e0a380a37, 0x7680b8206910c1f, 0xd000cfa0cf60690, 0xc0f11c90c380ce9, 0xbed11ba0c2c0ce0, 0xc2f0ce3076c0b86, 0x76f0b890bf011bd, 0x5d70999077b0bb4, 0x5e805ff0a2d0a2a, 0x6ae0b1306940a50, 0xba20722071f0b3a, 0xbc60bc20bbf0bbc, 0x8200c0b0bf90bf3, 0xd25082b08230cd5, 0x5d1092a09360869, 0x36c035d034a0337, 0x3a80399038a037b, 0x3490336032303b7, 0x389037a036b035c, 0x3fe03b603a70398, 0x42a041f04140409, 0x44a0443043c0435, 0xaf4047804710451, 0x0, 0x0, 0x0, 0x0, 0x26b4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe730e6b, 0x0, 0x256a258422132556, 0x26ae1ff91eff22c6, 0x202b25c8209226ae, 0x244a238221872090, 0x25a8251e250424e6, 0x233c22f0229a2254, 0x1f11265225bc24ca, 0x24e42302225e1fe3, 0x24dc22d620e6267a, 0x250a247821a12526, 0x232822a6221d211f, 0x1fb31f7d1f3125ae, 0x23922300225a21d5, 0x257e24ec24e02456, 0x23b02678266a2610, 0x25d624bc242c23d0, 0x212d206d2540267e, 0x23b4231a24682408, 0x20d4206b260c2566, 0x242422ca22b02256, 0x246e1fb125ec2438, 0x242e23e61f811f83, 0x21a3254e25f024c8, 0x20be1f0525462254, 0x1fc3237223322159, 0x1ef5214b1f3923b8, 0x1fed242221e12290, 0x253824cc23982063, 0x21ab228c25962276, 0x1f1f237021bb24a8, 0x241822441f7f1f5d, 0x1fb725c6253e2494, 0x21ef212720982011, 0x265625e423ba22d8, 0x220f1fa5268c2680, 0x217b210d2590226c, 0x22f622d021d12189, 0x2464243223e0234e, 0x25d8259c24d02588, 0x22ee201b1fa71f91, 0x2155211d25382514, 0x232c2406227221b7, 0x20f020be20491f27, 0x24502348233a215b, 0x2612260a25ca2460, 0x25c023da1f332630, 0x1f451f15216725fe, 0x225421e720d020c0, 0x25a624d6238022fc, 0x1fa325ea220726aa, 0x22be22a22233222d, 0x242023ae236e2340, 0x25f221911f612636, 0x258a22b220e21f3d, 0x23322237216b2143, 0x20d820091f9525f6, 0x2294224a222521fc, 0x251624462378233e, 0x1fcb260425c4251c, 0x235022fe200b22c0, 0x2682266e25f824de, 0x23f6247c22ae2231, 0x22ea2326240e23fc, 0x1f9724b01f23254c, 0x241421a521151f8f, 0x258e220d229c20b6, 0x2123252c25ee250e, 0x20371f4d, 0x220500002061, 0x238c232a1f850000, 0x23d823ce23cc23be, 0x245224102616, 0x2544000024e2, 0x25b4259e0000, 0x2642264000000000, 0x25fc25b026762644, 0x1faf1f511f471f35, 0x203d202f1fd51fb5, 0x20d62065205f2041, 0x21792175216120da, 0x220921f321db2185, 0x22ce22b622a82246, 0x23b22342230822f8, 0x23c623c223c42240, 0x23d623d423ca23c8, 0x2432240023f223e8, 0x24582444243a2436, 0x24ce249a249a2480, 0x254a2548252e2522, 0x259e259a256e256c, 0x215d263426282606, 0x248c274b, 0x1f2f1f5b1f7b1ef9, 0x1fbb1fad1f651f4f, 0x203b202d2025202f, 0x2094208e20692061, 0x2125212120aa20a2, 0x21712165214d213d, 0x2185217321792169, 0x21c921c521bf2193, 0x221f221d220521dd, 0x22a22276226e2229, 0x22dc22ce22c422c8, 0x2324230a23a422f8, 0x236a2358234a232a, 0x238e238c237e237c, 0x23b6239e23a02396, 0x2428240c240023f4, 0x24b2245824402432, 0x252a2524250024c6, 0x253c2544253a252e, 0x254a254225462548, 0x25a4258c256e2550, 0x260625f425ce25be, 0x262e262826202616, 0x272126ae265e2634, 0x1ea11e8d2733271f, 0x27a9277927671ea3, 0x26ac26a4, 0x0, 0xade0ae30adf0adb, 0xd280d280ae2, 0x0, 0x0, 0x134e000000000000, 0x134b135113481345, 0x0, 0x13d2000013850000, 0x1374136f135413a6, 0x13b7139b1360138e, 0x13ca13c702f413cd, 0x1359135613c313bf, 0x1371136c1364135c, 0x137f137c1376, 0x1390138b13881382, 0x139d00001398, 0x13a8000013a313a0, 0x13b413b1000013ab, 0x137913cf13bc13b9, 0x135f13ae13931367, 0x181e181e18181818, 0x18201820181e181e, 0x1824182418201820, 0x181c181c18241824, 0x18221822181c181c, 0x181a181a18221822, 0x183c183c181a181a, 0x183e183e183c183c, 0x18281828183e183e, 0x1826182618281828, 0x182a182a18261826, 0x182c182c182a182a, 0x18321832182c182c, 0x1834183418301830, 0x18381838182e182e, 0x1840184018361836, 0x1844184418401840, 0x1848184818441844, 0x1846184618481848, 0x184a184a18461846, 0x184c184c184c184c, 0x18501850186d186d, 0x184e184e18501850, 0x15911591184e184e, 0x186a186a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1842000000000000, 0x1803184218421842, 0x180717ff17ff1803, 0x18621862185b1807, 0x1860186018551855, 0x180b180b180b180b, 0x17cd17cd14151415, 0x17f117f1180d180d, 0x17fd17fd18011801, 0x1809180918051805, 0x17f517f517f51809, 0x1864186418641864, 0x17f517e517d517d1, 0x13fe13f713f417f9, 0x141e14171414140b, 0x146a144d1438142d, 0x1484147b1472146d, 0x14311422148c1487, 0x143c14d414d11435, 0x151a150c150514fa, 0x15a515a215931562, 0x15c815c515ba15b0, 0x1607157515e415df, 0x16451642163f160a, 0x165b16561653164c, 0x1679167416711662, 0x16851682167f167c, 0x16aa169616931688, 0x1579158c16c816b9, 0x14591455145116e0, 0x172d1461145d1526, 0x17691758174f1740, 0x177f17741771176c, 0x17aa17a3179c1782, 0x14e417c717c417b3, 0x64005d179714ee, 0x8000790072006b, 0x17e917e517e117dd, 0x140813db17f917f5, 0x14171414140e140b, 0x1464144d144a1447, 0x14781475146d146a, 0x14871484147e147b, 0x1674167116561653, 0x1693168816851679, 0x16e01579158c1696, 0x17551752152616e5, 0x176c176917631758, 0x17b317b017ad1797, 0x17d117c717c417be, 0x17ed17e517d917d5, 0x140b13fe13f713f4, 0x1438142d141e1411, 0x148c147b1467144d, 0x14d1143514311422, 0x150c150514fa143c, 0x1593156d1562151a, 0x15ba15b015a515a2, 0x157515e415df15c5, 0x1642163f160a1607, 0x1662165b164c1645, 0x16851682167f167c, 0x16c816b916aa1688, 0x1455145113e0158c, 0x1740172d15261459, 0x177117661758174f, 0x17a3179c17851774, 0x17e515ed17b317aa, 0x144d1411140b17ed, 0x151a1481147b1467, 0x16851557154c1529, 0x17661758158c1688, 0x162c162515ed17b3, 0x15ff15da15d71633, 0x152c161c16191602, 0x1490155d155a152f, 0x1440142a142613fb, 0x15bd159d159a1402, 0x1546153b153415c0, 0x157015171549154c, 0x15ff15da15d715b7, 0x152c161c16191602, 0x1490155d155a152f, 0x1440142a142613fb, 0x15bd159d159a1402, 0x1546153b153415c0, 0x157015171549154c, 0x1546153b153415b7, 0x15c815571529154c, 0x1534150c150514fa, 0x15df15c81546153b, 0x13e313e3, 0x0, 0x0, 0x0, 0x0, 0x1434143014301421, 0x145814541450143b, 0x14c114c514a314a3, 0x1521150114fd1508, 0x15251525151d1521, 0x153e159615651565, 0x154f154f1537153e, 0x15b315a815531553, 0x15cf15cb15cb15b3, 0x15f315f315e715d3, 0x16111615160d15f7, 0x1669166516481648, 0x16ad16c016c416bc, 0x16d216cb16cb16ad, 0x170b170216fe16d2, 0x1716171216f316eb, 0x177716ef00000000, 0x173417471743177b, 0x175b175f17381734, 0x1429140117b617b6, 0x1460143f14431425, 0x14a7148f14ab145c, 0x15ac15421569150f, 0x179f17a616d616b5, 0x174b166d172117ba, 0x168f15fb16bc1665, 0x168b16b1171a1730, 0x14ba1493173016b1, 0x168b13fa164f16f7, 0x173c1513159615e7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13d913de165e158f, 0x15eb14e915731706, 0x1497157c1578158a, 0x14f1, 0x0, 0x0, 0x0, 0x0, 0x5401b331b3102f6, 0x1b770093008d0546, 0x2ff1b79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9931a6b1a6d02fc, 0xe3b00a500a10993, 0x1b451b4f1b4b0e3f, 0x1b351b3b1b391b47, 0x1b411b3f1b3d1b37, 0x98b000000001b43, 0xc000c000c098f, 0x99309930993000c, 0x2fa1b3102f6, 0x8d009305400546, 0xe3b00a500a11a6d, 0x971b4f1b4b0e3f, 0x2f802f402f2009d, 0x54405590548, 0x566009b0099098d, 0x0, 0x5a161f0057, 0x1622006800000061, 0x163000761629006f, 0x163a00841637007d, 0x13e913e613e613d5, 0x13ec178f178f13e9, 0x17ca17ca17ca13ec, 0x13f213d713d717ca, 0x141a13f213f213f2, 0x141c141c141c141a, 0x147014701470141c, 0x13f513f513f51470, 0x13f813f813f813f5, 0x13ff13ff13ff13f8, 0x14e214e014e013ff, 0x140913dc13dc14e2, 0x14f814f814f81409, 0x15321532153214f8, 0x1560156015601532, 0x15a015a015a01560, 0x15c315c315c315a0, 0x15dd15dd15dd15c3, 0x15e215e215e215dd, 0x16051605160515e2, 0x163d163d163d1605, 0x165916591659163d, 0x1677167716771659, 0x14ec14ec14ec1677, 0x140c140c140c14ec, 0x140f140f140f140c, 0x13e113e113e1140f, 0x14151788178813e1, 0x13fc13fc13fc1415, 0x16a2169e169e13fc, 0x169b16a616a616a2, 0x169b, 0x970095008d0000, 0x9f009d009b0099, 0x2f402f200a500a1, 0x30302fa02f802f6, 0x30f034303140305, 0x392038303740365, 0x546054003b003a1, 0x93055905440548, 0x5e305d505680566, 0x687067e062905e6, 0x71a060706cf06ac, 0x7a4077e07230734, 0x85e082c083b06af, 0x77006b2056b088d, 0x98b060a095a0682, 0x9930991098f098d, 0x9a0093706920995, 0x6020ad90a7d0a2e, 0xb79073e0ae00b0d, 0x7870b3b05d30a28, 0x8400a1105d80cd3, 0xde1086a0a240ba3, 0xe3b061106950b41, 0x1b280e410e3f0e3d, 0x1b3f1b3d1b331b2a, 0x1bd61e551e5c1b31, 0x1c181c081bfd1bef, 0x1cee1e171e0f1e02, 0x1bff1bf11bd81c16, 0x1c411c241c1a1c0a, 0x1cad1c991c901c73, 0x1cda1ccd1c1e1cbd, 0x1cf51cf01bfb1cdf, 0x1d111d0f1ca61bde, 0x1d391d1b1d0d1c8e, 0x1c311d9f1d741d55, 0x1e001def1c221ddc, 0x1e1b1e191e111e04, 0x1c5d1e341bed1c35, 0x8b00881c061e42, 0x1a101949194419d4, 0x19501a141a12194b, 0x1a181a1619571955, 0x1a201a1e1a1c1a1a, 0x19661961195c19a6, 0x196f196d196819b0, 0x198e198319811977, 0x199d19981993, 0x19d8194700000000, 0x19e019de19dc19da, 0x19e419e200000000, 0x19ec19ea19e8198c, 0x197519ee00000000, 0x19f819f619f419f2, 0x197f19fa00000000, 0x19fe, 0x90e4b0e450e43, 0x1a820e470e49, 0x1a8b1a891a841b22, 0x1b261b241a90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b600000000, 0x26b9, 0x0, 0x0, 0x26bc000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c226bf00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c826c500000000, 0x26d726d326cf26cb, 0x26db, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26df000000000000, 0x26e626ed26e226ea, 0x26f1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae000000602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e300000568, 0x68700000000, 0x71a06070000, 0x6af07a4077e0000, 0x88d085e0000083b, 0x682077006b2056b, 0x9370692060a095a, 0xad900000a2e09a0, 0x73e0ae00b0d0000, 0xb3b05d30a280b79, 0xa1105d80cd30000, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e6000005d50568, 0x687067e0629, 0x734071a06070000, 0x6af07a4077e0723, 0x88d085e0000083b, 0x682077006b2056b, 0x93706920000095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e6000005d50568, 0x687067e0629, 0x734071a060706cf, 0x7a400000723, 0x88d085e00000000, 0x682077006b2056b, 0x93706920000095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x61106950b410de1, 0xe800e6f, 0xf3c0f3a0f380ee3, 0xfad0f5e0f5c0f3e, 0xfe20fe00fde0faf, 0x10060fe80fe60fe4, 0x100f100d0fad1008, 0x1035103310311011, 0x10ea10861aa3077c, 0x110e10f010ee10ec, 0x11ae1170116e1110, 0x11ce11cc11b211b0, 0x11f811f011ee11d0, 0x123c11fe11fc11fa, 0x1a9e12421240123e, 0x123c11ae116e10f0, 0xf380ee311ee11f0, 0xf5c0f3e0f3c0f3a, 0xfde0faf0fad0f5e, 0xfe60fe40fe20fe0, 0xfad100810060fe8, 0x10311011100f100d, 0x1aa3077c10351033, 0x10ee10ec10ea1086, 0x116e1110110e10f0, 0x11b211b011ae1170, 0x11ee11d011ce11cc, 0x11fc11fa11f811f0, 0x1240123e123c11fe, 0x116e10f01a9e1242, 0x11ee11f0123c11ae, 0xf3c0f3a0f380ee3, 0xfad0f5e0f5c0f3e, 0xfe20fe00fde0faf, 0x10060fe80fe60fe4, 0x100f100d0fad1008, 0x1035103310311011, 0x10ea10861aa3077c, 0x110e10f010ee10ec, 0x11ae1170116e1110, 0x11ce11cc11b211b0, 0x11f811f011ee11d0, 0x123c11fe11fc11fa, 0x1a9e12421240123e, 0x123c11ae116e10f0, 0xf380ee311ee11f0, 0xf5c0f3e0f3c0f3a, 0xfde0faf0fad0f5e, 0xfe60fe40fe20fe0, 0xfad100810060fe8, 0x10311011100f100d, 0x1aa3077c10351033, 0x10ee10ec10ea1086, 0x116e1110110e10f0, 0x11b211b011ae1170, 0x11ee11d011ce11cc, 0x11fc11fa11f811f0, 0x1240123e123c11fe, 0x116e10f01a9e1242, 0x11ee11f0123c11ae, 0xf3c0f3a0f380ee3, 0xfad0f5e0f5c0f3e, 0xfe20fe00fde0faf, 0x10060fe80fe60fe4, 0x100f100d0fad1008, 0x1035103310311011, 0x10ea10861aa3077c, 0x110e10f010ee10ec, 0x11ae1170116e1110, 0x11ce11cc11b211b0, 0x11f811f011ee11d0, 0x123c11fe11fc11fa, 0x1a9e12421240123e, 0x123c11ae116e10f0, 0x12a212a011ee11f0, 0x314030500000000, 0x3740365030f0343, 0x3b003a103920383, 0x30f034303140305, 0x392038303740365, 0x314030503b003a1, 0x3740365030f0343, 0x3b003a103920383, 0x30f034303140305, 0x392038303740365, 0x314030503b003a1, 0x3740365030f0343, 0x3b003a103920383, 0x14e013f513f213d7, 0x13f8140917880000, 0x14ec167713fc15c3, 0x15e214f8140f140c, 0x13dc16591560163d, 0x13ff1470141c1532, 0x160515dd15a014e2, 0x1816183a184a1814, 0x13f513f20000, 0x13f80000000013e1, 0x14ec167713fc0000, 0x15e214f8140f140c, 0x16591560163d, 0x13ff1470141c1532, 0x1605000015a00000, 0x0, 0x13f500000000, 0x13f8000000000000, 0x14ec000013fc0000, 0x15e214f8140f0000, 0x165915600000, 0x13ff000000001532, 0x1605000015a00000, 0x18160000184a0000, 0x13f513f20000, 0x13f80000000013e1, 0x167713fc15c3, 0x15e214f8140f140c, 0x16591560163d, 0x13ff1470141c1532, 0x160515dd15a00000, 0x183a00001814, 0x14e013f513f213d7, 0x13f81409178813e1, 0x14ec000013fc15c3, 0x15e214f8140f140c, 0x13dc16591560163d, 0x13ff1470141c1532, 0x160515dd15a014e2, 0x0, 0x14e013f513f20000, 0x13f8140917880000, 0x14ec000013fc15c3, 0x15e214f8140f140c, 0x13dc16591560163d, 0x13ff1470141c1532, 0x160515dd15a014e2, 0x0, 0x3f103160307030a, 0x4fa04de04ab0468, 0x5310520050b, 0x0, 0x10a0106010200fe, 0x11a01160112010e, 0x12a01260122011e, 0x13a01360132012e, 0x14a01460142013e, 0x15a01560152014e, 0x5e31b4d0162015e, 0x93305e5082c, 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, 0x76c06b1060a095a, 0x930082708660860, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x761075e00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x1cad1c9e1bc3, 0x0, 0x0, 0x0, 0x1cf71ff320b02197, 0x208c253220811f17, 0x21e722f221fe1f1d, 0x21eb1f6921451f9d, 0x2560235c24261f93, 0x219d22cc200f2073, 0x25a01eef1ee921b3, 0x21ad20011efd20fa, 0x23f023d221992574, 0x329221b22bc2005, 0x20351f9f2366, 0x0, 0x1b5d1b551b511b69, 0x1b591b711b611b6d, 0x1b65, 0x0, 0x1ffd2147, 0x0, 0x0, 0x0, 0x26f51f0b1f031f07, 0x1f3b1f371f351f2d, 0x1f431f471f411f3f, 0x1f531f5126fd1e63, 0x1e6526f71f631f55, 0x1f7126fb1f691f59, 0x1f7b1f791f251f75, 0x1e691f8d1f8927b9, 0x1fa11f9f1f9b1f99, 0x1fb51faf1fad1e6b, 0x1fc31fbf1fbd1fbb, 0x1fe11fd91fd51fd3, 0x1fe71fe71fe71fe5, 0x1ff51ff122e42703, 0x20031fff1ffb2705, 0x20152013200d2017, 0x2023201f201d2019, 0x202d202920292027, 0x204b203920332031, 0x2043203f204d203d, 0x2057205520711f8f, 0x205b205d20532059, 0x2077207527072067, 0x209620852081207b, 0x209e209c270b2709, 0x1e6d20a4209a20a0, 0x20ac20ac20a81e6f, 0x20be20bc20ba270d, 0x20c820c6270f20c2, 0x20d21e7120cc2137, 0x271320de20e020da, 0x20e820ea271520e4, 0x1e7320f620f420ec, 0x21062104210220fe, 0x21171e7727171e75, 0x27cd211f211b2119, 0x2486271b271b212b, 0x27291e7921332133, 0x1e7b213f213b277d, 0x2157215321512149, 0x21611e7d1e7f215f, 0x216f216d2163271d, 0x21792177216f2171, 0x2183217f217d2181, 0x218f210b21872185, 0x21b121a7219f219b, 0x21b521a921af2723, 0x21c7272521c321b9, 0x21cb1e8121bd21c1, 0x1e8321cd21d321cf, 0x21f5272721df21db, 0x22091e8922032215, 0x1f6d1f6b1e851e87, 0x1ebb2470220b2217, 0x222b2221221f221d, 0x22351e8b27312227, 0x273522462242222f, 0x1e8d224c22392248, 0x225822522250224e, 0x22621e8f225c2737, 0x226a1e9122642739, 0x273b227822762270, 0x273f2288273d2711, 0x2298228a2292228e, 0x22a422a222a822a0, 0x229e274122ac22aa, 0x22c41e9322ba22b8, 0x22d222b4274322c2, 0x22de22d427472745, 0x22e01e9522da22dc, 0x26f922ec22e622e8, 0x274d22fa274922f4, 0x274f2314230a2304, 0x275327512320231e, 0x23381e972336232e, 0x234623441e991e99, 0x1e9b2352234c234a, 0x2757236c2755235e, 0x2759237a27192372, 0x1e9f1e9d275d275b, 0x2763275f27612396, 0x239c239c239a2765, 0x1ea523a21ea323a0, 0x23b023ac27691ea7, 0x23c8276b1ea923b6, 0x23e423d8276f276d, 0x23ec23ea23e81eab, 0x23f8277327732771, 0x2404240227751ead, 0x1eb1241227771eaf, 0x277b241e2416241a, 0x243424301eb3242a, 0x2781277f1eb5243c, 0x2785244827831eb7, 0x278724582454244e, 0x2466278b24622789, 0x247424721eb9272b, 0x278d20a624761ebd, 0x2486272f272d278f, 0x249e1ebf25942488, 0x24a21fa924a0249c, 0x279124aa24a624a4, 0x24b824b624ac24a8, 0x24ce24c424ba24ae, 0x24c224c024be24b4, 0x1ec1279527972793, 0x279f24d824d424d2, 0x1ec51ec3279924da, 0x24ea1ec7279d279b, 0x24f624f024ee24ec, 0x250024f824fa24f4, 0x1ec9250224fe24fc, 0x25101ecb25082506, 0x251a251827a12512, 0x27a31e6725201ecd, 0x25361ed11ecf27a5, 0x27a7255825502542, 0x2576257025642562, 0x257a257c26ff27ab, 0x258c258627012580, 0x25b225ac27af27ad, 0x25cc25b827b125b6, 0x25da25d025d425d2, 0x1ed325e227b325dc, 0x26021ed527b525e6, 0x27bb27b7260e20ee, 0x27bd26221ed91ed7, 0x262e262e27bf1edb, 0x1edd263e27c12632, 0x26542650264c2646, 0x266c265e27c31edf, 0x26741ee31ee12672, 0x27c927c71ee527c5, 0x26901ee7268627cb, 0x269e269a26962694, 0x27cf26a2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//12288 bytes -enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x120], [ 0x100, 0x400, 0x1380], [ 0x302020202020100, 0x205020202020204, 0x602020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x1000000000000, 0x5000400030002, 0x6, 0x9000800070000, 0xc0000000b000a, 0x0, 0xe00000000000d, 0x0, 0x0, 0x1100000010000f, 0x130012, 0x16001500140000, 0x18000000170000, 0x1a000000190000, 0x0, 0x1c001b0000, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f001e, 0x0, 0x0, 0x23002200210020, 0x27002600250024, 0x28, 0x2b002a00000029, 0x2f002e002d002c, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x34003300320000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38003700360035, 0x3c003b003a0039, 0x3e003d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f00000000, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x43004200410000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x47004600450044, 0x4b004a00490048, 0x4c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x250012000f000c, 0x850000004f0045, 0xcb00a400a1009e, 0x13301240121011e, 0x1a0019d01880000, 0x1da01b601a3, 0x2730270026d0000, 0x2f30287, 0x33803250322031f, 0x398000003620358, 0x3de03b703b403b1, 0x446043a04370434, 0x4b404b1049c0000, 0x4ee04ca04b7, 0x58a058705840000, 0x61c0000060d059e, 0x33e002b033b0028, 0x38c00790380006d, 0x392007f038f007c, 0x3a2008f03950082, 0x3cd00ba00000000, 0x3db00c803d800c5, 0x3e400d103fb00e8, 0x41000fd040a00f7, 0x419010604130100, 0x41c0109, 0x440012a043d0127, 0x45c01490443012d, 0x130, 0x471015d0462014f, 0x170047701630000, 0x47a01660484, 0x185000000000000, 0x18e04a801940499, 0x4a2, 0x4e401d004d901c5, 0x4f801e4, 0x5450231052f021b, 0x54b023705350221, 0x56902550552023e, 0x57b026405580244, 0x572025b, 0x594027d058d0276, 0x5b4029d059b0284, 0x5e002c905b702a0, 0x61002f605f502de, 0x3110628030b0302, 0x6310314062e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50401f0, 0x0, 0x0, 0x2ac000000000000, 0x5c3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13d036900560000, 0x2a304fb01e70450, 0x28e05a9029205ba, 0x28a05ad029605a5, 0x35b0048000005a1, 0x653064a03540041, 0x416010300000000, 0x522020e046b0157, 0x65f065c05250211, 0x465, 0x40700f4, 0x365005204960182, 0x656064d06500647, 0x36f005c036c0059, 0x3ea00d703e700d4, 0x456014304530140, 0x50101ed04fe01ea, 0x53b022705380224, 0x5c002a905bd02a6, 0x578026105660252, 0x425011200000000, 0x0, 0x351003e00000000, 0x4f101dd03f400e1, 0x4e701d304d101bd, 0x61602fc04ea01d6, 0x0, 0x0, 0x0, 0x66b00000010000d, 0x137, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x662, 0x0, 0x100000000, 0x0, 0x6450670063d0000, 0x72c06df06c3, 0x798077800000759, 0x8d1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x781073500000000, 0x8c10867084707e9, 0x92f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92808ca00000000, 0x95f091f08fd, 0x9b4000000000000, 0x9b7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9c3000009cc09c6, 0x9ba000000000000, 0x0, 0x9ed09d809e4, 0x0, 0x0, 0x9de0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0500000a0e0a08, 0xa41000000000000, 0x0, 0xa2f0a1a0a26, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa470a4400000000, 0x0, 0x0, 0xa1109cf0000, 0x0, 0x0, 0x0, 0xa0209c009ff09bd, 0xa0b09c900000000, 0xa4d0a4a00000000, 0xa1709d50a1409d2, 0xa1d09db00000000, 0xa2909e70a2309e1, 0xa530a5000000000, 0xa2c09ea0a3e09fc, 0xa3509f30a3209f0, 0xa3809f6, 0xa3b09f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac10abe00000000, 0xaca0ac40ac7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xacd00000ad3, 0x0, 0x0, 0x0, 0xad0000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae80000, 0x0, 0xaf10000, 0xaf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xadf0adc0ad90ad6, 0xaee0aeb0ae50ae2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb00000000000000, 0xb03, 0x0, 0x0, 0x0, 0xafd00000afa0af7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb12000000000000, 0xb1500000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb0c0b090b060000, 0xb0f00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb1e000000000b21, 0xb24, 0x0, 0x0, 0x0, 0xb1b0b18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb300b2a00000000, 0xb2d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb36, 0xb40000000000000, 0xb3c0b3900000b43, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4c0b4600000000, 0xb49, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4f00000000, 0xb590b550b52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb5f000000000000, 0x0, 0x0, 0xb620000, 0xb6500000000, 0xb68000000000000, 0x0, 0xb6b, 0x0, 0x0, 0xb5c0000, 0x0, 0xb6e000000000000, 0xb890b710000, 0xb8c, 0x0, 0xb740000, 0x0, 0x0, 0x0, 0xb7a000000000000, 0x0, 0x0, 0xb7d0000, 0xb8000000000, 0xb83000000000000, 0x0, 0xb86, 0x0, 0x0, 0xb770000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8f00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb9200000000, 0xb9800000b95, 0xb9e00000b9b, 0xba100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xba4000000000000, 0xba70000, 0xbb000000bad0baa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3830070037d006a, 0x389007603860073, 0x39f008c039b0088, 0x3ae009b03a50092, 0x3ab009803a80095, 0x3d400c103d000bd, 0x40100ee03fe00eb, 0x40400f103f700e4, 0x41f010c040d00fa, 0x422010f04280115, 0x42e011b042b0118, 0x4490136045f014c, 0x46e015a04680154, 0x47d016904740160, 0x48a01760480016c, 0x48d017904870173, 0x493017f0490017c, 0x4a50191049f018b, 0x4ab019704ae019a, 0x4d501c104cd01b9, 0x4e001cc04dc01c8, 0x52c021805290215, 0x53e022a0532021e, 0x54802340541022d, 0x55f024b05550241, 0x55b0247054e023a, 0x56c02580562024e, 0x581026a0575025e, 0x5dd02c6057e0267, 0x5e302cc05e602cf, 0x597028005900279, 0x5ec02d505e902d2, 0x5f202db05ef02d8, 0x5f802e105fb02e4, 0x60402ea060102e7, 0x61902ff060702ed, 0x6340317062b030e, 0x56f04310637031a, 0x6590000062205fe, 0x0, 0x35f004c0372005f, 0x3280015032c0019, 0x330001d03340021, 0x345003203750062, 0x34d003a0341002e, 0x379006603490036, 0x3e100ce03ed00da, 0x3be00ab03ca00b7, 0x3c600b303ba00a7, 0x3f000dd03c200af, 0x4590146044d013a, 0x4f501e1051b0207, 0x4ba01a604be01aa, 0x4c201ae04c601b2, 0x50b01f7051e020a, 0x51301ff050701f3, 0x5170203050f01fb, 0x5b1029a05da02c3, 0x5c602af05ca02b3, 0x5ce02b705d202bb, 0x60a02f005d602bf, 0x61f030506250308, 0x61302f9, 0x0, 0x81b07f9081807f6, 0x82d080b08240802, 0x69e067c069b0679, 0x6b0068e06a70685, 0x858084d0855084a, 0x85c0851, 0x6d406c906d106c6, 0x6d806cd, 0x89308710890086e, 0x8a50883089c087a, 0x70706e5070406e2, 0x71906f7071006ee, 0x8eb08dc08e808d9, 0x8f308e408ef08e0, 0x74a073b07470738, 0x7520743074e073f, 0x90e0903090b0900, 0x9120907, 0x76a075f0767075c, 0x76e0763, 0x949093a09460937, 0x9510942094d093e, 0x787000007840000, 0x78f0000078b0000, 0x98b096909880966, 0x99d097b09940972, 0x7c0079e07bd079b, 0x7d207b007c907a7, 0x847084407e907e2, 0x8c108be08670860, 0x91f091c08fd08fa, 0x95f0958, 0x81f07fd08360814, 0x831080f08280806, 0x6a2068006b90697, 0x6b4069206ab0689, 0x897087508ae088c, 0x8a9088708a0087e, 0x70b06e907220700, 0x71d06fb071406f2, 0x98f096d09a60984, 0x9a1097f09980976, 0x7c407a207db07b9, 0x7d607b407cd07ab, 0x84107e507f007f3, 0x83d083a000007ec, 0x670066d06730676, 0x8bc000006bd, 0x8b9086306400000, 0x8b508b20000086a, 0x6df06dc06c306c0, 0xbb90bb60bb30726, 0x8d108cd08c408c7, 0x8d508f700000000, 0x72c0729072f0732, 0xbc20bbf0bbc0000, 0x92f092b09220925, 0x933095509190916, 0x7780775077b077e, 0x31d063d063a0772, 0x9b1095b00000000, 0x9ad09aa00000962, 0x798079507590756, 0x64307df, 0xbc70bc5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x79300000000, 0x4f015200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbcc0bc900000000, 0x0, 0x0, 0x0, 0x0, 0xbcf00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd50bd80bd20000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbdb, 0xbde0000, 0xbe1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe700000be4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbea0000, 0xbf0000000000bed, 0xbf30000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf900000006, 0x0, 0x0, 0x900030bf60000, 0xbff0bfc, 0xc050c02, 0xc0b0c08, 0x0, 0xc110c0e, 0xc1d0c1a, 0xc230c20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc350c320c2f0c2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc290c260c170c14, 0x0, 0xc3b0c3800000000, 0xc410c3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc490c470000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc5100000c4e, 0xc5700000c54, 0xc5d00000c5a, 0xc6300000c60, 0xc6900000c66, 0xc6f00000c6c, 0xc7500000c720000, 0xc780000, 0x0, 0xc8100000c7e0c7b, 0xc8a0c8700000c84, 0xc900c8d0000, 0xc960c93, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc4b, 0x0, 0xc9900000000, 0x0, 0x0, 0x0, 0xca200000c9f, 0xca800000ca5, 0xcae00000cab, 0xcb400000cb1, 0xcba00000cb7, 0xcc000000cbd, 0xcc600000cc30000, 0xcc90000, 0x0, 0xcd200000ccf0ccc, 0xcdb0cd800000cd5, 0xce10cde0000, 0xce70ce4, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcea000000000c9c, 0xcf30cf00ced, 0xcf600000000, 0x124b125d0fb71241, 0x13270e290d831043, 0xe4f12930e991327, 0x116710cd0f550e97, 0x1279121511fd11e3, 0x109d106910190feb, 0xd8d12f3128911c7, 0x11e110790ff50e1d, 0x11d910510edb1309, 0x120311890f65121d, 0x108d10250fbd0eff, 0xe050dd90d9d127d, 0x10d310770ff10f95, 0x125911e711dd1171, 0x10e9130712fb12cf, 0x12a111b9114d1107, 0xf0b0e87122f130b, 0x10ed1083117d112f, 0xecb0e8512cb1249, 0x11471047102f0fed, 0x117f0e0312b11159, 0x114f11150ddd0ddf, 0xf67123d12b511c5, 0xebb0d8712350feb, 0xe1110c110950f27, 0xd7f0f1b0da510f1, 0xe2311450f9d1011, 0x122711c910d70e7d, 0xf6f100d126d1005, 0xd9110bf0f7b11a5, 0x113d0fdb0ddb0dc3, 0xe091291122d1195, 0xfa10f070e9f0e37, 0x12f712ab10f31053, 0xfb50df91313130d, 0xf490ef312690ffd, 0x106d104b0f910f57, 0x11791153111110af, 0x12a3127111cd1261, 0x10670e410dfb0de9, 0xf230efd1227120b, 0x1091112d10030f77, 0xee50ebb0e670d97, 0x116b10a9109b0f29, 0x12d112c912951175, 0x128d110f0d9f12dd, 0xdb10d8f0f3512c1, 0xfeb0f9f0ec70ebd, 0x127711d310cb1073, 0xdf712af0fad1323, 0x103b10210fd10fcb, 0x114310e710bd10a1, 0x12b70f5d0dc512e3, 0x126310310ed70da9, 0x10950fd50f390f17, 0xecf0e310deb12bb, 0x10150fe10fc30fa7, 0x120d116310c3109f, 0xe1312c5128f1213, 0x10b110750e33103d, 0x130f12ff12bd11db, 0x1121118b102d0fcf, 0x1063108b11331125, 0xded11ad0d93123b, 0x11390f690ef50de7, 0x12670fb3101b0eb5, 0xf03122112b31205, 0xe590db5, 0xfab00000e7b, 0x10cf108f0de10000, 0x110d1105110310f5, 0x116d113512d3, 0x1233000011df, 0x128312730000, 0x12e912e700000000, 0x12bf127f130512eb, 0xe010db90db30da1, 0xe5f0e530e170e07, 0xecd0e7f0e790e63, 0xf470f430f2f0ed1, 0xfaf0fa30f970f53, 0x1049103510270fdd, 0x10eb10a3107d106f, 0x10fd10f910fb10f7, 0x110b1109110110ff, 0x11531127111d1117, 0x11731161115b1157, 0x11cb11971197118d, 0x1239123712231219, 0x1273126f124f124d, 0xf2b12e112d912c7, 0x119313be, 0xd9b0dc10dd70d81, 0xe0b0dff0dc90db7, 0xe5d0e510e490e53, 0xe9b0e950e830e7b, 0xf050f010eb10ea9, 0xf3f0f330f1d0f13, 0xf530f410f470f37, 0xf890f850f7f0f5f, 0xfbf0fbd0fab0f99, 0x102110050fff0fc7, 0x1057104910411045, 0x1089107f10e3106f, 0x10b910b510ab108f, 0x10d110cf10c910c7, 0x10ef10dd10df10d5, 0x114911311127111f, 0x11af1173115f1153, 0x121f121b11f911c3, 0x122b123312291223, 0x1239123112351237, 0x12751265124f123f, 0x12c712b91299128b, 0x12db12d912d512d3, 0x1394132712f912e1, 0xd370d2313a61392, 0x141c13ec13da0d39, 0x13251321, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xabb00000a7a0000, 0x0, 0x0, 0xab50ab200000000, 0xa590a560aae0aaa, 0xa680a650a5f0a5c, 0xa740a710a6b, 0xa830a800a7d0a77, 0xa8c00000a89, 0xa9500000a920a8f, 0xaa10a9e00000a98, 0xa6e0ab80aa70aa4, 0xa9b0a860a62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x132900000000, 0x132c, 0x0, 0x0, 0x132f000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1335133200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x133b133800000000, 0x134a13461342133e, 0x134e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1352000000000000, 0x135913601355135d, 0x1364, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13680d8b0d850d89, 0xda70da30da10d99, 0xdaf0db30dad0dab, 0xdbb0db913700cf9, 0xcfb136a0dc70dbd, 0xdd1136e0dcb0dbf, 0xdd70dd50d950dd3, 0xcff0de50de3142c, 0xdf50df30df10def, 0xe070e010dff0d01, 0xe110e0f0e0d0e0b, 0xe1b0e190e170e15, 0xe210e210e210e1f, 0xe270e25105d1376, 0xe2f0e2d0e2b1378, 0xe3b0e390e350e3d, 0xe470e450e430e3f, 0xe510e4d0e4d0e4b, 0xe690e5b0e570e55, 0xe650e610e6b0e5f, 0xe710e6f0e890de7, 0xe750e770e6d0e73, 0xe8d0e8b137a0e81, 0xe9d0e930e910e8f, 0xea50ea3137e137c, 0xd030eab0ea10ea7, 0xeb30eb30eaf0d05, 0xebb0eb90eb71380, 0xec30ec113820ebf, 0xec90d070ec50f0f, 0x13860ed30ed50ed1, 0xedd0edf13880ed9, 0xd090ee90ee70ee1, 0xef10eef0eed0eeb, 0xef70d0d138a0d0b, 0x14400eff0efb0ef9, 0x118f138e138e0f09, 0x139c0d0f0f0d0f0d, 0xd110f150f1113f0, 0xf250f210f1f0f19, 0xf2f0d130d150f2d, 0xf3d0f3b0f311390, 0xf470f450f3d0f3f, 0xf510f4d0f4b0f4f, 0xf5b0f590f550f53, 0xf730f6b0f630f61, 0xf750f6d0f711396, 0xf8713980f830f79, 0xf8b0d170f7d0f81, 0xd190f8d0f930f8f, 0xfa5139a0f9b0f97, 0xfaf0d1f0fa90fb9, 0xdcf0dcd0d1b0d1d, 0xd5111810fb10fbb, 0xfc90fc10fbf0fbd, 0xfd30d2113a40fc5, 0x13a80fdd0fd90fcd, 0xd230fe30fd70fdf, 0xfef0fe90fe70fe5, 0xff70d250ff313aa, 0xffb0d270ff913ac, 0x13ae100710051001, 0x13b2100913b01384, 0x1017100b1013100f, 0x102310211027101f, 0x101d13b4102b1029, 0x10410d2910391037, 0x104d103313b6103f, 0x1059104f13ba13b8, 0x105b0d2b10551057, 0x136c1065105f1061, 0x13c0107113bc106b, 0x13c21081107f107b, 0x13c613c410871085, 0x10990d2d10971093, 0x10a710a50d2f0d2f, 0xd3110b310ad10ab, 0x13ca10bb13c810b7, 0x13cc10c5138c10c1, 0xd350d3313d013ce, 0x13d613d213d410d5, 0x10db10db10d913d8, 0xd3b10e10d3910df, 0x10e910e513dc0d3d, 0x10ff13de0d3f10ef, 0x1113110d13e213e0, 0x111b111911170d41, 0x112313e613e613e4, 0x112b112913e80d43, 0xd47113713ea0d45, 0x13ee1141113b113f, 0x115511510d49114b, 0x13f413f20d4b115d, 0x13f8116513f60d4d, 0x13fa1173116f1169, 0x117b13fe117713fc, 0x118511830d4f139e, 0x14000ead11870d53, 0x118f13a213a01402, 0x119b0d55126b1191, 0x119f0dfd119d1199, 0x140411a711a311a1, 0x11b511b311a911a5, 0x11cb11c111b711ab, 0x11bf11bd11bb11b1, 0xd571408140a1406, 0x141211d511d111cf, 0xd5b0d59140c11d7, 0x11e50d5d1410140e, 0x11ef11eb11e911e7, 0x11f911f111f311ed, 0xd5f11fb11f711f5, 0x12070d61120111ff, 0x1211120f14141209, 0x14160cfd12170d63, 0x12250d670d651418, 0x141a1243123f1231, 0x1253125112471245, 0x125512571372141e, 0x1265125f1374125b, 0x1281127b14221420, 0x1297128714241285, 0x12a5129b129f129d, 0xd6912a9142612a7, 0x12c30d6b142812ad, 0x142e142a12cd0ee3, 0x143012d70d6f0d6d, 0x12db12db14320d71, 0xd7312e5143412df, 0x12f512f112ef12ed, 0x12fd12f914360d75, 0x13030d790d771301, 0x143c143a0d7b1438, 0x13150d7d1311143e, 0x131d131b13191317, 0x1442131f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -@property +static if (size_t.sizeof == 8) { -private alias _IDCA = immutable(dchar[]); -_IDCA decompCanonTable() { static _IDCA t = [ 0x0, 0x3b, 0x0, 0x3c, 0x338, 0x0, 0x3d, 0x338, 0x0, 0x3e, 0x338, 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, 0x0, 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, 0x302, 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, 0x41, 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, 0x41, 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, 0x309, 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, 0x308, 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, 0x30a, 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, 0x30f, 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, 0x302, 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, 0x328, 0x0, 0x42, 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, 0x0, 0x43, 0x301, 0x0, 0x43, 0x302, 0x0, 0x43, 0x307, 0x0, 0x43, 0x30c, 0x0, 0x43, 0x327, 0x0, 0x43, 0x327, 0x301, 0x0, 0x44, 0x307, 0x0, 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, 0x327, 0x0, 0x44, 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x300, 0x0, 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, 0x0, 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, 0x302, 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, 0x304, 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, 0x45, 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, 0x30c, 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, 0x0, 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, 0x306, 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, 0x0, 0x46, 0x307, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, 0x47, 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, 0x30c, 0x0, 0x47, 0x327, 0x0, 0x48, 0x302, 0x0, 0x48, 0x307, 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, 0x0, 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x300, 0x0, 0x49, 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x302, 0x0, 0x4b, 0x0, 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x301, 0x0, 0x4c, 0x30c, 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, 0x0, 0x4c, 0x327, 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, 0x4d, 0x301, 0x0, 0x4d, 0x307, 0x0, 0x4d, 0x323, 0x0, 0x4e, 0x300, 0x0, 0x4e, 0x301, 0x0, 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, 0x0, 0x4e, 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, 0x4e, 0x331, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, 0x328, 0x304, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, 0x52, 0x301, 0x0, 0x52, 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, 0x0, 0x52, 0x311, 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, 0x0, 0x52, 0x327, 0x0, 0x52, 0x331, 0x0, 0x53, 0x301, 0x0, 0x53, 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, 0x53, 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, 0x53, 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, 0x54, 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, 0x331, 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, 0x55, 0x302, 0x0, 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, 0x55, 0x304, 0x0, 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, 0x55, 0x308, 0x0, 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, 0x0, 0x55, 0x308, 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, 0x309, 0x0, 0x55, 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, 0x0, 0x55, 0x30f, 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, 0x55, 0x31b, 0x300, 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, 0x303, 0x0, 0x55, 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, 0x55, 0x323, 0x0, 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, 0x32d, 0x0, 0x55, 0x330, 0x0, 0x56, 0x303, 0x0, 0x56, 0x323, 0x0, 0x57, 0x300, 0x0, 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, 0x57, 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x307, 0x0, 0x58, 0x308, 0x0, 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, 0x0, 0x59, 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, 0x59, 0x308, 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x60, 0x0, 0x61, 0x300, 0x0, 0x61, 0x301, 0x0, 0x61, 0x302, 0x0, 0x61, 0x302, 0x300, 0x0, 0x61, 0x302, 0x301, 0x0, 0x61, 0x302, 0x303, 0x0, 0x61, 0x302, 0x309, 0x0, 0x61, 0x303, 0x0, 0x61, 0x304, 0x0, 0x61, 0x306, 0x0, 0x61, 0x306, 0x300, 0x0, 0x61, 0x306, 0x301, 0x0, 0x61, 0x306, 0x303, 0x0, 0x61, 0x306, 0x309, 0x0, 0x61, 0x307, 0x0, 0x61, 0x307, 0x304, 0x0, 0x61, 0x308, 0x0, 0x61, 0x308, 0x304, 0x0, 0x61, 0x309, 0x0, 0x61, 0x30a, 0x0, 0x61, 0x30a, 0x301, 0x0, 0x61, 0x30c, 0x0, 0x61, 0x30f, 0x0, 0x61, 0x311, 0x0, 0x61, 0x323, 0x0, 0x61, 0x323, 0x302, 0x0, 0x61, 0x323, 0x306, 0x0, 0x61, 0x325, 0x0, 0x61, 0x328, 0x0, 0x62, 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x301, 0x0, 0x63, 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, 0x63, 0x327, 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x307, 0x0, 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, 0x64, 0x327, 0x0, 0x64, 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, 0x300, 0x0, 0x65, 0x301, 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, 0x65, 0x302, 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, 0x309, 0x0, 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, 0x300, 0x0, 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, 0x307, 0x0, 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, 0x0, 0x65, 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, 0x65, 0x323, 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, 0x0, 0x65, 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, 0x66, 0x307, 0x0, 0x67, 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, 0x67, 0x306, 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, 0x327, 0x0, 0x68, 0x302, 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, 0x0, 0x68, 0x30c, 0x0, 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, 0x68, 0x32e, 0x0, 0x68, 0x331, 0x0, 0x69, 0x300, 0x0, 0x69, 0x301, 0x0, 0x69, 0x302, 0x0, 0x69, 0x303, 0x0, 0x69, 0x304, 0x0, 0x69, 0x306, 0x0, 0x69, 0x308, 0x0, 0x69, 0x308, 0x301, 0x0, 0x69, 0x309, 0x0, 0x69, 0x30c, 0x0, 0x69, 0x30f, 0x0, 0x69, 0x311, 0x0, 0x69, 0x323, 0x0, 0x69, 0x328, 0x0, 0x69, 0x330, 0x0, 0x6a, 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x301, 0x0, 0x6b, 0x30c, 0x0, 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, 0x6b, 0x331, 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, 0x323, 0x0, 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, 0x32d, 0x0, 0x6c, 0x331, 0x0, 0x6d, 0x301, 0x0, 0x6d, 0x307, 0x0, 0x6d, 0x323, 0x0, 0x6e, 0x300, 0x0, 0x6e, 0x301, 0x0, 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, 0x0, 0x6e, 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, 0x6e, 0x331, 0x0, 0x6f, 0x300, 0x0, 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, 0x6f, 0x302, 0x300, 0x0, 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, 0x303, 0x0, 0x6f, 0x302, 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, 0x303, 0x301, 0x0, 0x6f, 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, 0x0, 0x6f, 0x304, 0x0, 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, 0x301, 0x0, 0x6f, 0x306, 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, 0x304, 0x0, 0x6f, 0x308, 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, 0x309, 0x0, 0x6f, 0x30b, 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, 0x0, 0x6f, 0x311, 0x0, 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, 0x0, 0x6f, 0x31b, 0x301, 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, 0x31b, 0x309, 0x0, 0x6f, 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, 0x6f, 0x323, 0x302, 0x0, 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, 0x0, 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x72, 0x301, 0x0, 0x72, 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, 0x311, 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, 0x327, 0x0, 0x72, 0x331, 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, 0x307, 0x0, 0x73, 0x302, 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, 0x0, 0x73, 0x30c, 0x307, 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, 0x307, 0x0, 0x73, 0x326, 0x0, 0x73, 0x327, 0x0, 0x74, 0x307, 0x0, 0x74, 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, 0x74, 0x326, 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, 0x331, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, 0x75, 0x330, 0x0, 0x76, 0x303, 0x0, 0x76, 0x323, 0x0, 0x77, 0x300, 0x0, 0x77, 0x301, 0x0, 0x77, 0x302, 0x0, 0x77, 0x307, 0x0, 0x77, 0x308, 0x0, 0x77, 0x30a, 0x0, 0x77, 0x323, 0x0, 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x300, 0x0, 0x79, 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, 0x303, 0x0, 0x79, 0x304, 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, 0x0, 0x79, 0x309, 0x0, 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, 0x7a, 0x301, 0x0, 0x7a, 0x302, 0x0, 0x7a, 0x307, 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, 0x0, 0x7a, 0x331, 0x0, 0xa8, 0x300, 0x0, 0xa8, 0x301, 0x0, 0xa8, 0x342, 0x0, 0xb4, 0x0, 0xb7, 0x0, 0xc6, 0x301, 0x0, 0xc6, 0x304, 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, 0x0, 0xf8, 0x301, 0x0, 0x17f, 0x307, 0x0, 0x1b7, 0x30c, 0x0, 0x292, 0x30c, 0x0, 0x2b9, 0x0, 0x300, 0x0, 0x301, 0x0, 0x308, 0x301, 0x0, 0x313, 0x0, 0x391, 0x300, 0x0, 0x391, 0x301, 0x0, 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, 0x313, 0x0, 0x391, 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, 0x345, 0x0, 0x391, 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, 0x345, 0x0, 0x391, 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, 0x345, 0x0, 0x391, 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, 0x314, 0x300, 0x0, 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, 0x314, 0x301, 0x0, 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, 0x314, 0x342, 0x0, 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, 0x314, 0x345, 0x0, 0x391, 0x345, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, 0x395, 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, 0x301, 0x0, 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, 0x314, 0x301, 0x0, 0x397, 0x300, 0x0, 0x397, 0x301, 0x0, 0x397, 0x313, 0x0, 0x397, 0x313, 0x300, 0x0, 0x397, 0x313, 0x300, 0x345, 0x0, 0x397, 0x313, 0x301, 0x0, 0x397, 0x313, 0x301, 0x345, 0x0, 0x397, 0x313, 0x342, 0x0, 0x397, 0x313, 0x342, 0x345, 0x0, 0x397, 0x313, 0x345, 0x0, 0x397, 0x314, 0x0, 0x397, 0x314, 0x300, 0x0, 0x397, 0x314, 0x300, 0x345, 0x0, 0x397, 0x314, 0x301, 0x0, 0x397, 0x314, 0x301, 0x345, 0x0, 0x397, 0x314, 0x342, 0x0, 0x397, 0x314, 0x342, 0x345, 0x0, 0x397, 0x314, 0x345, 0x0, 0x397, 0x345, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, 0x0, 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, 0x399, 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, 0x301, 0x0, 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, 0x314, 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, 0x342, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, 0x3a1, 0x314, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, 0x314, 0x342, 0x0, 0x3a9, 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, 0x0, 0x3a9, 0x313, 0x0, 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, 0x300, 0x345, 0x0, 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, 0x301, 0x345, 0x0, 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, 0x342, 0x345, 0x0, 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, 0x3a9, 0x314, 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, 0x3a9, 0x314, 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, 0x3a9, 0x314, 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, 0x3a9, 0x314, 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x300, 0x0, 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, 0x345, 0x0, 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, 0x0, 0x3b5, 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, 0x314, 0x0, 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, 0x301, 0x0, 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, 0x313, 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, 0x313, 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, 0x313, 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, 0x3b7, 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, 0x3b7, 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, 0x3b7, 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, 0x3b7, 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, 0x3b9, 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, 0x3b9, 0x304, 0x0, 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, 0x308, 0x300, 0x0, 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, 0x342, 0x0, 0x3b9, 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, 0x313, 0x301, 0x0, 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, 0x3b9, 0x314, 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, 0x314, 0x342, 0x0, 0x3b9, 0x342, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, 0x313, 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, 0x3bf, 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, 0x301, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, 0x3c5, 0x300, 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, 0x3c9, 0x300, 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, 0x3c9, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, 0x300, 0x0, 0x3c9, 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, 0x301, 0x0, 0x3c9, 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x342, 0x0, 0x3c9, 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, 0x345, 0x0, 0x3c9, 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, 0x314, 0x300, 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, 0x314, 0x301, 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, 0x314, 0x342, 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, 0x342, 0x0, 0x3c9, 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3d2, 0x301, 0x0, 0x3d2, 0x308, 0x0, 0x406, 0x308, 0x0, 0x410, 0x306, 0x0, 0x410, 0x308, 0x0, 0x413, 0x301, 0x0, 0x415, 0x300, 0x0, 0x415, 0x306, 0x0, 0x415, 0x308, 0x0, 0x416, 0x306, 0x0, 0x416, 0x308, 0x0, 0x417, 0x308, 0x0, 0x418, 0x300, 0x0, 0x418, 0x304, 0x0, 0x418, 0x306, 0x0, 0x418, 0x308, 0x0, 0x41a, 0x301, 0x0, 0x41e, 0x308, 0x0, 0x423, 0x304, 0x0, 0x423, 0x306, 0x0, 0x423, 0x308, 0x0, 0x423, 0x30b, 0x0, 0x427, 0x308, 0x0, 0x42b, 0x308, 0x0, 0x42d, 0x308, 0x0, 0x430, 0x306, 0x0, 0x430, 0x308, 0x0, 0x433, 0x301, 0x0, 0x435, 0x300, 0x0, 0x435, 0x306, 0x0, 0x435, 0x308, 0x0, 0x436, 0x306, 0x0, 0x436, 0x308, 0x0, 0x437, 0x308, 0x0, 0x438, 0x300, 0x0, 0x438, 0x304, 0x0, 0x438, 0x306, 0x0, 0x438, 0x308, 0x0, 0x43a, 0x301, 0x0, 0x43e, 0x308, 0x0, 0x443, 0x304, 0x0, 0x443, 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, 0x0, 0x447, 0x308, 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, 0x456, 0x308, 0x0, 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, 0x308, 0x0, 0x4d9, 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, 0x0, 0x5d0, 0x5b7, 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x5bc, 0x0, 0x5d3, 0x5bc, 0x0, 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, 0x0, 0x5d6, 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, 0x5d9, 0x5bc, 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, 0x5bf, 0x0, 0x5dc, 0x5bc, 0x0, 0x5de, 0x5bc, 0x0, 0x5e0, 0x5bc, 0x0, 0x5e1, 0x5bc, 0x0, 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, 0x5e4, 0x5bf, 0x0, 0x5e6, 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, 0x5bc, 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, 0x0, 0x627, 0x655, 0x0, 0x648, 0x654, 0x0, 0x64a, 0x654, 0x0, 0x6c1, 0x654, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, 0x0, 0x915, 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, 0x91c, 0x93c, 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, 0x93c, 0x0, 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, 0x0, 0x933, 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, 0x9af, 0x9bc, 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, 0xa3c, 0x0, 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, 0x0, 0xa32, 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, 0xb22, 0xb3c, 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, 0xb57, 0x0, 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, 0x0, 0xbc7, 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, 0xcc6, 0xcc2, 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, 0xcc6, 0xcd6, 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, 0xd3e, 0x0, 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, 0xdca, 0x0, 0xdd9, 0xddf, 0x0, 0xf40, 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, 0xfb7, 0x0, 0xf51, 0xfb7, 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, 0x0, 0xf71, 0xf72, 0x0, 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, 0xf90, 0xfb5, 0x0, 0xf92, 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, 0xfb7, 0x0, 0xfa6, 0xfb7, 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf80, 0x0, 0xfb3, 0xf80, 0x0, 0x1025, 0x102e, 0x0, 0x1b05, 0x1b35, 0x0, 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, 0x0, 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, 0x0, 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, 0x0, 0x1b42, 0x1b35, 0x0, 0x1fbf, 0x300, 0x0, 0x1fbf, 0x301, 0x0, 0x1fbf, 0x342, 0x0, 0x1ffe, 0x300, 0x0, 0x1ffe, 0x301, 0x0, 0x1ffe, 0x342, 0x0, 0x2002, 0x0, 0x2003, 0x0, 0x2190, 0x338, 0x0, 0x2192, 0x338, 0x0, 0x2194, 0x338, 0x0, 0x21d0, 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, 0x0, 0x2203, 0x338, 0x0, 0x2208, 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2223, 0x338, 0x0, 0x2225, 0x338, 0x0, 0x223c, 0x338, 0x0, 0x2243, 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, 0x338, 0x0, 0x224d, 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, 0x338, 0x0, 0x2265, 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, 0x338, 0x0, 0x2276, 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, 0x338, 0x0, 0x227b, 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, 0x338, 0x0, 0x2282, 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, 0x338, 0x0, 0x2287, 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, 0x338, 0x0, 0x22a2, 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, 0x338, 0x0, 0x22ab, 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, 0x338, 0x0, 0x22b4, 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2add, 0x338, 0x0, 0x3008, 0x0, 0x3009, 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, 0x304d, 0x3099, 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, 0x3053, 0x3099, 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, 0x3059, 0x3099, 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, 0x305f, 0x3099, 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, 0x3066, 0x3099, 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, 0x306f, 0x309a, 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, 0x3075, 0x3099, 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, 0x3078, 0x309a, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, 0x0, 0x309d, 0x3099, 0x0, 0x30a6, 0x3099, 0x0, 0x30ab, 0x3099, 0x0, 0x30ad, 0x3099, 0x0, 0x30af, 0x3099, 0x0, 0x30b1, 0x3099, 0x0, 0x30b3, 0x3099, 0x0, 0x30b5, 0x3099, 0x0, 0x30b7, 0x3099, 0x0, 0x30b9, 0x3099, 0x0, 0x30bb, 0x3099, 0x0, 0x30bd, 0x3099, 0x0, 0x30bf, 0x3099, 0x0, 0x30c1, 0x3099, 0x0, 0x30c4, 0x3099, 0x0, 0x30c6, 0x3099, 0x0, 0x30c8, 0x3099, 0x0, 0x30cf, 0x3099, 0x0, 0x30cf, 0x309a, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x309a, 0x0, 0x30d5, 0x3099, 0x0, 0x30d5, 0x309a, 0x0, 0x30d8, 0x3099, 0x0, 0x30d8, 0x309a, 0x0, 0x30db, 0x3099, 0x0, 0x30db, 0x309a, 0x0, 0x30ef, 0x3099, 0x0, 0x30f0, 0x3099, 0x0, 0x30f1, 0x3099, 0x0, 0x30f2, 0x3099, 0x0, 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, 0x0, 0x34bb, 0x0, 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, 0x36fc, 0x0, 0x3781, 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, 0x0, 0x38c7, 0x0, 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, 0x3a2e, 0x0, 0x3a6c, 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, 0x0, 0x3b49, 0x0, 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, 0x3d33, 0x0, 0x3d96, 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, 0x0, 0x3ffc, 0x0, 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, 0x4046, 0x0, 0x4096, 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, 0x0, 0x4227, 0x0, 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, 0x4359, 0x0, 0x43d5, 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, 0x0, 0x452b, 0x0, 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, 0x45d7, 0x0, 0x45f9, 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, 0x0, 0x4995, 0x0, 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, 0x4ab2, 0x0, 0x4b33, 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, 0x0, 0x4cf8, 0x0, 0x4d56, 0x0, 0x4e0d, 0x0, 0x4e26, 0x0, 0x4e32, 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e41, 0x0, 0x4e82, 0x0, 0x4e86, 0x0, 0x4eae, 0x0, 0x4ec0, 0x0, 0x4ecc, 0x0, 0x4ee4, 0x0, 0x4f60, 0x0, 0x4f80, 0x0, 0x4f86, 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, 0x0, 0x4fbf, 0x0, 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, 0x5099, 0x0, 0x50cf, 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x5140, 0x0, 0x5145, 0x0, 0x514d, 0x0, 0x5154, 0x0, 0x5164, 0x0, 0x5167, 0x0, 0x5168, 0x0, 0x5169, 0x0, 0x516d, 0x0, 0x5177, 0x0, 0x5180, 0x0, 0x518d, 0x0, 0x5192, 0x0, 0x5195, 0x0, 0x5197, 0x0, 0x51a4, 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, 0x0, 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51f5, 0x0, 0x5203, 0x0, 0x5207, 0x0, 0x5217, 0x0, 0x5229, 0x0, 0x523a, 0x0, 0x523b, 0x0, 0x5246, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52c7, 0x0, 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, 0x0, 0x52f5, 0x0, 0x52fa, 0x0, 0x5305, 0x0, 0x5306, 0x0, 0x5317, 0x0, 0x533f, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x535a, 0x0, 0x5373, 0x0, 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x53c3, 0x0, 0x53ca, 0x0, 0x53df, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53f1, 0x0, 0x5406, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, 0x54a2, 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x5553, 0x0, 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, 0x5587, 0x0, 0x5599, 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, 0x0, 0x55c0, 0x0, 0x55c2, 0x0, 0x55e2, 0x0, 0x5606, 0x0, 0x5651, 0x0, 0x5668, 0x0, 0x5674, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, 0x0, 0x580d, 0x0, 0x5831, 0x0, 0x5832, 0x0, 0x5840, 0x0, 0x585a, 0x0, 0x585e, 0x0, 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, 0x0, 0x58d8, 0x0, 0x58df, 0x0, 0x58ee, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5906, 0x0, 0x591a, 0x0, 0x5922, 0x0, 0x5944, 0x0, 0x5948, 0x0, 0x5951, 0x0, 0x5954, 0x0, 0x5962, 0x0, 0x5973, 0x0, 0x59d8, 0x0, 0x59ec, 0x0, 0x5a1b, 0x0, 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, 0x0, 0x5ab5, 0x0, 0x5b08, 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, 0x5b85, 0x0, 0x5bc3, 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, 0x0, 0x5bf3, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c22, 0x0, 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, 0x5c64, 0x0, 0x5c65, 0x0, 0x5c6e, 0x0, 0x5c8d, 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, 0x5d43, 0x0, 0x5d50, 0x0, 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, 0x0, 0x5db2, 0x0, 0x5dba, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, 0x5dfd, 0x0, 0x5e28, 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e74, 0x0, 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, 0x5ec9, 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, 0x0, 0x5eec, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f22, 0x0, 0x5f53, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f8b, 0x0, 0x5f9a, 0x0, 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fcd, 0x0, 0x5fd7, 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, 0x0, 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, 0x60d8, 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, 0x0, 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, 0x617a, 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, 0x0, 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, 0x6200, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, 0x6234, 0x0, 0x625d, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x633d, 0x0, 0x6350, 0x0, 0x6368, 0x0, 0x637b, 0x0, 0x6383, 0x0, 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, 0x0, 0x63c5, 0x0, 0x63e4, 0x0, 0x641c, 0x0, 0x6422, 0x0, 0x6452, 0x0, 0x6469, 0x0, 0x6477, 0x0, 0x647e, 0x0, 0x649a, 0x0, 0x649d, 0x0, 0x64c4, 0x0, 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, 0x6599, 0x0, 0x65c5, 0x0, 0x65e2, 0x0, 0x65e3, 0x0, 0x6613, 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, 0x0, 0x6691, 0x0, 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, 0x66f4, 0x0, 0x66f8, 0x0, 0x6700, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, 0x0, 0x677b, 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, 0x67fa, 0x0, 0x6817, 0x0, 0x681f, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b21, 0x0, 0x6b54, 0x0, 0x6b72, 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, 0x0, 0x6bae, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6c4e, 0x0, 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, 0x0, 0x6ccc, 0x0, 0x6ccd, 0x0, 0x6ce5, 0x0, 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, 0x6d34, 0x0, 0x6d3e, 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, 0x0, 0x6d77, 0x0, 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, 0x6dda, 0x0, 0x6dea, 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, 0x0, 0x6e6e, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, 0x6ecb, 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f22, 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, 0x0, 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, 0x704a, 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, 0x0, 0x7099, 0x0, 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, 0x7145, 0x0, 0x7149, 0x0, 0x716e, 0x0, 0x719c, 0x0, 0x71ce, 0x0, 0x71d0, 0x0, 0x7210, 0x0, 0x721b, 0x0, 0x7228, 0x0, 0x722b, 0x0, 0x7235, 0x0, 0x7250, 0x0, 0x7262, 0x0, 0x7280, 0x0, 0x7295, 0x0, 0x72af, 0x0, 0x72c0, 0x0, 0x72fc, 0x0, 0x732a, 0x0, 0x7375, 0x0, 0x737a, 0x0, 0x7387, 0x0, 0x738b, 0x0, 0x73a5, 0x0, 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x7506, 0x0, 0x7524, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, 0x7565, 0x0, 0x7570, 0x0, 0x75e2, 0x0, 0x7610, 0x0, 0x761d, 0x0, 0x761f, 0x0, 0x7642, 0x0, 0x7669, 0x0, 0x76ca, 0x0, 0x76db, 0x0, 0x76e7, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, 0x0, 0x771f, 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, 0x77a7, 0x0, 0x784e, 0x0, 0x786b, 0x0, 0x788c, 0x0, 0x7891, 0x0, 0x78ca, 0x0, 0x78cc, 0x0, 0x78fb, 0x0, 0x792a, 0x0, 0x793c, 0x0, 0x793e, 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, 0x0, 0x7956, 0x0, 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, 0x797f, 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, 0x0, 0x79ca, 0x0, 0x79eb, 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, 0x7a4a, 0x0, 0x7a4f, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, 0x0, 0x7aee, 0x0, 0x7b20, 0x0, 0x7bc0, 0x0, 0x7bc6, 0x0, 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, 0x0, 0x7c7b, 0x0, 0x7c92, 0x0, 0x7cbe, 0x0, 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, 0x7ce7, 0x0, 0x7ce8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, 0x0, 0x7d2f, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, 0x0, 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, 0x7e09, 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f3e, 0x0, 0x7f72, 0x0, 0x7f79, 0x0, 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f95, 0x0, 0x7f9a, 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, 0x8005, 0x0, 0x8046, 0x0, 0x8060, 0x0, 0x806f, 0x0, 0x8070, 0x0, 0x807e, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, 0x8103, 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e8, 0x0, 0x81ed, 0x0, 0x8201, 0x0, 0x8204, 0x0, 0x8218, 0x0, 0x826f, 0x0, 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, 0x82b1, 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, 0x0, 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, 0x8353, 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, 0x0, 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, 0x83ef, 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, 0x0, 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, 0x8516, 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, 0x0, 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x8650, 0x0, 0x865c, 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x8688, 0x0, 0x86a9, 0x0, 0x86e2, 0x0, 0x870e, 0x0, 0x8728, 0x0, 0x876b, 0x0, 0x8779, 0x0, 0x8786, 0x0, 0x87ba, 0x0, 0x87e1, 0x0, 0x8801, 0x0, 0x881f, 0x0, 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, 0x0, 0x88cf, 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, 0x88f8, 0x0, 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x8aa0, 0x0, 0x8aaa, 0x0, 0x8abf, 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, 0x0, 0x8aed, 0x0, 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, 0x8b39, 0x0, 0x8b58, 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c48, 0x0, 0x8c55, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, 0x0, 0x8d77, 0x0, 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, 0x8f38, 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9e, 0x0, 0x8fb0, 0x0, 0x8fb6, 0x0, 0x9023, 0x0, 0x9038, 0x0, 0x9072, 0x0, 0x907c, 0x0, 0x908f, 0x0, 0x9094, 0x0, 0x90ce, 0x0, 0x90de, 0x0, 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, 0x0, 0x916a, 0x0, 0x9199, 0x0, 0x91b4, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, 0x91d1, 0x0, 0x9234, 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, 0x0, 0x92d7, 0x0, 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, 0x93f9, 0x0, 0x9415, 0x0, 0x958b, 0x0, 0x95ad, 0x0, 0x95b7, 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, 0x0, 0x9675, 0x0, 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, 0x96a3, 0x0, 0x96b7, 0x0, 0x96b8, 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, 0x0, 0x9756, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ff, 0x0, 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, 0x0, 0x98e2, 0x0, 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, 0x9929, 0x0, 0x99a7, 0x0, 0x99c2, 0x0, 0x99f1, 0x0, 0x99fe, 0x0, 0x9a6a, 0x0, 0x9b12, 0x0, 0x9b6f, 0x0, 0x9c40, 0x0, 0x9c57, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, 0x9db4, 0x0, 0x9dfa, 0x0, 0x9e1e, 0x0, 0x9e7f, 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, 0x9ebb, 0x0, 0x9ece, 0x0, 0x9ef9, 0x0, 0x9efe, 0x0, 0x9f05, 0x0, 0x9f0f, 0x0, 0x9f16, 0x0, 0x9f3b, 0x0, 0x9f43, 0x0, 0x9f8d, 0x0, 0x9f8e, 0x0, 0x9f9c, 0x0, 0x11099, 0x110ba, 0x0, 0x1109b, 0x110ba, 0x0, 0x110a5, 0x110ba, 0x0, 0x11131, 0x11127, 0x0, 0x11132, 0x11127, 0x0, 0x1d157, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x1d16e, 0x0, 0x1d158, 0x1d165, 0x1d16f, 0x0, 0x1d158, 0x1d165, 0x1d170, 0x0, 0x1d158, 0x1d165, 0x1d171, 0x0, 0x1d158, 0x1d165, 0x1d172, 0x0, 0x1d1b9, 0x1d165, 0x0, 0x1d1b9, 0x1d165, 0x1d16e, 0x0, 0x1d1b9, 0x1d165, 0x1d16f, 0x0, 0x1d1ba, 0x1d165, 0x0, 0x1d1ba, 0x1d165, 0x1d16e, 0x0, 0x1d1ba, 0x1d165, 0x1d16f, 0x0, 0x20122, 0x0, 0x2051c, 0x0, 0x20525, 0x0, 0x2054b, 0x0, 0x2063a, 0x0, 0x20804, 0x0, 0x208de, 0x0, 0x20a2c, 0x0, 0x20b63, 0x0, 0x214e4, 0x0, 0x216a8, 0x0, 0x216ea, 0x0, 0x219c8, 0x0, 0x21b18, 0x0, 0x21d0b, 0x0, 0x21de4, 0x0, 0x21de6, 0x0, 0x22183, 0x0, 0x2219f, 0x0, 0x22331, 0x0, 0x226d4, 0x0, 0x22844, 0x0, 0x2284a, 0x0, 0x22b0c, 0x0, 0x22bf1, 0x0, 0x2300a, 0x0, 0x232b8, 0x0, 0x2335f, 0x0, 0x23393, 0x0, 0x2339c, 0x0, 0x233c3, 0x0, 0x233d5, 0x0, 0x2346d, 0x0, 0x236a3, 0x0, 0x238a7, 0x0, 0x23a8d, 0x0, 0x23afa, 0x0, 0x23cbc, 0x0, 0x23d1e, 0x0, 0x23ed1, 0x0, 0x23f5e, 0x0, 0x23f8e, 0x0, 0x24263, 0x0, 0x242ee, 0x0, 0x243ab, 0x0, 0x24608, 0x0, 0x24735, 0x0, 0x24814, 0x0, 0x24c36, 0x0, 0x24c92, 0x0, 0x24fa1, 0x0, 0x24fb8, 0x0, 0x25044, 0x0, 0x250f2, 0x0, 0x250f3, 0x0, 0x25119, 0x0, 0x25133, 0x0, 0x25249, 0x0, 0x2541d, 0x0, 0x25626, 0x0, 0x2569a, 0x0, 0x256c5, 0x0, 0x2597c, 0x0, 0x25aa7, 0x0, 0x25bab, 0x0, 0x25c80, 0x0, 0x25cd0, 0x0, 0x25f86, 0x0, 0x261da, 0x0, 0x26228, 0x0, 0x26247, 0x0, 0x262d9, 0x0, 0x2633e, 0x0, 0x264da, 0x0, 0x26523, 0x0, 0x265a8, 0x0, 0x267a7, 0x0, 0x267b5, 0x0, 0x26b3c, 0x0, 0x26c36, 0x0, 0x26cd5, 0x0, 0x26d6b, 0x0, 0x26f2c, 0x0, 0x26fb1, 0x0, 0x270d2, 0x0, 0x273ca, 0x0, 0x27667, 0x0, 0x278ae, 0x0, 0x27966, 0x0, 0x27ca8, 0x0, 0x27ed3, 0x0, 0x27f2f, 0x0, 0x285d2, 0x0, 0x285ed, 0x0, 0x2872e, 0x0, 0x28bfa, 0x0, 0x28d77, 0x0, 0x29145, 0x0, 0x291df, 0x0, 0x2921a, 0x0, 0x2940a, 0x0, 0x29496, 0x0, 0x295b6, 0x0, 0x29b30, 0x0, 0x2a0ce, 0x0, 0x2a105, 0x0, 0x2a20e, 0x0, 0x2a291, 0x0, 0x2a392, 0x0, 0x2a600, 0x0]; return t; } -_IDCA decompCompatTable() { static _IDCA t = [ 0x0, 0x20, 0x0, 0x20, 0x301, 0x0, 0x20, 0x303, 0x0, 0x20, 0x304, 0x0, 0x20, 0x305, 0x0, 0x20, 0x306, 0x0, 0x20, 0x307, 0x0, 0x20, 0x308, 0x0, 0x20, 0x308, 0x300, 0x0, 0x20, 0x308, 0x301, 0x0, 0x20, 0x308, 0x342, 0x0, 0x20, 0x30a, 0x0, 0x20, 0x30b, 0x0, 0x20, 0x313, 0x0, 0x20, 0x313, 0x300, 0x0, 0x20, 0x313, 0x301, 0x0, 0x20, 0x313, 0x342, 0x0, 0x20, 0x314, 0x0, 0x20, 0x314, 0x300, 0x0, 0x20, 0x314, 0x301, 0x0, 0x20, 0x314, 0x342, 0x0, 0x20, 0x327, 0x0, 0x20, 0x328, 0x0, 0x20, 0x333, 0x0, 0x20, 0x342, 0x0, 0x20, 0x345, 0x0, 0x20, 0x64b, 0x0, 0x20, 0x64c, 0x0, 0x20, 0x64c, 0x651, 0x0, 0x20, 0x64d, 0x0, 0x20, 0x64d, 0x651, 0x0, 0x20, 0x64e, 0x0, 0x20, 0x64e, 0x651, 0x0, 0x20, 0x64f, 0x0, 0x20, 0x64f, 0x651, 0x0, 0x20, 0x650, 0x0, 0x20, 0x650, 0x651, 0x0, 0x20, 0x651, 0x0, 0x20, 0x651, 0x670, 0x0, 0x20, 0x652, 0x0, 0x20, 0x3099, 0x0, 0x20, 0x309a, 0x0, 0x21, 0x0, 0x21, 0x21, 0x0, 0x21, 0x3f, 0x0, 0x22, 0x0, 0x23, 0x0, 0x24, 0x0, 0x25, 0x0, 0x26, 0x0, 0x27, 0x0, 0x28, 0x0, 0x28, 0x31, 0x29, 0x0, 0x28, 0x31, 0x30, 0x29, 0x0, 0x28, 0x31, 0x31, 0x29, 0x0, 0x28, 0x31, 0x32, 0x29, 0x0, 0x28, 0x31, 0x33, 0x29, 0x0, 0x28, 0x31, 0x34, 0x29, 0x0, 0x28, 0x31, 0x35, 0x29, 0x0, 0x28, 0x31, 0x36, 0x29, 0x0, 0x28, 0x31, 0x37, 0x29, 0x0, 0x28, 0x31, 0x38, 0x29, 0x0, 0x28, 0x31, 0x39, 0x29, 0x0, 0x28, 0x32, 0x29, 0x0, 0x28, 0x32, 0x30, 0x29, 0x0, 0x28, 0x33, 0x29, 0x0, 0x28, 0x34, 0x29, 0x0, 0x28, 0x35, 0x29, 0x0, 0x28, 0x36, 0x29, 0x0, 0x28, 0x37, 0x29, 0x0, 0x28, 0x38, 0x29, 0x0, 0x28, 0x39, 0x29, 0x0, 0x28, 0x41, 0x29, 0x0, 0x28, 0x42, 0x29, 0x0, 0x28, 0x43, 0x29, 0x0, 0x28, 0x44, 0x29, 0x0, 0x28, 0x45, 0x29, 0x0, 0x28, 0x46, 0x29, 0x0, 0x28, 0x47, 0x29, 0x0, 0x28, 0x48, 0x29, 0x0, 0x28, 0x49, 0x29, 0x0, 0x28, 0x4a, 0x29, 0x0, 0x28, 0x4b, 0x29, 0x0, 0x28, 0x4c, 0x29, 0x0, 0x28, 0x4d, 0x29, 0x0, 0x28, 0x4e, 0x29, 0x0, 0x28, 0x4f, 0x29, 0x0, 0x28, 0x50, 0x29, 0x0, 0x28, 0x51, 0x29, 0x0, 0x28, 0x52, 0x29, 0x0, 0x28, 0x53, 0x29, 0x0, 0x28, 0x54, 0x29, 0x0, 0x28, 0x55, 0x29, 0x0, 0x28, 0x56, 0x29, 0x0, 0x28, 0x57, 0x29, 0x0, 0x28, 0x58, 0x29, 0x0, 0x28, 0x59, 0x29, 0x0, 0x28, 0x5a, 0x29, 0x0, 0x28, 0x61, 0x29, 0x0, 0x28, 0x62, 0x29, 0x0, 0x28, 0x63, 0x29, 0x0, 0x28, 0x64, 0x29, 0x0, 0x28, 0x65, 0x29, 0x0, 0x28, 0x66, 0x29, 0x0, 0x28, 0x67, 0x29, 0x0, 0x28, 0x68, 0x29, 0x0, 0x28, 0x69, 0x29, 0x0, 0x28, 0x6a, 0x29, 0x0, 0x28, 0x6b, 0x29, 0x0, 0x28, 0x6c, 0x29, 0x0, 0x28, 0x6d, 0x29, 0x0, 0x28, 0x6e, 0x29, 0x0, 0x28, 0x6f, 0x29, 0x0, 0x28, 0x70, 0x29, 0x0, 0x28, 0x71, 0x29, 0x0, 0x28, 0x72, 0x29, 0x0, 0x28, 0x73, 0x29, 0x0, 0x28, 0x74, 0x29, 0x0, 0x28, 0x75, 0x29, 0x0, 0x28, 0x76, 0x29, 0x0, 0x28, 0x77, 0x29, 0x0, 0x28, 0x78, 0x29, 0x0, 0x28, 0x79, 0x29, 0x0, 0x28, 0x7a, 0x29, 0x0, 0x28, 0x1100, 0x29, 0x0, 0x28, 0x1100, 0x1161, 0x29, 0x0, 0x28, 0x1102, 0x29, 0x0, 0x28, 0x1102, 0x1161, 0x29, 0x0, 0x28, 0x1103, 0x29, 0x0, 0x28, 0x1103, 0x1161, 0x29, 0x0, 0x28, 0x1105, 0x29, 0x0, 0x28, 0x1105, 0x1161, 0x29, 0x0, 0x28, 0x1106, 0x29, 0x0, 0x28, 0x1106, 0x1161, 0x29, 0x0, 0x28, 0x1107, 0x29, 0x0, 0x28, 0x1107, 0x1161, 0x29, 0x0, 0x28, 0x1109, 0x29, 0x0, 0x28, 0x1109, 0x1161, 0x29, 0x0, 0x28, 0x110b, 0x29, 0x0, 0x28, 0x110b, 0x1161, 0x29, 0x0, 0x28, 0x110b, 0x1169, 0x110c, 0x1165, 0x11ab, 0x29, 0x0, 0x28, 0x110b, 0x1169, 0x1112, 0x116e, 0x29, 0x0, 0x28, 0x110c, 0x29, 0x0, 0x28, 0x110c, 0x1161, 0x29, 0x0, 0x28, 0x110c, 0x116e, 0x29, 0x0, 0x28, 0x110e, 0x29, 0x0, 0x28, 0x110e, 0x1161, 0x29, 0x0, 0x28, 0x110f, 0x29, 0x0, 0x28, 0x110f, 0x1161, 0x29, 0x0, 0x28, 0x1110, 0x29, 0x0, 0x28, 0x1110, 0x1161, 0x29, 0x0, 0x28, 0x1111, 0x29, 0x0, 0x28, 0x1111, 0x1161, 0x29, 0x0, 0x28, 0x1112, 0x29, 0x0, 0x28, 0x1112, 0x1161, 0x29, 0x0, 0x28, 0x4e00, 0x29, 0x0, 0x28, 0x4e03, 0x29, 0x0, 0x28, 0x4e09, 0x29, 0x0, 0x28, 0x4e5d, 0x29, 0x0, 0x28, 0x4e8c, 0x29, 0x0, 0x28, 0x4e94, 0x29, 0x0, 0x28, 0x4ee3, 0x29, 0x0, 0x28, 0x4f01, 0x29, 0x0, 0x28, 0x4f11, 0x29, 0x0, 0x28, 0x516b, 0x29, 0x0, 0x28, 0x516d, 0x29, 0x0, 0x28, 0x52b4, 0x29, 0x0, 0x28, 0x5341, 0x29, 0x0, 0x28, 0x5354, 0x29, 0x0, 0x28, 0x540d, 0x29, 0x0, 0x28, 0x547c, 0x29, 0x0, 0x28, 0x56db, 0x29, 0x0, 0x28, 0x571f, 0x29, 0x0, 0x28, 0x5b66, 0x29, 0x0, 0x28, 0x65e5, 0x29, 0x0, 0x28, 0x6708, 0x29, 0x0, 0x28, 0x6709, 0x29, 0x0, 0x28, 0x6728, 0x29, 0x0, 0x28, 0x682a, 0x29, 0x0, 0x28, 0x6c34, 0x29, 0x0, 0x28, 0x706b, 0x29, 0x0, 0x28, 0x7279, 0x29, 0x0, 0x28, 0x76e3, 0x29, 0x0, 0x28, 0x793e, 0x29, 0x0, 0x28, 0x795d, 0x29, 0x0, 0x28, 0x796d, 0x29, 0x0, 0x28, 0x81ea, 0x29, 0x0, 0x28, 0x81f3, 0x29, 0x0, 0x28, 0x8ca1, 0x29, 0x0, 0x28, 0x8cc7, 0x29, 0x0, 0x28, 0x91d1, 0x29, 0x0, 0x29, 0x0, 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0, 0x2e, 0x0, 0x2e, 0x2e, 0x0, 0x2e, 0x2e, 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x30, 0x2c, 0x0, 0x30, 0x2e, 0x0, 0x30, 0x2044, 0x33, 0x0, 0x30, 0x70b9, 0x0, 0x31, 0x0, 0x31, 0x2c, 0x0, 0x31, 0x2e, 0x0, 0x31, 0x30, 0x0, 0x31, 0x30, 0x2e, 0x0, 0x31, 0x30, 0x65e5, 0x0, 0x31, 0x30, 0x6708, 0x0, 0x31, 0x30, 0x70b9, 0x0, 0x31, 0x31, 0x0, 0x31, 0x31, 0x2e, 0x0, 0x31, 0x31, 0x65e5, 0x0, 0x31, 0x31, 0x6708, 0x0, 0x31, 0x31, 0x70b9, 0x0, 0x31, 0x32, 0x0, 0x31, 0x32, 0x2e, 0x0, 0x31, 0x32, 0x65e5, 0x0, 0x31, 0x32, 0x6708, 0x0, 0x31, 0x32, 0x70b9, 0x0, 0x31, 0x33, 0x0, 0x31, 0x33, 0x2e, 0x0, 0x31, 0x33, 0x65e5, 0x0, 0x31, 0x33, 0x70b9, 0x0, 0x31, 0x34, 0x0, 0x31, 0x34, 0x2e, 0x0, 0x31, 0x34, 0x65e5, 0x0, 0x31, 0x34, 0x70b9, 0x0, 0x31, 0x35, 0x0, 0x31, 0x35, 0x2e, 0x0, 0x31, 0x35, 0x65e5, 0x0, 0x31, 0x35, 0x70b9, 0x0, 0x31, 0x36, 0x0, 0x31, 0x36, 0x2e, 0x0, 0x31, 0x36, 0x65e5, 0x0, 0x31, 0x36, 0x70b9, 0x0, 0x31, 0x37, 0x0, 0x31, 0x37, 0x2e, 0x0, 0x31, 0x37, 0x65e5, 0x0, 0x31, 0x37, 0x70b9, 0x0, 0x31, 0x38, 0x0, 0x31, 0x38, 0x2e, 0x0, 0x31, 0x38, 0x65e5, 0x0, 0x31, 0x38, 0x70b9, 0x0, 0x31, 0x39, 0x0, 0x31, 0x39, 0x2e, 0x0, 0x31, 0x39, 0x65e5, 0x0, 0x31, 0x39, 0x70b9, 0x0, 0x31, 0x2044, 0x0, 0x31, 0x2044, 0x31, 0x30, 0x0, 0x31, 0x2044, 0x32, 0x0, 0x31, 0x2044, 0x33, 0x0, 0x31, 0x2044, 0x34, 0x0, 0x31, 0x2044, 0x35, 0x0, 0x31, 0x2044, 0x36, 0x0, 0x31, 0x2044, 0x37, 0x0, 0x31, 0x2044, 0x38, 0x0, 0x31, 0x2044, 0x39, 0x0, 0x31, 0x65e5, 0x0, 0x31, 0x6708, 0x0, 0x31, 0x70b9, 0x0, 0x32, 0x0, 0x32, 0x2c, 0x0, 0x32, 0x2e, 0x0, 0x32, 0x30, 0x0, 0x32, 0x30, 0x2e, 0x0, 0x32, 0x30, 0x65e5, 0x0, 0x32, 0x30, 0x70b9, 0x0, 0x32, 0x31, 0x0, 0x32, 0x31, 0x65e5, 0x0, 0x32, 0x31, 0x70b9, 0x0, 0x32, 0x32, 0x0, 0x32, 0x32, 0x65e5, 0x0, 0x32, 0x32, 0x70b9, 0x0, 0x32, 0x33, 0x0, 0x32, 0x33, 0x65e5, 0x0, 0x32, 0x33, 0x70b9, 0x0, 0x32, 0x34, 0x0, 0x32, 0x34, 0x65e5, 0x0, 0x32, 0x34, 0x70b9, 0x0, 0x32, 0x35, 0x0, 0x32, 0x35, 0x65e5, 0x0, 0x32, 0x36, 0x0, 0x32, 0x36, 0x65e5, 0x0, 0x32, 0x37, 0x0, 0x32, 0x37, 0x65e5, 0x0, 0x32, 0x38, 0x0, 0x32, 0x38, 0x65e5, 0x0, 0x32, 0x39, 0x0, 0x32, 0x39, 0x65e5, 0x0, 0x32, 0x2044, 0x33, 0x0, 0x32, 0x2044, 0x35, 0x0, 0x32, 0x65e5, 0x0, 0x32, 0x6708, 0x0, 0x32, 0x70b9, 0x0, 0x33, 0x0, 0x33, 0x2c, 0x0, 0x33, 0x2e, 0x0, 0x33, 0x30, 0x0, 0x33, 0x30, 0x65e5, 0x0, 0x33, 0x31, 0x0, 0x33, 0x31, 0x65e5, 0x0, 0x33, 0x32, 0x0, 0x33, 0x33, 0x0, 0x33, 0x34, 0x0, 0x33, 0x35, 0x0, 0x33, 0x36, 0x0, 0x33, 0x37, 0x0, 0x33, 0x38, 0x0, 0x33, 0x39, 0x0, 0x33, 0x2044, 0x34, 0x0, 0x33, 0x2044, 0x35, 0x0, 0x33, 0x2044, 0x38, 0x0, 0x33, 0x65e5, 0x0, 0x33, 0x6708, 0x0, 0x33, 0x70b9, 0x0, 0x34, 0x0, 0x34, 0x2c, 0x0, 0x34, 0x2e, 0x0, 0x34, 0x30, 0x0, 0x34, 0x31, 0x0, 0x34, 0x32, 0x0, 0x34, 0x33, 0x0, 0x34, 0x34, 0x0, 0x34, 0x35, 0x0, 0x34, 0x36, 0x0, 0x34, 0x37, 0x0, 0x34, 0x38, 0x0, 0x34, 0x39, 0x0, 0x34, 0x2044, 0x35, 0x0, 0x34, 0x65e5, 0x0, 0x34, 0x6708, 0x0, 0x34, 0x70b9, 0x0, 0x35, 0x0, 0x35, 0x2c, 0x0, 0x35, 0x2e, 0x0, 0x35, 0x30, 0x0, 0x35, 0x2044, 0x36, 0x0, 0x35, 0x2044, 0x38, 0x0, 0x35, 0x65e5, 0x0, 0x35, 0x6708, 0x0, 0x35, 0x70b9, 0x0, 0x36, 0x0, 0x36, 0x2c, 0x0, 0x36, 0x2e, 0x0, 0x36, 0x65e5, 0x0, 0x36, 0x6708, 0x0, 0x36, 0x70b9, 0x0, 0x37, 0x0, 0x37, 0x2c, 0x0, 0x37, 0x2e, 0x0, 0x37, 0x2044, 0x38, 0x0, 0x37, 0x65e5, 0x0, 0x37, 0x6708, 0x0, 0x37, 0x70b9, 0x0, 0x38, 0x0, 0x38, 0x2c, 0x0, 0x38, 0x2e, 0x0, 0x38, 0x65e5, 0x0, 0x38, 0x6708, 0x0, 0x38, 0x70b9, 0x0, 0x39, 0x0, 0x39, 0x2c, 0x0, 0x39, 0x2e, 0x0, 0x39, 0x65e5, 0x0, 0x39, 0x6708, 0x0, 0x39, 0x70b9, 0x0, 0x3a, 0x0, 0x3a, 0x3a, 0x3d, 0x0, 0x3b, 0x0, 0x3c, 0x0, 0x3c, 0x338, 0x0, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x3d, 0x3d, 0x3d, 0x0, 0x3d, 0x338, 0x0, 0x3e, 0x0, 0x3e, 0x338, 0x0, 0x3f, 0x0, 0x3f, 0x21, 0x0, 0x3f, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, 0x41, 0x55, 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, 0x0, 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, 0x302, 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, 0x41, 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, 0x41, 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, 0x309, 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, 0x308, 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, 0x30a, 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, 0x30f, 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, 0x302, 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, 0x328, 0x0, 0x41, 0x2215, 0x6d, 0x0, 0x42, 0x0, 0x42, 0x71, 0x0, 0x42, 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, 0x0, 0x43, 0x0, 0x43, 0x44, 0x0, 0x43, 0x6f, 0x2e, 0x0, 0x43, 0x301, 0x0, 0x43, 0x302, 0x0, 0x43, 0x307, 0x0, 0x43, 0x30c, 0x0, 0x43, 0x327, 0x0, 0x43, 0x327, 0x301, 0x0, 0x43, 0x2215, 0x6b, 0x67, 0x0, 0x44, 0x0, 0x44, 0x4a, 0x0, 0x44, 0x5a, 0x0, 0x44, 0x5a, 0x30c, 0x0, 0x44, 0x7a, 0x0, 0x44, 0x7a, 0x30c, 0x0, 0x44, 0x307, 0x0, 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, 0x327, 0x0, 0x44, 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x0, 0x45, 0x300, 0x0, 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, 0x0, 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, 0x302, 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, 0x304, 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, 0x45, 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, 0x30c, 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, 0x0, 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, 0x306, 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, 0x0, 0x46, 0x0, 0x46, 0x41, 0x58, 0x0, 0x46, 0x307, 0x0, 0x47, 0x0, 0x47, 0x42, 0x0, 0x47, 0x48, 0x7a, 0x0, 0x47, 0x50, 0x61, 0x0, 0x47, 0x79, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, 0x47, 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, 0x30c, 0x0, 0x47, 0x327, 0x0, 0x48, 0x0, 0x48, 0x50, 0x0, 0x48, 0x56, 0x0, 0x48, 0x67, 0x0, 0x48, 0x7a, 0x0, 0x48, 0x302, 0x0, 0x48, 0x307, 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, 0x0, 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x0, 0x49, 0x49, 0x0, 0x49, 0x49, 0x49, 0x0, 0x49, 0x4a, 0x0, 0x49, 0x55, 0x0, 0x49, 0x56, 0x0, 0x49, 0x58, 0x0, 0x49, 0x300, 0x0, 0x49, 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x0, 0x4a, 0x302, 0x0, 0x4b, 0x0, 0x4b, 0x42, 0x0, 0x4b, 0x4b, 0x0, 0x4b, 0x4d, 0x0, 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x0, 0x4c, 0x4a, 0x0, 0x4c, 0x54, 0x44, 0x0, 0x4c, 0x6a, 0x0, 0x4c, 0xb7, 0x0, 0x4c, 0x301, 0x0, 0x4c, 0x30c, 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, 0x0, 0x4c, 0x327, 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, 0x4d, 0x0, 0x4d, 0x42, 0x0, 0x4d, 0x43, 0x0, 0x4d, 0x44, 0x0, 0x4d, 0x48, 0x7a, 0x0, 0x4d, 0x50, 0x61, 0x0, 0x4d, 0x56, 0x0, 0x4d, 0x57, 0x0, 0x4d, 0x301, 0x0, 0x4d, 0x307, 0x0, 0x4d, 0x323, 0x0, 0x4d, 0x3a9, 0x0, 0x4e, 0x0, 0x4e, 0x4a, 0x0, 0x4e, 0x6a, 0x0, 0x4e, 0x6f, 0x0, 0x4e, 0x300, 0x0, 0x4e, 0x301, 0x0, 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, 0x0, 0x4e, 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, 0x4e, 0x331, 0x0, 0x4f, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, 0x328, 0x304, 0x0, 0x50, 0x0, 0x50, 0x48, 0x0, 0x50, 0x50, 0x4d, 0x0, 0x50, 0x50, 0x56, 0x0, 0x50, 0x52, 0x0, 0x50, 0x54, 0x45, 0x0, 0x50, 0x61, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, 0x51, 0x0, 0x52, 0x0, 0x52, 0x73, 0x0, 0x52, 0x301, 0x0, 0x52, 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, 0x0, 0x52, 0x311, 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, 0x0, 0x52, 0x327, 0x0, 0x52, 0x331, 0x0, 0x53, 0x0, 0x53, 0x44, 0x0, 0x53, 0x4d, 0x0, 0x53, 0x53, 0x0, 0x53, 0x76, 0x0, 0x53, 0x301, 0x0, 0x53, 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, 0x53, 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, 0x53, 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, 0x54, 0x0, 0x54, 0x45, 0x4c, 0x0, 0x54, 0x48, 0x7a, 0x0, 0x54, 0x4d, 0x0, 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, 0x54, 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, 0x331, 0x0, 0x55, 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, 0x55, 0x302, 0x0, 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, 0x55, 0x304, 0x0, 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, 0x55, 0x308, 0x0, 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, 0x0, 0x55, 0x308, 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, 0x309, 0x0, 0x55, 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, 0x0, 0x55, 0x30f, 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, 0x55, 0x31b, 0x300, 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, 0x303, 0x0, 0x55, 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, 0x55, 0x323, 0x0, 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, 0x32d, 0x0, 0x55, 0x330, 0x0, 0x56, 0x0, 0x56, 0x49, 0x0, 0x56, 0x49, 0x49, 0x0, 0x56, 0x49, 0x49, 0x49, 0x0, 0x56, 0x303, 0x0, 0x56, 0x323, 0x0, 0x56, 0x2215, 0x6d, 0x0, 0x57, 0x0, 0x57, 0x43, 0x0, 0x57, 0x5a, 0x0, 0x57, 0x62, 0x0, 0x57, 0x300, 0x0, 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, 0x57, 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x0, 0x58, 0x49, 0x0, 0x58, 0x49, 0x49, 0x0, 0x58, 0x307, 0x0, 0x58, 0x308, 0x0, 0x59, 0x0, 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, 0x0, 0x59, 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, 0x59, 0x308, 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, 0x0, 0x5a, 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x5b, 0x0, 0x5c, 0x0, 0x5d, 0x0, 0x5e, 0x0, 0x5f, 0x0, 0x60, 0x0, 0x61, 0x0, 0x61, 0x2e, 0x6d, 0x2e, 0x0, 0x61, 0x2f, 0x63, 0x0, 0x61, 0x2f, 0x73, 0x0, 0x61, 0x2be, 0x0, 0x61, 0x300, 0x0, 0x61, 0x301, 0x0, 0x61, 0x302, 0x0, 0x61, 0x302, 0x300, 0x0, 0x61, 0x302, 0x301, 0x0, 0x61, 0x302, 0x303, 0x0, 0x61, 0x302, 0x309, 0x0, 0x61, 0x303, 0x0, 0x61, 0x304, 0x0, 0x61, 0x306, 0x0, 0x61, 0x306, 0x300, 0x0, 0x61, 0x306, 0x301, 0x0, 0x61, 0x306, 0x303, 0x0, 0x61, 0x306, 0x309, 0x0, 0x61, 0x307, 0x0, 0x61, 0x307, 0x304, 0x0, 0x61, 0x308, 0x0, 0x61, 0x308, 0x304, 0x0, 0x61, 0x309, 0x0, 0x61, 0x30a, 0x0, 0x61, 0x30a, 0x301, 0x0, 0x61, 0x30c, 0x0, 0x61, 0x30f, 0x0, 0x61, 0x311, 0x0, 0x61, 0x323, 0x0, 0x61, 0x323, 0x302, 0x0, 0x61, 0x323, 0x306, 0x0, 0x61, 0x325, 0x0, 0x61, 0x328, 0x0, 0x62, 0x0, 0x62, 0x61, 0x72, 0x0, 0x62, 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x0, 0x63, 0x2f, 0x6f, 0x0, 0x63, 0x2f, 0x75, 0x0, 0x63, 0x61, 0x6c, 0x0, 0x63, 0x63, 0x0, 0x63, 0x64, 0x0, 0x63, 0x6d, 0x0, 0x63, 0x6d, 0x32, 0x0, 0x63, 0x6d, 0x33, 0x0, 0x63, 0x301, 0x0, 0x63, 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, 0x63, 0x327, 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x0, 0x64, 0x42, 0x0, 0x64, 0x61, 0x0, 0x64, 0x6c, 0x0, 0x64, 0x6d, 0x0, 0x64, 0x6d, 0x32, 0x0, 0x64, 0x6d, 0x33, 0x0, 0x64, 0x7a, 0x0, 0x64, 0x7a, 0x30c, 0x0, 0x64, 0x307, 0x0, 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, 0x64, 0x327, 0x0, 0x64, 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, 0x0, 0x65, 0x56, 0x0, 0x65, 0x72, 0x67, 0x0, 0x65, 0x300, 0x0, 0x65, 0x301, 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, 0x65, 0x302, 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, 0x309, 0x0, 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, 0x300, 0x0, 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, 0x307, 0x0, 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, 0x0, 0x65, 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, 0x65, 0x323, 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, 0x0, 0x65, 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, 0x66, 0x0, 0x66, 0x66, 0x0, 0x66, 0x66, 0x69, 0x0, 0x66, 0x66, 0x6c, 0x0, 0x66, 0x69, 0x0, 0x66, 0x6c, 0x0, 0x66, 0x6d, 0x0, 0x66, 0x307, 0x0, 0x67, 0x0, 0x67, 0x61, 0x6c, 0x0, 0x67, 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, 0x67, 0x306, 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, 0x327, 0x0, 0x68, 0x0, 0x68, 0x50, 0x61, 0x0, 0x68, 0x61, 0x0, 0x68, 0x302, 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, 0x0, 0x68, 0x30c, 0x0, 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, 0x68, 0x32e, 0x0, 0x68, 0x331, 0x0, 0x69, 0x0, 0x69, 0x69, 0x0, 0x69, 0x69, 0x69, 0x0, 0x69, 0x6a, 0x0, 0x69, 0x6e, 0x0, 0x69, 0x76, 0x0, 0x69, 0x78, 0x0, 0x69, 0x300, 0x0, 0x69, 0x301, 0x0, 0x69, 0x302, 0x0, 0x69, 0x303, 0x0, 0x69, 0x304, 0x0, 0x69, 0x306, 0x0, 0x69, 0x308, 0x0, 0x69, 0x308, 0x301, 0x0, 0x69, 0x309, 0x0, 0x69, 0x30c, 0x0, 0x69, 0x30f, 0x0, 0x69, 0x311, 0x0, 0x69, 0x323, 0x0, 0x69, 0x328, 0x0, 0x69, 0x330, 0x0, 0x6a, 0x0, 0x6a, 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x0, 0x6b, 0x41, 0x0, 0x6b, 0x48, 0x7a, 0x0, 0x6b, 0x50, 0x61, 0x0, 0x6b, 0x56, 0x0, 0x6b, 0x57, 0x0, 0x6b, 0x63, 0x61, 0x6c, 0x0, 0x6b, 0x67, 0x0, 0x6b, 0x6c, 0x0, 0x6b, 0x6d, 0x0, 0x6b, 0x6d, 0x32, 0x0, 0x6b, 0x6d, 0x33, 0x0, 0x6b, 0x74, 0x0, 0x6b, 0x301, 0x0, 0x6b, 0x30c, 0x0, 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, 0x6b, 0x331, 0x0, 0x6b, 0x3a9, 0x0, 0x6c, 0x0, 0x6c, 0x6a, 0x0, 0x6c, 0x6d, 0x0, 0x6c, 0x6e, 0x0, 0x6c, 0x6f, 0x67, 0x0, 0x6c, 0x78, 0x0, 0x6c, 0xb7, 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, 0x323, 0x0, 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, 0x32d, 0x0, 0x6c, 0x331, 0x0, 0x6d, 0x0, 0x6d, 0x32, 0x0, 0x6d, 0x33, 0x0, 0x6d, 0x41, 0x0, 0x6d, 0x56, 0x0, 0x6d, 0x57, 0x0, 0x6d, 0x62, 0x0, 0x6d, 0x67, 0x0, 0x6d, 0x69, 0x6c, 0x0, 0x6d, 0x6c, 0x0, 0x6d, 0x6d, 0x0, 0x6d, 0x6d, 0x32, 0x0, 0x6d, 0x6d, 0x33, 0x0, 0x6d, 0x6f, 0x6c, 0x0, 0x6d, 0x73, 0x0, 0x6d, 0x301, 0x0, 0x6d, 0x307, 0x0, 0x6d, 0x323, 0x0, 0x6d, 0x2215, 0x73, 0x0, 0x6d, 0x2215, 0x73, 0x32, 0x0, 0x6e, 0x0, 0x6e, 0x41, 0x0, 0x6e, 0x46, 0x0, 0x6e, 0x56, 0x0, 0x6e, 0x57, 0x0, 0x6e, 0x6a, 0x0, 0x6e, 0x6d, 0x0, 0x6e, 0x73, 0x0, 0x6e, 0x300, 0x0, 0x6e, 0x301, 0x0, 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, 0x0, 0x6e, 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, 0x6e, 0x331, 0x0, 0x6f, 0x0, 0x6f, 0x56, 0x0, 0x6f, 0x300, 0x0, 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, 0x6f, 0x302, 0x300, 0x0, 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, 0x303, 0x0, 0x6f, 0x302, 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, 0x303, 0x301, 0x0, 0x6f, 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, 0x0, 0x6f, 0x304, 0x0, 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, 0x301, 0x0, 0x6f, 0x306, 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, 0x304, 0x0, 0x6f, 0x308, 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, 0x309, 0x0, 0x6f, 0x30b, 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, 0x0, 0x6f, 0x311, 0x0, 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, 0x0, 0x6f, 0x31b, 0x301, 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, 0x31b, 0x309, 0x0, 0x6f, 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, 0x6f, 0x323, 0x302, 0x0, 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, 0x0, 0x70, 0x0, 0x70, 0x2e, 0x6d, 0x2e, 0x0, 0x70, 0x41, 0x0, 0x70, 0x46, 0x0, 0x70, 0x56, 0x0, 0x70, 0x57, 0x0, 0x70, 0x63, 0x0, 0x70, 0x73, 0x0, 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x71, 0x0, 0x72, 0x0, 0x72, 0x61, 0x64, 0x0, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x0, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x32, 0x0, 0x72, 0x301, 0x0, 0x72, 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, 0x311, 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, 0x327, 0x0, 0x72, 0x331, 0x0, 0x73, 0x0, 0x73, 0x72, 0x0, 0x73, 0x74, 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, 0x307, 0x0, 0x73, 0x302, 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, 0x0, 0x73, 0x30c, 0x307, 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, 0x307, 0x0, 0x73, 0x326, 0x0, 0x73, 0x327, 0x0, 0x74, 0x0, 0x74, 0x307, 0x0, 0x74, 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, 0x74, 0x326, 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, 0x331, 0x0, 0x75, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, 0x75, 0x330, 0x0, 0x76, 0x0, 0x76, 0x69, 0x0, 0x76, 0x69, 0x69, 0x0, 0x76, 0x69, 0x69, 0x69, 0x0, 0x76, 0x303, 0x0, 0x76, 0x323, 0x0, 0x77, 0x0, 0x77, 0x300, 0x0, 0x77, 0x301, 0x0, 0x77, 0x302, 0x0, 0x77, 0x307, 0x0, 0x77, 0x308, 0x0, 0x77, 0x30a, 0x0, 0x77, 0x323, 0x0, 0x78, 0x0, 0x78, 0x69, 0x0, 0x78, 0x69, 0x69, 0x0, 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x0, 0x79, 0x300, 0x0, 0x79, 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, 0x303, 0x0, 0x79, 0x304, 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, 0x0, 0x79, 0x309, 0x0, 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, 0x7a, 0x0, 0x7a, 0x301, 0x0, 0x7a, 0x302, 0x0, 0x7a, 0x307, 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, 0x0, 0x7a, 0x331, 0x0, 0x7b, 0x0, 0x7c, 0x0, 0x7d, 0x0, 0x7e, 0x0, 0xa2, 0x0, 0xa3, 0x0, 0xa5, 0x0, 0xa6, 0x0, 0xac, 0x0, 0xb0, 0x43, 0x0, 0xb0, 0x46, 0x0, 0xb7, 0x0, 0xc6, 0x0, 0xc6, 0x301, 0x0, 0xc6, 0x304, 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, 0x0, 0xf0, 0x0, 0xf8, 0x301, 0x0, 0x126, 0x0, 0x127, 0x0, 0x131, 0x0, 0x14b, 0x0, 0x153, 0x0, 0x18e, 0x0, 0x190, 0x0, 0x1ab, 0x0, 0x1b7, 0x30c, 0x0, 0x222, 0x0, 0x237, 0x0, 0x250, 0x0, 0x251, 0x0, 0x252, 0x0, 0x254, 0x0, 0x255, 0x0, 0x259, 0x0, 0x25b, 0x0, 0x25c, 0x0, 0x25f, 0x0, 0x261, 0x0, 0x263, 0x0, 0x265, 0x0, 0x266, 0x0, 0x268, 0x0, 0x269, 0x0, 0x26a, 0x0, 0x26d, 0x0, 0x26f, 0x0, 0x270, 0x0, 0x271, 0x0, 0x272, 0x0, 0x273, 0x0, 0x274, 0x0, 0x275, 0x0, 0x278, 0x0, 0x279, 0x0, 0x27b, 0x0, 0x281, 0x0, 0x282, 0x0, 0x283, 0x0, 0x289, 0x0, 0x28a, 0x0, 0x28b, 0x0, 0x28c, 0x0, 0x290, 0x0, 0x291, 0x0, 0x292, 0x0, 0x292, 0x30c, 0x0, 0x295, 0x0, 0x29d, 0x0, 0x29f, 0x0, 0x2b9, 0x0, 0x2bc, 0x6e, 0x0, 0x300, 0x0, 0x301, 0x0, 0x308, 0x301, 0x0, 0x313, 0x0, 0x391, 0x0, 0x391, 0x300, 0x0, 0x391, 0x301, 0x0, 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, 0x313, 0x0, 0x391, 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, 0x345, 0x0, 0x391, 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, 0x345, 0x0, 0x391, 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, 0x345, 0x0, 0x391, 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, 0x314, 0x300, 0x0, 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, 0x314, 0x301, 0x0, 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, 0x314, 0x342, 0x0, 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, 0x314, 0x345, 0x0, 0x391, 0x345, 0x0, 0x392, 0x0, 0x393, 0x0, 0x394, 0x0, 0x395, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, 0x395, 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, 0x301, 0x0, 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, 0x314, 0x301, 0x0, 0x396, 0x0, 0x397, 0x0, 0x397, 0x300, 0x0, 0x397, 0x301, 0x0, 0x397, 0x313, 0x0, 0x397, 0x313, 0x300, 0x0, 0x397, 0x313, 0x300, 0x345, 0x0, 0x397, 0x313, 0x301, 0x0, 0x397, 0x313, 0x301, 0x345, 0x0, 0x397, 0x313, 0x342, 0x0, 0x397, 0x313, 0x342, 0x345, 0x0, 0x397, 0x313, 0x345, 0x0, 0x397, 0x314, 0x0, 0x397, 0x314, 0x300, 0x0, 0x397, 0x314, 0x300, 0x345, 0x0, 0x397, 0x314, 0x301, 0x0, 0x397, 0x314, 0x301, 0x345, 0x0, 0x397, 0x314, 0x342, 0x0, 0x397, 0x314, 0x342, 0x345, 0x0, 0x397, 0x314, 0x345, 0x0, 0x397, 0x345, 0x0, 0x398, 0x0, 0x399, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, 0x0, 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, 0x399, 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, 0x301, 0x0, 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, 0x314, 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, 0x342, 0x0, 0x39a, 0x0, 0x39b, 0x0, 0x39c, 0x0, 0x39d, 0x0, 0x39e, 0x0, 0x39f, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, 0x3a0, 0x0, 0x3a1, 0x0, 0x3a1, 0x314, 0x0, 0x3a3, 0x0, 0x3a4, 0x0, 0x3a5, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, 0x314, 0x342, 0x0, 0x3a6, 0x0, 0x3a7, 0x0, 0x3a8, 0x0, 0x3a9, 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, 0x0, 0x3a9, 0x313, 0x0, 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, 0x300, 0x345, 0x0, 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, 0x301, 0x345, 0x0, 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, 0x342, 0x345, 0x0, 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, 0x3a9, 0x314, 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, 0x3a9, 0x314, 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, 0x3a9, 0x314, 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, 0x3a9, 0x314, 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x0, 0x3b1, 0x300, 0x0, 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, 0x345, 0x0, 0x3b2, 0x0, 0x3b3, 0x0, 0x3b4, 0x0, 0x3b5, 0x0, 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, 0x0, 0x3b5, 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, 0x314, 0x0, 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, 0x3b6, 0x0, 0x3b7, 0x0, 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, 0x301, 0x0, 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, 0x313, 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, 0x313, 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, 0x313, 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, 0x3b7, 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, 0x3b7, 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, 0x3b7, 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, 0x3b7, 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, 0x3b8, 0x0, 0x3b9, 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, 0x3b9, 0x304, 0x0, 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, 0x308, 0x300, 0x0, 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, 0x342, 0x0, 0x3b9, 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, 0x313, 0x301, 0x0, 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, 0x3b9, 0x314, 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, 0x314, 0x342, 0x0, 0x3b9, 0x342, 0x0, 0x3ba, 0x0, 0x3bb, 0x0, 0x3bc, 0x0, 0x3bc, 0x41, 0x0, 0x3bc, 0x46, 0x0, 0x3bc, 0x56, 0x0, 0x3bc, 0x57, 0x0, 0x3bc, 0x67, 0x0, 0x3bc, 0x6c, 0x0, 0x3bc, 0x6d, 0x0, 0x3bc, 0x73, 0x0, 0x3bd, 0x0, 0x3be, 0x0, 0x3bf, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, 0x313, 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, 0x3bf, 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, 0x301, 0x0, 0x3c0, 0x0, 0x3c1, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, 0x3c2, 0x0, 0x3c3, 0x0, 0x3c4, 0x0, 0x3c5, 0x0, 0x3c5, 0x300, 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, 0x3c6, 0x0, 0x3c7, 0x0, 0x3c8, 0x0, 0x3c9, 0x0, 0x3c9, 0x300, 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, 0x3c9, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, 0x300, 0x0, 0x3c9, 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, 0x301, 0x0, 0x3c9, 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x342, 0x0, 0x3c9, 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, 0x345, 0x0, 0x3c9, 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, 0x314, 0x300, 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, 0x314, 0x301, 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, 0x314, 0x342, 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, 0x342, 0x0, 0x3c9, 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3dc, 0x0, 0x3dd, 0x0, 0x406, 0x308, 0x0, 0x410, 0x306, 0x0, 0x410, 0x308, 0x0, 0x413, 0x301, 0x0, 0x415, 0x300, 0x0, 0x415, 0x306, 0x0, 0x415, 0x308, 0x0, 0x416, 0x306, 0x0, 0x416, 0x308, 0x0, 0x417, 0x308, 0x0, 0x418, 0x300, 0x0, 0x418, 0x304, 0x0, 0x418, 0x306, 0x0, 0x418, 0x308, 0x0, 0x41a, 0x301, 0x0, 0x41e, 0x308, 0x0, 0x423, 0x304, 0x0, 0x423, 0x306, 0x0, 0x423, 0x308, 0x0, 0x423, 0x30b, 0x0, 0x427, 0x308, 0x0, 0x42b, 0x308, 0x0, 0x42d, 0x308, 0x0, 0x430, 0x306, 0x0, 0x430, 0x308, 0x0, 0x433, 0x301, 0x0, 0x435, 0x300, 0x0, 0x435, 0x306, 0x0, 0x435, 0x308, 0x0, 0x436, 0x306, 0x0, 0x436, 0x308, 0x0, 0x437, 0x308, 0x0, 0x438, 0x300, 0x0, 0x438, 0x304, 0x0, 0x438, 0x306, 0x0, 0x438, 0x308, 0x0, 0x43a, 0x301, 0x0, 0x43d, 0x0, 0x43e, 0x308, 0x0, 0x443, 0x304, 0x0, 0x443, 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, 0x0, 0x447, 0x308, 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, 0x456, 0x308, 0x0, 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, 0x308, 0x0, 0x4d9, 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, 0x0, 0x565, 0x582, 0x0, 0x574, 0x565, 0x0, 0x574, 0x56b, 0x0, 0x574, 0x56d, 0x0, 0x574, 0x576, 0x0, 0x57e, 0x576, 0x0, 0x5d0, 0x0, 0x5d0, 0x5b7, 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, 0x5d0, 0x5dc, 0x0, 0x5d1, 0x0, 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x0, 0x5d2, 0x5bc, 0x0, 0x5d3, 0x0, 0x5d3, 0x5bc, 0x0, 0x5d4, 0x0, 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, 0x0, 0x5d6, 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, 0x5d9, 0x5bc, 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, 0x5bf, 0x0, 0x5dc, 0x0, 0x5dc, 0x5bc, 0x0, 0x5dd, 0x0, 0x5de, 0x5bc, 0x0, 0x5e0, 0x5bc, 0x0, 0x5e1, 0x5bc, 0x0, 0x5e2, 0x0, 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, 0x5e4, 0x5bf, 0x0, 0x5e6, 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, 0x0, 0x5e8, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, 0x5bc, 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, 0x0, 0x5ea, 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x621, 0x0, 0x627, 0x0, 0x627, 0x643, 0x628, 0x631, 0x0, 0x627, 0x644, 0x644, 0x647, 0x0, 0x627, 0x64b, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, 0x0, 0x627, 0x655, 0x0, 0x627, 0x674, 0x0, 0x628, 0x0, 0x628, 0x62c, 0x0, 0x628, 0x62d, 0x0, 0x628, 0x62d, 0x64a, 0x0, 0x628, 0x62e, 0x0, 0x628, 0x62e, 0x64a, 0x0, 0x628, 0x631, 0x0, 0x628, 0x632, 0x0, 0x628, 0x645, 0x0, 0x628, 0x646, 0x0, 0x628, 0x647, 0x0, 0x628, 0x649, 0x0, 0x628, 0x64a, 0x0, 0x629, 0x0, 0x62a, 0x0, 0x62a, 0x62c, 0x0, 0x62a, 0x62c, 0x645, 0x0, 0x62a, 0x62c, 0x649, 0x0, 0x62a, 0x62c, 0x64a, 0x0, 0x62a, 0x62d, 0x0, 0x62a, 0x62d, 0x62c, 0x0, 0x62a, 0x62d, 0x645, 0x0, 0x62a, 0x62e, 0x0, 0x62a, 0x62e, 0x645, 0x0, 0x62a, 0x62e, 0x649, 0x0, 0x62a, 0x62e, 0x64a, 0x0, 0x62a, 0x631, 0x0, 0x62a, 0x632, 0x0, 0x62a, 0x645, 0x0, 0x62a, 0x645, 0x62c, 0x0, 0x62a, 0x645, 0x62d, 0x0, 0x62a, 0x645, 0x62e, 0x0, 0x62a, 0x645, 0x649, 0x0, 0x62a, 0x645, 0x64a, 0x0, 0x62a, 0x646, 0x0, 0x62a, 0x647, 0x0, 0x62a, 0x649, 0x0, 0x62a, 0x64a, 0x0, 0x62b, 0x0, 0x62b, 0x62c, 0x0, 0x62b, 0x631, 0x0, 0x62b, 0x632, 0x0, 0x62b, 0x645, 0x0, 0x62b, 0x646, 0x0, 0x62b, 0x647, 0x0, 0x62b, 0x649, 0x0, 0x62b, 0x64a, 0x0, 0x62c, 0x0, 0x62c, 0x62d, 0x0, 0x62c, 0x62d, 0x649, 0x0, 0x62c, 0x62d, 0x64a, 0x0, 0x62c, 0x644, 0x20, 0x62c, 0x644, 0x627, 0x644, 0x647, 0x0, 0x62c, 0x645, 0x0, 0x62c, 0x645, 0x62d, 0x0, 0x62c, 0x645, 0x649, 0x0, 0x62c, 0x645, 0x64a, 0x0, 0x62c, 0x649, 0x0, 0x62c, 0x64a, 0x0, 0x62d, 0x0, 0x62d, 0x62c, 0x0, 0x62d, 0x62c, 0x64a, 0x0, 0x62d, 0x645, 0x0, 0x62d, 0x645, 0x649, 0x0, 0x62d, 0x645, 0x64a, 0x0, 0x62d, 0x649, 0x0, 0x62d, 0x64a, 0x0, 0x62e, 0x0, 0x62e, 0x62c, 0x0, 0x62e, 0x62d, 0x0, 0x62e, 0x645, 0x0, 0x62e, 0x649, 0x0, 0x62e, 0x64a, 0x0, 0x62f, 0x0, 0x630, 0x0, 0x630, 0x670, 0x0, 0x631, 0x0, 0x631, 0x633, 0x648, 0x644, 0x0, 0x631, 0x670, 0x0, 0x631, 0x6cc, 0x627, 0x644, 0x0, 0x632, 0x0, 0x633, 0x0, 0x633, 0x62c, 0x0, 0x633, 0x62c, 0x62d, 0x0, 0x633, 0x62c, 0x649, 0x0, 0x633, 0x62d, 0x0, 0x633, 0x62d, 0x62c, 0x0, 0x633, 0x62e, 0x0, 0x633, 0x62e, 0x649, 0x0, 0x633, 0x62e, 0x64a, 0x0, 0x633, 0x631, 0x0, 0x633, 0x645, 0x0, 0x633, 0x645, 0x62c, 0x0, 0x633, 0x645, 0x62d, 0x0, 0x633, 0x645, 0x645, 0x0, 0x633, 0x647, 0x0, 0x633, 0x649, 0x0, 0x633, 0x64a, 0x0, 0x634, 0x0, 0x634, 0x62c, 0x0, 0x634, 0x62c, 0x64a, 0x0, 0x634, 0x62d, 0x0, 0x634, 0x62d, 0x645, 0x0, 0x634, 0x62d, 0x64a, 0x0, 0x634, 0x62e, 0x0, 0x634, 0x631, 0x0, 0x634, 0x645, 0x0, 0x634, 0x645, 0x62e, 0x0, 0x634, 0x645, 0x645, 0x0, 0x634, 0x647, 0x0, 0x634, 0x649, 0x0, 0x634, 0x64a, 0x0, 0x635, 0x0, 0x635, 0x62d, 0x0, 0x635, 0x62d, 0x62d, 0x0, 0x635, 0x62d, 0x64a, 0x0, 0x635, 0x62e, 0x0, 0x635, 0x631, 0x0, 0x635, 0x644, 0x639, 0x645, 0x0, 0x635, 0x644, 0x649, 0x0, 0x635, 0x644, 0x649, 0x20, 0x627, 0x644, 0x644, 0x647, 0x20, 0x639, 0x644, 0x64a, 0x647, 0x20, 0x648, 0x633, 0x644, 0x645, 0x0, 0x635, 0x644, 0x6d2, 0x0, 0x635, 0x645, 0x0, 0x635, 0x645, 0x645, 0x0, 0x635, 0x649, 0x0, 0x635, 0x64a, 0x0, 0x636, 0x0, 0x636, 0x62c, 0x0, 0x636, 0x62d, 0x0, 0x636, 0x62d, 0x649, 0x0, 0x636, 0x62d, 0x64a, 0x0, 0x636, 0x62e, 0x0, 0x636, 0x62e, 0x645, 0x0, 0x636, 0x631, 0x0, 0x636, 0x645, 0x0, 0x636, 0x649, 0x0, 0x636, 0x64a, 0x0, 0x637, 0x0, 0x637, 0x62d, 0x0, 0x637, 0x645, 0x0, 0x637, 0x645, 0x62d, 0x0, 0x637, 0x645, 0x645, 0x0, 0x637, 0x645, 0x64a, 0x0, 0x637, 0x649, 0x0, 0x637, 0x64a, 0x0, 0x638, 0x0, 0x638, 0x645, 0x0, 0x639, 0x0, 0x639, 0x62c, 0x0, 0x639, 0x62c, 0x645, 0x0, 0x639, 0x644, 0x64a, 0x647, 0x0, 0x639, 0x645, 0x0, 0x639, 0x645, 0x645, 0x0, 0x639, 0x645, 0x649, 0x0, 0x639, 0x645, 0x64a, 0x0, 0x639, 0x649, 0x0, 0x639, 0x64a, 0x0, 0x63a, 0x0, 0x63a, 0x62c, 0x0, 0x63a, 0x645, 0x0, 0x63a, 0x645, 0x645, 0x0, 0x63a, 0x645, 0x649, 0x0, 0x63a, 0x645, 0x64a, 0x0, 0x63a, 0x649, 0x0, 0x63a, 0x64a, 0x0, 0x640, 0x64b, 0x0, 0x640, 0x64e, 0x0, 0x640, 0x64e, 0x651, 0x0, 0x640, 0x64f, 0x0, 0x640, 0x64f, 0x651, 0x0, 0x640, 0x650, 0x0, 0x640, 0x650, 0x651, 0x0, 0x640, 0x651, 0x0, 0x640, 0x652, 0x0, 0x641, 0x0, 0x641, 0x62c, 0x0, 0x641, 0x62d, 0x0, 0x641, 0x62e, 0x0, 0x641, 0x62e, 0x645, 0x0, 0x641, 0x645, 0x0, 0x641, 0x645, 0x64a, 0x0, 0x641, 0x649, 0x0, 0x641, 0x64a, 0x0, 0x642, 0x0, 0x642, 0x62d, 0x0, 0x642, 0x644, 0x6d2, 0x0, 0x642, 0x645, 0x0, 0x642, 0x645, 0x62d, 0x0, 0x642, 0x645, 0x645, 0x0, 0x642, 0x645, 0x64a, 0x0, 0x642, 0x649, 0x0, 0x642, 0x64a, 0x0, 0x643, 0x0, 0x643, 0x627, 0x0, 0x643, 0x62c, 0x0, 0x643, 0x62d, 0x0, 0x643, 0x62e, 0x0, 0x643, 0x644, 0x0, 0x643, 0x645, 0x0, 0x643, 0x645, 0x645, 0x0, 0x643, 0x645, 0x64a, 0x0, 0x643, 0x649, 0x0, 0x643, 0x64a, 0x0, 0x644, 0x0, 0x644, 0x627, 0x0, 0x644, 0x627, 0x653, 0x0, 0x644, 0x627, 0x654, 0x0, 0x644, 0x627, 0x655, 0x0, 0x644, 0x62c, 0x0, 0x644, 0x62c, 0x62c, 0x0, 0x644, 0x62c, 0x645, 0x0, 0x644, 0x62c, 0x64a, 0x0, 0x644, 0x62d, 0x0, 0x644, 0x62d, 0x645, 0x0, 0x644, 0x62d, 0x649, 0x0, 0x644, 0x62d, 0x64a, 0x0, 0x644, 0x62e, 0x0, 0x644, 0x62e, 0x645, 0x0, 0x644, 0x645, 0x0, 0x644, 0x645, 0x62d, 0x0, 0x644, 0x645, 0x64a, 0x0, 0x644, 0x647, 0x0, 0x644, 0x649, 0x0, 0x644, 0x64a, 0x0, 0x645, 0x0, 0x645, 0x627, 0x0, 0x645, 0x62c, 0x0, 0x645, 0x62c, 0x62d, 0x0, 0x645, 0x62c, 0x62e, 0x0, 0x645, 0x62c, 0x645, 0x0, 0x645, 0x62c, 0x64a, 0x0, 0x645, 0x62d, 0x0, 0x645, 0x62d, 0x62c, 0x0, 0x645, 0x62d, 0x645, 0x0, 0x645, 0x62d, 0x645, 0x62f, 0x0, 0x645, 0x62d, 0x64a, 0x0, 0x645, 0x62e, 0x0, 0x645, 0x62e, 0x62c, 0x0, 0x645, 0x62e, 0x645, 0x0, 0x645, 0x62e, 0x64a, 0x0, 0x645, 0x645, 0x0, 0x645, 0x645, 0x64a, 0x0, 0x645, 0x649, 0x0, 0x645, 0x64a, 0x0, 0x646, 0x0, 0x646, 0x62c, 0x0, 0x646, 0x62c, 0x62d, 0x0, 0x646, 0x62c, 0x645, 0x0, 0x646, 0x62c, 0x649, 0x0, 0x646, 0x62c, 0x64a, 0x0, 0x646, 0x62d, 0x0, 0x646, 0x62d, 0x645, 0x0, 0x646, 0x62d, 0x649, 0x0, 0x646, 0x62d, 0x64a, 0x0, 0x646, 0x62e, 0x0, 0x646, 0x631, 0x0, 0x646, 0x632, 0x0, 0x646, 0x645, 0x0, 0x646, 0x645, 0x649, 0x0, 0x646, 0x645, 0x64a, 0x0, 0x646, 0x646, 0x0, 0x646, 0x647, 0x0, 0x646, 0x649, 0x0, 0x646, 0x64a, 0x0, 0x647, 0x0, 0x647, 0x62c, 0x0, 0x647, 0x645, 0x0, 0x647, 0x645, 0x62c, 0x0, 0x647, 0x645, 0x645, 0x0, 0x647, 0x649, 0x0, 0x647, 0x64a, 0x0, 0x647, 0x670, 0x0, 0x648, 0x0, 0x648, 0x633, 0x644, 0x645, 0x0, 0x648, 0x654, 0x0, 0x648, 0x674, 0x0, 0x649, 0x0, 0x649, 0x670, 0x0, 0x64a, 0x0, 0x64a, 0x62c, 0x0, 0x64a, 0x62c, 0x64a, 0x0, 0x64a, 0x62d, 0x0, 0x64a, 0x62d, 0x64a, 0x0, 0x64a, 0x62e, 0x0, 0x64a, 0x631, 0x0, 0x64a, 0x632, 0x0, 0x64a, 0x645, 0x0, 0x64a, 0x645, 0x645, 0x0, 0x64a, 0x645, 0x64a, 0x0, 0x64a, 0x646, 0x0, 0x64a, 0x647, 0x0, 0x64a, 0x649, 0x0, 0x64a, 0x64a, 0x0, 0x64a, 0x654, 0x0, 0x64a, 0x654, 0x627, 0x0, 0x64a, 0x654, 0x62c, 0x0, 0x64a, 0x654, 0x62d, 0x0, 0x64a, 0x654, 0x62e, 0x0, 0x64a, 0x654, 0x631, 0x0, 0x64a, 0x654, 0x632, 0x0, 0x64a, 0x654, 0x645, 0x0, 0x64a, 0x654, 0x646, 0x0, 0x64a, 0x654, 0x647, 0x0, 0x64a, 0x654, 0x648, 0x0, 0x64a, 0x654, 0x649, 0x0, 0x64a, 0x654, 0x64a, 0x0, 0x64a, 0x654, 0x6c6, 0x0, 0x64a, 0x654, 0x6c7, 0x0, 0x64a, 0x654, 0x6c8, 0x0, 0x64a, 0x654, 0x6d0, 0x0, 0x64a, 0x654, 0x6d5, 0x0, 0x64a, 0x674, 0x0, 0x66e, 0x0, 0x66f, 0x0, 0x671, 0x0, 0x679, 0x0, 0x67a, 0x0, 0x67b, 0x0, 0x67e, 0x0, 0x67f, 0x0, 0x680, 0x0, 0x683, 0x0, 0x684, 0x0, 0x686, 0x0, 0x687, 0x0, 0x688, 0x0, 0x68c, 0x0, 0x68d, 0x0, 0x68e, 0x0, 0x691, 0x0, 0x698, 0x0, 0x6a1, 0x0, 0x6a4, 0x0, 0x6a6, 0x0, 0x6a9, 0x0, 0x6ad, 0x0, 0x6af, 0x0, 0x6b1, 0x0, 0x6b3, 0x0, 0x6ba, 0x0, 0x6bb, 0x0, 0x6be, 0x0, 0x6c1, 0x0, 0x6c1, 0x654, 0x0, 0x6c5, 0x0, 0x6c6, 0x0, 0x6c7, 0x0, 0x6c7, 0x674, 0x0, 0x6c8, 0x0, 0x6c9, 0x0, 0x6cb, 0x0, 0x6cc, 0x0, 0x6d0, 0x0, 0x6d2, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, 0x0, 0x915, 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, 0x91c, 0x93c, 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, 0x93c, 0x0, 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, 0x0, 0x933, 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, 0x9af, 0x9bc, 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, 0xa3c, 0x0, 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, 0x0, 0xa32, 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, 0xb22, 0xb3c, 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, 0xb57, 0x0, 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, 0x0, 0xbc7, 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, 0xcc6, 0xcc2, 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, 0xcc6, 0xcd6, 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, 0xd3e, 0x0, 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, 0xdca, 0x0, 0xdd9, 0xddf, 0x0, 0xe4d, 0xe32, 0x0, 0xeab, 0xe99, 0x0, 0xeab, 0xea1, 0x0, 0xecd, 0xeb2, 0x0, 0xf0b, 0x0, 0xf40, 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, 0xfb7, 0x0, 0xf51, 0xfb7, 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, 0x0, 0xf71, 0xf72, 0x0, 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, 0xf90, 0xfb5, 0x0, 0xf92, 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, 0xfb7, 0x0, 0xfa6, 0xfb7, 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf71, 0xf80, 0x0, 0xfb2, 0xf80, 0x0, 0xfb3, 0xf71, 0xf80, 0x0, 0xfb3, 0xf80, 0x0, 0x1025, 0x102e, 0x0, 0x10dc, 0x0, 0x1100, 0x0, 0x1100, 0x1161, 0x0, 0x1101, 0x0, 0x1102, 0x0, 0x1102, 0x1161, 0x0, 0x1103, 0x0, 0x1103, 0x1161, 0x0, 0x1104, 0x0, 0x1105, 0x0, 0x1105, 0x1161, 0x0, 0x1106, 0x0, 0x1106, 0x1161, 0x0, 0x1107, 0x0, 0x1107, 0x1161, 0x0, 0x1108, 0x0, 0x1109, 0x0, 0x1109, 0x1161, 0x0, 0x110a, 0x0, 0x110b, 0x0, 0x110b, 0x1161, 0x0, 0x110b, 0x116e, 0x0, 0x110c, 0x0, 0x110c, 0x1161, 0x0, 0x110c, 0x116e, 0x110b, 0x1174, 0x0, 0x110d, 0x0, 0x110e, 0x0, 0x110e, 0x1161, 0x0, 0x110e, 0x1161, 0x11b7, 0x1100, 0x1169, 0x0, 0x110f, 0x0, 0x110f, 0x1161, 0x0, 0x1110, 0x0, 0x1110, 0x1161, 0x0, 0x1111, 0x0, 0x1111, 0x1161, 0x0, 0x1112, 0x0, 0x1112, 0x1161, 0x0, 0x1114, 0x0, 0x1115, 0x0, 0x111a, 0x0, 0x111c, 0x0, 0x111d, 0x0, 0x111e, 0x0, 0x1120, 0x0, 0x1121, 0x0, 0x1122, 0x0, 0x1123, 0x0, 0x1127, 0x0, 0x1129, 0x0, 0x112b, 0x0, 0x112c, 0x0, 0x112d, 0x0, 0x112e, 0x0, 0x112f, 0x0, 0x1132, 0x0, 0x1136, 0x0, 0x1140, 0x0, 0x1147, 0x0, 0x114c, 0x0, 0x1157, 0x0, 0x1158, 0x0, 0x1159, 0x0, 0x1160, 0x0, 0x1161, 0x0, 0x1162, 0x0, 0x1163, 0x0, 0x1164, 0x0, 0x1165, 0x0, 0x1166, 0x0, 0x1167, 0x0, 0x1168, 0x0, 0x1169, 0x0, 0x116a, 0x0, 0x116b, 0x0, 0x116c, 0x0, 0x116d, 0x0, 0x116e, 0x0, 0x116f, 0x0, 0x1170, 0x0, 0x1171, 0x0, 0x1172, 0x0, 0x1173, 0x0, 0x1174, 0x0, 0x1175, 0x0, 0x1184, 0x0, 0x1185, 0x0, 0x1188, 0x0, 0x1191, 0x0, 0x1192, 0x0, 0x1194, 0x0, 0x119e, 0x0, 0x11a1, 0x0, 0x11aa, 0x0, 0x11ac, 0x0, 0x11ad, 0x0, 0x11b0, 0x0, 0x11b1, 0x0, 0x11b2, 0x0, 0x11b3, 0x0, 0x11b4, 0x0, 0x11b5, 0x0, 0x11c7, 0x0, 0x11c8, 0x0, 0x11cc, 0x0, 0x11ce, 0x0, 0x11d3, 0x0, 0x11d7, 0x0, 0x11d9, 0x0, 0x11dd, 0x0, 0x11df, 0x0, 0x11f1, 0x0, 0x11f2, 0x0, 0x1b05, 0x1b35, 0x0, 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, 0x0, 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, 0x0, 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, 0x0, 0x1b42, 0x1b35, 0x0, 0x1d02, 0x0, 0x1d16, 0x0, 0x1d17, 0x0, 0x1d1c, 0x0, 0x1d1d, 0x0, 0x1d25, 0x0, 0x1d7b, 0x0, 0x1d85, 0x0, 0x2010, 0x0, 0x2013, 0x0, 0x2014, 0x0, 0x2032, 0x2032, 0x0, 0x2032, 0x2032, 0x2032, 0x0, 0x2032, 0x2032, 0x2032, 0x2032, 0x0, 0x2035, 0x2035, 0x0, 0x2035, 0x2035, 0x2035, 0x0, 0x20a9, 0x0, 0x2190, 0x0, 0x2190, 0x338, 0x0, 0x2191, 0x0, 0x2192, 0x0, 0x2192, 0x338, 0x0, 0x2193, 0x0, 0x2194, 0x338, 0x0, 0x21d0, 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, 0x0, 0x2202, 0x0, 0x2203, 0x338, 0x0, 0x2207, 0x0, 0x2208, 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2211, 0x0, 0x2212, 0x0, 0x2223, 0x338, 0x0, 0x2225, 0x338, 0x0, 0x222b, 0x222b, 0x0, 0x222b, 0x222b, 0x222b, 0x0, 0x222b, 0x222b, 0x222b, 0x222b, 0x0, 0x222e, 0x222e, 0x0, 0x222e, 0x222e, 0x222e, 0x0, 0x223c, 0x338, 0x0, 0x2243, 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, 0x338, 0x0, 0x224d, 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, 0x338, 0x0, 0x2265, 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, 0x338, 0x0, 0x2276, 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, 0x338, 0x0, 0x227b, 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, 0x338, 0x0, 0x2282, 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, 0x338, 0x0, 0x2287, 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, 0x338, 0x0, 0x22a2, 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, 0x338, 0x0, 0x22ab, 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, 0x338, 0x0, 0x22b4, 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2502, 0x0, 0x25a0, 0x0, 0x25cb, 0x0, 0x2985, 0x0, 0x2986, 0x0, 0x2add, 0x338, 0x0, 0x2d61, 0x0, 0x3001, 0x0, 0x3002, 0x0, 0x3008, 0x0, 0x3009, 0x0, 0x300a, 0x0, 0x300b, 0x0, 0x300c, 0x0, 0x300d, 0x0, 0x300e, 0x0, 0x300f, 0x0, 0x3010, 0x0, 0x3011, 0x0, 0x3012, 0x0, 0x3014, 0x0, 0x3014, 0x53, 0x3015, 0x0, 0x3014, 0x4e09, 0x3015, 0x0, 0x3014, 0x4e8c, 0x3015, 0x0, 0x3014, 0x52dd, 0x3015, 0x0, 0x3014, 0x5b89, 0x3015, 0x0, 0x3014, 0x6253, 0x3015, 0x0, 0x3014, 0x6557, 0x3015, 0x0, 0x3014, 0x672c, 0x3015, 0x0, 0x3014, 0x70b9, 0x3015, 0x0, 0x3014, 0x76d7, 0x3015, 0x0, 0x3015, 0x0, 0x3016, 0x0, 0x3017, 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, 0x304d, 0x3099, 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, 0x3053, 0x3099, 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, 0x3059, 0x3099, 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, 0x305f, 0x3099, 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, 0x3066, 0x3099, 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, 0x306f, 0x309a, 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, 0x3075, 0x3099, 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, 0x3078, 0x309a, 0x0, 0x307b, 0x304b, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, 0x0, 0x3088, 0x308a, 0x0, 0x3099, 0x0, 0x309a, 0x0, 0x309d, 0x3099, 0x0, 0x30a1, 0x0, 0x30a2, 0x0, 0x30a2, 0x30cf, 0x309a, 0x30fc, 0x30c8, 0x0, 0x30a2, 0x30eb, 0x30d5, 0x30a1, 0x0, 0x30a2, 0x30f3, 0x30d8, 0x309a, 0x30a2, 0x0, 0x30a2, 0x30fc, 0x30eb, 0x0, 0x30a3, 0x0, 0x30a4, 0x0, 0x30a4, 0x30cb, 0x30f3, 0x30af, 0x3099, 0x0, 0x30a4, 0x30f3, 0x30c1, 0x0, 0x30a5, 0x0, 0x30a6, 0x0, 0x30a6, 0x3099, 0x0, 0x30a6, 0x30a9, 0x30f3, 0x0, 0x30a7, 0x0, 0x30a8, 0x0, 0x30a8, 0x30b9, 0x30af, 0x30fc, 0x30c8, 0x3099, 0x0, 0x30a8, 0x30fc, 0x30ab, 0x30fc, 0x0, 0x30a9, 0x0, 0x30aa, 0x0, 0x30aa, 0x30f3, 0x30b9, 0x0, 0x30aa, 0x30fc, 0x30e0, 0x0, 0x30ab, 0x0, 0x30ab, 0x3099, 0x0, 0x30ab, 0x3099, 0x30ed, 0x30f3, 0x0, 0x30ab, 0x3099, 0x30f3, 0x30de, 0x0, 0x30ab, 0x30a4, 0x30ea, 0x0, 0x30ab, 0x30e9, 0x30c3, 0x30c8, 0x0, 0x30ab, 0x30ed, 0x30ea, 0x30fc, 0x0, 0x30ad, 0x0, 0x30ad, 0x3099, 0x0, 0x30ad, 0x3099, 0x30ab, 0x3099, 0x0, 0x30ad, 0x3099, 0x30cb, 0x30fc, 0x0, 0x30ad, 0x3099, 0x30eb, 0x30bf, 0x3099, 0x30fc, 0x0, 0x30ad, 0x30e5, 0x30ea, 0x30fc, 0x0, 0x30ad, 0x30ed, 0x0, 0x30ad, 0x30ed, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x0, 0x30ad, 0x30ed, 0x30e1, 0x30fc, 0x30c8, 0x30eb, 0x0, 0x30ad, 0x30ed, 0x30ef, 0x30c3, 0x30c8, 0x0, 0x30af, 0x0, 0x30af, 0x3099, 0x0, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x0, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x30c8, 0x30f3, 0x0, 0x30af, 0x30eb, 0x30bb, 0x3099, 0x30a4, 0x30ed, 0x0, 0x30af, 0x30ed, 0x30fc, 0x30cd, 0x0, 0x30b1, 0x0, 0x30b1, 0x3099, 0x0, 0x30b1, 0x30fc, 0x30b9, 0x0, 0x30b3, 0x0, 0x30b3, 0x3099, 0x0, 0x30b3, 0x30b3, 0x0, 0x30b3, 0x30c8, 0x0, 0x30b3, 0x30eb, 0x30ca, 0x0, 0x30b3, 0x30fc, 0x30db, 0x309a, 0x0, 0x30b5, 0x0, 0x30b5, 0x3099, 0x0, 0x30b5, 0x30a4, 0x30af, 0x30eb, 0x0, 0x30b5, 0x30f3, 0x30c1, 0x30fc, 0x30e0, 0x0, 0x30b7, 0x0, 0x30b7, 0x3099, 0x0, 0x30b7, 0x30ea, 0x30f3, 0x30af, 0x3099, 0x0, 0x30b9, 0x0, 0x30b9, 0x3099, 0x0, 0x30bb, 0x0, 0x30bb, 0x3099, 0x0, 0x30bb, 0x30f3, 0x30c1, 0x0, 0x30bb, 0x30f3, 0x30c8, 0x0, 0x30bd, 0x0, 0x30bd, 0x3099, 0x0, 0x30bf, 0x0, 0x30bf, 0x3099, 0x0, 0x30bf, 0x3099, 0x30fc, 0x30b9, 0x0, 0x30c1, 0x0, 0x30c1, 0x3099, 0x0, 0x30c3, 0x0, 0x30c4, 0x0, 0x30c4, 0x3099, 0x0, 0x30c6, 0x0, 0x30c6, 0x3099, 0x0, 0x30c6, 0x3099, 0x30b7, 0x0, 0x30c8, 0x0, 0x30c8, 0x3099, 0x0, 0x30c8, 0x3099, 0x30eb, 0x0, 0x30c8, 0x30f3, 0x0, 0x30ca, 0x0, 0x30ca, 0x30ce, 0x0, 0x30cb, 0x0, 0x30cc, 0x0, 0x30cd, 0x0, 0x30ce, 0x0, 0x30ce, 0x30c3, 0x30c8, 0x0, 0x30cf, 0x0, 0x30cf, 0x3099, 0x0, 0x30cf, 0x3099, 0x30fc, 0x30ec, 0x30eb, 0x0, 0x30cf, 0x309a, 0x0, 0x30cf, 0x309a, 0x30fc, 0x30bb, 0x30f3, 0x30c8, 0x0, 0x30cf, 0x309a, 0x30fc, 0x30c4, 0x0, 0x30cf, 0x30a4, 0x30c4, 0x0, 0x30d2, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x3099, 0x30eb, 0x0, 0x30d2, 0x309a, 0x0, 0x30d2, 0x309a, 0x30a2, 0x30b9, 0x30c8, 0x30eb, 0x0, 0x30d2, 0x309a, 0x30af, 0x30eb, 0x0, 0x30d2, 0x309a, 0x30b3, 0x0, 0x30d5, 0x0, 0x30d5, 0x3099, 0x0, 0x30d5, 0x3099, 0x30c3, 0x30b7, 0x30a7, 0x30eb, 0x0, 0x30d5, 0x309a, 0x0, 0x30d5, 0x30a1, 0x30e9, 0x30c3, 0x30c8, 0x3099, 0x0, 0x30d5, 0x30a3, 0x30fc, 0x30c8, 0x0, 0x30d5, 0x30e9, 0x30f3, 0x0, 0x30d8, 0x0, 0x30d8, 0x3099, 0x0, 0x30d8, 0x3099, 0x30fc, 0x30bf, 0x0, 0x30d8, 0x309a, 0x0, 0x30d8, 0x309a, 0x30bd, 0x0, 0x30d8, 0x309a, 0x30cb, 0x30d2, 0x0, 0x30d8, 0x309a, 0x30f3, 0x30b9, 0x0, 0x30d8, 0x309a, 0x30fc, 0x30b7, 0x3099, 0x0, 0x30d8, 0x30af, 0x30bf, 0x30fc, 0x30eb, 0x0, 0x30d8, 0x30eb, 0x30c4, 0x0, 0x30db, 0x0, 0x30db, 0x3099, 0x0, 0x30db, 0x3099, 0x30eb, 0x30c8, 0x0, 0x30db, 0x309a, 0x0, 0x30db, 0x309a, 0x30a4, 0x30f3, 0x30c8, 0x0, 0x30db, 0x309a, 0x30f3, 0x30c8, 0x3099, 0x0, 0x30db, 0x30f3, 0x0, 0x30db, 0x30fc, 0x30eb, 0x0, 0x30db, 0x30fc, 0x30f3, 0x0, 0x30de, 0x0, 0x30de, 0x30a4, 0x30af, 0x30ed, 0x0, 0x30de, 0x30a4, 0x30eb, 0x0, 0x30de, 0x30c3, 0x30cf, 0x0, 0x30de, 0x30eb, 0x30af, 0x0, 0x30de, 0x30f3, 0x30b7, 0x30e7, 0x30f3, 0x0, 0x30df, 0x0, 0x30df, 0x30af, 0x30ed, 0x30f3, 0x0, 0x30df, 0x30ea, 0x0, 0x30df, 0x30ea, 0x30cf, 0x3099, 0x30fc, 0x30eb, 0x0, 0x30e0, 0x0, 0x30e1, 0x0, 0x30e1, 0x30ab, 0x3099, 0x0, 0x30e1, 0x30ab, 0x3099, 0x30c8, 0x30f3, 0x0, 0x30e1, 0x30fc, 0x30c8, 0x30eb, 0x0, 0x30e2, 0x0, 0x30e3, 0x0, 0x30e4, 0x0, 0x30e4, 0x30fc, 0x30c8, 0x3099, 0x0, 0x30e4, 0x30fc, 0x30eb, 0x0, 0x30e5, 0x0, 0x30e6, 0x0, 0x30e6, 0x30a2, 0x30f3, 0x0, 0x30e7, 0x0, 0x30e8, 0x0, 0x30e9, 0x0, 0x30ea, 0x0, 0x30ea, 0x30c3, 0x30c8, 0x30eb, 0x0, 0x30ea, 0x30e9, 0x0, 0x30eb, 0x0, 0x30eb, 0x30d2, 0x309a, 0x30fc, 0x0, 0x30eb, 0x30fc, 0x30d5, 0x3099, 0x30eb, 0x0, 0x30ec, 0x0, 0x30ec, 0x30e0, 0x0, 0x30ec, 0x30f3, 0x30c8, 0x30b1, 0x3099, 0x30f3, 0x0, 0x30ed, 0x0, 0x30ef, 0x0, 0x30ef, 0x3099, 0x0, 0x30ef, 0x30c3, 0x30c8, 0x0, 0x30f0, 0x0, 0x30f0, 0x3099, 0x0, 0x30f1, 0x0, 0x30f1, 0x3099, 0x0, 0x30f2, 0x0, 0x30f2, 0x3099, 0x0, 0x30f3, 0x0, 0x30fb, 0x0, 0x30fc, 0x0, 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, 0x0, 0x34bb, 0x0, 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, 0x36fc, 0x0, 0x3781, 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, 0x0, 0x38c7, 0x0, 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, 0x3a2e, 0x0, 0x3a6c, 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, 0x0, 0x3b49, 0x0, 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, 0x3d33, 0x0, 0x3d96, 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, 0x0, 0x3ffc, 0x0, 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, 0x4046, 0x0, 0x4096, 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, 0x0, 0x4227, 0x0, 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, 0x4359, 0x0, 0x43d5, 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, 0x0, 0x452b, 0x0, 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, 0x45d7, 0x0, 0x45f9, 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, 0x0, 0x4995, 0x0, 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, 0x4ab2, 0x0, 0x4b33, 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, 0x0, 0x4cf8, 0x0, 0x4d56, 0x0, 0x4e00, 0x0, 0x4e01, 0x0, 0x4e03, 0x0, 0x4e09, 0x0, 0x4e0a, 0x0, 0x4e0b, 0x0, 0x4e0d, 0x0, 0x4e19, 0x0, 0x4e26, 0x0, 0x4e28, 0x0, 0x4e2d, 0x0, 0x4e32, 0x0, 0x4e36, 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e3f, 0x0, 0x4e41, 0x0, 0x4e59, 0x0, 0x4e5d, 0x0, 0x4e82, 0x0, 0x4e85, 0x0, 0x4e86, 0x0, 0x4e8c, 0x0, 0x4e94, 0x0, 0x4ea0, 0x0, 0x4ea4, 0x0, 0x4eae, 0x0, 0x4eba, 0x0, 0x4ec0, 0x0, 0x4ecc, 0x0, 0x4ee4, 0x0, 0x4f01, 0x0, 0x4f11, 0x0, 0x4f60, 0x0, 0x4f80, 0x0, 0x4f86, 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, 0x0, 0x4fbf, 0x0, 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, 0x5099, 0x0, 0x50cf, 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x512a, 0x0, 0x513f, 0x0, 0x5140, 0x0, 0x5145, 0x0, 0x514d, 0x0, 0x5154, 0x0, 0x5164, 0x0, 0x5165, 0x0, 0x5167, 0x0, 0x5168, 0x0, 0x5169, 0x0, 0x516b, 0x0, 0x516d, 0x0, 0x5177, 0x0, 0x5180, 0x0, 0x5182, 0x0, 0x518d, 0x0, 0x5192, 0x0, 0x5195, 0x0, 0x5196, 0x0, 0x5197, 0x0, 0x5199, 0x0, 0x51a4, 0x0, 0x51ab, 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, 0x0, 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51e0, 0x0, 0x51f5, 0x0, 0x5200, 0x0, 0x5203, 0x0, 0x5207, 0x0, 0x5217, 0x0, 0x521d, 0x0, 0x5229, 0x0, 0x523a, 0x0, 0x523b, 0x0, 0x5246, 0x0, 0x524d, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52b4, 0x0, 0x52c7, 0x0, 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, 0x0, 0x52f5, 0x0, 0x52f9, 0x0, 0x52fa, 0x0, 0x5305, 0x0, 0x5306, 0x0, 0x5315, 0x0, 0x5317, 0x0, 0x531a, 0x0, 0x5338, 0x0, 0x533b, 0x0, 0x533f, 0x0, 0x5341, 0x0, 0x5344, 0x0, 0x5345, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x5354, 0x0, 0x535a, 0x0, 0x535c, 0x0, 0x5369, 0x0, 0x5370, 0x0, 0x5373, 0x0, 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x5382, 0x0, 0x53b6, 0x0, 0x53c3, 0x0, 0x53c8, 0x0, 0x53ca, 0x0, 0x53cc, 0x0, 0x53df, 0x0, 0x53e3, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53ef, 0x0, 0x53f1, 0x0, 0x53f3, 0x0, 0x5406, 0x0, 0x5408, 0x0, 0x540d, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, 0x5439, 0x0, 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, 0x54a2, 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x554f, 0x0, 0x5553, 0x0, 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, 0x5587, 0x0, 0x5599, 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, 0x0, 0x55b6, 0x0, 0x55c0, 0x0, 0x55c2, 0x0, 0x55e2, 0x0, 0x5606, 0x0, 0x5651, 0x0, 0x5668, 0x0, 0x5674, 0x0, 0x56d7, 0x0, 0x56db, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, 0x571f, 0x0, 0x5730, 0x0, 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, 0x0, 0x580d, 0x0, 0x5831, 0x0, 0x5832, 0x0, 0x5840, 0x0, 0x585a, 0x0, 0x585e, 0x0, 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, 0x0, 0x58d8, 0x0, 0x58df, 0x0, 0x58eb, 0x0, 0x58ee, 0x0, 0x58f0, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5902, 0x0, 0x5906, 0x0, 0x590a, 0x0, 0x5915, 0x0, 0x591a, 0x0, 0x591c, 0x0, 0x5922, 0x0, 0x5927, 0x0, 0x5927, 0x6b63, 0x0, 0x5929, 0x0, 0x5944, 0x0, 0x5948, 0x0, 0x5951, 0x0, 0x5954, 0x0, 0x5962, 0x0, 0x5973, 0x0, 0x59d8, 0x0, 0x59ec, 0x0, 0x5a1b, 0x0, 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, 0x0, 0x5ab5, 0x0, 0x5b08, 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, 0x5b50, 0x0, 0x5b57, 0x0, 0x5b66, 0x0, 0x5b80, 0x0, 0x5b85, 0x0, 0x5b97, 0x0, 0x5bc3, 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, 0x0, 0x5bf3, 0x0, 0x5bf8, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c0f, 0x0, 0x5c22, 0x0, 0x5c38, 0x0, 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, 0x5c64, 0x0, 0x5c65, 0x0, 0x5c6e, 0x0, 0x5c71, 0x0, 0x5c8d, 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, 0x5d43, 0x0, 0x5d50, 0x0, 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, 0x0, 0x5db2, 0x0, 0x5dba, 0x0, 0x5ddb, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, 0x5de5, 0x0, 0x5de6, 0x0, 0x5df1, 0x0, 0x5dfd, 0x0, 0x5dfe, 0x0, 0x5e28, 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e72, 0x0, 0x5e73, 0x6210, 0x0, 0x5e74, 0x0, 0x5e7a, 0x0, 0x5e7c, 0x0, 0x5e7f, 0x0, 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, 0x5ec9, 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, 0x0, 0x5eec, 0x0, 0x5ef4, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f0b, 0x0, 0x5f13, 0x0, 0x5f22, 0x0, 0x5f50, 0x0, 0x5f53, 0x0, 0x5f61, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f73, 0x0, 0x5f8b, 0x0, 0x5f8c, 0x0, 0x5f97, 0x0, 0x5f9a, 0x0, 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fc3, 0x0, 0x5fcd, 0x0, 0x5fd7, 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, 0x0, 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, 0x60d8, 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, 0x0, 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, 0x617a, 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, 0x0, 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, 0x6200, 0x0, 0x6208, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, 0x6234, 0x0, 0x6236, 0x0, 0x624b, 0x0, 0x6253, 0x0, 0x625d, 0x0, 0x6295, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x6307, 0x0, 0x633d, 0x0, 0x6350, 0x0, 0x6355, 0x0, 0x6368, 0x0, 0x637b, 0x0, 0x6383, 0x0, 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, 0x0, 0x63c5, 0x0, 0x63e4, 0x0, 0x641c, 0x0, 0x6422, 0x0, 0x6452, 0x0, 0x6469, 0x0, 0x6477, 0x0, 0x647e, 0x0, 0x649a, 0x0, 0x649d, 0x0, 0x64c4, 0x0, 0x652f, 0x0, 0x6534, 0x0, 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, 0x6587, 0x0, 0x6597, 0x0, 0x6599, 0x0, 0x65a4, 0x0, 0x65b0, 0x0, 0x65b9, 0x0, 0x65c5, 0x0, 0x65e0, 0x0, 0x65e2, 0x0, 0x65e3, 0x0, 0x65e5, 0x0, 0x660e, 0x6cbb, 0x0, 0x6613, 0x0, 0x6620, 0x0, 0x662d, 0x548c, 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, 0x0, 0x6691, 0x0, 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, 0x66f0, 0x0, 0x66f4, 0x0, 0x66f8, 0x0, 0x6700, 0x0, 0x6708, 0x0, 0x6709, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, 0x6728, 0x0, 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, 0x0, 0x677b, 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, 0x67fa, 0x0, 0x6817, 0x0, 0x681f, 0x0, 0x682a, 0x0, 0x682a, 0x5f0f, 0x4f1a, 0x793e, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b20, 0x0, 0x6b21, 0x0, 0x6b54, 0x0, 0x6b62, 0x0, 0x6b63, 0x0, 0x6b72, 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, 0x0, 0x6bae, 0x0, 0x6bb3, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6bcb, 0x0, 0x6bcd, 0x0, 0x6bd4, 0x0, 0x6bdb, 0x0, 0x6c0f, 0x0, 0x6c14, 0x0, 0x6c34, 0x0, 0x6c4e, 0x0, 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, 0x0, 0x6ccc, 0x0, 0x6ccd, 0x0, 0x6ce5, 0x0, 0x6ce8, 0x0, 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, 0x6d34, 0x0, 0x6d3e, 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, 0x0, 0x6d77, 0x0, 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, 0x6dda, 0x0, 0x6dea, 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, 0x0, 0x6e6e, 0x0, 0x6e80, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, 0x6ecb, 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f14, 0x0, 0x6f22, 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, 0x0, 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, 0x704a, 0x0, 0x706b, 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, 0x0, 0x7099, 0x0, 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, 0x7121, 0x0, 0x7145, 0x0, 0x7149, 0x0, 0x716e, 0x0, 0x719c, 0x0, 0x71ce, 0x0, 0x71d0, 0x0, 0x7210, 0x0, 0x721b, 0x0, 0x7228, 0x0, 0x722a, 0x0, 0x722b, 0x0, 0x7235, 0x0, 0x7236, 0x0, 0x723b, 0x0, 0x723f, 0x0, 0x7247, 0x0, 0x7250, 0x0, 0x7259, 0x0, 0x725b, 0x0, 0x7262, 0x0, 0x7279, 0x0, 0x7280, 0x0, 0x7295, 0x0, 0x72ac, 0x0, 0x72af, 0x0, 0x72c0, 0x0, 0x72fc, 0x0, 0x732a, 0x0, 0x7375, 0x0, 0x737a, 0x0, 0x7384, 0x0, 0x7387, 0x0, 0x7389, 0x0, 0x738b, 0x0, 0x73a5, 0x0, 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x74dc, 0x0, 0x74e6, 0x0, 0x7506, 0x0, 0x7518, 0x0, 0x751f, 0x0, 0x7524, 0x0, 0x7528, 0x0, 0x7530, 0x0, 0x7532, 0x0, 0x7533, 0x0, 0x7537, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, 0x7565, 0x0, 0x7570, 0x0, 0x758b, 0x0, 0x7592, 0x0, 0x75e2, 0x0, 0x7610, 0x0, 0x761d, 0x0, 0x761f, 0x0, 0x7642, 0x0, 0x7669, 0x0, 0x7676, 0x0, 0x767d, 0x0, 0x76ae, 0x0, 0x76bf, 0x0, 0x76ca, 0x0, 0x76db, 0x0, 0x76e3, 0x0, 0x76e7, 0x0, 0x76ee, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, 0x0, 0x771f, 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, 0x77a7, 0x0, 0x77db, 0x0, 0x77e2, 0x0, 0x77f3, 0x0, 0x784e, 0x0, 0x786b, 0x0, 0x788c, 0x0, 0x7891, 0x0, 0x78ca, 0x0, 0x78cc, 0x0, 0x78fb, 0x0, 0x792a, 0x0, 0x793a, 0x0, 0x793c, 0x0, 0x793e, 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, 0x0, 0x7956, 0x0, 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, 0x797f, 0x0, 0x7981, 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, 0x0, 0x79b8, 0x0, 0x79be, 0x0, 0x79ca, 0x0, 0x79d8, 0x0, 0x79eb, 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, 0x7a4a, 0x0, 0x7a4f, 0x0, 0x7a74, 0x0, 0x7a7a, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, 0x0, 0x7aee, 0x0, 0x7af9, 0x0, 0x7b20, 0x0, 0x7b8f, 0x0, 0x7bc0, 0x0, 0x7bc6, 0x0, 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, 0x0, 0x7c73, 0x0, 0x7c7b, 0x0, 0x7c92, 0x0, 0x7cbe, 0x0, 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, 0x7ce7, 0x0, 0x7ce8, 0x0, 0x7cf8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, 0x0, 0x7d2f, 0x0, 0x7d42, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, 0x0, 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, 0x7e09, 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f36, 0x0, 0x7f3e, 0x0, 0x7f51, 0x0, 0x7f72, 0x0, 0x7f79, 0x0, 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f8a, 0x0, 0x7f95, 0x0, 0x7f9a, 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, 0x8005, 0x0, 0x800c, 0x0, 0x8012, 0x0, 0x8033, 0x0, 0x8046, 0x0, 0x8060, 0x0, 0x806f, 0x0, 0x8070, 0x0, 0x807e, 0x0, 0x807f, 0x0, 0x8089, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, 0x8103, 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e3, 0x0, 0x81e8, 0x0, 0x81ea, 0x0, 0x81ed, 0x0, 0x81f3, 0x0, 0x81fc, 0x0, 0x8201, 0x0, 0x8204, 0x0, 0x820c, 0x0, 0x8218, 0x0, 0x821b, 0x0, 0x821f, 0x0, 0x826e, 0x0, 0x826f, 0x0, 0x8272, 0x0, 0x8278, 0x0, 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, 0x82b1, 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, 0x0, 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, 0x8353, 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, 0x0, 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, 0x83ef, 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, 0x0, 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, 0x8516, 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, 0x0, 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x864d, 0x0, 0x8650, 0x0, 0x865c, 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x866b, 0x0, 0x8688, 0x0, 0x86a9, 0x0, 0x86e2, 0x0, 0x870e, 0x0, 0x8728, 0x0, 0x876b, 0x0, 0x8779, 0x0, 0x8786, 0x0, 0x87ba, 0x0, 0x87e1, 0x0, 0x8801, 0x0, 0x881f, 0x0, 0x8840, 0x0, 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, 0x0, 0x88cf, 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, 0x88f8, 0x0, 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, 0x0, 0x897e, 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x89d2, 0x0, 0x89e3, 0x0, 0x8a00, 0x0, 0x8aa0, 0x0, 0x8aaa, 0x0, 0x8abf, 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, 0x0, 0x8aed, 0x0, 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, 0x8b39, 0x0, 0x8b58, 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c37, 0x0, 0x8c46, 0x0, 0x8c48, 0x0, 0x8c55, 0x0, 0x8c78, 0x0, 0x8c9d, 0x0, 0x8ca1, 0x0, 0x8ca9, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, 0x8cc7, 0x0, 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, 0x0, 0x8d64, 0x0, 0x8d70, 0x0, 0x8d77, 0x0, 0x8db3, 0x0, 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, 0x8eab, 0x0, 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, 0x8f38, 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9b, 0x0, 0x8f9e, 0x0, 0x8fb0, 0x0, 0x8fb5, 0x0, 0x8fb6, 0x0, 0x9023, 0x0, 0x9038, 0x0, 0x904a, 0x0, 0x9069, 0x0, 0x9072, 0x0, 0x907c, 0x0, 0x908f, 0x0, 0x9091, 0x0, 0x9094, 0x0, 0x90ce, 0x0, 0x90de, 0x0, 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, 0x0, 0x9149, 0x0, 0x916a, 0x0, 0x9199, 0x0, 0x91b4, 0x0, 0x91c6, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, 0x91d1, 0x0, 0x9234, 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, 0x0, 0x92d7, 0x0, 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, 0x93f9, 0x0, 0x9415, 0x0, 0x9577, 0x0, 0x9580, 0x0, 0x958b, 0x0, 0x95ad, 0x0, 0x95b7, 0x0, 0x961c, 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, 0x0, 0x9675, 0x0, 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, 0x96a3, 0x0, 0x96b6, 0x0, 0x96b7, 0x0, 0x96b8, 0x0, 0x96b9, 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, 0x96e8, 0x0, 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, 0x0, 0x9751, 0x0, 0x9756, 0x0, 0x975e, 0x0, 0x9762, 0x0, 0x9769, 0x0, 0x97cb, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ed, 0x0, 0x97f3, 0x0, 0x97ff, 0x0, 0x9801, 0x0, 0x9805, 0x0, 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, 0x0, 0x98a8, 0x0, 0x98db, 0x0, 0x98df, 0x0, 0x98e2, 0x0, 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, 0x9929, 0x0, 0x9996, 0x0, 0x9999, 0x0, 0x99a7, 0x0, 0x99ac, 0x0, 0x99c2, 0x0, 0x99f1, 0x0, 0x99fe, 0x0, 0x9a6a, 0x0, 0x9aa8, 0x0, 0x9ad8, 0x0, 0x9adf, 0x0, 0x9b12, 0x0, 0x9b25, 0x0, 0x9b2f, 0x0, 0x9b32, 0x0, 0x9b3c, 0x0, 0x9b5a, 0x0, 0x9b6f, 0x0, 0x9c40, 0x0, 0x9c57, 0x0, 0x9ce5, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, 0x9db4, 0x0, 0x9dfa, 0x0, 0x9e1e, 0x0, 0x9e75, 0x0, 0x9e7f, 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, 0x9ea5, 0x0, 0x9ebb, 0x0, 0x9ec3, 0x0, 0x9ecd, 0x0, 0x9ece, 0x0, 0x9ed1, 0x0, 0x9ef9, 0x0, 0x9efd, 0x0, 0x9efe, 0x0, 0x9f05, 0x0, 0x9f0e, 0x0, 0x9f0f, 0x0, 0x9f13, 0x0, 0x9f16, 0x0, 0x9f20, 0x0, 0x9f3b, 0x0, 0x9f43, 0x0, 0x9f4a, 0x0, 0x9f52, 0x0, 0x9f8d, 0x0, 0x9f8e, 0x0, 0x9f9c, 0x0, 0x9f9f, 0x0, 0x9fa0, 0x0, 0xa76f, 0x0, 0x11099, 0x110ba, 0x0, 0x1109b, 0x110ba, 0x0, 0x110a5, 0x110ba, 0x0, 0x11131, 0x11127, 0x0, 0x11132, 0x11127, 0x0, 0x1d157, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x1d16e, 0x0, 0x1d158, 0x1d165, 0x1d16f, 0x0, 0x1d158, 0x1d165, 0x1d170, 0x0, 0x1d158, 0x1d165, 0x1d171, 0x0, 0x1d158, 0x1d165, 0x1d172, 0x0, 0x1d1b9, 0x1d165, 0x0, 0x1d1b9, 0x1d165, 0x1d16e, 0x0, 0x1d1b9, 0x1d165, 0x1d16f, 0x0, 0x1d1ba, 0x1d165, 0x0, 0x1d1ba, 0x1d165, 0x1d16e, 0x0, 0x1d1ba, 0x1d165, 0x1d16f, 0x0, 0x20122, 0x0, 0x2051c, 0x0, 0x20525, 0x0, 0x2054b, 0x0, 0x2063a, 0x0, 0x20804, 0x0, 0x208de, 0x0, 0x20a2c, 0x0, 0x20b63, 0x0, 0x214e4, 0x0, 0x216a8, 0x0, 0x216ea, 0x0, 0x219c8, 0x0, 0x21b18, 0x0, 0x21d0b, 0x0, 0x21de4, 0x0, 0x21de6, 0x0, 0x22183, 0x0, 0x2219f, 0x0, 0x22331, 0x0, 0x226d4, 0x0, 0x22844, 0x0, 0x2284a, 0x0, 0x22b0c, 0x0, 0x22bf1, 0x0, 0x2300a, 0x0, 0x232b8, 0x0, 0x2335f, 0x0, 0x23393, 0x0, 0x2339c, 0x0, 0x233c3, 0x0, 0x233d5, 0x0, 0x2346d, 0x0, 0x236a3, 0x0, 0x238a7, 0x0, 0x23a8d, 0x0, 0x23afa, 0x0, 0x23cbc, 0x0, 0x23d1e, 0x0, 0x23ed1, 0x0, 0x23f5e, 0x0, 0x23f8e, 0x0, 0x24263, 0x0, 0x242ee, 0x0, 0x243ab, 0x0, 0x24608, 0x0, 0x24735, 0x0, 0x24814, 0x0, 0x24c36, 0x0, 0x24c92, 0x0, 0x24fa1, 0x0, 0x24fb8, 0x0, 0x25044, 0x0, 0x250f2, 0x0, 0x250f3, 0x0, 0x25119, 0x0, 0x25133, 0x0, 0x25249, 0x0, 0x2541d, 0x0, 0x25626, 0x0, 0x2569a, 0x0, 0x256c5, 0x0, 0x2597c, 0x0, 0x25aa7, 0x0, 0x25bab, 0x0, 0x25c80, 0x0, 0x25cd0, 0x0, 0x25f86, 0x0, 0x261da, 0x0, 0x26228, 0x0, 0x26247, 0x0, 0x262d9, 0x0, 0x2633e, 0x0, 0x264da, 0x0, 0x26523, 0x0, 0x265a8, 0x0, 0x267a7, 0x0, 0x267b5, 0x0, 0x26b3c, 0x0, 0x26c36, 0x0, 0x26cd5, 0x0, 0x26d6b, 0x0, 0x26f2c, 0x0, 0x26fb1, 0x0, 0x270d2, 0x0, 0x273ca, 0x0, 0x27667, 0x0, 0x278ae, 0x0, 0x27966, 0x0, 0x27ca8, 0x0, 0x27ed3, 0x0, 0x27f2f, 0x0, 0x285d2, 0x0, 0x285ed, 0x0, 0x2872e, 0x0, 0x28bfa, 0x0, 0x28d77, 0x0, 0x29145, 0x0, 0x291df, 0x0, 0x2921a, 0x0, 0x2940a, 0x0, 0x29496, 0x0, 0x295b6, 0x0, 0x29b30, 0x0, 0x2a0ce, 0x0, 0x2a105, 0x0, 0x2a20e, 0x0, 0x2a291, 0x0, 0x2a392, 0x0, 0x2a600, 0x0]; return t; } -} + //22656 bytes + enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)([0x0, 0x20, + 0x2a0], [0x100, 0xa00, 0x21c0], [0x402030202020100, + 0x706020202020205, 0x802020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x0, 0x3000200010000, 0x7000600050004, + 0xa000900080000, 0xc000b, 0xf000e000d0000, 0x11001000000000, + 0x15001400130012, 0x19001800170016, 0x1b001a00000000, 0x0, 0x1c, + 0x1e0000001d0000, 0x1f00000000, 0x0, 0x0, 0x0, 0x0, 0x2100200000, + 0x2200000000, 0x2400230000, 0x0, 0x2500000000, 0x2700000026, + 0x2800000000, 0x2900000000, 0x2a00000000, 0x2b00000000, 0x2c0000, + 0x2e002d0000, 0x3100300000002f, 0x330032, 0x340000, + 0x35000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3800370036, + 0x0, 0x0, 0x0, 0x3b003a00390000, 0x3d003c, 0x410040003f003e, + 0x45004400430042, 0x49004800470046, 0x4d004c004b004a, + 0x510050004f004e, 0x530052, 0x57005600550054, 0x5a00590058, + 0x5e005d005c005b, 0x6100000060005f, 0x620000, 0x0, + 0x63000000000000, 0x67006600650064, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x69000000000068, 0x6a00000000, 0x0, 0x0, + 0x6b000000000000, 0x0, 0x6c000000000000, 0x0, 0x0, + 0x6e00000000006d, 0x7200710070006f, 0x7500740073, 0x79007800770076, + 0x7d007c007b007a, 0x80007f007e0000, 0x81, 0x85008400830082, + 0x89008800870086, 0x8d008c008b008a, 0x910090008f008e, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92000000000000, + 0x93000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x97009600950094, + 0x9b009a00990098, 0x9f009e009d009c, 0xa200a100a0, 0xa600a500a400a3, + 0xaa00a900a800a7, 0xae00ad00ac00ab, 0xb200b100b000af, + 0xb600b500b400b3, 0xba00b900b800b7, 0xbe00bd00bc00bb, + 0xc200c100c000bf, 0xc600c500c400c3, 0xca00c900c800c7, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc00cb, 0xcd0000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf00ce00000000, 0xd100d00000, + 0x0, 0x0, 0x0, 0x0, 0xd500d400d300d2, 0xd900d800d700d6, + 0xdd00dc00db00da, 0xdf00d300d200de, 0xe200e100e000d5, + 0xe500e400e300d9, 0xe900e800e700e6, 0xed00ec00eb00ea, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xf100f000ef00ee, 0xf300f2, 0x0, 0x0, 0x0, 0x0, + 0xf700f600f500f4, 0xf8, 0xfb00fa00f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xff00fe00fd00fc, 0x103010201010100, + 0x107010601050104, 0x10b010a01090108, 0x10c, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x0, 0x69200000015, 0x9000000000000, 0x30f034300000000, + 0x11b20003, 0x78703140048, 0x49403c603ce, 0x58605730570056d, + 0x5f8000005b005a6, 0x6580631062e062b, 0x6f906ea06e706e4, + 0x7a907a6078f0000, 0x7e307bf07ac, 0x8b708b408b10000, 0x95f08cb, + 0x9c209af09ac09a9, 0xa47000009ec09e2, 0xab30a8c0a890a86, + 0xb550b490b460b43, 0xc5e0c5b0c410000, 0xc980c740c61, + 0xd6e0d6b0d680000, 0xe1b00000e0c0d82, 0x9c8058c09c50589, + 0xa3b05ec0a0a05ce, 0xa4105f20a3e05ef, 0xa6e061a0a4405f5, + 0xaa2064700000000, 0xab006550aad0652, 0xab9065e0ad00675, + 0xb0106a00afb069a, 0xb0a06a90b0406a3, 0xb1606ba, 0xb4f06f00b4c06ed, + 0xb6b070f0b5206f3, 0xb3706d8000006f6, 0xbae072e0b730717, + 0x7500bcc07430000, 0x7400bcf07460bd9, 0x78c000000000bc9, + 0x7950c4d079b0c3e, 0xed70c47, 0xc8e07d90c8307ce, 0xca207ed, + 0xd1d08580d070842, 0xd2b086c0d0d0848, 0xd49088a0d320873, + 0xd5d08a60d380879, 0xd54089d, 0xd7808c10d7108ba, 0xd9808e10d7f08c8, + 0xdc4090d0d9b08e4, 0xe0f09620de9093f, 0x97f0e290979096e, + 0x8400614060d0e2f, 0xcae07f9, 0x0, 0x0, 0x8f0000000000000, 0xda7, + 0x0, 0x0, 0x0, 0x0, 0x7360a670613060c, 0x78307800bb9073d, + 0x70309f305b70c32, 0x8e70ca507f00b5f, 0x8d20d8d08d60d9e, + 0x8ce0d9108da0d89, 0x9e505a900000d85, 0xe630e5a09de05a2, + 0xb0706a600000000, 0xccc08170ba80728, 0xecc0e7b0ccf081a, + 0xa64061006090b76, 0xaf80697, 0x9ef05b30c3b0789, 0xe680e5d0e600e57, + 0x9f905bd09f605ba, 0xabf06640abc0661, 0xb6507090b620706, + 0xcab07f60ca807f3, 0xd13084e0d10084b, 0xda408ed0da108ea, + 0xd5a08a30d460887, 0xb1f06c300000000, 0x0, 0x9db059f00000000, + 0xc9b07e60ac9066e, 0xc9107dc0c7b07c6, 0xe1509680c9407df, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xa11073e0e9a0b0d, 0xde10eb80eb60eb4, + 0x695, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4b00240012000f, + 0x270006, 0xb4108400a280e96, 0xecf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2b00000004001a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xed5, 0x5400000000, 0x54600000000, 0x0, + 0x7410ee8001c0003, 0xfb40f630f43, 0x103c101600000fed, 0x1185, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x101f0fbd00000000, 0x1175111910f5108f, 0x1213, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x120c117e00000000, 0x124b120311d5, + 0x10161011116e10ea, 0x11ee123c101f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x11f811f011ae, 0x10f00fad, 0x100d0000, 0x0, 0x12ad000012b612b0, + 0x12a4000000000000, 0x0, 0x12d712c212ce, 0x0, 0x0, 0x12c80000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130a0000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x12ef000012f812f2, 0x132d000000000000, 0x0, 0x131b13041310, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1333133000000000, 0x0, 0x0, 0x12fb12b90000, + 0x0, 0x0, 0x0, 0x12ec12aa12e912a7, 0x12f512b300000000, + 0x1339133600000000, 0x130112bf12fe12bc, 0x130712c500000000, + 0x131512d1130d12cb, 0x133f133c00000000, 0x131812d4132a12e6, + 0x132112dd131e12da, 0x132412e0, 0x132712e3, 0x0, 0x0, + 0x1342000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x13e913e600000000, 0x17ca13ec178f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x185b179213ef0000, 0x1811, 0x0, + 0x18520000186d, 0x0, 0x0, 0x0, 0x186a000000000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x18820000, 0x0, 0x188b0000, 0x188e, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1879187618731870, 0x18881885187f187c, 0x0, + 0x0, 0x189a000000000000, 0x189d, 0x0, 0x0, 0x0, 0x1897000018941891, + 0x0, 0x0, 0x0, 0x0, 0x18ac000000000000, 0x18af00000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18a618a318a00000, 0x18a900000000, + 0x0, 0x0, 0x18b80000000018bb, 0x18be, 0x0, 0x0, 0x0, 0x18b518b2, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x18c1, 0x0, 0x0, 0x0, 0x0, + 0x18ca18c400000000, 0x18c7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18cd, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x18d0, 0x18da000000000000, + 0x18d618d3000018dd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x18e618e000000000, 0x18e3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x18e900000000, 0x18f318ef18ec, 0x0, 0x0, 0x0, 0x0, + 0x18f6000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x18ff000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x18fc18f9, 0x0, 0x0, 0x0, 0x1902, 0x0, 0x0, 0x0, 0x0, + 0x1907000000000000, 0x0, 0x0, 0x190a0000, 0x190d00000000, + 0x1910000000000000, 0x0, 0x1913, 0x0, 0x0, 0x19040000, 0x0, + 0x1916000000000000, 0x1931193519190000, 0x1938193c, 0x0, + 0x191c0000, 0x0, 0x0, 0x0, 0x1922000000000000, 0x0, 0x0, + 0x19250000, 0x192800000000, 0x192b000000000000, 0x0, 0x192e, 0x0, + 0x0, 0x191f0000, 0x0, 0x0, 0x193f00000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1942, 0x0, + 0x1a3800000000, 0x1a3e00001a3b, 0x1a4400001a41, 0x1a4700000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a4a000000000000, + 0x1a4d0000, 0x1a5600001a531a50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x5d50e550568, 0x6870e75062905e6, 0x71a060706cf06ac, + 0x77e07230734, 0x82c06af0e7e07a4, 0x6920770056b088d, + 0x9371a590e840e82, 0xe8e0e8c0a7d0a2e, 0xb79000006020e90, + 0xe8807870e7105d3, 0xba30cd31a5d1a5b, 0x86a0ea41a610a24, + 0x10ee10ec10ea1a63, 0xa110ae0123e123c, 0x10ec10ea086a0a24, + 0x123e123c11f0, 0x0, 0x0, 0x0, 0x1313, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xe86000000000000, 0xe900e660e8a09a0, 0xe980e940e920ad9, + 0x1a650ea00e9e0e9c, 0xed31a670ea20ed1, 0xeac0eaa0ea60ea8, + 0xeba0eb20eb00eae, 0xec00ebe0e790ebc, 0x6110ec40ec21a5f, + 0x116e0eca0ec80ec6, 0xa1305da0a0705cb, 0xa1905e00a1605dd, + 0xa6b06170a4a05fb, 0xa7a06260a71061d, 0xa7706230a740620, + 0xaa9064e0aa5064a, 0xad6067b0ad30678, 0xaef06840acc0671, + 0xb1906bd0afe069d, 0xb1c06c00b2206c6, 0xb2806cc0b2506c9, + 0xb5806fc0b6e0712, 0xbab072b0ba50725, 0xbd207490bb10731, + 0xbdf07560bd5074c, 0xc1207720bdc0753, 0xc1807780c150775, + 0xc4a07980c440792, 0xc50079e0c5307a1, 0xc7f07ca0c7707c2, + 0xc8a07d50c8607d1, 0xcef08380cec0835, 0xd1608510d0a0845, + 0xd20085b0d190854, 0xd3f08800d350876, 0xd3b087c0d2e086f, + 0xd4e089a0d420883, 0xd6308ac0d5708a0, 0xdc1090a0d6008a9, + 0xdc709100dca0913, 0xd7b08c40d7408bd, 0xdde09270ddb0924, + 0xde6093c0de30939, 0xdec09420def0945, 0xe0109540df50948, + 0xe18096b0e040957, 0xe3509850e2c097c, 0xd510b2b0e380988, + 0xd3509a60e210df2, 0x0, 0x9e905ad09fc05c0, 0x9b2057609b6057a, + 0x9ba057e09be0582, 0x9cf059309ff05c3, 0x9d7059b09cb058f, + 0xa0305c709d30597, 0xab6065b0ac20667, 0xa9306380a9f0644, + 0xa9b06400a8f0634, 0xac5066a0a97063c, 0xb68070c0b5c0700, + 0xc9f07ea0cc50810, 0xc6407af0c6807b3, 0xc6c07b70c7007bb, + 0xcb508000cc80813, 0xcbd08080cb107fc, 0xcc1080c0cb90804, + 0xd9508de0dbe0907, 0xdaa08f30dae08f7, 0xdb208fb0db608ff, + 0xe09095c0dba0903, 0xe1e09710e240974, 0xe120965, 0x0, + 0x10c1109f10be109c, 0x10d310b110ca10a8, 0xf160ef40f130ef1, + 0xf280f060f1f0efd, 0x110610fb110310f8, 0x110a10ff, + 0xf540f490f510f46, 0xf580f4d, 0x1145112311421120, + 0x11571135114e112c, 0xf8b0f690f880f66, 0xf9d0f7b0f940f72, + 0x119f1190119c118d, 0x11a7119811a31194, 0xfd20fc30fcf0fc0, + 0xfda0fcb0fd60fc7, 0x11e611db11e311d8, 0x11ea11df, + 0xffe0ff30ffb0ff0, 0x10020ff7, 0x122d121e122a121b, + 0x1235122612311222, 0x1025000010220000, 0x102d000010290000, + 0x1277125512741252, 0x128912671280125e, 0x106410421061103f, + 0x10761054106d104b, 0x10f510f2108f1088, 0x1175117211191112, + 0x1203120011d511d2, 0x124b1244, 0x10c510a310dc10ba, + 0x10d710b510ce10ac, 0xf1a0ef80f310f0f, 0xf2c0f0a0f230f01, + 0x114911271160113e, 0x115b113911521130, 0xf8f0f6d0fa60f84, + 0xfa10f7f0f980f76, 0x127b125912921270, 0x128d126b12841262, + 0x10681046107f105d, 0x107a10581071104f, 0x10e7108b10961099, + 0x10e310e000001092, 0xee80ee50eeb0eee, 0x2a1170002a0f35, + 0x116b111500200051, 0x116711640000111c, 0xf630f600f430f40, + 0x350031002d0faa, 0x118511811178117b, 0x118911ab00000000, + 0xfb40fb10fb70fba, 0x440040003c0000, 0x1213120f12061209, + 0x1217123911f511f2, 0x101610131019101c, 0x995001c0018100a, + 0x129d124700000000, 0x129912960000124e, 0x103c10390fed0fea, + 0x3900031083, 0x1000100010001, 0x1000100010001, 0x100010001, 0x0, + 0x1a690000, 0x4e000000000000, 0x0, 0x0, 0x0, 0x2ff02fc02fa, 0x0, + 0x1000000000000, 0x1a6f000000000000, 0x1a7e1a7b00001a72, 0x0, + 0xc0000008f, 0x0, 0x563000000000000, 0x920560, 0x0, 0x0, + 0x1a76000000000000, 0x0, 0x1000000000000, 0x0, 0x0, 0x0, 0x0, + 0xae00305, 0x392038303740365, 0x1aad02f403b003a1, + 0xb3b00a500a10544, 0x30f034303140305, 0x392038303740365, + 0x1aad02f403b003a1, 0xa500a10544, 0xb4107870a7d0692, + 0xa280b790b0d0e8c, 0x8400cd30b3b05d3, 0xba3, 0x0, 0x0, 0x83f, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe4d05e309a2099e, 0xe770a220a1e0000, + 0x6ac06020e500000, 0xe6d0b0d06ac06ac, 0xa28073406cf06cf, + 0x786077e0000, 0x82c083b06af0000, 0x82c082c, 0x897088f0863, + 0x77c0000060a, 0x5b0071a0000060a, 0xa7d000005e305d5, + 0x7230000067e0629, 0x136a136213540787, 0x68000000ae0136f, + 0x10060f3a10ec11ee, 0x1aab, 0xa7d0a2e05e60000, 0x73e0ae0, 0x0, + 0x3ca03c103e203da, 0x498045903d20455, 0x3de04e703d604cf, + 0x3be051104eb049c, 0x6de06d406d106cf, 0x91f091b091806b2, + 0x950094d068206e1, 0x72305e605e30734, 0xb3d0b330b300ae0, + 0xdd60dd20dcf086a, 0xdfd0dfa0b410b40, 0x5d30a2e09a00a28, 0x0, 0x0, + 0x30d0000, 0x0, 0x0, 0x0, 0x1a8d1a8600000000, 0x0, 0x0, 0x0, 0x0, + 0x1a9200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1a981a9b1a950000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1aa0, 0x1aa50000, + 0x1aa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ab200001aaf, 0x0, + 0x1ac100001ab81ab5, 0x1ac4, 0x0, 0x0, 0x0, 0x1ac80000, + 0x1ace000000001acb, 0x1ad10000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1ad700000556, 0x0, 0x0, 0x55b054a1ad40000, 0x1add1ada, + 0x1ae31ae0, 0x1ae91ae6, 0x0, 0x1aef1aec, 0x1afb1af8, 0x1b011afe, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b131b101b0d1b0a, 0x0, + 0x0, 0x0, 0x0, 0x1b071b041af51af2, 0x0, 0x1b191b1600000000, + 0x1b1f1b1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b371b350000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x365030f03430314, 0x3a1039203830374, + 0x342032f031c03b0, 0x382037303640355, 0x3f703af03a00391, + 0xe600e200d900a3, 0xf600f200ee00ea, 0xb100ac00a700fa, + 0xc500c000bb00b6, 0xdd00d400cf00ca, 0x368035903460319, + 0x3a4039503860377, 0x3450332031f03b3, 0x385037603670358, + 0x3fa03b203a30394, 0x172016e016a0166, 0x182017e017a0176, + 0x192018e018a0186, 0x1a2019e019a0196, 0x1b201ae01aa01a6, + 0x1c201be01ba01b6, 0x5d5056801ca01c6, 0x67e062905e605e3, + 0x60706cf06ac0687, 0x77e07230734071a, 0x82c083b06af07a4, + 0x6b2056b088d085e, 0x60a095a06820770, 0xa2e09a009370692, + 0xb0d06020ad90a7d, 0xa280b79073e0ae0, 0xcd307870b3b05d3, + 0xba308400a1105d8, 0xb410de1086a0a24, 0x30506110695, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1abc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x552054f0542, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1b2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6b2073e, 0x0, + 0x0, 0x0, 0x1b2f000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x227c000000000000, 0x0, 0x0, 0x0, 0x0, + 0x26b0000000000000, 0x0, 0x0, 0x0, 0x1f091f011efb1ee9, + 0x1f1b1f171f131f0d, 0x1f5f1f571f4b1f21, 0x1f871f771f6f1f67, + 0x1fb91fa51f8b1f89, 0x1fcd1fc71fc51fc1, 0x1feb1fe91fdd1fdb, + 0x204f20451ff71fef, 0x207f207d2079206f, 0x20b420ae20982087, + 0x20ce20cc20ca20c4, 0x20f820f220dc20da, 0x210f2108210020fc, + 0x212f212b21292113, 0x2141213921352131, 0x21972195218b214f, + 0x21e521e321d921d7, 0x32521f121ed21e9, 0x2260222303292211, + 0x227a2274226e2266, 0x228422822280227e, 0x230c230622e22286, + 0x231623122310230e, 0x2334233023222318, 0x235c235a23562354, + 0x2376237423622360, 0x238a238823862384, 0x23aa23a823a62394, + 0x23ee23de23dc23bc, 0x241c240a23fa23f6, 0x2452244c2442243e, + 0x245e245c245a2456, 0x247e247a246c246a, 0x248e248a24842482, + 0x2498249624922490, 0x250e250c24f224e8, 0x2530252c25282512, + 0x2558255425522534, 0x25742572255c255a, 0x2592258425822578, + 0x25ba25aa25982596, 0x25de25c825c425c2, 0x260025fa25e825e0, + 0x261a261826142608, 0x26262624261e261c, 0x263c263a2638262a, + 0x2658264e264a2648, 0x26622660265c265a, 0x2670266826662664, + 0x26862684267e267c, 0x2690268e268a2688, 0x26a0269c26982692, + 0x26aa26a826a626a2, 0x26b226ae, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b4900000000, + 0x1fd11fcf1fcd, 0x0, 0x0, 0x0, 0x0, 0x1b8100001b7e, 0x1b8700001b84, + 0x1b8d00001b8a, 0x1b9300001b90, 0x1b9900001b96, 0x1b9f00001b9c, + 0x1ba500001ba20000, 0x1ba80000, 0x0, 0x1bb100001bae1bab, + 0x1bba1bb700001bb4, 0x1bc01bbd0000, 0x1bc91bc6, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1b7b, 0x87000000000000, 0x1bcc1bd30000008a, 0x0, 0x0, 0x0, + 0x1c4300001c26, 0x1c9200001bf6, 0x1caf00001c9b, 0x1cca00001cbf, + 0x1cdc00001ccf, 0x1ceb00001ce1, 0x1cf700001cf20000, 0x1c100000, + 0x0, 0x1d3b00001d261d1d, 0x1d611d5700001d42, 0x1d7e1d760000, + 0x1caa1da1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e44000000001c01, + 0x1e571e521e4d, 0x1ca11e6000000000, 0x0, 0x0, 0x0, 0x0, + 0x1a10194919440000, 0x19501a141a12194b, 0x1a181a1619571955, + 0x1a201a1e1a1c1a1a, 0x19661961195c19a6, 0x196f196d196819b0, + 0x198e198319811977, 0x1947199d19981993, 0x19de19dc19da19d8, + 0x198c19e419e219e0, 0x19ee19ec19ea19e8, 0x19f619f419f21975, + 0x19fe197f19fa19f8, 0x1a2219a419a219d4, 0x1a2a1a281a261a24, + 0x1a3019a81a2e1a2c, 0x19ae19ac19aa1a32, 0x19b819b619b419b2, + 0x19c019be19bc19ba, 0x19c819c619c419c2, 0x1a361a3419cc19ca, + 0x1a0019d219d019ce, 0x1a081a061a041a02, 0x1a0e1a0c1a0a, + 0x1f171ee900000000, 0x1efd1ef120471eef, 0x1ef71f0d23641ef3, + 0x1f212051208c1eeb, 0x1e901e001d701ce, 0x20d020401fb01f2, + 0x245023c02330225, 0x1db01d20257024e, 0x1ff01f601ed01e4, + 0x237022902110208, 0x25b025202490240, 0x21e0216022e, + 0x2a0026802700260, 0x284026402880274, 0x2c402b00290026c, + 0x2a402ec02b802c0, 0x2d002b402bc02ac, 0x2d402e402c80298, + 0x2a8029c0278028c, 0x29402e8027c02cc, 0x2e002dc028002d8, + 0x23fe21e321112021, 0x0, 0x0, 0x41c04110406082e, 0x440043904320427, + 0x475046e044e0447, 0x4850482047f047c, 0x19571950194b1944, + 0x196f19681961195c, 0x1993198e19831977, 0x194d1946199d1998, + 0x1963195e19591952, 0x198519791971196a, 0x199f199a19951990, + 0x1974197c1988, 0x20471eef1f171ee9, 0x1f5f1eed1f611f19, + 0x22e203291fcd1f0f, 0x204f25c822232286, 0x2240221b223b0325, + 0x23ca255e231c2007, 0x2098236823e21fab, 0x22961fdf1f4925a2, + 0x208a1f731f2b262c, 0x20fa1ef31efd1ef1, 0x20b220b81fc92001, + 0x1fd725681f292390, 0x48e048b04882083, 0x4b704b404b10491, + 0x4c304c004bd04ba, 0x4e404cc04c904c6, 0x4d604a3034e033b, + 0x5290518050304f2, 0x34d033a0327053a, 0x7390a7f0a8206b4, + 0x1c0a1bff1bf11bd8, 0x1c731c411c241c1a, 0x1cbd1cad1c991c90, + 0x1cdf1cda1ccd1c1e, 0x1bde1cf51cf01bfb, 0x1c8e1d111d0f1ca6, + 0x1d551d391d1b1d0d, 0x1ddc1c311d9f1d74, 0x1e041e001def1c22, + 0x1c351e1b1e191e11, 0x1e421c5d1e341bed, 0x1e551e501e4b, + 0x1beb1be51be01bda, 0x1c0c1c041bf91bf3, 0x1c331c201c1c1c13, + 0x1c2e1c291c3c1c37, 0x1c501c571c4b1c46, 0x1c6d1c661c5f1c5c, + 0x1c8b1c841c7d1c61, 0x1cb21ca81ca41c95, 0x1cd61cd21cc21cb7, + 0x1c811d031cfa1ce4, 0x1d291d351d171d0c, 0x1d4c1d451d201d30, + 0x1d6b1d641d3e1d51, 0x1d811d951d701d5a, 0x1d8f1d8a1d9b1d85, + 0x1db81da41dac1d79, 0x1dc51dbf1dbb1db2, 0x1dd61dd21dce1dca, + 0x1df11de61de31dde, 0x1e0b1e061c681df5, 0x1e291e241e1f1e13, + 0x1c6f1e391e361e2e, 0x3610352033f0311, 0x39d038e037f0370, + 0x33e032b03bb03ac, 0x37e036f03600351, 0x3ba03ab039c038d, + 0x4230418040d0402, 0x56a0a530b0f042e, 0xa590ce60c580a0f, + 0x210a06db0a600a5c, 0x223d21f920892200, 0xbea11b40c260cda, + 0x689075b071c0b7b, 0xc290cdd0b8c0a26, 0x6010bf611c011b7, + 0x68c07640b7e068d, 0xa560bfd11c30893, 0x11c60c350aec0b94, + 0xc030b970a300c00, 0xc070b9a0a340a33, 0xc1b0b9e0a380a37, + 0x7680b8206910c1f, 0xd000cfa0cf60690, 0xc0f11c90c380ce9, + 0xbed11ba0c2c0ce0, 0xc2f0ce3076c0b86, 0x76f0b890bf011bd, + 0x5d70999077b0bb4, 0x5e805ff0a2d0a2a, 0x6ae0b1306940a50, + 0xba20722071f0b3a, 0xbc60bc20bbf0bbc, 0x8200c0b0bf90bf3, + 0xd25082b08230cd5, 0x5d1092a09360869, 0x36c035d034a0337, + 0x3a80399038a037b, 0x3490336032303b7, 0x389037a036b035c, + 0x3fe03b603a70398, 0x42a041f04140409, 0x44a0443043c0435, + 0xaf4047804710451, 0x0, 0x0, 0x0, 0x0, 0x26b4, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xe730e6b, 0x0, 0x256a258422132556, + 0x26ae1ff91eff22c6, 0x202b25c8209226ae, 0x244a238221872090, + 0x25a8251e250424e6, 0x233c22f0229a2254, 0x1f11265225bc24ca, + 0x24e42302225e1fe3, 0x24dc22d620e6267a, 0x250a247821a12526, + 0x232822a6221d211f, 0x1fb31f7d1f3125ae, 0x23922300225a21d5, + 0x257e24ec24e02456, 0x23b02678266a2610, 0x25d624bc242c23d0, + 0x212d206d2540267e, 0x23b4231a24682408, 0x20d4206b260c2566, + 0x242422ca22b02256, 0x246e1fb125ec2438, 0x242e23e61f811f83, + 0x21a3254e25f024c8, 0x20be1f0525462254, 0x1fc3237223322159, + 0x1ef5214b1f3923b8, 0x1fed242221e12290, 0x253824cc23982063, + 0x21ab228c25962276, 0x1f1f237021bb24a8, 0x241822441f7f1f5d, + 0x1fb725c6253e2494, 0x21ef212720982011, 0x265625e423ba22d8, + 0x220f1fa5268c2680, 0x217b210d2590226c, 0x22f622d021d12189, + 0x2464243223e0234e, 0x25d8259c24d02588, 0x22ee201b1fa71f91, + 0x2155211d25382514, 0x232c2406227221b7, 0x20f020be20491f27, + 0x24502348233a215b, 0x2612260a25ca2460, 0x25c023da1f332630, + 0x1f451f15216725fe, 0x225421e720d020c0, 0x25a624d6238022fc, + 0x1fa325ea220726aa, 0x22be22a22233222d, 0x242023ae236e2340, + 0x25f221911f612636, 0x258a22b220e21f3d, 0x23322237216b2143, + 0x20d820091f9525f6, 0x2294224a222521fc, 0x251624462378233e, + 0x1fcb260425c4251c, 0x235022fe200b22c0, 0x2682266e25f824de, + 0x23f6247c22ae2231, 0x22ea2326240e23fc, 0x1f9724b01f23254c, + 0x241421a521151f8f, 0x258e220d229c20b6, 0x2123252c25ee250e, + 0x20371f4d, 0x220500002061, 0x238c232a1f850000, 0x23d823ce23cc23be, + 0x245224102616, 0x2544000024e2, 0x25b4259e0000, 0x2642264000000000, + 0x25fc25b026762644, 0x1faf1f511f471f35, 0x203d202f1fd51fb5, + 0x20d62065205f2041, 0x21792175216120da, 0x220921f321db2185, + 0x22ce22b622a82246, 0x23b22342230822f8, 0x23c623c223c42240, + 0x23d623d423ca23c8, 0x2432240023f223e8, 0x24582444243a2436, + 0x24ce249a249a2480, 0x254a2548252e2522, 0x259e259a256e256c, + 0x215d263426282606, 0x248c274b, 0x1f2f1f5b1f7b1ef9, + 0x1fbb1fad1f651f4f, 0x203b202d2025202f, 0x2094208e20692061, + 0x2125212120aa20a2, 0x21712165214d213d, 0x2185217321792169, + 0x21c921c521bf2193, 0x221f221d220521dd, 0x22a22276226e2229, + 0x22dc22ce22c422c8, 0x2324230a23a422f8, 0x236a2358234a232a, + 0x238e238c237e237c, 0x23b6239e23a02396, 0x2428240c240023f4, + 0x24b2245824402432, 0x252a2524250024c6, 0x253c2544253a252e, + 0x254a254225462548, 0x25a4258c256e2550, 0x260625f425ce25be, + 0x262e262826202616, 0x272126ae265e2634, 0x1ea11e8d2733271f, + 0x27a9277927671ea3, 0x26ac26a4, 0x0, 0xade0ae30adf0adb, + 0xd280d280ae2, 0x0, 0x0, 0x134e000000000000, 0x134b135113481345, + 0x0, 0x13d2000013850000, 0x1374136f135413a6, 0x13b7139b1360138e, + 0x13ca13c702f413cd, 0x1359135613c313bf, 0x1371136c1364135c, + 0x137f137c1376, 0x1390138b13881382, 0x139d00001398, + 0x13a8000013a313a0, 0x13b413b1000013ab, 0x137913cf13bc13b9, + 0x135f13ae13931367, 0x181e181e18181818, 0x18201820181e181e, + 0x1824182418201820, 0x181c181c18241824, 0x18221822181c181c, + 0x181a181a18221822, 0x183c183c181a181a, 0x183e183e183c183c, + 0x18281828183e183e, 0x1826182618281828, 0x182a182a18261826, + 0x182c182c182a182a, 0x18321832182c182c, 0x1834183418301830, + 0x18381838182e182e, 0x1840184018361836, 0x1844184418401840, + 0x1848184818441844, 0x1846184618481848, 0x184a184a18461846, + 0x184c184c184c184c, 0x18501850186d186d, 0x184e184e18501850, + 0x15911591184e184e, 0x186a186a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1842000000000000, 0x1803184218421842, 0x180717ff17ff1803, + 0x18621862185b1807, 0x1860186018551855, 0x180b180b180b180b, + 0x17cd17cd14151415, 0x17f117f1180d180d, 0x17fd17fd18011801, + 0x1809180918051805, 0x17f517f517f51809, 0x1864186418641864, + 0x17f517e517d517d1, 0x13fe13f713f417f9, 0x141e14171414140b, + 0x146a144d1438142d, 0x1484147b1472146d, 0x14311422148c1487, + 0x143c14d414d11435, 0x151a150c150514fa, 0x15a515a215931562, + 0x15c815c515ba15b0, 0x1607157515e415df, 0x16451642163f160a, + 0x165b16561653164c, 0x1679167416711662, 0x16851682167f167c, + 0x16aa169616931688, 0x1579158c16c816b9, 0x14591455145116e0, + 0x172d1461145d1526, 0x17691758174f1740, 0x177f17741771176c, + 0x17aa17a3179c1782, 0x14e417c717c417b3, 0x64005d179714ee, + 0x8000790072006b, 0x17e917e517e117dd, 0x140813db17f917f5, + 0x14171414140e140b, 0x1464144d144a1447, 0x14781475146d146a, + 0x14871484147e147b, 0x1674167116561653, 0x1693168816851679, + 0x16e01579158c1696, 0x17551752152616e5, 0x176c176917631758, + 0x17b317b017ad1797, 0x17d117c717c417be, 0x17ed17e517d917d5, + 0x140b13fe13f713f4, 0x1438142d141e1411, 0x148c147b1467144d, + 0x14d1143514311422, 0x150c150514fa143c, 0x1593156d1562151a, + 0x15ba15b015a515a2, 0x157515e415df15c5, 0x1642163f160a1607, + 0x1662165b164c1645, 0x16851682167f167c, 0x16c816b916aa1688, + 0x1455145113e0158c, 0x1740172d15261459, 0x177117661758174f, + 0x17a3179c17851774, 0x17e515ed17b317aa, 0x144d1411140b17ed, + 0x151a1481147b1467, 0x16851557154c1529, 0x17661758158c1688, + 0x162c162515ed17b3, 0x15ff15da15d71633, 0x152c161c16191602, + 0x1490155d155a152f, 0x1440142a142613fb, 0x15bd159d159a1402, + 0x1546153b153415c0, 0x157015171549154c, 0x15ff15da15d715b7, + 0x152c161c16191602, 0x1490155d155a152f, 0x1440142a142613fb, + 0x15bd159d159a1402, 0x1546153b153415c0, 0x157015171549154c, + 0x1546153b153415b7, 0x15c815571529154c, 0x1534150c150514fa, + 0x15df15c81546153b, 0x13e313e3, 0x0, 0x0, 0x0, 0x0, + 0x1434143014301421, 0x145814541450143b, 0x14c114c514a314a3, + 0x1521150114fd1508, 0x15251525151d1521, 0x153e159615651565, + 0x154f154f1537153e, 0x15b315a815531553, 0x15cf15cb15cb15b3, + 0x15f315f315e715d3, 0x16111615160d15f7, 0x1669166516481648, + 0x16ad16c016c416bc, 0x16d216cb16cb16ad, 0x170b170216fe16d2, + 0x1716171216f316eb, 0x177716ef00000000, 0x173417471743177b, + 0x175b175f17381734, 0x1429140117b617b6, 0x1460143f14431425, + 0x14a7148f14ab145c, 0x15ac15421569150f, 0x179f17a616d616b5, + 0x174b166d172117ba, 0x168f15fb16bc1665, 0x168b16b1171a1730, + 0x14ba1493173016b1, 0x168b13fa164f16f7, 0x173c1513159615e7, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13d913de165e158f, + 0x15eb14e915731706, 0x1497157c1578158a, 0x14f1, 0x0, 0x0, 0x0, 0x0, + 0x5401b331b3102f6, 0x1b770093008d0546, 0x2ff1b79, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x9931a6b1a6d02fc, 0xe3b00a500a10993, 0x1b451b4f1b4b0e3f, + 0x1b351b3b1b391b47, 0x1b411b3f1b3d1b37, 0x98b000000001b43, + 0xc000c000c098f, 0x99309930993000c, 0x2fa1b3102f6, + 0x8d009305400546, 0xe3b00a500a11a6d, 0x971b4f1b4b0e3f, + 0x2f802f402f2009d, 0x54405590548, 0x566009b0099098d, 0x0, + 0x5a161f0057, 0x1622006800000061, 0x163000761629006f, + 0x163a00841637007d, 0x13e913e613e613d5, 0x13ec178f178f13e9, + 0x17ca17ca17ca13ec, 0x13f213d713d717ca, 0x141a13f213f213f2, + 0x141c141c141c141a, 0x147014701470141c, 0x13f513f513f51470, + 0x13f813f813f813f5, 0x13ff13ff13ff13f8, 0x14e214e014e013ff, + 0x140913dc13dc14e2, 0x14f814f814f81409, 0x15321532153214f8, + 0x1560156015601532, 0x15a015a015a01560, 0x15c315c315c315a0, + 0x15dd15dd15dd15c3, 0x15e215e215e215dd, 0x16051605160515e2, + 0x163d163d163d1605, 0x165916591659163d, 0x1677167716771659, + 0x14ec14ec14ec1677, 0x140c140c140c14ec, 0x140f140f140f140c, + 0x13e113e113e1140f, 0x14151788178813e1, 0x13fc13fc13fc1415, + 0x16a2169e169e13fc, 0x169b16a616a616a2, 0x169b, 0x970095008d0000, + 0x9f009d009b0099, 0x2f402f200a500a1, 0x30302fa02f802f6, + 0x30f034303140305, 0x392038303740365, 0x546054003b003a1, + 0x93055905440548, 0x5e305d505680566, 0x687067e062905e6, + 0x71a060706cf06ac, 0x7a4077e07230734, 0x85e082c083b06af, + 0x77006b2056b088d, 0x98b060a095a0682, 0x9930991098f098d, + 0x9a0093706920995, 0x6020ad90a7d0a2e, 0xb79073e0ae00b0d, + 0x7870b3b05d30a28, 0x8400a1105d80cd3, 0xde1086a0a240ba3, + 0xe3b061106950b41, 0x1b280e410e3f0e3d, 0x1b3f1b3d1b331b2a, + 0x1bd61e551e5c1b31, 0x1c181c081bfd1bef, 0x1cee1e171e0f1e02, + 0x1bff1bf11bd81c16, 0x1c411c241c1a1c0a, 0x1cad1c991c901c73, + 0x1cda1ccd1c1e1cbd, 0x1cf51cf01bfb1cdf, 0x1d111d0f1ca61bde, + 0x1d391d1b1d0d1c8e, 0x1c311d9f1d741d55, 0x1e001def1c221ddc, + 0x1e1b1e191e111e04, 0x1c5d1e341bed1c35, 0x8b00881c061e42, + 0x1a101949194419d4, 0x19501a141a12194b, 0x1a181a1619571955, + 0x1a201a1e1a1c1a1a, 0x19661961195c19a6, 0x196f196d196819b0, + 0x198e198319811977, 0x199d19981993, 0x19d8194700000000, + 0x19e019de19dc19da, 0x19e419e200000000, 0x19ec19ea19e8198c, + 0x197519ee00000000, 0x19f819f619f419f2, 0x197f19fa00000000, 0x19fe, + 0x90e4b0e450e43, 0x1a820e470e49, 0x1a8b1a891a841b22, + 0x1b261b241a90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x26b600000000, 0x26b9, 0x0, 0x0, 0x26bc000000000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c226bf00000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c826c500000000, + 0x26d726d326cf26cb, 0x26db, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x26df000000000000, 0x26e626ed26e226ea, 0x26f1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5e605e305d50568, + 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, + 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, + 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, + 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, + 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, + 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, + 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae000000602, + 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, + 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, + 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, + 0x682077006b2056b, 0x9370692060a095a, 0xad90a7d0a2e09a0, + 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, + 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e300000568, + 0x68700000000, 0x71a06070000, 0x6af07a4077e0000, 0x88d085e0000083b, + 0x682077006b2056b, 0x9370692060a095a, 0xad900000a2e09a0, + 0x73e0ae00b0d0000, 0xb3b05d30a280b79, 0xa1105d80cd30000, + 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, + 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, + 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, + 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, + 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, + 0x5e6000005d50568, 0x687067e0629, 0x734071a06070000, + 0x6af07a4077e0723, 0x88d085e0000083b, 0x682077006b2056b, + 0x93706920000095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, + 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, + 0x61106950b410de1, 0x5e6000005d50568, 0x687067e0629, + 0x734071a060706cf, 0x7a400000723, 0x88d085e00000000, + 0x682077006b2056b, 0x93706920000095a, 0xad90a7d0a2e09a0, + 0x73e0ae00b0d0602, 0xb3b05d30a280b79, 0xa1105d80cd30787, + 0x86a0a240ba30840, 0x61106950b410de1, 0x5e605e305d50568, + 0x6ac0687067e0629, 0x734071a060706cf, 0x6af07a4077e0723, + 0x88d085e082c083b, 0x682077006b2056b, 0x9370692060a095a, + 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, + 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, + 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, + 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, + 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x9370692060a095a, + 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, 0xb3b05d30a280b79, + 0xa1105d80cd30787, 0x86a0a240ba30840, 0x61106950b410de1, + 0x5e605e305d50568, 0x6ac0687067e0629, 0x734071a060706cf, + 0x6af07a4077e0723, 0x88d085e082c083b, 0x682077006b2056b, + 0x9370692060a095a, 0xad90a7d0a2e09a0, 0x73e0ae00b0d0602, + 0xb3b05d30a280b79, 0xa1105d80cd30787, 0x86a0a240ba30840, + 0x61106950b410de1, 0x5e605e305d50568, 0x6ac0687067e0629, + 0x734071a060706cf, 0x6af07a4077e0723, 0x61106950b410de1, 0xe800e6f, + 0xf3c0f3a0f380ee3, 0xfad0f5e0f5c0f3e, 0xfe20fe00fde0faf, + 0x10060fe80fe60fe4, 0x100f100d0fad1008, 0x1035103310311011, + 0x10ea10861aa3077c, 0x110e10f010ee10ec, 0x11ae1170116e1110, + 0x11ce11cc11b211b0, 0x11f811f011ee11d0, 0x123c11fe11fc11fa, + 0x1a9e12421240123e, 0x123c11ae116e10f0, 0xf380ee311ee11f0, + 0xf5c0f3e0f3c0f3a, 0xfde0faf0fad0f5e, 0xfe60fe40fe20fe0, + 0xfad100810060fe8, 0x10311011100f100d, 0x1aa3077c10351033, + 0x10ee10ec10ea1086, 0x116e1110110e10f0, 0x11b211b011ae1170, + 0x11ee11d011ce11cc, 0x11fc11fa11f811f0, 0x1240123e123c11fe, + 0x116e10f01a9e1242, 0x11ee11f0123c11ae, 0xf3c0f3a0f380ee3, + 0xfad0f5e0f5c0f3e, 0xfe20fe00fde0faf, 0x10060fe80fe60fe4, + 0x100f100d0fad1008, 0x1035103310311011, 0x10ea10861aa3077c, + 0x110e10f010ee10ec, 0x11ae1170116e1110, 0x11ce11cc11b211b0, + 0x11f811f011ee11d0, 0x123c11fe11fc11fa, 0x1a9e12421240123e, + 0x123c11ae116e10f0, 0xf380ee311ee11f0, 0xf5c0f3e0f3c0f3a, + 0xfde0faf0fad0f5e, 0xfe60fe40fe20fe0, 0xfad100810060fe8, + 0x10311011100f100d, 0x1aa3077c10351033, 0x10ee10ec10ea1086, + 0x116e1110110e10f0, 0x11b211b011ae1170, 0x11ee11d011ce11cc, + 0x11fc11fa11f811f0, 0x1240123e123c11fe, 0x116e10f01a9e1242, + 0x11ee11f0123c11ae, 0xf3c0f3a0f380ee3, 0xfad0f5e0f5c0f3e, + 0xfe20fe00fde0faf, 0x10060fe80fe60fe4, 0x100f100d0fad1008, + 0x1035103310311011, 0x10ea10861aa3077c, 0x110e10f010ee10ec, + 0x11ae1170116e1110, 0x11ce11cc11b211b0, 0x11f811f011ee11d0, + 0x123c11fe11fc11fa, 0x1a9e12421240123e, 0x123c11ae116e10f0, + 0x12a212a011ee11f0, 0x314030500000000, 0x3740365030f0343, + 0x3b003a103920383, 0x30f034303140305, 0x392038303740365, + 0x314030503b003a1, 0x3740365030f0343, 0x3b003a103920383, + 0x30f034303140305, 0x392038303740365, 0x314030503b003a1, + 0x3740365030f0343, 0x3b003a103920383, 0x14e013f513f213d7, + 0x13f8140917880000, 0x14ec167713fc15c3, 0x15e214f8140f140c, + 0x13dc16591560163d, 0x13ff1470141c1532, 0x160515dd15a014e2, + 0x1816183a184a1814, 0x13f513f20000, 0x13f80000000013e1, + 0x14ec167713fc0000, 0x15e214f8140f140c, 0x16591560163d, + 0x13ff1470141c1532, 0x1605000015a00000, 0x0, 0x13f500000000, + 0x13f8000000000000, 0x14ec000013fc0000, 0x15e214f8140f0000, + 0x165915600000, 0x13ff000000001532, 0x1605000015a00000, + 0x18160000184a0000, 0x13f513f20000, 0x13f80000000013e1, + 0x167713fc15c3, 0x15e214f8140f140c, 0x16591560163d, + 0x13ff1470141c1532, 0x160515dd15a00000, 0x183a00001814, + 0x14e013f513f213d7, 0x13f81409178813e1, 0x14ec000013fc15c3, + 0x15e214f8140f140c, 0x13dc16591560163d, 0x13ff1470141c1532, + 0x160515dd15a014e2, 0x0, 0x14e013f513f20000, 0x13f8140917880000, + 0x14ec000013fc15c3, 0x15e214f8140f140c, 0x13dc16591560163d, + 0x13ff1470141c1532, 0x160515dd15a014e2, 0x0, 0x3f103160307030a, + 0x4fa04de04ab0468, 0x5310520050b, 0x0, 0x10a0106010200fe, + 0x11a01160112010e, 0x12a01260122011e, 0x13a01360132012e, + 0x14a01460142013e, 0x15a01560152014e, 0x5e31b4d0162015e, + 0x93305e5082c, 0x5e605e305d50568, 0x6ac0687067e0629, + 0x734071a060706cf, 0x6af07a4077e0723, 0x88d085e082c083b, + 0x682077006b2056b, 0x76c06b1060a095a, 0x930082708660860, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x761075e00000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x1cad1c9e1bc3, 0x0, 0x0, + 0x0, 0x1cf71ff320b02197, 0x208c253220811f17, 0x21e722f221fe1f1d, + 0x21eb1f6921451f9d, 0x2560235c24261f93, 0x219d22cc200f2073, + 0x25a01eef1ee921b3, 0x21ad20011efd20fa, 0x23f023d221992574, + 0x329221b22bc2005, 0x20351f9f2366, 0x0, 0x1b5d1b551b511b69, + 0x1b591b711b611b6d, 0x1b65, 0x0, 0x1ffd2147, 0x0, 0x0, 0x0, + 0x26f51f0b1f031f07, 0x1f3b1f371f351f2d, 0x1f431f471f411f3f, + 0x1f531f5126fd1e63, 0x1e6526f71f631f55, 0x1f7126fb1f691f59, + 0x1f7b1f791f251f75, 0x1e691f8d1f8927b9, 0x1fa11f9f1f9b1f99, + 0x1fb51faf1fad1e6b, 0x1fc31fbf1fbd1fbb, 0x1fe11fd91fd51fd3, + 0x1fe71fe71fe71fe5, 0x1ff51ff122e42703, 0x20031fff1ffb2705, + 0x20152013200d2017, 0x2023201f201d2019, 0x202d202920292027, + 0x204b203920332031, 0x2043203f204d203d, 0x2057205520711f8f, + 0x205b205d20532059, 0x2077207527072067, 0x209620852081207b, + 0x209e209c270b2709, 0x1e6d20a4209a20a0, 0x20ac20ac20a81e6f, + 0x20be20bc20ba270d, 0x20c820c6270f20c2, 0x20d21e7120cc2137, + 0x271320de20e020da, 0x20e820ea271520e4, 0x1e7320f620f420ec, + 0x21062104210220fe, 0x21171e7727171e75, 0x27cd211f211b2119, + 0x2486271b271b212b, 0x27291e7921332133, 0x1e7b213f213b277d, + 0x2157215321512149, 0x21611e7d1e7f215f, 0x216f216d2163271d, + 0x21792177216f2171, 0x2183217f217d2181, 0x218f210b21872185, + 0x21b121a7219f219b, 0x21b521a921af2723, 0x21c7272521c321b9, + 0x21cb1e8121bd21c1, 0x1e8321cd21d321cf, 0x21f5272721df21db, + 0x22091e8922032215, 0x1f6d1f6b1e851e87, 0x1ebb2470220b2217, + 0x222b2221221f221d, 0x22351e8b27312227, 0x273522462242222f, + 0x1e8d224c22392248, 0x225822522250224e, 0x22621e8f225c2737, + 0x226a1e9122642739, 0x273b227822762270, 0x273f2288273d2711, + 0x2298228a2292228e, 0x22a422a222a822a0, 0x229e274122ac22aa, + 0x22c41e9322ba22b8, 0x22d222b4274322c2, 0x22de22d427472745, + 0x22e01e9522da22dc, 0x26f922ec22e622e8, 0x274d22fa274922f4, + 0x274f2314230a2304, 0x275327512320231e, 0x23381e972336232e, + 0x234623441e991e99, 0x1e9b2352234c234a, 0x2757236c2755235e, + 0x2759237a27192372, 0x1e9f1e9d275d275b, 0x2763275f27612396, + 0x239c239c239a2765, 0x1ea523a21ea323a0, 0x23b023ac27691ea7, + 0x23c8276b1ea923b6, 0x23e423d8276f276d, 0x23ec23ea23e81eab, + 0x23f8277327732771, 0x2404240227751ead, 0x1eb1241227771eaf, + 0x277b241e2416241a, 0x243424301eb3242a, 0x2781277f1eb5243c, + 0x2785244827831eb7, 0x278724582454244e, 0x2466278b24622789, + 0x247424721eb9272b, 0x278d20a624761ebd, 0x2486272f272d278f, + 0x249e1ebf25942488, 0x24a21fa924a0249c, 0x279124aa24a624a4, + 0x24b824b624ac24a8, 0x24ce24c424ba24ae, 0x24c224c024be24b4, + 0x1ec1279527972793, 0x279f24d824d424d2, 0x1ec51ec3279924da, + 0x24ea1ec7279d279b, 0x24f624f024ee24ec, 0x250024f824fa24f4, + 0x1ec9250224fe24fc, 0x25101ecb25082506, 0x251a251827a12512, + 0x27a31e6725201ecd, 0x25361ed11ecf27a5, 0x27a7255825502542, + 0x2576257025642562, 0x257a257c26ff27ab, 0x258c258627012580, + 0x25b225ac27af27ad, 0x25cc25b827b125b6, 0x25da25d025d425d2, + 0x1ed325e227b325dc, 0x26021ed527b525e6, 0x27bb27b7260e20ee, + 0x27bd26221ed91ed7, 0x262e262e27bf1edb, 0x1edd263e27c12632, + 0x26542650264c2646, 0x266c265e27c31edf, 0x26741ee31ee12672, + 0x27c927c71ee527c5, 0x26901ee7268627cb, 0x269e269a26962694, + 0x27cf26a2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //12288 bytes + enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, + 0x120], [0x100, 0x400, 0x1380], [0x302020202020100, + 0x205020202020204, 0x602020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x1000000000000, 0x5000400030002, 0x6, + 0x9000800070000, 0xc0000000b000a, 0x0, 0xe00000000000d, 0x0, 0x0, + 0x1100000010000f, 0x130012, 0x16001500140000, 0x18000000170000, + 0x1a000000190000, 0x0, 0x1c001b0000, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f001e, 0x0, 0x0, 0x23002200210020, + 0x27002600250024, 0x28, 0x2b002a00000029, 0x2f002e002d002c, 0x30, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31000000000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x34003300320000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x38003700360035, 0x3c003b003a0039, 0x3e003d, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x3f00000000, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x43004200410000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x47004600450044, 0x4b004a00490048, 0x4c, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x250012000f000c, 0x850000004f0045, 0xcb00a400a1009e, + 0x13301240121011e, 0x1a0019d01880000, 0x1da01b601a3, + 0x2730270026d0000, 0x2f30287, 0x33803250322031f, 0x398000003620358, + 0x3de03b703b403b1, 0x446043a04370434, 0x4b404b1049c0000, + 0x4ee04ca04b7, 0x58a058705840000, 0x61c0000060d059e, + 0x33e002b033b0028, 0x38c00790380006d, 0x392007f038f007c, + 0x3a2008f03950082, 0x3cd00ba00000000, 0x3db00c803d800c5, + 0x3e400d103fb00e8, 0x41000fd040a00f7, 0x419010604130100, 0x41c0109, + 0x440012a043d0127, 0x45c01490443012d, 0x130, 0x471015d0462014f, + 0x170047701630000, 0x47a01660484, 0x185000000000000, + 0x18e04a801940499, 0x4a2, 0x4e401d004d901c5, 0x4f801e4, + 0x5450231052f021b, 0x54b023705350221, 0x56902550552023e, + 0x57b026405580244, 0x572025b, 0x594027d058d0276, 0x5b4029d059b0284, + 0x5e002c905b702a0, 0x61002f605f502de, 0x3110628030b0302, + 0x6310314062e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50401f0, + 0x0, 0x0, 0x2ac000000000000, 0x5c3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x13d036900560000, 0x2a304fb01e70450, 0x28e05a9029205ba, + 0x28a05ad029605a5, 0x35b0048000005a1, 0x653064a03540041, + 0x416010300000000, 0x522020e046b0157, 0x65f065c05250211, 0x465, + 0x40700f4, 0x365005204960182, 0x656064d06500647, 0x36f005c036c0059, + 0x3ea00d703e700d4, 0x456014304530140, 0x50101ed04fe01ea, + 0x53b022705380224, 0x5c002a905bd02a6, 0x578026105660252, + 0x425011200000000, 0x0, 0x351003e00000000, 0x4f101dd03f400e1, + 0x4e701d304d101bd, 0x61602fc04ea01d6, 0x0, 0x0, 0x0, + 0x66b00000010000d, 0x137, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x662, 0x0, 0x100000000, 0x0, 0x6450670063d0000, + 0x72c06df06c3, 0x798077800000759, 0x8d1, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x781073500000000, 0x8c10867084707e9, 0x92f, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x92808ca00000000, 0x95f091f08fd, 0x9b4000000000000, 0x9b7, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x9c3000009cc09c6, 0x9ba000000000000, 0x0, 0x9ed09d809e4, 0x0, 0x0, + 0x9de0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa200000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xa0500000a0e0a08, 0xa41000000000000, 0x0, + 0xa2f0a1a0a26, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa470a4400000000, 0x0, + 0x0, 0xa1109cf0000, 0x0, 0x0, 0x0, 0xa0209c009ff09bd, + 0xa0b09c900000000, 0xa4d0a4a00000000, 0xa1709d50a1409d2, + 0xa1d09db00000000, 0xa2909e70a2309e1, 0xa530a5000000000, + 0xa2c09ea0a3e09fc, 0xa3509f30a3209f0, 0xa3809f6, 0xa3b09f9, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac10abe00000000, + 0xaca0ac40ac7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xacd00000ad3, 0x0, + 0x0, 0x0, 0xad0000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xae80000, 0x0, 0xaf10000, 0xaf4, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xadf0adc0ad90ad6, 0xaee0aeb0ae50ae2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb00000000000000, 0xb03, 0x0, + 0x0, 0x0, 0xafd00000afa0af7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xb12000000000000, 0xb1500000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xb0c0b090b060000, 0xb0f00000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb1e000000000b21, 0xb24, 0x0, 0x0, + 0x0, 0xb1b0b18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xb27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xb300b2a00000000, 0xb2d, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb33, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb36, + 0xb40000000000000, 0xb3c0b3900000b43, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4c0b4600000000, + 0xb49, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4f00000000, 0xb590b550b52, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb5f000000000000, 0x0, 0x0, + 0xb620000, 0xb6500000000, 0xb68000000000000, 0x0, 0xb6b, 0x0, 0x0, + 0xb5c0000, 0x0, 0xb6e000000000000, 0xb890b710000, 0xb8c, 0x0, + 0xb740000, 0x0, 0x0, 0x0, 0xb7a000000000000, 0x0, 0x0, 0xb7d0000, + 0xb8000000000, 0xb83000000000000, 0x0, 0xb86, 0x0, 0x0, 0xb770000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8f00000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb9200000000, 0xb9800000b95, + 0xb9e00000b9b, 0xba100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xba4000000000000, 0xba70000, 0xbb000000bad0baa, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x3830070037d006a, 0x389007603860073, 0x39f008c039b0088, + 0x3ae009b03a50092, 0x3ab009803a80095, 0x3d400c103d000bd, + 0x40100ee03fe00eb, 0x40400f103f700e4, 0x41f010c040d00fa, + 0x422010f04280115, 0x42e011b042b0118, 0x4490136045f014c, + 0x46e015a04680154, 0x47d016904740160, 0x48a01760480016c, + 0x48d017904870173, 0x493017f0490017c, 0x4a50191049f018b, + 0x4ab019704ae019a, 0x4d501c104cd01b9, 0x4e001cc04dc01c8, + 0x52c021805290215, 0x53e022a0532021e, 0x54802340541022d, + 0x55f024b05550241, 0x55b0247054e023a, 0x56c02580562024e, + 0x581026a0575025e, 0x5dd02c6057e0267, 0x5e302cc05e602cf, + 0x597028005900279, 0x5ec02d505e902d2, 0x5f202db05ef02d8, + 0x5f802e105fb02e4, 0x60402ea060102e7, 0x61902ff060702ed, + 0x6340317062b030e, 0x56f04310637031a, 0x6590000062205fe, 0x0, + 0x35f004c0372005f, 0x3280015032c0019, 0x330001d03340021, + 0x345003203750062, 0x34d003a0341002e, 0x379006603490036, + 0x3e100ce03ed00da, 0x3be00ab03ca00b7, 0x3c600b303ba00a7, + 0x3f000dd03c200af, 0x4590146044d013a, 0x4f501e1051b0207, + 0x4ba01a604be01aa, 0x4c201ae04c601b2, 0x50b01f7051e020a, + 0x51301ff050701f3, 0x5170203050f01fb, 0x5b1029a05da02c3, + 0x5c602af05ca02b3, 0x5ce02b705d202bb, 0x60a02f005d602bf, + 0x61f030506250308, 0x61302f9, 0x0, 0x81b07f9081807f6, + 0x82d080b08240802, 0x69e067c069b0679, 0x6b0068e06a70685, + 0x858084d0855084a, 0x85c0851, 0x6d406c906d106c6, 0x6d806cd, + 0x89308710890086e, 0x8a50883089c087a, 0x70706e5070406e2, + 0x71906f7071006ee, 0x8eb08dc08e808d9, 0x8f308e408ef08e0, + 0x74a073b07470738, 0x7520743074e073f, 0x90e0903090b0900, 0x9120907, + 0x76a075f0767075c, 0x76e0763, 0x949093a09460937, 0x9510942094d093e, + 0x787000007840000, 0x78f0000078b0000, 0x98b096909880966, + 0x99d097b09940972, 0x7c0079e07bd079b, 0x7d207b007c907a7, + 0x847084407e907e2, 0x8c108be08670860, 0x91f091c08fd08fa, 0x95f0958, + 0x81f07fd08360814, 0x831080f08280806, 0x6a2068006b90697, + 0x6b4069206ab0689, 0x897087508ae088c, 0x8a9088708a0087e, + 0x70b06e907220700, 0x71d06fb071406f2, 0x98f096d09a60984, + 0x9a1097f09980976, 0x7c407a207db07b9, 0x7d607b407cd07ab, + 0x84107e507f007f3, 0x83d083a000007ec, 0x670066d06730676, + 0x8bc000006bd, 0x8b9086306400000, 0x8b508b20000086a, + 0x6df06dc06c306c0, 0xbb90bb60bb30726, 0x8d108cd08c408c7, + 0x8d508f700000000, 0x72c0729072f0732, 0xbc20bbf0bbc0000, + 0x92f092b09220925, 0x933095509190916, 0x7780775077b077e, + 0x31d063d063a0772, 0x9b1095b00000000, 0x9ad09aa00000962, + 0x798079507590756, 0x64307df, 0xbc70bc5, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x79300000000, 0x4f015200000000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xbcc0bc900000000, 0x0, 0x0, 0x0, 0x0, 0xbcf00000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd50bd80bd20000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbdb, 0xbde0000, + 0xbe1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe700000be4, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xbea0000, 0xbf0000000000bed, 0xbf30000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xbf900000006, 0x0, 0x0, 0x900030bf60000, 0xbff0bfc, + 0xc050c02, 0xc0b0c08, 0x0, 0xc110c0e, 0xc1d0c1a, 0xc230c20, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc350c320c2f0c2c, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xc290c260c170c14, 0x0, 0xc3b0c3800000000, 0xc410c3e, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xc490c470000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xc5100000c4e, 0xc5700000c54, 0xc5d00000c5a, 0xc6300000c60, + 0xc6900000c66, 0xc6f00000c6c, 0xc7500000c720000, 0xc780000, 0x0, + 0xc8100000c7e0c7b, 0xc8a0c8700000c84, 0xc900c8d0000, 0xc960c93, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xc4b, 0x0, 0xc9900000000, 0x0, 0x0, 0x0, + 0xca200000c9f, 0xca800000ca5, 0xcae00000cab, 0xcb400000cb1, + 0xcba00000cb7, 0xcc000000cbd, 0xcc600000cc30000, 0xcc90000, 0x0, + 0xcd200000ccf0ccc, 0xcdb0cd800000cd5, 0xce10cde0000, 0xce70ce4, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xcea000000000c9c, 0xcf30cf00ced, + 0xcf600000000, 0x124b125d0fb71241, 0x13270e290d831043, + 0xe4f12930e991327, 0x116710cd0f550e97, 0x1279121511fd11e3, + 0x109d106910190feb, 0xd8d12f3128911c7, 0x11e110790ff50e1d, + 0x11d910510edb1309, 0x120311890f65121d, 0x108d10250fbd0eff, + 0xe050dd90d9d127d, 0x10d310770ff10f95, 0x125911e711dd1171, + 0x10e9130712fb12cf, 0x12a111b9114d1107, 0xf0b0e87122f130b, + 0x10ed1083117d112f, 0xecb0e8512cb1249, 0x11471047102f0fed, + 0x117f0e0312b11159, 0x114f11150ddd0ddf, 0xf67123d12b511c5, + 0xebb0d8712350feb, 0xe1110c110950f27, 0xd7f0f1b0da510f1, + 0xe2311450f9d1011, 0x122711c910d70e7d, 0xf6f100d126d1005, + 0xd9110bf0f7b11a5, 0x113d0fdb0ddb0dc3, 0xe091291122d1195, + 0xfa10f070e9f0e37, 0x12f712ab10f31053, 0xfb50df91313130d, + 0xf490ef312690ffd, 0x106d104b0f910f57, 0x11791153111110af, + 0x12a3127111cd1261, 0x10670e410dfb0de9, 0xf230efd1227120b, + 0x1091112d10030f77, 0xee50ebb0e670d97, 0x116b10a9109b0f29, + 0x12d112c912951175, 0x128d110f0d9f12dd, 0xdb10d8f0f3512c1, + 0xfeb0f9f0ec70ebd, 0x127711d310cb1073, 0xdf712af0fad1323, + 0x103b10210fd10fcb, 0x114310e710bd10a1, 0x12b70f5d0dc512e3, + 0x126310310ed70da9, 0x10950fd50f390f17, 0xecf0e310deb12bb, + 0x10150fe10fc30fa7, 0x120d116310c3109f, 0xe1312c5128f1213, + 0x10b110750e33103d, 0x130f12ff12bd11db, 0x1121118b102d0fcf, + 0x1063108b11331125, 0xded11ad0d93123b, 0x11390f690ef50de7, + 0x12670fb3101b0eb5, 0xf03122112b31205, 0xe590db5, 0xfab00000e7b, + 0x10cf108f0de10000, 0x110d1105110310f5, 0x116d113512d3, + 0x1233000011df, 0x128312730000, 0x12e912e700000000, + 0x12bf127f130512eb, 0xe010db90db30da1, 0xe5f0e530e170e07, + 0xecd0e7f0e790e63, 0xf470f430f2f0ed1, 0xfaf0fa30f970f53, + 0x1049103510270fdd, 0x10eb10a3107d106f, 0x10fd10f910fb10f7, + 0x110b1109110110ff, 0x11531127111d1117, 0x11731161115b1157, + 0x11cb11971197118d, 0x1239123712231219, 0x1273126f124f124d, + 0xf2b12e112d912c7, 0x119313be, 0xd9b0dc10dd70d81, + 0xe0b0dff0dc90db7, 0xe5d0e510e490e53, 0xe9b0e950e830e7b, + 0xf050f010eb10ea9, 0xf3f0f330f1d0f13, 0xf530f410f470f37, + 0xf890f850f7f0f5f, 0xfbf0fbd0fab0f99, 0x102110050fff0fc7, + 0x1057104910411045, 0x1089107f10e3106f, 0x10b910b510ab108f, + 0x10d110cf10c910c7, 0x10ef10dd10df10d5, 0x114911311127111f, + 0x11af1173115f1153, 0x121f121b11f911c3, 0x122b123312291223, + 0x1239123112351237, 0x12751265124f123f, 0x12c712b91299128b, + 0x12db12d912d512d3, 0x1394132712f912e1, 0xd370d2313a61392, + 0x141c13ec13da0d39, 0x13251321, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xabb00000a7a0000, + 0x0, 0x0, 0xab50ab200000000, 0xa590a560aae0aaa, 0xa680a650a5f0a5c, + 0xa740a710a6b, 0xa830a800a7d0a77, 0xa8c00000a89, 0xa9500000a920a8f, + 0xaa10a9e00000a98, 0xa6e0ab80aa70aa4, 0xa9b0a860a62, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x132900000000, 0x132c, 0x0, 0x0, 0x132f000000000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1335133200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x133b133800000000, 0x134a13461342133e, + 0x134e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1352000000000000, + 0x135913601355135d, 0x1364, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13680d8b0d850d89, + 0xda70da30da10d99, 0xdaf0db30dad0dab, 0xdbb0db913700cf9, + 0xcfb136a0dc70dbd, 0xdd1136e0dcb0dbf, 0xdd70dd50d950dd3, + 0xcff0de50de3142c, 0xdf50df30df10def, 0xe070e010dff0d01, + 0xe110e0f0e0d0e0b, 0xe1b0e190e170e15, 0xe210e210e210e1f, + 0xe270e25105d1376, 0xe2f0e2d0e2b1378, 0xe3b0e390e350e3d, + 0xe470e450e430e3f, 0xe510e4d0e4d0e4b, 0xe690e5b0e570e55, + 0xe650e610e6b0e5f, 0xe710e6f0e890de7, 0xe750e770e6d0e73, + 0xe8d0e8b137a0e81, 0xe9d0e930e910e8f, 0xea50ea3137e137c, + 0xd030eab0ea10ea7, 0xeb30eb30eaf0d05, 0xebb0eb90eb71380, + 0xec30ec113820ebf, 0xec90d070ec50f0f, 0x13860ed30ed50ed1, + 0xedd0edf13880ed9, 0xd090ee90ee70ee1, 0xef10eef0eed0eeb, + 0xef70d0d138a0d0b, 0x14400eff0efb0ef9, 0x118f138e138e0f09, + 0x139c0d0f0f0d0f0d, 0xd110f150f1113f0, 0xf250f210f1f0f19, + 0xf2f0d130d150f2d, 0xf3d0f3b0f311390, 0xf470f450f3d0f3f, + 0xf510f4d0f4b0f4f, 0xf5b0f590f550f53, 0xf730f6b0f630f61, + 0xf750f6d0f711396, 0xf8713980f830f79, 0xf8b0d170f7d0f81, + 0xd190f8d0f930f8f, 0xfa5139a0f9b0f97, 0xfaf0d1f0fa90fb9, + 0xdcf0dcd0d1b0d1d, 0xd5111810fb10fbb, 0xfc90fc10fbf0fbd, + 0xfd30d2113a40fc5, 0x13a80fdd0fd90fcd, 0xd230fe30fd70fdf, + 0xfef0fe90fe70fe5, 0xff70d250ff313aa, 0xffb0d270ff913ac, + 0x13ae100710051001, 0x13b2100913b01384, 0x1017100b1013100f, + 0x102310211027101f, 0x101d13b4102b1029, 0x10410d2910391037, + 0x104d103313b6103f, 0x1059104f13ba13b8, 0x105b0d2b10551057, + 0x136c1065105f1061, 0x13c0107113bc106b, 0x13c21081107f107b, + 0x13c613c410871085, 0x10990d2d10971093, 0x10a710a50d2f0d2f, + 0xd3110b310ad10ab, 0x13ca10bb13c810b7, 0x13cc10c5138c10c1, + 0xd350d3313d013ce, 0x13d613d213d410d5, 0x10db10db10d913d8, + 0xd3b10e10d3910df, 0x10e910e513dc0d3d, 0x10ff13de0d3f10ef, + 0x1113110d13e213e0, 0x111b111911170d41, 0x112313e613e613e4, + 0x112b112913e80d43, 0xd47113713ea0d45, 0x13ee1141113b113f, + 0x115511510d49114b, 0x13f413f20d4b115d, 0x13f8116513f60d4d, + 0x13fa1173116f1169, 0x117b13fe117713fc, 0x118511830d4f139e, + 0x14000ead11870d53, 0x118f13a213a01402, 0x119b0d55126b1191, + 0x119f0dfd119d1199, 0x140411a711a311a1, 0x11b511b311a911a5, + 0x11cb11c111b711ab, 0x11bf11bd11bb11b1, 0xd571408140a1406, + 0x141211d511d111cf, 0xd5b0d59140c11d7, 0x11e50d5d1410140e, + 0x11ef11eb11e911e7, 0x11f911f111f311ed, 0xd5f11fb11f711f5, + 0x12070d61120111ff, 0x1211120f14141209, 0x14160cfd12170d63, + 0x12250d670d651418, 0x141a1243123f1231, 0x1253125112471245, + 0x125512571372141e, 0x1265125f1374125b, 0x1281127b14221420, + 0x1297128714241285, 0x12a5129b129f129d, 0xd6912a9142612a7, + 0x12c30d6b142812ad, 0x142e142a12cd0ee3, 0x143012d70d6f0d6d, + 0x12db12db14320d71, 0xd7312e5143412df, 0x12f512f112ef12ed, + 0x12fd12f914360d75, 0x13030d790d771301, 0x143c143a0d7b1438, + 0x13150d7d1311143e, 0x131d131b13191317, 0x1442131f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + @property + { + private alias _IDCA = immutable(dchar[]); + _IDCA decompCanonTable() + { + static _IDCA t = [ + 0x0, 0x3b, 0x0, 0x3c, 0x338, 0x0, 0x3d, 0x338, 0x0, 0x3e, + 0x338, 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, + 0x0, 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, + 0x302, 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, + 0x41, 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, + 0x41, 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, + 0x309, 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, + 0x308, 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, + 0x30a, 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, + 0x30f, 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, + 0x302, 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, + 0x328, 0x0, 0x42, 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, + 0x0, 0x43, 0x301, 0x0, 0x43, 0x302, 0x0, 0x43, 0x307, 0x0, + 0x43, 0x30c, 0x0, 0x43, 0x327, 0x0, 0x43, 0x327, 0x301, 0x0, + 0x44, 0x307, 0x0, 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, + 0x327, 0x0, 0x44, 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x300, + 0x0, 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, + 0x0, 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, + 0x302, 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, + 0x304, 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, + 0x45, 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, + 0x30c, 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, + 0x0, 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, + 0x306, 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, + 0x0, 0x46, 0x307, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, + 0x47, 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, + 0x30c, 0x0, 0x47, 0x327, 0x0, 0x48, 0x302, 0x0, 0x48, 0x307, + 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, 0x0, + 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x300, 0x0, 0x49, + 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, + 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, + 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, + 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, + 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x302, 0x0, 0x4b, 0x0, + 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, + 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x301, 0x0, 0x4c, 0x30c, + 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, 0x0, 0x4c, 0x327, + 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, 0x4d, 0x301, 0x0, + 0x4d, 0x307, 0x0, 0x4d, 0x323, 0x0, 0x4e, 0x300, 0x0, 0x4e, + 0x301, 0x0, 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, + 0x0, 0x4e, 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, + 0x4e, 0x331, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, + 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, + 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, + 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, + 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, + 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, + 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, + 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, + 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, + 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, + 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, + 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, + 0x328, 0x304, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, 0x52, + 0x301, 0x0, 0x52, 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, + 0x0, 0x52, 0x311, 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, + 0x0, 0x52, 0x327, 0x0, 0x52, 0x331, 0x0, 0x53, 0x301, 0x0, + 0x53, 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, + 0x53, 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, + 0x53, 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, + 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, 0x54, + 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, 0x331, + 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, 0x55, 0x302, 0x0, + 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, 0x55, 0x304, 0x0, + 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, 0x55, 0x308, 0x0, + 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, 0x0, 0x55, 0x308, + 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, 0x309, 0x0, 0x55, + 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, 0x0, 0x55, 0x30f, + 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, 0x55, 0x31b, 0x300, + 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, 0x303, 0x0, 0x55, + 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, 0x55, 0x323, 0x0, + 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, 0x32d, 0x0, 0x55, + 0x330, 0x0, 0x56, 0x303, 0x0, 0x56, 0x323, 0x0, 0x57, 0x300, + 0x0, 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, + 0x57, 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x307, 0x0, 0x58, + 0x308, 0x0, 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, + 0x0, 0x59, 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, + 0x59, 0x308, 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, + 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, + 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x60, 0x0, 0x61, + 0x300, 0x0, 0x61, 0x301, 0x0, 0x61, 0x302, 0x0, 0x61, 0x302, + 0x300, 0x0, 0x61, 0x302, 0x301, 0x0, 0x61, 0x302, 0x303, 0x0, + 0x61, 0x302, 0x309, 0x0, 0x61, 0x303, 0x0, 0x61, 0x304, 0x0, + 0x61, 0x306, 0x0, 0x61, 0x306, 0x300, 0x0, 0x61, 0x306, 0x301, + 0x0, 0x61, 0x306, 0x303, 0x0, 0x61, 0x306, 0x309, 0x0, 0x61, + 0x307, 0x0, 0x61, 0x307, 0x304, 0x0, 0x61, 0x308, 0x0, 0x61, + 0x308, 0x304, 0x0, 0x61, 0x309, 0x0, 0x61, 0x30a, 0x0, 0x61, + 0x30a, 0x301, 0x0, 0x61, 0x30c, 0x0, 0x61, 0x30f, 0x0, 0x61, + 0x311, 0x0, 0x61, 0x323, 0x0, 0x61, 0x323, 0x302, 0x0, 0x61, + 0x323, 0x306, 0x0, 0x61, 0x325, 0x0, 0x61, 0x328, 0x0, 0x62, + 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x301, + 0x0, 0x63, 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, + 0x63, 0x327, 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x307, 0x0, + 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, 0x64, 0x327, 0x0, 0x64, + 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, 0x300, 0x0, 0x65, 0x301, + 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, 0x65, 0x302, + 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, 0x309, 0x0, + 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, 0x300, 0x0, + 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, 0x307, 0x0, + 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, 0x0, 0x65, + 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, 0x65, 0x323, + 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, 0x0, 0x65, + 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, 0x66, 0x307, + 0x0, 0x67, 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, + 0x67, 0x306, 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, + 0x327, 0x0, 0x68, 0x302, 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, + 0x0, 0x68, 0x30c, 0x0, 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, + 0x68, 0x32e, 0x0, 0x68, 0x331, 0x0, 0x69, 0x300, 0x0, 0x69, + 0x301, 0x0, 0x69, 0x302, 0x0, 0x69, 0x303, 0x0, 0x69, 0x304, + 0x0, 0x69, 0x306, 0x0, 0x69, 0x308, 0x0, 0x69, 0x308, 0x301, + 0x0, 0x69, 0x309, 0x0, 0x69, 0x30c, 0x0, 0x69, 0x30f, 0x0, + 0x69, 0x311, 0x0, 0x69, 0x323, 0x0, 0x69, 0x328, 0x0, 0x69, + 0x330, 0x0, 0x6a, 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x301, + 0x0, 0x6b, 0x30c, 0x0, 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, + 0x6b, 0x331, 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, + 0x323, 0x0, 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, + 0x32d, 0x0, 0x6c, 0x331, 0x0, 0x6d, 0x301, 0x0, 0x6d, 0x307, + 0x0, 0x6d, 0x323, 0x0, 0x6e, 0x300, 0x0, 0x6e, 0x301, 0x0, + 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, 0x0, 0x6e, + 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, 0x6e, 0x331, + 0x0, 0x6f, 0x300, 0x0, 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, + 0x6f, 0x302, 0x300, 0x0, 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, + 0x303, 0x0, 0x6f, 0x302, 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, + 0x303, 0x301, 0x0, 0x6f, 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, + 0x0, 0x6f, 0x304, 0x0, 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, + 0x301, 0x0, 0x6f, 0x306, 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, + 0x304, 0x0, 0x6f, 0x308, 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, + 0x309, 0x0, 0x6f, 0x30b, 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, + 0x0, 0x6f, 0x311, 0x0, 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, + 0x0, 0x6f, 0x31b, 0x301, 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, + 0x31b, 0x309, 0x0, 0x6f, 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, + 0x6f, 0x323, 0x302, 0x0, 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, + 0x0, 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x72, 0x301, 0x0, + 0x72, 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, + 0x311, 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, + 0x327, 0x0, 0x72, 0x331, 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, + 0x307, 0x0, 0x73, 0x302, 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, + 0x0, 0x73, 0x30c, 0x307, 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, + 0x307, 0x0, 0x73, 0x326, 0x0, 0x73, 0x327, 0x0, 0x74, 0x307, + 0x0, 0x74, 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, + 0x74, 0x326, 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, + 0x331, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, + 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, + 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, + 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, + 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, + 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, + 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, + 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, + 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, + 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, + 0x75, 0x330, 0x0, 0x76, 0x303, 0x0, 0x76, 0x323, 0x0, 0x77, + 0x300, 0x0, 0x77, 0x301, 0x0, 0x77, 0x302, 0x0, 0x77, 0x307, + 0x0, 0x77, 0x308, 0x0, 0x77, 0x30a, 0x0, 0x77, 0x323, 0x0, + 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x300, 0x0, 0x79, + 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, 0x303, 0x0, 0x79, 0x304, + 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, 0x0, 0x79, 0x309, 0x0, + 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, 0x7a, 0x301, 0x0, 0x7a, + 0x302, 0x0, 0x7a, 0x307, 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, + 0x0, 0x7a, 0x331, 0x0, 0xa8, 0x300, 0x0, 0xa8, 0x301, 0x0, + 0xa8, 0x342, 0x0, 0xb4, 0x0, 0xb7, 0x0, 0xc6, 0x301, 0x0, 0xc6, + 0x304, 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, + 0x0, 0xf8, 0x301, 0x0, 0x17f, 0x307, 0x0, 0x1b7, 0x30c, 0x0, + 0x292, 0x30c, 0x0, 0x2b9, 0x0, 0x300, 0x0, 0x301, 0x0, 0x308, + 0x301, 0x0, 0x313, 0x0, 0x391, 0x300, 0x0, 0x391, 0x301, 0x0, + 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, 0x313, 0x0, 0x391, + 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, 0x345, 0x0, 0x391, + 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, 0x345, 0x0, 0x391, + 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, 0x345, 0x0, 0x391, + 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, 0x314, 0x300, 0x0, + 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, 0x314, 0x301, 0x0, + 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, 0x314, 0x342, 0x0, + 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, 0x314, 0x345, 0x0, + 0x391, 0x345, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, 0x395, + 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, 0x301, 0x0, + 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, 0x314, + 0x301, 0x0, 0x397, 0x300, 0x0, 0x397, 0x301, 0x0, 0x397, 0x313, + 0x0, 0x397, 0x313, 0x300, 0x0, 0x397, 0x313, 0x300, 0x345, 0x0, + 0x397, 0x313, 0x301, 0x0, 0x397, 0x313, 0x301, 0x345, 0x0, + 0x397, 0x313, 0x342, 0x0, 0x397, 0x313, 0x342, 0x345, 0x0, + 0x397, 0x313, 0x345, 0x0, 0x397, 0x314, 0x0, 0x397, 0x314, + 0x300, 0x0, 0x397, 0x314, 0x300, 0x345, 0x0, 0x397, 0x314, + 0x301, 0x0, 0x397, 0x314, 0x301, 0x345, 0x0, 0x397, 0x314, + 0x342, 0x0, 0x397, 0x314, 0x342, 0x345, 0x0, 0x397, 0x314, + 0x345, 0x0, 0x397, 0x345, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, + 0x0, 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, + 0x399, 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, + 0x301, 0x0, 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, + 0x314, 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, + 0x342, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, + 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, + 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, + 0x3a1, 0x314, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, + 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, + 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, + 0x314, 0x342, 0x0, 0x3a9, 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, + 0x0, 0x3a9, 0x313, 0x0, 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, + 0x300, 0x345, 0x0, 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, + 0x301, 0x345, 0x0, 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, + 0x342, 0x345, 0x0, 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, + 0x3a9, 0x314, 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, + 0x3a9, 0x314, 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, + 0x3a9, 0x314, 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, + 0x3a9, 0x314, 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x300, 0x0, + 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, + 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, + 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, + 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, + 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, + 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, + 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, + 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, + 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, + 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, + 0x345, 0x0, 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, + 0x0, 0x3b5, 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, + 0x314, 0x0, 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, + 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, 0x301, 0x0, + 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, 0x313, + 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, 0x313, + 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, 0x313, + 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, 0x313, + 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, 0x3b7, + 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, 0x3b7, + 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, 0x3b7, + 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, 0x3b7, + 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, 0x3b9, + 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, 0x3b9, 0x304, 0x0, + 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, 0x308, 0x300, 0x0, + 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, 0x342, 0x0, 0x3b9, + 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, 0x313, 0x301, 0x0, + 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, 0x3b9, 0x314, + 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, 0x314, 0x342, 0x0, + 0x3b9, 0x342, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, + 0x313, 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, + 0x3bf, 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, + 0x301, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, 0x3c5, 0x300, + 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, + 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, + 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, + 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, + 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, + 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, + 0x3c9, 0x300, 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, + 0x3c9, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, + 0x300, 0x0, 0x3c9, 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, + 0x301, 0x0, 0x3c9, 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, + 0x342, 0x0, 0x3c9, 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, + 0x345, 0x0, 0x3c9, 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, + 0x314, 0x300, 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, + 0x314, 0x301, 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, + 0x314, 0x342, 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, + 0x342, 0x0, 0x3c9, 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3d2, + 0x301, 0x0, 0x3d2, 0x308, 0x0, 0x406, 0x308, 0x0, 0x410, 0x306, + 0x0, 0x410, 0x308, 0x0, 0x413, 0x301, 0x0, 0x415, 0x300, 0x0, + 0x415, 0x306, 0x0, 0x415, 0x308, 0x0, 0x416, 0x306, 0x0, 0x416, + 0x308, 0x0, 0x417, 0x308, 0x0, 0x418, 0x300, 0x0, 0x418, 0x304, + 0x0, 0x418, 0x306, 0x0, 0x418, 0x308, 0x0, 0x41a, 0x301, 0x0, + 0x41e, 0x308, 0x0, 0x423, 0x304, 0x0, 0x423, 0x306, 0x0, 0x423, + 0x308, 0x0, 0x423, 0x30b, 0x0, 0x427, 0x308, 0x0, 0x42b, 0x308, + 0x0, 0x42d, 0x308, 0x0, 0x430, 0x306, 0x0, 0x430, 0x308, 0x0, + 0x433, 0x301, 0x0, 0x435, 0x300, 0x0, 0x435, 0x306, 0x0, 0x435, + 0x308, 0x0, 0x436, 0x306, 0x0, 0x436, 0x308, 0x0, 0x437, 0x308, + 0x0, 0x438, 0x300, 0x0, 0x438, 0x304, 0x0, 0x438, 0x306, 0x0, + 0x438, 0x308, 0x0, 0x43a, 0x301, 0x0, 0x43e, 0x308, 0x0, 0x443, + 0x304, 0x0, 0x443, 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, + 0x0, 0x447, 0x308, 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, + 0x456, 0x308, 0x0, 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, + 0x308, 0x0, 0x4d9, 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, + 0x0, 0x5d0, 0x5b7, 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, + 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x5bc, 0x0, 0x5d3, + 0x5bc, 0x0, 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, + 0x0, 0x5d6, 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, + 0x5d9, 0x5bc, 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, + 0x5bf, 0x0, 0x5dc, 0x5bc, 0x0, 0x5de, 0x5bc, 0x0, 0x5e0, 0x5bc, + 0x0, 0x5e1, 0x5bc, 0x0, 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, + 0x5e4, 0x5bf, 0x0, 0x5e6, 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, + 0x5bc, 0x0, 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, + 0x5bc, 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, + 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, + 0x0, 0x627, 0x655, 0x0, 0x648, 0x654, 0x0, 0x64a, 0x654, 0x0, + 0x6c1, 0x654, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, 0x0, 0x915, + 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, 0x91c, 0x93c, + 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, 0x93c, 0x0, + 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, 0x0, 0x933, + 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, 0x9af, 0x9bc, + 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, 0xa3c, 0x0, + 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, 0x0, 0xa32, + 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, 0xb22, 0xb3c, + 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, 0xb57, 0x0, + 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, 0x0, 0xbc7, + 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, 0xcc6, 0xcc2, + 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, 0xcc6, 0xcd6, + 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, 0xd3e, 0x0, + 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, 0xdca, 0x0, + 0xdd9, 0xddf, 0x0, 0xf40, 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, + 0xfb7, 0x0, 0xf51, 0xfb7, 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, + 0x0, 0xf71, 0xf72, 0x0, 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, + 0xf90, 0xfb5, 0x0, 0xf92, 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, + 0xfb7, 0x0, 0xfa6, 0xfb7, 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf80, + 0x0, 0xfb3, 0xf80, 0x0, 0x1025, 0x102e, 0x0, 0x1b05, 0x1b35, + 0x0, 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, + 0x0, 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, + 0x0, 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, + 0x0, 0x1b42, 0x1b35, 0x0, 0x1fbf, 0x300, 0x0, 0x1fbf, 0x301, + 0x0, 0x1fbf, 0x342, 0x0, 0x1ffe, 0x300, 0x0, 0x1ffe, 0x301, + 0x0, 0x1ffe, 0x342, 0x0, 0x2002, 0x0, 0x2003, 0x0, 0x2190, + 0x338, 0x0, 0x2192, 0x338, 0x0, 0x2194, 0x338, 0x0, 0x21d0, + 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, 0x0, 0x2203, + 0x338, 0x0, 0x2208, 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2223, + 0x338, 0x0, 0x2225, 0x338, 0x0, 0x223c, 0x338, 0x0, 0x2243, + 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, 0x338, 0x0, 0x224d, + 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, 0x338, 0x0, 0x2265, + 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, 0x338, 0x0, 0x2276, + 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, 0x338, 0x0, 0x227b, + 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, 0x338, 0x0, 0x2282, + 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, 0x338, 0x0, 0x2287, + 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, 0x338, 0x0, 0x22a2, + 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, 0x338, 0x0, 0x22ab, + 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, 0x338, 0x0, 0x22b4, + 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2add, 0x338, 0x0, 0x3008, + 0x0, 0x3009, 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, + 0x304d, 0x3099, 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, + 0x3053, 0x3099, 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, + 0x3059, 0x3099, 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, + 0x305f, 0x3099, 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, + 0x3066, 0x3099, 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, + 0x306f, 0x309a, 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, + 0x3075, 0x3099, 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, + 0x3078, 0x309a, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, 0x0, + 0x309d, 0x3099, 0x0, 0x30a6, 0x3099, 0x0, 0x30ab, 0x3099, 0x0, + 0x30ad, 0x3099, 0x0, 0x30af, 0x3099, 0x0, 0x30b1, 0x3099, 0x0, + 0x30b3, 0x3099, 0x0, 0x30b5, 0x3099, 0x0, 0x30b7, 0x3099, 0x0, + 0x30b9, 0x3099, 0x0, 0x30bb, 0x3099, 0x0, 0x30bd, 0x3099, 0x0, + 0x30bf, 0x3099, 0x0, 0x30c1, 0x3099, 0x0, 0x30c4, 0x3099, 0x0, + 0x30c6, 0x3099, 0x0, 0x30c8, 0x3099, 0x0, 0x30cf, 0x3099, 0x0, + 0x30cf, 0x309a, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x309a, 0x0, + 0x30d5, 0x3099, 0x0, 0x30d5, 0x309a, 0x0, 0x30d8, 0x3099, 0x0, + 0x30d8, 0x309a, 0x0, 0x30db, 0x3099, 0x0, 0x30db, 0x309a, 0x0, + 0x30ef, 0x3099, 0x0, 0x30f0, 0x3099, 0x0, 0x30f1, 0x3099, 0x0, + 0x30f2, 0x3099, 0x0, 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, + 0x0, 0x34bb, 0x0, 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, + 0x36fc, 0x0, 0x3781, 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, + 0x0, 0x38c7, 0x0, 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, + 0x3a2e, 0x0, 0x3a6c, 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, + 0x0, 0x3b49, 0x0, 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, + 0x3d33, 0x0, 0x3d96, 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, + 0x0, 0x3ffc, 0x0, 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, + 0x4046, 0x0, 0x4096, 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, + 0x0, 0x4227, 0x0, 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, + 0x4359, 0x0, 0x43d5, 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, + 0x0, 0x452b, 0x0, 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, + 0x45d7, 0x0, 0x45f9, 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, + 0x0, 0x4995, 0x0, 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, + 0x4ab2, 0x0, 0x4b33, 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, + 0x0, 0x4cf8, 0x0, 0x4d56, 0x0, 0x4e0d, 0x0, 0x4e26, 0x0, + 0x4e32, 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e41, + 0x0, 0x4e82, 0x0, 0x4e86, 0x0, 0x4eae, 0x0, 0x4ec0, 0x0, + 0x4ecc, 0x0, 0x4ee4, 0x0, 0x4f60, 0x0, 0x4f80, 0x0, 0x4f86, + 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, 0x0, 0x4fbf, 0x0, + 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, 0x5099, 0x0, 0x50cf, + 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x5140, 0x0, 0x5145, 0x0, + 0x514d, 0x0, 0x5154, 0x0, 0x5164, 0x0, 0x5167, 0x0, 0x5168, + 0x0, 0x5169, 0x0, 0x516d, 0x0, 0x5177, 0x0, 0x5180, 0x0, + 0x518d, 0x0, 0x5192, 0x0, 0x5195, 0x0, 0x5197, 0x0, 0x51a4, + 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, 0x0, + 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51f5, 0x0, 0x5203, + 0x0, 0x5207, 0x0, 0x5217, 0x0, 0x5229, 0x0, 0x523a, 0x0, + 0x523b, 0x0, 0x5246, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, + 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52c7, 0x0, + 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, 0x0, 0x52f5, + 0x0, 0x52fa, 0x0, 0x5305, 0x0, 0x5306, 0x0, 0x5317, 0x0, + 0x533f, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x535a, 0x0, 0x5373, + 0x0, 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x53c3, 0x0, + 0x53ca, 0x0, 0x53df, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53f1, + 0x0, 0x5406, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, + 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, 0x54a2, + 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x5553, 0x0, + 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, 0x5587, 0x0, 0x5599, + 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, 0x0, 0x55c0, 0x0, + 0x55c2, 0x0, 0x55e2, 0x0, 0x5606, 0x0, 0x5651, 0x0, 0x5668, + 0x0, 0x5674, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, + 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, 0x0, 0x580d, 0x0, 0x5831, + 0x0, 0x5832, 0x0, 0x5840, 0x0, 0x585a, 0x0, 0x585e, 0x0, + 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, 0x0, 0x58d8, 0x0, 0x58df, + 0x0, 0x58ee, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5906, 0x0, + 0x591a, 0x0, 0x5922, 0x0, 0x5944, 0x0, 0x5948, 0x0, 0x5951, + 0x0, 0x5954, 0x0, 0x5962, 0x0, 0x5973, 0x0, 0x59d8, 0x0, + 0x59ec, 0x0, 0x5a1b, 0x0, 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, + 0x0, 0x5ab5, 0x0, 0x5b08, 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, + 0x5b85, 0x0, 0x5bc3, 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, + 0x0, 0x5bf3, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c22, 0x0, + 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, 0x5c64, 0x0, 0x5c65, + 0x0, 0x5c6e, 0x0, 0x5c8d, 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, + 0x5d43, 0x0, 0x5d50, 0x0, 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, + 0x0, 0x5db2, 0x0, 0x5dba, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, + 0x5dfd, 0x0, 0x5e28, 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e74, + 0x0, 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, + 0x5ec9, 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, + 0x0, 0x5eec, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f22, 0x0, + 0x5f53, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f8b, + 0x0, 0x5f9a, 0x0, 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fcd, 0x0, + 0x5fd7, 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, + 0x0, 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, + 0x60d8, 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, + 0x0, 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, + 0x617a, 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, + 0x0, 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, + 0x6200, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, 0x6234, + 0x0, 0x625d, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, + 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x633d, + 0x0, 0x6350, 0x0, 0x6368, 0x0, 0x637b, 0x0, 0x6383, 0x0, + 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, 0x0, 0x63c5, 0x0, 0x63e4, + 0x0, 0x641c, 0x0, 0x6422, 0x0, 0x6452, 0x0, 0x6469, 0x0, + 0x6477, 0x0, 0x647e, 0x0, 0x649a, 0x0, 0x649d, 0x0, 0x64c4, + 0x0, 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, + 0x6599, 0x0, 0x65c5, 0x0, 0x65e2, 0x0, 0x65e3, 0x0, 0x6613, + 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, 0x0, 0x6691, 0x0, + 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, 0x66f4, 0x0, 0x66f8, + 0x0, 0x6700, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, + 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, 0x0, 0x677b, + 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, 0x67fa, 0x0, + 0x6817, 0x0, 0x681f, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, + 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, + 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, + 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b21, 0x0, + 0x6b54, 0x0, 0x6b72, 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, + 0x0, 0x6bae, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6c4e, 0x0, + 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, 0x0, 0x6ccc, 0x0, 0x6ccd, + 0x0, 0x6ce5, 0x0, 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, + 0x6d34, 0x0, 0x6d3e, 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, + 0x0, 0x6d77, 0x0, 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, + 0x6dda, 0x0, 0x6dea, 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, + 0x0, 0x6e6e, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, + 0x6ecb, 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f22, + 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, 0x0, + 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, 0x704a, + 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, 0x0, 0x7099, 0x0, + 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, 0x7145, 0x0, 0x7149, + 0x0, 0x716e, 0x0, 0x719c, 0x0, 0x71ce, 0x0, 0x71d0, 0x0, + 0x7210, 0x0, 0x721b, 0x0, 0x7228, 0x0, 0x722b, 0x0, 0x7235, + 0x0, 0x7250, 0x0, 0x7262, 0x0, 0x7280, 0x0, 0x7295, 0x0, + 0x72af, 0x0, 0x72c0, 0x0, 0x72fc, 0x0, 0x732a, 0x0, 0x7375, + 0x0, 0x737a, 0x0, 0x7387, 0x0, 0x738b, 0x0, 0x73a5, 0x0, + 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, + 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, + 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x7506, + 0x0, 0x7524, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, + 0x7565, 0x0, 0x7570, 0x0, 0x75e2, 0x0, 0x7610, 0x0, 0x761d, + 0x0, 0x761f, 0x0, 0x7642, 0x0, 0x7669, 0x0, 0x76ca, 0x0, + 0x76db, 0x0, 0x76e7, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, + 0x0, 0x771f, 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, + 0x77a7, 0x0, 0x784e, 0x0, 0x786b, 0x0, 0x788c, 0x0, 0x7891, + 0x0, 0x78ca, 0x0, 0x78cc, 0x0, 0x78fb, 0x0, 0x792a, 0x0, + 0x793c, 0x0, 0x793e, 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, + 0x0, 0x7956, 0x0, 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, + 0x797f, 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, + 0x0, 0x79ca, 0x0, 0x79eb, 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, + 0x7a4a, 0x0, 0x7a4f, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, + 0x0, 0x7aee, 0x0, 0x7b20, 0x0, 0x7bc0, 0x0, 0x7bc6, 0x0, + 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, 0x0, 0x7c7b, 0x0, 0x7c92, + 0x0, 0x7cbe, 0x0, 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, + 0x7ce7, 0x0, 0x7ce8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, + 0x0, 0x7d2f, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, 0x0, + 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, 0x7e09, + 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f3e, 0x0, + 0x7f72, 0x0, 0x7f79, 0x0, 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f95, + 0x0, 0x7f9a, 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, + 0x8005, 0x0, 0x8046, 0x0, 0x8060, 0x0, 0x806f, 0x0, 0x8070, + 0x0, 0x807e, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, + 0x8103, 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e8, 0x0, 0x81ed, + 0x0, 0x8201, 0x0, 0x8204, 0x0, 0x8218, 0x0, 0x826f, 0x0, + 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, 0x82b1, + 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, 0x0, + 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, 0x8353, + 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, 0x0, + 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, 0x83ef, + 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, 0x0, + 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, 0x8516, + 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, 0x0, + 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x8650, 0x0, 0x865c, + 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x8688, 0x0, 0x86a9, 0x0, + 0x86e2, 0x0, 0x870e, 0x0, 0x8728, 0x0, 0x876b, 0x0, 0x8779, + 0x0, 0x8786, 0x0, 0x87ba, 0x0, 0x87e1, 0x0, 0x8801, 0x0, + 0x881f, 0x0, 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, + 0x0, 0x88cf, 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, + 0x88f8, 0x0, 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, + 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x8aa0, 0x0, + 0x8aaa, 0x0, 0x8abf, 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, + 0x0, 0x8aed, 0x0, 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, + 0x8b39, 0x0, 0x8b58, 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c48, + 0x0, 0x8c55, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, + 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, 0x0, 0x8d77, + 0x0, 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, + 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, 0x8f38, + 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9e, 0x0, 0x8fb0, 0x0, + 0x8fb6, 0x0, 0x9023, 0x0, 0x9038, 0x0, 0x9072, 0x0, 0x907c, + 0x0, 0x908f, 0x0, 0x9094, 0x0, 0x90ce, 0x0, 0x90de, 0x0, + 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, 0x0, 0x916a, + 0x0, 0x9199, 0x0, 0x91b4, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, + 0x91d1, 0x0, 0x9234, 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, + 0x0, 0x92d7, 0x0, 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, + 0x93f9, 0x0, 0x9415, 0x0, 0x958b, 0x0, 0x95ad, 0x0, 0x95b7, + 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, 0x0, 0x9675, 0x0, + 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, 0x96a3, 0x0, 0x96b7, + 0x0, 0x96b8, 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, + 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, + 0x0, 0x9756, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ff, 0x0, + 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, + 0x0, 0x98e2, 0x0, 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, + 0x9929, 0x0, 0x99a7, 0x0, 0x99c2, 0x0, 0x99f1, 0x0, 0x99fe, + 0x0, 0x9a6a, 0x0, 0x9b12, 0x0, 0x9b6f, 0x0, 0x9c40, 0x0, + 0x9c57, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, 0x9db4, 0x0, 0x9dfa, + 0x0, 0x9e1e, 0x0, 0x9e7f, 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, + 0x9ebb, 0x0, 0x9ece, 0x0, 0x9ef9, 0x0, 0x9efe, 0x0, 0x9f05, + 0x0, 0x9f0f, 0x0, 0x9f16, 0x0, 0x9f3b, 0x0, 0x9f43, 0x0, + 0x9f8d, 0x0, 0x9f8e, 0x0, 0x9f9c, 0x0, 0x11099, 0x110ba, 0x0, + 0x1109b, 0x110ba, 0x0, 0x110a5, 0x110ba, 0x0, 0x11131, 0x11127, + 0x0, 0x11132, 0x11127, 0x0, 0x1d157, 0x1d165, 0x0, 0x1d158, + 0x1d165, 0x0, 0x1d158, 0x1d165, 0x1d16e, 0x0, 0x1d158, 0x1d165, + 0x1d16f, 0x0, 0x1d158, 0x1d165, 0x1d170, 0x0, 0x1d158, 0x1d165, + 0x1d171, 0x0, 0x1d158, 0x1d165, 0x1d172, 0x0, 0x1d1b9, 0x1d165, + 0x0, 0x1d1b9, 0x1d165, 0x1d16e, 0x0, 0x1d1b9, 0x1d165, 0x1d16f, + 0x0, 0x1d1ba, 0x1d165, 0x0, 0x1d1ba, 0x1d165, 0x1d16e, 0x0, + 0x1d1ba, 0x1d165, 0x1d16f, 0x0, 0x20122, 0x0, 0x2051c, 0x0, + 0x20525, 0x0, 0x2054b, 0x0, 0x2063a, 0x0, 0x20804, 0x0, + 0x208de, 0x0, 0x20a2c, 0x0, 0x20b63, 0x0, 0x214e4, 0x0, + 0x216a8, 0x0, 0x216ea, 0x0, 0x219c8, 0x0, 0x21b18, 0x0, + 0x21d0b, 0x0, 0x21de4, 0x0, 0x21de6, 0x0, 0x22183, 0x0, + 0x2219f, 0x0, 0x22331, 0x0, 0x226d4, 0x0, 0x22844, 0x0, + 0x2284a, 0x0, 0x22b0c, 0x0, 0x22bf1, 0x0, 0x2300a, 0x0, + 0x232b8, 0x0, 0x2335f, 0x0, 0x23393, 0x0, 0x2339c, 0x0, + 0x233c3, 0x0, 0x233d5, 0x0, 0x2346d, 0x0, 0x236a3, 0x0, + 0x238a7, 0x0, 0x23a8d, 0x0, 0x23afa, 0x0, 0x23cbc, 0x0, + 0x23d1e, 0x0, 0x23ed1, 0x0, 0x23f5e, 0x0, 0x23f8e, 0x0, + 0x24263, 0x0, 0x242ee, 0x0, 0x243ab, 0x0, 0x24608, 0x0, + 0x24735, 0x0, 0x24814, 0x0, 0x24c36, 0x0, 0x24c92, 0x0, + 0x24fa1, 0x0, 0x24fb8, 0x0, 0x25044, 0x0, 0x250f2, 0x0, + 0x250f3, 0x0, 0x25119, 0x0, 0x25133, 0x0, 0x25249, 0x0, + 0x2541d, 0x0, 0x25626, 0x0, 0x2569a, 0x0, 0x256c5, 0x0, + 0x2597c, 0x0, 0x25aa7, 0x0, 0x25bab, 0x0, 0x25c80, 0x0, + 0x25cd0, 0x0, 0x25f86, 0x0, 0x261da, 0x0, 0x26228, 0x0, + 0x26247, 0x0, 0x262d9, 0x0, 0x2633e, 0x0, 0x264da, 0x0, + 0x26523, 0x0, 0x265a8, 0x0, 0x267a7, 0x0, 0x267b5, 0x0, + 0x26b3c, 0x0, 0x26c36, 0x0, 0x26cd5, 0x0, 0x26d6b, 0x0, + 0x26f2c, 0x0, 0x26fb1, 0x0, 0x270d2, 0x0, 0x273ca, 0x0, + 0x27667, 0x0, 0x278ae, 0x0, 0x27966, 0x0, 0x27ca8, 0x0, + 0x27ed3, 0x0, 0x27f2f, 0x0, 0x285d2, 0x0, 0x285ed, 0x0, + 0x2872e, 0x0, 0x28bfa, 0x0, 0x28d77, 0x0, 0x29145, 0x0, + 0x291df, 0x0, 0x2921a, 0x0, 0x2940a, 0x0, 0x29496, 0x0, + 0x295b6, 0x0, 0x29b30, 0x0, 0x2a0ce, 0x0, 0x2a105, 0x0, + 0x2a20e, 0x0, 0x2a291, 0x0, 0x2a392, 0x0, 0x2a600, 0x0 + ]; + return t; + } -} + _IDCA decompCompatTable() + { + static _IDCA t = [ + 0x0, 0x20, 0x0, 0x20, 0x301, 0x0, 0x20, 0x303, 0x0, 0x20, + 0x304, 0x0, 0x20, 0x305, 0x0, 0x20, 0x306, 0x0, 0x20, 0x307, + 0x0, 0x20, 0x308, 0x0, 0x20, 0x308, 0x300, 0x0, 0x20, 0x308, + 0x301, 0x0, 0x20, 0x308, 0x342, 0x0, 0x20, 0x30a, 0x0, 0x20, + 0x30b, 0x0, 0x20, 0x313, 0x0, 0x20, 0x313, 0x300, 0x0, 0x20, + 0x313, 0x301, 0x0, 0x20, 0x313, 0x342, 0x0, 0x20, 0x314, 0x0, + 0x20, 0x314, 0x300, 0x0, 0x20, 0x314, 0x301, 0x0, 0x20, 0x314, + 0x342, 0x0, 0x20, 0x327, 0x0, 0x20, 0x328, 0x0, 0x20, 0x333, + 0x0, 0x20, 0x342, 0x0, 0x20, 0x345, 0x0, 0x20, 0x64b, 0x0, + 0x20, 0x64c, 0x0, 0x20, 0x64c, 0x651, 0x0, 0x20, 0x64d, 0x0, + 0x20, 0x64d, 0x651, 0x0, 0x20, 0x64e, 0x0, 0x20, 0x64e, 0x651, + 0x0, 0x20, 0x64f, 0x0, 0x20, 0x64f, 0x651, 0x0, 0x20, 0x650, + 0x0, 0x20, 0x650, 0x651, 0x0, 0x20, 0x651, 0x0, 0x20, 0x651, + 0x670, 0x0, 0x20, 0x652, 0x0, 0x20, 0x3099, 0x0, 0x20, 0x309a, + 0x0, 0x21, 0x0, 0x21, 0x21, 0x0, 0x21, 0x3f, 0x0, 0x22, 0x0, + 0x23, 0x0, 0x24, 0x0, 0x25, 0x0, 0x26, 0x0, 0x27, 0x0, 0x28, + 0x0, 0x28, 0x31, 0x29, 0x0, 0x28, 0x31, 0x30, 0x29, 0x0, 0x28, + 0x31, 0x31, 0x29, 0x0, 0x28, 0x31, 0x32, 0x29, 0x0, 0x28, 0x31, + 0x33, 0x29, 0x0, 0x28, 0x31, 0x34, 0x29, 0x0, 0x28, 0x31, 0x35, + 0x29, 0x0, 0x28, 0x31, 0x36, 0x29, 0x0, 0x28, 0x31, 0x37, 0x29, + 0x0, 0x28, 0x31, 0x38, 0x29, 0x0, 0x28, 0x31, 0x39, 0x29, 0x0, + 0x28, 0x32, 0x29, 0x0, 0x28, 0x32, 0x30, 0x29, 0x0, 0x28, 0x33, + 0x29, 0x0, 0x28, 0x34, 0x29, 0x0, 0x28, 0x35, 0x29, 0x0, 0x28, + 0x36, 0x29, 0x0, 0x28, 0x37, 0x29, 0x0, 0x28, 0x38, 0x29, 0x0, + 0x28, 0x39, 0x29, 0x0, 0x28, 0x41, 0x29, 0x0, 0x28, 0x42, 0x29, + 0x0, 0x28, 0x43, 0x29, 0x0, 0x28, 0x44, 0x29, 0x0, 0x28, 0x45, + 0x29, 0x0, 0x28, 0x46, 0x29, 0x0, 0x28, 0x47, 0x29, 0x0, 0x28, + 0x48, 0x29, 0x0, 0x28, 0x49, 0x29, 0x0, 0x28, 0x4a, 0x29, 0x0, + 0x28, 0x4b, 0x29, 0x0, 0x28, 0x4c, 0x29, 0x0, 0x28, 0x4d, 0x29, + 0x0, 0x28, 0x4e, 0x29, 0x0, 0x28, 0x4f, 0x29, 0x0, 0x28, 0x50, + 0x29, 0x0, 0x28, 0x51, 0x29, 0x0, 0x28, 0x52, 0x29, 0x0, 0x28, + 0x53, 0x29, 0x0, 0x28, 0x54, 0x29, 0x0, 0x28, 0x55, 0x29, 0x0, + 0x28, 0x56, 0x29, 0x0, 0x28, 0x57, 0x29, 0x0, 0x28, 0x58, 0x29, + 0x0, 0x28, 0x59, 0x29, 0x0, 0x28, 0x5a, 0x29, 0x0, 0x28, 0x61, + 0x29, 0x0, 0x28, 0x62, 0x29, 0x0, 0x28, 0x63, 0x29, 0x0, 0x28, + 0x64, 0x29, 0x0, 0x28, 0x65, 0x29, 0x0, 0x28, 0x66, 0x29, 0x0, + 0x28, 0x67, 0x29, 0x0, 0x28, 0x68, 0x29, 0x0, 0x28, 0x69, 0x29, + 0x0, 0x28, 0x6a, 0x29, 0x0, 0x28, 0x6b, 0x29, 0x0, 0x28, 0x6c, + 0x29, 0x0, 0x28, 0x6d, 0x29, 0x0, 0x28, 0x6e, 0x29, 0x0, 0x28, + 0x6f, 0x29, 0x0, 0x28, 0x70, 0x29, 0x0, 0x28, 0x71, 0x29, 0x0, + 0x28, 0x72, 0x29, 0x0, 0x28, 0x73, 0x29, 0x0, 0x28, 0x74, 0x29, + 0x0, 0x28, 0x75, 0x29, 0x0, 0x28, 0x76, 0x29, 0x0, 0x28, 0x77, + 0x29, 0x0, 0x28, 0x78, 0x29, 0x0, 0x28, 0x79, 0x29, 0x0, 0x28, + 0x7a, 0x29, 0x0, 0x28, 0x1100, 0x29, 0x0, 0x28, 0x1100, 0x1161, + 0x29, 0x0, 0x28, 0x1102, 0x29, 0x0, 0x28, 0x1102, 0x1161, 0x29, + 0x0, 0x28, 0x1103, 0x29, 0x0, 0x28, 0x1103, 0x1161, 0x29, 0x0, + 0x28, 0x1105, 0x29, 0x0, 0x28, 0x1105, 0x1161, 0x29, 0x0, 0x28, + 0x1106, 0x29, 0x0, 0x28, 0x1106, 0x1161, 0x29, 0x0, 0x28, + 0x1107, 0x29, 0x0, 0x28, 0x1107, 0x1161, 0x29, 0x0, 0x28, + 0x1109, 0x29, 0x0, 0x28, 0x1109, 0x1161, 0x29, 0x0, 0x28, + 0x110b, 0x29, 0x0, 0x28, 0x110b, 0x1161, 0x29, 0x0, 0x28, + 0x110b, 0x1169, 0x110c, 0x1165, 0x11ab, 0x29, 0x0, 0x28, + 0x110b, 0x1169, 0x1112, 0x116e, 0x29, 0x0, 0x28, 0x110c, 0x29, + 0x0, 0x28, 0x110c, 0x1161, 0x29, 0x0, 0x28, 0x110c, 0x116e, + 0x29, 0x0, 0x28, 0x110e, 0x29, 0x0, 0x28, 0x110e, 0x1161, 0x29, + 0x0, 0x28, 0x110f, 0x29, 0x0, 0x28, 0x110f, 0x1161, 0x29, 0x0, + 0x28, 0x1110, 0x29, 0x0, 0x28, 0x1110, 0x1161, 0x29, 0x0, 0x28, + 0x1111, 0x29, 0x0, 0x28, 0x1111, 0x1161, 0x29, 0x0, 0x28, + 0x1112, 0x29, 0x0, 0x28, 0x1112, 0x1161, 0x29, 0x0, 0x28, + 0x4e00, 0x29, 0x0, 0x28, 0x4e03, 0x29, 0x0, 0x28, 0x4e09, 0x29, + 0x0, 0x28, 0x4e5d, 0x29, 0x0, 0x28, 0x4e8c, 0x29, 0x0, 0x28, + 0x4e94, 0x29, 0x0, 0x28, 0x4ee3, 0x29, 0x0, 0x28, 0x4f01, 0x29, + 0x0, 0x28, 0x4f11, 0x29, 0x0, 0x28, 0x516b, 0x29, 0x0, 0x28, + 0x516d, 0x29, 0x0, 0x28, 0x52b4, 0x29, 0x0, 0x28, 0x5341, 0x29, + 0x0, 0x28, 0x5354, 0x29, 0x0, 0x28, 0x540d, 0x29, 0x0, 0x28, + 0x547c, 0x29, 0x0, 0x28, 0x56db, 0x29, 0x0, 0x28, 0x571f, 0x29, + 0x0, 0x28, 0x5b66, 0x29, 0x0, 0x28, 0x65e5, 0x29, 0x0, 0x28, + 0x6708, 0x29, 0x0, 0x28, 0x6709, 0x29, 0x0, 0x28, 0x6728, 0x29, + 0x0, 0x28, 0x682a, 0x29, 0x0, 0x28, 0x6c34, 0x29, 0x0, 0x28, + 0x706b, 0x29, 0x0, 0x28, 0x7279, 0x29, 0x0, 0x28, 0x76e3, 0x29, + 0x0, 0x28, 0x793e, 0x29, 0x0, 0x28, 0x795d, 0x29, 0x0, 0x28, + 0x796d, 0x29, 0x0, 0x28, 0x81ea, 0x29, 0x0, 0x28, 0x81f3, 0x29, + 0x0, 0x28, 0x8ca1, 0x29, 0x0, 0x28, 0x8cc7, 0x29, 0x0, 0x28, + 0x91d1, 0x29, 0x0, 0x29, 0x0, 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, + 0x2d, 0x0, 0x2e, 0x0, 0x2e, 0x2e, 0x0, 0x2e, 0x2e, 0x2e, 0x0, + 0x2f, 0x0, 0x30, 0x0, 0x30, 0x2c, 0x0, 0x30, 0x2e, 0x0, 0x30, + 0x2044, 0x33, 0x0, 0x30, 0x70b9, 0x0, 0x31, 0x0, 0x31, 0x2c, + 0x0, 0x31, 0x2e, 0x0, 0x31, 0x30, 0x0, 0x31, 0x30, 0x2e, 0x0, + 0x31, 0x30, 0x65e5, 0x0, 0x31, 0x30, 0x6708, 0x0, 0x31, 0x30, + 0x70b9, 0x0, 0x31, 0x31, 0x0, 0x31, 0x31, 0x2e, 0x0, 0x31, + 0x31, 0x65e5, 0x0, 0x31, 0x31, 0x6708, 0x0, 0x31, 0x31, 0x70b9, + 0x0, 0x31, 0x32, 0x0, 0x31, 0x32, 0x2e, 0x0, 0x31, 0x32, + 0x65e5, 0x0, 0x31, 0x32, 0x6708, 0x0, 0x31, 0x32, 0x70b9, 0x0, + 0x31, 0x33, 0x0, 0x31, 0x33, 0x2e, 0x0, 0x31, 0x33, 0x65e5, + 0x0, 0x31, 0x33, 0x70b9, 0x0, 0x31, 0x34, 0x0, 0x31, 0x34, + 0x2e, 0x0, 0x31, 0x34, 0x65e5, 0x0, 0x31, 0x34, 0x70b9, 0x0, + 0x31, 0x35, 0x0, 0x31, 0x35, 0x2e, 0x0, 0x31, 0x35, 0x65e5, + 0x0, 0x31, 0x35, 0x70b9, 0x0, 0x31, 0x36, 0x0, 0x31, 0x36, + 0x2e, 0x0, 0x31, 0x36, 0x65e5, 0x0, 0x31, 0x36, 0x70b9, 0x0, + 0x31, 0x37, 0x0, 0x31, 0x37, 0x2e, 0x0, 0x31, 0x37, 0x65e5, + 0x0, 0x31, 0x37, 0x70b9, 0x0, 0x31, 0x38, 0x0, 0x31, 0x38, + 0x2e, 0x0, 0x31, 0x38, 0x65e5, 0x0, 0x31, 0x38, 0x70b9, 0x0, + 0x31, 0x39, 0x0, 0x31, 0x39, 0x2e, 0x0, 0x31, 0x39, 0x65e5, + 0x0, 0x31, 0x39, 0x70b9, 0x0, 0x31, 0x2044, 0x0, 0x31, 0x2044, + 0x31, 0x30, 0x0, 0x31, 0x2044, 0x32, 0x0, 0x31, 0x2044, 0x33, + 0x0, 0x31, 0x2044, 0x34, 0x0, 0x31, 0x2044, 0x35, 0x0, 0x31, + 0x2044, 0x36, 0x0, 0x31, 0x2044, 0x37, 0x0, 0x31, 0x2044, 0x38, + 0x0, 0x31, 0x2044, 0x39, 0x0, 0x31, 0x65e5, 0x0, 0x31, 0x6708, + 0x0, 0x31, 0x70b9, 0x0, 0x32, 0x0, 0x32, 0x2c, 0x0, 0x32, 0x2e, + 0x0, 0x32, 0x30, 0x0, 0x32, 0x30, 0x2e, 0x0, 0x32, 0x30, + 0x65e5, 0x0, 0x32, 0x30, 0x70b9, 0x0, 0x32, 0x31, 0x0, 0x32, + 0x31, 0x65e5, 0x0, 0x32, 0x31, 0x70b9, 0x0, 0x32, 0x32, 0x0, + 0x32, 0x32, 0x65e5, 0x0, 0x32, 0x32, 0x70b9, 0x0, 0x32, 0x33, + 0x0, 0x32, 0x33, 0x65e5, 0x0, 0x32, 0x33, 0x70b9, 0x0, 0x32, + 0x34, 0x0, 0x32, 0x34, 0x65e5, 0x0, 0x32, 0x34, 0x70b9, 0x0, + 0x32, 0x35, 0x0, 0x32, 0x35, 0x65e5, 0x0, 0x32, 0x36, 0x0, + 0x32, 0x36, 0x65e5, 0x0, 0x32, 0x37, 0x0, 0x32, 0x37, 0x65e5, + 0x0, 0x32, 0x38, 0x0, 0x32, 0x38, 0x65e5, 0x0, 0x32, 0x39, 0x0, + 0x32, 0x39, 0x65e5, 0x0, 0x32, 0x2044, 0x33, 0x0, 0x32, 0x2044, + 0x35, 0x0, 0x32, 0x65e5, 0x0, 0x32, 0x6708, 0x0, 0x32, 0x70b9, + 0x0, 0x33, 0x0, 0x33, 0x2c, 0x0, 0x33, 0x2e, 0x0, 0x33, 0x30, + 0x0, 0x33, 0x30, 0x65e5, 0x0, 0x33, 0x31, 0x0, 0x33, 0x31, + 0x65e5, 0x0, 0x33, 0x32, 0x0, 0x33, 0x33, 0x0, 0x33, 0x34, 0x0, + 0x33, 0x35, 0x0, 0x33, 0x36, 0x0, 0x33, 0x37, 0x0, 0x33, 0x38, + 0x0, 0x33, 0x39, 0x0, 0x33, 0x2044, 0x34, 0x0, 0x33, 0x2044, + 0x35, 0x0, 0x33, 0x2044, 0x38, 0x0, 0x33, 0x65e5, 0x0, 0x33, + 0x6708, 0x0, 0x33, 0x70b9, 0x0, 0x34, 0x0, 0x34, 0x2c, 0x0, + 0x34, 0x2e, 0x0, 0x34, 0x30, 0x0, 0x34, 0x31, 0x0, 0x34, 0x32, + 0x0, 0x34, 0x33, 0x0, 0x34, 0x34, 0x0, 0x34, 0x35, 0x0, 0x34, + 0x36, 0x0, 0x34, 0x37, 0x0, 0x34, 0x38, 0x0, 0x34, 0x39, 0x0, + 0x34, 0x2044, 0x35, 0x0, 0x34, 0x65e5, 0x0, 0x34, 0x6708, 0x0, + 0x34, 0x70b9, 0x0, 0x35, 0x0, 0x35, 0x2c, 0x0, 0x35, 0x2e, 0x0, + 0x35, 0x30, 0x0, 0x35, 0x2044, 0x36, 0x0, 0x35, 0x2044, 0x38, + 0x0, 0x35, 0x65e5, 0x0, 0x35, 0x6708, 0x0, 0x35, 0x70b9, 0x0, + 0x36, 0x0, 0x36, 0x2c, 0x0, 0x36, 0x2e, 0x0, 0x36, 0x65e5, 0x0, + 0x36, 0x6708, 0x0, 0x36, 0x70b9, 0x0, 0x37, 0x0, 0x37, 0x2c, + 0x0, 0x37, 0x2e, 0x0, 0x37, 0x2044, 0x38, 0x0, 0x37, 0x65e5, + 0x0, 0x37, 0x6708, 0x0, 0x37, 0x70b9, 0x0, 0x38, 0x0, 0x38, + 0x2c, 0x0, 0x38, 0x2e, 0x0, 0x38, 0x65e5, 0x0, 0x38, 0x6708, + 0x0, 0x38, 0x70b9, 0x0, 0x39, 0x0, 0x39, 0x2c, 0x0, 0x39, 0x2e, + 0x0, 0x39, 0x65e5, 0x0, 0x39, 0x6708, 0x0, 0x39, 0x70b9, 0x0, + 0x3a, 0x0, 0x3a, 0x3a, 0x3d, 0x0, 0x3b, 0x0, 0x3c, 0x0, 0x3c, + 0x338, 0x0, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x3d, 0x3d, 0x3d, 0x0, + 0x3d, 0x338, 0x0, 0x3e, 0x0, 0x3e, 0x338, 0x0, 0x3f, 0x0, 0x3f, + 0x21, 0x0, 0x3f, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, 0x41, 0x55, + 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, 0x0, + 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, 0x302, + 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, 0x41, + 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, 0x41, + 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, 0x309, + 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, 0x308, + 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, 0x30a, + 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, 0x30f, + 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, 0x302, + 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, 0x328, + 0x0, 0x41, 0x2215, 0x6d, 0x0, 0x42, 0x0, 0x42, 0x71, 0x0, 0x42, + 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, 0x0, 0x43, 0x0, + 0x43, 0x44, 0x0, 0x43, 0x6f, 0x2e, 0x0, 0x43, 0x301, 0x0, 0x43, + 0x302, 0x0, 0x43, 0x307, 0x0, 0x43, 0x30c, 0x0, 0x43, 0x327, + 0x0, 0x43, 0x327, 0x301, 0x0, 0x43, 0x2215, 0x6b, 0x67, 0x0, + 0x44, 0x0, 0x44, 0x4a, 0x0, 0x44, 0x5a, 0x0, 0x44, 0x5a, 0x30c, + 0x0, 0x44, 0x7a, 0x0, 0x44, 0x7a, 0x30c, 0x0, 0x44, 0x307, 0x0, + 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, 0x327, 0x0, 0x44, + 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x0, 0x45, 0x300, 0x0, + 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, 0x0, + 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, 0x302, + 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, 0x304, + 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, 0x45, + 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, 0x30c, + 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, 0x0, + 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, 0x306, + 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, 0x0, + 0x46, 0x0, 0x46, 0x41, 0x58, 0x0, 0x46, 0x307, 0x0, 0x47, 0x0, + 0x47, 0x42, 0x0, 0x47, 0x48, 0x7a, 0x0, 0x47, 0x50, 0x61, 0x0, + 0x47, 0x79, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, 0x47, + 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, 0x30c, + 0x0, 0x47, 0x327, 0x0, 0x48, 0x0, 0x48, 0x50, 0x0, 0x48, 0x56, + 0x0, 0x48, 0x67, 0x0, 0x48, 0x7a, 0x0, 0x48, 0x302, 0x0, 0x48, + 0x307, 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, + 0x0, 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x0, 0x49, 0x49, + 0x0, 0x49, 0x49, 0x49, 0x0, 0x49, 0x4a, 0x0, 0x49, 0x55, 0x0, + 0x49, 0x56, 0x0, 0x49, 0x58, 0x0, 0x49, 0x300, 0x0, 0x49, + 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, + 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, + 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, + 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, + 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x0, 0x4a, 0x302, 0x0, + 0x4b, 0x0, 0x4b, 0x42, 0x0, 0x4b, 0x4b, 0x0, 0x4b, 0x4d, 0x0, + 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, + 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x0, 0x4c, 0x4a, 0x0, 0x4c, + 0x54, 0x44, 0x0, 0x4c, 0x6a, 0x0, 0x4c, 0xb7, 0x0, 0x4c, 0x301, + 0x0, 0x4c, 0x30c, 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, + 0x0, 0x4c, 0x327, 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, + 0x4d, 0x0, 0x4d, 0x42, 0x0, 0x4d, 0x43, 0x0, 0x4d, 0x44, 0x0, + 0x4d, 0x48, 0x7a, 0x0, 0x4d, 0x50, 0x61, 0x0, 0x4d, 0x56, 0x0, + 0x4d, 0x57, 0x0, 0x4d, 0x301, 0x0, 0x4d, 0x307, 0x0, 0x4d, + 0x323, 0x0, 0x4d, 0x3a9, 0x0, 0x4e, 0x0, 0x4e, 0x4a, 0x0, 0x4e, + 0x6a, 0x0, 0x4e, 0x6f, 0x0, 0x4e, 0x300, 0x0, 0x4e, 0x301, 0x0, + 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, 0x0, 0x4e, + 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, 0x4e, 0x331, + 0x0, 0x4f, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, + 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, + 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, + 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, + 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, + 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, + 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, + 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, + 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, + 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, + 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, + 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, + 0x328, 0x304, 0x0, 0x50, 0x0, 0x50, 0x48, 0x0, 0x50, 0x50, + 0x4d, 0x0, 0x50, 0x50, 0x56, 0x0, 0x50, 0x52, 0x0, 0x50, 0x54, + 0x45, 0x0, 0x50, 0x61, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, + 0x51, 0x0, 0x52, 0x0, 0x52, 0x73, 0x0, 0x52, 0x301, 0x0, 0x52, + 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, 0x0, 0x52, 0x311, + 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, 0x0, 0x52, 0x327, + 0x0, 0x52, 0x331, 0x0, 0x53, 0x0, 0x53, 0x44, 0x0, 0x53, 0x4d, + 0x0, 0x53, 0x53, 0x0, 0x53, 0x76, 0x0, 0x53, 0x301, 0x0, 0x53, + 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, 0x53, + 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, 0x53, + 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, 0x54, + 0x0, 0x54, 0x45, 0x4c, 0x0, 0x54, 0x48, 0x7a, 0x0, 0x54, 0x4d, + 0x0, 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, + 0x54, 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, + 0x331, 0x0, 0x55, 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, + 0x55, 0x302, 0x0, 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, + 0x55, 0x304, 0x0, 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, + 0x55, 0x308, 0x0, 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, + 0x0, 0x55, 0x308, 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, + 0x309, 0x0, 0x55, 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, + 0x0, 0x55, 0x30f, 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, + 0x55, 0x31b, 0x300, 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, + 0x303, 0x0, 0x55, 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, + 0x55, 0x323, 0x0, 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, + 0x32d, 0x0, 0x55, 0x330, 0x0, 0x56, 0x0, 0x56, 0x49, 0x0, 0x56, + 0x49, 0x49, 0x0, 0x56, 0x49, 0x49, 0x49, 0x0, 0x56, 0x303, 0x0, + 0x56, 0x323, 0x0, 0x56, 0x2215, 0x6d, 0x0, 0x57, 0x0, 0x57, + 0x43, 0x0, 0x57, 0x5a, 0x0, 0x57, 0x62, 0x0, 0x57, 0x300, 0x0, + 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, 0x57, + 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x0, 0x58, 0x49, 0x0, 0x58, + 0x49, 0x49, 0x0, 0x58, 0x307, 0x0, 0x58, 0x308, 0x0, 0x59, 0x0, + 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, 0x0, 0x59, + 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, 0x59, 0x308, + 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, 0x0, 0x5a, + 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, + 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x5b, 0x0, 0x5c, 0x0, + 0x5d, 0x0, 0x5e, 0x0, 0x5f, 0x0, 0x60, 0x0, 0x61, 0x0, 0x61, + 0x2e, 0x6d, 0x2e, 0x0, 0x61, 0x2f, 0x63, 0x0, 0x61, 0x2f, 0x73, + 0x0, 0x61, 0x2be, 0x0, 0x61, 0x300, 0x0, 0x61, 0x301, 0x0, + 0x61, 0x302, 0x0, 0x61, 0x302, 0x300, 0x0, 0x61, 0x302, 0x301, + 0x0, 0x61, 0x302, 0x303, 0x0, 0x61, 0x302, 0x309, 0x0, 0x61, + 0x303, 0x0, 0x61, 0x304, 0x0, 0x61, 0x306, 0x0, 0x61, 0x306, + 0x300, 0x0, 0x61, 0x306, 0x301, 0x0, 0x61, 0x306, 0x303, 0x0, + 0x61, 0x306, 0x309, 0x0, 0x61, 0x307, 0x0, 0x61, 0x307, 0x304, + 0x0, 0x61, 0x308, 0x0, 0x61, 0x308, 0x304, 0x0, 0x61, 0x309, + 0x0, 0x61, 0x30a, 0x0, 0x61, 0x30a, 0x301, 0x0, 0x61, 0x30c, + 0x0, 0x61, 0x30f, 0x0, 0x61, 0x311, 0x0, 0x61, 0x323, 0x0, + 0x61, 0x323, 0x302, 0x0, 0x61, 0x323, 0x306, 0x0, 0x61, 0x325, + 0x0, 0x61, 0x328, 0x0, 0x62, 0x0, 0x62, 0x61, 0x72, 0x0, 0x62, + 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x0, + 0x63, 0x2f, 0x6f, 0x0, 0x63, 0x2f, 0x75, 0x0, 0x63, 0x61, 0x6c, + 0x0, 0x63, 0x63, 0x0, 0x63, 0x64, 0x0, 0x63, 0x6d, 0x0, 0x63, + 0x6d, 0x32, 0x0, 0x63, 0x6d, 0x33, 0x0, 0x63, 0x301, 0x0, 0x63, + 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, 0x63, 0x327, + 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x0, 0x64, 0x42, 0x0, 0x64, + 0x61, 0x0, 0x64, 0x6c, 0x0, 0x64, 0x6d, 0x0, 0x64, 0x6d, 0x32, + 0x0, 0x64, 0x6d, 0x33, 0x0, 0x64, 0x7a, 0x0, 0x64, 0x7a, 0x30c, + 0x0, 0x64, 0x307, 0x0, 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, + 0x64, 0x327, 0x0, 0x64, 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, + 0x0, 0x65, 0x56, 0x0, 0x65, 0x72, 0x67, 0x0, 0x65, 0x300, 0x0, + 0x65, 0x301, 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, + 0x65, 0x302, 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, + 0x309, 0x0, 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, + 0x300, 0x0, 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, + 0x307, 0x0, 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, + 0x0, 0x65, 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, + 0x65, 0x323, 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, + 0x0, 0x65, 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, + 0x66, 0x0, 0x66, 0x66, 0x0, 0x66, 0x66, 0x69, 0x0, 0x66, 0x66, + 0x6c, 0x0, 0x66, 0x69, 0x0, 0x66, 0x6c, 0x0, 0x66, 0x6d, 0x0, + 0x66, 0x307, 0x0, 0x67, 0x0, 0x67, 0x61, 0x6c, 0x0, 0x67, + 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, 0x67, 0x306, + 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, 0x327, 0x0, + 0x68, 0x0, 0x68, 0x50, 0x61, 0x0, 0x68, 0x61, 0x0, 0x68, 0x302, + 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, 0x0, 0x68, 0x30c, 0x0, + 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, 0x68, 0x32e, 0x0, 0x68, + 0x331, 0x0, 0x69, 0x0, 0x69, 0x69, 0x0, 0x69, 0x69, 0x69, 0x0, + 0x69, 0x6a, 0x0, 0x69, 0x6e, 0x0, 0x69, 0x76, 0x0, 0x69, 0x78, + 0x0, 0x69, 0x300, 0x0, 0x69, 0x301, 0x0, 0x69, 0x302, 0x0, + 0x69, 0x303, 0x0, 0x69, 0x304, 0x0, 0x69, 0x306, 0x0, 0x69, + 0x308, 0x0, 0x69, 0x308, 0x301, 0x0, 0x69, 0x309, 0x0, 0x69, + 0x30c, 0x0, 0x69, 0x30f, 0x0, 0x69, 0x311, 0x0, 0x69, 0x323, + 0x0, 0x69, 0x328, 0x0, 0x69, 0x330, 0x0, 0x6a, 0x0, 0x6a, + 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x0, 0x6b, 0x41, 0x0, 0x6b, + 0x48, 0x7a, 0x0, 0x6b, 0x50, 0x61, 0x0, 0x6b, 0x56, 0x0, 0x6b, + 0x57, 0x0, 0x6b, 0x63, 0x61, 0x6c, 0x0, 0x6b, 0x67, 0x0, 0x6b, + 0x6c, 0x0, 0x6b, 0x6d, 0x0, 0x6b, 0x6d, 0x32, 0x0, 0x6b, 0x6d, + 0x33, 0x0, 0x6b, 0x74, 0x0, 0x6b, 0x301, 0x0, 0x6b, 0x30c, 0x0, + 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, 0x6b, 0x331, 0x0, 0x6b, + 0x3a9, 0x0, 0x6c, 0x0, 0x6c, 0x6a, 0x0, 0x6c, 0x6d, 0x0, 0x6c, + 0x6e, 0x0, 0x6c, 0x6f, 0x67, 0x0, 0x6c, 0x78, 0x0, 0x6c, 0xb7, + 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, 0x323, 0x0, + 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, 0x32d, 0x0, + 0x6c, 0x331, 0x0, 0x6d, 0x0, 0x6d, 0x32, 0x0, 0x6d, 0x33, 0x0, + 0x6d, 0x41, 0x0, 0x6d, 0x56, 0x0, 0x6d, 0x57, 0x0, 0x6d, 0x62, + 0x0, 0x6d, 0x67, 0x0, 0x6d, 0x69, 0x6c, 0x0, 0x6d, 0x6c, 0x0, + 0x6d, 0x6d, 0x0, 0x6d, 0x6d, 0x32, 0x0, 0x6d, 0x6d, 0x33, 0x0, + 0x6d, 0x6f, 0x6c, 0x0, 0x6d, 0x73, 0x0, 0x6d, 0x301, 0x0, 0x6d, + 0x307, 0x0, 0x6d, 0x323, 0x0, 0x6d, 0x2215, 0x73, 0x0, 0x6d, + 0x2215, 0x73, 0x32, 0x0, 0x6e, 0x0, 0x6e, 0x41, 0x0, 0x6e, + 0x46, 0x0, 0x6e, 0x56, 0x0, 0x6e, 0x57, 0x0, 0x6e, 0x6a, 0x0, + 0x6e, 0x6d, 0x0, 0x6e, 0x73, 0x0, 0x6e, 0x300, 0x0, 0x6e, + 0x301, 0x0, 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, + 0x0, 0x6e, 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, + 0x6e, 0x331, 0x0, 0x6f, 0x0, 0x6f, 0x56, 0x0, 0x6f, 0x300, 0x0, + 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, 0x6f, 0x302, 0x300, 0x0, + 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, 0x303, 0x0, 0x6f, 0x302, + 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, 0x303, 0x301, 0x0, 0x6f, + 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, 0x0, 0x6f, 0x304, 0x0, + 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, 0x301, 0x0, 0x6f, 0x306, + 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, 0x304, 0x0, 0x6f, 0x308, + 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, 0x309, 0x0, 0x6f, 0x30b, + 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, 0x0, 0x6f, 0x311, 0x0, + 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, 0x0, 0x6f, 0x31b, 0x301, + 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, 0x31b, 0x309, 0x0, 0x6f, + 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, 0x6f, 0x323, 0x302, 0x0, + 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, 0x0, 0x70, 0x0, 0x70, + 0x2e, 0x6d, 0x2e, 0x0, 0x70, 0x41, 0x0, 0x70, 0x46, 0x0, 0x70, + 0x56, 0x0, 0x70, 0x57, 0x0, 0x70, 0x63, 0x0, 0x70, 0x73, 0x0, + 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x71, 0x0, 0x72, 0x0, 0x72, + 0x61, 0x64, 0x0, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x0, 0x72, + 0x61, 0x64, 0x2215, 0x73, 0x32, 0x0, 0x72, 0x301, 0x0, 0x72, + 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, 0x311, + 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, 0x327, + 0x0, 0x72, 0x331, 0x0, 0x73, 0x0, 0x73, 0x72, 0x0, 0x73, 0x74, + 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, 0x307, 0x0, 0x73, 0x302, + 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, 0x0, 0x73, 0x30c, 0x307, + 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, 0x307, 0x0, 0x73, 0x326, + 0x0, 0x73, 0x327, 0x0, 0x74, 0x0, 0x74, 0x307, 0x0, 0x74, + 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, 0x74, 0x326, + 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, 0x331, 0x0, + 0x75, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, + 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, + 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, + 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, + 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, + 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, + 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, + 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, + 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, + 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, + 0x75, 0x330, 0x0, 0x76, 0x0, 0x76, 0x69, 0x0, 0x76, 0x69, 0x69, + 0x0, 0x76, 0x69, 0x69, 0x69, 0x0, 0x76, 0x303, 0x0, 0x76, + 0x323, 0x0, 0x77, 0x0, 0x77, 0x300, 0x0, 0x77, 0x301, 0x0, + 0x77, 0x302, 0x0, 0x77, 0x307, 0x0, 0x77, 0x308, 0x0, 0x77, + 0x30a, 0x0, 0x77, 0x323, 0x0, 0x78, 0x0, 0x78, 0x69, 0x0, 0x78, + 0x69, 0x69, 0x0, 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x0, + 0x79, 0x300, 0x0, 0x79, 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, + 0x303, 0x0, 0x79, 0x304, 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, + 0x0, 0x79, 0x309, 0x0, 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, + 0x7a, 0x0, 0x7a, 0x301, 0x0, 0x7a, 0x302, 0x0, 0x7a, 0x307, + 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, 0x0, 0x7a, 0x331, 0x0, + 0x7b, 0x0, 0x7c, 0x0, 0x7d, 0x0, 0x7e, 0x0, 0xa2, 0x0, 0xa3, + 0x0, 0xa5, 0x0, 0xa6, 0x0, 0xac, 0x0, 0xb0, 0x43, 0x0, 0xb0, + 0x46, 0x0, 0xb7, 0x0, 0xc6, 0x0, 0xc6, 0x301, 0x0, 0xc6, 0x304, + 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, 0x0, + 0xf0, 0x0, 0xf8, 0x301, 0x0, 0x126, 0x0, 0x127, 0x0, 0x131, + 0x0, 0x14b, 0x0, 0x153, 0x0, 0x18e, 0x0, 0x190, 0x0, 0x1ab, + 0x0, 0x1b7, 0x30c, 0x0, 0x222, 0x0, 0x237, 0x0, 0x250, 0x0, + 0x251, 0x0, 0x252, 0x0, 0x254, 0x0, 0x255, 0x0, 0x259, 0x0, + 0x25b, 0x0, 0x25c, 0x0, 0x25f, 0x0, 0x261, 0x0, 0x263, 0x0, + 0x265, 0x0, 0x266, 0x0, 0x268, 0x0, 0x269, 0x0, 0x26a, 0x0, + 0x26d, 0x0, 0x26f, 0x0, 0x270, 0x0, 0x271, 0x0, 0x272, 0x0, + 0x273, 0x0, 0x274, 0x0, 0x275, 0x0, 0x278, 0x0, 0x279, 0x0, + 0x27b, 0x0, 0x281, 0x0, 0x282, 0x0, 0x283, 0x0, 0x289, 0x0, + 0x28a, 0x0, 0x28b, 0x0, 0x28c, 0x0, 0x290, 0x0, 0x291, 0x0, + 0x292, 0x0, 0x292, 0x30c, 0x0, 0x295, 0x0, 0x29d, 0x0, 0x29f, + 0x0, 0x2b9, 0x0, 0x2bc, 0x6e, 0x0, 0x300, 0x0, 0x301, 0x0, + 0x308, 0x301, 0x0, 0x313, 0x0, 0x391, 0x0, 0x391, 0x300, 0x0, + 0x391, 0x301, 0x0, 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, + 0x313, 0x0, 0x391, 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, + 0x345, 0x0, 0x391, 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, + 0x345, 0x0, 0x391, 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, + 0x345, 0x0, 0x391, 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, + 0x314, 0x300, 0x0, 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, + 0x314, 0x301, 0x0, 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, + 0x314, 0x342, 0x0, 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, + 0x314, 0x345, 0x0, 0x391, 0x345, 0x0, 0x392, 0x0, 0x393, 0x0, + 0x394, 0x0, 0x395, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, + 0x395, 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, + 0x301, 0x0, 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, + 0x314, 0x301, 0x0, 0x396, 0x0, 0x397, 0x0, 0x397, 0x300, 0x0, + 0x397, 0x301, 0x0, 0x397, 0x313, 0x0, 0x397, 0x313, 0x300, 0x0, + 0x397, 0x313, 0x300, 0x345, 0x0, 0x397, 0x313, 0x301, 0x0, + 0x397, 0x313, 0x301, 0x345, 0x0, 0x397, 0x313, 0x342, 0x0, + 0x397, 0x313, 0x342, 0x345, 0x0, 0x397, 0x313, 0x345, 0x0, + 0x397, 0x314, 0x0, 0x397, 0x314, 0x300, 0x0, 0x397, 0x314, + 0x300, 0x345, 0x0, 0x397, 0x314, 0x301, 0x0, 0x397, 0x314, + 0x301, 0x345, 0x0, 0x397, 0x314, 0x342, 0x0, 0x397, 0x314, + 0x342, 0x345, 0x0, 0x397, 0x314, 0x345, 0x0, 0x397, 0x345, 0x0, + 0x398, 0x0, 0x399, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, 0x0, + 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, 0x399, + 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, 0x301, 0x0, + 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, 0x314, + 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, 0x342, 0x0, + 0x39a, 0x0, 0x39b, 0x0, 0x39c, 0x0, 0x39d, 0x0, 0x39e, 0x0, + 0x39f, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, + 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, + 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, + 0x3a0, 0x0, 0x3a1, 0x0, 0x3a1, 0x314, 0x0, 0x3a3, 0x0, 0x3a4, + 0x0, 0x3a5, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, + 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, + 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, + 0x314, 0x342, 0x0, 0x3a6, 0x0, 0x3a7, 0x0, 0x3a8, 0x0, 0x3a9, + 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, 0x0, 0x3a9, 0x313, 0x0, + 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, 0x300, 0x345, 0x0, + 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, 0x301, 0x345, 0x0, + 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, 0x342, 0x345, 0x0, + 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, 0x3a9, 0x314, + 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, 0x3a9, 0x314, + 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, 0x3a9, 0x314, + 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, 0x3a9, 0x314, + 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x0, 0x3b1, 0x300, 0x0, + 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, + 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, + 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, + 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, + 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, + 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, + 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, + 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, + 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, + 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, + 0x345, 0x0, 0x3b2, 0x0, 0x3b3, 0x0, 0x3b4, 0x0, 0x3b5, 0x0, + 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, 0x0, 0x3b5, + 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, 0x314, 0x0, + 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, 0x3b6, 0x0, + 0x3b7, 0x0, 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, + 0x301, 0x0, 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, + 0x313, 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, + 0x313, 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, + 0x313, 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, + 0x313, 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, + 0x3b7, 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, + 0x3b7, 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, + 0x3b7, 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, + 0x3b7, 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, + 0x3b8, 0x0, 0x3b9, 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, + 0x3b9, 0x304, 0x0, 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, + 0x308, 0x300, 0x0, 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, + 0x342, 0x0, 0x3b9, 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, + 0x313, 0x301, 0x0, 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, + 0x3b9, 0x314, 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, + 0x314, 0x342, 0x0, 0x3b9, 0x342, 0x0, 0x3ba, 0x0, 0x3bb, 0x0, + 0x3bc, 0x0, 0x3bc, 0x41, 0x0, 0x3bc, 0x46, 0x0, 0x3bc, 0x56, + 0x0, 0x3bc, 0x57, 0x0, 0x3bc, 0x67, 0x0, 0x3bc, 0x6c, 0x0, + 0x3bc, 0x6d, 0x0, 0x3bc, 0x73, 0x0, 0x3bd, 0x0, 0x3be, 0x0, + 0x3bf, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, 0x313, + 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, 0x3bf, + 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, 0x301, 0x0, + 0x3c0, 0x0, 0x3c1, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, + 0x3c2, 0x0, 0x3c3, 0x0, 0x3c4, 0x0, 0x3c5, 0x0, 0x3c5, 0x300, + 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, + 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, + 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, + 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, + 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, + 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, + 0x3c6, 0x0, 0x3c7, 0x0, 0x3c8, 0x0, 0x3c9, 0x0, 0x3c9, 0x300, + 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, 0x3c9, 0x301, + 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, 0x300, 0x0, 0x3c9, + 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, 0x301, 0x0, 0x3c9, + 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x342, 0x0, 0x3c9, + 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, 0x345, 0x0, 0x3c9, + 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, 0x314, 0x300, + 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, 0x314, 0x301, + 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, 0x314, 0x342, + 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, 0x342, 0x0, 0x3c9, + 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3dc, 0x0, 0x3dd, 0x0, + 0x406, 0x308, 0x0, 0x410, 0x306, 0x0, 0x410, 0x308, 0x0, 0x413, + 0x301, 0x0, 0x415, 0x300, 0x0, 0x415, 0x306, 0x0, 0x415, 0x308, + 0x0, 0x416, 0x306, 0x0, 0x416, 0x308, 0x0, 0x417, 0x308, 0x0, + 0x418, 0x300, 0x0, 0x418, 0x304, 0x0, 0x418, 0x306, 0x0, 0x418, + 0x308, 0x0, 0x41a, 0x301, 0x0, 0x41e, 0x308, 0x0, 0x423, 0x304, + 0x0, 0x423, 0x306, 0x0, 0x423, 0x308, 0x0, 0x423, 0x30b, 0x0, + 0x427, 0x308, 0x0, 0x42b, 0x308, 0x0, 0x42d, 0x308, 0x0, 0x430, + 0x306, 0x0, 0x430, 0x308, 0x0, 0x433, 0x301, 0x0, 0x435, 0x300, + 0x0, 0x435, 0x306, 0x0, 0x435, 0x308, 0x0, 0x436, 0x306, 0x0, + 0x436, 0x308, 0x0, 0x437, 0x308, 0x0, 0x438, 0x300, 0x0, 0x438, + 0x304, 0x0, 0x438, 0x306, 0x0, 0x438, 0x308, 0x0, 0x43a, 0x301, + 0x0, 0x43d, 0x0, 0x43e, 0x308, 0x0, 0x443, 0x304, 0x0, 0x443, + 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, 0x0, 0x447, 0x308, + 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, 0x456, 0x308, 0x0, + 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, 0x308, 0x0, 0x4d9, + 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, 0x0, 0x565, 0x582, + 0x0, 0x574, 0x565, 0x0, 0x574, 0x56b, 0x0, 0x574, 0x56d, 0x0, + 0x574, 0x576, 0x0, 0x57e, 0x576, 0x0, 0x5d0, 0x0, 0x5d0, 0x5b7, + 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, 0x5d0, 0x5dc, 0x0, + 0x5d1, 0x0, 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x0, + 0x5d2, 0x5bc, 0x0, 0x5d3, 0x0, 0x5d3, 0x5bc, 0x0, 0x5d4, 0x0, + 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, 0x0, 0x5d6, + 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, 0x5d9, 0x5bc, + 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, + 0x5bf, 0x0, 0x5dc, 0x0, 0x5dc, 0x5bc, 0x0, 0x5dd, 0x0, 0x5de, + 0x5bc, 0x0, 0x5e0, 0x5bc, 0x0, 0x5e1, 0x5bc, 0x0, 0x5e2, 0x0, + 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, 0x5e4, 0x5bf, 0x0, 0x5e6, + 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, 0x0, 0x5e8, 0x5bc, 0x0, + 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, 0x5bc, + 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, 0x0, + 0x5ea, 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x621, 0x0, 0x627, 0x0, + 0x627, 0x643, 0x628, 0x631, 0x0, 0x627, 0x644, 0x644, 0x647, + 0x0, 0x627, 0x64b, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, 0x0, + 0x627, 0x655, 0x0, 0x627, 0x674, 0x0, 0x628, 0x0, 0x628, 0x62c, + 0x0, 0x628, 0x62d, 0x0, 0x628, 0x62d, 0x64a, 0x0, 0x628, 0x62e, + 0x0, 0x628, 0x62e, 0x64a, 0x0, 0x628, 0x631, 0x0, 0x628, 0x632, + 0x0, 0x628, 0x645, 0x0, 0x628, 0x646, 0x0, 0x628, 0x647, 0x0, + 0x628, 0x649, 0x0, 0x628, 0x64a, 0x0, 0x629, 0x0, 0x62a, 0x0, + 0x62a, 0x62c, 0x0, 0x62a, 0x62c, 0x645, 0x0, 0x62a, 0x62c, + 0x649, 0x0, 0x62a, 0x62c, 0x64a, 0x0, 0x62a, 0x62d, 0x0, 0x62a, + 0x62d, 0x62c, 0x0, 0x62a, 0x62d, 0x645, 0x0, 0x62a, 0x62e, 0x0, + 0x62a, 0x62e, 0x645, 0x0, 0x62a, 0x62e, 0x649, 0x0, 0x62a, + 0x62e, 0x64a, 0x0, 0x62a, 0x631, 0x0, 0x62a, 0x632, 0x0, 0x62a, + 0x645, 0x0, 0x62a, 0x645, 0x62c, 0x0, 0x62a, 0x645, 0x62d, 0x0, + 0x62a, 0x645, 0x62e, 0x0, 0x62a, 0x645, 0x649, 0x0, 0x62a, + 0x645, 0x64a, 0x0, 0x62a, 0x646, 0x0, 0x62a, 0x647, 0x0, 0x62a, + 0x649, 0x0, 0x62a, 0x64a, 0x0, 0x62b, 0x0, 0x62b, 0x62c, 0x0, + 0x62b, 0x631, 0x0, 0x62b, 0x632, 0x0, 0x62b, 0x645, 0x0, 0x62b, + 0x646, 0x0, 0x62b, 0x647, 0x0, 0x62b, 0x649, 0x0, 0x62b, 0x64a, + 0x0, 0x62c, 0x0, 0x62c, 0x62d, 0x0, 0x62c, 0x62d, 0x649, 0x0, + 0x62c, 0x62d, 0x64a, 0x0, 0x62c, 0x644, 0x20, 0x62c, 0x644, + 0x627, 0x644, 0x647, 0x0, 0x62c, 0x645, 0x0, 0x62c, 0x645, + 0x62d, 0x0, 0x62c, 0x645, 0x649, 0x0, 0x62c, 0x645, 0x64a, 0x0, + 0x62c, 0x649, 0x0, 0x62c, 0x64a, 0x0, 0x62d, 0x0, 0x62d, 0x62c, + 0x0, 0x62d, 0x62c, 0x64a, 0x0, 0x62d, 0x645, 0x0, 0x62d, 0x645, + 0x649, 0x0, 0x62d, 0x645, 0x64a, 0x0, 0x62d, 0x649, 0x0, 0x62d, + 0x64a, 0x0, 0x62e, 0x0, 0x62e, 0x62c, 0x0, 0x62e, 0x62d, 0x0, + 0x62e, 0x645, 0x0, 0x62e, 0x649, 0x0, 0x62e, 0x64a, 0x0, 0x62f, + 0x0, 0x630, 0x0, 0x630, 0x670, 0x0, 0x631, 0x0, 0x631, 0x633, + 0x648, 0x644, 0x0, 0x631, 0x670, 0x0, 0x631, 0x6cc, 0x627, + 0x644, 0x0, 0x632, 0x0, 0x633, 0x0, 0x633, 0x62c, 0x0, 0x633, + 0x62c, 0x62d, 0x0, 0x633, 0x62c, 0x649, 0x0, 0x633, 0x62d, 0x0, + 0x633, 0x62d, 0x62c, 0x0, 0x633, 0x62e, 0x0, 0x633, 0x62e, + 0x649, 0x0, 0x633, 0x62e, 0x64a, 0x0, 0x633, 0x631, 0x0, 0x633, + 0x645, 0x0, 0x633, 0x645, 0x62c, 0x0, 0x633, 0x645, 0x62d, 0x0, + 0x633, 0x645, 0x645, 0x0, 0x633, 0x647, 0x0, 0x633, 0x649, 0x0, + 0x633, 0x64a, 0x0, 0x634, 0x0, 0x634, 0x62c, 0x0, 0x634, 0x62c, + 0x64a, 0x0, 0x634, 0x62d, 0x0, 0x634, 0x62d, 0x645, 0x0, 0x634, + 0x62d, 0x64a, 0x0, 0x634, 0x62e, 0x0, 0x634, 0x631, 0x0, 0x634, + 0x645, 0x0, 0x634, 0x645, 0x62e, 0x0, 0x634, 0x645, 0x645, 0x0, + 0x634, 0x647, 0x0, 0x634, 0x649, 0x0, 0x634, 0x64a, 0x0, 0x635, + 0x0, 0x635, 0x62d, 0x0, 0x635, 0x62d, 0x62d, 0x0, 0x635, 0x62d, + 0x64a, 0x0, 0x635, 0x62e, 0x0, 0x635, 0x631, 0x0, 0x635, 0x644, + 0x639, 0x645, 0x0, 0x635, 0x644, 0x649, 0x0, 0x635, 0x644, + 0x649, 0x20, 0x627, 0x644, 0x644, 0x647, 0x20, 0x639, 0x644, + 0x64a, 0x647, 0x20, 0x648, 0x633, 0x644, 0x645, 0x0, 0x635, + 0x644, 0x6d2, 0x0, 0x635, 0x645, 0x0, 0x635, 0x645, 0x645, 0x0, + 0x635, 0x649, 0x0, 0x635, 0x64a, 0x0, 0x636, 0x0, 0x636, 0x62c, + 0x0, 0x636, 0x62d, 0x0, 0x636, 0x62d, 0x649, 0x0, 0x636, 0x62d, + 0x64a, 0x0, 0x636, 0x62e, 0x0, 0x636, 0x62e, 0x645, 0x0, 0x636, + 0x631, 0x0, 0x636, 0x645, 0x0, 0x636, 0x649, 0x0, 0x636, 0x64a, + 0x0, 0x637, 0x0, 0x637, 0x62d, 0x0, 0x637, 0x645, 0x0, 0x637, + 0x645, 0x62d, 0x0, 0x637, 0x645, 0x645, 0x0, 0x637, 0x645, + 0x64a, 0x0, 0x637, 0x649, 0x0, 0x637, 0x64a, 0x0, 0x638, 0x0, + 0x638, 0x645, 0x0, 0x639, 0x0, 0x639, 0x62c, 0x0, 0x639, 0x62c, + 0x645, 0x0, 0x639, 0x644, 0x64a, 0x647, 0x0, 0x639, 0x645, 0x0, + 0x639, 0x645, 0x645, 0x0, 0x639, 0x645, 0x649, 0x0, 0x639, + 0x645, 0x64a, 0x0, 0x639, 0x649, 0x0, 0x639, 0x64a, 0x0, 0x63a, + 0x0, 0x63a, 0x62c, 0x0, 0x63a, 0x645, 0x0, 0x63a, 0x645, 0x645, + 0x0, 0x63a, 0x645, 0x649, 0x0, 0x63a, 0x645, 0x64a, 0x0, 0x63a, + 0x649, 0x0, 0x63a, 0x64a, 0x0, 0x640, 0x64b, 0x0, 0x640, 0x64e, + 0x0, 0x640, 0x64e, 0x651, 0x0, 0x640, 0x64f, 0x0, 0x640, 0x64f, + 0x651, 0x0, 0x640, 0x650, 0x0, 0x640, 0x650, 0x651, 0x0, 0x640, + 0x651, 0x0, 0x640, 0x652, 0x0, 0x641, 0x0, 0x641, 0x62c, 0x0, + 0x641, 0x62d, 0x0, 0x641, 0x62e, 0x0, 0x641, 0x62e, 0x645, 0x0, + 0x641, 0x645, 0x0, 0x641, 0x645, 0x64a, 0x0, 0x641, 0x649, 0x0, + 0x641, 0x64a, 0x0, 0x642, 0x0, 0x642, 0x62d, 0x0, 0x642, 0x644, + 0x6d2, 0x0, 0x642, 0x645, 0x0, 0x642, 0x645, 0x62d, 0x0, 0x642, + 0x645, 0x645, 0x0, 0x642, 0x645, 0x64a, 0x0, 0x642, 0x649, 0x0, + 0x642, 0x64a, 0x0, 0x643, 0x0, 0x643, 0x627, 0x0, 0x643, 0x62c, + 0x0, 0x643, 0x62d, 0x0, 0x643, 0x62e, 0x0, 0x643, 0x644, 0x0, + 0x643, 0x645, 0x0, 0x643, 0x645, 0x645, 0x0, 0x643, 0x645, + 0x64a, 0x0, 0x643, 0x649, 0x0, 0x643, 0x64a, 0x0, 0x644, 0x0, + 0x644, 0x627, 0x0, 0x644, 0x627, 0x653, 0x0, 0x644, 0x627, + 0x654, 0x0, 0x644, 0x627, 0x655, 0x0, 0x644, 0x62c, 0x0, 0x644, + 0x62c, 0x62c, 0x0, 0x644, 0x62c, 0x645, 0x0, 0x644, 0x62c, + 0x64a, 0x0, 0x644, 0x62d, 0x0, 0x644, 0x62d, 0x645, 0x0, 0x644, + 0x62d, 0x649, 0x0, 0x644, 0x62d, 0x64a, 0x0, 0x644, 0x62e, 0x0, + 0x644, 0x62e, 0x645, 0x0, 0x644, 0x645, 0x0, 0x644, 0x645, + 0x62d, 0x0, 0x644, 0x645, 0x64a, 0x0, 0x644, 0x647, 0x0, 0x644, + 0x649, 0x0, 0x644, 0x64a, 0x0, 0x645, 0x0, 0x645, 0x627, 0x0, + 0x645, 0x62c, 0x0, 0x645, 0x62c, 0x62d, 0x0, 0x645, 0x62c, + 0x62e, 0x0, 0x645, 0x62c, 0x645, 0x0, 0x645, 0x62c, 0x64a, 0x0, + 0x645, 0x62d, 0x0, 0x645, 0x62d, 0x62c, 0x0, 0x645, 0x62d, + 0x645, 0x0, 0x645, 0x62d, 0x645, 0x62f, 0x0, 0x645, 0x62d, + 0x64a, 0x0, 0x645, 0x62e, 0x0, 0x645, 0x62e, 0x62c, 0x0, 0x645, + 0x62e, 0x645, 0x0, 0x645, 0x62e, 0x64a, 0x0, 0x645, 0x645, 0x0, + 0x645, 0x645, 0x64a, 0x0, 0x645, 0x649, 0x0, 0x645, 0x64a, 0x0, + 0x646, 0x0, 0x646, 0x62c, 0x0, 0x646, 0x62c, 0x62d, 0x0, 0x646, + 0x62c, 0x645, 0x0, 0x646, 0x62c, 0x649, 0x0, 0x646, 0x62c, + 0x64a, 0x0, 0x646, 0x62d, 0x0, 0x646, 0x62d, 0x645, 0x0, 0x646, + 0x62d, 0x649, 0x0, 0x646, 0x62d, 0x64a, 0x0, 0x646, 0x62e, 0x0, + 0x646, 0x631, 0x0, 0x646, 0x632, 0x0, 0x646, 0x645, 0x0, 0x646, + 0x645, 0x649, 0x0, 0x646, 0x645, 0x64a, 0x0, 0x646, 0x646, 0x0, + 0x646, 0x647, 0x0, 0x646, 0x649, 0x0, 0x646, 0x64a, 0x0, 0x647, + 0x0, 0x647, 0x62c, 0x0, 0x647, 0x645, 0x0, 0x647, 0x645, 0x62c, + 0x0, 0x647, 0x645, 0x645, 0x0, 0x647, 0x649, 0x0, 0x647, 0x64a, + 0x0, 0x647, 0x670, 0x0, 0x648, 0x0, 0x648, 0x633, 0x644, 0x645, + 0x0, 0x648, 0x654, 0x0, 0x648, 0x674, 0x0, 0x649, 0x0, 0x649, + 0x670, 0x0, 0x64a, 0x0, 0x64a, 0x62c, 0x0, 0x64a, 0x62c, 0x64a, + 0x0, 0x64a, 0x62d, 0x0, 0x64a, 0x62d, 0x64a, 0x0, 0x64a, 0x62e, + 0x0, 0x64a, 0x631, 0x0, 0x64a, 0x632, 0x0, 0x64a, 0x645, 0x0, + 0x64a, 0x645, 0x645, 0x0, 0x64a, 0x645, 0x64a, 0x0, 0x64a, + 0x646, 0x0, 0x64a, 0x647, 0x0, 0x64a, 0x649, 0x0, 0x64a, 0x64a, + 0x0, 0x64a, 0x654, 0x0, 0x64a, 0x654, 0x627, 0x0, 0x64a, 0x654, + 0x62c, 0x0, 0x64a, 0x654, 0x62d, 0x0, 0x64a, 0x654, 0x62e, 0x0, + 0x64a, 0x654, 0x631, 0x0, 0x64a, 0x654, 0x632, 0x0, 0x64a, + 0x654, 0x645, 0x0, 0x64a, 0x654, 0x646, 0x0, 0x64a, 0x654, + 0x647, 0x0, 0x64a, 0x654, 0x648, 0x0, 0x64a, 0x654, 0x649, 0x0, + 0x64a, 0x654, 0x64a, 0x0, 0x64a, 0x654, 0x6c6, 0x0, 0x64a, + 0x654, 0x6c7, 0x0, 0x64a, 0x654, 0x6c8, 0x0, 0x64a, 0x654, + 0x6d0, 0x0, 0x64a, 0x654, 0x6d5, 0x0, 0x64a, 0x674, 0x0, 0x66e, + 0x0, 0x66f, 0x0, 0x671, 0x0, 0x679, 0x0, 0x67a, 0x0, 0x67b, + 0x0, 0x67e, 0x0, 0x67f, 0x0, 0x680, 0x0, 0x683, 0x0, 0x684, + 0x0, 0x686, 0x0, 0x687, 0x0, 0x688, 0x0, 0x68c, 0x0, 0x68d, + 0x0, 0x68e, 0x0, 0x691, 0x0, 0x698, 0x0, 0x6a1, 0x0, 0x6a4, + 0x0, 0x6a6, 0x0, 0x6a9, 0x0, 0x6ad, 0x0, 0x6af, 0x0, 0x6b1, + 0x0, 0x6b3, 0x0, 0x6ba, 0x0, 0x6bb, 0x0, 0x6be, 0x0, 0x6c1, + 0x0, 0x6c1, 0x654, 0x0, 0x6c5, 0x0, 0x6c6, 0x0, 0x6c7, 0x0, + 0x6c7, 0x674, 0x0, 0x6c8, 0x0, 0x6c9, 0x0, 0x6cb, 0x0, 0x6cc, + 0x0, 0x6d0, 0x0, 0x6d2, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, + 0x0, 0x915, 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, + 0x91c, 0x93c, 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, + 0x93c, 0x0, 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, + 0x0, 0x933, 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, + 0x9af, 0x9bc, 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, + 0xa3c, 0x0, 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, + 0x0, 0xa32, 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, + 0xb22, 0xb3c, 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, + 0xb57, 0x0, 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, + 0x0, 0xbc7, 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, + 0xcc6, 0xcc2, 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, + 0xcc6, 0xcd6, 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, + 0xd3e, 0x0, 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, + 0xdca, 0x0, 0xdd9, 0xddf, 0x0, 0xe4d, 0xe32, 0x0, 0xeab, 0xe99, + 0x0, 0xeab, 0xea1, 0x0, 0xecd, 0xeb2, 0x0, 0xf0b, 0x0, 0xf40, + 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, 0xfb7, 0x0, 0xf51, 0xfb7, + 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, 0x0, 0xf71, 0xf72, 0x0, + 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, 0xf90, 0xfb5, 0x0, 0xf92, + 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, 0xfb7, 0x0, 0xfa6, 0xfb7, + 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf71, 0xf80, 0x0, 0xfb2, 0xf80, + 0x0, 0xfb3, 0xf71, 0xf80, 0x0, 0xfb3, 0xf80, 0x0, 0x1025, + 0x102e, 0x0, 0x10dc, 0x0, 0x1100, 0x0, 0x1100, 0x1161, 0x0, + 0x1101, 0x0, 0x1102, 0x0, 0x1102, 0x1161, 0x0, 0x1103, 0x0, + 0x1103, 0x1161, 0x0, 0x1104, 0x0, 0x1105, 0x0, 0x1105, 0x1161, + 0x0, 0x1106, 0x0, 0x1106, 0x1161, 0x0, 0x1107, 0x0, 0x1107, + 0x1161, 0x0, 0x1108, 0x0, 0x1109, 0x0, 0x1109, 0x1161, 0x0, + 0x110a, 0x0, 0x110b, 0x0, 0x110b, 0x1161, 0x0, 0x110b, 0x116e, + 0x0, 0x110c, 0x0, 0x110c, 0x1161, 0x0, 0x110c, 0x116e, 0x110b, + 0x1174, 0x0, 0x110d, 0x0, 0x110e, 0x0, 0x110e, 0x1161, 0x0, + 0x110e, 0x1161, 0x11b7, 0x1100, 0x1169, 0x0, 0x110f, 0x0, + 0x110f, 0x1161, 0x0, 0x1110, 0x0, 0x1110, 0x1161, 0x0, 0x1111, + 0x0, 0x1111, 0x1161, 0x0, 0x1112, 0x0, 0x1112, 0x1161, 0x0, + 0x1114, 0x0, 0x1115, 0x0, 0x111a, 0x0, 0x111c, 0x0, 0x111d, + 0x0, 0x111e, 0x0, 0x1120, 0x0, 0x1121, 0x0, 0x1122, 0x0, + 0x1123, 0x0, 0x1127, 0x0, 0x1129, 0x0, 0x112b, 0x0, 0x112c, + 0x0, 0x112d, 0x0, 0x112e, 0x0, 0x112f, 0x0, 0x1132, 0x0, + 0x1136, 0x0, 0x1140, 0x0, 0x1147, 0x0, 0x114c, 0x0, 0x1157, + 0x0, 0x1158, 0x0, 0x1159, 0x0, 0x1160, 0x0, 0x1161, 0x0, + 0x1162, 0x0, 0x1163, 0x0, 0x1164, 0x0, 0x1165, 0x0, 0x1166, + 0x0, 0x1167, 0x0, 0x1168, 0x0, 0x1169, 0x0, 0x116a, 0x0, + 0x116b, 0x0, 0x116c, 0x0, 0x116d, 0x0, 0x116e, 0x0, 0x116f, + 0x0, 0x1170, 0x0, 0x1171, 0x0, 0x1172, 0x0, 0x1173, 0x0, + 0x1174, 0x0, 0x1175, 0x0, 0x1184, 0x0, 0x1185, 0x0, 0x1188, + 0x0, 0x1191, 0x0, 0x1192, 0x0, 0x1194, 0x0, 0x119e, 0x0, + 0x11a1, 0x0, 0x11aa, 0x0, 0x11ac, 0x0, 0x11ad, 0x0, 0x11b0, + 0x0, 0x11b1, 0x0, 0x11b2, 0x0, 0x11b3, 0x0, 0x11b4, 0x0, + 0x11b5, 0x0, 0x11c7, 0x0, 0x11c8, 0x0, 0x11cc, 0x0, 0x11ce, + 0x0, 0x11d3, 0x0, 0x11d7, 0x0, 0x11d9, 0x0, 0x11dd, 0x0, + 0x11df, 0x0, 0x11f1, 0x0, 0x11f2, 0x0, 0x1b05, 0x1b35, 0x0, + 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, 0x0, + 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, 0x0, + 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, 0x0, + 0x1b42, 0x1b35, 0x0, 0x1d02, 0x0, 0x1d16, 0x0, 0x1d17, 0x0, + 0x1d1c, 0x0, 0x1d1d, 0x0, 0x1d25, 0x0, 0x1d7b, 0x0, 0x1d85, + 0x0, 0x2010, 0x0, 0x2013, 0x0, 0x2014, 0x0, 0x2032, 0x2032, + 0x0, 0x2032, 0x2032, 0x2032, 0x0, 0x2032, 0x2032, 0x2032, + 0x2032, 0x0, 0x2035, 0x2035, 0x0, 0x2035, 0x2035, 0x2035, 0x0, + 0x20a9, 0x0, 0x2190, 0x0, 0x2190, 0x338, 0x0, 0x2191, 0x0, + 0x2192, 0x0, 0x2192, 0x338, 0x0, 0x2193, 0x0, 0x2194, 0x338, + 0x0, 0x21d0, 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, + 0x0, 0x2202, 0x0, 0x2203, 0x338, 0x0, 0x2207, 0x0, 0x2208, + 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2211, 0x0, 0x2212, 0x0, + 0x2223, 0x338, 0x0, 0x2225, 0x338, 0x0, 0x222b, 0x222b, 0x0, + 0x222b, 0x222b, 0x222b, 0x0, 0x222b, 0x222b, 0x222b, 0x222b, + 0x0, 0x222e, 0x222e, 0x0, 0x222e, 0x222e, 0x222e, 0x0, 0x223c, + 0x338, 0x0, 0x2243, 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, + 0x338, 0x0, 0x224d, 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, + 0x338, 0x0, 0x2265, 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, + 0x338, 0x0, 0x2276, 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, + 0x338, 0x0, 0x227b, 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, + 0x338, 0x0, 0x2282, 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, + 0x338, 0x0, 0x2287, 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, + 0x338, 0x0, 0x22a2, 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, + 0x338, 0x0, 0x22ab, 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, + 0x338, 0x0, 0x22b4, 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2502, + 0x0, 0x25a0, 0x0, 0x25cb, 0x0, 0x2985, 0x0, 0x2986, 0x0, + 0x2add, 0x338, 0x0, 0x2d61, 0x0, 0x3001, 0x0, 0x3002, 0x0, + 0x3008, 0x0, 0x3009, 0x0, 0x300a, 0x0, 0x300b, 0x0, 0x300c, + 0x0, 0x300d, 0x0, 0x300e, 0x0, 0x300f, 0x0, 0x3010, 0x0, + 0x3011, 0x0, 0x3012, 0x0, 0x3014, 0x0, 0x3014, 0x53, 0x3015, + 0x0, 0x3014, 0x4e09, 0x3015, 0x0, 0x3014, 0x4e8c, 0x3015, 0x0, + 0x3014, 0x52dd, 0x3015, 0x0, 0x3014, 0x5b89, 0x3015, 0x0, + 0x3014, 0x6253, 0x3015, 0x0, 0x3014, 0x6557, 0x3015, 0x0, + 0x3014, 0x672c, 0x3015, 0x0, 0x3014, 0x70b9, 0x3015, 0x0, + 0x3014, 0x76d7, 0x3015, 0x0, 0x3015, 0x0, 0x3016, 0x0, 0x3017, + 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, 0x304d, 0x3099, + 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, 0x3053, 0x3099, + 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, 0x3059, 0x3099, + 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, 0x305f, 0x3099, + 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, 0x3066, 0x3099, + 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, 0x306f, 0x309a, + 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, 0x3075, 0x3099, + 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, 0x3078, 0x309a, + 0x0, 0x307b, 0x304b, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, + 0x0, 0x3088, 0x308a, 0x0, 0x3099, 0x0, 0x309a, 0x0, 0x309d, + 0x3099, 0x0, 0x30a1, 0x0, 0x30a2, 0x0, 0x30a2, 0x30cf, 0x309a, + 0x30fc, 0x30c8, 0x0, 0x30a2, 0x30eb, 0x30d5, 0x30a1, 0x0, + 0x30a2, 0x30f3, 0x30d8, 0x309a, 0x30a2, 0x0, 0x30a2, 0x30fc, + 0x30eb, 0x0, 0x30a3, 0x0, 0x30a4, 0x0, 0x30a4, 0x30cb, 0x30f3, + 0x30af, 0x3099, 0x0, 0x30a4, 0x30f3, 0x30c1, 0x0, 0x30a5, 0x0, + 0x30a6, 0x0, 0x30a6, 0x3099, 0x0, 0x30a6, 0x30a9, 0x30f3, 0x0, + 0x30a7, 0x0, 0x30a8, 0x0, 0x30a8, 0x30b9, 0x30af, 0x30fc, + 0x30c8, 0x3099, 0x0, 0x30a8, 0x30fc, 0x30ab, 0x30fc, 0x0, + 0x30a9, 0x0, 0x30aa, 0x0, 0x30aa, 0x30f3, 0x30b9, 0x0, 0x30aa, + 0x30fc, 0x30e0, 0x0, 0x30ab, 0x0, 0x30ab, 0x3099, 0x0, 0x30ab, + 0x3099, 0x30ed, 0x30f3, 0x0, 0x30ab, 0x3099, 0x30f3, 0x30de, + 0x0, 0x30ab, 0x30a4, 0x30ea, 0x0, 0x30ab, 0x30e9, 0x30c3, + 0x30c8, 0x0, 0x30ab, 0x30ed, 0x30ea, 0x30fc, 0x0, 0x30ad, 0x0, + 0x30ad, 0x3099, 0x0, 0x30ad, 0x3099, 0x30ab, 0x3099, 0x0, + 0x30ad, 0x3099, 0x30cb, 0x30fc, 0x0, 0x30ad, 0x3099, 0x30eb, + 0x30bf, 0x3099, 0x30fc, 0x0, 0x30ad, 0x30e5, 0x30ea, 0x30fc, + 0x0, 0x30ad, 0x30ed, 0x0, 0x30ad, 0x30ed, 0x30af, 0x3099, + 0x30e9, 0x30e0, 0x0, 0x30ad, 0x30ed, 0x30e1, 0x30fc, 0x30c8, + 0x30eb, 0x0, 0x30ad, 0x30ed, 0x30ef, 0x30c3, 0x30c8, 0x0, + 0x30af, 0x0, 0x30af, 0x3099, 0x0, 0x30af, 0x3099, 0x30e9, + 0x30e0, 0x0, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x30c8, 0x30f3, + 0x0, 0x30af, 0x30eb, 0x30bb, 0x3099, 0x30a4, 0x30ed, 0x0, + 0x30af, 0x30ed, 0x30fc, 0x30cd, 0x0, 0x30b1, 0x0, 0x30b1, + 0x3099, 0x0, 0x30b1, 0x30fc, 0x30b9, 0x0, 0x30b3, 0x0, 0x30b3, + 0x3099, 0x0, 0x30b3, 0x30b3, 0x0, 0x30b3, 0x30c8, 0x0, 0x30b3, + 0x30eb, 0x30ca, 0x0, 0x30b3, 0x30fc, 0x30db, 0x309a, 0x0, + 0x30b5, 0x0, 0x30b5, 0x3099, 0x0, 0x30b5, 0x30a4, 0x30af, + 0x30eb, 0x0, 0x30b5, 0x30f3, 0x30c1, 0x30fc, 0x30e0, 0x0, + 0x30b7, 0x0, 0x30b7, 0x3099, 0x0, 0x30b7, 0x30ea, 0x30f3, + 0x30af, 0x3099, 0x0, 0x30b9, 0x0, 0x30b9, 0x3099, 0x0, 0x30bb, + 0x0, 0x30bb, 0x3099, 0x0, 0x30bb, 0x30f3, 0x30c1, 0x0, 0x30bb, + 0x30f3, 0x30c8, 0x0, 0x30bd, 0x0, 0x30bd, 0x3099, 0x0, 0x30bf, + 0x0, 0x30bf, 0x3099, 0x0, 0x30bf, 0x3099, 0x30fc, 0x30b9, 0x0, + 0x30c1, 0x0, 0x30c1, 0x3099, 0x0, 0x30c3, 0x0, 0x30c4, 0x0, + 0x30c4, 0x3099, 0x0, 0x30c6, 0x0, 0x30c6, 0x3099, 0x0, 0x30c6, + 0x3099, 0x30b7, 0x0, 0x30c8, 0x0, 0x30c8, 0x3099, 0x0, 0x30c8, + 0x3099, 0x30eb, 0x0, 0x30c8, 0x30f3, 0x0, 0x30ca, 0x0, 0x30ca, + 0x30ce, 0x0, 0x30cb, 0x0, 0x30cc, 0x0, 0x30cd, 0x0, 0x30ce, + 0x0, 0x30ce, 0x30c3, 0x30c8, 0x0, 0x30cf, 0x0, 0x30cf, 0x3099, + 0x0, 0x30cf, 0x3099, 0x30fc, 0x30ec, 0x30eb, 0x0, 0x30cf, + 0x309a, 0x0, 0x30cf, 0x309a, 0x30fc, 0x30bb, 0x30f3, 0x30c8, + 0x0, 0x30cf, 0x309a, 0x30fc, 0x30c4, 0x0, 0x30cf, 0x30a4, + 0x30c4, 0x0, 0x30d2, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x3099, + 0x30eb, 0x0, 0x30d2, 0x309a, 0x0, 0x30d2, 0x309a, 0x30a2, + 0x30b9, 0x30c8, 0x30eb, 0x0, 0x30d2, 0x309a, 0x30af, 0x30eb, + 0x0, 0x30d2, 0x309a, 0x30b3, 0x0, 0x30d5, 0x0, 0x30d5, 0x3099, + 0x0, 0x30d5, 0x3099, 0x30c3, 0x30b7, 0x30a7, 0x30eb, 0x0, + 0x30d5, 0x309a, 0x0, 0x30d5, 0x30a1, 0x30e9, 0x30c3, 0x30c8, + 0x3099, 0x0, 0x30d5, 0x30a3, 0x30fc, 0x30c8, 0x0, 0x30d5, + 0x30e9, 0x30f3, 0x0, 0x30d8, 0x0, 0x30d8, 0x3099, 0x0, 0x30d8, + 0x3099, 0x30fc, 0x30bf, 0x0, 0x30d8, 0x309a, 0x0, 0x30d8, + 0x309a, 0x30bd, 0x0, 0x30d8, 0x309a, 0x30cb, 0x30d2, 0x0, + 0x30d8, 0x309a, 0x30f3, 0x30b9, 0x0, 0x30d8, 0x309a, 0x30fc, + 0x30b7, 0x3099, 0x0, 0x30d8, 0x30af, 0x30bf, 0x30fc, 0x30eb, + 0x0, 0x30d8, 0x30eb, 0x30c4, 0x0, 0x30db, 0x0, 0x30db, 0x3099, + 0x0, 0x30db, 0x3099, 0x30eb, 0x30c8, 0x0, 0x30db, 0x309a, 0x0, + 0x30db, 0x309a, 0x30a4, 0x30f3, 0x30c8, 0x0, 0x30db, 0x309a, + 0x30f3, 0x30c8, 0x3099, 0x0, 0x30db, 0x30f3, 0x0, 0x30db, + 0x30fc, 0x30eb, 0x0, 0x30db, 0x30fc, 0x30f3, 0x0, 0x30de, 0x0, + 0x30de, 0x30a4, 0x30af, 0x30ed, 0x0, 0x30de, 0x30a4, 0x30eb, + 0x0, 0x30de, 0x30c3, 0x30cf, 0x0, 0x30de, 0x30eb, 0x30af, 0x0, + 0x30de, 0x30f3, 0x30b7, 0x30e7, 0x30f3, 0x0, 0x30df, 0x0, + 0x30df, 0x30af, 0x30ed, 0x30f3, 0x0, 0x30df, 0x30ea, 0x0, + 0x30df, 0x30ea, 0x30cf, 0x3099, 0x30fc, 0x30eb, 0x0, 0x30e0, + 0x0, 0x30e1, 0x0, 0x30e1, 0x30ab, 0x3099, 0x0, 0x30e1, 0x30ab, + 0x3099, 0x30c8, 0x30f3, 0x0, 0x30e1, 0x30fc, 0x30c8, 0x30eb, + 0x0, 0x30e2, 0x0, 0x30e3, 0x0, 0x30e4, 0x0, 0x30e4, 0x30fc, + 0x30c8, 0x3099, 0x0, 0x30e4, 0x30fc, 0x30eb, 0x0, 0x30e5, 0x0, + 0x30e6, 0x0, 0x30e6, 0x30a2, 0x30f3, 0x0, 0x30e7, 0x0, 0x30e8, + 0x0, 0x30e9, 0x0, 0x30ea, 0x0, 0x30ea, 0x30c3, 0x30c8, 0x30eb, + 0x0, 0x30ea, 0x30e9, 0x0, 0x30eb, 0x0, 0x30eb, 0x30d2, 0x309a, + 0x30fc, 0x0, 0x30eb, 0x30fc, 0x30d5, 0x3099, 0x30eb, 0x0, + 0x30ec, 0x0, 0x30ec, 0x30e0, 0x0, 0x30ec, 0x30f3, 0x30c8, + 0x30b1, 0x3099, 0x30f3, 0x0, 0x30ed, 0x0, 0x30ef, 0x0, 0x30ef, + 0x3099, 0x0, 0x30ef, 0x30c3, 0x30c8, 0x0, 0x30f0, 0x0, 0x30f0, + 0x3099, 0x0, 0x30f1, 0x0, 0x30f1, 0x3099, 0x0, 0x30f2, 0x0, + 0x30f2, 0x3099, 0x0, 0x30f3, 0x0, 0x30fb, 0x0, 0x30fc, 0x0, + 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, 0x0, 0x34bb, 0x0, + 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, 0x36fc, 0x0, 0x3781, + 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, 0x0, 0x38c7, 0x0, + 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, 0x3a2e, 0x0, 0x3a6c, + 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, 0x0, 0x3b49, 0x0, + 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, 0x3d33, 0x0, 0x3d96, + 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, 0x0, 0x3ffc, 0x0, + 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, 0x4046, 0x0, 0x4096, + 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, 0x0, 0x4227, 0x0, + 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, 0x4359, 0x0, 0x43d5, + 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, 0x0, 0x452b, 0x0, + 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, 0x45d7, 0x0, 0x45f9, + 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, 0x0, 0x4995, 0x0, + 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, 0x4ab2, 0x0, 0x4b33, + 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, 0x0, 0x4cf8, 0x0, + 0x4d56, 0x0, 0x4e00, 0x0, 0x4e01, 0x0, 0x4e03, 0x0, 0x4e09, + 0x0, 0x4e0a, 0x0, 0x4e0b, 0x0, 0x4e0d, 0x0, 0x4e19, 0x0, + 0x4e26, 0x0, 0x4e28, 0x0, 0x4e2d, 0x0, 0x4e32, 0x0, 0x4e36, + 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e3f, 0x0, + 0x4e41, 0x0, 0x4e59, 0x0, 0x4e5d, 0x0, 0x4e82, 0x0, 0x4e85, + 0x0, 0x4e86, 0x0, 0x4e8c, 0x0, 0x4e94, 0x0, 0x4ea0, 0x0, + 0x4ea4, 0x0, 0x4eae, 0x0, 0x4eba, 0x0, 0x4ec0, 0x0, 0x4ecc, + 0x0, 0x4ee4, 0x0, 0x4f01, 0x0, 0x4f11, 0x0, 0x4f60, 0x0, + 0x4f80, 0x0, 0x4f86, 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, + 0x0, 0x4fbf, 0x0, 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, + 0x5099, 0x0, 0x50cf, 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x512a, + 0x0, 0x513f, 0x0, 0x5140, 0x0, 0x5145, 0x0, 0x514d, 0x0, + 0x5154, 0x0, 0x5164, 0x0, 0x5165, 0x0, 0x5167, 0x0, 0x5168, + 0x0, 0x5169, 0x0, 0x516b, 0x0, 0x516d, 0x0, 0x5177, 0x0, + 0x5180, 0x0, 0x5182, 0x0, 0x518d, 0x0, 0x5192, 0x0, 0x5195, + 0x0, 0x5196, 0x0, 0x5197, 0x0, 0x5199, 0x0, 0x51a4, 0x0, + 0x51ab, 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, + 0x0, 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51e0, 0x0, + 0x51f5, 0x0, 0x5200, 0x0, 0x5203, 0x0, 0x5207, 0x0, 0x5217, + 0x0, 0x521d, 0x0, 0x5229, 0x0, 0x523a, 0x0, 0x523b, 0x0, + 0x5246, 0x0, 0x524d, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, + 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52b4, 0x0, + 0x52c7, 0x0, 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, + 0x0, 0x52f5, 0x0, 0x52f9, 0x0, 0x52fa, 0x0, 0x5305, 0x0, + 0x5306, 0x0, 0x5315, 0x0, 0x5317, 0x0, 0x531a, 0x0, 0x5338, + 0x0, 0x533b, 0x0, 0x533f, 0x0, 0x5341, 0x0, 0x5344, 0x0, + 0x5345, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x5354, 0x0, 0x535a, + 0x0, 0x535c, 0x0, 0x5369, 0x0, 0x5370, 0x0, 0x5373, 0x0, + 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x5382, 0x0, 0x53b6, + 0x0, 0x53c3, 0x0, 0x53c8, 0x0, 0x53ca, 0x0, 0x53cc, 0x0, + 0x53df, 0x0, 0x53e3, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53ef, + 0x0, 0x53f1, 0x0, 0x53f3, 0x0, 0x5406, 0x0, 0x5408, 0x0, + 0x540d, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, 0x5439, + 0x0, 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, + 0x54a2, 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x554f, + 0x0, 0x5553, 0x0, 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, + 0x5587, 0x0, 0x5599, 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, + 0x0, 0x55b6, 0x0, 0x55c0, 0x0, 0x55c2, 0x0, 0x55e2, 0x0, + 0x5606, 0x0, 0x5651, 0x0, 0x5668, 0x0, 0x5674, 0x0, 0x56d7, + 0x0, 0x56db, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, + 0x571f, 0x0, 0x5730, 0x0, 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, + 0x0, 0x580d, 0x0, 0x5831, 0x0, 0x5832, 0x0, 0x5840, 0x0, + 0x585a, 0x0, 0x585e, 0x0, 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, + 0x0, 0x58d8, 0x0, 0x58df, 0x0, 0x58eb, 0x0, 0x58ee, 0x0, + 0x58f0, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5902, 0x0, 0x5906, + 0x0, 0x590a, 0x0, 0x5915, 0x0, 0x591a, 0x0, 0x591c, 0x0, + 0x5922, 0x0, 0x5927, 0x0, 0x5927, 0x6b63, 0x0, 0x5929, 0x0, + 0x5944, 0x0, 0x5948, 0x0, 0x5951, 0x0, 0x5954, 0x0, 0x5962, + 0x0, 0x5973, 0x0, 0x59d8, 0x0, 0x59ec, 0x0, 0x5a1b, 0x0, + 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, 0x0, 0x5ab5, 0x0, 0x5b08, + 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, 0x5b50, 0x0, 0x5b57, 0x0, + 0x5b66, 0x0, 0x5b80, 0x0, 0x5b85, 0x0, 0x5b97, 0x0, 0x5bc3, + 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, 0x0, 0x5bf3, 0x0, + 0x5bf8, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c0f, 0x0, 0x5c22, + 0x0, 0x5c38, 0x0, 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, + 0x5c64, 0x0, 0x5c65, 0x0, 0x5c6e, 0x0, 0x5c71, 0x0, 0x5c8d, + 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, 0x5d43, 0x0, 0x5d50, 0x0, + 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, 0x0, 0x5db2, 0x0, 0x5dba, + 0x0, 0x5ddb, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, 0x5de5, 0x0, + 0x5de6, 0x0, 0x5df1, 0x0, 0x5dfd, 0x0, 0x5dfe, 0x0, 0x5e28, + 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e72, 0x0, 0x5e73, 0x6210, + 0x0, 0x5e74, 0x0, 0x5e7a, 0x0, 0x5e7c, 0x0, 0x5e7f, 0x0, + 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, 0x5ec9, + 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, 0x0, + 0x5eec, 0x0, 0x5ef4, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f0b, + 0x0, 0x5f13, 0x0, 0x5f22, 0x0, 0x5f50, 0x0, 0x5f53, 0x0, + 0x5f61, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f73, + 0x0, 0x5f8b, 0x0, 0x5f8c, 0x0, 0x5f97, 0x0, 0x5f9a, 0x0, + 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fc3, 0x0, 0x5fcd, 0x0, 0x5fd7, + 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, 0x0, + 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, 0x60d8, + 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, 0x0, + 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, 0x617a, + 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, 0x0, + 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, 0x6200, + 0x0, 0x6208, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, + 0x6234, 0x0, 0x6236, 0x0, 0x624b, 0x0, 0x6253, 0x0, 0x625d, + 0x0, 0x6295, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, + 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x6307, + 0x0, 0x633d, 0x0, 0x6350, 0x0, 0x6355, 0x0, 0x6368, 0x0, + 0x637b, 0x0, 0x6383, 0x0, 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, + 0x0, 0x63c5, 0x0, 0x63e4, 0x0, 0x641c, 0x0, 0x6422, 0x0, + 0x6452, 0x0, 0x6469, 0x0, 0x6477, 0x0, 0x647e, 0x0, 0x649a, + 0x0, 0x649d, 0x0, 0x64c4, 0x0, 0x652f, 0x0, 0x6534, 0x0, + 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, 0x6587, + 0x0, 0x6597, 0x0, 0x6599, 0x0, 0x65a4, 0x0, 0x65b0, 0x0, + 0x65b9, 0x0, 0x65c5, 0x0, 0x65e0, 0x0, 0x65e2, 0x0, 0x65e3, + 0x0, 0x65e5, 0x0, 0x660e, 0x6cbb, 0x0, 0x6613, 0x0, 0x6620, + 0x0, 0x662d, 0x548c, 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, + 0x0, 0x6691, 0x0, 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, + 0x66f0, 0x0, 0x66f4, 0x0, 0x66f8, 0x0, 0x6700, 0x0, 0x6708, + 0x0, 0x6709, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, + 0x6728, 0x0, 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, + 0x0, 0x677b, 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, + 0x67fa, 0x0, 0x6817, 0x0, 0x681f, 0x0, 0x682a, 0x0, 0x682a, + 0x5f0f, 0x4f1a, 0x793e, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, + 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, + 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, + 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b20, 0x0, + 0x6b21, 0x0, 0x6b54, 0x0, 0x6b62, 0x0, 0x6b63, 0x0, 0x6b72, + 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, 0x0, 0x6bae, 0x0, + 0x6bb3, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6bcb, 0x0, 0x6bcd, + 0x0, 0x6bd4, 0x0, 0x6bdb, 0x0, 0x6c0f, 0x0, 0x6c14, 0x0, + 0x6c34, 0x0, 0x6c4e, 0x0, 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, + 0x0, 0x6ccc, 0x0, 0x6ccd, 0x0, 0x6ce5, 0x0, 0x6ce8, 0x0, + 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, 0x6d34, 0x0, 0x6d3e, + 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, 0x0, 0x6d77, 0x0, + 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, 0x6dda, 0x0, 0x6dea, + 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, 0x0, 0x6e6e, 0x0, + 0x6e80, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, 0x6ecb, + 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f14, 0x0, + 0x6f22, 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, + 0x0, 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, + 0x704a, 0x0, 0x706b, 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, + 0x0, 0x7099, 0x0, 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, + 0x7121, 0x0, 0x7145, 0x0, 0x7149, 0x0, 0x716e, 0x0, 0x719c, + 0x0, 0x71ce, 0x0, 0x71d0, 0x0, 0x7210, 0x0, 0x721b, 0x0, + 0x7228, 0x0, 0x722a, 0x0, 0x722b, 0x0, 0x7235, 0x0, 0x7236, + 0x0, 0x723b, 0x0, 0x723f, 0x0, 0x7247, 0x0, 0x7250, 0x0, + 0x7259, 0x0, 0x725b, 0x0, 0x7262, 0x0, 0x7279, 0x0, 0x7280, + 0x0, 0x7295, 0x0, 0x72ac, 0x0, 0x72af, 0x0, 0x72c0, 0x0, + 0x72fc, 0x0, 0x732a, 0x0, 0x7375, 0x0, 0x737a, 0x0, 0x7384, + 0x0, 0x7387, 0x0, 0x7389, 0x0, 0x738b, 0x0, 0x73a5, 0x0, + 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, + 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, + 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x74dc, + 0x0, 0x74e6, 0x0, 0x7506, 0x0, 0x7518, 0x0, 0x751f, 0x0, + 0x7524, 0x0, 0x7528, 0x0, 0x7530, 0x0, 0x7532, 0x0, 0x7533, + 0x0, 0x7537, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, + 0x7565, 0x0, 0x7570, 0x0, 0x758b, 0x0, 0x7592, 0x0, 0x75e2, + 0x0, 0x7610, 0x0, 0x761d, 0x0, 0x761f, 0x0, 0x7642, 0x0, + 0x7669, 0x0, 0x7676, 0x0, 0x767d, 0x0, 0x76ae, 0x0, 0x76bf, + 0x0, 0x76ca, 0x0, 0x76db, 0x0, 0x76e3, 0x0, 0x76e7, 0x0, + 0x76ee, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, 0x0, 0x771f, + 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, 0x77a7, 0x0, + 0x77db, 0x0, 0x77e2, 0x0, 0x77f3, 0x0, 0x784e, 0x0, 0x786b, + 0x0, 0x788c, 0x0, 0x7891, 0x0, 0x78ca, 0x0, 0x78cc, 0x0, + 0x78fb, 0x0, 0x792a, 0x0, 0x793a, 0x0, 0x793c, 0x0, 0x793e, + 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, 0x0, 0x7956, 0x0, + 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, 0x797f, 0x0, 0x7981, + 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, 0x0, + 0x79b8, 0x0, 0x79be, 0x0, 0x79ca, 0x0, 0x79d8, 0x0, 0x79eb, + 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, 0x7a4a, 0x0, 0x7a4f, 0x0, + 0x7a74, 0x0, 0x7a7a, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, + 0x0, 0x7aee, 0x0, 0x7af9, 0x0, 0x7b20, 0x0, 0x7b8f, 0x0, + 0x7bc0, 0x0, 0x7bc6, 0x0, 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, + 0x0, 0x7c73, 0x0, 0x7c7b, 0x0, 0x7c92, 0x0, 0x7cbe, 0x0, + 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, 0x7ce7, 0x0, 0x7ce8, + 0x0, 0x7cf8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, 0x0, + 0x7d2f, 0x0, 0x7d42, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, + 0x0, 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, + 0x7e09, 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f36, + 0x0, 0x7f3e, 0x0, 0x7f51, 0x0, 0x7f72, 0x0, 0x7f79, 0x0, + 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f8a, 0x0, 0x7f95, 0x0, 0x7f9a, + 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, 0x8005, 0x0, + 0x800c, 0x0, 0x8012, 0x0, 0x8033, 0x0, 0x8046, 0x0, 0x8060, + 0x0, 0x806f, 0x0, 0x8070, 0x0, 0x807e, 0x0, 0x807f, 0x0, + 0x8089, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, 0x8103, + 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e3, 0x0, 0x81e8, 0x0, + 0x81ea, 0x0, 0x81ed, 0x0, 0x81f3, 0x0, 0x81fc, 0x0, 0x8201, + 0x0, 0x8204, 0x0, 0x820c, 0x0, 0x8218, 0x0, 0x821b, 0x0, + 0x821f, 0x0, 0x826e, 0x0, 0x826f, 0x0, 0x8272, 0x0, 0x8278, + 0x0, 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, + 0x82b1, 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, + 0x0, 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, + 0x8353, 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, + 0x0, 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, + 0x83ef, 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, + 0x0, 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, + 0x8516, 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, + 0x0, 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x864d, 0x0, + 0x8650, 0x0, 0x865c, 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x866b, + 0x0, 0x8688, 0x0, 0x86a9, 0x0, 0x86e2, 0x0, 0x870e, 0x0, + 0x8728, 0x0, 0x876b, 0x0, 0x8779, 0x0, 0x8786, 0x0, 0x87ba, + 0x0, 0x87e1, 0x0, 0x8801, 0x0, 0x881f, 0x0, 0x8840, 0x0, + 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, 0x0, 0x88cf, + 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, 0x88f8, 0x0, + 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, 0x0, 0x897e, + 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x89d2, 0x0, + 0x89e3, 0x0, 0x8a00, 0x0, 0x8aa0, 0x0, 0x8aaa, 0x0, 0x8abf, + 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, 0x0, 0x8aed, 0x0, + 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, 0x8b39, 0x0, 0x8b58, + 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c37, 0x0, 0x8c46, 0x0, + 0x8c48, 0x0, 0x8c55, 0x0, 0x8c78, 0x0, 0x8c9d, 0x0, 0x8ca1, + 0x0, 0x8ca9, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, + 0x8cc7, 0x0, 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, + 0x0, 0x8d64, 0x0, 0x8d70, 0x0, 0x8d77, 0x0, 0x8db3, 0x0, + 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, 0x8eab, + 0x0, 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, + 0x8f38, 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9b, 0x0, 0x8f9e, + 0x0, 0x8fb0, 0x0, 0x8fb5, 0x0, 0x8fb6, 0x0, 0x9023, 0x0, + 0x9038, 0x0, 0x904a, 0x0, 0x9069, 0x0, 0x9072, 0x0, 0x907c, + 0x0, 0x908f, 0x0, 0x9091, 0x0, 0x9094, 0x0, 0x90ce, 0x0, + 0x90de, 0x0, 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, + 0x0, 0x9149, 0x0, 0x916a, 0x0, 0x9199, 0x0, 0x91b4, 0x0, + 0x91c6, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, 0x91d1, 0x0, 0x9234, + 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, 0x0, 0x92d7, 0x0, + 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, 0x93f9, 0x0, 0x9415, + 0x0, 0x9577, 0x0, 0x9580, 0x0, 0x958b, 0x0, 0x95ad, 0x0, + 0x95b7, 0x0, 0x961c, 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, + 0x0, 0x9675, 0x0, 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, + 0x96a3, 0x0, 0x96b6, 0x0, 0x96b7, 0x0, 0x96b8, 0x0, 0x96b9, + 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, 0x96e8, 0x0, + 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, + 0x0, 0x9751, 0x0, 0x9756, 0x0, 0x975e, 0x0, 0x9762, 0x0, + 0x9769, 0x0, 0x97cb, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ed, + 0x0, 0x97f3, 0x0, 0x97ff, 0x0, 0x9801, 0x0, 0x9805, 0x0, + 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, + 0x0, 0x98a8, 0x0, 0x98db, 0x0, 0x98df, 0x0, 0x98e2, 0x0, + 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, 0x9929, 0x0, 0x9996, + 0x0, 0x9999, 0x0, 0x99a7, 0x0, 0x99ac, 0x0, 0x99c2, 0x0, + 0x99f1, 0x0, 0x99fe, 0x0, 0x9a6a, 0x0, 0x9aa8, 0x0, 0x9ad8, + 0x0, 0x9adf, 0x0, 0x9b12, 0x0, 0x9b25, 0x0, 0x9b2f, 0x0, + 0x9b32, 0x0, 0x9b3c, 0x0, 0x9b5a, 0x0, 0x9b6f, 0x0, 0x9c40, + 0x0, 0x9c57, 0x0, 0x9ce5, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, + 0x9db4, 0x0, 0x9dfa, 0x0, 0x9e1e, 0x0, 0x9e75, 0x0, 0x9e7f, + 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, 0x9ea5, 0x0, 0x9ebb, 0x0, + 0x9ec3, 0x0, 0x9ecd, 0x0, 0x9ece, 0x0, 0x9ed1, 0x0, 0x9ef9, + 0x0, 0x9efd, 0x0, 0x9efe, 0x0, 0x9f05, 0x0, 0x9f0e, 0x0, + 0x9f0f, 0x0, 0x9f13, 0x0, 0x9f16, 0x0, 0x9f20, 0x0, 0x9f3b, + 0x0, 0x9f43, 0x0, 0x9f4a, 0x0, 0x9f52, 0x0, 0x9f8d, 0x0, + 0x9f8e, 0x0, 0x9f9c, 0x0, 0x9f9f, 0x0, 0x9fa0, 0x0, 0xa76f, + 0x0, 0x11099, 0x110ba, 0x0, 0x1109b, 0x110ba, 0x0, 0x110a5, + 0x110ba, 0x0, 0x11131, 0x11127, 0x0, 0x11132, 0x11127, 0x0, + 0x1d157, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x0, 0x1d158, 0x1d165, + 0x1d16e, 0x0, 0x1d158, 0x1d165, 0x1d16f, 0x0, 0x1d158, 0x1d165, + 0x1d170, 0x0, 0x1d158, 0x1d165, 0x1d171, 0x0, 0x1d158, 0x1d165, + 0x1d172, 0x0, 0x1d1b9, 0x1d165, 0x0, 0x1d1b9, 0x1d165, 0x1d16e, + 0x0, 0x1d1b9, 0x1d165, 0x1d16f, 0x0, 0x1d1ba, 0x1d165, 0x0, + 0x1d1ba, 0x1d165, 0x1d16e, 0x0, 0x1d1ba, 0x1d165, 0x1d16f, 0x0, + 0x20122, 0x0, 0x2051c, 0x0, 0x20525, 0x0, 0x2054b, 0x0, + 0x2063a, 0x0, 0x20804, 0x0, 0x208de, 0x0, 0x20a2c, 0x0, + 0x20b63, 0x0, 0x214e4, 0x0, 0x216a8, 0x0, 0x216ea, 0x0, + 0x219c8, 0x0, 0x21b18, 0x0, 0x21d0b, 0x0, 0x21de4, 0x0, + 0x21de6, 0x0, 0x22183, 0x0, 0x2219f, 0x0, 0x22331, 0x0, + 0x226d4, 0x0, 0x22844, 0x0, 0x2284a, 0x0, 0x22b0c, 0x0, + 0x22bf1, 0x0, 0x2300a, 0x0, 0x232b8, 0x0, 0x2335f, 0x0, + 0x23393, 0x0, 0x2339c, 0x0, 0x233c3, 0x0, 0x233d5, 0x0, + 0x2346d, 0x0, 0x236a3, 0x0, 0x238a7, 0x0, 0x23a8d, 0x0, + 0x23afa, 0x0, 0x23cbc, 0x0, 0x23d1e, 0x0, 0x23ed1, 0x0, + 0x23f5e, 0x0, 0x23f8e, 0x0, 0x24263, 0x0, 0x242ee, 0x0, + 0x243ab, 0x0, 0x24608, 0x0, 0x24735, 0x0, 0x24814, 0x0, + 0x24c36, 0x0, 0x24c92, 0x0, 0x24fa1, 0x0, 0x24fb8, 0x0, + 0x25044, 0x0, 0x250f2, 0x0, 0x250f3, 0x0, 0x25119, 0x0, + 0x25133, 0x0, 0x25249, 0x0, 0x2541d, 0x0, 0x25626, 0x0, + 0x2569a, 0x0, 0x256c5, 0x0, 0x2597c, 0x0, 0x25aa7, 0x0, + 0x25bab, 0x0, 0x25c80, 0x0, 0x25cd0, 0x0, 0x25f86, 0x0, + 0x261da, 0x0, 0x26228, 0x0, 0x26247, 0x0, 0x262d9, 0x0, + 0x2633e, 0x0, 0x264da, 0x0, 0x26523, 0x0, 0x265a8, 0x0, + 0x267a7, 0x0, 0x267b5, 0x0, 0x26b3c, 0x0, 0x26c36, 0x0, + 0x26cd5, 0x0, 0x26d6b, 0x0, 0x26f2c, 0x0, 0x26fb1, 0x0, + 0x270d2, 0x0, 0x273ca, 0x0, 0x27667, 0x0, 0x278ae, 0x0, + 0x27966, 0x0, 0x27ca8, 0x0, 0x27ed3, 0x0, 0x27f2f, 0x0, + 0x285d2, 0x0, 0x285ed, 0x0, 0x2872e, 0x0, 0x28bfa, 0x0, + 0x28d77, 0x0, 0x29145, 0x0, 0x291df, 0x0, 0x2921a, 0x0, + 0x2940a, 0x0, 0x29496, 0x0, 0x295b6, 0x0, 0x29b30, 0x0, + 0x2a0ce, 0x0, 0x2a105, 0x0, 0x2a20e, 0x0, 0x2a291, 0x0, + 0x2a392, 0x0, 0x2a600, 0x0 + ]; + return t; + } + } +} -static if(size_t.sizeof == 4) { -//22656 bytes -enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)([ 0x0, 0x40, 0x540], [ 0x100, 0xa00, 0x21c0], [ 0x2020100, 0x4020302, 0x2020205, 0x7060202, 0x2020202, 0x8020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x80000, 0xa0009, 0xc000b, 0x0, 0xd0000, 0xf000e, 0x0, 0x110010, 0x130012, 0x150014, 0x170016, 0x190018, 0x0, 0x1b001a, 0x0, 0x0, 0x1c, 0x0, 0x1d0000, 0x1e0000, 0x0, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x21, 0x0, 0x22, 0x230000, 0x24, 0x0, 0x0, 0x0, 0x25, 0x26, 0x27, 0x0, 0x28, 0x0, 0x29, 0x0, 0x2a, 0x0, 0x2b, 0x2c0000, 0x0, 0x2d0000, 0x2e, 0x2f, 0x310030, 0x330032, 0x0, 0x340000, 0x0, 0x0, 0x350000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x370036, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x390000, 0x3b003a, 0x3d003c, 0x0, 0x3f003e, 0x410040, 0x430042, 0x450044, 0x470046, 0x490048, 0x4b004a, 0x4d004c, 0x4f004e, 0x510050, 0x530052, 0x0, 0x550054, 0x570056, 0x590058, 0x5a, 0x5c005b, 0x5e005d, 0x60005f, 0x610000, 0x620000, 0x0, 0x0, 0x0, 0x0, 0x630000, 0x650064, 0x670066, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x690000, 0x0, 0x6a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6b0000, 0x0, 0x0, 0x0, 0x6c0000, 0x0, 0x0, 0x0, 0x0, 0x6d, 0x6e0000, 0x70006f, 0x720071, 0x740073, 0x75, 0x770076, 0x790078, 0x7b007a, 0x7d007c, 0x7e0000, 0x80007f, 0x81, 0x0, 0x830082, 0x850084, 0x870086, 0x890088, 0x8b008a, 0x8d008c, 0x8f008e, 0x910090, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x920000, 0x0, 0x930000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x950094, 0x970096, 0x990098, 0x9b009a, 0x9d009c, 0x9f009e, 0xa100a0, 0xa2, 0xa400a3, 0xa600a5, 0xa800a7, 0xaa00a9, 0xac00ab, 0xae00ad, 0xb000af, 0xb200b1, 0xb400b3, 0xb600b5, 0xb800b7, 0xba00b9, 0xbc00bb, 0xbe00bd, 0xc000bf, 0xc200c1, 0xc400c3, 0xc600c5, 0xc800c7, 0xca00c9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc00cb, 0x0, 0xcd0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf00ce, 0xd00000, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd300d2, 0xd500d4, 0xd700d6, 0xd900d8, 0xdb00da, 0xdd00dc, 0xd200de, 0xdf00d3, 0xe000d5, 0xe200e1, 0xe300d9, 0xe500e4, 0xe700e6, 0xe900e8, 0xeb00ea, 0xed00ec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef00ee, 0xf100f0, 0xf300f2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf500f4, 0xf700f6, 0xf8, 0x0, 0xfa00f9, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x15, 0x692, 0x0, 0x90000, 0x0, 0x30f0343, 0x11b20003, 0x0, 0x3140048, 0x787, 0x3c603ce, 0x494, 0x570056d, 0x5860573, 0x5b005a6, 0x5f80000, 0x62e062b, 0x6580631, 0x6e706e4, 0x6f906ea, 0x78f0000, 0x7a907a6, 0x7bf07ac, 0x7e3, 0x8b10000, 0x8b708b4, 0x95f08cb, 0x0, 0x9ac09a9, 0x9c209af, 0x9ec09e2, 0xa470000, 0xa890a86, 0xab30a8c, 0xb460b43, 0xb550b49, 0xc410000, 0xc5e0c5b, 0xc740c61, 0xc98, 0xd680000, 0xd6e0d6b, 0xe0c0d82, 0xe1b0000, 0x9c50589, 0x9c8058c, 0xa0a05ce, 0xa3b05ec, 0xa3e05ef, 0xa4105f2, 0xa4405f5, 0xa6e061a, 0x0, 0xaa20647, 0xaad0652, 0xab00655, 0xad00675, 0xab9065e, 0xafb069a, 0xb0106a0, 0xb0406a3, 0xb0a06a9, 0xb1606ba, 0x0, 0xb4c06ed, 0xb4f06f0, 0xb5206f3, 0xb6b070f, 0x6f6, 0xb3706d8, 0xb730717, 0xbae072e, 0x7430000, 0x7500bcc, 0x7460bd9, 0x7400bcf, 0xbc9, 0x78c0000, 0x79b0c3e, 0x7950c4d, 0xed70c47, 0x0, 0xc8307ce, 0xc8e07d9, 0xca207ed, 0x0, 0xd070842, 0xd1d0858, 0xd0d0848, 0xd2b086c, 0xd320873, 0xd49088a, 0xd380879, 0xd5d08a6, 0xd54089d, 0x0, 0xd7108ba, 0xd7808c1, 0xd7f08c8, 0xd9808e1, 0xd9b08e4, 0xdc4090d, 0xde9093f, 0xe0f0962, 0x979096e, 0x97f0e29, 0x60d0e2f, 0x8400614, 0xcae07f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f00000, 0xda7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x613060c, 0x7360a67, 0xbb9073d, 0x7830780, 0x5b70c32, 0x70309f3, 0x7f00b5f, 0x8e70ca5, 0x8d60d9e, 0x8d20d8d, 0x8da0d89, 0x8ce0d91, 0xd85, 0x9e505a9, 0x9de05a2, 0xe630e5a, 0x0, 0xb0706a6, 0xba80728, 0xccc0817, 0xccf081a, 0xecc0e7b, 0x6090b76, 0xa640610, 0xaf80697, 0x0, 0xc3b0789, 0x9ef05b3, 0xe600e57, 0xe680e5d, 0x9f605ba, 0x9f905bd, 0xabc0661, 0xabf0664, 0xb620706, 0xb650709, 0xca807f3, 0xcab07f6, 0xd10084b, 0xd13084e, 0xda108ea, 0xda408ed, 0xd460887, 0xd5a08a3, 0x0, 0xb1f06c3, 0x0, 0x0, 0x0, 0x9db059f, 0xac9066e, 0xc9b07e6, 0xc7b07c6, 0xc9107dc, 0xc9407df, 0xe150968, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9a0b0d, 0xa11073e, 0xeb60eb4, 0xde10eb8, 0x695, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12000f, 0x4b0024, 0x270006, 0x0, 0xa280e96, 0xb410840, 0xecf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4001a, 0x2b0000, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xed5, 0x0, 0x0, 0x54, 0x0, 0x546, 0x0, 0x0, 0x1c0003, 0x7410ee8, 0xf630f43, 0xfb4, 0xfed, 0x103c1016, 0x1185, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x101f0fbd, 0x10f5108f, 0x11751119, 0x1213, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120c117e, 0x120311d5, 0x124b, 0x116e10ea, 0x10161011, 0x123c101f, 0x11ee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11f011ae, 0x11f8, 0x10f00fad, 0x0, 0x100d0000, 0x0, 0x0, 0x0, 0x12b612b0, 0x12ad0000, 0x0, 0x12a40000, 0x0, 0x0, 0x12c212ce, 0x12d7, 0x0, 0x0, 0x0, 0x0, 0x12c80000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12f812f2, 0x12ef0000, 0x0, 0x132d0000, 0x0, 0x0, 0x13041310, 0x131b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13331330, 0x0, 0x0, 0x0, 0x0, 0x12b90000, 0x12fb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12e912a7, 0x12ec12aa, 0x0, 0x12f512b3, 0x0, 0x13391336, 0x12fe12bc, 0x130112bf, 0x0, 0x130712c5, 0x130d12cb, 0x131512d1, 0x0, 0x133f133c, 0x132a12e6, 0x131812d4, 0x131e12da, 0x132112dd, 0x132412e0, 0x0, 0x132712e3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13420000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13e913e6, 0x13ec178f, 0x17ca, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13ef0000, 0x185b1792, 0x1811, 0x0, 0x0, 0x0, 0x186d, 0x1852, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x186a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18820000, 0x0, 0x0, 0x0, 0x188b0000, 0x0, 0x188e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18731870, 0x18791876, 0x187f187c, 0x18881885, 0x0, 0x0, 0x0, 0x0, 0x0, 0x189a0000, 0x189d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18941891, 0x18970000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18ac0000, 0x0, 0x18af, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18a00000, 0x18a618a3, 0x0, 0x18a9, 0x0, 0x0, 0x0, 0x0, 0x18bb, 0x18b80000, 0x18be, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18b518b2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18c1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18ca18c4, 0x18c7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18cd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18d0, 0x0, 0x0, 0x18da0000, 0x18dd, 0x18d618d3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18e618e0, 0x18e3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18e9, 0x18ef18ec, 0x18f3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18f60000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18ff0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18fc18f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1902, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19070000, 0x0, 0x0, 0x0, 0x0, 0x190a0000, 0x0, 0x0, 0x190d, 0x0, 0x19100000, 0x0, 0x0, 0x1913, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19040000, 0x0, 0x0, 0x0, 0x0, 0x19160000, 0x19190000, 0x19311935, 0x1938193c, 0x0, 0x0, 0x0, 0x191c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19220000, 0x0, 0x0, 0x0, 0x0, 0x19250000, 0x0, 0x0, 0x1928, 0x0, 0x192b0000, 0x0, 0x0, 0x192e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x191f0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x193f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1942, 0x0, 0x0, 0x0, 0x0, 0x1a38, 0x1a3b, 0x1a3e, 0x1a41, 0x1a44, 0x0, 0x1a47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a4a0000, 0x1a4d0000, 0x0, 0x1a531a50, 0x1a560000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe550568, 0x5d5, 0x62905e6, 0x6870e75, 0x6cf06ac, 0x71a0607, 0x7230734, 0x77e, 0xe7e07a4, 0x82c06af, 0x56b088d, 0x6920770, 0xe840e82, 0x9371a59, 0xa7d0a2e, 0xe8e0e8c, 0x6020e90, 0xb790000, 0xe7105d3, 0xe880787, 0x1a5d1a5b, 0xba30cd3, 0x1a610a24, 0x86a0ea4, 0x10ea1a63, 0x10ee10ec, 0x123e123c, 0xa110ae0, 0x86a0a24, 0x10ec10ea, 0x123c11f0, 0x123e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1313, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe860000, 0xe8a09a0, 0xe900e66, 0xe920ad9, 0xe980e94, 0xe9e0e9c, 0x1a650ea0, 0xea20ed1, 0xed31a67, 0xea60ea8, 0xeac0eaa, 0xeb00eae, 0xeba0eb2, 0xe790ebc, 0xec00ebe, 0xec21a5f, 0x6110ec4, 0xec80ec6, 0x116e0eca, 0xa0705cb, 0xa1305da, 0xa1605dd, 0xa1905e0, 0xa4a05fb, 0xa6b0617, 0xa71061d, 0xa7a0626, 0xa740620, 0xa770623, 0xaa5064a, 0xaa9064e, 0xad30678, 0xad6067b, 0xacc0671, 0xaef0684, 0xafe069d, 0xb1906bd, 0xb2206c6, 0xb1c06c0, 0xb2506c9, 0xb2806cc, 0xb6e0712, 0xb5806fc, 0xba50725, 0xbab072b, 0xbb10731, 0xbd20749, 0xbd5074c, 0xbdf0756, 0xbdc0753, 0xc120772, 0xc150775, 0xc180778, 0xc440792, 0xc4a0798, 0xc5307a1, 0xc50079e, 0xc7707c2, 0xc7f07ca, 0xc8607d1, 0xc8a07d5, 0xcec0835, 0xcef0838, 0xd0a0845, 0xd160851, 0xd190854, 0xd20085b, 0xd350876, 0xd3f0880, 0xd2e086f, 0xd3b087c, 0xd420883, 0xd4e089a, 0xd5708a0, 0xd6308ac, 0xd6008a9, 0xdc1090a, 0xdca0913, 0xdc70910, 0xd7408bd, 0xd7b08c4, 0xddb0924, 0xdde0927, 0xde30939, 0xde6093c, 0xdef0945, 0xdec0942, 0xdf50948, 0xe010954, 0xe040957, 0xe18096b, 0xe2c097c, 0xe350985, 0xe380988, 0xd510b2b, 0xe210df2, 0xd3509a6, 0x0, 0x0, 0x9fc05c0, 0x9e905ad, 0x9b6057a, 0x9b20576, 0x9be0582, 0x9ba057e, 0x9ff05c3, 0x9cf0593, 0x9cb058f, 0x9d7059b, 0x9d30597, 0xa0305c7, 0xac20667, 0xab6065b, 0xa9f0644, 0xa930638, 0xa8f0634, 0xa9b0640, 0xa97063c, 0xac5066a, 0xb5c0700, 0xb68070c, 0xcc50810, 0xc9f07ea, 0xc6807b3, 0xc6407af, 0xc7007bb, 0xc6c07b7, 0xcc80813, 0xcb50800, 0xcb107fc, 0xcbd0808, 0xcb90804, 0xcc1080c, 0xdbe0907, 0xd9508de, 0xdae08f7, 0xdaa08f3, 0xdb608ff, 0xdb208fb, 0xdba0903, 0xe09095c, 0xe240974, 0xe1e0971, 0xe120965, 0x0, 0x0, 0x0, 0x10be109c, 0x10c1109f, 0x10ca10a8, 0x10d310b1, 0xf130ef1, 0xf160ef4, 0xf1f0efd, 0xf280f06, 0x110310f8, 0x110610fb, 0x110a10ff, 0x0, 0xf510f46, 0xf540f49, 0xf580f4d, 0x0, 0x11421120, 0x11451123, 0x114e112c, 0x11571135, 0xf880f66, 0xf8b0f69, 0xf940f72, 0xf9d0f7b, 0x119c118d, 0x119f1190, 0x11a31194, 0x11a71198, 0xfcf0fc0, 0xfd20fc3, 0xfd60fc7, 0xfda0fcb, 0x11e311d8, 0x11e611db, 0x11ea11df, 0x0, 0xffb0ff0, 0xffe0ff3, 0x10020ff7, 0x0, 0x122a121b, 0x122d121e, 0x12311222, 0x12351226, 0x10220000, 0x10250000, 0x10290000, 0x102d0000, 0x12741252, 0x12771255, 0x1280125e, 0x12891267, 0x1061103f, 0x10641042, 0x106d104b, 0x10761054, 0x108f1088, 0x10f510f2, 0x11191112, 0x11751172, 0x11d511d2, 0x12031200, 0x124b1244, 0x0, 0x10dc10ba, 0x10c510a3, 0x10ce10ac, 0x10d710b5, 0xf310f0f, 0xf1a0ef8, 0xf230f01, 0xf2c0f0a, 0x1160113e, 0x11491127, 0x11521130, 0x115b1139, 0xfa60f84, 0xf8f0f6d, 0xf980f76, 0xfa10f7f, 0x12921270, 0x127b1259, 0x12841262, 0x128d126b, 0x107f105d, 0x10681046, 0x1071104f, 0x107a1058, 0x10961099, 0x10e7108b, 0x1092, 0x10e310e0, 0xeeb0eee, 0xee80ee5, 0x2a0f35, 0x2a1170, 0x200051, 0x116b1115, 0x111c, 0x11671164, 0xf430f40, 0xf630f60, 0x2d0faa, 0x350031, 0x1178117b, 0x11851181, 0x0, 0x118911ab, 0xfb70fba, 0xfb40fb1, 0x3c0000, 0x440040, 0x12061209, 0x1213120f, 0x11f511f2, 0x12171239, 0x1019101c, 0x10161013, 0x18100a, 0x995001c, 0x0, 0x129d1247, 0x124e, 0x12991296, 0xfed0fea, 0x103c1039, 0x31083, 0x39, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x1, 0x0, 0x0, 0x1a690000, 0x0, 0x0, 0x4e0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2fc02fa, 0x2ff, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x1a6f0000, 0x1a72, 0x1a7e1a7b, 0x0, 0x0, 0x8f, 0xc, 0x0, 0x0, 0x0, 0x5630000, 0x920560, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a760000, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae00305, 0x0, 0x3740365, 0x3920383, 0x3b003a1, 0x1aad02f4, 0xa10544, 0xb3b00a5, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x1aad02f4, 0xa10544, 0xa5, 0xa7d0692, 0xb410787, 0xb0d0e8c, 0xa280b79, 0xb3b05d3, 0x8400cd3, 0xba3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9a2099e, 0xe4d05e3, 0xa1e0000, 0xe770a22, 0xe500000, 0x6ac0602, 0x6ac06ac, 0xe6d0b0d, 0x6cf06cf, 0xa280734, 0x77e0000, 0x786, 0x6af0000, 0x82c083b, 0x82c082c, 0x0, 0x88f0863, 0x897, 0x60a, 0x77c, 0x60a, 0x5b0071a, 0x5e305d5, 0xa7d0000, 0x67e0629, 0x7230000, 0x13540787, 0x136a1362, 0xae0136f, 0x6800000, 0x10ec11ee, 0x10060f3a, 0x1aab, 0x0, 0x5e60000, 0xa7d0a2e, 0x73e0ae0, 0x0, 0x0, 0x0, 0x3e203da, 0x3ca03c1, 0x3d20455, 0x4980459, 0x3d604cf, 0x3de04e7, 0x4eb049c, 0x3be0511, 0x6d106cf, 0x6de06d4, 0x91806b2, 0x91f091b, 0x68206e1, 0x950094d, 0x5e30734, 0x72305e6, 0xb300ae0, 0xb3d0b33, 0xdcf086a, 0xdd60dd2, 0xb410b40, 0xdfd0dfa, 0x9a00a28, 0x5d30a2e, 0x0, 0x0, 0x0, 0x0, 0x30d0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a8d1a86, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a92, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a950000, 0x1a981a9b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1aa0, 0x0, 0x1aa50000, 0x0, 0x1aa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1aaf, 0x1ab2, 0x0, 0x0, 0x1ab81ab5, 0x1ac10000, 0x1ac4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ac80000, 0x0, 0x1acb, 0x1ace0000, 0x1ad10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x556, 0x1ad7, 0x0, 0x0, 0x0, 0x0, 0x1ad40000, 0x55b054a, 0x1add1ada, 0x0, 0x1ae31ae0, 0x0, 0x1ae91ae6, 0x0, 0x0, 0x0, 0x1aef1aec, 0x0, 0x1afb1af8, 0x0, 0x1b011afe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b0d1b0a, 0x1b131b10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1af51af2, 0x1b071b04, 0x0, 0x0, 0x0, 0x1b191b16, 0x1b1f1b1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b350000, 0x1b37, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3430314, 0x365030f, 0x3830374, 0x3a10392, 0x31c03b0, 0x342032f, 0x3640355, 0x3820373, 0x3a00391, 0x3f703af, 0xd900a3, 0xe600e2, 0xee00ea, 0xf600f2, 0xa700fa, 0xb100ac, 0xbb00b6, 0xc500c0, 0xcf00ca, 0xdd00d4, 0x3460319, 0x3680359, 0x3860377, 0x3a40395, 0x31f03b3, 0x3450332, 0x3670358, 0x3850376, 0x3a30394, 0x3fa03b2, 0x16a0166, 0x172016e, 0x17a0176, 0x182017e, 0x18a0186, 0x192018e, 0x19a0196, 0x1a2019e, 0x1aa01a6, 0x1b201ae, 0x1ba01b6, 0x1c201be, 0x1ca01c6, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x305, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1abc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x54f0542, 0x552, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6b2073e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b2f0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x227c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1efb1ee9, 0x1f091f01, 0x1f131f0d, 0x1f1b1f17, 0x1f4b1f21, 0x1f5f1f57, 0x1f6f1f67, 0x1f871f77, 0x1f8b1f89, 0x1fb91fa5, 0x1fc51fc1, 0x1fcd1fc7, 0x1fdd1fdb, 0x1feb1fe9, 0x1ff71fef, 0x204f2045, 0x2079206f, 0x207f207d, 0x20982087, 0x20b420ae, 0x20ca20c4, 0x20ce20cc, 0x20dc20da, 0x20f820f2, 0x210020fc, 0x210f2108, 0x21292113, 0x212f212b, 0x21352131, 0x21412139, 0x218b214f, 0x21972195, 0x21d921d7, 0x21e521e3, 0x21ed21e9, 0x32521f1, 0x3292211, 0x22602223, 0x226e2266, 0x227a2274, 0x2280227e, 0x22842282, 0x22e22286, 0x230c2306, 0x2310230e, 0x23162312, 0x23222318, 0x23342330, 0x23562354, 0x235c235a, 0x23622360, 0x23762374, 0x23862384, 0x238a2388, 0x23a62394, 0x23aa23a8, 0x23dc23bc, 0x23ee23de, 0x23fa23f6, 0x241c240a, 0x2442243e, 0x2452244c, 0x245a2456, 0x245e245c, 0x246c246a, 0x247e247a, 0x24842482, 0x248e248a, 0x24922490, 0x24982496, 0x24f224e8, 0x250e250c, 0x25282512, 0x2530252c, 0x25522534, 0x25582554, 0x255c255a, 0x25742572, 0x25822578, 0x25922584, 0x25982596, 0x25ba25aa, 0x25c425c2, 0x25de25c8, 0x25e825e0, 0x260025fa, 0x26142608, 0x261a2618, 0x261e261c, 0x26262624, 0x2638262a, 0x263c263a, 0x264a2648, 0x2658264e, 0x265c265a, 0x26622660, 0x26662664, 0x26702668, 0x267e267c, 0x26862684, 0x268a2688, 0x2690268e, 0x26982692, 0x26a0269c, 0x26a626a2, 0x26aa26a8, 0x26b226ae, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b49, 0x1fcf1fcd, 0x1fd1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b7e, 0x1b81, 0x1b84, 0x1b87, 0x1b8a, 0x1b8d, 0x1b90, 0x1b93, 0x1b96, 0x1b99, 0x1b9c, 0x1b9f, 0x1ba20000, 0x1ba50000, 0x1ba80000, 0x0, 0x0, 0x0, 0x1bae1bab, 0x1bb10000, 0x1bb4, 0x1bba1bb7, 0x1bbd0000, 0x1bc0, 0x1bc91bc6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b7b, 0x0, 0x0, 0x870000, 0x8a, 0x1bcc1bd3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c26, 0x1c43, 0x1bf6, 0x1c92, 0x1c9b, 0x1caf, 0x1cbf, 0x1cca, 0x1ccf, 0x1cdc, 0x1ce1, 0x1ceb, 0x1cf20000, 0x1cf70000, 0x1c100000, 0x0, 0x0, 0x0, 0x1d261d1d, 0x1d3b0000, 0x1d42, 0x1d611d57, 0x1d760000, 0x1d7e, 0x1caa1da1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c01, 0x1e440000, 0x1e521e4d, 0x1e57, 0x0, 0x1ca11e60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19440000, 0x1a101949, 0x1a12194b, 0x19501a14, 0x19571955, 0x1a181a16, 0x1a1c1a1a, 0x1a201a1e, 0x195c19a6, 0x19661961, 0x196819b0, 0x196f196d, 0x19811977, 0x198e1983, 0x19981993, 0x1947199d, 0x19da19d8, 0x19de19dc, 0x19e219e0, 0x198c19e4, 0x19ea19e8, 0x19ee19ec, 0x19f21975, 0x19f619f4, 0x19fa19f8, 0x19fe197f, 0x19a219d4, 0x1a2219a4, 0x1a261a24, 0x1a2a1a28, 0x1a2e1a2c, 0x1a3019a8, 0x19aa1a32, 0x19ae19ac, 0x19b419b2, 0x19b819b6, 0x19bc19ba, 0x19c019be, 0x19c419c2, 0x19c819c6, 0x19cc19ca, 0x1a361a34, 0x19d019ce, 0x1a0019d2, 0x1a041a02, 0x1a081a06, 0x1a0c1a0a, 0x1a0e, 0x0, 0x1f171ee9, 0x20471eef, 0x1efd1ef1, 0x23641ef3, 0x1ef71f0d, 0x208c1eeb, 0x1f212051, 0x1d701ce, 0x1e901e0, 0x1fb01f2, 0x20d0204, 0x2330225, 0x245023c, 0x257024e, 0x1db01d2, 0x1ed01e4, 0x1ff01f6, 0x2110208, 0x2370229, 0x2490240, 0x25b0252, 0x216022e, 0x21e, 0x2700260, 0x2a00268, 0x2880274, 0x2840264, 0x290026c, 0x2c402b0, 0x2b802c0, 0x2a402ec, 0x2bc02ac, 0x2d002b4, 0x2c80298, 0x2d402e4, 0x278028c, 0x2a8029c, 0x27c02cc, 0x29402e8, 0x28002d8, 0x2e002dc, 0x21112021, 0x23fe21e3, 0x0, 0x0, 0x0, 0x0, 0x406082e, 0x41c0411, 0x4320427, 0x4400439, 0x44e0447, 0x475046e, 0x47f047c, 0x4850482, 0x194b1944, 0x19571950, 0x1961195c, 0x196f1968, 0x19831977, 0x1993198e, 0x199d1998, 0x194d1946, 0x19591952, 0x1963195e, 0x1971196a, 0x19851979, 0x19951990, 0x199f199a, 0x197c1988, 0x1974, 0x1f171ee9, 0x20471eef, 0x1f611f19, 0x1f5f1eed, 0x1fcd1f0f, 0x22e20329, 0x22232286, 0x204f25c8, 0x223b0325, 0x2240221b, 0x231c2007, 0x23ca255e, 0x23e21fab, 0x20982368, 0x1f4925a2, 0x22961fdf, 0x1f2b262c, 0x208a1f73, 0x1efd1ef1, 0x20fa1ef3, 0x1fc92001, 0x20b220b8, 0x1f292390, 0x1fd72568, 0x4882083, 0x48e048b, 0x4b10491, 0x4b704b4, 0x4bd04ba, 0x4c304c0, 0x4c904c6, 0x4e404cc, 0x34e033b, 0x4d604a3, 0x50304f2, 0x5290518, 0x327053a, 0x34d033a, 0xa8206b4, 0x7390a7f, 0x1bf11bd8, 0x1c0a1bff, 0x1c241c1a, 0x1c731c41, 0x1c991c90, 0x1cbd1cad, 0x1ccd1c1e, 0x1cdf1cda, 0x1cf01bfb, 0x1bde1cf5, 0x1d0f1ca6, 0x1c8e1d11, 0x1d1b1d0d, 0x1d551d39, 0x1d9f1d74, 0x1ddc1c31, 0x1def1c22, 0x1e041e00, 0x1e191e11, 0x1c351e1b, 0x1e341bed, 0x1e421c5d, 0x1e501e4b, 0x1e55, 0x1be01bda, 0x1beb1be5, 0x1bf91bf3, 0x1c0c1c04, 0x1c1c1c13, 0x1c331c20, 0x1c3c1c37, 0x1c2e1c29, 0x1c4b1c46, 0x1c501c57, 0x1c5f1c5c, 0x1c6d1c66, 0x1c7d1c61, 0x1c8b1c84, 0x1ca41c95, 0x1cb21ca8, 0x1cc21cb7, 0x1cd61cd2, 0x1cfa1ce4, 0x1c811d03, 0x1d171d0c, 0x1d291d35, 0x1d201d30, 0x1d4c1d45, 0x1d3e1d51, 0x1d6b1d64, 0x1d701d5a, 0x1d811d95, 0x1d9b1d85, 0x1d8f1d8a, 0x1dac1d79, 0x1db81da4, 0x1dbb1db2, 0x1dc51dbf, 0x1dce1dca, 0x1dd61dd2, 0x1de31dde, 0x1df11de6, 0x1c681df5, 0x1e0b1e06, 0x1e1f1e13, 0x1e291e24, 0x1e361e2e, 0x1c6f1e39, 0x33f0311, 0x3610352, 0x37f0370, 0x39d038e, 0x3bb03ac, 0x33e032b, 0x3600351, 0x37e036f, 0x39c038d, 0x3ba03ab, 0x40d0402, 0x4230418, 0xb0f042e, 0x56a0a53, 0xc580a0f, 0xa590ce6, 0xa600a5c, 0x210a06db, 0x20892200, 0x223d21f9, 0xc260cda, 0xbea11b4, 0x71c0b7b, 0x689075b, 0xb8c0a26, 0xc290cdd, 0x11c011b7, 0x6010bf6, 0xb7e068d, 0x68c0764, 0x11c30893, 0xa560bfd, 0xaec0b94, 0x11c60c35, 0xa300c00, 0xc030b97, 0xa340a33, 0xc070b9a, 0xa380a37, 0xc1b0b9e, 0x6910c1f, 0x7680b82, 0xcf60690, 0xd000cfa, 0xc380ce9, 0xc0f11c9, 0xc2c0ce0, 0xbed11ba, 0x76c0b86, 0xc2f0ce3, 0xbf011bd, 0x76f0b89, 0x77b0bb4, 0x5d70999, 0xa2d0a2a, 0x5e805ff, 0x6940a50, 0x6ae0b13, 0x71f0b3a, 0xba20722, 0xbbf0bbc, 0xbc60bc2, 0xbf90bf3, 0x8200c0b, 0x8230cd5, 0xd25082b, 0x9360869, 0x5d1092a, 0x34a0337, 0x36c035d, 0x38a037b, 0x3a80399, 0x32303b7, 0x3490336, 0x36b035c, 0x389037a, 0x3a70398, 0x3fe03b6, 0x4140409, 0x42a041f, 0x43c0435, 0x44a0443, 0x4710451, 0xaf40478, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe730e6b, 0x0, 0x0, 0x0, 0x22132556, 0x256a2584, 0x1eff22c6, 0x26ae1ff9, 0x209226ae, 0x202b25c8, 0x21872090, 0x244a2382, 0x250424e6, 0x25a8251e, 0x229a2254, 0x233c22f0, 0x25bc24ca, 0x1f112652, 0x225e1fe3, 0x24e42302, 0x20e6267a, 0x24dc22d6, 0x21a12526, 0x250a2478, 0x221d211f, 0x232822a6, 0x1f3125ae, 0x1fb31f7d, 0x225a21d5, 0x23922300, 0x24e02456, 0x257e24ec, 0x266a2610, 0x23b02678, 0x242c23d0, 0x25d624bc, 0x2540267e, 0x212d206d, 0x24682408, 0x23b4231a, 0x260c2566, 0x20d4206b, 0x22b02256, 0x242422ca, 0x25ec2438, 0x246e1fb1, 0x1f811f83, 0x242e23e6, 0x25f024c8, 0x21a3254e, 0x25462254, 0x20be1f05, 0x23322159, 0x1fc32372, 0x1f3923b8, 0x1ef5214b, 0x21e12290, 0x1fed2422, 0x23982063, 0x253824cc, 0x25962276, 0x21ab228c, 0x21bb24a8, 0x1f1f2370, 0x1f7f1f5d, 0x24182244, 0x253e2494, 0x1fb725c6, 0x20982011, 0x21ef2127, 0x23ba22d8, 0x265625e4, 0x268c2680, 0x220f1fa5, 0x2590226c, 0x217b210d, 0x21d12189, 0x22f622d0, 0x23e0234e, 0x24642432, 0x24d02588, 0x25d8259c, 0x1fa71f91, 0x22ee201b, 0x25382514, 0x2155211d, 0x227221b7, 0x232c2406, 0x20491f27, 0x20f020be, 0x233a215b, 0x24502348, 0x25ca2460, 0x2612260a, 0x1f332630, 0x25c023da, 0x216725fe, 0x1f451f15, 0x20d020c0, 0x225421e7, 0x238022fc, 0x25a624d6, 0x220726aa, 0x1fa325ea, 0x2233222d, 0x22be22a2, 0x236e2340, 0x242023ae, 0x1f612636, 0x25f22191, 0x20e21f3d, 0x258a22b2, 0x216b2143, 0x23322237, 0x1f9525f6, 0x20d82009, 0x222521fc, 0x2294224a, 0x2378233e, 0x25162446, 0x25c4251c, 0x1fcb2604, 0x200b22c0, 0x235022fe, 0x25f824de, 0x2682266e, 0x22ae2231, 0x23f6247c, 0x240e23fc, 0x22ea2326, 0x1f23254c, 0x1f9724b0, 0x21151f8f, 0x241421a5, 0x229c20b6, 0x258e220d, 0x25ee250e, 0x2123252c, 0x20371f4d, 0x0, 0x2061, 0x2205, 0x1f850000, 0x238c232a, 0x23cc23be, 0x23d823ce, 0x24102616, 0x2452, 0x24e2, 0x2544, 0x259e0000, 0x25b4, 0x0, 0x26422640, 0x26762644, 0x25fc25b0, 0x1f471f35, 0x1faf1f51, 0x1fd51fb5, 0x203d202f, 0x205f2041, 0x20d62065, 0x216120da, 0x21792175, 0x21db2185, 0x220921f3, 0x22a82246, 0x22ce22b6, 0x230822f8, 0x23b22342, 0x23c42240, 0x23c623c2, 0x23ca23c8, 0x23d623d4, 0x23f223e8, 0x24322400, 0x243a2436, 0x24582444, 0x249a2480, 0x24ce249a, 0x252e2522, 0x254a2548, 0x256e256c, 0x259e259a, 0x26282606, 0x215d2634, 0x248c274b, 0x0, 0x1f7b1ef9, 0x1f2f1f5b, 0x1f651f4f, 0x1fbb1fad, 0x2025202f, 0x203b202d, 0x20692061, 0x2094208e, 0x20aa20a2, 0x21252121, 0x214d213d, 0x21712165, 0x21792169, 0x21852173, 0x21bf2193, 0x21c921c5, 0x220521dd, 0x221f221d, 0x226e2229, 0x22a22276, 0x22c422c8, 0x22dc22ce, 0x23a422f8, 0x2324230a, 0x234a232a, 0x236a2358, 0x237e237c, 0x238e238c, 0x23a02396, 0x23b6239e, 0x240023f4, 0x2428240c, 0x24402432, 0x24b22458, 0x250024c6, 0x252a2524, 0x253a252e, 0x253c2544, 0x25462548, 0x254a2542, 0x256e2550, 0x25a4258c, 0x25ce25be, 0x260625f4, 0x26202616, 0x262e2628, 0x265e2634, 0x272126ae, 0x2733271f, 0x1ea11e8d, 0x27671ea3, 0x27a92779, 0x26ac26a4, 0x0, 0x0, 0x0, 0xadf0adb, 0xade0ae3, 0xd280ae2, 0xd28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x134e0000, 0x13481345, 0x134b1351, 0x0, 0x0, 0x13850000, 0x13d20000, 0x135413a6, 0x1374136f, 0x1360138e, 0x13b7139b, 0x2f413cd, 0x13ca13c7, 0x13c313bf, 0x13591356, 0x1364135c, 0x1371136c, 0x137c1376, 0x137f, 0x13881382, 0x1390138b, 0x1398, 0x139d, 0x13a313a0, 0x13a80000, 0x13ab, 0x13b413b1, 0x13bc13b9, 0x137913cf, 0x13931367, 0x135f13ae, 0x18181818, 0x181e181e, 0x181e181e, 0x18201820, 0x18201820, 0x18241824, 0x18241824, 0x181c181c, 0x181c181c, 0x18221822, 0x18221822, 0x181a181a, 0x181a181a, 0x183c183c, 0x183c183c, 0x183e183e, 0x183e183e, 0x18281828, 0x18281828, 0x18261826, 0x18261826, 0x182a182a, 0x182a182a, 0x182c182c, 0x182c182c, 0x18321832, 0x18301830, 0x18341834, 0x182e182e, 0x18381838, 0x18361836, 0x18401840, 0x18401840, 0x18441844, 0x18441844, 0x18481848, 0x18481848, 0x18461846, 0x18461846, 0x184a184a, 0x184c184c, 0x184c184c, 0x186d186d, 0x18501850, 0x18501850, 0x184e184e, 0x184e184e, 0x15911591, 0x186a186a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18420000, 0x18421842, 0x18031842, 0x17ff1803, 0x180717ff, 0x185b1807, 0x18621862, 0x18551855, 0x18601860, 0x180b180b, 0x180b180b, 0x14151415, 0x17cd17cd, 0x180d180d, 0x17f117f1, 0x18011801, 0x17fd17fd, 0x18051805, 0x18091809, 0x17f51809, 0x17f517f5, 0x18641864, 0x18641864, 0x17d517d1, 0x17f517e5, 0x13f417f9, 0x13fe13f7, 0x1414140b, 0x141e1417, 0x1438142d, 0x146a144d, 0x1472146d, 0x1484147b, 0x148c1487, 0x14311422, 0x14d11435, 0x143c14d4, 0x150514fa, 0x151a150c, 0x15931562, 0x15a515a2, 0x15ba15b0, 0x15c815c5, 0x15e415df, 0x16071575, 0x163f160a, 0x16451642, 0x1653164c, 0x165b1656, 0x16711662, 0x16791674, 0x167f167c, 0x16851682, 0x16931688, 0x16aa1696, 0x16c816b9, 0x1579158c, 0x145116e0, 0x14591455, 0x145d1526, 0x172d1461, 0x174f1740, 0x17691758, 0x1771176c, 0x177f1774, 0x179c1782, 0x17aa17a3, 0x17c417b3, 0x14e417c7, 0x179714ee, 0x64005d, 0x72006b, 0x800079, 0x17e117dd, 0x17e917e5, 0x17f917f5, 0x140813db, 0x140e140b, 0x14171414, 0x144a1447, 0x1464144d, 0x146d146a, 0x14781475, 0x147e147b, 0x14871484, 0x16561653, 0x16741671, 0x16851679, 0x16931688, 0x158c1696, 0x16e01579, 0x152616e5, 0x17551752, 0x17631758, 0x176c1769, 0x17ad1797, 0x17b317b0, 0x17c417be, 0x17d117c7, 0x17d917d5, 0x17ed17e5, 0x13f713f4, 0x140b13fe, 0x141e1411, 0x1438142d, 0x1467144d, 0x148c147b, 0x14311422, 0x14d11435, 0x14fa143c, 0x150c1505, 0x1562151a, 0x1593156d, 0x15a515a2, 0x15ba15b0, 0x15df15c5, 0x157515e4, 0x160a1607, 0x1642163f, 0x164c1645, 0x1662165b, 0x167f167c, 0x16851682, 0x16aa1688, 0x16c816b9, 0x13e0158c, 0x14551451, 0x15261459, 0x1740172d, 0x1758174f, 0x17711766, 0x17851774, 0x17a3179c, 0x17b317aa, 0x17e515ed, 0x140b17ed, 0x144d1411, 0x147b1467, 0x151a1481, 0x154c1529, 0x16851557, 0x158c1688, 0x17661758, 0x15ed17b3, 0x162c1625, 0x15d71633, 0x15ff15da, 0x16191602, 0x152c161c, 0x155a152f, 0x1490155d, 0x142613fb, 0x1440142a, 0x159a1402, 0x15bd159d, 0x153415c0, 0x1546153b, 0x1549154c, 0x15701517, 0x15d715b7, 0x15ff15da, 0x16191602, 0x152c161c, 0x155a152f, 0x1490155d, 0x142613fb, 0x1440142a, 0x159a1402, 0x15bd159d, 0x153415c0, 0x1546153b, 0x1549154c, 0x15701517, 0x153415b7, 0x1546153b, 0x1529154c, 0x15c81557, 0x150514fa, 0x1534150c, 0x1546153b, 0x15df15c8, 0x13e313e3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14301421, 0x14341430, 0x1450143b, 0x14581454, 0x14a314a3, 0x14c114c5, 0x14fd1508, 0x15211501, 0x151d1521, 0x15251525, 0x15651565, 0x153e1596, 0x1537153e, 0x154f154f, 0x15531553, 0x15b315a8, 0x15cb15b3, 0x15cf15cb, 0x15e715d3, 0x15f315f3, 0x160d15f7, 0x16111615, 0x16481648, 0x16691665, 0x16c416bc, 0x16ad16c0, 0x16cb16ad, 0x16d216cb, 0x16fe16d2, 0x170b1702, 0x16f316eb, 0x17161712, 0x0, 0x177716ef, 0x1743177b, 0x17341747, 0x17381734, 0x175b175f, 0x17b617b6, 0x14291401, 0x14431425, 0x1460143f, 0x14ab145c, 0x14a7148f, 0x1569150f, 0x15ac1542, 0x16d616b5, 0x179f17a6, 0x172117ba, 0x174b166d, 0x16bc1665, 0x168f15fb, 0x171a1730, 0x168b16b1, 0x173016b1, 0x14ba1493, 0x164f16f7, 0x168b13fa, 0x159615e7, 0x173c1513, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x165e158f, 0x13d913de, 0x15731706, 0x15eb14e9, 0x1578158a, 0x1497157c, 0x14f1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b3102f6, 0x5401b33, 0x8d0546, 0x1b770093, 0x2ff1b79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a6d02fc, 0x9931a6b, 0xa10993, 0xe3b00a5, 0x1b4b0e3f, 0x1b451b4f, 0x1b391b47, 0x1b351b3b, 0x1b3d1b37, 0x1b411b3f, 0x1b43, 0x98b0000, 0xc098f, 0xc000c, 0x993000c, 0x9930993, 0x1b3102f6, 0x2fa, 0x5400546, 0x8d0093, 0xa11a6d, 0xe3b00a5, 0x1b4b0e3f, 0x971b4f, 0x2f2009d, 0x2f802f4, 0x5590548, 0x544, 0x99098d, 0x566009b, 0x0, 0x0, 0x161f0057, 0x5a, 0x61, 0x16220068, 0x1629006f, 0x16300076, 0x1637007d, 0x163a0084, 0x13e613d5, 0x13e913e6, 0x178f13e9, 0x13ec178f, 0x17ca13ec, 0x17ca17ca, 0x13d717ca, 0x13f213d7, 0x13f213f2, 0x141a13f2, 0x141c141a, 0x141c141c, 0x1470141c, 0x14701470, 0x13f51470, 0x13f513f5, 0x13f813f5, 0x13f813f8, 0x13ff13f8, 0x13ff13ff, 0x14e013ff, 0x14e214e0, 0x13dc14e2, 0x140913dc, 0x14f81409, 0x14f814f8, 0x153214f8, 0x15321532, 0x15601532, 0x15601560, 0x15a01560, 0x15a015a0, 0x15c315a0, 0x15c315c3, 0x15dd15c3, 0x15dd15dd, 0x15e215dd, 0x15e215e2, 0x160515e2, 0x16051605, 0x163d1605, 0x163d163d, 0x1659163d, 0x16591659, 0x16771659, 0x16771677, 0x14ec1677, 0x14ec14ec, 0x140c14ec, 0x140c140c, 0x140f140c, 0x140f140f, 0x13e1140f, 0x13e113e1, 0x178813e1, 0x14151788, 0x13fc1415, 0x13fc13fc, 0x169e13fc, 0x16a2169e, 0x16a616a2, 0x169b16a6, 0x169b, 0x0, 0x8d0000, 0x970095, 0x9b0099, 0x9f009d, 0xa500a1, 0x2f402f2, 0x2f802f6, 0x30302fa, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x5460540, 0x5440548, 0x930559, 0x5680566, 0x5e305d5, 0x62905e6, 0x687067e, 0x6cf06ac, 0x71a0607, 0x7230734, 0x7a4077e, 0x83b06af, 0x85e082c, 0x56b088d, 0x77006b2, 0x95a0682, 0x98b060a, 0x98f098d, 0x9930991, 0x6920995, 0x9a00937, 0xa7d0a2e, 0x6020ad9, 0xae00b0d, 0xb79073e, 0x5d30a28, 0x7870b3b, 0x5d80cd3, 0x8400a11, 0xa240ba3, 0xde1086a, 0x6950b41, 0xe3b0611, 0xe3f0e3d, 0x1b280e41, 0x1b331b2a, 0x1b3f1b3d, 0x1e5c1b31, 0x1bd61e55, 0x1bfd1bef, 0x1c181c08, 0x1e0f1e02, 0x1cee1e17, 0x1bd81c16, 0x1bff1bf1, 0x1c1a1c0a, 0x1c411c24, 0x1c901c73, 0x1cad1c99, 0x1c1e1cbd, 0x1cda1ccd, 0x1bfb1cdf, 0x1cf51cf0, 0x1ca61bde, 0x1d111d0f, 0x1d0d1c8e, 0x1d391d1b, 0x1d741d55, 0x1c311d9f, 0x1c221ddc, 0x1e001def, 0x1e111e04, 0x1e1b1e19, 0x1bed1c35, 0x1c5d1e34, 0x1c061e42, 0x8b0088, 0x194419d4, 0x1a101949, 0x1a12194b, 0x19501a14, 0x19571955, 0x1a181a16, 0x1a1c1a1a, 0x1a201a1e, 0x195c19a6, 0x19661961, 0x196819b0, 0x196f196d, 0x19811977, 0x198e1983, 0x19981993, 0x199d, 0x0, 0x19d81947, 0x19dc19da, 0x19e019de, 0x0, 0x19e419e2, 0x19e8198c, 0x19ec19ea, 0x0, 0x197519ee, 0x19f419f2, 0x19f819f6, 0x0, 0x197f19fa, 0x19fe, 0x0, 0xe450e43, 0x90e4b, 0xe470e49, 0x1a82, 0x1a841b22, 0x1a8b1a89, 0x1b241a90, 0x1b26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b6, 0x26b9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26bc0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c226bf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c826c5, 0x26cf26cb, 0x26d726d3, 0x26db, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26df0000, 0x26e226ea, 0x26e626ed, 0x26f1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0x602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x568, 0x5e605e3, 0x0, 0x687, 0x6070000, 0x71a, 0x77e0000, 0x6af07a4, 0x83b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90000, 0xb0d0000, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30000, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e60000, 0x67e0629, 0x687, 0x6070000, 0x734071a, 0x77e0723, 0x6af07a4, 0x83b, 0x88d085e, 0x6b2056b, 0x6820770, 0x95a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e60000, 0x67e0629, 0x687, 0x60706cf, 0x734071a, 0x723, 0x7a4, 0x0, 0x88d085e, 0x6b2056b, 0x6820770, 0x95a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0xb410de1, 0x6110695, 0xe800e6f, 0x0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0x12a212a0, 0x0, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x13f213d7, 0x14e013f5, 0x17880000, 0x13f81409, 0x13fc15c3, 0x14ec1677, 0x140f140c, 0x15e214f8, 0x1560163d, 0x13dc1659, 0x141c1532, 0x13ff1470, 0x15a014e2, 0x160515dd, 0x184a1814, 0x1816183a, 0x13f20000, 0x13f5, 0x13e1, 0x13f80000, 0x13fc0000, 0x14ec1677, 0x140f140c, 0x15e214f8, 0x1560163d, 0x1659, 0x141c1532, 0x13ff1470, 0x15a00000, 0x16050000, 0x0, 0x0, 0x0, 0x13f5, 0x0, 0x13f80000, 0x13fc0000, 0x14ec0000, 0x140f0000, 0x15e214f8, 0x15600000, 0x1659, 0x1532, 0x13ff0000, 0x15a00000, 0x16050000, 0x184a0000, 0x18160000, 0x13f20000, 0x13f5, 0x13e1, 0x13f80000, 0x13fc15c3, 0x1677, 0x140f140c, 0x15e214f8, 0x1560163d, 0x1659, 0x141c1532, 0x13ff1470, 0x15a00000, 0x160515dd, 0x1814, 0x183a, 0x13f213d7, 0x14e013f5, 0x178813e1, 0x13f81409, 0x13fc15c3, 0x14ec0000, 0x140f140c, 0x15e214f8, 0x1560163d, 0x13dc1659, 0x141c1532, 0x13ff1470, 0x15a014e2, 0x160515dd, 0x0, 0x0, 0x13f20000, 0x14e013f5, 0x17880000, 0x13f81409, 0x13fc15c3, 0x14ec0000, 0x140f140c, 0x15e214f8, 0x1560163d, 0x13dc1659, 0x141c1532, 0x13ff1470, 0x15a014e2, 0x160515dd, 0x0, 0x0, 0x307030a, 0x3f10316, 0x4ab0468, 0x4fa04de, 0x520050b, 0x531, 0x0, 0x0, 0x10200fe, 0x10a0106, 0x112010e, 0x11a0116, 0x122011e, 0x12a0126, 0x132012e, 0x13a0136, 0x142013e, 0x14a0146, 0x152014e, 0x15a0156, 0x162015e, 0x5e31b4d, 0x5e5082c, 0x933, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x76c06b1, 0x8660860, 0x9300827, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x761075e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c9e1bc3, 0x1cad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20b02197, 0x1cf71ff3, 0x20811f17, 0x208c2532, 0x21fe1f1d, 0x21e722f2, 0x21451f9d, 0x21eb1f69, 0x24261f93, 0x2560235c, 0x200f2073, 0x219d22cc, 0x1ee921b3, 0x25a01eef, 0x1efd20fa, 0x21ad2001, 0x21992574, 0x23f023d2, 0x22bc2005, 0x329221b, 0x1f9f2366, 0x2035, 0x0, 0x0, 0x1b511b69, 0x1b5d1b55, 0x1b611b6d, 0x1b591b71, 0x1b65, 0x0, 0x0, 0x0, 0x1ffd2147, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f031f07, 0x26f51f0b, 0x1f351f2d, 0x1f3b1f37, 0x1f411f3f, 0x1f431f47, 0x26fd1e63, 0x1f531f51, 0x1f631f55, 0x1e6526f7, 0x1f691f59, 0x1f7126fb, 0x1f251f75, 0x1f7b1f79, 0x1f8927b9, 0x1e691f8d, 0x1f9b1f99, 0x1fa11f9f, 0x1fad1e6b, 0x1fb51faf, 0x1fbd1fbb, 0x1fc31fbf, 0x1fd51fd3, 0x1fe11fd9, 0x1fe71fe5, 0x1fe71fe7, 0x22e42703, 0x1ff51ff1, 0x1ffb2705, 0x20031fff, 0x200d2017, 0x20152013, 0x201d2019, 0x2023201f, 0x20292027, 0x202d2029, 0x20332031, 0x204b2039, 0x204d203d, 0x2043203f, 0x20711f8f, 0x20572055, 0x20532059, 0x205b205d, 0x27072067, 0x20772075, 0x2081207b, 0x20962085, 0x270b2709, 0x209e209c, 0x209a20a0, 0x1e6d20a4, 0x20a81e6f, 0x20ac20ac, 0x20ba270d, 0x20be20bc, 0x270f20c2, 0x20c820c6, 0x20cc2137, 0x20d21e71, 0x20e020da, 0x271320de, 0x271520e4, 0x20e820ea, 0x20f420ec, 0x1e7320f6, 0x210220fe, 0x21062104, 0x27171e75, 0x21171e77, 0x211b2119, 0x27cd211f, 0x271b212b, 0x2486271b, 0x21332133, 0x27291e79, 0x213b277d, 0x1e7b213f, 0x21512149, 0x21572153, 0x1e7f215f, 0x21611e7d, 0x2163271d, 0x216f216d, 0x216f2171, 0x21792177, 0x217d2181, 0x2183217f, 0x21872185, 0x218f210b, 0x219f219b, 0x21b121a7, 0x21af2723, 0x21b521a9, 0x21c321b9, 0x21c72725, 0x21bd21c1, 0x21cb1e81, 0x21d321cf, 0x1e8321cd, 0x21df21db, 0x21f52727, 0x22032215, 0x22091e89, 0x1e851e87, 0x1f6d1f6b, 0x220b2217, 0x1ebb2470, 0x221f221d, 0x222b2221, 0x27312227, 0x22351e8b, 0x2242222f, 0x27352246, 0x22392248, 0x1e8d224c, 0x2250224e, 0x22582252, 0x225c2737, 0x22621e8f, 0x22642739, 0x226a1e91, 0x22762270, 0x273b2278, 0x273d2711, 0x273f2288, 0x2292228e, 0x2298228a, 0x22a822a0, 0x22a422a2, 0x22ac22aa, 0x229e2741, 0x22ba22b8, 0x22c41e93, 0x274322c2, 0x22d222b4, 0x27472745, 0x22de22d4, 0x22da22dc, 0x22e01e95, 0x22e622e8, 0x26f922ec, 0x274922f4, 0x274d22fa, 0x230a2304, 0x274f2314, 0x2320231e, 0x27532751, 0x2336232e, 0x23381e97, 0x1e991e99, 0x23462344, 0x234c234a, 0x1e9b2352, 0x2755235e, 0x2757236c, 0x27192372, 0x2759237a, 0x275d275b, 0x1e9f1e9d, 0x27612396, 0x2763275f, 0x239a2765, 0x239c239c, 0x1ea323a0, 0x1ea523a2, 0x27691ea7, 0x23b023ac, 0x1ea923b6, 0x23c8276b, 0x276f276d, 0x23e423d8, 0x23e81eab, 0x23ec23ea, 0x27732771, 0x23f82773, 0x27751ead, 0x24042402, 0x27771eaf, 0x1eb12412, 0x2416241a, 0x277b241e, 0x1eb3242a, 0x24342430, 0x1eb5243c, 0x2781277f, 0x27831eb7, 0x27852448, 0x2454244e, 0x27872458, 0x24622789, 0x2466278b, 0x1eb9272b, 0x24742472, 0x24761ebd, 0x278d20a6, 0x272d278f, 0x2486272f, 0x25942488, 0x249e1ebf, 0x24a0249c, 0x24a21fa9, 0x24a624a4, 0x279124aa, 0x24ac24a8, 0x24b824b6, 0x24ba24ae, 0x24ce24c4, 0x24be24b4, 0x24c224c0, 0x27972793, 0x1ec12795, 0x24d424d2, 0x279f24d8, 0x279924da, 0x1ec51ec3, 0x279d279b, 0x24ea1ec7, 0x24ee24ec, 0x24f624f0, 0x24fa24f4, 0x250024f8, 0x24fe24fc, 0x1ec92502, 0x25082506, 0x25101ecb, 0x27a12512, 0x251a2518, 0x25201ecd, 0x27a31e67, 0x1ecf27a5, 0x25361ed1, 0x25502542, 0x27a72558, 0x25642562, 0x25762570, 0x26ff27ab, 0x257a257c, 0x27012580, 0x258c2586, 0x27af27ad, 0x25b225ac, 0x27b125b6, 0x25cc25b8, 0x25d425d2, 0x25da25d0, 0x27b325dc, 0x1ed325e2, 0x27b525e6, 0x26021ed5, 0x260e20ee, 0x27bb27b7, 0x1ed91ed7, 0x27bd2622, 0x27bf1edb, 0x262e262e, 0x27c12632, 0x1edd263e, 0x264c2646, 0x26542650, 0x27c31edf, 0x266c265e, 0x1ee12672, 0x26741ee3, 0x1ee527c5, 0x27c927c7, 0x268627cb, 0x26901ee7, 0x26962694, 0x269e269a, 0x27cf26a2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//12288 bytes -enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x240], [ 0x100, 0x400, 0x1380], [ 0x2020100, 0x3020202, 0x2020204, 0x2050202, 0x2020202, 0x6020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x10000, 0x30002, 0x50004, 0x6, 0x0, 0x70000, 0x90008, 0xb000a, 0xc0000, 0x0, 0x0, 0xd, 0xe0000, 0x0, 0x0, 0x0, 0x0, 0x10000f, 0x110000, 0x130012, 0x0, 0x140000, 0x160015, 0x170000, 0x180000, 0x190000, 0x1a0000, 0x0, 0x0, 0x1b0000, 0x1c, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f001e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210020, 0x230022, 0x250024, 0x270026, 0x28, 0x0, 0x29, 0x2b002a, 0x2d002c, 0x2f002e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x310000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x320000, 0x340033, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x360035, 0x380037, 0x3a0039, 0x3c003b, 0x3e003d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x410000, 0x430042, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x450044, 0x470046, 0x490048, 0x4b004a, 0x4c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf000c, 0x250012, 0x4f0045, 0x850000, 0xa1009e, 0xcb00a4, 0x121011e, 0x1330124, 0x1880000, 0x1a0019d, 0x1b601a3, 0x1da, 0x26d0000, 0x2730270, 0x2f30287, 0x0, 0x322031f, 0x3380325, 0x3620358, 0x3980000, 0x3b403b1, 0x3de03b7, 0x4370434, 0x446043a, 0x49c0000, 0x4b404b1, 0x4ca04b7, 0x4ee, 0x5840000, 0x58a0587, 0x60d059e, 0x61c0000, 0x33b0028, 0x33e002b, 0x380006d, 0x38c0079, 0x38f007c, 0x392007f, 0x3950082, 0x3a2008f, 0x0, 0x3cd00ba, 0x3d800c5, 0x3db00c8, 0x3fb00e8, 0x3e400d1, 0x40a00f7, 0x41000fd, 0x4130100, 0x4190106, 0x41c0109, 0x0, 0x43d0127, 0x440012a, 0x443012d, 0x45c0149, 0x130, 0x0, 0x462014f, 0x471015d, 0x1630000, 0x1700477, 0x1660484, 0x47a, 0x0, 0x1850000, 0x1940499, 0x18e04a8, 0x4a2, 0x0, 0x4d901c5, 0x4e401d0, 0x4f801e4, 0x0, 0x52f021b, 0x5450231, 0x5350221, 0x54b0237, 0x552023e, 0x5690255, 0x5580244, 0x57b0264, 0x572025b, 0x0, 0x58d0276, 0x594027d, 0x59b0284, 0x5b4029d, 0x5b702a0, 0x5e002c9, 0x5f502de, 0x61002f6, 0x30b0302, 0x3110628, 0x314062e, 0x631, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50401f0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2ac0000, 0x5c3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x560000, 0x13d0369, 0x1e70450, 0x2a304fb, 0x29205ba, 0x28e05a9, 0x29605a5, 0x28a05ad, 0x5a1, 0x35b0048, 0x3540041, 0x653064a, 0x0, 0x4160103, 0x46b0157, 0x522020e, 0x5250211, 0x65f065c, 0x465, 0x0, 0x40700f4, 0x0, 0x4960182, 0x3650052, 0x6500647, 0x656064d, 0x36c0059, 0x36f005c, 0x3e700d4, 0x3ea00d7, 0x4530140, 0x4560143, 0x4fe01ea, 0x50101ed, 0x5380224, 0x53b0227, 0x5bd02a6, 0x5c002a9, 0x5660252, 0x5780261, 0x0, 0x4250112, 0x0, 0x0, 0x0, 0x351003e, 0x3f400e1, 0x4f101dd, 0x4d101bd, 0x4e701d3, 0x4ea01d6, 0x61602fc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000d, 0x66b0000, 0x137, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x662, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x63d0000, 0x6450670, 0x6df06c3, 0x72c, 0x759, 0x7980778, 0x8d1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7810735, 0x84707e9, 0x8c10867, 0x92f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92808ca, 0x91f08fd, 0x95f, 0x0, 0x9b40000, 0x9b7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9cc09c6, 0x9c30000, 0x0, 0x9ba0000, 0x0, 0x0, 0x9d809e4, 0x9ed, 0x0, 0x0, 0x0, 0x0, 0x9de0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0e0a08, 0xa050000, 0x0, 0xa410000, 0x0, 0x0, 0xa1a0a26, 0xa2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa470a44, 0x0, 0x0, 0x0, 0x0, 0x9cf0000, 0xa11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9ff09bd, 0xa0209c0, 0x0, 0xa0b09c9, 0x0, 0xa4d0a4a, 0xa1409d2, 0xa1709d5, 0x0, 0xa1d09db, 0xa2309e1, 0xa2909e7, 0x0, 0xa530a50, 0xa3e09fc, 0xa2c09ea, 0xa3209f0, 0xa3509f3, 0xa3809f6, 0x0, 0xa3b09f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac10abe, 0xac40ac7, 0xaca, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xad3, 0xacd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xad00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae80000, 0x0, 0x0, 0x0, 0xaf10000, 0x0, 0xaf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xad90ad6, 0xadf0adc, 0xae50ae2, 0xaee0aeb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb000000, 0xb03, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xafa0af7, 0xafd0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb120000, 0x0, 0xb15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb060000, 0xb0c0b09, 0x0, 0xb0f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb21, 0xb1e0000, 0xb24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb1b0b18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb300b2a, 0xb2d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb36, 0x0, 0x0, 0xb400000, 0xb43, 0xb3c0b39, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4c0b46, 0xb49, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb4f, 0xb550b52, 0xb59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb5f0000, 0x0, 0x0, 0x0, 0x0, 0xb620000, 0x0, 0x0, 0xb65, 0x0, 0xb680000, 0x0, 0x0, 0xb6b, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb5c0000, 0x0, 0x0, 0x0, 0x0, 0xb6e0000, 0xb710000, 0xb89, 0xb8c, 0x0, 0x0, 0x0, 0xb740000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb7a0000, 0x0, 0x0, 0x0, 0x0, 0xb7d0000, 0x0, 0x0, 0xb80, 0x0, 0xb830000, 0x0, 0x0, 0xb86, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb770000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb92, 0xb95, 0xb98, 0xb9b, 0xb9e, 0x0, 0xba1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xba40000, 0xba70000, 0x0, 0xbad0baa, 0xbb00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37d006a, 0x3830070, 0x3860073, 0x3890076, 0x39b0088, 0x39f008c, 0x3a50092, 0x3ae009b, 0x3a80095, 0x3ab0098, 0x3d000bd, 0x3d400c1, 0x3fe00eb, 0x40100ee, 0x3f700e4, 0x40400f1, 0x40d00fa, 0x41f010c, 0x4280115, 0x422010f, 0x42b0118, 0x42e011b, 0x45f014c, 0x4490136, 0x4680154, 0x46e015a, 0x4740160, 0x47d0169, 0x480016c, 0x48a0176, 0x4870173, 0x48d0179, 0x490017c, 0x493017f, 0x49f018b, 0x4a50191, 0x4ae019a, 0x4ab0197, 0x4cd01b9, 0x4d501c1, 0x4dc01c8, 0x4e001cc, 0x5290215, 0x52c0218, 0x532021e, 0x53e022a, 0x541022d, 0x5480234, 0x5550241, 0x55f024b, 0x54e023a, 0x55b0247, 0x562024e, 0x56c0258, 0x575025e, 0x581026a, 0x57e0267, 0x5dd02c6, 0x5e602cf, 0x5e302cc, 0x5900279, 0x5970280, 0x5e902d2, 0x5ec02d5, 0x5ef02d8, 0x5f202db, 0x5fb02e4, 0x5f802e1, 0x60102e7, 0x60402ea, 0x60702ed, 0x61902ff, 0x62b030e, 0x6340317, 0x637031a, 0x56f0431, 0x62205fe, 0x6590000, 0x0, 0x0, 0x372005f, 0x35f004c, 0x32c0019, 0x3280015, 0x3340021, 0x330001d, 0x3750062, 0x3450032, 0x341002e, 0x34d003a, 0x3490036, 0x3790066, 0x3ed00da, 0x3e100ce, 0x3ca00b7, 0x3be00ab, 0x3ba00a7, 0x3c600b3, 0x3c200af, 0x3f000dd, 0x44d013a, 0x4590146, 0x51b0207, 0x4f501e1, 0x4be01aa, 0x4ba01a6, 0x4c601b2, 0x4c201ae, 0x51e020a, 0x50b01f7, 0x50701f3, 0x51301ff, 0x50f01fb, 0x5170203, 0x5da02c3, 0x5b1029a, 0x5ca02b3, 0x5c602af, 0x5d202bb, 0x5ce02b7, 0x5d602bf, 0x60a02f0, 0x6250308, 0x61f0305, 0x61302f9, 0x0, 0x0, 0x0, 0x81807f6, 0x81b07f9, 0x8240802, 0x82d080b, 0x69b0679, 0x69e067c, 0x6a70685, 0x6b0068e, 0x855084a, 0x858084d, 0x85c0851, 0x0, 0x6d106c6, 0x6d406c9, 0x6d806cd, 0x0, 0x890086e, 0x8930871, 0x89c087a, 0x8a50883, 0x70406e2, 0x70706e5, 0x71006ee, 0x71906f7, 0x8e808d9, 0x8eb08dc, 0x8ef08e0, 0x8f308e4, 0x7470738, 0x74a073b, 0x74e073f, 0x7520743, 0x90b0900, 0x90e0903, 0x9120907, 0x0, 0x767075c, 0x76a075f, 0x76e0763, 0x0, 0x9460937, 0x949093a, 0x94d093e, 0x9510942, 0x7840000, 0x7870000, 0x78b0000, 0x78f0000, 0x9880966, 0x98b0969, 0x9940972, 0x99d097b, 0x7bd079b, 0x7c0079e, 0x7c907a7, 0x7d207b0, 0x7e907e2, 0x8470844, 0x8670860, 0x8c108be, 0x8fd08fa, 0x91f091c, 0x95f0958, 0x0, 0x8360814, 0x81f07fd, 0x8280806, 0x831080f, 0x6b90697, 0x6a20680, 0x6ab0689, 0x6b40692, 0x8ae088c, 0x8970875, 0x8a0087e, 0x8a90887, 0x7220700, 0x70b06e9, 0x71406f2, 0x71d06fb, 0x9a60984, 0x98f096d, 0x9980976, 0x9a1097f, 0x7db07b9, 0x7c407a2, 0x7cd07ab, 0x7d607b4, 0x7f007f3, 0x84107e5, 0x7ec, 0x83d083a, 0x6730676, 0x670066d, 0x6bd, 0x8bc, 0x6400000, 0x8b90863, 0x86a, 0x8b508b2, 0x6c306c0, 0x6df06dc, 0xbb30726, 0xbb90bb6, 0x8c408c7, 0x8d108cd, 0x0, 0x8d508f7, 0x72f0732, 0x72c0729, 0xbbc0000, 0xbc20bbf, 0x9220925, 0x92f092b, 0x9190916, 0x9330955, 0x77b077e, 0x7780775, 0x63a0772, 0x31d063d, 0x0, 0x9b1095b, 0x962, 0x9ad09aa, 0x7590756, 0x7980795, 0x64307df, 0x0, 0xbc70bc5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x793, 0x0, 0x4f0152, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbcc0bc9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbcf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd20000, 0xbd50bd8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbdb, 0x0, 0xbde0000, 0x0, 0xbe1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe4, 0xbe7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbea0000, 0x0, 0xbed, 0xbf00000, 0xbf30000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xbf9, 0x0, 0x0, 0x0, 0x0, 0xbf60000, 0x90003, 0xbff0bfc, 0x0, 0xc050c02, 0x0, 0xc0b0c08, 0x0, 0x0, 0x0, 0xc110c0e, 0x0, 0xc1d0c1a, 0x0, 0xc230c20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc2f0c2c, 0xc350c32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc170c14, 0xc290c26, 0x0, 0x0, 0x0, 0xc3b0c38, 0xc410c3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc470000, 0xc49, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc4e, 0xc51, 0xc54, 0xc57, 0xc5a, 0xc5d, 0xc60, 0xc63, 0xc66, 0xc69, 0xc6c, 0xc6f, 0xc720000, 0xc750000, 0xc780000, 0x0, 0x0, 0x0, 0xc7e0c7b, 0xc810000, 0xc84, 0xc8a0c87, 0xc8d0000, 0xc90, 0xc960c93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc4b, 0x0, 0x0, 0x0, 0x0, 0xc99, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc9f, 0xca2, 0xca5, 0xca8, 0xcab, 0xcae, 0xcb1, 0xcb4, 0xcb7, 0xcba, 0xcbd, 0xcc0, 0xcc30000, 0xcc60000, 0xcc90000, 0x0, 0x0, 0x0, 0xccf0ccc, 0xcd20000, 0xcd5, 0xcdb0cd8, 0xcde0000, 0xce1, 0xce70ce4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc9c, 0xcea0000, 0xcf00ced, 0xcf3, 0x0, 0xcf6, 0xfb71241, 0x124b125d, 0xd831043, 0x13270e29, 0xe991327, 0xe4f1293, 0xf550e97, 0x116710cd, 0x11fd11e3, 0x12791215, 0x10190feb, 0x109d1069, 0x128911c7, 0xd8d12f3, 0xff50e1d, 0x11e11079, 0xedb1309, 0x11d91051, 0xf65121d, 0x12031189, 0xfbd0eff, 0x108d1025, 0xd9d127d, 0xe050dd9, 0xff10f95, 0x10d31077, 0x11dd1171, 0x125911e7, 0x12fb12cf, 0x10e91307, 0x114d1107, 0x12a111b9, 0x122f130b, 0xf0b0e87, 0x117d112f, 0x10ed1083, 0x12cb1249, 0xecb0e85, 0x102f0fed, 0x11471047, 0x12b11159, 0x117f0e03, 0xddd0ddf, 0x114f1115, 0x12b511c5, 0xf67123d, 0x12350feb, 0xebb0d87, 0x10950f27, 0xe1110c1, 0xda510f1, 0xd7f0f1b, 0xf9d1011, 0xe231145, 0x10d70e7d, 0x122711c9, 0x126d1005, 0xf6f100d, 0xf7b11a5, 0xd9110bf, 0xddb0dc3, 0x113d0fdb, 0x122d1195, 0xe091291, 0xe9f0e37, 0xfa10f07, 0x10f31053, 0x12f712ab, 0x1313130d, 0xfb50df9, 0x12690ffd, 0xf490ef3, 0xf910f57, 0x106d104b, 0x111110af, 0x11791153, 0x11cd1261, 0x12a31271, 0xdfb0de9, 0x10670e41, 0x1227120b, 0xf230efd, 0x10030f77, 0x1091112d, 0xe670d97, 0xee50ebb, 0x109b0f29, 0x116b10a9, 0x12951175, 0x12d112c9, 0xd9f12dd, 0x128d110f, 0xf3512c1, 0xdb10d8f, 0xec70ebd, 0xfeb0f9f, 0x10cb1073, 0x127711d3, 0xfad1323, 0xdf712af, 0xfd10fcb, 0x103b1021, 0x10bd10a1, 0x114310e7, 0xdc512e3, 0x12b70f5d, 0xed70da9, 0x12631031, 0xf390f17, 0x10950fd5, 0xdeb12bb, 0xecf0e31, 0xfc30fa7, 0x10150fe1, 0x10c3109f, 0x120d1163, 0x128f1213, 0xe1312c5, 0xe33103d, 0x10b11075, 0x12bd11db, 0x130f12ff, 0x102d0fcf, 0x1121118b, 0x11331125, 0x1063108b, 0xd93123b, 0xded11ad, 0xef50de7, 0x11390f69, 0x101b0eb5, 0x12670fb3, 0x12b31205, 0xf031221, 0xe590db5, 0x0, 0xe7b, 0xfab, 0xde10000, 0x10cf108f, 0x110310f5, 0x110d1105, 0x113512d3, 0x116d, 0x11df, 0x1233, 0x12730000, 0x1283, 0x0, 0x12e912e7, 0x130512eb, 0x12bf127f, 0xdb30da1, 0xe010db9, 0xe170e07, 0xe5f0e53, 0xe790e63, 0xecd0e7f, 0xf2f0ed1, 0xf470f43, 0xf970f53, 0xfaf0fa3, 0x10270fdd, 0x10491035, 0x107d106f, 0x10eb10a3, 0x10fb10f7, 0x10fd10f9, 0x110110ff, 0x110b1109, 0x111d1117, 0x11531127, 0x115b1157, 0x11731161, 0x1197118d, 0x11cb1197, 0x12231219, 0x12391237, 0x124f124d, 0x1273126f, 0x12d912c7, 0xf2b12e1, 0x119313be, 0x0, 0xdd70d81, 0xd9b0dc1, 0xdc90db7, 0xe0b0dff, 0xe490e53, 0xe5d0e51, 0xe830e7b, 0xe9b0e95, 0xeb10ea9, 0xf050f01, 0xf1d0f13, 0xf3f0f33, 0xf470f37, 0xf530f41, 0xf7f0f5f, 0xf890f85, 0xfab0f99, 0xfbf0fbd, 0xfff0fc7, 0x10211005, 0x10411045, 0x10571049, 0x10e3106f, 0x1089107f, 0x10ab108f, 0x10b910b5, 0x10c910c7, 0x10d110cf, 0x10df10d5, 0x10ef10dd, 0x1127111f, 0x11491131, 0x115f1153, 0x11af1173, 0x11f911c3, 0x121f121b, 0x12291223, 0x122b1233, 0x12351237, 0x12391231, 0x124f123f, 0x12751265, 0x1299128b, 0x12c712b9, 0x12d512d3, 0x12db12d9, 0x12f912e1, 0x13941327, 0x13a61392, 0xd370d23, 0x13da0d39, 0x141c13ec, 0x13251321, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa7a0000, 0xabb0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xab50ab2, 0xaae0aaa, 0xa590a56, 0xa5f0a5c, 0xa680a65, 0xa710a6b, 0xa74, 0xa7d0a77, 0xa830a80, 0xa89, 0xa8c, 0xa920a8f, 0xa950000, 0xa98, 0xaa10a9e, 0xaa70aa4, 0xa6e0ab8, 0xa860a62, 0xa9b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1329, 0x132c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x132f0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13351332, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x133b1338, 0x1342133e, 0x134a1346, 0x134e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13520000, 0x1355135d, 0x13591360, 0x1364, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd850d89, 0x13680d8b, 0xda10d99, 0xda70da3, 0xdad0dab, 0xdaf0db3, 0x13700cf9, 0xdbb0db9, 0xdc70dbd, 0xcfb136a, 0xdcb0dbf, 0xdd1136e, 0xd950dd3, 0xdd70dd5, 0xde3142c, 0xcff0de5, 0xdf10def, 0xdf50df3, 0xdff0d01, 0xe070e01, 0xe0d0e0b, 0xe110e0f, 0xe170e15, 0xe1b0e19, 0xe210e1f, 0xe210e21, 0x105d1376, 0xe270e25, 0xe2b1378, 0xe2f0e2d, 0xe350e3d, 0xe3b0e39, 0xe430e3f, 0xe470e45, 0xe4d0e4b, 0xe510e4d, 0xe570e55, 0xe690e5b, 0xe6b0e5f, 0xe650e61, 0xe890de7, 0xe710e6f, 0xe6d0e73, 0xe750e77, 0x137a0e81, 0xe8d0e8b, 0xe910e8f, 0xe9d0e93, 0x137e137c, 0xea50ea3, 0xea10ea7, 0xd030eab, 0xeaf0d05, 0xeb30eb3, 0xeb71380, 0xebb0eb9, 0x13820ebf, 0xec30ec1, 0xec50f0f, 0xec90d07, 0xed50ed1, 0x13860ed3, 0x13880ed9, 0xedd0edf, 0xee70ee1, 0xd090ee9, 0xeed0eeb, 0xef10eef, 0x138a0d0b, 0xef70d0d, 0xefb0ef9, 0x14400eff, 0x138e0f09, 0x118f138e, 0xf0d0f0d, 0x139c0d0f, 0xf1113f0, 0xd110f15, 0xf1f0f19, 0xf250f21, 0xd150f2d, 0xf2f0d13, 0xf311390, 0xf3d0f3b, 0xf3d0f3f, 0xf470f45, 0xf4b0f4f, 0xf510f4d, 0xf550f53, 0xf5b0f59, 0xf630f61, 0xf730f6b, 0xf711396, 0xf750f6d, 0xf830f79, 0xf871398, 0xf7d0f81, 0xf8b0d17, 0xf930f8f, 0xd190f8d, 0xf9b0f97, 0xfa5139a, 0xfa90fb9, 0xfaf0d1f, 0xd1b0d1d, 0xdcf0dcd, 0xfb10fbb, 0xd511181, 0xfbf0fbd, 0xfc90fc1, 0x13a40fc5, 0xfd30d21, 0xfd90fcd, 0x13a80fdd, 0xfd70fdf, 0xd230fe3, 0xfe70fe5, 0xfef0fe9, 0xff313aa, 0xff70d25, 0xff913ac, 0xffb0d27, 0x10051001, 0x13ae1007, 0x13b01384, 0x13b21009, 0x1013100f, 0x1017100b, 0x1027101f, 0x10231021, 0x102b1029, 0x101d13b4, 0x10391037, 0x10410d29, 0x13b6103f, 0x104d1033, 0x13ba13b8, 0x1059104f, 0x10551057, 0x105b0d2b, 0x105f1061, 0x136c1065, 0x13bc106b, 0x13c01071, 0x107f107b, 0x13c21081, 0x10871085, 0x13c613c4, 0x10971093, 0x10990d2d, 0xd2f0d2f, 0x10a710a5, 0x10ad10ab, 0xd3110b3, 0x13c810b7, 0x13ca10bb, 0x138c10c1, 0x13cc10c5, 0x13d013ce, 0xd350d33, 0x13d410d5, 0x13d613d2, 0x10d913d8, 0x10db10db, 0xd3910df, 0xd3b10e1, 0x13dc0d3d, 0x10e910e5, 0xd3f10ef, 0x10ff13de, 0x13e213e0, 0x1113110d, 0x11170d41, 0x111b1119, 0x13e613e4, 0x112313e6, 0x13e80d43, 0x112b1129, 0x13ea0d45, 0xd471137, 0x113b113f, 0x13ee1141, 0xd49114b, 0x11551151, 0xd4b115d, 0x13f413f2, 0x13f60d4d, 0x13f81165, 0x116f1169, 0x13fa1173, 0x117713fc, 0x117b13fe, 0xd4f139e, 0x11851183, 0x11870d53, 0x14000ead, 0x13a01402, 0x118f13a2, 0x126b1191, 0x119b0d55, 0x119d1199, 0x119f0dfd, 0x11a311a1, 0x140411a7, 0x11a911a5, 0x11b511b3, 0x11b711ab, 0x11cb11c1, 0x11bb11b1, 0x11bf11bd, 0x140a1406, 0xd571408, 0x11d111cf, 0x141211d5, 0x140c11d7, 0xd5b0d59, 0x1410140e, 0x11e50d5d, 0x11e911e7, 0x11ef11eb, 0x11f311ed, 0x11f911f1, 0x11f711f5, 0xd5f11fb, 0x120111ff, 0x12070d61, 0x14141209, 0x1211120f, 0x12170d63, 0x14160cfd, 0xd651418, 0x12250d67, 0x123f1231, 0x141a1243, 0x12471245, 0x12531251, 0x1372141e, 0x12551257, 0x1374125b, 0x1265125f, 0x14221420, 0x1281127b, 0x14241285, 0x12971287, 0x129f129d, 0x12a5129b, 0x142612a7, 0xd6912a9, 0x142812ad, 0x12c30d6b, 0x12cd0ee3, 0x142e142a, 0xd6f0d6d, 0x143012d7, 0x14320d71, 0x12db12db, 0x143412df, 0xd7312e5, 0x12ef12ed, 0x12f512f1, 0x14360d75, 0x12fd12f9, 0xd771301, 0x13030d79, 0xd7b1438, 0x143c143a, 0x1311143e, 0x13150d7d, 0x13191317, 0x131d131b, 0x1442131f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -@property +static if (size_t.sizeof == 4) { -private alias _IDCA = immutable(dchar[]); -_IDCA decompCanonTable() { static _IDCA t = [ 0x0, 0x3b, 0x0, 0x3c, 0x338, 0x0, 0x3d, 0x338, 0x0, 0x3e, 0x338, 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, 0x0, 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, 0x302, 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, 0x41, 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, 0x41, 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, 0x309, 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, 0x308, 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, 0x30a, 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, 0x30f, 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, 0x302, 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, 0x328, 0x0, 0x42, 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, 0x0, 0x43, 0x301, 0x0, 0x43, 0x302, 0x0, 0x43, 0x307, 0x0, 0x43, 0x30c, 0x0, 0x43, 0x327, 0x0, 0x43, 0x327, 0x301, 0x0, 0x44, 0x307, 0x0, 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, 0x327, 0x0, 0x44, 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x300, 0x0, 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, 0x0, 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, 0x302, 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, 0x304, 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, 0x45, 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, 0x30c, 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, 0x0, 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, 0x306, 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, 0x0, 0x46, 0x307, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, 0x47, 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, 0x30c, 0x0, 0x47, 0x327, 0x0, 0x48, 0x302, 0x0, 0x48, 0x307, 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, 0x0, 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x300, 0x0, 0x49, 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x302, 0x0, 0x4b, 0x0, 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x301, 0x0, 0x4c, 0x30c, 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, 0x0, 0x4c, 0x327, 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, 0x4d, 0x301, 0x0, 0x4d, 0x307, 0x0, 0x4d, 0x323, 0x0, 0x4e, 0x300, 0x0, 0x4e, 0x301, 0x0, 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, 0x0, 0x4e, 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, 0x4e, 0x331, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, 0x328, 0x304, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, 0x52, 0x301, 0x0, 0x52, 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, 0x0, 0x52, 0x311, 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, 0x0, 0x52, 0x327, 0x0, 0x52, 0x331, 0x0, 0x53, 0x301, 0x0, 0x53, 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, 0x53, 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, 0x53, 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, 0x54, 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, 0x331, 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, 0x55, 0x302, 0x0, 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, 0x55, 0x304, 0x0, 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, 0x55, 0x308, 0x0, 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, 0x0, 0x55, 0x308, 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, 0x309, 0x0, 0x55, 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, 0x0, 0x55, 0x30f, 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, 0x55, 0x31b, 0x300, 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, 0x303, 0x0, 0x55, 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, 0x55, 0x323, 0x0, 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, 0x32d, 0x0, 0x55, 0x330, 0x0, 0x56, 0x303, 0x0, 0x56, 0x323, 0x0, 0x57, 0x300, 0x0, 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, 0x57, 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x307, 0x0, 0x58, 0x308, 0x0, 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, 0x0, 0x59, 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, 0x59, 0x308, 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x60, 0x0, 0x61, 0x300, 0x0, 0x61, 0x301, 0x0, 0x61, 0x302, 0x0, 0x61, 0x302, 0x300, 0x0, 0x61, 0x302, 0x301, 0x0, 0x61, 0x302, 0x303, 0x0, 0x61, 0x302, 0x309, 0x0, 0x61, 0x303, 0x0, 0x61, 0x304, 0x0, 0x61, 0x306, 0x0, 0x61, 0x306, 0x300, 0x0, 0x61, 0x306, 0x301, 0x0, 0x61, 0x306, 0x303, 0x0, 0x61, 0x306, 0x309, 0x0, 0x61, 0x307, 0x0, 0x61, 0x307, 0x304, 0x0, 0x61, 0x308, 0x0, 0x61, 0x308, 0x304, 0x0, 0x61, 0x309, 0x0, 0x61, 0x30a, 0x0, 0x61, 0x30a, 0x301, 0x0, 0x61, 0x30c, 0x0, 0x61, 0x30f, 0x0, 0x61, 0x311, 0x0, 0x61, 0x323, 0x0, 0x61, 0x323, 0x302, 0x0, 0x61, 0x323, 0x306, 0x0, 0x61, 0x325, 0x0, 0x61, 0x328, 0x0, 0x62, 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x301, 0x0, 0x63, 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, 0x63, 0x327, 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x307, 0x0, 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, 0x64, 0x327, 0x0, 0x64, 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, 0x300, 0x0, 0x65, 0x301, 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, 0x65, 0x302, 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, 0x309, 0x0, 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, 0x300, 0x0, 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, 0x307, 0x0, 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, 0x0, 0x65, 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, 0x65, 0x323, 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, 0x0, 0x65, 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, 0x66, 0x307, 0x0, 0x67, 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, 0x67, 0x306, 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, 0x327, 0x0, 0x68, 0x302, 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, 0x0, 0x68, 0x30c, 0x0, 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, 0x68, 0x32e, 0x0, 0x68, 0x331, 0x0, 0x69, 0x300, 0x0, 0x69, 0x301, 0x0, 0x69, 0x302, 0x0, 0x69, 0x303, 0x0, 0x69, 0x304, 0x0, 0x69, 0x306, 0x0, 0x69, 0x308, 0x0, 0x69, 0x308, 0x301, 0x0, 0x69, 0x309, 0x0, 0x69, 0x30c, 0x0, 0x69, 0x30f, 0x0, 0x69, 0x311, 0x0, 0x69, 0x323, 0x0, 0x69, 0x328, 0x0, 0x69, 0x330, 0x0, 0x6a, 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x301, 0x0, 0x6b, 0x30c, 0x0, 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, 0x6b, 0x331, 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, 0x323, 0x0, 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, 0x32d, 0x0, 0x6c, 0x331, 0x0, 0x6d, 0x301, 0x0, 0x6d, 0x307, 0x0, 0x6d, 0x323, 0x0, 0x6e, 0x300, 0x0, 0x6e, 0x301, 0x0, 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, 0x0, 0x6e, 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, 0x6e, 0x331, 0x0, 0x6f, 0x300, 0x0, 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, 0x6f, 0x302, 0x300, 0x0, 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, 0x303, 0x0, 0x6f, 0x302, 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, 0x303, 0x301, 0x0, 0x6f, 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, 0x0, 0x6f, 0x304, 0x0, 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, 0x301, 0x0, 0x6f, 0x306, 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, 0x304, 0x0, 0x6f, 0x308, 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, 0x309, 0x0, 0x6f, 0x30b, 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, 0x0, 0x6f, 0x311, 0x0, 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, 0x0, 0x6f, 0x31b, 0x301, 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, 0x31b, 0x309, 0x0, 0x6f, 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, 0x6f, 0x323, 0x302, 0x0, 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, 0x0, 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x72, 0x301, 0x0, 0x72, 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, 0x311, 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, 0x327, 0x0, 0x72, 0x331, 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, 0x307, 0x0, 0x73, 0x302, 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, 0x0, 0x73, 0x30c, 0x307, 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, 0x307, 0x0, 0x73, 0x326, 0x0, 0x73, 0x327, 0x0, 0x74, 0x307, 0x0, 0x74, 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, 0x74, 0x326, 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, 0x331, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, 0x75, 0x330, 0x0, 0x76, 0x303, 0x0, 0x76, 0x323, 0x0, 0x77, 0x300, 0x0, 0x77, 0x301, 0x0, 0x77, 0x302, 0x0, 0x77, 0x307, 0x0, 0x77, 0x308, 0x0, 0x77, 0x30a, 0x0, 0x77, 0x323, 0x0, 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x300, 0x0, 0x79, 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, 0x303, 0x0, 0x79, 0x304, 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, 0x0, 0x79, 0x309, 0x0, 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, 0x7a, 0x301, 0x0, 0x7a, 0x302, 0x0, 0x7a, 0x307, 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, 0x0, 0x7a, 0x331, 0x0, 0xa8, 0x300, 0x0, 0xa8, 0x301, 0x0, 0xa8, 0x342, 0x0, 0xb4, 0x0, 0xb7, 0x0, 0xc6, 0x301, 0x0, 0xc6, 0x304, 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, 0x0, 0xf8, 0x301, 0x0, 0x17f, 0x307, 0x0, 0x1b7, 0x30c, 0x0, 0x292, 0x30c, 0x0, 0x2b9, 0x0, 0x300, 0x0, 0x301, 0x0, 0x308, 0x301, 0x0, 0x313, 0x0, 0x391, 0x300, 0x0, 0x391, 0x301, 0x0, 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, 0x313, 0x0, 0x391, 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, 0x345, 0x0, 0x391, 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, 0x345, 0x0, 0x391, 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, 0x345, 0x0, 0x391, 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, 0x314, 0x300, 0x0, 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, 0x314, 0x301, 0x0, 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, 0x314, 0x342, 0x0, 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, 0x314, 0x345, 0x0, 0x391, 0x345, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, 0x395, 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, 0x301, 0x0, 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, 0x314, 0x301, 0x0, 0x397, 0x300, 0x0, 0x397, 0x301, 0x0, 0x397, 0x313, 0x0, 0x397, 0x313, 0x300, 0x0, 0x397, 0x313, 0x300, 0x345, 0x0, 0x397, 0x313, 0x301, 0x0, 0x397, 0x313, 0x301, 0x345, 0x0, 0x397, 0x313, 0x342, 0x0, 0x397, 0x313, 0x342, 0x345, 0x0, 0x397, 0x313, 0x345, 0x0, 0x397, 0x314, 0x0, 0x397, 0x314, 0x300, 0x0, 0x397, 0x314, 0x300, 0x345, 0x0, 0x397, 0x314, 0x301, 0x0, 0x397, 0x314, 0x301, 0x345, 0x0, 0x397, 0x314, 0x342, 0x0, 0x397, 0x314, 0x342, 0x345, 0x0, 0x397, 0x314, 0x345, 0x0, 0x397, 0x345, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, 0x0, 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, 0x399, 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, 0x301, 0x0, 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, 0x314, 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, 0x342, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, 0x3a1, 0x314, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, 0x314, 0x342, 0x0, 0x3a9, 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, 0x0, 0x3a9, 0x313, 0x0, 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, 0x300, 0x345, 0x0, 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, 0x301, 0x345, 0x0, 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, 0x342, 0x345, 0x0, 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, 0x3a9, 0x314, 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, 0x3a9, 0x314, 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, 0x3a9, 0x314, 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, 0x3a9, 0x314, 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x300, 0x0, 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, 0x345, 0x0, 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, 0x0, 0x3b5, 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, 0x314, 0x0, 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, 0x301, 0x0, 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, 0x313, 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, 0x313, 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, 0x313, 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, 0x3b7, 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, 0x3b7, 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, 0x3b7, 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, 0x3b7, 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, 0x3b9, 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, 0x3b9, 0x304, 0x0, 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, 0x308, 0x300, 0x0, 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, 0x342, 0x0, 0x3b9, 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, 0x313, 0x301, 0x0, 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, 0x3b9, 0x314, 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, 0x314, 0x342, 0x0, 0x3b9, 0x342, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, 0x313, 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, 0x3bf, 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, 0x301, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, 0x3c5, 0x300, 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, 0x3c9, 0x300, 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, 0x3c9, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, 0x300, 0x0, 0x3c9, 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, 0x301, 0x0, 0x3c9, 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x342, 0x0, 0x3c9, 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, 0x345, 0x0, 0x3c9, 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, 0x314, 0x300, 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, 0x314, 0x301, 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, 0x314, 0x342, 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, 0x342, 0x0, 0x3c9, 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3d2, 0x301, 0x0, 0x3d2, 0x308, 0x0, 0x406, 0x308, 0x0, 0x410, 0x306, 0x0, 0x410, 0x308, 0x0, 0x413, 0x301, 0x0, 0x415, 0x300, 0x0, 0x415, 0x306, 0x0, 0x415, 0x308, 0x0, 0x416, 0x306, 0x0, 0x416, 0x308, 0x0, 0x417, 0x308, 0x0, 0x418, 0x300, 0x0, 0x418, 0x304, 0x0, 0x418, 0x306, 0x0, 0x418, 0x308, 0x0, 0x41a, 0x301, 0x0, 0x41e, 0x308, 0x0, 0x423, 0x304, 0x0, 0x423, 0x306, 0x0, 0x423, 0x308, 0x0, 0x423, 0x30b, 0x0, 0x427, 0x308, 0x0, 0x42b, 0x308, 0x0, 0x42d, 0x308, 0x0, 0x430, 0x306, 0x0, 0x430, 0x308, 0x0, 0x433, 0x301, 0x0, 0x435, 0x300, 0x0, 0x435, 0x306, 0x0, 0x435, 0x308, 0x0, 0x436, 0x306, 0x0, 0x436, 0x308, 0x0, 0x437, 0x308, 0x0, 0x438, 0x300, 0x0, 0x438, 0x304, 0x0, 0x438, 0x306, 0x0, 0x438, 0x308, 0x0, 0x43a, 0x301, 0x0, 0x43e, 0x308, 0x0, 0x443, 0x304, 0x0, 0x443, 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, 0x0, 0x447, 0x308, 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, 0x456, 0x308, 0x0, 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, 0x308, 0x0, 0x4d9, 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, 0x0, 0x5d0, 0x5b7, 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x5bc, 0x0, 0x5d3, 0x5bc, 0x0, 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, 0x0, 0x5d6, 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, 0x5d9, 0x5bc, 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, 0x5bf, 0x0, 0x5dc, 0x5bc, 0x0, 0x5de, 0x5bc, 0x0, 0x5e0, 0x5bc, 0x0, 0x5e1, 0x5bc, 0x0, 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, 0x5e4, 0x5bf, 0x0, 0x5e6, 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, 0x5bc, 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, 0x0, 0x627, 0x655, 0x0, 0x648, 0x654, 0x0, 0x64a, 0x654, 0x0, 0x6c1, 0x654, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, 0x0, 0x915, 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, 0x91c, 0x93c, 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, 0x93c, 0x0, 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, 0x0, 0x933, 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, 0x9af, 0x9bc, 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, 0xa3c, 0x0, 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, 0x0, 0xa32, 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, 0xb22, 0xb3c, 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, 0xb57, 0x0, 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, 0x0, 0xbc7, 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, 0xcc6, 0xcc2, 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, 0xcc6, 0xcd6, 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, 0xd3e, 0x0, 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, 0xdca, 0x0, 0xdd9, 0xddf, 0x0, 0xf40, 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, 0xfb7, 0x0, 0xf51, 0xfb7, 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, 0x0, 0xf71, 0xf72, 0x0, 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, 0xf90, 0xfb5, 0x0, 0xf92, 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, 0xfb7, 0x0, 0xfa6, 0xfb7, 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf80, 0x0, 0xfb3, 0xf80, 0x0, 0x1025, 0x102e, 0x0, 0x1b05, 0x1b35, 0x0, 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, 0x0, 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, 0x0, 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, 0x0, 0x1b42, 0x1b35, 0x0, 0x1fbf, 0x300, 0x0, 0x1fbf, 0x301, 0x0, 0x1fbf, 0x342, 0x0, 0x1ffe, 0x300, 0x0, 0x1ffe, 0x301, 0x0, 0x1ffe, 0x342, 0x0, 0x2002, 0x0, 0x2003, 0x0, 0x2190, 0x338, 0x0, 0x2192, 0x338, 0x0, 0x2194, 0x338, 0x0, 0x21d0, 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, 0x0, 0x2203, 0x338, 0x0, 0x2208, 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2223, 0x338, 0x0, 0x2225, 0x338, 0x0, 0x223c, 0x338, 0x0, 0x2243, 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, 0x338, 0x0, 0x224d, 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, 0x338, 0x0, 0x2265, 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, 0x338, 0x0, 0x2276, 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, 0x338, 0x0, 0x227b, 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, 0x338, 0x0, 0x2282, 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, 0x338, 0x0, 0x2287, 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, 0x338, 0x0, 0x22a2, 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, 0x338, 0x0, 0x22ab, 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, 0x338, 0x0, 0x22b4, 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2add, 0x338, 0x0, 0x3008, 0x0, 0x3009, 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, 0x304d, 0x3099, 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, 0x3053, 0x3099, 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, 0x3059, 0x3099, 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, 0x305f, 0x3099, 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, 0x3066, 0x3099, 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, 0x306f, 0x309a, 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, 0x3075, 0x3099, 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, 0x3078, 0x309a, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, 0x0, 0x309d, 0x3099, 0x0, 0x30a6, 0x3099, 0x0, 0x30ab, 0x3099, 0x0, 0x30ad, 0x3099, 0x0, 0x30af, 0x3099, 0x0, 0x30b1, 0x3099, 0x0, 0x30b3, 0x3099, 0x0, 0x30b5, 0x3099, 0x0, 0x30b7, 0x3099, 0x0, 0x30b9, 0x3099, 0x0, 0x30bb, 0x3099, 0x0, 0x30bd, 0x3099, 0x0, 0x30bf, 0x3099, 0x0, 0x30c1, 0x3099, 0x0, 0x30c4, 0x3099, 0x0, 0x30c6, 0x3099, 0x0, 0x30c8, 0x3099, 0x0, 0x30cf, 0x3099, 0x0, 0x30cf, 0x309a, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x309a, 0x0, 0x30d5, 0x3099, 0x0, 0x30d5, 0x309a, 0x0, 0x30d8, 0x3099, 0x0, 0x30d8, 0x309a, 0x0, 0x30db, 0x3099, 0x0, 0x30db, 0x309a, 0x0, 0x30ef, 0x3099, 0x0, 0x30f0, 0x3099, 0x0, 0x30f1, 0x3099, 0x0, 0x30f2, 0x3099, 0x0, 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, 0x0, 0x34bb, 0x0, 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, 0x36fc, 0x0, 0x3781, 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, 0x0, 0x38c7, 0x0, 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, 0x3a2e, 0x0, 0x3a6c, 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, 0x0, 0x3b49, 0x0, 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, 0x3d33, 0x0, 0x3d96, 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, 0x0, 0x3ffc, 0x0, 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, 0x4046, 0x0, 0x4096, 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, 0x0, 0x4227, 0x0, 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, 0x4359, 0x0, 0x43d5, 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, 0x0, 0x452b, 0x0, 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, 0x45d7, 0x0, 0x45f9, 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, 0x0, 0x4995, 0x0, 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, 0x4ab2, 0x0, 0x4b33, 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, 0x0, 0x4cf8, 0x0, 0x4d56, 0x0, 0x4e0d, 0x0, 0x4e26, 0x0, 0x4e32, 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e41, 0x0, 0x4e82, 0x0, 0x4e86, 0x0, 0x4eae, 0x0, 0x4ec0, 0x0, 0x4ecc, 0x0, 0x4ee4, 0x0, 0x4f60, 0x0, 0x4f80, 0x0, 0x4f86, 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, 0x0, 0x4fbf, 0x0, 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, 0x5099, 0x0, 0x50cf, 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x5140, 0x0, 0x5145, 0x0, 0x514d, 0x0, 0x5154, 0x0, 0x5164, 0x0, 0x5167, 0x0, 0x5168, 0x0, 0x5169, 0x0, 0x516d, 0x0, 0x5177, 0x0, 0x5180, 0x0, 0x518d, 0x0, 0x5192, 0x0, 0x5195, 0x0, 0x5197, 0x0, 0x51a4, 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, 0x0, 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51f5, 0x0, 0x5203, 0x0, 0x5207, 0x0, 0x5217, 0x0, 0x5229, 0x0, 0x523a, 0x0, 0x523b, 0x0, 0x5246, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52c7, 0x0, 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, 0x0, 0x52f5, 0x0, 0x52fa, 0x0, 0x5305, 0x0, 0x5306, 0x0, 0x5317, 0x0, 0x533f, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x535a, 0x0, 0x5373, 0x0, 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x53c3, 0x0, 0x53ca, 0x0, 0x53df, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53f1, 0x0, 0x5406, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, 0x54a2, 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x5553, 0x0, 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, 0x5587, 0x0, 0x5599, 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, 0x0, 0x55c0, 0x0, 0x55c2, 0x0, 0x55e2, 0x0, 0x5606, 0x0, 0x5651, 0x0, 0x5668, 0x0, 0x5674, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, 0x0, 0x580d, 0x0, 0x5831, 0x0, 0x5832, 0x0, 0x5840, 0x0, 0x585a, 0x0, 0x585e, 0x0, 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, 0x0, 0x58d8, 0x0, 0x58df, 0x0, 0x58ee, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5906, 0x0, 0x591a, 0x0, 0x5922, 0x0, 0x5944, 0x0, 0x5948, 0x0, 0x5951, 0x0, 0x5954, 0x0, 0x5962, 0x0, 0x5973, 0x0, 0x59d8, 0x0, 0x59ec, 0x0, 0x5a1b, 0x0, 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, 0x0, 0x5ab5, 0x0, 0x5b08, 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, 0x5b85, 0x0, 0x5bc3, 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, 0x0, 0x5bf3, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c22, 0x0, 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, 0x5c64, 0x0, 0x5c65, 0x0, 0x5c6e, 0x0, 0x5c8d, 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, 0x5d43, 0x0, 0x5d50, 0x0, 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, 0x0, 0x5db2, 0x0, 0x5dba, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, 0x5dfd, 0x0, 0x5e28, 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e74, 0x0, 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, 0x5ec9, 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, 0x0, 0x5eec, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f22, 0x0, 0x5f53, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f8b, 0x0, 0x5f9a, 0x0, 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fcd, 0x0, 0x5fd7, 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, 0x0, 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, 0x60d8, 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, 0x0, 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, 0x617a, 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, 0x0, 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, 0x6200, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, 0x6234, 0x0, 0x625d, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x633d, 0x0, 0x6350, 0x0, 0x6368, 0x0, 0x637b, 0x0, 0x6383, 0x0, 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, 0x0, 0x63c5, 0x0, 0x63e4, 0x0, 0x641c, 0x0, 0x6422, 0x0, 0x6452, 0x0, 0x6469, 0x0, 0x6477, 0x0, 0x647e, 0x0, 0x649a, 0x0, 0x649d, 0x0, 0x64c4, 0x0, 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, 0x6599, 0x0, 0x65c5, 0x0, 0x65e2, 0x0, 0x65e3, 0x0, 0x6613, 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, 0x0, 0x6691, 0x0, 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, 0x66f4, 0x0, 0x66f8, 0x0, 0x6700, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, 0x0, 0x677b, 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, 0x67fa, 0x0, 0x6817, 0x0, 0x681f, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b21, 0x0, 0x6b54, 0x0, 0x6b72, 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, 0x0, 0x6bae, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6c4e, 0x0, 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, 0x0, 0x6ccc, 0x0, 0x6ccd, 0x0, 0x6ce5, 0x0, 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, 0x6d34, 0x0, 0x6d3e, 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, 0x0, 0x6d77, 0x0, 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, 0x6dda, 0x0, 0x6dea, 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, 0x0, 0x6e6e, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, 0x6ecb, 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f22, 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, 0x0, 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, 0x704a, 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, 0x0, 0x7099, 0x0, 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, 0x7145, 0x0, 0x7149, 0x0, 0x716e, 0x0, 0x719c, 0x0, 0x71ce, 0x0, 0x71d0, 0x0, 0x7210, 0x0, 0x721b, 0x0, 0x7228, 0x0, 0x722b, 0x0, 0x7235, 0x0, 0x7250, 0x0, 0x7262, 0x0, 0x7280, 0x0, 0x7295, 0x0, 0x72af, 0x0, 0x72c0, 0x0, 0x72fc, 0x0, 0x732a, 0x0, 0x7375, 0x0, 0x737a, 0x0, 0x7387, 0x0, 0x738b, 0x0, 0x73a5, 0x0, 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x7506, 0x0, 0x7524, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, 0x7565, 0x0, 0x7570, 0x0, 0x75e2, 0x0, 0x7610, 0x0, 0x761d, 0x0, 0x761f, 0x0, 0x7642, 0x0, 0x7669, 0x0, 0x76ca, 0x0, 0x76db, 0x0, 0x76e7, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, 0x0, 0x771f, 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, 0x77a7, 0x0, 0x784e, 0x0, 0x786b, 0x0, 0x788c, 0x0, 0x7891, 0x0, 0x78ca, 0x0, 0x78cc, 0x0, 0x78fb, 0x0, 0x792a, 0x0, 0x793c, 0x0, 0x793e, 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, 0x0, 0x7956, 0x0, 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, 0x797f, 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, 0x0, 0x79ca, 0x0, 0x79eb, 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, 0x7a4a, 0x0, 0x7a4f, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, 0x0, 0x7aee, 0x0, 0x7b20, 0x0, 0x7bc0, 0x0, 0x7bc6, 0x0, 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, 0x0, 0x7c7b, 0x0, 0x7c92, 0x0, 0x7cbe, 0x0, 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, 0x7ce7, 0x0, 0x7ce8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, 0x0, 0x7d2f, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, 0x0, 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, 0x7e09, 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f3e, 0x0, 0x7f72, 0x0, 0x7f79, 0x0, 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f95, 0x0, 0x7f9a, 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, 0x8005, 0x0, 0x8046, 0x0, 0x8060, 0x0, 0x806f, 0x0, 0x8070, 0x0, 0x807e, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, 0x8103, 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e8, 0x0, 0x81ed, 0x0, 0x8201, 0x0, 0x8204, 0x0, 0x8218, 0x0, 0x826f, 0x0, 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, 0x82b1, 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, 0x0, 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, 0x8353, 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, 0x0, 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, 0x83ef, 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, 0x0, 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, 0x8516, 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, 0x0, 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x8650, 0x0, 0x865c, 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x8688, 0x0, 0x86a9, 0x0, 0x86e2, 0x0, 0x870e, 0x0, 0x8728, 0x0, 0x876b, 0x0, 0x8779, 0x0, 0x8786, 0x0, 0x87ba, 0x0, 0x87e1, 0x0, 0x8801, 0x0, 0x881f, 0x0, 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, 0x0, 0x88cf, 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, 0x88f8, 0x0, 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x8aa0, 0x0, 0x8aaa, 0x0, 0x8abf, 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, 0x0, 0x8aed, 0x0, 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, 0x8b39, 0x0, 0x8b58, 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c48, 0x0, 0x8c55, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, 0x0, 0x8d77, 0x0, 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, 0x8f38, 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9e, 0x0, 0x8fb0, 0x0, 0x8fb6, 0x0, 0x9023, 0x0, 0x9038, 0x0, 0x9072, 0x0, 0x907c, 0x0, 0x908f, 0x0, 0x9094, 0x0, 0x90ce, 0x0, 0x90de, 0x0, 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, 0x0, 0x916a, 0x0, 0x9199, 0x0, 0x91b4, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, 0x91d1, 0x0, 0x9234, 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, 0x0, 0x92d7, 0x0, 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, 0x93f9, 0x0, 0x9415, 0x0, 0x958b, 0x0, 0x95ad, 0x0, 0x95b7, 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, 0x0, 0x9675, 0x0, 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, 0x96a3, 0x0, 0x96b7, 0x0, 0x96b8, 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, 0x0, 0x9756, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ff, 0x0, 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, 0x0, 0x98e2, 0x0, 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, 0x9929, 0x0, 0x99a7, 0x0, 0x99c2, 0x0, 0x99f1, 0x0, 0x99fe, 0x0, 0x9a6a, 0x0, 0x9b12, 0x0, 0x9b6f, 0x0, 0x9c40, 0x0, 0x9c57, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, 0x9db4, 0x0, 0x9dfa, 0x0, 0x9e1e, 0x0, 0x9e7f, 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, 0x9ebb, 0x0, 0x9ece, 0x0, 0x9ef9, 0x0, 0x9efe, 0x0, 0x9f05, 0x0, 0x9f0f, 0x0, 0x9f16, 0x0, 0x9f3b, 0x0, 0x9f43, 0x0, 0x9f8d, 0x0, 0x9f8e, 0x0, 0x9f9c, 0x0, 0x11099, 0x110ba, 0x0, 0x1109b, 0x110ba, 0x0, 0x110a5, 0x110ba, 0x0, 0x11131, 0x11127, 0x0, 0x11132, 0x11127, 0x0, 0x1d157, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x1d16e, 0x0, 0x1d158, 0x1d165, 0x1d16f, 0x0, 0x1d158, 0x1d165, 0x1d170, 0x0, 0x1d158, 0x1d165, 0x1d171, 0x0, 0x1d158, 0x1d165, 0x1d172, 0x0, 0x1d1b9, 0x1d165, 0x0, 0x1d1b9, 0x1d165, 0x1d16e, 0x0, 0x1d1b9, 0x1d165, 0x1d16f, 0x0, 0x1d1ba, 0x1d165, 0x0, 0x1d1ba, 0x1d165, 0x1d16e, 0x0, 0x1d1ba, 0x1d165, 0x1d16f, 0x0, 0x20122, 0x0, 0x2051c, 0x0, 0x20525, 0x0, 0x2054b, 0x0, 0x2063a, 0x0, 0x20804, 0x0, 0x208de, 0x0, 0x20a2c, 0x0, 0x20b63, 0x0, 0x214e4, 0x0, 0x216a8, 0x0, 0x216ea, 0x0, 0x219c8, 0x0, 0x21b18, 0x0, 0x21d0b, 0x0, 0x21de4, 0x0, 0x21de6, 0x0, 0x22183, 0x0, 0x2219f, 0x0, 0x22331, 0x0, 0x226d4, 0x0, 0x22844, 0x0, 0x2284a, 0x0, 0x22b0c, 0x0, 0x22bf1, 0x0, 0x2300a, 0x0, 0x232b8, 0x0, 0x2335f, 0x0, 0x23393, 0x0, 0x2339c, 0x0, 0x233c3, 0x0, 0x233d5, 0x0, 0x2346d, 0x0, 0x236a3, 0x0, 0x238a7, 0x0, 0x23a8d, 0x0, 0x23afa, 0x0, 0x23cbc, 0x0, 0x23d1e, 0x0, 0x23ed1, 0x0, 0x23f5e, 0x0, 0x23f8e, 0x0, 0x24263, 0x0, 0x242ee, 0x0, 0x243ab, 0x0, 0x24608, 0x0, 0x24735, 0x0, 0x24814, 0x0, 0x24c36, 0x0, 0x24c92, 0x0, 0x24fa1, 0x0, 0x24fb8, 0x0, 0x25044, 0x0, 0x250f2, 0x0, 0x250f3, 0x0, 0x25119, 0x0, 0x25133, 0x0, 0x25249, 0x0, 0x2541d, 0x0, 0x25626, 0x0, 0x2569a, 0x0, 0x256c5, 0x0, 0x2597c, 0x0, 0x25aa7, 0x0, 0x25bab, 0x0, 0x25c80, 0x0, 0x25cd0, 0x0, 0x25f86, 0x0, 0x261da, 0x0, 0x26228, 0x0, 0x26247, 0x0, 0x262d9, 0x0, 0x2633e, 0x0, 0x264da, 0x0, 0x26523, 0x0, 0x265a8, 0x0, 0x267a7, 0x0, 0x267b5, 0x0, 0x26b3c, 0x0, 0x26c36, 0x0, 0x26cd5, 0x0, 0x26d6b, 0x0, 0x26f2c, 0x0, 0x26fb1, 0x0, 0x270d2, 0x0, 0x273ca, 0x0, 0x27667, 0x0, 0x278ae, 0x0, 0x27966, 0x0, 0x27ca8, 0x0, 0x27ed3, 0x0, 0x27f2f, 0x0, 0x285d2, 0x0, 0x285ed, 0x0, 0x2872e, 0x0, 0x28bfa, 0x0, 0x28d77, 0x0, 0x29145, 0x0, 0x291df, 0x0, 0x2921a, 0x0, 0x2940a, 0x0, 0x29496, 0x0, 0x295b6, 0x0, 0x29b30, 0x0, 0x2a0ce, 0x0, 0x2a105, 0x0, 0x2a20e, 0x0, 0x2a291, 0x0, 0x2a392, 0x0, 0x2a600, 0x0]; return t; } -_IDCA decompCompatTable() { static _IDCA t = [ 0x0, 0x20, 0x0, 0x20, 0x301, 0x0, 0x20, 0x303, 0x0, 0x20, 0x304, 0x0, 0x20, 0x305, 0x0, 0x20, 0x306, 0x0, 0x20, 0x307, 0x0, 0x20, 0x308, 0x0, 0x20, 0x308, 0x300, 0x0, 0x20, 0x308, 0x301, 0x0, 0x20, 0x308, 0x342, 0x0, 0x20, 0x30a, 0x0, 0x20, 0x30b, 0x0, 0x20, 0x313, 0x0, 0x20, 0x313, 0x300, 0x0, 0x20, 0x313, 0x301, 0x0, 0x20, 0x313, 0x342, 0x0, 0x20, 0x314, 0x0, 0x20, 0x314, 0x300, 0x0, 0x20, 0x314, 0x301, 0x0, 0x20, 0x314, 0x342, 0x0, 0x20, 0x327, 0x0, 0x20, 0x328, 0x0, 0x20, 0x333, 0x0, 0x20, 0x342, 0x0, 0x20, 0x345, 0x0, 0x20, 0x64b, 0x0, 0x20, 0x64c, 0x0, 0x20, 0x64c, 0x651, 0x0, 0x20, 0x64d, 0x0, 0x20, 0x64d, 0x651, 0x0, 0x20, 0x64e, 0x0, 0x20, 0x64e, 0x651, 0x0, 0x20, 0x64f, 0x0, 0x20, 0x64f, 0x651, 0x0, 0x20, 0x650, 0x0, 0x20, 0x650, 0x651, 0x0, 0x20, 0x651, 0x0, 0x20, 0x651, 0x670, 0x0, 0x20, 0x652, 0x0, 0x20, 0x3099, 0x0, 0x20, 0x309a, 0x0, 0x21, 0x0, 0x21, 0x21, 0x0, 0x21, 0x3f, 0x0, 0x22, 0x0, 0x23, 0x0, 0x24, 0x0, 0x25, 0x0, 0x26, 0x0, 0x27, 0x0, 0x28, 0x0, 0x28, 0x31, 0x29, 0x0, 0x28, 0x31, 0x30, 0x29, 0x0, 0x28, 0x31, 0x31, 0x29, 0x0, 0x28, 0x31, 0x32, 0x29, 0x0, 0x28, 0x31, 0x33, 0x29, 0x0, 0x28, 0x31, 0x34, 0x29, 0x0, 0x28, 0x31, 0x35, 0x29, 0x0, 0x28, 0x31, 0x36, 0x29, 0x0, 0x28, 0x31, 0x37, 0x29, 0x0, 0x28, 0x31, 0x38, 0x29, 0x0, 0x28, 0x31, 0x39, 0x29, 0x0, 0x28, 0x32, 0x29, 0x0, 0x28, 0x32, 0x30, 0x29, 0x0, 0x28, 0x33, 0x29, 0x0, 0x28, 0x34, 0x29, 0x0, 0x28, 0x35, 0x29, 0x0, 0x28, 0x36, 0x29, 0x0, 0x28, 0x37, 0x29, 0x0, 0x28, 0x38, 0x29, 0x0, 0x28, 0x39, 0x29, 0x0, 0x28, 0x41, 0x29, 0x0, 0x28, 0x42, 0x29, 0x0, 0x28, 0x43, 0x29, 0x0, 0x28, 0x44, 0x29, 0x0, 0x28, 0x45, 0x29, 0x0, 0x28, 0x46, 0x29, 0x0, 0x28, 0x47, 0x29, 0x0, 0x28, 0x48, 0x29, 0x0, 0x28, 0x49, 0x29, 0x0, 0x28, 0x4a, 0x29, 0x0, 0x28, 0x4b, 0x29, 0x0, 0x28, 0x4c, 0x29, 0x0, 0x28, 0x4d, 0x29, 0x0, 0x28, 0x4e, 0x29, 0x0, 0x28, 0x4f, 0x29, 0x0, 0x28, 0x50, 0x29, 0x0, 0x28, 0x51, 0x29, 0x0, 0x28, 0x52, 0x29, 0x0, 0x28, 0x53, 0x29, 0x0, 0x28, 0x54, 0x29, 0x0, 0x28, 0x55, 0x29, 0x0, 0x28, 0x56, 0x29, 0x0, 0x28, 0x57, 0x29, 0x0, 0x28, 0x58, 0x29, 0x0, 0x28, 0x59, 0x29, 0x0, 0x28, 0x5a, 0x29, 0x0, 0x28, 0x61, 0x29, 0x0, 0x28, 0x62, 0x29, 0x0, 0x28, 0x63, 0x29, 0x0, 0x28, 0x64, 0x29, 0x0, 0x28, 0x65, 0x29, 0x0, 0x28, 0x66, 0x29, 0x0, 0x28, 0x67, 0x29, 0x0, 0x28, 0x68, 0x29, 0x0, 0x28, 0x69, 0x29, 0x0, 0x28, 0x6a, 0x29, 0x0, 0x28, 0x6b, 0x29, 0x0, 0x28, 0x6c, 0x29, 0x0, 0x28, 0x6d, 0x29, 0x0, 0x28, 0x6e, 0x29, 0x0, 0x28, 0x6f, 0x29, 0x0, 0x28, 0x70, 0x29, 0x0, 0x28, 0x71, 0x29, 0x0, 0x28, 0x72, 0x29, 0x0, 0x28, 0x73, 0x29, 0x0, 0x28, 0x74, 0x29, 0x0, 0x28, 0x75, 0x29, 0x0, 0x28, 0x76, 0x29, 0x0, 0x28, 0x77, 0x29, 0x0, 0x28, 0x78, 0x29, 0x0, 0x28, 0x79, 0x29, 0x0, 0x28, 0x7a, 0x29, 0x0, 0x28, 0x1100, 0x29, 0x0, 0x28, 0x1100, 0x1161, 0x29, 0x0, 0x28, 0x1102, 0x29, 0x0, 0x28, 0x1102, 0x1161, 0x29, 0x0, 0x28, 0x1103, 0x29, 0x0, 0x28, 0x1103, 0x1161, 0x29, 0x0, 0x28, 0x1105, 0x29, 0x0, 0x28, 0x1105, 0x1161, 0x29, 0x0, 0x28, 0x1106, 0x29, 0x0, 0x28, 0x1106, 0x1161, 0x29, 0x0, 0x28, 0x1107, 0x29, 0x0, 0x28, 0x1107, 0x1161, 0x29, 0x0, 0x28, 0x1109, 0x29, 0x0, 0x28, 0x1109, 0x1161, 0x29, 0x0, 0x28, 0x110b, 0x29, 0x0, 0x28, 0x110b, 0x1161, 0x29, 0x0, 0x28, 0x110b, 0x1169, 0x110c, 0x1165, 0x11ab, 0x29, 0x0, 0x28, 0x110b, 0x1169, 0x1112, 0x116e, 0x29, 0x0, 0x28, 0x110c, 0x29, 0x0, 0x28, 0x110c, 0x1161, 0x29, 0x0, 0x28, 0x110c, 0x116e, 0x29, 0x0, 0x28, 0x110e, 0x29, 0x0, 0x28, 0x110e, 0x1161, 0x29, 0x0, 0x28, 0x110f, 0x29, 0x0, 0x28, 0x110f, 0x1161, 0x29, 0x0, 0x28, 0x1110, 0x29, 0x0, 0x28, 0x1110, 0x1161, 0x29, 0x0, 0x28, 0x1111, 0x29, 0x0, 0x28, 0x1111, 0x1161, 0x29, 0x0, 0x28, 0x1112, 0x29, 0x0, 0x28, 0x1112, 0x1161, 0x29, 0x0, 0x28, 0x4e00, 0x29, 0x0, 0x28, 0x4e03, 0x29, 0x0, 0x28, 0x4e09, 0x29, 0x0, 0x28, 0x4e5d, 0x29, 0x0, 0x28, 0x4e8c, 0x29, 0x0, 0x28, 0x4e94, 0x29, 0x0, 0x28, 0x4ee3, 0x29, 0x0, 0x28, 0x4f01, 0x29, 0x0, 0x28, 0x4f11, 0x29, 0x0, 0x28, 0x516b, 0x29, 0x0, 0x28, 0x516d, 0x29, 0x0, 0x28, 0x52b4, 0x29, 0x0, 0x28, 0x5341, 0x29, 0x0, 0x28, 0x5354, 0x29, 0x0, 0x28, 0x540d, 0x29, 0x0, 0x28, 0x547c, 0x29, 0x0, 0x28, 0x56db, 0x29, 0x0, 0x28, 0x571f, 0x29, 0x0, 0x28, 0x5b66, 0x29, 0x0, 0x28, 0x65e5, 0x29, 0x0, 0x28, 0x6708, 0x29, 0x0, 0x28, 0x6709, 0x29, 0x0, 0x28, 0x6728, 0x29, 0x0, 0x28, 0x682a, 0x29, 0x0, 0x28, 0x6c34, 0x29, 0x0, 0x28, 0x706b, 0x29, 0x0, 0x28, 0x7279, 0x29, 0x0, 0x28, 0x76e3, 0x29, 0x0, 0x28, 0x793e, 0x29, 0x0, 0x28, 0x795d, 0x29, 0x0, 0x28, 0x796d, 0x29, 0x0, 0x28, 0x81ea, 0x29, 0x0, 0x28, 0x81f3, 0x29, 0x0, 0x28, 0x8ca1, 0x29, 0x0, 0x28, 0x8cc7, 0x29, 0x0, 0x28, 0x91d1, 0x29, 0x0, 0x29, 0x0, 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0, 0x2e, 0x0, 0x2e, 0x2e, 0x0, 0x2e, 0x2e, 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x30, 0x2c, 0x0, 0x30, 0x2e, 0x0, 0x30, 0x2044, 0x33, 0x0, 0x30, 0x70b9, 0x0, 0x31, 0x0, 0x31, 0x2c, 0x0, 0x31, 0x2e, 0x0, 0x31, 0x30, 0x0, 0x31, 0x30, 0x2e, 0x0, 0x31, 0x30, 0x65e5, 0x0, 0x31, 0x30, 0x6708, 0x0, 0x31, 0x30, 0x70b9, 0x0, 0x31, 0x31, 0x0, 0x31, 0x31, 0x2e, 0x0, 0x31, 0x31, 0x65e5, 0x0, 0x31, 0x31, 0x6708, 0x0, 0x31, 0x31, 0x70b9, 0x0, 0x31, 0x32, 0x0, 0x31, 0x32, 0x2e, 0x0, 0x31, 0x32, 0x65e5, 0x0, 0x31, 0x32, 0x6708, 0x0, 0x31, 0x32, 0x70b9, 0x0, 0x31, 0x33, 0x0, 0x31, 0x33, 0x2e, 0x0, 0x31, 0x33, 0x65e5, 0x0, 0x31, 0x33, 0x70b9, 0x0, 0x31, 0x34, 0x0, 0x31, 0x34, 0x2e, 0x0, 0x31, 0x34, 0x65e5, 0x0, 0x31, 0x34, 0x70b9, 0x0, 0x31, 0x35, 0x0, 0x31, 0x35, 0x2e, 0x0, 0x31, 0x35, 0x65e5, 0x0, 0x31, 0x35, 0x70b9, 0x0, 0x31, 0x36, 0x0, 0x31, 0x36, 0x2e, 0x0, 0x31, 0x36, 0x65e5, 0x0, 0x31, 0x36, 0x70b9, 0x0, 0x31, 0x37, 0x0, 0x31, 0x37, 0x2e, 0x0, 0x31, 0x37, 0x65e5, 0x0, 0x31, 0x37, 0x70b9, 0x0, 0x31, 0x38, 0x0, 0x31, 0x38, 0x2e, 0x0, 0x31, 0x38, 0x65e5, 0x0, 0x31, 0x38, 0x70b9, 0x0, 0x31, 0x39, 0x0, 0x31, 0x39, 0x2e, 0x0, 0x31, 0x39, 0x65e5, 0x0, 0x31, 0x39, 0x70b9, 0x0, 0x31, 0x2044, 0x0, 0x31, 0x2044, 0x31, 0x30, 0x0, 0x31, 0x2044, 0x32, 0x0, 0x31, 0x2044, 0x33, 0x0, 0x31, 0x2044, 0x34, 0x0, 0x31, 0x2044, 0x35, 0x0, 0x31, 0x2044, 0x36, 0x0, 0x31, 0x2044, 0x37, 0x0, 0x31, 0x2044, 0x38, 0x0, 0x31, 0x2044, 0x39, 0x0, 0x31, 0x65e5, 0x0, 0x31, 0x6708, 0x0, 0x31, 0x70b9, 0x0, 0x32, 0x0, 0x32, 0x2c, 0x0, 0x32, 0x2e, 0x0, 0x32, 0x30, 0x0, 0x32, 0x30, 0x2e, 0x0, 0x32, 0x30, 0x65e5, 0x0, 0x32, 0x30, 0x70b9, 0x0, 0x32, 0x31, 0x0, 0x32, 0x31, 0x65e5, 0x0, 0x32, 0x31, 0x70b9, 0x0, 0x32, 0x32, 0x0, 0x32, 0x32, 0x65e5, 0x0, 0x32, 0x32, 0x70b9, 0x0, 0x32, 0x33, 0x0, 0x32, 0x33, 0x65e5, 0x0, 0x32, 0x33, 0x70b9, 0x0, 0x32, 0x34, 0x0, 0x32, 0x34, 0x65e5, 0x0, 0x32, 0x34, 0x70b9, 0x0, 0x32, 0x35, 0x0, 0x32, 0x35, 0x65e5, 0x0, 0x32, 0x36, 0x0, 0x32, 0x36, 0x65e5, 0x0, 0x32, 0x37, 0x0, 0x32, 0x37, 0x65e5, 0x0, 0x32, 0x38, 0x0, 0x32, 0x38, 0x65e5, 0x0, 0x32, 0x39, 0x0, 0x32, 0x39, 0x65e5, 0x0, 0x32, 0x2044, 0x33, 0x0, 0x32, 0x2044, 0x35, 0x0, 0x32, 0x65e5, 0x0, 0x32, 0x6708, 0x0, 0x32, 0x70b9, 0x0, 0x33, 0x0, 0x33, 0x2c, 0x0, 0x33, 0x2e, 0x0, 0x33, 0x30, 0x0, 0x33, 0x30, 0x65e5, 0x0, 0x33, 0x31, 0x0, 0x33, 0x31, 0x65e5, 0x0, 0x33, 0x32, 0x0, 0x33, 0x33, 0x0, 0x33, 0x34, 0x0, 0x33, 0x35, 0x0, 0x33, 0x36, 0x0, 0x33, 0x37, 0x0, 0x33, 0x38, 0x0, 0x33, 0x39, 0x0, 0x33, 0x2044, 0x34, 0x0, 0x33, 0x2044, 0x35, 0x0, 0x33, 0x2044, 0x38, 0x0, 0x33, 0x65e5, 0x0, 0x33, 0x6708, 0x0, 0x33, 0x70b9, 0x0, 0x34, 0x0, 0x34, 0x2c, 0x0, 0x34, 0x2e, 0x0, 0x34, 0x30, 0x0, 0x34, 0x31, 0x0, 0x34, 0x32, 0x0, 0x34, 0x33, 0x0, 0x34, 0x34, 0x0, 0x34, 0x35, 0x0, 0x34, 0x36, 0x0, 0x34, 0x37, 0x0, 0x34, 0x38, 0x0, 0x34, 0x39, 0x0, 0x34, 0x2044, 0x35, 0x0, 0x34, 0x65e5, 0x0, 0x34, 0x6708, 0x0, 0x34, 0x70b9, 0x0, 0x35, 0x0, 0x35, 0x2c, 0x0, 0x35, 0x2e, 0x0, 0x35, 0x30, 0x0, 0x35, 0x2044, 0x36, 0x0, 0x35, 0x2044, 0x38, 0x0, 0x35, 0x65e5, 0x0, 0x35, 0x6708, 0x0, 0x35, 0x70b9, 0x0, 0x36, 0x0, 0x36, 0x2c, 0x0, 0x36, 0x2e, 0x0, 0x36, 0x65e5, 0x0, 0x36, 0x6708, 0x0, 0x36, 0x70b9, 0x0, 0x37, 0x0, 0x37, 0x2c, 0x0, 0x37, 0x2e, 0x0, 0x37, 0x2044, 0x38, 0x0, 0x37, 0x65e5, 0x0, 0x37, 0x6708, 0x0, 0x37, 0x70b9, 0x0, 0x38, 0x0, 0x38, 0x2c, 0x0, 0x38, 0x2e, 0x0, 0x38, 0x65e5, 0x0, 0x38, 0x6708, 0x0, 0x38, 0x70b9, 0x0, 0x39, 0x0, 0x39, 0x2c, 0x0, 0x39, 0x2e, 0x0, 0x39, 0x65e5, 0x0, 0x39, 0x6708, 0x0, 0x39, 0x70b9, 0x0, 0x3a, 0x0, 0x3a, 0x3a, 0x3d, 0x0, 0x3b, 0x0, 0x3c, 0x0, 0x3c, 0x338, 0x0, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x3d, 0x3d, 0x3d, 0x0, 0x3d, 0x338, 0x0, 0x3e, 0x0, 0x3e, 0x338, 0x0, 0x3f, 0x0, 0x3f, 0x21, 0x0, 0x3f, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, 0x41, 0x55, 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, 0x0, 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, 0x302, 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, 0x41, 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, 0x41, 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, 0x309, 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, 0x308, 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, 0x30a, 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, 0x30f, 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, 0x302, 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, 0x328, 0x0, 0x41, 0x2215, 0x6d, 0x0, 0x42, 0x0, 0x42, 0x71, 0x0, 0x42, 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, 0x0, 0x43, 0x0, 0x43, 0x44, 0x0, 0x43, 0x6f, 0x2e, 0x0, 0x43, 0x301, 0x0, 0x43, 0x302, 0x0, 0x43, 0x307, 0x0, 0x43, 0x30c, 0x0, 0x43, 0x327, 0x0, 0x43, 0x327, 0x301, 0x0, 0x43, 0x2215, 0x6b, 0x67, 0x0, 0x44, 0x0, 0x44, 0x4a, 0x0, 0x44, 0x5a, 0x0, 0x44, 0x5a, 0x30c, 0x0, 0x44, 0x7a, 0x0, 0x44, 0x7a, 0x30c, 0x0, 0x44, 0x307, 0x0, 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, 0x327, 0x0, 0x44, 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x0, 0x45, 0x300, 0x0, 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, 0x0, 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, 0x302, 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, 0x304, 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, 0x45, 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, 0x30c, 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, 0x0, 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, 0x306, 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, 0x0, 0x46, 0x0, 0x46, 0x41, 0x58, 0x0, 0x46, 0x307, 0x0, 0x47, 0x0, 0x47, 0x42, 0x0, 0x47, 0x48, 0x7a, 0x0, 0x47, 0x50, 0x61, 0x0, 0x47, 0x79, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, 0x47, 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, 0x30c, 0x0, 0x47, 0x327, 0x0, 0x48, 0x0, 0x48, 0x50, 0x0, 0x48, 0x56, 0x0, 0x48, 0x67, 0x0, 0x48, 0x7a, 0x0, 0x48, 0x302, 0x0, 0x48, 0x307, 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, 0x0, 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x0, 0x49, 0x49, 0x0, 0x49, 0x49, 0x49, 0x0, 0x49, 0x4a, 0x0, 0x49, 0x55, 0x0, 0x49, 0x56, 0x0, 0x49, 0x58, 0x0, 0x49, 0x300, 0x0, 0x49, 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x0, 0x4a, 0x302, 0x0, 0x4b, 0x0, 0x4b, 0x42, 0x0, 0x4b, 0x4b, 0x0, 0x4b, 0x4d, 0x0, 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x0, 0x4c, 0x4a, 0x0, 0x4c, 0x54, 0x44, 0x0, 0x4c, 0x6a, 0x0, 0x4c, 0xb7, 0x0, 0x4c, 0x301, 0x0, 0x4c, 0x30c, 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, 0x0, 0x4c, 0x327, 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, 0x4d, 0x0, 0x4d, 0x42, 0x0, 0x4d, 0x43, 0x0, 0x4d, 0x44, 0x0, 0x4d, 0x48, 0x7a, 0x0, 0x4d, 0x50, 0x61, 0x0, 0x4d, 0x56, 0x0, 0x4d, 0x57, 0x0, 0x4d, 0x301, 0x0, 0x4d, 0x307, 0x0, 0x4d, 0x323, 0x0, 0x4d, 0x3a9, 0x0, 0x4e, 0x0, 0x4e, 0x4a, 0x0, 0x4e, 0x6a, 0x0, 0x4e, 0x6f, 0x0, 0x4e, 0x300, 0x0, 0x4e, 0x301, 0x0, 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, 0x0, 0x4e, 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, 0x4e, 0x331, 0x0, 0x4f, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, 0x328, 0x304, 0x0, 0x50, 0x0, 0x50, 0x48, 0x0, 0x50, 0x50, 0x4d, 0x0, 0x50, 0x50, 0x56, 0x0, 0x50, 0x52, 0x0, 0x50, 0x54, 0x45, 0x0, 0x50, 0x61, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, 0x51, 0x0, 0x52, 0x0, 0x52, 0x73, 0x0, 0x52, 0x301, 0x0, 0x52, 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, 0x0, 0x52, 0x311, 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, 0x0, 0x52, 0x327, 0x0, 0x52, 0x331, 0x0, 0x53, 0x0, 0x53, 0x44, 0x0, 0x53, 0x4d, 0x0, 0x53, 0x53, 0x0, 0x53, 0x76, 0x0, 0x53, 0x301, 0x0, 0x53, 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, 0x53, 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, 0x53, 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, 0x54, 0x0, 0x54, 0x45, 0x4c, 0x0, 0x54, 0x48, 0x7a, 0x0, 0x54, 0x4d, 0x0, 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, 0x54, 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, 0x331, 0x0, 0x55, 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, 0x55, 0x302, 0x0, 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, 0x55, 0x304, 0x0, 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, 0x55, 0x308, 0x0, 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, 0x0, 0x55, 0x308, 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, 0x309, 0x0, 0x55, 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, 0x0, 0x55, 0x30f, 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, 0x55, 0x31b, 0x300, 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, 0x303, 0x0, 0x55, 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, 0x55, 0x323, 0x0, 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, 0x32d, 0x0, 0x55, 0x330, 0x0, 0x56, 0x0, 0x56, 0x49, 0x0, 0x56, 0x49, 0x49, 0x0, 0x56, 0x49, 0x49, 0x49, 0x0, 0x56, 0x303, 0x0, 0x56, 0x323, 0x0, 0x56, 0x2215, 0x6d, 0x0, 0x57, 0x0, 0x57, 0x43, 0x0, 0x57, 0x5a, 0x0, 0x57, 0x62, 0x0, 0x57, 0x300, 0x0, 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, 0x57, 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x0, 0x58, 0x49, 0x0, 0x58, 0x49, 0x49, 0x0, 0x58, 0x307, 0x0, 0x58, 0x308, 0x0, 0x59, 0x0, 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, 0x0, 0x59, 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, 0x59, 0x308, 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, 0x0, 0x5a, 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x5b, 0x0, 0x5c, 0x0, 0x5d, 0x0, 0x5e, 0x0, 0x5f, 0x0, 0x60, 0x0, 0x61, 0x0, 0x61, 0x2e, 0x6d, 0x2e, 0x0, 0x61, 0x2f, 0x63, 0x0, 0x61, 0x2f, 0x73, 0x0, 0x61, 0x2be, 0x0, 0x61, 0x300, 0x0, 0x61, 0x301, 0x0, 0x61, 0x302, 0x0, 0x61, 0x302, 0x300, 0x0, 0x61, 0x302, 0x301, 0x0, 0x61, 0x302, 0x303, 0x0, 0x61, 0x302, 0x309, 0x0, 0x61, 0x303, 0x0, 0x61, 0x304, 0x0, 0x61, 0x306, 0x0, 0x61, 0x306, 0x300, 0x0, 0x61, 0x306, 0x301, 0x0, 0x61, 0x306, 0x303, 0x0, 0x61, 0x306, 0x309, 0x0, 0x61, 0x307, 0x0, 0x61, 0x307, 0x304, 0x0, 0x61, 0x308, 0x0, 0x61, 0x308, 0x304, 0x0, 0x61, 0x309, 0x0, 0x61, 0x30a, 0x0, 0x61, 0x30a, 0x301, 0x0, 0x61, 0x30c, 0x0, 0x61, 0x30f, 0x0, 0x61, 0x311, 0x0, 0x61, 0x323, 0x0, 0x61, 0x323, 0x302, 0x0, 0x61, 0x323, 0x306, 0x0, 0x61, 0x325, 0x0, 0x61, 0x328, 0x0, 0x62, 0x0, 0x62, 0x61, 0x72, 0x0, 0x62, 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x0, 0x63, 0x2f, 0x6f, 0x0, 0x63, 0x2f, 0x75, 0x0, 0x63, 0x61, 0x6c, 0x0, 0x63, 0x63, 0x0, 0x63, 0x64, 0x0, 0x63, 0x6d, 0x0, 0x63, 0x6d, 0x32, 0x0, 0x63, 0x6d, 0x33, 0x0, 0x63, 0x301, 0x0, 0x63, 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, 0x63, 0x327, 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x0, 0x64, 0x42, 0x0, 0x64, 0x61, 0x0, 0x64, 0x6c, 0x0, 0x64, 0x6d, 0x0, 0x64, 0x6d, 0x32, 0x0, 0x64, 0x6d, 0x33, 0x0, 0x64, 0x7a, 0x0, 0x64, 0x7a, 0x30c, 0x0, 0x64, 0x307, 0x0, 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, 0x64, 0x327, 0x0, 0x64, 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, 0x0, 0x65, 0x56, 0x0, 0x65, 0x72, 0x67, 0x0, 0x65, 0x300, 0x0, 0x65, 0x301, 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, 0x65, 0x302, 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, 0x309, 0x0, 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, 0x300, 0x0, 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, 0x307, 0x0, 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, 0x0, 0x65, 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, 0x65, 0x323, 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, 0x0, 0x65, 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, 0x66, 0x0, 0x66, 0x66, 0x0, 0x66, 0x66, 0x69, 0x0, 0x66, 0x66, 0x6c, 0x0, 0x66, 0x69, 0x0, 0x66, 0x6c, 0x0, 0x66, 0x6d, 0x0, 0x66, 0x307, 0x0, 0x67, 0x0, 0x67, 0x61, 0x6c, 0x0, 0x67, 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, 0x67, 0x306, 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, 0x327, 0x0, 0x68, 0x0, 0x68, 0x50, 0x61, 0x0, 0x68, 0x61, 0x0, 0x68, 0x302, 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, 0x0, 0x68, 0x30c, 0x0, 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, 0x68, 0x32e, 0x0, 0x68, 0x331, 0x0, 0x69, 0x0, 0x69, 0x69, 0x0, 0x69, 0x69, 0x69, 0x0, 0x69, 0x6a, 0x0, 0x69, 0x6e, 0x0, 0x69, 0x76, 0x0, 0x69, 0x78, 0x0, 0x69, 0x300, 0x0, 0x69, 0x301, 0x0, 0x69, 0x302, 0x0, 0x69, 0x303, 0x0, 0x69, 0x304, 0x0, 0x69, 0x306, 0x0, 0x69, 0x308, 0x0, 0x69, 0x308, 0x301, 0x0, 0x69, 0x309, 0x0, 0x69, 0x30c, 0x0, 0x69, 0x30f, 0x0, 0x69, 0x311, 0x0, 0x69, 0x323, 0x0, 0x69, 0x328, 0x0, 0x69, 0x330, 0x0, 0x6a, 0x0, 0x6a, 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x0, 0x6b, 0x41, 0x0, 0x6b, 0x48, 0x7a, 0x0, 0x6b, 0x50, 0x61, 0x0, 0x6b, 0x56, 0x0, 0x6b, 0x57, 0x0, 0x6b, 0x63, 0x61, 0x6c, 0x0, 0x6b, 0x67, 0x0, 0x6b, 0x6c, 0x0, 0x6b, 0x6d, 0x0, 0x6b, 0x6d, 0x32, 0x0, 0x6b, 0x6d, 0x33, 0x0, 0x6b, 0x74, 0x0, 0x6b, 0x301, 0x0, 0x6b, 0x30c, 0x0, 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, 0x6b, 0x331, 0x0, 0x6b, 0x3a9, 0x0, 0x6c, 0x0, 0x6c, 0x6a, 0x0, 0x6c, 0x6d, 0x0, 0x6c, 0x6e, 0x0, 0x6c, 0x6f, 0x67, 0x0, 0x6c, 0x78, 0x0, 0x6c, 0xb7, 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, 0x323, 0x0, 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, 0x32d, 0x0, 0x6c, 0x331, 0x0, 0x6d, 0x0, 0x6d, 0x32, 0x0, 0x6d, 0x33, 0x0, 0x6d, 0x41, 0x0, 0x6d, 0x56, 0x0, 0x6d, 0x57, 0x0, 0x6d, 0x62, 0x0, 0x6d, 0x67, 0x0, 0x6d, 0x69, 0x6c, 0x0, 0x6d, 0x6c, 0x0, 0x6d, 0x6d, 0x0, 0x6d, 0x6d, 0x32, 0x0, 0x6d, 0x6d, 0x33, 0x0, 0x6d, 0x6f, 0x6c, 0x0, 0x6d, 0x73, 0x0, 0x6d, 0x301, 0x0, 0x6d, 0x307, 0x0, 0x6d, 0x323, 0x0, 0x6d, 0x2215, 0x73, 0x0, 0x6d, 0x2215, 0x73, 0x32, 0x0, 0x6e, 0x0, 0x6e, 0x41, 0x0, 0x6e, 0x46, 0x0, 0x6e, 0x56, 0x0, 0x6e, 0x57, 0x0, 0x6e, 0x6a, 0x0, 0x6e, 0x6d, 0x0, 0x6e, 0x73, 0x0, 0x6e, 0x300, 0x0, 0x6e, 0x301, 0x0, 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, 0x0, 0x6e, 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, 0x6e, 0x331, 0x0, 0x6f, 0x0, 0x6f, 0x56, 0x0, 0x6f, 0x300, 0x0, 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, 0x6f, 0x302, 0x300, 0x0, 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, 0x303, 0x0, 0x6f, 0x302, 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, 0x303, 0x301, 0x0, 0x6f, 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, 0x0, 0x6f, 0x304, 0x0, 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, 0x301, 0x0, 0x6f, 0x306, 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, 0x304, 0x0, 0x6f, 0x308, 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, 0x309, 0x0, 0x6f, 0x30b, 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, 0x0, 0x6f, 0x311, 0x0, 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, 0x0, 0x6f, 0x31b, 0x301, 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, 0x31b, 0x309, 0x0, 0x6f, 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, 0x6f, 0x323, 0x302, 0x0, 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, 0x0, 0x70, 0x0, 0x70, 0x2e, 0x6d, 0x2e, 0x0, 0x70, 0x41, 0x0, 0x70, 0x46, 0x0, 0x70, 0x56, 0x0, 0x70, 0x57, 0x0, 0x70, 0x63, 0x0, 0x70, 0x73, 0x0, 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x71, 0x0, 0x72, 0x0, 0x72, 0x61, 0x64, 0x0, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x0, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x32, 0x0, 0x72, 0x301, 0x0, 0x72, 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, 0x311, 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, 0x327, 0x0, 0x72, 0x331, 0x0, 0x73, 0x0, 0x73, 0x72, 0x0, 0x73, 0x74, 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, 0x307, 0x0, 0x73, 0x302, 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, 0x0, 0x73, 0x30c, 0x307, 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, 0x307, 0x0, 0x73, 0x326, 0x0, 0x73, 0x327, 0x0, 0x74, 0x0, 0x74, 0x307, 0x0, 0x74, 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, 0x74, 0x326, 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, 0x331, 0x0, 0x75, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, 0x75, 0x330, 0x0, 0x76, 0x0, 0x76, 0x69, 0x0, 0x76, 0x69, 0x69, 0x0, 0x76, 0x69, 0x69, 0x69, 0x0, 0x76, 0x303, 0x0, 0x76, 0x323, 0x0, 0x77, 0x0, 0x77, 0x300, 0x0, 0x77, 0x301, 0x0, 0x77, 0x302, 0x0, 0x77, 0x307, 0x0, 0x77, 0x308, 0x0, 0x77, 0x30a, 0x0, 0x77, 0x323, 0x0, 0x78, 0x0, 0x78, 0x69, 0x0, 0x78, 0x69, 0x69, 0x0, 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x0, 0x79, 0x300, 0x0, 0x79, 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, 0x303, 0x0, 0x79, 0x304, 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, 0x0, 0x79, 0x309, 0x0, 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, 0x7a, 0x0, 0x7a, 0x301, 0x0, 0x7a, 0x302, 0x0, 0x7a, 0x307, 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, 0x0, 0x7a, 0x331, 0x0, 0x7b, 0x0, 0x7c, 0x0, 0x7d, 0x0, 0x7e, 0x0, 0xa2, 0x0, 0xa3, 0x0, 0xa5, 0x0, 0xa6, 0x0, 0xac, 0x0, 0xb0, 0x43, 0x0, 0xb0, 0x46, 0x0, 0xb7, 0x0, 0xc6, 0x0, 0xc6, 0x301, 0x0, 0xc6, 0x304, 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, 0x0, 0xf0, 0x0, 0xf8, 0x301, 0x0, 0x126, 0x0, 0x127, 0x0, 0x131, 0x0, 0x14b, 0x0, 0x153, 0x0, 0x18e, 0x0, 0x190, 0x0, 0x1ab, 0x0, 0x1b7, 0x30c, 0x0, 0x222, 0x0, 0x237, 0x0, 0x250, 0x0, 0x251, 0x0, 0x252, 0x0, 0x254, 0x0, 0x255, 0x0, 0x259, 0x0, 0x25b, 0x0, 0x25c, 0x0, 0x25f, 0x0, 0x261, 0x0, 0x263, 0x0, 0x265, 0x0, 0x266, 0x0, 0x268, 0x0, 0x269, 0x0, 0x26a, 0x0, 0x26d, 0x0, 0x26f, 0x0, 0x270, 0x0, 0x271, 0x0, 0x272, 0x0, 0x273, 0x0, 0x274, 0x0, 0x275, 0x0, 0x278, 0x0, 0x279, 0x0, 0x27b, 0x0, 0x281, 0x0, 0x282, 0x0, 0x283, 0x0, 0x289, 0x0, 0x28a, 0x0, 0x28b, 0x0, 0x28c, 0x0, 0x290, 0x0, 0x291, 0x0, 0x292, 0x0, 0x292, 0x30c, 0x0, 0x295, 0x0, 0x29d, 0x0, 0x29f, 0x0, 0x2b9, 0x0, 0x2bc, 0x6e, 0x0, 0x300, 0x0, 0x301, 0x0, 0x308, 0x301, 0x0, 0x313, 0x0, 0x391, 0x0, 0x391, 0x300, 0x0, 0x391, 0x301, 0x0, 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, 0x313, 0x0, 0x391, 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, 0x345, 0x0, 0x391, 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, 0x345, 0x0, 0x391, 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, 0x345, 0x0, 0x391, 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, 0x314, 0x300, 0x0, 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, 0x314, 0x301, 0x0, 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, 0x314, 0x342, 0x0, 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, 0x314, 0x345, 0x0, 0x391, 0x345, 0x0, 0x392, 0x0, 0x393, 0x0, 0x394, 0x0, 0x395, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, 0x395, 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, 0x301, 0x0, 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, 0x314, 0x301, 0x0, 0x396, 0x0, 0x397, 0x0, 0x397, 0x300, 0x0, 0x397, 0x301, 0x0, 0x397, 0x313, 0x0, 0x397, 0x313, 0x300, 0x0, 0x397, 0x313, 0x300, 0x345, 0x0, 0x397, 0x313, 0x301, 0x0, 0x397, 0x313, 0x301, 0x345, 0x0, 0x397, 0x313, 0x342, 0x0, 0x397, 0x313, 0x342, 0x345, 0x0, 0x397, 0x313, 0x345, 0x0, 0x397, 0x314, 0x0, 0x397, 0x314, 0x300, 0x0, 0x397, 0x314, 0x300, 0x345, 0x0, 0x397, 0x314, 0x301, 0x0, 0x397, 0x314, 0x301, 0x345, 0x0, 0x397, 0x314, 0x342, 0x0, 0x397, 0x314, 0x342, 0x345, 0x0, 0x397, 0x314, 0x345, 0x0, 0x397, 0x345, 0x0, 0x398, 0x0, 0x399, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, 0x0, 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, 0x399, 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, 0x301, 0x0, 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, 0x314, 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, 0x342, 0x0, 0x39a, 0x0, 0x39b, 0x0, 0x39c, 0x0, 0x39d, 0x0, 0x39e, 0x0, 0x39f, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, 0x3a0, 0x0, 0x3a1, 0x0, 0x3a1, 0x314, 0x0, 0x3a3, 0x0, 0x3a4, 0x0, 0x3a5, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, 0x314, 0x342, 0x0, 0x3a6, 0x0, 0x3a7, 0x0, 0x3a8, 0x0, 0x3a9, 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, 0x0, 0x3a9, 0x313, 0x0, 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, 0x300, 0x345, 0x0, 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, 0x301, 0x345, 0x0, 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, 0x342, 0x345, 0x0, 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, 0x3a9, 0x314, 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, 0x3a9, 0x314, 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, 0x3a9, 0x314, 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, 0x3a9, 0x314, 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x0, 0x3b1, 0x300, 0x0, 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, 0x345, 0x0, 0x3b2, 0x0, 0x3b3, 0x0, 0x3b4, 0x0, 0x3b5, 0x0, 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, 0x0, 0x3b5, 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, 0x314, 0x0, 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, 0x3b6, 0x0, 0x3b7, 0x0, 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, 0x301, 0x0, 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, 0x313, 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, 0x313, 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, 0x313, 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, 0x3b7, 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, 0x3b7, 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, 0x3b7, 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, 0x3b7, 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, 0x3b8, 0x0, 0x3b9, 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, 0x3b9, 0x304, 0x0, 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, 0x308, 0x300, 0x0, 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, 0x342, 0x0, 0x3b9, 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, 0x313, 0x301, 0x0, 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, 0x3b9, 0x314, 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, 0x314, 0x342, 0x0, 0x3b9, 0x342, 0x0, 0x3ba, 0x0, 0x3bb, 0x0, 0x3bc, 0x0, 0x3bc, 0x41, 0x0, 0x3bc, 0x46, 0x0, 0x3bc, 0x56, 0x0, 0x3bc, 0x57, 0x0, 0x3bc, 0x67, 0x0, 0x3bc, 0x6c, 0x0, 0x3bc, 0x6d, 0x0, 0x3bc, 0x73, 0x0, 0x3bd, 0x0, 0x3be, 0x0, 0x3bf, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, 0x313, 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, 0x3bf, 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, 0x301, 0x0, 0x3c0, 0x0, 0x3c1, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, 0x3c2, 0x0, 0x3c3, 0x0, 0x3c4, 0x0, 0x3c5, 0x0, 0x3c5, 0x300, 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, 0x3c6, 0x0, 0x3c7, 0x0, 0x3c8, 0x0, 0x3c9, 0x0, 0x3c9, 0x300, 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, 0x3c9, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, 0x300, 0x0, 0x3c9, 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, 0x301, 0x0, 0x3c9, 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x342, 0x0, 0x3c9, 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, 0x345, 0x0, 0x3c9, 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, 0x314, 0x300, 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, 0x314, 0x301, 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, 0x314, 0x342, 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, 0x342, 0x0, 0x3c9, 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3dc, 0x0, 0x3dd, 0x0, 0x406, 0x308, 0x0, 0x410, 0x306, 0x0, 0x410, 0x308, 0x0, 0x413, 0x301, 0x0, 0x415, 0x300, 0x0, 0x415, 0x306, 0x0, 0x415, 0x308, 0x0, 0x416, 0x306, 0x0, 0x416, 0x308, 0x0, 0x417, 0x308, 0x0, 0x418, 0x300, 0x0, 0x418, 0x304, 0x0, 0x418, 0x306, 0x0, 0x418, 0x308, 0x0, 0x41a, 0x301, 0x0, 0x41e, 0x308, 0x0, 0x423, 0x304, 0x0, 0x423, 0x306, 0x0, 0x423, 0x308, 0x0, 0x423, 0x30b, 0x0, 0x427, 0x308, 0x0, 0x42b, 0x308, 0x0, 0x42d, 0x308, 0x0, 0x430, 0x306, 0x0, 0x430, 0x308, 0x0, 0x433, 0x301, 0x0, 0x435, 0x300, 0x0, 0x435, 0x306, 0x0, 0x435, 0x308, 0x0, 0x436, 0x306, 0x0, 0x436, 0x308, 0x0, 0x437, 0x308, 0x0, 0x438, 0x300, 0x0, 0x438, 0x304, 0x0, 0x438, 0x306, 0x0, 0x438, 0x308, 0x0, 0x43a, 0x301, 0x0, 0x43d, 0x0, 0x43e, 0x308, 0x0, 0x443, 0x304, 0x0, 0x443, 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, 0x0, 0x447, 0x308, 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, 0x456, 0x308, 0x0, 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, 0x308, 0x0, 0x4d9, 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, 0x0, 0x565, 0x582, 0x0, 0x574, 0x565, 0x0, 0x574, 0x56b, 0x0, 0x574, 0x56d, 0x0, 0x574, 0x576, 0x0, 0x57e, 0x576, 0x0, 0x5d0, 0x0, 0x5d0, 0x5b7, 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, 0x5d0, 0x5dc, 0x0, 0x5d1, 0x0, 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x0, 0x5d2, 0x5bc, 0x0, 0x5d3, 0x0, 0x5d3, 0x5bc, 0x0, 0x5d4, 0x0, 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, 0x0, 0x5d6, 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, 0x5d9, 0x5bc, 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, 0x5bf, 0x0, 0x5dc, 0x0, 0x5dc, 0x5bc, 0x0, 0x5dd, 0x0, 0x5de, 0x5bc, 0x0, 0x5e0, 0x5bc, 0x0, 0x5e1, 0x5bc, 0x0, 0x5e2, 0x0, 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, 0x5e4, 0x5bf, 0x0, 0x5e6, 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, 0x0, 0x5e8, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, 0x5bc, 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, 0x0, 0x5ea, 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x621, 0x0, 0x627, 0x0, 0x627, 0x643, 0x628, 0x631, 0x0, 0x627, 0x644, 0x644, 0x647, 0x0, 0x627, 0x64b, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, 0x0, 0x627, 0x655, 0x0, 0x627, 0x674, 0x0, 0x628, 0x0, 0x628, 0x62c, 0x0, 0x628, 0x62d, 0x0, 0x628, 0x62d, 0x64a, 0x0, 0x628, 0x62e, 0x0, 0x628, 0x62e, 0x64a, 0x0, 0x628, 0x631, 0x0, 0x628, 0x632, 0x0, 0x628, 0x645, 0x0, 0x628, 0x646, 0x0, 0x628, 0x647, 0x0, 0x628, 0x649, 0x0, 0x628, 0x64a, 0x0, 0x629, 0x0, 0x62a, 0x0, 0x62a, 0x62c, 0x0, 0x62a, 0x62c, 0x645, 0x0, 0x62a, 0x62c, 0x649, 0x0, 0x62a, 0x62c, 0x64a, 0x0, 0x62a, 0x62d, 0x0, 0x62a, 0x62d, 0x62c, 0x0, 0x62a, 0x62d, 0x645, 0x0, 0x62a, 0x62e, 0x0, 0x62a, 0x62e, 0x645, 0x0, 0x62a, 0x62e, 0x649, 0x0, 0x62a, 0x62e, 0x64a, 0x0, 0x62a, 0x631, 0x0, 0x62a, 0x632, 0x0, 0x62a, 0x645, 0x0, 0x62a, 0x645, 0x62c, 0x0, 0x62a, 0x645, 0x62d, 0x0, 0x62a, 0x645, 0x62e, 0x0, 0x62a, 0x645, 0x649, 0x0, 0x62a, 0x645, 0x64a, 0x0, 0x62a, 0x646, 0x0, 0x62a, 0x647, 0x0, 0x62a, 0x649, 0x0, 0x62a, 0x64a, 0x0, 0x62b, 0x0, 0x62b, 0x62c, 0x0, 0x62b, 0x631, 0x0, 0x62b, 0x632, 0x0, 0x62b, 0x645, 0x0, 0x62b, 0x646, 0x0, 0x62b, 0x647, 0x0, 0x62b, 0x649, 0x0, 0x62b, 0x64a, 0x0, 0x62c, 0x0, 0x62c, 0x62d, 0x0, 0x62c, 0x62d, 0x649, 0x0, 0x62c, 0x62d, 0x64a, 0x0, 0x62c, 0x644, 0x20, 0x62c, 0x644, 0x627, 0x644, 0x647, 0x0, 0x62c, 0x645, 0x0, 0x62c, 0x645, 0x62d, 0x0, 0x62c, 0x645, 0x649, 0x0, 0x62c, 0x645, 0x64a, 0x0, 0x62c, 0x649, 0x0, 0x62c, 0x64a, 0x0, 0x62d, 0x0, 0x62d, 0x62c, 0x0, 0x62d, 0x62c, 0x64a, 0x0, 0x62d, 0x645, 0x0, 0x62d, 0x645, 0x649, 0x0, 0x62d, 0x645, 0x64a, 0x0, 0x62d, 0x649, 0x0, 0x62d, 0x64a, 0x0, 0x62e, 0x0, 0x62e, 0x62c, 0x0, 0x62e, 0x62d, 0x0, 0x62e, 0x645, 0x0, 0x62e, 0x649, 0x0, 0x62e, 0x64a, 0x0, 0x62f, 0x0, 0x630, 0x0, 0x630, 0x670, 0x0, 0x631, 0x0, 0x631, 0x633, 0x648, 0x644, 0x0, 0x631, 0x670, 0x0, 0x631, 0x6cc, 0x627, 0x644, 0x0, 0x632, 0x0, 0x633, 0x0, 0x633, 0x62c, 0x0, 0x633, 0x62c, 0x62d, 0x0, 0x633, 0x62c, 0x649, 0x0, 0x633, 0x62d, 0x0, 0x633, 0x62d, 0x62c, 0x0, 0x633, 0x62e, 0x0, 0x633, 0x62e, 0x649, 0x0, 0x633, 0x62e, 0x64a, 0x0, 0x633, 0x631, 0x0, 0x633, 0x645, 0x0, 0x633, 0x645, 0x62c, 0x0, 0x633, 0x645, 0x62d, 0x0, 0x633, 0x645, 0x645, 0x0, 0x633, 0x647, 0x0, 0x633, 0x649, 0x0, 0x633, 0x64a, 0x0, 0x634, 0x0, 0x634, 0x62c, 0x0, 0x634, 0x62c, 0x64a, 0x0, 0x634, 0x62d, 0x0, 0x634, 0x62d, 0x645, 0x0, 0x634, 0x62d, 0x64a, 0x0, 0x634, 0x62e, 0x0, 0x634, 0x631, 0x0, 0x634, 0x645, 0x0, 0x634, 0x645, 0x62e, 0x0, 0x634, 0x645, 0x645, 0x0, 0x634, 0x647, 0x0, 0x634, 0x649, 0x0, 0x634, 0x64a, 0x0, 0x635, 0x0, 0x635, 0x62d, 0x0, 0x635, 0x62d, 0x62d, 0x0, 0x635, 0x62d, 0x64a, 0x0, 0x635, 0x62e, 0x0, 0x635, 0x631, 0x0, 0x635, 0x644, 0x639, 0x645, 0x0, 0x635, 0x644, 0x649, 0x0, 0x635, 0x644, 0x649, 0x20, 0x627, 0x644, 0x644, 0x647, 0x20, 0x639, 0x644, 0x64a, 0x647, 0x20, 0x648, 0x633, 0x644, 0x645, 0x0, 0x635, 0x644, 0x6d2, 0x0, 0x635, 0x645, 0x0, 0x635, 0x645, 0x645, 0x0, 0x635, 0x649, 0x0, 0x635, 0x64a, 0x0, 0x636, 0x0, 0x636, 0x62c, 0x0, 0x636, 0x62d, 0x0, 0x636, 0x62d, 0x649, 0x0, 0x636, 0x62d, 0x64a, 0x0, 0x636, 0x62e, 0x0, 0x636, 0x62e, 0x645, 0x0, 0x636, 0x631, 0x0, 0x636, 0x645, 0x0, 0x636, 0x649, 0x0, 0x636, 0x64a, 0x0, 0x637, 0x0, 0x637, 0x62d, 0x0, 0x637, 0x645, 0x0, 0x637, 0x645, 0x62d, 0x0, 0x637, 0x645, 0x645, 0x0, 0x637, 0x645, 0x64a, 0x0, 0x637, 0x649, 0x0, 0x637, 0x64a, 0x0, 0x638, 0x0, 0x638, 0x645, 0x0, 0x639, 0x0, 0x639, 0x62c, 0x0, 0x639, 0x62c, 0x645, 0x0, 0x639, 0x644, 0x64a, 0x647, 0x0, 0x639, 0x645, 0x0, 0x639, 0x645, 0x645, 0x0, 0x639, 0x645, 0x649, 0x0, 0x639, 0x645, 0x64a, 0x0, 0x639, 0x649, 0x0, 0x639, 0x64a, 0x0, 0x63a, 0x0, 0x63a, 0x62c, 0x0, 0x63a, 0x645, 0x0, 0x63a, 0x645, 0x645, 0x0, 0x63a, 0x645, 0x649, 0x0, 0x63a, 0x645, 0x64a, 0x0, 0x63a, 0x649, 0x0, 0x63a, 0x64a, 0x0, 0x640, 0x64b, 0x0, 0x640, 0x64e, 0x0, 0x640, 0x64e, 0x651, 0x0, 0x640, 0x64f, 0x0, 0x640, 0x64f, 0x651, 0x0, 0x640, 0x650, 0x0, 0x640, 0x650, 0x651, 0x0, 0x640, 0x651, 0x0, 0x640, 0x652, 0x0, 0x641, 0x0, 0x641, 0x62c, 0x0, 0x641, 0x62d, 0x0, 0x641, 0x62e, 0x0, 0x641, 0x62e, 0x645, 0x0, 0x641, 0x645, 0x0, 0x641, 0x645, 0x64a, 0x0, 0x641, 0x649, 0x0, 0x641, 0x64a, 0x0, 0x642, 0x0, 0x642, 0x62d, 0x0, 0x642, 0x644, 0x6d2, 0x0, 0x642, 0x645, 0x0, 0x642, 0x645, 0x62d, 0x0, 0x642, 0x645, 0x645, 0x0, 0x642, 0x645, 0x64a, 0x0, 0x642, 0x649, 0x0, 0x642, 0x64a, 0x0, 0x643, 0x0, 0x643, 0x627, 0x0, 0x643, 0x62c, 0x0, 0x643, 0x62d, 0x0, 0x643, 0x62e, 0x0, 0x643, 0x644, 0x0, 0x643, 0x645, 0x0, 0x643, 0x645, 0x645, 0x0, 0x643, 0x645, 0x64a, 0x0, 0x643, 0x649, 0x0, 0x643, 0x64a, 0x0, 0x644, 0x0, 0x644, 0x627, 0x0, 0x644, 0x627, 0x653, 0x0, 0x644, 0x627, 0x654, 0x0, 0x644, 0x627, 0x655, 0x0, 0x644, 0x62c, 0x0, 0x644, 0x62c, 0x62c, 0x0, 0x644, 0x62c, 0x645, 0x0, 0x644, 0x62c, 0x64a, 0x0, 0x644, 0x62d, 0x0, 0x644, 0x62d, 0x645, 0x0, 0x644, 0x62d, 0x649, 0x0, 0x644, 0x62d, 0x64a, 0x0, 0x644, 0x62e, 0x0, 0x644, 0x62e, 0x645, 0x0, 0x644, 0x645, 0x0, 0x644, 0x645, 0x62d, 0x0, 0x644, 0x645, 0x64a, 0x0, 0x644, 0x647, 0x0, 0x644, 0x649, 0x0, 0x644, 0x64a, 0x0, 0x645, 0x0, 0x645, 0x627, 0x0, 0x645, 0x62c, 0x0, 0x645, 0x62c, 0x62d, 0x0, 0x645, 0x62c, 0x62e, 0x0, 0x645, 0x62c, 0x645, 0x0, 0x645, 0x62c, 0x64a, 0x0, 0x645, 0x62d, 0x0, 0x645, 0x62d, 0x62c, 0x0, 0x645, 0x62d, 0x645, 0x0, 0x645, 0x62d, 0x645, 0x62f, 0x0, 0x645, 0x62d, 0x64a, 0x0, 0x645, 0x62e, 0x0, 0x645, 0x62e, 0x62c, 0x0, 0x645, 0x62e, 0x645, 0x0, 0x645, 0x62e, 0x64a, 0x0, 0x645, 0x645, 0x0, 0x645, 0x645, 0x64a, 0x0, 0x645, 0x649, 0x0, 0x645, 0x64a, 0x0, 0x646, 0x0, 0x646, 0x62c, 0x0, 0x646, 0x62c, 0x62d, 0x0, 0x646, 0x62c, 0x645, 0x0, 0x646, 0x62c, 0x649, 0x0, 0x646, 0x62c, 0x64a, 0x0, 0x646, 0x62d, 0x0, 0x646, 0x62d, 0x645, 0x0, 0x646, 0x62d, 0x649, 0x0, 0x646, 0x62d, 0x64a, 0x0, 0x646, 0x62e, 0x0, 0x646, 0x631, 0x0, 0x646, 0x632, 0x0, 0x646, 0x645, 0x0, 0x646, 0x645, 0x649, 0x0, 0x646, 0x645, 0x64a, 0x0, 0x646, 0x646, 0x0, 0x646, 0x647, 0x0, 0x646, 0x649, 0x0, 0x646, 0x64a, 0x0, 0x647, 0x0, 0x647, 0x62c, 0x0, 0x647, 0x645, 0x0, 0x647, 0x645, 0x62c, 0x0, 0x647, 0x645, 0x645, 0x0, 0x647, 0x649, 0x0, 0x647, 0x64a, 0x0, 0x647, 0x670, 0x0, 0x648, 0x0, 0x648, 0x633, 0x644, 0x645, 0x0, 0x648, 0x654, 0x0, 0x648, 0x674, 0x0, 0x649, 0x0, 0x649, 0x670, 0x0, 0x64a, 0x0, 0x64a, 0x62c, 0x0, 0x64a, 0x62c, 0x64a, 0x0, 0x64a, 0x62d, 0x0, 0x64a, 0x62d, 0x64a, 0x0, 0x64a, 0x62e, 0x0, 0x64a, 0x631, 0x0, 0x64a, 0x632, 0x0, 0x64a, 0x645, 0x0, 0x64a, 0x645, 0x645, 0x0, 0x64a, 0x645, 0x64a, 0x0, 0x64a, 0x646, 0x0, 0x64a, 0x647, 0x0, 0x64a, 0x649, 0x0, 0x64a, 0x64a, 0x0, 0x64a, 0x654, 0x0, 0x64a, 0x654, 0x627, 0x0, 0x64a, 0x654, 0x62c, 0x0, 0x64a, 0x654, 0x62d, 0x0, 0x64a, 0x654, 0x62e, 0x0, 0x64a, 0x654, 0x631, 0x0, 0x64a, 0x654, 0x632, 0x0, 0x64a, 0x654, 0x645, 0x0, 0x64a, 0x654, 0x646, 0x0, 0x64a, 0x654, 0x647, 0x0, 0x64a, 0x654, 0x648, 0x0, 0x64a, 0x654, 0x649, 0x0, 0x64a, 0x654, 0x64a, 0x0, 0x64a, 0x654, 0x6c6, 0x0, 0x64a, 0x654, 0x6c7, 0x0, 0x64a, 0x654, 0x6c8, 0x0, 0x64a, 0x654, 0x6d0, 0x0, 0x64a, 0x654, 0x6d5, 0x0, 0x64a, 0x674, 0x0, 0x66e, 0x0, 0x66f, 0x0, 0x671, 0x0, 0x679, 0x0, 0x67a, 0x0, 0x67b, 0x0, 0x67e, 0x0, 0x67f, 0x0, 0x680, 0x0, 0x683, 0x0, 0x684, 0x0, 0x686, 0x0, 0x687, 0x0, 0x688, 0x0, 0x68c, 0x0, 0x68d, 0x0, 0x68e, 0x0, 0x691, 0x0, 0x698, 0x0, 0x6a1, 0x0, 0x6a4, 0x0, 0x6a6, 0x0, 0x6a9, 0x0, 0x6ad, 0x0, 0x6af, 0x0, 0x6b1, 0x0, 0x6b3, 0x0, 0x6ba, 0x0, 0x6bb, 0x0, 0x6be, 0x0, 0x6c1, 0x0, 0x6c1, 0x654, 0x0, 0x6c5, 0x0, 0x6c6, 0x0, 0x6c7, 0x0, 0x6c7, 0x674, 0x0, 0x6c8, 0x0, 0x6c9, 0x0, 0x6cb, 0x0, 0x6cc, 0x0, 0x6d0, 0x0, 0x6d2, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, 0x0, 0x915, 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, 0x91c, 0x93c, 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, 0x93c, 0x0, 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, 0x0, 0x933, 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, 0x9af, 0x9bc, 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, 0xa3c, 0x0, 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, 0x0, 0xa32, 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, 0xb22, 0xb3c, 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, 0xb57, 0x0, 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, 0x0, 0xbc7, 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, 0xcc6, 0xcc2, 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, 0xcc6, 0xcd6, 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, 0xd3e, 0x0, 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, 0xdca, 0x0, 0xdd9, 0xddf, 0x0, 0xe4d, 0xe32, 0x0, 0xeab, 0xe99, 0x0, 0xeab, 0xea1, 0x0, 0xecd, 0xeb2, 0x0, 0xf0b, 0x0, 0xf40, 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, 0xfb7, 0x0, 0xf51, 0xfb7, 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, 0x0, 0xf71, 0xf72, 0x0, 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, 0xf90, 0xfb5, 0x0, 0xf92, 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, 0xfb7, 0x0, 0xfa6, 0xfb7, 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf71, 0xf80, 0x0, 0xfb2, 0xf80, 0x0, 0xfb3, 0xf71, 0xf80, 0x0, 0xfb3, 0xf80, 0x0, 0x1025, 0x102e, 0x0, 0x10dc, 0x0, 0x1100, 0x0, 0x1100, 0x1161, 0x0, 0x1101, 0x0, 0x1102, 0x0, 0x1102, 0x1161, 0x0, 0x1103, 0x0, 0x1103, 0x1161, 0x0, 0x1104, 0x0, 0x1105, 0x0, 0x1105, 0x1161, 0x0, 0x1106, 0x0, 0x1106, 0x1161, 0x0, 0x1107, 0x0, 0x1107, 0x1161, 0x0, 0x1108, 0x0, 0x1109, 0x0, 0x1109, 0x1161, 0x0, 0x110a, 0x0, 0x110b, 0x0, 0x110b, 0x1161, 0x0, 0x110b, 0x116e, 0x0, 0x110c, 0x0, 0x110c, 0x1161, 0x0, 0x110c, 0x116e, 0x110b, 0x1174, 0x0, 0x110d, 0x0, 0x110e, 0x0, 0x110e, 0x1161, 0x0, 0x110e, 0x1161, 0x11b7, 0x1100, 0x1169, 0x0, 0x110f, 0x0, 0x110f, 0x1161, 0x0, 0x1110, 0x0, 0x1110, 0x1161, 0x0, 0x1111, 0x0, 0x1111, 0x1161, 0x0, 0x1112, 0x0, 0x1112, 0x1161, 0x0, 0x1114, 0x0, 0x1115, 0x0, 0x111a, 0x0, 0x111c, 0x0, 0x111d, 0x0, 0x111e, 0x0, 0x1120, 0x0, 0x1121, 0x0, 0x1122, 0x0, 0x1123, 0x0, 0x1127, 0x0, 0x1129, 0x0, 0x112b, 0x0, 0x112c, 0x0, 0x112d, 0x0, 0x112e, 0x0, 0x112f, 0x0, 0x1132, 0x0, 0x1136, 0x0, 0x1140, 0x0, 0x1147, 0x0, 0x114c, 0x0, 0x1157, 0x0, 0x1158, 0x0, 0x1159, 0x0, 0x1160, 0x0, 0x1161, 0x0, 0x1162, 0x0, 0x1163, 0x0, 0x1164, 0x0, 0x1165, 0x0, 0x1166, 0x0, 0x1167, 0x0, 0x1168, 0x0, 0x1169, 0x0, 0x116a, 0x0, 0x116b, 0x0, 0x116c, 0x0, 0x116d, 0x0, 0x116e, 0x0, 0x116f, 0x0, 0x1170, 0x0, 0x1171, 0x0, 0x1172, 0x0, 0x1173, 0x0, 0x1174, 0x0, 0x1175, 0x0, 0x1184, 0x0, 0x1185, 0x0, 0x1188, 0x0, 0x1191, 0x0, 0x1192, 0x0, 0x1194, 0x0, 0x119e, 0x0, 0x11a1, 0x0, 0x11aa, 0x0, 0x11ac, 0x0, 0x11ad, 0x0, 0x11b0, 0x0, 0x11b1, 0x0, 0x11b2, 0x0, 0x11b3, 0x0, 0x11b4, 0x0, 0x11b5, 0x0, 0x11c7, 0x0, 0x11c8, 0x0, 0x11cc, 0x0, 0x11ce, 0x0, 0x11d3, 0x0, 0x11d7, 0x0, 0x11d9, 0x0, 0x11dd, 0x0, 0x11df, 0x0, 0x11f1, 0x0, 0x11f2, 0x0, 0x1b05, 0x1b35, 0x0, 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, 0x0, 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, 0x0, 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, 0x0, 0x1b42, 0x1b35, 0x0, 0x1d02, 0x0, 0x1d16, 0x0, 0x1d17, 0x0, 0x1d1c, 0x0, 0x1d1d, 0x0, 0x1d25, 0x0, 0x1d7b, 0x0, 0x1d85, 0x0, 0x2010, 0x0, 0x2013, 0x0, 0x2014, 0x0, 0x2032, 0x2032, 0x0, 0x2032, 0x2032, 0x2032, 0x0, 0x2032, 0x2032, 0x2032, 0x2032, 0x0, 0x2035, 0x2035, 0x0, 0x2035, 0x2035, 0x2035, 0x0, 0x20a9, 0x0, 0x2190, 0x0, 0x2190, 0x338, 0x0, 0x2191, 0x0, 0x2192, 0x0, 0x2192, 0x338, 0x0, 0x2193, 0x0, 0x2194, 0x338, 0x0, 0x21d0, 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, 0x0, 0x2202, 0x0, 0x2203, 0x338, 0x0, 0x2207, 0x0, 0x2208, 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2211, 0x0, 0x2212, 0x0, 0x2223, 0x338, 0x0, 0x2225, 0x338, 0x0, 0x222b, 0x222b, 0x0, 0x222b, 0x222b, 0x222b, 0x0, 0x222b, 0x222b, 0x222b, 0x222b, 0x0, 0x222e, 0x222e, 0x0, 0x222e, 0x222e, 0x222e, 0x0, 0x223c, 0x338, 0x0, 0x2243, 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, 0x338, 0x0, 0x224d, 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, 0x338, 0x0, 0x2265, 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, 0x338, 0x0, 0x2276, 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, 0x338, 0x0, 0x227b, 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, 0x338, 0x0, 0x2282, 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, 0x338, 0x0, 0x2287, 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, 0x338, 0x0, 0x22a2, 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, 0x338, 0x0, 0x22ab, 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, 0x338, 0x0, 0x22b4, 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2502, 0x0, 0x25a0, 0x0, 0x25cb, 0x0, 0x2985, 0x0, 0x2986, 0x0, 0x2add, 0x338, 0x0, 0x2d61, 0x0, 0x3001, 0x0, 0x3002, 0x0, 0x3008, 0x0, 0x3009, 0x0, 0x300a, 0x0, 0x300b, 0x0, 0x300c, 0x0, 0x300d, 0x0, 0x300e, 0x0, 0x300f, 0x0, 0x3010, 0x0, 0x3011, 0x0, 0x3012, 0x0, 0x3014, 0x0, 0x3014, 0x53, 0x3015, 0x0, 0x3014, 0x4e09, 0x3015, 0x0, 0x3014, 0x4e8c, 0x3015, 0x0, 0x3014, 0x52dd, 0x3015, 0x0, 0x3014, 0x5b89, 0x3015, 0x0, 0x3014, 0x6253, 0x3015, 0x0, 0x3014, 0x6557, 0x3015, 0x0, 0x3014, 0x672c, 0x3015, 0x0, 0x3014, 0x70b9, 0x3015, 0x0, 0x3014, 0x76d7, 0x3015, 0x0, 0x3015, 0x0, 0x3016, 0x0, 0x3017, 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, 0x304d, 0x3099, 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, 0x3053, 0x3099, 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, 0x3059, 0x3099, 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, 0x305f, 0x3099, 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, 0x3066, 0x3099, 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, 0x306f, 0x309a, 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, 0x3075, 0x3099, 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, 0x3078, 0x309a, 0x0, 0x307b, 0x304b, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, 0x0, 0x3088, 0x308a, 0x0, 0x3099, 0x0, 0x309a, 0x0, 0x309d, 0x3099, 0x0, 0x30a1, 0x0, 0x30a2, 0x0, 0x30a2, 0x30cf, 0x309a, 0x30fc, 0x30c8, 0x0, 0x30a2, 0x30eb, 0x30d5, 0x30a1, 0x0, 0x30a2, 0x30f3, 0x30d8, 0x309a, 0x30a2, 0x0, 0x30a2, 0x30fc, 0x30eb, 0x0, 0x30a3, 0x0, 0x30a4, 0x0, 0x30a4, 0x30cb, 0x30f3, 0x30af, 0x3099, 0x0, 0x30a4, 0x30f3, 0x30c1, 0x0, 0x30a5, 0x0, 0x30a6, 0x0, 0x30a6, 0x3099, 0x0, 0x30a6, 0x30a9, 0x30f3, 0x0, 0x30a7, 0x0, 0x30a8, 0x0, 0x30a8, 0x30b9, 0x30af, 0x30fc, 0x30c8, 0x3099, 0x0, 0x30a8, 0x30fc, 0x30ab, 0x30fc, 0x0, 0x30a9, 0x0, 0x30aa, 0x0, 0x30aa, 0x30f3, 0x30b9, 0x0, 0x30aa, 0x30fc, 0x30e0, 0x0, 0x30ab, 0x0, 0x30ab, 0x3099, 0x0, 0x30ab, 0x3099, 0x30ed, 0x30f3, 0x0, 0x30ab, 0x3099, 0x30f3, 0x30de, 0x0, 0x30ab, 0x30a4, 0x30ea, 0x0, 0x30ab, 0x30e9, 0x30c3, 0x30c8, 0x0, 0x30ab, 0x30ed, 0x30ea, 0x30fc, 0x0, 0x30ad, 0x0, 0x30ad, 0x3099, 0x0, 0x30ad, 0x3099, 0x30ab, 0x3099, 0x0, 0x30ad, 0x3099, 0x30cb, 0x30fc, 0x0, 0x30ad, 0x3099, 0x30eb, 0x30bf, 0x3099, 0x30fc, 0x0, 0x30ad, 0x30e5, 0x30ea, 0x30fc, 0x0, 0x30ad, 0x30ed, 0x0, 0x30ad, 0x30ed, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x0, 0x30ad, 0x30ed, 0x30e1, 0x30fc, 0x30c8, 0x30eb, 0x0, 0x30ad, 0x30ed, 0x30ef, 0x30c3, 0x30c8, 0x0, 0x30af, 0x0, 0x30af, 0x3099, 0x0, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x0, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x30c8, 0x30f3, 0x0, 0x30af, 0x30eb, 0x30bb, 0x3099, 0x30a4, 0x30ed, 0x0, 0x30af, 0x30ed, 0x30fc, 0x30cd, 0x0, 0x30b1, 0x0, 0x30b1, 0x3099, 0x0, 0x30b1, 0x30fc, 0x30b9, 0x0, 0x30b3, 0x0, 0x30b3, 0x3099, 0x0, 0x30b3, 0x30b3, 0x0, 0x30b3, 0x30c8, 0x0, 0x30b3, 0x30eb, 0x30ca, 0x0, 0x30b3, 0x30fc, 0x30db, 0x309a, 0x0, 0x30b5, 0x0, 0x30b5, 0x3099, 0x0, 0x30b5, 0x30a4, 0x30af, 0x30eb, 0x0, 0x30b5, 0x30f3, 0x30c1, 0x30fc, 0x30e0, 0x0, 0x30b7, 0x0, 0x30b7, 0x3099, 0x0, 0x30b7, 0x30ea, 0x30f3, 0x30af, 0x3099, 0x0, 0x30b9, 0x0, 0x30b9, 0x3099, 0x0, 0x30bb, 0x0, 0x30bb, 0x3099, 0x0, 0x30bb, 0x30f3, 0x30c1, 0x0, 0x30bb, 0x30f3, 0x30c8, 0x0, 0x30bd, 0x0, 0x30bd, 0x3099, 0x0, 0x30bf, 0x0, 0x30bf, 0x3099, 0x0, 0x30bf, 0x3099, 0x30fc, 0x30b9, 0x0, 0x30c1, 0x0, 0x30c1, 0x3099, 0x0, 0x30c3, 0x0, 0x30c4, 0x0, 0x30c4, 0x3099, 0x0, 0x30c6, 0x0, 0x30c6, 0x3099, 0x0, 0x30c6, 0x3099, 0x30b7, 0x0, 0x30c8, 0x0, 0x30c8, 0x3099, 0x0, 0x30c8, 0x3099, 0x30eb, 0x0, 0x30c8, 0x30f3, 0x0, 0x30ca, 0x0, 0x30ca, 0x30ce, 0x0, 0x30cb, 0x0, 0x30cc, 0x0, 0x30cd, 0x0, 0x30ce, 0x0, 0x30ce, 0x30c3, 0x30c8, 0x0, 0x30cf, 0x0, 0x30cf, 0x3099, 0x0, 0x30cf, 0x3099, 0x30fc, 0x30ec, 0x30eb, 0x0, 0x30cf, 0x309a, 0x0, 0x30cf, 0x309a, 0x30fc, 0x30bb, 0x30f3, 0x30c8, 0x0, 0x30cf, 0x309a, 0x30fc, 0x30c4, 0x0, 0x30cf, 0x30a4, 0x30c4, 0x0, 0x30d2, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x3099, 0x30eb, 0x0, 0x30d2, 0x309a, 0x0, 0x30d2, 0x309a, 0x30a2, 0x30b9, 0x30c8, 0x30eb, 0x0, 0x30d2, 0x309a, 0x30af, 0x30eb, 0x0, 0x30d2, 0x309a, 0x30b3, 0x0, 0x30d5, 0x0, 0x30d5, 0x3099, 0x0, 0x30d5, 0x3099, 0x30c3, 0x30b7, 0x30a7, 0x30eb, 0x0, 0x30d5, 0x309a, 0x0, 0x30d5, 0x30a1, 0x30e9, 0x30c3, 0x30c8, 0x3099, 0x0, 0x30d5, 0x30a3, 0x30fc, 0x30c8, 0x0, 0x30d5, 0x30e9, 0x30f3, 0x0, 0x30d8, 0x0, 0x30d8, 0x3099, 0x0, 0x30d8, 0x3099, 0x30fc, 0x30bf, 0x0, 0x30d8, 0x309a, 0x0, 0x30d8, 0x309a, 0x30bd, 0x0, 0x30d8, 0x309a, 0x30cb, 0x30d2, 0x0, 0x30d8, 0x309a, 0x30f3, 0x30b9, 0x0, 0x30d8, 0x309a, 0x30fc, 0x30b7, 0x3099, 0x0, 0x30d8, 0x30af, 0x30bf, 0x30fc, 0x30eb, 0x0, 0x30d8, 0x30eb, 0x30c4, 0x0, 0x30db, 0x0, 0x30db, 0x3099, 0x0, 0x30db, 0x3099, 0x30eb, 0x30c8, 0x0, 0x30db, 0x309a, 0x0, 0x30db, 0x309a, 0x30a4, 0x30f3, 0x30c8, 0x0, 0x30db, 0x309a, 0x30f3, 0x30c8, 0x3099, 0x0, 0x30db, 0x30f3, 0x0, 0x30db, 0x30fc, 0x30eb, 0x0, 0x30db, 0x30fc, 0x30f3, 0x0, 0x30de, 0x0, 0x30de, 0x30a4, 0x30af, 0x30ed, 0x0, 0x30de, 0x30a4, 0x30eb, 0x0, 0x30de, 0x30c3, 0x30cf, 0x0, 0x30de, 0x30eb, 0x30af, 0x0, 0x30de, 0x30f3, 0x30b7, 0x30e7, 0x30f3, 0x0, 0x30df, 0x0, 0x30df, 0x30af, 0x30ed, 0x30f3, 0x0, 0x30df, 0x30ea, 0x0, 0x30df, 0x30ea, 0x30cf, 0x3099, 0x30fc, 0x30eb, 0x0, 0x30e0, 0x0, 0x30e1, 0x0, 0x30e1, 0x30ab, 0x3099, 0x0, 0x30e1, 0x30ab, 0x3099, 0x30c8, 0x30f3, 0x0, 0x30e1, 0x30fc, 0x30c8, 0x30eb, 0x0, 0x30e2, 0x0, 0x30e3, 0x0, 0x30e4, 0x0, 0x30e4, 0x30fc, 0x30c8, 0x3099, 0x0, 0x30e4, 0x30fc, 0x30eb, 0x0, 0x30e5, 0x0, 0x30e6, 0x0, 0x30e6, 0x30a2, 0x30f3, 0x0, 0x30e7, 0x0, 0x30e8, 0x0, 0x30e9, 0x0, 0x30ea, 0x0, 0x30ea, 0x30c3, 0x30c8, 0x30eb, 0x0, 0x30ea, 0x30e9, 0x0, 0x30eb, 0x0, 0x30eb, 0x30d2, 0x309a, 0x30fc, 0x0, 0x30eb, 0x30fc, 0x30d5, 0x3099, 0x30eb, 0x0, 0x30ec, 0x0, 0x30ec, 0x30e0, 0x0, 0x30ec, 0x30f3, 0x30c8, 0x30b1, 0x3099, 0x30f3, 0x0, 0x30ed, 0x0, 0x30ef, 0x0, 0x30ef, 0x3099, 0x0, 0x30ef, 0x30c3, 0x30c8, 0x0, 0x30f0, 0x0, 0x30f0, 0x3099, 0x0, 0x30f1, 0x0, 0x30f1, 0x3099, 0x0, 0x30f2, 0x0, 0x30f2, 0x3099, 0x0, 0x30f3, 0x0, 0x30fb, 0x0, 0x30fc, 0x0, 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, 0x0, 0x34bb, 0x0, 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, 0x36fc, 0x0, 0x3781, 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, 0x0, 0x38c7, 0x0, 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, 0x3a2e, 0x0, 0x3a6c, 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, 0x0, 0x3b49, 0x0, 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, 0x3d33, 0x0, 0x3d96, 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, 0x0, 0x3ffc, 0x0, 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, 0x4046, 0x0, 0x4096, 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, 0x0, 0x4227, 0x0, 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, 0x4359, 0x0, 0x43d5, 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, 0x0, 0x452b, 0x0, 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, 0x45d7, 0x0, 0x45f9, 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, 0x0, 0x4995, 0x0, 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, 0x4ab2, 0x0, 0x4b33, 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, 0x0, 0x4cf8, 0x0, 0x4d56, 0x0, 0x4e00, 0x0, 0x4e01, 0x0, 0x4e03, 0x0, 0x4e09, 0x0, 0x4e0a, 0x0, 0x4e0b, 0x0, 0x4e0d, 0x0, 0x4e19, 0x0, 0x4e26, 0x0, 0x4e28, 0x0, 0x4e2d, 0x0, 0x4e32, 0x0, 0x4e36, 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e3f, 0x0, 0x4e41, 0x0, 0x4e59, 0x0, 0x4e5d, 0x0, 0x4e82, 0x0, 0x4e85, 0x0, 0x4e86, 0x0, 0x4e8c, 0x0, 0x4e94, 0x0, 0x4ea0, 0x0, 0x4ea4, 0x0, 0x4eae, 0x0, 0x4eba, 0x0, 0x4ec0, 0x0, 0x4ecc, 0x0, 0x4ee4, 0x0, 0x4f01, 0x0, 0x4f11, 0x0, 0x4f60, 0x0, 0x4f80, 0x0, 0x4f86, 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, 0x0, 0x4fbf, 0x0, 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, 0x5099, 0x0, 0x50cf, 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x512a, 0x0, 0x513f, 0x0, 0x5140, 0x0, 0x5145, 0x0, 0x514d, 0x0, 0x5154, 0x0, 0x5164, 0x0, 0x5165, 0x0, 0x5167, 0x0, 0x5168, 0x0, 0x5169, 0x0, 0x516b, 0x0, 0x516d, 0x0, 0x5177, 0x0, 0x5180, 0x0, 0x5182, 0x0, 0x518d, 0x0, 0x5192, 0x0, 0x5195, 0x0, 0x5196, 0x0, 0x5197, 0x0, 0x5199, 0x0, 0x51a4, 0x0, 0x51ab, 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, 0x0, 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51e0, 0x0, 0x51f5, 0x0, 0x5200, 0x0, 0x5203, 0x0, 0x5207, 0x0, 0x5217, 0x0, 0x521d, 0x0, 0x5229, 0x0, 0x523a, 0x0, 0x523b, 0x0, 0x5246, 0x0, 0x524d, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52b4, 0x0, 0x52c7, 0x0, 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, 0x0, 0x52f5, 0x0, 0x52f9, 0x0, 0x52fa, 0x0, 0x5305, 0x0, 0x5306, 0x0, 0x5315, 0x0, 0x5317, 0x0, 0x531a, 0x0, 0x5338, 0x0, 0x533b, 0x0, 0x533f, 0x0, 0x5341, 0x0, 0x5344, 0x0, 0x5345, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x5354, 0x0, 0x535a, 0x0, 0x535c, 0x0, 0x5369, 0x0, 0x5370, 0x0, 0x5373, 0x0, 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x5382, 0x0, 0x53b6, 0x0, 0x53c3, 0x0, 0x53c8, 0x0, 0x53ca, 0x0, 0x53cc, 0x0, 0x53df, 0x0, 0x53e3, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53ef, 0x0, 0x53f1, 0x0, 0x53f3, 0x0, 0x5406, 0x0, 0x5408, 0x0, 0x540d, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, 0x5439, 0x0, 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, 0x54a2, 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x554f, 0x0, 0x5553, 0x0, 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, 0x5587, 0x0, 0x5599, 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, 0x0, 0x55b6, 0x0, 0x55c0, 0x0, 0x55c2, 0x0, 0x55e2, 0x0, 0x5606, 0x0, 0x5651, 0x0, 0x5668, 0x0, 0x5674, 0x0, 0x56d7, 0x0, 0x56db, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, 0x571f, 0x0, 0x5730, 0x0, 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, 0x0, 0x580d, 0x0, 0x5831, 0x0, 0x5832, 0x0, 0x5840, 0x0, 0x585a, 0x0, 0x585e, 0x0, 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, 0x0, 0x58d8, 0x0, 0x58df, 0x0, 0x58eb, 0x0, 0x58ee, 0x0, 0x58f0, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5902, 0x0, 0x5906, 0x0, 0x590a, 0x0, 0x5915, 0x0, 0x591a, 0x0, 0x591c, 0x0, 0x5922, 0x0, 0x5927, 0x0, 0x5927, 0x6b63, 0x0, 0x5929, 0x0, 0x5944, 0x0, 0x5948, 0x0, 0x5951, 0x0, 0x5954, 0x0, 0x5962, 0x0, 0x5973, 0x0, 0x59d8, 0x0, 0x59ec, 0x0, 0x5a1b, 0x0, 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, 0x0, 0x5ab5, 0x0, 0x5b08, 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, 0x5b50, 0x0, 0x5b57, 0x0, 0x5b66, 0x0, 0x5b80, 0x0, 0x5b85, 0x0, 0x5b97, 0x0, 0x5bc3, 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, 0x0, 0x5bf3, 0x0, 0x5bf8, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c0f, 0x0, 0x5c22, 0x0, 0x5c38, 0x0, 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, 0x5c64, 0x0, 0x5c65, 0x0, 0x5c6e, 0x0, 0x5c71, 0x0, 0x5c8d, 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, 0x5d43, 0x0, 0x5d50, 0x0, 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, 0x0, 0x5db2, 0x0, 0x5dba, 0x0, 0x5ddb, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, 0x5de5, 0x0, 0x5de6, 0x0, 0x5df1, 0x0, 0x5dfd, 0x0, 0x5dfe, 0x0, 0x5e28, 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e72, 0x0, 0x5e73, 0x6210, 0x0, 0x5e74, 0x0, 0x5e7a, 0x0, 0x5e7c, 0x0, 0x5e7f, 0x0, 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, 0x5ec9, 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, 0x0, 0x5eec, 0x0, 0x5ef4, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f0b, 0x0, 0x5f13, 0x0, 0x5f22, 0x0, 0x5f50, 0x0, 0x5f53, 0x0, 0x5f61, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f73, 0x0, 0x5f8b, 0x0, 0x5f8c, 0x0, 0x5f97, 0x0, 0x5f9a, 0x0, 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fc3, 0x0, 0x5fcd, 0x0, 0x5fd7, 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, 0x0, 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, 0x60d8, 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, 0x0, 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, 0x617a, 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, 0x0, 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, 0x6200, 0x0, 0x6208, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, 0x6234, 0x0, 0x6236, 0x0, 0x624b, 0x0, 0x6253, 0x0, 0x625d, 0x0, 0x6295, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x6307, 0x0, 0x633d, 0x0, 0x6350, 0x0, 0x6355, 0x0, 0x6368, 0x0, 0x637b, 0x0, 0x6383, 0x0, 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, 0x0, 0x63c5, 0x0, 0x63e4, 0x0, 0x641c, 0x0, 0x6422, 0x0, 0x6452, 0x0, 0x6469, 0x0, 0x6477, 0x0, 0x647e, 0x0, 0x649a, 0x0, 0x649d, 0x0, 0x64c4, 0x0, 0x652f, 0x0, 0x6534, 0x0, 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, 0x6587, 0x0, 0x6597, 0x0, 0x6599, 0x0, 0x65a4, 0x0, 0x65b0, 0x0, 0x65b9, 0x0, 0x65c5, 0x0, 0x65e0, 0x0, 0x65e2, 0x0, 0x65e3, 0x0, 0x65e5, 0x0, 0x660e, 0x6cbb, 0x0, 0x6613, 0x0, 0x6620, 0x0, 0x662d, 0x548c, 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, 0x0, 0x6691, 0x0, 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, 0x66f0, 0x0, 0x66f4, 0x0, 0x66f8, 0x0, 0x6700, 0x0, 0x6708, 0x0, 0x6709, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, 0x6728, 0x0, 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, 0x0, 0x677b, 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, 0x67fa, 0x0, 0x6817, 0x0, 0x681f, 0x0, 0x682a, 0x0, 0x682a, 0x5f0f, 0x4f1a, 0x793e, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b20, 0x0, 0x6b21, 0x0, 0x6b54, 0x0, 0x6b62, 0x0, 0x6b63, 0x0, 0x6b72, 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, 0x0, 0x6bae, 0x0, 0x6bb3, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6bcb, 0x0, 0x6bcd, 0x0, 0x6bd4, 0x0, 0x6bdb, 0x0, 0x6c0f, 0x0, 0x6c14, 0x0, 0x6c34, 0x0, 0x6c4e, 0x0, 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, 0x0, 0x6ccc, 0x0, 0x6ccd, 0x0, 0x6ce5, 0x0, 0x6ce8, 0x0, 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, 0x6d34, 0x0, 0x6d3e, 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, 0x0, 0x6d77, 0x0, 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, 0x6dda, 0x0, 0x6dea, 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, 0x0, 0x6e6e, 0x0, 0x6e80, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, 0x6ecb, 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f14, 0x0, 0x6f22, 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, 0x0, 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, 0x704a, 0x0, 0x706b, 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, 0x0, 0x7099, 0x0, 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, 0x7121, 0x0, 0x7145, 0x0, 0x7149, 0x0, 0x716e, 0x0, 0x719c, 0x0, 0x71ce, 0x0, 0x71d0, 0x0, 0x7210, 0x0, 0x721b, 0x0, 0x7228, 0x0, 0x722a, 0x0, 0x722b, 0x0, 0x7235, 0x0, 0x7236, 0x0, 0x723b, 0x0, 0x723f, 0x0, 0x7247, 0x0, 0x7250, 0x0, 0x7259, 0x0, 0x725b, 0x0, 0x7262, 0x0, 0x7279, 0x0, 0x7280, 0x0, 0x7295, 0x0, 0x72ac, 0x0, 0x72af, 0x0, 0x72c0, 0x0, 0x72fc, 0x0, 0x732a, 0x0, 0x7375, 0x0, 0x737a, 0x0, 0x7384, 0x0, 0x7387, 0x0, 0x7389, 0x0, 0x738b, 0x0, 0x73a5, 0x0, 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x74dc, 0x0, 0x74e6, 0x0, 0x7506, 0x0, 0x7518, 0x0, 0x751f, 0x0, 0x7524, 0x0, 0x7528, 0x0, 0x7530, 0x0, 0x7532, 0x0, 0x7533, 0x0, 0x7537, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, 0x7565, 0x0, 0x7570, 0x0, 0x758b, 0x0, 0x7592, 0x0, 0x75e2, 0x0, 0x7610, 0x0, 0x761d, 0x0, 0x761f, 0x0, 0x7642, 0x0, 0x7669, 0x0, 0x7676, 0x0, 0x767d, 0x0, 0x76ae, 0x0, 0x76bf, 0x0, 0x76ca, 0x0, 0x76db, 0x0, 0x76e3, 0x0, 0x76e7, 0x0, 0x76ee, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, 0x0, 0x771f, 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, 0x77a7, 0x0, 0x77db, 0x0, 0x77e2, 0x0, 0x77f3, 0x0, 0x784e, 0x0, 0x786b, 0x0, 0x788c, 0x0, 0x7891, 0x0, 0x78ca, 0x0, 0x78cc, 0x0, 0x78fb, 0x0, 0x792a, 0x0, 0x793a, 0x0, 0x793c, 0x0, 0x793e, 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, 0x0, 0x7956, 0x0, 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, 0x797f, 0x0, 0x7981, 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, 0x0, 0x79b8, 0x0, 0x79be, 0x0, 0x79ca, 0x0, 0x79d8, 0x0, 0x79eb, 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, 0x7a4a, 0x0, 0x7a4f, 0x0, 0x7a74, 0x0, 0x7a7a, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, 0x0, 0x7aee, 0x0, 0x7af9, 0x0, 0x7b20, 0x0, 0x7b8f, 0x0, 0x7bc0, 0x0, 0x7bc6, 0x0, 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, 0x0, 0x7c73, 0x0, 0x7c7b, 0x0, 0x7c92, 0x0, 0x7cbe, 0x0, 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, 0x7ce7, 0x0, 0x7ce8, 0x0, 0x7cf8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, 0x0, 0x7d2f, 0x0, 0x7d42, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, 0x0, 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, 0x7e09, 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f36, 0x0, 0x7f3e, 0x0, 0x7f51, 0x0, 0x7f72, 0x0, 0x7f79, 0x0, 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f8a, 0x0, 0x7f95, 0x0, 0x7f9a, 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, 0x8005, 0x0, 0x800c, 0x0, 0x8012, 0x0, 0x8033, 0x0, 0x8046, 0x0, 0x8060, 0x0, 0x806f, 0x0, 0x8070, 0x0, 0x807e, 0x0, 0x807f, 0x0, 0x8089, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, 0x8103, 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e3, 0x0, 0x81e8, 0x0, 0x81ea, 0x0, 0x81ed, 0x0, 0x81f3, 0x0, 0x81fc, 0x0, 0x8201, 0x0, 0x8204, 0x0, 0x820c, 0x0, 0x8218, 0x0, 0x821b, 0x0, 0x821f, 0x0, 0x826e, 0x0, 0x826f, 0x0, 0x8272, 0x0, 0x8278, 0x0, 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, 0x82b1, 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, 0x0, 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, 0x8353, 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, 0x0, 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, 0x83ef, 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, 0x0, 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, 0x8516, 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, 0x0, 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x864d, 0x0, 0x8650, 0x0, 0x865c, 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x866b, 0x0, 0x8688, 0x0, 0x86a9, 0x0, 0x86e2, 0x0, 0x870e, 0x0, 0x8728, 0x0, 0x876b, 0x0, 0x8779, 0x0, 0x8786, 0x0, 0x87ba, 0x0, 0x87e1, 0x0, 0x8801, 0x0, 0x881f, 0x0, 0x8840, 0x0, 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, 0x0, 0x88cf, 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, 0x88f8, 0x0, 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, 0x0, 0x897e, 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x89d2, 0x0, 0x89e3, 0x0, 0x8a00, 0x0, 0x8aa0, 0x0, 0x8aaa, 0x0, 0x8abf, 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, 0x0, 0x8aed, 0x0, 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, 0x8b39, 0x0, 0x8b58, 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c37, 0x0, 0x8c46, 0x0, 0x8c48, 0x0, 0x8c55, 0x0, 0x8c78, 0x0, 0x8c9d, 0x0, 0x8ca1, 0x0, 0x8ca9, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, 0x8cc7, 0x0, 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, 0x0, 0x8d64, 0x0, 0x8d70, 0x0, 0x8d77, 0x0, 0x8db3, 0x0, 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, 0x8eab, 0x0, 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, 0x8f38, 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9b, 0x0, 0x8f9e, 0x0, 0x8fb0, 0x0, 0x8fb5, 0x0, 0x8fb6, 0x0, 0x9023, 0x0, 0x9038, 0x0, 0x904a, 0x0, 0x9069, 0x0, 0x9072, 0x0, 0x907c, 0x0, 0x908f, 0x0, 0x9091, 0x0, 0x9094, 0x0, 0x90ce, 0x0, 0x90de, 0x0, 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, 0x0, 0x9149, 0x0, 0x916a, 0x0, 0x9199, 0x0, 0x91b4, 0x0, 0x91c6, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, 0x91d1, 0x0, 0x9234, 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, 0x0, 0x92d7, 0x0, 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, 0x93f9, 0x0, 0x9415, 0x0, 0x9577, 0x0, 0x9580, 0x0, 0x958b, 0x0, 0x95ad, 0x0, 0x95b7, 0x0, 0x961c, 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, 0x0, 0x9675, 0x0, 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, 0x96a3, 0x0, 0x96b6, 0x0, 0x96b7, 0x0, 0x96b8, 0x0, 0x96b9, 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, 0x96e8, 0x0, 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, 0x0, 0x9751, 0x0, 0x9756, 0x0, 0x975e, 0x0, 0x9762, 0x0, 0x9769, 0x0, 0x97cb, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ed, 0x0, 0x97f3, 0x0, 0x97ff, 0x0, 0x9801, 0x0, 0x9805, 0x0, 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, 0x0, 0x98a8, 0x0, 0x98db, 0x0, 0x98df, 0x0, 0x98e2, 0x0, 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, 0x9929, 0x0, 0x9996, 0x0, 0x9999, 0x0, 0x99a7, 0x0, 0x99ac, 0x0, 0x99c2, 0x0, 0x99f1, 0x0, 0x99fe, 0x0, 0x9a6a, 0x0, 0x9aa8, 0x0, 0x9ad8, 0x0, 0x9adf, 0x0, 0x9b12, 0x0, 0x9b25, 0x0, 0x9b2f, 0x0, 0x9b32, 0x0, 0x9b3c, 0x0, 0x9b5a, 0x0, 0x9b6f, 0x0, 0x9c40, 0x0, 0x9c57, 0x0, 0x9ce5, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, 0x9db4, 0x0, 0x9dfa, 0x0, 0x9e1e, 0x0, 0x9e75, 0x0, 0x9e7f, 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, 0x9ea5, 0x0, 0x9ebb, 0x0, 0x9ec3, 0x0, 0x9ecd, 0x0, 0x9ece, 0x0, 0x9ed1, 0x0, 0x9ef9, 0x0, 0x9efd, 0x0, 0x9efe, 0x0, 0x9f05, 0x0, 0x9f0e, 0x0, 0x9f0f, 0x0, 0x9f13, 0x0, 0x9f16, 0x0, 0x9f20, 0x0, 0x9f3b, 0x0, 0x9f43, 0x0, 0x9f4a, 0x0, 0x9f52, 0x0, 0x9f8d, 0x0, 0x9f8e, 0x0, 0x9f9c, 0x0, 0x9f9f, 0x0, 0x9fa0, 0x0, 0xa76f, 0x0, 0x11099, 0x110ba, 0x0, 0x1109b, 0x110ba, 0x0, 0x110a5, 0x110ba, 0x0, 0x11131, 0x11127, 0x0, 0x11132, 0x11127, 0x0, 0x1d157, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x1d16e, 0x0, 0x1d158, 0x1d165, 0x1d16f, 0x0, 0x1d158, 0x1d165, 0x1d170, 0x0, 0x1d158, 0x1d165, 0x1d171, 0x0, 0x1d158, 0x1d165, 0x1d172, 0x0, 0x1d1b9, 0x1d165, 0x0, 0x1d1b9, 0x1d165, 0x1d16e, 0x0, 0x1d1b9, 0x1d165, 0x1d16f, 0x0, 0x1d1ba, 0x1d165, 0x0, 0x1d1ba, 0x1d165, 0x1d16e, 0x0, 0x1d1ba, 0x1d165, 0x1d16f, 0x0, 0x20122, 0x0, 0x2051c, 0x0, 0x20525, 0x0, 0x2054b, 0x0, 0x2063a, 0x0, 0x20804, 0x0, 0x208de, 0x0, 0x20a2c, 0x0, 0x20b63, 0x0, 0x214e4, 0x0, 0x216a8, 0x0, 0x216ea, 0x0, 0x219c8, 0x0, 0x21b18, 0x0, 0x21d0b, 0x0, 0x21de4, 0x0, 0x21de6, 0x0, 0x22183, 0x0, 0x2219f, 0x0, 0x22331, 0x0, 0x226d4, 0x0, 0x22844, 0x0, 0x2284a, 0x0, 0x22b0c, 0x0, 0x22bf1, 0x0, 0x2300a, 0x0, 0x232b8, 0x0, 0x2335f, 0x0, 0x23393, 0x0, 0x2339c, 0x0, 0x233c3, 0x0, 0x233d5, 0x0, 0x2346d, 0x0, 0x236a3, 0x0, 0x238a7, 0x0, 0x23a8d, 0x0, 0x23afa, 0x0, 0x23cbc, 0x0, 0x23d1e, 0x0, 0x23ed1, 0x0, 0x23f5e, 0x0, 0x23f8e, 0x0, 0x24263, 0x0, 0x242ee, 0x0, 0x243ab, 0x0, 0x24608, 0x0, 0x24735, 0x0, 0x24814, 0x0, 0x24c36, 0x0, 0x24c92, 0x0, 0x24fa1, 0x0, 0x24fb8, 0x0, 0x25044, 0x0, 0x250f2, 0x0, 0x250f3, 0x0, 0x25119, 0x0, 0x25133, 0x0, 0x25249, 0x0, 0x2541d, 0x0, 0x25626, 0x0, 0x2569a, 0x0, 0x256c5, 0x0, 0x2597c, 0x0, 0x25aa7, 0x0, 0x25bab, 0x0, 0x25c80, 0x0, 0x25cd0, 0x0, 0x25f86, 0x0, 0x261da, 0x0, 0x26228, 0x0, 0x26247, 0x0, 0x262d9, 0x0, 0x2633e, 0x0, 0x264da, 0x0, 0x26523, 0x0, 0x265a8, 0x0, 0x267a7, 0x0, 0x267b5, 0x0, 0x26b3c, 0x0, 0x26c36, 0x0, 0x26cd5, 0x0, 0x26d6b, 0x0, 0x26f2c, 0x0, 0x26fb1, 0x0, 0x270d2, 0x0, 0x273ca, 0x0, 0x27667, 0x0, 0x278ae, 0x0, 0x27966, 0x0, 0x27ca8, 0x0, 0x27ed3, 0x0, 0x27f2f, 0x0, 0x285d2, 0x0, 0x285ed, 0x0, 0x2872e, 0x0, 0x28bfa, 0x0, 0x28d77, 0x0, 0x29145, 0x0, 0x291df, 0x0, 0x2921a, 0x0, 0x2940a, 0x0, 0x29496, 0x0, 0x295b6, 0x0, 0x29b30, 0x0, 0x2a0ce, 0x0, 0x2a105, 0x0, 0x2a20e, 0x0, 0x2a291, 0x0, 0x2a392, 0x0, 0x2a600, 0x0]; return t; } -} + //22656 bytes + enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)([0x0, 0x40, + 0x540], [0x100, 0xa00, 0x21c0], [0x2020100, 0x4020302, 0x2020205, + 0x7060202, 0x2020202, 0x8020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x80000, + 0xa0009, 0xc000b, 0x0, 0xd0000, 0xf000e, 0x0, 0x110010, 0x130012, + 0x150014, 0x170016, 0x190018, 0x0, 0x1b001a, 0x0, 0x0, 0x1c, 0x0, + 0x1d0000, 0x1e0000, 0x0, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x200000, 0x21, 0x0, 0x22, 0x230000, 0x24, 0x0, 0x0, 0x0, + 0x25, 0x26, 0x27, 0x0, 0x28, 0x0, 0x29, 0x0, 0x2a, 0x0, 0x2b, + 0x2c0000, 0x0, 0x2d0000, 0x2e, 0x2f, 0x310030, 0x330032, 0x0, + 0x340000, 0x0, 0x0, 0x350000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x370036, 0x38, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x390000, 0x3b003a, 0x3d003c, 0x0, 0x3f003e, + 0x410040, 0x430042, 0x450044, 0x470046, 0x490048, 0x4b004a, + 0x4d004c, 0x4f004e, 0x510050, 0x530052, 0x0, 0x550054, 0x570056, + 0x590058, 0x5a, 0x5c005b, 0x5e005d, 0x60005f, 0x610000, 0x620000, + 0x0, 0x0, 0x0, 0x0, 0x630000, 0x650064, 0x670066, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x68, 0x690000, 0x0, 0x6a, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x6b0000, 0x0, 0x0, 0x0, 0x6c0000, 0x0, 0x0, 0x0, 0x0, 0x6d, + 0x6e0000, 0x70006f, 0x720071, 0x740073, 0x75, 0x770076, 0x790078, + 0x7b007a, 0x7d007c, 0x7e0000, 0x80007f, 0x81, 0x0, 0x830082, + 0x850084, 0x870086, 0x890088, 0x8b008a, 0x8d008c, 0x8f008e, + 0x910090, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x920000, 0x0, 0x930000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x950094, 0x970096, 0x990098, + 0x9b009a, 0x9d009c, 0x9f009e, 0xa100a0, 0xa2, 0xa400a3, 0xa600a5, + 0xa800a7, 0xaa00a9, 0xac00ab, 0xae00ad, 0xb000af, 0xb200b1, + 0xb400b3, 0xb600b5, 0xb800b7, 0xba00b9, 0xbc00bb, 0xbe00bd, + 0xc000bf, 0xc200c1, 0xc400c3, 0xc600c5, 0xc800c7, 0xca00c9, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xcc00cb, 0x0, 0xcd0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xcf00ce, 0xd00000, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xd300d2, 0xd500d4, 0xd700d6, 0xd900d8, 0xdb00da, + 0xdd00dc, 0xd200de, 0xdf00d3, 0xe000d5, 0xe200e1, 0xe300d9, + 0xe500e4, 0xe700e6, 0xe900e8, 0xeb00ea, 0xed00ec, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef00ee, 0xf100f0, 0xf300f2, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf500f4, 0xf700f6, + 0xf8, 0x0, 0xfa00f9, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, + 0x1090108, 0x10b010a, 0x10c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x15, 0x692, 0x0, 0x90000, + 0x0, 0x30f0343, 0x11b20003, 0x0, 0x3140048, 0x787, 0x3c603ce, + 0x494, 0x570056d, 0x5860573, 0x5b005a6, 0x5f80000, 0x62e062b, + 0x6580631, 0x6e706e4, 0x6f906ea, 0x78f0000, 0x7a907a6, 0x7bf07ac, + 0x7e3, 0x8b10000, 0x8b708b4, 0x95f08cb, 0x0, 0x9ac09a9, 0x9c209af, + 0x9ec09e2, 0xa470000, 0xa890a86, 0xab30a8c, 0xb460b43, 0xb550b49, + 0xc410000, 0xc5e0c5b, 0xc740c61, 0xc98, 0xd680000, 0xd6e0d6b, + 0xe0c0d82, 0xe1b0000, 0x9c50589, 0x9c8058c, 0xa0a05ce, 0xa3b05ec, + 0xa3e05ef, 0xa4105f2, 0xa4405f5, 0xa6e061a, 0x0, 0xaa20647, + 0xaad0652, 0xab00655, 0xad00675, 0xab9065e, 0xafb069a, 0xb0106a0, + 0xb0406a3, 0xb0a06a9, 0xb1606ba, 0x0, 0xb4c06ed, 0xb4f06f0, + 0xb5206f3, 0xb6b070f, 0x6f6, 0xb3706d8, 0xb730717, 0xbae072e, + 0x7430000, 0x7500bcc, 0x7460bd9, 0x7400bcf, 0xbc9, 0x78c0000, + 0x79b0c3e, 0x7950c4d, 0xed70c47, 0x0, 0xc8307ce, 0xc8e07d9, + 0xca207ed, 0x0, 0xd070842, 0xd1d0858, 0xd0d0848, 0xd2b086c, + 0xd320873, 0xd49088a, 0xd380879, 0xd5d08a6, 0xd54089d, 0x0, + 0xd7108ba, 0xd7808c1, 0xd7f08c8, 0xd9808e1, 0xd9b08e4, 0xdc4090d, + 0xde9093f, 0xe0f0962, 0x979096e, 0x97f0e29, 0x60d0e2f, 0x8400614, + 0xcae07f9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f00000, 0xda7, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x613060c, 0x7360a67, + 0xbb9073d, 0x7830780, 0x5b70c32, 0x70309f3, 0x7f00b5f, 0x8e70ca5, + 0x8d60d9e, 0x8d20d8d, 0x8da0d89, 0x8ce0d91, 0xd85, 0x9e505a9, + 0x9de05a2, 0xe630e5a, 0x0, 0xb0706a6, 0xba80728, 0xccc0817, + 0xccf081a, 0xecc0e7b, 0x6090b76, 0xa640610, 0xaf80697, 0x0, + 0xc3b0789, 0x9ef05b3, 0xe600e57, 0xe680e5d, 0x9f605ba, 0x9f905bd, + 0xabc0661, 0xabf0664, 0xb620706, 0xb650709, 0xca807f3, 0xcab07f6, + 0xd10084b, 0xd13084e, 0xda108ea, 0xda408ed, 0xd460887, 0xd5a08a3, + 0x0, 0xb1f06c3, 0x0, 0x0, 0x0, 0x9db059f, 0xac9066e, 0xc9b07e6, + 0xc7b07c6, 0xc9107dc, 0xc9407df, 0xe150968, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9a0b0d, + 0xa11073e, 0xeb60eb4, 0xde10eb8, 0x695, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12000f, + 0x4b0024, 0x270006, 0x0, 0xa280e96, 0xb410840, 0xecf, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4001a, + 0x2b0000, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xed5, 0x0, 0x0, 0x54, 0x0, 0x546, 0x0, 0x0, 0x1c0003, 0x7410ee8, + 0xf630f43, 0xfb4, 0xfed, 0x103c1016, 0x1185, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x101f0fbd, 0x10f5108f, + 0x11751119, 0x1213, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x120c117e, 0x120311d5, 0x124b, 0x116e10ea, + 0x10161011, 0x123c101f, 0x11ee, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x11f011ae, 0x11f8, 0x10f00fad, 0x0, + 0x100d0000, 0x0, 0x0, 0x0, 0x12b612b0, 0x12ad0000, 0x0, 0x12a40000, + 0x0, 0x0, 0x12c212ce, 0x12d7, 0x0, 0x0, 0x0, 0x0, 0x12c80000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x130a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x12f812f2, 0x12ef0000, 0x0, 0x132d0000, 0x0, 0x0, 0x13041310, + 0x131b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x13331330, 0x0, 0x0, 0x0, 0x0, 0x12b90000, 0x12fb, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x12e912a7, 0x12ec12aa, 0x0, 0x12f512b3, 0x0, + 0x13391336, 0x12fe12bc, 0x130112bf, 0x0, 0x130712c5, 0x130d12cb, + 0x131512d1, 0x0, 0x133f133c, 0x132a12e6, 0x131812d4, 0x131e12da, + 0x132112dd, 0x132412e0, 0x0, 0x132712e3, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x13420000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x13e913e6, 0x13ec178f, 0x17ca, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x13ef0000, 0x185b1792, 0x1811, 0x0, 0x0, + 0x0, 0x186d, 0x1852, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x186a0000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18820000, 0x0, + 0x0, 0x0, 0x188b0000, 0x0, 0x188e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18731870, + 0x18791876, 0x187f187c, 0x18881885, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x189a0000, 0x189d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18941891, + 0x18970000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x18ac0000, 0x0, 0x18af, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18a00000, 0x18a618a3, + 0x0, 0x18a9, 0x0, 0x0, 0x0, 0x0, 0x18bb, 0x18b80000, 0x18be, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18b518b2, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18c1, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x18ca18c4, 0x18c7, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18cd, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18d0, 0x0, 0x0, + 0x18da0000, 0x18dd, 0x18d618d3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18e618e0, 0x18e3, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18e9, 0x18ef18ec, 0x18f3, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18f60000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x18ff0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18fc18f9, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1902, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x19070000, 0x0, 0x0, 0x0, 0x0, 0x190a0000, 0x0, + 0x0, 0x190d, 0x0, 0x19100000, 0x0, 0x0, 0x1913, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x19040000, 0x0, 0x0, 0x0, 0x0, 0x19160000, 0x19190000, + 0x19311935, 0x1938193c, 0x0, 0x0, 0x0, 0x191c0000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x19220000, 0x0, 0x0, 0x0, 0x0, + 0x19250000, 0x0, 0x0, 0x1928, 0x0, 0x192b0000, 0x0, 0x0, 0x192e, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x191f0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x193f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1942, 0x0, 0x0, 0x0, 0x0, 0x1a38, 0x1a3b, 0x1a3e, + 0x1a41, 0x1a44, 0x0, 0x1a47, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1a4a0000, 0x1a4d0000, 0x0, 0x1a531a50, 0x1a560000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe550568, 0x5d5, 0x62905e6, 0x6870e75, + 0x6cf06ac, 0x71a0607, 0x7230734, 0x77e, 0xe7e07a4, 0x82c06af, + 0x56b088d, 0x6920770, 0xe840e82, 0x9371a59, 0xa7d0a2e, 0xe8e0e8c, + 0x6020e90, 0xb790000, 0xe7105d3, 0xe880787, 0x1a5d1a5b, 0xba30cd3, + 0x1a610a24, 0x86a0ea4, 0x10ea1a63, 0x10ee10ec, 0x123e123c, + 0xa110ae0, 0x86a0a24, 0x10ec10ea, 0x123c11f0, 0x123e, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1313, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe860000, 0xe8a09a0, + 0xe900e66, 0xe920ad9, 0xe980e94, 0xe9e0e9c, 0x1a650ea0, 0xea20ed1, + 0xed31a67, 0xea60ea8, 0xeac0eaa, 0xeb00eae, 0xeba0eb2, 0xe790ebc, + 0xec00ebe, 0xec21a5f, 0x6110ec4, 0xec80ec6, 0x116e0eca, 0xa0705cb, + 0xa1305da, 0xa1605dd, 0xa1905e0, 0xa4a05fb, 0xa6b0617, 0xa71061d, + 0xa7a0626, 0xa740620, 0xa770623, 0xaa5064a, 0xaa9064e, 0xad30678, + 0xad6067b, 0xacc0671, 0xaef0684, 0xafe069d, 0xb1906bd, 0xb2206c6, + 0xb1c06c0, 0xb2506c9, 0xb2806cc, 0xb6e0712, 0xb5806fc, 0xba50725, + 0xbab072b, 0xbb10731, 0xbd20749, 0xbd5074c, 0xbdf0756, 0xbdc0753, + 0xc120772, 0xc150775, 0xc180778, 0xc440792, 0xc4a0798, 0xc5307a1, + 0xc50079e, 0xc7707c2, 0xc7f07ca, 0xc8607d1, 0xc8a07d5, 0xcec0835, + 0xcef0838, 0xd0a0845, 0xd160851, 0xd190854, 0xd20085b, 0xd350876, + 0xd3f0880, 0xd2e086f, 0xd3b087c, 0xd420883, 0xd4e089a, 0xd5708a0, + 0xd6308ac, 0xd6008a9, 0xdc1090a, 0xdca0913, 0xdc70910, 0xd7408bd, + 0xd7b08c4, 0xddb0924, 0xdde0927, 0xde30939, 0xde6093c, 0xdef0945, + 0xdec0942, 0xdf50948, 0xe010954, 0xe040957, 0xe18096b, 0xe2c097c, + 0xe350985, 0xe380988, 0xd510b2b, 0xe210df2, 0xd3509a6, 0x0, 0x0, + 0x9fc05c0, 0x9e905ad, 0x9b6057a, 0x9b20576, 0x9be0582, 0x9ba057e, + 0x9ff05c3, 0x9cf0593, 0x9cb058f, 0x9d7059b, 0x9d30597, 0xa0305c7, + 0xac20667, 0xab6065b, 0xa9f0644, 0xa930638, 0xa8f0634, 0xa9b0640, + 0xa97063c, 0xac5066a, 0xb5c0700, 0xb68070c, 0xcc50810, 0xc9f07ea, + 0xc6807b3, 0xc6407af, 0xc7007bb, 0xc6c07b7, 0xcc80813, 0xcb50800, + 0xcb107fc, 0xcbd0808, 0xcb90804, 0xcc1080c, 0xdbe0907, 0xd9508de, + 0xdae08f7, 0xdaa08f3, 0xdb608ff, 0xdb208fb, 0xdba0903, 0xe09095c, + 0xe240974, 0xe1e0971, 0xe120965, 0x0, 0x0, 0x0, 0x10be109c, + 0x10c1109f, 0x10ca10a8, 0x10d310b1, 0xf130ef1, 0xf160ef4, + 0xf1f0efd, 0xf280f06, 0x110310f8, 0x110610fb, 0x110a10ff, 0x0, + 0xf510f46, 0xf540f49, 0xf580f4d, 0x0, 0x11421120, 0x11451123, + 0x114e112c, 0x11571135, 0xf880f66, 0xf8b0f69, 0xf940f72, 0xf9d0f7b, + 0x119c118d, 0x119f1190, 0x11a31194, 0x11a71198, 0xfcf0fc0, + 0xfd20fc3, 0xfd60fc7, 0xfda0fcb, 0x11e311d8, 0x11e611db, + 0x11ea11df, 0x0, 0xffb0ff0, 0xffe0ff3, 0x10020ff7, 0x0, 0x122a121b, + 0x122d121e, 0x12311222, 0x12351226, 0x10220000, 0x10250000, + 0x10290000, 0x102d0000, 0x12741252, 0x12771255, 0x1280125e, + 0x12891267, 0x1061103f, 0x10641042, 0x106d104b, 0x10761054, + 0x108f1088, 0x10f510f2, 0x11191112, 0x11751172, 0x11d511d2, + 0x12031200, 0x124b1244, 0x0, 0x10dc10ba, 0x10c510a3, 0x10ce10ac, + 0x10d710b5, 0xf310f0f, 0xf1a0ef8, 0xf230f01, 0xf2c0f0a, 0x1160113e, + 0x11491127, 0x11521130, 0x115b1139, 0xfa60f84, 0xf8f0f6d, + 0xf980f76, 0xfa10f7f, 0x12921270, 0x127b1259, 0x12841262, + 0x128d126b, 0x107f105d, 0x10681046, 0x1071104f, 0x107a1058, + 0x10961099, 0x10e7108b, 0x1092, 0x10e310e0, 0xeeb0eee, 0xee80ee5, + 0x2a0f35, 0x2a1170, 0x200051, 0x116b1115, 0x111c, 0x11671164, + 0xf430f40, 0xf630f60, 0x2d0faa, 0x350031, 0x1178117b, 0x11851181, + 0x0, 0x118911ab, 0xfb70fba, 0xfb40fb1, 0x3c0000, 0x440040, + 0x12061209, 0x1213120f, 0x11f511f2, 0x12171239, 0x1019101c, + 0x10161013, 0x18100a, 0x995001c, 0x0, 0x129d1247, 0x124e, + 0x12991296, 0xfed0fea, 0x103c1039, 0x31083, 0x39, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x1, 0x0, 0x0, 0x1a690000, 0x0, 0x0, + 0x4e0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2fc02fa, 0x2ff, 0x0, 0x0, + 0x0, 0x10000, 0x0, 0x1a6f0000, 0x1a72, 0x1a7e1a7b, 0x0, 0x0, 0x8f, + 0xc, 0x0, 0x0, 0x0, 0x5630000, 0x920560, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1a760000, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xae00305, 0x0, 0x3740365, 0x3920383, 0x3b003a1, + 0x1aad02f4, 0xa10544, 0xb3b00a5, 0x3140305, 0x30f0343, 0x3740365, + 0x3920383, 0x3b003a1, 0x1aad02f4, 0xa10544, 0xa5, 0xa7d0692, + 0xb410787, 0xb0d0e8c, 0xa280b79, 0xb3b05d3, 0x8400cd3, 0xba3, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x83f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x9a2099e, 0xe4d05e3, 0xa1e0000, 0xe770a22, + 0xe500000, 0x6ac0602, 0x6ac06ac, 0xe6d0b0d, 0x6cf06cf, 0xa280734, + 0x77e0000, 0x786, 0x6af0000, 0x82c083b, 0x82c082c, 0x0, 0x88f0863, + 0x897, 0x60a, 0x77c, 0x60a, 0x5b0071a, 0x5e305d5, 0xa7d0000, + 0x67e0629, 0x7230000, 0x13540787, 0x136a1362, 0xae0136f, 0x6800000, + 0x10ec11ee, 0x10060f3a, 0x1aab, 0x0, 0x5e60000, 0xa7d0a2e, + 0x73e0ae0, 0x0, 0x0, 0x0, 0x3e203da, 0x3ca03c1, 0x3d20455, + 0x4980459, 0x3d604cf, 0x3de04e7, 0x4eb049c, 0x3be0511, 0x6d106cf, + 0x6de06d4, 0x91806b2, 0x91f091b, 0x68206e1, 0x950094d, 0x5e30734, + 0x72305e6, 0xb300ae0, 0xb3d0b33, 0xdcf086a, 0xdd60dd2, 0xb410b40, + 0xdfd0dfa, 0x9a00a28, 0x5d30a2e, 0x0, 0x0, 0x0, 0x0, 0x30d0000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a8d1a86, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a92, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a950000, + 0x1a981a9b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1aa0, 0x0, 0x1aa50000, 0x0, 0x1aa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1aaf, 0x1ab2, 0x0, 0x0, 0x1ab81ab5, + 0x1ac10000, 0x1ac4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ac80000, + 0x0, 0x1acb, 0x1ace0000, 0x1ad10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x556, 0x1ad7, 0x0, 0x0, 0x0, 0x0, + 0x1ad40000, 0x55b054a, 0x1add1ada, 0x0, 0x1ae31ae0, 0x0, + 0x1ae91ae6, 0x0, 0x0, 0x0, 0x1aef1aec, 0x0, 0x1afb1af8, 0x0, + 0x1b011afe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b0d1b0a, 0x1b131b10, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1af51af2, 0x1b071b04, 0x0, 0x0, + 0x0, 0x1b191b16, 0x1b1f1b1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b350000, 0x1b37, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3430314, 0x365030f, 0x3830374, + 0x3a10392, 0x31c03b0, 0x342032f, 0x3640355, 0x3820373, 0x3a00391, + 0x3f703af, 0xd900a3, 0xe600e2, 0xee00ea, 0xf600f2, 0xa700fa, + 0xb100ac, 0xbb00b6, 0xc500c0, 0xcf00ca, 0xdd00d4, 0x3460319, + 0x3680359, 0x3860377, 0x3a40395, 0x31f03b3, 0x3450332, 0x3670358, + 0x3850376, 0x3a30394, 0x3fa03b2, 0x16a0166, 0x172016e, 0x17a0176, + 0x182017e, 0x18a0186, 0x192018e, 0x19a0196, 0x1a2019e, 0x1aa01a6, + 0x1b201ae, 0x1ba01b6, 0x1c201be, 0x1ca01c6, 0x5d50568, 0x5e605e3, + 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, + 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, + 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, + 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, + 0x305, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1abc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x54f0542, 0x552, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b2c, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x6b2073e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b2f0000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x227c0000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b00000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1efb1ee9, 0x1f091f01, 0x1f131f0d, 0x1f1b1f17, + 0x1f4b1f21, 0x1f5f1f57, 0x1f6f1f67, 0x1f871f77, 0x1f8b1f89, + 0x1fb91fa5, 0x1fc51fc1, 0x1fcd1fc7, 0x1fdd1fdb, 0x1feb1fe9, + 0x1ff71fef, 0x204f2045, 0x2079206f, 0x207f207d, 0x20982087, + 0x20b420ae, 0x20ca20c4, 0x20ce20cc, 0x20dc20da, 0x20f820f2, + 0x210020fc, 0x210f2108, 0x21292113, 0x212f212b, 0x21352131, + 0x21412139, 0x218b214f, 0x21972195, 0x21d921d7, 0x21e521e3, + 0x21ed21e9, 0x32521f1, 0x3292211, 0x22602223, 0x226e2266, + 0x227a2274, 0x2280227e, 0x22842282, 0x22e22286, 0x230c2306, + 0x2310230e, 0x23162312, 0x23222318, 0x23342330, 0x23562354, + 0x235c235a, 0x23622360, 0x23762374, 0x23862384, 0x238a2388, + 0x23a62394, 0x23aa23a8, 0x23dc23bc, 0x23ee23de, 0x23fa23f6, + 0x241c240a, 0x2442243e, 0x2452244c, 0x245a2456, 0x245e245c, + 0x246c246a, 0x247e247a, 0x24842482, 0x248e248a, 0x24922490, + 0x24982496, 0x24f224e8, 0x250e250c, 0x25282512, 0x2530252c, + 0x25522534, 0x25582554, 0x255c255a, 0x25742572, 0x25822578, + 0x25922584, 0x25982596, 0x25ba25aa, 0x25c425c2, 0x25de25c8, + 0x25e825e0, 0x260025fa, 0x26142608, 0x261a2618, 0x261e261c, + 0x26262624, 0x2638262a, 0x263c263a, 0x264a2648, 0x2658264e, + 0x265c265a, 0x26622660, 0x26662664, 0x26702668, 0x267e267c, + 0x26862684, 0x268a2688, 0x2690268e, 0x26982692, 0x26a0269c, + 0x26a626a2, 0x26aa26a8, 0x26b226ae, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1b49, 0x1fcf1fcd, 0x1fd1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1b7e, 0x1b81, 0x1b84, 0x1b87, 0x1b8a, 0x1b8d, 0x1b90, 0x1b93, + 0x1b96, 0x1b99, 0x1b9c, 0x1b9f, 0x1ba20000, 0x1ba50000, 0x1ba80000, + 0x0, 0x0, 0x0, 0x1bae1bab, 0x1bb10000, 0x1bb4, 0x1bba1bb7, + 0x1bbd0000, 0x1bc0, 0x1bc91bc6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1b7b, 0x0, 0x0, 0x870000, 0x8a, 0x1bcc1bd3, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c26, 0x1c43, 0x1bf6, 0x1c92, + 0x1c9b, 0x1caf, 0x1cbf, 0x1cca, 0x1ccf, 0x1cdc, 0x1ce1, 0x1ceb, + 0x1cf20000, 0x1cf70000, 0x1c100000, 0x0, 0x0, 0x0, 0x1d261d1d, + 0x1d3b0000, 0x1d42, 0x1d611d57, 0x1d760000, 0x1d7e, 0x1caa1da1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c01, + 0x1e440000, 0x1e521e4d, 0x1e57, 0x0, 0x1ca11e60, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x19440000, 0x1a101949, 0x1a12194b, + 0x19501a14, 0x19571955, 0x1a181a16, 0x1a1c1a1a, 0x1a201a1e, + 0x195c19a6, 0x19661961, 0x196819b0, 0x196f196d, 0x19811977, + 0x198e1983, 0x19981993, 0x1947199d, 0x19da19d8, 0x19de19dc, + 0x19e219e0, 0x198c19e4, 0x19ea19e8, 0x19ee19ec, 0x19f21975, + 0x19f619f4, 0x19fa19f8, 0x19fe197f, 0x19a219d4, 0x1a2219a4, + 0x1a261a24, 0x1a2a1a28, 0x1a2e1a2c, 0x1a3019a8, 0x19aa1a32, + 0x19ae19ac, 0x19b419b2, 0x19b819b6, 0x19bc19ba, 0x19c019be, + 0x19c419c2, 0x19c819c6, 0x19cc19ca, 0x1a361a34, 0x19d019ce, + 0x1a0019d2, 0x1a041a02, 0x1a081a06, 0x1a0c1a0a, 0x1a0e, 0x0, + 0x1f171ee9, 0x20471eef, 0x1efd1ef1, 0x23641ef3, 0x1ef71f0d, + 0x208c1eeb, 0x1f212051, 0x1d701ce, 0x1e901e0, 0x1fb01f2, 0x20d0204, + 0x2330225, 0x245023c, 0x257024e, 0x1db01d2, 0x1ed01e4, 0x1ff01f6, + 0x2110208, 0x2370229, 0x2490240, 0x25b0252, 0x216022e, 0x21e, + 0x2700260, 0x2a00268, 0x2880274, 0x2840264, 0x290026c, 0x2c402b0, + 0x2b802c0, 0x2a402ec, 0x2bc02ac, 0x2d002b4, 0x2c80298, 0x2d402e4, + 0x278028c, 0x2a8029c, 0x27c02cc, 0x29402e8, 0x28002d8, 0x2e002dc, + 0x21112021, 0x23fe21e3, 0x0, 0x0, 0x0, 0x0, 0x406082e, 0x41c0411, + 0x4320427, 0x4400439, 0x44e0447, 0x475046e, 0x47f047c, 0x4850482, + 0x194b1944, 0x19571950, 0x1961195c, 0x196f1968, 0x19831977, + 0x1993198e, 0x199d1998, 0x194d1946, 0x19591952, 0x1963195e, + 0x1971196a, 0x19851979, 0x19951990, 0x199f199a, 0x197c1988, 0x1974, + 0x1f171ee9, 0x20471eef, 0x1f611f19, 0x1f5f1eed, 0x1fcd1f0f, + 0x22e20329, 0x22232286, 0x204f25c8, 0x223b0325, 0x2240221b, + 0x231c2007, 0x23ca255e, 0x23e21fab, 0x20982368, 0x1f4925a2, + 0x22961fdf, 0x1f2b262c, 0x208a1f73, 0x1efd1ef1, 0x20fa1ef3, + 0x1fc92001, 0x20b220b8, 0x1f292390, 0x1fd72568, 0x4882083, + 0x48e048b, 0x4b10491, 0x4b704b4, 0x4bd04ba, 0x4c304c0, 0x4c904c6, + 0x4e404cc, 0x34e033b, 0x4d604a3, 0x50304f2, 0x5290518, 0x327053a, + 0x34d033a, 0xa8206b4, 0x7390a7f, 0x1bf11bd8, 0x1c0a1bff, + 0x1c241c1a, 0x1c731c41, 0x1c991c90, 0x1cbd1cad, 0x1ccd1c1e, + 0x1cdf1cda, 0x1cf01bfb, 0x1bde1cf5, 0x1d0f1ca6, 0x1c8e1d11, + 0x1d1b1d0d, 0x1d551d39, 0x1d9f1d74, 0x1ddc1c31, 0x1def1c22, + 0x1e041e00, 0x1e191e11, 0x1c351e1b, 0x1e341bed, 0x1e421c5d, + 0x1e501e4b, 0x1e55, 0x1be01bda, 0x1beb1be5, 0x1bf91bf3, 0x1c0c1c04, + 0x1c1c1c13, 0x1c331c20, 0x1c3c1c37, 0x1c2e1c29, 0x1c4b1c46, + 0x1c501c57, 0x1c5f1c5c, 0x1c6d1c66, 0x1c7d1c61, 0x1c8b1c84, + 0x1ca41c95, 0x1cb21ca8, 0x1cc21cb7, 0x1cd61cd2, 0x1cfa1ce4, + 0x1c811d03, 0x1d171d0c, 0x1d291d35, 0x1d201d30, 0x1d4c1d45, + 0x1d3e1d51, 0x1d6b1d64, 0x1d701d5a, 0x1d811d95, 0x1d9b1d85, + 0x1d8f1d8a, 0x1dac1d79, 0x1db81da4, 0x1dbb1db2, 0x1dc51dbf, + 0x1dce1dca, 0x1dd61dd2, 0x1de31dde, 0x1df11de6, 0x1c681df5, + 0x1e0b1e06, 0x1e1f1e13, 0x1e291e24, 0x1e361e2e, 0x1c6f1e39, + 0x33f0311, 0x3610352, 0x37f0370, 0x39d038e, 0x3bb03ac, 0x33e032b, + 0x3600351, 0x37e036f, 0x39c038d, 0x3ba03ab, 0x40d0402, 0x4230418, + 0xb0f042e, 0x56a0a53, 0xc580a0f, 0xa590ce6, 0xa600a5c, 0x210a06db, + 0x20892200, 0x223d21f9, 0xc260cda, 0xbea11b4, 0x71c0b7b, 0x689075b, + 0xb8c0a26, 0xc290cdd, 0x11c011b7, 0x6010bf6, 0xb7e068d, 0x68c0764, + 0x11c30893, 0xa560bfd, 0xaec0b94, 0x11c60c35, 0xa300c00, 0xc030b97, + 0xa340a33, 0xc070b9a, 0xa380a37, 0xc1b0b9e, 0x6910c1f, 0x7680b82, + 0xcf60690, 0xd000cfa, 0xc380ce9, 0xc0f11c9, 0xc2c0ce0, 0xbed11ba, + 0x76c0b86, 0xc2f0ce3, 0xbf011bd, 0x76f0b89, 0x77b0bb4, 0x5d70999, + 0xa2d0a2a, 0x5e805ff, 0x6940a50, 0x6ae0b13, 0x71f0b3a, 0xba20722, + 0xbbf0bbc, 0xbc60bc2, 0xbf90bf3, 0x8200c0b, 0x8230cd5, 0xd25082b, + 0x9360869, 0x5d1092a, 0x34a0337, 0x36c035d, 0x38a037b, 0x3a80399, + 0x32303b7, 0x3490336, 0x36b035c, 0x389037a, 0x3a70398, 0x3fe03b6, + 0x4140409, 0x42a041f, 0x43c0435, 0x44a0443, 0x4710451, 0xaf40478, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xe730e6b, 0x0, 0x0, 0x0, 0x22132556, 0x256a2584, + 0x1eff22c6, 0x26ae1ff9, 0x209226ae, 0x202b25c8, 0x21872090, + 0x244a2382, 0x250424e6, 0x25a8251e, 0x229a2254, 0x233c22f0, + 0x25bc24ca, 0x1f112652, 0x225e1fe3, 0x24e42302, 0x20e6267a, + 0x24dc22d6, 0x21a12526, 0x250a2478, 0x221d211f, 0x232822a6, + 0x1f3125ae, 0x1fb31f7d, 0x225a21d5, 0x23922300, 0x24e02456, + 0x257e24ec, 0x266a2610, 0x23b02678, 0x242c23d0, 0x25d624bc, + 0x2540267e, 0x212d206d, 0x24682408, 0x23b4231a, 0x260c2566, + 0x20d4206b, 0x22b02256, 0x242422ca, 0x25ec2438, 0x246e1fb1, + 0x1f811f83, 0x242e23e6, 0x25f024c8, 0x21a3254e, 0x25462254, + 0x20be1f05, 0x23322159, 0x1fc32372, 0x1f3923b8, 0x1ef5214b, + 0x21e12290, 0x1fed2422, 0x23982063, 0x253824cc, 0x25962276, + 0x21ab228c, 0x21bb24a8, 0x1f1f2370, 0x1f7f1f5d, 0x24182244, + 0x253e2494, 0x1fb725c6, 0x20982011, 0x21ef2127, 0x23ba22d8, + 0x265625e4, 0x268c2680, 0x220f1fa5, 0x2590226c, 0x217b210d, + 0x21d12189, 0x22f622d0, 0x23e0234e, 0x24642432, 0x24d02588, + 0x25d8259c, 0x1fa71f91, 0x22ee201b, 0x25382514, 0x2155211d, + 0x227221b7, 0x232c2406, 0x20491f27, 0x20f020be, 0x233a215b, + 0x24502348, 0x25ca2460, 0x2612260a, 0x1f332630, 0x25c023da, + 0x216725fe, 0x1f451f15, 0x20d020c0, 0x225421e7, 0x238022fc, + 0x25a624d6, 0x220726aa, 0x1fa325ea, 0x2233222d, 0x22be22a2, + 0x236e2340, 0x242023ae, 0x1f612636, 0x25f22191, 0x20e21f3d, + 0x258a22b2, 0x216b2143, 0x23322237, 0x1f9525f6, 0x20d82009, + 0x222521fc, 0x2294224a, 0x2378233e, 0x25162446, 0x25c4251c, + 0x1fcb2604, 0x200b22c0, 0x235022fe, 0x25f824de, 0x2682266e, + 0x22ae2231, 0x23f6247c, 0x240e23fc, 0x22ea2326, 0x1f23254c, + 0x1f9724b0, 0x21151f8f, 0x241421a5, 0x229c20b6, 0x258e220d, + 0x25ee250e, 0x2123252c, 0x20371f4d, 0x0, 0x2061, 0x2205, + 0x1f850000, 0x238c232a, 0x23cc23be, 0x23d823ce, 0x24102616, 0x2452, + 0x24e2, 0x2544, 0x259e0000, 0x25b4, 0x0, 0x26422640, 0x26762644, + 0x25fc25b0, 0x1f471f35, 0x1faf1f51, 0x1fd51fb5, 0x203d202f, + 0x205f2041, 0x20d62065, 0x216120da, 0x21792175, 0x21db2185, + 0x220921f3, 0x22a82246, 0x22ce22b6, 0x230822f8, 0x23b22342, + 0x23c42240, 0x23c623c2, 0x23ca23c8, 0x23d623d4, 0x23f223e8, + 0x24322400, 0x243a2436, 0x24582444, 0x249a2480, 0x24ce249a, + 0x252e2522, 0x254a2548, 0x256e256c, 0x259e259a, 0x26282606, + 0x215d2634, 0x248c274b, 0x0, 0x1f7b1ef9, 0x1f2f1f5b, 0x1f651f4f, + 0x1fbb1fad, 0x2025202f, 0x203b202d, 0x20692061, 0x2094208e, + 0x20aa20a2, 0x21252121, 0x214d213d, 0x21712165, 0x21792169, + 0x21852173, 0x21bf2193, 0x21c921c5, 0x220521dd, 0x221f221d, + 0x226e2229, 0x22a22276, 0x22c422c8, 0x22dc22ce, 0x23a422f8, + 0x2324230a, 0x234a232a, 0x236a2358, 0x237e237c, 0x238e238c, + 0x23a02396, 0x23b6239e, 0x240023f4, 0x2428240c, 0x24402432, + 0x24b22458, 0x250024c6, 0x252a2524, 0x253a252e, 0x253c2544, + 0x25462548, 0x254a2542, 0x256e2550, 0x25a4258c, 0x25ce25be, + 0x260625f4, 0x26202616, 0x262e2628, 0x265e2634, 0x272126ae, + 0x2733271f, 0x1ea11e8d, 0x27671ea3, 0x27a92779, 0x26ac26a4, 0x0, + 0x0, 0x0, 0xadf0adb, 0xade0ae3, 0xd280ae2, 0xd28, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x134e0000, 0x13481345, 0x134b1351, 0x0, 0x0, 0x13850000, + 0x13d20000, 0x135413a6, 0x1374136f, 0x1360138e, 0x13b7139b, + 0x2f413cd, 0x13ca13c7, 0x13c313bf, 0x13591356, 0x1364135c, + 0x1371136c, 0x137c1376, 0x137f, 0x13881382, 0x1390138b, 0x1398, + 0x139d, 0x13a313a0, 0x13a80000, 0x13ab, 0x13b413b1, 0x13bc13b9, + 0x137913cf, 0x13931367, 0x135f13ae, 0x18181818, 0x181e181e, + 0x181e181e, 0x18201820, 0x18201820, 0x18241824, 0x18241824, + 0x181c181c, 0x181c181c, 0x18221822, 0x18221822, 0x181a181a, + 0x181a181a, 0x183c183c, 0x183c183c, 0x183e183e, 0x183e183e, + 0x18281828, 0x18281828, 0x18261826, 0x18261826, 0x182a182a, + 0x182a182a, 0x182c182c, 0x182c182c, 0x18321832, 0x18301830, + 0x18341834, 0x182e182e, 0x18381838, 0x18361836, 0x18401840, + 0x18401840, 0x18441844, 0x18441844, 0x18481848, 0x18481848, + 0x18461846, 0x18461846, 0x184a184a, 0x184c184c, 0x184c184c, + 0x186d186d, 0x18501850, 0x18501850, 0x184e184e, 0x184e184e, + 0x15911591, 0x186a186a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18420000, 0x18421842, + 0x18031842, 0x17ff1803, 0x180717ff, 0x185b1807, 0x18621862, + 0x18551855, 0x18601860, 0x180b180b, 0x180b180b, 0x14151415, + 0x17cd17cd, 0x180d180d, 0x17f117f1, 0x18011801, 0x17fd17fd, + 0x18051805, 0x18091809, 0x17f51809, 0x17f517f5, 0x18641864, + 0x18641864, 0x17d517d1, 0x17f517e5, 0x13f417f9, 0x13fe13f7, + 0x1414140b, 0x141e1417, 0x1438142d, 0x146a144d, 0x1472146d, + 0x1484147b, 0x148c1487, 0x14311422, 0x14d11435, 0x143c14d4, + 0x150514fa, 0x151a150c, 0x15931562, 0x15a515a2, 0x15ba15b0, + 0x15c815c5, 0x15e415df, 0x16071575, 0x163f160a, 0x16451642, + 0x1653164c, 0x165b1656, 0x16711662, 0x16791674, 0x167f167c, + 0x16851682, 0x16931688, 0x16aa1696, 0x16c816b9, 0x1579158c, + 0x145116e0, 0x14591455, 0x145d1526, 0x172d1461, 0x174f1740, + 0x17691758, 0x1771176c, 0x177f1774, 0x179c1782, 0x17aa17a3, + 0x17c417b3, 0x14e417c7, 0x179714ee, 0x64005d, 0x72006b, 0x800079, + 0x17e117dd, 0x17e917e5, 0x17f917f5, 0x140813db, 0x140e140b, + 0x14171414, 0x144a1447, 0x1464144d, 0x146d146a, 0x14781475, + 0x147e147b, 0x14871484, 0x16561653, 0x16741671, 0x16851679, + 0x16931688, 0x158c1696, 0x16e01579, 0x152616e5, 0x17551752, + 0x17631758, 0x176c1769, 0x17ad1797, 0x17b317b0, 0x17c417be, + 0x17d117c7, 0x17d917d5, 0x17ed17e5, 0x13f713f4, 0x140b13fe, + 0x141e1411, 0x1438142d, 0x1467144d, 0x148c147b, 0x14311422, + 0x14d11435, 0x14fa143c, 0x150c1505, 0x1562151a, 0x1593156d, + 0x15a515a2, 0x15ba15b0, 0x15df15c5, 0x157515e4, 0x160a1607, + 0x1642163f, 0x164c1645, 0x1662165b, 0x167f167c, 0x16851682, + 0x16aa1688, 0x16c816b9, 0x13e0158c, 0x14551451, 0x15261459, + 0x1740172d, 0x1758174f, 0x17711766, 0x17851774, 0x17a3179c, + 0x17b317aa, 0x17e515ed, 0x140b17ed, 0x144d1411, 0x147b1467, + 0x151a1481, 0x154c1529, 0x16851557, 0x158c1688, 0x17661758, + 0x15ed17b3, 0x162c1625, 0x15d71633, 0x15ff15da, 0x16191602, + 0x152c161c, 0x155a152f, 0x1490155d, 0x142613fb, 0x1440142a, + 0x159a1402, 0x15bd159d, 0x153415c0, 0x1546153b, 0x1549154c, + 0x15701517, 0x15d715b7, 0x15ff15da, 0x16191602, 0x152c161c, + 0x155a152f, 0x1490155d, 0x142613fb, 0x1440142a, 0x159a1402, + 0x15bd159d, 0x153415c0, 0x1546153b, 0x1549154c, 0x15701517, + 0x153415b7, 0x1546153b, 0x1529154c, 0x15c81557, 0x150514fa, + 0x1534150c, 0x1546153b, 0x15df15c8, 0x13e313e3, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x14301421, 0x14341430, 0x1450143b, + 0x14581454, 0x14a314a3, 0x14c114c5, 0x14fd1508, 0x15211501, + 0x151d1521, 0x15251525, 0x15651565, 0x153e1596, 0x1537153e, + 0x154f154f, 0x15531553, 0x15b315a8, 0x15cb15b3, 0x15cf15cb, + 0x15e715d3, 0x15f315f3, 0x160d15f7, 0x16111615, 0x16481648, + 0x16691665, 0x16c416bc, 0x16ad16c0, 0x16cb16ad, 0x16d216cb, + 0x16fe16d2, 0x170b1702, 0x16f316eb, 0x17161712, 0x0, 0x177716ef, + 0x1743177b, 0x17341747, 0x17381734, 0x175b175f, 0x17b617b6, + 0x14291401, 0x14431425, 0x1460143f, 0x14ab145c, 0x14a7148f, + 0x1569150f, 0x15ac1542, 0x16d616b5, 0x179f17a6, 0x172117ba, + 0x174b166d, 0x16bc1665, 0x168f15fb, 0x171a1730, 0x168b16b1, + 0x173016b1, 0x14ba1493, 0x164f16f7, 0x168b13fa, 0x159615e7, + 0x173c1513, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x165e158f, + 0x13d913de, 0x15731706, 0x15eb14e9, 0x1578158a, 0x1497157c, 0x14f1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b3102f6, 0x5401b33, + 0x8d0546, 0x1b770093, 0x2ff1b79, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1a6d02fc, 0x9931a6b, 0xa10993, 0xe3b00a5, + 0x1b4b0e3f, 0x1b451b4f, 0x1b391b47, 0x1b351b3b, 0x1b3d1b37, + 0x1b411b3f, 0x1b43, 0x98b0000, 0xc098f, 0xc000c, 0x993000c, + 0x9930993, 0x1b3102f6, 0x2fa, 0x5400546, 0x8d0093, 0xa11a6d, + 0xe3b00a5, 0x1b4b0e3f, 0x971b4f, 0x2f2009d, 0x2f802f4, 0x5590548, + 0x544, 0x99098d, 0x566009b, 0x0, 0x0, 0x161f0057, 0x5a, 0x61, + 0x16220068, 0x1629006f, 0x16300076, 0x1637007d, 0x163a0084, + 0x13e613d5, 0x13e913e6, 0x178f13e9, 0x13ec178f, 0x17ca13ec, + 0x17ca17ca, 0x13d717ca, 0x13f213d7, 0x13f213f2, 0x141a13f2, + 0x141c141a, 0x141c141c, 0x1470141c, 0x14701470, 0x13f51470, + 0x13f513f5, 0x13f813f5, 0x13f813f8, 0x13ff13f8, 0x13ff13ff, + 0x14e013ff, 0x14e214e0, 0x13dc14e2, 0x140913dc, 0x14f81409, + 0x14f814f8, 0x153214f8, 0x15321532, 0x15601532, 0x15601560, + 0x15a01560, 0x15a015a0, 0x15c315a0, 0x15c315c3, 0x15dd15c3, + 0x15dd15dd, 0x15e215dd, 0x15e215e2, 0x160515e2, 0x16051605, + 0x163d1605, 0x163d163d, 0x1659163d, 0x16591659, 0x16771659, + 0x16771677, 0x14ec1677, 0x14ec14ec, 0x140c14ec, 0x140c140c, + 0x140f140c, 0x140f140f, 0x13e1140f, 0x13e113e1, 0x178813e1, + 0x14151788, 0x13fc1415, 0x13fc13fc, 0x169e13fc, 0x16a2169e, + 0x16a616a2, 0x169b16a6, 0x169b, 0x0, 0x8d0000, 0x970095, 0x9b0099, + 0x9f009d, 0xa500a1, 0x2f402f2, 0x2f802f6, 0x30302fa, 0x3140305, + 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x5460540, 0x5440548, + 0x930559, 0x5680566, 0x5e305d5, 0x62905e6, 0x687067e, 0x6cf06ac, + 0x71a0607, 0x7230734, 0x7a4077e, 0x83b06af, 0x85e082c, 0x56b088d, + 0x77006b2, 0x95a0682, 0x98b060a, 0x98f098d, 0x9930991, 0x6920995, + 0x9a00937, 0xa7d0a2e, 0x6020ad9, 0xae00b0d, 0xb79073e, 0x5d30a28, + 0x7870b3b, 0x5d80cd3, 0x8400a11, 0xa240ba3, 0xde1086a, 0x6950b41, + 0xe3b0611, 0xe3f0e3d, 0x1b280e41, 0x1b331b2a, 0x1b3f1b3d, + 0x1e5c1b31, 0x1bd61e55, 0x1bfd1bef, 0x1c181c08, 0x1e0f1e02, + 0x1cee1e17, 0x1bd81c16, 0x1bff1bf1, 0x1c1a1c0a, 0x1c411c24, + 0x1c901c73, 0x1cad1c99, 0x1c1e1cbd, 0x1cda1ccd, 0x1bfb1cdf, + 0x1cf51cf0, 0x1ca61bde, 0x1d111d0f, 0x1d0d1c8e, 0x1d391d1b, + 0x1d741d55, 0x1c311d9f, 0x1c221ddc, 0x1e001def, 0x1e111e04, + 0x1e1b1e19, 0x1bed1c35, 0x1c5d1e34, 0x1c061e42, 0x8b0088, + 0x194419d4, 0x1a101949, 0x1a12194b, 0x19501a14, 0x19571955, + 0x1a181a16, 0x1a1c1a1a, 0x1a201a1e, 0x195c19a6, 0x19661961, + 0x196819b0, 0x196f196d, 0x19811977, 0x198e1983, 0x19981993, 0x199d, + 0x0, 0x19d81947, 0x19dc19da, 0x19e019de, 0x0, 0x19e419e2, + 0x19e8198c, 0x19ec19ea, 0x0, 0x197519ee, 0x19f419f2, 0x19f819f6, + 0x0, 0x197f19fa, 0x19fe, 0x0, 0xe450e43, 0x90e4b, 0xe470e49, + 0x1a82, 0x1a841b22, 0x1a8b1a89, 0x1b241a90, 0x1b26, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x26b6, 0x26b9, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x26bc0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c226bf, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26c826c5, 0x26cf26cb, 0x26d726d3, + 0x26db, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x26df0000, 0x26e226ea, 0x26e626ed, 0x26f1, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, + 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, + 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, + 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, + 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, + 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, + 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, + 0x602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, + 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, + 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, + 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, 0x9370692, + 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, + 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, + 0x568, 0x5e605e3, 0x0, 0x687, 0x6070000, 0x71a, 0x77e0000, + 0x6af07a4, 0x83b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, + 0x9370692, 0xa2e09a0, 0xad90000, 0xb0d0000, 0x73e0ae0, 0xa280b79, + 0xb3b05d3, 0xcd30000, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, + 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, + 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, + 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, + 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, + 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e60000, 0x67e0629, + 0x687, 0x6070000, 0x734071a, 0x77e0723, 0x6af07a4, 0x83b, + 0x88d085e, 0x6b2056b, 0x6820770, 0x95a, 0x9370692, 0xa2e09a0, + 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, + 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, + 0x5e60000, 0x67e0629, 0x687, 0x60706cf, 0x734071a, 0x723, 0x7a4, + 0x0, 0x88d085e, 0x6b2056b, 0x6820770, 0x95a, 0x9370692, 0xa2e09a0, + 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, + 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, + 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, + 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, 0x6820770, 0x60a095a, + 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, + 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, + 0x6110695, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, + 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, + 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0x60a095a, + 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, 0x73e0ae0, 0xa280b79, + 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, 0x86a0a24, 0xb410de1, + 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, 0x60706cf, + 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, 0x6b2056b, + 0x6820770, 0x60a095a, 0x9370692, 0xa2e09a0, 0xad90a7d, 0xb0d0602, + 0x73e0ae0, 0xa280b79, 0xb3b05d3, 0xcd30787, 0xa1105d8, 0xba30840, + 0x86a0a24, 0xb410de1, 0x6110695, 0x5d50568, 0x5e605e3, 0x67e0629, + 0x6ac0687, 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0xb410de1, + 0x6110695, 0xe800e6f, 0x0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, + 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, + 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, + 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, + 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, + 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, + 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, + 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, + 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, + 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, + 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, + 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0xf380ee3, 0xf3c0f3a, + 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, 0x10060fe8, + 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, 0x1aa3077c, + 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, 0x11ae1170, + 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, 0x11fc11fa, + 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, 0x123c11ae, + 0x11ee11f0, 0xf380ee3, 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, + 0xfe20fe0, 0xfe60fe4, 0x10060fe8, 0xfad1008, 0x100f100d, + 0x10311011, 0x10351033, 0x1aa3077c, 0x10ea1086, 0x10ee10ec, + 0x110e10f0, 0x116e1110, 0x11ae1170, 0x11b211b0, 0x11ce11cc, + 0x11ee11d0, 0x11f811f0, 0x11fc11fa, 0x123c11fe, 0x1240123e, + 0x1a9e1242, 0x116e10f0, 0x123c11ae, 0x11ee11f0, 0xf380ee3, + 0xf3c0f3a, 0xf5c0f3e, 0xfad0f5e, 0xfde0faf, 0xfe20fe0, 0xfe60fe4, + 0x10060fe8, 0xfad1008, 0x100f100d, 0x10311011, 0x10351033, + 0x1aa3077c, 0x10ea1086, 0x10ee10ec, 0x110e10f0, 0x116e1110, + 0x11ae1170, 0x11b211b0, 0x11ce11cc, 0x11ee11d0, 0x11f811f0, + 0x11fc11fa, 0x123c11fe, 0x1240123e, 0x1a9e1242, 0x116e10f0, + 0x123c11ae, 0x11ee11f0, 0x12a212a0, 0x0, 0x3140305, 0x30f0343, + 0x3740365, 0x3920383, 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, + 0x3920383, 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, + 0x3b003a1, 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, + 0x3140305, 0x30f0343, 0x3740365, 0x3920383, 0x3b003a1, 0x13f213d7, + 0x14e013f5, 0x17880000, 0x13f81409, 0x13fc15c3, 0x14ec1677, + 0x140f140c, 0x15e214f8, 0x1560163d, 0x13dc1659, 0x141c1532, + 0x13ff1470, 0x15a014e2, 0x160515dd, 0x184a1814, 0x1816183a, + 0x13f20000, 0x13f5, 0x13e1, 0x13f80000, 0x13fc0000, 0x14ec1677, + 0x140f140c, 0x15e214f8, 0x1560163d, 0x1659, 0x141c1532, 0x13ff1470, + 0x15a00000, 0x16050000, 0x0, 0x0, 0x0, 0x13f5, 0x0, 0x13f80000, + 0x13fc0000, 0x14ec0000, 0x140f0000, 0x15e214f8, 0x15600000, 0x1659, + 0x1532, 0x13ff0000, 0x15a00000, 0x16050000, 0x184a0000, 0x18160000, + 0x13f20000, 0x13f5, 0x13e1, 0x13f80000, 0x13fc15c3, 0x1677, + 0x140f140c, 0x15e214f8, 0x1560163d, 0x1659, 0x141c1532, 0x13ff1470, + 0x15a00000, 0x160515dd, 0x1814, 0x183a, 0x13f213d7, 0x14e013f5, + 0x178813e1, 0x13f81409, 0x13fc15c3, 0x14ec0000, 0x140f140c, + 0x15e214f8, 0x1560163d, 0x13dc1659, 0x141c1532, 0x13ff1470, + 0x15a014e2, 0x160515dd, 0x0, 0x0, 0x13f20000, 0x14e013f5, + 0x17880000, 0x13f81409, 0x13fc15c3, 0x14ec0000, 0x140f140c, + 0x15e214f8, 0x1560163d, 0x13dc1659, 0x141c1532, 0x13ff1470, + 0x15a014e2, 0x160515dd, 0x0, 0x0, 0x307030a, 0x3f10316, 0x4ab0468, + 0x4fa04de, 0x520050b, 0x531, 0x0, 0x0, 0x10200fe, 0x10a0106, + 0x112010e, 0x11a0116, 0x122011e, 0x12a0126, 0x132012e, 0x13a0136, + 0x142013e, 0x14a0146, 0x152014e, 0x15a0156, 0x162015e, 0x5e31b4d, + 0x5e5082c, 0x933, 0x5d50568, 0x5e605e3, 0x67e0629, 0x6ac0687, + 0x60706cf, 0x734071a, 0x77e0723, 0x6af07a4, 0x82c083b, 0x88d085e, + 0x6b2056b, 0x6820770, 0x60a095a, 0x76c06b1, 0x8660860, 0x9300827, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x761075e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x606, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1c9e1bc3, 0x1cad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x20b02197, 0x1cf71ff3, 0x20811f17, 0x208c2532, 0x21fe1f1d, + 0x21e722f2, 0x21451f9d, 0x21eb1f69, 0x24261f93, 0x2560235c, + 0x200f2073, 0x219d22cc, 0x1ee921b3, 0x25a01eef, 0x1efd20fa, + 0x21ad2001, 0x21992574, 0x23f023d2, 0x22bc2005, 0x329221b, + 0x1f9f2366, 0x2035, 0x0, 0x0, 0x1b511b69, 0x1b5d1b55, 0x1b611b6d, + 0x1b591b71, 0x1b65, 0x0, 0x0, 0x0, 0x1ffd2147, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1f031f07, 0x26f51f0b, 0x1f351f2d, 0x1f3b1f37, + 0x1f411f3f, 0x1f431f47, 0x26fd1e63, 0x1f531f51, 0x1f631f55, + 0x1e6526f7, 0x1f691f59, 0x1f7126fb, 0x1f251f75, 0x1f7b1f79, + 0x1f8927b9, 0x1e691f8d, 0x1f9b1f99, 0x1fa11f9f, 0x1fad1e6b, + 0x1fb51faf, 0x1fbd1fbb, 0x1fc31fbf, 0x1fd51fd3, 0x1fe11fd9, + 0x1fe71fe5, 0x1fe71fe7, 0x22e42703, 0x1ff51ff1, 0x1ffb2705, + 0x20031fff, 0x200d2017, 0x20152013, 0x201d2019, 0x2023201f, + 0x20292027, 0x202d2029, 0x20332031, 0x204b2039, 0x204d203d, + 0x2043203f, 0x20711f8f, 0x20572055, 0x20532059, 0x205b205d, + 0x27072067, 0x20772075, 0x2081207b, 0x20962085, 0x270b2709, + 0x209e209c, 0x209a20a0, 0x1e6d20a4, 0x20a81e6f, 0x20ac20ac, + 0x20ba270d, 0x20be20bc, 0x270f20c2, 0x20c820c6, 0x20cc2137, + 0x20d21e71, 0x20e020da, 0x271320de, 0x271520e4, 0x20e820ea, + 0x20f420ec, 0x1e7320f6, 0x210220fe, 0x21062104, 0x27171e75, + 0x21171e77, 0x211b2119, 0x27cd211f, 0x271b212b, 0x2486271b, + 0x21332133, 0x27291e79, 0x213b277d, 0x1e7b213f, 0x21512149, + 0x21572153, 0x1e7f215f, 0x21611e7d, 0x2163271d, 0x216f216d, + 0x216f2171, 0x21792177, 0x217d2181, 0x2183217f, 0x21872185, + 0x218f210b, 0x219f219b, 0x21b121a7, 0x21af2723, 0x21b521a9, + 0x21c321b9, 0x21c72725, 0x21bd21c1, 0x21cb1e81, 0x21d321cf, + 0x1e8321cd, 0x21df21db, 0x21f52727, 0x22032215, 0x22091e89, + 0x1e851e87, 0x1f6d1f6b, 0x220b2217, 0x1ebb2470, 0x221f221d, + 0x222b2221, 0x27312227, 0x22351e8b, 0x2242222f, 0x27352246, + 0x22392248, 0x1e8d224c, 0x2250224e, 0x22582252, 0x225c2737, + 0x22621e8f, 0x22642739, 0x226a1e91, 0x22762270, 0x273b2278, + 0x273d2711, 0x273f2288, 0x2292228e, 0x2298228a, 0x22a822a0, + 0x22a422a2, 0x22ac22aa, 0x229e2741, 0x22ba22b8, 0x22c41e93, + 0x274322c2, 0x22d222b4, 0x27472745, 0x22de22d4, 0x22da22dc, + 0x22e01e95, 0x22e622e8, 0x26f922ec, 0x274922f4, 0x274d22fa, + 0x230a2304, 0x274f2314, 0x2320231e, 0x27532751, 0x2336232e, + 0x23381e97, 0x1e991e99, 0x23462344, 0x234c234a, 0x1e9b2352, + 0x2755235e, 0x2757236c, 0x27192372, 0x2759237a, 0x275d275b, + 0x1e9f1e9d, 0x27612396, 0x2763275f, 0x239a2765, 0x239c239c, + 0x1ea323a0, 0x1ea523a2, 0x27691ea7, 0x23b023ac, 0x1ea923b6, + 0x23c8276b, 0x276f276d, 0x23e423d8, 0x23e81eab, 0x23ec23ea, + 0x27732771, 0x23f82773, 0x27751ead, 0x24042402, 0x27771eaf, + 0x1eb12412, 0x2416241a, 0x277b241e, 0x1eb3242a, 0x24342430, + 0x1eb5243c, 0x2781277f, 0x27831eb7, 0x27852448, 0x2454244e, + 0x27872458, 0x24622789, 0x2466278b, 0x1eb9272b, 0x24742472, + 0x24761ebd, 0x278d20a6, 0x272d278f, 0x2486272f, 0x25942488, + 0x249e1ebf, 0x24a0249c, 0x24a21fa9, 0x24a624a4, 0x279124aa, + 0x24ac24a8, 0x24b824b6, 0x24ba24ae, 0x24ce24c4, 0x24be24b4, + 0x24c224c0, 0x27972793, 0x1ec12795, 0x24d424d2, 0x279f24d8, + 0x279924da, 0x1ec51ec3, 0x279d279b, 0x24ea1ec7, 0x24ee24ec, + 0x24f624f0, 0x24fa24f4, 0x250024f8, 0x24fe24fc, 0x1ec92502, + 0x25082506, 0x25101ecb, 0x27a12512, 0x251a2518, 0x25201ecd, + 0x27a31e67, 0x1ecf27a5, 0x25361ed1, 0x25502542, 0x27a72558, + 0x25642562, 0x25762570, 0x26ff27ab, 0x257a257c, 0x27012580, + 0x258c2586, 0x27af27ad, 0x25b225ac, 0x27b125b6, 0x25cc25b8, + 0x25d425d2, 0x25da25d0, 0x27b325dc, 0x1ed325e2, 0x27b525e6, + 0x26021ed5, 0x260e20ee, 0x27bb27b7, 0x1ed91ed7, 0x27bd2622, + 0x27bf1edb, 0x262e262e, 0x27c12632, 0x1edd263e, 0x264c2646, + 0x26542650, 0x27c31edf, 0x266c265e, 0x1ee12672, 0x26741ee3, + 0x1ee527c5, 0x27c927c7, 0x268627cb, 0x26901ee7, 0x26962694, + 0x269e269a, 0x27cf26a2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //12288 bytes + enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, + 0x240], [0x100, 0x400, 0x1380], [0x2020100, 0x3020202, 0x2020204, + 0x2050202, 0x2020202, 0x6020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x10000, 0x30002, 0x50004, 0x6, 0x0, 0x70000, + 0x90008, 0xb000a, 0xc0000, 0x0, 0x0, 0xd, 0xe0000, 0x0, 0x0, 0x0, + 0x0, 0x10000f, 0x110000, 0x130012, 0x0, 0x140000, 0x160015, + 0x170000, 0x180000, 0x190000, 0x1a0000, 0x0, 0x0, 0x1b0000, 0x1c, + 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f001e, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x210020, 0x230022, 0x250024, 0x270026, 0x28, 0x0, + 0x29, 0x2b002a, 0x2d002c, 0x2f002e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x310000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x320000, 0x340033, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x360035, 0x380037, 0x3a0039, 0x3c003b, 0x3e003d, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x410000, 0x430042, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x450044, 0x470046, 0x490048, 0x4b004a, 0x4c, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xf000c, 0x250012, 0x4f0045, 0x850000, 0xa1009e, 0xcb00a4, + 0x121011e, 0x1330124, 0x1880000, 0x1a0019d, 0x1b601a3, 0x1da, + 0x26d0000, 0x2730270, 0x2f30287, 0x0, 0x322031f, 0x3380325, + 0x3620358, 0x3980000, 0x3b403b1, 0x3de03b7, 0x4370434, 0x446043a, + 0x49c0000, 0x4b404b1, 0x4ca04b7, 0x4ee, 0x5840000, 0x58a0587, + 0x60d059e, 0x61c0000, 0x33b0028, 0x33e002b, 0x380006d, 0x38c0079, + 0x38f007c, 0x392007f, 0x3950082, 0x3a2008f, 0x0, 0x3cd00ba, + 0x3d800c5, 0x3db00c8, 0x3fb00e8, 0x3e400d1, 0x40a00f7, 0x41000fd, + 0x4130100, 0x4190106, 0x41c0109, 0x0, 0x43d0127, 0x440012a, + 0x443012d, 0x45c0149, 0x130, 0x0, 0x462014f, 0x471015d, 0x1630000, + 0x1700477, 0x1660484, 0x47a, 0x0, 0x1850000, 0x1940499, 0x18e04a8, + 0x4a2, 0x0, 0x4d901c5, 0x4e401d0, 0x4f801e4, 0x0, 0x52f021b, + 0x5450231, 0x5350221, 0x54b0237, 0x552023e, 0x5690255, 0x5580244, + 0x57b0264, 0x572025b, 0x0, 0x58d0276, 0x594027d, 0x59b0284, + 0x5b4029d, 0x5b702a0, 0x5e002c9, 0x5f502de, 0x61002f6, 0x30b0302, + 0x3110628, 0x314062e, 0x631, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50401f0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x2ac0000, 0x5c3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x560000, 0x13d0369, 0x1e70450, + 0x2a304fb, 0x29205ba, 0x28e05a9, 0x29605a5, 0x28a05ad, 0x5a1, + 0x35b0048, 0x3540041, 0x653064a, 0x0, 0x4160103, 0x46b0157, + 0x522020e, 0x5250211, 0x65f065c, 0x465, 0x0, 0x40700f4, 0x0, + 0x4960182, 0x3650052, 0x6500647, 0x656064d, 0x36c0059, 0x36f005c, + 0x3e700d4, 0x3ea00d7, 0x4530140, 0x4560143, 0x4fe01ea, 0x50101ed, + 0x5380224, 0x53b0227, 0x5bd02a6, 0x5c002a9, 0x5660252, 0x5780261, + 0x0, 0x4250112, 0x0, 0x0, 0x0, 0x351003e, 0x3f400e1, 0x4f101dd, + 0x4d101bd, 0x4e701d3, 0x4ea01d6, 0x61602fc, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x10000d, 0x66b0000, 0x137, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x662, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, + 0x63d0000, 0x6450670, 0x6df06c3, 0x72c, 0x759, 0x7980778, 0x8d1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x7810735, 0x84707e9, 0x8c10867, 0x92f, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92808ca, 0x91f08fd, 0x95f, + 0x0, 0x9b40000, 0x9b7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x9cc09c6, 0x9c30000, 0x0, 0x9ba0000, 0x0, 0x0, 0x9d809e4, 0x9ed, + 0x0, 0x0, 0x0, 0x0, 0x9de0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa200000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0e0a08, 0xa050000, 0x0, + 0xa410000, 0x0, 0x0, 0xa1a0a26, 0xa2f, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa470a44, 0x0, 0x0, 0x0, 0x0, + 0x9cf0000, 0xa11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9ff09bd, + 0xa0209c0, 0x0, 0xa0b09c9, 0x0, 0xa4d0a4a, 0xa1409d2, 0xa1709d5, + 0x0, 0xa1d09db, 0xa2309e1, 0xa2909e7, 0x0, 0xa530a50, 0xa3e09fc, + 0xa2c09ea, 0xa3209f0, 0xa3509f3, 0xa3809f6, 0x0, 0xa3b09f9, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac10abe, 0xac40ac7, 0xaca, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xad3, + 0xacd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xad00000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xae80000, 0x0, 0x0, 0x0, 0xaf10000, 0x0, 0xaf4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xad90ad6, 0xadf0adc, 0xae50ae2, 0xaee0aeb, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xb000000, 0xb03, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xafa0af7, 0xafd0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb120000, 0x0, 0xb15, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xb060000, 0xb0c0b09, 0x0, 0xb0f, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xb21, 0xb1e0000, 0xb24, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xb1b0b18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xb27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb300b2a, 0xb2d, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb33, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xb36, 0x0, 0x0, 0xb400000, 0xb43, 0xb3c0b39, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xb4c0b46, 0xb49, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xb4f, 0xb550b52, 0xb59, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xb5f0000, 0x0, 0x0, 0x0, 0x0, 0xb620000, 0x0, 0x0, 0xb65, 0x0, + 0xb680000, 0x0, 0x0, 0xb6b, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb5c0000, + 0x0, 0x0, 0x0, 0x0, 0xb6e0000, 0xb710000, 0xb89, 0xb8c, 0x0, 0x0, + 0x0, 0xb740000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb7a0000, + 0x0, 0x0, 0x0, 0x0, 0xb7d0000, 0x0, 0x0, 0xb80, 0x0, 0xb830000, + 0x0, 0x0, 0xb86, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb770000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb92, 0xb95, 0xb98, + 0xb9b, 0xb9e, 0x0, 0xba1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xba40000, + 0xba70000, 0x0, 0xbad0baa, 0xbb00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37d006a, + 0x3830070, 0x3860073, 0x3890076, 0x39b0088, 0x39f008c, 0x3a50092, + 0x3ae009b, 0x3a80095, 0x3ab0098, 0x3d000bd, 0x3d400c1, 0x3fe00eb, + 0x40100ee, 0x3f700e4, 0x40400f1, 0x40d00fa, 0x41f010c, 0x4280115, + 0x422010f, 0x42b0118, 0x42e011b, 0x45f014c, 0x4490136, 0x4680154, + 0x46e015a, 0x4740160, 0x47d0169, 0x480016c, 0x48a0176, 0x4870173, + 0x48d0179, 0x490017c, 0x493017f, 0x49f018b, 0x4a50191, 0x4ae019a, + 0x4ab0197, 0x4cd01b9, 0x4d501c1, 0x4dc01c8, 0x4e001cc, 0x5290215, + 0x52c0218, 0x532021e, 0x53e022a, 0x541022d, 0x5480234, 0x5550241, + 0x55f024b, 0x54e023a, 0x55b0247, 0x562024e, 0x56c0258, 0x575025e, + 0x581026a, 0x57e0267, 0x5dd02c6, 0x5e602cf, 0x5e302cc, 0x5900279, + 0x5970280, 0x5e902d2, 0x5ec02d5, 0x5ef02d8, 0x5f202db, 0x5fb02e4, + 0x5f802e1, 0x60102e7, 0x60402ea, 0x60702ed, 0x61902ff, 0x62b030e, + 0x6340317, 0x637031a, 0x56f0431, 0x62205fe, 0x6590000, 0x0, 0x0, + 0x372005f, 0x35f004c, 0x32c0019, 0x3280015, 0x3340021, 0x330001d, + 0x3750062, 0x3450032, 0x341002e, 0x34d003a, 0x3490036, 0x3790066, + 0x3ed00da, 0x3e100ce, 0x3ca00b7, 0x3be00ab, 0x3ba00a7, 0x3c600b3, + 0x3c200af, 0x3f000dd, 0x44d013a, 0x4590146, 0x51b0207, 0x4f501e1, + 0x4be01aa, 0x4ba01a6, 0x4c601b2, 0x4c201ae, 0x51e020a, 0x50b01f7, + 0x50701f3, 0x51301ff, 0x50f01fb, 0x5170203, 0x5da02c3, 0x5b1029a, + 0x5ca02b3, 0x5c602af, 0x5d202bb, 0x5ce02b7, 0x5d602bf, 0x60a02f0, + 0x6250308, 0x61f0305, 0x61302f9, 0x0, 0x0, 0x0, 0x81807f6, + 0x81b07f9, 0x8240802, 0x82d080b, 0x69b0679, 0x69e067c, 0x6a70685, + 0x6b0068e, 0x855084a, 0x858084d, 0x85c0851, 0x0, 0x6d106c6, + 0x6d406c9, 0x6d806cd, 0x0, 0x890086e, 0x8930871, 0x89c087a, + 0x8a50883, 0x70406e2, 0x70706e5, 0x71006ee, 0x71906f7, 0x8e808d9, + 0x8eb08dc, 0x8ef08e0, 0x8f308e4, 0x7470738, 0x74a073b, 0x74e073f, + 0x7520743, 0x90b0900, 0x90e0903, 0x9120907, 0x0, 0x767075c, + 0x76a075f, 0x76e0763, 0x0, 0x9460937, 0x949093a, 0x94d093e, + 0x9510942, 0x7840000, 0x7870000, 0x78b0000, 0x78f0000, 0x9880966, + 0x98b0969, 0x9940972, 0x99d097b, 0x7bd079b, 0x7c0079e, 0x7c907a7, + 0x7d207b0, 0x7e907e2, 0x8470844, 0x8670860, 0x8c108be, 0x8fd08fa, + 0x91f091c, 0x95f0958, 0x0, 0x8360814, 0x81f07fd, 0x8280806, + 0x831080f, 0x6b90697, 0x6a20680, 0x6ab0689, 0x6b40692, 0x8ae088c, + 0x8970875, 0x8a0087e, 0x8a90887, 0x7220700, 0x70b06e9, 0x71406f2, + 0x71d06fb, 0x9a60984, 0x98f096d, 0x9980976, 0x9a1097f, 0x7db07b9, + 0x7c407a2, 0x7cd07ab, 0x7d607b4, 0x7f007f3, 0x84107e5, 0x7ec, + 0x83d083a, 0x6730676, 0x670066d, 0x6bd, 0x8bc, 0x6400000, + 0x8b90863, 0x86a, 0x8b508b2, 0x6c306c0, 0x6df06dc, 0xbb30726, + 0xbb90bb6, 0x8c408c7, 0x8d108cd, 0x0, 0x8d508f7, 0x72f0732, + 0x72c0729, 0xbbc0000, 0xbc20bbf, 0x9220925, 0x92f092b, 0x9190916, + 0x9330955, 0x77b077e, 0x7780775, 0x63a0772, 0x31d063d, 0x0, + 0x9b1095b, 0x962, 0x9ad09aa, 0x7590756, 0x7980795, 0x64307df, 0x0, + 0xbc70bc5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x793, 0x0, 0x4f0152, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xbcc0bc9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbcf, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xbd20000, 0xbd50bd8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xbdb, 0x0, 0xbde0000, 0x0, 0xbe1, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe4, 0xbe7, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xbea0000, 0x0, 0xbed, 0xbf00000, 0xbf30000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xbf9, 0x0, 0x0, 0x0, 0x0, + 0xbf60000, 0x90003, 0xbff0bfc, 0x0, 0xc050c02, 0x0, 0xc0b0c08, 0x0, + 0x0, 0x0, 0xc110c0e, 0x0, 0xc1d0c1a, 0x0, 0xc230c20, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xc2f0c2c, 0xc350c32, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xc170c14, 0xc290c26, 0x0, 0x0, 0x0, 0xc3b0c38, + 0xc410c3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xc470000, 0xc49, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc4e, 0xc51, 0xc54, 0xc57, 0xc5a, 0xc5d, + 0xc60, 0xc63, 0xc66, 0xc69, 0xc6c, 0xc6f, 0xc720000, 0xc750000, + 0xc780000, 0x0, 0x0, 0x0, 0xc7e0c7b, 0xc810000, 0xc84, 0xc8a0c87, + 0xc8d0000, 0xc90, 0xc960c93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc4b, 0x0, 0x0, 0x0, 0x0, 0xc99, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc9f, 0xca2, 0xca5, 0xca8, 0xcab, 0xcae, + 0xcb1, 0xcb4, 0xcb7, 0xcba, 0xcbd, 0xcc0, 0xcc30000, 0xcc60000, + 0xcc90000, 0x0, 0x0, 0x0, 0xccf0ccc, 0xcd20000, 0xcd5, 0xcdb0cd8, + 0xcde0000, 0xce1, 0xce70ce4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc9c, 0xcea0000, 0xcf00ced, 0xcf3, 0x0, 0xcf6, + 0xfb71241, 0x124b125d, 0xd831043, 0x13270e29, 0xe991327, 0xe4f1293, + 0xf550e97, 0x116710cd, 0x11fd11e3, 0x12791215, 0x10190feb, + 0x109d1069, 0x128911c7, 0xd8d12f3, 0xff50e1d, 0x11e11079, + 0xedb1309, 0x11d91051, 0xf65121d, 0x12031189, 0xfbd0eff, + 0x108d1025, 0xd9d127d, 0xe050dd9, 0xff10f95, 0x10d31077, + 0x11dd1171, 0x125911e7, 0x12fb12cf, 0x10e91307, 0x114d1107, + 0x12a111b9, 0x122f130b, 0xf0b0e87, 0x117d112f, 0x10ed1083, + 0x12cb1249, 0xecb0e85, 0x102f0fed, 0x11471047, 0x12b11159, + 0x117f0e03, 0xddd0ddf, 0x114f1115, 0x12b511c5, 0xf67123d, + 0x12350feb, 0xebb0d87, 0x10950f27, 0xe1110c1, 0xda510f1, 0xd7f0f1b, + 0xf9d1011, 0xe231145, 0x10d70e7d, 0x122711c9, 0x126d1005, + 0xf6f100d, 0xf7b11a5, 0xd9110bf, 0xddb0dc3, 0x113d0fdb, 0x122d1195, + 0xe091291, 0xe9f0e37, 0xfa10f07, 0x10f31053, 0x12f712ab, + 0x1313130d, 0xfb50df9, 0x12690ffd, 0xf490ef3, 0xf910f57, + 0x106d104b, 0x111110af, 0x11791153, 0x11cd1261, 0x12a31271, + 0xdfb0de9, 0x10670e41, 0x1227120b, 0xf230efd, 0x10030f77, + 0x1091112d, 0xe670d97, 0xee50ebb, 0x109b0f29, 0x116b10a9, + 0x12951175, 0x12d112c9, 0xd9f12dd, 0x128d110f, 0xf3512c1, + 0xdb10d8f, 0xec70ebd, 0xfeb0f9f, 0x10cb1073, 0x127711d3, 0xfad1323, + 0xdf712af, 0xfd10fcb, 0x103b1021, 0x10bd10a1, 0x114310e7, + 0xdc512e3, 0x12b70f5d, 0xed70da9, 0x12631031, 0xf390f17, + 0x10950fd5, 0xdeb12bb, 0xecf0e31, 0xfc30fa7, 0x10150fe1, + 0x10c3109f, 0x120d1163, 0x128f1213, 0xe1312c5, 0xe33103d, + 0x10b11075, 0x12bd11db, 0x130f12ff, 0x102d0fcf, 0x1121118b, + 0x11331125, 0x1063108b, 0xd93123b, 0xded11ad, 0xef50de7, + 0x11390f69, 0x101b0eb5, 0x12670fb3, 0x12b31205, 0xf031221, + 0xe590db5, 0x0, 0xe7b, 0xfab, 0xde10000, 0x10cf108f, 0x110310f5, + 0x110d1105, 0x113512d3, 0x116d, 0x11df, 0x1233, 0x12730000, 0x1283, + 0x0, 0x12e912e7, 0x130512eb, 0x12bf127f, 0xdb30da1, 0xe010db9, + 0xe170e07, 0xe5f0e53, 0xe790e63, 0xecd0e7f, 0xf2f0ed1, 0xf470f43, + 0xf970f53, 0xfaf0fa3, 0x10270fdd, 0x10491035, 0x107d106f, + 0x10eb10a3, 0x10fb10f7, 0x10fd10f9, 0x110110ff, 0x110b1109, + 0x111d1117, 0x11531127, 0x115b1157, 0x11731161, 0x1197118d, + 0x11cb1197, 0x12231219, 0x12391237, 0x124f124d, 0x1273126f, + 0x12d912c7, 0xf2b12e1, 0x119313be, 0x0, 0xdd70d81, 0xd9b0dc1, + 0xdc90db7, 0xe0b0dff, 0xe490e53, 0xe5d0e51, 0xe830e7b, 0xe9b0e95, + 0xeb10ea9, 0xf050f01, 0xf1d0f13, 0xf3f0f33, 0xf470f37, 0xf530f41, + 0xf7f0f5f, 0xf890f85, 0xfab0f99, 0xfbf0fbd, 0xfff0fc7, 0x10211005, + 0x10411045, 0x10571049, 0x10e3106f, 0x1089107f, 0x10ab108f, + 0x10b910b5, 0x10c910c7, 0x10d110cf, 0x10df10d5, 0x10ef10dd, + 0x1127111f, 0x11491131, 0x115f1153, 0x11af1173, 0x11f911c3, + 0x121f121b, 0x12291223, 0x122b1233, 0x12351237, 0x12391231, + 0x124f123f, 0x12751265, 0x1299128b, 0x12c712b9, 0x12d512d3, + 0x12db12d9, 0x12f912e1, 0x13941327, 0x13a61392, 0xd370d23, + 0x13da0d39, 0x141c13ec, 0x13251321, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xa7a0000, 0xabb0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xab50ab2, + 0xaae0aaa, 0xa590a56, 0xa5f0a5c, 0xa680a65, 0xa710a6b, 0xa74, + 0xa7d0a77, 0xa830a80, 0xa89, 0xa8c, 0xa920a8f, 0xa950000, 0xa98, + 0xaa10a9e, 0xaa70aa4, 0xa6e0ab8, 0xa860a62, 0xa9b, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1329, 0x132c, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x132f0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x13351332, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x133b1338, 0x1342133e, 0x134a1346, 0x134e, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x13520000, 0x1355135d, 0x13591360, 0x1364, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xd850d89, 0x13680d8b, 0xda10d99, 0xda70da3, 0xdad0dab, + 0xdaf0db3, 0x13700cf9, 0xdbb0db9, 0xdc70dbd, 0xcfb136a, 0xdcb0dbf, + 0xdd1136e, 0xd950dd3, 0xdd70dd5, 0xde3142c, 0xcff0de5, 0xdf10def, + 0xdf50df3, 0xdff0d01, 0xe070e01, 0xe0d0e0b, 0xe110e0f, 0xe170e15, + 0xe1b0e19, 0xe210e1f, 0xe210e21, 0x105d1376, 0xe270e25, 0xe2b1378, + 0xe2f0e2d, 0xe350e3d, 0xe3b0e39, 0xe430e3f, 0xe470e45, 0xe4d0e4b, + 0xe510e4d, 0xe570e55, 0xe690e5b, 0xe6b0e5f, 0xe650e61, 0xe890de7, + 0xe710e6f, 0xe6d0e73, 0xe750e77, 0x137a0e81, 0xe8d0e8b, 0xe910e8f, + 0xe9d0e93, 0x137e137c, 0xea50ea3, 0xea10ea7, 0xd030eab, 0xeaf0d05, + 0xeb30eb3, 0xeb71380, 0xebb0eb9, 0x13820ebf, 0xec30ec1, 0xec50f0f, + 0xec90d07, 0xed50ed1, 0x13860ed3, 0x13880ed9, 0xedd0edf, 0xee70ee1, + 0xd090ee9, 0xeed0eeb, 0xef10eef, 0x138a0d0b, 0xef70d0d, 0xefb0ef9, + 0x14400eff, 0x138e0f09, 0x118f138e, 0xf0d0f0d, 0x139c0d0f, + 0xf1113f0, 0xd110f15, 0xf1f0f19, 0xf250f21, 0xd150f2d, 0xf2f0d13, + 0xf311390, 0xf3d0f3b, 0xf3d0f3f, 0xf470f45, 0xf4b0f4f, 0xf510f4d, + 0xf550f53, 0xf5b0f59, 0xf630f61, 0xf730f6b, 0xf711396, 0xf750f6d, + 0xf830f79, 0xf871398, 0xf7d0f81, 0xf8b0d17, 0xf930f8f, 0xd190f8d, + 0xf9b0f97, 0xfa5139a, 0xfa90fb9, 0xfaf0d1f, 0xd1b0d1d, 0xdcf0dcd, + 0xfb10fbb, 0xd511181, 0xfbf0fbd, 0xfc90fc1, 0x13a40fc5, 0xfd30d21, + 0xfd90fcd, 0x13a80fdd, 0xfd70fdf, 0xd230fe3, 0xfe70fe5, 0xfef0fe9, + 0xff313aa, 0xff70d25, 0xff913ac, 0xffb0d27, 0x10051001, 0x13ae1007, + 0x13b01384, 0x13b21009, 0x1013100f, 0x1017100b, 0x1027101f, + 0x10231021, 0x102b1029, 0x101d13b4, 0x10391037, 0x10410d29, + 0x13b6103f, 0x104d1033, 0x13ba13b8, 0x1059104f, 0x10551057, + 0x105b0d2b, 0x105f1061, 0x136c1065, 0x13bc106b, 0x13c01071, + 0x107f107b, 0x13c21081, 0x10871085, 0x13c613c4, 0x10971093, + 0x10990d2d, 0xd2f0d2f, 0x10a710a5, 0x10ad10ab, 0xd3110b3, + 0x13c810b7, 0x13ca10bb, 0x138c10c1, 0x13cc10c5, 0x13d013ce, + 0xd350d33, 0x13d410d5, 0x13d613d2, 0x10d913d8, 0x10db10db, + 0xd3910df, 0xd3b10e1, 0x13dc0d3d, 0x10e910e5, 0xd3f10ef, + 0x10ff13de, 0x13e213e0, 0x1113110d, 0x11170d41, 0x111b1119, + 0x13e613e4, 0x112313e6, 0x13e80d43, 0x112b1129, 0x13ea0d45, + 0xd471137, 0x113b113f, 0x13ee1141, 0xd49114b, 0x11551151, + 0xd4b115d, 0x13f413f2, 0x13f60d4d, 0x13f81165, 0x116f1169, + 0x13fa1173, 0x117713fc, 0x117b13fe, 0xd4f139e, 0x11851183, + 0x11870d53, 0x14000ead, 0x13a01402, 0x118f13a2, 0x126b1191, + 0x119b0d55, 0x119d1199, 0x119f0dfd, 0x11a311a1, 0x140411a7, + 0x11a911a5, 0x11b511b3, 0x11b711ab, 0x11cb11c1, 0x11bb11b1, + 0x11bf11bd, 0x140a1406, 0xd571408, 0x11d111cf, 0x141211d5, + 0x140c11d7, 0xd5b0d59, 0x1410140e, 0x11e50d5d, 0x11e911e7, + 0x11ef11eb, 0x11f311ed, 0x11f911f1, 0x11f711f5, 0xd5f11fb, + 0x120111ff, 0x12070d61, 0x14141209, 0x1211120f, 0x12170d63, + 0x14160cfd, 0xd651418, 0x12250d67, 0x123f1231, 0x141a1243, + 0x12471245, 0x12531251, 0x1372141e, 0x12551257, 0x1374125b, + 0x1265125f, 0x14221420, 0x1281127b, 0x14241285, 0x12971287, + 0x129f129d, 0x12a5129b, 0x142612a7, 0xd6912a9, 0x142812ad, + 0x12c30d6b, 0x12cd0ee3, 0x142e142a, 0xd6f0d6d, 0x143012d7, + 0x14320d71, 0x12db12db, 0x143412df, 0xd7312e5, 0x12ef12ed, + 0x12f512f1, 0x14360d75, 0x12fd12f9, 0xd771301, 0x13030d79, + 0xd7b1438, 0x143c143a, 0x1311143e, 0x13150d7d, 0x13191317, + 0x131d131b, 0x1442131f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + @property + { + private alias _IDCA = immutable(dchar[]); + _IDCA decompCanonTable() + { + static _IDCA t = [ + 0x0, 0x3b, 0x0, 0x3c, 0x338, 0x0, 0x3d, 0x338, 0x0, 0x3e, + 0x338, 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, + 0x0, 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, + 0x302, 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, + 0x41, 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, + 0x41, 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, + 0x309, 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, + 0x308, 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, + 0x30a, 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, + 0x30f, 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, + 0x302, 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, + 0x328, 0x0, 0x42, 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, + 0x0, 0x43, 0x301, 0x0, 0x43, 0x302, 0x0, 0x43, 0x307, 0x0, + 0x43, 0x30c, 0x0, 0x43, 0x327, 0x0, 0x43, 0x327, 0x301, 0x0, + 0x44, 0x307, 0x0, 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, + 0x327, 0x0, 0x44, 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x300, + 0x0, 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, + 0x0, 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, + 0x302, 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, + 0x304, 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, + 0x45, 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, + 0x30c, 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, + 0x0, 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, + 0x306, 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, + 0x0, 0x46, 0x307, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, + 0x47, 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, + 0x30c, 0x0, 0x47, 0x327, 0x0, 0x48, 0x302, 0x0, 0x48, 0x307, + 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, 0x0, + 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x300, 0x0, 0x49, + 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, + 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, + 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, + 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, + 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x302, 0x0, 0x4b, 0x0, + 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, + 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x301, 0x0, 0x4c, 0x30c, + 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, 0x0, 0x4c, 0x327, + 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, 0x4d, 0x301, 0x0, + 0x4d, 0x307, 0x0, 0x4d, 0x323, 0x0, 0x4e, 0x300, 0x0, 0x4e, + 0x301, 0x0, 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, + 0x0, 0x4e, 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, + 0x4e, 0x331, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, + 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, + 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, + 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, + 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, + 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, + 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, + 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, + 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, + 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, + 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, + 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, + 0x328, 0x304, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, 0x52, + 0x301, 0x0, 0x52, 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, + 0x0, 0x52, 0x311, 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, + 0x0, 0x52, 0x327, 0x0, 0x52, 0x331, 0x0, 0x53, 0x301, 0x0, + 0x53, 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, + 0x53, 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, + 0x53, 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, + 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, 0x54, + 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, 0x331, + 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, 0x55, 0x302, 0x0, + 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, 0x55, 0x304, 0x0, + 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, 0x55, 0x308, 0x0, + 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, 0x0, 0x55, 0x308, + 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, 0x309, 0x0, 0x55, + 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, 0x0, 0x55, 0x30f, + 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, 0x55, 0x31b, 0x300, + 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, 0x303, 0x0, 0x55, + 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, 0x55, 0x323, 0x0, + 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, 0x32d, 0x0, 0x55, + 0x330, 0x0, 0x56, 0x303, 0x0, 0x56, 0x323, 0x0, 0x57, 0x300, + 0x0, 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, + 0x57, 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x307, 0x0, 0x58, + 0x308, 0x0, 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, + 0x0, 0x59, 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, + 0x59, 0x308, 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, + 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, + 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x60, 0x0, 0x61, + 0x300, 0x0, 0x61, 0x301, 0x0, 0x61, 0x302, 0x0, 0x61, 0x302, + 0x300, 0x0, 0x61, 0x302, 0x301, 0x0, 0x61, 0x302, 0x303, 0x0, + 0x61, 0x302, 0x309, 0x0, 0x61, 0x303, 0x0, 0x61, 0x304, 0x0, + 0x61, 0x306, 0x0, 0x61, 0x306, 0x300, 0x0, 0x61, 0x306, 0x301, + 0x0, 0x61, 0x306, 0x303, 0x0, 0x61, 0x306, 0x309, 0x0, 0x61, + 0x307, 0x0, 0x61, 0x307, 0x304, 0x0, 0x61, 0x308, 0x0, 0x61, + 0x308, 0x304, 0x0, 0x61, 0x309, 0x0, 0x61, 0x30a, 0x0, 0x61, + 0x30a, 0x301, 0x0, 0x61, 0x30c, 0x0, 0x61, 0x30f, 0x0, 0x61, + 0x311, 0x0, 0x61, 0x323, 0x0, 0x61, 0x323, 0x302, 0x0, 0x61, + 0x323, 0x306, 0x0, 0x61, 0x325, 0x0, 0x61, 0x328, 0x0, 0x62, + 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x301, + 0x0, 0x63, 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, + 0x63, 0x327, 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x307, 0x0, + 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, 0x64, 0x327, 0x0, 0x64, + 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, 0x300, 0x0, 0x65, 0x301, + 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, 0x65, 0x302, + 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, 0x309, 0x0, + 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, 0x300, 0x0, + 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, 0x307, 0x0, + 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, 0x0, 0x65, + 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, 0x65, 0x323, + 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, 0x0, 0x65, + 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, 0x66, 0x307, + 0x0, 0x67, 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, + 0x67, 0x306, 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, + 0x327, 0x0, 0x68, 0x302, 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, + 0x0, 0x68, 0x30c, 0x0, 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, + 0x68, 0x32e, 0x0, 0x68, 0x331, 0x0, 0x69, 0x300, 0x0, 0x69, + 0x301, 0x0, 0x69, 0x302, 0x0, 0x69, 0x303, 0x0, 0x69, 0x304, + 0x0, 0x69, 0x306, 0x0, 0x69, 0x308, 0x0, 0x69, 0x308, 0x301, + 0x0, 0x69, 0x309, 0x0, 0x69, 0x30c, 0x0, 0x69, 0x30f, 0x0, + 0x69, 0x311, 0x0, 0x69, 0x323, 0x0, 0x69, 0x328, 0x0, 0x69, + 0x330, 0x0, 0x6a, 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x301, + 0x0, 0x6b, 0x30c, 0x0, 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, + 0x6b, 0x331, 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, + 0x323, 0x0, 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, + 0x32d, 0x0, 0x6c, 0x331, 0x0, 0x6d, 0x301, 0x0, 0x6d, 0x307, + 0x0, 0x6d, 0x323, 0x0, 0x6e, 0x300, 0x0, 0x6e, 0x301, 0x0, + 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, 0x0, 0x6e, + 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, 0x6e, 0x331, + 0x0, 0x6f, 0x300, 0x0, 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, + 0x6f, 0x302, 0x300, 0x0, 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, + 0x303, 0x0, 0x6f, 0x302, 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, + 0x303, 0x301, 0x0, 0x6f, 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, + 0x0, 0x6f, 0x304, 0x0, 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, + 0x301, 0x0, 0x6f, 0x306, 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, + 0x304, 0x0, 0x6f, 0x308, 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, + 0x309, 0x0, 0x6f, 0x30b, 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, + 0x0, 0x6f, 0x311, 0x0, 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, + 0x0, 0x6f, 0x31b, 0x301, 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, + 0x31b, 0x309, 0x0, 0x6f, 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, + 0x6f, 0x323, 0x302, 0x0, 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, + 0x0, 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x72, 0x301, 0x0, + 0x72, 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, + 0x311, 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, + 0x327, 0x0, 0x72, 0x331, 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, + 0x307, 0x0, 0x73, 0x302, 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, + 0x0, 0x73, 0x30c, 0x307, 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, + 0x307, 0x0, 0x73, 0x326, 0x0, 0x73, 0x327, 0x0, 0x74, 0x307, + 0x0, 0x74, 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, + 0x74, 0x326, 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, + 0x331, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, + 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, + 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, + 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, + 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, + 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, + 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, + 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, + 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, + 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, + 0x75, 0x330, 0x0, 0x76, 0x303, 0x0, 0x76, 0x323, 0x0, 0x77, + 0x300, 0x0, 0x77, 0x301, 0x0, 0x77, 0x302, 0x0, 0x77, 0x307, + 0x0, 0x77, 0x308, 0x0, 0x77, 0x30a, 0x0, 0x77, 0x323, 0x0, + 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x300, 0x0, 0x79, + 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, 0x303, 0x0, 0x79, 0x304, + 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, 0x0, 0x79, 0x309, 0x0, + 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, 0x7a, 0x301, 0x0, 0x7a, + 0x302, 0x0, 0x7a, 0x307, 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, + 0x0, 0x7a, 0x331, 0x0, 0xa8, 0x300, 0x0, 0xa8, 0x301, 0x0, + 0xa8, 0x342, 0x0, 0xb4, 0x0, 0xb7, 0x0, 0xc6, 0x301, 0x0, 0xc6, + 0x304, 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, + 0x0, 0xf8, 0x301, 0x0, 0x17f, 0x307, 0x0, 0x1b7, 0x30c, 0x0, + 0x292, 0x30c, 0x0, 0x2b9, 0x0, 0x300, 0x0, 0x301, 0x0, 0x308, + 0x301, 0x0, 0x313, 0x0, 0x391, 0x300, 0x0, 0x391, 0x301, 0x0, + 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, 0x313, 0x0, 0x391, + 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, 0x345, 0x0, 0x391, + 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, 0x345, 0x0, 0x391, + 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, 0x345, 0x0, 0x391, + 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, 0x314, 0x300, 0x0, + 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, 0x314, 0x301, 0x0, + 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, 0x314, 0x342, 0x0, + 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, 0x314, 0x345, 0x0, + 0x391, 0x345, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, 0x395, + 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, 0x301, 0x0, + 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, 0x314, + 0x301, 0x0, 0x397, 0x300, 0x0, 0x397, 0x301, 0x0, 0x397, 0x313, + 0x0, 0x397, 0x313, 0x300, 0x0, 0x397, 0x313, 0x300, 0x345, 0x0, + 0x397, 0x313, 0x301, 0x0, 0x397, 0x313, 0x301, 0x345, 0x0, + 0x397, 0x313, 0x342, 0x0, 0x397, 0x313, 0x342, 0x345, 0x0, + 0x397, 0x313, 0x345, 0x0, 0x397, 0x314, 0x0, 0x397, 0x314, + 0x300, 0x0, 0x397, 0x314, 0x300, 0x345, 0x0, 0x397, 0x314, + 0x301, 0x0, 0x397, 0x314, 0x301, 0x345, 0x0, 0x397, 0x314, + 0x342, 0x0, 0x397, 0x314, 0x342, 0x345, 0x0, 0x397, 0x314, + 0x345, 0x0, 0x397, 0x345, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, + 0x0, 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, + 0x399, 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, + 0x301, 0x0, 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, + 0x314, 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, + 0x342, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, + 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, + 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, + 0x3a1, 0x314, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, + 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, + 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, + 0x314, 0x342, 0x0, 0x3a9, 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, + 0x0, 0x3a9, 0x313, 0x0, 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, + 0x300, 0x345, 0x0, 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, + 0x301, 0x345, 0x0, 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, + 0x342, 0x345, 0x0, 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, + 0x3a9, 0x314, 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, + 0x3a9, 0x314, 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, + 0x3a9, 0x314, 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, + 0x3a9, 0x314, 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x300, 0x0, + 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, + 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, + 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, + 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, + 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, + 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, + 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, + 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, + 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, + 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, + 0x345, 0x0, 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, + 0x0, 0x3b5, 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, + 0x314, 0x0, 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, + 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, 0x301, 0x0, + 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, 0x313, + 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, 0x313, + 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, 0x313, + 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, 0x313, + 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, 0x3b7, + 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, 0x3b7, + 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, 0x3b7, + 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, 0x3b7, + 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, 0x3b9, + 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, 0x3b9, 0x304, 0x0, + 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, 0x308, 0x300, 0x0, + 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, 0x342, 0x0, 0x3b9, + 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, 0x313, 0x301, 0x0, + 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, 0x3b9, 0x314, + 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, 0x314, 0x342, 0x0, + 0x3b9, 0x342, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, + 0x313, 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, + 0x3bf, 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, + 0x301, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, 0x3c5, 0x300, + 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, + 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, + 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, + 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, + 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, + 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, + 0x3c9, 0x300, 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, + 0x3c9, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, + 0x300, 0x0, 0x3c9, 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, + 0x301, 0x0, 0x3c9, 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, + 0x342, 0x0, 0x3c9, 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, + 0x345, 0x0, 0x3c9, 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, + 0x314, 0x300, 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, + 0x314, 0x301, 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, + 0x314, 0x342, 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, + 0x342, 0x0, 0x3c9, 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3d2, + 0x301, 0x0, 0x3d2, 0x308, 0x0, 0x406, 0x308, 0x0, 0x410, 0x306, + 0x0, 0x410, 0x308, 0x0, 0x413, 0x301, 0x0, 0x415, 0x300, 0x0, + 0x415, 0x306, 0x0, 0x415, 0x308, 0x0, 0x416, 0x306, 0x0, 0x416, + 0x308, 0x0, 0x417, 0x308, 0x0, 0x418, 0x300, 0x0, 0x418, 0x304, + 0x0, 0x418, 0x306, 0x0, 0x418, 0x308, 0x0, 0x41a, 0x301, 0x0, + 0x41e, 0x308, 0x0, 0x423, 0x304, 0x0, 0x423, 0x306, 0x0, 0x423, + 0x308, 0x0, 0x423, 0x30b, 0x0, 0x427, 0x308, 0x0, 0x42b, 0x308, + 0x0, 0x42d, 0x308, 0x0, 0x430, 0x306, 0x0, 0x430, 0x308, 0x0, + 0x433, 0x301, 0x0, 0x435, 0x300, 0x0, 0x435, 0x306, 0x0, 0x435, + 0x308, 0x0, 0x436, 0x306, 0x0, 0x436, 0x308, 0x0, 0x437, 0x308, + 0x0, 0x438, 0x300, 0x0, 0x438, 0x304, 0x0, 0x438, 0x306, 0x0, + 0x438, 0x308, 0x0, 0x43a, 0x301, 0x0, 0x43e, 0x308, 0x0, 0x443, + 0x304, 0x0, 0x443, 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, + 0x0, 0x447, 0x308, 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, + 0x456, 0x308, 0x0, 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, + 0x308, 0x0, 0x4d9, 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, + 0x0, 0x5d0, 0x5b7, 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, + 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x5bc, 0x0, 0x5d3, + 0x5bc, 0x0, 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, + 0x0, 0x5d6, 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, + 0x5d9, 0x5bc, 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, + 0x5bf, 0x0, 0x5dc, 0x5bc, 0x0, 0x5de, 0x5bc, 0x0, 0x5e0, 0x5bc, + 0x0, 0x5e1, 0x5bc, 0x0, 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, + 0x5e4, 0x5bf, 0x0, 0x5e6, 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, + 0x5bc, 0x0, 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, + 0x5bc, 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, + 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, + 0x0, 0x627, 0x655, 0x0, 0x648, 0x654, 0x0, 0x64a, 0x654, 0x0, + 0x6c1, 0x654, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, 0x0, 0x915, + 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, 0x91c, 0x93c, + 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, 0x93c, 0x0, + 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, 0x0, 0x933, + 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, 0x9af, 0x9bc, + 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, 0xa3c, 0x0, + 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, 0x0, 0xa32, + 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, 0xb22, 0xb3c, + 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, 0xb57, 0x0, + 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, 0x0, 0xbc7, + 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, 0xcc6, 0xcc2, + 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, 0xcc6, 0xcd6, + 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, 0xd3e, 0x0, + 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, 0xdca, 0x0, + 0xdd9, 0xddf, 0x0, 0xf40, 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, + 0xfb7, 0x0, 0xf51, 0xfb7, 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, + 0x0, 0xf71, 0xf72, 0x0, 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, + 0xf90, 0xfb5, 0x0, 0xf92, 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, + 0xfb7, 0x0, 0xfa6, 0xfb7, 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf80, + 0x0, 0xfb3, 0xf80, 0x0, 0x1025, 0x102e, 0x0, 0x1b05, 0x1b35, + 0x0, 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, + 0x0, 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, + 0x0, 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, + 0x0, 0x1b42, 0x1b35, 0x0, 0x1fbf, 0x300, 0x0, 0x1fbf, 0x301, + 0x0, 0x1fbf, 0x342, 0x0, 0x1ffe, 0x300, 0x0, 0x1ffe, 0x301, + 0x0, 0x1ffe, 0x342, 0x0, 0x2002, 0x0, 0x2003, 0x0, 0x2190, + 0x338, 0x0, 0x2192, 0x338, 0x0, 0x2194, 0x338, 0x0, 0x21d0, + 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, 0x0, 0x2203, + 0x338, 0x0, 0x2208, 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2223, + 0x338, 0x0, 0x2225, 0x338, 0x0, 0x223c, 0x338, 0x0, 0x2243, + 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, 0x338, 0x0, 0x224d, + 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, 0x338, 0x0, 0x2265, + 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, 0x338, 0x0, 0x2276, + 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, 0x338, 0x0, 0x227b, + 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, 0x338, 0x0, 0x2282, + 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, 0x338, 0x0, 0x2287, + 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, 0x338, 0x0, 0x22a2, + 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, 0x338, 0x0, 0x22ab, + 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, 0x338, 0x0, 0x22b4, + 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2add, 0x338, 0x0, 0x3008, + 0x0, 0x3009, 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, + 0x304d, 0x3099, 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, + 0x3053, 0x3099, 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, + 0x3059, 0x3099, 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, + 0x305f, 0x3099, 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, + 0x3066, 0x3099, 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, + 0x306f, 0x309a, 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, + 0x3075, 0x3099, 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, + 0x3078, 0x309a, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, 0x0, + 0x309d, 0x3099, 0x0, 0x30a6, 0x3099, 0x0, 0x30ab, 0x3099, 0x0, + 0x30ad, 0x3099, 0x0, 0x30af, 0x3099, 0x0, 0x30b1, 0x3099, 0x0, + 0x30b3, 0x3099, 0x0, 0x30b5, 0x3099, 0x0, 0x30b7, 0x3099, 0x0, + 0x30b9, 0x3099, 0x0, 0x30bb, 0x3099, 0x0, 0x30bd, 0x3099, 0x0, + 0x30bf, 0x3099, 0x0, 0x30c1, 0x3099, 0x0, 0x30c4, 0x3099, 0x0, + 0x30c6, 0x3099, 0x0, 0x30c8, 0x3099, 0x0, 0x30cf, 0x3099, 0x0, + 0x30cf, 0x309a, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x309a, 0x0, + 0x30d5, 0x3099, 0x0, 0x30d5, 0x309a, 0x0, 0x30d8, 0x3099, 0x0, + 0x30d8, 0x309a, 0x0, 0x30db, 0x3099, 0x0, 0x30db, 0x309a, 0x0, + 0x30ef, 0x3099, 0x0, 0x30f0, 0x3099, 0x0, 0x30f1, 0x3099, 0x0, + 0x30f2, 0x3099, 0x0, 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, + 0x0, 0x34bb, 0x0, 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, + 0x36fc, 0x0, 0x3781, 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, + 0x0, 0x38c7, 0x0, 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, + 0x3a2e, 0x0, 0x3a6c, 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, + 0x0, 0x3b49, 0x0, 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, + 0x3d33, 0x0, 0x3d96, 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, + 0x0, 0x3ffc, 0x0, 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, + 0x4046, 0x0, 0x4096, 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, + 0x0, 0x4227, 0x0, 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, + 0x4359, 0x0, 0x43d5, 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, + 0x0, 0x452b, 0x0, 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, + 0x45d7, 0x0, 0x45f9, 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, + 0x0, 0x4995, 0x0, 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, + 0x4ab2, 0x0, 0x4b33, 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, + 0x0, 0x4cf8, 0x0, 0x4d56, 0x0, 0x4e0d, 0x0, 0x4e26, 0x0, + 0x4e32, 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e41, + 0x0, 0x4e82, 0x0, 0x4e86, 0x0, 0x4eae, 0x0, 0x4ec0, 0x0, + 0x4ecc, 0x0, 0x4ee4, 0x0, 0x4f60, 0x0, 0x4f80, 0x0, 0x4f86, + 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, 0x0, 0x4fbf, 0x0, + 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, 0x5099, 0x0, 0x50cf, + 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x5140, 0x0, 0x5145, 0x0, + 0x514d, 0x0, 0x5154, 0x0, 0x5164, 0x0, 0x5167, 0x0, 0x5168, + 0x0, 0x5169, 0x0, 0x516d, 0x0, 0x5177, 0x0, 0x5180, 0x0, + 0x518d, 0x0, 0x5192, 0x0, 0x5195, 0x0, 0x5197, 0x0, 0x51a4, + 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, 0x0, + 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51f5, 0x0, 0x5203, + 0x0, 0x5207, 0x0, 0x5217, 0x0, 0x5229, 0x0, 0x523a, 0x0, + 0x523b, 0x0, 0x5246, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, + 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52c7, 0x0, + 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, 0x0, 0x52f5, + 0x0, 0x52fa, 0x0, 0x5305, 0x0, 0x5306, 0x0, 0x5317, 0x0, + 0x533f, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x535a, 0x0, 0x5373, + 0x0, 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x53c3, 0x0, + 0x53ca, 0x0, 0x53df, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53f1, + 0x0, 0x5406, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, + 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, 0x54a2, + 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x5553, 0x0, + 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, 0x5587, 0x0, 0x5599, + 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, 0x0, 0x55c0, 0x0, + 0x55c2, 0x0, 0x55e2, 0x0, 0x5606, 0x0, 0x5651, 0x0, 0x5668, + 0x0, 0x5674, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, + 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, 0x0, 0x580d, 0x0, 0x5831, + 0x0, 0x5832, 0x0, 0x5840, 0x0, 0x585a, 0x0, 0x585e, 0x0, + 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, 0x0, 0x58d8, 0x0, 0x58df, + 0x0, 0x58ee, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5906, 0x0, + 0x591a, 0x0, 0x5922, 0x0, 0x5944, 0x0, 0x5948, 0x0, 0x5951, + 0x0, 0x5954, 0x0, 0x5962, 0x0, 0x5973, 0x0, 0x59d8, 0x0, + 0x59ec, 0x0, 0x5a1b, 0x0, 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, + 0x0, 0x5ab5, 0x0, 0x5b08, 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, + 0x5b85, 0x0, 0x5bc3, 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, + 0x0, 0x5bf3, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c22, 0x0, + 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, 0x5c64, 0x0, 0x5c65, + 0x0, 0x5c6e, 0x0, 0x5c8d, 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, + 0x5d43, 0x0, 0x5d50, 0x0, 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, + 0x0, 0x5db2, 0x0, 0x5dba, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, + 0x5dfd, 0x0, 0x5e28, 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e74, + 0x0, 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, + 0x5ec9, 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, + 0x0, 0x5eec, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f22, 0x0, + 0x5f53, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f8b, + 0x0, 0x5f9a, 0x0, 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fcd, 0x0, + 0x5fd7, 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, + 0x0, 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, + 0x60d8, 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, + 0x0, 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, + 0x617a, 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, + 0x0, 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, + 0x6200, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, 0x6234, + 0x0, 0x625d, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, + 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x633d, + 0x0, 0x6350, 0x0, 0x6368, 0x0, 0x637b, 0x0, 0x6383, 0x0, + 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, 0x0, 0x63c5, 0x0, 0x63e4, + 0x0, 0x641c, 0x0, 0x6422, 0x0, 0x6452, 0x0, 0x6469, 0x0, + 0x6477, 0x0, 0x647e, 0x0, 0x649a, 0x0, 0x649d, 0x0, 0x64c4, + 0x0, 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, + 0x6599, 0x0, 0x65c5, 0x0, 0x65e2, 0x0, 0x65e3, 0x0, 0x6613, + 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, 0x0, 0x6691, 0x0, + 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, 0x66f4, 0x0, 0x66f8, + 0x0, 0x6700, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, + 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, 0x0, 0x677b, + 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, 0x67fa, 0x0, + 0x6817, 0x0, 0x681f, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, + 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, + 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, + 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b21, 0x0, + 0x6b54, 0x0, 0x6b72, 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, + 0x0, 0x6bae, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6c4e, 0x0, + 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, 0x0, 0x6ccc, 0x0, 0x6ccd, + 0x0, 0x6ce5, 0x0, 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, + 0x6d34, 0x0, 0x6d3e, 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, + 0x0, 0x6d77, 0x0, 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, + 0x6dda, 0x0, 0x6dea, 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, + 0x0, 0x6e6e, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, + 0x6ecb, 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f22, + 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, 0x0, + 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, 0x704a, + 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, 0x0, 0x7099, 0x0, + 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, 0x7145, 0x0, 0x7149, + 0x0, 0x716e, 0x0, 0x719c, 0x0, 0x71ce, 0x0, 0x71d0, 0x0, + 0x7210, 0x0, 0x721b, 0x0, 0x7228, 0x0, 0x722b, 0x0, 0x7235, + 0x0, 0x7250, 0x0, 0x7262, 0x0, 0x7280, 0x0, 0x7295, 0x0, + 0x72af, 0x0, 0x72c0, 0x0, 0x72fc, 0x0, 0x732a, 0x0, 0x7375, + 0x0, 0x737a, 0x0, 0x7387, 0x0, 0x738b, 0x0, 0x73a5, 0x0, + 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, + 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, + 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x7506, + 0x0, 0x7524, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, + 0x7565, 0x0, 0x7570, 0x0, 0x75e2, 0x0, 0x7610, 0x0, 0x761d, + 0x0, 0x761f, 0x0, 0x7642, 0x0, 0x7669, 0x0, 0x76ca, 0x0, + 0x76db, 0x0, 0x76e7, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, + 0x0, 0x771f, 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, + 0x77a7, 0x0, 0x784e, 0x0, 0x786b, 0x0, 0x788c, 0x0, 0x7891, + 0x0, 0x78ca, 0x0, 0x78cc, 0x0, 0x78fb, 0x0, 0x792a, 0x0, + 0x793c, 0x0, 0x793e, 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, + 0x0, 0x7956, 0x0, 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, + 0x797f, 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, + 0x0, 0x79ca, 0x0, 0x79eb, 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, + 0x7a4a, 0x0, 0x7a4f, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, + 0x0, 0x7aee, 0x0, 0x7b20, 0x0, 0x7bc0, 0x0, 0x7bc6, 0x0, + 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, 0x0, 0x7c7b, 0x0, 0x7c92, + 0x0, 0x7cbe, 0x0, 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, + 0x7ce7, 0x0, 0x7ce8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, + 0x0, 0x7d2f, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, 0x0, + 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, 0x7e09, + 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f3e, 0x0, + 0x7f72, 0x0, 0x7f79, 0x0, 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f95, + 0x0, 0x7f9a, 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, + 0x8005, 0x0, 0x8046, 0x0, 0x8060, 0x0, 0x806f, 0x0, 0x8070, + 0x0, 0x807e, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, + 0x8103, 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e8, 0x0, 0x81ed, + 0x0, 0x8201, 0x0, 0x8204, 0x0, 0x8218, 0x0, 0x826f, 0x0, + 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, 0x82b1, + 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, 0x0, + 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, 0x8353, + 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, 0x0, + 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, 0x83ef, + 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, 0x0, + 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, 0x8516, + 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, 0x0, + 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x8650, 0x0, 0x865c, + 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x8688, 0x0, 0x86a9, 0x0, + 0x86e2, 0x0, 0x870e, 0x0, 0x8728, 0x0, 0x876b, 0x0, 0x8779, + 0x0, 0x8786, 0x0, 0x87ba, 0x0, 0x87e1, 0x0, 0x8801, 0x0, + 0x881f, 0x0, 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, + 0x0, 0x88cf, 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, + 0x88f8, 0x0, 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, + 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x8aa0, 0x0, + 0x8aaa, 0x0, 0x8abf, 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, + 0x0, 0x8aed, 0x0, 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, + 0x8b39, 0x0, 0x8b58, 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c48, + 0x0, 0x8c55, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, + 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, 0x0, 0x8d77, + 0x0, 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, + 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, 0x8f38, + 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9e, 0x0, 0x8fb0, 0x0, + 0x8fb6, 0x0, 0x9023, 0x0, 0x9038, 0x0, 0x9072, 0x0, 0x907c, + 0x0, 0x908f, 0x0, 0x9094, 0x0, 0x90ce, 0x0, 0x90de, 0x0, + 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, 0x0, 0x916a, + 0x0, 0x9199, 0x0, 0x91b4, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, + 0x91d1, 0x0, 0x9234, 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, + 0x0, 0x92d7, 0x0, 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, + 0x93f9, 0x0, 0x9415, 0x0, 0x958b, 0x0, 0x95ad, 0x0, 0x95b7, + 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, 0x0, 0x9675, 0x0, + 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, 0x96a3, 0x0, 0x96b7, + 0x0, 0x96b8, 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, + 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, + 0x0, 0x9756, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ff, 0x0, + 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, + 0x0, 0x98e2, 0x0, 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, + 0x9929, 0x0, 0x99a7, 0x0, 0x99c2, 0x0, 0x99f1, 0x0, 0x99fe, + 0x0, 0x9a6a, 0x0, 0x9b12, 0x0, 0x9b6f, 0x0, 0x9c40, 0x0, + 0x9c57, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, 0x9db4, 0x0, 0x9dfa, + 0x0, 0x9e1e, 0x0, 0x9e7f, 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, + 0x9ebb, 0x0, 0x9ece, 0x0, 0x9ef9, 0x0, 0x9efe, 0x0, 0x9f05, + 0x0, 0x9f0f, 0x0, 0x9f16, 0x0, 0x9f3b, 0x0, 0x9f43, 0x0, + 0x9f8d, 0x0, 0x9f8e, 0x0, 0x9f9c, 0x0, 0x11099, 0x110ba, 0x0, + 0x1109b, 0x110ba, 0x0, 0x110a5, 0x110ba, 0x0, 0x11131, 0x11127, + 0x0, 0x11132, 0x11127, 0x0, 0x1d157, 0x1d165, 0x0, 0x1d158, + 0x1d165, 0x0, 0x1d158, 0x1d165, 0x1d16e, 0x0, 0x1d158, 0x1d165, + 0x1d16f, 0x0, 0x1d158, 0x1d165, 0x1d170, 0x0, 0x1d158, 0x1d165, + 0x1d171, 0x0, 0x1d158, 0x1d165, 0x1d172, 0x0, 0x1d1b9, 0x1d165, + 0x0, 0x1d1b9, 0x1d165, 0x1d16e, 0x0, 0x1d1b9, 0x1d165, 0x1d16f, + 0x0, 0x1d1ba, 0x1d165, 0x0, 0x1d1ba, 0x1d165, 0x1d16e, 0x0, + 0x1d1ba, 0x1d165, 0x1d16f, 0x0, 0x20122, 0x0, 0x2051c, 0x0, + 0x20525, 0x0, 0x2054b, 0x0, 0x2063a, 0x0, 0x20804, 0x0, + 0x208de, 0x0, 0x20a2c, 0x0, 0x20b63, 0x0, 0x214e4, 0x0, + 0x216a8, 0x0, 0x216ea, 0x0, 0x219c8, 0x0, 0x21b18, 0x0, + 0x21d0b, 0x0, 0x21de4, 0x0, 0x21de6, 0x0, 0x22183, 0x0, + 0x2219f, 0x0, 0x22331, 0x0, 0x226d4, 0x0, 0x22844, 0x0, + 0x2284a, 0x0, 0x22b0c, 0x0, 0x22bf1, 0x0, 0x2300a, 0x0, + 0x232b8, 0x0, 0x2335f, 0x0, 0x23393, 0x0, 0x2339c, 0x0, + 0x233c3, 0x0, 0x233d5, 0x0, 0x2346d, 0x0, 0x236a3, 0x0, + 0x238a7, 0x0, 0x23a8d, 0x0, 0x23afa, 0x0, 0x23cbc, 0x0, + 0x23d1e, 0x0, 0x23ed1, 0x0, 0x23f5e, 0x0, 0x23f8e, 0x0, + 0x24263, 0x0, 0x242ee, 0x0, 0x243ab, 0x0, 0x24608, 0x0, + 0x24735, 0x0, 0x24814, 0x0, 0x24c36, 0x0, 0x24c92, 0x0, + 0x24fa1, 0x0, 0x24fb8, 0x0, 0x25044, 0x0, 0x250f2, 0x0, + 0x250f3, 0x0, 0x25119, 0x0, 0x25133, 0x0, 0x25249, 0x0, + 0x2541d, 0x0, 0x25626, 0x0, 0x2569a, 0x0, 0x256c5, 0x0, + 0x2597c, 0x0, 0x25aa7, 0x0, 0x25bab, 0x0, 0x25c80, 0x0, + 0x25cd0, 0x0, 0x25f86, 0x0, 0x261da, 0x0, 0x26228, 0x0, + 0x26247, 0x0, 0x262d9, 0x0, 0x2633e, 0x0, 0x264da, 0x0, + 0x26523, 0x0, 0x265a8, 0x0, 0x267a7, 0x0, 0x267b5, 0x0, + 0x26b3c, 0x0, 0x26c36, 0x0, 0x26cd5, 0x0, 0x26d6b, 0x0, + 0x26f2c, 0x0, 0x26fb1, 0x0, 0x270d2, 0x0, 0x273ca, 0x0, + 0x27667, 0x0, 0x278ae, 0x0, 0x27966, 0x0, 0x27ca8, 0x0, + 0x27ed3, 0x0, 0x27f2f, 0x0, 0x285d2, 0x0, 0x285ed, 0x0, + 0x2872e, 0x0, 0x28bfa, 0x0, 0x28d77, 0x0, 0x29145, 0x0, + 0x291df, 0x0, 0x2921a, 0x0, 0x2940a, 0x0, 0x29496, 0x0, + 0x295b6, 0x0, 0x29b30, 0x0, 0x2a0ce, 0x0, 0x2a105, 0x0, + 0x2a20e, 0x0, 0x2a291, 0x0, 0x2a392, 0x0, 0x2a600, 0x0 + ]; + return t; + } -} + _IDCA decompCompatTable() + { + static _IDCA t = [ + 0x0, 0x20, 0x0, 0x20, 0x301, 0x0, 0x20, 0x303, 0x0, 0x20, + 0x304, 0x0, 0x20, 0x305, 0x0, 0x20, 0x306, 0x0, 0x20, 0x307, + 0x0, 0x20, 0x308, 0x0, 0x20, 0x308, 0x300, 0x0, 0x20, 0x308, + 0x301, 0x0, 0x20, 0x308, 0x342, 0x0, 0x20, 0x30a, 0x0, 0x20, + 0x30b, 0x0, 0x20, 0x313, 0x0, 0x20, 0x313, 0x300, 0x0, 0x20, + 0x313, 0x301, 0x0, 0x20, 0x313, 0x342, 0x0, 0x20, 0x314, 0x0, + 0x20, 0x314, 0x300, 0x0, 0x20, 0x314, 0x301, 0x0, 0x20, 0x314, + 0x342, 0x0, 0x20, 0x327, 0x0, 0x20, 0x328, 0x0, 0x20, 0x333, + 0x0, 0x20, 0x342, 0x0, 0x20, 0x345, 0x0, 0x20, 0x64b, 0x0, + 0x20, 0x64c, 0x0, 0x20, 0x64c, 0x651, 0x0, 0x20, 0x64d, 0x0, + 0x20, 0x64d, 0x651, 0x0, 0x20, 0x64e, 0x0, 0x20, 0x64e, 0x651, + 0x0, 0x20, 0x64f, 0x0, 0x20, 0x64f, 0x651, 0x0, 0x20, 0x650, + 0x0, 0x20, 0x650, 0x651, 0x0, 0x20, 0x651, 0x0, 0x20, 0x651, + 0x670, 0x0, 0x20, 0x652, 0x0, 0x20, 0x3099, 0x0, 0x20, 0x309a, + 0x0, 0x21, 0x0, 0x21, 0x21, 0x0, 0x21, 0x3f, 0x0, 0x22, 0x0, + 0x23, 0x0, 0x24, 0x0, 0x25, 0x0, 0x26, 0x0, 0x27, 0x0, 0x28, + 0x0, 0x28, 0x31, 0x29, 0x0, 0x28, 0x31, 0x30, 0x29, 0x0, 0x28, + 0x31, 0x31, 0x29, 0x0, 0x28, 0x31, 0x32, 0x29, 0x0, 0x28, 0x31, + 0x33, 0x29, 0x0, 0x28, 0x31, 0x34, 0x29, 0x0, 0x28, 0x31, 0x35, + 0x29, 0x0, 0x28, 0x31, 0x36, 0x29, 0x0, 0x28, 0x31, 0x37, 0x29, + 0x0, 0x28, 0x31, 0x38, 0x29, 0x0, 0x28, 0x31, 0x39, 0x29, 0x0, + 0x28, 0x32, 0x29, 0x0, 0x28, 0x32, 0x30, 0x29, 0x0, 0x28, 0x33, + 0x29, 0x0, 0x28, 0x34, 0x29, 0x0, 0x28, 0x35, 0x29, 0x0, 0x28, + 0x36, 0x29, 0x0, 0x28, 0x37, 0x29, 0x0, 0x28, 0x38, 0x29, 0x0, + 0x28, 0x39, 0x29, 0x0, 0x28, 0x41, 0x29, 0x0, 0x28, 0x42, 0x29, + 0x0, 0x28, 0x43, 0x29, 0x0, 0x28, 0x44, 0x29, 0x0, 0x28, 0x45, + 0x29, 0x0, 0x28, 0x46, 0x29, 0x0, 0x28, 0x47, 0x29, 0x0, 0x28, + 0x48, 0x29, 0x0, 0x28, 0x49, 0x29, 0x0, 0x28, 0x4a, 0x29, 0x0, + 0x28, 0x4b, 0x29, 0x0, 0x28, 0x4c, 0x29, 0x0, 0x28, 0x4d, 0x29, + 0x0, 0x28, 0x4e, 0x29, 0x0, 0x28, 0x4f, 0x29, 0x0, 0x28, 0x50, + 0x29, 0x0, 0x28, 0x51, 0x29, 0x0, 0x28, 0x52, 0x29, 0x0, 0x28, + 0x53, 0x29, 0x0, 0x28, 0x54, 0x29, 0x0, 0x28, 0x55, 0x29, 0x0, + 0x28, 0x56, 0x29, 0x0, 0x28, 0x57, 0x29, 0x0, 0x28, 0x58, 0x29, + 0x0, 0x28, 0x59, 0x29, 0x0, 0x28, 0x5a, 0x29, 0x0, 0x28, 0x61, + 0x29, 0x0, 0x28, 0x62, 0x29, 0x0, 0x28, 0x63, 0x29, 0x0, 0x28, + 0x64, 0x29, 0x0, 0x28, 0x65, 0x29, 0x0, 0x28, 0x66, 0x29, 0x0, + 0x28, 0x67, 0x29, 0x0, 0x28, 0x68, 0x29, 0x0, 0x28, 0x69, 0x29, + 0x0, 0x28, 0x6a, 0x29, 0x0, 0x28, 0x6b, 0x29, 0x0, 0x28, 0x6c, + 0x29, 0x0, 0x28, 0x6d, 0x29, 0x0, 0x28, 0x6e, 0x29, 0x0, 0x28, + 0x6f, 0x29, 0x0, 0x28, 0x70, 0x29, 0x0, 0x28, 0x71, 0x29, 0x0, + 0x28, 0x72, 0x29, 0x0, 0x28, 0x73, 0x29, 0x0, 0x28, 0x74, 0x29, + 0x0, 0x28, 0x75, 0x29, 0x0, 0x28, 0x76, 0x29, 0x0, 0x28, 0x77, + 0x29, 0x0, 0x28, 0x78, 0x29, 0x0, 0x28, 0x79, 0x29, 0x0, 0x28, + 0x7a, 0x29, 0x0, 0x28, 0x1100, 0x29, 0x0, 0x28, 0x1100, 0x1161, + 0x29, 0x0, 0x28, 0x1102, 0x29, 0x0, 0x28, 0x1102, 0x1161, 0x29, + 0x0, 0x28, 0x1103, 0x29, 0x0, 0x28, 0x1103, 0x1161, 0x29, 0x0, + 0x28, 0x1105, 0x29, 0x0, 0x28, 0x1105, 0x1161, 0x29, 0x0, 0x28, + 0x1106, 0x29, 0x0, 0x28, 0x1106, 0x1161, 0x29, 0x0, 0x28, + 0x1107, 0x29, 0x0, 0x28, 0x1107, 0x1161, 0x29, 0x0, 0x28, + 0x1109, 0x29, 0x0, 0x28, 0x1109, 0x1161, 0x29, 0x0, 0x28, + 0x110b, 0x29, 0x0, 0x28, 0x110b, 0x1161, 0x29, 0x0, 0x28, + 0x110b, 0x1169, 0x110c, 0x1165, 0x11ab, 0x29, 0x0, 0x28, + 0x110b, 0x1169, 0x1112, 0x116e, 0x29, 0x0, 0x28, 0x110c, 0x29, + 0x0, 0x28, 0x110c, 0x1161, 0x29, 0x0, 0x28, 0x110c, 0x116e, + 0x29, 0x0, 0x28, 0x110e, 0x29, 0x0, 0x28, 0x110e, 0x1161, 0x29, + 0x0, 0x28, 0x110f, 0x29, 0x0, 0x28, 0x110f, 0x1161, 0x29, 0x0, + 0x28, 0x1110, 0x29, 0x0, 0x28, 0x1110, 0x1161, 0x29, 0x0, 0x28, + 0x1111, 0x29, 0x0, 0x28, 0x1111, 0x1161, 0x29, 0x0, 0x28, + 0x1112, 0x29, 0x0, 0x28, 0x1112, 0x1161, 0x29, 0x0, 0x28, + 0x4e00, 0x29, 0x0, 0x28, 0x4e03, 0x29, 0x0, 0x28, 0x4e09, 0x29, + 0x0, 0x28, 0x4e5d, 0x29, 0x0, 0x28, 0x4e8c, 0x29, 0x0, 0x28, + 0x4e94, 0x29, 0x0, 0x28, 0x4ee3, 0x29, 0x0, 0x28, 0x4f01, 0x29, + 0x0, 0x28, 0x4f11, 0x29, 0x0, 0x28, 0x516b, 0x29, 0x0, 0x28, + 0x516d, 0x29, 0x0, 0x28, 0x52b4, 0x29, 0x0, 0x28, 0x5341, 0x29, + 0x0, 0x28, 0x5354, 0x29, 0x0, 0x28, 0x540d, 0x29, 0x0, 0x28, + 0x547c, 0x29, 0x0, 0x28, 0x56db, 0x29, 0x0, 0x28, 0x571f, 0x29, + 0x0, 0x28, 0x5b66, 0x29, 0x0, 0x28, 0x65e5, 0x29, 0x0, 0x28, + 0x6708, 0x29, 0x0, 0x28, 0x6709, 0x29, 0x0, 0x28, 0x6728, 0x29, + 0x0, 0x28, 0x682a, 0x29, 0x0, 0x28, 0x6c34, 0x29, 0x0, 0x28, + 0x706b, 0x29, 0x0, 0x28, 0x7279, 0x29, 0x0, 0x28, 0x76e3, 0x29, + 0x0, 0x28, 0x793e, 0x29, 0x0, 0x28, 0x795d, 0x29, 0x0, 0x28, + 0x796d, 0x29, 0x0, 0x28, 0x81ea, 0x29, 0x0, 0x28, 0x81f3, 0x29, + 0x0, 0x28, 0x8ca1, 0x29, 0x0, 0x28, 0x8cc7, 0x29, 0x0, 0x28, + 0x91d1, 0x29, 0x0, 0x29, 0x0, 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, + 0x2d, 0x0, 0x2e, 0x0, 0x2e, 0x2e, 0x0, 0x2e, 0x2e, 0x2e, 0x0, + 0x2f, 0x0, 0x30, 0x0, 0x30, 0x2c, 0x0, 0x30, 0x2e, 0x0, 0x30, + 0x2044, 0x33, 0x0, 0x30, 0x70b9, 0x0, 0x31, 0x0, 0x31, 0x2c, + 0x0, 0x31, 0x2e, 0x0, 0x31, 0x30, 0x0, 0x31, 0x30, 0x2e, 0x0, + 0x31, 0x30, 0x65e5, 0x0, 0x31, 0x30, 0x6708, 0x0, 0x31, 0x30, + 0x70b9, 0x0, 0x31, 0x31, 0x0, 0x31, 0x31, 0x2e, 0x0, 0x31, + 0x31, 0x65e5, 0x0, 0x31, 0x31, 0x6708, 0x0, 0x31, 0x31, 0x70b9, + 0x0, 0x31, 0x32, 0x0, 0x31, 0x32, 0x2e, 0x0, 0x31, 0x32, + 0x65e5, 0x0, 0x31, 0x32, 0x6708, 0x0, 0x31, 0x32, 0x70b9, 0x0, + 0x31, 0x33, 0x0, 0x31, 0x33, 0x2e, 0x0, 0x31, 0x33, 0x65e5, + 0x0, 0x31, 0x33, 0x70b9, 0x0, 0x31, 0x34, 0x0, 0x31, 0x34, + 0x2e, 0x0, 0x31, 0x34, 0x65e5, 0x0, 0x31, 0x34, 0x70b9, 0x0, + 0x31, 0x35, 0x0, 0x31, 0x35, 0x2e, 0x0, 0x31, 0x35, 0x65e5, + 0x0, 0x31, 0x35, 0x70b9, 0x0, 0x31, 0x36, 0x0, 0x31, 0x36, + 0x2e, 0x0, 0x31, 0x36, 0x65e5, 0x0, 0x31, 0x36, 0x70b9, 0x0, + 0x31, 0x37, 0x0, 0x31, 0x37, 0x2e, 0x0, 0x31, 0x37, 0x65e5, + 0x0, 0x31, 0x37, 0x70b9, 0x0, 0x31, 0x38, 0x0, 0x31, 0x38, + 0x2e, 0x0, 0x31, 0x38, 0x65e5, 0x0, 0x31, 0x38, 0x70b9, 0x0, + 0x31, 0x39, 0x0, 0x31, 0x39, 0x2e, 0x0, 0x31, 0x39, 0x65e5, + 0x0, 0x31, 0x39, 0x70b9, 0x0, 0x31, 0x2044, 0x0, 0x31, 0x2044, + 0x31, 0x30, 0x0, 0x31, 0x2044, 0x32, 0x0, 0x31, 0x2044, 0x33, + 0x0, 0x31, 0x2044, 0x34, 0x0, 0x31, 0x2044, 0x35, 0x0, 0x31, + 0x2044, 0x36, 0x0, 0x31, 0x2044, 0x37, 0x0, 0x31, 0x2044, 0x38, + 0x0, 0x31, 0x2044, 0x39, 0x0, 0x31, 0x65e5, 0x0, 0x31, 0x6708, + 0x0, 0x31, 0x70b9, 0x0, 0x32, 0x0, 0x32, 0x2c, 0x0, 0x32, 0x2e, + 0x0, 0x32, 0x30, 0x0, 0x32, 0x30, 0x2e, 0x0, 0x32, 0x30, + 0x65e5, 0x0, 0x32, 0x30, 0x70b9, 0x0, 0x32, 0x31, 0x0, 0x32, + 0x31, 0x65e5, 0x0, 0x32, 0x31, 0x70b9, 0x0, 0x32, 0x32, 0x0, + 0x32, 0x32, 0x65e5, 0x0, 0x32, 0x32, 0x70b9, 0x0, 0x32, 0x33, + 0x0, 0x32, 0x33, 0x65e5, 0x0, 0x32, 0x33, 0x70b9, 0x0, 0x32, + 0x34, 0x0, 0x32, 0x34, 0x65e5, 0x0, 0x32, 0x34, 0x70b9, 0x0, + 0x32, 0x35, 0x0, 0x32, 0x35, 0x65e5, 0x0, 0x32, 0x36, 0x0, + 0x32, 0x36, 0x65e5, 0x0, 0x32, 0x37, 0x0, 0x32, 0x37, 0x65e5, + 0x0, 0x32, 0x38, 0x0, 0x32, 0x38, 0x65e5, 0x0, 0x32, 0x39, 0x0, + 0x32, 0x39, 0x65e5, 0x0, 0x32, 0x2044, 0x33, 0x0, 0x32, 0x2044, + 0x35, 0x0, 0x32, 0x65e5, 0x0, 0x32, 0x6708, 0x0, 0x32, 0x70b9, + 0x0, 0x33, 0x0, 0x33, 0x2c, 0x0, 0x33, 0x2e, 0x0, 0x33, 0x30, + 0x0, 0x33, 0x30, 0x65e5, 0x0, 0x33, 0x31, 0x0, 0x33, 0x31, + 0x65e5, 0x0, 0x33, 0x32, 0x0, 0x33, 0x33, 0x0, 0x33, 0x34, 0x0, + 0x33, 0x35, 0x0, 0x33, 0x36, 0x0, 0x33, 0x37, 0x0, 0x33, 0x38, + 0x0, 0x33, 0x39, 0x0, 0x33, 0x2044, 0x34, 0x0, 0x33, 0x2044, + 0x35, 0x0, 0x33, 0x2044, 0x38, 0x0, 0x33, 0x65e5, 0x0, 0x33, + 0x6708, 0x0, 0x33, 0x70b9, 0x0, 0x34, 0x0, 0x34, 0x2c, 0x0, + 0x34, 0x2e, 0x0, 0x34, 0x30, 0x0, 0x34, 0x31, 0x0, 0x34, 0x32, + 0x0, 0x34, 0x33, 0x0, 0x34, 0x34, 0x0, 0x34, 0x35, 0x0, 0x34, + 0x36, 0x0, 0x34, 0x37, 0x0, 0x34, 0x38, 0x0, 0x34, 0x39, 0x0, + 0x34, 0x2044, 0x35, 0x0, 0x34, 0x65e5, 0x0, 0x34, 0x6708, 0x0, + 0x34, 0x70b9, 0x0, 0x35, 0x0, 0x35, 0x2c, 0x0, 0x35, 0x2e, 0x0, + 0x35, 0x30, 0x0, 0x35, 0x2044, 0x36, 0x0, 0x35, 0x2044, 0x38, + 0x0, 0x35, 0x65e5, 0x0, 0x35, 0x6708, 0x0, 0x35, 0x70b9, 0x0, + 0x36, 0x0, 0x36, 0x2c, 0x0, 0x36, 0x2e, 0x0, 0x36, 0x65e5, 0x0, + 0x36, 0x6708, 0x0, 0x36, 0x70b9, 0x0, 0x37, 0x0, 0x37, 0x2c, + 0x0, 0x37, 0x2e, 0x0, 0x37, 0x2044, 0x38, 0x0, 0x37, 0x65e5, + 0x0, 0x37, 0x6708, 0x0, 0x37, 0x70b9, 0x0, 0x38, 0x0, 0x38, + 0x2c, 0x0, 0x38, 0x2e, 0x0, 0x38, 0x65e5, 0x0, 0x38, 0x6708, + 0x0, 0x38, 0x70b9, 0x0, 0x39, 0x0, 0x39, 0x2c, 0x0, 0x39, 0x2e, + 0x0, 0x39, 0x65e5, 0x0, 0x39, 0x6708, 0x0, 0x39, 0x70b9, 0x0, + 0x3a, 0x0, 0x3a, 0x3a, 0x3d, 0x0, 0x3b, 0x0, 0x3c, 0x0, 0x3c, + 0x338, 0x0, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x3d, 0x3d, 0x3d, 0x0, + 0x3d, 0x338, 0x0, 0x3e, 0x0, 0x3e, 0x338, 0x0, 0x3f, 0x0, 0x3f, + 0x21, 0x0, 0x3f, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, 0x41, 0x55, + 0x0, 0x41, 0x300, 0x0, 0x41, 0x301, 0x0, 0x41, 0x302, 0x0, + 0x41, 0x302, 0x300, 0x0, 0x41, 0x302, 0x301, 0x0, 0x41, 0x302, + 0x303, 0x0, 0x41, 0x302, 0x309, 0x0, 0x41, 0x303, 0x0, 0x41, + 0x304, 0x0, 0x41, 0x306, 0x0, 0x41, 0x306, 0x300, 0x0, 0x41, + 0x306, 0x301, 0x0, 0x41, 0x306, 0x303, 0x0, 0x41, 0x306, 0x309, + 0x0, 0x41, 0x307, 0x0, 0x41, 0x307, 0x304, 0x0, 0x41, 0x308, + 0x0, 0x41, 0x308, 0x304, 0x0, 0x41, 0x309, 0x0, 0x41, 0x30a, + 0x0, 0x41, 0x30a, 0x301, 0x0, 0x41, 0x30c, 0x0, 0x41, 0x30f, + 0x0, 0x41, 0x311, 0x0, 0x41, 0x323, 0x0, 0x41, 0x323, 0x302, + 0x0, 0x41, 0x323, 0x306, 0x0, 0x41, 0x325, 0x0, 0x41, 0x328, + 0x0, 0x41, 0x2215, 0x6d, 0x0, 0x42, 0x0, 0x42, 0x71, 0x0, 0x42, + 0x307, 0x0, 0x42, 0x323, 0x0, 0x42, 0x331, 0x0, 0x43, 0x0, + 0x43, 0x44, 0x0, 0x43, 0x6f, 0x2e, 0x0, 0x43, 0x301, 0x0, 0x43, + 0x302, 0x0, 0x43, 0x307, 0x0, 0x43, 0x30c, 0x0, 0x43, 0x327, + 0x0, 0x43, 0x327, 0x301, 0x0, 0x43, 0x2215, 0x6b, 0x67, 0x0, + 0x44, 0x0, 0x44, 0x4a, 0x0, 0x44, 0x5a, 0x0, 0x44, 0x5a, 0x30c, + 0x0, 0x44, 0x7a, 0x0, 0x44, 0x7a, 0x30c, 0x0, 0x44, 0x307, 0x0, + 0x44, 0x30c, 0x0, 0x44, 0x323, 0x0, 0x44, 0x327, 0x0, 0x44, + 0x32d, 0x0, 0x44, 0x331, 0x0, 0x45, 0x0, 0x45, 0x300, 0x0, + 0x45, 0x301, 0x0, 0x45, 0x302, 0x0, 0x45, 0x302, 0x300, 0x0, + 0x45, 0x302, 0x301, 0x0, 0x45, 0x302, 0x303, 0x0, 0x45, 0x302, + 0x309, 0x0, 0x45, 0x303, 0x0, 0x45, 0x304, 0x0, 0x45, 0x304, + 0x300, 0x0, 0x45, 0x304, 0x301, 0x0, 0x45, 0x306, 0x0, 0x45, + 0x307, 0x0, 0x45, 0x308, 0x0, 0x45, 0x309, 0x0, 0x45, 0x30c, + 0x0, 0x45, 0x30f, 0x0, 0x45, 0x311, 0x0, 0x45, 0x323, 0x0, + 0x45, 0x323, 0x302, 0x0, 0x45, 0x327, 0x0, 0x45, 0x327, 0x306, + 0x0, 0x45, 0x328, 0x0, 0x45, 0x32d, 0x0, 0x45, 0x330, 0x0, + 0x46, 0x0, 0x46, 0x41, 0x58, 0x0, 0x46, 0x307, 0x0, 0x47, 0x0, + 0x47, 0x42, 0x0, 0x47, 0x48, 0x7a, 0x0, 0x47, 0x50, 0x61, 0x0, + 0x47, 0x79, 0x0, 0x47, 0x301, 0x0, 0x47, 0x302, 0x0, 0x47, + 0x304, 0x0, 0x47, 0x306, 0x0, 0x47, 0x307, 0x0, 0x47, 0x30c, + 0x0, 0x47, 0x327, 0x0, 0x48, 0x0, 0x48, 0x50, 0x0, 0x48, 0x56, + 0x0, 0x48, 0x67, 0x0, 0x48, 0x7a, 0x0, 0x48, 0x302, 0x0, 0x48, + 0x307, 0x0, 0x48, 0x308, 0x0, 0x48, 0x30c, 0x0, 0x48, 0x323, + 0x0, 0x48, 0x327, 0x0, 0x48, 0x32e, 0x0, 0x49, 0x0, 0x49, 0x49, + 0x0, 0x49, 0x49, 0x49, 0x0, 0x49, 0x4a, 0x0, 0x49, 0x55, 0x0, + 0x49, 0x56, 0x0, 0x49, 0x58, 0x0, 0x49, 0x300, 0x0, 0x49, + 0x301, 0x0, 0x49, 0x302, 0x0, 0x49, 0x303, 0x0, 0x49, 0x304, + 0x0, 0x49, 0x306, 0x0, 0x49, 0x307, 0x0, 0x49, 0x308, 0x0, + 0x49, 0x308, 0x301, 0x0, 0x49, 0x309, 0x0, 0x49, 0x30c, 0x0, + 0x49, 0x30f, 0x0, 0x49, 0x311, 0x0, 0x49, 0x323, 0x0, 0x49, + 0x328, 0x0, 0x49, 0x330, 0x0, 0x4a, 0x0, 0x4a, 0x302, 0x0, + 0x4b, 0x0, 0x4b, 0x42, 0x0, 0x4b, 0x4b, 0x0, 0x4b, 0x4d, 0x0, + 0x4b, 0x301, 0x0, 0x4b, 0x30c, 0x0, 0x4b, 0x323, 0x0, 0x4b, + 0x327, 0x0, 0x4b, 0x331, 0x0, 0x4c, 0x0, 0x4c, 0x4a, 0x0, 0x4c, + 0x54, 0x44, 0x0, 0x4c, 0x6a, 0x0, 0x4c, 0xb7, 0x0, 0x4c, 0x301, + 0x0, 0x4c, 0x30c, 0x0, 0x4c, 0x323, 0x0, 0x4c, 0x323, 0x304, + 0x0, 0x4c, 0x327, 0x0, 0x4c, 0x32d, 0x0, 0x4c, 0x331, 0x0, + 0x4d, 0x0, 0x4d, 0x42, 0x0, 0x4d, 0x43, 0x0, 0x4d, 0x44, 0x0, + 0x4d, 0x48, 0x7a, 0x0, 0x4d, 0x50, 0x61, 0x0, 0x4d, 0x56, 0x0, + 0x4d, 0x57, 0x0, 0x4d, 0x301, 0x0, 0x4d, 0x307, 0x0, 0x4d, + 0x323, 0x0, 0x4d, 0x3a9, 0x0, 0x4e, 0x0, 0x4e, 0x4a, 0x0, 0x4e, + 0x6a, 0x0, 0x4e, 0x6f, 0x0, 0x4e, 0x300, 0x0, 0x4e, 0x301, 0x0, + 0x4e, 0x303, 0x0, 0x4e, 0x307, 0x0, 0x4e, 0x30c, 0x0, 0x4e, + 0x323, 0x0, 0x4e, 0x327, 0x0, 0x4e, 0x32d, 0x0, 0x4e, 0x331, + 0x0, 0x4f, 0x0, 0x4f, 0x300, 0x0, 0x4f, 0x301, 0x0, 0x4f, + 0x302, 0x0, 0x4f, 0x302, 0x300, 0x0, 0x4f, 0x302, 0x301, 0x0, + 0x4f, 0x302, 0x303, 0x0, 0x4f, 0x302, 0x309, 0x0, 0x4f, 0x303, + 0x0, 0x4f, 0x303, 0x301, 0x0, 0x4f, 0x303, 0x304, 0x0, 0x4f, + 0x303, 0x308, 0x0, 0x4f, 0x304, 0x0, 0x4f, 0x304, 0x300, 0x0, + 0x4f, 0x304, 0x301, 0x0, 0x4f, 0x306, 0x0, 0x4f, 0x307, 0x0, + 0x4f, 0x307, 0x304, 0x0, 0x4f, 0x308, 0x0, 0x4f, 0x308, 0x304, + 0x0, 0x4f, 0x309, 0x0, 0x4f, 0x30b, 0x0, 0x4f, 0x30c, 0x0, + 0x4f, 0x30f, 0x0, 0x4f, 0x311, 0x0, 0x4f, 0x31b, 0x0, 0x4f, + 0x31b, 0x300, 0x0, 0x4f, 0x31b, 0x301, 0x0, 0x4f, 0x31b, 0x303, + 0x0, 0x4f, 0x31b, 0x309, 0x0, 0x4f, 0x31b, 0x323, 0x0, 0x4f, + 0x323, 0x0, 0x4f, 0x323, 0x302, 0x0, 0x4f, 0x328, 0x0, 0x4f, + 0x328, 0x304, 0x0, 0x50, 0x0, 0x50, 0x48, 0x0, 0x50, 0x50, + 0x4d, 0x0, 0x50, 0x50, 0x56, 0x0, 0x50, 0x52, 0x0, 0x50, 0x54, + 0x45, 0x0, 0x50, 0x61, 0x0, 0x50, 0x301, 0x0, 0x50, 0x307, 0x0, + 0x51, 0x0, 0x52, 0x0, 0x52, 0x73, 0x0, 0x52, 0x301, 0x0, 0x52, + 0x307, 0x0, 0x52, 0x30c, 0x0, 0x52, 0x30f, 0x0, 0x52, 0x311, + 0x0, 0x52, 0x323, 0x0, 0x52, 0x323, 0x304, 0x0, 0x52, 0x327, + 0x0, 0x52, 0x331, 0x0, 0x53, 0x0, 0x53, 0x44, 0x0, 0x53, 0x4d, + 0x0, 0x53, 0x53, 0x0, 0x53, 0x76, 0x0, 0x53, 0x301, 0x0, 0x53, + 0x301, 0x307, 0x0, 0x53, 0x302, 0x0, 0x53, 0x307, 0x0, 0x53, + 0x30c, 0x0, 0x53, 0x30c, 0x307, 0x0, 0x53, 0x323, 0x0, 0x53, + 0x323, 0x307, 0x0, 0x53, 0x326, 0x0, 0x53, 0x327, 0x0, 0x54, + 0x0, 0x54, 0x45, 0x4c, 0x0, 0x54, 0x48, 0x7a, 0x0, 0x54, 0x4d, + 0x0, 0x54, 0x307, 0x0, 0x54, 0x30c, 0x0, 0x54, 0x323, 0x0, + 0x54, 0x326, 0x0, 0x54, 0x327, 0x0, 0x54, 0x32d, 0x0, 0x54, + 0x331, 0x0, 0x55, 0x0, 0x55, 0x300, 0x0, 0x55, 0x301, 0x0, + 0x55, 0x302, 0x0, 0x55, 0x303, 0x0, 0x55, 0x303, 0x301, 0x0, + 0x55, 0x304, 0x0, 0x55, 0x304, 0x308, 0x0, 0x55, 0x306, 0x0, + 0x55, 0x308, 0x0, 0x55, 0x308, 0x300, 0x0, 0x55, 0x308, 0x301, + 0x0, 0x55, 0x308, 0x304, 0x0, 0x55, 0x308, 0x30c, 0x0, 0x55, + 0x309, 0x0, 0x55, 0x30a, 0x0, 0x55, 0x30b, 0x0, 0x55, 0x30c, + 0x0, 0x55, 0x30f, 0x0, 0x55, 0x311, 0x0, 0x55, 0x31b, 0x0, + 0x55, 0x31b, 0x300, 0x0, 0x55, 0x31b, 0x301, 0x0, 0x55, 0x31b, + 0x303, 0x0, 0x55, 0x31b, 0x309, 0x0, 0x55, 0x31b, 0x323, 0x0, + 0x55, 0x323, 0x0, 0x55, 0x324, 0x0, 0x55, 0x328, 0x0, 0x55, + 0x32d, 0x0, 0x55, 0x330, 0x0, 0x56, 0x0, 0x56, 0x49, 0x0, 0x56, + 0x49, 0x49, 0x0, 0x56, 0x49, 0x49, 0x49, 0x0, 0x56, 0x303, 0x0, + 0x56, 0x323, 0x0, 0x56, 0x2215, 0x6d, 0x0, 0x57, 0x0, 0x57, + 0x43, 0x0, 0x57, 0x5a, 0x0, 0x57, 0x62, 0x0, 0x57, 0x300, 0x0, + 0x57, 0x301, 0x0, 0x57, 0x302, 0x0, 0x57, 0x307, 0x0, 0x57, + 0x308, 0x0, 0x57, 0x323, 0x0, 0x58, 0x0, 0x58, 0x49, 0x0, 0x58, + 0x49, 0x49, 0x0, 0x58, 0x307, 0x0, 0x58, 0x308, 0x0, 0x59, 0x0, + 0x59, 0x300, 0x0, 0x59, 0x301, 0x0, 0x59, 0x302, 0x0, 0x59, + 0x303, 0x0, 0x59, 0x304, 0x0, 0x59, 0x307, 0x0, 0x59, 0x308, + 0x0, 0x59, 0x309, 0x0, 0x59, 0x323, 0x0, 0x5a, 0x0, 0x5a, + 0x301, 0x0, 0x5a, 0x302, 0x0, 0x5a, 0x307, 0x0, 0x5a, 0x30c, + 0x0, 0x5a, 0x323, 0x0, 0x5a, 0x331, 0x0, 0x5b, 0x0, 0x5c, 0x0, + 0x5d, 0x0, 0x5e, 0x0, 0x5f, 0x0, 0x60, 0x0, 0x61, 0x0, 0x61, + 0x2e, 0x6d, 0x2e, 0x0, 0x61, 0x2f, 0x63, 0x0, 0x61, 0x2f, 0x73, + 0x0, 0x61, 0x2be, 0x0, 0x61, 0x300, 0x0, 0x61, 0x301, 0x0, + 0x61, 0x302, 0x0, 0x61, 0x302, 0x300, 0x0, 0x61, 0x302, 0x301, + 0x0, 0x61, 0x302, 0x303, 0x0, 0x61, 0x302, 0x309, 0x0, 0x61, + 0x303, 0x0, 0x61, 0x304, 0x0, 0x61, 0x306, 0x0, 0x61, 0x306, + 0x300, 0x0, 0x61, 0x306, 0x301, 0x0, 0x61, 0x306, 0x303, 0x0, + 0x61, 0x306, 0x309, 0x0, 0x61, 0x307, 0x0, 0x61, 0x307, 0x304, + 0x0, 0x61, 0x308, 0x0, 0x61, 0x308, 0x304, 0x0, 0x61, 0x309, + 0x0, 0x61, 0x30a, 0x0, 0x61, 0x30a, 0x301, 0x0, 0x61, 0x30c, + 0x0, 0x61, 0x30f, 0x0, 0x61, 0x311, 0x0, 0x61, 0x323, 0x0, + 0x61, 0x323, 0x302, 0x0, 0x61, 0x323, 0x306, 0x0, 0x61, 0x325, + 0x0, 0x61, 0x328, 0x0, 0x62, 0x0, 0x62, 0x61, 0x72, 0x0, 0x62, + 0x307, 0x0, 0x62, 0x323, 0x0, 0x62, 0x331, 0x0, 0x63, 0x0, + 0x63, 0x2f, 0x6f, 0x0, 0x63, 0x2f, 0x75, 0x0, 0x63, 0x61, 0x6c, + 0x0, 0x63, 0x63, 0x0, 0x63, 0x64, 0x0, 0x63, 0x6d, 0x0, 0x63, + 0x6d, 0x32, 0x0, 0x63, 0x6d, 0x33, 0x0, 0x63, 0x301, 0x0, 0x63, + 0x302, 0x0, 0x63, 0x307, 0x0, 0x63, 0x30c, 0x0, 0x63, 0x327, + 0x0, 0x63, 0x327, 0x301, 0x0, 0x64, 0x0, 0x64, 0x42, 0x0, 0x64, + 0x61, 0x0, 0x64, 0x6c, 0x0, 0x64, 0x6d, 0x0, 0x64, 0x6d, 0x32, + 0x0, 0x64, 0x6d, 0x33, 0x0, 0x64, 0x7a, 0x0, 0x64, 0x7a, 0x30c, + 0x0, 0x64, 0x307, 0x0, 0x64, 0x30c, 0x0, 0x64, 0x323, 0x0, + 0x64, 0x327, 0x0, 0x64, 0x32d, 0x0, 0x64, 0x331, 0x0, 0x65, + 0x0, 0x65, 0x56, 0x0, 0x65, 0x72, 0x67, 0x0, 0x65, 0x300, 0x0, + 0x65, 0x301, 0x0, 0x65, 0x302, 0x0, 0x65, 0x302, 0x300, 0x0, + 0x65, 0x302, 0x301, 0x0, 0x65, 0x302, 0x303, 0x0, 0x65, 0x302, + 0x309, 0x0, 0x65, 0x303, 0x0, 0x65, 0x304, 0x0, 0x65, 0x304, + 0x300, 0x0, 0x65, 0x304, 0x301, 0x0, 0x65, 0x306, 0x0, 0x65, + 0x307, 0x0, 0x65, 0x308, 0x0, 0x65, 0x309, 0x0, 0x65, 0x30c, + 0x0, 0x65, 0x30f, 0x0, 0x65, 0x311, 0x0, 0x65, 0x323, 0x0, + 0x65, 0x323, 0x302, 0x0, 0x65, 0x327, 0x0, 0x65, 0x327, 0x306, + 0x0, 0x65, 0x328, 0x0, 0x65, 0x32d, 0x0, 0x65, 0x330, 0x0, + 0x66, 0x0, 0x66, 0x66, 0x0, 0x66, 0x66, 0x69, 0x0, 0x66, 0x66, + 0x6c, 0x0, 0x66, 0x69, 0x0, 0x66, 0x6c, 0x0, 0x66, 0x6d, 0x0, + 0x66, 0x307, 0x0, 0x67, 0x0, 0x67, 0x61, 0x6c, 0x0, 0x67, + 0x301, 0x0, 0x67, 0x302, 0x0, 0x67, 0x304, 0x0, 0x67, 0x306, + 0x0, 0x67, 0x307, 0x0, 0x67, 0x30c, 0x0, 0x67, 0x327, 0x0, + 0x68, 0x0, 0x68, 0x50, 0x61, 0x0, 0x68, 0x61, 0x0, 0x68, 0x302, + 0x0, 0x68, 0x307, 0x0, 0x68, 0x308, 0x0, 0x68, 0x30c, 0x0, + 0x68, 0x323, 0x0, 0x68, 0x327, 0x0, 0x68, 0x32e, 0x0, 0x68, + 0x331, 0x0, 0x69, 0x0, 0x69, 0x69, 0x0, 0x69, 0x69, 0x69, 0x0, + 0x69, 0x6a, 0x0, 0x69, 0x6e, 0x0, 0x69, 0x76, 0x0, 0x69, 0x78, + 0x0, 0x69, 0x300, 0x0, 0x69, 0x301, 0x0, 0x69, 0x302, 0x0, + 0x69, 0x303, 0x0, 0x69, 0x304, 0x0, 0x69, 0x306, 0x0, 0x69, + 0x308, 0x0, 0x69, 0x308, 0x301, 0x0, 0x69, 0x309, 0x0, 0x69, + 0x30c, 0x0, 0x69, 0x30f, 0x0, 0x69, 0x311, 0x0, 0x69, 0x323, + 0x0, 0x69, 0x328, 0x0, 0x69, 0x330, 0x0, 0x6a, 0x0, 0x6a, + 0x302, 0x0, 0x6a, 0x30c, 0x0, 0x6b, 0x0, 0x6b, 0x41, 0x0, 0x6b, + 0x48, 0x7a, 0x0, 0x6b, 0x50, 0x61, 0x0, 0x6b, 0x56, 0x0, 0x6b, + 0x57, 0x0, 0x6b, 0x63, 0x61, 0x6c, 0x0, 0x6b, 0x67, 0x0, 0x6b, + 0x6c, 0x0, 0x6b, 0x6d, 0x0, 0x6b, 0x6d, 0x32, 0x0, 0x6b, 0x6d, + 0x33, 0x0, 0x6b, 0x74, 0x0, 0x6b, 0x301, 0x0, 0x6b, 0x30c, 0x0, + 0x6b, 0x323, 0x0, 0x6b, 0x327, 0x0, 0x6b, 0x331, 0x0, 0x6b, + 0x3a9, 0x0, 0x6c, 0x0, 0x6c, 0x6a, 0x0, 0x6c, 0x6d, 0x0, 0x6c, + 0x6e, 0x0, 0x6c, 0x6f, 0x67, 0x0, 0x6c, 0x78, 0x0, 0x6c, 0xb7, + 0x0, 0x6c, 0x301, 0x0, 0x6c, 0x30c, 0x0, 0x6c, 0x323, 0x0, + 0x6c, 0x323, 0x304, 0x0, 0x6c, 0x327, 0x0, 0x6c, 0x32d, 0x0, + 0x6c, 0x331, 0x0, 0x6d, 0x0, 0x6d, 0x32, 0x0, 0x6d, 0x33, 0x0, + 0x6d, 0x41, 0x0, 0x6d, 0x56, 0x0, 0x6d, 0x57, 0x0, 0x6d, 0x62, + 0x0, 0x6d, 0x67, 0x0, 0x6d, 0x69, 0x6c, 0x0, 0x6d, 0x6c, 0x0, + 0x6d, 0x6d, 0x0, 0x6d, 0x6d, 0x32, 0x0, 0x6d, 0x6d, 0x33, 0x0, + 0x6d, 0x6f, 0x6c, 0x0, 0x6d, 0x73, 0x0, 0x6d, 0x301, 0x0, 0x6d, + 0x307, 0x0, 0x6d, 0x323, 0x0, 0x6d, 0x2215, 0x73, 0x0, 0x6d, + 0x2215, 0x73, 0x32, 0x0, 0x6e, 0x0, 0x6e, 0x41, 0x0, 0x6e, + 0x46, 0x0, 0x6e, 0x56, 0x0, 0x6e, 0x57, 0x0, 0x6e, 0x6a, 0x0, + 0x6e, 0x6d, 0x0, 0x6e, 0x73, 0x0, 0x6e, 0x300, 0x0, 0x6e, + 0x301, 0x0, 0x6e, 0x303, 0x0, 0x6e, 0x307, 0x0, 0x6e, 0x30c, + 0x0, 0x6e, 0x323, 0x0, 0x6e, 0x327, 0x0, 0x6e, 0x32d, 0x0, + 0x6e, 0x331, 0x0, 0x6f, 0x0, 0x6f, 0x56, 0x0, 0x6f, 0x300, 0x0, + 0x6f, 0x301, 0x0, 0x6f, 0x302, 0x0, 0x6f, 0x302, 0x300, 0x0, + 0x6f, 0x302, 0x301, 0x0, 0x6f, 0x302, 0x303, 0x0, 0x6f, 0x302, + 0x309, 0x0, 0x6f, 0x303, 0x0, 0x6f, 0x303, 0x301, 0x0, 0x6f, + 0x303, 0x304, 0x0, 0x6f, 0x303, 0x308, 0x0, 0x6f, 0x304, 0x0, + 0x6f, 0x304, 0x300, 0x0, 0x6f, 0x304, 0x301, 0x0, 0x6f, 0x306, + 0x0, 0x6f, 0x307, 0x0, 0x6f, 0x307, 0x304, 0x0, 0x6f, 0x308, + 0x0, 0x6f, 0x308, 0x304, 0x0, 0x6f, 0x309, 0x0, 0x6f, 0x30b, + 0x0, 0x6f, 0x30c, 0x0, 0x6f, 0x30f, 0x0, 0x6f, 0x311, 0x0, + 0x6f, 0x31b, 0x0, 0x6f, 0x31b, 0x300, 0x0, 0x6f, 0x31b, 0x301, + 0x0, 0x6f, 0x31b, 0x303, 0x0, 0x6f, 0x31b, 0x309, 0x0, 0x6f, + 0x31b, 0x323, 0x0, 0x6f, 0x323, 0x0, 0x6f, 0x323, 0x302, 0x0, + 0x6f, 0x328, 0x0, 0x6f, 0x328, 0x304, 0x0, 0x70, 0x0, 0x70, + 0x2e, 0x6d, 0x2e, 0x0, 0x70, 0x41, 0x0, 0x70, 0x46, 0x0, 0x70, + 0x56, 0x0, 0x70, 0x57, 0x0, 0x70, 0x63, 0x0, 0x70, 0x73, 0x0, + 0x70, 0x301, 0x0, 0x70, 0x307, 0x0, 0x71, 0x0, 0x72, 0x0, 0x72, + 0x61, 0x64, 0x0, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x0, 0x72, + 0x61, 0x64, 0x2215, 0x73, 0x32, 0x0, 0x72, 0x301, 0x0, 0x72, + 0x307, 0x0, 0x72, 0x30c, 0x0, 0x72, 0x30f, 0x0, 0x72, 0x311, + 0x0, 0x72, 0x323, 0x0, 0x72, 0x323, 0x304, 0x0, 0x72, 0x327, + 0x0, 0x72, 0x331, 0x0, 0x73, 0x0, 0x73, 0x72, 0x0, 0x73, 0x74, + 0x0, 0x73, 0x301, 0x0, 0x73, 0x301, 0x307, 0x0, 0x73, 0x302, + 0x0, 0x73, 0x307, 0x0, 0x73, 0x30c, 0x0, 0x73, 0x30c, 0x307, + 0x0, 0x73, 0x323, 0x0, 0x73, 0x323, 0x307, 0x0, 0x73, 0x326, + 0x0, 0x73, 0x327, 0x0, 0x74, 0x0, 0x74, 0x307, 0x0, 0x74, + 0x308, 0x0, 0x74, 0x30c, 0x0, 0x74, 0x323, 0x0, 0x74, 0x326, + 0x0, 0x74, 0x327, 0x0, 0x74, 0x32d, 0x0, 0x74, 0x331, 0x0, + 0x75, 0x0, 0x75, 0x300, 0x0, 0x75, 0x301, 0x0, 0x75, 0x302, + 0x0, 0x75, 0x303, 0x0, 0x75, 0x303, 0x301, 0x0, 0x75, 0x304, + 0x0, 0x75, 0x304, 0x308, 0x0, 0x75, 0x306, 0x0, 0x75, 0x308, + 0x0, 0x75, 0x308, 0x300, 0x0, 0x75, 0x308, 0x301, 0x0, 0x75, + 0x308, 0x304, 0x0, 0x75, 0x308, 0x30c, 0x0, 0x75, 0x309, 0x0, + 0x75, 0x30a, 0x0, 0x75, 0x30b, 0x0, 0x75, 0x30c, 0x0, 0x75, + 0x30f, 0x0, 0x75, 0x311, 0x0, 0x75, 0x31b, 0x0, 0x75, 0x31b, + 0x300, 0x0, 0x75, 0x31b, 0x301, 0x0, 0x75, 0x31b, 0x303, 0x0, + 0x75, 0x31b, 0x309, 0x0, 0x75, 0x31b, 0x323, 0x0, 0x75, 0x323, + 0x0, 0x75, 0x324, 0x0, 0x75, 0x328, 0x0, 0x75, 0x32d, 0x0, + 0x75, 0x330, 0x0, 0x76, 0x0, 0x76, 0x69, 0x0, 0x76, 0x69, 0x69, + 0x0, 0x76, 0x69, 0x69, 0x69, 0x0, 0x76, 0x303, 0x0, 0x76, + 0x323, 0x0, 0x77, 0x0, 0x77, 0x300, 0x0, 0x77, 0x301, 0x0, + 0x77, 0x302, 0x0, 0x77, 0x307, 0x0, 0x77, 0x308, 0x0, 0x77, + 0x30a, 0x0, 0x77, 0x323, 0x0, 0x78, 0x0, 0x78, 0x69, 0x0, 0x78, + 0x69, 0x69, 0x0, 0x78, 0x307, 0x0, 0x78, 0x308, 0x0, 0x79, 0x0, + 0x79, 0x300, 0x0, 0x79, 0x301, 0x0, 0x79, 0x302, 0x0, 0x79, + 0x303, 0x0, 0x79, 0x304, 0x0, 0x79, 0x307, 0x0, 0x79, 0x308, + 0x0, 0x79, 0x309, 0x0, 0x79, 0x30a, 0x0, 0x79, 0x323, 0x0, + 0x7a, 0x0, 0x7a, 0x301, 0x0, 0x7a, 0x302, 0x0, 0x7a, 0x307, + 0x0, 0x7a, 0x30c, 0x0, 0x7a, 0x323, 0x0, 0x7a, 0x331, 0x0, + 0x7b, 0x0, 0x7c, 0x0, 0x7d, 0x0, 0x7e, 0x0, 0xa2, 0x0, 0xa3, + 0x0, 0xa5, 0x0, 0xa6, 0x0, 0xac, 0x0, 0xb0, 0x43, 0x0, 0xb0, + 0x46, 0x0, 0xb7, 0x0, 0xc6, 0x0, 0xc6, 0x301, 0x0, 0xc6, 0x304, + 0x0, 0xd8, 0x301, 0x0, 0xe6, 0x301, 0x0, 0xe6, 0x304, 0x0, + 0xf0, 0x0, 0xf8, 0x301, 0x0, 0x126, 0x0, 0x127, 0x0, 0x131, + 0x0, 0x14b, 0x0, 0x153, 0x0, 0x18e, 0x0, 0x190, 0x0, 0x1ab, + 0x0, 0x1b7, 0x30c, 0x0, 0x222, 0x0, 0x237, 0x0, 0x250, 0x0, + 0x251, 0x0, 0x252, 0x0, 0x254, 0x0, 0x255, 0x0, 0x259, 0x0, + 0x25b, 0x0, 0x25c, 0x0, 0x25f, 0x0, 0x261, 0x0, 0x263, 0x0, + 0x265, 0x0, 0x266, 0x0, 0x268, 0x0, 0x269, 0x0, 0x26a, 0x0, + 0x26d, 0x0, 0x26f, 0x0, 0x270, 0x0, 0x271, 0x0, 0x272, 0x0, + 0x273, 0x0, 0x274, 0x0, 0x275, 0x0, 0x278, 0x0, 0x279, 0x0, + 0x27b, 0x0, 0x281, 0x0, 0x282, 0x0, 0x283, 0x0, 0x289, 0x0, + 0x28a, 0x0, 0x28b, 0x0, 0x28c, 0x0, 0x290, 0x0, 0x291, 0x0, + 0x292, 0x0, 0x292, 0x30c, 0x0, 0x295, 0x0, 0x29d, 0x0, 0x29f, + 0x0, 0x2b9, 0x0, 0x2bc, 0x6e, 0x0, 0x300, 0x0, 0x301, 0x0, + 0x308, 0x301, 0x0, 0x313, 0x0, 0x391, 0x0, 0x391, 0x300, 0x0, + 0x391, 0x301, 0x0, 0x391, 0x304, 0x0, 0x391, 0x306, 0x0, 0x391, + 0x313, 0x0, 0x391, 0x313, 0x300, 0x0, 0x391, 0x313, 0x300, + 0x345, 0x0, 0x391, 0x313, 0x301, 0x0, 0x391, 0x313, 0x301, + 0x345, 0x0, 0x391, 0x313, 0x342, 0x0, 0x391, 0x313, 0x342, + 0x345, 0x0, 0x391, 0x313, 0x345, 0x0, 0x391, 0x314, 0x0, 0x391, + 0x314, 0x300, 0x0, 0x391, 0x314, 0x300, 0x345, 0x0, 0x391, + 0x314, 0x301, 0x0, 0x391, 0x314, 0x301, 0x345, 0x0, 0x391, + 0x314, 0x342, 0x0, 0x391, 0x314, 0x342, 0x345, 0x0, 0x391, + 0x314, 0x345, 0x0, 0x391, 0x345, 0x0, 0x392, 0x0, 0x393, 0x0, + 0x394, 0x0, 0x395, 0x0, 0x395, 0x300, 0x0, 0x395, 0x301, 0x0, + 0x395, 0x313, 0x0, 0x395, 0x313, 0x300, 0x0, 0x395, 0x313, + 0x301, 0x0, 0x395, 0x314, 0x0, 0x395, 0x314, 0x300, 0x0, 0x395, + 0x314, 0x301, 0x0, 0x396, 0x0, 0x397, 0x0, 0x397, 0x300, 0x0, + 0x397, 0x301, 0x0, 0x397, 0x313, 0x0, 0x397, 0x313, 0x300, 0x0, + 0x397, 0x313, 0x300, 0x345, 0x0, 0x397, 0x313, 0x301, 0x0, + 0x397, 0x313, 0x301, 0x345, 0x0, 0x397, 0x313, 0x342, 0x0, + 0x397, 0x313, 0x342, 0x345, 0x0, 0x397, 0x313, 0x345, 0x0, + 0x397, 0x314, 0x0, 0x397, 0x314, 0x300, 0x0, 0x397, 0x314, + 0x300, 0x345, 0x0, 0x397, 0x314, 0x301, 0x0, 0x397, 0x314, + 0x301, 0x345, 0x0, 0x397, 0x314, 0x342, 0x0, 0x397, 0x314, + 0x342, 0x345, 0x0, 0x397, 0x314, 0x345, 0x0, 0x397, 0x345, 0x0, + 0x398, 0x0, 0x399, 0x0, 0x399, 0x300, 0x0, 0x399, 0x301, 0x0, + 0x399, 0x304, 0x0, 0x399, 0x306, 0x0, 0x399, 0x308, 0x0, 0x399, + 0x313, 0x0, 0x399, 0x313, 0x300, 0x0, 0x399, 0x313, 0x301, 0x0, + 0x399, 0x313, 0x342, 0x0, 0x399, 0x314, 0x0, 0x399, 0x314, + 0x300, 0x0, 0x399, 0x314, 0x301, 0x0, 0x399, 0x314, 0x342, 0x0, + 0x39a, 0x0, 0x39b, 0x0, 0x39c, 0x0, 0x39d, 0x0, 0x39e, 0x0, + 0x39f, 0x0, 0x39f, 0x300, 0x0, 0x39f, 0x301, 0x0, 0x39f, 0x313, + 0x0, 0x39f, 0x313, 0x300, 0x0, 0x39f, 0x313, 0x301, 0x0, 0x39f, + 0x314, 0x0, 0x39f, 0x314, 0x300, 0x0, 0x39f, 0x314, 0x301, 0x0, + 0x3a0, 0x0, 0x3a1, 0x0, 0x3a1, 0x314, 0x0, 0x3a3, 0x0, 0x3a4, + 0x0, 0x3a5, 0x0, 0x3a5, 0x300, 0x0, 0x3a5, 0x301, 0x0, 0x3a5, + 0x304, 0x0, 0x3a5, 0x306, 0x0, 0x3a5, 0x308, 0x0, 0x3a5, 0x314, + 0x0, 0x3a5, 0x314, 0x300, 0x0, 0x3a5, 0x314, 0x301, 0x0, 0x3a5, + 0x314, 0x342, 0x0, 0x3a6, 0x0, 0x3a7, 0x0, 0x3a8, 0x0, 0x3a9, + 0x0, 0x3a9, 0x300, 0x0, 0x3a9, 0x301, 0x0, 0x3a9, 0x313, 0x0, + 0x3a9, 0x313, 0x300, 0x0, 0x3a9, 0x313, 0x300, 0x345, 0x0, + 0x3a9, 0x313, 0x301, 0x0, 0x3a9, 0x313, 0x301, 0x345, 0x0, + 0x3a9, 0x313, 0x342, 0x0, 0x3a9, 0x313, 0x342, 0x345, 0x0, + 0x3a9, 0x313, 0x345, 0x0, 0x3a9, 0x314, 0x0, 0x3a9, 0x314, + 0x300, 0x0, 0x3a9, 0x314, 0x300, 0x345, 0x0, 0x3a9, 0x314, + 0x301, 0x0, 0x3a9, 0x314, 0x301, 0x345, 0x0, 0x3a9, 0x314, + 0x342, 0x0, 0x3a9, 0x314, 0x342, 0x345, 0x0, 0x3a9, 0x314, + 0x345, 0x0, 0x3a9, 0x345, 0x0, 0x3b1, 0x0, 0x3b1, 0x300, 0x0, + 0x3b1, 0x300, 0x345, 0x0, 0x3b1, 0x301, 0x0, 0x3b1, 0x301, + 0x345, 0x0, 0x3b1, 0x304, 0x0, 0x3b1, 0x306, 0x0, 0x3b1, 0x313, + 0x0, 0x3b1, 0x313, 0x300, 0x0, 0x3b1, 0x313, 0x300, 0x345, 0x0, + 0x3b1, 0x313, 0x301, 0x0, 0x3b1, 0x313, 0x301, 0x345, 0x0, + 0x3b1, 0x313, 0x342, 0x0, 0x3b1, 0x313, 0x342, 0x345, 0x0, + 0x3b1, 0x313, 0x345, 0x0, 0x3b1, 0x314, 0x0, 0x3b1, 0x314, + 0x300, 0x0, 0x3b1, 0x314, 0x300, 0x345, 0x0, 0x3b1, 0x314, + 0x301, 0x0, 0x3b1, 0x314, 0x301, 0x345, 0x0, 0x3b1, 0x314, + 0x342, 0x0, 0x3b1, 0x314, 0x342, 0x345, 0x0, 0x3b1, 0x314, + 0x345, 0x0, 0x3b1, 0x342, 0x0, 0x3b1, 0x342, 0x345, 0x0, 0x3b1, + 0x345, 0x0, 0x3b2, 0x0, 0x3b3, 0x0, 0x3b4, 0x0, 0x3b5, 0x0, + 0x3b5, 0x300, 0x0, 0x3b5, 0x301, 0x0, 0x3b5, 0x313, 0x0, 0x3b5, + 0x313, 0x300, 0x0, 0x3b5, 0x313, 0x301, 0x0, 0x3b5, 0x314, 0x0, + 0x3b5, 0x314, 0x300, 0x0, 0x3b5, 0x314, 0x301, 0x0, 0x3b6, 0x0, + 0x3b7, 0x0, 0x3b7, 0x300, 0x0, 0x3b7, 0x300, 0x345, 0x0, 0x3b7, + 0x301, 0x0, 0x3b7, 0x301, 0x345, 0x0, 0x3b7, 0x313, 0x0, 0x3b7, + 0x313, 0x300, 0x0, 0x3b7, 0x313, 0x300, 0x345, 0x0, 0x3b7, + 0x313, 0x301, 0x0, 0x3b7, 0x313, 0x301, 0x345, 0x0, 0x3b7, + 0x313, 0x342, 0x0, 0x3b7, 0x313, 0x342, 0x345, 0x0, 0x3b7, + 0x313, 0x345, 0x0, 0x3b7, 0x314, 0x0, 0x3b7, 0x314, 0x300, 0x0, + 0x3b7, 0x314, 0x300, 0x345, 0x0, 0x3b7, 0x314, 0x301, 0x0, + 0x3b7, 0x314, 0x301, 0x345, 0x0, 0x3b7, 0x314, 0x342, 0x0, + 0x3b7, 0x314, 0x342, 0x345, 0x0, 0x3b7, 0x314, 0x345, 0x0, + 0x3b7, 0x342, 0x0, 0x3b7, 0x342, 0x345, 0x0, 0x3b7, 0x345, 0x0, + 0x3b8, 0x0, 0x3b9, 0x0, 0x3b9, 0x300, 0x0, 0x3b9, 0x301, 0x0, + 0x3b9, 0x304, 0x0, 0x3b9, 0x306, 0x0, 0x3b9, 0x308, 0x0, 0x3b9, + 0x308, 0x300, 0x0, 0x3b9, 0x308, 0x301, 0x0, 0x3b9, 0x308, + 0x342, 0x0, 0x3b9, 0x313, 0x0, 0x3b9, 0x313, 0x300, 0x0, 0x3b9, + 0x313, 0x301, 0x0, 0x3b9, 0x313, 0x342, 0x0, 0x3b9, 0x314, 0x0, + 0x3b9, 0x314, 0x300, 0x0, 0x3b9, 0x314, 0x301, 0x0, 0x3b9, + 0x314, 0x342, 0x0, 0x3b9, 0x342, 0x0, 0x3ba, 0x0, 0x3bb, 0x0, + 0x3bc, 0x0, 0x3bc, 0x41, 0x0, 0x3bc, 0x46, 0x0, 0x3bc, 0x56, + 0x0, 0x3bc, 0x57, 0x0, 0x3bc, 0x67, 0x0, 0x3bc, 0x6c, 0x0, + 0x3bc, 0x6d, 0x0, 0x3bc, 0x73, 0x0, 0x3bd, 0x0, 0x3be, 0x0, + 0x3bf, 0x0, 0x3bf, 0x300, 0x0, 0x3bf, 0x301, 0x0, 0x3bf, 0x313, + 0x0, 0x3bf, 0x313, 0x300, 0x0, 0x3bf, 0x313, 0x301, 0x0, 0x3bf, + 0x314, 0x0, 0x3bf, 0x314, 0x300, 0x0, 0x3bf, 0x314, 0x301, 0x0, + 0x3c0, 0x0, 0x3c1, 0x0, 0x3c1, 0x313, 0x0, 0x3c1, 0x314, 0x0, + 0x3c2, 0x0, 0x3c3, 0x0, 0x3c4, 0x0, 0x3c5, 0x0, 0x3c5, 0x300, + 0x0, 0x3c5, 0x301, 0x0, 0x3c5, 0x304, 0x0, 0x3c5, 0x306, 0x0, + 0x3c5, 0x308, 0x0, 0x3c5, 0x308, 0x300, 0x0, 0x3c5, 0x308, + 0x301, 0x0, 0x3c5, 0x308, 0x342, 0x0, 0x3c5, 0x313, 0x0, 0x3c5, + 0x313, 0x300, 0x0, 0x3c5, 0x313, 0x301, 0x0, 0x3c5, 0x313, + 0x342, 0x0, 0x3c5, 0x314, 0x0, 0x3c5, 0x314, 0x300, 0x0, 0x3c5, + 0x314, 0x301, 0x0, 0x3c5, 0x314, 0x342, 0x0, 0x3c5, 0x342, 0x0, + 0x3c6, 0x0, 0x3c7, 0x0, 0x3c8, 0x0, 0x3c9, 0x0, 0x3c9, 0x300, + 0x0, 0x3c9, 0x300, 0x345, 0x0, 0x3c9, 0x301, 0x0, 0x3c9, 0x301, + 0x345, 0x0, 0x3c9, 0x313, 0x0, 0x3c9, 0x313, 0x300, 0x0, 0x3c9, + 0x313, 0x300, 0x345, 0x0, 0x3c9, 0x313, 0x301, 0x0, 0x3c9, + 0x313, 0x301, 0x345, 0x0, 0x3c9, 0x313, 0x342, 0x0, 0x3c9, + 0x313, 0x342, 0x345, 0x0, 0x3c9, 0x313, 0x345, 0x0, 0x3c9, + 0x314, 0x0, 0x3c9, 0x314, 0x300, 0x0, 0x3c9, 0x314, 0x300, + 0x345, 0x0, 0x3c9, 0x314, 0x301, 0x0, 0x3c9, 0x314, 0x301, + 0x345, 0x0, 0x3c9, 0x314, 0x342, 0x0, 0x3c9, 0x314, 0x342, + 0x345, 0x0, 0x3c9, 0x314, 0x345, 0x0, 0x3c9, 0x342, 0x0, 0x3c9, + 0x342, 0x345, 0x0, 0x3c9, 0x345, 0x0, 0x3dc, 0x0, 0x3dd, 0x0, + 0x406, 0x308, 0x0, 0x410, 0x306, 0x0, 0x410, 0x308, 0x0, 0x413, + 0x301, 0x0, 0x415, 0x300, 0x0, 0x415, 0x306, 0x0, 0x415, 0x308, + 0x0, 0x416, 0x306, 0x0, 0x416, 0x308, 0x0, 0x417, 0x308, 0x0, + 0x418, 0x300, 0x0, 0x418, 0x304, 0x0, 0x418, 0x306, 0x0, 0x418, + 0x308, 0x0, 0x41a, 0x301, 0x0, 0x41e, 0x308, 0x0, 0x423, 0x304, + 0x0, 0x423, 0x306, 0x0, 0x423, 0x308, 0x0, 0x423, 0x30b, 0x0, + 0x427, 0x308, 0x0, 0x42b, 0x308, 0x0, 0x42d, 0x308, 0x0, 0x430, + 0x306, 0x0, 0x430, 0x308, 0x0, 0x433, 0x301, 0x0, 0x435, 0x300, + 0x0, 0x435, 0x306, 0x0, 0x435, 0x308, 0x0, 0x436, 0x306, 0x0, + 0x436, 0x308, 0x0, 0x437, 0x308, 0x0, 0x438, 0x300, 0x0, 0x438, + 0x304, 0x0, 0x438, 0x306, 0x0, 0x438, 0x308, 0x0, 0x43a, 0x301, + 0x0, 0x43d, 0x0, 0x43e, 0x308, 0x0, 0x443, 0x304, 0x0, 0x443, + 0x306, 0x0, 0x443, 0x308, 0x0, 0x443, 0x30b, 0x0, 0x447, 0x308, + 0x0, 0x44b, 0x308, 0x0, 0x44d, 0x308, 0x0, 0x456, 0x308, 0x0, + 0x474, 0x30f, 0x0, 0x475, 0x30f, 0x0, 0x4d8, 0x308, 0x0, 0x4d9, + 0x308, 0x0, 0x4e8, 0x308, 0x0, 0x4e9, 0x308, 0x0, 0x565, 0x582, + 0x0, 0x574, 0x565, 0x0, 0x574, 0x56b, 0x0, 0x574, 0x56d, 0x0, + 0x574, 0x576, 0x0, 0x57e, 0x576, 0x0, 0x5d0, 0x0, 0x5d0, 0x5b7, + 0x0, 0x5d0, 0x5b8, 0x0, 0x5d0, 0x5bc, 0x0, 0x5d0, 0x5dc, 0x0, + 0x5d1, 0x0, 0x5d1, 0x5bc, 0x0, 0x5d1, 0x5bf, 0x0, 0x5d2, 0x0, + 0x5d2, 0x5bc, 0x0, 0x5d3, 0x0, 0x5d3, 0x5bc, 0x0, 0x5d4, 0x0, + 0x5d4, 0x5bc, 0x0, 0x5d5, 0x5b9, 0x0, 0x5d5, 0x5bc, 0x0, 0x5d6, + 0x5bc, 0x0, 0x5d8, 0x5bc, 0x0, 0x5d9, 0x5b4, 0x0, 0x5d9, 0x5bc, + 0x0, 0x5da, 0x5bc, 0x0, 0x5db, 0x0, 0x5db, 0x5bc, 0x0, 0x5db, + 0x5bf, 0x0, 0x5dc, 0x0, 0x5dc, 0x5bc, 0x0, 0x5dd, 0x0, 0x5de, + 0x5bc, 0x0, 0x5e0, 0x5bc, 0x0, 0x5e1, 0x5bc, 0x0, 0x5e2, 0x0, + 0x5e3, 0x5bc, 0x0, 0x5e4, 0x5bc, 0x0, 0x5e4, 0x5bf, 0x0, 0x5e6, + 0x5bc, 0x0, 0x5e7, 0x5bc, 0x0, 0x5e8, 0x0, 0x5e8, 0x5bc, 0x0, + 0x5e9, 0x5bc, 0x0, 0x5e9, 0x5bc, 0x5c1, 0x0, 0x5e9, 0x5bc, + 0x5c2, 0x0, 0x5e9, 0x5c1, 0x0, 0x5e9, 0x5c2, 0x0, 0x5ea, 0x0, + 0x5ea, 0x5bc, 0x0, 0x5f2, 0x5b7, 0x0, 0x621, 0x0, 0x627, 0x0, + 0x627, 0x643, 0x628, 0x631, 0x0, 0x627, 0x644, 0x644, 0x647, + 0x0, 0x627, 0x64b, 0x0, 0x627, 0x653, 0x0, 0x627, 0x654, 0x0, + 0x627, 0x655, 0x0, 0x627, 0x674, 0x0, 0x628, 0x0, 0x628, 0x62c, + 0x0, 0x628, 0x62d, 0x0, 0x628, 0x62d, 0x64a, 0x0, 0x628, 0x62e, + 0x0, 0x628, 0x62e, 0x64a, 0x0, 0x628, 0x631, 0x0, 0x628, 0x632, + 0x0, 0x628, 0x645, 0x0, 0x628, 0x646, 0x0, 0x628, 0x647, 0x0, + 0x628, 0x649, 0x0, 0x628, 0x64a, 0x0, 0x629, 0x0, 0x62a, 0x0, + 0x62a, 0x62c, 0x0, 0x62a, 0x62c, 0x645, 0x0, 0x62a, 0x62c, + 0x649, 0x0, 0x62a, 0x62c, 0x64a, 0x0, 0x62a, 0x62d, 0x0, 0x62a, + 0x62d, 0x62c, 0x0, 0x62a, 0x62d, 0x645, 0x0, 0x62a, 0x62e, 0x0, + 0x62a, 0x62e, 0x645, 0x0, 0x62a, 0x62e, 0x649, 0x0, 0x62a, + 0x62e, 0x64a, 0x0, 0x62a, 0x631, 0x0, 0x62a, 0x632, 0x0, 0x62a, + 0x645, 0x0, 0x62a, 0x645, 0x62c, 0x0, 0x62a, 0x645, 0x62d, 0x0, + 0x62a, 0x645, 0x62e, 0x0, 0x62a, 0x645, 0x649, 0x0, 0x62a, + 0x645, 0x64a, 0x0, 0x62a, 0x646, 0x0, 0x62a, 0x647, 0x0, 0x62a, + 0x649, 0x0, 0x62a, 0x64a, 0x0, 0x62b, 0x0, 0x62b, 0x62c, 0x0, + 0x62b, 0x631, 0x0, 0x62b, 0x632, 0x0, 0x62b, 0x645, 0x0, 0x62b, + 0x646, 0x0, 0x62b, 0x647, 0x0, 0x62b, 0x649, 0x0, 0x62b, 0x64a, + 0x0, 0x62c, 0x0, 0x62c, 0x62d, 0x0, 0x62c, 0x62d, 0x649, 0x0, + 0x62c, 0x62d, 0x64a, 0x0, 0x62c, 0x644, 0x20, 0x62c, 0x644, + 0x627, 0x644, 0x647, 0x0, 0x62c, 0x645, 0x0, 0x62c, 0x645, + 0x62d, 0x0, 0x62c, 0x645, 0x649, 0x0, 0x62c, 0x645, 0x64a, 0x0, + 0x62c, 0x649, 0x0, 0x62c, 0x64a, 0x0, 0x62d, 0x0, 0x62d, 0x62c, + 0x0, 0x62d, 0x62c, 0x64a, 0x0, 0x62d, 0x645, 0x0, 0x62d, 0x645, + 0x649, 0x0, 0x62d, 0x645, 0x64a, 0x0, 0x62d, 0x649, 0x0, 0x62d, + 0x64a, 0x0, 0x62e, 0x0, 0x62e, 0x62c, 0x0, 0x62e, 0x62d, 0x0, + 0x62e, 0x645, 0x0, 0x62e, 0x649, 0x0, 0x62e, 0x64a, 0x0, 0x62f, + 0x0, 0x630, 0x0, 0x630, 0x670, 0x0, 0x631, 0x0, 0x631, 0x633, + 0x648, 0x644, 0x0, 0x631, 0x670, 0x0, 0x631, 0x6cc, 0x627, + 0x644, 0x0, 0x632, 0x0, 0x633, 0x0, 0x633, 0x62c, 0x0, 0x633, + 0x62c, 0x62d, 0x0, 0x633, 0x62c, 0x649, 0x0, 0x633, 0x62d, 0x0, + 0x633, 0x62d, 0x62c, 0x0, 0x633, 0x62e, 0x0, 0x633, 0x62e, + 0x649, 0x0, 0x633, 0x62e, 0x64a, 0x0, 0x633, 0x631, 0x0, 0x633, + 0x645, 0x0, 0x633, 0x645, 0x62c, 0x0, 0x633, 0x645, 0x62d, 0x0, + 0x633, 0x645, 0x645, 0x0, 0x633, 0x647, 0x0, 0x633, 0x649, 0x0, + 0x633, 0x64a, 0x0, 0x634, 0x0, 0x634, 0x62c, 0x0, 0x634, 0x62c, + 0x64a, 0x0, 0x634, 0x62d, 0x0, 0x634, 0x62d, 0x645, 0x0, 0x634, + 0x62d, 0x64a, 0x0, 0x634, 0x62e, 0x0, 0x634, 0x631, 0x0, 0x634, + 0x645, 0x0, 0x634, 0x645, 0x62e, 0x0, 0x634, 0x645, 0x645, 0x0, + 0x634, 0x647, 0x0, 0x634, 0x649, 0x0, 0x634, 0x64a, 0x0, 0x635, + 0x0, 0x635, 0x62d, 0x0, 0x635, 0x62d, 0x62d, 0x0, 0x635, 0x62d, + 0x64a, 0x0, 0x635, 0x62e, 0x0, 0x635, 0x631, 0x0, 0x635, 0x644, + 0x639, 0x645, 0x0, 0x635, 0x644, 0x649, 0x0, 0x635, 0x644, + 0x649, 0x20, 0x627, 0x644, 0x644, 0x647, 0x20, 0x639, 0x644, + 0x64a, 0x647, 0x20, 0x648, 0x633, 0x644, 0x645, 0x0, 0x635, + 0x644, 0x6d2, 0x0, 0x635, 0x645, 0x0, 0x635, 0x645, 0x645, 0x0, + 0x635, 0x649, 0x0, 0x635, 0x64a, 0x0, 0x636, 0x0, 0x636, 0x62c, + 0x0, 0x636, 0x62d, 0x0, 0x636, 0x62d, 0x649, 0x0, 0x636, 0x62d, + 0x64a, 0x0, 0x636, 0x62e, 0x0, 0x636, 0x62e, 0x645, 0x0, 0x636, + 0x631, 0x0, 0x636, 0x645, 0x0, 0x636, 0x649, 0x0, 0x636, 0x64a, + 0x0, 0x637, 0x0, 0x637, 0x62d, 0x0, 0x637, 0x645, 0x0, 0x637, + 0x645, 0x62d, 0x0, 0x637, 0x645, 0x645, 0x0, 0x637, 0x645, + 0x64a, 0x0, 0x637, 0x649, 0x0, 0x637, 0x64a, 0x0, 0x638, 0x0, + 0x638, 0x645, 0x0, 0x639, 0x0, 0x639, 0x62c, 0x0, 0x639, 0x62c, + 0x645, 0x0, 0x639, 0x644, 0x64a, 0x647, 0x0, 0x639, 0x645, 0x0, + 0x639, 0x645, 0x645, 0x0, 0x639, 0x645, 0x649, 0x0, 0x639, + 0x645, 0x64a, 0x0, 0x639, 0x649, 0x0, 0x639, 0x64a, 0x0, 0x63a, + 0x0, 0x63a, 0x62c, 0x0, 0x63a, 0x645, 0x0, 0x63a, 0x645, 0x645, + 0x0, 0x63a, 0x645, 0x649, 0x0, 0x63a, 0x645, 0x64a, 0x0, 0x63a, + 0x649, 0x0, 0x63a, 0x64a, 0x0, 0x640, 0x64b, 0x0, 0x640, 0x64e, + 0x0, 0x640, 0x64e, 0x651, 0x0, 0x640, 0x64f, 0x0, 0x640, 0x64f, + 0x651, 0x0, 0x640, 0x650, 0x0, 0x640, 0x650, 0x651, 0x0, 0x640, + 0x651, 0x0, 0x640, 0x652, 0x0, 0x641, 0x0, 0x641, 0x62c, 0x0, + 0x641, 0x62d, 0x0, 0x641, 0x62e, 0x0, 0x641, 0x62e, 0x645, 0x0, + 0x641, 0x645, 0x0, 0x641, 0x645, 0x64a, 0x0, 0x641, 0x649, 0x0, + 0x641, 0x64a, 0x0, 0x642, 0x0, 0x642, 0x62d, 0x0, 0x642, 0x644, + 0x6d2, 0x0, 0x642, 0x645, 0x0, 0x642, 0x645, 0x62d, 0x0, 0x642, + 0x645, 0x645, 0x0, 0x642, 0x645, 0x64a, 0x0, 0x642, 0x649, 0x0, + 0x642, 0x64a, 0x0, 0x643, 0x0, 0x643, 0x627, 0x0, 0x643, 0x62c, + 0x0, 0x643, 0x62d, 0x0, 0x643, 0x62e, 0x0, 0x643, 0x644, 0x0, + 0x643, 0x645, 0x0, 0x643, 0x645, 0x645, 0x0, 0x643, 0x645, + 0x64a, 0x0, 0x643, 0x649, 0x0, 0x643, 0x64a, 0x0, 0x644, 0x0, + 0x644, 0x627, 0x0, 0x644, 0x627, 0x653, 0x0, 0x644, 0x627, + 0x654, 0x0, 0x644, 0x627, 0x655, 0x0, 0x644, 0x62c, 0x0, 0x644, + 0x62c, 0x62c, 0x0, 0x644, 0x62c, 0x645, 0x0, 0x644, 0x62c, + 0x64a, 0x0, 0x644, 0x62d, 0x0, 0x644, 0x62d, 0x645, 0x0, 0x644, + 0x62d, 0x649, 0x0, 0x644, 0x62d, 0x64a, 0x0, 0x644, 0x62e, 0x0, + 0x644, 0x62e, 0x645, 0x0, 0x644, 0x645, 0x0, 0x644, 0x645, + 0x62d, 0x0, 0x644, 0x645, 0x64a, 0x0, 0x644, 0x647, 0x0, 0x644, + 0x649, 0x0, 0x644, 0x64a, 0x0, 0x645, 0x0, 0x645, 0x627, 0x0, + 0x645, 0x62c, 0x0, 0x645, 0x62c, 0x62d, 0x0, 0x645, 0x62c, + 0x62e, 0x0, 0x645, 0x62c, 0x645, 0x0, 0x645, 0x62c, 0x64a, 0x0, + 0x645, 0x62d, 0x0, 0x645, 0x62d, 0x62c, 0x0, 0x645, 0x62d, + 0x645, 0x0, 0x645, 0x62d, 0x645, 0x62f, 0x0, 0x645, 0x62d, + 0x64a, 0x0, 0x645, 0x62e, 0x0, 0x645, 0x62e, 0x62c, 0x0, 0x645, + 0x62e, 0x645, 0x0, 0x645, 0x62e, 0x64a, 0x0, 0x645, 0x645, 0x0, + 0x645, 0x645, 0x64a, 0x0, 0x645, 0x649, 0x0, 0x645, 0x64a, 0x0, + 0x646, 0x0, 0x646, 0x62c, 0x0, 0x646, 0x62c, 0x62d, 0x0, 0x646, + 0x62c, 0x645, 0x0, 0x646, 0x62c, 0x649, 0x0, 0x646, 0x62c, + 0x64a, 0x0, 0x646, 0x62d, 0x0, 0x646, 0x62d, 0x645, 0x0, 0x646, + 0x62d, 0x649, 0x0, 0x646, 0x62d, 0x64a, 0x0, 0x646, 0x62e, 0x0, + 0x646, 0x631, 0x0, 0x646, 0x632, 0x0, 0x646, 0x645, 0x0, 0x646, + 0x645, 0x649, 0x0, 0x646, 0x645, 0x64a, 0x0, 0x646, 0x646, 0x0, + 0x646, 0x647, 0x0, 0x646, 0x649, 0x0, 0x646, 0x64a, 0x0, 0x647, + 0x0, 0x647, 0x62c, 0x0, 0x647, 0x645, 0x0, 0x647, 0x645, 0x62c, + 0x0, 0x647, 0x645, 0x645, 0x0, 0x647, 0x649, 0x0, 0x647, 0x64a, + 0x0, 0x647, 0x670, 0x0, 0x648, 0x0, 0x648, 0x633, 0x644, 0x645, + 0x0, 0x648, 0x654, 0x0, 0x648, 0x674, 0x0, 0x649, 0x0, 0x649, + 0x670, 0x0, 0x64a, 0x0, 0x64a, 0x62c, 0x0, 0x64a, 0x62c, 0x64a, + 0x0, 0x64a, 0x62d, 0x0, 0x64a, 0x62d, 0x64a, 0x0, 0x64a, 0x62e, + 0x0, 0x64a, 0x631, 0x0, 0x64a, 0x632, 0x0, 0x64a, 0x645, 0x0, + 0x64a, 0x645, 0x645, 0x0, 0x64a, 0x645, 0x64a, 0x0, 0x64a, + 0x646, 0x0, 0x64a, 0x647, 0x0, 0x64a, 0x649, 0x0, 0x64a, 0x64a, + 0x0, 0x64a, 0x654, 0x0, 0x64a, 0x654, 0x627, 0x0, 0x64a, 0x654, + 0x62c, 0x0, 0x64a, 0x654, 0x62d, 0x0, 0x64a, 0x654, 0x62e, 0x0, + 0x64a, 0x654, 0x631, 0x0, 0x64a, 0x654, 0x632, 0x0, 0x64a, + 0x654, 0x645, 0x0, 0x64a, 0x654, 0x646, 0x0, 0x64a, 0x654, + 0x647, 0x0, 0x64a, 0x654, 0x648, 0x0, 0x64a, 0x654, 0x649, 0x0, + 0x64a, 0x654, 0x64a, 0x0, 0x64a, 0x654, 0x6c6, 0x0, 0x64a, + 0x654, 0x6c7, 0x0, 0x64a, 0x654, 0x6c8, 0x0, 0x64a, 0x654, + 0x6d0, 0x0, 0x64a, 0x654, 0x6d5, 0x0, 0x64a, 0x674, 0x0, 0x66e, + 0x0, 0x66f, 0x0, 0x671, 0x0, 0x679, 0x0, 0x67a, 0x0, 0x67b, + 0x0, 0x67e, 0x0, 0x67f, 0x0, 0x680, 0x0, 0x683, 0x0, 0x684, + 0x0, 0x686, 0x0, 0x687, 0x0, 0x688, 0x0, 0x68c, 0x0, 0x68d, + 0x0, 0x68e, 0x0, 0x691, 0x0, 0x698, 0x0, 0x6a1, 0x0, 0x6a4, + 0x0, 0x6a6, 0x0, 0x6a9, 0x0, 0x6ad, 0x0, 0x6af, 0x0, 0x6b1, + 0x0, 0x6b3, 0x0, 0x6ba, 0x0, 0x6bb, 0x0, 0x6be, 0x0, 0x6c1, + 0x0, 0x6c1, 0x654, 0x0, 0x6c5, 0x0, 0x6c6, 0x0, 0x6c7, 0x0, + 0x6c7, 0x674, 0x0, 0x6c8, 0x0, 0x6c9, 0x0, 0x6cb, 0x0, 0x6cc, + 0x0, 0x6d0, 0x0, 0x6d2, 0x0, 0x6d2, 0x654, 0x0, 0x6d5, 0x654, + 0x0, 0x915, 0x93c, 0x0, 0x916, 0x93c, 0x0, 0x917, 0x93c, 0x0, + 0x91c, 0x93c, 0x0, 0x921, 0x93c, 0x0, 0x922, 0x93c, 0x0, 0x928, + 0x93c, 0x0, 0x92b, 0x93c, 0x0, 0x92f, 0x93c, 0x0, 0x930, 0x93c, + 0x0, 0x933, 0x93c, 0x0, 0x9a1, 0x9bc, 0x0, 0x9a2, 0x9bc, 0x0, + 0x9af, 0x9bc, 0x0, 0x9c7, 0x9be, 0x0, 0x9c7, 0x9d7, 0x0, 0xa16, + 0xa3c, 0x0, 0xa17, 0xa3c, 0x0, 0xa1c, 0xa3c, 0x0, 0xa2b, 0xa3c, + 0x0, 0xa32, 0xa3c, 0x0, 0xa38, 0xa3c, 0x0, 0xb21, 0xb3c, 0x0, + 0xb22, 0xb3c, 0x0, 0xb47, 0xb3e, 0x0, 0xb47, 0xb56, 0x0, 0xb47, + 0xb57, 0x0, 0xb92, 0xbd7, 0x0, 0xbc6, 0xbbe, 0x0, 0xbc6, 0xbd7, + 0x0, 0xbc7, 0xbbe, 0x0, 0xc46, 0xc56, 0x0, 0xcbf, 0xcd5, 0x0, + 0xcc6, 0xcc2, 0x0, 0xcc6, 0xcc2, 0xcd5, 0x0, 0xcc6, 0xcd5, 0x0, + 0xcc6, 0xcd6, 0x0, 0xd46, 0xd3e, 0x0, 0xd46, 0xd57, 0x0, 0xd47, + 0xd3e, 0x0, 0xdd9, 0xdca, 0x0, 0xdd9, 0xdcf, 0x0, 0xdd9, 0xdcf, + 0xdca, 0x0, 0xdd9, 0xddf, 0x0, 0xe4d, 0xe32, 0x0, 0xeab, 0xe99, + 0x0, 0xeab, 0xea1, 0x0, 0xecd, 0xeb2, 0x0, 0xf0b, 0x0, 0xf40, + 0xfb5, 0x0, 0xf42, 0xfb7, 0x0, 0xf4c, 0xfb7, 0x0, 0xf51, 0xfb7, + 0x0, 0xf56, 0xfb7, 0x0, 0xf5b, 0xfb7, 0x0, 0xf71, 0xf72, 0x0, + 0xf71, 0xf74, 0x0, 0xf71, 0xf80, 0x0, 0xf90, 0xfb5, 0x0, 0xf92, + 0xfb7, 0x0, 0xf9c, 0xfb7, 0x0, 0xfa1, 0xfb7, 0x0, 0xfa6, 0xfb7, + 0x0, 0xfab, 0xfb7, 0x0, 0xfb2, 0xf71, 0xf80, 0x0, 0xfb2, 0xf80, + 0x0, 0xfb3, 0xf71, 0xf80, 0x0, 0xfb3, 0xf80, 0x0, 0x1025, + 0x102e, 0x0, 0x10dc, 0x0, 0x1100, 0x0, 0x1100, 0x1161, 0x0, + 0x1101, 0x0, 0x1102, 0x0, 0x1102, 0x1161, 0x0, 0x1103, 0x0, + 0x1103, 0x1161, 0x0, 0x1104, 0x0, 0x1105, 0x0, 0x1105, 0x1161, + 0x0, 0x1106, 0x0, 0x1106, 0x1161, 0x0, 0x1107, 0x0, 0x1107, + 0x1161, 0x0, 0x1108, 0x0, 0x1109, 0x0, 0x1109, 0x1161, 0x0, + 0x110a, 0x0, 0x110b, 0x0, 0x110b, 0x1161, 0x0, 0x110b, 0x116e, + 0x0, 0x110c, 0x0, 0x110c, 0x1161, 0x0, 0x110c, 0x116e, 0x110b, + 0x1174, 0x0, 0x110d, 0x0, 0x110e, 0x0, 0x110e, 0x1161, 0x0, + 0x110e, 0x1161, 0x11b7, 0x1100, 0x1169, 0x0, 0x110f, 0x0, + 0x110f, 0x1161, 0x0, 0x1110, 0x0, 0x1110, 0x1161, 0x0, 0x1111, + 0x0, 0x1111, 0x1161, 0x0, 0x1112, 0x0, 0x1112, 0x1161, 0x0, + 0x1114, 0x0, 0x1115, 0x0, 0x111a, 0x0, 0x111c, 0x0, 0x111d, + 0x0, 0x111e, 0x0, 0x1120, 0x0, 0x1121, 0x0, 0x1122, 0x0, + 0x1123, 0x0, 0x1127, 0x0, 0x1129, 0x0, 0x112b, 0x0, 0x112c, + 0x0, 0x112d, 0x0, 0x112e, 0x0, 0x112f, 0x0, 0x1132, 0x0, + 0x1136, 0x0, 0x1140, 0x0, 0x1147, 0x0, 0x114c, 0x0, 0x1157, + 0x0, 0x1158, 0x0, 0x1159, 0x0, 0x1160, 0x0, 0x1161, 0x0, + 0x1162, 0x0, 0x1163, 0x0, 0x1164, 0x0, 0x1165, 0x0, 0x1166, + 0x0, 0x1167, 0x0, 0x1168, 0x0, 0x1169, 0x0, 0x116a, 0x0, + 0x116b, 0x0, 0x116c, 0x0, 0x116d, 0x0, 0x116e, 0x0, 0x116f, + 0x0, 0x1170, 0x0, 0x1171, 0x0, 0x1172, 0x0, 0x1173, 0x0, + 0x1174, 0x0, 0x1175, 0x0, 0x1184, 0x0, 0x1185, 0x0, 0x1188, + 0x0, 0x1191, 0x0, 0x1192, 0x0, 0x1194, 0x0, 0x119e, 0x0, + 0x11a1, 0x0, 0x11aa, 0x0, 0x11ac, 0x0, 0x11ad, 0x0, 0x11b0, + 0x0, 0x11b1, 0x0, 0x11b2, 0x0, 0x11b3, 0x0, 0x11b4, 0x0, + 0x11b5, 0x0, 0x11c7, 0x0, 0x11c8, 0x0, 0x11cc, 0x0, 0x11ce, + 0x0, 0x11d3, 0x0, 0x11d7, 0x0, 0x11d9, 0x0, 0x11dd, 0x0, + 0x11df, 0x0, 0x11f1, 0x0, 0x11f2, 0x0, 0x1b05, 0x1b35, 0x0, + 0x1b07, 0x1b35, 0x0, 0x1b09, 0x1b35, 0x0, 0x1b0b, 0x1b35, 0x0, + 0x1b0d, 0x1b35, 0x0, 0x1b11, 0x1b35, 0x0, 0x1b3a, 0x1b35, 0x0, + 0x1b3c, 0x1b35, 0x0, 0x1b3e, 0x1b35, 0x0, 0x1b3f, 0x1b35, 0x0, + 0x1b42, 0x1b35, 0x0, 0x1d02, 0x0, 0x1d16, 0x0, 0x1d17, 0x0, + 0x1d1c, 0x0, 0x1d1d, 0x0, 0x1d25, 0x0, 0x1d7b, 0x0, 0x1d85, + 0x0, 0x2010, 0x0, 0x2013, 0x0, 0x2014, 0x0, 0x2032, 0x2032, + 0x0, 0x2032, 0x2032, 0x2032, 0x0, 0x2032, 0x2032, 0x2032, + 0x2032, 0x0, 0x2035, 0x2035, 0x0, 0x2035, 0x2035, 0x2035, 0x0, + 0x20a9, 0x0, 0x2190, 0x0, 0x2190, 0x338, 0x0, 0x2191, 0x0, + 0x2192, 0x0, 0x2192, 0x338, 0x0, 0x2193, 0x0, 0x2194, 0x338, + 0x0, 0x21d0, 0x338, 0x0, 0x21d2, 0x338, 0x0, 0x21d4, 0x338, + 0x0, 0x2202, 0x0, 0x2203, 0x338, 0x0, 0x2207, 0x0, 0x2208, + 0x338, 0x0, 0x220b, 0x338, 0x0, 0x2211, 0x0, 0x2212, 0x0, + 0x2223, 0x338, 0x0, 0x2225, 0x338, 0x0, 0x222b, 0x222b, 0x0, + 0x222b, 0x222b, 0x222b, 0x0, 0x222b, 0x222b, 0x222b, 0x222b, + 0x0, 0x222e, 0x222e, 0x0, 0x222e, 0x222e, 0x222e, 0x0, 0x223c, + 0x338, 0x0, 0x2243, 0x338, 0x0, 0x2245, 0x338, 0x0, 0x2248, + 0x338, 0x0, 0x224d, 0x338, 0x0, 0x2261, 0x338, 0x0, 0x2264, + 0x338, 0x0, 0x2265, 0x338, 0x0, 0x2272, 0x338, 0x0, 0x2273, + 0x338, 0x0, 0x2276, 0x338, 0x0, 0x2277, 0x338, 0x0, 0x227a, + 0x338, 0x0, 0x227b, 0x338, 0x0, 0x227c, 0x338, 0x0, 0x227d, + 0x338, 0x0, 0x2282, 0x338, 0x0, 0x2283, 0x338, 0x0, 0x2286, + 0x338, 0x0, 0x2287, 0x338, 0x0, 0x2291, 0x338, 0x0, 0x2292, + 0x338, 0x0, 0x22a2, 0x338, 0x0, 0x22a8, 0x338, 0x0, 0x22a9, + 0x338, 0x0, 0x22ab, 0x338, 0x0, 0x22b2, 0x338, 0x0, 0x22b3, + 0x338, 0x0, 0x22b4, 0x338, 0x0, 0x22b5, 0x338, 0x0, 0x2502, + 0x0, 0x25a0, 0x0, 0x25cb, 0x0, 0x2985, 0x0, 0x2986, 0x0, + 0x2add, 0x338, 0x0, 0x2d61, 0x0, 0x3001, 0x0, 0x3002, 0x0, + 0x3008, 0x0, 0x3009, 0x0, 0x300a, 0x0, 0x300b, 0x0, 0x300c, + 0x0, 0x300d, 0x0, 0x300e, 0x0, 0x300f, 0x0, 0x3010, 0x0, + 0x3011, 0x0, 0x3012, 0x0, 0x3014, 0x0, 0x3014, 0x53, 0x3015, + 0x0, 0x3014, 0x4e09, 0x3015, 0x0, 0x3014, 0x4e8c, 0x3015, 0x0, + 0x3014, 0x52dd, 0x3015, 0x0, 0x3014, 0x5b89, 0x3015, 0x0, + 0x3014, 0x6253, 0x3015, 0x0, 0x3014, 0x6557, 0x3015, 0x0, + 0x3014, 0x672c, 0x3015, 0x0, 0x3014, 0x70b9, 0x3015, 0x0, + 0x3014, 0x76d7, 0x3015, 0x0, 0x3015, 0x0, 0x3016, 0x0, 0x3017, + 0x0, 0x3046, 0x3099, 0x0, 0x304b, 0x3099, 0x0, 0x304d, 0x3099, + 0x0, 0x304f, 0x3099, 0x0, 0x3051, 0x3099, 0x0, 0x3053, 0x3099, + 0x0, 0x3055, 0x3099, 0x0, 0x3057, 0x3099, 0x0, 0x3059, 0x3099, + 0x0, 0x305b, 0x3099, 0x0, 0x305d, 0x3099, 0x0, 0x305f, 0x3099, + 0x0, 0x3061, 0x3099, 0x0, 0x3064, 0x3099, 0x0, 0x3066, 0x3099, + 0x0, 0x3068, 0x3099, 0x0, 0x306f, 0x3099, 0x0, 0x306f, 0x309a, + 0x0, 0x3072, 0x3099, 0x0, 0x3072, 0x309a, 0x0, 0x3075, 0x3099, + 0x0, 0x3075, 0x309a, 0x0, 0x3078, 0x3099, 0x0, 0x3078, 0x309a, + 0x0, 0x307b, 0x304b, 0x0, 0x307b, 0x3099, 0x0, 0x307b, 0x309a, + 0x0, 0x3088, 0x308a, 0x0, 0x3099, 0x0, 0x309a, 0x0, 0x309d, + 0x3099, 0x0, 0x30a1, 0x0, 0x30a2, 0x0, 0x30a2, 0x30cf, 0x309a, + 0x30fc, 0x30c8, 0x0, 0x30a2, 0x30eb, 0x30d5, 0x30a1, 0x0, + 0x30a2, 0x30f3, 0x30d8, 0x309a, 0x30a2, 0x0, 0x30a2, 0x30fc, + 0x30eb, 0x0, 0x30a3, 0x0, 0x30a4, 0x0, 0x30a4, 0x30cb, 0x30f3, + 0x30af, 0x3099, 0x0, 0x30a4, 0x30f3, 0x30c1, 0x0, 0x30a5, 0x0, + 0x30a6, 0x0, 0x30a6, 0x3099, 0x0, 0x30a6, 0x30a9, 0x30f3, 0x0, + 0x30a7, 0x0, 0x30a8, 0x0, 0x30a8, 0x30b9, 0x30af, 0x30fc, + 0x30c8, 0x3099, 0x0, 0x30a8, 0x30fc, 0x30ab, 0x30fc, 0x0, + 0x30a9, 0x0, 0x30aa, 0x0, 0x30aa, 0x30f3, 0x30b9, 0x0, 0x30aa, + 0x30fc, 0x30e0, 0x0, 0x30ab, 0x0, 0x30ab, 0x3099, 0x0, 0x30ab, + 0x3099, 0x30ed, 0x30f3, 0x0, 0x30ab, 0x3099, 0x30f3, 0x30de, + 0x0, 0x30ab, 0x30a4, 0x30ea, 0x0, 0x30ab, 0x30e9, 0x30c3, + 0x30c8, 0x0, 0x30ab, 0x30ed, 0x30ea, 0x30fc, 0x0, 0x30ad, 0x0, + 0x30ad, 0x3099, 0x0, 0x30ad, 0x3099, 0x30ab, 0x3099, 0x0, + 0x30ad, 0x3099, 0x30cb, 0x30fc, 0x0, 0x30ad, 0x3099, 0x30eb, + 0x30bf, 0x3099, 0x30fc, 0x0, 0x30ad, 0x30e5, 0x30ea, 0x30fc, + 0x0, 0x30ad, 0x30ed, 0x0, 0x30ad, 0x30ed, 0x30af, 0x3099, + 0x30e9, 0x30e0, 0x0, 0x30ad, 0x30ed, 0x30e1, 0x30fc, 0x30c8, + 0x30eb, 0x0, 0x30ad, 0x30ed, 0x30ef, 0x30c3, 0x30c8, 0x0, + 0x30af, 0x0, 0x30af, 0x3099, 0x0, 0x30af, 0x3099, 0x30e9, + 0x30e0, 0x0, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x30c8, 0x30f3, + 0x0, 0x30af, 0x30eb, 0x30bb, 0x3099, 0x30a4, 0x30ed, 0x0, + 0x30af, 0x30ed, 0x30fc, 0x30cd, 0x0, 0x30b1, 0x0, 0x30b1, + 0x3099, 0x0, 0x30b1, 0x30fc, 0x30b9, 0x0, 0x30b3, 0x0, 0x30b3, + 0x3099, 0x0, 0x30b3, 0x30b3, 0x0, 0x30b3, 0x30c8, 0x0, 0x30b3, + 0x30eb, 0x30ca, 0x0, 0x30b3, 0x30fc, 0x30db, 0x309a, 0x0, + 0x30b5, 0x0, 0x30b5, 0x3099, 0x0, 0x30b5, 0x30a4, 0x30af, + 0x30eb, 0x0, 0x30b5, 0x30f3, 0x30c1, 0x30fc, 0x30e0, 0x0, + 0x30b7, 0x0, 0x30b7, 0x3099, 0x0, 0x30b7, 0x30ea, 0x30f3, + 0x30af, 0x3099, 0x0, 0x30b9, 0x0, 0x30b9, 0x3099, 0x0, 0x30bb, + 0x0, 0x30bb, 0x3099, 0x0, 0x30bb, 0x30f3, 0x30c1, 0x0, 0x30bb, + 0x30f3, 0x30c8, 0x0, 0x30bd, 0x0, 0x30bd, 0x3099, 0x0, 0x30bf, + 0x0, 0x30bf, 0x3099, 0x0, 0x30bf, 0x3099, 0x30fc, 0x30b9, 0x0, + 0x30c1, 0x0, 0x30c1, 0x3099, 0x0, 0x30c3, 0x0, 0x30c4, 0x0, + 0x30c4, 0x3099, 0x0, 0x30c6, 0x0, 0x30c6, 0x3099, 0x0, 0x30c6, + 0x3099, 0x30b7, 0x0, 0x30c8, 0x0, 0x30c8, 0x3099, 0x0, 0x30c8, + 0x3099, 0x30eb, 0x0, 0x30c8, 0x30f3, 0x0, 0x30ca, 0x0, 0x30ca, + 0x30ce, 0x0, 0x30cb, 0x0, 0x30cc, 0x0, 0x30cd, 0x0, 0x30ce, + 0x0, 0x30ce, 0x30c3, 0x30c8, 0x0, 0x30cf, 0x0, 0x30cf, 0x3099, + 0x0, 0x30cf, 0x3099, 0x30fc, 0x30ec, 0x30eb, 0x0, 0x30cf, + 0x309a, 0x0, 0x30cf, 0x309a, 0x30fc, 0x30bb, 0x30f3, 0x30c8, + 0x0, 0x30cf, 0x309a, 0x30fc, 0x30c4, 0x0, 0x30cf, 0x30a4, + 0x30c4, 0x0, 0x30d2, 0x0, 0x30d2, 0x3099, 0x0, 0x30d2, 0x3099, + 0x30eb, 0x0, 0x30d2, 0x309a, 0x0, 0x30d2, 0x309a, 0x30a2, + 0x30b9, 0x30c8, 0x30eb, 0x0, 0x30d2, 0x309a, 0x30af, 0x30eb, + 0x0, 0x30d2, 0x309a, 0x30b3, 0x0, 0x30d5, 0x0, 0x30d5, 0x3099, + 0x0, 0x30d5, 0x3099, 0x30c3, 0x30b7, 0x30a7, 0x30eb, 0x0, + 0x30d5, 0x309a, 0x0, 0x30d5, 0x30a1, 0x30e9, 0x30c3, 0x30c8, + 0x3099, 0x0, 0x30d5, 0x30a3, 0x30fc, 0x30c8, 0x0, 0x30d5, + 0x30e9, 0x30f3, 0x0, 0x30d8, 0x0, 0x30d8, 0x3099, 0x0, 0x30d8, + 0x3099, 0x30fc, 0x30bf, 0x0, 0x30d8, 0x309a, 0x0, 0x30d8, + 0x309a, 0x30bd, 0x0, 0x30d8, 0x309a, 0x30cb, 0x30d2, 0x0, + 0x30d8, 0x309a, 0x30f3, 0x30b9, 0x0, 0x30d8, 0x309a, 0x30fc, + 0x30b7, 0x3099, 0x0, 0x30d8, 0x30af, 0x30bf, 0x30fc, 0x30eb, + 0x0, 0x30d8, 0x30eb, 0x30c4, 0x0, 0x30db, 0x0, 0x30db, 0x3099, + 0x0, 0x30db, 0x3099, 0x30eb, 0x30c8, 0x0, 0x30db, 0x309a, 0x0, + 0x30db, 0x309a, 0x30a4, 0x30f3, 0x30c8, 0x0, 0x30db, 0x309a, + 0x30f3, 0x30c8, 0x3099, 0x0, 0x30db, 0x30f3, 0x0, 0x30db, + 0x30fc, 0x30eb, 0x0, 0x30db, 0x30fc, 0x30f3, 0x0, 0x30de, 0x0, + 0x30de, 0x30a4, 0x30af, 0x30ed, 0x0, 0x30de, 0x30a4, 0x30eb, + 0x0, 0x30de, 0x30c3, 0x30cf, 0x0, 0x30de, 0x30eb, 0x30af, 0x0, + 0x30de, 0x30f3, 0x30b7, 0x30e7, 0x30f3, 0x0, 0x30df, 0x0, + 0x30df, 0x30af, 0x30ed, 0x30f3, 0x0, 0x30df, 0x30ea, 0x0, + 0x30df, 0x30ea, 0x30cf, 0x3099, 0x30fc, 0x30eb, 0x0, 0x30e0, + 0x0, 0x30e1, 0x0, 0x30e1, 0x30ab, 0x3099, 0x0, 0x30e1, 0x30ab, + 0x3099, 0x30c8, 0x30f3, 0x0, 0x30e1, 0x30fc, 0x30c8, 0x30eb, + 0x0, 0x30e2, 0x0, 0x30e3, 0x0, 0x30e4, 0x0, 0x30e4, 0x30fc, + 0x30c8, 0x3099, 0x0, 0x30e4, 0x30fc, 0x30eb, 0x0, 0x30e5, 0x0, + 0x30e6, 0x0, 0x30e6, 0x30a2, 0x30f3, 0x0, 0x30e7, 0x0, 0x30e8, + 0x0, 0x30e9, 0x0, 0x30ea, 0x0, 0x30ea, 0x30c3, 0x30c8, 0x30eb, + 0x0, 0x30ea, 0x30e9, 0x0, 0x30eb, 0x0, 0x30eb, 0x30d2, 0x309a, + 0x30fc, 0x0, 0x30eb, 0x30fc, 0x30d5, 0x3099, 0x30eb, 0x0, + 0x30ec, 0x0, 0x30ec, 0x30e0, 0x0, 0x30ec, 0x30f3, 0x30c8, + 0x30b1, 0x3099, 0x30f3, 0x0, 0x30ed, 0x0, 0x30ef, 0x0, 0x30ef, + 0x3099, 0x0, 0x30ef, 0x30c3, 0x30c8, 0x0, 0x30f0, 0x0, 0x30f0, + 0x3099, 0x0, 0x30f1, 0x0, 0x30f1, 0x3099, 0x0, 0x30f2, 0x0, + 0x30f2, 0x3099, 0x0, 0x30f3, 0x0, 0x30fb, 0x0, 0x30fc, 0x0, + 0x30fd, 0x3099, 0x0, 0x349e, 0x0, 0x34b9, 0x0, 0x34bb, 0x0, + 0x34df, 0x0, 0x3515, 0x0, 0x36ee, 0x0, 0x36fc, 0x0, 0x3781, + 0x0, 0x382f, 0x0, 0x3862, 0x0, 0x387c, 0x0, 0x38c7, 0x0, + 0x38e3, 0x0, 0x391c, 0x0, 0x393a, 0x0, 0x3a2e, 0x0, 0x3a6c, + 0x0, 0x3ae4, 0x0, 0x3b08, 0x0, 0x3b19, 0x0, 0x3b49, 0x0, + 0x3b9d, 0x0, 0x3c18, 0x0, 0x3c4e, 0x0, 0x3d33, 0x0, 0x3d96, + 0x0, 0x3eac, 0x0, 0x3eb8, 0x0, 0x3f1b, 0x0, 0x3ffc, 0x0, + 0x4008, 0x0, 0x4018, 0x0, 0x4039, 0x0, 0x4046, 0x0, 0x4096, + 0x0, 0x40e3, 0x0, 0x412f, 0x0, 0x4202, 0x0, 0x4227, 0x0, + 0x42a0, 0x0, 0x4301, 0x0, 0x4334, 0x0, 0x4359, 0x0, 0x43d5, + 0x0, 0x43d9, 0x0, 0x440b, 0x0, 0x446b, 0x0, 0x452b, 0x0, + 0x455d, 0x0, 0x4561, 0x0, 0x456b, 0x0, 0x45d7, 0x0, 0x45f9, + 0x0, 0x4635, 0x0, 0x46be, 0x0, 0x46c7, 0x0, 0x4995, 0x0, + 0x49e6, 0x0, 0x4a6e, 0x0, 0x4a76, 0x0, 0x4ab2, 0x0, 0x4b33, + 0x0, 0x4bce, 0x0, 0x4cce, 0x0, 0x4ced, 0x0, 0x4cf8, 0x0, + 0x4d56, 0x0, 0x4e00, 0x0, 0x4e01, 0x0, 0x4e03, 0x0, 0x4e09, + 0x0, 0x4e0a, 0x0, 0x4e0b, 0x0, 0x4e0d, 0x0, 0x4e19, 0x0, + 0x4e26, 0x0, 0x4e28, 0x0, 0x4e2d, 0x0, 0x4e32, 0x0, 0x4e36, + 0x0, 0x4e38, 0x0, 0x4e39, 0x0, 0x4e3d, 0x0, 0x4e3f, 0x0, + 0x4e41, 0x0, 0x4e59, 0x0, 0x4e5d, 0x0, 0x4e82, 0x0, 0x4e85, + 0x0, 0x4e86, 0x0, 0x4e8c, 0x0, 0x4e94, 0x0, 0x4ea0, 0x0, + 0x4ea4, 0x0, 0x4eae, 0x0, 0x4eba, 0x0, 0x4ec0, 0x0, 0x4ecc, + 0x0, 0x4ee4, 0x0, 0x4f01, 0x0, 0x4f11, 0x0, 0x4f60, 0x0, + 0x4f80, 0x0, 0x4f86, 0x0, 0x4f8b, 0x0, 0x4fae, 0x0, 0x4fbb, + 0x0, 0x4fbf, 0x0, 0x5002, 0x0, 0x502b, 0x0, 0x507a, 0x0, + 0x5099, 0x0, 0x50cf, 0x0, 0x50da, 0x0, 0x50e7, 0x0, 0x512a, + 0x0, 0x513f, 0x0, 0x5140, 0x0, 0x5145, 0x0, 0x514d, 0x0, + 0x5154, 0x0, 0x5164, 0x0, 0x5165, 0x0, 0x5167, 0x0, 0x5168, + 0x0, 0x5169, 0x0, 0x516b, 0x0, 0x516d, 0x0, 0x5177, 0x0, + 0x5180, 0x0, 0x5182, 0x0, 0x518d, 0x0, 0x5192, 0x0, 0x5195, + 0x0, 0x5196, 0x0, 0x5197, 0x0, 0x5199, 0x0, 0x51a4, 0x0, + 0x51ab, 0x0, 0x51ac, 0x0, 0x51b5, 0x0, 0x51b7, 0x0, 0x51c9, + 0x0, 0x51cc, 0x0, 0x51dc, 0x0, 0x51de, 0x0, 0x51e0, 0x0, + 0x51f5, 0x0, 0x5200, 0x0, 0x5203, 0x0, 0x5207, 0x0, 0x5217, + 0x0, 0x521d, 0x0, 0x5229, 0x0, 0x523a, 0x0, 0x523b, 0x0, + 0x5246, 0x0, 0x524d, 0x0, 0x5272, 0x0, 0x5277, 0x0, 0x5289, + 0x0, 0x529b, 0x0, 0x52a3, 0x0, 0x52b3, 0x0, 0x52b4, 0x0, + 0x52c7, 0x0, 0x52c9, 0x0, 0x52d2, 0x0, 0x52de, 0x0, 0x52e4, + 0x0, 0x52f5, 0x0, 0x52f9, 0x0, 0x52fa, 0x0, 0x5305, 0x0, + 0x5306, 0x0, 0x5315, 0x0, 0x5317, 0x0, 0x531a, 0x0, 0x5338, + 0x0, 0x533b, 0x0, 0x533f, 0x0, 0x5341, 0x0, 0x5344, 0x0, + 0x5345, 0x0, 0x5349, 0x0, 0x5351, 0x0, 0x5354, 0x0, 0x535a, + 0x0, 0x535c, 0x0, 0x5369, 0x0, 0x5370, 0x0, 0x5373, 0x0, + 0x5375, 0x0, 0x537d, 0x0, 0x537f, 0x0, 0x5382, 0x0, 0x53b6, + 0x0, 0x53c3, 0x0, 0x53c8, 0x0, 0x53ca, 0x0, 0x53cc, 0x0, + 0x53df, 0x0, 0x53e3, 0x0, 0x53e5, 0x0, 0x53eb, 0x0, 0x53ef, + 0x0, 0x53f1, 0x0, 0x53f3, 0x0, 0x5406, 0x0, 0x5408, 0x0, + 0x540d, 0x0, 0x540f, 0x0, 0x541d, 0x0, 0x5438, 0x0, 0x5439, + 0x0, 0x5442, 0x0, 0x5448, 0x0, 0x5468, 0x0, 0x549e, 0x0, + 0x54a2, 0x0, 0x54bd, 0x0, 0x54f6, 0x0, 0x5510, 0x0, 0x554f, + 0x0, 0x5553, 0x0, 0x5555, 0x0, 0x5563, 0x0, 0x5584, 0x0, + 0x5587, 0x0, 0x5599, 0x0, 0x559d, 0x0, 0x55ab, 0x0, 0x55b3, + 0x0, 0x55b6, 0x0, 0x55c0, 0x0, 0x55c2, 0x0, 0x55e2, 0x0, + 0x5606, 0x0, 0x5651, 0x0, 0x5668, 0x0, 0x5674, 0x0, 0x56d7, + 0x0, 0x56db, 0x0, 0x56f9, 0x0, 0x5716, 0x0, 0x5717, 0x0, + 0x571f, 0x0, 0x5730, 0x0, 0x578b, 0x0, 0x57ce, 0x0, 0x57f4, + 0x0, 0x580d, 0x0, 0x5831, 0x0, 0x5832, 0x0, 0x5840, 0x0, + 0x585a, 0x0, 0x585e, 0x0, 0x58a8, 0x0, 0x58ac, 0x0, 0x58b3, + 0x0, 0x58d8, 0x0, 0x58df, 0x0, 0x58eb, 0x0, 0x58ee, 0x0, + 0x58f0, 0x0, 0x58f2, 0x0, 0x58f7, 0x0, 0x5902, 0x0, 0x5906, + 0x0, 0x590a, 0x0, 0x5915, 0x0, 0x591a, 0x0, 0x591c, 0x0, + 0x5922, 0x0, 0x5927, 0x0, 0x5927, 0x6b63, 0x0, 0x5929, 0x0, + 0x5944, 0x0, 0x5948, 0x0, 0x5951, 0x0, 0x5954, 0x0, 0x5962, + 0x0, 0x5973, 0x0, 0x59d8, 0x0, 0x59ec, 0x0, 0x5a1b, 0x0, + 0x5a27, 0x0, 0x5a62, 0x0, 0x5a66, 0x0, 0x5ab5, 0x0, 0x5b08, + 0x0, 0x5b28, 0x0, 0x5b3e, 0x0, 0x5b50, 0x0, 0x5b57, 0x0, + 0x5b66, 0x0, 0x5b80, 0x0, 0x5b85, 0x0, 0x5b97, 0x0, 0x5bc3, + 0x0, 0x5bd8, 0x0, 0x5be7, 0x0, 0x5bee, 0x0, 0x5bf3, 0x0, + 0x5bf8, 0x0, 0x5bff, 0x0, 0x5c06, 0x0, 0x5c0f, 0x0, 0x5c22, + 0x0, 0x5c38, 0x0, 0x5c3f, 0x0, 0x5c60, 0x0, 0x5c62, 0x0, + 0x5c64, 0x0, 0x5c65, 0x0, 0x5c6e, 0x0, 0x5c71, 0x0, 0x5c8d, + 0x0, 0x5cc0, 0x0, 0x5d19, 0x0, 0x5d43, 0x0, 0x5d50, 0x0, + 0x5d6b, 0x0, 0x5d6e, 0x0, 0x5d7c, 0x0, 0x5db2, 0x0, 0x5dba, + 0x0, 0x5ddb, 0x0, 0x5de1, 0x0, 0x5de2, 0x0, 0x5de5, 0x0, + 0x5de6, 0x0, 0x5df1, 0x0, 0x5dfd, 0x0, 0x5dfe, 0x0, 0x5e28, + 0x0, 0x5e3d, 0x0, 0x5e69, 0x0, 0x5e72, 0x0, 0x5e73, 0x6210, + 0x0, 0x5e74, 0x0, 0x5e7a, 0x0, 0x5e7c, 0x0, 0x5e7f, 0x0, + 0x5ea6, 0x0, 0x5eb0, 0x0, 0x5eb3, 0x0, 0x5eb6, 0x0, 0x5ec9, + 0x0, 0x5eca, 0x0, 0x5ed2, 0x0, 0x5ed3, 0x0, 0x5ed9, 0x0, + 0x5eec, 0x0, 0x5ef4, 0x0, 0x5efe, 0x0, 0x5f04, 0x0, 0x5f0b, + 0x0, 0x5f13, 0x0, 0x5f22, 0x0, 0x5f50, 0x0, 0x5f53, 0x0, + 0x5f61, 0x0, 0x5f62, 0x0, 0x5f69, 0x0, 0x5f6b, 0x0, 0x5f73, + 0x0, 0x5f8b, 0x0, 0x5f8c, 0x0, 0x5f97, 0x0, 0x5f9a, 0x0, + 0x5fa9, 0x0, 0x5fad, 0x0, 0x5fc3, 0x0, 0x5fcd, 0x0, 0x5fd7, + 0x0, 0x5ff5, 0x0, 0x5ff9, 0x0, 0x6012, 0x0, 0x601c, 0x0, + 0x6075, 0x0, 0x6081, 0x0, 0x6094, 0x0, 0x60c7, 0x0, 0x60d8, + 0x0, 0x60e1, 0x0, 0x6108, 0x0, 0x6144, 0x0, 0x6148, 0x0, + 0x614c, 0x0, 0x614e, 0x0, 0x6160, 0x0, 0x6168, 0x0, 0x617a, + 0x0, 0x618e, 0x0, 0x6190, 0x0, 0x61a4, 0x0, 0x61af, 0x0, + 0x61b2, 0x0, 0x61de, 0x0, 0x61f2, 0x0, 0x61f6, 0x0, 0x6200, + 0x0, 0x6208, 0x0, 0x6210, 0x0, 0x621b, 0x0, 0x622e, 0x0, + 0x6234, 0x0, 0x6236, 0x0, 0x624b, 0x0, 0x6253, 0x0, 0x625d, + 0x0, 0x6295, 0x0, 0x62b1, 0x0, 0x62c9, 0x0, 0x62cf, 0x0, + 0x62d3, 0x0, 0x62d4, 0x0, 0x62fc, 0x0, 0x62fe, 0x0, 0x6307, + 0x0, 0x633d, 0x0, 0x6350, 0x0, 0x6355, 0x0, 0x6368, 0x0, + 0x637b, 0x0, 0x6383, 0x0, 0x63a0, 0x0, 0x63a9, 0x0, 0x63c4, + 0x0, 0x63c5, 0x0, 0x63e4, 0x0, 0x641c, 0x0, 0x6422, 0x0, + 0x6452, 0x0, 0x6469, 0x0, 0x6477, 0x0, 0x647e, 0x0, 0x649a, + 0x0, 0x649d, 0x0, 0x64c4, 0x0, 0x652f, 0x0, 0x6534, 0x0, + 0x654f, 0x0, 0x6556, 0x0, 0x656c, 0x0, 0x6578, 0x0, 0x6587, + 0x0, 0x6597, 0x0, 0x6599, 0x0, 0x65a4, 0x0, 0x65b0, 0x0, + 0x65b9, 0x0, 0x65c5, 0x0, 0x65e0, 0x0, 0x65e2, 0x0, 0x65e3, + 0x0, 0x65e5, 0x0, 0x660e, 0x6cbb, 0x0, 0x6613, 0x0, 0x6620, + 0x0, 0x662d, 0x548c, 0x0, 0x6649, 0x0, 0x6674, 0x0, 0x6688, + 0x0, 0x6691, 0x0, 0x669c, 0x0, 0x66b4, 0x0, 0x66c6, 0x0, + 0x66f0, 0x0, 0x66f4, 0x0, 0x66f8, 0x0, 0x6700, 0x0, 0x6708, + 0x0, 0x6709, 0x0, 0x6717, 0x0, 0x671b, 0x0, 0x6721, 0x0, + 0x6728, 0x0, 0x674e, 0x0, 0x6753, 0x0, 0x6756, 0x0, 0x675e, + 0x0, 0x677b, 0x0, 0x6785, 0x0, 0x6797, 0x0, 0x67f3, 0x0, + 0x67fa, 0x0, 0x6817, 0x0, 0x681f, 0x0, 0x682a, 0x0, 0x682a, + 0x5f0f, 0x4f1a, 0x793e, 0x0, 0x6852, 0x0, 0x6881, 0x0, 0x6885, + 0x0, 0x688e, 0x0, 0x68a8, 0x0, 0x6914, 0x0, 0x6942, 0x0, + 0x69a3, 0x0, 0x69ea, 0x0, 0x6a02, 0x0, 0x6a13, 0x0, 0x6aa8, + 0x0, 0x6ad3, 0x0, 0x6adb, 0x0, 0x6b04, 0x0, 0x6b20, 0x0, + 0x6b21, 0x0, 0x6b54, 0x0, 0x6b62, 0x0, 0x6b63, 0x0, 0x6b72, + 0x0, 0x6b77, 0x0, 0x6b79, 0x0, 0x6b9f, 0x0, 0x6bae, 0x0, + 0x6bb3, 0x0, 0x6bba, 0x0, 0x6bbb, 0x0, 0x6bcb, 0x0, 0x6bcd, + 0x0, 0x6bd4, 0x0, 0x6bdb, 0x0, 0x6c0f, 0x0, 0x6c14, 0x0, + 0x6c34, 0x0, 0x6c4e, 0x0, 0x6c67, 0x0, 0x6c88, 0x0, 0x6cbf, + 0x0, 0x6ccc, 0x0, 0x6ccd, 0x0, 0x6ce5, 0x0, 0x6ce8, 0x0, + 0x6d16, 0x0, 0x6d1b, 0x0, 0x6d1e, 0x0, 0x6d34, 0x0, 0x6d3e, + 0x0, 0x6d41, 0x0, 0x6d69, 0x0, 0x6d6a, 0x0, 0x6d77, 0x0, + 0x6d78, 0x0, 0x6d85, 0x0, 0x6dcb, 0x0, 0x6dda, 0x0, 0x6dea, + 0x0, 0x6df9, 0x0, 0x6e1a, 0x0, 0x6e2f, 0x0, 0x6e6e, 0x0, + 0x6e80, 0x0, 0x6e9c, 0x0, 0x6eba, 0x0, 0x6ec7, 0x0, 0x6ecb, + 0x0, 0x6ed1, 0x0, 0x6edb, 0x0, 0x6f0f, 0x0, 0x6f14, 0x0, + 0x6f22, 0x0, 0x6f23, 0x0, 0x6f6e, 0x0, 0x6fc6, 0x0, 0x6feb, + 0x0, 0x6ffe, 0x0, 0x701b, 0x0, 0x701e, 0x0, 0x7039, 0x0, + 0x704a, 0x0, 0x706b, 0x0, 0x7070, 0x0, 0x7077, 0x0, 0x707d, + 0x0, 0x7099, 0x0, 0x70ad, 0x0, 0x70c8, 0x0, 0x70d9, 0x0, + 0x7121, 0x0, 0x7145, 0x0, 0x7149, 0x0, 0x716e, 0x0, 0x719c, + 0x0, 0x71ce, 0x0, 0x71d0, 0x0, 0x7210, 0x0, 0x721b, 0x0, + 0x7228, 0x0, 0x722a, 0x0, 0x722b, 0x0, 0x7235, 0x0, 0x7236, + 0x0, 0x723b, 0x0, 0x723f, 0x0, 0x7247, 0x0, 0x7250, 0x0, + 0x7259, 0x0, 0x725b, 0x0, 0x7262, 0x0, 0x7279, 0x0, 0x7280, + 0x0, 0x7295, 0x0, 0x72ac, 0x0, 0x72af, 0x0, 0x72c0, 0x0, + 0x72fc, 0x0, 0x732a, 0x0, 0x7375, 0x0, 0x737a, 0x0, 0x7384, + 0x0, 0x7387, 0x0, 0x7389, 0x0, 0x738b, 0x0, 0x73a5, 0x0, + 0x73b2, 0x0, 0x73de, 0x0, 0x7406, 0x0, 0x7409, 0x0, 0x7422, + 0x0, 0x7447, 0x0, 0x745c, 0x0, 0x7469, 0x0, 0x7471, 0x0, + 0x7485, 0x0, 0x7489, 0x0, 0x7498, 0x0, 0x74ca, 0x0, 0x74dc, + 0x0, 0x74e6, 0x0, 0x7506, 0x0, 0x7518, 0x0, 0x751f, 0x0, + 0x7524, 0x0, 0x7528, 0x0, 0x7530, 0x0, 0x7532, 0x0, 0x7533, + 0x0, 0x7537, 0x0, 0x753b, 0x0, 0x753e, 0x0, 0x7559, 0x0, + 0x7565, 0x0, 0x7570, 0x0, 0x758b, 0x0, 0x7592, 0x0, 0x75e2, + 0x0, 0x7610, 0x0, 0x761d, 0x0, 0x761f, 0x0, 0x7642, 0x0, + 0x7669, 0x0, 0x7676, 0x0, 0x767d, 0x0, 0x76ae, 0x0, 0x76bf, + 0x0, 0x76ca, 0x0, 0x76db, 0x0, 0x76e3, 0x0, 0x76e7, 0x0, + 0x76ee, 0x0, 0x76f4, 0x0, 0x7701, 0x0, 0x771e, 0x0, 0x771f, + 0x0, 0x7740, 0x0, 0x774a, 0x0, 0x778b, 0x0, 0x77a7, 0x0, + 0x77db, 0x0, 0x77e2, 0x0, 0x77f3, 0x0, 0x784e, 0x0, 0x786b, + 0x0, 0x788c, 0x0, 0x7891, 0x0, 0x78ca, 0x0, 0x78cc, 0x0, + 0x78fb, 0x0, 0x792a, 0x0, 0x793a, 0x0, 0x793c, 0x0, 0x793e, + 0x0, 0x7948, 0x0, 0x7949, 0x0, 0x7950, 0x0, 0x7956, 0x0, + 0x795d, 0x0, 0x795e, 0x0, 0x7965, 0x0, 0x797f, 0x0, 0x7981, + 0x0, 0x798d, 0x0, 0x798e, 0x0, 0x798f, 0x0, 0x79ae, 0x0, + 0x79b8, 0x0, 0x79be, 0x0, 0x79ca, 0x0, 0x79d8, 0x0, 0x79eb, + 0x0, 0x7a1c, 0x0, 0x7a40, 0x0, 0x7a4a, 0x0, 0x7a4f, 0x0, + 0x7a74, 0x0, 0x7a7a, 0x0, 0x7a81, 0x0, 0x7ab1, 0x0, 0x7acb, + 0x0, 0x7aee, 0x0, 0x7af9, 0x0, 0x7b20, 0x0, 0x7b8f, 0x0, + 0x7bc0, 0x0, 0x7bc6, 0x0, 0x7bc9, 0x0, 0x7c3e, 0x0, 0x7c60, + 0x0, 0x7c73, 0x0, 0x7c7b, 0x0, 0x7c92, 0x0, 0x7cbe, 0x0, + 0x7cd2, 0x0, 0x7cd6, 0x0, 0x7ce3, 0x0, 0x7ce7, 0x0, 0x7ce8, + 0x0, 0x7cf8, 0x0, 0x7d00, 0x0, 0x7d10, 0x0, 0x7d22, 0x0, + 0x7d2f, 0x0, 0x7d42, 0x0, 0x7d5b, 0x0, 0x7d63, 0x0, 0x7da0, + 0x0, 0x7dbe, 0x0, 0x7dc7, 0x0, 0x7df4, 0x0, 0x7e02, 0x0, + 0x7e09, 0x0, 0x7e37, 0x0, 0x7e41, 0x0, 0x7e45, 0x0, 0x7f36, + 0x0, 0x7f3e, 0x0, 0x7f51, 0x0, 0x7f72, 0x0, 0x7f79, 0x0, + 0x7f7a, 0x0, 0x7f85, 0x0, 0x7f8a, 0x0, 0x7f95, 0x0, 0x7f9a, + 0x0, 0x7fbd, 0x0, 0x7ffa, 0x0, 0x8001, 0x0, 0x8005, 0x0, + 0x800c, 0x0, 0x8012, 0x0, 0x8033, 0x0, 0x8046, 0x0, 0x8060, + 0x0, 0x806f, 0x0, 0x8070, 0x0, 0x807e, 0x0, 0x807f, 0x0, + 0x8089, 0x0, 0x808b, 0x0, 0x80ad, 0x0, 0x80b2, 0x0, 0x8103, + 0x0, 0x813e, 0x0, 0x81d8, 0x0, 0x81e3, 0x0, 0x81e8, 0x0, + 0x81ea, 0x0, 0x81ed, 0x0, 0x81f3, 0x0, 0x81fc, 0x0, 0x8201, + 0x0, 0x8204, 0x0, 0x820c, 0x0, 0x8218, 0x0, 0x821b, 0x0, + 0x821f, 0x0, 0x826e, 0x0, 0x826f, 0x0, 0x8272, 0x0, 0x8278, + 0x0, 0x8279, 0x0, 0x828b, 0x0, 0x8291, 0x0, 0x829d, 0x0, + 0x82b1, 0x0, 0x82b3, 0x0, 0x82bd, 0x0, 0x82e5, 0x0, 0x82e6, + 0x0, 0x831d, 0x0, 0x8323, 0x0, 0x8336, 0x0, 0x8352, 0x0, + 0x8353, 0x0, 0x8363, 0x0, 0x83ad, 0x0, 0x83bd, 0x0, 0x83c9, + 0x0, 0x83ca, 0x0, 0x83cc, 0x0, 0x83dc, 0x0, 0x83e7, 0x0, + 0x83ef, 0x0, 0x83f1, 0x0, 0x843d, 0x0, 0x8449, 0x0, 0x8457, + 0x0, 0x84ee, 0x0, 0x84f1, 0x0, 0x84f3, 0x0, 0x84fc, 0x0, + 0x8516, 0x0, 0x8564, 0x0, 0x85cd, 0x0, 0x85fa, 0x0, 0x8606, + 0x0, 0x8612, 0x0, 0x862d, 0x0, 0x863f, 0x0, 0x864d, 0x0, + 0x8650, 0x0, 0x865c, 0x0, 0x8667, 0x0, 0x8669, 0x0, 0x866b, + 0x0, 0x8688, 0x0, 0x86a9, 0x0, 0x86e2, 0x0, 0x870e, 0x0, + 0x8728, 0x0, 0x876b, 0x0, 0x8779, 0x0, 0x8786, 0x0, 0x87ba, + 0x0, 0x87e1, 0x0, 0x8801, 0x0, 0x881f, 0x0, 0x8840, 0x0, + 0x884c, 0x0, 0x8860, 0x0, 0x8863, 0x0, 0x88c2, 0x0, 0x88cf, + 0x0, 0x88d7, 0x0, 0x88de, 0x0, 0x88e1, 0x0, 0x88f8, 0x0, + 0x88fa, 0x0, 0x8910, 0x0, 0x8941, 0x0, 0x8964, 0x0, 0x897e, + 0x0, 0x8986, 0x0, 0x898b, 0x0, 0x8996, 0x0, 0x89d2, 0x0, + 0x89e3, 0x0, 0x8a00, 0x0, 0x8aa0, 0x0, 0x8aaa, 0x0, 0x8abf, + 0x0, 0x8acb, 0x0, 0x8ad2, 0x0, 0x8ad6, 0x0, 0x8aed, 0x0, + 0x8af8, 0x0, 0x8afe, 0x0, 0x8b01, 0x0, 0x8b39, 0x0, 0x8b58, + 0x0, 0x8b80, 0x0, 0x8b8a, 0x0, 0x8c37, 0x0, 0x8c46, 0x0, + 0x8c48, 0x0, 0x8c55, 0x0, 0x8c78, 0x0, 0x8c9d, 0x0, 0x8ca1, + 0x0, 0x8ca9, 0x0, 0x8cab, 0x0, 0x8cc1, 0x0, 0x8cc2, 0x0, + 0x8cc7, 0x0, 0x8cc8, 0x0, 0x8cd3, 0x0, 0x8d08, 0x0, 0x8d1b, + 0x0, 0x8d64, 0x0, 0x8d70, 0x0, 0x8d77, 0x0, 0x8db3, 0x0, + 0x8dbc, 0x0, 0x8dcb, 0x0, 0x8def, 0x0, 0x8df0, 0x0, 0x8eab, + 0x0, 0x8eca, 0x0, 0x8ed4, 0x0, 0x8f26, 0x0, 0x8f2a, 0x0, + 0x8f38, 0x0, 0x8f3b, 0x0, 0x8f62, 0x0, 0x8f9b, 0x0, 0x8f9e, + 0x0, 0x8fb0, 0x0, 0x8fb5, 0x0, 0x8fb6, 0x0, 0x9023, 0x0, + 0x9038, 0x0, 0x904a, 0x0, 0x9069, 0x0, 0x9072, 0x0, 0x907c, + 0x0, 0x908f, 0x0, 0x9091, 0x0, 0x9094, 0x0, 0x90ce, 0x0, + 0x90de, 0x0, 0x90f1, 0x0, 0x90fd, 0x0, 0x9111, 0x0, 0x911b, + 0x0, 0x9149, 0x0, 0x916a, 0x0, 0x9199, 0x0, 0x91b4, 0x0, + 0x91c6, 0x0, 0x91cc, 0x0, 0x91cf, 0x0, 0x91d1, 0x0, 0x9234, + 0x0, 0x9238, 0x0, 0x9276, 0x0, 0x927c, 0x0, 0x92d7, 0x0, + 0x92d8, 0x0, 0x9304, 0x0, 0x934a, 0x0, 0x93f9, 0x0, 0x9415, + 0x0, 0x9577, 0x0, 0x9580, 0x0, 0x958b, 0x0, 0x95ad, 0x0, + 0x95b7, 0x0, 0x961c, 0x0, 0x962e, 0x0, 0x964b, 0x0, 0x964d, + 0x0, 0x9675, 0x0, 0x9678, 0x0, 0x967c, 0x0, 0x9686, 0x0, + 0x96a3, 0x0, 0x96b6, 0x0, 0x96b7, 0x0, 0x96b8, 0x0, 0x96b9, + 0x0, 0x96c3, 0x0, 0x96e2, 0x0, 0x96e3, 0x0, 0x96e8, 0x0, + 0x96f6, 0x0, 0x96f7, 0x0, 0x9723, 0x0, 0x9732, 0x0, 0x9748, + 0x0, 0x9751, 0x0, 0x9756, 0x0, 0x975e, 0x0, 0x9762, 0x0, + 0x9769, 0x0, 0x97cb, 0x0, 0x97db, 0x0, 0x97e0, 0x0, 0x97ed, + 0x0, 0x97f3, 0x0, 0x97ff, 0x0, 0x9801, 0x0, 0x9805, 0x0, + 0x980b, 0x0, 0x9818, 0x0, 0x9829, 0x0, 0x983b, 0x0, 0x985e, + 0x0, 0x98a8, 0x0, 0x98db, 0x0, 0x98df, 0x0, 0x98e2, 0x0, + 0x98ef, 0x0, 0x98fc, 0x0, 0x9928, 0x0, 0x9929, 0x0, 0x9996, + 0x0, 0x9999, 0x0, 0x99a7, 0x0, 0x99ac, 0x0, 0x99c2, 0x0, + 0x99f1, 0x0, 0x99fe, 0x0, 0x9a6a, 0x0, 0x9aa8, 0x0, 0x9ad8, + 0x0, 0x9adf, 0x0, 0x9b12, 0x0, 0x9b25, 0x0, 0x9b2f, 0x0, + 0x9b32, 0x0, 0x9b3c, 0x0, 0x9b5a, 0x0, 0x9b6f, 0x0, 0x9c40, + 0x0, 0x9c57, 0x0, 0x9ce5, 0x0, 0x9cfd, 0x0, 0x9d67, 0x0, + 0x9db4, 0x0, 0x9dfa, 0x0, 0x9e1e, 0x0, 0x9e75, 0x0, 0x9e7f, + 0x0, 0x9e97, 0x0, 0x9e9f, 0x0, 0x9ea5, 0x0, 0x9ebb, 0x0, + 0x9ec3, 0x0, 0x9ecd, 0x0, 0x9ece, 0x0, 0x9ed1, 0x0, 0x9ef9, + 0x0, 0x9efd, 0x0, 0x9efe, 0x0, 0x9f05, 0x0, 0x9f0e, 0x0, + 0x9f0f, 0x0, 0x9f13, 0x0, 0x9f16, 0x0, 0x9f20, 0x0, 0x9f3b, + 0x0, 0x9f43, 0x0, 0x9f4a, 0x0, 0x9f52, 0x0, 0x9f8d, 0x0, + 0x9f8e, 0x0, 0x9f9c, 0x0, 0x9f9f, 0x0, 0x9fa0, 0x0, 0xa76f, + 0x0, 0x11099, 0x110ba, 0x0, 0x1109b, 0x110ba, 0x0, 0x110a5, + 0x110ba, 0x0, 0x11131, 0x11127, 0x0, 0x11132, 0x11127, 0x0, + 0x1d157, 0x1d165, 0x0, 0x1d158, 0x1d165, 0x0, 0x1d158, 0x1d165, + 0x1d16e, 0x0, 0x1d158, 0x1d165, 0x1d16f, 0x0, 0x1d158, 0x1d165, + 0x1d170, 0x0, 0x1d158, 0x1d165, 0x1d171, 0x0, 0x1d158, 0x1d165, + 0x1d172, 0x0, 0x1d1b9, 0x1d165, 0x0, 0x1d1b9, 0x1d165, 0x1d16e, + 0x0, 0x1d1b9, 0x1d165, 0x1d16f, 0x0, 0x1d1ba, 0x1d165, 0x0, + 0x1d1ba, 0x1d165, 0x1d16e, 0x0, 0x1d1ba, 0x1d165, 0x1d16f, 0x0, + 0x20122, 0x0, 0x2051c, 0x0, 0x20525, 0x0, 0x2054b, 0x0, + 0x2063a, 0x0, 0x20804, 0x0, 0x208de, 0x0, 0x20a2c, 0x0, + 0x20b63, 0x0, 0x214e4, 0x0, 0x216a8, 0x0, 0x216ea, 0x0, + 0x219c8, 0x0, 0x21b18, 0x0, 0x21d0b, 0x0, 0x21de4, 0x0, + 0x21de6, 0x0, 0x22183, 0x0, 0x2219f, 0x0, 0x22331, 0x0, + 0x226d4, 0x0, 0x22844, 0x0, 0x2284a, 0x0, 0x22b0c, 0x0, + 0x22bf1, 0x0, 0x2300a, 0x0, 0x232b8, 0x0, 0x2335f, 0x0, + 0x23393, 0x0, 0x2339c, 0x0, 0x233c3, 0x0, 0x233d5, 0x0, + 0x2346d, 0x0, 0x236a3, 0x0, 0x238a7, 0x0, 0x23a8d, 0x0, + 0x23afa, 0x0, 0x23cbc, 0x0, 0x23d1e, 0x0, 0x23ed1, 0x0, + 0x23f5e, 0x0, 0x23f8e, 0x0, 0x24263, 0x0, 0x242ee, 0x0, + 0x243ab, 0x0, 0x24608, 0x0, 0x24735, 0x0, 0x24814, 0x0, + 0x24c36, 0x0, 0x24c92, 0x0, 0x24fa1, 0x0, 0x24fb8, 0x0, + 0x25044, 0x0, 0x250f2, 0x0, 0x250f3, 0x0, 0x25119, 0x0, + 0x25133, 0x0, 0x25249, 0x0, 0x2541d, 0x0, 0x25626, 0x0, + 0x2569a, 0x0, 0x256c5, 0x0, 0x2597c, 0x0, 0x25aa7, 0x0, + 0x25bab, 0x0, 0x25c80, 0x0, 0x25cd0, 0x0, 0x25f86, 0x0, + 0x261da, 0x0, 0x26228, 0x0, 0x26247, 0x0, 0x262d9, 0x0, + 0x2633e, 0x0, 0x264da, 0x0, 0x26523, 0x0, 0x265a8, 0x0, + 0x267a7, 0x0, 0x267b5, 0x0, 0x26b3c, 0x0, 0x26c36, 0x0, + 0x26cd5, 0x0, 0x26d6b, 0x0, 0x26f2c, 0x0, 0x26fb1, 0x0, + 0x270d2, 0x0, 0x273ca, 0x0, 0x27667, 0x0, 0x278ae, 0x0, + 0x27966, 0x0, 0x27ca8, 0x0, 0x27ed3, 0x0, 0x27f2f, 0x0, + 0x285d2, 0x0, 0x285ed, 0x0, 0x2872e, 0x0, 0x28bfa, 0x0, + 0x28d77, 0x0, 0x29145, 0x0, 0x291df, 0x0, 0x2921a, 0x0, + 0x2940a, 0x0, 0x29496, 0x0, 0x295b6, 0x0, 0x29b30, 0x0, + 0x2a0ce, 0x0, 0x2a105, 0x0, 0x2a20e, 0x0, 0x2a291, 0x0, + 0x2a392, 0x0, 0x2a600, 0x0 + ]; + return t; + } + } +} diff --git a/std/internal/unicode_grapheme.d b/std/internal/unicode_grapheme.d index ffc5c061db3..b4befc9c1d8 100644 --- a/std/internal/unicode_grapheme.d +++ b/std/internal/unicode_grapheme.d @@ -1,28 +1,293 @@ module std.internal.unicode_grapheme; import std.internal.unicode_tables; -static if(size_t.sizeof == 8) { -//832 bytes -enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x40], [ 0x100, 0x80, 0xa00], [ 0x2010000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000300020001, 0x1000700060005, 0x5000400030002, 0x2000100070006, 0x6000500040003, 0x3000200010007, 0x7000600050004, 0x4000300020001, 0x1000700060005, 0x5000400030002, 0x8000100070006, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000010000001, 0x1000000100000, 0x10000001000, 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, 0x100000010000001, 0x1000000100000, 0x10000001000, 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, 0x100000010000001, 0x1000000100000, 0x10000001000, 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, 0x100000010000001, 0x1000000100000, 0x10000001000, 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, 0x10000001000000, 0x100000010000, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0]); -//832 bytes -enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x40], [ 0x100, 0x80, 0xa00], [ 0x2010000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000300020001, 0x1000700060005, 0x5000400030002, 0x2000100070006, 0x6000500040003, 0x3000200010007, 0x7000600050004, 0x4000300020001, 0x1000700060005, 0x5000400030002, 0x8000100070006, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfeffffffeffffffe, 0xfffeffffffefffff, 0xfffffeffffffefff, 0xeffffffeffffffef, 0xffeffffffeffffff, 0xffffeffffffeffff, 0xffffffeffffffeff, 0xfeffffffeffffffe, 0xfffeffffffefffff, 0xfffffeffffffefff, 0xeffffffeffffffef, 0xffeffffffeffffff, 0xffffeffffffeffff, 0xffffffeffffffeff, 0xfeffffffeffffffe, 0xfffeffffffefffff, 0xfffffeffffffefff, 0xeffffffeffffffef, 0xffeffffffeffffff, 0xffffeffffffeffff, 0xffffffeffffffeff, 0xfeffffffeffffffe, 0xfffeffffffefffff, 0xfffffeffffffefff, 0xeffffffeffffffef, 0xffeffffffeffffff, 0xffffeffffffeffff, 0xffffffeffffffeff, 0xffeffffffeffffff, 0xffffeffffffeffff, 0xffffffeff, 0x0, 0x0, 0x0, 0x0, 0x0]); -//1536 bytes -enum mcTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x60], [ 0x100, 0x100, 0x1800], [ 0x202030202020100, 0x206020205020204, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x6000000050004, 0x7, 0x8000000000000, 0xb000a00090000, 0xc, 0x0, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x110010000f000e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130012, 0x1400000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc800000000000008, 0xde01, 0xc00000000000000c, 0x801981, 0xc000000000000008, 0x1, 0xc000000000000008, 0x1a01, 0x400000000000000c, 0x801981, 0xc000000000000000, 0x801dc6, 0xe, 0x1e, 0x400000000000000c, 0x600d9f, 0xc00000000000000c, 0x801dc1, 0xc, 0xc0000ff038000, 0xc000000000000000, 0x8000000000000000, 0x0, 0x0, 0x1902180000000000, 0x3f9c00c00000, 0x1c009f98, 0x0, 0x0, 0x0, 0xc040000000000000, 0x1bf, 0x1fb0e7800000000, 0x0, 0xffff000000000000, 0x301, 0x6000000, 0x7e01a00a00000, 0x0, 0x0, 0xe820000000000010, 0x1b, 0x34c200000004, 0xc5c8000000000, 0x300ff000000000, 0x0, 0x0, 0xc000200000000, 0xc00000000000, 0x0, 0x0, 0x0, 0x9800000000, 0x0, 0xfff0000000000003, 0xf, 0x0, 0xc0000, 0xec30000000000008, 0x1, 0x19800000000000, 0x800000000002000, 0x0, 0x20c80000000000, 0x0, 0x0, 0x0, 0x16d800000000, 0x5, 0x0, 0x187000000000004, 0x0, 0x100000000000, 0x0, 0x8038000000000004, 0x1, 0x0, 0x0, 0x40d00000000000, 0x0, 0x0, 0x7ffffffffffe0000, 0x0, 0x0, 0x0, 0x7e06000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//2336 bytes -enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x70], [ 0x100, 0x140, 0x2d00], [ 0x402030202020100, 0x207020206020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020208, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000, 0x5000400030002, 0x9000800070006, 0xd000c000b000a, 0xf00000000000e, 0x10000000000000, 0x14001300120011, 0x160015, 0x17, 0x0, 0x0, 0x190018, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b00000000, 0x1f001e001d001c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000, 0x22002100000000, 0x230000, 0x0, 0x2400000000, 0x0, 0x260025, 0x2700000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a00290000, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, 0x0, 0x0, 0xbffffffffffe0000, 0xb6, 0x7ff0000, 0x10000fffff800, 0x0, 0x3d9f9fc00000, 0xffff000000020000, 0x7ff, 0x1ffc000000000, 0xff80000000000, 0x3eeffbc00000, 0xe000000, 0x0, 0x7ffffff000000000, 0x1400000000000007, 0xc00fe21fe, 0x5000000000000002, 0xc0080201e, 0x1000000000000006, 0x23000000023986, 0x1000000000000006, 0xc000021be, 0xd000000000000002, 0xc00c0201e, 0x4000000000000004, 0x802001, 0xc000000000000000, 0xc00603dc1, 0x9000000000000000, 0xc00603044, 0x4000000000000000, 0xc0080201e, 0x0, 0x805c8400, 0x7f2000000000000, 0x7f80, 0x1bf2000000000000, 0x3f00, 0x2a0000003000000, 0x7ffe000000000000, 0x1ffffffffeffe0df, 0x40, 0x66fde00000000000, 0x1e0001c3000000, 0x20002064, 0x0, 0x0, 0xe0000000, 0x0, 0x0, 0x1c0000001c0000, 0xc0000000c0000, 0x3fb0000000000000, 0x200ffe40, 0x3800, 0x0, 0x20000000000, 0x0, 0xe04018700000000, 0x0, 0x0, 0x0, 0x9800000, 0x9ff81fe57f400000, 0x0, 0x0, 0x17d000000000000f, 0xff80000000004, 0xb3c00000003, 0x3a34000000000, 0xcff00000000000, 0x0, 0x0, 0x1021fdfff70000, 0x0, 0x0, 0x0, 0xf000007fffffffff, 0x3000, 0x0, 0x0, 0x1ffffffff0000, 0x0, 0x0, 0x0, 0x3800000000000, 0x0, 0x8000000000000000, 0x0, 0xffffffff00000000, 0xfc0000000000, 0x0, 0x6000000, 0x0, 0x0, 0x3ff7800000000000, 0x80000000, 0x3000000000000, 0x6000000844, 0x0, 0x0, 0x3ffff00000010, 0x3fc000000000, 0x3ff80, 0x13c8000000000007, 0x0, 0x667e0000000000, 0x1008, 0xc19d000000000000, 0x40300000000002, 0x0, 0x0, 0x0, 0x212000000000, 0x40000000, 0x0, 0x0, 0x0, 0x7f0000ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, 0x2000000000000000, 0x870000000000f06e, 0x0, 0x0, 0x0, 0xff00000000000002, 0x7f, 0x678000000000003, 0x0, 0x1fef8000000007, 0x0, 0x7fc0000000000003, 0x0, 0x0, 0x0, 0xbf280000000000, 0x0, 0x0, 0x0, 0x78000, 0x0, 0x0, 0xf807c3a000000000, 0x3c0000000fe7, 0x0, 0x0, 0x1c, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0]); +package(std): -} +static if (size_t.sizeof == 8) +{ + //832 bytes + enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x40], + [0x100, 0x80, 0xa00], [0x2010000000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x4000300020001, 0x1000700060005, 0x5000400030002, 0x2000100070006, + 0x6000500040003, 0x3000200010007, 0x7000600050004, 0x4000300020001, + 0x1000700060005, 0x5000400030002, 0x8000100070006, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x100000010000001, 0x1000000100000, 0x10000001000, + 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, + 0x100000010000001, 0x1000000100000, 0x10000001000, + 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, + 0x100000010000001, 0x1000000100000, 0x10000001000, + 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, + 0x100000010000001, 0x1000000100000, 0x10000001000, + 0x1000000100000010, 0x10000001000000, 0x100000010000, 0x1000000100, + 0x10000001000000, 0x100000010000, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0]); + //832 bytes + enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x40], + [0x100, 0x80, 0xa00], [0x2010000000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x4000300020001, 0x1000700060005, 0x5000400030002, 0x2000100070006, + 0x6000500040003, 0x3000200010007, 0x7000600050004, 0x4000300020001, + 0x1000700060005, 0x5000400030002, 0x8000100070006, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xfeffffffeffffffe, 0xfffeffffffefffff, 0xfffffeffffffefff, + 0xeffffffeffffffef, 0xffeffffffeffffff, 0xffffeffffffeffff, + 0xffffffeffffffeff, 0xfeffffffeffffffe, 0xfffeffffffefffff, + 0xfffffeffffffefff, 0xeffffffeffffffef, 0xffeffffffeffffff, + 0xffffeffffffeffff, 0xffffffeffffffeff, 0xfeffffffeffffffe, + 0xfffeffffffefffff, 0xfffffeffffffefff, 0xeffffffeffffffef, + 0xffeffffffeffffff, 0xffffeffffffeffff, 0xffffffeffffffeff, + 0xfeffffffeffffffe, 0xfffeffffffefffff, 0xfffffeffffffefff, + 0xeffffffeffffffef, 0xffeffffffeffffff, 0xffffeffffffeffff, + 0xffffffeffffffeff, 0xffeffffffeffffff, 0xffffeffffffeffff, + 0xffffffeff, 0x0, 0x0, 0x0, 0x0, 0x0]); + //1536 bytes + enum mcTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x60], [0x100, + 0x100, 0x1800], [0x202030202020100, 0x206020205020204, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3000200010000, 0x6000000050004, 0x7, 0x8000000000000, + 0xb000a00090000, 0xc, 0x0, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x110010000f000e, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130012, 0x1400000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x15000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x160000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc800000000000008, 0xde01, + 0xc00000000000000c, 0x801981, 0xc000000000000008, 0x1, + 0xc000000000000008, 0x1a01, 0x400000000000000c, 0x801981, + 0xc000000000000000, 0x801dc6, 0xe, 0x1e, 0x400000000000000c, + 0x600d9f, 0xc00000000000000c, 0x801dc1, 0xc, 0xc0000ff038000, + 0xc000000000000000, 0x8000000000000000, 0x0, 0x0, + 0x1902180000000000, 0x3f9c00c00000, 0x1c009f98, 0x0, 0x0, 0x0, + 0xc040000000000000, 0x1bf, 0x1fb0e7800000000, 0x0, + 0xffff000000000000, 0x301, 0x6000000, 0x7e01a00a00000, 0x0, 0x0, + 0xe820000000000010, 0x1b, 0x34c200000004, 0xc5c8000000000, + 0x300ff000000000, 0x0, 0x0, 0xc000200000000, 0xc00000000000, 0x0, + 0x0, 0x0, 0x9800000000, 0x0, 0xfff0000000000003, 0xf, 0x0, 0xc0000, + 0xec30000000000008, 0x1, 0x19800000000000, 0x800000000002000, 0x0, + 0x20c80000000000, 0x0, 0x0, 0x0, 0x16d800000000, 0x5, 0x0, + 0x187000000000004, 0x0, 0x100000000000, 0x0, 0x8038000000000004, + 0x1, 0x0, 0x0, 0x40d00000000000, 0x0, 0x0, 0x7ffffffffffe0000, 0x0, + 0x0, 0x0, 0x7e06000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //2336 bytes + enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, + 0x70], [0x100, 0x140, 0x2d00], [0x402030202020100, + 0x207020206020205, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020208, 0x202020202020202, + 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000, 0x5000400030002, + 0x9000800070006, 0xd000c000b000a, 0xf00000000000e, + 0x10000000000000, 0x14001300120011, 0x160015, 0x17, 0x0, 0x0, + 0x190018, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1b00000000, 0x1f001e001d001c, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000, 0x22002100000000, + 0x230000, 0x0, 0x2400000000, 0x0, 0x260025, 0x2700000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x28000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x2a00290000, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0, + 0x3f8, 0x0, 0x0, 0x0, 0xbffffffffffe0000, 0xb6, 0x7ff0000, + 0x10000fffff800, 0x0, 0x3d9f9fc00000, 0xffff000000020000, 0x7ff, + 0x1ffc000000000, 0xff80000000000, 0x3eeffbc00000, 0xe000000, 0x0, + 0x7ffffff000000000, 0x1400000000000007, 0xc00fe21fe, + 0x5000000000000002, 0xc0080201e, 0x1000000000000006, + 0x23000000023986, 0x1000000000000006, 0xc000021be, + 0xd000000000000002, 0xc00c0201e, 0x4000000000000004, 0x802001, + 0xc000000000000000, 0xc00603dc1, 0x9000000000000000, 0xc00603044, + 0x4000000000000000, 0xc0080201e, 0x0, 0x805c8400, + 0x7f2000000000000, 0x7f80, 0x1bf2000000000000, 0x3f00, + 0x2a0000003000000, 0x7ffe000000000000, 0x1ffffffffeffe0df, 0x40, + 0x66fde00000000000, 0x1e0001c3000000, 0x20002064, 0x0, 0x0, + 0xe0000000, 0x0, 0x0, 0x1c0000001c0000, 0xc0000000c0000, + 0x3fb0000000000000, 0x200ffe40, 0x3800, 0x0, 0x20000000000, 0x0, + 0xe04018700000000, 0x0, 0x0, 0x0, 0x9800000, 0x9ff81fe57f400000, + 0x0, 0x0, 0x17d000000000000f, 0xff80000000004, 0xb3c00000003, + 0x3a34000000000, 0xcff00000000000, 0x0, 0x0, 0x1021fdfff70000, 0x0, + 0x0, 0x0, 0xf000007fffffffff, 0x3000, 0x0, 0x0, 0x1ffffffff0000, + 0x0, 0x0, 0x0, 0x3800000000000, 0x0, 0x8000000000000000, 0x0, + 0xffffffff00000000, 0xfc0000000000, 0x0, 0x6000000, 0x0, 0x0, + 0x3ff7800000000000, 0x80000000, 0x3000000000000, 0x6000000844, 0x0, + 0x0, 0x3ffff00000010, 0x3fc000000000, 0x3ff80, 0x13c8000000000007, + 0x0, 0x667e0000000000, 0x1008, 0xc19d000000000000, + 0x40300000000002, 0x0, 0x0, 0x0, 0x212000000000, 0x40000000, 0x0, + 0x0, 0x0, 0x7f0000ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, + 0x0, 0x0, 0x0, 0x2000000000000000, 0x870000000000f06e, 0x0, 0x0, + 0x0, 0xff00000000000002, 0x7f, 0x678000000000003, 0x0, + 0x1fef8000000007, 0x0, 0x7fc0000000000003, 0x0, 0x0, 0x0, + 0xbf280000000000, 0x0, 0x0, 0x0, 0x78000, 0x0, 0x0, + 0xf807c3a000000000, 0x3c0000000fe7, 0x0, 0x0, 0x1c, 0x0, 0x0, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffff, 0x0, 0x0, 0x0, 0x0]); +} -static if(size_t.sizeof == 4) { -//832 bytes -enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0x80], [ 0x100, 0x80, 0xa00], [ 0x0, 0x20100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, 0x80001, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x10, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//832 bytes -enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0x80], [ 0x100, 0x80, 0xa00], [ 0x0, 0x20100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, 0x80001, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//1536 bytes -enum mcTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xc0], [ 0x100, 0x100, 0x1800], [ 0x2020100, 0x2020302, 0x5020204, 0x2060202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x60000, 0x7, 0x0, 0x0, 0x80000, 0x90000, 0xb000a, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf000e, 0x110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130012, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc8000000, 0xde01, 0x0, 0xc, 0xc0000000, 0x801981, 0x0, 0x8, 0xc0000000, 0x1, 0x0, 0x8, 0xc0000000, 0x1a01, 0x0, 0xc, 0x40000000, 0x801981, 0x0, 0x0, 0xc0000000, 0x801dc6, 0x0, 0xe, 0x0, 0x1e, 0x0, 0xc, 0x40000000, 0x600d9f, 0x0, 0xc, 0xc0000000, 0x801dc1, 0x0, 0xc, 0x0, 0xff038000, 0xc0000, 0x0, 0xc0000000, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19021800, 0xc00000, 0x3f9c, 0x1c009f98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400000, 0x1bf, 0x0, 0x0, 0x1fb0e78, 0x0, 0x0, 0x0, 0xffff0000, 0x301, 0x0, 0x6000000, 0x0, 0xa00000, 0x7e01a, 0x0, 0x0, 0x0, 0x0, 0x10, 0xe8200000, 0x1b, 0x0, 0x4, 0x34c2, 0x0, 0xc5c80, 0x0, 0x300ff0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0002, 0x0, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x3, 0xfff00000, 0xf, 0x0, 0x0, 0x0, 0xc0000, 0x0, 0x8, 0xec300000, 0x1, 0x0, 0x0, 0x198000, 0x2000, 0x8000000, 0x0, 0x0, 0x0, 0x20c800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16d8, 0x5, 0x0, 0x0, 0x0, 0x4, 0x1870000, 0x0, 0x0, 0x0, 0x1000, 0x0, 0x0, 0x4, 0x80380000, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40d000, 0x0, 0x0, 0x0, 0x0, 0xfffe0000, 0x7fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e060, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//2336 bytes -enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xe0], [ 0x100, 0x140, 0x2d00], [ 0x2020100, 0x4020302, 0x6020205, 0x2070202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020208, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0xe, 0xf0000, 0x0, 0x100000, 0x120011, 0x140013, 0x160015, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x190018, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x220021, 0x230000, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x260025, 0x0, 0x0, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x280000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x290000, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffe0000, 0xbfffffff, 0xb6, 0x0, 0x7ff0000, 0x0, 0xfffff800, 0x10000, 0x0, 0x0, 0x9fc00000, 0x3d9f, 0x20000, 0xffff0000, 0x7ff, 0x0, 0x0, 0x1ffc0, 0x0, 0xff800, 0xfbc00000, 0x3eef, 0xe000000, 0x0, 0x0, 0x0, 0x0, 0x7ffffff0, 0x7, 0x14000000, 0xfe21fe, 0xc, 0x2, 0x50000000, 0x80201e, 0xc, 0x6, 0x10000000, 0x23986, 0x230000, 0x6, 0x10000000, 0x21be, 0xc, 0x2, 0xd0000000, 0xc0201e, 0xc, 0x4, 0x40000000, 0x802001, 0x0, 0x0, 0xc0000000, 0x603dc1, 0xc, 0x0, 0x90000000, 0x603044, 0xc, 0x0, 0x40000000, 0x80201e, 0xc, 0x0, 0x0, 0x805c8400, 0x0, 0x0, 0x7f20000, 0x7f80, 0x0, 0x0, 0x1bf20000, 0x3f00, 0x0, 0x3000000, 0x2a00000, 0x0, 0x7ffe0000, 0xfeffe0df, 0x1fffffff, 0x40, 0x0, 0x0, 0x66fde000, 0xc3000000, 0x1e0001, 0x20002064, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c0000, 0x1c0000, 0xc0000, 0xc0000, 0x0, 0x3fb00000, 0x200ffe40, 0x0, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0x0, 0xe040187, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9800000, 0x0, 0x7f400000, 0x9ff81fe5, 0x0, 0x0, 0x0, 0x0, 0xf, 0x17d00000, 0x4, 0xff800, 0x3, 0xb3c, 0x0, 0x3a340, 0x0, 0xcff000, 0x0, 0x0, 0x0, 0x0, 0xfff70000, 0x1021fd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xf000007f, 0x3000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x1ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38000, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0xffffffff, 0x0, 0xfc00, 0x0, 0x0, 0x6000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff78000, 0x80000000, 0x0, 0x0, 0x30000, 0x844, 0x60, 0x0, 0x0, 0x0, 0x0, 0x10, 0x3ffff, 0x0, 0x3fc0, 0x3ff80, 0x0, 0x7, 0x13c80000, 0x0, 0x0, 0x0, 0x667e00, 0x1008, 0x0, 0x0, 0xc19d0000, 0x2, 0x403000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2120, 0x40000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000, 0xf06e, 0x87000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff000000, 0x7f, 0x0, 0x3, 0x6780000, 0x0, 0x0, 0x7, 0x1fef80, 0x0, 0x0, 0x3, 0x7fc00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf2800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf807c3a0, 0xfe7, 0x3c00, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +static if (size_t.sizeof == 4) +{ + //832 bytes + enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0x80], + [0x100, 0x80, 0xa00], [0x0, 0x20100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, + 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, + 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, + 0x80001, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x10000001, 0x1000000, 0x100000, 0x10000, + 0x1000, 0x100, 0x10, 0x1000000, 0x100000, 0x10000, 0x1000, 0x100, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //832 bytes + enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0x80], + [0x100, 0x80, 0xa00], [0x0, 0x20100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, + 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, + 0x20001, 0x40003, 0x60005, 0x10007, 0x30002, 0x50004, 0x70006, + 0x80001, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, + 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, + 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, + 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, + 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, + 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, + 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, + 0xffffffef, 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, + 0xffffefff, 0xfffffeff, 0xffffffef, 0xeffffffe, 0xfeffffff, + 0xffefffff, 0xfffeffff, 0xffffefff, 0xfffffeff, 0xffffffef, + 0xeffffffe, 0xfeffffff, 0xffefffff, 0xfffeffff, 0xffffefff, + 0xfffffeff, 0xffffffef, 0xfeffffff, 0xffefffff, 0xfffeffff, + 0xffffefff, 0xfffffeff, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0]); + //1536 bytes + enum mcTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xc0], [0x100, + 0x100, 0x1800], [0x2020100, 0x2020302, 0x5020204, 0x2060202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, + 0x60000, 0x7, 0x0, 0x0, 0x80000, 0x90000, 0xb000a, 0xc, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf000e, 0x110010, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x130012, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x160000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc8000000, 0xde01, 0x0, + 0xc, 0xc0000000, 0x801981, 0x0, 0x8, 0xc0000000, 0x1, 0x0, 0x8, + 0xc0000000, 0x1a01, 0x0, 0xc, 0x40000000, 0x801981, 0x0, 0x0, + 0xc0000000, 0x801dc6, 0x0, 0xe, 0x0, 0x1e, 0x0, 0xc, 0x40000000, + 0x600d9f, 0x0, 0xc, 0xc0000000, 0x801dc1, 0x0, 0xc, 0x0, + 0xff038000, 0xc0000, 0x0, 0xc0000000, 0x0, 0x80000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x19021800, 0xc00000, 0x3f9c, 0x1c009f98, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400000, 0x1bf, 0x0, 0x0, + 0x1fb0e78, 0x0, 0x0, 0x0, 0xffff0000, 0x301, 0x0, 0x6000000, 0x0, + 0xa00000, 0x7e01a, 0x0, 0x0, 0x0, 0x0, 0x10, 0xe8200000, 0x1b, 0x0, + 0x4, 0x34c2, 0x0, 0xc5c80, 0x0, 0x300ff0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xc0002, 0x0, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x98, 0x0, + 0x0, 0x3, 0xfff00000, 0xf, 0x0, 0x0, 0x0, 0xc0000, 0x0, 0x8, + 0xec300000, 0x1, 0x0, 0x0, 0x198000, 0x2000, 0x8000000, 0x0, 0x0, + 0x0, 0x20c800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16d8, 0x5, 0x0, + 0x0, 0x0, 0x4, 0x1870000, 0x0, 0x0, 0x0, 0x1000, 0x0, 0x0, 0x4, + 0x80380000, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40d000, 0x0, 0x0, + 0x0, 0x0, 0xfffe0000, 0x7fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x7e060, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //2336 bytes + enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, + 0xe0], [0x100, 0x140, 0x2d00], [0x2020100, 0x4020302, 0x6020205, + 0x2070202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020208, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, + 0x70006, 0x90008, 0xb000a, 0xd000c, 0xe, 0xf0000, 0x0, 0x100000, + 0x120011, 0x140013, 0x160015, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x190018, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x220021, 0x230000, + 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x260025, 0x0, 0x0, 0x27, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x280000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x290000, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xfffe0000, 0xbfffffff, 0xb6, 0x0, 0x7ff0000, 0x0, 0xfffff800, + 0x10000, 0x0, 0x0, 0x9fc00000, 0x3d9f, 0x20000, 0xffff0000, 0x7ff, + 0x0, 0x0, 0x1ffc0, 0x0, 0xff800, 0xfbc00000, 0x3eef, 0xe000000, + 0x0, 0x0, 0x0, 0x0, 0x7ffffff0, 0x7, 0x14000000, 0xfe21fe, 0xc, + 0x2, 0x50000000, 0x80201e, 0xc, 0x6, 0x10000000, 0x23986, 0x230000, + 0x6, 0x10000000, 0x21be, 0xc, 0x2, 0xd0000000, 0xc0201e, 0xc, 0x4, + 0x40000000, 0x802001, 0x0, 0x0, 0xc0000000, 0x603dc1, 0xc, 0x0, + 0x90000000, 0x603044, 0xc, 0x0, 0x40000000, 0x80201e, 0xc, 0x0, + 0x0, 0x805c8400, 0x0, 0x0, 0x7f20000, 0x7f80, 0x0, 0x0, 0x1bf20000, + 0x3f00, 0x0, 0x3000000, 0x2a00000, 0x0, 0x7ffe0000, 0xfeffe0df, + 0x1fffffff, 0x40, 0x0, 0x0, 0x66fde000, 0xc3000000, 0x1e0001, + 0x20002064, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1c0000, 0x1c0000, 0xc0000, 0xc0000, 0x0, 0x3fb00000, + 0x200ffe40, 0x0, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0x0, + 0xe040187, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9800000, 0x0, + 0x7f400000, 0x9ff81fe5, 0x0, 0x0, 0x0, 0x0, 0xf, 0x17d00000, 0x4, + 0xff800, 0x3, 0xb3c, 0x0, 0x3a340, 0x0, 0xcff000, 0x0, 0x0, 0x0, + 0x0, 0xfff70000, 0x1021fd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xf000007f, 0x3000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffff0000, 0x1ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38000, + 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0xffffffff, 0x0, 0xfc00, + 0x0, 0x0, 0x6000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff78000, + 0x80000000, 0x0, 0x0, 0x30000, 0x844, 0x60, 0x0, 0x0, 0x0, 0x0, + 0x10, 0x3ffff, 0x0, 0x3fc0, 0x3ff80, 0x0, 0x7, 0x13c80000, 0x0, + 0x0, 0x0, 0x667e00, 0x1008, 0x0, 0x0, 0xc19d0000, 0x2, 0x403000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2120, 0x40000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x20000000, 0xf06e, 0x87000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2, 0xff000000, 0x7f, 0x0, 0x3, 0x6780000, 0x0, + 0x0, 0x7, 0x1fef80, 0x0, 0x0, 0x3, 0x7fc00000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xbf2800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf807c3a0, 0xfe7, 0x3c00, 0x0, 0x0, + 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); } - diff --git a/std/internal/unicode_norm.d b/std/internal/unicode_norm.d index 7d142d8b654..16059cd14fb 100644 --- a/std/internal/unicode_norm.d +++ b/std/internal/unicode_norm.d @@ -1,28 +1,548 @@ module std.internal.unicode_norm; import std.internal.unicode_tables; -static if(size_t.sizeof == 8) { -//1600 bytes -enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x60], [ 0x100, 0x100, 0x1a00], [ 0x302020202020100, 0x205020202020204, 0x602020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000, 0x200000000, 0x5000400030000, 0x8000000070006, 0xa0009, 0x0, 0xb000000000000, 0xc000000000000, 0xf0000000e000d, 0x0, 0x1000000000, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14001300120000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1800120012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10361f8081a9fdf, 0x401000000000003f, 0x80, 0x0, 0x0, 0x380000, 0x0, 0x0, 0x1000000000000000, 0xff000000, 0x4000000000000000, 0xb0800000, 0x48000000000000, 0x4e000000, 0x0, 0x0, 0x4000000000000000, 0x30c00000, 0x4000000000000000, 0x800000, 0x0, 0x400000, 0x0, 0x600004, 0x4000000000000000, 0x800000, 0x0, 0x80008400, 0x0, 0x168020010842008, 0x200108420080002, 0x0, 0x400000000000, 0x0, 0x0, 0x0, 0x0, 0x3ffffe00000000, 0xffffff0000000000, 0x7, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x2aaa000000000000, 0x4800000000000000, 0x2a00c80808080a00, 0x3, 0x0, 0x0, 0x0, 0xc4000000000, 0x0, 0x0, 0x0, 0x60000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x6000000, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffc657fe53fff, 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7ffc00a0000000, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x400000000000000, 0x0, 0x8000000000, 0x0, 0x0, 0x0, 0x0, 0x1fc0000000, 0xf800000000000000, 0x1, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//1920 bytes -enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x70], [ 0x100, 0x140, 0x2000], [ 0x504030202020100, 0x207020202020206, 0x802020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x5000600050004, 0x9000800070005, 0xc0005000b000a, 0x500050005000d, 0x5000500050005, 0xe000500050005, 0x10000f00050005, 0x14001300120011, 0x5000500050005, 0x5001500050005, 0x5000500050005, 0x5000500050016, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x17001700170017, 0x18001700170017, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x1a001900170005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x50005001c001b, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x50005001d0005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5001e00170017, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x0, 0x0, 0x0, 0xbe7effbf3e7effbf, 0x7ef1ff3ffffcffff, 0x7fffff3ffff3f1f8, 0x1800300000000, 0xff31ffcfdfffe000, 0xfffc0cfffffff, 0x0, 0x0, 0x0, 0x0, 0x401000000000001b, 0x1fc000001d7e0, 0x187c00, 0x20000000200708b, 0xc00000708b0000, 0x0, 0x33ffcfcfccf0006, 0x0, 0x0, 0x0, 0x0, 0x7c00000000, 0x0, 0x0, 0x80005, 0x12020000000000, 0xff000000, 0x0, 0xb0001800, 0x48000000000000, 0x4e000000, 0x0, 0x0, 0x0, 0x30001900, 0x100000, 0x1c00, 0x0, 0x100, 0x0, 0xd81, 0x0, 0x1c00, 0x0, 0x74000000, 0x0, 0x168020010842008, 0x200108420080002, 0x0, 0x4000000000, 0x0, 0x0, 0x0, 0x2800000000045540, 0xb, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0bffffff, 0x3ffffffffffffff, 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0x5fdfffffffffffff, 0x3fdcffffefcfffde, 0x3, 0x0, 0x0, 0x0, 0xc4000000000, 0x0, 0x40000c000000, 0xe000, 0x5000001210, 0x333e00500000292, 0xf00000000333, 0x3c0f00000000, 0x60000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x0, 0x36db02a555555000, 0x5555500040100000, 0x4790000036db02a5, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffff, 0x0, 0xfffffc657fe53fff, 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7ffc00a0000000, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x80014000000, 0x0, 0xc00000000000, 0x0, 0x0, 0x0, 0x0, 0x1fc0000000, 0xf800000000000000, 0x1, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//2560 bytes -enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x70], [ 0x100, 0x140, 0x3400], [ 0x402030202020100, 0x706020202020205, 0x802020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x4000600050004, 0x9000800070004, 0xd000c000b000a, 0x40004000f000e, 0x4000400040004, 0x10000400040004, 0x13001200110004, 0x17001600150014, 0x4000400040018, 0x4001900040004, 0x1d001c001b001a, 0x210020001f001e, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x22000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x24002300210004, 0x27002600250021, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400290028, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x40004002a0004, 0x2e002d002c002b, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4002f00040004, 0x4003100300004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4003200210021, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x0, 0x0, 0x773c850100000000, 0x0, 0x800c000000000000, 0x8000000000000201, 0x0, 0xe000000001ff0, 0x0, 0x0, 0x1ff000000000000, 0x1f3f000000, 0x10361f8081a9fdf, 0x441000000000003f, 0xb0, 0x2370000007f0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x1e0000000380000, 0x0, 0x0, 0x1000000000000000, 0xff000000, 0x4000000000000000, 0xb0800000, 0x48000000000000, 0x4e000000, 0x0, 0x0, 0x4000000000000000, 0x30c00000, 0x4000000000000000, 0x800000, 0x0, 0x400000, 0x0, 0x600004, 0x4000000000000000, 0x800000, 0x0, 0x80008400, 0x8000000000000, 0x0, 0x8000000000000, 0x30000000, 0x1000, 0x3e8020010842008, 0x200108420080002, 0x0, 0x400000000000, 0x0, 0x0, 0x1000000000000000, 0x0, 0x3ffffe00000000, 0xffffff0000000000, 0x7, 0x20000000000000, 0x0, 0x0, 0x0, 0xf7ff700000000000, 0x10007ffffffbfff, 0xfffffffff8000000, 0x0, 0x0, 0x0, 0xc000000, 0x0, 0x0, 0x2aaa000000000000, 0xe800000000000000, 0x6a00e808e808ea03, 0x50d88070008207ff, 0xfff3000080800380, 0x1001fff7fff, 0x0, 0xfbfbbd573e6ffeef, 0xffffffffffff03e1, 0x200, 0x0, 0x1b00000000000, 0x0, 0x0, 0x0, 0x60000000000, 0x0, 0x0, 0x0, 0x0, 0xffffffff00000000, 0xffffffffffffffff, 0x7ffffffffff, 0x1000, 0x70000000000000, 0x0, 0x10000000, 0x0, 0x3000000000000000, 0x0, 0x0, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x8000000000000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffff, 0x740000000000001, 0x0, 0x9e000000, 0x8000000000000000, 0xfffe000000000000, 0xffffffffffffffff, 0xfffc7fff, 0x0, 0xffffffff7fffffff, 0x7fffffffffff00ff, 0xffffffffffffffff, 0x7fffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x1000000000000, 0x0, 0x300000000000000, 0xfffffc657fe53fff, 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7fffffa0f8007f, 0xffffffffffffffdb, 0x3ffffffffffff, 0xfffffffffff80000, 0x3fffffffffffffff, 0xffffffffffff0000, 0xfffffffffffcffff, 0x1fff0000000000ff, 0xffff000003ff0000, 0xffd70f7ffff7ff9f, 0xffffffffffffffff, 0x1fffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, 0x7fffffffffffffff, 0x7f7f1cfcfcfc, 0x0, 0x0, 0x400000000000000, 0x0, 0x8000000000, 0x0, 0x0, 0x0, 0x0, 0x1fc0000000, 0xf800000000000000, 0x1, 0xffffffffffffffff, 0xffffffffffdfffff, 0xebffde64dfffffff, 0xffffffffffffffef, 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffff3fffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffcfff, 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, 0x0, 0xffff7fffffff07ff, 0xc000000ffff, 0x10000, 0x0, 0x7ffffffffff0007, 0x301ff, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//2656 bytes -enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x78], [ 0x100, 0x160, 0x3500], [ 0x504030202020100, 0x807020202020206, 0x902020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x7000600050004, 0xa000900080007, 0xe000d000c000b, 0x700070007000f, 0x7000700070007, 0x10000700070007, 0x13001200110007, 0x17001600150014, 0x7000700070018, 0x7001900070007, 0x1d001c001b001a, 0x210020001f001e, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x22000700070007, 0x7000700070007, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x21002100210021, 0x23002100210021, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x25002400210007, 0x28002700260021, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x70007002a0029, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x70007002b0007, 0x2f002e002d002c, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7003000070007, 0x7003200310007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7003300210021, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x0, 0x0, 0x773c850100000000, 0xbe7effbf3e7effbf, 0xfefdff3ffffcffff, 0xffffff3ffff3f3f9, 0x1800300000000, 0xff3fffcfdffffff0, 0xfffc0cfffffff, 0x0, 0x1ff000000000000, 0x1f3f000000, 0x0, 0x441000000000001b, 0x1fc000001d7f0, 0x2370000007f7c00, 0x20000000200708b, 0xc00000708b0000, 0x0, 0x33ffcfcfccf0006, 0x0, 0x0, 0x80, 0x0, 0x7c00000000, 0x1e0000000000000, 0x0, 0x80005, 0x0, 0x0, 0x0, 0x0, 0x12020000000000, 0xff000000, 0x0, 0xb0001800, 0x48000000000000, 0x4e000000, 0x0, 0x0, 0x0, 0x30001900, 0x100000, 0x1c00, 0x0, 0x100, 0x0, 0xd81, 0x0, 0x1c00, 0x0, 0x74000000, 0x8000000000000, 0x0, 0x8000000000000, 0x30000000, 0x1000, 0x3e8020010842008, 0x200108420080002, 0x0, 0x4000000000, 0x0, 0x0, 0x1000000000000000, 0x2800000000045540, 0xb, 0x0, 0x0, 0xf7ff700000000000, 0x10007ffffffbfff, 0xfffffffff8000000, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0fffffff, 0x3ffffffffffffff, 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0xffdfffffffffffff, 0x7fdcffffefcfffdf, 0x50d88070008207ff, 0xfff3000080800380, 0x1001fff7fff, 0x0, 0xfbfbbd573e6ffeef, 0xffffffffffff03e1, 0x40000c000200, 0xe000, 0x1b05000001210, 0x333e00500000292, 0xf00000000333, 0x3c0f00000000, 0x60000000000, 0x0, 0x0, 0x0, 0x0, 0xffffffff00000000, 0xffffffffffffffff, 0x7ffffffffff, 0x1000, 0x70000000000000, 0x0, 0x10000000, 0x0, 0x3000000000000000, 0x0, 0x0, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x8000000000000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffff, 0x740000000000001, 0x36db02a555555000, 0x55555000d8100000, 0xc790000036db02a5, 0xfffe000000000000, 0xffffffffffffffff, 0xfffc7fff, 0x0, 0xffffffff7fffffff, 0x7fffffffffff00ff, 0xffffffffffffffff, 0x7fffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x1000000000000, 0x0, 0x300000000000000, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffff, 0x0, 0xfffffc657fe53fff, 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7fffffa0f8007f, 0xffffffffffffffdb, 0x3ffffffffffff, 0xfffffffffff80000, 0x3fffffffffffffff, 0xffffffffffff0000, 0xfffffffffffcffff, 0x1fff0000000000ff, 0xffff000003ff0000, 0xffd70f7ffff7ff9f, 0xffffffffffffffff, 0x1fffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, 0x7fffffffffffffff, 0x7f7f1cfcfcfc, 0x0, 0x0, 0x80014000000, 0x0, 0xc00000000000, 0x0, 0x0, 0x0, 0x0, 0x1fc0000000, 0xf800000000000000, 0x1, 0xffffffffffffffff, 0xffffffffffdfffff, 0xebffde64dfffffff, 0xffffffffffffffef, 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffff3fffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffcfff, 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, 0x0, 0xffff7fffffff07ff, 0xc000000ffff, 0x10000, 0x0, 0x7ffffffffff0007, 0x301ff, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +package(std): -} +static if (size_t.sizeof == 8) +{ + //1600 bytes + enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x60], + [0x100, 0x100, 0x1a00], [0x302020202020100, 0x205020202020204, + 0x602020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1000000000000, 0x200000000, 0x5000400030000, 0x8000000070006, + 0xa0009, 0x0, 0xb000000000000, 0xc000000000000, 0xf0000000e000d, + 0x0, 0x1000000000, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14001300120000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x170000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1800120012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x10361f8081a9fdf, 0x401000000000003f, 0x80, 0x0, + 0x0, 0x380000, 0x0, 0x0, 0x1000000000000000, 0xff000000, + 0x4000000000000000, 0xb0800000, 0x48000000000000, 0x4e000000, 0x0, + 0x0, 0x4000000000000000, 0x30c00000, 0x4000000000000000, 0x800000, + 0x0, 0x400000, 0x0, 0x600004, 0x4000000000000000, 0x800000, 0x0, + 0x80008400, 0x0, 0x168020010842008, 0x200108420080002, 0x0, + 0x400000000000, 0x0, 0x0, 0x0, 0x0, 0x3ffffe00000000, + 0xffffff0000000000, 0x7, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, + 0x2aaa000000000000, 0x4800000000000000, 0x2a00c80808080a00, 0x3, + 0x0, 0x0, 0x0, 0xc4000000000, 0x0, 0x0, 0x0, 0x60000000000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x6000000, 0x0, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfffffc657fe53fff, 0xffff3fffffffffff, + 0xffffffffffffffff, 0x3ffffff, 0x5f7ffc00a0000000, 0x7fdb, 0x0, + 0x0, 0x0, 0x0, 0x400000000000000, 0x0, 0x8000000000, 0x0, 0x0, 0x0, + 0x0, 0x1fc0000000, 0xf800000000000000, 0x1, 0x3fffffff, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0]); + //1920 bytes + enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x70], + [0x100, 0x140, 0x2000], [0x504030202020100, 0x207020202020206, + 0x802020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x5000600050004, 0x9000800070005, 0xc0005000b000a, + 0x500050005000d, 0x5000500050005, 0xe000500050005, + 0x10000f00050005, 0x14001300120011, 0x5000500050005, + 0x5001500050005, 0x5000500050005, 0x5000500050016, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x17001700170017, 0x17001700170017, + 0x17001700170017, 0x17001700170017, 0x17001700170017, + 0x17001700170017, 0x17001700170017, 0x17001700170017, + 0x17001700170017, 0x17001700170017, 0x18001700170017, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x1a001900170005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x50005001c001b, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x50005001d0005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5001e00170017, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x5000500050005, + 0x5000500050005, 0x5000500050005, 0x5000500050005, 0x0, 0x0, 0x0, + 0xbe7effbf3e7effbf, 0x7ef1ff3ffffcffff, 0x7fffff3ffff3f1f8, + 0x1800300000000, 0xff31ffcfdfffe000, 0xfffc0cfffffff, 0x0, 0x0, + 0x0, 0x0, 0x401000000000001b, 0x1fc000001d7e0, 0x187c00, + 0x20000000200708b, 0xc00000708b0000, 0x0, 0x33ffcfcfccf0006, 0x0, + 0x0, 0x0, 0x0, 0x7c00000000, 0x0, 0x0, 0x80005, 0x12020000000000, + 0xff000000, 0x0, 0xb0001800, 0x48000000000000, 0x4e000000, 0x0, + 0x0, 0x0, 0x30001900, 0x100000, 0x1c00, 0x0, 0x100, 0x0, 0xd81, + 0x0, 0x1c00, 0x0, 0x74000000, 0x0, 0x168020010842008, + 0x200108420080002, 0x0, 0x4000000000, 0x0, 0x0, 0x0, + 0x2800000000045540, 0xb, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff0bffffff, 0x3ffffffffffffff, + 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0x5fdfffffffffffff, + 0x3fdcffffefcfffde, 0x3, 0x0, 0x0, 0x0, 0xc4000000000, 0x0, + 0x40000c000000, 0xe000, 0x5000001210, 0x333e00500000292, + 0xf00000000333, 0x3c0f00000000, 0x60000000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x10000000, 0x0, 0x36db02a555555000, 0x5555500040100000, + 0x4790000036db02a5, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfffffffff, 0x0, 0xfffffc657fe53fff, + 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, + 0x5f7ffc00a0000000, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x80014000000, 0x0, + 0xc00000000000, 0x0, 0x0, 0x0, 0x0, 0x1fc0000000, + 0xf800000000000000, 0x1, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //2560 bytes + enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x70], + [0x100, 0x140, 0x3400], [0x402030202020100, 0x706020202020205, + 0x802020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x4000600050004, 0x9000800070004, 0xd000c000b000a, + 0x40004000f000e, 0x4000400040004, 0x10000400040004, + 0x13001200110004, 0x17001600150014, 0x4000400040018, + 0x4001900040004, 0x1d001c001b001a, 0x210020001f001e, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x22000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x24002300210004, + 0x27002600250021, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400290028, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x40004002a0004, + 0x2e002d002c002b, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4002f00040004, + 0x4003100300004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4003200210021, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x4000400040004, 0x4000400040004, 0x4000400040004, 0x4000400040004, + 0x0, 0x0, 0x773c850100000000, 0x0, 0x800c000000000000, + 0x8000000000000201, 0x0, 0xe000000001ff0, 0x0, 0x0, + 0x1ff000000000000, 0x1f3f000000, 0x10361f8081a9fdf, + 0x441000000000003f, 0xb0, 0x2370000007f0000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x80, 0x0, 0x0, 0x1e0000000380000, 0x0, 0x0, + 0x1000000000000000, 0xff000000, 0x4000000000000000, 0xb0800000, + 0x48000000000000, 0x4e000000, 0x0, 0x0, 0x4000000000000000, + 0x30c00000, 0x4000000000000000, 0x800000, 0x0, 0x400000, 0x0, + 0x600004, 0x4000000000000000, 0x800000, 0x0, 0x80008400, + 0x8000000000000, 0x0, 0x8000000000000, 0x30000000, 0x1000, + 0x3e8020010842008, 0x200108420080002, 0x0, 0x400000000000, 0x0, + 0x0, 0x1000000000000000, 0x0, 0x3ffffe00000000, 0xffffff0000000000, + 0x7, 0x20000000000000, 0x0, 0x0, 0x0, 0xf7ff700000000000, + 0x10007ffffffbfff, 0xfffffffff8000000, 0x0, 0x0, 0x0, 0xc000000, + 0x0, 0x0, 0x2aaa000000000000, 0xe800000000000000, + 0x6a00e808e808ea03, 0x50d88070008207ff, 0xfff3000080800380, + 0x1001fff7fff, 0x0, 0xfbfbbd573e6ffeef, 0xffffffffffff03e1, 0x200, + 0x0, 0x1b00000000000, 0x0, 0x0, 0x0, 0x60000000000, 0x0, 0x0, 0x0, + 0x0, 0xffffffff00000000, 0xffffffffffffffff, 0x7ffffffffff, 0x1000, + 0x70000000000000, 0x0, 0x10000000, 0x0, 0x3000000000000000, 0x0, + 0x0, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0x0, 0x80000000, + 0x8000000000000, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3fffff, 0x740000000000001, 0x0, 0x9e000000, + 0x8000000000000000, 0xfffe000000000000, 0xffffffffffffffff, + 0xfffc7fff, 0x0, 0xffffffff7fffffff, 0x7fffffffffff00ff, + 0xffffffffffffffff, 0x7fffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, + 0x1000000000000, 0x0, 0x300000000000000, 0xfffffc657fe53fff, + 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, + 0x5f7fffffa0f8007f, 0xffffffffffffffdb, 0x3ffffffffffff, + 0xfffffffffff80000, 0x3fffffffffffffff, 0xffffffffffff0000, + 0xfffffffffffcffff, 0x1fff0000000000ff, 0xffff000003ff0000, + 0xffd70f7ffff7ff9f, 0xffffffffffffffff, 0x1fffffffffffffff, + 0xfffffffffffffffe, 0xffffffffffffffff, 0x7fffffffffffffff, + 0x7f7f1cfcfcfc, 0x0, 0x0, 0x400000000000000, 0x0, 0x8000000000, + 0x0, 0x0, 0x0, 0x0, 0x1fc0000000, 0xf800000000000000, 0x1, + 0xffffffffffffffff, 0xffffffffffdfffff, 0xebffde64dfffffff, + 0xffffffffffffffef, 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffff3fffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffcfff, 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, + 0xffffbee0ffffbff, 0x0, 0xffff7fffffff07ff, 0xc000000ffff, 0x10000, + 0x0, 0x7ffffffffff0007, 0x301ff, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0]); + //2656 bytes + enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x78], + [0x100, 0x160, 0x3500], [0x504030202020100, 0x807020202020206, + 0x902020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x7000600050004, 0xa000900080007, 0xe000d000c000b, + 0x700070007000f, 0x7000700070007, 0x10000700070007, + 0x13001200110007, 0x17001600150014, 0x7000700070018, + 0x7001900070007, 0x1d001c001b001a, 0x210020001f001e, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x22000700070007, 0x7000700070007, 0x21002100210021, + 0x21002100210021, 0x21002100210021, 0x21002100210021, + 0x21002100210021, 0x21002100210021, 0x21002100210021, + 0x21002100210021, 0x21002100210021, 0x21002100210021, + 0x23002100210021, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x25002400210007, + 0x28002700260021, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x70007002a0029, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x70007002b0007, + 0x2f002e002d002c, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7003000070007, + 0x7003200310007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7003300210021, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x7000700070007, 0x7000700070007, 0x7000700070007, 0x7000700070007, + 0x0, 0x0, 0x773c850100000000, 0xbe7effbf3e7effbf, + 0xfefdff3ffffcffff, 0xffffff3ffff3f3f9, 0x1800300000000, + 0xff3fffcfdffffff0, 0xfffc0cfffffff, 0x0, 0x1ff000000000000, + 0x1f3f000000, 0x0, 0x441000000000001b, 0x1fc000001d7f0, + 0x2370000007f7c00, 0x20000000200708b, 0xc00000708b0000, 0x0, + 0x33ffcfcfccf0006, 0x0, 0x0, 0x80, 0x0, 0x7c00000000, + 0x1e0000000000000, 0x0, 0x80005, 0x0, 0x0, 0x0, 0x0, + 0x12020000000000, 0xff000000, 0x0, 0xb0001800, 0x48000000000000, + 0x4e000000, 0x0, 0x0, 0x0, 0x30001900, 0x100000, 0x1c00, 0x0, + 0x100, 0x0, 0xd81, 0x0, 0x1c00, 0x0, 0x74000000, 0x8000000000000, + 0x0, 0x8000000000000, 0x30000000, 0x1000, 0x3e8020010842008, + 0x200108420080002, 0x0, 0x4000000000, 0x0, 0x0, 0x1000000000000000, + 0x2800000000045540, 0xb, 0x0, 0x0, 0xf7ff700000000000, + 0x10007ffffffbfff, 0xfffffffff8000000, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff0fffffff, 0x3ffffffffffffff, + 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0xffdfffffffffffff, + 0x7fdcffffefcfffdf, 0x50d88070008207ff, 0xfff3000080800380, + 0x1001fff7fff, 0x0, 0xfbfbbd573e6ffeef, 0xffffffffffff03e1, + 0x40000c000200, 0xe000, 0x1b05000001210, 0x333e00500000292, + 0xf00000000333, 0x3c0f00000000, 0x60000000000, 0x0, 0x0, 0x0, 0x0, + 0xffffffff00000000, 0xffffffffffffffff, 0x7ffffffffff, 0x1000, + 0x70000000000000, 0x0, 0x10000000, 0x0, 0x3000000000000000, 0x0, + 0x0, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0x0, 0x80000000, + 0x8000000000000, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3fffff, 0x740000000000001, + 0x36db02a555555000, 0x55555000d8100000, 0xc790000036db02a5, + 0xfffe000000000000, 0xffffffffffffffff, 0xfffc7fff, 0x0, + 0xffffffff7fffffff, 0x7fffffffffff00ff, 0xffffffffffffffff, + 0x7fffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x1000000000000, 0x0, + 0x300000000000000, 0xffffffffffffffff, 0xffffffffffffffff, + 0xfffffffff, 0x0, 0xfffffc657fe53fff, 0xffff3fffffffffff, + 0xffffffffffffffff, 0x3ffffff, 0x5f7fffffa0f8007f, + 0xffffffffffffffdb, 0x3ffffffffffff, 0xfffffffffff80000, + 0x3fffffffffffffff, 0xffffffffffff0000, 0xfffffffffffcffff, + 0x1fff0000000000ff, 0xffff000003ff0000, 0xffd70f7ffff7ff9f, + 0xffffffffffffffff, 0x1fffffffffffffff, 0xfffffffffffffffe, + 0xffffffffffffffff, 0x7fffffffffffffff, 0x7f7f1cfcfcfc, 0x0, 0x0, + 0x80014000000, 0x0, 0xc00000000000, 0x0, 0x0, 0x0, 0x0, + 0x1fc0000000, 0xf800000000000000, 0x1, 0xffffffffffffffff, + 0xffffffffffdfffff, 0xebffde64dfffffff, 0xffffffffffffffef, + 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffff3fffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffcfff, + 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, 0x0, + 0xffff7fffffff07ff, 0xc000000ffff, 0x10000, 0x0, 0x7ffffffffff0007, + 0x301ff, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +} -static if(size_t.sizeof == 4) { -//1600 bytes -enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xc0], [ 0x100, 0x100, 0x1a00], [ 0x2020100, 0x3020202, 0x2020204, 0x2050202, 0x2020202, 0x6020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x2, 0x30000, 0x50004, 0x70006, 0x80000, 0xa0009, 0x0, 0x0, 0x0, 0x0, 0xb0000, 0x0, 0xc0000, 0xe000d, 0xf0000, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120000, 0x140013, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120012, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x81a9fdf, 0x10361f8, 0x3f, 0x40100000, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x380000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0xff000000, 0x0, 0x0, 0x40000000, 0xb0800000, 0x0, 0x0, 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, 0x30c00000, 0x0, 0x0, 0x40000000, 0x800000, 0x0, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x600004, 0x0, 0x0, 0x40000000, 0x800000, 0x0, 0x0, 0x0, 0x80008400, 0x0, 0x0, 0x0, 0x10842008, 0x1680200, 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ffffe, 0x0, 0xffffff00, 0x7, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2aaa0000, 0x0, 0x48000000, 0x8080a00, 0x2a00c808, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6000000, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fe53fff, 0xfffffc65, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xa0000000, 0x5f7ffc00, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//1920 bytes -enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xe0], [ 0x100, 0x140, 0x2000], [ 0x2020100, 0x5040302, 0x2020206, 0x2070202, 0x2020202, 0x8020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x50006, 0x70005, 0x90008, 0xb000a, 0xc0005, 0x5000d, 0x50005, 0x50005, 0x50005, 0x50005, 0xe0005, 0x50005, 0x10000f, 0x120011, 0x140013, 0x50005, 0x50005, 0x50005, 0x50015, 0x50005, 0x50005, 0x50016, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x180017, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x170005, 0x1a0019, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x1c001b, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x1d0005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x170017, 0x5001e, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e7effbf, 0xbe7effbf, 0xfffcffff, 0x7ef1ff3f, 0xfff3f1f8, 0x7fffff3f, 0x0, 0x18003, 0xdfffe000, 0xff31ffcf, 0xcfffffff, 0xfffc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x40100000, 0x1d7e0, 0x1fc00, 0x187c00, 0x0, 0x200708b, 0x2000000, 0x708b0000, 0xc00000, 0x0, 0x0, 0xfccf0006, 0x33ffcfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x80005, 0x0, 0x0, 0x120200, 0xff000000, 0x0, 0x0, 0x0, 0xb0001800, 0x0, 0x0, 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30001900, 0x0, 0x100000, 0x0, 0x1c00, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x0, 0xd81, 0x0, 0x0, 0x0, 0x1c00, 0x0, 0x0, 0x0, 0x74000000, 0x0, 0x0, 0x0, 0x10842008, 0x1680200, 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45540, 0x28000000, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xbffffff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, 0x5fdfffff, 0xefcfffde, 0x3fdcffff, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc40, 0x0, 0x0, 0xc000000, 0x4000, 0xe000, 0x0, 0x1210, 0x50, 0x292, 0x333e005, 0x333, 0xf000, 0x0, 0x3c0f, 0x0, 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x55555000, 0x36db02a5, 0x40100000, 0x55555000, 0x36db02a5, 0x47900000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf, 0x0, 0x0, 0x7fe53fff, 0xfffffc65, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xa0000000, 0x5f7ffc00, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14000000, 0x800, 0x0, 0x0, 0x0, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//2560 bytes -enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xe0], [ 0x100, 0x140, 0x3400], [ 0x2020100, 0x4020302, 0x2020205, 0x7060202, 0x2020202, 0x8020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x40006, 0x70004, 0x90008, 0xb000a, 0xd000c, 0xf000e, 0x40004, 0x40004, 0x40004, 0x40004, 0x100004, 0x110004, 0x130012, 0x150014, 0x170016, 0x40018, 0x40004, 0x40004, 0x40019, 0x1b001a, 0x1d001c, 0x1f001e, 0x210020, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x220004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x210004, 0x240023, 0x250021, 0x270026, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x290028, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x2a0004, 0x40004, 0x2c002b, 0x2e002d, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x4002f, 0x300004, 0x40031, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x210021, 0x40032, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x0, 0x0, 0x0, 0x0, 0x0, 0x773c8501, 0x0, 0x0, 0x0, 0x800c0000, 0x201, 0x80000000, 0x0, 0x0, 0x1ff0, 0xe0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ff0000, 0x3f000000, 0x1f, 0x81a9fdf, 0x10361f8, 0x3f, 0x44100000, 0xb0, 0x0, 0x7f0000, 0x2370000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x380000, 0x1e00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0xff000000, 0x0, 0x0, 0x40000000, 0xb0800000, 0x0, 0x0, 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, 0x30c00000, 0x0, 0x0, 0x40000000, 0x800000, 0x0, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x600004, 0x0, 0x0, 0x40000000, 0x800000, 0x0, 0x0, 0x0, 0x80008400, 0x0, 0x0, 0x80000, 0x0, 0x0, 0x0, 0x80000, 0x30000000, 0x0, 0x1000, 0x0, 0x10842008, 0x3e80200, 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x3ffffe, 0x0, 0xffffff00, 0x7, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf7ff7000, 0xffffbfff, 0x10007ff, 0xf8000000, 0xffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2aaa0000, 0x0, 0xe8000000, 0xe808ea03, 0x6a00e808, 0x8207ff, 0x50d88070, 0x80800380, 0xfff30000, 0x1fff7fff, 0x100, 0x0, 0x0, 0x3e6ffeef, 0xfbfbbd57, 0xffff03e1, 0xffffffff, 0x200, 0x0, 0x0, 0x0, 0x0, 0x1b000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ff, 0x1000, 0x0, 0x0, 0x700000, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x30000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x80000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0x0, 0x1, 0x7400000, 0x0, 0x0, 0x9e000000, 0x0, 0x0, 0x80000000, 0x0, 0xfffe0000, 0xffffffff, 0xffffffff, 0xfffc7fff, 0x0, 0x0, 0x0, 0x7fffffff, 0xffffffff, 0xffff00ff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x3000000, 0x7fe53fff, 0xfffffc65, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xa0f8007f, 0x5f7fffff, 0xffffffdb, 0xffffffff, 0xffffffff, 0x3ffff, 0xfff80000, 0xffffffff, 0xffffffff, 0x3fffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, 0x1fff0000, 0x3ff0000, 0xffff0000, 0xfff7ff9f, 0xffd70f7f, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0x1cfcfcfc, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, 0xffffffff, 0xffffffff, 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, 0xffffffef, 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff3f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffcfff, 0xffffffff, 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, 0xffffbff, 0xffffbee, 0x0, 0x0, 0xffff07ff, 0xffff7fff, 0xffff, 0xc00, 0x10000, 0x0, 0x0, 0x0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); -//2656 bytes -enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xf0], [ 0x100, 0x160, 0x3500], [ 0x2020100, 0x5040302, 0x2020206, 0x8070202, 0x2020202, 0x9020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x7000f, 0x70007, 0x70007, 0x70007, 0x70007, 0x100007, 0x110007, 0x130012, 0x150014, 0x170016, 0x70018, 0x70007, 0x70007, 0x70019, 0x1b001a, 0x1d001c, 0x1f001e, 0x210020, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x220007, 0x70007, 0x70007, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x230021, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x210007, 0x250024, 0x260021, 0x280027, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x2a0029, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x2b0007, 0x70007, 0x2d002c, 0x2f002e, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70030, 0x310007, 0x70032, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x210021, 0x70033, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x0, 0x0, 0x0, 0x0, 0x0, 0x773c8501, 0x3e7effbf, 0xbe7effbf, 0xfffcffff, 0xfefdff3f, 0xfff3f3f9, 0xffffff3f, 0x0, 0x18003, 0xdffffff0, 0xff3fffcf, 0xcfffffff, 0xfffc0, 0x0, 0x0, 0x0, 0x1ff0000, 0x3f000000, 0x1f, 0x0, 0x0, 0x1b, 0x44100000, 0x1d7f0, 0x1fc00, 0x7f7c00, 0x2370000, 0x200708b, 0x2000000, 0x708b0000, 0xc00000, 0x0, 0x0, 0xfccf0006, 0x33ffcfc, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x1e00000, 0x0, 0x0, 0x80005, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120200, 0xff000000, 0x0, 0x0, 0x0, 0xb0001800, 0x0, 0x0, 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30001900, 0x0, 0x100000, 0x0, 0x1c00, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x0, 0xd81, 0x0, 0x0, 0x0, 0x1c00, 0x0, 0x0, 0x0, 0x74000000, 0x0, 0x0, 0x80000, 0x0, 0x0, 0x0, 0x80000, 0x30000000, 0x0, 0x1000, 0x0, 0x10842008, 0x3e80200, 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000, 0x45540, 0x28000000, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf7ff7000, 0xffffbfff, 0x10007ff, 0xf8000000, 0xffffffff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, 0xffdfffff, 0xefcfffdf, 0x7fdcffff, 0x8207ff, 0x50d88070, 0x80800380, 0xfff30000, 0x1fff7fff, 0x100, 0x0, 0x0, 0x3e6ffeef, 0xfbfbbd57, 0xffff03e1, 0xffffffff, 0xc000200, 0x4000, 0xe000, 0x0, 0x1210, 0x1b050, 0x292, 0x333e005, 0x333, 0xf000, 0x0, 0x3c0f, 0x0, 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ff, 0x1000, 0x0, 0x0, 0x700000, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x30000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x80000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0x0, 0x1, 0x7400000, 0x55555000, 0x36db02a5, 0xd8100000, 0x55555000, 0x36db02a5, 0xc7900000, 0x0, 0xfffe0000, 0xffffffff, 0xffffffff, 0xfffc7fff, 0x0, 0x0, 0x0, 0x7fffffff, 0xffffffff, 0xffff00ff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x3000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf, 0x0, 0x0, 0x7fe53fff, 0xfffffc65, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xa0f8007f, 0x5f7fffff, 0xffffffdb, 0xffffffff, 0xffffffff, 0x3ffff, 0xfff80000, 0xffffffff, 0xffffffff, 0x3fffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, 0x1fff0000, 0x3ff0000, 0xffff0000, 0xfff7ff9f, 0xffd70f7f, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0x1cfcfcfc, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0x14000000, 0x800, 0x0, 0x0, 0x0, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, 0xffffffff, 0xffffffff, 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, 0xffffffef, 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff3f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffcfff, 0xffffffff, 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, 0xffffbff, 0xffffbee, 0x0, 0x0, 0xffff07ff, 0xffff7fff, 0xffff, 0xc00, 0x10000, 0x0, 0x0, 0x0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +static if (size_t.sizeof == 4) +{ + //1600 bytes + enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xc0], + [0x100, 0x100, 0x1a00], [0x2020100, 0x3020202, 0x2020204, + 0x2050202, 0x2020202, 0x6020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x2, 0x30000, + 0x50004, 0x70006, 0x80000, 0xa0009, 0x0, 0x0, 0x0, 0x0, 0xb0000, + 0x0, 0xc0000, 0xe000d, 0xf0000, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, + 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120000, + 0x140013, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x170000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x120012, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x81a9fdf, 0x10361f8, 0x3f, 0x40100000, 0x80, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x380000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000000, 0xff000000, 0x0, 0x0, 0x40000000, 0xb0800000, 0x0, 0x0, + 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, + 0x30c00000, 0x0, 0x0, 0x40000000, 0x800000, 0x0, 0x0, 0x0, + 0x400000, 0x0, 0x0, 0x0, 0x600004, 0x0, 0x0, 0x40000000, 0x800000, + 0x0, 0x0, 0x0, 0x80008400, 0x0, 0x0, 0x0, 0x10842008, 0x1680200, + 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ffffe, 0x0, 0xffffff00, 0x7, 0x0, 0x0, + 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2aaa0000, + 0x0, 0x48000000, 0x8080a00, 0x2a00c808, 0x3, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6000000, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x7fe53fff, 0xfffffc65, + 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, + 0xa0000000, 0x5f7ffc00, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x4000000, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, + 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0]); + //1920 bytes + enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xe0], + [0x100, 0x140, 0x2000], [0x2020100, 0x5040302, 0x2020206, + 0x2070202, 0x2020202, 0x8020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x50006, + 0x70005, 0x90008, 0xb000a, 0xc0005, 0x5000d, 0x50005, 0x50005, + 0x50005, 0x50005, 0xe0005, 0x50005, 0x10000f, 0x120011, 0x140013, + 0x50005, 0x50005, 0x50005, 0x50015, 0x50005, 0x50005, 0x50016, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, + 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, + 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, 0x170017, + 0x170017, 0x170017, 0x170017, 0x170017, 0x180017, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x170005, 0x1a0019, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x1c001b, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x1d0005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x170017, + 0x5001e, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, + 0x50005, 0x50005, 0x50005, 0x50005, 0x50005, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3e7effbf, 0xbe7effbf, 0xfffcffff, 0x7ef1ff3f, + 0xfff3f1f8, 0x7fffff3f, 0x0, 0x18003, 0xdfffe000, 0xff31ffcf, + 0xcfffffff, 0xfffc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, + 0x40100000, 0x1d7e0, 0x1fc00, 0x187c00, 0x0, 0x200708b, 0x2000000, + 0x708b0000, 0xc00000, 0x0, 0x0, 0xfccf0006, 0x33ffcfc, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x0, + 0x80005, 0x0, 0x0, 0x120200, 0xff000000, 0x0, 0x0, 0x0, 0xb0001800, + 0x0, 0x0, 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x30001900, 0x0, 0x100000, 0x0, 0x1c00, 0x0, 0x0, 0x0, 0x100, 0x0, + 0x0, 0x0, 0xd81, 0x0, 0x0, 0x0, 0x1c00, 0x0, 0x0, 0x0, 0x74000000, + 0x0, 0x0, 0x0, 0x10842008, 0x1680200, 0x20080002, 0x2001084, 0x0, + 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45540, 0x28000000, + 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xbffffff, 0xffffffff, 0xffffffff, 0x3ffffff, + 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, + 0x5fdfffff, 0xefcfffde, 0x3fdcffff, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xc40, 0x0, 0x0, 0xc000000, 0x4000, 0xe000, 0x0, + 0x1210, 0x50, 0x292, 0x333e005, 0x333, 0xf000, 0x0, 0x3c0f, 0x0, + 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000000, 0x0, 0x0, 0x0, 0x55555000, 0x36db02a5, 0x40100000, + 0x55555000, 0x36db02a5, 0x47900000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xf, 0x0, 0x0, 0x7fe53fff, 0xfffffc65, 0xffffffff, + 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xa0000000, + 0x5f7ffc00, 0x7fdb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x14000000, 0x800, 0x0, 0x0, 0x0, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, + 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0]); + //2560 bytes + enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xe0], + [0x100, 0x140, 0x3400], [0x2020100, 0x4020302, 0x2020205, + 0x7060202, 0x2020202, 0x8020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x40006, + 0x70004, 0x90008, 0xb000a, 0xd000c, 0xf000e, 0x40004, 0x40004, + 0x40004, 0x40004, 0x100004, 0x110004, 0x130012, 0x150014, 0x170016, + 0x40018, 0x40004, 0x40004, 0x40019, 0x1b001a, 0x1d001c, 0x1f001e, + 0x210020, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x220004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x210004, 0x240023, 0x250021, 0x270026, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x290028, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x2a0004, 0x40004, 0x2c002b, 0x2e002d, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x4002f, 0x300004, 0x40031, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x210021, 0x40032, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, 0x40004, + 0x40004, 0x40004, 0x0, 0x0, 0x0, 0x0, 0x0, 0x773c8501, 0x0, 0x0, + 0x0, 0x800c0000, 0x201, 0x80000000, 0x0, 0x0, 0x1ff0, 0xe0000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1ff0000, 0x3f000000, 0x1f, 0x81a9fdf, + 0x10361f8, 0x3f, 0x44100000, 0xb0, 0x0, 0x7f0000, 0x2370000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x380000, 0x1e00000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000000, 0xff000000, 0x0, 0x0, 0x40000000, 0xb0800000, 0x0, 0x0, + 0x480000, 0x4e000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, + 0x30c00000, 0x0, 0x0, 0x40000000, 0x800000, 0x0, 0x0, 0x0, + 0x400000, 0x0, 0x0, 0x0, 0x600004, 0x0, 0x0, 0x40000000, 0x800000, + 0x0, 0x0, 0x0, 0x80008400, 0x0, 0x0, 0x80000, 0x0, 0x0, 0x0, + 0x80000, 0x30000000, 0x0, 0x1000, 0x0, 0x10842008, 0x3e80200, + 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x3ffffe, 0x0, 0xffffff00, 0x7, + 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf7ff7000, + 0xffffbfff, 0x10007ff, 0xf8000000, 0xffffffff, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2aaa0000, 0x0, + 0xe8000000, 0xe808ea03, 0x6a00e808, 0x8207ff, 0x50d88070, + 0x80800380, 0xfff30000, 0x1fff7fff, 0x100, 0x0, 0x0, 0x3e6ffeef, + 0xfbfbbd57, 0xffff03e1, 0xffffffff, 0x200, 0x0, 0x0, 0x0, 0x0, + 0x1b000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x600, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x7ff, 0x1000, 0x0, 0x0, 0x700000, 0x0, 0x0, + 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x30000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80000000, 0x0, 0x0, 0x80000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0x0, 0x1, 0x7400000, + 0x0, 0x0, 0x9e000000, 0x0, 0x0, 0x80000000, 0x0, 0xfffe0000, + 0xffffffff, 0xffffffff, 0xfffc7fff, 0x0, 0x0, 0x0, 0x7fffffff, + 0xffffffff, 0xffff00ff, 0x7fffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, + 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x3000000, 0x7fe53fff, + 0xfffffc65, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, + 0x3ffffff, 0x0, 0xa0f8007f, 0x5f7fffff, 0xffffffdb, 0xffffffff, + 0xffffffff, 0x3ffff, 0xfff80000, 0xffffffff, 0xffffffff, + 0x3fffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, + 0x1fff0000, 0x3ff0000, 0xffff0000, 0xfff7ff9f, 0xffd70f7f, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xfffffffe, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, + 0x1cfcfcfc, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000, 0x0, 0x0, + 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, + 0x1f, 0x0, 0xf8000000, 0x1, 0x0, 0xffffffff, 0xffffffff, + 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, 0xffffffef, + 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff3f, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffcfff, 0xffffffff, + 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, 0xffffbff, + 0xffffbee, 0x0, 0x0, 0xffff07ff, 0xffff7fff, 0xffff, 0xc00, + 0x10000, 0x0, 0x0, 0x0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); + //2656 bytes + enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xf0], + [0x100, 0x160, 0x3500], [0x2020100, 0x5040302, 0x2020206, + 0x8070202, 0x2020202, 0x9020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, + 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x7000f, 0x70007, 0x70007, + 0x70007, 0x70007, 0x100007, 0x110007, 0x130012, 0x150014, 0x170016, + 0x70018, 0x70007, 0x70007, 0x70019, 0x1b001a, 0x1d001c, 0x1f001e, + 0x210020, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x220007, 0x70007, + 0x70007, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, + 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, + 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, 0x210021, + 0x210021, 0x210021, 0x210021, 0x210021, 0x230021, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x210007, 0x250024, 0x260021, 0x280027, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x2a0029, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x2b0007, 0x70007, 0x2d002c, 0x2f002e, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70030, 0x310007, 0x70032, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x210021, 0x70033, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, + 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x70007, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x773c8501, 0x3e7effbf, 0xbe7effbf, 0xfffcffff, + 0xfefdff3f, 0xfff3f3f9, 0xffffff3f, 0x0, 0x18003, 0xdffffff0, + 0xff3fffcf, 0xcfffffff, 0xfffc0, 0x0, 0x0, 0x0, 0x1ff0000, + 0x3f000000, 0x1f, 0x0, 0x0, 0x1b, 0x44100000, 0x1d7f0, 0x1fc00, + 0x7f7c00, 0x2370000, 0x200708b, 0x2000000, 0x708b0000, 0xc00000, + 0x0, 0x0, 0xfccf0006, 0x33ffcfc, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, + 0x0, 0x0, 0x0, 0x7c, 0x0, 0x1e00000, 0x0, 0x0, 0x80005, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x120200, 0xff000000, 0x0, + 0x0, 0x0, 0xb0001800, 0x0, 0x0, 0x480000, 0x4e000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x30001900, 0x0, 0x100000, 0x0, 0x1c00, + 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x0, 0xd81, 0x0, 0x0, 0x0, 0x1c00, + 0x0, 0x0, 0x0, 0x74000000, 0x0, 0x0, 0x80000, 0x0, 0x0, 0x0, + 0x80000, 0x30000000, 0x0, 0x1000, 0x0, 0x10842008, 0x3e80200, + 0x20080002, 0x2001084, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x10000000, 0x45540, 0x28000000, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xf7ff7000, 0xffffbfff, 0x10007ff, 0xf8000000, 0xffffffff, + 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffffff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x3f3fffff, + 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, 0xffdfffff, + 0xefcfffdf, 0x7fdcffff, 0x8207ff, 0x50d88070, 0x80800380, + 0xfff30000, 0x1fff7fff, 0x100, 0x0, 0x0, 0x3e6ffeef, 0xfbfbbd57, + 0xffff03e1, 0xffffffff, 0xc000200, 0x4000, 0xe000, 0x0, 0x1210, + 0x1b050, 0x292, 0x333e005, 0x333, 0xf000, 0x0, 0x3c0f, 0x0, 0x600, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x7ff, 0x1000, 0x0, 0x0, + 0x700000, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x0, 0x0, 0x30000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x80000, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x3fffff, 0x0, 0x1, 0x7400000, 0x55555000, 0x36db02a5, 0xd8100000, + 0x55555000, 0x36db02a5, 0xc7900000, 0x0, 0xfffe0000, 0xffffffff, + 0xffffffff, 0xfffc7fff, 0x0, 0x0, 0x0, 0x7fffffff, 0xffffffff, + 0xffff00ff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, 0x0, 0x0, + 0x10000, 0x0, 0x0, 0x0, 0x3000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf, 0x0, 0x0, 0x7fe53fff, + 0xfffffc65, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, + 0x3ffffff, 0x0, 0xa0f8007f, 0x5f7fffff, 0xffffffdb, 0xffffffff, + 0xffffffff, 0x3ffff, 0xfff80000, 0xffffffff, 0xffffffff, + 0x3fffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, + 0x1fff0000, 0x3ff0000, 0xffff0000, 0xfff7ff9f, 0xffd70f7f, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xfffffffe, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, + 0x1cfcfcfc, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0x14000000, 0x800, 0x0, + 0x0, 0x0, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xc0000000, 0x1f, 0x0, 0xf8000000, 0x1, 0x0, 0xffffffff, + 0xffffffff, 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, + 0xffffffef, 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffff3f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffcfff, + 0xffffffff, 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, + 0xffffbff, 0xffffbee, 0x0, 0x0, 0xffff07ff, 0xffff7fff, 0xffff, + 0xc00, 0x10000, 0x0, 0x0, 0x0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); } - diff --git a/std/internal/unicode_tables.d b/std/internal/unicode_tables.d index 38cc7239bf3..00b94bdcbb5 100644 --- a/std/internal/unicode_tables.d +++ b/std/internal/unicode_tables.d @@ -1,14 +1,15 @@ //Written in the D programming language /** - * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). * * Authors: Dmitry Olshansky * */ //Automatically generated from Unicode Character Database files +//dfmt off module std.internal.unicode_tables; -@safe pure nothrow @nogc: +@safe pure nothrow @nogc package(std): struct SimpleCaseEntry @@ -40,7 +41,7 @@ struct FullCaseEntry @property auto value() const @trusted pure nothrow @nogc return { - return seq[0..entry_len]; + return seq[0 .. entry_len]; } } @@ -1215,82 +1216,1554 @@ private alias _U = immutable(UnicodeProperty); @property static _U[] tab() pure { return _tab; } static immutable: private alias _T = ubyte[]; -_T So = [0x80, 0xa6, 0x1, 0x2, 0x1, 0x4, 0x1, 0x1, 0x1, 0x83, 0xd1, 0x1, 0x81, 0x8b, 0x2, 0x80, 0xce, 0x1, 0xa, 0x1, 0x13, 0x2, 0x80, 0xf7, 0x1, 0x82, 0x3, 0x1, 0x81, 0x75, 0x1, 0x80, 0x82, 0x6, 0x1, 0x1, 0x80, 0x84, 0x1, 0x80, 0xf9, 0x1, 0x81, 0x87, 0x3, 0xf, 0x1, 0x1, 0x3, 0x2, 0x6, 0x14, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x85, 0x8, 0x1, 0x6, 0x1, 0x2, 0x5, 0x4, 0x80, 0xc5, 0x2, 0x82, 0xf0, 0xa, 0x85, 0xa6, 0x1, 0x80, 0x9d, 0x22, 0x81, 0x61, 0xa, 0x9, 0x9, 0x85, 0x83, 0x2, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0xb, 0x2, 0xe, 0x1, 0x1, 0x2, 0x1, 0x1, 0x45, 0x5, 0x2, 0x4, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7, 0x1, 0x1f, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1f, 0x81, 0xc, 0x8, 0x4, 0x14, 0x2, 0x7, 0x2, 0x51, 0x1, 0x1e, 0x19, 0x28, 0x6, 0x12, 0xc, 0x27, 0x19, 0xb, 0x51, 0x4e, 0x16, 0x80, 0xb7, 0x1, 0x9, 0x1, 0x36, 0x8, 0x6f, 0x1, 0x80, 0x90, 0x1, 0x67, 0x2c, 0x2c, 0x40, 0x81, 0x0, 0x82, 0x0, 0x30, 0x15, 0x2, 0x9, 0xa, 0x81, 0x8b, 0x6, 0x81, 0x95, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x1a, 0xc, 0x8, 0x1, 0xd, 0x2, 0xc, 0x1, 0x15, 0x2, 0x6, 0x2, 0x81, 0x50, 0x2, 0x4, 0xa, 0x20, 0x24, 0x1c, 0x1f, 0xb, 0x1e, 0x8, 0x1, 0xf, 0x20, 0xa, 0x27, 0xf, 0x3f, 0x1, 0x81, 0x0, 0x99, 0xc0, 0x40, 0xa0, 0x56, 0x90, 0x37, 0x83, 0x61, 0x4, 0xa, 0x2, 0x1, 0x1, 0x82, 0x3d, 0x3, 0xa0, 0x53, 0x83, 0x1, 0x81, 0xe6, 0x1, 0x3, 0x1, 0x4, 0x2, 0xd, 0x2, 0x81, 0x39, 0x9, 0x39, 0x11, 0x6, 0xc, 0x34, 0x2d, 0xa0, 0xce, 0x3, 0x80, 0xf6, 0xa, 0x27, 0x2, 0x3c, 0x5, 0x3, 0x16, 0x2, 0x7, 0x1e, 0x4, 0x30, 0x22, 0x42, 0x3, 0x1, 0x80, 0xba, 0x57, 0x9c, 0xa9, 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, 0x2, 0xf, 0x1, 0xf, 0x30, 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1d, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74]; -_T Pf = [0x80, 0xbb, 0x1, 0x9f, 0x5d, 0x1, 0x3, 0x1, 0x1c, 0x1, 0x8d, 0xc8, 0x1, 0x1, 0x1, 0x4, 0x1, 0x2, 0x1, 0xf, 0x1, 0x3, 0x1]; +_T So = [0x80, 0xa6, 0x1, 0x2, 0x1, 0x4, 0x1, 0x1, 0x1, 0x83, 0xd1, 0x1, 0x81, + 0x8b, 0x2, 0x80, 0xce, 0x1, 0xa, 0x1, 0x13, 0x2, 0x80, 0xf7, 0x1, 0x82, + 0x3, 0x1, 0x81, 0x75, 0x1, 0x80, 0x82, 0x6, 0x1, 0x1, 0x80, 0x84, 0x1, + 0x80, 0xf9, 0x1, 0x81, 0x87, 0x3, 0xf, 0x1, 0x1, 0x3, 0x2, 0x6, 0x14, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x80, 0x85, 0x8, 0x1, 0x6, 0x1, 0x2, 0x5, 0x4, 0x80, + 0xc5, 0x2, 0x82, 0xf0, 0xa, 0x85, 0xa6, 0x1, 0x80, 0x9d, 0x22, 0x81, + 0x61, 0xa, 0x9, 0x9, 0x85, 0x83, 0x2, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, + 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0xb, 0x2, 0xe, 0x1, + 0x1, 0x2, 0x1, 0x1, 0x45, 0x5, 0x2, 0x4, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7, + 0x1, 0x1f, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1f, 0x81, 0xc, 0x8, 0x4, 0x14, 0x2, + 0x7, 0x2, 0x51, 0x1, 0x1e, 0x19, 0x28, 0x6, 0x12, 0xc, 0x27, 0x19, 0xb, + 0x51, 0x4e, 0x16, 0x80, 0xb7, 0x1, 0x9, 0x1, 0x36, 0x8, 0x6f, 0x1, 0x80, + 0x90, 0x1, 0x67, 0x2c, 0x2c, 0x40, 0x81, 0x0, 0x82, 0x0, 0x30, 0x15, 0x2, + 0x9, 0xa, 0x81, 0x8b, 0x6, 0x81, 0x95, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, + 0x1a, 0xc, 0x8, 0x1, 0xd, 0x2, 0xc, 0x1, 0x15, 0x2, 0x6, 0x2, 0x81, 0x50, + 0x2, 0x4, 0xa, 0x20, 0x24, 0x1c, 0x1f, 0xb, 0x1e, 0x8, 0x1, 0xf, 0x20, 0xa, + 0x27, 0xf, 0x3f, 0x1, 0x81, 0x0, 0x99, 0xc0, 0x40, 0xa0, 0x56, 0x90, + 0x37, 0x83, 0x61, 0x4, 0xa, 0x2, 0x1, 0x1, 0x82, 0x3d, 0x3, 0xa0, 0x53, + 0x83, 0x1, 0x81, 0xe6, 0x1, 0x3, 0x1, 0x4, 0x2, 0xd, 0x2, 0x81, 0x39, + 0x9, 0x39, 0x11, 0x6, 0xc, 0x34, 0x2d, 0xa0, 0xce, 0x3, 0x80, 0xf6, 0xa, + 0x27, 0x2, 0x3c, 0x5, 0x3, 0x16, 0x2, 0x7, 0x1e, 0x4, 0x30, 0x22, 0x42, + 0x3, 0x1, 0x80, 0xba, 0x57, 0x9c, 0xa9, 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, + 0xe, 0x2, 0xf, 0x1, 0xf, 0x30, 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1d, + 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, + 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, + 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, + 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74]; +_T Pf = [0x80, 0xbb, 0x1, 0x9f, 0x5d, 0x1, 0x3, 0x1, 0x1c, 0x1, 0x8d, 0xc8, + 0x1, 0x1, 0x1, 0x4, 0x1, 0x2, 0x1, 0xf, 0x1, 0x3, 0x1]; _T Bidi_Control = [0x86, 0x1c, 0x1, 0x99, 0xf1, 0x2, 0x1a, 0x5, 0x37, 0x4]; -_T Hex_Digit = [0x30, 0xa, 0x7, 0x6, 0x1a, 0x6, 0xa0, 0xfe, 0xa9, 0xa, 0x7, 0x6, 0x1a, 0x6]; -_T Other_Lowercase = [0x80, 0xaa, 0x1, 0xf, 0x1, 0x81, 0xf5, 0x9, 0x7, 0x2, 0x1e, 0x5, 0x60, 0x1, 0x34, 0x1, 0x99, 0xb1, 0x3f, 0xd, 0x1, 0x22, 0x25, 0x82, 0xb1, 0x1, 0xd, 0x1, 0x10, 0xd, 0x80, 0xd3, 0x10, 0x83, 0x50, 0x1a, 0x87, 0x92, 0x2, 0xa0, 0x7a, 0xf2, 0x1, 0x80, 0x87, 0x2]; -_T Quotation_Mark = [0x22, 0x1, 0x4, 0x1, 0x80, 0x83, 0x1, 0xf, 0x1, 0x9f, 0x5c, 0x8, 0x19, 0x2, 0x8f, 0xd1, 0x4, 0xd, 0x3, 0xa0, 0xce, 0x21, 0x4, 0x80, 0xbd, 0x1, 0x4, 0x1, 0x5a, 0x2]; -_T XID_Start = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x80, 0x81, 0x5, 0x1, 0x2, 0x3, 0x3, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x48, 0x1b, 0x5, 0x3, 0x2d, 0x2b, 0x23, 0x2, 0x1, 0x63, 0x1, 0x1, 0xf, 0x2, 0x7, 0x2, 0xa, 0x3, 0x2, 0x1, 0x10, 0x1, 0x1, 0x1e, 0x1d, 0x59, 0xb, 0x1, 0x18, 0x21, 0x9, 0x2, 0x4, 0x1, 0x5, 0x16, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x17, 0x19, 0x47, 0x1, 0x1, 0xb, 0x57, 0x36, 0x3, 0x1, 0x12, 0x1, 0x7, 0xa, 0xf, 0x7, 0x1, 0x7, 0x5, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x3, 0x1, 0x10, 0x1, 0xd, 0x2, 0x1, 0x3, 0xe, 0x2, 0x13, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1f, 0x4, 0x1, 0x1, 0x13, 0x3, 0x10, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x12, 0x1, 0xf, 0x2, 0x23, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x1e, 0x2, 0x1, 0x3, 0xf, 0x1, 0x11, 0x1, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x16, 0x1, 0x34, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x1a, 0x2, 0x6, 0x2, 0x23, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x20, 0x1, 0x1, 0x2, 0xf, 0x2, 0x12, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x10, 0x1, 0x11, 0x2, 0x18, 0x6, 0x5, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3a, 0x30, 0x1, 0x1, 0xd, 0x7, 0x3a, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, 0x1, 0xa, 0x1, 0x2, 0x5, 0x1, 0x1, 0x15, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, 0x24, 0x1b, 0x5, 0x73, 0x2b, 0x14, 0x1, 0x10, 0x6, 0x4, 0x4, 0x3, 0x1, 0x3, 0x2, 0x7, 0x3, 0x4, 0xd, 0xc, 0x1, 0x11, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x25, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x4, 0xe, 0x12, 0xe, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x23, 0x1, 0x4, 0x1, 0x43, 0x58, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x33, 0x1e, 0x2, 0x5, 0xb, 0x2c, 0x15, 0x7, 0x38, 0x17, 0x9, 0x35, 0x52, 0x1, 0x5d, 0x2f, 0x11, 0x7, 0x37, 0x1e, 0xd, 0x2, 0xa, 0x2c, 0x1a, 0x24, 0x29, 0x3, 0xa, 0x24, 0x6b, 0x4, 0x1, 0x4, 0x3, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x10, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, 0x10, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x82, 0x26, 0x3, 0x19, 0x9, 0x7, 0x5, 0x2, 0x5, 0x4, 0x56, 0x6, 0x3, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x10, 0xa, 0x2, 0x14, 0x2f, 0x10, 0x19, 0x8, 0x50, 0x27, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x17, 0x1d, 0x34, 0xe, 0x32, 0x3e, 0x6, 0x3, 0x1, 0xe, 0x1c, 0xa, 0x17, 0x19, 0x1d, 0x7, 0x2f, 0x1c, 0x1, 0x30, 0x29, 0x17, 0x3, 0x1, 0x8, 0x14, 0x17, 0x3, 0x1, 0x5, 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x3, 0x2, 0xb, 0x7, 0x3, 0xc, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x23, 0x1d, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1, 0x1, 0xa, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x80, 0x8b, 0x6, 0x80, 0xda, 0x12, 0x40, 0x2, 0x36, 0x28, 0xa, 0x77, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7e, 0x24, 0x1a, 0x6, 0x1a, 0xb, 0x38, 0x2, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, 0x81, 0xb, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xba, 0x35, 0x4b, 0x2d, 0x20, 0x19, 0x1a, 0x24, 0x5c, 0x30, 0xe, 0x4, 0x84, 0xbb, 0x2b, 0x89, 0x55, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x1, 0x42, 0xd, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x23, 0xfe, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x96, 0x34, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; -_T Terminal_Punctuation = [0x21, 0x1, 0xa, 0x1, 0x1, 0x1, 0xb, 0x2, 0x3, 0x1, 0x83, 0x3e, 0x1, 0x8, 0x1, 0x82, 0x1, 0x1, 0x39, 0x1, 0x48, 0x1, 0xe, 0x1, 0x3, 0x1, 0x80, 0xb4, 0x1, 0x2b, 0xb, 0x1, 0x1, 0x80, 0xeb, 0x2, 0x36, 0xf, 0x1f, 0x1, 0x81, 0x5, 0x2, 0x84, 0xf4, 0x2, 0x80, 0xac, 0x1, 0x4, 0x6, 0x81, 0x37, 0x2, 0x83, 0x15, 0x8, 0x83, 0x4, 0x2, 0x7c, 0x3, 0x80, 0xe6, 0x3, 0x3, 0x1, 0x27, 0x4, 0x2, 0x2, 0x81, 0x3a, 0x2, 0x81, 0x62, 0x4, 0x80, 0xae, 0x2, 0x1, 0x3, 0x80, 0xdb, 0x5, 0x3e, 0x2, 0x83, 0xbc, 0x2, 0x9, 0x3, 0x8d, 0xe4, 0x1, 0x81, 0xd2, 0x2, 0xa0, 0x74, 0xfb, 0x2, 0x81, 0xd, 0x3, 0x80, 0xe3, 0x5, 0x81, 0x7e, 0x2, 0x56, 0x2, 0x5f, 0x1, 0x80, 0x97, 0x3, 0x80, 0x93, 0x3, 0x7f, 0x1, 0x10, 0x2, 0x80, 0xf9, 0x1, 0xa0, 0x52, 0x64, 0x3, 0x1, 0x4, 0x80, 0xa9, 0x1, 0xa, 0x1, 0x1, 0x1, 0xb, 0x2, 0x3, 0x1, 0x41, 0x1, 0x2, 0x1, 0x84, 0x3a, 0x1, 0x30, 0x1, 0x84, 0x86, 0x1, 0x80, 0xc7, 0x1, 0x82, 0x1a, 0x6, 0x85, 0x7, 0x7, 0x70, 0x4, 0x7f, 0x3, 0x80, 0x81, 0x2, 0x92, 0xa9, 0x4]; -_T Math = [0x2b, 0x1, 0x10, 0x3, 0x1f, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x2d, 0x1, 0x4, 0x1, 0x25, 0x1, 0x1f, 0x1, 0x82, 0xd8, 0x3, 0x2, 0x1, 0x1a, 0x2, 0x2, 0x3, 0x82, 0xf, 0x3, 0x9a, 0xd, 0x1, 0x1b, 0x3, 0xb, 0x1, 0x3, 0x1, 0xd, 0x1, 0xe, 0x4, 0x15, 0x5, 0xb, 0x5, 0x41, 0xd, 0x4, 0x1, 0x3, 0x2, 0x4, 0x5, 0x12, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x3, 0x2, 0x2, 0x2, 0x1, 0x3, 0x1, 0x6, 0x3, 0xe, 0x1, 0x1, 0x44, 0x18, 0x1, 0x6, 0x1, 0x2, 0x4, 0x2, 0x4, 0x20, 0x1, 0x1, 0x6, 0x2, 0xe, 0x81, 0xc, 0x8, 0x4, 0x14, 0x2, 0x5a, 0x1, 0x1e, 0x1b, 0x1, 0x1, 0x18, 0x1, 0xb, 0x7, 0x81, 0xbd, 0x2, 0xc, 0xa, 0x4, 0x6, 0x4, 0x2, 0x2, 0x2, 0x3, 0x5, 0xe, 0x1, 0x1, 0x1, 0x2, 0x6, 0xb, 0x8, 0x5, 0x2, 0x39, 0x1, 0x1, 0x1, 0x1d, 0x4, 0x9, 0x3, 0x81, 0x50, 0x40, 0x81, 0x0, 0x82, 0x0, 0x30, 0x15, 0x2, 0x6, 0xa0, 0xcf, 0xdc, 0x1, 0x83, 0x37, 0x6, 0x1, 0x1, 0x80, 0xa2, 0x1, 0x10, 0x3, 0x1d, 0x1, 0x1, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x80, 0x83, 0x1, 0x6, 0x4, 0xa0, 0xd4, 0x13, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2]; -_T Lu = [0x41, 0x1a, 0x65, 0x17, 0x1, 0x7, 0x21, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x3, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x21, 0x1, 0x1, 0x1, 0x3, 0x1, 0xf, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x1, 0x11, 0x1, 0x9, 0x23, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x2, 0x1, 0x1, 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x26, 0x8b, 0x49, 0x26, 0x1, 0x1, 0x5, 0x1, 0x8d, 0x32, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0x6, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x8, 0x48, 0x4, 0xc, 0x4, 0xc, 0x4, 0xc, 0x5, 0xb, 0x4, 0x81, 0x6, 0x1, 0x4, 0x1, 0x3, 0x3, 0x2, 0x3, 0x2, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x2, 0x4, 0xa, 0x2, 0x5, 0x1, 0x3d, 0x1, 0x8a, 0x7c, 0x2f, 0x31, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x8, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x1, 0x1, 0x1, 0x4, 0x1, 0xa0, 0x79, 0x4d, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa0, 0x57, 0x76, 0x1a, 0x84, 0xc5, 0x28, 0xa0, 0xcf, 0xd8, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0x8, 0x1a, 0x1a, 0x1a, 0x2, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1b, 0x2, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1e, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x1]; +_T Hex_Digit = [0x30, 0xa, 0x7, 0x6, 0x1a, 0x6, 0xa0, 0xfe, 0xa9, 0xa, 0x7, 0x6, 0x1a, + 0x6]; +_T Other_Lowercase = [ + 0x80, 0xaa, 0x1, 0xf, 0x1, 0x81, 0xf5, 0x9, 0x7, 0x2, 0x1e, 0x5, 0x60, 0x1, + 0x34, 0x1, 0x99, 0xb1, 0x3f, 0xd, 0x1, 0x22, 0x25, 0x82, 0xb1, 0x1, 0xd, + 0x1, 0x10, 0xd, 0x80, 0xd3, 0x10, 0x83, 0x50, 0x1a, 0x87, 0x92, 0x2, 0xa0, + 0x7a, 0xf2, 0x1, 0x80, 0x87, 0x2 +]; +_T Quotation_Mark = [ + 0x22, 0x1, 0x4, 0x1, 0x80, 0x83, 0x1, 0xf, 0x1, 0x9f, 0x5c, 0x8, 0x19, 0x2, + 0x8f, 0xd1, 0x4, 0xd, 0x3, 0xa0, 0xce, 0x21, 0x4, 0x80, 0xbd, 0x1, 0x4, 0x1, 0x5a, + 0x2 +]; +_T XID_Start = [ + 0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, + 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x80, 0x81, 0x5, + 0x1, 0x2, 0x3, 0x3, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, + 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x48, + 0x1b, 0x5, 0x3, 0x2d, 0x2b, 0x23, 0x2, 0x1, 0x63, 0x1, 0x1, 0xf, 0x2, 0x7, + 0x2, 0xa, 0x3, 0x2, 0x1, 0x10, 0x1, 0x1, 0x1e, 0x1d, 0x59, 0xb, 0x1, + 0x18, 0x21, 0x9, 0x2, 0x4, 0x1, 0x5, 0x16, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, + 0x17, 0x19, 0x47, 0x1, 0x1, 0xb, 0x57, 0x36, 0x3, 0x1, 0x12, 0x1, 0x7, + 0xa, 0xf, 0x7, 0x1, 0x7, 0x5, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x1, 0x3, 0x4, 0x3, 0x1, 0x10, 0x1, 0xd, 0x2, 0x1, 0x3, 0xe, 0x2, 0x13, + 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1f, + 0x4, 0x1, 0x1, 0x13, 0x3, 0x10, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x5, 0x3, 0x1, 0x12, 0x1, 0xf, 0x2, 0x23, 0x8, 0x2, 0x2, 0x2, + 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x1e, 0x2, 0x1, 0x3, 0xf, + 0x1, 0x11, 0x1, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, + 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x16, 0x1, 0x34, 0x8, 0x1, 0x3, 0x1, + 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x1a, 0x2, 0x6, 0x2, 0x23, 0x8, 0x1, + 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x20, 0x1, 0x1, 0x2, 0xf, + 0x2, 0x12, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x10, 0x1, 0x11, 0x2, + 0x18, 0x6, 0x5, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3a, 0x30, + 0x1, 0x1, 0xd, 0x7, 0x3a, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, + 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, + 0x1, 0xa, 0x1, 0x2, 0x5, 0x1, 0x1, 0x15, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, + 0x24, 0x1b, 0x5, 0x73, 0x2b, 0x14, 0x1, 0x10, 0x6, 0x4, 0x4, 0x3, 0x1, + 0x3, 0x2, 0x7, 0x3, 0x4, 0xd, 0xc, 0x1, 0x11, 0x26, 0x1, 0x1, 0x5, 0x1, + 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, + 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, + 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x25, 0x10, 0x10, 0x55, 0xc, 0x82, + 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x4, 0xe, + 0x12, 0xe, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x23, 0x1, 0x4, 0x1, + 0x43, 0x58, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x33, 0x1e, 0x2, + 0x5, 0xb, 0x2c, 0x15, 0x7, 0x38, 0x17, 0x9, 0x35, 0x52, 0x1, 0x5d, 0x2f, + 0x11, 0x7, 0x37, 0x1e, 0xd, 0x2, 0xa, 0x2c, 0x1a, 0x24, 0x29, 0x3, 0xa, + 0x24, 0x6b, 0x4, 0x1, 0x4, 0x3, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, + 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, + 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, + 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x10, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, + 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, + 0x1, 0x2, 0x38, 0x7, 0x1, 0x10, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, + 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x82, 0x26, 0x3, 0x19, 0x9, + 0x7, 0x5, 0x2, 0x5, 0x4, 0x56, 0x6, 0x3, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, + 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, + 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x10, 0xa, 0x2, + 0x14, 0x2f, 0x10, 0x19, 0x8, 0x50, 0x27, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, + 0x4, 0xc, 0xb, 0x4d, 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x17, 0x1d, 0x34, 0xe, + 0x32, 0x3e, 0x6, 0x3, 0x1, 0xe, 0x1c, 0xa, 0x17, 0x19, 0x1d, 0x7, 0x2f, + 0x1c, 0x1, 0x30, 0x29, 0x17, 0x3, 0x1, 0x8, 0x14, 0x17, 0x3, 0x1, 0x5, + 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x3, 0x2, + 0xb, 0x7, 0x3, 0xc, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, + 0x91, 0x23, 0x1d, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, + 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1, 0x1, 0xa, 0x1, 0xd, + 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x80, 0x8b, 0x6, + 0x80, 0xda, 0x12, 0x40, 0x2, 0x36, 0x28, 0xa, 0x77, 0x1, 0x1, 0x1, 0x3, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7e, 0x24, 0x1a, 0x6, 0x1a, 0xb, + 0x38, 0x2, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, + 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, + 0x81, 0xb, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, + 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, + 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, + 0x38, 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, + 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xba, 0x35, 0x4b, + 0x2d, 0x20, 0x19, 0x1a, 0x24, 0x5c, 0x30, 0xe, 0x4, 0x84, 0xbb, 0x2b, 0x89, + 0x55, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, + 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x1, 0x42, 0xd, 0xa0, 0x40, 0x60, + 0x2, 0xa0, 0x23, 0xfe, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, + 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, + 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, + 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, + 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x96, 0x34, 0x4, 0x1, + 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, + 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, + 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, + 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, + 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e +]; +_T Terminal_Punctuation = [ + 0x21, 0x1, 0xa, 0x1, 0x1, 0x1, 0xb, 0x2, 0x3, 0x1, 0x83, 0x3e, 0x1, 0x8, + 0x1, 0x82, 0x1, 0x1, 0x39, 0x1, 0x48, 0x1, 0xe, 0x1, 0x3, 0x1, 0x80, + 0xb4, 0x1, 0x2b, 0xb, 0x1, 0x1, 0x80, 0xeb, 0x2, 0x36, 0xf, 0x1f, 0x1, + 0x81, 0x5, 0x2, 0x84, 0xf4, 0x2, 0x80, 0xac, 0x1, 0x4, 0x6, 0x81, 0x37, + 0x2, 0x83, 0x15, 0x8, 0x83, 0x4, 0x2, 0x7c, 0x3, 0x80, 0xe6, 0x3, 0x3, + 0x1, 0x27, 0x4, 0x2, 0x2, 0x81, 0x3a, 0x2, 0x81, 0x62, 0x4, 0x80, 0xae, + 0x2, 0x1, 0x3, 0x80, 0xdb, 0x5, 0x3e, 0x2, 0x83, 0xbc, 0x2, 0x9, 0x3, 0x8d, + 0xe4, 0x1, 0x81, 0xd2, 0x2, 0xa0, 0x74, 0xfb, 0x2, 0x81, 0xd, 0x3, 0x80, + 0xe3, 0x5, 0x81, 0x7e, 0x2, 0x56, 0x2, 0x5f, 0x1, 0x80, 0x97, 0x3, 0x80, + 0x93, 0x3, 0x7f, 0x1, 0x10, 0x2, 0x80, 0xf9, 0x1, 0xa0, 0x52, 0x64, 0x3, + 0x1, 0x4, 0x80, 0xa9, 0x1, 0xa, 0x1, 0x1, 0x1, 0xb, 0x2, 0x3, 0x1, 0x41, + 0x1, 0x2, 0x1, 0x84, 0x3a, 0x1, 0x30, 0x1, 0x84, 0x86, 0x1, 0x80, 0xc7, + 0x1, 0x82, 0x1a, 0x6, 0x85, 0x7, 0x7, 0x70, 0x4, 0x7f, 0x3, 0x80, 0x81, 0x2, 0x92, + 0xa9, 0x4 +]; +_T Math = [0x2b, 0x1, 0x10, 0x3, 0x1f, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x2d, 0x1, + 0x4, 0x1, 0x25, 0x1, 0x1f, 0x1, 0x82, 0xd8, 0x3, 0x2, 0x1, 0x1a, 0x2, + 0x2, 0x3, 0x82, 0xf, 0x3, 0x9a, 0xd, 0x1, 0x1b, 0x3, 0xb, 0x1, 0x3, 0x1, + 0xd, 0x1, 0xe, 0x4, 0x15, 0x5, 0xb, 0x5, 0x41, 0xd, 0x4, 0x1, 0x3, 0x2, + 0x4, 0x5, 0x12, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, + 0x3, 0x2, 0x2, 0x2, 0x1, 0x3, 0x1, 0x6, 0x3, 0xe, 0x1, 0x1, 0x44, 0x18, + 0x1, 0x6, 0x1, 0x2, 0x4, 0x2, 0x4, 0x20, 0x1, 0x1, 0x6, 0x2, 0xe, 0x81, + 0xc, 0x8, 0x4, 0x14, 0x2, 0x5a, 0x1, 0x1e, 0x1b, 0x1, 0x1, 0x18, 0x1, + 0xb, 0x7, 0x81, 0xbd, 0x2, 0xc, 0xa, 0x4, 0x6, 0x4, 0x2, 0x2, 0x2, 0x3, + 0x5, 0xe, 0x1, 0x1, 0x1, 0x2, 0x6, 0xb, 0x8, 0x5, 0x2, 0x39, 0x1, 0x1, + 0x1, 0x1d, 0x4, 0x9, 0x3, 0x81, 0x50, 0x40, 0x81, 0x0, 0x82, 0x0, 0x30, + 0x15, 0x2, 0x6, 0xa0, 0xcf, 0xdc, 0x1, 0x83, 0x37, 0x6, 0x1, 0x1, 0x80, + 0xa2, 0x1, 0x10, 0x3, 0x1d, 0x1, 0x1, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x80, + 0x83, 0x1, 0x6, 0x4, 0xa0, 0xd4, 0x13, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, + 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, + 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, + 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, + 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, + 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, + 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, + 0x1, 0x11, 0x34, 0x2]; +_T Lu = [0x41, 0x1a, 0x65, 0x17, 0x1, 0x7, 0x21, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, 0x1, + 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, 0x2, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, + 0x1, 0x1, 0x2, 0x3, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x2, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x21, 0x1, 0x1, 0x1, 0x3, 0x1, 0xf, 0x1, + 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x1, 0x11, 0x1, 0x9, 0x23, 0x1, 0x2, 0x3, + 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x2, 0x1, 0x1, + 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, + 0x26, 0x8b, 0x49, 0x26, 0x1, 0x1, 0x5, 0x1, 0x8d, 0x32, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0x6, 0xb, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x8, 0x48, 0x4, 0xc, 0x4, 0xc, + 0x4, 0xc, 0x5, 0xb, 0x4, 0x81, 0x6, 0x1, 0x4, 0x1, 0x3, 0x3, 0x2, 0x3, + 0x2, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x2, 0x4, + 0xa, 0x2, 0x5, 0x1, 0x3d, 0x1, 0x8a, 0x7c, 0x2f, 0x31, 0x1, 0x1, 0x3, 0x2, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x8, 0x3, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x1, 0x1, 0x1, 0x4, 0x1, 0xa0, 0x79, + 0x4d, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, + 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa0, 0x57, 0x76, 0x1a, 0x84, 0xc5, 0x28, + 0xa0, 0xcf, 0xd8, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1, 0x1, 0x2, 0x2, + 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0x8, 0x1a, 0x1a, 0x1a, 0x2, 0x1, 0x4, 0x2, + 0x8, 0x1, 0x7, 0x1b, 0x2, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1b, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1e, + 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x1]; _T Other_Uppercase = [0xa0, 0x21, 0x60, 0x10, 0x83, 0x46, 0x1a]; -_T Sk = [0x5e, 0x1, 0x1, 0x1, 0x47, 0x1, 0x6, 0x1, 0x4, 0x1, 0x3, 0x1, 0x82, 0x9, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x11, 0x75, 0x1, 0xe, 0x2, 0x9c, 0x37, 0x1, 0x1, 0x3, 0xb, 0x3, 0xd, 0x3, 0xd, 0x3, 0xd, 0x2, 0x90, 0x9c, 0x2, 0xa0, 0x76, 0x63, 0x17, 0x9, 0x2, 0x67, 0x2, 0xa0, 0x54, 0x27, 0x10, 0x83, 0x7c, 0x1, 0x1, 0x1, 0x80, 0xa2, 0x1]; +_T Sk = [0x5e, 0x1, 0x1, 0x1, 0x47, 0x1, 0x6, 0x1, 0x4, 0x1, 0x3, 0x1, 0x82, + 0x9, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x11, 0x75, 0x1, 0xe, 0x2, + 0x9c, 0x37, 0x1, 0x1, 0x3, 0xb, 0x3, 0xd, 0x3, 0xd, 0x3, 0xd, 0x2, 0x90, + 0x9c, 0x2, 0xa0, 0x76, 0x63, 0x17, 0x9, 0x2, 0x67, 0x2, 0xa0, 0x54, 0x27, + 0x10, 0x83, 0x7c, 0x1, 0x1, 0x1, 0x80, 0xa2, 0x1]; _T Other_ID_Start = [0xa0, 0x21, 0x18, 0x1, 0x15, 0x1, 0x8f, 0x6c, 0x2]; -_T Nl = [0x96, 0xee, 0x3, 0x8a, 0x6f, 0x23, 0x2, 0x4, 0x8e, 0x7e, 0x1, 0x19, 0x9, 0xe, 0x3, 0xa0, 0x76, 0xab, 0xa, 0xa0, 0x5a, 0x50, 0x35, 0x81, 0xcc, 0x1, 0x8, 0x1, 0x80, 0x86, 0x5, 0xa0, 0x20, 0x2a, 0x63]; -_T Other_Alphabetic = [0x83, 0x45, 0x1, 0x82, 0x6a, 0xe, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x48, 0xb, 0x30, 0xd, 0x1, 0x7, 0x10, 0x1, 0x65, 0x7, 0x4, 0x4, 0x2, 0x2, 0x4, 0x1, 0x23, 0x1, 0x1e, 0x10, 0x66, 0xb, 0x65, 0x2, 0x3, 0x9, 0x1, 0x3, 0x1, 0x4, 0x80, 0xb7, 0x6, 0x6, 0xf, 0x1, 0x4, 0x36, 0x2, 0x2, 0xf, 0x1, 0x2, 0x5, 0x3, 0xa, 0x2, 0x1d, 0x3, 0x3a, 0x7, 0x2, 0x2, 0x2, 0x2, 0xa, 0x1, 0xa, 0x2, 0x1d, 0x3, 0x3a, 0x5, 0x4, 0x2, 0x2, 0x2, 0x4, 0x1, 0x1e, 0x2, 0x3, 0x1, 0xb, 0x3, 0x3a, 0x8, 0x1, 0x3, 0x1, 0x2, 0x15, 0x2, 0x1d, 0x3, 0x3a, 0x7, 0x2, 0x2, 0x2, 0x2, 0x9, 0x2, 0xa, 0x2, 0x1e, 0x1, 0x3b, 0x5, 0x3, 0x3, 0x1, 0x3, 0xa, 0x1, 0x29, 0x3, 0x3a, 0x7, 0x1, 0x3, 0x1, 0x3, 0x8, 0x2, 0xb, 0x2, 0x1e, 0x2, 0x3a, 0x7, 0x1, 0x3, 0x1, 0x3, 0x8, 0x2, 0xb, 0x2, 0x1e, 0x2, 0x3a, 0x7, 0x1, 0x3, 0x1, 0x3, 0xa, 0x1, 0xa, 0x2, 0x1e, 0x2, 0x4b, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0x3d, 0x1, 0x2, 0x7, 0x12, 0x1, 0x63, 0x1, 0x2, 0x6, 0x1, 0x2, 0x10, 0x1, 0x80, 0xa3, 0x11, 0xb, 0xb, 0x1, 0x24, 0x6e, 0xc, 0x1, 0x1, 0x2, 0x4, 0x17, 0x4, 0x4, 0x3, 0x1, 0x1, 0x4, 0x2, 0x8, 0x4, 0xd, 0x5, 0x15, 0x2, 0x82, 0xc1, 0x1, 0x83, 0xb2, 0x2, 0x1e, 0x2, 0x1e, 0x2, 0x1e, 0x2, 0x42, 0x13, 0x80, 0xe0, 0x1, 0x76, 0xc, 0x4, 0x9, 0x77, 0x11, 0x7, 0x2, 0x4d, 0x5, 0x39, 0xa, 0x2, 0x14, 0x80, 0x8b, 0x5, 0x30, 0xf, 0x3c, 0x3, 0x1e, 0x9, 0x2, 0x2, 0x39, 0xb, 0x32, 0x12, 0x80, 0xbc, 0x2, 0x87, 0xc2, 0x34, 0x88, 0xf6, 0x20, 0xa0, 0x78, 0x74, 0x8, 0x23, 0x1, 0x81, 0x83, 0x5, 0x58, 0x2, 0x32, 0x10, 0x62, 0x5, 0x1c, 0xc, 0x2d, 0x4, 0x30, 0xc, 0x69, 0xe, 0xc, 0x1, 0x8, 0x2, 0x62, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x1, 0x2c, 0x5, 0x5, 0x1, 0x80, 0xed, 0x8, 0xa0, 0x4f, 0x33, 0x1, 0x8e, 0xe2, 0x3, 0x1, 0x2, 0x5, 0x4, 0x85, 0xf0, 0x3, 0x35, 0xe, 0x3c, 0x1, 0x2d, 0x9, 0x47, 0x3, 0x24, 0xc, 0x4d, 0x3, 0x30, 0xd, 0x84, 0xeb, 0xb, 0xa0, 0x58, 0x9b, 0x2e]; -_T Alphabetic = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x56, 0x1, 0x2a, 0x5, 0x1, 0x2, 0x2, 0x4, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x28, 0xe, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x8, 0x1b, 0x5, 0x3, 0x1d, 0xb, 0x5, 0x38, 0x1, 0x7, 0xe, 0x66, 0x1, 0x8, 0x4, 0x8, 0x4, 0x3, 0xa, 0x3, 0x2, 0x1, 0x10, 0x30, 0xd, 0x65, 0x18, 0x21, 0x9, 0x2, 0x4, 0x1, 0x5, 0x18, 0x2, 0x13, 0x13, 0x19, 0x47, 0x1, 0x1, 0xb, 0x37, 0x6, 0x6, 0xf, 0x1, 0x3c, 0x1, 0x10, 0x1, 0x3, 0x4, 0xf, 0xd, 0x7, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x3, 0x8, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0xc, 0x2, 0xf, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x4, 0x5, 0x4, 0x2, 0x2, 0x2, 0x4, 0x1, 0x7, 0x4, 0x1, 0x1, 0x11, 0x6, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x9, 0x1, 0x3, 0x1, 0x2, 0x3, 0x1, 0xf, 0x4, 0x1d, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x8, 0x2, 0x2, 0x2, 0x2, 0x9, 0x2, 0x4, 0x2, 0x1, 0x5, 0xd, 0x1, 0x10, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x6, 0x1, 0x29, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x3, 0x8, 0x2, 0x1, 0x2, 0x6, 0x4, 0x1e, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x3, 0x8, 0x2, 0x7, 0x1, 0x1, 0x4, 0xd, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x3, 0x1, 0x1, 0x8, 0x1, 0x8, 0x4, 0x16, 0x6, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x8, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0xd, 0x3a, 0x5, 0x7, 0x6, 0x1, 0x33, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x6, 0x1, 0xe, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, 0x24, 0x4, 0x11, 0x6, 0x10, 0x1, 0x24, 0x43, 0x37, 0x1, 0x1, 0x2, 0x5, 0x10, 0x13, 0x2, 0x4, 0x5, 0x19, 0x7, 0x1, 0xd, 0x2, 0x2, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x4, 0x1, 0x20, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x6, 0xc, 0x14, 0xc, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x34, 0x2, 0x13, 0xe, 0x1, 0x4, 0x1, 0x43, 0x58, 0x8, 0x2b, 0x5, 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0x9, 0x17, 0x1e, 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x36, 0x1c, 0x4, 0x3f, 0x2, 0x14, 0x32, 0x1, 0x58, 0x34, 0x1, 0xf, 0x1, 0x7, 0x34, 0x2a, 0x2, 0x4, 0xa, 0x2c, 0x1, 0xb, 0xe, 0x36, 0x17, 0x3, 0xa, 0x24, 0x6b, 0x4, 0x1, 0x6, 0x1, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0xb, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x83, 0x2d, 0x34, 0x87, 0x16, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, 0x10, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x20, 0x2f, 0x1, 0x81, 0xd5, 0x3, 0x19, 0x9, 0x7, 0x5, 0x2, 0x5, 0x4, 0x56, 0x6, 0x3, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x10, 0xa, 0x2, 0x14, 0x2f, 0x5, 0x8, 0x3, 0x19, 0x7, 0x51, 0x27, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x1c, 0x18, 0x34, 0xc, 0x44, 0x2e, 0x6, 0x3, 0x1, 0xe, 0x21, 0x5, 0x23, 0xd, 0x1d, 0x3, 0x33, 0x1, 0xc, 0xf, 0x1, 0x30, 0x37, 0x9, 0xe, 0x12, 0x17, 0x3, 0x1, 0x5, 0x3f, 0x1, 0x1, 0x1, 0x1, 0x18, 0x3, 0x2, 0x10, 0x2, 0x4, 0xb, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x2b, 0x15, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0xc, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xc, 0x74, 0x5, 0x1, 0x80, 0x87, 0x24, 0x1a, 0x6, 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, 0x81, 0xb, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xb7, 0x46, 0x3c, 0x37, 0x17, 0x19, 0x17, 0x33, 0x4d, 0x40, 0x1, 0x4, 0x84, 0xbb, 0x36, 0x89, 0x4a, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x14, 0xd, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x23, 0xfe, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x96, 0x34, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; -_T Zs = [0x20, 0x1, 0x7f, 0x1, 0x95, 0xdf, 0x1, 0x89, 0x7f, 0xb, 0x24, 0x1, 0x2f, 0x1, 0x8f, 0xa0, 0x1]; -_T Variation_Selector = [0x98, 0xb, 0x3, 0xa0, 0xe5, 0xf2, 0x10, 0xad, 0x2, 0xf0, 0x80, 0xf0]; -_T Other_Default_Ignorable_Code_Point = [0x83, 0x4f, 0x1, 0x8e, 0xf, 0x2, 0x86, 0x53, 0x2, 0x88, 0xaf, 0x1, 0x90, 0xfe, 0x1, 0xa0, 0xce, 0x3b, 0x1, 0x4f, 0x9, 0xad, 0x0, 0x7, 0x1, 0x1, 0x1e, 0x60, 0x80, 0x80, 0x80, 0xf0, 0x8e, 0x10]; +_T Nl = [0x96, 0xee, 0x3, 0x8a, 0x6f, 0x23, 0x2, 0x4, 0x8e, 0x7e, 0x1, 0x19, + 0x9, 0xe, 0x3, 0xa0, 0x76, 0xab, 0xa, 0xa0, 0x5a, 0x50, 0x35, 0x81, 0xcc, + 0x1, 0x8, 0x1, 0x80, 0x86, 0x5, 0xa0, 0x20, 0x2a, 0x63]; +_T Other_Alphabetic = [ + 0x83, 0x45, 0x1, 0x82, 0x6a, 0xe, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, + 0x48, 0xb, 0x30, 0xd, 0x1, 0x7, 0x10, 0x1, 0x65, 0x7, 0x4, 0x4, 0x2, 0x2, + 0x4, 0x1, 0x23, 0x1, 0x1e, 0x10, 0x66, 0xb, 0x65, 0x2, 0x3, 0x9, 0x1, + 0x3, 0x1, 0x4, 0x80, 0xb7, 0x6, 0x6, 0xf, 0x1, 0x4, 0x36, 0x2, 0x2, 0xf, + 0x1, 0x2, 0x5, 0x3, 0xa, 0x2, 0x1d, 0x3, 0x3a, 0x7, 0x2, 0x2, 0x2, 0x2, + 0xa, 0x1, 0xa, 0x2, 0x1d, 0x3, 0x3a, 0x5, 0x4, 0x2, 0x2, 0x2, 0x4, 0x1, + 0x1e, 0x2, 0x3, 0x1, 0xb, 0x3, 0x3a, 0x8, 0x1, 0x3, 0x1, 0x2, 0x15, 0x2, + 0x1d, 0x3, 0x3a, 0x7, 0x2, 0x2, 0x2, 0x2, 0x9, 0x2, 0xa, 0x2, 0x1e, 0x1, + 0x3b, 0x5, 0x3, 0x3, 0x1, 0x3, 0xa, 0x1, 0x29, 0x3, 0x3a, 0x7, 0x1, 0x3, + 0x1, 0x3, 0x8, 0x2, 0xb, 0x2, 0x1e, 0x2, 0x3a, 0x7, 0x1, 0x3, 0x1, 0x3, + 0x8, 0x2, 0xb, 0x2, 0x1e, 0x2, 0x3a, 0x7, 0x1, 0x3, 0x1, 0x3, 0xa, 0x1, + 0xa, 0x2, 0x1e, 0x2, 0x4b, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0x3d, 0x1, + 0x2, 0x7, 0x12, 0x1, 0x63, 0x1, 0x2, 0x6, 0x1, 0x2, 0x10, 0x1, 0x80, + 0xa3, 0x11, 0xb, 0xb, 0x1, 0x24, 0x6e, 0xc, 0x1, 0x1, 0x2, 0x4, 0x17, 0x4, + 0x4, 0x3, 0x1, 0x1, 0x4, 0x2, 0x8, 0x4, 0xd, 0x5, 0x15, 0x2, 0x82, 0xc1, + 0x1, 0x83, 0xb2, 0x2, 0x1e, 0x2, 0x1e, 0x2, 0x1e, 0x2, 0x42, 0x13, 0x80, + 0xe0, 0x1, 0x76, 0xc, 0x4, 0x9, 0x77, 0x11, 0x7, 0x2, 0x4d, 0x5, 0x39, 0xa, + 0x2, 0x14, 0x80, 0x8b, 0x5, 0x30, 0xf, 0x3c, 0x3, 0x1e, 0x9, 0x2, 0x2, + 0x39, 0xb, 0x32, 0x12, 0x80, 0xbc, 0x2, 0x87, 0xc2, 0x34, 0x88, 0xf6, 0x20, + 0xa0, 0x78, 0x74, 0x8, 0x23, 0x1, 0x81, 0x83, 0x5, 0x58, 0x2, 0x32, 0x10, + 0x62, 0x5, 0x1c, 0xc, 0x2d, 0x4, 0x30, 0xc, 0x69, 0xe, 0xc, 0x1, 0x8, + 0x2, 0x62, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x1, 0x2c, 0x5, 0x5, 0x1, 0x80, + 0xed, 0x8, 0xa0, 0x4f, 0x33, 0x1, 0x8e, 0xe2, 0x3, 0x1, 0x2, 0x5, 0x4, + 0x85, 0xf0, 0x3, 0x35, 0xe, 0x3c, 0x1, 0x2d, 0x9, 0x47, 0x3, 0x24, 0xc, + 0x4d, 0x3, 0x30, 0xd, 0x84, 0xeb, 0xb, 0xa0, 0x58, 0x9b, 0x2e +]; +_T Alphabetic = [ + 0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, + 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x56, 0x1, 0x2a, + 0x5, 0x1, 0x2, 0x2, 0x4, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, + 0x53, 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, + 0x28, 0xe, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x8, 0x1b, 0x5, 0x3, + 0x1d, 0xb, 0x5, 0x38, 0x1, 0x7, 0xe, 0x66, 0x1, 0x8, 0x4, 0x8, 0x4, 0x3, + 0xa, 0x3, 0x2, 0x1, 0x10, 0x30, 0xd, 0x65, 0x18, 0x21, 0x9, 0x2, 0x4, + 0x1, 0x5, 0x18, 0x2, 0x13, 0x13, 0x19, 0x47, 0x1, 0x1, 0xb, 0x37, 0x6, + 0x6, 0xf, 0x1, 0x3c, 0x1, 0x10, 0x1, 0x3, 0x4, 0xf, 0xd, 0x7, 0x1, 0x7, + 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, + 0x3, 0x8, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0xc, + 0x2, 0xf, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, + 0x2, 0x1, 0x2, 0x4, 0x5, 0x4, 0x2, 0x2, 0x2, 0x4, 0x1, 0x7, 0x4, 0x1, + 0x1, 0x11, 0x6, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x5, 0x3, 0x9, 0x1, 0x3, 0x1, 0x2, 0x3, 0x1, 0xf, 0x4, 0x1d, + 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, + 0x8, 0x2, 0x2, 0x2, 0x2, 0x9, 0x2, 0x4, 0x2, 0x1, 0x5, 0xd, 0x1, 0x10, + 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, + 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x6, 0x1, + 0x29, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, + 0x1, 0x3, 0x1, 0x3, 0x8, 0x2, 0x1, 0x2, 0x6, 0x4, 0x1e, 0x2, 0x1, 0x8, + 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x3, + 0x8, 0x2, 0x7, 0x1, 0x1, 0x4, 0xd, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, + 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x3, 0x1, 0x1, 0x8, 0x1, 0x8, 0x4, + 0x16, 0x6, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, + 0x8, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0xd, 0x3a, 0x5, 0x7, 0x6, 0x1, + 0x33, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, + 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, + 0x1, 0x6, 0x1, 0xe, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, 0x24, 0x4, 0x11, 0x6, + 0x10, 0x1, 0x24, 0x43, 0x37, 0x1, 0x1, 0x2, 0x5, 0x10, 0x13, 0x2, 0x4, + 0x5, 0x19, 0x7, 0x1, 0xd, 0x2, 0x2, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, + 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, + 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, + 0x39, 0x1, 0x4, 0x2, 0x43, 0x4, 0x1, 0x20, 0x10, 0x10, 0x55, 0xc, 0x82, + 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x6, 0xc, + 0x14, 0xc, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x34, 0x2, 0x13, 0xe, + 0x1, 0x4, 0x1, 0x43, 0x58, 0x8, 0x2b, 0x5, 0x46, 0xa, 0x1d, 0x3, 0xc, + 0x4, 0x9, 0x17, 0x1e, 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x36, 0x1c, 0x4, + 0x3f, 0x2, 0x14, 0x32, 0x1, 0x58, 0x34, 0x1, 0xf, 0x1, 0x7, 0x34, 0x2a, + 0x2, 0x4, 0xa, 0x2c, 0x1, 0xb, 0xe, 0x36, 0x17, 0x3, 0xa, 0x24, 0x6b, 0x4, + 0x1, 0x6, 0x1, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, + 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, + 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, + 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, 0x1, 0x4, + 0x1, 0x2, 0xa, 0x1, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, + 0x1, 0xb, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x83, 0x2d, 0x34, 0x87, + 0x16, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, + 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, 0x10, 0x17, 0x9, 0x7, 0x1, 0x7, + 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x20, + 0x2f, 0x1, 0x81, 0xd5, 0x3, 0x19, 0x9, 0x7, 0x5, 0x2, 0x5, 0x4, 0x56, + 0x6, 0x3, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, + 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x84, 0x8d, + 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x10, 0xa, 0x2, 0x14, 0x2f, 0x5, 0x8, 0x3, + 0x19, 0x7, 0x51, 0x27, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, + 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x1c, 0x18, 0x34, 0xc, 0x44, 0x2e, 0x6, + 0x3, 0x1, 0xe, 0x21, 0x5, 0x23, 0xd, 0x1d, 0x3, 0x33, 0x1, 0xc, 0xf, 0x1, + 0x30, 0x37, 0x9, 0xe, 0x12, 0x17, 0x3, 0x1, 0x5, 0x3f, 0x1, 0x1, 0x1, + 0x1, 0x18, 0x3, 0x2, 0x10, 0x2, 0x4, 0xb, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, + 0x7, 0x1, 0x7, 0x80, 0x91, 0x2b, 0x15, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, + 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, + 0xc, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, + 0x81, 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xc, 0x74, 0x5, 0x1, 0x80, 0x87, + 0x24, 0x1a, 0x6, 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, + 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, + 0x45, 0x35, 0x81, 0xb, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, + 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x83, 0x62, 0x6, + 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, + 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, + 0x1b, 0x2c, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, + 0x83, 0xb7, 0x46, 0x3c, 0x37, 0x17, 0x19, 0x17, 0x33, 0x4d, 0x40, 0x1, 0x4, + 0x84, 0xbb, 0x36, 0x89, 0x4a, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, + 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, + 0x14, 0xd, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x23, 0xfe, 0x55, 0x1, 0x47, 0x1, + 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, + 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, + 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, + 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, + 0x96, 0x34, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, + 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, + 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, + 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, + 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e +]; +_T Zs = [0x20, 0x1, 0x7f, 0x1, 0x95, 0xdf, 0x1, 0x89, 0x7f, 0xb, 0x24, 0x1, + 0x2f, 0x1, 0x8f, 0xa0, 0x1]; +_T Variation_Selector = [0x98, 0xb, 0x3, 0xa0, 0xe5, 0xf2, 0x10, 0xad, 0x2, 0xf0, 0x80, + 0xf0]; +_T Other_Default_Ignorable_Code_Point = [ + 0x83, 0x4f, 0x1, 0x8e, 0xf, 0x2, 0x86, 0x53, 0x2, 0x88, 0xaf, 0x1, 0x90, + 0xfe, 0x1, 0xa0, 0xce, 0x3b, 0x1, 0x4f, 0x9, 0xad, 0x0, 0x7, 0x1, 0x1, + 0x1e, 0x60, 0x80, 0x80, 0x80, 0xf0, 0x8e, 0x10 +]; _T IDS_Binary_Operator = [0xa0, 0x2f, 0xf0, 0x2, 0x2, 0x8]; -_T Grapheme_Base = [0x20, 0x5f, 0x21, 0xd, 0x1, 0x82, 0x52, 0x70, 0x8, 0x2, 0x5, 0x5, 0x7, 0x1, 0x1, 0x1, 0x14, 0x1, 0x80, 0xe0, 0x7, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x7, 0x1, 0x27, 0x1, 0x2, 0x4, 0x1, 0x2e, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x9, 0x1b, 0x5, 0x5, 0x11, 0xa, 0xb, 0x1, 0x2, 0x2d, 0x15, 0x10, 0x1, 0x65, 0x8, 0x1, 0x6, 0x2, 0x2, 0x1, 0x4, 0x20, 0x2, 0x1, 0x1, 0x1e, 0x1d, 0x59, 0xb, 0x1, 0xe, 0x2b, 0x9, 0x7, 0x5, 0x16, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x7, 0xf, 0x1, 0x19, 0x5, 0x1, 0x41, 0x1, 0x1, 0xb, 0x56, 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, 0x14, 0x1, 0x7, 0x2, 0x2, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x3, 0x1, 0x1, 0x2, 0x6, 0x2, 0x2, 0x2, 0x1, 0x1, 0xd, 0x2, 0x1, 0x3, 0x4, 0x16, 0x7, 0x1, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x4, 0x3, 0x18, 0x4, 0x1, 0x1, 0x7, 0xa, 0x2, 0x3, 0xe, 0x1, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x4, 0x8, 0x1, 0x1, 0x2, 0x3, 0x1, 0xf, 0x2, 0x4, 0xc, 0x10, 0x2, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x2, 0x1, 0x6, 0x2, 0x2, 0x2, 0xf, 0x2, 0x1, 0x3, 0x4, 0x12, 0xb, 0x1, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x5, 0x1, 0x1, 0x2, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x15, 0x15, 0x6, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x3, 0x4, 0x13, 0x2, 0x6, 0x2, 0x4, 0xa, 0x8, 0x8, 0x2, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x2, 0x1, 0x2, 0x12, 0x1, 0x1, 0x2, 0x4, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x1, 0x2, 0x5, 0x3, 0x1, 0x3, 0x1, 0x1, 0x11, 0x2, 0x4, 0x10, 0x3, 0x7, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x9, 0x2, 0x6, 0x7, 0x13, 0x3, 0xc, 0x30, 0x1, 0x2, 0xb, 0x8, 0x8, 0xd, 0x25, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, 0x2, 0x9, 0x1, 0x2, 0x5, 0x1, 0x1, 0x9, 0xa, 0x2, 0x4, 0x20, 0x18, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0xe, 0x1, 0x24, 0x12, 0x1, 0x5, 0x1, 0x2, 0x5, 0x31, 0x8, 0x1, 0x6, 0x1, 0xd, 0x25, 0x2d, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, 0x28, 0x1, 0x1, 0x5, 0x1, 0x2, 0x81, 0x79, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x5, 0x1d, 0x3, 0x1a, 0x6, 0x55, 0xb, 0x82, 0x9d, 0x3, 0x51, 0xf, 0xd, 0x1, 0x4, 0xe, 0x12, 0x3, 0x2, 0x9, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x9, 0x3, 0xa, 0x6, 0xa, 0x6, 0xb, 0x5, 0xa, 0x6, 0x58, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x6, 0x4, 0x2, 0x3, 0x4, 0x2, 0x1, 0x6, 0x7, 0x1, 0x3, 0x2a, 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x6, 0xb, 0x3, 0x39, 0x2, 0x2, 0x3, 0x38, 0x1, 0x1, 0x9, 0x1, 0x1, 0x2, 0x8, 0x6, 0xd, 0xa, 0x6, 0xa, 0x6, 0xe, 0x56, 0x30, 0x1, 0x1, 0x5, 0x1, 0x1, 0x5, 0x1, 0x9, 0x4, 0x1b, 0x9, 0x9, 0x5, 0x20, 0x4, 0x2, 0x2, 0x1, 0x1, 0x3a, 0x1, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x2, 0x8, 0x30, 0x8, 0x2, 0x5, 0xf, 0x3, 0x33, 0x40, 0x8, 0xb, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0xf, 0x1, 0xe, 0x2, 0x6, 0x1, 0x13, 0x2, 0x3, 0x1, 0x9, 0x1, 0xb, 0x5, 0x18, 0x7, 0x31, 0x10, 0x2, 0x2, 0x1b, 0x1, 0xd, 0x3, 0x1b, 0x45, 0x80, 0x8a, 0x6, 0x82, 0x64, 0xc, 0x27, 0x19, 0xb, 0x15, 0x82, 0xa0, 0x1, 0x84, 0x4c, 0x3, 0xa, 0x80, 0xa6, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x8f, 0x3, 0x2, 0x5, 0x2d, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x2, 0xf, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x21, 0x3c, 0x44, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x1a, 0xc, 0x4, 0x2a, 0x6, 0x10, 0x1, 0x56, 0x4, 0x65, 0x5, 0x29, 0x3, 0x5e, 0x1, 0x2b, 0x5, 0x24, 0xc, 0x2f, 0x1, 0x80, 0xdf, 0x1, 0x9a, 0xb6, 0xa, 0xa0, 0x52, 0xd, 0x33, 0x84, 0x8d, 0x3, 0x37, 0x9, 0x81, 0x5c, 0x14, 0x2f, 0x4, 0x1, 0xa, 0x1a, 0x8, 0x50, 0x2, 0x6, 0x8, 0x80, 0x8f, 0x1, 0x4, 0xc, 0xb, 0x4d, 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, 0x5, 0x4, 0xa, 0x6, 0x38, 0x8, 0x44, 0xa, 0xc, 0x18, 0xa, 0x4, 0x26, 0x8, 0x19, 0xb, 0x2, 0xb, 0x1e, 0x6, 0x30, 0x1, 0x2, 0x4, 0x2, 0x1, 0x11, 0x1, 0xb, 0x4, 0x2, 0x20, 0x29, 0x6, 0x2, 0x2, 0x2, 0xb, 0x3, 0x1, 0x8, 0x1, 0x1, 0x2, 0xa, 0x2, 0x20, 0x4, 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x11, 0x2, 0x8, 0xb, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x25, 0x1, 0x2, 0x1, 0x4, 0x3, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1, 0x1, 0x18, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7c, 0x11, 0x81, 0x6d, 0x10, 0x40, 0x2, 0x36, 0x28, 0xe, 0x12, 0xa, 0x16, 0x23, 0x1, 0x13, 0x1, 0x4, 0x4, 0x5, 0x1, 0x80, 0x87, 0x4, 0x80, 0x9d, 0x2, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x3, 0x7, 0x1, 0x7, 0xd, 0x2, 0x2, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x5, 0x3, 0x4, 0x2d, 0x3, 0x54, 0x5, 0xc, 0x34, 0x2d, 0x80, 0x83, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x1, 0x4, 0xc, 0x1b, 0x35, 0x1e, 0x1, 0x25, 0x4, 0xe, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x1, 0x9, 0x80, 0xa0, 0x1c, 0x3, 0x1b, 0x5, 0x1, 0x40, 0x38, 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0xc, 0x8, 0x8, 0x9, 0x7, 0x20, 0x80, 0x80, 0x36, 0x3, 0x1d, 0x2, 0x1b, 0x5, 0x8, 0x80, 0x80, 0x49, 0x82, 0x17, 0x1f, 0x81, 0x81, 0x1, 0x1, 0x36, 0xf, 0x7, 0x4, 0x1e, 0x12, 0x31, 0x4, 0x2, 0x2, 0x2, 0x1, 0x4, 0xe, 0x19, 0x7, 0xa, 0x9, 0x24, 0x5, 0x1, 0x9, 0xe, 0x3e, 0x34, 0x9, 0xa, 0x7, 0xa, 0x84, 0xa6, 0x2b, 0x1, 0x1, 0x1, 0x2, 0x6, 0x1, 0x9, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0xd, 0x4, 0x8b, 0x8c, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x14, 0xd, 0xa0, 0x40, 0x60, 0x2, 0x9f, 0xfe, 0x80, 0xf6, 0xa, 0x27, 0x2, 0x3c, 0x1, 0x1, 0x3, 0x4, 0x15, 0x2, 0x7, 0x1e, 0x4, 0x30, 0x22, 0x42, 0x3, 0x1, 0x80, 0xba, 0x57, 0x9, 0x12, 0x80, 0x8e, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2, 0x81, 0xe, 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, 0x2, 0xf, 0x1, 0xf, 0x20, 0xb, 0x5, 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1d, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74, 0x88, 0x8c, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; -_T Case_Ignorable = [0x27, 0x1, 0x6, 0x1, 0xb, 0x1, 0x23, 0x1, 0x1, 0x1, 0x47, 0x1, 0x4, 0x1, 0x1, 0x1, 0x4, 0x1, 0x2, 0x2, 0x81, 0xf7, 0x80, 0xc0, 0x4, 0x2, 0x4, 0x1, 0x9, 0x2, 0x1, 0x1, 0x80, 0xfb, 0x7, 0x80, 0xcf, 0x1, 0x37, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x2c, 0x1, 0xb, 0x5, 0xb, 0xb, 0x1, 0x1, 0x23, 0x1, 0xa, 0x15, 0x10, 0x1, 0x65, 0x8, 0x1, 0xa, 0x1, 0x4, 0x21, 0x1, 0x1, 0x1, 0x1e, 0x1b, 0x5b, 0xb, 0x3a, 0xb, 0x4, 0x1, 0x1b, 0x18, 0x2b, 0x3, 0x80, 0x88, 0x1b, 0x1, 0x3, 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, 0xd, 0x1, 0xf, 0x1, 0x3a, 0x1, 0x4, 0x4, 0x8, 0x1, 0x14, 0x2, 0x1d, 0x2, 0x39, 0x1, 0x4, 0x2, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x1e, 0x2, 0x3, 0x1, 0xb, 0x2, 0x39, 0x1, 0x4, 0x5, 0x1, 0x2, 0x4, 0x1, 0x14, 0x2, 0x1d, 0x1, 0x3a, 0x1, 0x2, 0x1, 0x1, 0x4, 0x8, 0x1, 0x8, 0x1, 0xb, 0x2, 0x1e, 0x1, 0x3d, 0x1, 0xc, 0x1, 0x70, 0x3, 0x5, 0x3, 0x1, 0x4, 0x7, 0x2, 0xb, 0x2, 0x58, 0x1, 0x2, 0x1, 0x6, 0x1, 0x5, 0x2, 0x14, 0x2, 0x5d, 0x4, 0x8, 0x1, 0x14, 0x2, 0x66, 0x1, 0x7, 0x3, 0x1, 0x1, 0x5a, 0x1, 0x2, 0x7, 0xb, 0x9, 0x62, 0x1, 0x2, 0x6, 0x1, 0x2, 0x9, 0x1, 0x1, 0x6, 0x4a, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x37, 0xe, 0x1, 0x5, 0x1, 0x2, 0x5, 0xb, 0x1, 0x24, 0x9, 0x1, 0x66, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, 0x5e, 0x1, 0x82, 0x60, 0x3, 0x83, 0xb2, 0x3, 0x1d, 0x3, 0x1d, 0x2, 0x1e, 0x2, 0x40, 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x3, 0x1, 0x5, 0x1, 0x2d, 0x4, 0x34, 0x1, 0x65, 0x1, 0x76, 0x3, 0x4, 0x2, 0x9, 0x1, 0x6, 0x3, 0x80, 0xdb, 0x2, 0x2, 0x1, 0x3a, 0x1, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x2, 0x8, 0x6, 0xa, 0x2, 0x1, 0x27, 0x1, 0x58, 0x4, 0x30, 0x1, 0x1, 0x5, 0x1, 0x1, 0x5, 0x1, 0x28, 0x9, 0xc, 0x2, 0x20, 0x4, 0x2, 0x2, 0x1, 0x1, 0x3a, 0x1, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x3a, 0x8, 0x2, 0x2, 0x40, 0x6, 0x52, 0x3, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x37, 0x3f, 0xd, 0x1, 0x22, 0x4c, 0x15, 0x4, 0x81, 0xbd, 0x1, 0x1, 0x3, 0xb, 0x3, 0xd, 0x3, 0xd, 0x3, 0xd, 0x2, 0xc, 0x5, 0x8, 0x2, 0xa, 0x1, 0x2, 0x1, 0x2, 0x5, 0x31, 0x5, 0x1, 0xa, 0x1, 0x1, 0xd, 0x1, 0x10, 0xd, 0x33, 0x21, 0x8b, 0x8b, 0x2, 0x71, 0x3, 0x7d, 0x1, 0xf, 0x1, 0x60, 0x20, 0x2f, 0x1, 0x81, 0xd5, 0x1, 0x24, 0x4, 0x3, 0x5, 0x5, 0x1, 0x5d, 0x6, 0x5d, 0x3, 0xa0, 0x6f, 0x16, 0x1, 0x84, 0xe2, 0x6, 0x81, 0xe, 0x1, 0x62, 0x4, 0x1, 0xa, 0x1, 0x1, 0x1f, 0x1, 0x50, 0x2, 0xe, 0x22, 0x4e, 0x1, 0x17, 0x3, 0x6d, 0x2, 0x8, 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, 0x80, 0x9d, 0x1, 0x1b, 0x12, 0x34, 0x8, 0x19, 0xb, 0x2e, 0x3, 0x30, 0x1, 0x2, 0x4, 0x2, 0x1, 0x12, 0x1, 0x59, 0x6, 0x2, 0x2, 0x2, 0x2, 0xc, 0x1, 0x8, 0x1, 0x23, 0x1, 0x3f, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1b, 0x1, 0xe, 0x2, 0x5, 0x2, 0x1, 0x1, 0x80, 0xee, 0x1, 0x2, 0x1, 0x4, 0x1, 0xa0, 0x4f, 0x30, 0x1, 0x80, 0x93, 0x10, 0x82, 0x3e, 0x10, 0x3, 0x1, 0xc, 0x7, 0x2b, 0x1, 0x2, 0x1, 0x80, 0xa9, 0x1, 0x7, 0x1, 0x6, 0x1, 0xb, 0x1, 0x23, 0x1, 0x1, 0x1, 0x2f, 0x1, 0x2d, 0x2, 0x43, 0x1, 0x15, 0x3, 0x82, 0x1, 0x1, 0x88, 0x3, 0x3, 0x1, 0x2, 0x5, 0x4, 0x28, 0x3, 0x4, 0x1, 0x85, 0xc1, 0x1, 0x36, 0xf, 0x39, 0x2, 0x31, 0x4, 0x2, 0x2, 0x2, 0x1, 0x42, 0x3, 0x24, 0x5, 0x1, 0x8, 0x4b, 0x2, 0x34, 0x9, 0x84, 0xec, 0x1, 0x1, 0x1, 0x2, 0x6, 0x1, 0x1, 0xa0, 0x58, 0xd7, 0x11, 0xa0, 0x61, 0xc7, 0x3, 0x9, 0x10, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0xac, 0x2d, 0xbc, 0x1, 0x1e, 0x60, 0x80, 0x80, 0x80, 0xf0]; -_T STerm = [0x21, 0x1, 0xc, 0x1, 0x10, 0x1, 0x85, 0x1c, 0x1, 0x1, 0x1, 0x2a, 0x1, 0x80, 0x95, 0x1, 0x80, 0xb4, 0x1, 0x2b, 0x3, 0x80, 0xf6, 0x1, 0x81, 0x6a, 0x2, 0x86, 0xe4, 0x2, 0x83, 0x16, 0x1, 0x4, 0x2, 0x83, 0x5, 0x1, 0x80, 0xc6, 0x2, 0x80, 0xcc, 0x1, 0x5, 0x1, 0x81, 0x3a, 0x2, 0x81, 0x62, 0x4, 0x80, 0xae, 0x2, 0x2, 0x2, 0x80, 0xdb, 0x2, 0x41, 0x2, 0x83, 0xbc, 0x2, 0x9, 0x3, 0x8d, 0xe4, 0x1, 0x81, 0xd3, 0x1, 0xa0, 0x74, 0xfc, 0x1, 0x81, 0xe, 0x2, 0x80, 0xe3, 0x1, 0x3, 0x1, 0x81, 0x7e, 0x2, 0x56, 0x2, 0x5f, 0x1, 0x80, 0x98, 0x2, 0x80, 0x93, 0x3, 0x80, 0x90, 0x2, 0x80, 0xf9, 0x1, 0xa0, 0x52, 0x66, 0x1, 0x3, 0x2, 0x80, 0xa9, 0x1, 0xc, 0x1, 0x10, 0x1, 0x41, 0x1, 0x8a, 0xf4, 0x2, 0x85, 0xef, 0x2, 0x75, 0x4, 0x7f, 0x3, 0x80, 0x81, 0x2]; -_T Diacritic = [0x5e, 0x1, 0x1, 0x1, 0x47, 0x1, 0x6, 0x1, 0x4, 0x1, 0x2, 0x2, 0x81, 0xf7, 0x80, 0x9f, 0x1, 0x8, 0x5, 0x6, 0x11, 0x2, 0x4, 0x1, 0x9, 0x2, 0x80, 0xfd, 0x5, 0x80, 0xd1, 0x1, 0x37, 0x11, 0x1, 0x1b, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x80, 0x86, 0x8, 0x4, 0x2, 0x80, 0x86, 0x2, 0x4, 0x2, 0x3, 0x3, 0x43, 0x1b, 0x5b, 0xb, 0x3a, 0xb, 0x22, 0x2, 0x80, 0xca, 0x1b, 0x3d, 0x1, 0x10, 0x1, 0x3, 0x4, 0x1c, 0x1, 0x4a, 0x1, 0x10, 0x1, 0x6e, 0x1, 0x10, 0x1, 0x6e, 0x1, 0x10, 0x1, 0x6e, 0x1, 0x10, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x6e, 0x1, 0x10, 0x1, 0x7f, 0x1, 0x7c, 0x1, 0x7c, 0x6, 0x1, 0x1, 0x79, 0x5, 0x4b, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x2, 0x42, 0x3, 0x1, 0x2, 0x3e, 0x1, 0x70, 0x1, 0x1, 0x2, 0x4c, 0x7, 0x1, 0x1, 0xa, 0x2, 0x87, 0x2d, 0xb, 0x9, 0x1, 0x81, 0x5b, 0x3, 0x81, 0x39, 0x8, 0x2, 0x1, 0x80, 0xb4, 0x1, 0xf, 0x1, 0x26, 0x9, 0x36, 0x2, 0x80, 0x8a, 0x2, 0x40, 0x6, 0x52, 0x19, 0x4, 0x1, 0x6, 0x1, 0x37, 0x3f, 0x59, 0xc, 0x2d, 0x3, 0x81, 0xbd, 0x1, 0x1, 0x3, 0xb, 0x3, 0xd, 0x3, 0xd, 0x3, 0xd, 0x2, 0x8c, 0xf0, 0x3, 0x81, 0x3d, 0x1, 0x81, 0xfa, 0x6, 0x69, 0x4, 0x5f, 0x1, 0xa0, 0x75, 0x72, 0x1, 0xc, 0x2, 0x1, 0x1, 0x70, 0x2, 0x25, 0xb, 0x66, 0x1, 0x6f, 0x2, 0x80, 0xca, 0x1, 0x1b, 0x12, 0x39, 0x4, 0x24, 0x1, 0x5f, 0x1, 0xc, 0x1, 0x80, 0xba, 0x1, 0x43, 0x4, 0x33, 0x1, 0x80, 0xf5, 0x2, 0xa0, 0x4f, 0x30, 0x1, 0x83, 0x1, 0x7, 0x81, 0x17, 0x1, 0x1, 0x1, 0x2f, 0x1, 0x2d, 0x2, 0x43, 0x1, 0x90, 0xd5, 0x2, 0x78, 0x2, 0x80, 0x8b, 0x1, 0x84, 0xf5, 0x2, 0xa0, 0x58, 0xd7, 0x11, 0xa0, 0x61, 0xc7, 0x3, 0x3, 0x6, 0x8, 0x8, 0x2, 0x7, 0x1e, 0x4]; -_T Lm = [0x82, 0xb0, 0x12, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x80, 0x85, 0x1, 0x5, 0x1, 0x81, 0xde, 0x1, 0x80, 0xe6, 0x1, 0x80, 0xa4, 0x2, 0x81, 0xd, 0x2, 0x4, 0x1, 0x1f, 0x1, 0x9, 0x1, 0x3, 0x1, 0x81, 0x48, 0x1, 0x84, 0xd4, 0x1, 0x7f, 0x1, 0x82, 0x35, 0x1, 0x86, 0xda, 0x1, 0x6b, 0x1, 0x82, 0x63, 0x1, 0x81, 0xd0, 0x6, 0x80, 0xae, 0x3f, 0xd, 0x1, 0x22, 0x25, 0x82, 0xb1, 0x1, 0xd, 0x1, 0x10, 0xd, 0x8b, 0xdf, 0x2, 0x80, 0xf1, 0x1, 0x80, 0xbf, 0x1, 0x81, 0xd5, 0x1, 0x2b, 0x5, 0x5, 0x1, 0x61, 0x2, 0x5d, 0x3, 0xa0, 0x6f, 0x16, 0x1, 0x84, 0xe2, 0x6, 0x81, 0xe, 0x1, 0x72, 0x1, 0x80, 0x97, 0x9, 0x50, 0x1, 0x17, 0x1, 0x6f, 0x2, 0x81, 0xd5, 0x1, 0x80, 0xa0, 0x1, 0x6c, 0x1, 0x15, 0x2, 0xa0, 0x54, 0x7b, 0x1, 0x2d, 0x2, 0xa0, 0x6f, 0xf3, 0xd]; -_T Mc = [0x89, 0x3, 0x1, 0x37, 0x1, 0x2, 0x3, 0x8, 0x4, 0x1, 0x2, 0x32, 0x2, 0x3a, 0x3, 0x6, 0x2, 0x2, 0x2, 0xa, 0x1, 0x2b, 0x1, 0x3a, 0x3, 0x42, 0x1, 0x3a, 0x3, 0x8, 0x1, 0x1, 0x2, 0x35, 0x2, 0x3a, 0x1, 0x1, 0x1, 0x6, 0x2, 0x2, 0x2, 0xa, 0x1, 0x66, 0x2, 0x1, 0x2, 0x3, 0x3, 0x1, 0x3, 0xa, 0x1, 0x29, 0x3, 0x3d, 0x4, 0x3d, 0x2, 0x3a, 0x1, 0x1, 0x5, 0x2, 0x2, 0x1, 0x2, 0x9, 0x2, 0x2b, 0x2, 0x3a, 0x3, 0x5, 0x3, 0x1, 0x3, 0xa, 0x1, 0x2a, 0x2, 0x4b, 0x3, 0x6, 0x8, 0x12, 0x2, 0x81, 0x4a, 0x2, 0x3f, 0x1, 0x80, 0xab, 0x2, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x19, 0x2, 0xa, 0x3, 0x2, 0x7, 0x15, 0x2, 0x2, 0x6, 0x2, 0x1, 0xa, 0x3, 0x87, 0x19, 0x1, 0x7, 0x8, 0x1, 0x2, 0x81, 0x5a, 0x4, 0x2, 0x3, 0x4, 0x2, 0x1, 0x6, 0x77, 0x11, 0x7, 0x2, 0x4f, 0x2, 0x3a, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x2, 0x8, 0x6, 0x80, 0x91, 0x1, 0x30, 0x1, 0x5, 0x1, 0x1, 0x5, 0x1, 0x2, 0x3d, 0x1, 0x1e, 0x1, 0x4, 0x2, 0x2, 0x1, 0x1, 0x2, 0x39, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x2, 0x30, 0x8, 0x8, 0x2, 0x80, 0xab, 0x1, 0x10, 0x2, 0x93, 0x3a, 0x2, 0xa0, 0x77, 0xf3, 0x2, 0x2, 0x1, 0x58, 0x2, 0x32, 0x10, 0x80, 0x8e, 0x2, 0x2f, 0x1, 0x30, 0x2, 0x4, 0x2, 0x1, 0x4, 0x6e, 0x2, 0x2, 0x2, 0x18, 0x1, 0x2d, 0x1, 0x6f, 0x1, 0x2, 0x2, 0x5, 0x1, 0x80, 0xed, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0xa0, 0x64, 0x13, 0x1, 0x1, 0x1, 0x7f, 0x1, 0x2d, 0x3, 0x4, 0x2, 0x73, 0x1, 0x55, 0x1, 0x30, 0x3, 0x9, 0x2, 0x84, 0xeb, 0x1, 0x1, 0x2, 0x6, 0x1, 0xa0, 0x58, 0x9a, 0x2e, 0xa0, 0x61, 0xe6, 0x2, 0x6, 0x6]; -_T Lo = [0x80, 0xaa, 0x1, 0xf, 0x1, 0x81, 0x0, 0x1, 0x4, 0x4, 0x80, 0xd0, 0x1, 0x83, 0x3b, 0x1b, 0x5, 0x3, 0x2d, 0x20, 0x1, 0xa, 0x23, 0x2, 0x1, 0x63, 0x1, 0x1, 0x18, 0x2, 0xa, 0x3, 0x2, 0x1, 0x10, 0x1, 0x1, 0x1e, 0x1d, 0x59, 0xb, 0x1, 0x18, 0x21, 0x15, 0x16, 0x2a, 0x19, 0x47, 0x1, 0x1, 0xb, 0x57, 0x36, 0x3, 0x1, 0x12, 0x1, 0x7, 0xa, 0x10, 0x6, 0x1, 0x7, 0x5, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x3, 0x1, 0x10, 0x1, 0xd, 0x2, 0x1, 0x3, 0xe, 0x2, 0x13, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1f, 0x4, 0x1, 0x1, 0x13, 0x3, 0x10, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x12, 0x1, 0xf, 0x2, 0x23, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x1e, 0x2, 0x1, 0x3, 0xf, 0x1, 0x11, 0x1, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x16, 0x1, 0x34, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x1a, 0x2, 0x6, 0x2, 0x23, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x20, 0x1, 0x1, 0x2, 0xf, 0x2, 0x12, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x10, 0x1, 0x11, 0x2, 0x18, 0x6, 0x5, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3a, 0x30, 0x1, 0x2, 0xc, 0x6, 0x3b, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, 0x2, 0x9, 0x1, 0x2, 0x5, 0x17, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, 0x24, 0x1b, 0x5, 0x73, 0x2b, 0x14, 0x1, 0x10, 0x6, 0x4, 0x4, 0x3, 0x1, 0x3, 0x2, 0x7, 0x3, 0x4, 0xd, 0xc, 0x1, 0x41, 0x2b, 0x2, 0x81, 0x4c, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x25, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x15, 0xd, 0x1, 0x4, 0xe, 0x12, 0xe, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x28, 0x1, 0x43, 0x23, 0x1, 0x34, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x33, 0x1e, 0x2, 0x5, 0xb, 0x2c, 0x15, 0x7, 0x38, 0x17, 0x9, 0x35, 0x80, 0xb0, 0x2f, 0x11, 0x7, 0x37, 0x1e, 0xd, 0x2, 0xa, 0x2c, 0x1a, 0x24, 0x29, 0x3, 0xa, 0x1e, 0x71, 0x4, 0x1, 0x4, 0x3, 0x2, 0x84, 0x3e, 0x4, 0x8b, 0xf7, 0x38, 0x18, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x82, 0x27, 0x1, 0x35, 0x1, 0x4, 0x56, 0x8, 0x1, 0x1, 0x5a, 0x4, 0x1, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x15, 0x1, 0x84, 0x77, 0x43, 0x28, 0x8, 0x81, 0xc, 0x4, 0x10, 0xa, 0x2, 0x42, 0x1, 0x31, 0x46, 0x81, 0x15, 0x7, 0x1, 0x3, 0x1, 0x4, 0x1, 0x17, 0x1d, 0x34, 0xe, 0x32, 0x3e, 0x6, 0x3, 0x1, 0xe, 0x1c, 0xa, 0x17, 0x19, 0x1d, 0x7, 0x2f, 0x4d, 0x29, 0x17, 0x3, 0x1, 0x8, 0x14, 0x10, 0x1, 0x6, 0x3, 0x1, 0x5, 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x2, 0x3, 0xb, 0x7, 0x1, 0xe, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x23, 0x1d, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x43, 0x1, 0x1, 0xa, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xc, 0x74, 0x5, 0x1, 0x80, 0x87, 0x69, 0xa, 0x1, 0x2d, 0x2, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x81, 0x85, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x11, 0x1, 0x8, 0x36, 0x1e, 0x2, 0x24, 0x4, 0x8, 0x80, 0x80, 0x4e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xba, 0x35, 0x4b, 0x2d, 0x20, 0x19, 0x1a, 0x24, 0x5c, 0x30, 0xe, 0x4, 0x84, 0xbb, 0x2b, 0x89, 0x55, 0x83, 0x6f, 0x8c, 0x91, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x1, 0xa0, 0x40, 0xaf, 0x2, 0xa0, 0x3d, 0xfe, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; +_T Grapheme_Base = [ + 0x20, 0x5f, 0x21, 0xd, 0x1, 0x82, 0x52, 0x70, 0x8, 0x2, 0x5, 0x5, 0x7, + 0x1, 0x1, 0x1, 0x14, 0x1, 0x80, 0xe0, 0x7, 0x80, 0x9e, 0x9, 0x26, 0x2, + 0x7, 0x1, 0x27, 0x1, 0x2, 0x4, 0x1, 0x2e, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, + 0x1, 0x9, 0x1b, 0x5, 0x5, 0x11, 0xa, 0xb, 0x1, 0x2, 0x2d, 0x15, 0x10, + 0x1, 0x65, 0x8, 0x1, 0x6, 0x2, 0x2, 0x1, 0x4, 0x20, 0x2, 0x1, 0x1, 0x1e, + 0x1d, 0x59, 0xb, 0x1, 0xe, 0x2b, 0x9, 0x7, 0x5, 0x16, 0x4, 0x1, 0x9, 0x1, + 0x3, 0x1, 0x7, 0xf, 0x1, 0x19, 0x5, 0x1, 0x41, 0x1, 0x1, 0xb, 0x56, 0x37, + 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, 0x14, 0x1, 0x7, + 0x2, 0x2, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, + 0x3, 0x1, 0x1, 0x2, 0x6, 0x2, 0x2, 0x2, 0x1, 0x1, 0xd, 0x2, 0x1, 0x3, + 0x4, 0x16, 0x7, 0x1, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, + 0x1, 0x2, 0x1, 0x2, 0x4, 0x3, 0x18, 0x4, 0x1, 0x1, 0x7, 0xa, 0x2, 0x3, + 0xe, 0x1, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, + 0x3, 0x4, 0x8, 0x1, 0x1, 0x2, 0x3, 0x1, 0xf, 0x2, 0x4, 0xc, 0x10, 0x2, + 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, + 0x2, 0x1, 0x6, 0x2, 0x2, 0x2, 0xf, 0x2, 0x1, 0x3, 0x4, 0x12, 0xb, 0x1, + 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, + 0x3, 0x3, 0xc, 0x5, 0x1, 0x1, 0x2, 0x3, 0x3, 0x1, 0x3, 0x3, 0x1, 0x15, + 0x15, 0x6, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, + 0x1, 0x3, 0x4, 0x13, 0x2, 0x6, 0x2, 0x4, 0xa, 0x8, 0x8, 0x2, 0x2, 0x1, + 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x2, 0x1, 0x2, 0x1, + 0x2, 0x2, 0x2, 0x1, 0x2, 0x12, 0x1, 0x1, 0x2, 0x4, 0xa, 0x1, 0x2, 0xf, + 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x1, 0x2, 0x5, 0x3, 0x1, + 0x3, 0x1, 0x1, 0x11, 0x2, 0x4, 0x10, 0x3, 0x7, 0x2, 0x2, 0x1, 0x12, 0x3, + 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x9, 0x2, 0x6, 0x7, 0x13, 0x3, 0xc, + 0x30, 0x1, 0x2, 0xb, 0x8, 0x8, 0xd, 0x25, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, + 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, + 0x1, 0x4, 0x1, 0x2, 0x9, 0x1, 0x2, 0x5, 0x1, 0x1, 0x9, 0xa, 0x2, 0x4, 0x20, + 0x18, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0xe, 0x1, 0x24, 0x12, 0x1, 0x5, + 0x1, 0x2, 0x5, 0x31, 0x8, 0x1, 0x6, 0x1, 0xd, 0x25, 0x2d, 0x4, 0x1, 0x6, + 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, + 0x6, 0x1, 0xf, 0x1, 0x28, 0x1, 0x1, 0x5, 0x1, 0x2, 0x81, 0x79, 0x1, 0x4, + 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, + 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, + 0x5, 0x1d, 0x3, 0x1a, 0x6, 0x55, 0xb, 0x82, 0x9d, 0x3, 0x51, 0xf, 0xd, + 0x1, 0x4, 0xe, 0x12, 0x3, 0x2, 0x9, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, + 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x9, 0x3, 0xa, 0x6, 0xa, 0x6, 0xb, + 0x5, 0xa, 0x6, 0x58, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x6, 0x4, + 0x2, 0x3, 0x4, 0x2, 0x1, 0x6, 0x7, 0x1, 0x3, 0x2a, 0x2, 0x5, 0xb, 0x2c, + 0x4, 0x1a, 0x6, 0xb, 0x3, 0x39, 0x2, 0x2, 0x3, 0x38, 0x1, 0x1, 0x9, 0x1, + 0x1, 0x2, 0x8, 0x6, 0xd, 0xa, 0x6, 0xa, 0x6, 0xe, 0x56, 0x30, 0x1, 0x1, + 0x5, 0x1, 0x1, 0x5, 0x1, 0x9, 0x4, 0x1b, 0x9, 0x9, 0x5, 0x20, 0x4, 0x2, + 0x2, 0x1, 0x1, 0x3a, 0x1, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x2, 0x8, 0x30, + 0x8, 0x2, 0x5, 0xf, 0x3, 0x33, 0x40, 0x8, 0xb, 0x1, 0xd, 0x1, 0x7, 0x4, + 0x1, 0x6, 0x1, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, + 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, + 0x35, 0x1, 0xf, 0x1, 0xe, 0x2, 0x6, 0x1, 0x13, 0x2, 0x3, 0x1, 0x9, 0x1, + 0xb, 0x5, 0x18, 0x7, 0x31, 0x10, 0x2, 0x2, 0x1b, 0x1, 0xd, 0x3, 0x1b, 0x45, + 0x80, 0x8a, 0x6, 0x82, 0x64, 0xc, 0x27, 0x19, 0xb, 0x15, 0x82, 0xa0, 0x1, + 0x84, 0x4c, 0x3, 0xa, 0x80, 0xa6, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x8f, 0x3, + 0x2, 0x5, 0x2d, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x2, 0xf, 0x17, 0x9, + 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, + 0x21, 0x3c, 0x44, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x1a, 0xc, 0x4, 0x2a, + 0x6, 0x10, 0x1, 0x56, 0x4, 0x65, 0x5, 0x29, 0x3, 0x5e, 0x1, 0x2b, 0x5, + 0x24, 0xc, 0x2f, 0x1, 0x80, 0xdf, 0x1, 0x9a, 0xb6, 0xa, 0xa0, 0x52, 0xd, + 0x33, 0x84, 0x8d, 0x3, 0x37, 0x9, 0x81, 0x5c, 0x14, 0x2f, 0x4, 0x1, 0xa, + 0x1a, 0x8, 0x50, 0x2, 0x6, 0x8, 0x80, 0x8f, 0x1, 0x4, 0xc, 0xb, 0x4d, 0xa, + 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, 0x5, 0x4, 0xa, 0x6, 0x38, 0x8, 0x44, + 0xa, 0xc, 0x18, 0xa, 0x4, 0x26, 0x8, 0x19, 0xb, 0x2, 0xb, 0x1e, 0x6, 0x30, + 0x1, 0x2, 0x4, 0x2, 0x1, 0x11, 0x1, 0xb, 0x4, 0x2, 0x20, 0x29, 0x6, 0x2, + 0x2, 0x2, 0xb, 0x3, 0x1, 0x8, 0x1, 0x1, 0x2, 0xa, 0x2, 0x20, 0x4, 0x30, + 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x11, 0x2, 0x8, + 0xb, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x25, 0x1, + 0x2, 0x1, 0x4, 0x3, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, + 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1, 0x1, + 0x18, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7c, 0x11, 0x81, 0x6d, + 0x10, 0x40, 0x2, 0x36, 0x28, 0xe, 0x12, 0xa, 0x16, 0x23, 0x1, 0x13, 0x1, + 0x4, 0x4, 0x5, 0x1, 0x80, 0x87, 0x4, 0x80, 0x9d, 0x2, 0x1f, 0x3, 0x6, 0x2, + 0x6, 0x2, 0x6, 0x2, 0x3, 0x3, 0x7, 0x1, 0x7, 0xd, 0x2, 0x2, 0xc, 0x1, + 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x5, 0x3, 0x4, + 0x2d, 0x3, 0x54, 0x5, 0xc, 0x34, 0x2d, 0x80, 0x83, 0x1d, 0x3, 0x31, 0x2f, + 0x1f, 0x1, 0x4, 0xc, 0x1b, 0x35, 0x1e, 0x1, 0x25, 0x4, 0xe, 0x2a, 0x80, + 0x9e, 0x2, 0xa, 0x83, 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, + 0x2, 0x17, 0x1, 0x9, 0x80, 0xa0, 0x1c, 0x3, 0x1b, 0x5, 0x1, 0x40, 0x38, + 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0xc, 0x8, 0x8, 0x9, + 0x7, 0x20, 0x80, 0x80, 0x36, 0x3, 0x1d, 0x2, 0x1b, 0x5, 0x8, 0x80, 0x80, + 0x49, 0x82, 0x17, 0x1f, 0x81, 0x81, 0x1, 0x1, 0x36, 0xf, 0x7, 0x4, 0x1e, + 0x12, 0x31, 0x4, 0x2, 0x2, 0x2, 0x1, 0x4, 0xe, 0x19, 0x7, 0xa, 0x9, 0x24, + 0x5, 0x1, 0x9, 0xe, 0x3e, 0x34, 0x9, 0xa, 0x7, 0xa, 0x84, 0xa6, 0x2b, 0x1, + 0x1, 0x1, 0x2, 0x6, 0x1, 0x9, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, + 0x63, 0xd, 0x4, 0x8b, 0x8c, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, + 0xc7, 0x45, 0xb, 0x2f, 0x14, 0xd, 0xa0, 0x40, 0x60, 0x2, 0x9f, 0xfe, + 0x80, 0xf6, 0xa, 0x27, 0x2, 0x3c, 0x1, 0x1, 0x3, 0x4, 0x15, 0x2, 0x7, 0x1e, + 0x4, 0x30, 0x22, 0x42, 0x3, 0x1, 0x80, 0xba, 0x57, 0x9, 0x12, 0x80, 0x8e, + 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, + 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, + 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, + 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, + 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, + 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2, 0x81, 0xe, + 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, 0x2, 0xf, 0x1, 0xf, 0x20, 0xb, 0x5, + 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1d, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, + 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, + 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, + 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74, 0x88, + 0x8c, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, + 0x1e +]; +_T Case_Ignorable = [ + 0x27, 0x1, 0x6, 0x1, 0xb, 0x1, 0x23, 0x1, 0x1, 0x1, 0x47, 0x1, 0x4, 0x1, + 0x1, 0x1, 0x4, 0x1, 0x2, 0x2, 0x81, 0xf7, 0x80, 0xc0, 0x4, 0x2, 0x4, 0x1, + 0x9, 0x2, 0x1, 0x1, 0x80, 0xfb, 0x7, 0x80, 0xcf, 0x1, 0x37, 0x2d, 0x1, + 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x2c, 0x1, 0xb, 0x5, 0xb, 0xb, 0x1, 0x1, + 0x23, 0x1, 0xa, 0x15, 0x10, 0x1, 0x65, 0x8, 0x1, 0xa, 0x1, 0x4, 0x21, + 0x1, 0x1, 0x1, 0x1e, 0x1b, 0x5b, 0xb, 0x3a, 0xb, 0x4, 0x1, 0x1b, 0x18, + 0x2b, 0x3, 0x80, 0x88, 0x1b, 0x1, 0x3, 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, + 0x1, 0x3, 0x7, 0xa, 0x2, 0xd, 0x1, 0xf, 0x1, 0x3a, 0x1, 0x4, 0x4, 0x8, 0x1, + 0x14, 0x2, 0x1d, 0x2, 0x39, 0x1, 0x4, 0x2, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, + 0x1e, 0x2, 0x3, 0x1, 0xb, 0x2, 0x39, 0x1, 0x4, 0x5, 0x1, 0x2, 0x4, 0x1, + 0x14, 0x2, 0x1d, 0x1, 0x3a, 0x1, 0x2, 0x1, 0x1, 0x4, 0x8, 0x1, 0x8, 0x1, + 0xb, 0x2, 0x1e, 0x1, 0x3d, 0x1, 0xc, 0x1, 0x70, 0x3, 0x5, 0x3, 0x1, 0x4, + 0x7, 0x2, 0xb, 0x2, 0x58, 0x1, 0x2, 0x1, 0x6, 0x1, 0x5, 0x2, 0x14, 0x2, + 0x5d, 0x4, 0x8, 0x1, 0x14, 0x2, 0x66, 0x1, 0x7, 0x3, 0x1, 0x1, 0x5a, 0x1, + 0x2, 0x7, 0xb, 0x9, 0x62, 0x1, 0x2, 0x6, 0x1, 0x2, 0x9, 0x1, 0x1, 0x6, + 0x4a, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x37, 0xe, 0x1, 0x5, 0x1, 0x2, + 0x5, 0xb, 0x1, 0x24, 0x9, 0x1, 0x66, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, + 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, + 0x5e, 0x1, 0x82, 0x60, 0x3, 0x83, 0xb2, 0x3, 0x1d, 0x3, 0x1d, 0x2, 0x1e, + 0x2, 0x40, 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x3, 0x1, 0x5, 0x1, 0x2d, + 0x4, 0x34, 0x1, 0x65, 0x1, 0x76, 0x3, 0x4, 0x2, 0x9, 0x1, 0x6, 0x3, 0x80, + 0xdb, 0x2, 0x2, 0x1, 0x3a, 0x1, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x2, 0x8, + 0x6, 0xa, 0x2, 0x1, 0x27, 0x1, 0x58, 0x4, 0x30, 0x1, 0x1, 0x5, 0x1, 0x1, + 0x5, 0x1, 0x28, 0x9, 0xc, 0x2, 0x20, 0x4, 0x2, 0x2, 0x1, 0x1, 0x3a, 0x1, + 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x3a, 0x8, 0x2, 0x2, 0x40, 0x6, 0x52, 0x3, + 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x37, 0x3f, 0xd, 0x1, 0x22, 0x4c, + 0x15, 0x4, 0x81, 0xbd, 0x1, 0x1, 0x3, 0xb, 0x3, 0xd, 0x3, 0xd, 0x3, 0xd, + 0x2, 0xc, 0x5, 0x8, 0x2, 0xa, 0x1, 0x2, 0x1, 0x2, 0x5, 0x31, 0x5, 0x1, + 0xa, 0x1, 0x1, 0xd, 0x1, 0x10, 0xd, 0x33, 0x21, 0x8b, 0x8b, 0x2, 0x71, + 0x3, 0x7d, 0x1, 0xf, 0x1, 0x60, 0x20, 0x2f, 0x1, 0x81, 0xd5, 0x1, 0x24, + 0x4, 0x3, 0x5, 0x5, 0x1, 0x5d, 0x6, 0x5d, 0x3, 0xa0, 0x6f, 0x16, 0x1, 0x84, + 0xe2, 0x6, 0x81, 0xe, 0x1, 0x62, 0x4, 0x1, 0xa, 0x1, 0x1, 0x1f, 0x1, + 0x50, 0x2, 0xe, 0x22, 0x4e, 0x1, 0x17, 0x3, 0x6d, 0x2, 0x8, 0x1, 0x3, + 0x1, 0x4, 0x1, 0x19, 0x2, 0x80, 0x9d, 0x1, 0x1b, 0x12, 0x34, 0x8, 0x19, + 0xb, 0x2e, 0x3, 0x30, 0x1, 0x2, 0x4, 0x2, 0x1, 0x12, 0x1, 0x59, 0x6, 0x2, + 0x2, 0x2, 0x2, 0xc, 0x1, 0x8, 0x1, 0x23, 0x1, 0x3f, 0x1, 0x1, 0x3, 0x2, + 0x2, 0x5, 0x2, 0x1, 0x1, 0x1b, 0x1, 0xe, 0x2, 0x5, 0x2, 0x1, 0x1, 0x80, + 0xee, 0x1, 0x2, 0x1, 0x4, 0x1, 0xa0, 0x4f, 0x30, 0x1, 0x80, 0x93, 0x10, + 0x82, 0x3e, 0x10, 0x3, 0x1, 0xc, 0x7, 0x2b, 0x1, 0x2, 0x1, 0x80, 0xa9, + 0x1, 0x7, 0x1, 0x6, 0x1, 0xb, 0x1, 0x23, 0x1, 0x1, 0x1, 0x2f, 0x1, 0x2d, + 0x2, 0x43, 0x1, 0x15, 0x3, 0x82, 0x1, 0x1, 0x88, 0x3, 0x3, 0x1, 0x2, 0x5, + 0x4, 0x28, 0x3, 0x4, 0x1, 0x85, 0xc1, 0x1, 0x36, 0xf, 0x39, 0x2, 0x31, + 0x4, 0x2, 0x2, 0x2, 0x1, 0x42, 0x3, 0x24, 0x5, 0x1, 0x8, 0x4b, 0x2, 0x34, + 0x9, 0x84, 0xec, 0x1, 0x1, 0x1, 0x2, 0x6, 0x1, 0x1, 0xa0, 0x58, 0xd7, 0x11, + 0xa0, 0x61, 0xc7, 0x3, 0x9, 0x10, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, + 0xac, 0x2d, 0xbc, 0x1, 0x1e, 0x60, 0x80, 0x80, 0x80, 0xf0 +]; +_T STerm = [0x21, 0x1, 0xc, 0x1, 0x10, 0x1, 0x85, 0x1c, 0x1, 0x1, 0x1, 0x2a, + 0x1, 0x80, 0x95, 0x1, 0x80, 0xb4, 0x1, 0x2b, 0x3, 0x80, 0xf6, 0x1, 0x81, + 0x6a, 0x2, 0x86, 0xe4, 0x2, 0x83, 0x16, 0x1, 0x4, 0x2, 0x83, 0x5, 0x1, + 0x80, 0xc6, 0x2, 0x80, 0xcc, 0x1, 0x5, 0x1, 0x81, 0x3a, 0x2, 0x81, 0x62, + 0x4, 0x80, 0xae, 0x2, 0x2, 0x2, 0x80, 0xdb, 0x2, 0x41, 0x2, 0x83, 0xbc, + 0x2, 0x9, 0x3, 0x8d, 0xe4, 0x1, 0x81, 0xd3, 0x1, 0xa0, 0x74, 0xfc, 0x1, + 0x81, 0xe, 0x2, 0x80, 0xe3, 0x1, 0x3, 0x1, 0x81, 0x7e, 0x2, 0x56, 0x2, + 0x5f, 0x1, 0x80, 0x98, 0x2, 0x80, 0x93, 0x3, 0x80, 0x90, 0x2, 0x80, 0xf9, + 0x1, 0xa0, 0x52, 0x66, 0x1, 0x3, 0x2, 0x80, 0xa9, 0x1, 0xc, 0x1, 0x10, 0x1, + 0x41, 0x1, 0x8a, 0xf4, 0x2, 0x85, 0xef, 0x2, 0x75, 0x4, 0x7f, 0x3, 0x80, 0x81, + 0x2]; +_T Diacritic = [ + 0x5e, 0x1, 0x1, 0x1, 0x47, 0x1, 0x6, 0x1, 0x4, 0x1, 0x2, 0x2, 0x81, 0xf7, + 0x80, 0x9f, 0x1, 0x8, 0x5, 0x6, 0x11, 0x2, 0x4, 0x1, 0x9, 0x2, 0x80, + 0xfd, 0x5, 0x80, 0xd1, 0x1, 0x37, 0x11, 0x1, 0x1b, 0x1, 0x1, 0x1, 0x2, 0x1, + 0x1, 0x80, 0x86, 0x8, 0x4, 0x2, 0x80, 0x86, 0x2, 0x4, 0x2, 0x3, 0x3, 0x43, + 0x1b, 0x5b, 0xb, 0x3a, 0xb, 0x22, 0x2, 0x80, 0xca, 0x1b, 0x3d, 0x1, 0x10, + 0x1, 0x3, 0x4, 0x1c, 0x1, 0x4a, 0x1, 0x10, 0x1, 0x6e, 0x1, 0x10, 0x1, 0x6e, + 0x1, 0x10, 0x1, 0x6e, 0x1, 0x10, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x6e, 0x1, + 0x10, 0x1, 0x7f, 0x1, 0x7c, 0x1, 0x7c, 0x6, 0x1, 0x1, 0x79, 0x5, 0x4b, + 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x2, 0x42, 0x3, 0x1, 0x2, 0x3e, + 0x1, 0x70, 0x1, 0x1, 0x2, 0x4c, 0x7, 0x1, 0x1, 0xa, 0x2, 0x87, 0x2d, 0xb, + 0x9, 0x1, 0x81, 0x5b, 0x3, 0x81, 0x39, 0x8, 0x2, 0x1, 0x80, 0xb4, 0x1, 0xf, + 0x1, 0x26, 0x9, 0x36, 0x2, 0x80, 0x8a, 0x2, 0x40, 0x6, 0x52, 0x19, 0x4, + 0x1, 0x6, 0x1, 0x37, 0x3f, 0x59, 0xc, 0x2d, 0x3, 0x81, 0xbd, 0x1, 0x1, + 0x3, 0xb, 0x3, 0xd, 0x3, 0xd, 0x3, 0xd, 0x2, 0x8c, 0xf0, 0x3, 0x81, 0x3d, + 0x1, 0x81, 0xfa, 0x6, 0x69, 0x4, 0x5f, 0x1, 0xa0, 0x75, 0x72, 0x1, 0xc, + 0x2, 0x1, 0x1, 0x70, 0x2, 0x25, 0xb, 0x66, 0x1, 0x6f, 0x2, 0x80, 0xca, 0x1, + 0x1b, 0x12, 0x39, 0x4, 0x24, 0x1, 0x5f, 0x1, 0xc, 0x1, 0x80, 0xba, 0x1, + 0x43, 0x4, 0x33, 0x1, 0x80, 0xf5, 0x2, 0xa0, 0x4f, 0x30, 0x1, 0x83, 0x1, + 0x7, 0x81, 0x17, 0x1, 0x1, 0x1, 0x2f, 0x1, 0x2d, 0x2, 0x43, 0x1, 0x90, + 0xd5, 0x2, 0x78, 0x2, 0x80, 0x8b, 0x1, 0x84, 0xf5, 0x2, 0xa0, 0x58, 0xd7, + 0x11, 0xa0, 0x61, 0xc7, 0x3, 0x3, 0x6, 0x8, 0x8, 0x2, 0x7, 0x1e, 0x4 +]; +_T Lm = [0x82, 0xb0, 0x12, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x80, + 0x85, 0x1, 0x5, 0x1, 0x81, 0xde, 0x1, 0x80, 0xe6, 0x1, 0x80, 0xa4, 0x2, + 0x81, 0xd, 0x2, 0x4, 0x1, 0x1f, 0x1, 0x9, 0x1, 0x3, 0x1, 0x81, 0x48, 0x1, + 0x84, 0xd4, 0x1, 0x7f, 0x1, 0x82, 0x35, 0x1, 0x86, 0xda, 0x1, 0x6b, 0x1, + 0x82, 0x63, 0x1, 0x81, 0xd0, 0x6, 0x80, 0xae, 0x3f, 0xd, 0x1, 0x22, 0x25, + 0x82, 0xb1, 0x1, 0xd, 0x1, 0x10, 0xd, 0x8b, 0xdf, 0x2, 0x80, 0xf1, 0x1, + 0x80, 0xbf, 0x1, 0x81, 0xd5, 0x1, 0x2b, 0x5, 0x5, 0x1, 0x61, 0x2, 0x5d, + 0x3, 0xa0, 0x6f, 0x16, 0x1, 0x84, 0xe2, 0x6, 0x81, 0xe, 0x1, 0x72, 0x1, + 0x80, 0x97, 0x9, 0x50, 0x1, 0x17, 0x1, 0x6f, 0x2, 0x81, 0xd5, 0x1, 0x80, + 0xa0, 0x1, 0x6c, 0x1, 0x15, 0x2, 0xa0, 0x54, 0x7b, 0x1, 0x2d, 0x2, 0xa0, 0x6f, + 0xf3, 0xd]; +_T Mc = [0x89, 0x3, 0x1, 0x37, 0x1, 0x2, 0x3, 0x8, 0x4, 0x1, 0x2, 0x32, 0x2, + 0x3a, 0x3, 0x6, 0x2, 0x2, 0x2, 0xa, 0x1, 0x2b, 0x1, 0x3a, 0x3, 0x42, 0x1, + 0x3a, 0x3, 0x8, 0x1, 0x1, 0x2, 0x35, 0x2, 0x3a, 0x1, 0x1, 0x1, 0x6, 0x2, + 0x2, 0x2, 0xa, 0x1, 0x66, 0x2, 0x1, 0x2, 0x3, 0x3, 0x1, 0x3, 0xa, 0x1, + 0x29, 0x3, 0x3d, 0x4, 0x3d, 0x2, 0x3a, 0x1, 0x1, 0x5, 0x2, 0x2, 0x1, 0x2, + 0x9, 0x2, 0x2b, 0x2, 0x3a, 0x3, 0x5, 0x3, 0x1, 0x3, 0xa, 0x1, 0x2a, 0x2, + 0x4b, 0x3, 0x6, 0x8, 0x12, 0x2, 0x81, 0x4a, 0x2, 0x3f, 0x1, 0x80, 0xab, + 0x2, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x19, 0x2, 0xa, 0x3, 0x2, 0x7, 0x15, + 0x2, 0x2, 0x6, 0x2, 0x1, 0xa, 0x3, 0x87, 0x19, 0x1, 0x7, 0x8, 0x1, 0x2, + 0x81, 0x5a, 0x4, 0x2, 0x3, 0x4, 0x2, 0x1, 0x6, 0x77, 0x11, 0x7, 0x2, + 0x4f, 0x2, 0x3a, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x2, 0x8, 0x6, 0x80, 0x91, + 0x1, 0x30, 0x1, 0x5, 0x1, 0x1, 0x5, 0x1, 0x2, 0x3d, 0x1, 0x1e, 0x1, 0x4, + 0x2, 0x2, 0x1, 0x1, 0x2, 0x39, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x2, 0x30, + 0x8, 0x8, 0x2, 0x80, 0xab, 0x1, 0x10, 0x2, 0x93, 0x3a, 0x2, 0xa0, 0x77, + 0xf3, 0x2, 0x2, 0x1, 0x58, 0x2, 0x32, 0x10, 0x80, 0x8e, 0x2, 0x2f, 0x1, + 0x30, 0x2, 0x4, 0x2, 0x1, 0x4, 0x6e, 0x2, 0x2, 0x2, 0x18, 0x1, 0x2d, 0x1, + 0x6f, 0x1, 0x2, 0x2, 0x5, 0x1, 0x80, 0xed, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, + 0x1, 0xa0, 0x64, 0x13, 0x1, 0x1, 0x1, 0x7f, 0x1, 0x2d, 0x3, 0x4, 0x2, 0x73, + 0x1, 0x55, 0x1, 0x30, 0x3, 0x9, 0x2, 0x84, 0xeb, 0x1, 0x1, 0x2, 0x6, 0x1, + 0xa0, 0x58, 0x9a, 0x2e, 0xa0, 0x61, 0xe6, 0x2, 0x6, 0x6]; +_T Lo = [0x80, 0xaa, 0x1, 0xf, 0x1, 0x81, 0x0, 0x1, 0x4, 0x4, 0x80, 0xd0, 0x1, + 0x83, 0x3b, 0x1b, 0x5, 0x3, 0x2d, 0x20, 0x1, 0xa, 0x23, 0x2, 0x1, 0x63, + 0x1, 0x1, 0x18, 0x2, 0xa, 0x3, 0x2, 0x1, 0x10, 0x1, 0x1, 0x1e, 0x1d, + 0x59, 0xb, 0x1, 0x18, 0x21, 0x15, 0x16, 0x2a, 0x19, 0x47, 0x1, 0x1, 0xb, + 0x57, 0x36, 0x3, 0x1, 0x12, 0x1, 0x7, 0xa, 0x10, 0x6, 0x1, 0x7, 0x5, 0x8, + 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x3, 0x1, 0x10, 0x1, + 0xd, 0x2, 0x1, 0x3, 0xe, 0x2, 0x13, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, + 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1f, 0x4, 0x1, 0x1, 0x13, 0x3, 0x10, 0x9, + 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x12, 0x1, + 0xf, 0x2, 0x23, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, + 0x3, 0x1, 0x1e, 0x2, 0x1, 0x3, 0xf, 0x1, 0x11, 0x1, 0x1, 0x6, 0x3, 0x3, + 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, + 0x16, 0x1, 0x34, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, + 0x1a, 0x2, 0x6, 0x2, 0x23, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, + 0x3, 0x1, 0x20, 0x1, 0x1, 0x2, 0xf, 0x2, 0x12, 0x8, 0x1, 0x3, 0x1, 0x29, + 0x2, 0x1, 0x10, 0x1, 0x11, 0x2, 0x18, 0x6, 0x5, 0x12, 0x3, 0x18, 0x1, 0x9, + 0x1, 0x1, 0x2, 0x7, 0x3a, 0x30, 0x1, 0x2, 0xc, 0x6, 0x3b, 0x2, 0x1, 0x1, + 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, + 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, 0x2, 0x9, 0x1, 0x2, 0x5, 0x17, 0x4, 0x20, + 0x1, 0x3f, 0x8, 0x1, 0x24, 0x1b, 0x5, 0x73, 0x2b, 0x14, 0x1, 0x10, 0x6, + 0x4, 0x4, 0x3, 0x1, 0x3, 0x2, 0x7, 0x3, 0x4, 0xd, 0xc, 0x1, 0x41, 0x2b, + 0x2, 0x81, 0x4c, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, + 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, + 0x39, 0x1, 0x4, 0x2, 0x43, 0x25, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, + 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x15, 0xd, 0x1, 0x4, 0xe, 0x12, 0xe, 0x12, + 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x28, 0x1, 0x43, 0x23, 0x1, 0x34, 0x8, + 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x33, 0x1e, 0x2, 0x5, 0xb, 0x2c, + 0x15, 0x7, 0x38, 0x17, 0x9, 0x35, 0x80, 0xb0, 0x2f, 0x11, 0x7, 0x37, + 0x1e, 0xd, 0x2, 0xa, 0x2c, 0x1a, 0x24, 0x29, 0x3, 0xa, 0x1e, 0x71, 0x4, + 0x1, 0x4, 0x3, 0x2, 0x84, 0x3e, 0x4, 0x8b, 0xf7, 0x38, 0x18, 0x17, 0x9, + 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, + 0x82, 0x27, 0x1, 0x35, 0x1, 0x4, 0x56, 0x8, 0x1, 0x1, 0x5a, 0x4, 0x1, 0x5, + 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, + 0x51, 0xcd, 0x33, 0x15, 0x1, 0x84, 0x77, 0x43, 0x28, 0x8, 0x81, 0xc, 0x4, + 0x10, 0xa, 0x2, 0x42, 0x1, 0x31, 0x46, 0x81, 0x15, 0x7, 0x1, 0x3, 0x1, 0x4, + 0x1, 0x17, 0x1d, 0x34, 0xe, 0x32, 0x3e, 0x6, 0x3, 0x1, 0xe, 0x1c, 0xa, + 0x17, 0x19, 0x1d, 0x7, 0x2f, 0x4d, 0x29, 0x17, 0x3, 0x1, 0x8, 0x14, 0x10, + 0x1, 0x6, 0x3, 0x1, 0x5, 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, + 0x1, 0x1, 0x18, 0x2, 0x3, 0xb, 0x7, 0x1, 0xe, 0x6, 0x2, 0x6, 0x2, 0x6, + 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x23, 0x1d, 0xa0, 0x2b, 0xa4, 0xc, 0x17, + 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x43, 0x1, 0x1, 0xa, + 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, + 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xc, 0x74, 0x5, 0x1, 0x80, 0x87, 0x69, + 0xa, 0x1, 0x2d, 0x2, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, + 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x81, + 0x85, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x11, 0x1, 0x8, 0x36, 0x1e, 0x2, + 0x24, 0x4, 0x8, 0x80, 0x80, 0x4e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, 0x2c, + 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, + 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, 0x83, + 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xba, 0x35, 0x4b, 0x2d, + 0x20, 0x19, 0x1a, 0x24, 0x5c, 0x30, 0xe, 0x4, 0x84, 0xbb, 0x2b, 0x89, 0x55, + 0x83, 0x6f, 0x8c, 0x91, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, + 0xc7, 0x45, 0xb, 0x1, 0xa0, 0x40, 0xaf, 0x2, 0xa0, 0x3d, 0xfe, 0x4, 0x1, + 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, + 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, + 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, + 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, + 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; _T Me = [0x84, 0x88, 0x2, 0x9c, 0x53, 0x4, 0x1, 0x3, 0xa0, 0x85, 0x8b, 0x3]; -_T ID_Start = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x80, 0x81, 0x5, 0x1, 0x2, 0x2, 0x4, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x48, 0x1b, 0x5, 0x3, 0x2d, 0x2b, 0x23, 0x2, 0x1, 0x63, 0x1, 0x1, 0xf, 0x2, 0x7, 0x2, 0xa, 0x3, 0x2, 0x1, 0x10, 0x1, 0x1, 0x1e, 0x1d, 0x59, 0xb, 0x1, 0x18, 0x21, 0x9, 0x2, 0x4, 0x1, 0x5, 0x16, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x17, 0x19, 0x47, 0x1, 0x1, 0xb, 0x57, 0x36, 0x3, 0x1, 0x12, 0x1, 0x7, 0xa, 0xf, 0x7, 0x1, 0x7, 0x5, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x3, 0x1, 0x10, 0x1, 0xd, 0x2, 0x1, 0x3, 0xe, 0x2, 0x13, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1f, 0x4, 0x1, 0x1, 0x13, 0x3, 0x10, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x12, 0x1, 0xf, 0x2, 0x23, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x1e, 0x2, 0x1, 0x3, 0xf, 0x1, 0x11, 0x1, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x16, 0x1, 0x34, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x1a, 0x2, 0x6, 0x2, 0x23, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x20, 0x1, 0x1, 0x2, 0xf, 0x2, 0x12, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x10, 0x1, 0x11, 0x2, 0x18, 0x6, 0x5, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3a, 0x30, 0x1, 0x2, 0xc, 0x7, 0x3a, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, 0x2, 0x9, 0x1, 0x2, 0x5, 0x1, 0x1, 0x15, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, 0x24, 0x1b, 0x5, 0x73, 0x2b, 0x14, 0x1, 0x10, 0x6, 0x4, 0x4, 0x3, 0x1, 0x3, 0x2, 0x7, 0x3, 0x4, 0xd, 0xc, 0x1, 0x11, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x25, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x4, 0xe, 0x12, 0xe, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x23, 0x1, 0x4, 0x1, 0x43, 0x58, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x33, 0x1e, 0x2, 0x5, 0xb, 0x2c, 0x15, 0x7, 0x38, 0x17, 0x9, 0x35, 0x52, 0x1, 0x5d, 0x2f, 0x11, 0x7, 0x37, 0x1e, 0xd, 0x2, 0xa, 0x2c, 0x1a, 0x24, 0x29, 0x3, 0xa, 0x24, 0x6b, 0x4, 0x1, 0x4, 0x3, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x10, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, 0x10, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x82, 0x26, 0x3, 0x19, 0x9, 0x7, 0x5, 0x2, 0x5, 0x4, 0x56, 0x4, 0x5, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x10, 0xa, 0x2, 0x14, 0x2f, 0x10, 0x19, 0x8, 0x50, 0x27, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x17, 0x1d, 0x34, 0xe, 0x32, 0x3e, 0x6, 0x3, 0x1, 0xe, 0x1c, 0xa, 0x17, 0x19, 0x1d, 0x7, 0x2f, 0x1c, 0x1, 0x30, 0x29, 0x17, 0x3, 0x1, 0x8, 0x14, 0x17, 0x3, 0x1, 0x5, 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x3, 0x2, 0xb, 0x7, 0x3, 0xc, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x23, 0x1d, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1, 0x1, 0xa, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xc, 0x74, 0x5, 0x1, 0x80, 0x87, 0x24, 0x1a, 0x6, 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, 0x81, 0xb, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xba, 0x35, 0x4b, 0x2d, 0x20, 0x19, 0x1a, 0x24, 0x5c, 0x30, 0xe, 0x4, 0x84, 0xbb, 0x2b, 0x89, 0x55, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x1, 0x42, 0xd, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x23, 0xfe, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x96, 0x34, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; -_T Other_Grapheme_Extend = [0x89, 0xbe, 0x1, 0x18, 0x1, 0x81, 0x66, 0x1, 0x18, 0x1, 0x66, 0x1, 0x18, 0x1, 0x80, 0xea, 0x1, 0x12, 0x2, 0x67, 0x1, 0x18, 0x1, 0x77, 0x1, 0xf, 0x1, 0x92, 0x2c, 0x2, 0x90, 0x20, 0x2, 0xa0, 0xcf, 0x6e, 0x2, 0xa0, 0xd1, 0xc5, 0x1, 0x8, 0x5]; -_T Lt = [0x81, 0xc5, 0x1, 0x2, 0x1, 0x2, 0x1, 0x26, 0x1, 0x9d, 0x95, 0x8, 0x8, 0x8, 0x8, 0x8, 0xc, 0x1, 0xf, 0x1, 0x2f, 0x1]; +_T ID_Start = [ + 0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, + 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x80, 0x81, 0x5, + 0x1, 0x2, 0x2, 0x4, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, + 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x48, + 0x1b, 0x5, 0x3, 0x2d, 0x2b, 0x23, 0x2, 0x1, 0x63, 0x1, 0x1, 0xf, 0x2, 0x7, + 0x2, 0xa, 0x3, 0x2, 0x1, 0x10, 0x1, 0x1, 0x1e, 0x1d, 0x59, 0xb, 0x1, + 0x18, 0x21, 0x9, 0x2, 0x4, 0x1, 0x5, 0x16, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, + 0x17, 0x19, 0x47, 0x1, 0x1, 0xb, 0x57, 0x36, 0x3, 0x1, 0x12, 0x1, 0x7, + 0xa, 0xf, 0x7, 0x1, 0x7, 0x5, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x1, 0x3, 0x4, 0x3, 0x1, 0x10, 0x1, 0xd, 0x2, 0x1, 0x3, 0xe, 0x2, 0x13, + 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1f, + 0x4, 0x1, 0x1, 0x13, 0x3, 0x10, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x5, 0x3, 0x1, 0x12, 0x1, 0xf, 0x2, 0x23, 0x8, 0x2, 0x2, 0x2, + 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x3, 0x1, 0x1e, 0x2, 0x1, 0x3, 0xf, + 0x1, 0x11, 0x1, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, + 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x16, 0x1, 0x34, 0x8, 0x1, 0x3, 0x1, + 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x1a, 0x2, 0x6, 0x2, 0x23, 0x8, 0x1, + 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x1, 0x20, 0x1, 0x1, 0x2, 0xf, + 0x2, 0x12, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x1, 0x10, 0x1, 0x11, 0x2, + 0x18, 0x6, 0x5, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3a, 0x30, + 0x1, 0x2, 0xc, 0x7, 0x3a, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, + 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x4, 0x1, + 0x2, 0x9, 0x1, 0x2, 0x5, 0x1, 0x1, 0x15, 0x4, 0x20, 0x1, 0x3f, 0x8, 0x1, + 0x24, 0x1b, 0x5, 0x73, 0x2b, 0x14, 0x1, 0x10, 0x6, 0x4, 0x4, 0x3, 0x1, + 0x3, 0x2, 0x7, 0x3, 0x4, 0xd, 0xc, 0x1, 0x11, 0x26, 0x1, 0x1, 0x5, 0x1, + 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, + 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, + 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x25, 0x10, 0x10, 0x55, 0xc, 0x82, + 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x4, 0xe, + 0x12, 0xe, 0x12, 0xe, 0xd, 0x1, 0x3, 0xf, 0x34, 0x23, 0x1, 0x4, 0x1, + 0x43, 0x58, 0x8, 0x29, 0x1, 0x1, 0x5, 0x46, 0xa, 0x1d, 0x33, 0x1e, 0x2, + 0x5, 0xb, 0x2c, 0x15, 0x7, 0x38, 0x17, 0x9, 0x35, 0x52, 0x1, 0x5d, 0x2f, + 0x11, 0x7, 0x37, 0x1e, 0xd, 0x2, 0xa, 0x2c, 0x1a, 0x24, 0x29, 0x3, 0xa, + 0x24, 0x6b, 0x4, 0x1, 0x4, 0x3, 0x2, 0x9, 0x80, 0xc0, 0x40, 0x81, 0x16, + 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, + 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, + 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x10, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, + 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, + 0x1, 0x2, 0x38, 0x7, 0x1, 0x10, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, + 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x82, 0x26, 0x3, 0x19, 0x9, + 0x7, 0x5, 0x2, 0x5, 0x4, 0x56, 0x4, 0x5, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, + 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, + 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x10, 0xa, 0x2, + 0x14, 0x2f, 0x10, 0x19, 0x8, 0x50, 0x27, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, + 0x4, 0xc, 0xb, 0x4d, 0xa, 0x1, 0x3, 0x1, 0x4, 0x1, 0x17, 0x1d, 0x34, 0xe, + 0x32, 0x3e, 0x6, 0x3, 0x1, 0xe, 0x1c, 0xa, 0x17, 0x19, 0x1d, 0x7, 0x2f, + 0x1c, 0x1, 0x30, 0x29, 0x17, 0x3, 0x1, 0x8, 0x14, 0x17, 0x3, 0x1, 0x5, + 0x30, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x1, 0x18, 0x3, 0x2, + 0xb, 0x7, 0x3, 0xc, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, + 0x91, 0x23, 0x1d, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, + 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1, 0x1, 0xa, 0x1, 0xd, + 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, 0x6b, 0x12, + 0x40, 0x2, 0x36, 0x28, 0xc, 0x74, 0x5, 0x1, 0x80, 0x87, 0x24, 0x1a, 0x6, + 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, + 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, + 0x81, 0xb, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, + 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x83, 0x62, 0x6, 0x2, 0x1, 0x1, + 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, + 0x38, 0x6, 0x2, 0x40, 0x1, 0xf, 0x4, 0x1, 0x3, 0x1, 0x1b, 0x2c, 0x1d, 0x80, + 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xba, 0x35, 0x4b, + 0x2d, 0x20, 0x19, 0x1a, 0x24, 0x5c, 0x30, 0xe, 0x4, 0x84, 0xbb, 0x2b, 0x89, + 0x55, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, + 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x1, 0x42, 0xd, 0xa0, 0x40, 0x60, + 0x2, 0xa0, 0x23, 0xfe, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, + 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, + 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, + 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, + 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x96, 0x34, 0x4, 0x1, + 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, + 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, + 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, + 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, + 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e +]; +_T Other_Grapheme_Extend = [ + 0x89, 0xbe, 0x1, 0x18, 0x1, 0x81, 0x66, 0x1, 0x18, 0x1, 0x66, 0x1, 0x18, + 0x1, 0x80, 0xea, 0x1, 0x12, 0x2, 0x67, 0x1, 0x18, 0x1, 0x77, 0x1, 0xf, 0x1, + 0x92, 0x2c, 0x2, 0x90, 0x20, 0x2, 0xa0, 0xcf, 0x6e, 0x2, 0xa0, 0xd1, 0xc5, 0x1, + 0x8, 0x5 +]; +_T Lt = [0x81, 0xc5, 0x1, 0x2, 0x1, 0x2, 0x1, 0x26, 0x1, 0x9d, 0x95, 0x8, 0x8, + 0x8, 0x8, 0x8, 0xc, 0x1, 0xf, 0x1, 0x2f, 0x1]; _T Pattern_White_Space = [0x9, 0x5, 0x12, 0x1, 0x64, 0x1, 0x9f, 0x88, 0x2, 0x18, 0x2]; -_T Cased = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x80, 0xc3, 0x1, 0x4, 0x4, 0x80, 0xd0, 0x1, 0x24, 0x7, 0x2, 0x1e, 0x5, 0x60, 0x1, 0x2a, 0x4, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, 0x26, 0xa, 0x27, 0x8b, 0x18, 0x26, 0x1, 0x1, 0x5, 0x1, 0x8c, 0x32, 0x80, 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, 0x1, 0x10, 0xd, 0x65, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x6, 0x4, 0x1, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x20, 0x3, 0x2, 0x83, 0x31, 0x34, 0x87, 0x16, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0xa0, 0x79, 0x12, 0x2e, 0x12, 0x18, 0x80, 0x8a, 0x66, 0x3, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x3, 0xa0, 0x53, 0x5, 0x7, 0xc, 0x5, 0x84, 0x9, 0x1a, 0x6, 0x1a, 0x84, 0xa5, 0x50, 0xa0, 0xcf, 0xb0, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8]; -_T Mn = [0x83, 0x0, 0x70, 0x81, 0x13, 0x5, 0x81, 0x9, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x48, 0xb, 0x30, 0x15, 0x10, 0x1, 0x65, 0x7, 0x2, 0x6, 0x2, 0x2, 0x1, 0x4, 0x23, 0x1, 0x1e, 0x1b, 0x5b, 0xb, 0x3a, 0x9, 0x22, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x5, 0x2b, 0x3, 0x80, 0x88, 0x1b, 0x1, 0x3, 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, 0x1d, 0x1, 0x3a, 0x1, 0x4, 0x4, 0x8, 0x1, 0x14, 0x2, 0x1d, 0x2, 0x39, 0x1, 0x4, 0x2, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x1e, 0x2, 0x3, 0x1, 0xb, 0x2, 0x39, 0x1, 0x4, 0x5, 0x1, 0x2, 0x4, 0x1, 0x14, 0x2, 0x1d, 0x1, 0x3a, 0x1, 0x2, 0x1, 0x1, 0x4, 0x8, 0x1, 0x8, 0x1, 0xb, 0x2, 0x1e, 0x1, 0x3d, 0x1, 0xc, 0x1, 0x70, 0x3, 0x5, 0x3, 0x1, 0x4, 0x7, 0x2, 0xb, 0x2, 0x58, 0x1, 0x2, 0x1, 0x6, 0x1, 0x5, 0x2, 0x14, 0x2, 0x5d, 0x4, 0x8, 0x1, 0x14, 0x2, 0x66, 0x1, 0x7, 0x3, 0x1, 0x1, 0x5a, 0x1, 0x2, 0x7, 0xc, 0x8, 0x62, 0x1, 0x2, 0x6, 0x1, 0x2, 0xb, 0x6, 0x4a, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x37, 0xe, 0x1, 0x5, 0x1, 0x2, 0x5, 0xb, 0x1, 0x24, 0x9, 0x1, 0x66, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, 0x82, 0xbf, 0x3, 0x83, 0xb2, 0x3, 0x1d, 0x3, 0x1d, 0x2, 0x1e, 0x2, 0x40, 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x9, 0x1, 0x2d, 0x3, 0x80, 0x9b, 0x1, 0x76, 0x3, 0x4, 0x2, 0x9, 0x1, 0x6, 0x3, 0x80, 0xdb, 0x2, 0x2, 0x1, 0x3a, 0x1, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x2, 0x8, 0x6, 0xa, 0x2, 0x1, 0x80, 0x80, 0x4, 0x30, 0x1, 0x1, 0x5, 0x1, 0x1, 0x5, 0x1, 0x28, 0x9, 0xc, 0x2, 0x20, 0x4, 0x2, 0x2, 0x1, 0x1, 0x3a, 0x1, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x3a, 0x8, 0x2, 0x2, 0x80, 0x98, 0x3, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x80, 0xcb, 0x27, 0x15, 0x4, 0x82, 0xd0, 0xd, 0x4, 0x1, 0x3, 0xc, 0x8b, 0xfe, 0x3, 0x80, 0x8d, 0x1, 0x60, 0x20, 0x82, 0x2a, 0x4, 0x6b, 0x2, 0xa0, 0x75, 0xd4, 0x1, 0x4, 0xa, 0x21, 0x1, 0x50, 0x2, 0x81, 0x10, 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, 0x80, 0x9d, 0x1, 0x1b, 0x12, 0x34, 0x8, 0x19, 0xb, 0x2e, 0x3, 0x30, 0x1, 0x2, 0x4, 0x2, 0x1, 0x6c, 0x6, 0x2, 0x2, 0x2, 0x2, 0xc, 0x1, 0x8, 0x1, 0x63, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x2a, 0x2, 0x8, 0x1, 0x80, 0xee, 0x1, 0x2, 0x1, 0x4, 0x1, 0xa0, 0x4f, 0x30, 0x1, 0x82, 0xe1, 0x10, 0x10, 0x7, 0x83, 0xd6, 0x1, 0x88, 0x3, 0x3, 0x1, 0x2, 0x5, 0x4, 0x28, 0x3, 0x4, 0x1, 0x85, 0xc1, 0x1, 0x36, 0xf, 0x39, 0x2, 0x31, 0x4, 0x2, 0x2, 0x45, 0x3, 0x24, 0x5, 0x1, 0x8, 0x4b, 0x2, 0x34, 0x9, 0x84, 0xec, 0x1, 0x1, 0x1, 0x2, 0x6, 0x1, 0x1, 0xa0, 0x58, 0xd7, 0x4, 0xa0, 0x61, 0xd4, 0x3, 0x11, 0x8, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0xac, 0x2e, 0xbb, 0x80, 0xf0]; -_T Dash = [0x2d, 0x1, 0x85, 0x5c, 0x1, 0x33, 0x1, 0x8e, 0x41, 0x1, 0x84, 0x5, 0x1, 0x88, 0x9, 0x6, 0x3d, 0x1, 0x27, 0x1, 0xf, 0x1, 0x81, 0x86, 0x1, 0x8c, 0x4, 0x1, 0x2, 0x1, 0x1f, 0x2, 0x81, 0xe0, 0x1, 0x13, 0x1, 0x6f, 0x1, 0xa0, 0xcd, 0x90, 0x2, 0x25, 0x1, 0xa, 0x1, 0x80, 0xa9, 0x1]; -_T ID_Continue = [0x30, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x1, 0x1, 0x2, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x11, 0x75, 0x1, 0x2, 0x2, 0x4, 0x8, 0x5, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x1, 0x5, 0x2, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x9, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x8, 0x1b, 0x5, 0x3, 0x1d, 0xb, 0x5, 0x4a, 0x4, 0x66, 0x1, 0x8, 0x2, 0xa, 0x1, 0x13, 0x2, 0x1, 0x10, 0x3b, 0x2, 0x65, 0xe, 0x36, 0x4, 0x1, 0x5, 0x2e, 0x12, 0x1c, 0x44, 0x1, 0x1, 0xb, 0x37, 0x1b, 0x1, 0x64, 0x2, 0xa, 0x1, 0x7, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0x2, 0xc, 0xf, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x7, 0x4, 0x1, 0x1, 0x7, 0x10, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, 0x4, 0x2, 0xa, 0x11, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x1, 0x10, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0xa, 0x11, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, 0x2, 0x6, 0x4, 0x2, 0xa, 0x12, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, 0x2, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0xa, 0xa, 0x6, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0xd, 0x3a, 0x5, 0xf, 0x1, 0xa, 0x27, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x1, 0x6, 0x2, 0xa, 0x2, 0x4, 0x20, 0x1, 0x17, 0x2, 0x6, 0xa, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0xa, 0x1, 0x24, 0x4, 0x14, 0x1, 0x12, 0x1, 0x24, 0x9, 0x1, 0x39, 0x4a, 0x6, 0x4e, 0x2, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x2, 0x3, 0x9, 0x9, 0xe, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x7, 0xb, 0x15, 0xb, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x54, 0x3, 0x1, 0x4, 0x2, 0x2, 0xa, 0x21, 0x3, 0x2, 0xa, 0x6, 0x58, 0x8, 0x2b, 0x5, 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0xc, 0xa, 0x28, 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x6, 0xb, 0x25, 0x1c, 0x4, 0x3f, 0x1, 0x1d, 0x2, 0xb, 0x6, 0xa, 0xd, 0x1, 0x58, 0x4c, 0x4, 0xa, 0x11, 0x9, 0xc, 0x74, 0xc, 0x38, 0x8, 0xa, 0x3, 0x31, 0x52, 0x3, 0x1, 0x23, 0x9, 0x80, 0xe7, 0x15, 0x81, 0x1a, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x42, 0x2, 0x13, 0x1, 0x1c, 0x1, 0xd, 0x1, 0x10, 0xd, 0x33, 0xd, 0x4, 0x1, 0x3, 0xc, 0x11, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x10, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x9, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, 0xf, 0x18, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x20, 0x82, 0x5, 0x3, 0x19, 0xf, 0x1, 0x5, 0x2, 0x5, 0x4, 0x56, 0x2, 0x7, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x1c, 0x14, 0x30, 0x4, 0xa, 0x1, 0x19, 0x7, 0x53, 0x25, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x30, 0x18, 0x34, 0xc, 0x45, 0xb, 0xa, 0x6, 0x18, 0x3, 0x1, 0x4, 0x2e, 0x2, 0x24, 0xc, 0x1d, 0x3, 0x41, 0xe, 0xb, 0x26, 0x37, 0x9, 0xe, 0x2, 0xa, 0x6, 0x17, 0x3, 0x2, 0x4, 0x43, 0x18, 0x3, 0x2, 0x10, 0x2, 0x5, 0xa, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x2b, 0x1, 0x2, 0x2, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0xc, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xc, 0x4, 0x10, 0x10, 0x7, 0xc, 0x2, 0x18, 0x3, 0x20, 0x5, 0x1, 0x80, 0x87, 0x13, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, 0x80, 0x88, 0x1, 0x80, 0x82, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, 0x4, 0x3, 0x4, 0x1, 0x20, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xb7, 0x47, 0x1f, 0xa, 0x10, 0x3b, 0x15, 0x19, 0x7, 0xa, 0x6, 0x35, 0x1, 0xa, 0x40, 0x45, 0xb, 0xa, 0x84, 0xa6, 0x38, 0x8, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x10, 0x11, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x21, 0x63, 0x5, 0x3, 0x6, 0x8, 0x8, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0x81, 0xbb, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e, 0xab, 0x6, 0xe2, 0x80, 0xf0]; -_T White_Space = [0x9, 0x5, 0x12, 0x1, 0x64, 0x1, 0x1a, 0x1, 0x95, 0xdf, 0x1, 0x89, 0x7f, 0xb, 0x1d, 0x2, 0x5, 0x1, 0x2f, 0x1, 0x8f, 0xa0, 0x1]; -_T Grapheme_Link = [0x89, 0x4d, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7c, 0x1, 0x6f, 0x1, 0x81, 0x49, 0x1, 0x80, 0xb4, 0x2, 0x86, 0xd9, 0x1, 0x1f, 0x1, 0x80, 0x9d, 0x1, 0x82, 0x8d, 0x1, 0x80, 0xe3, 0x1, 0x65, 0x2, 0x46, 0x2, 0x91, 0x8b, 0x1, 0xa0, 0x7a, 0x86, 0x1, 0x80, 0xbd, 0x1, 0x80, 0x8e, 0x1, 0x6c, 0x1, 0x81, 0x35, 0x1, 0x80, 0xf6, 0x1, 0xa0, 0x5e, 0x51, 0x1, 0x86, 0x6, 0x1, 0x72, 0x1, 0x79, 0x2, 0x80, 0x8b, 0x1, 0x84, 0xf5, 0x1]; -_T Ll = [0x61, 0x1a, 0x3a, 0x1, 0x29, 0x18, 0x1, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x3, 0x6, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x45, 0x1, 0x1b, 0x80, 0xc1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x3, 0x3, 0x12, 0x1, 0x1b, 0x23, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x2, 0x1, 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x39, 0x27, 0x97, 0x78, 0x2c, 0x3f, 0xd, 0x1, 0x22, 0x66, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0xe, 0x2, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x5, 0x1, 0x2, 0x6, 0x1, 0x3, 0x3, 0x1, 0x2, 0x8, 0x4, 0x2, 0x2, 0x8, 0x8, 0xa, 0x3, 0x1, 0x2, 0x81, 0x12, 0x1, 0x3, 0x2, 0x3, 0x1, 0x1b, 0x1, 0x4, 0x1, 0x4, 0x1, 0x2, 0x2, 0x8, 0x4, 0x4, 0x1, 0x35, 0x1, 0x8a, 0xab, 0x2f, 0x2, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x6, 0x5, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x1, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0xa0, 0x79, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x50, 0x1, 0xa0, 0x53, 0x5, 0x7, 0xc, 0x5, 0x84, 0x29, 0x1a, 0x84, 0xcd, 0x28, 0xa0, 0xcf, 0xca, 0x1a, 0x1a, 0x7, 0x1, 0x12, 0x1a, 0x1a, 0x1a, 0x4, 0x1, 0x1, 0x1, 0x7, 0x1, 0xb, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1c, 0x1c, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1, 0x1]; +_T Cased = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x5, 0x17, + 0x1, 0x1f, 0x1, 0x80, 0xc3, 0x1, 0x4, 0x4, 0x80, 0xd0, 0x1, 0x24, 0x7, 0x2, + 0x1e, 0x5, 0x60, 0x1, 0x2a, 0x4, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x1, 0x3, + 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x8, 0x80, 0x9e, 0x9, + 0x26, 0xa, 0x27, 0x8b, 0x18, 0x26, 0x1, 0x1, 0x5, 0x1, 0x8c, 0x32, 0x80, + 0xc0, 0x40, 0x81, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, + 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x74, 0x1, 0xd, + 0x1, 0x10, 0xd, 0x65, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x3, 0x5, 0x6, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x6, 0x4, 0x1, 0x2, 0x4, 0x5, + 0x5, 0x4, 0x1, 0x11, 0x20, 0x3, 0x2, 0x83, 0x31, 0x34, 0x87, 0x16, 0x2f, + 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x4, 0x3, 0x2, 0xc, 0x26, 0x1, 0x1, 0x5, + 0x1, 0xa0, 0x79, 0x12, 0x2e, 0x12, 0x18, 0x80, 0x8a, 0x66, 0x3, 0x4, 0x1, + 0x4, 0xc, 0xb, 0x4d, 0x3, 0xa0, 0x53, 0x5, 0x7, 0xc, 0x5, 0x84, 0x9, 0x1a, + 0x6, 0x1a, 0x84, 0xa5, 0x50, 0xa0, 0xcf, 0xb0, 0x55, 0x1, 0x47, 0x1, 0x2, + 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, + 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, + 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, + 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8]; +_T Mn = [0x83, 0x0, 0x70, 0x81, 0x13, 0x5, 0x81, 0x9, 0x2d, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x2, 0x1, 0x1, 0x48, 0xb, 0x30, 0x15, 0x10, 0x1, 0x65, 0x7, 0x2, + 0x6, 0x2, 0x2, 0x1, 0x4, 0x23, 0x1, 0x1e, 0x1b, 0x5b, 0xb, 0x3a, 0x9, + 0x22, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x5, 0x2b, 0x3, 0x80, 0x88, 0x1b, + 0x1, 0x3, 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, + 0x1d, 0x1, 0x3a, 0x1, 0x4, 0x4, 0x8, 0x1, 0x14, 0x2, 0x1d, 0x2, 0x39, 0x1, + 0x4, 0x2, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x1e, 0x2, 0x3, 0x1, 0xb, 0x2, + 0x39, 0x1, 0x4, 0x5, 0x1, 0x2, 0x4, 0x1, 0x14, 0x2, 0x1d, 0x1, 0x3a, 0x1, + 0x2, 0x1, 0x1, 0x4, 0x8, 0x1, 0x8, 0x1, 0xb, 0x2, 0x1e, 0x1, 0x3d, 0x1, + 0xc, 0x1, 0x70, 0x3, 0x5, 0x3, 0x1, 0x4, 0x7, 0x2, 0xb, 0x2, 0x58, 0x1, + 0x2, 0x1, 0x6, 0x1, 0x5, 0x2, 0x14, 0x2, 0x5d, 0x4, 0x8, 0x1, 0x14, 0x2, + 0x66, 0x1, 0x7, 0x3, 0x1, 0x1, 0x5a, 0x1, 0x2, 0x7, 0xc, 0x8, 0x62, 0x1, + 0x2, 0x6, 0x1, 0x2, 0xb, 0x6, 0x4a, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x37, 0xe, 0x1, 0x5, 0x1, 0x2, 0x5, 0xb, 0x1, 0x24, 0x9, 0x1, 0x66, 0x4, + 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, + 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, 0x82, 0xbf, 0x3, 0x83, 0xb2, 0x3, 0x1d, + 0x3, 0x1d, 0x2, 0x1e, 0x2, 0x40, 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x9, + 0x1, 0x2d, 0x3, 0x80, 0x9b, 0x1, 0x76, 0x3, 0x4, 0x2, 0x9, 0x1, 0x6, 0x3, + 0x80, 0xdb, 0x2, 0x2, 0x1, 0x3a, 0x1, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x2, + 0x8, 0x6, 0xa, 0x2, 0x1, 0x80, 0x80, 0x4, 0x30, 0x1, 0x1, 0x5, 0x1, 0x1, + 0x5, 0x1, 0x28, 0x9, 0xc, 0x2, 0x20, 0x4, 0x2, 0x2, 0x1, 0x1, 0x3a, 0x1, + 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x3a, 0x8, 0x2, 0x2, 0x80, 0x98, 0x3, 0x1, + 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x80, 0xcb, 0x27, 0x15, 0x4, 0x82, + 0xd0, 0xd, 0x4, 0x1, 0x3, 0xc, 0x8b, 0xfe, 0x3, 0x80, 0x8d, 0x1, 0x60, + 0x20, 0x82, 0x2a, 0x4, 0x6b, 0x2, 0xa0, 0x75, 0xd4, 0x1, 0x4, 0xa, 0x21, + 0x1, 0x50, 0x2, 0x81, 0x10, 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, 0x80, 0x9d, + 0x1, 0x1b, 0x12, 0x34, 0x8, 0x19, 0xb, 0x2e, 0x3, 0x30, 0x1, 0x2, 0x4, + 0x2, 0x1, 0x6c, 0x6, 0x2, 0x2, 0x2, 0x2, 0xc, 0x1, 0x8, 0x1, 0x63, 0x1, + 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x2a, 0x2, 0x8, 0x1, 0x80, 0xee, + 0x1, 0x2, 0x1, 0x4, 0x1, 0xa0, 0x4f, 0x30, 0x1, 0x82, 0xe1, 0x10, 0x10, + 0x7, 0x83, 0xd6, 0x1, 0x88, 0x3, 0x3, 0x1, 0x2, 0x5, 0x4, 0x28, 0x3, 0x4, + 0x1, 0x85, 0xc1, 0x1, 0x36, 0xf, 0x39, 0x2, 0x31, 0x4, 0x2, 0x2, 0x45, + 0x3, 0x24, 0x5, 0x1, 0x8, 0x4b, 0x2, 0x34, 0x9, 0x84, 0xec, 0x1, 0x1, 0x1, + 0x2, 0x6, 0x1, 0x1, 0xa0, 0x58, 0xd7, 0x4, 0xa0, 0x61, 0xd4, 0x3, 0x11, + 0x8, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0xac, 0x2e, 0xbb, 0x80, 0xf0]; +_T Dash = [0x2d, 0x1, 0x85, 0x5c, 0x1, 0x33, 0x1, 0x8e, 0x41, 0x1, 0x84, 0x5, + 0x1, 0x88, 0x9, 0x6, 0x3d, 0x1, 0x27, 0x1, 0xf, 0x1, 0x81, 0x86, 0x1, + 0x8c, 0x4, 0x1, 0x2, 0x1, 0x1f, 0x2, 0x81, 0xe0, 0x1, 0x13, 0x1, 0x6f, 0x1, + 0xa0, 0xcd, 0x90, 0x2, 0x25, 0x1, 0xa, 0x1, 0x80, 0xa9, 0x1]; +_T ID_Continue = [ + 0x30, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, + 0x1, 0x1, 0x1, 0x11, 0x75, 0x1, 0x2, 0x2, 0x4, 0x8, 0x5, 0x1, 0x1, 0x1, + 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x1, 0x5, 0x2, 0x80, 0x9e, 0x9, 0x26, + 0x2, 0x1, 0x7, 0x27, 0x9, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, + 0x8, 0x1b, 0x5, 0x3, 0x1d, 0xb, 0x5, 0x4a, 0x4, 0x66, 0x1, 0x8, 0x2, 0xa, + 0x1, 0x13, 0x2, 0x1, 0x10, 0x3b, 0x2, 0x65, 0xe, 0x36, 0x4, 0x1, 0x5, 0x2e, + 0x12, 0x1c, 0x44, 0x1, 0x1, 0xb, 0x37, 0x1b, 0x1, 0x64, 0x2, 0xa, 0x1, + 0x7, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, + 0x2, 0xc, 0xf, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, + 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x7, + 0x4, 0x1, 0x1, 0x7, 0x10, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, + 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, + 0x4, 0x2, 0xa, 0x11, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, + 0x2, 0xa, 0x1, 0x1, 0x10, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, + 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, + 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0xa, 0x11, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, + 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, + 0x2, 0x6, 0x4, 0x2, 0xa, 0x12, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, + 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, + 0x2, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, + 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0xa, 0xa, 0x6, 0x2, 0x2, + 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3, 0x1, 0x4, 0x6, + 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0xd, 0x3a, 0x5, 0xf, 0x1, 0xa, 0x27, 0x2, + 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, + 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x1, 0x6, + 0x2, 0xa, 0x2, 0x4, 0x20, 0x1, 0x17, 0x2, 0x6, 0xa, 0xb, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x4, 0xa, 0x1, 0x24, 0x4, 0x14, 0x1, 0x12, 0x1, 0x24, 0x9, 0x1, + 0x39, 0x4a, 0x6, 0x4e, 0x2, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, + 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, + 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, + 0x1, 0x4, 0x2, 0x43, 0x2, 0x3, 0x9, 0x9, 0xe, 0x10, 0x10, 0x55, 0xc, + 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, + 0x7, 0xb, 0x15, 0xb, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x54, 0x3, + 0x1, 0x4, 0x2, 0x2, 0xa, 0x21, 0x3, 0x2, 0xa, 0x6, 0x58, 0x8, 0x2b, 0x5, + 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0xc, 0xa, 0x28, 0x2, 0x5, 0xb, 0x2c, 0x4, + 0x1a, 0x6, 0xb, 0x25, 0x1c, 0x4, 0x3f, 0x1, 0x1d, 0x2, 0xb, 0x6, 0xa, + 0xd, 0x1, 0x58, 0x4c, 0x4, 0xa, 0x11, 0x9, 0xc, 0x74, 0xc, 0x38, 0x8, + 0xa, 0x3, 0x31, 0x52, 0x3, 0x1, 0x23, 0x9, 0x80, 0xe7, 0x15, 0x81, 0x1a, + 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, + 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x42, 0x2, 0x13, 0x1, 0x1c, 0x1, 0xd, + 0x1, 0x10, 0xd, 0x33, 0xd, 0x4, 0x1, 0x3, 0xc, 0x11, 0x1, 0x4, 0x1, 0x2, + 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x10, 0x2, + 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, 0x1, 0x2f, 0x1, + 0x80, 0x85, 0x6, 0x9, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, + 0xf, 0x18, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, + 0x1, 0x7, 0x1, 0x7, 0x1, 0x20, 0x82, 0x5, 0x3, 0x19, 0xf, 0x1, 0x5, 0x2, + 0x5, 0x4, 0x56, 0x2, 0x7, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, + 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, + 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x1c, 0x14, 0x30, 0x4, 0xa, + 0x1, 0x19, 0x7, 0x53, 0x25, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, + 0x4d, 0x30, 0x18, 0x34, 0xc, 0x45, 0xb, 0xa, 0x6, 0x18, 0x3, 0x1, 0x4, + 0x2e, 0x2, 0x24, 0xc, 0x1d, 0x3, 0x41, 0xe, 0xb, 0x26, 0x37, 0x9, 0xe, + 0x2, 0xa, 0x6, 0x17, 0x3, 0x2, 0x4, 0x43, 0x18, 0x3, 0x2, 0x10, 0x2, 0x5, + 0xa, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x2b, 0x1, + 0x2, 0x2, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, + 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0xc, 0x1, 0xd, 0x1, + 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x81, 0x6b, 0x12, 0x40, + 0x2, 0x36, 0x28, 0xc, 0x4, 0x10, 0x10, 0x7, 0xc, 0x2, 0x18, 0x3, 0x20, 0x5, + 0x1, 0x80, 0x87, 0x13, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0xb, 0x59, + 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, + 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, 0x80, 0x88, 0x1, + 0x80, 0x82, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, + 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, 0x56, 0x6, + 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, + 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, + 0x1b, 0x4, 0x3, 0x4, 0x1, 0x20, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, + 0x13, 0x80, 0x8d, 0x49, 0x83, 0xb7, 0x47, 0x1f, 0xa, 0x10, 0x3b, 0x15, + 0x19, 0x7, 0xa, 0x6, 0x35, 0x1, 0xa, 0x40, 0x45, 0xb, 0xa, 0x84, 0xa6, + 0x38, 0x8, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, + 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x10, + 0x11, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x21, 0x63, 0x5, 0x3, 0x6, 0x8, 0x8, + 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0x81, 0xbb, 0x55, 0x1, 0x47, 0x1, + 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, + 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, + 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, + 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, + 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, + 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, + 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, + 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, + 0x1e, 0xab, 0x6, 0xe2, 0x80, 0xf0 +]; +_T White_Space = [ + 0x9, 0x5, 0x12, 0x1, 0x64, 0x1, 0x1a, 0x1, 0x95, 0xdf, 0x1, 0x89, 0x7f, + 0xb, 0x1d, 0x2, 0x5, 0x1, 0x2f, 0x1, 0x8f, 0xa0, 0x1 +]; +_T Grapheme_Link = [ + 0x89, 0x4d, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, + 0x7f, 0x1, 0x7f, 0x1, 0x7f, 0x1, 0x7c, 0x1, 0x6f, 0x1, 0x81, 0x49, 0x1, + 0x80, 0xb4, 0x2, 0x86, 0xd9, 0x1, 0x1f, 0x1, 0x80, 0x9d, 0x1, 0x82, 0x8d, + 0x1, 0x80, 0xe3, 0x1, 0x65, 0x2, 0x46, 0x2, 0x91, 0x8b, 0x1, 0xa0, 0x7a, + 0x86, 0x1, 0x80, 0xbd, 0x1, 0x80, 0x8e, 0x1, 0x6c, 0x1, 0x81, 0x35, 0x1, + 0x80, 0xf6, 0x1, 0xa0, 0x5e, 0x51, 0x1, 0x86, 0x6, 0x1, 0x72, 0x1, 0x79, + 0x2, 0x80, 0x8b, 0x1, 0x84, 0xf5, 0x1 +]; +_T Ll = [0x61, 0x1a, 0x3a, 0x1, 0x29, 0x18, 0x1, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, + 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, + 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, + 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x3, 0x6, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x45, 0x1, 0x1b, 0x80, 0xc1, 0x1, 0x1, 0x1, + 0x3, 0x1, 0x3, 0x3, 0x12, 0x1, 0x1b, 0x23, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x2, 0x1, 0x2, 0x2, 0x33, + 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x39, 0x27, + 0x97, 0x78, 0x2c, 0x3f, 0xd, 0x1, 0x22, 0x66, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x9, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, + 0xe, 0x2, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x5, 0x1, 0x2, 0x6, 0x1, 0x3, 0x3, + 0x1, 0x2, 0x8, 0x4, 0x2, 0x2, 0x8, 0x8, 0xa, 0x3, 0x1, 0x2, 0x81, 0x12, + 0x1, 0x3, 0x2, 0x3, 0x1, 0x1b, 0x1, 0x4, 0x1, 0x4, 0x1, 0x2, 0x2, 0x8, + 0x4, 0x4, 0x1, 0x35, 0x1, 0x8a, 0xab, 0x2f, 0x2, 0x1, 0x3, 0x2, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x6, 0x5, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x1, 0xc, 0x26, 0x1, 0x1, + 0x5, 0x1, 0xa0, 0x79, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0xd, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x50, 0x1, 0xa0, 0x53, 0x5, + 0x7, 0xc, 0x5, 0x84, 0x29, 0x1a, 0x84, 0xcd, 0x28, 0xa0, 0xcf, 0xca, + 0x1a, 0x1a, 0x7, 0x1, 0x12, 0x1a, 0x1a, 0x1a, 0x4, 0x1, 0x1, 0x1, 0x7, 0x1, + 0xb, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1c, 0x1c, 0x19, 0x1, 0x6, 0x1a, 0x19, + 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1, + 0x1]; _T Cc = [0x0, 0x20, 0x5f, 0x21]; -_T Pattern_Syntax = [0x21, 0xf, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0x4, 0x22, 0x7, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x2, 0x4, 0x1, 0x4, 0x1, 0x3, 0x1, 0x17, 0x1, 0x1f, 0x1, 0x9f, 0x18, 0x18, 0x8, 0xf, 0x2, 0x13, 0x1, 0xa, 0x81, 0x31, 0x82, 0xd0, 0x80, 0xa0, 0x82, 0x76, 0x1e, 0x84, 0x6c, 0x82, 0x0, 0x80, 0x80, 0x81, 0x81, 0x3, 0x4, 0x19, 0xf, 0x1, 0xa0, 0xcd, 0xd, 0x2, 0x81, 0x5, 0x2]; -_T XID_Continue = [0x30, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x1, 0x1, 0x2, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, 0x1, 0x1, 0x1, 0x11, 0x75, 0x1, 0x2, 0x3, 0x3, 0x8, 0x5, 0x1, 0x1, 0x1, 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x1, 0x5, 0x2, 0x80, 0x9e, 0x9, 0x26, 0x2, 0x1, 0x7, 0x27, 0x9, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x8, 0x1b, 0x5, 0x3, 0x1d, 0xb, 0x5, 0x4a, 0x4, 0x66, 0x1, 0x8, 0x2, 0xa, 0x1, 0x13, 0x2, 0x1, 0x10, 0x3b, 0x2, 0x65, 0xe, 0x36, 0x4, 0x1, 0x5, 0x2e, 0x12, 0x1c, 0x44, 0x1, 0x1, 0xb, 0x37, 0x1b, 0x1, 0x64, 0x2, 0xa, 0x1, 0x7, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0x2, 0xc, 0xf, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x7, 0x4, 0x1, 0x1, 0x7, 0x10, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, 0x4, 0x2, 0xa, 0x11, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x1, 0x10, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0xa, 0x11, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, 0x2, 0x6, 0x4, 0x2, 0xa, 0x12, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, 0x2, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0xa, 0xa, 0x6, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0xd, 0x3a, 0x5, 0xf, 0x1, 0xa, 0x27, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x1, 0x6, 0x2, 0xa, 0x2, 0x4, 0x20, 0x1, 0x17, 0x2, 0x6, 0xa, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0xa, 0x1, 0x24, 0x4, 0x14, 0x1, 0x12, 0x1, 0x24, 0x9, 0x1, 0x39, 0x4a, 0x6, 0x4e, 0x2, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x2, 0x3, 0x9, 0x9, 0xe, 0x10, 0x10, 0x55, 0xc, 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, 0x7, 0xb, 0x15, 0xb, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x54, 0x3, 0x1, 0x4, 0x2, 0x2, 0xa, 0x21, 0x3, 0x2, 0xa, 0x6, 0x58, 0x8, 0x2b, 0x5, 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0xc, 0xa, 0x28, 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x6, 0xb, 0x25, 0x1c, 0x4, 0x3f, 0x1, 0x1d, 0x2, 0xb, 0x6, 0xa, 0xd, 0x1, 0x58, 0x4c, 0x4, 0xa, 0x11, 0x9, 0xc, 0x74, 0xc, 0x38, 0x8, 0xa, 0x3, 0x31, 0x52, 0x3, 0x1, 0x23, 0x9, 0x80, 0xe7, 0x15, 0x81, 0x1a, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x42, 0x2, 0x13, 0x1, 0x1c, 0x1, 0xd, 0x1, 0x10, 0xd, 0x33, 0xd, 0x4, 0x1, 0x3, 0xc, 0x11, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x10, 0x2, 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x85, 0x6, 0x9, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, 0xf, 0x18, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x20, 0x82, 0x5, 0x3, 0x19, 0xf, 0x1, 0x5, 0x2, 0x5, 0x4, 0x56, 0x2, 0x2, 0x2, 0x3, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x1c, 0x14, 0x30, 0x4, 0xa, 0x1, 0x19, 0x7, 0x53, 0x25, 0x9, 0x2, 0x67, 0x2, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x30, 0x18, 0x34, 0xc, 0x45, 0xb, 0xa, 0x6, 0x18, 0x3, 0x1, 0x4, 0x2e, 0x2, 0x24, 0xc, 0x1d, 0x3, 0x41, 0xe, 0xb, 0x26, 0x37, 0x9, 0xe, 0x2, 0xa, 0x6, 0x17, 0x3, 0x2, 0x4, 0x43, 0x18, 0x3, 0x2, 0x10, 0x2, 0x5, 0xa, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x2b, 0x1, 0x2, 0x2, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0xc, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, 0x21, 0x80, 0x8b, 0x6, 0x80, 0xda, 0x12, 0x40, 0x2, 0x36, 0x28, 0xa, 0x6, 0x10, 0x10, 0x7, 0xc, 0x2, 0x18, 0x3, 0x21, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7e, 0x13, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, 0x80, 0x88, 0x1, 0x80, 0x82, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, 0x4, 0x3, 0x4, 0x1, 0x20, 0x1d, 0x80, 0x83, 0x36, 0xa, 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xb7, 0x47, 0x1f, 0xa, 0x10, 0x3b, 0x15, 0x19, 0x7, 0xa, 0x6, 0x35, 0x1, 0xa, 0x40, 0x45, 0xb, 0xa, 0x84, 0xa6, 0x38, 0x8, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x10, 0x11, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x21, 0x63, 0x5, 0x3, 0x6, 0x8, 0x8, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0x81, 0xbb, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e, 0xab, 0x6, 0xe2, 0x80, 0xf0]; -_T Lowercase = [0x61, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x24, 0x18, 0x1, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x3, 0x6, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x45, 0x1, 0x24, 0x7, 0x2, 0x1e, 0x5, 0x60, 0x1, 0x2b, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x4, 0x12, 0x1, 0x1b, 0x23, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x2, 0x1, 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x39, 0x27, 0x97, 0x78, 0x80, 0xc0, 0x41, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0xe, 0x2, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x5, 0x1, 0x2, 0x6, 0x1, 0x3, 0x3, 0x1, 0x2, 0x8, 0x4, 0x2, 0x2, 0x8, 0x8, 0xa, 0x3, 0x1, 0x2, 0x79, 0x1, 0xd, 0x1, 0x10, 0xd, 0x6d, 0x1, 0x3, 0x2, 0x3, 0x1, 0x1b, 0x1, 0x4, 0x1, 0x4, 0x1, 0x2, 0x2, 0x8, 0x4, 0x4, 0x1, 0x21, 0x10, 0x4, 0x1, 0x83, 0x4b, 0x1a, 0x87, 0x46, 0x2f, 0x2, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x8, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x1, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0xa0, 0x79, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4e, 0x3, 0xa0, 0x53, 0x5, 0x7, 0xc, 0x5, 0x84, 0x29, 0x1a, 0x84, 0xcd, 0x28, 0xa0, 0xcf, 0xca, 0x1a, 0x1a, 0x7, 0x1, 0x12, 0x1a, 0x1a, 0x1a, 0x4, 0x1, 0x1, 0x1, 0x7, 0x1, 0xb, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1c, 0x1c, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1, 0x1]; +_T Pattern_Syntax = [ + 0x21, 0xf, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0x4, 0x22, 0x7, 0x1, 0x1, + 0x1, 0x2, 0x1, 0x1, 0x1, 0x2, 0x4, 0x1, 0x4, 0x1, 0x3, 0x1, 0x17, 0x1, + 0x1f, 0x1, 0x9f, 0x18, 0x18, 0x8, 0xf, 0x2, 0x13, 0x1, 0xa, 0x81, 0x31, + 0x82, 0xd0, 0x80, 0xa0, 0x82, 0x76, 0x1e, 0x84, 0x6c, 0x82, 0x0, 0x80, + 0x80, 0x81, 0x81, 0x3, 0x4, 0x19, 0xf, 0x1, 0xa0, 0xcd, 0xd, 0x2, 0x81, 0x5, 0x2 +]; +_T XID_Continue = [ + 0x30, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xca, 0x4, 0xc, 0xe, 0x5, 0x7, + 0x1, 0x1, 0x1, 0x11, 0x75, 0x1, 0x2, 0x3, 0x3, 0x8, 0x5, 0x1, 0x1, 0x1, + 0x14, 0x1, 0x53, 0x1, 0x80, 0x8b, 0x1, 0x5, 0x2, 0x80, 0x9e, 0x9, 0x26, + 0x2, 0x1, 0x7, 0x27, 0x9, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, + 0x8, 0x1b, 0x5, 0x3, 0x1d, 0xb, 0x5, 0x4a, 0x4, 0x66, 0x1, 0x8, 0x2, 0xa, + 0x1, 0x13, 0x2, 0x1, 0x10, 0x3b, 0x2, 0x65, 0xe, 0x36, 0x4, 0x1, 0x5, 0x2e, + 0x12, 0x1c, 0x44, 0x1, 0x1, 0xb, 0x37, 0x1b, 0x1, 0x64, 0x2, 0xa, 0x1, + 0x7, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, + 0x2, 0xc, 0xf, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, + 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x7, + 0x4, 0x1, 0x1, 0x7, 0x10, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, + 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, + 0x4, 0x2, 0xa, 0x11, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, + 0x2, 0xa, 0x1, 0x1, 0x10, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, + 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, + 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0xa, 0x11, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, + 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, + 0x2, 0x6, 0x4, 0x2, 0xa, 0x12, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, + 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, + 0x2, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, + 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0xa, 0xa, 0x6, 0x2, 0x2, + 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3, 0x1, 0x4, 0x6, + 0x1, 0x1, 0x1, 0x8, 0x12, 0x2, 0xd, 0x3a, 0x5, 0xf, 0x1, 0xa, 0x27, 0x2, + 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, + 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x1, 0x6, + 0x2, 0xa, 0x2, 0x4, 0x20, 0x1, 0x17, 0x2, 0x6, 0xa, 0xb, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x4, 0xa, 0x1, 0x24, 0x4, 0x14, 0x1, 0x12, 0x1, 0x24, 0x9, 0x1, + 0x39, 0x4a, 0x6, 0x4e, 0x2, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, + 0x81, 0x4d, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, + 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, + 0x1, 0x4, 0x2, 0x43, 0x2, 0x3, 0x9, 0x9, 0xe, 0x10, 0x10, 0x55, 0xc, + 0x82, 0x6c, 0x2, 0x11, 0x1, 0x1a, 0x5, 0x4b, 0x3, 0x3, 0xf, 0xd, 0x1, + 0x7, 0xb, 0x15, 0xb, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x54, 0x3, + 0x1, 0x4, 0x2, 0x2, 0xa, 0x21, 0x3, 0x2, 0xa, 0x6, 0x58, 0x8, 0x2b, 0x5, + 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0xc, 0xa, 0x28, 0x2, 0x5, 0xb, 0x2c, 0x4, + 0x1a, 0x6, 0xb, 0x25, 0x1c, 0x4, 0x3f, 0x1, 0x1d, 0x2, 0xb, 0x6, 0xa, + 0xd, 0x1, 0x58, 0x4c, 0x4, 0xa, 0x11, 0x9, 0xc, 0x74, 0xc, 0x38, 0x8, + 0xa, 0x3, 0x31, 0x52, 0x3, 0x1, 0x23, 0x9, 0x80, 0xe7, 0x15, 0x81, 0x1a, + 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1f, 0x2, 0x35, 0x1, 0x7, 0x1, 0x1, 0x3, 0x3, 0x1, 0x7, 0x3, 0x4, 0x2, + 0x6, 0x4, 0xd, 0x5, 0x3, 0x1, 0x7, 0x42, 0x2, 0x13, 0x1, 0x1c, 0x1, 0xd, + 0x1, 0x10, 0xd, 0x33, 0xd, 0x4, 0x1, 0x3, 0xc, 0x11, 0x1, 0x4, 0x1, 0x2, + 0xa, 0x1, 0x1, 0x2, 0x6, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x10, 0x2, + 0x4, 0x5, 0x5, 0x4, 0x1, 0x11, 0x29, 0x8a, 0x77, 0x2f, 0x1, 0x2f, 0x1, + 0x80, 0x85, 0x6, 0x9, 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x1, + 0xf, 0x18, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, + 0x1, 0x7, 0x1, 0x7, 0x1, 0x20, 0x82, 0x5, 0x3, 0x19, 0xf, 0x1, 0x5, 0x2, + 0x5, 0x4, 0x56, 0x2, 0x2, 0x2, 0x3, 0x1, 0x5a, 0x1, 0x4, 0x5, 0x29, 0x3, + 0x5e, 0x11, 0x1b, 0x35, 0x10, 0x82, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, + 0xcd, 0x33, 0x84, 0x8d, 0x43, 0x2e, 0x2, 0x81, 0xd, 0x3, 0x1c, 0x14, + 0x30, 0x4, 0xa, 0x1, 0x19, 0x7, 0x53, 0x25, 0x9, 0x2, 0x67, 0x2, 0x4, + 0x1, 0x4, 0xc, 0xb, 0x4d, 0x30, 0x18, 0x34, 0xc, 0x45, 0xb, 0xa, 0x6, + 0x18, 0x3, 0x1, 0x4, 0x2e, 0x2, 0x24, 0xc, 0x1d, 0x3, 0x41, 0xe, 0xb, + 0x26, 0x37, 0x9, 0xe, 0x2, 0xa, 0x6, 0x17, 0x3, 0x2, 0x4, 0x43, 0x18, + 0x3, 0x2, 0x10, 0x2, 0x5, 0xa, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, + 0x80, 0x91, 0x2b, 0x1, 0x2, 0x2, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, + 0x4, 0x31, 0xa0, 0x21, 0x4, 0x81, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, + 0x5, 0xc, 0x1, 0xd, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x6c, + 0x21, 0x80, 0x8b, 0x6, 0x80, 0xda, 0x12, 0x40, 0x2, 0x36, 0x28, 0xa, 0x6, + 0x10, 0x10, 0x7, 0xc, 0x2, 0x18, 0x3, 0x21, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7e, 0x13, 0xa, 0x7, 0x1a, 0x4, 0x1, 0x1, + 0x1a, 0xb, 0x59, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x23, 0xc, 0x1, + 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x45, 0x35, + 0x80, 0x88, 0x1, 0x80, 0x82, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x11, 0x1b, 0x35, + 0x1e, 0x2, 0x24, 0x4, 0x8, 0x1, 0x5, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, + 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x80, 0xaa, + 0x16, 0xa, 0x1a, 0x46, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, + 0x1, 0x3, 0x1, 0x1b, 0x4, 0x3, 0x4, 0x1, 0x20, 0x1d, 0x80, 0x83, 0x36, 0xa, + 0x16, 0xa, 0x13, 0x80, 0x8d, 0x49, 0x83, 0xb7, 0x47, 0x1f, 0xa, 0x10, 0x3b, + 0x15, 0x19, 0x7, 0xa, 0x6, 0x35, 0x1, 0xa, 0x40, 0x45, 0xb, 0xa, 0x84, + 0xa6, 0x38, 0x8, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0x8b, 0x9d, + 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, + 0x10, 0x11, 0xa0, 0x40, 0x60, 0x2, 0xa0, 0x21, 0x63, 0x5, 0x3, 0x6, 0x8, + 0x8, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0x81, 0xbb, 0x55, 0x1, 0x47, + 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, + 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, + 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, + 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, + 0x19, 0x1, 0x8, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, + 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, + 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x91, + 0x44, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, + 0xe2, 0x82, 0x1e, 0xab, 0x6, 0xe2, 0x80, 0xf0 +]; +_T Lowercase = [0x61, 0x1a, 0x2f, 0x1, 0xa, 0x1, 0x4, 0x1, 0x24, 0x18, 0x1, + 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, + 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, + 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x3, 0x6, 0x1, + 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, + 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x45, 0x1, + 0x24, 0x7, 0x2, 0x1e, 0x5, 0x60, 0x1, 0x2b, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, + 0x4, 0x12, 0x1, 0x1b, 0x23, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x2, 0x1, 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x39, 0x27, 0x97, 0x78, 0x80, + 0xc0, 0x41, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x6, 0xa, 0x8, 0x8, + 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0xe, 0x2, 0x8, 0x8, 0x8, 0x8, 0x8, + 0x8, 0x5, 0x1, 0x2, 0x6, 0x1, 0x3, 0x3, 0x1, 0x2, 0x8, 0x4, 0x2, 0x2, 0x8, + 0x8, 0xa, 0x3, 0x1, 0x2, 0x79, 0x1, 0xd, 0x1, 0x10, 0xd, 0x6d, 0x1, 0x3, + 0x2, 0x3, 0x1, 0x1b, 0x1, 0x4, 0x1, 0x4, 0x1, 0x2, 0x2, 0x8, 0x4, 0x4, 0x1, + 0x21, 0x10, 0x4, 0x1, 0x83, 0x4b, 0x1a, 0x87, 0x46, 0x2f, 0x2, 0x1, 0x3, + 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x8, 0x3, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x1, + 0xc, 0x26, 0x1, 0x1, 0x5, 0x1, 0xa0, 0x79, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, + 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4e, 0x3, 0xa0, 0x53, + 0x5, 0x7, 0xc, 0x5, 0x84, 0x29, 0x1a, 0x84, 0xcd, 0x28, 0xa0, 0xcf, 0xca, + 0x1a, 0x1a, 0x7, 0x1, 0x12, 0x1a, 0x1a, 0x1a, 0x4, 0x1, 0x1, 0x1, 0x7, 0x1, + 0xb, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1c, 0x1c, 0x19, 0x1, 0x6, 0x1a, 0x19, + 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1a, 0x19, 0x1, 0x6, 0x1, + 0x1]; _T Zl = [0xa0, 0x20, 0x28, 0x1]; _T Zp = [0xa0, 0x20, 0x29, 0x1]; _T Radical = [0xa0, 0x2e, 0x80, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6]; -_T Extender = [0x80, 0xb7, 0x1, 0x82, 0x18, 0x2, 0x83, 0x6e, 0x1, 0x81, 0xb9, 0x1, 0x86, 0x4b, 0x1, 0x7f, 0x1, 0x89, 0x43, 0x1, 0x38, 0x1, 0x82, 0x63, 0x1, 0x81, 0x8e, 0x1, 0x44, 0x1, 0x93, 0x89, 0x1, 0x2b, 0x5, 0x67, 0x2, 0x5d, 0x3, 0xa0, 0x6f, 0x16, 0x1, 0x85, 0xf6, 0x1, 0x83, 0xc2, 0x1, 0x80, 0xa0, 0x1, 0x6c, 0x1, 0x15, 0x2, 0xa0, 0x54, 0x7b, 0x1]; -_T Co = [0xa0, 0xe0, 0x0, 0x99, 0x0, 0xae, 0x7, 0x0, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe]; -_T Unified_Ideograph = [0xa0, 0x34, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0xa0, 0x5a, 0x41, 0x2, 0x1, 0x1, 0x1, 0x2, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x3, 0xa1, 0x5, 0xd6, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde]; -_T Pc = [0x5f, 0x1, 0x9f, 0xdf, 0x2, 0x13, 0x1, 0xa0, 0xdd, 0xde, 0x2, 0x18, 0x3, 0x80, 0xef, 0x1]; +_T Extender = [ + 0x80, 0xb7, 0x1, 0x82, 0x18, 0x2, 0x83, 0x6e, 0x1, 0x81, 0xb9, 0x1, 0x86, + 0x4b, 0x1, 0x7f, 0x1, 0x89, 0x43, 0x1, 0x38, 0x1, 0x82, 0x63, 0x1, 0x81, + 0x8e, 0x1, 0x44, 0x1, 0x93, 0x89, 0x1, 0x2b, 0x5, 0x67, 0x2, 0x5d, 0x3, + 0xa0, 0x6f, 0x16, 0x1, 0x85, 0xf6, 0x1, 0x83, 0xc2, 0x1, 0x80, 0xa0, 0x1, + 0x6c, 0x1, 0x15, 0x2, 0xa0, 0x54, 0x7b, 0x1 +]; +_T Co = [0xa0, 0xe0, 0x0, 0x99, 0x0, 0xae, 0x7, 0x0, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, + 0xfe]; +_T Unified_Ideograph = [ + 0xa0, 0x34, 0x0, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0xa0, 0x5a, 0x41, + 0x2, 0x1, 0x1, 0x1, 0x2, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x3, 0xa1, 0x5, + 0xd6, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde +]; +_T Pc = [0x5f, 0x1, 0x9f, 0xdf, 0x2, 0x13, 0x1, 0xa0, 0xdd, 0xde, 0x2, 0x18, 0x3, 0x80, + 0xef, 0x1]; _T Cs = [0xa0, 0xd8, 0x0, 0x88, 0x0]; -_T Noncharacter_Code_Point = [0xa0, 0xfd, 0xd0, 0x20, 0x82, 0xe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe]; -_T Uppercase = [0x41, 0x1a, 0x65, 0x17, 0x1, 0x7, 0x21, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x3, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x21, 0x1, 0x1, 0x1, 0x3, 0x1, 0xf, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x1, 0x11, 0x1, 0x9, 0x23, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x2, 0x1, 0x1, 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x26, 0x8b, 0x49, 0x26, 0x1, 0x1, 0x5, 0x1, 0x8d, 0x32, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, 0x8, 0x6, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x8, 0x48, 0x4, 0xc, 0x4, 0xc, 0x4, 0xc, 0x5, 0xb, 0x4, 0x81, 0x6, 0x1, 0x4, 0x1, 0x3, 0x3, 0x2, 0x3, 0x2, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x2, 0x4, 0xa, 0x2, 0x5, 0x1, 0x1a, 0x10, 0x13, 0x1, 0x83, 0x32, 0x1a, 0x87, 0x30, 0x2f, 0x31, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x2, 0x1, 0x8, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x1, 0x1, 0x1, 0x4, 0x1, 0xa0, 0x79, 0x4d, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa0, 0x57, 0x76, 0x1a, 0x84, 0xc5, 0x28, 0xa0, 0xcf, 0xd8, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0x8, 0x1a, 0x1a, 0x1a, 0x2, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1b, 0x2, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1e, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x1]; +_T Noncharacter_Code_Point = [ + 0xa0, 0xfd, 0xd0, 0x20, 0x82, 0xe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, + 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, + 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, + 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, + 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, + 0xfe, 0x2, 0xa0, 0xff, 0xfe +]; +_T Uppercase = [0x41, 0x1a, 0x65, 0x17, 0x1, 0x7, 0x21, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x3, + 0x2, 0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x2, 0x4, 0x1, 0x2, 0x1, 0x3, 0x3, 0x2, + 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, + 0x3, 0x1, 0x1, 0x1, 0x2, 0x3, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x7, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x4, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x21, 0x1, 0x1, 0x1, 0x3, 0x1, + 0xf, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x2, 0x1, 0x11, 0x1, 0x9, 0x23, 0x1, + 0x2, 0x3, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x5, 0x1, 0x2, + 0x1, 0x1, 0x2, 0x2, 0x33, 0x30, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0xa, 0x26, 0x8b, 0x49, 0x26, 0x1, 0x1, 0x5, 0x1, 0x8d, 0x32, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x9, 0x8, 0x8, 0x6, 0xa, 0x8, 0x8, 0x8, + 0x8, 0x6, 0xb, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x8, 0x8, 0x48, 0x4, + 0xc, 0x4, 0xc, 0x4, 0xc, 0x5, 0xb, 0x4, 0x81, 0x6, 0x1, 0x4, 0x1, 0x3, + 0x3, 0x2, 0x3, 0x2, 0x1, 0x3, 0x5, 0x6, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, + 0x2, 0x4, 0xa, 0x2, 0x5, 0x1, 0x1a, 0x10, 0x13, 0x1, 0x83, 0x32, 0x1a, + 0x87, 0x30, 0x2f, 0x31, 0x1, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x4, 0x1, 0x1, 0x2, 0x1, 0x8, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x8, 0x1, 0x1, 0x1, 0x4, 0x1, 0xa0, 0x79, 0x4d, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x13, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0x8b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x4, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, + 0xd, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xa0, 0x57, + 0x76, 0x1a, 0x84, 0xc5, 0x28, 0xa0, 0xcf, 0xd8, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0x8, 0x1a, + 0x1a, 0x1a, 0x2, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1b, 0x2, 0x1, 0x4, 0x1, + 0x5, 0x1, 0x1, 0x3, 0x7, 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1e, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, 0x21, 0x19, + 0x21, 0x1]; _T IDS_Trinary_Operator = [0xa0, 0x2f, 0xf2, 0x2]; -_T Logical_Order_Exception = [0x8e, 0x40, 0x5, 0x7b, 0x5, 0xa0, 0x9b, 0xf0, 0x2, 0x2, 0x1, 0x1, 0x2]; -_T Pi = [0x80, 0xab, 0x1, 0x9f, 0x6c, 0x1, 0x2, 0x2, 0x2, 0x1, 0x19, 0x1, 0x8d, 0xc8, 0x1, 0x1, 0x1, 0x4, 0x1, 0x2, 0x1, 0xf, 0x1, 0x3, 0x1]; -_T Soft_Dotted = [0x69, 0x2, 0x80, 0xc4, 0x1, 0x81, 0x19, 0x1, 0x1e, 0x1, 0x34, 0x1, 0x14, 0x1, 0x81, 0x40, 0x1, 0x62, 0x1, 0x1, 0x1, 0x99, 0x9, 0x1, 0x33, 0x1, 0xd, 0x1, 0x3, 0x1, 0x80, 0x84, 0x1, 0x80, 0x9d, 0x1, 0x81, 0xa5, 0x1, 0x80, 0xd6, 0x2, 0x8b, 0x32, 0x1, 0xa1, 0xa7, 0xa5, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2]; -_T Po = [0x21, 0x3, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0xa, 0x2, 0x3, 0x2, 0x1b, 0x1, 0x44, 0x1, 0x5, 0x1, 0xe, 0x2, 0x7, 0x1, 0x82, 0xbe, 0x1, 0x8, 0x1, 0x81, 0xd2, 0x6, 0x29, 0x1, 0x36, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2c, 0x2, 0x14, 0x2, 0x1, 0x2, 0xd, 0x1, 0x2, 0x2, 0x4a, 0x4, 0x66, 0x1, 0x2b, 0xe, 0x80, 0xe9, 0x3, 0x36, 0xf, 0x1f, 0x1, 0x81, 0x5, 0x2, 0xa, 0x1, 0x81, 0x7f, 0x1, 0x83, 0x3, 0x1, 0x5a, 0x1, 0xa, 0x2, 0x80, 0xa8, 0xf, 0x1, 0x1, 0x70, 0x1, 0x4a, 0x5, 0x4, 0x2, 0x6f, 0x6, 0x80, 0xab, 0x1, 0x82, 0x64, 0x9, 0x83, 0x4, 0x2, 0x7c, 0x3, 0x47, 0x2, 0x80, 0x9d, 0x3, 0x1, 0x3, 0x25, 0x6, 0x1, 0x4, 0x81, 0x39, 0x2, 0x80, 0xd8, 0x2, 0x80, 0x80, 0x7, 0x1, 0x6, 0x80, 0xac, 0x7, 0x80, 0x9b, 0x4, 0x3b, 0x5, 0x3e, 0x2, 0x40, 0x8, 0xb, 0x1, 0x83, 0x42, 0x2, 0x8, 0x8, 0x8, 0x9, 0x2, 0x4, 0x2, 0x3, 0x3, 0xb, 0x1, 0x1, 0x1, 0xa, 0x8c, 0x9a, 0x4, 0x1, 0x2, 0x70, 0x1, 0x80, 0x8f, 0x2, 0x4, 0x3, 0x2, 0x1, 0x2, 0x9, 0x1, 0x2, 0x1, 0x1, 0x2, 0x2, 0xa, 0x5, 0x1, 0xa, 0x81, 0xc7, 0x3, 0x39, 0x1, 0x80, 0xbd, 0x1, 0xa0, 0x74, 0x2, 0x2, 0x81, 0xd, 0x3, 0x63, 0x1, 0xa, 0x1, 0x73, 0x6, 0x81, 0x7c, 0x4, 0x56, 0x2, 0x28, 0x3, 0x33, 0x2, 0x2f, 0x1, 0x61, 0xd, 0x10, 0x2, 0x7c, 0x4, 0x7e, 0x2, 0x10, 0x2, 0x80, 0xf9, 0x1, 0xa0, 0x52, 0x24, 0x7, 0x2, 0x1, 0x16, 0x1, 0x14, 0x2, 0x2, 0x4, 0x3, 0x3, 0x1, 0x4, 0x7, 0x3, 0x6, 0x1, 0x1, 0x2, 0x80, 0x95, 0x3, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0xa, 0x2, 0x3, 0x2, 0x1b, 0x1, 0x24, 0x1, 0x2, 0x2, 0x81, 0x9a, 0x3, 0x82, 0x9c, 0x1, 0x30, 0x1, 0x84, 0x86, 0x1, 0x80, 0xc7, 0x1, 0x1f, 0x1, 0x81, 0x10, 0x9, 0x26, 0x1, 0x80, 0xb9, 0x7, 0x85, 0x7, 0x7, 0x6d, 0x2, 0x1, 0x4, 0x7e, 0x4, 0x80, 0x81, 0x4, 0x92, 0xa7, 0x4]; -_T Cn = [0x83, 0x78, 0x2, 0x5, 0x5, 0x7, 0x1, 0x1, 0x1, 0x14, 0x1, 0x81, 0x85, 0x9, 0x26, 0x2, 0x7, 0x1, 0x27, 0x1, 0x2, 0x4, 0x1, 0x1, 0x37, 0x8, 0x1b, 0x5, 0x5, 0xb, 0x5, 0x1, 0x17, 0x1, 0x80, 0xf0, 0x1, 0x3c, 0x2, 0x65, 0xe, 0x3b, 0x5, 0x2e, 0x2, 0xf, 0x1, 0x1c, 0x2, 0x1, 0x41, 0x1, 0x1, 0xb, 0x37, 0x1b, 0x1, 0x78, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0x2, 0x16, 0x5, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x7, 0x4, 0x1, 0x1, 0x7, 0x10, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, 0x4, 0x2, 0xc, 0xf, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, 0x2, 0x12, 0xa, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0x15, 0x6, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, 0x2, 0x6, 0x4, 0x2, 0xa, 0x8, 0x8, 0x2, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, 0x2, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0x10, 0x3, 0x7, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x3, 0xc, 0x3a, 0x4, 0x1d, 0x25, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x1, 0x6, 0x2, 0xa, 0x2, 0x4, 0x20, 0x48, 0x1, 0x24, 0x4, 0x27, 0x1, 0x24, 0x1, 0xf, 0x1, 0xd, 0x25, 0x80, 0xc6, 0x1, 0x1, 0x5, 0x1, 0x2, 0x81, 0x79, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x2, 0x20, 0x3, 0x1a, 0x6, 0x55, 0xb, 0x82, 0x9d, 0x3, 0x51, 0xf, 0xd, 0x1, 0x7, 0xb, 0x17, 0x9, 0x14, 0xc, 0xd, 0x1, 0x3, 0x1, 0x2, 0xc, 0x5e, 0x2, 0xa, 0x6, 0xa, 0x6, 0xf, 0x1, 0xa, 0x6, 0x58, 0x8, 0x2b, 0x5, 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0xc, 0x4, 0x1, 0x3, 0x2a, 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x6, 0xb, 0x3, 0x3e, 0x2, 0x41, 0x1, 0x1d, 0x2, 0xb, 0x6, 0xa, 0x6, 0xe, 0x52, 0x4c, 0x4, 0x2d, 0x3, 0x74, 0x8, 0x3c, 0x3, 0xf, 0x3, 0x33, 0x40, 0x8, 0x8, 0x27, 0x9, 0x80, 0xe7, 0x15, 0x81, 0x1a, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0xf, 0x1, 0xe, 0x2, 0x6, 0x1, 0x13, 0x2, 0x3, 0x1, 0x9, 0x1, 0x65, 0x1, 0xc, 0x2, 0x1b, 0x1, 0xd, 0x3, 0x1b, 0x15, 0x21, 0xf, 0x80, 0x8a, 0x6, 0x82, 0x64, 0xc, 0x27, 0x19, 0xb, 0x15, 0x82, 0xa0, 0x1, 0x84, 0x4c, 0x3, 0xa, 0x80, 0xa6, 0x2f, 0x1, 0x2f, 0x1, 0x80, 0x94, 0x5, 0x2d, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x2, 0xe, 0x18, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x5c, 0x44, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x1a, 0xc, 0x4, 0x40, 0x1, 0x56, 0x2, 0x67, 0x5, 0x29, 0x3, 0x5e, 0x1, 0x2b, 0x5, 0x24, 0xc, 0x2f, 0x1, 0x80, 0xdf, 0x1, 0x9a, 0xb6, 0xa, 0xa0, 0x52, 0xd, 0x33, 0x84, 0x8d, 0x3, 0x37, 0x9, 0x81, 0x5c, 0x14, 0x58, 0x7, 0x59, 0x8, 0x80, 0x8f, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x34, 0x4, 0xa, 0x6, 0x38, 0x8, 0x45, 0x9, 0xc, 0x6, 0x1c, 0x4, 0x54, 0xb, 0x1e, 0x3, 0x4e, 0x1, 0xb, 0x4, 0x2, 0x20, 0x37, 0x9, 0xe, 0x2, 0xa, 0x2, 0x20, 0x4, 0x43, 0x18, 0x1c, 0xa, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x2e, 0x2, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0x4, 0xa0, 0x22, 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1a, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7c, 0x11, 0x81, 0x6d, 0x10, 0x40, 0x2, 0x36, 0x28, 0xe, 0x2, 0x1a, 0x6, 0x7, 0x9, 0x23, 0x1, 0x13, 0x1, 0x4, 0x4, 0x5, 0x1, 0x80, 0x87, 0x2, 0x1, 0x1, 0x80, 0xbe, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3, 0x3, 0x7, 0x1, 0x7, 0xa, 0x5, 0x2, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x5, 0x3, 0x4, 0x2d, 0x3, 0x54, 0x5, 0xc, 0x34, 0x2e, 0x80, 0x82, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x1, 0x4, 0xc, 0x1b, 0x35, 0x1e, 0x1, 0x25, 0x4, 0xe, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x1, 0x9, 0x80, 0xa0, 0x1c, 0x3, 0x1b, 0x5, 0x1, 0x40, 0x38, 0x6, 0x2, 0x40, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, 0x4, 0x3, 0x4, 0x9, 0x8, 0x9, 0x7, 0x20, 0x80, 0x80, 0x36, 0x3, 0x1d, 0x2, 0x1b, 0x5, 0x8, 0x80, 0x80, 0x49, 0x82, 0x17, 0x1f, 0x81, 0x81, 0x4e, 0x4, 0x1e, 0x10, 0x42, 0xe, 0x19, 0x7, 0xa, 0x6, 0x35, 0x1, 0xe, 0x3c, 0x49, 0x7, 0xa, 0x84, 0xa6, 0x38, 0x8, 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0xd, 0x4, 0x8b, 0x8c, 0x84, 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x10, 0x11, 0xa0, 0x40, 0x60, 0x2, 0x9f, 0xfe, 0x80, 0xf6, 0xa, 0x27, 0x2, 0x80, 0xb5, 0x22, 0x46, 0x80, 0xba, 0x57, 0x9, 0x12, 0x80, 0x8e, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2, 0x81, 0xe, 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, 0x2, 0xf, 0x1, 0xf, 0x20, 0xb, 0x5, 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1d, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74, 0x88, 0x8c, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e, 0xab, 0x5, 0xe3, 0x1, 0x1e, 0x60, 0x80, 0x80, 0x80, 0xf0, 0xa0, 0xfe, 0x10, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe]; -_T Ps = [0x28, 0x1, 0x32, 0x1, 0x1f, 0x1, 0x8e, 0xbe, 0x1, 0x1, 0x1, 0x87, 0x5e, 0x1, 0x89, 0x7e, 0x1, 0x3, 0x1, 0x26, 0x1, 0x37, 0x1, 0xf, 0x1, 0x82, 0x7a, 0x1, 0x1, 0x1, 0x1e, 0x1, 0x84, 0x3e, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x50, 0x1, 0x20, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x94, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x40, 0x1, 0x1, 0x1, 0x21, 0x1, 0x84, 0x25, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0xdf, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0xa0, 0xcd, 0x20, 0x1, 0x80, 0xd8, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x11, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0xaa, 0x1, 0x32, 0x1, 0x1f, 0x1, 0x3, 0x1, 0x2, 0x1]; +_T Logical_Order_Exception = [0x8e, 0x40, 0x5, 0x7b, 0x5, 0xa0, 0x9b, 0xf0, + 0x2, 0x2, 0x1, 0x1, 0x2]; +_T Pi = [0x80, 0xab, 0x1, 0x9f, 0x6c, 0x1, 0x2, 0x2, 0x2, 0x1, 0x19, 0x1, + 0x8d, 0xc8, 0x1, 0x1, 0x1, 0x4, 0x1, 0x2, 0x1, 0xf, 0x1, 0x3, 0x1]; +_T Soft_Dotted = [ + 0x69, 0x2, 0x80, 0xc4, 0x1, 0x81, 0x19, 0x1, 0x1e, 0x1, 0x34, 0x1, 0x14, + 0x1, 0x81, 0x40, 0x1, 0x62, 0x1, 0x1, 0x1, 0x99, 0x9, 0x1, 0x33, 0x1, + 0xd, 0x1, 0x3, 0x1, 0x80, 0x84, 0x1, 0x80, 0x9d, 0x1, 0x81, 0xa5, 0x1, + 0x80, 0xd6, 0x2, 0x8b, 0x32, 0x1, 0xa1, 0xa7, 0xa5, 0x2, 0x32, 0x2, 0x32, + 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, + 0x32, 0x2, 0x32, 0x2, 0x32, 0x2, 0x32, 0x2 +]; +_T Po = [0x21, 0x3, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, 0x2, 0xa, 0x2, 0x3, 0x2, + 0x1b, 0x1, 0x44, 0x1, 0x5, 0x1, 0xe, 0x2, 0x7, 0x1, 0x82, 0xbe, 0x1, 0x8, + 0x1, 0x81, 0xd2, 0x6, 0x29, 0x1, 0x36, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2c, + 0x2, 0x14, 0x2, 0x1, 0x2, 0xd, 0x1, 0x2, 0x2, 0x4a, 0x4, 0x66, 0x1, 0x2b, + 0xe, 0x80, 0xe9, 0x3, 0x36, 0xf, 0x1f, 0x1, 0x81, 0x5, 0x2, 0xa, 0x1, 0x81, + 0x7f, 0x1, 0x83, 0x3, 0x1, 0x5a, 0x1, 0xa, 0x2, 0x80, 0xa8, 0xf, 0x1, + 0x1, 0x70, 0x1, 0x4a, 0x5, 0x4, 0x2, 0x6f, 0x6, 0x80, 0xab, 0x1, 0x82, + 0x64, 0x9, 0x83, 0x4, 0x2, 0x7c, 0x3, 0x47, 0x2, 0x80, 0x9d, 0x3, 0x1, + 0x3, 0x25, 0x6, 0x1, 0x4, 0x81, 0x39, 0x2, 0x80, 0xd8, 0x2, 0x80, 0x80, + 0x7, 0x1, 0x6, 0x80, 0xac, 0x7, 0x80, 0x9b, 0x4, 0x3b, 0x5, 0x3e, 0x2, + 0x40, 0x8, 0xb, 0x1, 0x83, 0x42, 0x2, 0x8, 0x8, 0x8, 0x9, 0x2, 0x4, 0x2, + 0x3, 0x3, 0xb, 0x1, 0x1, 0x1, 0xa, 0x8c, 0x9a, 0x4, 0x1, 0x2, 0x70, 0x1, + 0x80, 0x8f, 0x2, 0x4, 0x3, 0x2, 0x1, 0x2, 0x9, 0x1, 0x2, 0x1, 0x1, 0x2, + 0x2, 0xa, 0x5, 0x1, 0xa, 0x81, 0xc7, 0x3, 0x39, 0x1, 0x80, 0xbd, 0x1, 0xa0, + 0x74, 0x2, 0x2, 0x81, 0xd, 0x3, 0x63, 0x1, 0xa, 0x1, 0x73, 0x6, 0x81, 0x7c, + 0x4, 0x56, 0x2, 0x28, 0x3, 0x33, 0x2, 0x2f, 0x1, 0x61, 0xd, 0x10, 0x2, + 0x7c, 0x4, 0x7e, 0x2, 0x10, 0x2, 0x80, 0xf9, 0x1, 0xa0, 0x52, 0x24, 0x7, + 0x2, 0x1, 0x16, 0x1, 0x14, 0x2, 0x2, 0x4, 0x3, 0x3, 0x1, 0x4, 0x7, 0x3, + 0x6, 0x1, 0x1, 0x2, 0x80, 0x95, 0x3, 0x1, 0x3, 0x2, 0x1, 0x1, 0x1, 0x1, + 0x2, 0xa, 0x2, 0x3, 0x2, 0x1b, 0x1, 0x24, 0x1, 0x2, 0x2, 0x81, 0x9a, 0x3, + 0x82, 0x9c, 0x1, 0x30, 0x1, 0x84, 0x86, 0x1, 0x80, 0xc7, 0x1, 0x1f, 0x1, + 0x81, 0x10, 0x9, 0x26, 0x1, 0x80, 0xb9, 0x7, 0x85, 0x7, 0x7, 0x6d, 0x2, + 0x1, 0x4, 0x7e, 0x4, 0x80, 0x81, 0x4, 0x92, 0xa7, 0x4]; +_T Cn = [0x83, 0x78, 0x2, 0x5, 0x5, 0x7, 0x1, 0x1, 0x1, 0x14, 0x1, 0x81, + 0x85, 0x9, 0x26, 0x2, 0x7, 0x1, 0x27, 0x1, 0x2, 0x4, 0x1, 0x1, 0x37, 0x8, + 0x1b, 0x5, 0x5, 0xb, 0x5, 0x1, 0x17, 0x1, 0x80, 0xf0, 0x1, 0x3c, 0x2, 0x65, + 0xe, 0x3b, 0x5, 0x2e, 0x2, 0xf, 0x1, 0x1c, 0x2, 0x1, 0x41, 0x1, 0x1, 0xb, + 0x37, 0x1b, 0x1, 0x78, 0x1, 0x7, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, + 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, + 0x2, 0x1, 0x5, 0x2, 0x16, 0x5, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, + 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, + 0x3, 0x1, 0x7, 0x4, 0x1, 0x1, 0x7, 0x10, 0xb, 0x3, 0x1, 0x9, 0x1, 0x3, + 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, + 0x2, 0x1, 0xf, 0x4, 0x2, 0xc, 0xf, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, + 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, + 0x2, 0x1, 0x5, 0x2, 0x12, 0xa, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, + 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, + 0x1, 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0x15, 0x6, 0x3, 0x1, 0x8, 0x1, 0x3, + 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, + 0x1, 0x2, 0x6, 0x4, 0x2, 0xa, 0x8, 0x8, 0x2, 0x2, 0x1, 0x8, 0x1, 0x3, + 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, + 0x7, 0x1, 0x1, 0x4, 0x2, 0xa, 0x1, 0x2, 0xf, 0x2, 0x1, 0x8, 0x1, 0x3, + 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0x10, + 0x3, 0x7, 0x2, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, + 0x3, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x3, 0xc, 0x3a, 0x4, 0x1d, + 0x25, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, + 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, + 0x1, 0x1, 0x6, 0x2, 0xa, 0x2, 0x4, 0x20, 0x48, 0x1, 0x24, 0x4, 0x27, 0x1, + 0x24, 0x1, 0xf, 0x1, 0xd, 0x25, 0x80, 0xc6, 0x1, 0x1, 0x5, 0x1, 0x2, + 0x81, 0x79, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, + 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, + 0x1, 0x4, 0x2, 0x43, 0x2, 0x20, 0x3, 0x1a, 0x6, 0x55, 0xb, 0x82, 0x9d, + 0x3, 0x51, 0xf, 0xd, 0x1, 0x7, 0xb, 0x17, 0x9, 0x14, 0xc, 0xd, 0x1, 0x3, + 0x1, 0x2, 0xc, 0x5e, 0x2, 0xa, 0x6, 0xa, 0x6, 0xf, 0x1, 0xa, 0x6, 0x58, + 0x8, 0x2b, 0x5, 0x46, 0xa, 0x1d, 0x3, 0xc, 0x4, 0xc, 0x4, 0x1, 0x3, 0x2a, + 0x2, 0x5, 0xb, 0x2c, 0x4, 0x1a, 0x6, 0xb, 0x3, 0x3e, 0x2, 0x41, 0x1, 0x1d, + 0x2, 0xb, 0x6, 0xa, 0x6, 0xe, 0x52, 0x4c, 0x4, 0x2d, 0x3, 0x74, 0x8, + 0x3c, 0x3, 0xf, 0x3, 0x33, 0x40, 0x8, 0x8, 0x27, 0x9, 0x80, 0xe7, 0x15, + 0x81, 0x1a, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0xf, 0x1, 0xe, 0x2, 0x6, 0x1, 0x13, + 0x2, 0x3, 0x1, 0x9, 0x1, 0x65, 0x1, 0xc, 0x2, 0x1b, 0x1, 0xd, 0x3, 0x1b, + 0x15, 0x21, 0xf, 0x80, 0x8a, 0x6, 0x82, 0x64, 0xc, 0x27, 0x19, 0xb, 0x15, + 0x82, 0xa0, 0x1, 0x84, 0x4c, 0x3, 0xa, 0x80, 0xa6, 0x2f, 0x1, 0x2f, 0x1, + 0x80, 0x94, 0x5, 0x2d, 0x1, 0x1, 0x5, 0x1, 0x2, 0x38, 0x7, 0x2, 0xe, 0x18, + 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, + 0x1, 0x7, 0x1, 0x5c, 0x44, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x1a, 0xc, + 0x4, 0x40, 0x1, 0x56, 0x2, 0x67, 0x5, 0x29, 0x3, 0x5e, 0x1, 0x2b, 0x5, + 0x24, 0xc, 0x2f, 0x1, 0x80, 0xdf, 0x1, 0x9a, 0xb6, 0xa, 0xa0, 0x52, 0xd, + 0x33, 0x84, 0x8d, 0x3, 0x37, 0x9, 0x81, 0x5c, 0x14, 0x58, 0x7, 0x59, 0x8, + 0x80, 0x8f, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x34, 0x4, 0xa, 0x6, 0x38, 0x8, + 0x45, 0x9, 0xc, 0x6, 0x1c, 0x4, 0x54, 0xb, 0x1e, 0x3, 0x4e, 0x1, 0xb, + 0x4, 0x2, 0x20, 0x37, 0x9, 0xe, 0x2, 0xa, 0x2, 0x20, 0x4, 0x43, 0x18, + 0x1c, 0xa, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7, 0x80, 0x91, 0x2e, + 0x2, 0xa, 0x6, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0x4, 0xa0, 0x22, + 0x6e, 0x2, 0x6a, 0x26, 0x7, 0xc, 0x5, 0x5, 0x1a, 0x1, 0x5, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x2, 0x1, 0x7c, 0x11, 0x81, 0x6d, 0x10, 0x40, 0x2, 0x36, 0x28, + 0xe, 0x2, 0x1a, 0x6, 0x7, 0x9, 0x23, 0x1, 0x13, 0x1, 0x4, 0x4, 0x5, 0x1, + 0x80, 0x87, 0x2, 0x1, 0x1, 0x80, 0xbe, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, + 0x3, 0x3, 0x7, 0x1, 0x7, 0xa, 0x5, 0x2, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, + 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b, 0x5, 0x3, 0x4, 0x2d, 0x3, 0x54, 0x5, + 0xc, 0x34, 0x2e, 0x80, 0x82, 0x1d, 0x3, 0x31, 0x2f, 0x1f, 0x1, 0x4, 0xc, + 0x1b, 0x35, 0x1e, 0x1, 0x25, 0x4, 0xe, 0x2a, 0x80, 0x9e, 0x2, 0xa, 0x83, + 0x56, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x17, 0x1, 0x9, + 0x80, 0xa0, 0x1c, 0x3, 0x1b, 0x5, 0x1, 0x40, 0x38, 0x6, 0x2, 0x40, 0x4, + 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, 0x4, 0x3, 0x4, 0x9, 0x8, 0x9, 0x7, + 0x20, 0x80, 0x80, 0x36, 0x3, 0x1d, 0x2, 0x1b, 0x5, 0x8, 0x80, 0x80, 0x49, + 0x82, 0x17, 0x1f, 0x81, 0x81, 0x4e, 0x4, 0x1e, 0x10, 0x42, 0xe, 0x19, + 0x7, 0xa, 0x6, 0x35, 0x1, 0xe, 0x3c, 0x49, 0x7, 0xa, 0x84, 0xa6, 0x38, 0x8, + 0xa, 0x89, 0x36, 0x83, 0x6f, 0x80, 0x91, 0x63, 0xd, 0x4, 0x8b, 0x8c, 0x84, + 0x2f, 0xa0, 0x33, 0xd1, 0x82, 0x39, 0x84, 0xc7, 0x45, 0xb, 0x2f, 0x10, + 0x11, 0xa0, 0x40, 0x60, 0x2, 0x9f, 0xfe, 0x80, 0xf6, 0xa, 0x27, 0x2, 0x80, + 0xb5, 0x22, 0x46, 0x80, 0xba, 0x57, 0x9, 0x12, 0x80, 0x8e, 0x55, 0x1, + 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, + 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, + 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, 0x32, + 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, + 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, + 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, + 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2, 0x81, 0xe, + 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, 0x2, 0xf, 0x1, 0xf, 0x20, 0xb, 0x5, + 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1d, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, + 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, + 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, + 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74, 0x88, + 0x8c, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, + 0xe2, 0x82, 0x1e, 0xab, 0x5, 0xe3, 0x1, 0x1e, 0x60, 0x80, 0x80, 0x80, 0xf0, + 0xa0, 0xfe, 0x10, 0xa0, 0xff, 0xfe, 0x2, 0xa0, 0xff, 0xfe]; +_T Ps = [0x28, 0x1, 0x32, 0x1, 0x1f, 0x1, 0x8e, 0xbe, 0x1, 0x1, 0x1, 0x87, + 0x5e, 0x1, 0x89, 0x7e, 0x1, 0x3, 0x1, 0x26, 0x1, 0x37, 0x1, 0xf, 0x1, + 0x82, 0x7a, 0x1, 0x1, 0x1, 0x1e, 0x1, 0x84, 0x3e, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x50, 0x1, 0x20, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x94, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x40, 0x1, 0x1, 0x1, 0x21, 0x1, 0x84, 0x25, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x81, 0xdf, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0xa0, 0xcd, 0x20, 0x1, 0x80, + 0xd8, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x11, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0xaa, + 0x1, 0x32, 0x1, 0x1f, 0x1, 0x3, 0x1, 0x2, 0x1]; _T ASCII_Hex_Digit = [0x30, 0xa, 0x7, 0x6, 0x1a, 0x6]; -_T No = [0x80, 0xb2, 0x2, 0x5, 0x1, 0x2, 0x3, 0x89, 0x35, 0x6, 0x81, 0x78, 0x6, 0x78, 0x3, 0x80, 0x85, 0x7, 0x80, 0xf1, 0x6, 0x81, 0xb4, 0xa, 0x84, 0x35, 0x14, 0x84, 0x73, 0xa, 0x81, 0xe0, 0x1, 0x86, 0x95, 0x1, 0x3, 0x6, 0x6, 0xa, 0x80, 0xc6, 0x10, 0x29, 0x1, 0x82, 0xd6, 0x3c, 0x4e, 0x16, 0x82, 0x76, 0x1e, 0x85, 0x69, 0x1, 0x84, 0x94, 0x4, 0x80, 0x8a, 0xa, 0x1e, 0x8, 0x1, 0xf, 0x20, 0xa, 0x27, 0xf, 0xa0, 0x75, 0x70, 0x6, 0xa0, 0x58, 0xd1, 0x2d, 0x41, 0x4, 0x11, 0x1, 0x81, 0x95, 0x4, 0x85, 0x34, 0x8, 0x80, 0xb6, 0x6, 0x81, 0x24, 0x8, 0x35, 0x2, 0x80, 0xd9, 0x8, 0x18, 0x8, 0x82, 0xe0, 0x1f, 0x81, 0xd3, 0x14, 0xa0, 0xc2, 0xfa, 0x12, 0x9d, 0x8e, 0xb]; -_T Sm = [0x2b, 0x1, 0x10, 0x3, 0x3d, 0x1, 0x1, 0x1, 0x2d, 0x1, 0x4, 0x1, 0x25, 0x1, 0x1f, 0x1, 0x82, 0xfe, 0x1, 0x82, 0xf, 0x3, 0x9a, 0x3b, 0x1, 0xd, 0x1, 0x27, 0x3, 0xd, 0x3, 0x80, 0x8b, 0x1, 0x27, 0x5, 0x6, 0x1, 0x44, 0x5, 0x5, 0x2, 0x4, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7, 0x1, 0x1f, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1f, 0x81, 0xc, 0x20, 0x2, 0x5a, 0x1, 0x1e, 0x19, 0x28, 0x6, 0x81, 0xd5, 0x1, 0x9, 0x1, 0x36, 0x8, 0x6f, 0x1, 0x81, 0x50, 0x5, 0x2, 0x1f, 0xa, 0x10, 0x81, 0x0, 0x80, 0x83, 0x16, 0x3f, 0x4, 0x20, 0x2, 0x81, 0x2, 0x30, 0x15, 0x2, 0x6, 0xa0, 0xcf, 0xdc, 0x1, 0x83, 0x38, 0x1, 0x1, 0x3, 0x80, 0xa4, 0x1, 0x10, 0x3, 0x3d, 0x1, 0x1, 0x1, 0x80, 0x83, 0x1, 0x6, 0x4, 0xa0, 0xd6, 0xd4, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x97, 0x2c, 0x2]; -_T Other_Math = [0x5e, 0x1, 0x83, 0x71, 0x3, 0x2, 0x1, 0x1a, 0x2, 0x2, 0x2, 0x9c, 0x20, 0x1, 0x1b, 0x3, 0xb, 0x1, 0x20, 0x4, 0x18, 0x2, 0xe, 0x2, 0x41, 0xd, 0x4, 0x1, 0x3, 0x2, 0x4, 0x5, 0x12, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x3, 0x5, 0x6, 0x1, 0x3, 0x2, 0x2, 0x2, 0x1, 0x3, 0x1, 0x6, 0x3, 0x4, 0x5, 0x5, 0x4b, 0x5, 0x2, 0x4, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x5, 0x2, 0x2, 0x4, 0x2, 0x4, 0x12, 0x2, 0x2, 0x1, 0x1, 0x1, 0x7, 0x1, 0x1, 0x6, 0x2, 0x81, 0x22, 0x4, 0x80, 0xa8, 0x2, 0x1, 0x1, 0x18, 0x1, 0x11, 0x1, 0x81, 0xbd, 0x2, 0xc, 0x9, 0x5, 0x5, 0x5, 0x2, 0x2, 0x2, 0x3, 0x5, 0xe, 0x1, 0x1, 0x1, 0x2, 0x6, 0x18, 0x2, 0x39, 0x1, 0x1, 0x1, 0x1d, 0x4, 0x9, 0x2, 0x81, 0x56, 0x2, 0x1f, 0xa, 0x81, 0x93, 0x16, 0x3f, 0x4, 0x20, 0x2, 0xa0, 0xd4, 0x63, 0x1, 0x1, 0x1, 0x4, 0x1, 0x80, 0xd3, 0x1, 0x1, 0x1, 0xa0, 0xd4, 0xc1, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11]; +_T No = [0x80, 0xb2, 0x2, 0x5, 0x1, 0x2, 0x3, 0x89, 0x35, 0x6, 0x81, 0x78, 0x6, + 0x78, 0x3, 0x80, 0x85, 0x7, 0x80, 0xf1, 0x6, 0x81, 0xb4, 0xa, 0x84, 0x35, + 0x14, 0x84, 0x73, 0xa, 0x81, 0xe0, 0x1, 0x86, 0x95, 0x1, 0x3, 0x6, 0x6, + 0xa, 0x80, 0xc6, 0x10, 0x29, 0x1, 0x82, 0xd6, 0x3c, 0x4e, 0x16, 0x82, 0x76, + 0x1e, 0x85, 0x69, 0x1, 0x84, 0x94, 0x4, 0x80, 0x8a, 0xa, 0x1e, 0x8, 0x1, + 0xf, 0x20, 0xa, 0x27, 0xf, 0xa0, 0x75, 0x70, 0x6, 0xa0, 0x58, 0xd1, 0x2d, + 0x41, 0x4, 0x11, 0x1, 0x81, 0x95, 0x4, 0x85, 0x34, 0x8, 0x80, 0xb6, 0x6, + 0x81, 0x24, 0x8, 0x35, 0x2, 0x80, 0xd9, 0x8, 0x18, 0x8, 0x82, 0xe0, 0x1f, + 0x81, 0xd3, 0x14, 0xa0, 0xc2, 0xfa, 0x12, 0x9d, 0x8e, 0xb]; +_T Sm = [0x2b, 0x1, 0x10, 0x3, 0x3d, 0x1, 0x1, 0x1, 0x2d, 0x1, 0x4, 0x1, + 0x25, 0x1, 0x1f, 0x1, 0x82, 0xfe, 0x1, 0x82, 0xf, 0x3, 0x9a, 0x3b, 0x1, + 0xd, 0x1, 0x27, 0x3, 0xd, 0x3, 0x80, 0x8b, 0x1, 0x27, 0x5, 0x6, 0x1, 0x44, + 0x5, 0x5, 0x2, 0x4, 0x1, 0x2, 0x1, 0x2, 0x1, 0x7, 0x1, 0x1f, 0x2, 0x2, 0x1, + 0x1, 0x1, 0x1f, 0x81, 0xc, 0x20, 0x2, 0x5a, 0x1, 0x1e, 0x19, 0x28, 0x6, + 0x81, 0xd5, 0x1, 0x9, 0x1, 0x36, 0x8, 0x6f, 0x1, 0x81, 0x50, 0x5, 0x2, + 0x1f, 0xa, 0x10, 0x81, 0x0, 0x80, 0x83, 0x16, 0x3f, 0x4, 0x20, 0x2, 0x81, + 0x2, 0x30, 0x15, 0x2, 0x6, 0xa0, 0xcf, 0xdc, 0x1, 0x83, 0x38, 0x1, 0x1, + 0x3, 0x80, 0xa4, 0x1, 0x10, 0x3, 0x3d, 0x1, 0x1, 0x1, 0x80, 0x83, 0x1, 0x6, + 0x4, 0xa0, 0xd6, 0xd4, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, + 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x97, 0x2c, 0x2]; +_T Other_Math = [ + 0x5e, 0x1, 0x83, 0x71, 0x3, 0x2, 0x1, 0x1a, 0x2, 0x2, 0x2, 0x9c, 0x20, + 0x1, 0x1b, 0x3, 0xb, 0x1, 0x20, 0x4, 0x18, 0x2, 0xe, 0x2, 0x41, 0xd, 0x4, + 0x1, 0x3, 0x2, 0x4, 0x5, 0x12, 0x1, 0x4, 0x1, 0x2, 0xa, 0x1, 0x1, 0x3, + 0x5, 0x6, 0x1, 0x3, 0x2, 0x2, 0x2, 0x1, 0x3, 0x1, 0x6, 0x3, 0x4, 0x5, + 0x5, 0x4b, 0x5, 0x2, 0x4, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x1, 0x5, 0x2, + 0x2, 0x4, 0x2, 0x4, 0x12, 0x2, 0x2, 0x1, 0x1, 0x1, 0x7, 0x1, 0x1, 0x6, 0x2, + 0x81, 0x22, 0x4, 0x80, 0xa8, 0x2, 0x1, 0x1, 0x18, 0x1, 0x11, 0x1, 0x81, + 0xbd, 0x2, 0xc, 0x9, 0x5, 0x5, 0x5, 0x2, 0x2, 0x2, 0x3, 0x5, 0xe, 0x1, + 0x1, 0x1, 0x2, 0x6, 0x18, 0x2, 0x39, 0x1, 0x1, 0x1, 0x1d, 0x4, 0x9, 0x2, + 0x81, 0x56, 0x2, 0x1f, 0xa, 0x81, 0x93, 0x16, 0x3f, 0x4, 0x20, 0x2, 0xa0, + 0xd4, 0x63, 0x1, 0x1, 0x1, 0x4, 0x1, 0x80, 0xd3, 0x1, 0x1, 0x1, 0xa0, 0xd4, + 0xc1, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, + 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, + 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x19, 0x1, + 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, 0x1, 0x1f, 0x1, 0x19, + 0x1, 0x1f, 0x1, 0x19, 0x1, 0x8, 0x2, 0x32, 0x96, 0x0, 0x4, 0x1, 0x1b, 0x1, + 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, + 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, + 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11 +]; _T Join_Control = [0xa0, 0x20, 0xc, 0x2]; -_T Cf = [0x80, 0xad, 0x1, 0x85, 0x52, 0x5, 0x17, 0x1, 0x80, 0xc0, 0x1, 0x31, 0x1, 0x90, 0xfe, 0x1, 0x87, 0xfc, 0x5, 0x1a, 0x5, 0x31, 0x5, 0x1, 0xa, 0xa0, 0xde, 0x8f, 0x1, 0x80, 0xf9, 0x3, 0x90, 0xc1, 0x1, 0xa0, 0xc0, 0xb5, 0x8, 0xac, 0x2e, 0x86, 0x1, 0x1e, 0x60]; -_T Ideographic = [0xa0, 0x30, 0x6, 0x2, 0x19, 0x9, 0xe, 0x3, 0x83, 0xc5, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0xa0, 0x59, 0x33, 0x81, 0x6e, 0x2, 0x6a, 0xa1, 0x5, 0x26, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; -_T Sc = [0x24, 0x1, 0x7d, 0x4, 0x84, 0xe9, 0x1, 0x7b, 0x1, 0x83, 0xe6, 0x2, 0x7, 0x1, 0x80, 0xf5, 0x1, 0x81, 0x7, 0x1, 0x82, 0x45, 0x1, 0x89, 0x9b, 0x1, 0x88, 0xc4, 0x1b, 0xa0, 0x87, 0x7d, 0x1, 0xa0, 0x55, 0xc3, 0x1, 0x6c, 0x1, 0x80, 0x9a, 0x1, 0x80, 0xdb, 0x2, 0x3, 0x2]; -_T Nd = [0x30, 0xa, 0x86, 0x26, 0xa, 0x80, 0x86, 0xa, 0x80, 0xc6, 0xa, 0x81, 0x9c, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x80, 0xe0, 0xa, 0x76, 0xa, 0x46, 0xa, 0x81, 0x16, 0xa, 0x46, 0xa, 0x87, 0x46, 0xa, 0x26, 0xa, 0x81, 0x2c, 0xa, 0x80, 0x80, 0xa, 0x80, 0xa6, 0xa, 0x6, 0xa, 0x80, 0xb6, 0xa, 0x56, 0xa, 0x80, 0x86, 0xa, 0x6, 0xa, 0xa0, 0x89, 0xc6, 0xa, 0x82, 0xa6, 0xa, 0x26, 0xa, 0x80, 0xc6, 0xa, 0x76, 0xa, 0x81, 0x96, 0xa, 0xa0, 0x53, 0x16, 0xa, 0x85, 0x86, 0xa, 0x8b, 0xbc, 0xa, 0x80, 0x80, 0xa, 0x3c, 0xa, 0x80, 0x90, 0xa, 0x84, 0xe6, 0xa, 0xa0, 0xc1, 0x4, 0x32]; -_T Default_Ignorable_Code_Point = [0x80, 0xad, 0x1, 0x82, 0xa1, 0x1, 0x82, 0xcc, 0x1, 0x8b, 0x42, 0x2, 0x86, 0x53, 0x2, 0x55, 0x4, 0x87, 0xfc, 0x5, 0x1a, 0x5, 0x31, 0x10, 0x90, 0xf4, 0x1, 0xa0, 0xcc, 0x9b, 0x10, 0x80, 0xef, 0x1, 0x80, 0xa0, 0x1, 0x4f, 0x9, 0xa0, 0xd1, 0x7a, 0x8, 0xac, 0x2e, 0x85, 0x90, 0x0]; -_T Other_ID_Continue = [0x80, 0xb7, 0x1, 0x82, 0xcf, 0x1, 0x8f, 0xe1, 0x9, 0x86, 0x68, 0x1]; -_T Pd = [0x2d, 0x1, 0x85, 0x5c, 0x1, 0x33, 0x1, 0x8e, 0x41, 0x1, 0x84, 0x5, 0x1, 0x88, 0x9, 0x6, 0x8e, 0x1, 0x1, 0x2, 0x1, 0x1f, 0x2, 0x81, 0xe0, 0x1, 0x13, 0x1, 0x6f, 0x1, 0xa0, 0xcd, 0x90, 0x2, 0x25, 0x1, 0xa, 0x1, 0x80, 0xa9, 0x1]; -_T Deprecated = [0x81, 0x49, 0x1, 0x85, 0x29, 0x1, 0x89, 0x3, 0x1, 0x1, 0x1, 0x88, 0x29, 0x2, 0x88, 0xc5, 0x6, 0x82, 0xb9, 0x2, 0xad, 0xdc, 0xd6, 0x1, 0x1e, 0x60]; -_T Grapheme_Extend = [0x83, 0x0, 0x70, 0x81, 0x13, 0x7, 0x81, 0x7, 0x2d, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0x1, 0x48, 0xb, 0x30, 0x15, 0x10, 0x1, 0x65, 0x7, 0x2, 0x6, 0x2, 0x2, 0x1, 0x4, 0x23, 0x1, 0x1e, 0x1b, 0x5b, 0xb, 0x3a, 0x9, 0x22, 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x5, 0x2b, 0x3, 0x80, 0x88, 0x1b, 0x1, 0x3, 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, 0x1d, 0x1, 0x3a, 0x1, 0x1, 0x1, 0x2, 0x4, 0x8, 0x1, 0x9, 0x1, 0xa, 0x2, 0x1d, 0x2, 0x39, 0x1, 0x4, 0x2, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x1e, 0x2, 0x3, 0x1, 0xb, 0x2, 0x39, 0x1, 0x4, 0x5, 0x1, 0x2, 0x4, 0x1, 0x14, 0x2, 0x1d, 0x1, 0x3a, 0x1, 0x1, 0x2, 0x1, 0x4, 0x8, 0x1, 0x8, 0x2, 0xa, 0x2, 0x1e, 0x1, 0x3b, 0x1, 0x1, 0x1, 0xc, 0x1, 0x9, 0x1, 0x66, 0x3, 0x5, 0x3, 0x1, 0x4, 0x7, 0x2, 0xb, 0x2, 0x58, 0x1, 0x2, 0x1, 0x2, 0x1, 0x3, 0x1, 0x5, 0x2, 0x7, 0x2, 0xb, 0x2, 0x5a, 0x1, 0x2, 0x4, 0x8, 0x1, 0x9, 0x1, 0xa, 0x2, 0x66, 0x1, 0x4, 0x1, 0x2, 0x3, 0x1, 0x1, 0x8, 0x1, 0x51, 0x1, 0x2, 0x7, 0xc, 0x8, 0x62, 0x1, 0x2, 0x6, 0x1, 0x2, 0xb, 0x6, 0x4a, 0x2, 0x1b, 0x1, 0x1, 0x1, 0x1, 0x1, 0x37, 0xe, 0x1, 0x5, 0x1, 0x2, 0x5, 0xb, 0x1, 0x24, 0x9, 0x1, 0x66, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, 0x82, 0xbf, 0x3, 0x83, 0xb2, 0x3, 0x1d, 0x3, 0x1d, 0x2, 0x1e, 0x2, 0x40, 0x2, 0x1, 0x7, 0x8, 0x1, 0x2, 0xb, 0x9, 0x1, 0x2d, 0x3, 0x80, 0x9b, 0x1, 0x76, 0x3, 0x4, 0x2, 0x9, 0x1, 0x6, 0x3, 0x80, 0xdb, 0x2, 0x2, 0x1, 0x3a, 0x1, 0x1, 0x7, 0x1, 0x1, 0x1, 0x1, 0x2, 0x8, 0x6, 0xa, 0x2, 0x1, 0x80, 0x80, 0x4, 0x30, 0x1, 0x1, 0x5, 0x1, 0x1, 0x5, 0x1, 0x28, 0x9, 0xc, 0x2, 0x20, 0x4, 0x2, 0x2, 0x1, 0x1, 0x3a, 0x1, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x3a, 0x8, 0x2, 0x2, 0x80, 0x98, 0x3, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x80, 0xcb, 0x27, 0x15, 0x4, 0x82, 0xc, 0x2, 0x80, 0xc2, 0x21, 0x8b, 0xfe, 0x3, 0x80, 0x8d, 0x1, 0x60, 0x20, 0x82, 0x2a, 0x6, 0x69, 0x2, 0xa0, 0x75, 0xd4, 0x4, 0x1, 0xa, 0x21, 0x1, 0x50, 0x2, 0x81, 0x10, 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, 0x80, 0x9d, 0x1, 0x1b, 0x12, 0x34, 0x8, 0x19, 0xb, 0x2e, 0x3, 0x30, 0x1, 0x2, 0x4, 0x2, 0x1, 0x6c, 0x6, 0x2, 0x2, 0x2, 0x2, 0xc, 0x1, 0x8, 0x1, 0x63, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x2a, 0x2, 0x8, 0x1, 0x80, 0xee, 0x1, 0x2, 0x1, 0x4, 0x1, 0xa0, 0x4f, 0x30, 0x1, 0x82, 0xe1, 0x10, 0x10, 0x7, 0x81, 0x77, 0x2, 0x82, 0x5d, 0x1, 0x88, 0x3, 0x3, 0x1, 0x2, 0x5, 0x4, 0x28, 0x3, 0x4, 0x1, 0x85, 0xc1, 0x1, 0x36, 0xf, 0x39, 0x2, 0x31, 0x4, 0x2, 0x2, 0x45, 0x3, 0x24, 0x5, 0x1, 0x8, 0x4b, 0x2, 0x34, 0x9, 0x84, 0xec, 0x1, 0x1, 0x1, 0x2, 0x6, 0x1, 0x1, 0xa0, 0x58, 0xd7, 0x4, 0xa0, 0x61, 0xd2, 0x1, 0x1, 0x3, 0x4, 0x5, 0x8, 0x8, 0x2, 0x7, 0x1e, 0x4, 0x80, 0x94, 0x3, 0xac, 0x2e, 0xbb, 0x80, 0xf0]; -_T Hyphen = [0x2d, 0x1, 0x7f, 0x1, 0x84, 0xdc, 0x1, 0x92, 0x7b, 0x1, 0x88, 0x9, 0x2, 0x8e, 0x5, 0x1, 0x82, 0xe3, 0x1, 0xa0, 0xcd, 0x67, 0x1, 0x80, 0xa9, 0x1, 0x57, 0x1]; -_T Pe = [0x29, 0x1, 0x33, 0x1, 0x1f, 0x1, 0x8e, 0xbd, 0x1, 0x1, 0x1, 0x87, 0x5e, 0x1, 0x89, 0xa9, 0x1, 0x37, 0x1, 0xf, 0x1, 0x82, 0x7a, 0x1, 0x1, 0x1, 0x1e, 0x1, 0x84, 0x3e, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x50, 0x1, 0x20, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0x94, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x40, 0x1, 0x1, 0x1, 0x21, 0x1, 0x84, 0x25, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0xdf, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0xa0, 0xcd, 0x1f, 0x1, 0x80, 0xd8, 0x1, 0x1d, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x11, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0xaa, 0x1, 0x33, 0x1, 0x1f, 0x1, 0x2, 0x1, 0x2, 0x1]; +_T Cf = [0x80, 0xad, 0x1, 0x85, 0x52, 0x5, 0x17, 0x1, 0x80, 0xc0, 0x1, 0x31, + 0x1, 0x90, 0xfe, 0x1, 0x87, 0xfc, 0x5, 0x1a, 0x5, 0x31, 0x5, 0x1, 0xa, + 0xa0, 0xde, 0x8f, 0x1, 0x80, 0xf9, 0x3, 0x90, 0xc1, 0x1, 0xa0, 0xc0, 0xb5, + 0x8, 0xac, 0x2e, 0x86, 0x1, 0x1e, 0x60]; +_T Ideographic = [ + 0xa0, 0x30, 0x6, 0x2, 0x19, 0x9, 0xe, 0x3, 0x83, 0xc5, 0x99, 0xb6, 0x4a, + 0xa0, 0x51, 0xcd, 0xa0, 0x59, 0x33, 0x81, 0x6e, 0x2, 0x6a, 0xa1, 0x5, 0x26, + 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e +]; +_T Sc = [0x24, 0x1, 0x7d, 0x4, 0x84, 0xe9, 0x1, 0x7b, 0x1, 0x83, 0xe6, 0x2, + 0x7, 0x1, 0x80, 0xf5, 0x1, 0x81, 0x7, 0x1, 0x82, 0x45, 0x1, 0x89, 0x9b, + 0x1, 0x88, 0xc4, 0x1b, 0xa0, 0x87, 0x7d, 0x1, 0xa0, 0x55, 0xc3, 0x1, + 0x6c, 0x1, 0x80, 0x9a, 0x1, 0x80, 0xdb, 0x2, 0x3, 0x2]; +_T Nd = [0x30, 0xa, 0x86, 0x26, 0xa, 0x80, 0x86, 0xa, 0x80, 0xc6, 0xa, 0x81, + 0x9c, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, 0xa, 0x76, + 0xa, 0x76, 0xa, 0x76, 0xa, 0x80, 0xe0, 0xa, 0x76, 0xa, 0x46, 0xa, 0x81, + 0x16, 0xa, 0x46, 0xa, 0x87, 0x46, 0xa, 0x26, 0xa, 0x81, 0x2c, 0xa, 0x80, + 0x80, 0xa, 0x80, 0xa6, 0xa, 0x6, 0xa, 0x80, 0xb6, 0xa, 0x56, 0xa, 0x80, + 0x86, 0xa, 0x6, 0xa, 0xa0, 0x89, 0xc6, 0xa, 0x82, 0xa6, 0xa, 0x26, 0xa, + 0x80, 0xc6, 0xa, 0x76, 0xa, 0x81, 0x96, 0xa, 0xa0, 0x53, 0x16, 0xa, 0x85, + 0x86, 0xa, 0x8b, 0xbc, 0xa, 0x80, 0x80, 0xa, 0x3c, 0xa, 0x80, 0x90, 0xa, + 0x84, 0xe6, 0xa, 0xa0, 0xc1, 0x4, 0x32]; +_T Default_Ignorable_Code_Point = [ + 0x80, 0xad, 0x1, 0x82, 0xa1, 0x1, 0x82, 0xcc, 0x1, 0x8b, 0x42, 0x2, 0x86, + 0x53, 0x2, 0x55, 0x4, 0x87, 0xfc, 0x5, 0x1a, 0x5, 0x31, 0x10, 0x90, 0xf4, + 0x1, 0xa0, 0xcc, 0x9b, 0x10, 0x80, 0xef, 0x1, 0x80, 0xa0, 0x1, 0x4f, 0x9, + 0xa0, 0xd1, 0x7a, 0x8, 0xac, 0x2e, 0x85, 0x90, 0x0 +]; +_T Other_ID_Continue = [0x80, 0xb7, 0x1, 0x82, 0xcf, 0x1, 0x8f, 0xe1, 0x9, 0x86, 0x68, + 0x1]; +_T Pd = [0x2d, 0x1, 0x85, 0x5c, 0x1, 0x33, 0x1, 0x8e, 0x41, 0x1, 0x84, 0x5, + 0x1, 0x88, 0x9, 0x6, 0x8e, 0x1, 0x1, 0x2, 0x1, 0x1f, 0x2, 0x81, 0xe0, + 0x1, 0x13, 0x1, 0x6f, 0x1, 0xa0, 0xcd, 0x90, 0x2, 0x25, 0x1, 0xa, 0x1, 0x80, 0xa9, + 0x1]; +_T Deprecated = [ + 0x81, 0x49, 0x1, 0x85, 0x29, 0x1, 0x89, 0x3, 0x1, 0x1, 0x1, 0x88, 0x29, + 0x2, 0x88, 0xc5, 0x6, 0x82, 0xb9, 0x2, 0xad, 0xdc, 0xd6, 0x1, 0x1e, 0x60 +]; +_T Grapheme_Extend = [ + 0x83, 0x0, 0x70, 0x81, 0x13, 0x7, 0x81, 0x7, 0x2d, 0x1, 0x1, 0x1, 0x2, + 0x1, 0x2, 0x1, 0x1, 0x48, 0xb, 0x30, 0x15, 0x10, 0x1, 0x65, 0x7, 0x2, 0x6, + 0x2, 0x2, 0x1, 0x4, 0x23, 0x1, 0x1e, 0x1b, 0x5b, 0xb, 0x3a, 0x9, 0x22, + 0x4, 0x1, 0x9, 0x1, 0x3, 0x1, 0x5, 0x2b, 0x3, 0x80, 0x88, 0x1b, 0x1, 0x3, + 0x37, 0x1, 0x1, 0x1, 0x4, 0x8, 0x4, 0x1, 0x3, 0x7, 0xa, 0x2, 0x1d, 0x1, + 0x3a, 0x1, 0x1, 0x1, 0x2, 0x4, 0x8, 0x1, 0x9, 0x1, 0xa, 0x2, 0x1d, 0x2, + 0x39, 0x1, 0x4, 0x2, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x1e, 0x2, 0x3, 0x1, + 0xb, 0x2, 0x39, 0x1, 0x4, 0x5, 0x1, 0x2, 0x4, 0x1, 0x14, 0x2, 0x1d, 0x1, + 0x3a, 0x1, 0x1, 0x2, 0x1, 0x4, 0x8, 0x1, 0x8, 0x2, 0xa, 0x2, 0x1e, 0x1, + 0x3b, 0x1, 0x1, 0x1, 0xc, 0x1, 0x9, 0x1, 0x66, 0x3, 0x5, 0x3, 0x1, 0x4, + 0x7, 0x2, 0xb, 0x2, 0x58, 0x1, 0x2, 0x1, 0x2, 0x1, 0x3, 0x1, 0x5, 0x2, + 0x7, 0x2, 0xb, 0x2, 0x5a, 0x1, 0x2, 0x4, 0x8, 0x1, 0x9, 0x1, 0xa, 0x2, + 0x66, 0x1, 0x4, 0x1, 0x2, 0x3, 0x1, 0x1, 0x8, 0x1, 0x51, 0x1, 0x2, 0x7, + 0xc, 0x8, 0x62, 0x1, 0x2, 0x6, 0x1, 0x2, 0xb, 0x6, 0x4a, 0x2, 0x1b, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x37, 0xe, 0x1, 0x5, 0x1, 0x2, 0x5, 0xb, 0x1, 0x24, + 0x9, 0x1, 0x66, 0x4, 0x1, 0x6, 0x1, 0x2, 0x2, 0x2, 0x19, 0x2, 0x4, 0x3, + 0x10, 0x4, 0xd, 0x1, 0x2, 0x2, 0x6, 0x1, 0xf, 0x1, 0x82, 0xbf, 0x3, 0x83, + 0xb2, 0x3, 0x1d, 0x3, 0x1d, 0x2, 0x1e, 0x2, 0x40, 0x2, 0x1, 0x7, 0x8, 0x1, + 0x2, 0xb, 0x9, 0x1, 0x2d, 0x3, 0x80, 0x9b, 0x1, 0x76, 0x3, 0x4, 0x2, 0x9, + 0x1, 0x6, 0x3, 0x80, 0xdb, 0x2, 0x2, 0x1, 0x3a, 0x1, 0x1, 0x7, 0x1, 0x1, + 0x1, 0x1, 0x2, 0x8, 0x6, 0xa, 0x2, 0x1, 0x80, 0x80, 0x4, 0x30, 0x1, 0x1, + 0x5, 0x1, 0x1, 0x5, 0x1, 0x28, 0x9, 0xc, 0x2, 0x20, 0x4, 0x2, 0x2, 0x1, + 0x1, 0x3a, 0x1, 0x1, 0x2, 0x3, 0x1, 0x1, 0x3, 0x3a, 0x8, 0x2, 0x2, 0x80, + 0x98, 0x3, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x80, 0xcb, 0x27, + 0x15, 0x4, 0x82, 0xc, 0x2, 0x80, 0xc2, 0x21, 0x8b, 0xfe, 0x3, 0x80, 0x8d, + 0x1, 0x60, 0x20, 0x82, 0x2a, 0x6, 0x69, 0x2, 0xa0, 0x75, 0xd4, 0x4, 0x1, + 0xa, 0x21, 0x1, 0x50, 0x2, 0x81, 0x10, 0x1, 0x3, 0x1, 0x4, 0x1, 0x19, 0x2, + 0x80, 0x9d, 0x1, 0x1b, 0x12, 0x34, 0x8, 0x19, 0xb, 0x2e, 0x3, 0x30, 0x1, + 0x2, 0x4, 0x2, 0x1, 0x6c, 0x6, 0x2, 0x2, 0x2, 0x2, 0xc, 0x1, 0x8, 0x1, + 0x63, 0x1, 0x1, 0x3, 0x2, 0x2, 0x5, 0x2, 0x1, 0x1, 0x2a, 0x2, 0x8, 0x1, + 0x80, 0xee, 0x1, 0x2, 0x1, 0x4, 0x1, 0xa0, 0x4f, 0x30, 0x1, 0x82, 0xe1, + 0x10, 0x10, 0x7, 0x81, 0x77, 0x2, 0x82, 0x5d, 0x1, 0x88, 0x3, 0x3, 0x1, + 0x2, 0x5, 0x4, 0x28, 0x3, 0x4, 0x1, 0x85, 0xc1, 0x1, 0x36, 0xf, 0x39, + 0x2, 0x31, 0x4, 0x2, 0x2, 0x45, 0x3, 0x24, 0x5, 0x1, 0x8, 0x4b, 0x2, + 0x34, 0x9, 0x84, 0xec, 0x1, 0x1, 0x1, 0x2, 0x6, 0x1, 0x1, 0xa0, 0x58, 0xd7, + 0x4, 0xa0, 0x61, 0xd2, 0x1, 0x1, 0x3, 0x4, 0x5, 0x8, 0x8, 0x2, 0x7, 0x1e, + 0x4, 0x80, 0x94, 0x3, 0xac, 0x2e, 0xbb, 0x80, 0xf0 +]; +_T Hyphen = [0x2d, 0x1, 0x7f, 0x1, 0x84, 0xdc, 0x1, 0x92, 0x7b, 0x1, 0x88, 0x9, + 0x2, 0x8e, 0x5, 0x1, 0x82, 0xe3, 0x1, 0xa0, 0xcd, 0x67, 0x1, 0x80, 0xa9, 0x1, 0x57, + 0x1]; +_T Pe = [0x29, 0x1, 0x33, 0x1, 0x1f, 0x1, 0x8e, 0xbd, 0x1, 0x1, 0x1, 0x87, + 0x5e, 0x1, 0x89, 0xa9, 0x1, 0x37, 0x1, 0xf, 0x1, 0x82, 0x7a, 0x1, 0x1, 0x1, + 0x1e, 0x1, 0x84, 0x3e, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x50, 0x1, 0x20, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x81, 0x94, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x40, 0x1, 0x1, 0x1, + 0x21, 0x1, 0x84, 0x25, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x81, 0xdf, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x2, 0x2, 0xa0, 0xcd, 0x1f, 0x1, 0x80, 0xd8, 0x1, 0x1d, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x3, 0x1, 0x11, 0x1, 0x1, 0x1, 0x1, 0x1, 0x80, 0xaa, 0x1, 0x33, 0x1, 0x1f, + 0x1, 0x2, 0x1, 0x2, 0x1]; _U[] _tab = [ _U("Alphabetic", Alphabetic), _U("ASCII_Hex_Digit", ASCII_Hex_Digit), @@ -1858,12 +3331,20 @@ private alias _U = immutable(UnicodeProperty); static immutable: private alias _T = ubyte[]; _T Buhid = [0x97, 0x40, 0x14]; -_T Sinhala = [0x8d, 0x82, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, 0x7, 0x3, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x3]; +_T Sinhala = [0x8d, 0x82, 0x2, 0x1, 0x12, 0x3, 0x18, 0x1, 0x9, 0x1, 0x1, 0x2, + 0x7, 0x3, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x8, 0x12, 0x3]; _T Phags_Pa = [0xa0, 0xa8, 0x40, 0x38]; _T Old_Turkic = [0xa1, 0xc, 0x0, 0x49]; -_T Oriya = [0x8b, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, 0x2, 0x12]; +_T Oriya = [0x8b, 0x1, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, + 0x1, 0x5, 0x2, 0x9, 0x2, 0x2, 0x2, 0x3, 0x8, 0x2, 0x4, 0x2, 0x1, 0x5, 0x2, 0x12]; _T Thaana = [0x87, 0x80, 0x32]; -_T Inherited = [0x83, 0x0, 0x70, 0x81, 0x15, 0x2, 0x81, 0xc4, 0xb, 0x1a, 0x1, 0x82, 0xe0, 0x2, 0x93, 0x7d, 0x3, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x80, 0xcb, 0x27, 0x15, 0x4, 0x82, 0xc, 0x2, 0x80, 0xc2, 0x21, 0x8f, 0x39, 0x4, 0x6b, 0x2, 0xa0, 0xcd, 0x65, 0x10, 0x10, 0x7, 0x83, 0xd6, 0x1, 0xa0, 0xcf, 0x69, 0x3, 0x11, 0x8, 0x2, 0x7, 0x1e, 0x4, 0xac, 0x2f, 0x52, 0x80, 0xf0]; +_T Inherited = [ + 0x83, 0x0, 0x70, 0x81, 0x15, 0x2, 0x81, 0xc4, 0xb, 0x1a, 0x1, 0x82, 0xe0, + 0x2, 0x93, 0x7d, 0x3, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x80, 0xcb, + 0x27, 0x15, 0x4, 0x82, 0xc, 0x2, 0x80, 0xc2, 0x21, 0x8f, 0x39, 0x4, 0x6b, + 0x2, 0xa0, 0xcd, 0x65, 0x10, 0x10, 0x7, 0x83, 0xd6, 0x1, 0xa0, 0xcf, + 0x69, 0x3, 0x11, 0x8, 0x2, 0x7, 0x1e, 0x4, 0xac, 0x2f, 0x52, 0x80, 0xf0 +]; _T Sharada = [0xa1, 0x11, 0x80, 0x49, 0x7, 0xa]; _T Rejang = [0xa0, 0xa9, 0x30, 0x24, 0xb, 0x1]; _T Imperial_Aramaic = [0xa1, 0x8, 0x40, 0x16, 0x1, 0x9]; @@ -1873,16 +3354,26 @@ _T Bopomofo = [0x82, 0xea, 0x2, 0xa0, 0x2e, 0x19, 0x29, 0x72, 0x1b]; _T Deseret = [0xa1, 0x4, 0x0, 0x50]; _T Syloti_Nagri = [0xa0, 0xa8, 0x0, 0x2c]; _T Lycian = [0xa1, 0x2, 0x80, 0x1d]; -_T Linear_B = [0xa1, 0x0, 0x0, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, 0x2, 0xe, 0x22, 0x7b]; -_T Hebrew = [0x85, 0x91, 0x37, 0x8, 0x1b, 0x5, 0x5, 0xa0, 0xf5, 0x28, 0x1a, 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0xa]; +_T Linear_B = [0xa1, 0x0, 0x0, 0xc, 0x1, 0x1a, 0x1, 0x13, 0x1, 0x2, 0x1, 0xf, + 0x2, 0xe, 0x22, 0x7b]; +_T Hebrew = [0x85, 0x91, 0x37, 0x8, 0x1b, 0x5, 0x5, 0xa0, 0xf5, 0x28, 0x1a, + 0x1, 0x5, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2, 0x1, 0xa]; _T Saurashtra = [0xa0, 0xa8, 0x80, 0x45, 0x9, 0xc]; _T Avestan = [0xa1, 0xb, 0x0, 0x36, 0x3, 0x7]; -_T Ethiopic = [0x92, 0x0, 0x49, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x2, 0x20, 0x3, 0x1a, 0x99, 0xe6, 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0xa0, 0x7d, 0x22, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7]; +_T Ethiopic = [0x92, 0x0, 0x49, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, + 0x29, 0x1, 0x4, 0x2, 0x21, 0x1, 0x4, 0x2, 0x7, 0x1, 0x1, 0x1, 0x4, 0x2, + 0xf, 0x1, 0x39, 0x1, 0x4, 0x2, 0x43, 0x2, 0x20, 0x3, 0x1a, 0x99, 0xe6, + 0x17, 0x9, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x7, + 0x1, 0x7, 0xa0, 0x7d, 0x22, 0x6, 0x2, 0x6, 0x2, 0x6, 0x9, 0x7, 0x1, 0x7]; _T Braille = [0xa0, 0x28, 0x0, 0x81, 0x0]; _T Lisu = [0xa0, 0xa4, 0xd0, 0x30]; _T Samaritan = [0x88, 0x0, 0x2e, 0x2, 0xf]; _T Mongolian = [0x98, 0x0, 0x2, 0x2, 0x1, 0x1, 0x9, 0x1, 0xa, 0x6, 0x58, 0x8, 0x2b]; -_T Hangul = [0x91, 0x0, 0x81, 0x0, 0x9e, 0x2e, 0x2, 0x81, 0x1, 0x5e, 0x71, 0x1f, 0x41, 0x1f, 0xa0, 0x76, 0xe1, 0x1d, 0x82, 0x83, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, 0x31, 0xa0, 0x27, 0xa4, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3]; +_T Hangul = [ + 0x91, 0x0, 0x81, 0x0, 0x9e, 0x2e, 0x2, 0x81, 0x1, 0x5e, 0x71, 0x1f, 0x41, + 0x1f, 0xa0, 0x76, 0xe1, 0x1d, 0x82, 0x83, 0xa0, 0x2b, 0xa4, 0xc, 0x17, 0x4, + 0x31, 0xa0, 0x27, 0xa4, 0x1f, 0x3, 0x6, 0x2, 0x6, 0x2, 0x6, 0x2, 0x3 +]; _T Takri = [0xa1, 0x16, 0x80, 0x38, 0x8, 0xa]; _T Phoenician = [0xa1, 0x9, 0x0, 0x1c, 0x3, 0x1]; _T Vai = [0xa0, 0xa5, 0x0, 0x81, 0x2c]; @@ -1895,11 +3386,20 @@ _T Canadian_Aboriginal = [0x94, 0x0, 0x82, 0x80, 0x82, 0x30, 0x46]; _T Meetei_Mayek = [0xa0, 0xaa, 0xe0, 0x17, 0x80, 0xc9, 0x2e, 0x2, 0xa]; _T Balinese = [0x9b, 0x0, 0x4c, 0x4, 0x2d]; _T Kayah_Li = [0xa0, 0xa9, 0x0, 0x30]; -_T Kharoshthi = [0xa1, 0xa, 0x0, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, 0x4, 0x3, 0x4, 0x9, 0x8, 0x9]; +_T Kharoshthi = [0xa1, 0xa, 0x0, 0x4, 0x1, 0x2, 0x5, 0x8, 0x1, 0x3, 0x1, 0x1b, + 0x4, 0x3, 0x4, 0x9, 0x8, 0x9]; _T Lepcha = [0x9c, 0x0, 0x38, 0x3, 0xf, 0x3, 0x3]; _T New_Tai_Lue = [0x99, 0x80, 0x2c, 0x4, 0x1a, 0x6, 0xb, 0x3, 0x2]; _T Sora_Sompeng = [0xa1, 0x10, 0xd0, 0x19, 0x7, 0xa]; -_T Arabic = [0x86, 0x0, 0x5, 0x1, 0x6, 0x1, 0xe, 0x1, 0x1, 0x1, 0x1, 0x1, 0x20, 0x1, 0xa, 0xb, 0xa, 0xa, 0x6, 0x1, 0x6c, 0x1, 0x22, 0x50, 0x30, 0x81, 0x20, 0x1, 0x1, 0xb, 0x37, 0x1b, 0xa0, 0xf2, 0x51, 0x72, 0x11, 0x81, 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xd, 0x73, 0x5, 0x1, 0x80, 0x87, 0x8f, 0x63, 0x1f, 0xa0, 0xdf, 0x81, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2]; +_T Arabic = [0x86, 0x0, 0x5, 0x1, 0x6, 0x1, 0xe, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x20, 0x1, 0xa, 0xb, 0xa, 0xa, 0x6, 0x1, 0x6c, 0x1, 0x22, 0x50, 0x30, + 0x81, 0x20, 0x1, 0x1, 0xb, 0x37, 0x1b, 0xa0, 0xf2, 0x51, 0x72, 0x11, 0x81, + 0x6b, 0x12, 0x40, 0x2, 0x36, 0x28, 0xd, 0x73, 0x5, 0x1, 0x80, 0x87, 0x8f, + 0x63, 0x1f, 0xa0, 0xdf, 0x81, 0x4, 0x1, 0x1b, 0x1, 0x2, 0x1, 0x1, 0x2, + 0x1, 0x1, 0xa, 0x1, 0x4, 0x1, 0x1, 0x1, 0x1, 0x6, 0x1, 0x4, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x3, 0x1, 0x2, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x2, 0x4, 0x1, 0x7, 0x1, 0x4, 0x1, 0x4, + 0x1, 0x1, 0x1, 0xa, 0x1, 0x11, 0x5, 0x3, 0x1, 0x5, 0x1, 0x11, 0x34, 0x2]; _T Hanunoo = [0x97, 0x20, 0x15]; _T Lydian = [0xa1, 0x9, 0x20, 0x1a, 0x5, 0x1]; _T Tai_Viet = [0xa0, 0xaa, 0x80, 0x43, 0x18, 0x5]; @@ -1910,41 +3410,101 @@ _T Egyptian_Hieroglyphs = [0xa1, 0x30, 0x0, 0x84, 0x2f]; _T Khmer = [0x97, 0x80, 0x5e, 0x2, 0xa, 0x6, 0xa, 0x81, 0xe6, 0x20]; _T Ogham = [0x96, 0x80, 0x1d]; _T Gothic = [0xa1, 0x3, 0x30, 0x1b]; -_T Katakana = [0xa0, 0x30, 0xa1, 0x5a, 0x2, 0x3, 0x80, 0xf0, 0x10, 0x80, 0xd0, 0x2f, 0x1, 0x58, 0xa0, 0xcc, 0xe, 0xa, 0x1, 0x2d, 0xa0, 0xb0, 0x62, 0x1]; +_T Katakana = [ + 0xa0, 0x30, 0xa1, 0x5a, 0x2, 0x3, 0x80, 0xf0, 0x10, 0x80, 0xd0, 0x2f, 0x1, + 0x58, 0xa0, 0xcc, 0xe, 0xa, 0x1, 0x2d, 0xa0, 0xb0, 0x62, 0x1 +]; _T Miao = [0xa1, 0x6f, 0x0, 0x45, 0xb, 0x2f, 0x10, 0x11]; _T Meroitic_Hieroglyphs = [0xa1, 0x9, 0x80, 0x20]; _T Thai = [0x8e, 0x1, 0x3a, 0x5, 0x1c]; _T Cypriot = [0xa1, 0x8, 0x0, 0x6, 0x2, 0x1, 0x1, 0x2c, 0x1, 0x2, 0x3, 0x1, 0x2, 0x1]; _T Meroitic_Cursive = [0xa1, 0x9, 0xa0, 0x18, 0x6, 0x2]; -_T Gujarati = [0x8a, 0x81, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, 0x4, 0x2, 0xc]; -_T Lao = [0x8e, 0x81, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, 0x5, 0x1, 0x1, 0x1, 0x6, 0x2, 0xa, 0x2, 0x4]; -_T Georgian = [0x90, 0xa0, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x4, 0x9c, 0x0, 0x26, 0x1, 0x1, 0x5, 0x1]; +_T Gujarati = [0x8a, 0x81, 0x3, 0x1, 0x9, 0x1, 0x3, 0x1, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x5, 0x2, 0xa, 0x1, 0x3, 0x1, 0x3, 0x2, 0x1, 0xf, 0x4, 0x2, 0xc]; +_T Lao = [0x8e, 0x81, 0x2, 0x1, 0x1, 0x2, 0x2, 0x1, 0x1, 0x2, 0x1, 0x6, 0x4, + 0x1, 0x7, 0x1, 0x3, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x1, 0xd, 0x1, 0x3, 0x2, + 0x5, 0x1, 0x1, 0x1, 0x6, 0x2, 0xa, 0x2, 0x4]; +_T Georgian = [0x90, 0xa0, 0x26, 0x1, 0x1, 0x5, 0x1, 0x2, 0x2b, 0x1, 0x4, 0x9c, + 0x0, 0x26, 0x1, 0x1, 0x5, 0x1]; _T Osmanya = [0xa1, 0x4, 0x80, 0x1e, 0x2, 0xa]; _T Inscriptional_Pahlavi = [0xa1, 0xb, 0x60, 0x13, 0x5, 0x8]; _T Shavian = [0xa1, 0x4, 0x50, 0x30]; _T Carian = [0xa1, 0x2, 0xa0, 0x31]; _T Cherokee = [0x93, 0xa0, 0x55]; _T Mandaic = [0x88, 0x40, 0x1c, 0x2, 0x1]; -_T Han = [0xa0, 0x2e, 0x80, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x2f, 0x1, 0x1, 0x1, 0x19, 0x9, 0xe, 0x4, 0x83, 0xc4, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, 0xa0, 0x59, 0x33, 0x81, 0x6e, 0x2, 0x6a, 0xa1, 0x5, 0x26, 0xa0, 0xa6, 0xd7, 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; -_T Latin = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xf, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xc1, 0x27, 0x5, 0x9a, 0x1b, 0x26, 0x6, 0x31, 0x5, 0x4, 0x5, 0xd, 0x1, 0x46, 0x41, 0x81, 0x0, 0x81, 0x71, 0x1, 0xd, 0x1, 0x10, 0xd, 0x80, 0x8d, 0x2, 0x6, 0x1, 0x1b, 0x1, 0x11, 0x29, 0x8a, 0xd7, 0x20, 0xa0, 0x7a, 0xa2, 0x66, 0x3, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x8, 0xa0, 0x53, 0x0, 0x7, 0x84, 0x1a, 0x1a, 0x6, 0x1a]; +_T Han = [0xa0, 0x2e, 0x80, 0x1a, 0x1, 0x59, 0xc, 0x80, 0xd6, 0x2f, 0x1, 0x1, + 0x1, 0x19, 0x9, 0xe, 0x4, 0x83, 0xc4, 0x99, 0xb6, 0x4a, 0xa0, 0x51, 0xcd, + 0xa0, 0x59, 0x33, 0x81, 0x6e, 0x2, 0x6a, 0xa1, 0x5, 0x26, 0xa0, 0xa6, 0xd7, + 0x29, 0x90, 0x35, 0xb, 0x80, 0xde, 0xa0, 0x3f, 0xe2, 0x82, 0x1e]; +_T Latin = [0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xf, 0x1, 0x5, 0x17, 0x1, 0x1f, + 0x1, 0x81, 0xc1, 0x27, 0x5, 0x9a, 0x1b, 0x26, 0x6, 0x31, 0x5, 0x4, 0x5, + 0xd, 0x1, 0x46, 0x41, 0x81, 0x0, 0x81, 0x71, 0x1, 0xd, 0x1, 0x10, 0xd, + 0x80, 0x8d, 0x2, 0x6, 0x1, 0x1b, 0x1, 0x11, 0x29, 0x8a, 0xd7, 0x20, 0xa0, + 0x7a, 0xa2, 0x66, 0x3, 0x4, 0x1, 0x4, 0xc, 0xb, 0x4d, 0x8, 0xa0, 0x53, 0x0, + 0x7, 0x84, 0x1a, 0x1a, 0x6, 0x1a]; _T Limbu = [0x99, 0x0, 0x1d, 0x3, 0xc, 0x4, 0xc, 0x4, 0x1, 0x3, 0xc]; _T Ol_Chiki = [0x9c, 0x50, 0x30]; -_T Bengali = [0x89, 0x81, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0x2, 0x16]; +_T Bengali = [0x89, 0x81, 0x3, 0x1, 0x8, 0x2, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x1, 0x3, 0x4, 0x2, 0x9, 0x2, 0x2, 0x2, 0x4, 0x8, 0x1, 0x4, 0x2, 0x1, 0x5, 0x2, + 0x16]; _T Myanmar = [0x90, 0x0, 0x80, 0xa0, 0xa0, 0x99, 0xc0, 0x1c]; -_T Malayalam = [0x8d, 0x2, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, 0x1, 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0x10, 0x3, 0x7]; -_T Hiragana = [0xa0, 0x30, 0x41, 0x56, 0x6, 0x3, 0xa1, 0x7f, 0x61, 0x1, 0xa0, 0x41, 0xfe, 0x1]; -_T Kannada = [0x8c, 0x82, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, 0x2, 0xa, 0x1, 0x2]; -_T Armenian = [0x85, 0x31, 0x26, 0x2, 0x7, 0x1, 0x27, 0x2, 0x1, 0x4, 0x1, 0xa0, 0xf5, 0x83, 0x5]; -_T Common = [0x0, 0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xf, 0x1, 0x5, 0x17, 0x1, 0x1f, 0x1, 0x81, 0xc1, 0x27, 0x5, 0x5, 0x2, 0x14, 0x74, 0x1, 0x9, 0x1, 0x6, 0x1, 0x1, 0x1, 0x82, 0x1, 0x1, 0x80, 0x82, 0x1, 0xe, 0x1, 0x3, 0x1, 0x20, 0x1, 0x1f, 0xa, 0x73, 0x1, 0x82, 0x86, 0x2, 0x84, 0xd9, 0x1, 0x81, 0x95, 0x4, 0x81, 0x22, 0x1, 0x85, 0xef, 0x3, 0x47, 0x2, 0x80, 0xcb, 0x2, 0x1, 0x1, 0x84, 0xcd, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x2, 0x83, 0x9, 0xc, 0x2, 0x57, 0x1, 0xb, 0x3, 0xb, 0x1, 0xf, 0x11, 0x1b, 0x45, 0x26, 0x1, 0x3, 0x2, 0x6, 0x1, 0x1b, 0x1, 0x11, 0x29, 0x1, 0x6, 0x82, 0x64, 0xc, 0x27, 0x19, 0xb, 0x15, 0x82, 0xa0, 0x1, 0x80, 0xff, 0x81, 0x0, 0x82, 0x4d, 0x3, 0xa, 0x82, 0xa6, 0x3c, 0x81, 0xb4, 0xc, 0x4, 0x5, 0x1, 0x1, 0x1, 0x19, 0xf, 0x8, 0x4, 0x4, 0x5b, 0x2, 0x3, 0x1, 0x5a, 0x2, 0x80, 0x93, 0x10, 0x20, 0x24, 0x3c, 0x40, 0x1f, 0x51, 0x80, 0x88, 0x80, 0xa8, 0x99, 0xc0, 0x40, 0xa0, 0x59, 0x0, 0x22, 0x66, 0x3, 0x80, 0xa5, 0xa, 0x81, 0x95, 0x1, 0xa0, 0x53, 0x6e, 0x2, 0x80, 0xbd, 0x1, 0x12, 0xa, 0x16, 0x23, 0x1, 0x13, 0x1, 0x4, 0x80, 0x93, 0x1, 0x1, 0x20, 0x1a, 0x6, 0x1a, 0xb, 0xa, 0x1, 0x2d, 0x2, 0x40, 0x7, 0x1, 0x7, 0xa, 0x5, 0x81, 0x2, 0x3, 0x4, 0x2d, 0x3, 0x9, 0x50, 0xc, 0x34, 0x2d, 0xa0, 0xce, 0x3, 0x80, 0xf6, 0xa, 0x27, 0x2, 0x3e, 0x3, 0x11, 0x8, 0x2, 0x7, 0x1e, 0x4, 0x30, 0x81, 0x22, 0x57, 0x9, 0x12, 0x80, 0x8e, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, 0x81, 0x24, 0x2, 0x32, 0x98, 0x0, 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, 0x2, 0xf, 0x1, 0xf, 0x20, 0xb, 0x5, 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, 0x1a, 0x1, 0x2, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, 0x80, 0xae, 0x21, 0xf, 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, 0x15, 0x11, 0xf, 0x3f, 0x1, 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, 0x4, 0xc, 0x18, 0x80, 0x93, 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74, 0xac, 0x8, 0x8d, 0x1, 0x1e, 0x60]; +_T Malayalam = [0x8d, 0x2, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x29, 0x2, 0x8, 0x1, + 0x3, 0x1, 0x5, 0x8, 0x1, 0x8, 0x4, 0x2, 0x10, 0x3, 0x7]; +_T Hiragana = [0xa0, 0x30, 0x41, 0x56, 0x6, 0x3, 0xa1, 0x7f, 0x61, 0x1, 0xa0, 0x41, + 0xfe, 0x1]; +_T Kannada = [0x8c, 0x82, 0x2, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, + 0x5, 0x2, 0x9, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x7, 0x1, 0x1, 0x4, 0x2, 0xa, 0x1, + 0x2]; +_T Armenian = [0x85, 0x31, 0x26, 0x2, 0x7, 0x1, 0x27, 0x2, 0x1, 0x4, 0x1, 0xa0, 0xf5, + 0x83, 0x5]; +_T Common = [0x0, 0x41, 0x1a, 0x6, 0x1a, 0x2f, 0x1, 0xf, 0x1, 0x5, 0x17, 0x1, + 0x1f, 0x1, 0x81, 0xc1, 0x27, 0x5, 0x5, 0x2, 0x14, 0x74, 0x1, 0x9, 0x1, + 0x6, 0x1, 0x1, 0x1, 0x82, 0x1, 0x1, 0x80, 0x82, 0x1, 0xe, 0x1, 0x3, 0x1, + 0x20, 0x1, 0x1f, 0xa, 0x73, 0x1, 0x82, 0x86, 0x2, 0x84, 0xd9, 0x1, 0x81, + 0x95, 0x4, 0x81, 0x22, 0x1, 0x85, 0xef, 0x3, 0x47, 0x2, 0x80, 0xcb, 0x2, + 0x1, 0x1, 0x84, 0xcd, 0x1, 0xd, 0x1, 0x7, 0x4, 0x1, 0x6, 0x1, 0x2, 0x83, + 0x9, 0xc, 0x2, 0x57, 0x1, 0xb, 0x3, 0xb, 0x1, 0xf, 0x11, 0x1b, 0x45, 0x26, + 0x1, 0x3, 0x2, 0x6, 0x1, 0x1b, 0x1, 0x11, 0x29, 0x1, 0x6, 0x82, 0x64, 0xc, + 0x27, 0x19, 0xb, 0x15, 0x82, 0xa0, 0x1, 0x80, 0xff, 0x81, 0x0, 0x82, + 0x4d, 0x3, 0xa, 0x82, 0xa6, 0x3c, 0x81, 0xb4, 0xc, 0x4, 0x5, 0x1, 0x1, + 0x1, 0x19, 0xf, 0x8, 0x4, 0x4, 0x5b, 0x2, 0x3, 0x1, 0x5a, 0x2, 0x80, 0x93, + 0x10, 0x20, 0x24, 0x3c, 0x40, 0x1f, 0x51, 0x80, 0x88, 0x80, 0xa8, 0x99, + 0xc0, 0x40, 0xa0, 0x59, 0x0, 0x22, 0x66, 0x3, 0x80, 0xa5, 0xa, 0x81, 0x95, + 0x1, 0xa0, 0x53, 0x6e, 0x2, 0x80, 0xbd, 0x1, 0x12, 0xa, 0x16, 0x23, 0x1, + 0x13, 0x1, 0x4, 0x80, 0x93, 0x1, 0x1, 0x20, 0x1a, 0x6, 0x1a, 0xb, 0xa, 0x1, + 0x2d, 0x2, 0x40, 0x7, 0x1, 0x7, 0xa, 0x5, 0x81, 0x2, 0x3, 0x4, 0x2d, 0x3, + 0x9, 0x50, 0xc, 0x34, 0x2d, 0xa0, 0xce, 0x3, 0x80, 0xf6, 0xa, 0x27, 0x2, + 0x3e, 0x3, 0x11, 0x8, 0x2, 0x7, 0x1e, 0x4, 0x30, 0x81, 0x22, 0x57, 0x9, + 0x12, 0x80, 0x8e, 0x55, 0x1, 0x47, 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x2, 0x4, + 0x1, 0xc, 0x1, 0x1, 0x1, 0x7, 0x1, 0x41, 0x1, 0x4, 0x2, 0x8, 0x1, 0x7, + 0x1, 0x1c, 0x1, 0x4, 0x1, 0x5, 0x1, 0x1, 0x3, 0x7, 0x1, 0x81, 0x54, 0x2, + 0x81, 0x24, 0x2, 0x32, 0x98, 0x0, 0x2c, 0x4, 0x64, 0xc, 0xf, 0x2, 0xe, + 0x2, 0xf, 0x1, 0xf, 0x20, 0xb, 0x5, 0x1f, 0x1, 0x3c, 0x4, 0x2b, 0x4b, + 0x1a, 0x1, 0x2, 0xd, 0x2b, 0x5, 0x9, 0x7, 0x2, 0x80, 0xae, 0x21, 0xf, + 0x6, 0x1, 0x46, 0x3, 0x14, 0xc, 0x25, 0x1, 0x5, 0x15, 0x11, 0xf, 0x3f, 0x1, + 0x1, 0x1, 0x80, 0xb6, 0x1, 0x4, 0x3, 0x3e, 0x2, 0x4, 0xc, 0x18, 0x80, 0x93, + 0x46, 0x4, 0xb, 0x30, 0x46, 0x3a, 0x74, 0xac, 0x8, 0x8d, 0x1, 0x1e, 0x60]; _T Old_Italic = [0xa1, 0x3, 0x0, 0x1f, 0x1, 0x4]; _T Old_Persian = [0xa1, 0x3, 0xa0, 0x24, 0x4, 0xe]; -_T Greek = [0x83, 0x70, 0x4, 0x1, 0x3, 0x2, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x3f, 0xe, 0x10, 0x99, 0x26, 0x5, 0x32, 0x5, 0x4, 0x5, 0x54, 0x1, 0x81, 0x40, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0xf, 0x1, 0xe, 0x2, 0x6, 0x1, 0x13, 0x2, 0x3, 0x1, 0x9, 0x81, 0x27, 0x1, 0xa0, 0xe0, 0x19, 0x4b, 0xa0, 0xd0, 0x75, 0x46]; +_T Greek = [0x83, 0x70, 0x4, 0x1, 0x3, 0x2, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, + 0x3, 0x1, 0x1, 0x1, 0x14, 0x1, 0x3f, 0xe, 0x10, 0x99, 0x26, 0x5, 0x32, + 0x5, 0x4, 0x5, 0x54, 0x1, 0x81, 0x40, 0x16, 0x2, 0x6, 0x2, 0x26, 0x2, 0x6, + 0x2, 0x8, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1f, 0x2, 0x35, 0x1, 0xf, + 0x1, 0xe, 0x2, 0x6, 0x1, 0x13, 0x2, 0x3, 0x1, 0x9, 0x81, 0x27, 0x1, 0xa0, + 0xe0, 0x19, 0x4b, 0xa0, 0xd0, 0x75, 0x46]; _T Sundanese = [0x9b, 0x80, 0x40, 0x81, 0x0, 0x8]; _T Syriac = [0x87, 0x0, 0xe, 0x1, 0x3c, 0x2, 0x3]; -_T Gurmukhi = [0x8a, 0x1, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, 0x7, 0x4, 0x1, 0x1, 0x7, 0x10]; -_T Tibetan = [0x8f, 0x0, 0x48, 0x1, 0x24, 0x4, 0x27, 0x1, 0x24, 0x1, 0xf, 0x1, 0x7, 0x4, 0x2]; -_T Tamil = [0x8b, 0x82, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x4, 0x2, 0x1, 0x6, 0x1, 0xe, 0x15]; -_T Telugu = [0x8c, 0x1, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, 0x2, 0x6, 0x4, 0x2, 0xa, 0x8, 0x8]; +_T Gurmukhi = [0x8a, 0x1, 0x3, 0x1, 0x6, 0x4, 0x2, 0x2, 0x16, 0x1, 0x7, 0x1, + 0x2, 0x1, 0x2, 0x1, 0x2, 0x2, 0x1, 0x1, 0x5, 0x4, 0x2, 0x2, 0x3, 0x3, 0x1, + 0x7, 0x4, 0x1, 0x1, 0x7, 0x10]; +_T Tibetan = [0x8f, 0x0, 0x48, 0x1, 0x24, 0x4, 0x27, 0x1, 0x24, 0x1, 0xf, 0x1, 0x7, + 0x4, 0x2]; +_T Tamil = [0x8b, 0x82, 0x2, 0x1, 0x6, 0x3, 0x3, 0x1, 0x4, 0x3, 0x2, 0x1, 0x1, + 0x1, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0xc, 0x4, 0x5, 0x3, 0x3, 0x1, 0x4, 0x2, + 0x1, 0x6, 0x1, 0xe, 0x15]; +_T Telugu = [0x8c, 0x1, 0x3, 0x1, 0x8, 0x1, 0x3, 0x1, 0x17, 0x1, 0xa, 0x1, + 0x5, 0x3, 0x8, 0x1, 0x3, 0x1, 0x4, 0x7, 0x2, 0x1, 0x2, 0x6, 0x4, 0x2, 0xa, 0x8, + 0x8]; _T Inscriptional_Parthian = [0xa1, 0xb, 0x40, 0x16, 0x2, 0x8]; _T Nko = [0x87, 0xc0, 0x3b]; _T Javanese = [0xa0, 0xa9, 0x80, 0x4e, 0x2, 0xa, 0x4, 0x2]; @@ -1955,10 +3515,14 @@ _T Chakma = [0xa1, 0x11, 0x0, 0x35, 0x1, 0xe]; _T Ugaritic = [0xa1, 0x3, 0x80, 0x1e, 0x1, 0x1]; _T Tagalog = [0x97, 0x0, 0xd, 0x1, 0x7]; _T Tagbanwa = [0x97, 0x60, 0xd, 0x1, 0x3, 0x1, 0x2]; -_T Devanagari = [0x89, 0x0, 0x51, 0x2, 0x11, 0x2, 0x12, 0x1, 0x7, 0xa0, 0x9f, 0x60, 0x1c]; +_T Devanagari = [0x89, 0x0, 0x51, 0x2, 0x11, 0x2, 0x12, 0x1, 0x7, 0xa0, 0x9f, 0x60, + 0x1c]; _T Buginese = [0x9a, 0x0, 0x1c, 0x2, 0x2]; _T Cuneiform = [0xa1, 0x20, 0x0, 0x83, 0x6f, 0x80, 0x91, 0x63, 0xd, 0x4]; -_T Cyrillic = [0x84, 0x0, 0x80, 0x85, 0x2, 0x80, 0xa1, 0x98, 0x3, 0x1, 0x4c, 0x1, 0x90, 0x67, 0x20, 0xa0, 0x78, 0x40, 0x58, 0x7, 0x1]; +_T Cyrillic = [ + 0x84, 0x0, 0x80, 0x85, 0x2, 0x80, 0xa1, 0x98, 0x3, 0x1, 0x4c, 0x1, 0x90, + 0x67, 0x20, 0xa0, 0x78, 0x40, 0x58, 0x7, 0x1 +]; _U[] _tab = [ _U("Arabic", Arabic), _U("Armenian", Armenian), @@ -2071,10 +3635,132 @@ private alias _U = immutable(UnicodeProperty); @property static _U[] tab() pure nothrow @nogc { return _tab; } static immutable: private alias _T = ubyte[]; -_T LVT = [0xa0, 0xac, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b]; +_T LVT = [0xa0, 0xac, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b]; _T T = [0x91, 0xa8, 0x58, 0xa0, 0xc5, 0xcb, 0x31]; _T V = [0x91, 0x60, 0x48, 0xa0, 0xc6, 0x8, 0x17]; -_T LV = [0xa0, 0xac, 0x0, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1]; +_T LV = [0xa0, 0xac, 0x0, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, + 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, + 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1, 0x1b, 0x1]; _T L = [0x91, 0x0, 0x60, 0xa0, 0x98, 0x0, 0x1d]; _U[] _tab = [ _U("L", L), @@ -2091,37 +3777,37 @@ _U("Vowel_Jamo", V), } bool isFormatGen(dchar ch) @safe pure nothrow { - if(ch < 8288) + if (ch < 8288) { - if(ch < 1807) + if (ch < 1807) { - if(ch < 1564) + if (ch < 1564) { - if(ch == 173) return true; - if(ch < 1536) return false; - if(ch < 1541) return true; + if (ch == 173) return true; + if (ch < 1536) return false; + if (ch < 1541) return true; return false; } else if (ch < 1565) return true; else { - if(ch == 1757) return true; + if (ch == 1757) return true; return false; } } else if (ch < 1808) return true; else { - if(ch < 8203) + if (ch < 8203) { - if(ch == 6158) return true; + if (ch == 6158) return true; return false; } else if (ch < 8208) return true; else { - if(ch < 8234) return false; - if(ch < 8239) return true; + if (ch < 8234) return false; + if (ch < 8239) return true; return false; } } @@ -2129,36 +3815,36 @@ bool isFormatGen(dchar ch) @safe pure nothrow else if (ch < 8293) return true; else { - if(ch < 69821) + if (ch < 69_821) { - if(ch < 65279) + if (ch < 65_279) { - if(ch < 8294) return false; - if(ch < 8304) return true; + if (ch < 8294) return false; + if (ch < 8304) return true; return false; } - else if (ch < 65280) return true; + else if (ch < 65_280) return true; else { - if(ch < 65529) return false; - if(ch < 65532) return true; + if (ch < 65_529) return false; + if (ch < 65_532) return true; return false; } } - else if (ch < 69822) return true; + else if (ch < 69_822) return true; else { - if(ch < 917505) + if (ch < 917_505) { - if(ch < 119155) return false; - if(ch < 119163) return true; + if (ch < 119_155) return false; + if (ch < 119_163) return true; return false; } - else if (ch < 917506) return true; + else if (ch < 917_506) return true; else { - if(ch < 917536) return false; - if(ch < 917632) return true; + if (ch < 917_536) return false; + if (ch < 917_632) return true; return false; } } @@ -2167,34 +3853,34 @@ bool isFormatGen(dchar ch) @safe pure nothrow bool isControlGen(dchar ch) @safe pure nothrow { - if(ch < 32) return true; - if(ch < 127) return false; - if(ch < 160) return true; + if (ch < 32) return true; + if (ch < 127) return false; + if (ch < 160) return true; return false; } bool isSpaceGen(dchar ch) @safe pure nothrow { - if(ch < 160) + if (ch < 160) { - if(ch == 32) return true; + if (ch == 32) return true; return false; } else if (ch < 161) return true; else { - if(ch < 8239) + if (ch < 8239) { - if(ch == 5760) return true; - if(ch < 8192) return false; - if(ch < 8203) return true; + if (ch == 5760) return true; + if (ch < 8192) return false; + if (ch < 8203) return true; return false; } else if (ch < 8240) return true; else { - if(ch == 8287) return true; - if(ch == 12288) return true; + if (ch == 8287) return true; + if (ch == 12_288) return true; return false; } } @@ -2202,43 +3888,43 @@ bool isSpaceGen(dchar ch) @safe pure nothrow bool isWhiteGen(dchar ch) @safe pure nothrow @nogc { - if(ch < 133) + if (ch < 133) { - if(ch < 9) return false; - if(ch < 14) return true; - if(ch == 32) return true; + if (ch < 9) return false; + if (ch < 14) return true; + if (ch == 32) return true; return false; } else if (ch < 134) return true; else { - if(ch < 8232) + if (ch < 8232) { - if(ch < 5760) + if (ch < 5760) { - if(ch == 160) return true; + if (ch == 160) return true; return false; } else if (ch < 5761) return true; else { - if(ch < 8192) return false; - if(ch < 8203) return true; + if (ch < 8192) return false; + if (ch < 8203) return true; return false; } } else if (ch < 8234) return true; else { - if(ch < 8287) + if (ch < 8287) { - if(ch == 8239) return true; + if (ch == 8239) return true; return false; } else if (ch < 8288) return true; else { - if(ch == 12288) return true; + if (ch == 12_288) return true; return false; } } @@ -2247,126 +3933,7149 @@ bool isWhiteGen(dchar ch) @safe pure nothrow @nogc bool isHangL(dchar ch) @safe pure nothrow { - if(ch < 4352) return false; - if(ch < 4448) return true; - if(ch < 43360) return false; - if(ch < 43389) return true; + if (ch < 4352) return false; + if (ch < 4448) return true; + if (ch < 43_360) return false; + if (ch < 43_389) return true; return false; } bool isHangV(dchar ch) @safe pure nothrow { - if(ch < 4448) return false; - if(ch < 4520) return true; - if(ch < 55216) return false; - if(ch < 55239) return true; + if (ch < 4448) return false; + if (ch < 4520) return true; + if (ch < 55_216) return false; + if (ch < 55_239) return true; return false; } bool isHangT(dchar ch) @safe pure nothrow { - if(ch < 4520) return false; - if(ch < 4608) return true; - if(ch < 55243) return false; - if(ch < 55292) return true; + if (ch < 4520) return false; + if (ch < 4608) return true; + if (ch < 55_243) return false; + if (ch < 55_292) return true; return false; } - -static if(size_t.sizeof == 8) { +static if (size_t.sizeof == 8) +{ //1536 bytes -enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([ 0x0, 0x20, 0x40], [ 0x100, 0x80, 0x2000], [ 0x402030202020100, 0x206020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x3000300030003, 0x3000300030003, 0x5000400030003, 0x3000700030006, 0x3000800030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x9000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0xb0003000a0003, 0x3000c00030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0xe000d00030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x0, 0x7fffffe00000000, 0x420040000000000, 0xff7fffff80000000, 0x55aaaaaaaaaaaaaa, 0xd4aaaaaaaaaaab55, 0xe6512d2a4e243129, 0xaa29aaaab5555240, 0x93faaaaaaaaaaaaa, 0xffffffffffffaa85, 0x1ffffffffefffff, 0x1f00000003, 0x0, 0x3c8a000000000020, 0xfffff00000010000, 0x192faaaaaae37fff, 0xffff000000000000, 0xaaaaaaaaffffffff, 0xaaaaaaaaaaaaa802, 0xaaaaaaaaaaaad554, 0xaaaaaaaaaa, 0xfffffffe00000000, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaabfeaaaaa, 0xaaaaaaaaaaaaaaaa, 0xff00ff003f00ff, 0x3fff00ff00ff003f, 0x40df00ff00ff00ff, 0xdc00ff00cf00dc, 0x0, 0x8002000000000000, 0x1fff0000, 0x0, 0x321080000008c400, 0xffff0000000043c0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x3ffffff0000, 0x0, 0x0, 0x0, 0x0, 0xffff000000000000, 0x3fda15627fffffff, 0xaaaaaaaaaaaaaaaa, 0x8501aaaaaaaaa, 0x20bfffffffff, 0x0, 0x0, 0x0, 0x0, 0x2aaaaaaaaaaa, 0xaaaaaa, 0x0, 0xaaabaaa800000000, 0x95ffaaaaaaaaaaaa, 0x2aa000a50aa, 0x700000000000000, 0x0, 0x0, 0x0, 0x0, 0xf8007f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0xffffff0000000000, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffc000000, 0xffffdfc000, 0xebc000000ffffffc, 0xfffffc000000ffef, 0xffffffc000000f, 0xffffffc0000, 0xfc000000ffffffc0, 0xffffc000000fffff, 0xffffffc000000ff, 0xffffffc00000, 0x3ffffffc00, 0xf0000003f7fffffc, 0xffc000000fdfffff, 0xffff0000003f7fff, 0xfffffc000000fdff, 0xbf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([0x0, 0x20, 0x40], + [0x100, 0x80, 0x2000], [0x402030202020100, 0x206020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x3000300030003, 0x3000300030003, 0x5000400030003, + 0x3000700030006, 0x3000800030003, 0x3000300030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x9000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0x3000300030003, 0xb0003000a0003, + 0x3000c00030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0xe000d00030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x0, 0x7fffffe00000000, 0x420040000000000, 0xff7fffff80000000, + 0x55aaaaaaaaaaaaaa, 0xd4aaaaaaaaaaab55, 0xe6512d2a4e243129, + 0xaa29aaaab5555240, 0x93faaaaaaaaaaaaa, 0xffffffffffffaa85, + 0x1ffffffffefffff, 0x1f00000003, 0x0, 0x3c8a000000000020, + 0xfffff00000010000, 0x192faaaaaae37fff, 0xffff000000000000, + 0xaaaaaaaaffffffff, 0xaaaaaaaaaaaaa802, 0xaaaaaaaaaaaad554, + 0xaaaaaaaaaa, 0xfffffffe00000000, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0xaaaaaaaaaaaaaaaa, + 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaabfeaaaaa, 0xaaaaaaaaaaaaaaaa, + 0xff00ff003f00ff, 0x3fff00ff00ff003f, 0x40df00ff00ff00ff, + 0xdc00ff00cf00dc, 0x0, 0x8002000000000000, 0x1fff0000, 0x0, + 0x321080000008c400, 0xffff0000000043c0, 0x10, 0x0, 0x0, 0x0, 0x0, + 0x3ffffff0000, 0x0, 0x0, 0x0, 0x0, 0xffff000000000000, + 0x3fda15627fffffff, 0xaaaaaaaaaaaaaaaa, 0x8501aaaaaaaaa, + 0x20bfffffffff, 0x0, 0x0, 0x0, 0x0, 0x2aaaaaaaaaaa, 0xaaaaaa, 0x0, + 0xaaabaaa800000000, 0x95ffaaaaaaaaaaaa, 0x2aa000a50aa, + 0x700000000000000, 0x0, 0x0, 0x0, 0x0, 0xf8007f, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0xffffff0000000000, 0xffff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffc000000, 0xffffdfc000, + 0xebc000000ffffffc, 0xfffffc000000ffef, 0xffffffc000000f, + 0xffffffc0000, 0xfc000000ffffffc0, 0xffffc000000fffff, + 0xffffffc000000ff, 0xffffffc00000, 0x3ffffffc00, 0xf0000003f7fffffc, + 0xffc000000fdfffff, 0xffff0000003f7fff, 0xfffffc000000fdff, 0xbf7, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //1472 bytes -enum upperCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([ 0x0, 0x20, 0x40], [ 0x100, 0x80, 0x1e00], [ 0x402030202020100, 0x206020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x3000300030003, 0x3000300030004, 0x5000300030003, 0x3000700030006, 0x3000800030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x9000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0xa000300030003, 0x3000b00030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0xd000c00030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x0, 0x7fffffe, 0x0, 0x7f7fffff, 0xaa55555555555555, 0x2b555555555554aa, 0x11aed2d5b1dbced6, 0x55d255554aaaa490, 0x6c05555555555555, 0x557a, 0x0, 0x0, 0x0, 0x45000000000000, 0xffbfffed740, 0xe6905555551c8000, 0xffffffffffff, 0x5555555500000000, 0x5555555555555401, 0x5555555555552aab, 0xfffe005555555555, 0x7fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff00000000, 0x20bf, 0x0, 0x0, 0x0, 0x0, 0x5555555555555555, 0x5555555555555555, 0x5555555540155555, 0x5555555555555555, 0xff00ff003f00ff00, 0xff00aa003f00, 0xf00000000000000, 0xf001f000f000f00, 0x0, 0x0, 0x0, 0x0, 0xc00f3d503e273884, 0xffff00000020, 0x8, 0x0, 0x0, 0x0, 0xffc0000000000000, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x7fffffffffff, 0xc025ea9d00000000, 0x5555555555555555, 0x4280555555555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x155555555555, 0x555555, 0x0, 0x5554555400000000, 0x6a00555555555555, 0x55500052855, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffe00000000, 0x0, 0x0, 0x0, 0xffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfff0000003ffffff, 0xffffff0000003fff, 0x3fde64d0000003, 0x3ffffff0000, 0x7b0000001fdfe7b0, 0xfffff0000001fc5f, 0x3ffffff0000003f, 0x3ffffff00000, 0xf0000003ffffff00, 0xffff0000003fffff, 0xffffff00000003ff, 0x7fffffc00000001, 0x1ffffff0000000, 0x7fffffc00000, 0x1ffffff0000, 0x400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum upperCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([0x0, 0x20, 0x40], + [0x100, 0x80, 0x1e00], [0x402030202020100, 0x206020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x3000300030003, 0x3000300030004, 0x5000300030003, + 0x3000700030006, 0x3000800030003, 0x3000300030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x9000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0x3000300030003, 0xa000300030003, + 0x3000b00030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0xd000c00030003, 0x3000300030003, + 0x3000300030003, 0x3000300030003, 0x3000300030003, 0x3000300030003, + 0x0, 0x7fffffe, 0x0, 0x7f7fffff, 0xaa55555555555555, + 0x2b555555555554aa, 0x11aed2d5b1dbced6, 0x55d255554aaaa490, + 0x6c05555555555555, 0x557a, 0x0, 0x0, 0x0, 0x45000000000000, + 0xffbfffed740, 0xe6905555551c8000, 0xffffffffffff, 0x5555555500000000, + 0x5555555555555401, 0x5555555555552aab, 0xfffe005555555555, 0x7fffff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff00000000, 0x20bf, 0x0, 0x0, 0x0, 0x0, 0x5555555555555555, + 0x5555555555555555, 0x5555555540155555, 0x5555555555555555, + 0xff00ff003f00ff00, 0xff00aa003f00, 0xf00000000000000, + 0xf001f000f000f00, 0x0, 0x0, 0x0, 0x0, 0xc00f3d503e273884, + 0xffff00000020, 0x8, 0x0, 0x0, 0x0, 0xffc0000000000000, 0xffff, 0x0, + 0x0, 0x0, 0x0, 0x7fffffffffff, 0xc025ea9d00000000, 0x5555555555555555, + 0x4280555555555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x155555555555, 0x555555, + 0x0, 0x5554555400000000, 0x6a00555555555555, 0x55500052855, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x7fffffe00000000, 0x0, 0x0, 0x0, 0xffffffffff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfff0000003ffffff, 0xffffff0000003fff, + 0x3fde64d0000003, 0x3ffffff0000, 0x7b0000001fdfe7b0, + 0xfffff0000001fc5f, 0x3ffffff0000003f, 0x3ffffff00000, + 0xf0000003ffffff00, 0xffff0000003fffff, 0xffffff00000003ff, + 0x7fffffc00000001, 0x1ffffff0000000, 0x7fffffc00000, 0x1ffffff0000, + 0x400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //8704 bytes -enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xd00], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16001500000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, 0x1b001a00190018, 0x1f001e001d001c, 0x0, 0x2200210020, 0x0, 0x0, 0x24002300000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28002700260025, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b002a0000, 0x2e002d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30002f, 0x0, 0x0, 0x0, 0x0, 0x320031, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2400220020ffff, 0x2c002a00280026, 0x72f00320030002e, 0x3d003b00390037, 0x1b000430041003f, 0x4e004c004a0048, 0xffff005400520050, 0xffffffffffffffff, 0x2500230021ffff, 0x2d002b00290027, 0x73000330031002f, 0x3e003c003a0038, 0x1b1004400420040, 0x4f004d004b0049, 0xffff005500530051, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff043fffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xcc049800c800c6, 0xd500d3014904aa, 0xdd00db00d900d7, 0xe500e300e100df, 0xed00eb00e900e7, 0xffff00f300f100ef, 0xfb00f900f700f5, 0x6be010100ff00fd, 0xcd049900c900c7, 0xd600d4014a04ab, 0xde00dc00da00d8, 0xe600e400e200e0, 0xee00ec00ea00e8, 0xffff00f400f200f0, 0xfc00fa00f800f6, 0x1a80102010000fe, 0x118011701160115, 0x11e011d011c011b, 0x12401230120011f, 0x128012701260125, 0x12e012d012c012b, 0x13401330130012f, 0x138013701360135, 0x13c013b013a0139, 0x140013f013e013d, 0x144014301420141, 0x148014701460145, 0x14f014e014d014c, 0x1510150ffffffff, 0x155015401530152, 0x15801570156ffff, 0x15e015d015c0159, 0x16201610160015f, 0x166016501640163, 0x1690168ffff0167, 0x16d016c016b016a, 0x1710170016f016e, 0x175017401730172, 0x179017801770176, 0x17d017c017b017a, 0x1830182017f017e, 0x18b018a01870186, 0x1930192018f018e, 0x19b019a01970196, 0x1a301a2019f019e, 0x1a701a601a501a4, 0x1ac01ab01aa01a9, 0x1b201af01ae01ad, 0x1b601b501b3028b, 0x1bd01bb01ba01b9, 0x1c301c101bf01be, 0x1c701c5ffff01c4, 0x1cd01cc01cb01c9, 0x1d301d1023b01cf, 0xffff028301d601d5, 0x1db026901d901d7, 0x1e001df01de01dd, 0x1e501e301e201e1, 0xffffffff01e701e6, 0x1ed01eb01ea01e9, 0x1f301f101ef01ee, 0x1f701f601f501f4, 0xffffffff01fa01f9, 0x23dffff01fc01fb, 0xffffffffffffffff, 0x206020202010200, 0x20d020c02080207, 0x2110210020f020e, 0x215021402130212, 0x219021802170216, 0x21d021c021b021a, 0x220021f01c6021e, 0x226022502240223, 0x22a022902280227, 0x22e022d022c022b, 0x23202310230022f, 0x23802370236ffff, 0x23e023c023a0239, 0x24402430240023f, 0x248024702460245, 0x24c024b024a0249, 0x250024f024e024d, 0x254025302520251, 0x258025702560255, 0x25c025b025a0259, 0x260025f025e025d, 0x264026302620261, 0x268026702660265, 0x26c026bffff026a, 0x270026f026e026d, 0x274027302720271, 0x278027702760275, 0x27c027b027a0279, 0xffffffffffffffff, 0x281027fffffffff, 0x2d7028502840282, 0x28c028802870482, 0x2920291028f028d, 0x296029502940293, 0x29c029b02980297, 0x1b402b70466046a, 0x1c201c0ffff01bc, 0x1caffff01c8ffff, 0xffffffffffffffff, 0x1d0ffffffff01ce, 0xffff05fa0748ffff, 0x528ffff01d201d4, 0x1d8ffffffffffff, 0xffff01da02b3ffff, 0xffffffff01dcffff, 0xffffffffffffffff, 0xffffffff02a3ffff, 0x1e8ffffffff01e4, 0xffffffffffffffff, 0x1f201f0028e01ec, 0xffffffffffff0290, 0xffff01f8ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff083affff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x320031f031e031d, 0x3240323ffffffff, 0x3d5ffffffffffff, 0xffffffff03d903d7, 0xffffffffffffffff, 0xffff0329ffffffff, 0xffff0331032f032d, 0x3370335ffff0333, 0x33e03950339ffff, 0x347034503cc0340, 0x35403c2083b03c8, 0x35d035b03590440, 0x388ffff03c5039f, 0x36f039c036a0368, 0x378037607100371, 0x3320330032e032a, 0x33f0396033affff, 0x348034603cd0341, 0x35503c3083c03c9, 0x35e035c035a0441, 0x38a038903c603a0, 0x370039d036b0369, 0x379037707110372, 0x393033803360334, 0xffffffff03ca0397, 0x39403a1039effff, 0x3a703a603a303a2, 0x3ab03aa03a903a8, 0x3af03ae03ad03ac, 0x3b503b403b103b0, 0x3bd03bc03b903b8, 0x3c103c003bf03be, 0xffff03d103c703c4, 0x3cfffff03ce03cb, 0x3d403d303d203d0, 0x3da03d803d6ffff, 0x3e103df03dd03db, 0x3e903e703e503e3, 0x3f103ef03ed03eb, 0x3f903f703f503f3, 0x40103ff03fd03fb, 0x409040704050403, 0x411040f040d040b, 0x419041704150413, 0x421041f041d041b, 0x429042704250423, 0x431042f042d042b, 0x439043704350433, 0x402040003fe03fc, 0x40a040804060404, 0x4120410040e040c, 0x41a041804160414, 0x4220420041e041c, 0x42a042804260424, 0x4320430042e042c, 0x43a043804360434, 0x3e203e003de03dc, 0x3ea03e803e603e4, 0x3f203f003ee03ec, 0x3fa03f803f603f4, 0x453045204510450, 0x459045804570456, 0x4610460045d045c, 0x469046804650464, 0x4710470046d046c, 0x477047604730472, 0x47b047a04790478, 0x4810480047d047c, 0xffffffff04850484, 0xffffffffffffffff, 0x4950494ffffffff, 0x49b049a04970496, 0x49f049e049d049c, 0x4a704a604a304a2, 0x4ad04ac04a904a8, 0x4b304b204b104b0, 0x4b904b804b704b6, 0x4bf04be04bb04ba, 0x4c504c404c104c0, 0x4cd04cc04c904c8, 0x4d304d204cf04ce, 0x4d704d604d504d4, 0x4df04de04db04da, 0x4e704e604e304e2, 0x4f004ed04ec04ea, 0x4f804f504f404f1, 0x50004fd04fc04f9, 0x4eb050505040501, 0x50d050c050b050a, 0x5130512050f050e, 0x519051805170516, 0x51f051e051d051c, 0x525052405210520, 0x52b052a05270526, 0x52f052e052d052c, 0x537053605330532, 0x53d053c05390538, 0x5410540053f053e, 0x547054605430542, 0x54b054a05490548, 0x54f054e054d054c, 0x555055405510550, 0x559055805570556, 0x55d055c055b055a, 0x5630562055f055e, 0x567056605650564, 0x56b056a05690568, 0x5730572056f056e, 0x577057605750574, 0x57b057a05790578, 0xffffffffffffffff, 0xffffffffffffffff, 0x58405820580ffff, 0x58c058a05880586, 0x59405920590058e, 0x59c059a05980596, 0x5a405a205a0059e, 0x5ac05aa05a805a6, 0x5b405b205b005ae, 0x5bc05ba05b805b6, 0x5c405c205c005be, 0xffff05ca05c805c6, 0xffffffffffffffff, 0xffffffffffffffff, 0x58505830581ffff, 0x58d058b05890587, 0x59505930591058f, 0x59d059b05990597, 0x5a505a305a1059f, 0x5ad05ab05a905a7, 0x5b505b305b105af, 0x5bd05bb05b905b7, 0x5c505c305c105bf, 0xffff05cb05c905c7, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x8c008a00880086, 0x9400920090008e, 0x9c009a00980096, 0xa400a200a0009e, 0xac00aa00a800a6, 0xb400b200b000ae, 0xbc00ba00b800b6, 0xc400c200c000be, 0x4a000ca048e0486, 0x4c6ffff04b400ce, 0xffffffffffffffff, 0xffffffff0508ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff07e8ffff, 0xffffffff0454ffff, 0x5ff05fe05fd05fc, 0x605060406010600, 0x60b060a06090608, 0x6110610060f060e, 0x617061606130612, 0x61d061c06190618, 0x6210620061f061e, 0x627062606230622, 0x62b062a06290628, 0x62f062e062d062c, 0x635063406310630, 0x639063806370636, 0x63d063c063b063a, 0x6430642063f063e, 0x647064606450644, 0x64b064a06490648, 0x6510650064d064c, 0x655065406530652, 0x65d065c06590658, 0x6630662065f065e, 0x667066606650664, 0x66b066a06690668, 0x6710670066d066c, 0x675067406730672, 0x67a067906bc06bb, 0x680067f067c067b, 0x684068306820681, 0x688068706860685, 0x68e068d068a0689, 0x69206910690068f, 0x698069706960695, 0x69e069d069a0699, 0x6a206a106a0069f, 0x6a606a506a406a3, 0x6ac06ab06a806a7, 0x6b006af06ae06ad, 0x6b406b306b206b1, 0xffffffff06b606b5, 0x6bdffffffffffff, 0xffff06bfffffffff, 0x6c306c206c106c0, 0x6c906c806c506c4, 0x6cd06cc06cb06ca, 0x6d106d006cf06ce, 0x6d706d606d506d4, 0x6dd06dc06db06da, 0x6e106e006df06de, 0x6e506e406e306e2, 0x6eb06ea06e906e8, 0x6f106f006ef06ee, 0x6f506f406f306f2, 0x6f906f806f706f6, 0x6fd06fc06fb06fa, 0x701070006ff06fe, 0x705070407030702, 0x709070807070706, 0x70d070c070b070a, 0x7140713070f070e, 0x718071707160715, 0x71e071d071c071b, 0x72207210720071f, 0x726072507240723, 0x72a072907280727, 0x7330732072e072d, 0x73c073a07380736, 0x74407420740073e, 0x73d073b07390737, 0x74507430741073f, 0x750074e074c074a, 0xffffffff07540752, 0x751074f074d074b, 0xffffffff07550753, 0x76a076807660764, 0x7720770076e076c, 0x76b076907670765, 0x7730771076f076d, 0x78a078807860784, 0x7920790078e078c, 0x78b078907870785, 0x7930791078f078d, 0x7a207a0079e079c, 0xffffffff07a607a4, 0x7a307a1079f079d, 0xffffffff07a707a5, 0x7baffff07b6ffff, 0x7c2ffff07beffff, 0x7bbffff07b7ffff, 0x7c3ffff07bfffff, 0x7d607d407d207d0, 0x7de07dc07da07d8, 0x7d707d507d307d1, 0x7df07dd07db07d9, 0x840083e08360834, 0x84e084c08440842, 0x858085608620860, 0xffffffff08660864, 0x7fa07f807f607f4, 0x802080007fe07fc, 0x7fb07f907f707f5, 0x803080107ff07fd, 0x80e080c080a0808, 0x816081408120810, 0x80f080d080b0809, 0x817081508130811, 0x826082408220820, 0x82e082c082a0828, 0x827082508230821, 0x82f082d082b0829, 0x838ffff08320830, 0xffffffffffffffff, 0x837083508330831, 0xffff083dffff0839, 0x846ffffffffffff, 0xffffffffffffffff, 0x84508430841083f, 0xffffffffffff0847, 0xffffffff084a0848, 0xffffffffffffffff, 0x84f084d084b0849, 0xffffffffffffffff, 0xffffffff08540852, 0xffffffff085affff, 0x859085708550853, 0xffffffffffff085b, 0x868ffffffffffff, 0xffffffffffffffff, 0x867086508630861, 0xffffffffffff0869, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0712ffffffff, 0x14b0731ffffffff, 0xffffffffffffffff, 0xffff0530ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0531ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x18402af0180029f, 0x18c005e018802c1, 0x194006c01900064, 0x19c007e01980076, 0x18502b0018102a0, 0x18d005f018902c2, 0x195006d01910065, 0x19d007f01990077, 0x1b7ffffffffffff, 0xffffffffffff01b8, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x4d80444ffffffff, 0x4e0044c04dc0446, 0x4e8047404e4045e, 0x204ee02d3086a, 0x6e04f606c604f2, 0x10d04fe037a04fa, 0x51a0506061a0502, 0x4dd044704d90445, 0x4e5045f04e1044d, 0x2d4086b04e90475, 0x6c704f3000304ef, 0x37b04fb006f04f7, 0x61b0503010e04ff, 0xffffffff051b0507, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xa000800040000, 0x160010000e000c, 0x2bb001c001a0018, 0x5602e702d102c7, 0x66006200600058, 0x7800740070006a, 0x29900820080007c, 0x6020084000607e0, 0x5d005ce02a7057c, 0x1070105010305de, 0x1190113010f0109, 0xffff013101290121, 0xb000900050001, 0x170011000f000d, 0x2bc001d001b0019, 0x5702e802d202c8, 0x67006300610059, 0x7900750071006b, 0x29a00830081007d, 0x6030085000707e1, 0x5d105cf02a8057d, 0x1080106010405df, 0x11a01140110010a, 0xffff0132012a0122, 0x455052904c304c2, 0x45a0286028002a4, 0x46202aa02a9045b, 0x46b02b404670463, 0x2ba02b9ffff02b8, 0xffff02c002bfffff, 0xffffffffffffffff, 0x48302d8ffffffff, 0x489048802e202e1, 0x48d048c048b048a, 0x2fe02fd04910490, 0x30e030d03040303, 0x31a031903160315, 0x328032703260325, 0x6ed06ec02fc02fb, 0x383038203810380, 0x392039103870386, 0x3b303b203a503a4, 0x5cd05cc056d056c, 0x5ed05ec05db05da, 0x6570656060d060c, 0x6e706e6043e043d, 0x7830782072c072b, 0x694069307e307e2, 0x150014065b065a, 0x4bd04bc005d005c, 0x5d505d404d104d0, 0x511051001a101a0, 0x535053405230522, 0x553055205450544, 0x571057005610560, 0x15b015a057f057e, 0x3bb03ba037d037c, 0xffffffffffffffff, 0x5d2ffffffffffff, 0xffff05d905d805d3, 0x5e305e2ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x8d008b00890087, 0x9500930091008f, 0x9d009b00990097, 0xa500a300a1009f, 0xad00ab00a900a7, 0xb500b300b100af, 0xbd00bb00b900b7, 0xc500c300c100bf, 0x4a100cb048f0487, 0x4c7ffff04b500cf, 0xffffffffffffffff, 0xffffffff0509ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x5d705d602c402c3, 0x5e105e005dd05dc, 0x5e905e805e705e6, 0x5ef05ee05eb05ea, 0x5f505f405f105f0, 0x308030705f905f8, 0x625062406150614, 0x641064006330632, 0x6610660064f064e, 0x67e067d066f066e, 0x69c069b068c068b, 0xffffffff06aa06a9, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x69006807350734, 0x75f075e027e027d, 0x390038f07770776, 0x7b107b0001f001e, 0x2a202a107c707c6, 0x6b806b707e507e4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7590758ffffffff, 0x7610760075d075c, 0x2e002df02d602d5, 0x2ee02ed02e602e5, 0x7790778ffffffff, 0x7810780077d077c, 0x3140313030c030b, 0x322032103180317, 0x797079607950794, 0x79b079a07990798, 0x3850384037f037e, 0x7a907a8038e038d, 0x7ad07ac07ab07aa, 0x7b307b207af07ae, 0x7b907b807b507b4, 0x7c107c007bd07bc, 0x7c907c807c507c4, 0x7cf07ce07cd07cc, 0x47f047e046f046e, 0x4a504a404930492, 0xffffffffffffffff, 0xffffffffffffffff, 0x7e605150514ffff, 0x7eb07ea07e907e7, 0x7ef07ee07ed07ec, 0x7f307f207f107f0, 0x5f2ffffffffffff, 0xffffffff074905f3, 0x807080608050804, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x81b081a08190818, 0x81f081e081d081c, 0xffff05fb05f705f6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x75a02c50756ffff, 0x76202cf02cd02cb, 0x2e3071902db06d2, 0x2f107ca02e90448, 0x77a02f502f30774, 0x3050221077e02f9, 0xffff043b030f007a, 0xffffffffffffffff, 0x75b02c60757ffff, 0x76302d002ce02cc, 0x2e4071a02dc06d3, 0x2f207cb02ea0449, 0x77b02f602f40775, 0x3060222077f02fa, 0xffff043c0310007b, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x72005a085c0012, 0x11106b9032b0311, 0x2dd029d02ab05e4, 0x10b060602ef085e, 0x4ca028902d902a5, 0x2c902bd02b502ad, 0x30902f702eb0746, 0x38b02b10241031b, 0x442053a044a03b6, 0x85004ae06d8044e, 0x73005b085d0013, 0x11206ba032c0312, 0x2de029e02ac05e5, 0x10c060702f0085f, 0x4cb028a02da02a6, 0x2ca02be02b602ae, 0x30a02f802ec0747, 0x38c02b20242031c, 0x443053b044b03b7, 0x85104af06d9044f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, 0x100], + [0x100, 0x380, 0xd00], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, + 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x16001500000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x170000, 0x1b001a00190018, 0x1f001e001d001c, 0x0, + 0x2200210020, 0x0, 0x0, 0x24002300000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x28002700260025, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b002a0000, 0x2e002d002c, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30002f, 0x0, + 0x0, 0x0, 0x0, 0x320031, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2400220020ffff, 0x2c002a00280026, 0x72f00320030002e, + 0x3d003b00390037, 0x1b000430041003f, 0x4e004c004a0048, + 0xffff005400520050, 0xffffffffffffffff, 0x2500230021ffff, + 0x2d002b00290027, 0x73000330031002f, 0x3e003c003a0038, + 0x1b1004400420040, 0x4f004d004b0049, 0xffff005500530051, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff043fffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xcc049800c800c6, + 0xd500d3014904aa, 0xdd00db00d900d7, 0xe500e300e100df, 0xed00eb00e900e7, + 0xffff00f300f100ef, 0xfb00f900f700f5, 0x6be010100ff00fd, + 0xcd049900c900c7, 0xd600d4014a04ab, 0xde00dc00da00d8, 0xe600e400e200e0, + 0xee00ec00ea00e8, 0xffff00f400f200f0, 0xfc00fa00f800f6, + 0x1a80102010000fe, 0x118011701160115, 0x11e011d011c011b, + 0x12401230120011f, 0x128012701260125, 0x12e012d012c012b, + 0x13401330130012f, 0x138013701360135, 0x13c013b013a0139, + 0x140013f013e013d, 0x144014301420141, 0x148014701460145, + 0x14f014e014d014c, 0x1510150ffffffff, 0x155015401530152, + 0x15801570156ffff, 0x15e015d015c0159, 0x16201610160015f, + 0x166016501640163, 0x1690168ffff0167, 0x16d016c016b016a, + 0x1710170016f016e, 0x175017401730172, 0x179017801770176, + 0x17d017c017b017a, 0x1830182017f017e, 0x18b018a01870186, + 0x1930192018f018e, 0x19b019a01970196, 0x1a301a2019f019e, + 0x1a701a601a501a4, 0x1ac01ab01aa01a9, 0x1b201af01ae01ad, + 0x1b601b501b3028b, 0x1bd01bb01ba01b9, 0x1c301c101bf01be, + 0x1c701c5ffff01c4, 0x1cd01cc01cb01c9, 0x1d301d1023b01cf, + 0xffff028301d601d5, 0x1db026901d901d7, 0x1e001df01de01dd, + 0x1e501e301e201e1, 0xffffffff01e701e6, 0x1ed01eb01ea01e9, + 0x1f301f101ef01ee, 0x1f701f601f501f4, 0xffffffff01fa01f9, + 0x23dffff01fc01fb, 0xffffffffffffffff, 0x206020202010200, + 0x20d020c02080207, 0x2110210020f020e, 0x215021402130212, + 0x219021802170216, 0x21d021c021b021a, 0x220021f01c6021e, + 0x226022502240223, 0x22a022902280227, 0x22e022d022c022b, + 0x23202310230022f, 0x23802370236ffff, 0x23e023c023a0239, + 0x24402430240023f, 0x248024702460245, 0x24c024b024a0249, + 0x250024f024e024d, 0x254025302520251, 0x258025702560255, + 0x25c025b025a0259, 0x260025f025e025d, 0x264026302620261, + 0x268026702660265, 0x26c026bffff026a, 0x270026f026e026d, + 0x274027302720271, 0x278027702760275, 0x27c027b027a0279, + 0xffffffffffffffff, 0x281027fffffffff, 0x2d7028502840282, + 0x28c028802870482, 0x2920291028f028d, 0x296029502940293, + 0x29c029b02980297, 0x1b402b70466046a, 0x1c201c0ffff01bc, + 0x1caffff01c8ffff, 0xffffffffffffffff, 0x1d0ffffffff01ce, + 0xffff05fa0748ffff, 0x528ffff01d201d4, 0x1d8ffffffffffff, + 0xffff01da02b3ffff, 0xffffffff01dcffff, 0xffffffffffffffff, + 0xffffffff02a3ffff, 0x1e8ffffffff01e4, 0xffffffffffffffff, + 0x1f201f0028e01ec, 0xffffffffffff0290, 0xffff01f8ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff083affff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x320031f031e031d, + 0x3240323ffffffff, 0x3d5ffffffffffff, 0xffffffff03d903d7, + 0xffffffffffffffff, 0xffff0329ffffffff, 0xffff0331032f032d, + 0x3370335ffff0333, 0x33e03950339ffff, 0x347034503cc0340, + 0x35403c2083b03c8, 0x35d035b03590440, 0x388ffff03c5039f, + 0x36f039c036a0368, 0x378037607100371, 0x3320330032e032a, + 0x33f0396033affff, 0x348034603cd0341, 0x35503c3083c03c9, + 0x35e035c035a0441, 0x38a038903c603a0, 0x370039d036b0369, + 0x379037707110372, 0x393033803360334, 0xffffffff03ca0397, + 0x39403a1039effff, 0x3a703a603a303a2, 0x3ab03aa03a903a8, + 0x3af03ae03ad03ac, 0x3b503b403b103b0, 0x3bd03bc03b903b8, + 0x3c103c003bf03be, 0xffff03d103c703c4, 0x3cfffff03ce03cb, + 0x3d403d303d203d0, 0x3da03d803d6ffff, 0x3e103df03dd03db, + 0x3e903e703e503e3, 0x3f103ef03ed03eb, 0x3f903f703f503f3, + 0x40103ff03fd03fb, 0x409040704050403, 0x411040f040d040b, + 0x419041704150413, 0x421041f041d041b, 0x429042704250423, + 0x431042f042d042b, 0x439043704350433, 0x402040003fe03fc, + 0x40a040804060404, 0x4120410040e040c, 0x41a041804160414, + 0x4220420041e041c, 0x42a042804260424, 0x4320430042e042c, + 0x43a043804360434, 0x3e203e003de03dc, 0x3ea03e803e603e4, + 0x3f203f003ee03ec, 0x3fa03f803f603f4, 0x453045204510450, + 0x459045804570456, 0x4610460045d045c, 0x469046804650464, + 0x4710470046d046c, 0x477047604730472, 0x47b047a04790478, + 0x4810480047d047c, 0xffffffff04850484, 0xffffffffffffffff, + 0x4950494ffffffff, 0x49b049a04970496, 0x49f049e049d049c, + 0x4a704a604a304a2, 0x4ad04ac04a904a8, 0x4b304b204b104b0, + 0x4b904b804b704b6, 0x4bf04be04bb04ba, 0x4c504c404c104c0, + 0x4cd04cc04c904c8, 0x4d304d204cf04ce, 0x4d704d604d504d4, + 0x4df04de04db04da, 0x4e704e604e304e2, 0x4f004ed04ec04ea, + 0x4f804f504f404f1, 0x50004fd04fc04f9, 0x4eb050505040501, + 0x50d050c050b050a, 0x5130512050f050e, 0x519051805170516, + 0x51f051e051d051c, 0x525052405210520, 0x52b052a05270526, + 0x52f052e052d052c, 0x537053605330532, 0x53d053c05390538, + 0x5410540053f053e, 0x547054605430542, 0x54b054a05490548, + 0x54f054e054d054c, 0x555055405510550, 0x559055805570556, + 0x55d055c055b055a, 0x5630562055f055e, 0x567056605650564, + 0x56b056a05690568, 0x5730572056f056e, 0x577057605750574, + 0x57b057a05790578, 0xffffffffffffffff, 0xffffffffffffffff, + 0x58405820580ffff, 0x58c058a05880586, 0x59405920590058e, + 0x59c059a05980596, 0x5a405a205a0059e, 0x5ac05aa05a805a6, + 0x5b405b205b005ae, 0x5bc05ba05b805b6, 0x5c405c205c005be, + 0xffff05ca05c805c6, 0xffffffffffffffff, 0xffffffffffffffff, + 0x58505830581ffff, 0x58d058b05890587, 0x59505930591058f, + 0x59d059b05990597, 0x5a505a305a1059f, 0x5ad05ab05a905a7, + 0x5b505b305b105af, 0x5bd05bb05b905b7, 0x5c505c305c105bf, + 0xffff05cb05c905c7, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x8c008a00880086, + 0x9400920090008e, 0x9c009a00980096, 0xa400a200a0009e, 0xac00aa00a800a6, + 0xb400b200b000ae, 0xbc00ba00b800b6, 0xc400c200c000be, + 0x4a000ca048e0486, 0x4c6ffff04b400ce, 0xffffffffffffffff, + 0xffffffff0508ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff07e8ffff, 0xffffffff0454ffff, 0x5ff05fe05fd05fc, + 0x605060406010600, 0x60b060a06090608, 0x6110610060f060e, + 0x617061606130612, 0x61d061c06190618, 0x6210620061f061e, + 0x627062606230622, 0x62b062a06290628, 0x62f062e062d062c, + 0x635063406310630, 0x639063806370636, 0x63d063c063b063a, + 0x6430642063f063e, 0x647064606450644, 0x64b064a06490648, + 0x6510650064d064c, 0x655065406530652, 0x65d065c06590658, + 0x6630662065f065e, 0x667066606650664, 0x66b066a06690668, + 0x6710670066d066c, 0x675067406730672, 0x67a067906bc06bb, + 0x680067f067c067b, 0x684068306820681, 0x688068706860685, + 0x68e068d068a0689, 0x69206910690068f, 0x698069706960695, + 0x69e069d069a0699, 0x6a206a106a0069f, 0x6a606a506a406a3, + 0x6ac06ab06a806a7, 0x6b006af06ae06ad, 0x6b406b306b206b1, + 0xffffffff06b606b5, 0x6bdffffffffffff, 0xffff06bfffffffff, + 0x6c306c206c106c0, 0x6c906c806c506c4, 0x6cd06cc06cb06ca, + 0x6d106d006cf06ce, 0x6d706d606d506d4, 0x6dd06dc06db06da, + 0x6e106e006df06de, 0x6e506e406e306e2, 0x6eb06ea06e906e8, + 0x6f106f006ef06ee, 0x6f506f406f306f2, 0x6f906f806f706f6, + 0x6fd06fc06fb06fa, 0x701070006ff06fe, 0x705070407030702, + 0x709070807070706, 0x70d070c070b070a, 0x7140713070f070e, + 0x718071707160715, 0x71e071d071c071b, 0x72207210720071f, + 0x726072507240723, 0x72a072907280727, 0x7330732072e072d, + 0x73c073a07380736, 0x74407420740073e, 0x73d073b07390737, + 0x74507430741073f, 0x750074e074c074a, 0xffffffff07540752, + 0x751074f074d074b, 0xffffffff07550753, 0x76a076807660764, + 0x7720770076e076c, 0x76b076907670765, 0x7730771076f076d, + 0x78a078807860784, 0x7920790078e078c, 0x78b078907870785, + 0x7930791078f078d, 0x7a207a0079e079c, 0xffffffff07a607a4, + 0x7a307a1079f079d, 0xffffffff07a707a5, 0x7baffff07b6ffff, + 0x7c2ffff07beffff, 0x7bbffff07b7ffff, 0x7c3ffff07bfffff, + 0x7d607d407d207d0, 0x7de07dc07da07d8, 0x7d707d507d307d1, + 0x7df07dd07db07d9, 0x840083e08360834, 0x84e084c08440842, + 0x858085608620860, 0xffffffff08660864, 0x7fa07f807f607f4, + 0x802080007fe07fc, 0x7fb07f907f707f5, 0x803080107ff07fd, + 0x80e080c080a0808, 0x816081408120810, 0x80f080d080b0809, + 0x817081508130811, 0x826082408220820, 0x82e082c082a0828, + 0x827082508230821, 0x82f082d082b0829, 0x838ffff08320830, + 0xffffffffffffffff, 0x837083508330831, 0xffff083dffff0839, + 0x846ffffffffffff, 0xffffffffffffffff, 0x84508430841083f, + 0xffffffffffff0847, 0xffffffff084a0848, 0xffffffffffffffff, + 0x84f084d084b0849, 0xffffffffffffffff, 0xffffffff08540852, + 0xffffffff085affff, 0x859085708550853, 0xffffffffffff085b, + 0x868ffffffffffff, 0xffffffffffffffff, 0x867086508630861, + 0xffffffffffff0869, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0712ffffffff, 0x14b0731ffffffff, + 0xffffffffffffffff, 0xffff0530ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0531ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x18402af0180029f, 0x18c005e018802c1, + 0x194006c01900064, 0x19c007e01980076, 0x18502b0018102a0, + 0x18d005f018902c2, 0x195006d01910065, 0x19d007f01990077, + 0x1b7ffffffffffff, 0xffffffffffff01b8, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x4d80444ffffffff, + 0x4e0044c04dc0446, 0x4e8047404e4045e, 0x204ee02d3086a, + 0x6e04f606c604f2, 0x10d04fe037a04fa, 0x51a0506061a0502, + 0x4dd044704d90445, 0x4e5045f04e1044d, 0x2d4086b04e90475, + 0x6c704f3000304ef, 0x37b04fb006f04f7, 0x61b0503010e04ff, + 0xffffffff051b0507, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xa000800040000, 0x160010000e000c, 0x2bb001c001a0018, 0x5602e702d102c7, + 0x66006200600058, 0x7800740070006a, 0x29900820080007c, + 0x6020084000607e0, 0x5d005ce02a7057c, 0x1070105010305de, + 0x1190113010f0109, 0xffff013101290121, 0xb000900050001, + 0x170011000f000d, 0x2bc001d001b0019, 0x5702e802d202c8, + 0x67006300610059, 0x7900750071006b, 0x29a00830081007d, + 0x6030085000707e1, 0x5d105cf02a8057d, 0x1080106010405df, + 0x11a01140110010a, 0xffff0132012a0122, 0x455052904c304c2, + 0x45a0286028002a4, 0x46202aa02a9045b, 0x46b02b404670463, + 0x2ba02b9ffff02b8, 0xffff02c002bfffff, 0xffffffffffffffff, + 0x48302d8ffffffff, 0x489048802e202e1, 0x48d048c048b048a, + 0x2fe02fd04910490, 0x30e030d03040303, 0x31a031903160315, + 0x328032703260325, 0x6ed06ec02fc02fb, 0x383038203810380, + 0x392039103870386, 0x3b303b203a503a4, 0x5cd05cc056d056c, + 0x5ed05ec05db05da, 0x6570656060d060c, 0x6e706e6043e043d, + 0x7830782072c072b, 0x694069307e307e2, 0x150014065b065a, + 0x4bd04bc005d005c, 0x5d505d404d104d0, 0x511051001a101a0, + 0x535053405230522, 0x553055205450544, 0x571057005610560, + 0x15b015a057f057e, 0x3bb03ba037d037c, 0xffffffffffffffff, + 0x5d2ffffffffffff, 0xffff05d905d805d3, 0x5e305e2ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x8d008b00890087, 0x9500930091008f, 0x9d009b00990097, 0xa500a300a1009f, + 0xad00ab00a900a7, 0xb500b300b100af, 0xbd00bb00b900b7, 0xc500c300c100bf, + 0x4a100cb048f0487, 0x4c7ffff04b500cf, 0xffffffffffffffff, + 0xffffffff0509ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x5d705d602c402c3, + 0x5e105e005dd05dc, 0x5e905e805e705e6, 0x5ef05ee05eb05ea, + 0x5f505f405f105f0, 0x308030705f905f8, 0x625062406150614, + 0x641064006330632, 0x6610660064f064e, 0x67e067d066f066e, + 0x69c069b068c068b, 0xffffffff06aa06a9, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x69006807350734, 0x75f075e027e027d, 0x390038f07770776, + 0x7b107b0001f001e, 0x2a202a107c707c6, 0x6b806b707e507e4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x7590758ffffffff, 0x7610760075d075c, 0x2e002df02d602d5, + 0x2ee02ed02e602e5, 0x7790778ffffffff, 0x7810780077d077c, + 0x3140313030c030b, 0x322032103180317, 0x797079607950794, + 0x79b079a07990798, 0x3850384037f037e, 0x7a907a8038e038d, + 0x7ad07ac07ab07aa, 0x7b307b207af07ae, 0x7b907b807b507b4, + 0x7c107c007bd07bc, 0x7c907c807c507c4, 0x7cf07ce07cd07cc, + 0x47f047e046f046e, 0x4a504a404930492, 0xffffffffffffffff, + 0xffffffffffffffff, 0x7e605150514ffff, 0x7eb07ea07e907e7, + 0x7ef07ee07ed07ec, 0x7f307f207f107f0, 0x5f2ffffffffffff, + 0xffffffff074905f3, 0x807080608050804, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x81b081a08190818, + 0x81f081e081d081c, 0xffff05fb05f705f6, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x75a02c50756ffff, 0x76202cf02cd02cb, 0x2e3071902db06d2, + 0x2f107ca02e90448, 0x77a02f502f30774, 0x3050221077e02f9, + 0xffff043b030f007a, 0xffffffffffffffff, 0x75b02c60757ffff, + 0x76302d002ce02cc, 0x2e4071a02dc06d3, 0x2f207cb02ea0449, + 0x77b02f602f40775, 0x3060222077f02fa, 0xffff043c0310007b, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x72005a085c0012, 0x11106b9032b0311, 0x2dd029d02ab05e4, + 0x10b060602ef085e, 0x4ca028902d902a5, 0x2c902bd02b502ad, + 0x30902f702eb0746, 0x38b02b10241031b, 0x442053a044a03b6, + 0x85004ae06d8044e, 0x73005b085d0013, 0x11206ba032c0312, + 0x2de029e02ac05e5, 0x10c060702f0085f, 0x4cb028a02da02a6, + 0x2ca02be02b602ae, 0x30a02f802ec0747, 0x38c02b20242031c, + 0x443053b044b03b7, 0x85104af06d9044f, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); //8832 bytes -enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xd40], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16001500000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, 0x1b001a00190018, 0x1f001e001d001c, 0x0, 0x2200210020, 0x0, 0x0, 0x24002300000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28002700260025, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b002a0000, 0x2e002d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x0, 0x310030, 0x0, 0x0, 0x0, 0x0, 0x330032, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2400220020ffff, 0x2c002a00280026, 0x78100320030002e, 0x3d003b00390037, 0x1b900430041003f, 0x4e004c004a0048, 0xffff005400520050, 0xffffffffffffffff, 0x2500230021ffff, 0x2d002b00290027, 0x78200330031002f, 0x3e003c003a0038, 0x1ba004400420040, 0x4f004d004b0049, 0xffff005500530051, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0470ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xcc04c900c800c6, 0xd500d3014e04db, 0xdd00db00d900d7, 0xe500e300e100df, 0xed00eb00e900e7, 0xffff00f300f100ef, 0xfb00f900f700f5, 0x70f010100ff00fd, 0xcd04ca00c900c7, 0xd600d4014f04dc, 0xde00dc00da00d8, 0xe600e400e200e0, 0xee00ec00ea00e8, 0xffff00f400f200f0, 0xfc00fa00f800f6, 0x1b10102010000fe, 0x11b011a01190118, 0x1210120011f011e, 0x127012601230122, 0x12b012a01290128, 0x1310130012f012e, 0x137013601330132, 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, 0x149014801470146, 0x14d014c014b014a, 0x154015301520151, 0x1580157ffff0155, 0x15c015b015a0159, 0x15f015e015dffff, 0x165016401630160, 0x169016801670166, 0x16d016c016b016a, 0x1720171016f016e, 0x176017501740173, 0x17a017901780177, 0x17e017d017c017b, 0x18201810180017f, 0x186018501840183, 0x18c018b01880187, 0x19401930190018f, 0x19c019b01980197, 0x1a401a301a0019f, 0x1ac01ab01a801a7, 0x1b001af01ae01ad, 0x1b501b401b301b2, 0x1bb01b801b701b6, 0x1bf01be01bc029c, 0x1c601c401c301c2, 0x1cc01ca01c801c7, 0x1d001ceffff01cd, 0x1d601d501d401d2, 0x1dc01da024801d8, 0xffff029401df01de, 0x1e6027801e201e0, 0x1eb01ea01e901e8, 0x1f001ee01ed01ec, 0xffffffff01f201f1, 0x1f801f601f501f4, 0x1fe01fc01fa01f9, 0x2020201020001ff, 0xffffffff02050204, 0x24affff02070206, 0xffffffffffffffff, 0x211020d020c020b, 0x218021702130212, 0x21c021b021a0219, 0x220021f021e021d, 0x224022302220221, 0x228022702260225, 0x22b022a01cf0229, 0x2310230022f022e, 0x235023402330232, 0x239023802370236, 0x23d023c023b023a, 0x24502440243023e, 0x24b024902470246, 0x2510250024d024c, 0x255025402530252, 0x259025802570256, 0x25d025c025b025a, 0x263026202610260, 0x267026602650264, 0x26b026a02690268, 0x26f026e026d026c, 0x273027202710270, 0x277027602750274, 0x27b027affff0279, 0x27f027e027d027c, 0x285028402810280, 0x289028802870286, 0x28d028c028b028a, 0xffffffffffffffff, 0x2920290ffffffff, 0x2ec029602950293, 0x29d0299029804b3, 0x2a302a202a0029e, 0x2a702a602a502a4, 0x2ad02ac02a902a8, 0x1bd02ca0497049b, 0x1cb01c9ffff01c5, 0x1d3ffff01d1ffff, 0xffffffffffffffff, 0x1d9ffffffff01d7, 0xffff0643079affff, 0x559ffff01db01dd, 0x1e1ffffffffffff, 0xffff01e302c6ffff, 0xffffffff01e7ffff, 0xffffffffffffffff, 0xffffffff02b4ffff, 0x1f3ffffffff01ef, 0xffffffffffffffff, 0x1fd01fb029f01f7, 0xffffffffffff02a1, 0xffff0203ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff08e4ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x347034603450344, 0x34b034affffffff, 0x406ffffffffffff, 0xffffffff040a0408, 0xffffffffffffffff, 0xffff0350ffffffff, 0xffff035803560354, 0x35e035cffff035a, 0x36803c203630902, 0x371036f03fd036a, 0x37e03f308e503f9, 0x387038503830471, 0x3b5ffff03f603cc, 0x39903c903940392, 0x3a203a00762039b, 0x359035703550351, 0x36903c303640915, 0x372037003fe036b, 0x37f03f408e603fa, 0x388038603840472, 0x3b703b603f703cd, 0x39a03ca03950393, 0x3a303a10763039c, 0x3c0035f035d035b, 0xffffffff03fb03c4, 0x3c103ce03cbffff, 0x3d403d303d003cf, 0x3d803d703d603d5, 0x3de03dd03da03d9, 0x3e403e303e003df, 0x3ec03eb03e803e7, 0x3f203f103ee03ed, 0xffff040203f803f5, 0x400ffff03ff03fc, 0x405040404030401, 0x40b04090407ffff, 0x4120410040e040c, 0x41a041804160414, 0x4220420041e041c, 0x42a042804260424, 0x4320430042e042c, 0x43a043804360434, 0x4420440043e043c, 0x44a044804460444, 0x4520450044e044c, 0x45a045804560454, 0x4620460045e045c, 0x46a046804660464, 0x4330431042f042d, 0x43b043904370435, 0x4430441043f043d, 0x44b044904470445, 0x4530451044f044d, 0x45b045904570455, 0x4630461045f045d, 0x46b046904670465, 0x4130411040f040d, 0x41b041904170415, 0x4230421041f041d, 0x42b042904270425, 0x484048304820481, 0x48a048904880487, 0x4920491048e048d, 0x49a049904960495, 0x4a204a1049e049d, 0x4a804a704a404a3, 0x4ac04ab04aa04a9, 0x4b204b104ae04ad, 0xffffffff04b604b5, 0xffffffffffffffff, 0x4c604c5ffffffff, 0x4cc04cb04c804c7, 0x4d004cf04ce04cd, 0x4d804d704d404d3, 0x4de04dd04da04d9, 0x4e404e304e204e1, 0x4ea04e904e804e7, 0x4f004ef04ec04eb, 0x4f604f504f204f1, 0x4fe04fd04fa04f9, 0x5040503050004ff, 0x508050705060505, 0x510050f050c050b, 0x518051705140513, 0x521051e051d051b, 0x529052605250522, 0x531052e052d052a, 0x51c053605350532, 0x53e053d053c053b, 0x54405430540053f, 0x54a054905480547, 0x550054f054e054d, 0x556055505520551, 0x55c055b05580557, 0x560055f055e055d, 0x568056705640563, 0x56e056d056a0569, 0x57205710570056f, 0x578057705740573, 0x57c057b057a0579, 0x5820581057e057d, 0x588058705840583, 0x58c058b058a0589, 0x5920591058e058d, 0x598059705940593, 0x59c059b059a0599, 0x5a205a1059e059d, 0x5aa05a905a605a5, 0x5ae05ad05ac05ab, 0x5b405b305b005af, 0xffffffffffffffff, 0xffffffffffffffff, 0x5bd05bb05b9ffff, 0x5c505c305c105bf, 0x5cd05cb05c905c7, 0x5d505d305d105cf, 0x5dd05db05d905d7, 0x5e505e305e105df, 0x5ed05eb05e905e7, 0x5f505f305f105ef, 0x5fd05fb05f905f7, 0xffff0603060105ff, 0xffffffffffffffff, 0xffffffffffffffff, 0x5be05bc05baffff, 0x5c605c405c205c0, 0x5ce05cc05ca05c8, 0x5d605d405d205d0, 0x5de05dc05da05d8, 0x5e605e405e205e0, 0x5ee05ec05ea05e8, 0x5f605f405f205f0, 0x5fe05fc05fa05f8, 0x613060406020600, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x8c008a00880086, 0x9400920090008e, 0x9c009a00980096, 0xa400a200a0009e, 0xac00aa00a800a6, 0xb400b200b000ae, 0xbc00ba00b800b6, 0xc400c200c000be, 0x4d100ca04bf04b7, 0x4f7ffff04e500ce, 0xffffffffffffffff, 0xffffffff0539ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff083affff, 0xffffffff0485ffff, 0x648064706460645, 0x64e064d064a0649, 0x654065306520651, 0x65a065906580657, 0x660065f065c065b, 0x666066506620661, 0x66a066906680667, 0x670066f066c066b, 0x674067306720671, 0x678067706760675, 0x67e067d067a0679, 0x68206810680067f, 0x686068506840683, 0x68c068b06880687, 0x690068f068e068d, 0x694069306920691, 0x69a069906960695, 0x69e069d069c069b, 0x6a606a506a206a1, 0x6ac06ab06a806a7, 0x6b006af06ae06ad, 0x6b406b306b206b1, 0x6ba06b906b606b5, 0x6be06bd06bc06bb, 0x6c306c2070d070c, 0x6cb06ca06c706c6, 0x6cf06ce06cd06cc, 0x6d306d206d106d0, 0x6d906d806d506d4, 0x6dd06dc06db06da, 0x6e306e206e106e0, 0x6e906e806e506e4, 0x6ed06ec06eb06ea, 0x6f106f006ef06ee, 0x6f706f606f306f2, 0x6fb06fa06f906f8, 0x6ff06fe06fd06fc, 0x704070207010700, 0x70e070a07080706, 0xffff0710ffffffff, 0x715071407130712, 0x71b071a07170716, 0x71f071e071d071c, 0x723072207210720, 0x729072807270726, 0x72f072e072d072c, 0x733073207310730, 0x737073607350734, 0x73d073c073b073a, 0x743074207410740, 0x747074607450744, 0x74b074a07490748, 0x74f074e074d074c, 0x753075207510750, 0x757075607550754, 0x75b075a07590758, 0x75f075e075d075c, 0x766076507610760, 0x76a076907680767, 0x770076f076e076d, 0x774077307720771, 0x778077707760775, 0x77c077b077a0779, 0x78507840780077f, 0x78e078c078a0788, 0x796079407920790, 0x78f078d078b0789, 0x797079507930791, 0x7a207a0079e079c, 0xffffffff07a607a4, 0x7a307a1079f079d, 0xffffffff07a707a5, 0x7bc07ba07b807b6, 0x7c407c207c007be, 0x7bd07bb07b907b7, 0x7c507c307c107bf, 0x7dc07da07d807d6, 0x7e407e207e007de, 0x7dd07db07d907d7, 0x7e507e307e107df, 0x7f407f207f007ee, 0xffffffff07f807f6, 0x7f507f307f107ef, 0xffffffff07f907f7, 0x80c07fe080807fc, 0x814080408100800, 0x80dffff0809ffff, 0x815ffff0811ffff, 0x828082608240822, 0x830082e082c082a, 0x829082708250823, 0x831082f082d082b, 0x8f708f508df08dd, 0x90f090d08fb08f9, 0x924092209370935, 0xffffffff093b0939, 0x85f085c08590856, 0x86b086808650862, 0x860085d085a0857, 0x86c086908660863, 0x88f088c08890886, 0x89b089808950892, 0x890088d088a0887, 0x89c089908960893, 0x8bf08bc08b908b6, 0x8cb08c808c508c2, 0x8c008bd08ba08b7, 0x8cc08c908c608c3, 0x8e108ce08db08d9, 0x8d708d5ffff08d3, 0x8e008de08dc08da, 0xffff08e7ffff08e2, 0x8fd08e8ffffffff, 0x8f308f1ffff08ed, 0x8fc08fa08f808f6, 0xffffffffffff08fe, 0x9030900090b0909, 0x9070905ffffffff, 0x910090e090c090a, 0xffffffffffffffff, 0x91609130920091e, 0x91c091a09260918, 0x92509230921091f, 0xffffffffffff0927, 0x93d092affffffff, 0x9330931ffff092f, 0x93c093a09380936, 0xffffffffffff093e, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0764ffffffff, 0x1500783ffffffff, 0xffffffffffffffff, 0xffff0561ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0562ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x18d02c2018902b0, 0x195005e019102d6, 0x19d006c01990064, 0x1a5007e01a10076, 0x18e02c3018a02b1, 0x196005f019202d7, 0x19e006d019a0065, 0x1a6007f01a20077, 0x1c0ffffffffffff, 0xffffffffffff01c1, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x5090475ffffffff, 0x511047d050d0477, 0x51904a50515048f, 0x2051f02e80940, 0x6e052707180523, 0x110052f03a4052b, 0x54b053706630533, 0x50e0478050a0476, 0x51604900512047e, 0x2e90941051a04a6, 0x719052400030520, 0x3a5052c006f0528, 0x664053401110530, 0xffffffff054c0538, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xa000800040000, 0x160010000e000c, 0x2ce001c001a0018, 0x56030802e602dc, 0x66006200600058, 0x7800740070006a, 0x2aa00820080007c, 0x64b008400060832, 0x60d060902b805b5, 0x10801060629061d, 0x11c01160112010a, 0xffff0134012c0124, 0xb000900050001, 0x170011000f000d, 0x2cf001d001b0019, 0x57030902e702dd, 0x67006300610059, 0x7900750071006b, 0x2ab00830081007d, 0x64c008500070833, 0x60e060a02b905b6, 0x1090107062a061e, 0x11d01170113010b, 0xffff0135012d0125, 0x486055a04f404f3, 0x48b0297029102b5, 0x49302bb02ba048c, 0x49c02c704980494, 0x2cd02ccffff02cb, 0xffff02d502d4ffff, 0xffffffffffffffff, 0x4b402edffffffff, 0x4ba04b902f902f8, 0x4be04bd04bc04bb, 0x325032404c204c1, 0x3350334032b032a, 0x3410340033d033c, 0x34f034e034d034c, 0x73f073e03230322, 0x3b003af03ae03ad, 0x3bf03be03b403b3, 0x3e203e103d203d1, 0x606060505a405a3, 0x6320631061a0619, 0x6a0069f06560655, 0x7390738046f046e, 0x7d507d4077e077d, 0x6df06de08350834, 0x15001406a406a3, 0x4ee04ed005d005c, 0x612061105020501, 0x542054101aa01a9, 0x566056505540553, 0x586058505760575, 0x5a805a705960595, 0x162016105b805b7, 0x3ea03e903a703a6, 0xffffffffffffffff, 0x60fffffffffffff, 0xffff061806170610, 0x6240623ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x8d008b00890087, 0x9500930091008f, 0x9d009b00990097, 0xa500a300a1009f, 0xad00ab00a900a7, 0xb500b300b100af, 0xbd00bb00b900b7, 0xc500c300c100bf, 0x4d200cb04c004b8, 0x4f8ffff04e600cf, 0xffffffffffffffff, 0xffffffff053affff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x616061502d902d8, 0x6220621061c061b, 0x1e501e406280627, 0x6340633062e062d, 0x63e063d06380637, 0x32f032e06420641, 0x66e066d065e065d, 0x68a0689067c067b, 0x6aa06a906980697, 0x6c906c806b806b7, 0x6e706e606d706d6, 0xffffffff06f506f4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x69006807870786, 0x7b107b0028f028e, 0x3bd03bc07c907c8, 0x8030802001f001e, 0x2b302b208190818, 0x2d302d208370836, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ab07aaffffffff, 0x7b307b207af07ae, 0x2f502f402eb02ea, 0x311031003070306, 0x7cb07caffffffff, 0x7d307d207cf07ce, 0x33b033a03330332, 0x3490348033f033e, 0x7e907e807e707e6, 0x7ed07ec07eb07ea, 0x3b203b103ac03ab, 0x7fb07fa03bb03ba, 0x3f003ef03dc03db, 0x28302820620061f, 0x80b080a08070806, 0x8130812080f080e, 0x81b081a08170816, 0x8210820081f081e, 0x4b004af04a0049f, 0x4d604d504c404c3, 0xffffffffffffffff, 0xffffffffffffffff, 0x83805460545ffff, 0x83d083c083b0839, 0x590058f0580057f, 0x5b205b105a0059f, 0x63bffffffffffff, 0xffffffff079b063c, 0x60c060b06080607, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x630062f062c062b, 0x63a063906360635, 0xffff06440640063f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2fc02fa025e02f6, 0xffff0304030302fe, 0xffffffffffffffff, 0xffffffffffffffff, 0x30cffffffffffff, 0x314031202c0030e, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ac02da07a8ffff, 0x7b402e402e202e0, 0x144076b02f00724, 0x318081c030a0479, 0x7cc031c031a07c6, 0x32c022c07d00320, 0xffff046c0336007a, 0xffffffffffffffff, 0x7ad02db07a9ffff, 0x7b502e502e302e1, 0x145076c02f10725, 0x319081d030b047a, 0x7cd031d031b07c7, 0x32d022d07d10321, 0xffff046d0337007b, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x72005a09280012, 0x114010c03520338, 0x2f202ae02bc0625, 0x10e064f031608ef, 0x4fb029a02ee02b6, 0x2de02d002c802be, 0x330031e047f0798, 0x3b802c4024e0342, 0x473056b047b03e5, 0x91104df072a06c4, 0x73005b09290013, 0x115010d03530339, 0x2f302af02bd0626, 0x10f0650031708f0, 0x4fc029b02ef02b7, 0x2df02d102c902bf, 0x331031f04800799, 0x3b902c5024f0343, 0x474056c047c03e6, 0x91204e0072b06c5, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, 0x100], + [0x100, 0x380, 0xd40], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, + 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x16001500000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x170000, 0x1b001a00190018, 0x1f001e001d001c, 0x0, + 0x2200210020, 0x0, 0x0, 0x24002300000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x28002700260025, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b002a0000, 0x2e002d002c, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x0, 0x310030, 0x0, + 0x0, 0x0, 0x0, 0x330032, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2400220020ffff, 0x2c002a00280026, 0x78100320030002e, + 0x3d003b00390037, 0x1b900430041003f, 0x4e004c004a0048, + 0xffff005400520050, 0xffffffffffffffff, 0x2500230021ffff, + 0x2d002b00290027, 0x78200330031002f, 0x3e003c003a0038, + 0x1ba004400420040, 0x4f004d004b0049, 0xffff005500530051, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff0470ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xcc04c900c800c6, + 0xd500d3014e04db, 0xdd00db00d900d7, 0xe500e300e100df, 0xed00eb00e900e7, + 0xffff00f300f100ef, 0xfb00f900f700f5, 0x70f010100ff00fd, + 0xcd04ca00c900c7, 0xd600d4014f04dc, 0xde00dc00da00d8, 0xe600e400e200e0, + 0xee00ec00ea00e8, 0xffff00f400f200f0, 0xfc00fa00f800f6, + 0x1b10102010000fe, 0x11b011a01190118, 0x1210120011f011e, + 0x127012601230122, 0x12b012a01290128, 0x1310130012f012e, + 0x137013601330132, 0x13b013a01390138, 0x13f013e013d013c, + 0x143014201410140, 0x149014801470146, 0x14d014c014b014a, + 0x154015301520151, 0x1580157ffff0155, 0x15c015b015a0159, + 0x15f015e015dffff, 0x165016401630160, 0x169016801670166, + 0x16d016c016b016a, 0x1720171016f016e, 0x176017501740173, + 0x17a017901780177, 0x17e017d017c017b, 0x18201810180017f, + 0x186018501840183, 0x18c018b01880187, 0x19401930190018f, + 0x19c019b01980197, 0x1a401a301a0019f, 0x1ac01ab01a801a7, + 0x1b001af01ae01ad, 0x1b501b401b301b2, 0x1bb01b801b701b6, + 0x1bf01be01bc029c, 0x1c601c401c301c2, 0x1cc01ca01c801c7, + 0x1d001ceffff01cd, 0x1d601d501d401d2, 0x1dc01da024801d8, + 0xffff029401df01de, 0x1e6027801e201e0, 0x1eb01ea01e901e8, + 0x1f001ee01ed01ec, 0xffffffff01f201f1, 0x1f801f601f501f4, + 0x1fe01fc01fa01f9, 0x2020201020001ff, 0xffffffff02050204, + 0x24affff02070206, 0xffffffffffffffff, 0x211020d020c020b, + 0x218021702130212, 0x21c021b021a0219, 0x220021f021e021d, + 0x224022302220221, 0x228022702260225, 0x22b022a01cf0229, + 0x2310230022f022e, 0x235023402330232, 0x239023802370236, + 0x23d023c023b023a, 0x24502440243023e, 0x24b024902470246, + 0x2510250024d024c, 0x255025402530252, 0x259025802570256, + 0x25d025c025b025a, 0x263026202610260, 0x267026602650264, + 0x26b026a02690268, 0x26f026e026d026c, 0x273027202710270, + 0x277027602750274, 0x27b027affff0279, 0x27f027e027d027c, + 0x285028402810280, 0x289028802870286, 0x28d028c028b028a, + 0xffffffffffffffff, 0x2920290ffffffff, 0x2ec029602950293, + 0x29d0299029804b3, 0x2a302a202a0029e, 0x2a702a602a502a4, + 0x2ad02ac02a902a8, 0x1bd02ca0497049b, 0x1cb01c9ffff01c5, + 0x1d3ffff01d1ffff, 0xffffffffffffffff, 0x1d9ffffffff01d7, + 0xffff0643079affff, 0x559ffff01db01dd, 0x1e1ffffffffffff, + 0xffff01e302c6ffff, 0xffffffff01e7ffff, 0xffffffffffffffff, + 0xffffffff02b4ffff, 0x1f3ffffffff01ef, 0xffffffffffffffff, + 0x1fd01fb029f01f7, 0xffffffffffff02a1, 0xffff0203ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff08e4ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x347034603450344, + 0x34b034affffffff, 0x406ffffffffffff, 0xffffffff040a0408, + 0xffffffffffffffff, 0xffff0350ffffffff, 0xffff035803560354, + 0x35e035cffff035a, 0x36803c203630902, 0x371036f03fd036a, + 0x37e03f308e503f9, 0x387038503830471, 0x3b5ffff03f603cc, + 0x39903c903940392, 0x3a203a00762039b, 0x359035703550351, + 0x36903c303640915, 0x372037003fe036b, 0x37f03f408e603fa, + 0x388038603840472, 0x3b703b603f703cd, 0x39a03ca03950393, + 0x3a303a10763039c, 0x3c0035f035d035b, 0xffffffff03fb03c4, + 0x3c103ce03cbffff, 0x3d403d303d003cf, 0x3d803d703d603d5, + 0x3de03dd03da03d9, 0x3e403e303e003df, 0x3ec03eb03e803e7, + 0x3f203f103ee03ed, 0xffff040203f803f5, 0x400ffff03ff03fc, + 0x405040404030401, 0x40b04090407ffff, 0x4120410040e040c, + 0x41a041804160414, 0x4220420041e041c, 0x42a042804260424, + 0x4320430042e042c, 0x43a043804360434, 0x4420440043e043c, + 0x44a044804460444, 0x4520450044e044c, 0x45a045804560454, + 0x4620460045e045c, 0x46a046804660464, 0x4330431042f042d, + 0x43b043904370435, 0x4430441043f043d, 0x44b044904470445, + 0x4530451044f044d, 0x45b045904570455, 0x4630461045f045d, + 0x46b046904670465, 0x4130411040f040d, 0x41b041904170415, + 0x4230421041f041d, 0x42b042904270425, 0x484048304820481, + 0x48a048904880487, 0x4920491048e048d, 0x49a049904960495, + 0x4a204a1049e049d, 0x4a804a704a404a3, 0x4ac04ab04aa04a9, + 0x4b204b104ae04ad, 0xffffffff04b604b5, 0xffffffffffffffff, + 0x4c604c5ffffffff, 0x4cc04cb04c804c7, 0x4d004cf04ce04cd, + 0x4d804d704d404d3, 0x4de04dd04da04d9, 0x4e404e304e204e1, + 0x4ea04e904e804e7, 0x4f004ef04ec04eb, 0x4f604f504f204f1, + 0x4fe04fd04fa04f9, 0x5040503050004ff, 0x508050705060505, + 0x510050f050c050b, 0x518051705140513, 0x521051e051d051b, + 0x529052605250522, 0x531052e052d052a, 0x51c053605350532, + 0x53e053d053c053b, 0x54405430540053f, 0x54a054905480547, + 0x550054f054e054d, 0x556055505520551, 0x55c055b05580557, + 0x560055f055e055d, 0x568056705640563, 0x56e056d056a0569, + 0x57205710570056f, 0x578057705740573, 0x57c057b057a0579, + 0x5820581057e057d, 0x588058705840583, 0x58c058b058a0589, + 0x5920591058e058d, 0x598059705940593, 0x59c059b059a0599, + 0x5a205a1059e059d, 0x5aa05a905a605a5, 0x5ae05ad05ac05ab, + 0x5b405b305b005af, 0xffffffffffffffff, 0xffffffffffffffff, + 0x5bd05bb05b9ffff, 0x5c505c305c105bf, 0x5cd05cb05c905c7, + 0x5d505d305d105cf, 0x5dd05db05d905d7, 0x5e505e305e105df, + 0x5ed05eb05e905e7, 0x5f505f305f105ef, 0x5fd05fb05f905f7, + 0xffff0603060105ff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x5be05bc05baffff, 0x5c605c405c205c0, 0x5ce05cc05ca05c8, + 0x5d605d405d205d0, 0x5de05dc05da05d8, 0x5e605e405e205e0, + 0x5ee05ec05ea05e8, 0x5f605f405f205f0, 0x5fe05fc05fa05f8, + 0x613060406020600, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x8c008a00880086, + 0x9400920090008e, 0x9c009a00980096, 0xa400a200a0009e, 0xac00aa00a800a6, + 0xb400b200b000ae, 0xbc00ba00b800b6, 0xc400c200c000be, + 0x4d100ca04bf04b7, 0x4f7ffff04e500ce, 0xffffffffffffffff, + 0xffffffff0539ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff083affff, 0xffffffff0485ffff, 0x648064706460645, + 0x64e064d064a0649, 0x654065306520651, 0x65a065906580657, + 0x660065f065c065b, 0x666066506620661, 0x66a066906680667, + 0x670066f066c066b, 0x674067306720671, 0x678067706760675, + 0x67e067d067a0679, 0x68206810680067f, 0x686068506840683, + 0x68c068b06880687, 0x690068f068e068d, 0x694069306920691, + 0x69a069906960695, 0x69e069d069c069b, 0x6a606a506a206a1, + 0x6ac06ab06a806a7, 0x6b006af06ae06ad, 0x6b406b306b206b1, + 0x6ba06b906b606b5, 0x6be06bd06bc06bb, 0x6c306c2070d070c, + 0x6cb06ca06c706c6, 0x6cf06ce06cd06cc, 0x6d306d206d106d0, + 0x6d906d806d506d4, 0x6dd06dc06db06da, 0x6e306e206e106e0, + 0x6e906e806e506e4, 0x6ed06ec06eb06ea, 0x6f106f006ef06ee, + 0x6f706f606f306f2, 0x6fb06fa06f906f8, 0x6ff06fe06fd06fc, + 0x704070207010700, 0x70e070a07080706, 0xffff0710ffffffff, + 0x715071407130712, 0x71b071a07170716, 0x71f071e071d071c, + 0x723072207210720, 0x729072807270726, 0x72f072e072d072c, + 0x733073207310730, 0x737073607350734, 0x73d073c073b073a, + 0x743074207410740, 0x747074607450744, 0x74b074a07490748, + 0x74f074e074d074c, 0x753075207510750, 0x757075607550754, + 0x75b075a07590758, 0x75f075e075d075c, 0x766076507610760, + 0x76a076907680767, 0x770076f076e076d, 0x774077307720771, + 0x778077707760775, 0x77c077b077a0779, 0x78507840780077f, + 0x78e078c078a0788, 0x796079407920790, 0x78f078d078b0789, + 0x797079507930791, 0x7a207a0079e079c, 0xffffffff07a607a4, + 0x7a307a1079f079d, 0xffffffff07a707a5, 0x7bc07ba07b807b6, + 0x7c407c207c007be, 0x7bd07bb07b907b7, 0x7c507c307c107bf, + 0x7dc07da07d807d6, 0x7e407e207e007de, 0x7dd07db07d907d7, + 0x7e507e307e107df, 0x7f407f207f007ee, 0xffffffff07f807f6, + 0x7f507f307f107ef, 0xffffffff07f907f7, 0x80c07fe080807fc, + 0x814080408100800, 0x80dffff0809ffff, 0x815ffff0811ffff, + 0x828082608240822, 0x830082e082c082a, 0x829082708250823, + 0x831082f082d082b, 0x8f708f508df08dd, 0x90f090d08fb08f9, + 0x924092209370935, 0xffffffff093b0939, 0x85f085c08590856, + 0x86b086808650862, 0x860085d085a0857, 0x86c086908660863, + 0x88f088c08890886, 0x89b089808950892, 0x890088d088a0887, + 0x89c089908960893, 0x8bf08bc08b908b6, 0x8cb08c808c508c2, + 0x8c008bd08ba08b7, 0x8cc08c908c608c3, 0x8e108ce08db08d9, + 0x8d708d5ffff08d3, 0x8e008de08dc08da, 0xffff08e7ffff08e2, + 0x8fd08e8ffffffff, 0x8f308f1ffff08ed, 0x8fc08fa08f808f6, + 0xffffffffffff08fe, 0x9030900090b0909, 0x9070905ffffffff, + 0x910090e090c090a, 0xffffffffffffffff, 0x91609130920091e, + 0x91c091a09260918, 0x92509230921091f, 0xffffffffffff0927, + 0x93d092affffffff, 0x9330931ffff092f, 0x93c093a09380936, + 0xffffffffffff093e, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0764ffffffff, 0x1500783ffffffff, + 0xffffffffffffffff, 0xffff0561ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0562ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x18d02c2018902b0, 0x195005e019102d6, + 0x19d006c01990064, 0x1a5007e01a10076, 0x18e02c3018a02b1, + 0x196005f019202d7, 0x19e006d019a0065, 0x1a6007f01a20077, + 0x1c0ffffffffffff, 0xffffffffffff01c1, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x5090475ffffffff, + 0x511047d050d0477, 0x51904a50515048f, 0x2051f02e80940, + 0x6e052707180523, 0x110052f03a4052b, 0x54b053706630533, + 0x50e0478050a0476, 0x51604900512047e, 0x2e90941051a04a6, + 0x719052400030520, 0x3a5052c006f0528, 0x664053401110530, + 0xffffffff054c0538, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xa000800040000, 0x160010000e000c, 0x2ce001c001a0018, 0x56030802e602dc, + 0x66006200600058, 0x7800740070006a, 0x2aa00820080007c, + 0x64b008400060832, 0x60d060902b805b5, 0x10801060629061d, + 0x11c01160112010a, 0xffff0134012c0124, 0xb000900050001, + 0x170011000f000d, 0x2cf001d001b0019, 0x57030902e702dd, + 0x67006300610059, 0x7900750071006b, 0x2ab00830081007d, + 0x64c008500070833, 0x60e060a02b905b6, 0x1090107062a061e, + 0x11d01170113010b, 0xffff0135012d0125, 0x486055a04f404f3, + 0x48b0297029102b5, 0x49302bb02ba048c, 0x49c02c704980494, + 0x2cd02ccffff02cb, 0xffff02d502d4ffff, 0xffffffffffffffff, + 0x4b402edffffffff, 0x4ba04b902f902f8, 0x4be04bd04bc04bb, + 0x325032404c204c1, 0x3350334032b032a, 0x3410340033d033c, + 0x34f034e034d034c, 0x73f073e03230322, 0x3b003af03ae03ad, + 0x3bf03be03b403b3, 0x3e203e103d203d1, 0x606060505a405a3, + 0x6320631061a0619, 0x6a0069f06560655, 0x7390738046f046e, + 0x7d507d4077e077d, 0x6df06de08350834, 0x15001406a406a3, + 0x4ee04ed005d005c, 0x612061105020501, 0x542054101aa01a9, + 0x566056505540553, 0x586058505760575, 0x5a805a705960595, + 0x162016105b805b7, 0x3ea03e903a703a6, 0xffffffffffffffff, + 0x60fffffffffffff, 0xffff061806170610, 0x6240623ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x8d008b00890087, 0x9500930091008f, 0x9d009b00990097, 0xa500a300a1009f, + 0xad00ab00a900a7, 0xb500b300b100af, 0xbd00bb00b900b7, 0xc500c300c100bf, + 0x4d200cb04c004b8, 0x4f8ffff04e600cf, 0xffffffffffffffff, + 0xffffffff053affff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x616061502d902d8, + 0x6220621061c061b, 0x1e501e406280627, 0x6340633062e062d, + 0x63e063d06380637, 0x32f032e06420641, 0x66e066d065e065d, + 0x68a0689067c067b, 0x6aa06a906980697, 0x6c906c806b806b7, + 0x6e706e606d706d6, 0xffffffff06f506f4, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x69006807870786, 0x7b107b0028f028e, 0x3bd03bc07c907c8, + 0x8030802001f001e, 0x2b302b208190818, 0x2d302d208370836, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x7ab07aaffffffff, 0x7b307b207af07ae, 0x2f502f402eb02ea, + 0x311031003070306, 0x7cb07caffffffff, 0x7d307d207cf07ce, + 0x33b033a03330332, 0x3490348033f033e, 0x7e907e807e707e6, + 0x7ed07ec07eb07ea, 0x3b203b103ac03ab, 0x7fb07fa03bb03ba, + 0x3f003ef03dc03db, 0x28302820620061f, 0x80b080a08070806, + 0x8130812080f080e, 0x81b081a08170816, 0x8210820081f081e, + 0x4b004af04a0049f, 0x4d604d504c404c3, 0xffffffffffffffff, + 0xffffffffffffffff, 0x83805460545ffff, 0x83d083c083b0839, + 0x590058f0580057f, 0x5b205b105a0059f, 0x63bffffffffffff, + 0xffffffff079b063c, 0x60c060b06080607, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x630062f062c062b, + 0x63a063906360635, 0xffff06440640063f, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2fc02fa025e02f6, 0xffff0304030302fe, + 0xffffffffffffffff, 0xffffffffffffffff, 0x30cffffffffffff, + 0x314031202c0030e, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x7ac02da07a8ffff, 0x7b402e402e202e0, + 0x144076b02f00724, 0x318081c030a0479, 0x7cc031c031a07c6, + 0x32c022c07d00320, 0xffff046c0336007a, 0xffffffffffffffff, + 0x7ad02db07a9ffff, 0x7b502e502e302e1, 0x145076c02f10725, + 0x319081d030b047a, 0x7cd031d031b07c7, 0x32d022d07d10321, + 0xffff046d0337007b, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x72005a09280012, 0x114010c03520338, + 0x2f202ae02bc0625, 0x10e064f031608ef, 0x4fb029a02ee02b6, + 0x2de02d002c802be, 0x330031e047f0798, 0x3b802c4024e0342, + 0x473056b047b03e5, 0x91104df072a06c4, 0x73005b09290013, + 0x115010d03530339, 0x2f302af02bd0626, 0x10f0650031708f0, + 0x4fc029b02ef02b7, 0x2df02d102c902bf, 0x331031f04800799, + 0x3b902c5024f0343, 0x474056c047c03e6, 0x91204e0072b06c5, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); //4000 bytes -enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0xb0], [ 0x100, 0x240, 0x5100], [ 0x706050403020100, 0xe0d0c0a0b0a0908, 0x100a0f0303030303, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x7000600050004, 0xb000a00090008, 0xf000e000d000c, 0x12001100010010, 0x15001400010013, 0x19001800170016, 0x1c0001001b001a, 0x1f001f001e001d, 0x1f001f001f0020, 0x1f001f001f001f, 0x1f002300220021, 0x1f001f00250024, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100260001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x27000100010001, 0x1000100010001, 0x2a002900010028, 0x2e002d002c002b, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x2f000100010001, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x3100300001001f, 0x34003300320001, 0x38003700360035, 0x1f001f001f0039, 0x3d003c003b003a, 0x1f001f001f003e, 0x1f001f0040003f, 0x1f0041001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x42000100010001, 0x1f001f001f0043, 0x1f001f001f001f, 0x1f001f001f001f, 0x1000100010001, 0x1f001f001f0044, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f004500010001, 0x46001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f0047, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x4b004a00490048, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f004c001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1000100010001, 0x1004d00010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x4e000100010001, 0x1f001f001f004f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f004f00010001, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x0, 0x7fffffe07fffffe, 0x420040000000000, 0xff7fffffff7fffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x501f0003ffc3, 0x0, 0x3cdf000000000020, 0xfffffffbffffd740, 0xffbfffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffffc03, 0xffffffffffffffff, 0xfffe00ffffffffff, 0xfffffffe027fffff, 0xbfff0000000000ff, 0x707ffffff00b6, 0xffffffff07ff0000, 0xffffc000feffffff, 0xffffffffffffffff, 0x9c00e1fe1fefffff, 0xffffffffffff0000, 0xffffffffffffe000, 0x3ffffffffffff, 0x43007fffffffc00, 0x1ffffcffffff, 0x1ffffff, 0x1ffd00000000, 0x7fff03f000000000, 0xefffffffffffffff, 0xfefe000fffe1dfff, 0xe3c5fdfffff99fee, 0x3000fb080599f, 0xc36dfdfffff987ee, 0x3f00005e021987, 0xe3edfdfffffbbfee, 0xf00011bbf, 0xe3edfdfffff99fee, 0x2000fb0c0199f, 0xc3ffc718d63dc7ec, 0x811dc7, 0xe3effdfffffddfee, 0xf03601ddf, 0xe3effdfffffddfec, 0x6000f40601ddf, 0xe7fffffffffddfec, 0xfc00000f00805ddf, 0x2ffbfffffc7fffec, 0xc0000ff5f807f, 0x7fffffffffffffe, 0x207f, 0x3bffecaefef02596, 0xf000205f, 0x1, 0xfffe1ffffffffeff, 0x1ffffffffeffff03, 0x0, 0xf97fffffffffffff, 0xffffc1e7ffff0000, 0xffffffff3000407f, 0xf7ffffffffff20bf, 0xffffffffffffffff, 0xffffffff3d7f3dff, 0x7f3dffffffff3dff, 0xffffffffff7fff3d, 0xffffffffff3dffff, 0x87ffffff, 0xffffffff0000ffff, 0x1fffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff9fffffffffff, 0xffffffff07fffffe, 0x1c7ffffffffff, 0xfffff000fdfff, 0xddfff000fffff, 0xffcfffffffffffff, 0x108001ff, 0xffffffff00000000, 0xffffffffffffff, 0xffff07ffffffffff, 0x3fffffffffffff, 0x1ff0fff1fffffff, 0x1f3fffffff0000, 0xffff0fffffffffff, 0x3ff, 0xffffffff0fffffff, 0x1ffffe7fffffff, 0x8000000000, 0x0, 0xffefffffffffffff, 0xfef, 0xfc00f3ffffffffff, 0x3ffbfffffffff, 0x3fffffffffffff, 0x3ffffffffc00e000, 0x0, 0x6fde0000000000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0x5fdfffffffffffff, 0x1fdc1fff0fcf1fdc, 0x0, 0x8002000000000000, 0x1fff0000, 0x0, 0xf3ffbd503e2ffc84, 0xffffffff000043e0, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffc0000000000000, 0x3ffffffffff, 0xffff7fffffffffff, 0xffffffff7fffffff, 0xffffffffffffffff, 0xc781fffffffff, 0xffff20bfffffffff, 0x80ffffffffff, 0x7f7f7f7f007fffff, 0xffffffff7f7f7f7f, 0x800000000000, 0x0, 0x0, 0x0, 0x1f3e03fe000000e0, 0xfffffffffffffffe, 0xfffffffee07fffff, 0xf7ffffffffffffff, 0xfffe3fffffffffe0, 0xffffffffffffffff, 0x7ffffff00007fff, 0xffff000000000000, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fff, 0x3fffffffffff0000, 0xc00ffff1fff, 0x8ff07fffffffffff, 0xffffffff80ffffff, 0xffffffffffff, 0xfffffffcff800000, 0xffffffffffffffff, 0x7ff000f79ff, 0xff00000000000000, 0xfffffff7bb, 0xfffffffffffff, 0xffffffffffffffff, 0x8fc00000000000f, 0xffff07fffffffc00, 0x1fffffff0007ffff, 0xfff7ffffffffffff, 0x8000, 0x7fffffffffffff, 0x47fffff00003fff, 0x7fffffffffffffff, 0x3cffff38000005, 0x7f7f007e7e7e, 0x0, 0x0, 0x7ffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff000fffffffff, 0xffffffffffff87f, 0xffffffffffffffff, 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7ffdffe0f8007f, 0xffffffffffffffdb, 0x3ffffffffffff, 0xfffffffffff80000, 0x3fffffffffffffff, 0xffffffffffff0000, 0xfffffffffffcffff, 0xfff0000000000ff, 0x0, 0xffdf000000000000, 0xffffffffffffffff, 0x1fffffffffffffff, 0x7fffffe00000000, 0xffffffc007fffffe, 0x7fffffffffffffff, 0x1cfcfcfc, 0xb7ffff7fffffefff, 0x3fff3fff, 0xffffffffffffffff, 0x7ffffffffffffff, 0x0, 0x1fffffffffffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff1fffffff, 0x1ffff, 0xffff00007fffffff, 0x7ff, 0xffffffff3fffffff, 0x3eff0f, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffff, 0x0, 0x91bffffffffffd3f, 0x3fffff, 0x0, 0x0, 0x3ffffff003fffff, 0x0, 0xc0ffffffffffffff, 0x0, 0xffffffeeff06f, 0x1fffffff00000000, 0x0, 0x0, 0x3fffffffffffff, 0x7ffff003fffff, 0x0, 0x0, 0xffffffffffffffff, 0x1ff, 0x0, 0x0, 0xffffffffffffffff, 0x3f, 0x1fffffffffffffc, 0x1ffffff0000, 0x7ffffffffffff, 0x0, 0xffffffffffffffff, 0x1e, 0x0, 0x0, 0x3fffffffffffff, 0x0, 0xffffffffffffffff, 0x7fffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0x7ffffffff, 0x0, 0x0, 0x7fffffffffff, 0x0, 0x0, 0x0, 0x1ffffffffffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x7fffffffffff001f, 0xfff80000, 0x0, 0x3, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffdfffff, 0xebffde64dfffffff, 0xffffffffffffffef, 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffff3fffffffff, 0xf7fffffff7fffffd, 0xffdfffffffdfffff, 0xffff7fffffff7fff, 0xfffffdfffffffdff, 0xff7, 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffff, 0x1fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0]); +enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0xb0], [0x100, + 0x240, 0x5100], [0x706050403020100, 0xe0d0c0a0b0a0908, + 0x100a0f0303030303, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x7000600050004, 0xb000a00090008, 0xf000e000d000c, + 0x12001100010010, 0x15001400010013, 0x19001800170016, 0x1c0001001b001a, + 0x1f001f001e001d, 0x1f001f001f0020, 0x1f001f001f001f, 0x1f002300220021, + 0x1f001f00250024, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100260001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x27000100010001, + 0x1000100010001, 0x2a002900010028, 0x2e002d002c002b, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x2f000100010001, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x3100300001001f, 0x34003300320001, + 0x38003700360035, 0x1f001f001f0039, 0x3d003c003b003a, 0x1f001f001f003e, + 0x1f001f0040003f, 0x1f0041001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x42000100010001, 0x1f001f001f0043, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1000100010001, 0x1f001f001f0044, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f004500010001, 0x46001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f0047, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x4b004a00490048, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f004c001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1000100010001, 0x1004d00010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x4e000100010001, 0x1f001f001f004f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f004f00010001, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, 0x1f001f001f001f, + 0x0, 0x7fffffe07fffffe, 0x420040000000000, 0xff7fffffff7fffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x501f0003ffc3, 0x0, 0x3cdf000000000020, + 0xfffffffbffffd740, 0xffbfffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfffffffffffffc03, 0xffffffffffffffff, + 0xfffe00ffffffffff, 0xfffffffe027fffff, 0xbfff0000000000ff, + 0x707ffffff00b6, 0xffffffff07ff0000, 0xffffc000feffffff, + 0xffffffffffffffff, 0x9c00e1fe1fefffff, 0xffffffffffff0000, + 0xffffffffffffe000, 0x3ffffffffffff, 0x43007fffffffc00, 0x1ffffcffffff, + 0x1ffffff, 0x1ffd00000000, 0x7fff03f000000000, 0xefffffffffffffff, + 0xfefe000fffe1dfff, 0xe3c5fdfffff99fee, 0x3000fb080599f, + 0xc36dfdfffff987ee, 0x3f00005e021987, 0xe3edfdfffffbbfee, 0xf00011bbf, + 0xe3edfdfffff99fee, 0x2000fb0c0199f, 0xc3ffc718d63dc7ec, 0x811dc7, + 0xe3effdfffffddfee, 0xf03601ddf, 0xe3effdfffffddfec, 0x6000f40601ddf, + 0xe7fffffffffddfec, 0xfc00000f00805ddf, 0x2ffbfffffc7fffec, + 0xc0000ff5f807f, 0x7fffffffffffffe, 0x207f, 0x3bffecaefef02596, + 0xf000205f, 0x1, 0xfffe1ffffffffeff, 0x1ffffffffeffff03, 0x0, + 0xf97fffffffffffff, 0xffffc1e7ffff0000, 0xffffffff3000407f, + 0xf7ffffffffff20bf, 0xffffffffffffffff, 0xffffffff3d7f3dff, + 0x7f3dffffffff3dff, 0xffffffffff7fff3d, 0xffffffffff3dffff, 0x87ffffff, + 0xffffffff0000ffff, 0x1fffffffffffff, 0xfffffffffffffffe, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff9fffffffffff, 0xffffffff07fffffe, + 0x1c7ffffffffff, 0xfffff000fdfff, 0xddfff000fffff, 0xffcfffffffffffff, + 0x108001ff, 0xffffffff00000000, 0xffffffffffffff, 0xffff07ffffffffff, + 0x3fffffffffffff, 0x1ff0fff1fffffff, 0x1f3fffffff0000, + 0xffff0fffffffffff, 0x3ff, 0xffffffff0fffffff, 0x1ffffe7fffffff, + 0x8000000000, 0x0, 0xffefffffffffffff, 0xfef, 0xfc00f3ffffffffff, + 0x3ffbfffffffff, 0x3fffffffffffff, 0x3ffffffffc00e000, 0x0, + 0x6fde0000000000, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x0, 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, + 0x5fdfffffffffffff, 0x1fdc1fff0fcf1fdc, 0x0, 0x8002000000000000, + 0x1fff0000, 0x0, 0xf3ffbd503e2ffc84, 0xffffffff000043e0, 0x1ff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffc0000000000000, 0x3ffffffffff, + 0xffff7fffffffffff, 0xffffffff7fffffff, 0xffffffffffffffff, + 0xc781fffffffff, 0xffff20bfffffffff, 0x80ffffffffff, + 0x7f7f7f7f007fffff, 0xffffffff7f7f7f7f, 0x800000000000, 0x0, 0x0, 0x0, + 0x1f3e03fe000000e0, 0xfffffffffffffffe, 0xfffffffee07fffff, + 0xf7ffffffffffffff, 0xfffe3fffffffffe0, 0xffffffffffffffff, + 0x7ffffff00007fff, 0xffff000000000000, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3fffffffffffff, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1fff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x1fff, 0x3fffffffffff0000, 0xc00ffff1fff, + 0x8ff07fffffffffff, 0xffffffff80ffffff, 0xffffffffffff, + 0xfffffffcff800000, 0xffffffffffffffff, 0x7ff000f79ff, + 0xff00000000000000, 0xfffffff7bb, 0xfffffffffffff, 0xffffffffffffffff, + 0x8fc00000000000f, 0xffff07fffffffc00, 0x1fffffff0007ffff, + 0xfff7ffffffffffff, 0x8000, 0x7fffffffffffff, 0x47fffff00003fff, + 0x7fffffffffffffff, 0x3cffff38000005, 0x7f7f007e7e7e, 0x0, 0x0, + 0x7ffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff000fffffffff, 0xffffffffffff87f, 0xffffffffffffffff, + 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7ffdffe0f8007f, + 0xffffffffffffffdb, 0x3ffffffffffff, 0xfffffffffff80000, + 0x3fffffffffffffff, 0xffffffffffff0000, 0xfffffffffffcffff, + 0xfff0000000000ff, 0x0, 0xffdf000000000000, 0xffffffffffffffff, + 0x1fffffffffffffff, 0x7fffffe00000000, 0xffffffc007fffffe, + 0x7fffffffffffffff, 0x1cfcfcfc, 0xb7ffff7fffffefff, 0x3fff3fff, + 0xffffffffffffffff, 0x7ffffffffffffff, 0x0, 0x1fffffffffffff, 0x0, 0x0, + 0x0, 0x0, 0xffffffff1fffffff, 0x1ffff, 0xffff00007fffffff, 0x7ff, + 0xffffffff3fffffff, 0x3eff0f, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3fffffff, 0x0, 0x91bffffffffffd3f, 0x3fffff, 0x0, 0x0, + 0x3ffffff003fffff, 0x0, 0xc0ffffffffffffff, 0x0, 0xffffffeeff06f, + 0x1fffffff00000000, 0x0, 0x0, 0x3fffffffffffff, 0x7ffff003fffff, 0x0, + 0x0, 0xffffffffffffffff, 0x1ff, 0x0, 0x0, 0xffffffffffffffff, 0x3f, + 0x1fffffffffffffc, 0x1ffffff0000, 0x7ffffffffffff, 0x0, + 0xffffffffffffffff, 0x1e, 0x0, 0x0, 0x3fffffffffffff, 0x0, + 0xffffffffffffffff, 0x7fffffffffff, 0x0, 0x0, 0xffffffffffffffff, + 0x7ffffffff, 0x0, 0x0, 0x7fffffffffff, 0x0, 0x0, 0x0, + 0x1ffffffffffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0x7fffffffffff001f, 0xfff80000, 0x0, 0x3, 0x0, 0x0, 0x0, + 0xffffffffffffffff, 0xffffffffffdfffff, 0xebffde64dfffffff, + 0xffffffffffffffef, 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffff3fffffffff, 0xf7fffffff7fffffd, + 0xffdfffffffdfffff, 0xffff7fffffff7fff, 0xfffffdfffffffdff, 0xff7, + 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, 0x0, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffff, + 0x1fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0]); //2304 bytes -enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x70], [ 0x100, 0x140, 0x2c00], [ 0x402030202020100, 0x207020206020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020208, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000, 0x5000400030002, 0x9000800070006, 0xd000c000b000a, 0xf00000000000e, 0x10000000000000, 0x14001300120011, 0x160015, 0x17, 0x0, 0x0, 0x190018, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b00000000, 0x1f001e001d001c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000, 0x2100000000, 0x220000, 0x0, 0x2300000000, 0x0, 0x250024, 0x2600000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x27000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2900280000, 0x0, 0x0, 0x0, 0x2a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, 0x0, 0x0, 0xbffffffffffe0000, 0xb6, 0x7ff0000, 0x10000fffff800, 0x0, 0x3d9f9fc00000, 0xffff000000020000, 0x7ff, 0x1ffc000000000, 0xff80000000000, 0x3eeffbc00000, 0xe000000, 0x0, 0x7ffffff000000000, 0xdc0000000000000f, 0xc00feffff, 0xd00000000000000e, 0xc0080399f, 0xd00000000000000e, 0x23000000023987, 0xd00000000000000e, 0xc00003bbf, 0xd00000000000000e, 0xc00c0399f, 0xc000000000000004, 0x803dc7, 0xc00000000000000e, 0xc00603ddf, 0xd00000000000000c, 0xc00603ddf, 0xc00000000000000c, 0xc00803ddf, 0xc, 0xc0000ff5f8400, 0x7f2000000000000, 0x7f80, 0x1bf2000000000000, 0x3f00, 0xc2a0000003000000, 0xfffe000000000000, 0x1ffffffffeffe0df, 0x40, 0x7ffff80000000000, 0x1e3f9dc3c00000, 0x3c00bffc, 0x0, 0x0, 0xe0000000, 0x0, 0x0, 0x1c0000001c0000, 0xc0000000c0000, 0xfff0000000000000, 0x200fffff, 0x3800, 0x0, 0x20000000000, 0x0, 0xfff0fff00000000, 0x0, 0xffff000000000000, 0x301, 0xf800000, 0x9fffffff7fe00000, 0x0, 0x0, 0xfff000000000001f, 0xff8000000001f, 0x3ffe00000007, 0xfffc000000000, 0xfffff000000000, 0x0, 0x0, 0x1c21fffff70000, 0x0, 0x0, 0x0, 0xf000007fffffffff, 0x0, 0x0, 0x0, 0x1ffffffff0000, 0x0, 0x0, 0x0, 0x3800000000000, 0x0, 0x8000000000000000, 0x0, 0xffffffff00000000, 0xfc0000000000, 0x0, 0x6000000, 0x0, 0x0, 0x3ff7800000000000, 0x80000000, 0x3000000000000, 0xf800000844, 0x0, 0xfff0000000000003, 0x3ffff0000001f, 0x3fc000000000, 0xfff80, 0xfff800000000000f, 0x1, 0x7ffe0000000000, 0x800000000003008, 0xc19d000000000000, 0x60f80000000002, 0x0, 0x0, 0x0, 0x37f800000000, 0x40000000, 0x0, 0x0, 0x0, 0x7f0000ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000000, 0x870000000000f06e, 0x0, 0x0, 0x0, 0xff00000000000007, 0x7f, 0x7ff000000000007, 0x0, 0x1fff8000000007, 0x0, 0xfff8000000000007, 0x1, 0x0, 0x0, 0xfff80000000000, 0x0, 0x0, 0x7ffffffffffe0000, 0x78000, 0x0, 0x0, 0xf807e3e000000000, 0x3c0000000fe7, 0x0, 0x0, 0x1c, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0]); +enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x70], [0x100, + 0x140, 0x2c00], [0x402030202020100, 0x207020206020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020208, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1000000000000, 0x5000400030002, 0x9000800070006, 0xd000c000b000a, + 0xf00000000000e, 0x10000000000000, 0x14001300120011, 0x160015, 0x17, + 0x0, 0x0, 0x190018, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1b00000000, 0x1f001e001d001c, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000, 0x2100000000, 0x220000, + 0x0, 0x2300000000, 0x0, 0x250024, 0x2600000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x27000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2900280000, 0x0, 0x0, 0x0, 0x2a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, + 0x0, 0x0, 0xbffffffffffe0000, 0xb6, 0x7ff0000, 0x10000fffff800, 0x0, + 0x3d9f9fc00000, 0xffff000000020000, 0x7ff, 0x1ffc000000000, + 0xff80000000000, 0x3eeffbc00000, 0xe000000, 0x0, 0x7ffffff000000000, + 0xdc0000000000000f, 0xc00feffff, 0xd00000000000000e, 0xc0080399f, + 0xd00000000000000e, 0x23000000023987, 0xd00000000000000e, 0xc00003bbf, + 0xd00000000000000e, 0xc00c0399f, 0xc000000000000004, 0x803dc7, + 0xc00000000000000e, 0xc00603ddf, 0xd00000000000000c, 0xc00603ddf, + 0xc00000000000000c, 0xc00803ddf, 0xc, 0xc0000ff5f8400, + 0x7f2000000000000, 0x7f80, 0x1bf2000000000000, 0x3f00, + 0xc2a0000003000000, 0xfffe000000000000, 0x1ffffffffeffe0df, 0x40, + 0x7ffff80000000000, 0x1e3f9dc3c00000, 0x3c00bffc, 0x0, 0x0, 0xe0000000, + 0x0, 0x0, 0x1c0000001c0000, 0xc0000000c0000, 0xfff0000000000000, + 0x200fffff, 0x3800, 0x0, 0x20000000000, 0x0, 0xfff0fff00000000, 0x0, + 0xffff000000000000, 0x301, 0xf800000, 0x9fffffff7fe00000, 0x0, 0x0, + 0xfff000000000001f, 0xff8000000001f, 0x3ffe00000007, 0xfffc000000000, + 0xfffff000000000, 0x0, 0x0, 0x1c21fffff70000, 0x0, 0x0, 0x0, + 0xf000007fffffffff, 0x0, 0x0, 0x0, 0x1ffffffff0000, 0x0, 0x0, 0x0, + 0x3800000000000, 0x0, 0x8000000000000000, 0x0, 0xffffffff00000000, + 0xfc0000000000, 0x0, 0x6000000, 0x0, 0x0, 0x3ff7800000000000, + 0x80000000, 0x3000000000000, 0xf800000844, 0x0, 0xfff0000000000003, + 0x3ffff0000001f, 0x3fc000000000, 0xfff80, 0xfff800000000000f, 0x1, + 0x7ffe0000000000, 0x800000000003008, 0xc19d000000000000, + 0x60f80000000002, 0x0, 0x0, 0x0, 0x37f800000000, 0x40000000, 0x0, 0x0, + 0x0, 0x7f0000ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000000, + 0x870000000000f06e, 0x0, 0x0, 0x0, 0xff00000000000007, 0x7f, + 0x7ff000000000007, 0x0, 0x1fff8000000007, 0x0, 0xfff8000000000007, 0x1, + 0x0, 0x0, 0xfff80000000000, 0x0, 0x0, 0x7ffffffffffe0000, 0x78000, 0x0, + 0x0, 0xf807e3e000000000, 0x3c0000000fe7, 0x0, 0x0, 0x1c, 0x0, 0x0, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffff, 0x0, 0x0, 0x0, 0x0]); //2384 bytes -enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)([ 0x0, 0x20, 0xc0], [ 0x100, 0x280, 0x1a80], [ 0x402030202020100, 0x807020202020605, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000200010000, 0x2000200020002, 0x2000200020002, 0x5000200040003, 0x7000600020002, 0x9000800060006, 0x2000b0006000a, 0x2000d000c000c, 0x20002000e0005, 0x2000f00020002, 0x2000200020002, 0x11000200100002, 0x1300120002000e, 0xc00140002, 0x2000200020015, 0x2000200020002, 0x19001800170016, 0x2000200020002, 0x20002001b001a, 0x1d001c00020002, 0x2000200020002, 0x2000200020002, 0x20002001e0002, 0x2000200020002, 0x2000020002001f, 0x2000200220021, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200060023, 0xc0017000c0024, 0x400020002000c, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000e00020002, 0x26002500020002, 0x28002700020002, 0x2000200230002, 0x2000200020002, 0x2002a00020029, 0x2002c0002002b, 0x2000200020002, 0x200020002002d, 0xc002f0004002e, 0x2000200020002, 0x2000200020002, 0x2000200050002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020030, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2003100020002, 0x2000200020002, 0x32000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2003300020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x3ff000000000000, 0x0, 0x720c000000000000, 0x0, 0x0, 0x0, 0x0, 0x3ff00000000, 0x0, 0x3ff000000000000, 0x0, 0x3ff, 0x0, 0xffc000000000, 0x0, 0x3f0ffc000000000, 0x0, 0xfcffc000000000, 0x0, 0x7ffc000000000, 0x0, 0x7f00ffc000000000, 0x0, 0x3fffc000000000, 0x0, 0x3ff0000, 0xfffff00000000, 0x0, 0x3ff0000, 0x0, 0x0, 0x1ffffe0000000000, 0x0, 0x1c00000000000, 0x0, 0x3ff03ff00000000, 0x0, 0xffc0, 0x0, 0x7ff0000, 0x3ff03ff, 0x0, 0x0, 0x3ff03ff, 0x0, 0x3f1000000000000, 0x3ff, 0x0, 0x0, 0xffffffffffff0000, 0x3e7, 0x0, 0x0, 0xffffffff00000000, 0xfffffff, 0xfffffc0000000000, 0x0, 0xffc0000000000000, 0xfffff, 0x0, 0x0, 0x2000000000000000, 0x70003fe00000080, 0x0, 0x3c0000, 0x0, 0x3ff00000000, 0xfffeff00, 0xfffe0000000003ff, 0x0, 0x3ff00000000, 0x0, 0x3f000000000000, 0x0, 0xfffffffffff80, 0x1ffffffffffffff, 0x400, 0x0, 0xf00000000, 0x402, 0x0, 0x3e0000, 0x0, 0xff000000, 0xfc00000, 0x0, 0x0, 0x60000000000000ff, 0x0, 0xff000000ff000000, 0x0, 0x7fffffff00000000, 0x0, 0xfffffffc0000, 0xffc0000000000000, 0x0, 0xffffffffffffffff, 0x7ffffffff, 0x0, 0x3ffff00000000, 0x0, 0xffffffffffffc000, 0x7ff, 0x0, 0x0, 0x0]); +enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)([0x0, 0x20, 0xc0], [0x100, + 0x280, 0x1a80], [0x402030202020100, 0x807020202020605, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2000200010000, 0x2000200020002, 0x2000200020002, 0x5000200040003, + 0x7000600020002, 0x9000800060006, 0x2000b0006000a, 0x2000d000c000c, + 0x20002000e0005, 0x2000f00020002, 0x2000200020002, 0x11000200100002, + 0x1300120002000e, 0xc00140002, 0x2000200020015, 0x2000200020002, + 0x19001800170016, 0x2000200020002, 0x20002001b001a, 0x1d001c00020002, + 0x2000200020002, 0x2000200020002, 0x20002001e0002, 0x2000200020002, + 0x2000020002001f, 0x2000200220021, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200060023, + 0xc0017000c0024, 0x400020002000c, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000e00020002, + 0x26002500020002, 0x28002700020002, 0x2000200230002, 0x2000200020002, + 0x2002a00020029, 0x2002c0002002b, 0x2000200020002, 0x200020002002d, + 0xc002f0004002e, 0x2000200020002, 0x2000200020002, 0x2000200050002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020030, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2003100020002, 0x2000200020002, 0x32000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2003300020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x2000200020002, 0x2000200020002, 0x2000200020002, 0x2000200020002, + 0x3ff000000000000, 0x0, 0x720c000000000000, 0x0, 0x0, 0x0, 0x0, + 0x3ff00000000, 0x0, 0x3ff000000000000, 0x0, 0x3ff, 0x0, 0xffc000000000, + 0x0, 0x3f0ffc000000000, 0x0, 0xfcffc000000000, 0x0, 0x7ffc000000000, + 0x0, 0x7f00ffc000000000, 0x0, 0x3fffc000000000, 0x0, 0x3ff0000, + 0xfffff00000000, 0x0, 0x3ff0000, 0x0, 0x0, 0x1ffffe0000000000, 0x0, + 0x1c00000000000, 0x0, 0x3ff03ff00000000, 0x0, 0xffc0, 0x0, 0x7ff0000, + 0x3ff03ff, 0x0, 0x0, 0x3ff03ff, 0x0, 0x3f1000000000000, 0x3ff, 0x0, + 0x0, 0xffffffffffff0000, 0x3e7, 0x0, 0x0, 0xffffffff00000000, + 0xfffffff, 0xfffffc0000000000, 0x0, 0xffc0000000000000, 0xfffff, 0x0, + 0x0, 0x2000000000000000, 0x70003fe00000080, 0x0, 0x3c0000, 0x0, + 0x3ff00000000, 0xfffeff00, 0xfffe0000000003ff, 0x0, 0x3ff00000000, 0x0, + 0x3f000000000000, 0x0, 0xfffffffffff80, 0x1ffffffffffffff, 0x400, 0x0, + 0xf00000000, 0x402, 0x0, 0x3e0000, 0x0, 0xff000000, 0xfc00000, 0x0, + 0x0, 0x60000000000000ff, 0x0, 0xff000000ff000000, 0x0, + 0x7fffffff00000000, 0x0, 0xfffffffc0000, 0xffc0000000000000, 0x0, + 0xffffffffffffffff, 0x7ffffffff, 0x0, 0x3ffff00000000, 0x0, + 0xffffffffffffc000, 0x7ff, 0x0, 0x0, 0x0]); //2336 bytes -enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x60], [ 0x100, 0x100, 0x3100], [ 0x402030202020100, 0x202020202020605, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000100010000, 0x5000400030001, 0x1000800070006, 0xb000a00090001, 0xd00010001000c, 0x10000f0001000e, 0x14001300120011, 0x1000100010015, 0x17000100010016, 0x18000100010001, 0x1000100190001, 0x1001c001b001a, 0x100010001001d, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1001f0001001e, 0x23002200210020, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x26002500240001, 0x28000100270001, 0x1000100010001, 0x2c002b002a0029, 0x1000100010001, 0x10001002e002d, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x100010001002f, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x8c00f7ee00000000, 0x28000000b8000001, 0x88c0088200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000000, 0x80, 0x0, 0x0, 0xfc000000, 0x4000000000000600, 0x18000000000049, 0xc8003600, 0x3c0000000000, 0x0, 0x100000, 0x3fff, 0x0, 0x0, 0x380000000000000, 0x7fff000000000000, 0x40000000, 0x0, 0x0, 0x0, 0x1003000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000, 0x0, 0x0, 0x0, 0x10000000000000, 0x0, 0xc008000, 0x0, 0x0, 0x3c0000000017fff0, 0x0, 0x20, 0x61f0000, 0x0, 0xfc00, 0x0, 0x800000000000000, 0x0, 0x1ff00000000, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x600000000000, 0x18000000, 0x380000000000, 0x60000000000000, 0x0, 0x0, 0x7700000, 0x7ff, 0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0xc0000000, 0x0, 0x3f7f00000000, 0x0, 0x0, 0x1fc000000, 0x0, 0xf000000000000000, 0xf800000000000000, 0xc000000000000000, 0x0, 0x800ff, 0xffff00ffffff0000, 0x600000007ffbffef, 0x6000, 0x0, 0x60000000f00, 0x0, 0x0, 0x0, 0x0, 0x3fff0000000000, 0x0, 0xffc000000060, 0x0, 0x0, 0x1fffff8, 0x300000000f000000, 0x0, 0x0, 0x0, 0xde00000000000000, 0x0, 0x1000000000000, 0x0, 0x0, 0xfff7fffffffffff, 0x0, 0x0, 0x0, 0x20010000fff3ff0e, 0x0, 0x100000000, 0x800000000000000, 0x0, 0x0, 0x0, 0xc000000000000000, 0xe000, 0x4008000000000000, 0x0, 0xfc000000000000, 0x0, 0xf0000000000000, 0x0, 0x70000000000c000, 0xc00000000000, 0x80000000, 0x0, 0xc0003ffe, 0x0, 0xf0000000, 0x0, 0x30000c0000000, 0x0, 0x0, 0x0, 0x80000000000, 0xc000000000000000, 0x0, 0x0, 0x0, 0xffff000003ff0000, 0xd0bfff7ffff, 0x0, 0x0, 0xb80000018c00f7ee, 0x3fa8000000, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x10000, 0x0, 0x800000, 0x0, 0x0, 0x8000000080000000, 0x0, 0x0, 0x0, 0x0, 0x8000000001ff0000, 0x0, 0x0, 0xfe00000000000000, 0x0, 0x0, 0x0, 0x0, 0x3f80, 0xd800000000000000, 0x3, 0x0, 0xf, 0x0, 0x1e0, 0x0, 0xf000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x60], + [0x100, 0x100, 0x3100], [0x402030202020100, 0x202020202020605, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2000100010000, 0x5000400030001, 0x1000800070006, 0xb000a00090001, + 0xd00010001000c, 0x10000f0001000e, 0x14001300120011, 0x1000100010015, + 0x17000100010016, 0x18000100010001, 0x1000100190001, 0x1001c001b001a, + 0x100010001001d, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1001f0001001e, 0x23002200210020, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x26002500240001, + 0x28000100270001, 0x1000100010001, 0x2c002b002a0029, 0x1000100010001, + 0x10001002e002d, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x100010001002f, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x8c00f7ee00000000, 0x28000000b8000001, 0x88c0088200000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x4000000000000000, 0x80, 0x0, 0x0, 0xfc000000, + 0x4000000000000600, 0x18000000000049, 0xc8003600, 0x3c0000000000, 0x0, + 0x100000, 0x3fff, 0x0, 0x0, 0x380000000000000, 0x7fff000000000000, + 0x40000000, 0x0, 0x0, 0x0, 0x1003000000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1000000000000, 0x0, 0x0, 0x0, 0x10000000000000, 0x0, 0xc008000, 0x0, + 0x0, 0x3c0000000017fff0, 0x0, 0x20, 0x61f0000, 0x0, 0xfc00, 0x0, + 0x800000000000000, 0x0, 0x1ff00000000, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, + 0x0, 0x600000000000, 0x18000000, 0x380000000000, 0x60000000000000, 0x0, + 0x0, 0x7700000, 0x7ff, 0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0xc0000000, + 0x0, 0x3f7f00000000, 0x0, 0x0, 0x1fc000000, 0x0, 0xf000000000000000, + 0xf800000000000000, 0xc000000000000000, 0x0, 0x800ff, + 0xffff00ffffff0000, 0x600000007ffbffef, 0x6000, 0x0, 0x60000000f00, + 0x0, 0x0, 0x0, 0x0, 0x3fff0000000000, 0x0, 0xffc000000060, 0x0, 0x0, + 0x1fffff8, 0x300000000f000000, 0x0, 0x0, 0x0, 0xde00000000000000, 0x0, + 0x1000000000000, 0x0, 0x0, 0xfff7fffffffffff, 0x0, 0x0, 0x0, + 0x20010000fff3ff0e, 0x0, 0x100000000, 0x800000000000000, 0x0, 0x0, 0x0, + 0xc000000000000000, 0xe000, 0x4008000000000000, 0x0, 0xfc000000000000, + 0x0, 0xf0000000000000, 0x0, 0x70000000000c000, 0xc00000000000, + 0x80000000, 0x0, 0xc0003ffe, 0x0, 0xf0000000, 0x0, 0x30000c0000000, + 0x0, 0x0, 0x0, 0x80000000000, 0xc000000000000000, 0x0, 0x0, 0x0, + 0xffff000003ff0000, 0xd0bfff7ffff, 0x0, 0x0, 0xb80000018c00f7ee, + 0x3fa8000000, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, + 0x10000, 0x0, 0x800000, 0x0, 0x0, 0x8000000080000000, 0x0, 0x0, 0x0, + 0x0, 0x8000000001ff0000, 0x0, 0x0, 0xfe00000000000000, 0x0, 0x0, 0x0, + 0x0, 0x3f80, 0xd800000000000000, 0x3, 0x0, 0xf, 0x0, 0x1e0, 0x0, + 0xf000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //2848 bytes -enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0x70], [ 0x100, 0x140, 0x3d00], [ 0x503040303020100, 0x807030303030306, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x7000600050004, 0xa000900080001, 0xe000d000c000b, 0x1000010001000f, 0x11000100010001, 0x13000100120001, 0x14000100010001, 0x18001700160015, 0x1a001700170019, 0x1c0017001b0017, 0x1f001e0001001d, 0x17002200210020, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100230001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x25000100010024, 0x1002700010026, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x28000100010001, 0x2b002a00290001, 0x10001002c0001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x30002f002e002d, 0x32003100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1003300010001, 0x37003600350034, 0x3b003a00390038, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x7000081000000000, 0x5000000140000000, 0x113d37c00000000, 0x80000000800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffafe0fffc003c, 0x0, 0x20000000000000, 0x30, 0x40000000000000, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x8000, 0x0, 0xc9c0, 0x0, 0x0, 0x6000020040000000, 0x0, 0x0, 0x0, 0x40000000000000, 0x0, 0x0, 0x0, 0xc0c000000000000, 0x0, 0x0, 0x0, 0x2000000000000, 0x0, 0x1000000000000, 0x0, 0x7f8000000000000, 0x0, 0x8000000000000000, 0x0, 0x0, 0x0, 0x200000000000000, 0x0, 0x0, 0x8000000000000000, 0x0, 0x0, 0x0, 0x1500000fce8000e, 0x0, 0xc000000000000000, 0x1e0dfbf, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x0, 0x0, 0x8000000, 0x0, 0x1, 0x0, 0xffffffffc0000000, 0x0, 0x1ff007fe00000000, 0x0, 0x0, 0x0, 0x0, 0xa000000000000000, 0x6000e000e000e003, 0x0, 0x1c00000000040010, 0x7ffffff00001c00, 0x0, 0xc0042afc1d0037b, 0xbc1f, 0xffffffffffff0000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffff9fffffff0ff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffff, 0x7fffffffff, 0x7ff, 0xfffffffff0000000, 0x3ffffffffff, 0xfffffffffffffffe, 0xffffffffff, 0xfffffffffff00000, 0xffff003fffffff9f, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffe000007, 0xcffffffff0ffffff, 0xffffffffffffffff, 0x3ff1fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e000000000, 0x0, 0x0, 0xfffffffffbffffff, 0xfffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfff0000003fffff, 0xc0c00001000c0010, 0x0, 0x18000000, 0x0, 0x0, 0x0, 0xffc30000, 0xfffffffff, 0xfffffc007fffffff, 0xffffffff000100ff, 0x1fffffffffc00, 0x7fffffffffffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffff0000, 0x7f, 0x3007fffff, 0x0, 0x600, 0x0, 0x3c00f0000000000, 0x0, 0x0, 0x0, 0x0, 0x380000000000000, 0x0, 0x0, 0x20000000000, 0x0, 0xfffc000000000000, 0x3, 0x0, 0x0, 0x0, 0x3000000000000000, 0x0, 0x27400000000, 0x0, 0x0, 0x4000000070000810, 0x50000001, 0x0, 0x30007f7f00000000, 0xff80000000000000, 0xfe00000000000000, 0xfff03ff, 0x1fffffffffff0000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff, 0xfffffe7fffffffff, 0x1c1fffffffff, 0xffffc3fffffff018, 0x3fffffff, 0xffffffffffffffff, 0x23, 0x0, 0x0, 0xffffffffffffffff, 0x7fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000008000002, 0x20000000200000, 0x800000008000, 0x20000000200, 0x8, 0x0, 0x0, 0x0, 0x3000000000000, 0xffff0fffffffffff, 0xffffffffffffffff, 0x7ffe7fff000fffff, 0xfffefffe, 0xffff7fffffff0000, 0xffff0fffffffffff, 0x7ffffff, 0xffffffc000000000, 0x7ffffffffff0007, 0x301ff, 0x0, 0x0, 0xffbf0001ffffffff, 0x1fffffffffffffff, 0xffffffff000fffff, 0x1ffff000007df, 0x7fffffffffffffff, 0xfffffffffffffffd, 0xffffffffffffffff, 0x1effffffffffffff, 0x3fffffffffffffff, 0xffffff000f, 0x0, 0xf800000000000000, 0xffffffffffffffff, 0xffe1, 0xffffffffffffffff, 0x3f, 0xffffffffffffffff, 0xfffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0x70], [0x100, + 0x140, 0x3d00], [0x503040303020100, 0x807030303030306, + 0x303030303030303, 0x303030303030303, 0x303030303030303, + 0x303030303030303, 0x303030303030303, 0x303030303030303, + 0x303030303030303, 0x303030303030303, 0x303030303030303, + 0x303030303030303, 0x303030303030303, 0x303030303030303, + 0x303030303030303, 0x303030303030303, 0x303030303030303, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x7000600050004, 0xa000900080001, 0xe000d000c000b, + 0x1000010001000f, 0x11000100010001, 0x13000100120001, 0x14000100010001, + 0x18001700160015, 0x1a001700170019, 0x1c0017001b0017, 0x1f001e0001001d, + 0x17002200210020, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100230001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x25000100010024, 0x1002700010026, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x28000100010001, 0x2b002a00290001, + 0x10001002c0001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x30002f002e002d, 0x32003100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1003300010001, + 0x37003600350034, 0x3b003a00390038, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x7000081000000000, 0x5000000140000000, 0x113d37c00000000, + 0x80000000800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffafe0fffc003c, 0x0, 0x20000000000000, 0x30, 0x40000000000000, 0x0, + 0x0, 0x4, 0x0, 0x0, 0x0, 0x8000, 0x0, 0xc9c0, 0x0, 0x0, + 0x6000020040000000, 0x0, 0x0, 0x0, 0x40000000000000, 0x0, 0x0, 0x0, + 0xc0c000000000000, 0x0, 0x0, 0x0, 0x2000000000000, 0x0, + 0x1000000000000, 0x0, 0x7f8000000000000, 0x0, 0x8000000000000000, 0x0, + 0x0, 0x0, 0x200000000000000, 0x0, 0x0, 0x8000000000000000, 0x0, 0x0, + 0x0, 0x1500000fce8000e, 0x0, 0xc000000000000000, 0x1e0dfbf, 0x0, 0x0, + 0xc0000000, 0x0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x0, 0x0, 0x8000000, + 0x0, 0x1, 0x0, 0xffffffffc0000000, 0x0, 0x1ff007fe00000000, 0x0, 0x0, + 0x0, 0x0, 0xa000000000000000, 0x6000e000e000e003, 0x0, + 0x1c00000000040010, 0x7ffffff00001c00, 0x0, 0xc0042afc1d0037b, 0xbc1f, + 0xffffffffffff0000, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xfffff9fffffff0ff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xfffffffffffff, 0x7fffffffff, 0x7ff, 0xfffffffff0000000, + 0x3ffffffffff, 0xfffffffffffffffe, 0xffffffffff, 0xfffffffffff00000, + 0xffff003fffffff9f, 0xffffffffffffffff, 0xffffffffffffffff, + 0xfffffffffe000007, 0xcffffffff0ffffff, 0xffffffffffffffff, 0x3ff1fff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e000000000, 0x0, 0x0, 0xfffffffffbffffff, + 0xfffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfff0000003fffff, 0xc0c00001000c0010, 0x0, + 0x18000000, 0x0, 0x0, 0x0, 0xffc30000, 0xfffffffff, 0xfffffc007fffffff, + 0xffffffff000100ff, 0x1fffffffffc00, 0x7fffffffffffffff, 0x0, 0x0, 0x0, + 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffff0000, 0x7f, 0x3007fffff, + 0x0, 0x600, 0x0, 0x3c00f0000000000, 0x0, 0x0, 0x0, 0x0, + 0x380000000000000, 0x0, 0x0, 0x20000000000, 0x0, 0xfffc000000000000, + 0x3, 0x0, 0x0, 0x0, 0x3000000000000000, 0x0, 0x27400000000, 0x0, 0x0, + 0x4000000070000810, 0x50000001, 0x0, 0x30007f7f00000000, + 0xff80000000000000, 0xfe00000000000000, 0xfff03ff, 0x1fffffffffff0000, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3fffffffffffff, 0xfffffe7fffffffff, 0x1c1fffffffff, + 0xffffc3fffffff018, 0x3fffffff, 0xffffffffffffffff, 0x23, 0x0, 0x0, + 0xffffffffffffffff, 0x7fffff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x800000008000002, 0x20000000200000, 0x800000008000, 0x20000000200, + 0x8, 0x0, 0x0, 0x0, 0x3000000000000, 0xffff0fffffffffff, + 0xffffffffffffffff, 0x7ffe7fff000fffff, 0xfffefffe, 0xffff7fffffff0000, + 0xffff0fffffffffff, 0x7ffffff, 0xffffffc000000000, 0x7ffffffffff0007, + 0x301ff, 0x0, 0x0, 0xffbf0001ffffffff, 0x1fffffffffffffff, + 0xffffffff000fffff, 0x1ffff000007df, 0x7fffffffffffffff, + 0xfffffffffffffffd, 0xffffffffffffffff, 0x1effffffffffffff, + 0x3fffffffffffffff, 0xffffff000f, 0x0, 0xf800000000000000, + 0xffffffffffffffff, 0xffe1, 0xffffffffffffffff, 0x3f, + 0xffffffffffffffff, 0xfffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //4576 bytes -enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x20, 0xb8], [ 0x100, 0x260, 0x6100], [ 0x706050403020100, 0xe0d0c0a0b0a0908, 0x100a0f0303030303, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a11, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000100010000, 0x5000400030001, 0x9000800070006, 0xd000c000b000a, 0x10000f0001000e, 0x12001100010001, 0x16001500140013, 0x19000100180017, 0x1c0001001b001a, 0x1e00010001001d, 0x1f000100010001, 0x23002200210020, 0x1002600250024, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100270001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x28000100010001, 0x1000100010001, 0x2b002a00010029, 0x2f002e002d002c, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x30000100010001, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x33003200010031, 0x36003500340001, 0x3a003900380037, 0x3100310031003b, 0x3f003e003d003c, 0x31004100310040, 0x31003100430042, 0x31004400310031, 0x31003100310031, 0x31003100310031, 0x45000100010001, 0x31003100310046, 0x31003100310031, 0x31003100310031, 0x1000100010001, 0x31003100310047, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31004800010001, 0x49003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x3100310031004a, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x4e004d004c004b, 0x5200510050004f, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31005300310031, 0x57005600550054, 0x5b005a00590058, 0x31003100310031, 0x31003100310031, 0x1000100010001, 0x1005c00010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x5d000100010001, 0x3100310031005e, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31005e00010001, 0x31003100310031, 0x310031005f0031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, 0xffffffff00000000, 0x7fffffffffffffff, 0xffffdfff00000000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7cffffffffffffff, 0xfffffffbffffd7f0, 0xffffffffffffffff, 0xfffe00ffffffffff, 0xfffffffefe7fffff, 0xfffffffffffe86ff, 0x1f07ffffff00ff, 0xffffffffcfffffc0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffdfffffff, 0xffffffffffff3fff, 0xffffffffffffe7ff, 0x3ffffffffffff, 0x7ffffffffffffff, 0x7fff3fffffffffff, 0x4fffffff, 0x1ffd00000000, 0x7ffffff000000000, 0xffffffffffffffff, 0xfeffffffffffffff, 0xf3c5fdfffff99fee, 0xfffffcfb080799f, 0xd36dfdfffff987ee, 0x3fffc05e023987, 0xf3edfdfffffbbfee, 0x3ffcf00013bbf, 0xf3edfdfffff99fee, 0xffffcfb0c0399f, 0xc3ffc718d63dc7ec, 0x7ffffc000813dc7, 0xe3effdfffffddfee, 0xff00ffcf03603ddf, 0xf3effdfffffddfec, 0x6ffcf40603ddf, 0xe7fffffffffddfec, 0xfe3fffcf00807ddf, 0x2ffbfffffc7fffec, 0x1c0000ff5f847f, 0x87fffffffffffffe, 0xfffffff, 0x3bffecaefef02596, 0xf3ff3f5f, 0xffffffffffffffff, 0xfffe1ffffffffeff, 0xdffffffffeffffff, 0x7ffdfff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff20bf, 0xffffffffffffffff, 0xffffffff3d7f3dff, 0x7f3dffffffff3dff, 0xffffffffff7fff3d, 0xffffffffff3dffff, 0x1fffffffe7ffffff, 0xffffffff03ffffff, 0x1fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff1fffffff, 0x1ffffffffffff, 0x7fffff001fdfff, 0xddfff000fffff, 0xffffffffffffffff, 0x3ff03ff3fffffff, 0xffffffff03ff3fff, 0xffffffffffffff, 0xffff07ffffffffff, 0x3fffffffffffff, 0xfff0fff1fffffff, 0x1f3ffffffffff1, 0xffff0fffffffffff, 0xffffffffc7ff03ff, 0xffffffffcfffffff, 0x9fffffff7fffffff, 0x3fff03ff03ff, 0x0, 0xffffffffffffffff, 0x1fffffffffff0fff, 0xffffffffffffffff, 0xf00fffffffffffff, 0xf8ffffffffffffff, 0xffffffffffffe3ff, 0x0, 0x7fffffffff00ff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf000007fffffffff, 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0xffdfffffffffffff, 0x7fdcffffefcfffdf, 0xffff80ffffff07ff, 0xfff30000ffffffff, 0x7ffffff1fff7fff, 0x1ffffffff0000, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff03ff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffff, 0x7fffffffff, 0xffffffff000007ff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ff1fff, 0x0, 0x0, 0xffff7fffffffffff, 0xffffffff7fffffff, 0xffffffffffffffff, 0xfe0fffffffffffff, 0xffff20bfffffffff, 0x800180ffffffffff, 0x7f7f7f7f007fffff, 0xffffffff7f7f7f7f, 0xfffffffffffffff, 0x0, 0xfffffffffbffffff, 0xfffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfff0000003fffff, 0xffffffffffffffff, 0xfffffffffffffffe, 0xfffffffffe7fffff, 0xffffffffffffffff, 0xfffe3fffffffffe0, 0xffffffffffffffff, 0x7ffffffffff7fff, 0xffff000fffffffff, 0xffffffff7fffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff1fff, 0xffffffffffff007f, 0xfffffffffff, 0xffffffffffffffff, 0xffffffff80ffffff, 0xffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ff000f7fff, 0xff00000000000000, 0x3ff0fffffffffff, 0xffffffffffffff, 0xffffffffffffffff, 0xfffffff03ffc01f, 0xffffffffffffffff, 0x1fffffff800fffff, 0xffffffffffffffff, 0xc3ffbfff, 0x7fffffffffffff, 0xffffffff3ff3fff, 0xffffffffffffffff, 0x7ffffff8000007, 0x7f7f007e7e7e, 0x0, 0x0, 0x3ff3fffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff000fffffffff, 0xffffffffffff87f, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffff3fffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x5f7fffffe0f8007f, 0xffffffffffffffdb, 0xffffffffffffffff, 0xfffffffffff80003, 0xffffffffffffffff, 0xffffffffffff0000, 0xfffffffffffcffff, 0x3fff0000000000ff, 0xffff007f03ffffff, 0xffdf0f7ffff7ffff, 0xffffffffffffffff, 0x1fffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, 0x7fffffffffffffff, 0x30007f7f1cfcfcfc, 0xb7ffff7fffffefff, 0x3fff3fff, 0xffffffffffffffff, 0x7ffffffffffffff, 0xff8fffffffffff87, 0xffffffffffffffff, 0xfff07ff, 0x3fffffffffff0000, 0x0, 0x0, 0xffffffff1fffffff, 0x1ffff, 0xffff000f7fffffff, 0x7ff, 0xffffffffbfffffff, 0x3fff0f, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ff3fffffff, 0x0, 0x91bffffffffffd3f, 0xffbfffff, 0x0, 0x0, 0x83ffffff8fffffff, 0x0, 0xc0ffffffffffffff, 0x0, 0x870ffffffeeff06f, 0xffffffff01ff00ff, 0x0, 0x0, 0xfe3fffffffffffff, 0xff07ffffff3fffff, 0x0, 0x0, 0xffffffffffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x7fffffff00000000, 0x0, 0x0, 0xffffffffffffffff, 0xfffffffc3fff, 0xdfffffffffffffff, 0x3ff01ffffff0003, 0xffdfffffffffffff, 0xf, 0xffffffffffffffff, 0x3ff01ff, 0x0, 0x0, 0xffffffffffffff, 0x3ff, 0xffffffffffffffff, 0x7fffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0xf0007ffffffff, 0x0, 0x0, 0x7fffffffffff, 0x0, 0x0, 0x0, 0x1ffffffffffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x7fffffffffff001f, 0xffff8000, 0x0, 0x3, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff, 0xfffffe7fffffffff, 0xf807ffffffffffff, 0xffffffffffffffff, 0x3fffffff, 0xffffffffffffffff, 0x3f, 0x0, 0x0, 0xffffffffffffffff, 0x3ffff007fffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffdfffff, 0xebffde64dfffffff, 0xffffffffffffffef, 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffff3fffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffcfff, 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, 0x3000000000000, 0xffff0fffffffffff, 0xffffffffffffffff, 0x7ffe7fff000fffff, 0xfffefffe, 0xffff7fffffff07ff, 0xffff0fffffffffff, 0x7ffffff, 0xffffffc000000000, 0x7ffffffffff0007, 0x301ff, 0x0, 0x0, 0xffbf0001ffffffff, 0x1fffffffffffffff, 0xffffffff000fffff, 0x1ffff000007df, 0x7fffffffffffffff, 0xfffffffffffffffd, 0xffffffffffffffff, 0x1effffffffffffff, 0x3fffffffffffffff, 0xffffff000f, 0x0, 0xf800000000000000, 0xffffffffffffffff, 0xffe1, 0xffffffffffffffff, 0x3f, 0xffffffffffffffff, 0xfffffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffff, 0x1fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0]); +enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x20, 0xb8], + [0x100, 0x260, 0x6100], [0x706050403020100, 0xe0d0c0a0b0a0908, + 0x100a0f0303030303, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, + 0xa0a0a0a0a0a0a11, 0xa0a0a0a0a0a0a0a, 0xa0a0a0a0a0a0a0a, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2000100010000, 0x5000400030001, 0x9000800070006, 0xd000c000b000a, + 0x10000f0001000e, 0x12001100010001, 0x16001500140013, 0x19000100180017, + 0x1c0001001b001a, 0x1e00010001001d, 0x1f000100010001, 0x23002200210020, + 0x1002600250024, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100270001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x28000100010001, + 0x1000100010001, 0x2b002a00010029, 0x2f002e002d002c, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x1000100010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x30000100010001, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x33003200010031, 0x36003500340001, + 0x3a003900380037, 0x3100310031003b, 0x3f003e003d003c, 0x31004100310040, + 0x31003100430042, 0x31004400310031, 0x31003100310031, 0x31003100310031, + 0x45000100010001, 0x31003100310046, 0x31003100310031, 0x31003100310031, + 0x1000100010001, 0x31003100310047, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31004800010001, 0x49003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x3100310031004a, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x4e004d004c004b, 0x5200510050004f, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31005300310031, + 0x57005600550054, 0x5b005a00590058, 0x31003100310031, 0x31003100310031, + 0x1000100010001, 0x1005c00010001, 0x1000100010001, 0x1000100010001, + 0x1000100010001, 0x5d000100010001, 0x3100310031005e, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31005e00010001, 0x31003100310031, + 0x310031005f0031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0x31003100310031, 0x31003100310031, 0x31003100310031, 0x31003100310031, + 0xffffffff00000000, 0x7fffffffffffffff, 0xffffdfff00000000, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x7cffffffffffffff, 0xfffffffbffffd7f0, 0xffffffffffffffff, + 0xfffe00ffffffffff, 0xfffffffefe7fffff, 0xfffffffffffe86ff, + 0x1f07ffffff00ff, 0xffffffffcfffffc0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffdfffffff, 0xffffffffffff3fff, + 0xffffffffffffe7ff, 0x3ffffffffffff, 0x7ffffffffffffff, + 0x7fff3fffffffffff, 0x4fffffff, 0x1ffd00000000, 0x7ffffff000000000, + 0xffffffffffffffff, 0xfeffffffffffffff, 0xf3c5fdfffff99fee, + 0xfffffcfb080799f, 0xd36dfdfffff987ee, 0x3fffc05e023987, + 0xf3edfdfffffbbfee, 0x3ffcf00013bbf, 0xf3edfdfffff99fee, + 0xffffcfb0c0399f, 0xc3ffc718d63dc7ec, 0x7ffffc000813dc7, + 0xe3effdfffffddfee, 0xff00ffcf03603ddf, 0xf3effdfffffddfec, + 0x6ffcf40603ddf, 0xe7fffffffffddfec, 0xfe3fffcf00807ddf, + 0x2ffbfffffc7fffec, 0x1c0000ff5f847f, 0x87fffffffffffffe, 0xfffffff, + 0x3bffecaefef02596, 0xf3ff3f5f, 0xffffffffffffffff, 0xfffe1ffffffffeff, + 0xdffffffffeffffff, 0x7ffdfff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffff20bf, 0xffffffffffffffff, + 0xffffffff3d7f3dff, 0x7f3dffffffff3dff, 0xffffffffff7fff3d, + 0xffffffffff3dffff, 0x1fffffffe7ffffff, 0xffffffff03ffffff, + 0x1fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff1fffffff, 0x1ffffffffffff, 0x7fffff001fdfff, 0xddfff000fffff, + 0xffffffffffffffff, 0x3ff03ff3fffffff, 0xffffffff03ff3fff, + 0xffffffffffffff, 0xffff07ffffffffff, 0x3fffffffffffff, + 0xfff0fff1fffffff, 0x1f3ffffffffff1, 0xffff0fffffffffff, + 0xffffffffc7ff03ff, 0xffffffffcfffffff, 0x9fffffff7fffffff, + 0x3fff03ff03ff, 0x0, 0xffffffffffffffff, 0x1fffffffffff0fff, + 0xffffffffffffffff, 0xf00fffffffffffff, 0xf8ffffffffffffff, + 0xffffffffffffe3ff, 0x0, 0x7fffffffff00ff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xf000007fffffffff, + 0xffffffff3f3fffff, 0x3fffffffaaff3f3f, 0xffdfffffffffffff, + 0x7fdcffffefcfffdf, 0xffff80ffffff07ff, 0xfff30000ffffffff, + 0x7ffffff1fff7fff, 0x1ffffffff0000, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffff03ff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xfffffffffffff, 0x7fffffffff, 0xffffffff000007ff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ff1fff, + 0x0, 0x0, 0xffff7fffffffffff, 0xffffffff7fffffff, 0xffffffffffffffff, + 0xfe0fffffffffffff, 0xffff20bfffffffff, 0x800180ffffffffff, + 0x7f7f7f7f007fffff, 0xffffffff7f7f7f7f, 0xfffffffffffffff, 0x0, + 0xfffffffffbffffff, 0xfffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xfff0000003fffff, + 0xffffffffffffffff, 0xfffffffffffffffe, 0xfffffffffe7fffff, + 0xffffffffffffffff, 0xfffe3fffffffffe0, 0xffffffffffffffff, + 0x7ffffffffff7fff, 0xffff000fffffffff, 0xffffffff7fffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x1fff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffff1fff, 0xffffffffffff007f, 0xfffffffffff, + 0xffffffffffffffff, 0xffffffff80ffffff, 0xffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x7ff000f7fff, + 0xff00000000000000, 0x3ff0fffffffffff, 0xffffffffffffff, + 0xffffffffffffffff, 0xfffffff03ffc01f, 0xffffffffffffffff, + 0x1fffffff800fffff, 0xffffffffffffffff, 0xc3ffbfff, 0x7fffffffffffff, + 0xffffffff3ff3fff, 0xffffffffffffffff, 0x7ffffff8000007, + 0x7f7f007e7e7e, 0x0, 0x0, 0x3ff3fffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff000fffffffff, 0xffffffffffff87f, 0x0, 0x0, + 0x0, 0x0, 0xffffffffffffffff, 0xffff3fffffffffff, 0xffffffffffffffff, + 0x3ffffff, 0x5f7fffffe0f8007f, 0xffffffffffffffdb, 0xffffffffffffffff, + 0xfffffffffff80003, 0xffffffffffffffff, 0xffffffffffff0000, + 0xfffffffffffcffff, 0x3fff0000000000ff, 0xffff007f03ffffff, + 0xffdf0f7ffff7ffff, 0xffffffffffffffff, 0x1fffffffffffffff, + 0xfffffffffffffffe, 0xffffffffffffffff, 0x7fffffffffffffff, + 0x30007f7f1cfcfcfc, 0xb7ffff7fffffefff, 0x3fff3fff, 0xffffffffffffffff, + 0x7ffffffffffffff, 0xff8fffffffffff87, 0xffffffffffffffff, 0xfff07ff, + 0x3fffffffffff0000, 0x0, 0x0, 0xffffffff1fffffff, 0x1ffff, + 0xffff000f7fffffff, 0x7ff, 0xffffffffbfffffff, 0x3fff0f, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3ff3fffffff, 0x0, + 0x91bffffffffffd3f, 0xffbfffff, 0x0, 0x0, 0x83ffffff8fffffff, 0x0, + 0xc0ffffffffffffff, 0x0, 0x870ffffffeeff06f, 0xffffffff01ff00ff, 0x0, + 0x0, 0xfe3fffffffffffff, 0xff07ffffff3fffff, 0x0, 0x0, + 0xffffffffffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x7fffffff00000000, 0x0, 0x0, + 0xffffffffffffffff, 0xfffffffc3fff, 0xdfffffffffffffff, + 0x3ff01ffffff0003, 0xffdfffffffffffff, 0xf, 0xffffffffffffffff, + 0x3ff01ff, 0x0, 0x0, 0xffffffffffffff, 0x3ff, 0xffffffffffffffff, + 0x7fffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0xf0007ffffffff, 0x0, + 0x0, 0x7fffffffffff, 0x0, 0x0, 0x0, 0x1ffffffffffffff, 0x0, 0x0, 0x0, + 0xffffffffffffffff, 0x7fffffffffff001f, 0xffff8000, 0x0, 0x3, 0x0, 0x0, + 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3fffffffffffff, 0xfffffe7fffffffff, 0xf807ffffffffffff, + 0xffffffffffffffff, 0x3fffffff, 0xffffffffffffffff, 0x3f, 0x0, 0x0, + 0xffffffffffffffff, 0x3ffff007fffff, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffdfffff, 0xebffde64dfffffff, 0xffffffffffffffef, + 0x7bffffffdfdfe7bf, 0xfffffffffffdfc5f, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffff3fffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffcfff, + 0xaf7fe96ffffffef, 0x5ef7f796aa96ea84, 0xffffbee0ffffbff, + 0x3000000000000, 0xffff0fffffffffff, 0xffffffffffffffff, + 0x7ffe7fff000fffff, 0xfffefffe, 0xffff7fffffff07ff, 0xffff0fffffffffff, + 0x7ffffff, 0xffffffc000000000, 0x7ffffffffff0007, 0x301ff, 0x0, 0x0, + 0xffbf0001ffffffff, 0x1fffffffffffffff, 0xffffffff000fffff, + 0x1ffff000007df, 0x7fffffffffffffff, 0xfffffffffffffffd, + 0xffffffffffffffff, 0x1effffffffffffff, 0x3fffffffffffffff, + 0xffffff000f, 0x0, 0xf800000000000000, 0xffffffffffffffff, 0xffe1, + 0xffffffffffffffff, 0x3f, 0xffffffffffffffff, 0xfffffffffffff, 0x0, + 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x7fffff, 0x1fffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0, 0x0, 0x0]); //3664 bytes -enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)([ 0x0, 0x10, 0x4c, 0x104], [ 0x80, 0xf0, 0x2e0, 0x3180], [ 0x706050403020100, 0xb0b0b0b0a090808, 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, 0xd0808080b0b0b0c, 0xd080808, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000200010000, 0x7000600050004, 0xb000a00090008, 0xd000d000d000c, 0xe000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xf000d000d000d, 0xd00110010000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d0012000d, 0xd000d000d000d, 0x140013000d000d, 0x18001700160015, 0x1b001b001a0019, 0x1b001b001d001c, 0x1b001b001e000d, 0x1b001b001b001b, 0x1b001b001b001b, 0x20001f001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b0021, 0x1b001b001b001b, 0x1b001b00230022, 0x24001b001b001b, 0x1b001b00260025, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d0027000d, 0x1b00290028000d, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b002a001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b002b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0x2c000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0x2c000d000d000d, 0x0, 0x0, 0x0, 0x200010000, 0x0, 0x6000500040003, 0x7, 0xb000a00090008, 0xf000e000d000c, 0x12001100100000, 0x16001500140013, 0x1a001900180017, 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x29002800270000, 0x2a000000000000, 0x0, 0x2d002c002b0000, 0x310030002f002e, 0x0, 0x0, 0x33003200000000, 0x36000000350034, 0x3a003900380037, 0x3e003d003c003b, 0x4200410040003f, 0x44000000430000, 0x47004200460045, 0x48000000000000, 0x0, 0x4c004b004a0049, 0x4f004e004d0000, 0x5000000000, 0x0, 0x51000000000000, 0x530052, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x42004200550000, 0x58000000570056, 0x5c005b005a0059, 0x51005e0042005d, 0x5f000000000000, 0x6000540000, 0x63006200000061, 0x64000000000057, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65000000000000, 0x67006600000000, 0x0, 0x38006900000068, 0x6b006a00000000, 0x6d00000038006c, 0x6f0000006e0000, 0x72000000710070, 0x74004200420073, 0x0, 0x0, 0x0, 0x75006300000000, 0x0, 0x0, 0x77000000760000, 0x7a000000790078, 0x0, 0x7d007c007b0000, 0x800000007f007e, 0x81006400000054, 0xb000000830082, 0x86008500000084, 0x87003200420042, 0x8b008a00890088, 0x42008c00000000, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x420042008e008d, 0x4200900042008f, 0x42004200920091, 0x42004200940093, 0x42004200950000, 0x42004200420042, 0x42004200960042, 0x42004200420042, 0x98000000970000, 0x9a00000099004b, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x9b003800420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x0, 0x0, 0x0, 0x420042009c0000, 0x420042009d0000, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x4200420042009c, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x0, 0x0, 0x4200420042009e, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x4200a0009f0000, 0x420042004200a1, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x3a000000000000, 0xa30000000000a2, 0x42004200a40000, 0x42004200a50000, 0xa800a700a60000, 0xaa00a9, 0xab00000000, 0xac000000000000, 0x42004200420042, 0x42004200420042, 0xb000af00ae00ad, 0x42004200420042, 0xb200b10000003d, 0xb500b4003d00b3, 0x42004200b700b6, 0xbb00ba00b900b8, 0xbd000000bc0064, 0xc0004200bf00be, 0xa4000000c10000, 0x42004200510000, 0x0, 0x0, 0xc2000000000000, 0x0, 0x0, 0x0, 0x0, 0x31, 0x420042004200a3, 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x0, 0x0, 0x420042004200a3, 0x42004200420042, 0x420042000000c3, 0xc4000000000000, 0x42004200420042, 0x42004200420042, 0x0, 0x0, 0x0, 0xbe000000000000, 0x0, 0x0, 0x0, 0xbe000000000000, 0x0, 0x8300000000000000, 0x40000280f, 0x1ff0000000000, 0x101800000, 0x17900, 0xffe0f8000000ff00, 0x20000020, 0x4000, 0x1800, 0xfffc000000000000, 0xf800000000000000, 0x8000c00000000000, 0xffffffffb0000000, 0xffffe002ffffffff, 0x8000000fffffffff, 0x100000000000000, 0xc3a020000066011, 0xf00000304f7f8660, 0x2c92020000067811, 0xffc0003fa1fdc678, 0xc12020000044011, 0xfffc0030fffec440, 0xc12020000066011, 0xff0000304f3fc660, 0x3c0038e729c23813, 0xf800003fff7ec238, 0x1c10020000022011, 0xff0030fc9fc220, 0xc10020000022013, 0xfff90030bf9fc220, 0x1800000000022013, 0x1c00030ff7f8220, 0xd004000003800013, 0xffe3ffff00a07b80, 0x7800000000000001, 0xfffffffff0000000, 0xc4001351010fda69, 0xffffffff0c00c0a0, 0x1e00000000100, 0x2000000001000000, 0xfffffffff8002000, 0xdf40, 0xc280c200, 0x80c200000000c200, 0x8000c2, 0xc20000, 0xe000000018000000, 0xfc000000, 0xffe0000000000000, 0xe0000000, 0xfffe000000000000, 0xff800000ffe02000, 0xfff22000fff00000, 0xfc00fc00c0000000, 0xfc008000, 0xff00000000000000, 0xf80000000000, 0xffc0000000000000, 0xf000f000e0000000, 0xffe0c0000000000e, 0xf00000000000, 0x3800fc00, 0x30000000, 0x6000000080000000, 0xffffc000fc00fc00, 0xffffffffffffffff, 0xe00000000000f000, 0xff0000000000000, 0x700000000000000, 0x1c00, 0xff8000000000ff00, 0xfffff8000000000, 0xc0c00000, 0xc00000005500c0c0, 0x20000000000000, 0x8023000010300020, 0xc002000000000, 0xf8000000e0008000, 0xfffe00000000ffff, 0xfc00, 0xfff0000000000000, 0xffffff8000000000, 0xfffff800, 0x1, 0xfffffffffc00e000, 0x800000000000, 0x80000000, 0x1f0000000000000, 0xdf4000000000, 0x7ffe7f0000000000, 0x80808080ff800000, 0x80808080, 0xf000000000000000, 0x4000000, 0xf000ffffffc00000, 0x1800000, 0x1c0000000001f, 0xf800000000008000, 0xfff000000000, 0x8000000000000000, 0xffffffffffffe000, 0xe000, 0xff80, 0xfffff00000000000, 0x7f000000, 0xfffff800fff08000, 0xffffffffffffff, 0xfc00f00000000000, 0xf0000000fc003fe0, 0xe00000007ff00000, 0xffffffff3c004000, 0xff80000000000000, 0xf00000000c00c000, 0xff80000007fffff8, 0xffff8080ff818181, 0xfc00c00000000000, 0xf000000000000780, 0xc00000000000, 0xfffffffffc000000, 0xa08000001f07ff80, 0x24, 0x7fffc, 0xffff, 0x30000, 0xc000ffffffffff00, 0xff80fc000000, 0x20f08000080000, 0x6000000000000000, 0xc1ff8080e3030303, 0x4800008000001000, 0xffffffffc000c000, 0x70000000000078, 0xfffffffff000f800, 0xc00000000000ffff, 0xfffffffffffe0000, 0xfff080000000, 0xfffffffffffff800, 0x40000000, 0xffffffffffc000f0, 0xfffffc00c0000000, 0x6e400000000002c0, 0xffffffff00400000, 0x7c00000070000000, 0x3f00000000000000, 0x78f0000001100f90, 0xfe00ff00, 0x1c0000000000000, 0xf8000000c00000, 0xfffffffffffffe00, 0x80000000ffffffff, 0xffff00000003c000, 0xfc00fe000000fffc, 0xfffffffffffffff0, 0xfffffffffc00fe00, 0xfffffffffffffc00, 0xffff800000000000, 0xfff0fff800000000, 0xfe00000000000000, 0x800000000000ffe0, 0xffffffff00007fff, 0xfffffffffffffffc, 0x18000000000, 0xffffffffc0000000, 0xffffffffffffffc0, 0xfffc0000ff800000, 0x200000, 0x1400219b20000000, 0x10, 0x8400000020201840, 0x203a0, 0xc000000000, 0x3000, 0xf508016900000010, 0xa10808695569157b, 0xf0000411f0000400, 0xfffcffffffffffff, 0x80018000fff00000, 0xffffffff00010001, 0x80000000f800, 0xfffffffff8000000, 0x3fffffffff, 0xf80000000000fff8, 0xfffffffffffcfe00, 0x40fffe00000000, 0xe000000000000000, 0xfff00000, 0xfffe0000fffff820, 0x2, 0xe100000000000000, 0xc000000000000000, 0xffffff000000fff0, 0x7ffffffffffffff, 0xffffffffffff001e, 0xffffffffff800000, 0xfffffffd, 0xffff000000000000, 0xc000000000000000]); +enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)([0x0, 0x10, 0x4c, + 0x104], [0x80, 0xf0, 0x2e0, 0x3180], [0x706050403020100, + 0xb0b0b0b0a090808, 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, + 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, 0xb0b0b0b0b0b0b0b, + 0xd0808080b0b0b0c, 0xd080808, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3000200010000, 0x7000600050004, 0xb000a00090008, 0xd000d000d000c, + 0xe000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, + 0xd000d000d000d, 0xf000d000d000d, 0xd00110010000d, 0xd000d000d000d, + 0xd000d000d000d, 0xd000d0012000d, 0xd000d000d000d, 0x140013000d000d, + 0x18001700160015, 0x1b001b001a0019, 0x1b001b001d001c, 0x1b001b001e000d, + 0x1b001b001b001b, 0x1b001b001b001b, 0x20001f001b001b, 0x1b001b001b001b, + 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b0021, + 0x1b001b001b001b, 0x1b001b00230022, 0x24001b001b001b, 0x1b001b00260025, + 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, + 0xd000d000d000d, 0xd000d000d000d, 0xd000d0027000d, 0x1b00290028000d, + 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b002a001b001b, + 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, + 0x1b001b001b002b, 0x1b001b001b001b, 0x1b001b001b001b, 0x1b001b001b001b, + 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0x2c000d000d000d, + 0xd000d000d000d, 0xd000d000d000d, 0xd000d000d000d, 0x2c000d000d000d, + 0x0, 0x0, 0x0, 0x200010000, 0x0, 0x6000500040003, 0x7, 0xb000a00090008, + 0xf000e000d000c, 0x12001100100000, 0x16001500140013, 0x1a001900180017, + 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x29002800270000, + 0x2a000000000000, 0x0, 0x2d002c002b0000, 0x310030002f002e, 0x0, 0x0, + 0x33003200000000, 0x36000000350034, 0x3a003900380037, 0x3e003d003c003b, + 0x4200410040003f, 0x44000000430000, 0x47004200460045, 0x48000000000000, + 0x0, 0x4c004b004a0049, 0x4f004e004d0000, 0x5000000000, 0x0, + 0x51000000000000, 0x530052, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, + 0x42004200550000, 0x58000000570056, 0x5c005b005a0059, 0x51005e0042005d, + 0x5f000000000000, 0x6000540000, 0x63006200000061, 0x64000000000057, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a00000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x65000000000000, 0x67006600000000, 0x0, 0x38006900000068, + 0x6b006a00000000, 0x6d00000038006c, 0x6f0000006e0000, 0x72000000710070, + 0x74004200420073, 0x0, 0x0, 0x0, 0x75006300000000, 0x0, 0x0, + 0x77000000760000, 0x7a000000790078, 0x0, 0x7d007c007b0000, + 0x800000007f007e, 0x81006400000054, 0xb000000830082, 0x86008500000084, + 0x87003200420042, 0x8b008a00890088, 0x42008c00000000, 0x42004200420042, + 0x42004200420042, 0x42004200420042, 0x420042008e008d, 0x4200900042008f, + 0x42004200920091, 0x42004200940093, 0x42004200950000, 0x42004200420042, + 0x42004200960042, 0x42004200420042, 0x98000000970000, 0x9a00000099004b, + 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, + 0x9b003800420042, 0x42004200420042, 0x42004200420042, 0x42004200420042, + 0x42004200420042, 0x42004200420042, 0x0, 0x0, 0x0, 0x420042009c0000, + 0x420042009d0000, 0x42004200420042, 0x42004200420042, 0x42004200420042, + 0x4200420042009c, 0x42004200420042, 0x42004200420042, 0x42004200420042, + 0x0, 0x0, 0x4200420042009e, 0x42004200420042, 0x42004200420042, + 0x42004200420042, 0x42004200420042, 0x4200a0009f0000, 0x420042004200a1, + 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x3a000000000000, + 0xa30000000000a2, 0x42004200a40000, 0x42004200a50000, 0xa800a700a60000, + 0xaa00a9, 0xab00000000, 0xac000000000000, 0x42004200420042, + 0x42004200420042, 0xb000af00ae00ad, 0x42004200420042, 0xb200b10000003d, + 0xb500b4003d00b3, 0x42004200b700b6, 0xbb00ba00b900b8, 0xbd000000bc0064, + 0xc0004200bf00be, 0xa4000000c10000, 0x42004200510000, 0x0, 0x0, + 0xc2000000000000, 0x0, 0x0, 0x0, 0x0, 0x31, 0x420042004200a3, + 0x42004200420042, 0x42004200420042, 0x42004200420042, 0x0, 0x0, + 0x420042004200a3, 0x42004200420042, 0x420042000000c3, 0xc4000000000000, + 0x42004200420042, 0x42004200420042, 0x0, 0x0, 0x0, 0xbe000000000000, + 0x0, 0x0, 0x0, 0xbe000000000000, 0x0, 0x8300000000000000, 0x40000280f, + 0x1ff0000000000, 0x101800000, 0x17900, 0xffe0f8000000ff00, 0x20000020, + 0x4000, 0x1800, 0xfffc000000000000, 0xf800000000000000, + 0x8000c00000000000, 0xffffffffb0000000, 0xffffe002ffffffff, + 0x8000000fffffffff, 0x100000000000000, 0xc3a020000066011, + 0xf00000304f7f8660, 0x2c92020000067811, 0xffc0003fa1fdc678, + 0xc12020000044011, 0xfffc0030fffec440, 0xc12020000066011, + 0xff0000304f3fc660, 0x3c0038e729c23813, 0xf800003fff7ec238, + 0x1c10020000022011, 0xff0030fc9fc220, 0xc10020000022013, + 0xfff90030bf9fc220, 0x1800000000022013, 0x1c00030ff7f8220, + 0xd004000003800013, 0xffe3ffff00a07b80, 0x7800000000000001, + 0xfffffffff0000000, 0xc4001351010fda69, 0xffffffff0c00c0a0, + 0x1e00000000100, 0x2000000001000000, 0xfffffffff8002000, 0xdf40, + 0xc280c200, 0x80c200000000c200, 0x8000c2, 0xc20000, 0xe000000018000000, + 0xfc000000, 0xffe0000000000000, 0xe0000000, 0xfffe000000000000, + 0xff800000ffe02000, 0xfff22000fff00000, 0xfc00fc00c0000000, 0xfc008000, + 0xff00000000000000, 0xf80000000000, 0xffc0000000000000, + 0xf000f000e0000000, 0xffe0c0000000000e, 0xf00000000000, 0x3800fc00, + 0x30000000, 0x6000000080000000, 0xffffc000fc00fc00, 0xffffffffffffffff, + 0xe00000000000f000, 0xff0000000000000, 0x700000000000000, 0x1c00, + 0xff8000000000ff00, 0xfffff8000000000, 0xc0c00000, 0xc00000005500c0c0, + 0x20000000000000, 0x8023000010300020, 0xc002000000000, + 0xf8000000e0008000, 0xfffe00000000ffff, 0xfc00, 0xfff0000000000000, + 0xffffff8000000000, 0xfffff800, 0x1, 0xfffffffffc00e000, + 0x800000000000, 0x80000000, 0x1f0000000000000, 0xdf4000000000, + 0x7ffe7f0000000000, 0x80808080ff800000, 0x80808080, 0xf000000000000000, + 0x4000000, 0xf000ffffffc00000, 0x1800000, 0x1c0000000001f, + 0xf800000000008000, 0xfff000000000, 0x8000000000000000, + 0xffffffffffffe000, 0xe000, 0xff80, 0xfffff00000000000, 0x7f000000, + 0xfffff800fff08000, 0xffffffffffffff, 0xfc00f00000000000, + 0xf0000000fc003fe0, 0xe00000007ff00000, 0xffffffff3c004000, + 0xff80000000000000, 0xf00000000c00c000, 0xff80000007fffff8, + 0xffff8080ff818181, 0xfc00c00000000000, 0xf000000000000780, + 0xc00000000000, 0xfffffffffc000000, 0xa08000001f07ff80, 0x24, 0x7fffc, + 0xffff, 0x30000, 0xc000ffffffffff00, 0xff80fc000000, 0x20f08000080000, + 0x6000000000000000, 0xc1ff8080e3030303, 0x4800008000001000, + 0xffffffffc000c000, 0x70000000000078, 0xfffffffff000f800, + 0xc00000000000ffff, 0xfffffffffffe0000, 0xfff080000000, + 0xfffffffffffff800, 0x40000000, 0xffffffffffc000f0, 0xfffffc00c0000000, + 0x6e400000000002c0, 0xffffffff00400000, 0x7c00000070000000, + 0x3f00000000000000, 0x78f0000001100f90, 0xfe00ff00, 0x1c0000000000000, + 0xf8000000c00000, 0xfffffffffffffe00, 0x80000000ffffffff, + 0xffff00000003c000, 0xfc00fe000000fffc, 0xfffffffffffffff0, + 0xfffffffffc00fe00, 0xfffffffffffffc00, 0xffff800000000000, + 0xfff0fff800000000, 0xfe00000000000000, 0x800000000000ffe0, + 0xffffffff00007fff, 0xfffffffffffffffc, 0x18000000000, + 0xffffffffc0000000, 0xffffffffffffffc0, 0xfffc0000ff800000, 0x200000, + 0x1400219b20000000, 0x10, 0x8400000020201840, 0x203a0, 0xc000000000, + 0x3000, 0xf508016900000010, 0xa10808695569157b, 0xf0000411f0000400, + 0xfffcffffffffffff, 0x80018000fff00000, 0xffffffff00010001, + 0x80000000f800, 0xfffffffff8000000, 0x3fffffffff, 0xf80000000000fff8, + 0xfffffffffffcfe00, 0x40fffe00000000, 0xe000000000000000, 0xfff00000, + 0xfffe0000fffff820, 0x2, 0xe100000000000000, 0xc000000000000000, + 0xffffff000000fff0, 0x7ffffffffffffff, 0xffffffffffff001e, + 0xffffffffff800000, 0xfffffffd, 0xffff000000000000, 0xc000000000000000]); enum MAX_SIMPLE_LOWER = 1043; enum MAX_SIMPLE_UPPER = 1051; enum MAX_SIMPLE_TITLE = 1055; //8192 bytes -enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xc00], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x41bffffffffffff, 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, 0x51ffff0050ffff, 0x53ffff0052041d, 0x55ffff0054ffff, 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, 0xffff005cffff005b, 0x5effff043a005d, 0x60ffff005fffff, 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, 0xffff008f008effff, 0x92ffff00910090, 0xffff0094ffff0093, 0xffff0096ffff0095, 0xffff0098ffff0097, 0xffff009affff0099, 0x9dffff009c009b, 0x9fffff009effff, 0xa1ffff00a0ffff, 0xa3ffff00a2ffff, 0xa5ffff00a4ffff, 0xa700a6ffff0442, 0xffffffff00a8ffff, 0xaaffff00a9ffff, 0xacffff00abffff, 0xaeffff00adffff, 0xb0ffff00afffff, 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, 0xbaffff00b9ffff, 0xbcffff00bbffff, 0xbdffffffffffff, 0xbfffff00beffff, 0xc1ffff00c0ffff, 0xc3ffff00c2ffff, 0xc5ffff00c4ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xc7ffffffff00c6, 0xffff00c9ffff00c8, 0xcaffffffffffff, 0xccffff00cbffff, 0xceffff00cdffff, 0xd200d100d000cf, 0xd500d4ffff00d3, 0xd7ffff00d6ffff, 0xffffffffffffffff, 0xd9ffffffff00d8, 0xffff00db00daffff, 0xdeffff00dd00dc, 0xdfffffffffffff, 0xffff00e100e0ffff, 0xffffffff00e2ffff, 0xffffffffffffffff, 0xffffffff00e3ffff, 0xe5ffffffff00e4, 0xffffffffffffffff, 0xe900e800e700e6, 0xffffffffffff00ea, 0xffff00ebffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00ecffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xeeffff00edffff, 0xefffffffffffff, 0xf0ffffffffffff, 0xffffffff00f200f1, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff043c, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf600f500f400f3, 0xf900f800f7043f, 0xfd00fc00fb00fa, 0x101010000ff00fe, 0x105010401030102, 0x109010801070106, 0x10d010c010b010a, 0x1110110010f010e, 0xffff011401130112, 0xffffffff01160115, 0x11901180117ffff, 0x11bffff011affff, 0x11dffff011cffff, 0x11fffff011effff, 0x121ffff0120ffff, 0x123ffff0122ffff, 0x125ffff0124ffff, 0xffff012801270126, 0xffffffff0129ffff, 0x12bffffffff012a, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x12f012e012d012c, 0x133013201310130, 0x137013601350134, 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, 0x147014601450144, 0x14b014a01490148, 0x14f014e014d014c, 0x153015201510150, 0x157015601550154, 0x15b015a01590158, 0x15dffff015cffff, 0x15fffff015effff, 0x161ffff0160ffff, 0x163ffff0162ffff, 0x165ffff0164ffff, 0x167ffff0166ffff, 0x169ffff0168ffff, 0x16bffff016affff, 0xffffffff016cffff, 0xffffffffffffffff, 0x16dffffffffffff, 0x16fffff016effff, 0x171ffff0170ffff, 0x173ffff0172ffff, 0x175ffff0174ffff, 0x177ffff0176ffff, 0x179ffff0178ffff, 0x17bffff017affff, 0x17dffff017cffff, 0x17fffff017effff, 0x181ffff0180ffff, 0x183ffff0182ffff, 0x185ffff0184ffff, 0x187ffff0186ffff, 0xffff0188ffffffff, 0xffff018affff0189, 0xffff018cffff018b, 0x18f018effff018d, 0x191ffff0190ffff, 0x193ffff0192ffff, 0x195ffff0194ffff, 0x197ffff0196ffff, 0x199ffff0198ffff, 0x19bffff019affff, 0x19dffff019cffff, 0x19fffff019effff, 0x1a1ffff01a0ffff, 0x1a3ffff01a2ffff, 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, 0x1a9ffff01a8ffff, 0x1abffff01aaffff, 0x1adffff01acffff, 0x1afffff01aeffff, 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, 0x1b5ffff01b4ffff, 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, 0x1bbffff01baffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1be01bd01bcffff, 0x1c201c101c001bf, 0x1c601c501c401c3, 0x1ca01c901c801c7, 0x1ce01cd01cc01cb, 0x1d201d101d001cf, 0x1d601d501d401d3, 0x1da01d901d801d7, 0x1de01dd01dc01db, 0x42e01e101e001df, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff01e2ffff, 0xffffffff01e3ffff, 0x1e5ffff01e4ffff, 0x1e7ffff01e6ffff, 0x1e9ffff01e8ffff, 0x1ebffff01eaffff, 0x1edffff01ecffff, 0x1efffff01eeffff, 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, 0x1f5ffff01f4ffff, 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, 0x1fbffff01faffff, 0x1fdffff01fcffff, 0x1ffffff01feffff, 0x201ffff0200ffff, 0x203ffff0202ffff, 0x205ffff0204ffff, 0x207ffff0206ffff, 0x209ffff0208ffff, 0x20bffff020affff, 0x20dffff020cffff, 0x20fffff020effff, 0x211ffff0210ffff, 0x213ffff0212ffff, 0x215ffff0214ffff, 0x217ffff0216ffff, 0x219ffff0218ffff, 0x21bffff021affff, 0x21dffff021cffff, 0x21fffff021effff, 0x221ffff0220ffff, 0x223ffff0222ffff, 0x225ffff0224ffff, 0x227ffff0226ffff, 0x229ffff0228ffff, 0x22bffff022affff, 0x22dffff022cffff, 0x4460444022effff, 0x22f044c044a0448, 0xffffffffffffffff, 0x231ffff0230ffff, 0x233ffff0232ffff, 0x235ffff0234ffff, 0x237ffff0236ffff, 0x239ffff0238ffff, 0x23bffff023affff, 0x23dffff023cffff, 0x23fffff023effff, 0x241ffff0240ffff, 0x243ffff0242ffff, 0x245ffff0244ffff, 0x247ffff0246ffff, 0x249ffff0248ffff, 0x24bffff024affff, 0x24dffff024cffff, 0x24fffff024effff, 0x251ffff0250ffff, 0x253ffff0252ffff, 0x255ffff0254ffff, 0x257ffff0256ffff, 0x259ffff0258ffff, 0x25bffff025affff, 0x25dffff025cffff, 0x25fffff025effff, 0x263026202610260, 0x267026602650264, 0xffffffffffffffff, 0xffffffffffffffff, 0x26b026a02690268, 0xffffffff026d026c, 0xffffffffffffffff, 0xffffffffffffffff, 0x2710270026f026e, 0x275027402730272, 0xffffffffffffffff, 0xffffffffffffffff, 0x279027802770276, 0x27d027c027b027a, 0xffffffffffffffff, 0xffffffffffffffff, 0x2810280027f027e, 0xffffffff02830282, 0xffffffffffffffff, 0xffffffffffffffff, 0x28504500284044e, 0x287045602860453, 0xffffffffffffffff, 0xffffffffffffffff, 0x28b028a02890288, 0x28f028e028d028c, 0xffffffffffffffff, 0xffffffffffffffff, 0x293029202910290, 0x297029602950294, 0x29b029a02990298, 0xffffffff029d029c, 0x47d047b04790477, 0x48504830481047f, 0x48d048b04890487, 0x49504930491048f, 0x49d049b04990497, 0x4a504a304a1049f, 0x4ad04ab04a904a7, 0x4b504b304b104af, 0x4bd04bb04b904b7, 0x4c504c304c104bf, 0x4cd04cb04c904c7, 0x4d504d304d104cf, 0x4d704e302b702b6, 0x4ef0459ffff04e5, 0xffffffffffffffff, 0xffff02b9ffff04d9, 0x4db04e7ffffffff, 0x4f2045bffff04e9, 0xffffffffffffffff, 0xffffffffffff04dd, 0x460045d02bc02bb, 0x4650463ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x46b046802be02bd, 0x472047002bf046e, 0xffffffffffffffff, 0xffffffffffffffff, 0x4df04ebffffffff, 0x4f50475ffff04ed, 0xffffffffffffffff, 0xffffffffffff04e1, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c1ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c502c402c302c2, 0x2c902c802c702c6, 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, 0xffffffffffffffff, 0xffffffffffff02d2, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2d602d502d402d3, 0x2da02d902d802d7, 0x2de02dd02dc02db, 0x2e202e102e002df, 0x2e602e502e402e3, 0x2ea02e902e802e7, 0xffffffff02ec02eb, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2f002ef02ee02ed, 0x2f402f302f202f1, 0x2f802f702f602f5, 0x2fc02fb02fa02f9, 0x30002ff02fe02fd, 0x304030303020301, 0x308030703060305, 0x30c030b030a0309, 0x310030f030e030d, 0x314031303120311, 0x318031703160315, 0xffff031b031a0319, 0xffffffff031cffff, 0xffff031e031dffff, 0xffff0320ffff031f, 0xffffffffffff0321, 0x322ffffffffffff, 0xffff0323ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x325ffff0324ffff, 0x327ffff0326ffff, 0x329ffff0328ffff, 0x32bffff032affff, 0x32dffff032cffff, 0x32fffff032effff, 0x331ffff0330ffff, 0x333ffff0332ffff, 0x335ffff0334ffff, 0x337ffff0336ffff, 0x339ffff0338ffff, 0x33bffff033affff, 0x33dffff033cffff, 0x33fffff033effff, 0x341ffff0340ffff, 0x343ffff0342ffff, 0x345ffff0344ffff, 0x347ffff0346ffff, 0x349ffff0348ffff, 0x34bffff034affff, 0x34dffff034cffff, 0x34fffff034effff, 0x351ffff0350ffff, 0x353ffff0352ffff, 0x355ffff0354ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0357ffff0356, 0x358ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x35c035b035a0359, 0x360035f035e035d, 0x364036303620361, 0x368036703660365, 0x36c036b036a0369, 0x370036f036e036d, 0x374037303720371, 0x378037703760375, 0x37c037b037a0379, 0x37fffff037e037d, 0xffffffffffffffff, 0xffffffff0380ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x382ffff0381ffff, 0x384ffff0383ffff, 0x386ffff0385ffff, 0x388ffff0387ffff, 0x38affff0389ffff, 0x38cffff038bffff, 0x38effff038dffff, 0x390ffff038fffff, 0x392ffff0391ffff, 0x394ffff0393ffff, 0x396ffff0395ffff, 0xffffffff0397ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x399ffff0398ffff, 0x39bffff039affff, 0x39dffff039cffff, 0x39fffff039effff, 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3a4ffffffffffff, 0x3a6ffff03a5ffff, 0x3a8ffff03a7ffff, 0x3aaffff03a9ffff, 0x3abffffffffffff, 0x3adffff03acffff, 0x3afffff03aeffff, 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, 0x3b5ffff03b4ffff, 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, 0x3bbffff03baffff, 0x3bdffff03bcffff, 0x3bfffff03beffff, 0x3c1ffff03c0ffff, 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, 0x3c7ffff03c6ffff, 0x3c9ffff03c8ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03caffffffff, 0x3ccffffffff03cb, 0x3ceffff03cdffff, 0x3d0ffff03cfffff, 0xffffffffffffffff, 0xffffffffffff03d1, 0x3d3ffff03d2ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3d5ffff03d4ffff, 0x3d7ffff03d6ffff, 0xffffffff03d8ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x42404220420041e, 0xffff042c042a0427, 0xffffffffffffffff, 0xffffffffffffffff, 0x430ffffffffffff, 0x438043604340432, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3db03da03d9ffff, 0x3df03de03dd03dc, 0x3e303e203e103e0, 0x3e703e603e503e4, 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, 0xffff03f203f103f0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3f603f503f403f3, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, 0x416041504140413, 0x41a041904180417, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, 0x100], + [0x100, 0x380, 0xc00], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, + 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, + 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, + 0x0, 0x0, 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, + 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, + 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x41bffffffffffff, + 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, + 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, + 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, + 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, + 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, + 0x51ffff0050ffff, 0x53ffff0052041d, 0x55ffff0054ffff, + 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, + 0xffff005cffff005b, 0x5effff043a005d, 0x60ffff005fffff, + 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, + 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, + 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, + 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, + 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, + 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, + 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, + 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, + 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, + 0xffff008f008effff, 0x92ffff00910090, 0xffff0094ffff0093, + 0xffff0096ffff0095, 0xffff0098ffff0097, 0xffff009affff0099, + 0x9dffff009c009b, 0x9fffff009effff, 0xa1ffff00a0ffff, 0xa3ffff00a2ffff, + 0xa5ffff00a4ffff, 0xa700a6ffff0442, 0xffffffff00a8ffff, + 0xaaffff00a9ffff, 0xacffff00abffff, 0xaeffff00adffff, 0xb0ffff00afffff, + 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, + 0xbaffff00b9ffff, 0xbcffff00bbffff, 0xbdffffffffffff, 0xbfffff00beffff, + 0xc1ffff00c0ffff, 0xc3ffff00c2ffff, 0xc5ffff00c4ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xc7ffffffff00c6, + 0xffff00c9ffff00c8, 0xcaffffffffffff, 0xccffff00cbffff, + 0xceffff00cdffff, 0xd200d100d000cf, 0xd500d4ffff00d3, 0xd7ffff00d6ffff, + 0xffffffffffffffff, 0xd9ffffffff00d8, 0xffff00db00daffff, + 0xdeffff00dd00dc, 0xdfffffffffffff, 0xffff00e100e0ffff, + 0xffffffff00e2ffff, 0xffffffffffffffff, 0xffffffff00e3ffff, + 0xe5ffffffff00e4, 0xffffffffffffffff, 0xe900e800e700e6, + 0xffffffffffff00ea, 0xffff00ebffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00ecffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xeeffff00edffff, 0xefffffffffffff, + 0xf0ffffffffffff, 0xffffffff00f200f1, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffff043c, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xf600f500f400f3, 0xf900f800f7043f, + 0xfd00fc00fb00fa, 0x101010000ff00fe, 0x105010401030102, + 0x109010801070106, 0x10d010c010b010a, 0x1110110010f010e, + 0xffff011401130112, 0xffffffff01160115, 0x11901180117ffff, + 0x11bffff011affff, 0x11dffff011cffff, 0x11fffff011effff, + 0x121ffff0120ffff, 0x123ffff0122ffff, 0x125ffff0124ffff, + 0xffff012801270126, 0xffffffff0129ffff, 0x12bffffffff012a, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x12f012e012d012c, 0x133013201310130, + 0x137013601350134, 0x13b013a01390138, 0x13f013e013d013c, + 0x143014201410140, 0x147014601450144, 0x14b014a01490148, + 0x14f014e014d014c, 0x153015201510150, 0x157015601550154, + 0x15b015a01590158, 0x15dffff015cffff, 0x15fffff015effff, + 0x161ffff0160ffff, 0x163ffff0162ffff, 0x165ffff0164ffff, + 0x167ffff0166ffff, 0x169ffff0168ffff, 0x16bffff016affff, + 0xffffffff016cffff, 0xffffffffffffffff, 0x16dffffffffffff, + 0x16fffff016effff, 0x171ffff0170ffff, 0x173ffff0172ffff, + 0x175ffff0174ffff, 0x177ffff0176ffff, 0x179ffff0178ffff, + 0x17bffff017affff, 0x17dffff017cffff, 0x17fffff017effff, + 0x181ffff0180ffff, 0x183ffff0182ffff, 0x185ffff0184ffff, + 0x187ffff0186ffff, 0xffff0188ffffffff, 0xffff018affff0189, + 0xffff018cffff018b, 0x18f018effff018d, 0x191ffff0190ffff, + 0x193ffff0192ffff, 0x195ffff0194ffff, 0x197ffff0196ffff, + 0x199ffff0198ffff, 0x19bffff019affff, 0x19dffff019cffff, + 0x19fffff019effff, 0x1a1ffff01a0ffff, 0x1a3ffff01a2ffff, + 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, 0x1a9ffff01a8ffff, + 0x1abffff01aaffff, 0x1adffff01acffff, 0x1afffff01aeffff, + 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, 0x1b5ffff01b4ffff, + 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, 0x1bbffff01baffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1be01bd01bcffff, + 0x1c201c101c001bf, 0x1c601c501c401c3, 0x1ca01c901c801c7, + 0x1ce01cd01cc01cb, 0x1d201d101d001cf, 0x1d601d501d401d3, + 0x1da01d901d801d7, 0x1de01dd01dc01db, 0x42e01e101e001df, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff01e2ffff, 0xffffffff01e3ffff, + 0x1e5ffff01e4ffff, 0x1e7ffff01e6ffff, 0x1e9ffff01e8ffff, + 0x1ebffff01eaffff, 0x1edffff01ecffff, 0x1efffff01eeffff, + 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, 0x1f5ffff01f4ffff, + 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, 0x1fbffff01faffff, + 0x1fdffff01fcffff, 0x1ffffff01feffff, 0x201ffff0200ffff, + 0x203ffff0202ffff, 0x205ffff0204ffff, 0x207ffff0206ffff, + 0x209ffff0208ffff, 0x20bffff020affff, 0x20dffff020cffff, + 0x20fffff020effff, 0x211ffff0210ffff, 0x213ffff0212ffff, + 0x215ffff0214ffff, 0x217ffff0216ffff, 0x219ffff0218ffff, + 0x21bffff021affff, 0x21dffff021cffff, 0x21fffff021effff, + 0x221ffff0220ffff, 0x223ffff0222ffff, 0x225ffff0224ffff, + 0x227ffff0226ffff, 0x229ffff0228ffff, 0x22bffff022affff, + 0x22dffff022cffff, 0x4460444022effff, 0x22f044c044a0448, + 0xffffffffffffffff, 0x231ffff0230ffff, 0x233ffff0232ffff, + 0x235ffff0234ffff, 0x237ffff0236ffff, 0x239ffff0238ffff, + 0x23bffff023affff, 0x23dffff023cffff, 0x23fffff023effff, + 0x241ffff0240ffff, 0x243ffff0242ffff, 0x245ffff0244ffff, + 0x247ffff0246ffff, 0x249ffff0248ffff, 0x24bffff024affff, + 0x24dffff024cffff, 0x24fffff024effff, 0x251ffff0250ffff, + 0x253ffff0252ffff, 0x255ffff0254ffff, 0x257ffff0256ffff, + 0x259ffff0258ffff, 0x25bffff025affff, 0x25dffff025cffff, + 0x25fffff025effff, 0x263026202610260, 0x267026602650264, + 0xffffffffffffffff, 0xffffffffffffffff, 0x26b026a02690268, + 0xffffffff026d026c, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2710270026f026e, 0x275027402730272, 0xffffffffffffffff, + 0xffffffffffffffff, 0x279027802770276, 0x27d027c027b027a, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2810280027f027e, + 0xffffffff02830282, 0xffffffffffffffff, 0xffffffffffffffff, + 0x28504500284044e, 0x287045602860453, 0xffffffffffffffff, + 0xffffffffffffffff, 0x28b028a02890288, 0x28f028e028d028c, + 0xffffffffffffffff, 0xffffffffffffffff, 0x293029202910290, + 0x297029602950294, 0x29b029a02990298, 0xffffffff029d029c, + 0x47d047b04790477, 0x48504830481047f, 0x48d048b04890487, + 0x49504930491048f, 0x49d049b04990497, 0x4a504a304a1049f, + 0x4ad04ab04a904a7, 0x4b504b304b104af, 0x4bd04bb04b904b7, + 0x4c504c304c104bf, 0x4cd04cb04c904c7, 0x4d504d304d104cf, + 0x4d704e302b702b6, 0x4ef0459ffff04e5, 0xffffffffffffffff, + 0xffff02b9ffff04d9, 0x4db04e7ffffffff, 0x4f2045bffff04e9, + 0xffffffffffffffff, 0xffffffffffff04dd, 0x460045d02bc02bb, + 0x4650463ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x46b046802be02bd, 0x472047002bf046e, 0xffffffffffffffff, + 0xffffffffffffffff, 0x4df04ebffffffff, 0x4f50475ffff04ed, + 0xffffffffffffffff, 0xffffffffffff04e1, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c1ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2c502c402c302c2, + 0x2c902c802c702c6, 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, + 0xffffffffffffffff, 0xffffffffffff02d2, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2d602d502d402d3, + 0x2da02d902d802d7, 0x2de02dd02dc02db, 0x2e202e102e002df, + 0x2e602e502e402e3, 0x2ea02e902e802e7, 0xffffffff02ec02eb, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2f002ef02ee02ed, + 0x2f402f302f202f1, 0x2f802f702f602f5, 0x2fc02fb02fa02f9, + 0x30002ff02fe02fd, 0x304030303020301, 0x308030703060305, + 0x30c030b030a0309, 0x310030f030e030d, 0x314031303120311, + 0x318031703160315, 0xffff031b031a0319, 0xffffffff031cffff, + 0xffff031e031dffff, 0xffff0320ffff031f, 0xffffffffffff0321, + 0x322ffffffffffff, 0xffff0323ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x325ffff0324ffff, 0x327ffff0326ffff, + 0x329ffff0328ffff, 0x32bffff032affff, 0x32dffff032cffff, + 0x32fffff032effff, 0x331ffff0330ffff, 0x333ffff0332ffff, + 0x335ffff0334ffff, 0x337ffff0336ffff, 0x339ffff0338ffff, + 0x33bffff033affff, 0x33dffff033cffff, 0x33fffff033effff, + 0x341ffff0340ffff, 0x343ffff0342ffff, 0x345ffff0344ffff, + 0x347ffff0346ffff, 0x349ffff0348ffff, 0x34bffff034affff, + 0x34dffff034cffff, 0x34fffff034effff, 0x351ffff0350ffff, + 0x353ffff0352ffff, 0x355ffff0354ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0357ffff0356, 0x358ffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x35c035b035a0359, 0x360035f035e035d, 0x364036303620361, + 0x368036703660365, 0x36c036b036a0369, 0x370036f036e036d, + 0x374037303720371, 0x378037703760375, 0x37c037b037a0379, + 0x37fffff037e037d, 0xffffffffffffffff, 0xffffffff0380ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x382ffff0381ffff, 0x384ffff0383ffff, + 0x386ffff0385ffff, 0x388ffff0387ffff, 0x38affff0389ffff, + 0x38cffff038bffff, 0x38effff038dffff, 0x390ffff038fffff, + 0x392ffff0391ffff, 0x394ffff0393ffff, 0x396ffff0395ffff, + 0xffffffff0397ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x399ffff0398ffff, + 0x39bffff039affff, 0x39dffff039cffff, 0x39fffff039effff, + 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3a4ffffffffffff, + 0x3a6ffff03a5ffff, 0x3a8ffff03a7ffff, 0x3aaffff03a9ffff, + 0x3abffffffffffff, 0x3adffff03acffff, 0x3afffff03aeffff, + 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, 0x3b5ffff03b4ffff, + 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, 0x3bbffff03baffff, + 0x3bdffff03bcffff, 0x3bfffff03beffff, 0x3c1ffff03c0ffff, + 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, 0x3c7ffff03c6ffff, + 0x3c9ffff03c8ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff03caffffffff, 0x3ccffffffff03cb, 0x3ceffff03cdffff, + 0x3d0ffff03cfffff, 0xffffffffffffffff, 0xffffffffffff03d1, + 0x3d3ffff03d2ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3d5ffff03d4ffff, 0x3d7ffff03d6ffff, + 0xffffffff03d8ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x42404220420041e, 0xffff042c042a0427, 0xffffffffffffffff, + 0xffffffffffffffff, 0x430ffffffffffff, 0x438043604340432, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3db03da03d9ffff, 0x3df03de03dd03dc, + 0x3e303e203e103e0, 0x3e703e603e503e4, 0x3eb03ea03e903e8, + 0x3ef03ee03ed03ec, 0xffff03f203f103f0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3f603f503f403f3, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, + 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, + 0x40e040d040c040b, 0x41204110410040f, 0x416041504140413, + 0x41a041904180417, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff]); //8064 bytes -enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xbc0], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x2000000010000, 0x6000500040003, 0x80007, 0xb000a00090000, 0xf000e000d000c, 0x1200110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14001300000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18001700160015, 0x1c001b001a0019, 0x0, 0x1f001e001d, 0x0, 0x0, 0x21002000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25002400230022, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1d001c001b001a, 0x210020001f001e, 0x25002400230022, 0x29002800270026, 0x2d002c002b002a, 0xffff0030002f002e, 0x34003300320031, 0x413003700360035, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0039ffff0038, 0xffff003bffff003a, 0xffff003dffff003c, 0xffff003fffff003e, 0xffff0041ffff0040, 0xffff0043ffff0042, 0xffff0045ffff0044, 0xffff0047ffff0046, 0xffff0049ffff0048, 0xffff004bffff004a, 0xffff004dffff004c, 0xffff004fffff004e, 0xffff0051ffff0414, 0xffff0053ffff0052, 0x55ffff0054ffff, 0x57ffff0056ffff, 0x59ffff0058ffff, 0x5bffff005affff, 0xffff005c0423ffff, 0xffff005effff005d, 0xffff0060ffff005f, 0xffff0062ffff0061, 0xffff0064ffff0063, 0xffff0066ffff0065, 0xffff0068ffff0067, 0xffff006affff0069, 0xffff006cffff006b, 0xffff006effff006d, 0xffff0070ffff006f, 0xffff0072ffff0071, 0x75ffff00740073, 0xffffffff0076ffff, 0xffff00780077ffff, 0x7b007affff0079, 0x7e007d007cffff, 0x80007fffffffff, 0x83ffff00820081, 0x860085ffff0084, 0xffffffffffff0087, 0x8affff00890088, 0xffff008cffff008b, 0x8f008effff008d, 0xffffffff0090ffff, 0x930092ffff0091, 0x9600950094ffff, 0x98ffff0097ffff, 0xffffffffffff0099, 0xffffffffffff009a, 0xffffffffffffffff, 0x9dffff009c009b, 0xa0009fffff009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, 0xa6ffff00a5ffff, 0xa8ffff00a7ffff, 0xffff00a9ffffffff, 0xffff00abffff00aa, 0xffff00adffff00ac, 0xffff00afffff00ae, 0xffff00b1ffff00b0, 0xffff00b300b20426, 0xb600b5ffff00b4, 0xffff00b8ffff00b7, 0xffff00baffff00b9, 0xffff00bcffff00bb, 0xffff00beffff00bd, 0xffff00c0ffff00bf, 0xffff00c2ffff00c1, 0xffff00c4ffff00c3, 0xffff00c6ffff00c5, 0xffff00c8ffff00c7, 0xffff00caffff00c9, 0xffff00ccffff00cb, 0xffff00ceffff00cd, 0xffff00d0ffff00cf, 0xffff00d2ffff00d1, 0xffff00d4ffff00d3, 0xffffffffffffffff, 0xd600d5ffffffff, 0xffff00d800d7ffff, 0xdaffff00d9ffff, 0xffff00dd00dc00db, 0xffff00dfffff00de, 0xffff00e1ffff00e0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff00e3ffff00e2, 0xffff00e4ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff00e5ffffffff, 0xffff00e800e700e6, 0xeb00eaffff00e9, 0xee00ed00ec0424, 0xf200f100f000ef, 0xf600f500f400f3, 0xfa00f900f800f7, 0xfdffff00fc00fb, 0x101010000ff00fe, 0x105010401030102, 0xffffffffffffffff, 0xffffffffffff0425, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x106ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0108ffff0107, 0xffff010affff0109, 0xffff010cffff010b, 0xffff010effff010d, 0xffff0110ffff010f, 0xffff0112ffff0111, 0xffffffffffffffff, 0x114ffffffff0113, 0xffff01160115ffff, 0x11901180117ffff, 0x11d011c011b011a, 0x1210120011f011e, 0x125012401230122, 0x129012801270126, 0x12d012c012b012a, 0x1310130012f012e, 0x135013401330132, 0x139013801370136, 0x13d013c013b013a, 0x1410140013f013e, 0x145014401430142, 0x149014801470146, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff014bffff014a, 0xffff014dffff014c, 0xffff014fffff014e, 0xffff0151ffff0150, 0xffff0153ffff0152, 0xffff0155ffff0154, 0xffff0157ffff0156, 0xffff0159ffff0158, 0xffffffffffff015a, 0xffffffffffffffff, 0xffff015bffffffff, 0xffff015dffff015c, 0xffff015fffff015e, 0xffff0161ffff0160, 0xffff0163ffff0162, 0xffff0165ffff0164, 0xffff0167ffff0166, 0xffff0169ffff0168, 0xffff016bffff016a, 0xffff016dffff016c, 0xffff016fffff016e, 0xffff0171ffff0170, 0xffff0173ffff0172, 0xffff0175ffff0174, 0x178ffff01770176, 0x17affff0179ffff, 0x17cffff017bffff, 0xffffffff017dffff, 0xffff017fffff017e, 0xffff0181ffff0180, 0xffff0183ffff0182, 0xffff0185ffff0184, 0xffff0187ffff0186, 0xffff0189ffff0188, 0xffff018bffff018a, 0xffff018dffff018c, 0xffff018fffff018e, 0xffff0191ffff0190, 0xffff0193ffff0192, 0xffff0195ffff0194, 0xffff0197ffff0196, 0xffff0199ffff0198, 0xffff019bffff019a, 0xffff019dffff019c, 0xffff019fffff019e, 0xffff01a1ffff01a0, 0xffff01a3ffff01a2, 0xffff01a5ffff01a4, 0xffff01a7ffff01a6, 0xffff01a9ffff01a8, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ac01ab01aaffff, 0x1b001af01ae01ad, 0x1b401b301b201b1, 0x1b801b701b601b5, 0x1bc01bb01ba01b9, 0x1c001bf01be01bd, 0x1c401c301c201c1, 0x1c801c701c601c5, 0x1cc01cb01ca01c9, 0xffff01cf01ce01cd, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x41dffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1d301d201d101d0, 0x1d701d601d501d4, 0x1db01da01d901d8, 0x1df01de01dd01dc, 0x1e301e201e101e0, 0x1e701e601e501e4, 0x1eb01ea01e901e8, 0x1ef01ee01ed01ec, 0x1f301f201f101f0, 0x1f6ffff01f501f4, 0xffffffffffffffff, 0xffffffff01f7ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff01f9ffff01f8, 0xffff01fbffff01fa, 0xffff01fdffff01fc, 0xffff01ffffff01fe, 0xffff0201ffff0200, 0xffff0203ffff0202, 0xffff0205ffff0204, 0xffff0207ffff0206, 0xffff0209ffff0208, 0xffff020bffff020a, 0xffff020dffff020c, 0xffff020fffff020e, 0xffff0211ffff0210, 0xffff0213ffff0212, 0xffff0215ffff0214, 0xffff0217ffff0216, 0xffff0219ffff0218, 0xffff021bffff021a, 0xffff021dffff021c, 0xffff021fffff021e, 0xffff0221ffff0220, 0xffff0223ffff0222, 0xffff0225ffff0224, 0xffff0227ffff0226, 0xffff0229ffff0228, 0xffff022bffff022a, 0xffff022dffff022c, 0xffff022fffff022e, 0xffff0231ffff0230, 0xffff0233ffff0232, 0xffff0235ffff0234, 0xffff0237ffff0236, 0xffff0239ffff0238, 0xffff023bffff023a, 0xffff023dffff023c, 0xffff023fffff023e, 0xffff0241ffff0240, 0x4280427ffff0242, 0xffff042b042a0429, 0xffff0243ffffffff, 0xffff0245ffff0244, 0xffff0247ffff0246, 0xffff0249ffff0248, 0xffff024bffff024a, 0xffff024dffff024c, 0xffff024fffff024e, 0xffff0251ffff0250, 0xffff0253ffff0252, 0xffff0255ffff0254, 0xffff0257ffff0256, 0xffff0259ffff0258, 0xffff025bffff025a, 0xffff025dffff025c, 0xffff025fffff025e, 0xffff0261ffff0260, 0xffff0263ffff0262, 0xffff0265ffff0264, 0xffff0267ffff0266, 0xffff0269ffff0268, 0xffff026bffff026a, 0xffff026dffff026c, 0xffff026fffff026e, 0xffff0271ffff0270, 0xffff0273ffff0272, 0xffffffffffffffff, 0xffffffffffffffff, 0x277027602750274, 0x27b027a02790278, 0xffffffffffffffff, 0xffffffffffffffff, 0x27f027e027d027c, 0xffffffff02810280, 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, 0x289028802870286, 0xffffffffffffffff, 0xffffffffffffffff, 0x28d028c028b028a, 0x2910290028f028e, 0xffffffffffffffff, 0xffffffffffffffff, 0x295029402930292, 0xffffffff02970296, 0xffff042dffff042c, 0xffff042fffff042e, 0x299ffff0298ffff, 0x29bffff029affff, 0xffffffffffffffff, 0xffffffffffffffff, 0x29f029e029d029c, 0x2a302a202a102a0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x43f043e043d043c, 0x443044204410440, 0x447044604450444, 0x44b044a04490448, 0x44f044e044d044c, 0x453045204510450, 0x457045604550454, 0x45b045a04590458, 0x45f045e045d045c, 0x463046204610460, 0x467046604650464, 0x46b046a04690468, 0x46c0472ffffffff, 0x4780430ffff0473, 0x2bf02be02bd02bc, 0xffffffffffff046d, 0x46e0474ffffffff, 0x4790431ffff0475, 0x2c402c302c202c1, 0xffffffffffff046f, 0x4330432ffffffff, 0x4350434ffffffff, 0x2c902c802c702c6, 0xffffffffffffffff, 0x4370436ffffffff, 0x43a0439ffff0438, 0x2cd02cc02cb02ca, 0xffffffffffff02ce, 0x4700476ffffffff, 0x47a043bffff0477, 0x2d202d102d002cf, 0xffffffffffff0471, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02d4ffffffff, 0x2d602d5ffffffff, 0xffffffffffffffff, 0xffff02d7ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2db02da02d902d8, 0x2df02de02dd02dc, 0x2e302e202e102e0, 0x2e702e602e502e4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2e8ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2ea02e9ffffffff, 0x2ee02ed02ec02eb, 0x2f202f102f002ef, 0x2f602f502f402f3, 0x2fa02f902f802f7, 0x2fe02fd02fc02fb, 0x3020301030002ff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x306030503040303, 0x30a030903080307, 0x30e030d030c030b, 0x31203110310030f, 0x316031503140313, 0x31a031903180317, 0x31e031d031c031b, 0x32203210320031f, 0x326032503240323, 0x32a032903280327, 0x32e032d032c032b, 0xffff03310330032f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3340333ffff0332, 0x336ffffffff0335, 0x338ffff0337ffff, 0x33b033a0339ffff, 0xffff033dffff033c, 0xffffffff033effff, 0xffffffffffffffff, 0x340033fffffffff, 0xffff0342ffff0341, 0xffff0344ffff0343, 0xffff0346ffff0345, 0xffff0348ffff0347, 0xffff034affff0349, 0xffff034cffff034b, 0xffff034effff034d, 0xffff0350ffff034f, 0xffff0352ffff0351, 0xffff0354ffff0353, 0xffff0356ffff0355, 0xffff0358ffff0357, 0xffff035affff0359, 0xffff035cffff035b, 0xffff035effff035d, 0xffff0360ffff035f, 0xffff0362ffff0361, 0xffff0364ffff0363, 0xffff0366ffff0365, 0xffff0368ffff0367, 0xffff036affff0369, 0xffff036cffff036b, 0xffff036effff036d, 0xffff0370ffff036f, 0xffff0372ffff0371, 0xffffffffffffffff, 0x373ffffffffffff, 0xffffffff0374ffff, 0xffff0375ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0377ffff0376, 0xffff0379ffff0378, 0xffff037bffff037a, 0xffff037dffff037c, 0xffff037fffff037e, 0xffff0381ffff0380, 0xffff0383ffff0382, 0xffff0385ffff0384, 0xffff0387ffff0386, 0xffff0389ffff0388, 0xffff038bffff038a, 0xffffffffffff038c, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff038effff038d, 0xffff0390ffff038f, 0xffff0392ffff0391, 0xffff0394ffff0393, 0xffff0396ffff0395, 0xffff0398ffff0397, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0399ffffffff, 0xffff039bffff039a, 0xffff039dffff039c, 0xffff039fffff039e, 0xffff03a0ffffffff, 0xffff03a2ffff03a1, 0xffff03a4ffff03a3, 0xffff03a6ffff03a5, 0xffff03a8ffff03a7, 0xffff03aaffff03a9, 0xffff03acffff03ab, 0xffff03aeffff03ad, 0xffff03b0ffff03af, 0xffff03b2ffff03b1, 0xffff03b4ffff03b3, 0xffff03b6ffff03b5, 0xffff03b8ffff03b7, 0xffff03baffff03b9, 0xffff03bcffff03bb, 0xffff03beffff03bd, 0xffffffffffffffff, 0xffffffffffffffff, 0x3c0ffff03bfffff, 0xffff03c203c1ffff, 0xffff03c4ffff03c3, 0xffff03c6ffff03c5, 0x3c7ffffffffffff, 0xffffffff03c8ffff, 0xffff03caffff03c9, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03ccffff03cb, 0xffff03ceffff03cd, 0xffff03d0ffff03cf, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x419041804170416, 0xffff041c041b041a, 0xffffffffffffffff, 0xffffffffffffffff, 0x41effffffffffff, 0x42204210420041f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3d303d203d1ffff, 0x3d703d603d503d4, 0x3db03da03d903d8, 0x3df03de03dd03dc, 0x3e303e203e103e0, 0x3e703e603e503e4, 0xffff03ea03e903e8, 0xffffffffffffffff, 0x3ee03ed03ec03eb, 0x3f203f103f003ef, 0x3f603f503f403f3, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, 0x100], + [0x100, 0x380, 0xbc0], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x2000000010000, 0x6000500040003, 0x80007, 0xb000a00090000, + 0xf000e000d000c, 0x1200110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x14001300000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x18001700160015, 0x1c001b001a0019, 0x0, + 0x1f001e001d, 0x0, 0x0, 0x21002000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x25002400230022, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x2c, 0x0, + 0x0, 0x0, 0x0, 0x2d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, + 0x1200110010000f, 0x16001500140013, 0xffff001900180017, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1d001c001b001a, 0x210020001f001e, 0x25002400230022, 0x29002800270026, + 0x2d002c002b002a, 0xffff0030002f002e, 0x34003300320031, + 0x413003700360035, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0039ffff0038, 0xffff003bffff003a, 0xffff003dffff003c, + 0xffff003fffff003e, 0xffff0041ffff0040, 0xffff0043ffff0042, + 0xffff0045ffff0044, 0xffff0047ffff0046, 0xffff0049ffff0048, + 0xffff004bffff004a, 0xffff004dffff004c, 0xffff004fffff004e, + 0xffff0051ffff0414, 0xffff0053ffff0052, 0x55ffff0054ffff, + 0x57ffff0056ffff, 0x59ffff0058ffff, 0x5bffff005affff, + 0xffff005c0423ffff, 0xffff005effff005d, 0xffff0060ffff005f, + 0xffff0062ffff0061, 0xffff0064ffff0063, 0xffff0066ffff0065, + 0xffff0068ffff0067, 0xffff006affff0069, 0xffff006cffff006b, + 0xffff006effff006d, 0xffff0070ffff006f, 0xffff0072ffff0071, + 0x75ffff00740073, 0xffffffff0076ffff, 0xffff00780077ffff, + 0x7b007affff0079, 0x7e007d007cffff, 0x80007fffffffff, 0x83ffff00820081, + 0x860085ffff0084, 0xffffffffffff0087, 0x8affff00890088, + 0xffff008cffff008b, 0x8f008effff008d, 0xffffffff0090ffff, + 0x930092ffff0091, 0x9600950094ffff, 0x98ffff0097ffff, + 0xffffffffffff0099, 0xffffffffffff009a, 0xffffffffffffffff, + 0x9dffff009c009b, 0xa0009fffff009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, + 0xa6ffff00a5ffff, 0xa8ffff00a7ffff, 0xffff00a9ffffffff, + 0xffff00abffff00aa, 0xffff00adffff00ac, 0xffff00afffff00ae, + 0xffff00b1ffff00b0, 0xffff00b300b20426, 0xb600b5ffff00b4, + 0xffff00b8ffff00b7, 0xffff00baffff00b9, 0xffff00bcffff00bb, + 0xffff00beffff00bd, 0xffff00c0ffff00bf, 0xffff00c2ffff00c1, + 0xffff00c4ffff00c3, 0xffff00c6ffff00c5, 0xffff00c8ffff00c7, + 0xffff00caffff00c9, 0xffff00ccffff00cb, 0xffff00ceffff00cd, + 0xffff00d0ffff00cf, 0xffff00d2ffff00d1, 0xffff00d4ffff00d3, + 0xffffffffffffffff, 0xd600d5ffffffff, 0xffff00d800d7ffff, + 0xdaffff00d9ffff, 0xffff00dd00dc00db, 0xffff00dfffff00de, + 0xffff00e1ffff00e0, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff00e3ffff00e2, 0xffff00e4ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff00e5ffffffff, 0xffff00e800e700e6, 0xeb00eaffff00e9, + 0xee00ed00ec0424, 0xf200f100f000ef, 0xf600f500f400f3, 0xfa00f900f800f7, + 0xfdffff00fc00fb, 0x101010000ff00fe, 0x105010401030102, + 0xffffffffffffffff, 0xffffffffffff0425, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x106ffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0108ffff0107, + 0xffff010affff0109, 0xffff010cffff010b, 0xffff010effff010d, + 0xffff0110ffff010f, 0xffff0112ffff0111, 0xffffffffffffffff, + 0x114ffffffff0113, 0xffff01160115ffff, 0x11901180117ffff, + 0x11d011c011b011a, 0x1210120011f011e, 0x125012401230122, + 0x129012801270126, 0x12d012c012b012a, 0x1310130012f012e, + 0x135013401330132, 0x139013801370136, 0x13d013c013b013a, + 0x1410140013f013e, 0x145014401430142, 0x149014801470146, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff014bffff014a, 0xffff014dffff014c, 0xffff014fffff014e, + 0xffff0151ffff0150, 0xffff0153ffff0152, 0xffff0155ffff0154, + 0xffff0157ffff0156, 0xffff0159ffff0158, 0xffffffffffff015a, + 0xffffffffffffffff, 0xffff015bffffffff, 0xffff015dffff015c, + 0xffff015fffff015e, 0xffff0161ffff0160, 0xffff0163ffff0162, + 0xffff0165ffff0164, 0xffff0167ffff0166, 0xffff0169ffff0168, + 0xffff016bffff016a, 0xffff016dffff016c, 0xffff016fffff016e, + 0xffff0171ffff0170, 0xffff0173ffff0172, 0xffff0175ffff0174, + 0x178ffff01770176, 0x17affff0179ffff, 0x17cffff017bffff, + 0xffffffff017dffff, 0xffff017fffff017e, 0xffff0181ffff0180, + 0xffff0183ffff0182, 0xffff0185ffff0184, 0xffff0187ffff0186, + 0xffff0189ffff0188, 0xffff018bffff018a, 0xffff018dffff018c, + 0xffff018fffff018e, 0xffff0191ffff0190, 0xffff0193ffff0192, + 0xffff0195ffff0194, 0xffff0197ffff0196, 0xffff0199ffff0198, + 0xffff019bffff019a, 0xffff019dffff019c, 0xffff019fffff019e, + 0xffff01a1ffff01a0, 0xffff01a3ffff01a2, 0xffff01a5ffff01a4, + 0xffff01a7ffff01a6, 0xffff01a9ffff01a8, 0xffffffffffffffff, + 0xffffffffffffffff, 0x1ac01ab01aaffff, 0x1b001af01ae01ad, + 0x1b401b301b201b1, 0x1b801b701b601b5, 0x1bc01bb01ba01b9, + 0x1c001bf01be01bd, 0x1c401c301c201c1, 0x1c801c701c601c5, + 0x1cc01cb01ca01c9, 0xffff01cf01ce01cd, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x41dffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1d301d201d101d0, 0x1d701d601d501d4, 0x1db01da01d901d8, + 0x1df01de01dd01dc, 0x1e301e201e101e0, 0x1e701e601e501e4, + 0x1eb01ea01e901e8, 0x1ef01ee01ed01ec, 0x1f301f201f101f0, + 0x1f6ffff01f501f4, 0xffffffffffffffff, 0xffffffff01f7ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff01f9ffff01f8, 0xffff01fbffff01fa, 0xffff01fdffff01fc, + 0xffff01ffffff01fe, 0xffff0201ffff0200, 0xffff0203ffff0202, + 0xffff0205ffff0204, 0xffff0207ffff0206, 0xffff0209ffff0208, + 0xffff020bffff020a, 0xffff020dffff020c, 0xffff020fffff020e, + 0xffff0211ffff0210, 0xffff0213ffff0212, 0xffff0215ffff0214, + 0xffff0217ffff0216, 0xffff0219ffff0218, 0xffff021bffff021a, + 0xffff021dffff021c, 0xffff021fffff021e, 0xffff0221ffff0220, + 0xffff0223ffff0222, 0xffff0225ffff0224, 0xffff0227ffff0226, + 0xffff0229ffff0228, 0xffff022bffff022a, 0xffff022dffff022c, + 0xffff022fffff022e, 0xffff0231ffff0230, 0xffff0233ffff0232, + 0xffff0235ffff0234, 0xffff0237ffff0236, 0xffff0239ffff0238, + 0xffff023bffff023a, 0xffff023dffff023c, 0xffff023fffff023e, + 0xffff0241ffff0240, 0x4280427ffff0242, 0xffff042b042a0429, + 0xffff0243ffffffff, 0xffff0245ffff0244, 0xffff0247ffff0246, + 0xffff0249ffff0248, 0xffff024bffff024a, 0xffff024dffff024c, + 0xffff024fffff024e, 0xffff0251ffff0250, 0xffff0253ffff0252, + 0xffff0255ffff0254, 0xffff0257ffff0256, 0xffff0259ffff0258, + 0xffff025bffff025a, 0xffff025dffff025c, 0xffff025fffff025e, + 0xffff0261ffff0260, 0xffff0263ffff0262, 0xffff0265ffff0264, + 0xffff0267ffff0266, 0xffff0269ffff0268, 0xffff026bffff026a, + 0xffff026dffff026c, 0xffff026fffff026e, 0xffff0271ffff0270, + 0xffff0273ffff0272, 0xffffffffffffffff, 0xffffffffffffffff, + 0x277027602750274, 0x27b027a02790278, 0xffffffffffffffff, + 0xffffffffffffffff, 0x27f027e027d027c, 0xffffffff02810280, + 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, + 0x289028802870286, 0xffffffffffffffff, 0xffffffffffffffff, + 0x28d028c028b028a, 0x2910290028f028e, 0xffffffffffffffff, + 0xffffffffffffffff, 0x295029402930292, 0xffffffff02970296, + 0xffff042dffff042c, 0xffff042fffff042e, 0x299ffff0298ffff, + 0x29bffff029affff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x29f029e029d029c, 0x2a302a202a102a0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x43f043e043d043c, 0x443044204410440, 0x447044604450444, + 0x44b044a04490448, 0x44f044e044d044c, 0x453045204510450, + 0x457045604550454, 0x45b045a04590458, 0x45f045e045d045c, + 0x463046204610460, 0x467046604650464, 0x46b046a04690468, + 0x46c0472ffffffff, 0x4780430ffff0473, 0x2bf02be02bd02bc, + 0xffffffffffff046d, 0x46e0474ffffffff, 0x4790431ffff0475, + 0x2c402c302c202c1, 0xffffffffffff046f, 0x4330432ffffffff, + 0x4350434ffffffff, 0x2c902c802c702c6, 0xffffffffffffffff, + 0x4370436ffffffff, 0x43a0439ffff0438, 0x2cd02cc02cb02ca, + 0xffffffffffff02ce, 0x4700476ffffffff, 0x47a043bffff0477, + 0x2d202d102d002cf, 0xffffffffffff0471, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02d4ffffffff, + 0x2d602d5ffffffff, 0xffffffffffffffff, 0xffff02d7ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2db02da02d902d8, + 0x2df02de02dd02dc, 0x2e302e202e102e0, 0x2e702e602e502e4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2e8ffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2ea02e9ffffffff, 0x2ee02ed02ec02eb, 0x2f202f102f002ef, + 0x2f602f502f402f3, 0x2fa02f902f802f7, 0x2fe02fd02fc02fb, + 0x3020301030002ff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x306030503040303, 0x30a030903080307, + 0x30e030d030c030b, 0x31203110310030f, 0x316031503140313, + 0x31a031903180317, 0x31e031d031c031b, 0x32203210320031f, + 0x326032503240323, 0x32a032903280327, 0x32e032d032c032b, + 0xffff03310330032f, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3340333ffff0332, 0x336ffffffff0335, + 0x338ffff0337ffff, 0x33b033a0339ffff, 0xffff033dffff033c, + 0xffffffff033effff, 0xffffffffffffffff, 0x340033fffffffff, + 0xffff0342ffff0341, 0xffff0344ffff0343, 0xffff0346ffff0345, + 0xffff0348ffff0347, 0xffff034affff0349, 0xffff034cffff034b, + 0xffff034effff034d, 0xffff0350ffff034f, 0xffff0352ffff0351, + 0xffff0354ffff0353, 0xffff0356ffff0355, 0xffff0358ffff0357, + 0xffff035affff0359, 0xffff035cffff035b, 0xffff035effff035d, + 0xffff0360ffff035f, 0xffff0362ffff0361, 0xffff0364ffff0363, + 0xffff0366ffff0365, 0xffff0368ffff0367, 0xffff036affff0369, + 0xffff036cffff036b, 0xffff036effff036d, 0xffff0370ffff036f, + 0xffff0372ffff0371, 0xffffffffffffffff, 0x373ffffffffffff, + 0xffffffff0374ffff, 0xffff0375ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0377ffff0376, + 0xffff0379ffff0378, 0xffff037bffff037a, 0xffff037dffff037c, + 0xffff037fffff037e, 0xffff0381ffff0380, 0xffff0383ffff0382, + 0xffff0385ffff0384, 0xffff0387ffff0386, 0xffff0389ffff0388, + 0xffff038bffff038a, 0xffffffffffff038c, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff038effff038d, 0xffff0390ffff038f, 0xffff0392ffff0391, + 0xffff0394ffff0393, 0xffff0396ffff0395, 0xffff0398ffff0397, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0399ffffffff, 0xffff039bffff039a, 0xffff039dffff039c, + 0xffff039fffff039e, 0xffff03a0ffffffff, 0xffff03a2ffff03a1, + 0xffff03a4ffff03a3, 0xffff03a6ffff03a5, 0xffff03a8ffff03a7, + 0xffff03aaffff03a9, 0xffff03acffff03ab, 0xffff03aeffff03ad, + 0xffff03b0ffff03af, 0xffff03b2ffff03b1, 0xffff03b4ffff03b3, + 0xffff03b6ffff03b5, 0xffff03b8ffff03b7, 0xffff03baffff03b9, + 0xffff03bcffff03bb, 0xffff03beffff03bd, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3c0ffff03bfffff, 0xffff03c203c1ffff, + 0xffff03c4ffff03c3, 0xffff03c6ffff03c5, 0x3c7ffffffffffff, + 0xffffffff03c8ffff, 0xffff03caffff03c9, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03ccffff03cb, + 0xffff03ceffff03cd, 0xffff03d0ffff03cf, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x419041804170416, 0xffff041c041b041a, + 0xffffffffffffffff, 0xffffffffffffffff, 0x41effffffffffff, + 0x42204210420041f, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3d303d203d1ffff, 0x3d703d603d503d4, + 0x3db03da03d903d8, 0x3df03de03dd03dc, 0x3e303e203e103e0, + 0x3e703e603e503e4, 0xffff03ea03e903e8, 0xffffffffffffffff, + 0x3ee03ed03ec03eb, 0x3f203f103f003ef, 0x3f603f503f403f3, + 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, + 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, + 0x41204110410040f, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff]); //8192 bytes -enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xc00], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x41fffffffffffff, 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, 0x51ffff0050ffff, 0x53ffff00520421, 0x55ffff0054ffff, 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, 0xffff005cffff005b, 0x5effff043e005d, 0x60ffff005fffff, 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, 0x910090008f008e, 0x95009400930092, 0xffff0097ffff0096, 0xffff0099ffff0098, 0xffff009bffff009a, 0xffff009dffff009c, 0xa0ffff009f009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, 0xa6ffff00a5ffff, 0xa8ffff00a7ffff, 0xab00aa00a90446, 0xffffffff00acffff, 0xaeffff00adffff, 0xb0ffff00afffff, 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, 0xbaffff00b9ffff, 0xbcffff00bbffff, 0xbeffff00bdffff, 0xc0ffff00bfffff, 0xc1ffffffffffff, 0xc3ffff00c2ffff, 0xc5ffff00c4ffff, 0xc7ffff00c6ffff, 0xc9ffff00c8ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xcbffffffff00ca, 0xffff00cdffff00cc, 0xceffffffffffff, 0xd0ffff00cfffff, 0xd2ffff00d1ffff, 0xd600d500d400d3, 0xd900d8ffff00d7, 0xdbffff00daffff, 0xffffffffffffffff, 0xddffffffff00dc, 0xffff00df00deffff, 0xe2ffff00e100e0, 0xe3ffffffffffff, 0xffff00e500e4ffff, 0xffffffff00e6ffff, 0xffffffffffffffff, 0xffffffff00e7ffff, 0xe9ffffffff00e8, 0xffffffffffffffff, 0xed00ec00eb00ea, 0xffffffffffff00ee, 0xffff00efffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00f0ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf2ffff00f1ffff, 0xf3ffffffffffff, 0xf4ffffffffffff, 0xffffffff00f600f5, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff0440, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfa00f900f800f7, 0xfd00fc00fb0443, 0x101010000ff00fe, 0x105010401030102, 0x109010801070106, 0x10d010c010b010a, 0x1110110010f010e, 0x115011401130112, 0xffff011801170116, 0xffffffff011a0119, 0x11d011c011bffff, 0x11fffff011effff, 0x121ffff0120ffff, 0x123ffff0122ffff, 0x125ffff0124ffff, 0x127ffff0126ffff, 0x129ffff0128ffff, 0xffff012c012b012a, 0xffffffff012dffff, 0x12fffffffff012e, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x133013201310130, 0x137013601350134, 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, 0x147014601450144, 0x14b014a01490148, 0x14f014e014d014c, 0x153015201510150, 0x157015601550154, 0x15b015a01590158, 0x15f015e015d015c, 0x161ffff0160ffff, 0x163ffff0162ffff, 0x165ffff0164ffff, 0x167ffff0166ffff, 0x169ffff0168ffff, 0x16bffff016affff, 0x16dffff016cffff, 0x16fffff016effff, 0xffffffff0170ffff, 0xffffffffffffffff, 0x171ffffffffffff, 0x173ffff0172ffff, 0x175ffff0174ffff, 0x177ffff0176ffff, 0x179ffff0178ffff, 0x17bffff017affff, 0x17dffff017cffff, 0x17fffff017effff, 0x181ffff0180ffff, 0x183ffff0182ffff, 0x185ffff0184ffff, 0x187ffff0186ffff, 0x189ffff0188ffff, 0x18bffff018affff, 0xffff018cffffffff, 0xffff018effff018d, 0xffff0190ffff018f, 0x1930192ffff0191, 0x195ffff0194ffff, 0x197ffff0196ffff, 0x199ffff0198ffff, 0x19bffff019affff, 0x19dffff019cffff, 0x19fffff019effff, 0x1a1ffff01a0ffff, 0x1a3ffff01a2ffff, 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, 0x1a9ffff01a8ffff, 0x1abffff01aaffff, 0x1adffff01acffff, 0x1afffff01aeffff, 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, 0x1b5ffff01b4ffff, 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, 0x1bbffff01baffff, 0x1bdffff01bcffff, 0x1bfffff01beffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1c201c101c0ffff, 0x1c601c501c401c3, 0x1ca01c901c801c7, 0x1ce01cd01cc01cb, 0x1d201d101d001cf, 0x1d601d501d401d3, 0x1da01d901d801d7, 0x1de01dd01dc01db, 0x1e201e101e001df, 0x43201e501e401e3, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff01e6ffff, 0xffffffff01e7ffff, 0x1e9ffff01e8ffff, 0x1ebffff01eaffff, 0x1edffff01ecffff, 0x1efffff01eeffff, 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, 0x1f5ffff01f4ffff, 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, 0x1fbffff01faffff, 0x1fdffff01fcffff, 0x1ffffff01feffff, 0x201ffff0200ffff, 0x203ffff0202ffff, 0x205ffff0204ffff, 0x207ffff0206ffff, 0x209ffff0208ffff, 0x20bffff020affff, 0x20dffff020cffff, 0x20fffff020effff, 0x211ffff0210ffff, 0x213ffff0212ffff, 0x215ffff0214ffff, 0x217ffff0216ffff, 0x219ffff0218ffff, 0x21bffff021affff, 0x21dffff021cffff, 0x21fffff021effff, 0x221ffff0220ffff, 0x223ffff0222ffff, 0x225ffff0224ffff, 0x227ffff0226ffff, 0x229ffff0228ffff, 0x22bffff022affff, 0x22dffff022cffff, 0x22fffff022effff, 0x231ffff0230ffff, 0x44a04480232ffff, 0x2330450044e044c, 0xffffffffffffffff, 0x235ffff0234ffff, 0x237ffff0236ffff, 0x239ffff0238ffff, 0x23bffff023affff, 0x23dffff023cffff, 0x23fffff023effff, 0x241ffff0240ffff, 0x243ffff0242ffff, 0x245ffff0244ffff, 0x247ffff0246ffff, 0x249ffff0248ffff, 0x24bffff024affff, 0x24dffff024cffff, 0x24fffff024effff, 0x251ffff0250ffff, 0x253ffff0252ffff, 0x255ffff0254ffff, 0x257ffff0256ffff, 0x259ffff0258ffff, 0x25bffff025affff, 0x25dffff025cffff, 0x25fffff025effff, 0x261ffff0260ffff, 0x263ffff0262ffff, 0x267026602650264, 0x26b026a02690268, 0xffffffffffffffff, 0xffffffffffffffff, 0x26f026e026d026c, 0xffffffff02710270, 0xffffffffffffffff, 0xffffffffffffffff, 0x275027402730272, 0x279027802770276, 0xffffffffffffffff, 0xffffffffffffffff, 0x27d027c027b027a, 0x2810280027f027e, 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, 0xffffffff02870286, 0xffffffffffffffff, 0xffffffffffffffff, 0x289045402880452, 0x28b045a028a0457, 0xffffffffffffffff, 0xffffffffffffffff, 0x28f028e028d028c, 0x293029202910290, 0xffffffffffffffff, 0xffffffffffffffff, 0x297029602950294, 0x29b029a02990298, 0x29f029e029d029c, 0xffffffff02a102a0, 0x47e047d047c047b, 0x48204810480047f, 0x486048504840483, 0x48a048904880487, 0x48e048d048c048b, 0x49204910490048f, 0x496049504940493, 0x49a049904980497, 0x49e049d049c049b, 0x4a204a104a0049f, 0x4a604a504a404a3, 0x4aa04a904a804a7, 0x4ab04b102bb02ba, 0x4bd045dffff04b3, 0xffffffffffffffff, 0xffff02bdffff04ac, 0x4ad04b5ffffffff, 0x4c0045fffff04b7, 0xffffffffffffffff, 0xffffffffffff04ae, 0x464046102c002bf, 0x4690467ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x46f046c02c202c1, 0x476047402c30472, 0xffffffffffffffff, 0xffffffffffffffff, 0x4af04b9ffffffff, 0x4c30479ffff04bb, 0xffffffffffffffff, 0xffffffffffff04b0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c5ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c902c802c702c6, 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, 0x2d502d402d302d2, 0xffffffffffffffff, 0xffffffffffff02d6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2da02d902d802d7, 0x2de02dd02dc02db, 0x2e202e102e002df, 0x2e602e502e402e3, 0x2ea02e902e802e7, 0x2ee02ed02ec02eb, 0xffffffff02f002ef, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2f402f302f202f1, 0x2f802f702f602f5, 0x2fc02fb02fa02f9, 0x30002ff02fe02fd, 0x304030303020301, 0x308030703060305, 0x30c030b030a0309, 0x310030f030e030d, 0x314031303120311, 0x318031703160315, 0x31c031b031a0319, 0xffff031f031e031d, 0xffffffff0320ffff, 0xffff03220321ffff, 0xffff0324ffff0323, 0xffffffffffff0325, 0x326ffffffffffff, 0xffff0327ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x329ffff0328ffff, 0x32bffff032affff, 0x32dffff032cffff, 0x32fffff032effff, 0x331ffff0330ffff, 0x333ffff0332ffff, 0x335ffff0334ffff, 0x337ffff0336ffff, 0x339ffff0338ffff, 0x33bffff033affff, 0x33dffff033cffff, 0x33fffff033effff, 0x341ffff0340ffff, 0x343ffff0342ffff, 0x345ffff0344ffff, 0x347ffff0346ffff, 0x349ffff0348ffff, 0x34bffff034affff, 0x34dffff034cffff, 0x34fffff034effff, 0x351ffff0350ffff, 0x353ffff0352ffff, 0x355ffff0354ffff, 0x357ffff0356ffff, 0x359ffff0358ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff035bffff035a, 0x35cffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x360035f035e035d, 0x364036303620361, 0x368036703660365, 0x36c036b036a0369, 0x370036f036e036d, 0x374037303720371, 0x378037703760375, 0x37c037b037a0379, 0x380037f037e037d, 0x383ffff03820381, 0xffffffffffffffff, 0xffffffff0384ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x386ffff0385ffff, 0x388ffff0387ffff, 0x38affff0389ffff, 0x38cffff038bffff, 0x38effff038dffff, 0x390ffff038fffff, 0x392ffff0391ffff, 0x394ffff0393ffff, 0x396ffff0395ffff, 0x398ffff0397ffff, 0x39affff0399ffff, 0xffffffff039bffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x39dffff039cffff, 0x39fffff039effff, 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, 0x3a5ffff03a4ffff, 0x3a7ffff03a6ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3a8ffffffffffff, 0x3aaffff03a9ffff, 0x3acffff03abffff, 0x3aeffff03adffff, 0x3afffffffffffff, 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, 0x3b5ffff03b4ffff, 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, 0x3bbffff03baffff, 0x3bdffff03bcffff, 0x3bfffff03beffff, 0x3c1ffff03c0ffff, 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, 0x3c7ffff03c6ffff, 0x3c9ffff03c8ffff, 0x3cbffff03caffff, 0x3cdffff03ccffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03ceffffffff, 0x3d0ffffffff03cf, 0x3d2ffff03d1ffff, 0x3d4ffff03d3ffff, 0xffffffffffffffff, 0xffffffffffff03d5, 0x3d7ffff03d6ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3d9ffff03d8ffff, 0x3dbffff03daffff, 0xffffffff03dcffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x428042604240422, 0xffff0430042e042b, 0xffffffffffffffff, 0xffffffffffffffff, 0x434ffffffffffff, 0x43c043a04380436, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3df03de03ddffff, 0x3e303e203e103e0, 0x3e703e603e503e4, 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, 0x3f303f203f103f0, 0xffff03f603f503f4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, 0x416041504140413, 0x41a041904180417, 0x41e041d041c041b, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, 0x100], + [0x100, 0x380, 0xc00], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, + 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, + 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, + 0x0, 0x0, 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, + 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, + 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x41fffffffffffff, + 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, + 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, + 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, + 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, + 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, + 0x51ffff0050ffff, 0x53ffff00520421, 0x55ffff0054ffff, + 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, + 0xffff005cffff005b, 0x5effff043e005d, 0x60ffff005fffff, + 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, + 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, + 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, + 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, + 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, + 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, + 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, + 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, + 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, + 0x910090008f008e, 0x95009400930092, 0xffff0097ffff0096, + 0xffff0099ffff0098, 0xffff009bffff009a, 0xffff009dffff009c, + 0xa0ffff009f009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, 0xa6ffff00a5ffff, + 0xa8ffff00a7ffff, 0xab00aa00a90446, 0xffffffff00acffff, + 0xaeffff00adffff, 0xb0ffff00afffff, 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, + 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, 0xbaffff00b9ffff, 0xbcffff00bbffff, + 0xbeffff00bdffff, 0xc0ffff00bfffff, 0xc1ffffffffffff, 0xc3ffff00c2ffff, + 0xc5ffff00c4ffff, 0xc7ffff00c6ffff, 0xc9ffff00c8ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xcbffffffff00ca, + 0xffff00cdffff00cc, 0xceffffffffffff, 0xd0ffff00cfffff, + 0xd2ffff00d1ffff, 0xd600d500d400d3, 0xd900d8ffff00d7, 0xdbffff00daffff, + 0xffffffffffffffff, 0xddffffffff00dc, 0xffff00df00deffff, + 0xe2ffff00e100e0, 0xe3ffffffffffff, 0xffff00e500e4ffff, + 0xffffffff00e6ffff, 0xffffffffffffffff, 0xffffffff00e7ffff, + 0xe9ffffffff00e8, 0xffffffffffffffff, 0xed00ec00eb00ea, + 0xffffffffffff00ee, 0xffff00efffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00f0ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xf2ffff00f1ffff, 0xf3ffffffffffff, + 0xf4ffffffffffff, 0xffffffff00f600f5, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffff0440, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfa00f900f800f7, 0xfd00fc00fb0443, + 0x101010000ff00fe, 0x105010401030102, 0x109010801070106, + 0x10d010c010b010a, 0x1110110010f010e, 0x115011401130112, + 0xffff011801170116, 0xffffffff011a0119, 0x11d011c011bffff, + 0x11fffff011effff, 0x121ffff0120ffff, 0x123ffff0122ffff, + 0x125ffff0124ffff, 0x127ffff0126ffff, 0x129ffff0128ffff, + 0xffff012c012b012a, 0xffffffff012dffff, 0x12fffffffff012e, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x133013201310130, 0x137013601350134, + 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, + 0x147014601450144, 0x14b014a01490148, 0x14f014e014d014c, + 0x153015201510150, 0x157015601550154, 0x15b015a01590158, + 0x15f015e015d015c, 0x161ffff0160ffff, 0x163ffff0162ffff, + 0x165ffff0164ffff, 0x167ffff0166ffff, 0x169ffff0168ffff, + 0x16bffff016affff, 0x16dffff016cffff, 0x16fffff016effff, + 0xffffffff0170ffff, 0xffffffffffffffff, 0x171ffffffffffff, + 0x173ffff0172ffff, 0x175ffff0174ffff, 0x177ffff0176ffff, + 0x179ffff0178ffff, 0x17bffff017affff, 0x17dffff017cffff, + 0x17fffff017effff, 0x181ffff0180ffff, 0x183ffff0182ffff, + 0x185ffff0184ffff, 0x187ffff0186ffff, 0x189ffff0188ffff, + 0x18bffff018affff, 0xffff018cffffffff, 0xffff018effff018d, + 0xffff0190ffff018f, 0x1930192ffff0191, 0x195ffff0194ffff, + 0x197ffff0196ffff, 0x199ffff0198ffff, 0x19bffff019affff, + 0x19dffff019cffff, 0x19fffff019effff, 0x1a1ffff01a0ffff, + 0x1a3ffff01a2ffff, 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, + 0x1a9ffff01a8ffff, 0x1abffff01aaffff, 0x1adffff01acffff, + 0x1afffff01aeffff, 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, + 0x1b5ffff01b4ffff, 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, + 0x1bbffff01baffff, 0x1bdffff01bcffff, 0x1bfffff01beffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1c201c101c0ffff, + 0x1c601c501c401c3, 0x1ca01c901c801c7, 0x1ce01cd01cc01cb, + 0x1d201d101d001cf, 0x1d601d501d401d3, 0x1da01d901d801d7, + 0x1de01dd01dc01db, 0x1e201e101e001df, 0x43201e501e401e3, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff01e6ffff, 0xffffffff01e7ffff, + 0x1e9ffff01e8ffff, 0x1ebffff01eaffff, 0x1edffff01ecffff, + 0x1efffff01eeffff, 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, + 0x1f5ffff01f4ffff, 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, + 0x1fbffff01faffff, 0x1fdffff01fcffff, 0x1ffffff01feffff, + 0x201ffff0200ffff, 0x203ffff0202ffff, 0x205ffff0204ffff, + 0x207ffff0206ffff, 0x209ffff0208ffff, 0x20bffff020affff, + 0x20dffff020cffff, 0x20fffff020effff, 0x211ffff0210ffff, + 0x213ffff0212ffff, 0x215ffff0214ffff, 0x217ffff0216ffff, + 0x219ffff0218ffff, 0x21bffff021affff, 0x21dffff021cffff, + 0x21fffff021effff, 0x221ffff0220ffff, 0x223ffff0222ffff, + 0x225ffff0224ffff, 0x227ffff0226ffff, 0x229ffff0228ffff, + 0x22bffff022affff, 0x22dffff022cffff, 0x22fffff022effff, + 0x231ffff0230ffff, 0x44a04480232ffff, 0x2330450044e044c, + 0xffffffffffffffff, 0x235ffff0234ffff, 0x237ffff0236ffff, + 0x239ffff0238ffff, 0x23bffff023affff, 0x23dffff023cffff, + 0x23fffff023effff, 0x241ffff0240ffff, 0x243ffff0242ffff, + 0x245ffff0244ffff, 0x247ffff0246ffff, 0x249ffff0248ffff, + 0x24bffff024affff, 0x24dffff024cffff, 0x24fffff024effff, + 0x251ffff0250ffff, 0x253ffff0252ffff, 0x255ffff0254ffff, + 0x257ffff0256ffff, 0x259ffff0258ffff, 0x25bffff025affff, + 0x25dffff025cffff, 0x25fffff025effff, 0x261ffff0260ffff, + 0x263ffff0262ffff, 0x267026602650264, 0x26b026a02690268, + 0xffffffffffffffff, 0xffffffffffffffff, 0x26f026e026d026c, + 0xffffffff02710270, 0xffffffffffffffff, 0xffffffffffffffff, + 0x275027402730272, 0x279027802770276, 0xffffffffffffffff, + 0xffffffffffffffff, 0x27d027c027b027a, 0x2810280027f027e, + 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, + 0xffffffff02870286, 0xffffffffffffffff, 0xffffffffffffffff, + 0x289045402880452, 0x28b045a028a0457, 0xffffffffffffffff, + 0xffffffffffffffff, 0x28f028e028d028c, 0x293029202910290, + 0xffffffffffffffff, 0xffffffffffffffff, 0x297029602950294, + 0x29b029a02990298, 0x29f029e029d029c, 0xffffffff02a102a0, + 0x47e047d047c047b, 0x48204810480047f, 0x486048504840483, + 0x48a048904880487, 0x48e048d048c048b, 0x49204910490048f, + 0x496049504940493, 0x49a049904980497, 0x49e049d049c049b, + 0x4a204a104a0049f, 0x4a604a504a404a3, 0x4aa04a904a804a7, + 0x4ab04b102bb02ba, 0x4bd045dffff04b3, 0xffffffffffffffff, + 0xffff02bdffff04ac, 0x4ad04b5ffffffff, 0x4c0045fffff04b7, + 0xffffffffffffffff, 0xffffffffffff04ae, 0x464046102c002bf, + 0x4690467ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x46f046c02c202c1, 0x476047402c30472, 0xffffffffffffffff, + 0xffffffffffffffff, 0x4af04b9ffffffff, 0x4c30479ffff04bb, + 0xffffffffffffffff, 0xffffffffffff04b0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c5ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2c902c802c702c6, + 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, 0x2d502d402d302d2, + 0xffffffffffffffff, 0xffffffffffff02d6, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2da02d902d802d7, + 0x2de02dd02dc02db, 0x2e202e102e002df, 0x2e602e502e402e3, + 0x2ea02e902e802e7, 0x2ee02ed02ec02eb, 0xffffffff02f002ef, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2f402f302f202f1, + 0x2f802f702f602f5, 0x2fc02fb02fa02f9, 0x30002ff02fe02fd, + 0x304030303020301, 0x308030703060305, 0x30c030b030a0309, + 0x310030f030e030d, 0x314031303120311, 0x318031703160315, + 0x31c031b031a0319, 0xffff031f031e031d, 0xffffffff0320ffff, + 0xffff03220321ffff, 0xffff0324ffff0323, 0xffffffffffff0325, + 0x326ffffffffffff, 0xffff0327ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x329ffff0328ffff, 0x32bffff032affff, + 0x32dffff032cffff, 0x32fffff032effff, 0x331ffff0330ffff, + 0x333ffff0332ffff, 0x335ffff0334ffff, 0x337ffff0336ffff, + 0x339ffff0338ffff, 0x33bffff033affff, 0x33dffff033cffff, + 0x33fffff033effff, 0x341ffff0340ffff, 0x343ffff0342ffff, + 0x345ffff0344ffff, 0x347ffff0346ffff, 0x349ffff0348ffff, + 0x34bffff034affff, 0x34dffff034cffff, 0x34fffff034effff, + 0x351ffff0350ffff, 0x353ffff0352ffff, 0x355ffff0354ffff, + 0x357ffff0356ffff, 0x359ffff0358ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff035bffff035a, 0x35cffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x360035f035e035d, 0x364036303620361, 0x368036703660365, + 0x36c036b036a0369, 0x370036f036e036d, 0x374037303720371, + 0x378037703760375, 0x37c037b037a0379, 0x380037f037e037d, + 0x383ffff03820381, 0xffffffffffffffff, 0xffffffff0384ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x386ffff0385ffff, 0x388ffff0387ffff, + 0x38affff0389ffff, 0x38cffff038bffff, 0x38effff038dffff, + 0x390ffff038fffff, 0x392ffff0391ffff, 0x394ffff0393ffff, + 0x396ffff0395ffff, 0x398ffff0397ffff, 0x39affff0399ffff, + 0xffffffff039bffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x39dffff039cffff, + 0x39fffff039effff, 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, + 0x3a5ffff03a4ffff, 0x3a7ffff03a6ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3a8ffffffffffff, + 0x3aaffff03a9ffff, 0x3acffff03abffff, 0x3aeffff03adffff, + 0x3afffffffffffff, 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, + 0x3b5ffff03b4ffff, 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, + 0x3bbffff03baffff, 0x3bdffff03bcffff, 0x3bfffff03beffff, + 0x3c1ffff03c0ffff, 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, + 0x3c7ffff03c6ffff, 0x3c9ffff03c8ffff, 0x3cbffff03caffff, + 0x3cdffff03ccffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff03ceffffffff, 0x3d0ffffffff03cf, 0x3d2ffff03d1ffff, + 0x3d4ffff03d3ffff, 0xffffffffffffffff, 0xffffffffffff03d5, + 0x3d7ffff03d6ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3d9ffff03d8ffff, 0x3dbffff03daffff, + 0xffffffff03dcffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x428042604240422, 0xffff0430042e042b, 0xffffffffffffffff, + 0xffffffffffffffff, 0x434ffffffffffff, 0x43c043a04380436, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3df03de03ddffff, 0x3e303e203e103e0, + 0x3e703e603e503e4, 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, + 0x3f303f203f103f0, 0xffff03f603f503f4, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, + 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, + 0x41204110410040f, 0x416041504140413, 0x41a041904180417, + 0x41e041d041c041b, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff]); //8064 bytes -enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xbc0], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, 0x51ffff0050ffff, 0x53ffff0052ffff, 0x55ffff0054ffff, 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, 0xffff005cffff005b, 0x5effffffff005d, 0x60ffff005fffff, 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, 0xffff008f008effff, 0x92ffff00910090, 0xffff0094ffff0093, 0xffff0096ffff0095, 0xffff0098ffff0097, 0xffff009affff0099, 0x9dffff009c009b, 0x9fffff009effff, 0xa1ffff00a0ffff, 0xa3ffff00a2ffff, 0xa5ffff00a4ffff, 0xa700a6ffffffff, 0xffffffff00a8ffff, 0xaaffff00a9ffff, 0xacffff00abffff, 0xaeffff00adffff, 0xb0ffff00afffff, 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, 0xbaffff00b9ffff, 0xbcffff00bbffff, 0xbdffffffffffff, 0xbfffff00beffff, 0xc1ffff00c0ffff, 0xc3ffff00c2ffff, 0xc5ffff00c4ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xc7ffffffff00c6, 0xffff00c9ffff00c8, 0xcaffffffffffff, 0xccffff00cbffff, 0xceffff00cdffff, 0xd200d100d000cf, 0xd500d4ffff00d3, 0xd7ffff00d6ffff, 0xffffffffffffffff, 0xd9ffffffff00d8, 0xffff00db00daffff, 0xdeffff00dd00dc, 0xdfffffffffffff, 0xffff00e100e0ffff, 0xffffffff00e2ffff, 0xffffffffffffffff, 0xffffffff00e3ffff, 0xe5ffffffff00e4, 0xffffffffffffffff, 0xe900e800e700e6, 0xffffffffffff00ea, 0xffff00ebffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00ecffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xeeffff00edffff, 0xefffffffffffff, 0xf0ffffffffffff, 0xffffffff00f200f1, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf600f500f400f3, 0xf900f800f7ffff, 0xfd00fc00fb00fa, 0x101010000ff00fe, 0x105010401030102, 0x109010801070106, 0x10d010c010b010a, 0x1110110010f010e, 0xffff011401130112, 0xffffffff01160115, 0x11901180117ffff, 0x11bffff011affff, 0x11dffff011cffff, 0x11fffff011effff, 0x121ffff0120ffff, 0x123ffff0122ffff, 0x125ffff0124ffff, 0xffff012801270126, 0xffffffff0129ffff, 0x12bffffffff012a, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x12f012e012d012c, 0x133013201310130, 0x137013601350134, 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, 0x147014601450144, 0x14b014a01490148, 0x14f014e014d014c, 0x153015201510150, 0x157015601550154, 0x15b015a01590158, 0x15dffff015cffff, 0x15fffff015effff, 0x161ffff0160ffff, 0x163ffff0162ffff, 0x165ffff0164ffff, 0x167ffff0166ffff, 0x169ffff0168ffff, 0x16bffff016affff, 0xffffffff016cffff, 0xffffffffffffffff, 0x16dffffffffffff, 0x16fffff016effff, 0x171ffff0170ffff, 0x173ffff0172ffff, 0x175ffff0174ffff, 0x177ffff0176ffff, 0x179ffff0178ffff, 0x17bffff017affff, 0x17dffff017cffff, 0x17fffff017effff, 0x181ffff0180ffff, 0x183ffff0182ffff, 0x185ffff0184ffff, 0x187ffff0186ffff, 0xffff0188ffffffff, 0xffff018affff0189, 0xffff018cffff018b, 0x18f018effff018d, 0x191ffff0190ffff, 0x193ffff0192ffff, 0x195ffff0194ffff, 0x197ffff0196ffff, 0x199ffff0198ffff, 0x19bffff019affff, 0x19dffff019cffff, 0x19fffff019effff, 0x1a1ffff01a0ffff, 0x1a3ffff01a2ffff, 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, 0x1a9ffff01a8ffff, 0x1abffff01aaffff, 0x1adffff01acffff, 0x1afffff01aeffff, 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, 0x1b5ffff01b4ffff, 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, 0x1bbffff01baffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1be01bd01bcffff, 0x1c201c101c001bf, 0x1c601c501c401c3, 0x1ca01c901c801c7, 0x1ce01cd01cc01cb, 0x1d201d101d001cf, 0x1d601d501d401d3, 0x1da01d901d801d7, 0x1de01dd01dc01db, 0xffff01e101e001df, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff01e2ffff, 0xffffffff01e3ffff, 0x1e5ffff01e4ffff, 0x1e7ffff01e6ffff, 0x1e9ffff01e8ffff, 0x1ebffff01eaffff, 0x1edffff01ecffff, 0x1efffff01eeffff, 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, 0x1f5ffff01f4ffff, 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, 0x1fbffff01faffff, 0x1fdffff01fcffff, 0x1ffffff01feffff, 0x201ffff0200ffff, 0x203ffff0202ffff, 0x205ffff0204ffff, 0x207ffff0206ffff, 0x209ffff0208ffff, 0x20bffff020affff, 0x20dffff020cffff, 0x20fffff020effff, 0x211ffff0210ffff, 0x213ffff0212ffff, 0x215ffff0214ffff, 0x217ffff0216ffff, 0x219ffff0218ffff, 0x21bffff021affff, 0x21dffff021cffff, 0x21fffff021effff, 0x221ffff0220ffff, 0x223ffff0222ffff, 0x225ffff0224ffff, 0x227ffff0226ffff, 0x229ffff0228ffff, 0x22bffff022affff, 0x22dffff022cffff, 0xffffffff022effff, 0x22fffffffffffff, 0xffffffffffffffff, 0x231ffff0230ffff, 0x233ffff0232ffff, 0x235ffff0234ffff, 0x237ffff0236ffff, 0x239ffff0238ffff, 0x23bffff023affff, 0x23dffff023cffff, 0x23fffff023effff, 0x241ffff0240ffff, 0x243ffff0242ffff, 0x245ffff0244ffff, 0x247ffff0246ffff, 0x249ffff0248ffff, 0x24bffff024affff, 0x24dffff024cffff, 0x24fffff024effff, 0x251ffff0250ffff, 0x253ffff0252ffff, 0x255ffff0254ffff, 0x257ffff0256ffff, 0x259ffff0258ffff, 0x25bffff025affff, 0x25dffff025cffff, 0x25fffff025effff, 0x263026202610260, 0x267026602650264, 0xffffffffffffffff, 0xffffffffffffffff, 0x26b026a02690268, 0xffffffff026d026c, 0xffffffffffffffff, 0xffffffffffffffff, 0x2710270026f026e, 0x275027402730272, 0xffffffffffffffff, 0xffffffffffffffff, 0x279027802770276, 0x27d027c027b027a, 0xffffffffffffffff, 0xffffffffffffffff, 0x2810280027f027e, 0xffffffff02830282, 0xffffffffffffffff, 0xffffffffffffffff, 0x285ffff0284ffff, 0x287ffff0286ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x28b028a02890288, 0x28f028e028d028c, 0xffffffffffffffff, 0xffffffffffffffff, 0x293029202910290, 0x297029602950294, 0x29b029a02990298, 0xffffffff029d029c, 0x2a102a0029f029e, 0x2a502a402a302a2, 0xffffffffffffffff, 0xffffffffffffffff, 0x2a902a802a702a6, 0x2ad02ac02ab02aa, 0xffffffffffffffff, 0xffffffffffffffff, 0x2b102b002af02ae, 0x2b502b402b302b2, 0xffffffffffffffff, 0xffffffffffffffff, 0x2b8ffff02b702b6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02b9ffffffff, 0x2baffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff02bc02bb, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff02be02bd, 0xffffffff02bfffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c0ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c1ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c502c402c302c2, 0x2c902c802c702c6, 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, 0xffffffffffffffff, 0xffffffffffff02d2, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2d602d502d402d3, 0x2da02d902d802d7, 0x2de02dd02dc02db, 0x2e202e102e002df, 0x2e602e502e402e3, 0x2ea02e902e802e7, 0xffffffff02ec02eb, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2f002ef02ee02ed, 0x2f402f302f202f1, 0x2f802f702f602f5, 0x2fc02fb02fa02f9, 0x30002ff02fe02fd, 0x304030303020301, 0x308030703060305, 0x30c030b030a0309, 0x310030f030e030d, 0x314031303120311, 0x318031703160315, 0xffff031b031a0319, 0xffffffff031cffff, 0xffff031e031dffff, 0xffff0320ffff031f, 0xffffffffffff0321, 0x322ffffffffffff, 0xffff0323ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x325ffff0324ffff, 0x327ffff0326ffff, 0x329ffff0328ffff, 0x32bffff032affff, 0x32dffff032cffff, 0x32fffff032effff, 0x331ffff0330ffff, 0x333ffff0332ffff, 0x335ffff0334ffff, 0x337ffff0336ffff, 0x339ffff0338ffff, 0x33bffff033affff, 0x33dffff033cffff, 0x33fffff033effff, 0x341ffff0340ffff, 0x343ffff0342ffff, 0x345ffff0344ffff, 0x347ffff0346ffff, 0x349ffff0348ffff, 0x34bffff034affff, 0x34dffff034cffff, 0x34fffff034effff, 0x351ffff0350ffff, 0x353ffff0352ffff, 0x355ffff0354ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0357ffff0356, 0x358ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x35c035b035a0359, 0x360035f035e035d, 0x364036303620361, 0x368036703660365, 0x36c036b036a0369, 0x370036f036e036d, 0x374037303720371, 0x378037703760375, 0x37c037b037a0379, 0x37fffff037e037d, 0xffffffffffffffff, 0xffffffff0380ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x382ffff0381ffff, 0x384ffff0383ffff, 0x386ffff0385ffff, 0x388ffff0387ffff, 0x38affff0389ffff, 0x38cffff038bffff, 0x38effff038dffff, 0x390ffff038fffff, 0x392ffff0391ffff, 0x394ffff0393ffff, 0x396ffff0395ffff, 0xffffffff0397ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x399ffff0398ffff, 0x39bffff039affff, 0x39dffff039cffff, 0x39fffff039effff, 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3a4ffffffffffff, 0x3a6ffff03a5ffff, 0x3a8ffff03a7ffff, 0x3aaffff03a9ffff, 0x3abffffffffffff, 0x3adffff03acffff, 0x3afffff03aeffff, 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, 0x3b5ffff03b4ffff, 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, 0x3bbffff03baffff, 0x3bdffff03bcffff, 0x3bfffff03beffff, 0x3c1ffff03c0ffff, 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, 0x3c7ffff03c6ffff, 0x3c9ffff03c8ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03caffffffff, 0x3ccffffffff03cb, 0x3ceffff03cdffff, 0x3d0ffff03cfffff, 0xffffffffffffffff, 0xffffffffffff03d1, 0x3d3ffff03d2ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3d5ffff03d4ffff, 0x3d7ffff03d6ffff, 0xffffffff03d8ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3db03da03d9ffff, 0x3df03de03dd03dc, 0x3e303e203e103e0, 0x3e703e603e503e4, 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, 0xffff03f203f103f0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3f603f503f403f3, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, 0x416041504140413, 0x41a041904180417, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, + 0x100], [0x100, 0x380, 0xbc0], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, + 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, + 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, + 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, + 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, + 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, + 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, + 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, + 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, + 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, + 0x51ffff0050ffff, 0x53ffff0052ffff, 0x55ffff0054ffff, + 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, + 0xffff005cffff005b, 0x5effffffff005d, 0x60ffff005fffff, + 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, + 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, + 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, + 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, + 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, + 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, + 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, + 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, + 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, + 0xffff008f008effff, 0x92ffff00910090, 0xffff0094ffff0093, + 0xffff0096ffff0095, 0xffff0098ffff0097, 0xffff009affff0099, + 0x9dffff009c009b, 0x9fffff009effff, 0xa1ffff00a0ffff, 0xa3ffff00a2ffff, + 0xa5ffff00a4ffff, 0xa700a6ffffffff, 0xffffffff00a8ffff, + 0xaaffff00a9ffff, 0xacffff00abffff, 0xaeffff00adffff, 0xb0ffff00afffff, + 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, + 0xbaffff00b9ffff, 0xbcffff00bbffff, 0xbdffffffffffff, 0xbfffff00beffff, + 0xc1ffff00c0ffff, 0xc3ffff00c2ffff, 0xc5ffff00c4ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xc7ffffffff00c6, + 0xffff00c9ffff00c8, 0xcaffffffffffff, 0xccffff00cbffff, + 0xceffff00cdffff, 0xd200d100d000cf, 0xd500d4ffff00d3, 0xd7ffff00d6ffff, + 0xffffffffffffffff, 0xd9ffffffff00d8, 0xffff00db00daffff, + 0xdeffff00dd00dc, 0xdfffffffffffff, 0xffff00e100e0ffff, + 0xffffffff00e2ffff, 0xffffffffffffffff, 0xffffffff00e3ffff, + 0xe5ffffffff00e4, 0xffffffffffffffff, 0xe900e800e700e6, + 0xffffffffffff00ea, 0xffff00ebffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00ecffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xeeffff00edffff, 0xefffffffffffff, + 0xf0ffffffffffff, 0xffffffff00f200f1, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xf600f500f400f3, 0xf900f800f7ffff, + 0xfd00fc00fb00fa, 0x101010000ff00fe, 0x105010401030102, + 0x109010801070106, 0x10d010c010b010a, 0x1110110010f010e, + 0xffff011401130112, 0xffffffff01160115, 0x11901180117ffff, + 0x11bffff011affff, 0x11dffff011cffff, 0x11fffff011effff, + 0x121ffff0120ffff, 0x123ffff0122ffff, 0x125ffff0124ffff, + 0xffff012801270126, 0xffffffff0129ffff, 0x12bffffffff012a, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x12f012e012d012c, 0x133013201310130, + 0x137013601350134, 0x13b013a01390138, 0x13f013e013d013c, + 0x143014201410140, 0x147014601450144, 0x14b014a01490148, + 0x14f014e014d014c, 0x153015201510150, 0x157015601550154, + 0x15b015a01590158, 0x15dffff015cffff, 0x15fffff015effff, + 0x161ffff0160ffff, 0x163ffff0162ffff, 0x165ffff0164ffff, + 0x167ffff0166ffff, 0x169ffff0168ffff, 0x16bffff016affff, + 0xffffffff016cffff, 0xffffffffffffffff, 0x16dffffffffffff, + 0x16fffff016effff, 0x171ffff0170ffff, 0x173ffff0172ffff, + 0x175ffff0174ffff, 0x177ffff0176ffff, 0x179ffff0178ffff, + 0x17bffff017affff, 0x17dffff017cffff, 0x17fffff017effff, + 0x181ffff0180ffff, 0x183ffff0182ffff, 0x185ffff0184ffff, + 0x187ffff0186ffff, 0xffff0188ffffffff, 0xffff018affff0189, + 0xffff018cffff018b, 0x18f018effff018d, 0x191ffff0190ffff, + 0x193ffff0192ffff, 0x195ffff0194ffff, 0x197ffff0196ffff, + 0x199ffff0198ffff, 0x19bffff019affff, 0x19dffff019cffff, + 0x19fffff019effff, 0x1a1ffff01a0ffff, 0x1a3ffff01a2ffff, + 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, 0x1a9ffff01a8ffff, + 0x1abffff01aaffff, 0x1adffff01acffff, 0x1afffff01aeffff, + 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, 0x1b5ffff01b4ffff, + 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, 0x1bbffff01baffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1be01bd01bcffff, + 0x1c201c101c001bf, 0x1c601c501c401c3, 0x1ca01c901c801c7, + 0x1ce01cd01cc01cb, 0x1d201d101d001cf, 0x1d601d501d401d3, + 0x1da01d901d801d7, 0x1de01dd01dc01db, 0xffff01e101e001df, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff01e2ffff, 0xffffffff01e3ffff, + 0x1e5ffff01e4ffff, 0x1e7ffff01e6ffff, 0x1e9ffff01e8ffff, + 0x1ebffff01eaffff, 0x1edffff01ecffff, 0x1efffff01eeffff, + 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, 0x1f5ffff01f4ffff, + 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, 0x1fbffff01faffff, + 0x1fdffff01fcffff, 0x1ffffff01feffff, 0x201ffff0200ffff, + 0x203ffff0202ffff, 0x205ffff0204ffff, 0x207ffff0206ffff, + 0x209ffff0208ffff, 0x20bffff020affff, 0x20dffff020cffff, + 0x20fffff020effff, 0x211ffff0210ffff, 0x213ffff0212ffff, + 0x215ffff0214ffff, 0x217ffff0216ffff, 0x219ffff0218ffff, + 0x21bffff021affff, 0x21dffff021cffff, 0x21fffff021effff, + 0x221ffff0220ffff, 0x223ffff0222ffff, 0x225ffff0224ffff, + 0x227ffff0226ffff, 0x229ffff0228ffff, 0x22bffff022affff, + 0x22dffff022cffff, 0xffffffff022effff, 0x22fffffffffffff, + 0xffffffffffffffff, 0x231ffff0230ffff, 0x233ffff0232ffff, + 0x235ffff0234ffff, 0x237ffff0236ffff, 0x239ffff0238ffff, + 0x23bffff023affff, 0x23dffff023cffff, 0x23fffff023effff, + 0x241ffff0240ffff, 0x243ffff0242ffff, 0x245ffff0244ffff, + 0x247ffff0246ffff, 0x249ffff0248ffff, 0x24bffff024affff, + 0x24dffff024cffff, 0x24fffff024effff, 0x251ffff0250ffff, + 0x253ffff0252ffff, 0x255ffff0254ffff, 0x257ffff0256ffff, + 0x259ffff0258ffff, 0x25bffff025affff, 0x25dffff025cffff, + 0x25fffff025effff, 0x263026202610260, 0x267026602650264, + 0xffffffffffffffff, 0xffffffffffffffff, 0x26b026a02690268, + 0xffffffff026d026c, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2710270026f026e, 0x275027402730272, 0xffffffffffffffff, + 0xffffffffffffffff, 0x279027802770276, 0x27d027c027b027a, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2810280027f027e, + 0xffffffff02830282, 0xffffffffffffffff, 0xffffffffffffffff, + 0x285ffff0284ffff, 0x287ffff0286ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x28b028a02890288, 0x28f028e028d028c, + 0xffffffffffffffff, 0xffffffffffffffff, 0x293029202910290, + 0x297029602950294, 0x29b029a02990298, 0xffffffff029d029c, + 0x2a102a0029f029e, 0x2a502a402a302a2, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2a902a802a702a6, 0x2ad02ac02ab02aa, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2b102b002af02ae, + 0x2b502b402b302b2, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2b8ffff02b702b6, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff02b9ffffffff, 0x2baffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff02bc02bb, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff02be02bd, 0xffffffff02bfffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2c0ffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c1ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2c502c402c302c2, + 0x2c902c802c702c6, 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, + 0xffffffffffffffff, 0xffffffffffff02d2, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2d602d502d402d3, + 0x2da02d902d802d7, 0x2de02dd02dc02db, 0x2e202e102e002df, + 0x2e602e502e402e3, 0x2ea02e902e802e7, 0xffffffff02ec02eb, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2f002ef02ee02ed, + 0x2f402f302f202f1, 0x2f802f702f602f5, 0x2fc02fb02fa02f9, + 0x30002ff02fe02fd, 0x304030303020301, 0x308030703060305, + 0x30c030b030a0309, 0x310030f030e030d, 0x314031303120311, + 0x318031703160315, 0xffff031b031a0319, 0xffffffff031cffff, + 0xffff031e031dffff, 0xffff0320ffff031f, 0xffffffffffff0321, + 0x322ffffffffffff, 0xffff0323ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x325ffff0324ffff, 0x327ffff0326ffff, + 0x329ffff0328ffff, 0x32bffff032affff, 0x32dffff032cffff, + 0x32fffff032effff, 0x331ffff0330ffff, 0x333ffff0332ffff, + 0x335ffff0334ffff, 0x337ffff0336ffff, 0x339ffff0338ffff, + 0x33bffff033affff, 0x33dffff033cffff, 0x33fffff033effff, + 0x341ffff0340ffff, 0x343ffff0342ffff, 0x345ffff0344ffff, + 0x347ffff0346ffff, 0x349ffff0348ffff, 0x34bffff034affff, + 0x34dffff034cffff, 0x34fffff034effff, 0x351ffff0350ffff, + 0x353ffff0352ffff, 0x355ffff0354ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0357ffff0356, 0x358ffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x35c035b035a0359, 0x360035f035e035d, 0x364036303620361, + 0x368036703660365, 0x36c036b036a0369, 0x370036f036e036d, + 0x374037303720371, 0x378037703760375, 0x37c037b037a0379, + 0x37fffff037e037d, 0xffffffffffffffff, 0xffffffff0380ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x382ffff0381ffff, 0x384ffff0383ffff, + 0x386ffff0385ffff, 0x388ffff0387ffff, 0x38affff0389ffff, + 0x38cffff038bffff, 0x38effff038dffff, 0x390ffff038fffff, + 0x392ffff0391ffff, 0x394ffff0393ffff, 0x396ffff0395ffff, + 0xffffffff0397ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x399ffff0398ffff, + 0x39bffff039affff, 0x39dffff039cffff, 0x39fffff039effff, + 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3a4ffffffffffff, + 0x3a6ffff03a5ffff, 0x3a8ffff03a7ffff, 0x3aaffff03a9ffff, + 0x3abffffffffffff, 0x3adffff03acffff, 0x3afffff03aeffff, + 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, 0x3b5ffff03b4ffff, + 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, 0x3bbffff03baffff, + 0x3bdffff03bcffff, 0x3bfffff03beffff, 0x3c1ffff03c0ffff, + 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, 0x3c7ffff03c6ffff, + 0x3c9ffff03c8ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff03caffffffff, 0x3ccffffffff03cb, 0x3ceffff03cdffff, + 0x3d0ffff03cfffff, 0xffffffffffffffff, 0xffffffffffff03d1, + 0x3d3ffff03d2ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3d5ffff03d4ffff, 0x3d7ffff03d6ffff, + 0xffffffff03d8ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3db03da03d9ffff, 0x3df03de03dd03dc, 0x3e303e203e103e0, + 0x3e703e603e503e4, 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, + 0xffff03f203f103f0, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3f603f503f403f3, + 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, + 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, + 0x41204110410040f, 0x416041504140413, 0x41a041904180417, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); //7808 bytes -enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xb40], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x2000000010000, 0x6000500040003, 0x80007, 0xb000a00090000, 0xf000e000d000c, 0x110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13001200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17001600150014, 0x1b001a00190018, 0x0, 0x1e001d001c, 0x0, 0x0, 0x20001f00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24002300220021, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2600250000, 0x2900280027, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1d001c001b001a, 0x210020001f001e, 0x25002400230022, 0x29002800270026, 0x2d002c002b002a, 0xffff0030002f002e, 0x34003300320031, 0xffff003700360035, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0039ffff0038, 0xffff003bffff003a, 0xffff003dffff003c, 0xffff003fffff003e, 0xffff0041ffff0040, 0xffff0043ffff0042, 0xffff0045ffff0044, 0xffff0047ffff0046, 0xffff0049ffff0048, 0xffff004bffff004a, 0xffff004dffff004c, 0xffff004fffff004e, 0xffff0051ffff0050, 0xffff0053ffff0052, 0x55ffff0054ffff, 0x57ffff0056ffff, 0x59ffff0058ffff, 0x5bffff005affff, 0xffff005cffffffff, 0xffff005effff005d, 0xffff0060ffff005f, 0xffff0062ffff0061, 0xffff0064ffff0063, 0xffff0066ffff0065, 0xffff0068ffff0067, 0xffff006affff0069, 0xffff006cffff006b, 0xffff006effff006d, 0xffff0070ffff006f, 0xffff0072ffff0071, 0x75ffff00740073, 0xffffffff0076ffff, 0xffff00780077ffff, 0x7b007affff0079, 0x7e007d007cffff, 0x80007fffffffff, 0x83ffff00820081, 0x860085ffff0084, 0xffffffffffff0087, 0x8affff00890088, 0xffff008cffff008b, 0x8f008effff008d, 0xffffffff0090ffff, 0x930092ffff0091, 0x9600950094ffff, 0x98ffff0097ffff, 0xffffffffffff0099, 0xffffffffffff009a, 0xffffffffffffffff, 0x9dffff009c009b, 0xa0009fffff009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, 0xa6ffff00a5ffff, 0xa8ffff00a7ffff, 0xffff00a9ffffffff, 0xffff00abffff00aa, 0xffff00adffff00ac, 0xffff00afffff00ae, 0xffff00b1ffff00b0, 0xffff00b300b2ffff, 0xb600b5ffff00b4, 0xffff00b8ffff00b7, 0xffff00baffff00b9, 0xffff00bcffff00bb, 0xffff00beffff00bd, 0xffff00c0ffff00bf, 0xffff00c2ffff00c1, 0xffff00c4ffff00c3, 0xffff00c6ffff00c5, 0xffff00c8ffff00c7, 0xffff00caffff00c9, 0xffff00ccffff00cb, 0xffff00ceffff00cd, 0xffff00d0ffff00cf, 0xffff00d2ffff00d1, 0xffff00d4ffff00d3, 0xffffffffffffffff, 0xd600d5ffffffff, 0xffff00d800d7ffff, 0xdaffff00d9ffff, 0xffff00dd00dc00db, 0xffff00dfffff00de, 0xffff00e1ffff00e0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff00e3ffff00e2, 0xffff00e4ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff00e5ffffffff, 0xffff00e800e700e6, 0xeb00eaffff00e9, 0xee00ed00ecffff, 0xf200f100f000ef, 0xf600f500f400f3, 0xfa00f900f800f7, 0xfdffff00fc00fb, 0x101010000ff00fe, 0x105010401030102, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x106ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0108ffff0107, 0xffff010affff0109, 0xffff010cffff010b, 0xffff010effff010d, 0xffff0110ffff010f, 0xffff0112ffff0111, 0xffffffffffffffff, 0x114ffffffff0113, 0xffff01160115ffff, 0x11901180117ffff, 0x11d011c011b011a, 0x1210120011f011e, 0x125012401230122, 0x129012801270126, 0x12d012c012b012a, 0x1310130012f012e, 0x135013401330132, 0x139013801370136, 0x13d013c013b013a, 0x1410140013f013e, 0x145014401430142, 0x149014801470146, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff014bffff014a, 0xffff014dffff014c, 0xffff014fffff014e, 0xffff0151ffff0150, 0xffff0153ffff0152, 0xffff0155ffff0154, 0xffff0157ffff0156, 0xffff0159ffff0158, 0xffffffffffff015a, 0xffffffffffffffff, 0xffff015bffffffff, 0xffff015dffff015c, 0xffff015fffff015e, 0xffff0161ffff0160, 0xffff0163ffff0162, 0xffff0165ffff0164, 0xffff0167ffff0166, 0xffff0169ffff0168, 0xffff016bffff016a, 0xffff016dffff016c, 0xffff016fffff016e, 0xffff0171ffff0170, 0xffff0173ffff0172, 0xffff0175ffff0174, 0x178ffff01770176, 0x17affff0179ffff, 0x17cffff017bffff, 0xffffffff017dffff, 0xffff017fffff017e, 0xffff0181ffff0180, 0xffff0183ffff0182, 0xffff0185ffff0184, 0xffff0187ffff0186, 0xffff0189ffff0188, 0xffff018bffff018a, 0xffff018dffff018c, 0xffff018fffff018e, 0xffff0191ffff0190, 0xffff0193ffff0192, 0xffff0195ffff0194, 0xffff0197ffff0196, 0xffff0199ffff0198, 0xffff019bffff019a, 0xffff019dffff019c, 0xffff019fffff019e, 0xffff01a1ffff01a0, 0xffff01a3ffff01a2, 0xffff01a5ffff01a4, 0xffff01a7ffff01a6, 0xffff01a9ffff01a8, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ac01ab01aaffff, 0x1b001af01ae01ad, 0x1b401b301b201b1, 0x1b801b701b601b5, 0x1bc01bb01ba01b9, 0x1c001bf01be01bd, 0x1c401c301c201c1, 0x1c801c701c601c5, 0x1cc01cb01ca01c9, 0xffff01cf01ce01cd, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1d301d201d101d0, 0x1d701d601d501d4, 0x1db01da01d901d8, 0x1df01de01dd01dc, 0x1e301e201e101e0, 0x1e701e601e501e4, 0x1eb01ea01e901e8, 0x1ef01ee01ed01ec, 0x1f301f201f101f0, 0x1f6ffff01f501f4, 0xffffffffffffffff, 0xffffffff01f7ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff01f9ffff01f8, 0xffff01fbffff01fa, 0xffff01fdffff01fc, 0xffff01ffffff01fe, 0xffff0201ffff0200, 0xffff0203ffff0202, 0xffff0205ffff0204, 0xffff0207ffff0206, 0xffff0209ffff0208, 0xffff020bffff020a, 0xffff020dffff020c, 0xffff020fffff020e, 0xffff0211ffff0210, 0xffff0213ffff0212, 0xffff0215ffff0214, 0xffff0217ffff0216, 0xffff0219ffff0218, 0xffff021bffff021a, 0xffff021dffff021c, 0xffff021fffff021e, 0xffff0221ffff0220, 0xffff0223ffff0222, 0xffff0225ffff0224, 0xffff0227ffff0226, 0xffff0229ffff0228, 0xffff022bffff022a, 0xffff022dffff022c, 0xffff022fffff022e, 0xffff0231ffff0230, 0xffff0233ffff0232, 0xffff0235ffff0234, 0xffff0237ffff0236, 0xffff0239ffff0238, 0xffff023bffff023a, 0xffff023dffff023c, 0xffff023fffff023e, 0xffff0241ffff0240, 0xffffffffffff0242, 0xffffffffffffffff, 0xffff0243ffffffff, 0xffff0245ffff0244, 0xffff0247ffff0246, 0xffff0249ffff0248, 0xffff024bffff024a, 0xffff024dffff024c, 0xffff024fffff024e, 0xffff0251ffff0250, 0xffff0253ffff0252, 0xffff0255ffff0254, 0xffff0257ffff0256, 0xffff0259ffff0258, 0xffff025bffff025a, 0xffff025dffff025c, 0xffff025fffff025e, 0xffff0261ffff0260, 0xffff0263ffff0262, 0xffff0265ffff0264, 0xffff0267ffff0266, 0xffff0269ffff0268, 0xffff026bffff026a, 0xffff026dffff026c, 0xffff026fffff026e, 0xffff0271ffff0270, 0xffff0273ffff0272, 0xffffffffffffffff, 0xffffffffffffffff, 0x277027602750274, 0x27b027a02790278, 0xffffffffffffffff, 0xffffffffffffffff, 0x27f027e027d027c, 0xffffffff02810280, 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, 0x289028802870286, 0xffffffffffffffff, 0xffffffffffffffff, 0x28d028c028b028a, 0x2910290028f028e, 0xffffffffffffffff, 0xffffffffffffffff, 0x295029402930292, 0xffffffff02970296, 0xffffffffffffffff, 0xffffffffffffffff, 0x299ffff0298ffff, 0x29bffff029affff, 0xffffffffffffffff, 0xffffffffffffffff, 0x29f029e029d029c, 0x2a302a202a102a0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2a702a602a502a4, 0x2ab02aa02a902a8, 0xffffffffffffffff, 0xffffffffffffffff, 0x2af02ae02ad02ac, 0x2b302b202b102b0, 0xffffffffffffffff, 0xffffffffffffffff, 0x2b702b602b502b4, 0x2bb02ba02b902b8, 0xffffffffffffffff, 0xffffffffffffffff, 0x2bf02be02bd02bc, 0xffffffffffff02c0, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c402c302c202c1, 0xffffffffffff02c5, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c902c802c702c6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2cd02cc02cb02ca, 0xffffffffffff02ce, 0xffffffffffffffff, 0xffffffffffffffff, 0x2d202d102d002cf, 0xffffffffffff02d3, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02d4ffffffff, 0x2d602d5ffffffff, 0xffffffffffffffff, 0xffff02d7ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2db02da02d902d8, 0x2df02de02dd02dc, 0x2e302e202e102e0, 0x2e702e602e502e4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2e8ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2ea02e9ffffffff, 0x2ee02ed02ec02eb, 0x2f202f102f002ef, 0x2f602f502f402f3, 0x2fa02f902f802f7, 0x2fe02fd02fc02fb, 0x3020301030002ff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x306030503040303, 0x30a030903080307, 0x30e030d030c030b, 0x31203110310030f, 0x316031503140313, 0x31a031903180317, 0x31e031d031c031b, 0x32203210320031f, 0x326032503240323, 0x32a032903280327, 0x32e032d032c032b, 0xffff03310330032f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3340333ffff0332, 0x336ffffffff0335, 0x338ffff0337ffff, 0x33b033a0339ffff, 0xffff033dffff033c, 0xffffffff033effff, 0xffffffffffffffff, 0x340033fffffffff, 0xffff0342ffff0341, 0xffff0344ffff0343, 0xffff0346ffff0345, 0xffff0348ffff0347, 0xffff034affff0349, 0xffff034cffff034b, 0xffff034effff034d, 0xffff0350ffff034f, 0xffff0352ffff0351, 0xffff0354ffff0353, 0xffff0356ffff0355, 0xffff0358ffff0357, 0xffff035affff0359, 0xffff035cffff035b, 0xffff035effff035d, 0xffff0360ffff035f, 0xffff0362ffff0361, 0xffff0364ffff0363, 0xffff0366ffff0365, 0xffff0368ffff0367, 0xffff036affff0369, 0xffff036cffff036b, 0xffff036effff036d, 0xffff0370ffff036f, 0xffff0372ffff0371, 0xffffffffffffffff, 0x373ffffffffffff, 0xffffffff0374ffff, 0xffff0375ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0377ffff0376, 0xffff0379ffff0378, 0xffff037bffff037a, 0xffff037dffff037c, 0xffff037fffff037e, 0xffff0381ffff0380, 0xffff0383ffff0382, 0xffff0385ffff0384, 0xffff0387ffff0386, 0xffff0389ffff0388, 0xffff038bffff038a, 0xffffffffffff038c, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff038effff038d, 0xffff0390ffff038f, 0xffff0392ffff0391, 0xffff0394ffff0393, 0xffff0396ffff0395, 0xffff0398ffff0397, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0399ffffffff, 0xffff039bffff039a, 0xffff039dffff039c, 0xffff039fffff039e, 0xffff03a0ffffffff, 0xffff03a2ffff03a1, 0xffff03a4ffff03a3, 0xffff03a6ffff03a5, 0xffff03a8ffff03a7, 0xffff03aaffff03a9, 0xffff03acffff03ab, 0xffff03aeffff03ad, 0xffff03b0ffff03af, 0xffff03b2ffff03b1, 0xffff03b4ffff03b3, 0xffff03b6ffff03b5, 0xffff03b8ffff03b7, 0xffff03baffff03b9, 0xffff03bcffff03bb, 0xffff03beffff03bd, 0xffffffffffffffff, 0xffffffffffffffff, 0x3c0ffff03bfffff, 0xffff03c203c1ffff, 0xffff03c4ffff03c3, 0xffff03c6ffff03c5, 0x3c7ffffffffffff, 0xffffffff03c8ffff, 0xffff03caffff03c9, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03ccffff03cb, 0xffff03ceffff03cd, 0xffff03d0ffff03cf, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3d303d203d1ffff, 0x3d703d603d503d4, 0x3db03da03d903d8, 0x3df03de03dd03dc, 0x3e303e203e103e0, 0x3e703e603e503e4, 0xffff03ea03e903e8, 0xffffffffffffffff, 0x3ee03ed03ec03eb, 0x3f203f103f003ef, 0x3f603f503f403f3, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, + 0x100], [0x100, 0x380, 0xb40], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x2000000010000, 0x6000500040003, 0x80007, 0xb000a00090000, + 0xf000e000d000c, 0x110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x13001200000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x17001600150014, 0x1b001a00190018, 0x0, + 0x1e001d001c, 0x0, 0x0, 0x20001f00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x24002300220021, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2600250000, 0x2900280027, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, + 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, + 0x1200110010000f, 0x16001500140013, 0xffff001900180017, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1d001c001b001a, 0x210020001f001e, 0x25002400230022, 0x29002800270026, + 0x2d002c002b002a, 0xffff0030002f002e, 0x34003300320031, + 0xffff003700360035, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff0039ffff0038, 0xffff003bffff003a, 0xffff003dffff003c, + 0xffff003fffff003e, 0xffff0041ffff0040, 0xffff0043ffff0042, + 0xffff0045ffff0044, 0xffff0047ffff0046, 0xffff0049ffff0048, + 0xffff004bffff004a, 0xffff004dffff004c, 0xffff004fffff004e, + 0xffff0051ffff0050, 0xffff0053ffff0052, 0x55ffff0054ffff, + 0x57ffff0056ffff, 0x59ffff0058ffff, 0x5bffff005affff, + 0xffff005cffffffff, 0xffff005effff005d, 0xffff0060ffff005f, + 0xffff0062ffff0061, 0xffff0064ffff0063, 0xffff0066ffff0065, + 0xffff0068ffff0067, 0xffff006affff0069, 0xffff006cffff006b, + 0xffff006effff006d, 0xffff0070ffff006f, 0xffff0072ffff0071, + 0x75ffff00740073, 0xffffffff0076ffff, 0xffff00780077ffff, + 0x7b007affff0079, 0x7e007d007cffff, 0x80007fffffffff, 0x83ffff00820081, + 0x860085ffff0084, 0xffffffffffff0087, 0x8affff00890088, + 0xffff008cffff008b, 0x8f008effff008d, 0xffffffff0090ffff, + 0x930092ffff0091, 0x9600950094ffff, 0x98ffff0097ffff, + 0xffffffffffff0099, 0xffffffffffff009a, 0xffffffffffffffff, + 0x9dffff009c009b, 0xa0009fffff009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, + 0xa6ffff00a5ffff, 0xa8ffff00a7ffff, 0xffff00a9ffffffff, + 0xffff00abffff00aa, 0xffff00adffff00ac, 0xffff00afffff00ae, + 0xffff00b1ffff00b0, 0xffff00b300b2ffff, 0xb600b5ffff00b4, + 0xffff00b8ffff00b7, 0xffff00baffff00b9, 0xffff00bcffff00bb, + 0xffff00beffff00bd, 0xffff00c0ffff00bf, 0xffff00c2ffff00c1, + 0xffff00c4ffff00c3, 0xffff00c6ffff00c5, 0xffff00c8ffff00c7, + 0xffff00caffff00c9, 0xffff00ccffff00cb, 0xffff00ceffff00cd, + 0xffff00d0ffff00cf, 0xffff00d2ffff00d1, 0xffff00d4ffff00d3, + 0xffffffffffffffff, 0xd600d5ffffffff, 0xffff00d800d7ffff, + 0xdaffff00d9ffff, 0xffff00dd00dc00db, 0xffff00dfffff00de, + 0xffff00e1ffff00e0, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff00e3ffff00e2, 0xffff00e4ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff00e5ffffffff, 0xffff00e800e700e6, 0xeb00eaffff00e9, + 0xee00ed00ecffff, 0xf200f100f000ef, 0xf600f500f400f3, 0xfa00f900f800f7, + 0xfdffff00fc00fb, 0x101010000ff00fe, 0x105010401030102, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x106ffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0108ffff0107, + 0xffff010affff0109, 0xffff010cffff010b, 0xffff010effff010d, + 0xffff0110ffff010f, 0xffff0112ffff0111, 0xffffffffffffffff, + 0x114ffffffff0113, 0xffff01160115ffff, 0x11901180117ffff, + 0x11d011c011b011a, 0x1210120011f011e, 0x125012401230122, + 0x129012801270126, 0x12d012c012b012a, 0x1310130012f012e, + 0x135013401330132, 0x139013801370136, 0x13d013c013b013a, + 0x1410140013f013e, 0x145014401430142, 0x149014801470146, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff014bffff014a, 0xffff014dffff014c, 0xffff014fffff014e, + 0xffff0151ffff0150, 0xffff0153ffff0152, 0xffff0155ffff0154, + 0xffff0157ffff0156, 0xffff0159ffff0158, 0xffffffffffff015a, + 0xffffffffffffffff, 0xffff015bffffffff, 0xffff015dffff015c, + 0xffff015fffff015e, 0xffff0161ffff0160, 0xffff0163ffff0162, + 0xffff0165ffff0164, 0xffff0167ffff0166, 0xffff0169ffff0168, + 0xffff016bffff016a, 0xffff016dffff016c, 0xffff016fffff016e, + 0xffff0171ffff0170, 0xffff0173ffff0172, 0xffff0175ffff0174, + 0x178ffff01770176, 0x17affff0179ffff, 0x17cffff017bffff, + 0xffffffff017dffff, 0xffff017fffff017e, 0xffff0181ffff0180, + 0xffff0183ffff0182, 0xffff0185ffff0184, 0xffff0187ffff0186, + 0xffff0189ffff0188, 0xffff018bffff018a, 0xffff018dffff018c, + 0xffff018fffff018e, 0xffff0191ffff0190, 0xffff0193ffff0192, + 0xffff0195ffff0194, 0xffff0197ffff0196, 0xffff0199ffff0198, + 0xffff019bffff019a, 0xffff019dffff019c, 0xffff019fffff019e, + 0xffff01a1ffff01a0, 0xffff01a3ffff01a2, 0xffff01a5ffff01a4, + 0xffff01a7ffff01a6, 0xffff01a9ffff01a8, 0xffffffffffffffff, + 0xffffffffffffffff, 0x1ac01ab01aaffff, 0x1b001af01ae01ad, + 0x1b401b301b201b1, 0x1b801b701b601b5, 0x1bc01bb01ba01b9, + 0x1c001bf01be01bd, 0x1c401c301c201c1, 0x1c801c701c601c5, + 0x1cc01cb01ca01c9, 0xffff01cf01ce01cd, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1d301d201d101d0, + 0x1d701d601d501d4, 0x1db01da01d901d8, 0x1df01de01dd01dc, + 0x1e301e201e101e0, 0x1e701e601e501e4, 0x1eb01ea01e901e8, + 0x1ef01ee01ed01ec, 0x1f301f201f101f0, 0x1f6ffff01f501f4, + 0xffffffffffffffff, 0xffffffff01f7ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff01f9ffff01f8, + 0xffff01fbffff01fa, 0xffff01fdffff01fc, 0xffff01ffffff01fe, + 0xffff0201ffff0200, 0xffff0203ffff0202, 0xffff0205ffff0204, + 0xffff0207ffff0206, 0xffff0209ffff0208, 0xffff020bffff020a, + 0xffff020dffff020c, 0xffff020fffff020e, 0xffff0211ffff0210, + 0xffff0213ffff0212, 0xffff0215ffff0214, 0xffff0217ffff0216, + 0xffff0219ffff0218, 0xffff021bffff021a, 0xffff021dffff021c, + 0xffff021fffff021e, 0xffff0221ffff0220, 0xffff0223ffff0222, + 0xffff0225ffff0224, 0xffff0227ffff0226, 0xffff0229ffff0228, + 0xffff022bffff022a, 0xffff022dffff022c, 0xffff022fffff022e, + 0xffff0231ffff0230, 0xffff0233ffff0232, 0xffff0235ffff0234, + 0xffff0237ffff0236, 0xffff0239ffff0238, 0xffff023bffff023a, + 0xffff023dffff023c, 0xffff023fffff023e, 0xffff0241ffff0240, + 0xffffffffffff0242, 0xffffffffffffffff, 0xffff0243ffffffff, + 0xffff0245ffff0244, 0xffff0247ffff0246, 0xffff0249ffff0248, + 0xffff024bffff024a, 0xffff024dffff024c, 0xffff024fffff024e, + 0xffff0251ffff0250, 0xffff0253ffff0252, 0xffff0255ffff0254, + 0xffff0257ffff0256, 0xffff0259ffff0258, 0xffff025bffff025a, + 0xffff025dffff025c, 0xffff025fffff025e, 0xffff0261ffff0260, + 0xffff0263ffff0262, 0xffff0265ffff0264, 0xffff0267ffff0266, + 0xffff0269ffff0268, 0xffff026bffff026a, 0xffff026dffff026c, + 0xffff026fffff026e, 0xffff0271ffff0270, 0xffff0273ffff0272, + 0xffffffffffffffff, 0xffffffffffffffff, 0x277027602750274, + 0x27b027a02790278, 0xffffffffffffffff, 0xffffffffffffffff, + 0x27f027e027d027c, 0xffffffff02810280, 0xffffffffffffffff, + 0xffffffffffffffff, 0x285028402830282, 0x289028802870286, + 0xffffffffffffffff, 0xffffffffffffffff, 0x28d028c028b028a, + 0x2910290028f028e, 0xffffffffffffffff, 0xffffffffffffffff, + 0x295029402930292, 0xffffffff02970296, 0xffffffffffffffff, + 0xffffffffffffffff, 0x299ffff0298ffff, 0x29bffff029affff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x29f029e029d029c, + 0x2a302a202a102a0, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2a702a602a502a4, 0x2ab02aa02a902a8, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2af02ae02ad02ac, + 0x2b302b202b102b0, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2b702b602b502b4, 0x2bb02ba02b902b8, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2bf02be02bd02bc, 0xffffffffffff02c0, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2c402c302c202c1, + 0xffffffffffff02c5, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2c902c802c702c6, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2cd02cc02cb02ca, 0xffffffffffff02ce, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2d202d102d002cf, + 0xffffffffffff02d3, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff02d4ffffffff, 0x2d602d5ffffffff, + 0xffffffffffffffff, 0xffff02d7ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2db02da02d902d8, 0x2df02de02dd02dc, + 0x2e302e202e102e0, 0x2e702e602e502e4, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2e8ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2ea02e9ffffffff, + 0x2ee02ed02ec02eb, 0x2f202f102f002ef, 0x2f602f502f402f3, + 0x2fa02f902f802f7, 0x2fe02fd02fc02fb, 0x3020301030002ff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x306030503040303, 0x30a030903080307, 0x30e030d030c030b, + 0x31203110310030f, 0x316031503140313, 0x31a031903180317, + 0x31e031d031c031b, 0x32203210320031f, 0x326032503240323, + 0x32a032903280327, 0x32e032d032c032b, 0xffff03310330032f, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3340333ffff0332, 0x336ffffffff0335, 0x338ffff0337ffff, + 0x33b033a0339ffff, 0xffff033dffff033c, 0xffffffff033effff, + 0xffffffffffffffff, 0x340033fffffffff, 0xffff0342ffff0341, + 0xffff0344ffff0343, 0xffff0346ffff0345, 0xffff0348ffff0347, + 0xffff034affff0349, 0xffff034cffff034b, 0xffff034effff034d, + 0xffff0350ffff034f, 0xffff0352ffff0351, 0xffff0354ffff0353, + 0xffff0356ffff0355, 0xffff0358ffff0357, 0xffff035affff0359, + 0xffff035cffff035b, 0xffff035effff035d, 0xffff0360ffff035f, + 0xffff0362ffff0361, 0xffff0364ffff0363, 0xffff0366ffff0365, + 0xffff0368ffff0367, 0xffff036affff0369, 0xffff036cffff036b, + 0xffff036effff036d, 0xffff0370ffff036f, 0xffff0372ffff0371, + 0xffffffffffffffff, 0x373ffffffffffff, 0xffffffff0374ffff, + 0xffff0375ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff0377ffff0376, 0xffff0379ffff0378, + 0xffff037bffff037a, 0xffff037dffff037c, 0xffff037fffff037e, + 0xffff0381ffff0380, 0xffff0383ffff0382, 0xffff0385ffff0384, + 0xffff0387ffff0386, 0xffff0389ffff0388, 0xffff038bffff038a, + 0xffffffffffff038c, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff038effff038d, + 0xffff0390ffff038f, 0xffff0392ffff0391, 0xffff0394ffff0393, + 0xffff0396ffff0395, 0xffff0398ffff0397, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff0399ffffffff, + 0xffff039bffff039a, 0xffff039dffff039c, 0xffff039fffff039e, + 0xffff03a0ffffffff, 0xffff03a2ffff03a1, 0xffff03a4ffff03a3, + 0xffff03a6ffff03a5, 0xffff03a8ffff03a7, 0xffff03aaffff03a9, + 0xffff03acffff03ab, 0xffff03aeffff03ad, 0xffff03b0ffff03af, + 0xffff03b2ffff03b1, 0xffff03b4ffff03b3, 0xffff03b6ffff03b5, + 0xffff03b8ffff03b7, 0xffff03baffff03b9, 0xffff03bcffff03bb, + 0xffff03beffff03bd, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3c0ffff03bfffff, 0xffff03c203c1ffff, 0xffff03c4ffff03c3, + 0xffff03c6ffff03c5, 0x3c7ffffffffffff, 0xffffffff03c8ffff, + 0xffff03caffff03c9, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff03ccffff03cb, 0xffff03ceffff03cd, + 0xffff03d0ffff03cf, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3d303d203d1ffff, + 0x3d703d603d503d4, 0x3db03da03d903d8, 0x3df03de03dd03dc, + 0x3e303e203e103e0, 0x3e703e603e503e4, 0xffff03ea03e903e8, + 0xffffffffffffffff, 0x3ee03ed03ec03eb, 0x3f203f103f003ef, + 0x3f603f503f403f3, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, + 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, + 0x40e040d040c040b, 0x41204110410040f, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); //8064 bytes -enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x20, 0x100], [ 0x100, 0x380, 0xbc0], [ 0x402030202020100, 0x202020202020205, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x202020202020202, 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, 0x51ffff0050ffff, 0x53ffff0052ffff, 0x55ffff0054ffff, 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, 0xffff005cffff005b, 0x5effffffff005d, 0x60ffff005fffff, 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, 0x910090008f008e, 0x95009400930092, 0xffff0097ffff0096, 0xffff0099ffff0098, 0xffff009bffff009a, 0xffff009dffff009c, 0xa0ffff009f009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, 0xa6ffff00a5ffff, 0xa8ffff00a7ffff, 0xab00aa00a9ffff, 0xffffffff00acffff, 0xaeffff00adffff, 0xb0ffff00afffff, 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, 0xbaffff00b9ffff, 0xbcffff00bbffff, 0xbeffff00bdffff, 0xc0ffff00bfffff, 0xc1ffffffffffff, 0xc3ffff00c2ffff, 0xc5ffff00c4ffff, 0xc7ffff00c6ffff, 0xc9ffff00c8ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xcbffffffff00ca, 0xffff00cdffff00cc, 0xceffffffffffff, 0xd0ffff00cfffff, 0xd2ffff00d1ffff, 0xd600d500d400d3, 0xd900d8ffff00d7, 0xdbffff00daffff, 0xffffffffffffffff, 0xddffffffff00dc, 0xffff00df00deffff, 0xe2ffff00e100e0, 0xe3ffffffffffff, 0xffff00e500e4ffff, 0xffffffff00e6ffff, 0xffffffffffffffff, 0xffffffff00e7ffff, 0xe9ffffffff00e8, 0xffffffffffffffff, 0xed00ec00eb00ea, 0xffffffffffff00ee, 0xffff00efffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00f0ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf2ffff00f1ffff, 0xf3ffffffffffff, 0xf4ffffffffffff, 0xffffffff00f600f5, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfa00f900f800f7, 0xfd00fc00fbffff, 0x101010000ff00fe, 0x105010401030102, 0x109010801070106, 0x10d010c010b010a, 0x1110110010f010e, 0x115011401130112, 0xffff011801170116, 0xffffffff011a0119, 0x11d011c011bffff, 0x11fffff011effff, 0x121ffff0120ffff, 0x123ffff0122ffff, 0x125ffff0124ffff, 0x127ffff0126ffff, 0x129ffff0128ffff, 0xffff012c012b012a, 0xffffffff012dffff, 0x12fffffffff012e, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x133013201310130, 0x137013601350134, 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, 0x147014601450144, 0x14b014a01490148, 0x14f014e014d014c, 0x153015201510150, 0x157015601550154, 0x15b015a01590158, 0x15f015e015d015c, 0x161ffff0160ffff, 0x163ffff0162ffff, 0x165ffff0164ffff, 0x167ffff0166ffff, 0x169ffff0168ffff, 0x16bffff016affff, 0x16dffff016cffff, 0x16fffff016effff, 0xffffffff0170ffff, 0xffffffffffffffff, 0x171ffffffffffff, 0x173ffff0172ffff, 0x175ffff0174ffff, 0x177ffff0176ffff, 0x179ffff0178ffff, 0x17bffff017affff, 0x17dffff017cffff, 0x17fffff017effff, 0x181ffff0180ffff, 0x183ffff0182ffff, 0x185ffff0184ffff, 0x187ffff0186ffff, 0x189ffff0188ffff, 0x18bffff018affff, 0xffff018cffffffff, 0xffff018effff018d, 0xffff0190ffff018f, 0x1930192ffff0191, 0x195ffff0194ffff, 0x197ffff0196ffff, 0x199ffff0198ffff, 0x19bffff019affff, 0x19dffff019cffff, 0x19fffff019effff, 0x1a1ffff01a0ffff, 0x1a3ffff01a2ffff, 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, 0x1a9ffff01a8ffff, 0x1abffff01aaffff, 0x1adffff01acffff, 0x1afffff01aeffff, 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, 0x1b5ffff01b4ffff, 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, 0x1bbffff01baffff, 0x1bdffff01bcffff, 0x1bfffff01beffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1c201c101c0ffff, 0x1c601c501c401c3, 0x1ca01c901c801c7, 0x1ce01cd01cc01cb, 0x1d201d101d001cf, 0x1d601d501d401d3, 0x1da01d901d801d7, 0x1de01dd01dc01db, 0x1e201e101e001df, 0xffff01e501e401e3, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff01e6ffff, 0xffffffff01e7ffff, 0x1e9ffff01e8ffff, 0x1ebffff01eaffff, 0x1edffff01ecffff, 0x1efffff01eeffff, 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, 0x1f5ffff01f4ffff, 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, 0x1fbffff01faffff, 0x1fdffff01fcffff, 0x1ffffff01feffff, 0x201ffff0200ffff, 0x203ffff0202ffff, 0x205ffff0204ffff, 0x207ffff0206ffff, 0x209ffff0208ffff, 0x20bffff020affff, 0x20dffff020cffff, 0x20fffff020effff, 0x211ffff0210ffff, 0x213ffff0212ffff, 0x215ffff0214ffff, 0x217ffff0216ffff, 0x219ffff0218ffff, 0x21bffff021affff, 0x21dffff021cffff, 0x21fffff021effff, 0x221ffff0220ffff, 0x223ffff0222ffff, 0x225ffff0224ffff, 0x227ffff0226ffff, 0x229ffff0228ffff, 0x22bffff022affff, 0x22dffff022cffff, 0x22fffff022effff, 0x231ffff0230ffff, 0xffffffff0232ffff, 0x233ffffffffffff, 0xffffffffffffffff, 0x235ffff0234ffff, 0x237ffff0236ffff, 0x239ffff0238ffff, 0x23bffff023affff, 0x23dffff023cffff, 0x23fffff023effff, 0x241ffff0240ffff, 0x243ffff0242ffff, 0x245ffff0244ffff, 0x247ffff0246ffff, 0x249ffff0248ffff, 0x24bffff024affff, 0x24dffff024cffff, 0x24fffff024effff, 0x251ffff0250ffff, 0x253ffff0252ffff, 0x255ffff0254ffff, 0x257ffff0256ffff, 0x259ffff0258ffff, 0x25bffff025affff, 0x25dffff025cffff, 0x25fffff025effff, 0x261ffff0260ffff, 0x263ffff0262ffff, 0x267026602650264, 0x26b026a02690268, 0xffffffffffffffff, 0xffffffffffffffff, 0x26f026e026d026c, 0xffffffff02710270, 0xffffffffffffffff, 0xffffffffffffffff, 0x275027402730272, 0x279027802770276, 0xffffffffffffffff, 0xffffffffffffffff, 0x27d027c027b027a, 0x2810280027f027e, 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, 0xffffffff02870286, 0xffffffffffffffff, 0xffffffffffffffff, 0x289ffff0288ffff, 0x28bffff028affff, 0xffffffffffffffff, 0xffffffffffffffff, 0x28f028e028d028c, 0x293029202910290, 0xffffffffffffffff, 0xffffffffffffffff, 0x297029602950294, 0x29b029a02990298, 0x29f029e029d029c, 0xffffffff02a102a0, 0x2a502a402a302a2, 0x2a902a802a702a6, 0xffffffffffffffff, 0xffffffffffffffff, 0x2ad02ac02ab02aa, 0x2b102b002af02ae, 0xffffffffffffffff, 0xffffffffffffffff, 0x2b502b402b302b2, 0x2b902b802b702b6, 0xffffffffffffffff, 0xffffffffffffffff, 0x2bcffff02bb02ba, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02bdffffffff, 0x2beffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff02c002bf, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff02c202c1, 0xffffffff02c3ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c4ffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c5ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2c902c802c702c6, 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, 0x2d502d402d302d2, 0xffffffffffffffff, 0xffffffffffff02d6, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2da02d902d802d7, 0x2de02dd02dc02db, 0x2e202e102e002df, 0x2e602e502e402e3, 0x2ea02e902e802e7, 0x2ee02ed02ec02eb, 0xffffffff02f002ef, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x2f402f302f202f1, 0x2f802f702f602f5, 0x2fc02fb02fa02f9, 0x30002ff02fe02fd, 0x304030303020301, 0x308030703060305, 0x30c030b030a0309, 0x310030f030e030d, 0x314031303120311, 0x318031703160315, 0x31c031b031a0319, 0xffff031f031e031d, 0xffffffff0320ffff, 0xffff03220321ffff, 0xffff0324ffff0323, 0xffffffffffff0325, 0x326ffffffffffff, 0xffff0327ffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x329ffff0328ffff, 0x32bffff032affff, 0x32dffff032cffff, 0x32fffff032effff, 0x331ffff0330ffff, 0x333ffff0332ffff, 0x335ffff0334ffff, 0x337ffff0336ffff, 0x339ffff0338ffff, 0x33bffff033affff, 0x33dffff033cffff, 0x33fffff033effff, 0x341ffff0340ffff, 0x343ffff0342ffff, 0x345ffff0344ffff, 0x347ffff0346ffff, 0x349ffff0348ffff, 0x34bffff034affff, 0x34dffff034cffff, 0x34fffff034effff, 0x351ffff0350ffff, 0x353ffff0352ffff, 0x355ffff0354ffff, 0x357ffff0356ffff, 0x359ffff0358ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff035bffff035a, 0x35cffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x360035f035e035d, 0x364036303620361, 0x368036703660365, 0x36c036b036a0369, 0x370036f036e036d, 0x374037303720371, 0x378037703760375, 0x37c037b037a0379, 0x380037f037e037d, 0x383ffff03820381, 0xffffffffffffffff, 0xffffffff0384ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x386ffff0385ffff, 0x388ffff0387ffff, 0x38affff0389ffff, 0x38cffff038bffff, 0x38effff038dffff, 0x390ffff038fffff, 0x392ffff0391ffff, 0x394ffff0393ffff, 0x396ffff0395ffff, 0x398ffff0397ffff, 0x39affff0399ffff, 0xffffffff039bffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x39dffff039cffff, 0x39fffff039effff, 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, 0x3a5ffff03a4ffff, 0x3a7ffff03a6ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3a8ffffffffffff, 0x3aaffff03a9ffff, 0x3acffff03abffff, 0x3aeffff03adffff, 0x3afffffffffffff, 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, 0x3b5ffff03b4ffff, 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, 0x3bbffff03baffff, 0x3bdffff03bcffff, 0x3bfffff03beffff, 0x3c1ffff03c0ffff, 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, 0x3c7ffff03c6ffff, 0x3c9ffff03c8ffff, 0x3cbffff03caffff, 0x3cdffff03ccffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff03ceffffffff, 0x3d0ffffffff03cf, 0x3d2ffff03d1ffff, 0x3d4ffff03d3ffff, 0xffffffffffffffff, 0xffffffffffff03d5, 0x3d7ffff03d6ffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3d9ffff03d8ffff, 0x3dbffff03daffff, 0xffffffff03dcffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3df03de03ddffff, 0x3e303e203e103e0, 0x3e703e603e503e4, 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, 0x3f303f203f103f0, 0xffff03f603f503f4, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fa03f903f803f7, 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, 0x416041504140413, 0x41a041904180417, 0x41e041d041c041b, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); +enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x20, + 0x100], [0x100, 0x380, 0xbc0], [0x402030202020100, 0x202020202020205, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x202020202020202, 0x202020202020202, 0x202020202020202, + 0x3000200010000, 0x7000600050004, 0xa00090008, 0xd000c000b0000, + 0x110010000f000e, 0x1400130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x150000, 0x19001800170016, 0x1d001c001b001a, 0x0, 0x1f001e0000, + 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x24002300220021, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2700260000, 0x2a00290028, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, + 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x200010000ffff, + 0x6000500040003, 0xa000900080007, 0xe000d000c000b, 0x1200110010000f, + 0x16001500140013, 0xffff001900180017, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff001affff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x1e001d001c001b, 0x2200210020001f, 0x26002500240023, 0x2a002900280027, + 0x2e002d002c002b, 0xffff00310030002f, 0x35003400330032, + 0x39003800370036, 0x3bffff003affff, 0x3dffff003cffff, 0x3fffff003effff, + 0x41ffff0040ffff, 0x43ffff0042ffff, 0x45ffff0044ffff, 0x47ffff0046ffff, + 0x49ffff0048ffff, 0x4bffff004affff, 0x4dffff004cffff, 0x4fffff004effff, + 0x51ffff0050ffff, 0x53ffff0052ffff, 0x55ffff0054ffff, + 0xffff0056ffffffff, 0xffff0058ffff0057, 0xffff005affff0059, + 0xffff005cffff005b, 0x5effffffff005d, 0x60ffff005fffff, + 0x62ffff0061ffff, 0x64ffff0063ffff, 0x66ffff0065ffff, 0x68ffff0067ffff, + 0x6affff0069ffff, 0x6cffff006bffff, 0x6effff006dffff, 0x70ffff006fffff, + 0x72ffff0071ffff, 0x74ffff0073ffff, 0xffff0075ffffffff, + 0x780077ffff0076, 0x7affffffff0079, 0xffffffff007bffff, + 0xffffffffffff007c, 0xffffffffffff007d, 0xffff007effffffff, + 0xffffffff007fffff, 0xffff00810080ffff, 0xffff0082ffffffff, + 0x84ffff0083ffff, 0xffffffff0085ffff, 0xffffffffffff0086, + 0xffffffff0087ffff, 0xffffffffffff0088, 0xffff008affff0089, + 0xffffffff008bffff, 0x8dffff008cffff, 0xffffffffffffffff, + 0x910090008f008e, 0x95009400930092, 0xffff0097ffff0096, + 0xffff0099ffff0098, 0xffff009bffff009a, 0xffff009dffff009c, + 0xa0ffff009f009e, 0xa2ffff00a1ffff, 0xa4ffff00a3ffff, 0xa6ffff00a5ffff, + 0xa8ffff00a7ffff, 0xab00aa00a9ffff, 0xffffffff00acffff, + 0xaeffff00adffff, 0xb0ffff00afffff, 0xb2ffff00b1ffff, 0xb4ffff00b3ffff, + 0xb6ffff00b5ffff, 0xb8ffff00b7ffff, 0xbaffff00b9ffff, 0xbcffff00bbffff, + 0xbeffff00bdffff, 0xc0ffff00bfffff, 0xc1ffffffffffff, 0xc3ffff00c2ffff, + 0xc5ffff00c4ffff, 0xc7ffff00c6ffff, 0xc9ffff00c8ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xcbffffffff00ca, + 0xffff00cdffff00cc, 0xceffffffffffff, 0xd0ffff00cfffff, + 0xd2ffff00d1ffff, 0xd600d500d400d3, 0xd900d8ffff00d7, 0xdbffff00daffff, + 0xffffffffffffffff, 0xddffffffff00dc, 0xffff00df00deffff, + 0xe2ffff00e100e0, 0xe3ffffffffffff, 0xffff00e500e4ffff, + 0xffffffff00e6ffff, 0xffffffffffffffff, 0xffffffff00e7ffff, + 0xe9ffffffff00e8, 0xffffffffffffffff, 0xed00ec00eb00ea, + 0xffffffffffff00ee, 0xffff00efffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff00f0ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xf2ffff00f1ffff, 0xf3ffffffffffff, + 0xf4ffffffffffff, 0xffffffff00f600f5, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xfa00f900f800f7, 0xfd00fc00fbffff, + 0x101010000ff00fe, 0x105010401030102, 0x109010801070106, + 0x10d010c010b010a, 0x1110110010f010e, 0x115011401130112, + 0xffff011801170116, 0xffffffff011a0119, 0x11d011c011bffff, + 0x11fffff011effff, 0x121ffff0120ffff, 0x123ffff0122ffff, + 0x125ffff0124ffff, 0x127ffff0126ffff, 0x129ffff0128ffff, + 0xffff012c012b012a, 0xffffffff012dffff, 0x12fffffffff012e, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x133013201310130, 0x137013601350134, + 0x13b013a01390138, 0x13f013e013d013c, 0x143014201410140, + 0x147014601450144, 0x14b014a01490148, 0x14f014e014d014c, + 0x153015201510150, 0x157015601550154, 0x15b015a01590158, + 0x15f015e015d015c, 0x161ffff0160ffff, 0x163ffff0162ffff, + 0x165ffff0164ffff, 0x167ffff0166ffff, 0x169ffff0168ffff, + 0x16bffff016affff, 0x16dffff016cffff, 0x16fffff016effff, + 0xffffffff0170ffff, 0xffffffffffffffff, 0x171ffffffffffff, + 0x173ffff0172ffff, 0x175ffff0174ffff, 0x177ffff0176ffff, + 0x179ffff0178ffff, 0x17bffff017affff, 0x17dffff017cffff, + 0x17fffff017effff, 0x181ffff0180ffff, 0x183ffff0182ffff, + 0x185ffff0184ffff, 0x187ffff0186ffff, 0x189ffff0188ffff, + 0x18bffff018affff, 0xffff018cffffffff, 0xffff018effff018d, + 0xffff0190ffff018f, 0x1930192ffff0191, 0x195ffff0194ffff, + 0x197ffff0196ffff, 0x199ffff0198ffff, 0x19bffff019affff, + 0x19dffff019cffff, 0x19fffff019effff, 0x1a1ffff01a0ffff, + 0x1a3ffff01a2ffff, 0x1a5ffff01a4ffff, 0x1a7ffff01a6ffff, + 0x1a9ffff01a8ffff, 0x1abffff01aaffff, 0x1adffff01acffff, + 0x1afffff01aeffff, 0x1b1ffff01b0ffff, 0x1b3ffff01b2ffff, + 0x1b5ffff01b4ffff, 0x1b7ffff01b6ffff, 0x1b9ffff01b8ffff, + 0x1bbffff01baffff, 0x1bdffff01bcffff, 0x1bfffff01beffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x1c201c101c0ffff, + 0x1c601c501c401c3, 0x1ca01c901c801c7, 0x1ce01cd01cc01cb, + 0x1d201d101d001cf, 0x1d601d501d401d3, 0x1da01d901d801d7, + 0x1de01dd01dc01db, 0x1e201e101e001df, 0xffff01e501e401e3, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffff01e6ffff, 0xffffffff01e7ffff, + 0x1e9ffff01e8ffff, 0x1ebffff01eaffff, 0x1edffff01ecffff, + 0x1efffff01eeffff, 0x1f1ffff01f0ffff, 0x1f3ffff01f2ffff, + 0x1f5ffff01f4ffff, 0x1f7ffff01f6ffff, 0x1f9ffff01f8ffff, + 0x1fbffff01faffff, 0x1fdffff01fcffff, 0x1ffffff01feffff, + 0x201ffff0200ffff, 0x203ffff0202ffff, 0x205ffff0204ffff, + 0x207ffff0206ffff, 0x209ffff0208ffff, 0x20bffff020affff, + 0x20dffff020cffff, 0x20fffff020effff, 0x211ffff0210ffff, + 0x213ffff0212ffff, 0x215ffff0214ffff, 0x217ffff0216ffff, + 0x219ffff0218ffff, 0x21bffff021affff, 0x21dffff021cffff, + 0x21fffff021effff, 0x221ffff0220ffff, 0x223ffff0222ffff, + 0x225ffff0224ffff, 0x227ffff0226ffff, 0x229ffff0228ffff, + 0x22bffff022affff, 0x22dffff022cffff, 0x22fffff022effff, + 0x231ffff0230ffff, 0xffffffff0232ffff, 0x233ffffffffffff, + 0xffffffffffffffff, 0x235ffff0234ffff, 0x237ffff0236ffff, + 0x239ffff0238ffff, 0x23bffff023affff, 0x23dffff023cffff, + 0x23fffff023effff, 0x241ffff0240ffff, 0x243ffff0242ffff, + 0x245ffff0244ffff, 0x247ffff0246ffff, 0x249ffff0248ffff, + 0x24bffff024affff, 0x24dffff024cffff, 0x24fffff024effff, + 0x251ffff0250ffff, 0x253ffff0252ffff, 0x255ffff0254ffff, + 0x257ffff0256ffff, 0x259ffff0258ffff, 0x25bffff025affff, + 0x25dffff025cffff, 0x25fffff025effff, 0x261ffff0260ffff, + 0x263ffff0262ffff, 0x267026602650264, 0x26b026a02690268, + 0xffffffffffffffff, 0xffffffffffffffff, 0x26f026e026d026c, + 0xffffffff02710270, 0xffffffffffffffff, 0xffffffffffffffff, + 0x275027402730272, 0x279027802770276, 0xffffffffffffffff, + 0xffffffffffffffff, 0x27d027c027b027a, 0x2810280027f027e, + 0xffffffffffffffff, 0xffffffffffffffff, 0x285028402830282, + 0xffffffff02870286, 0xffffffffffffffff, 0xffffffffffffffff, + 0x289ffff0288ffff, 0x28bffff028affff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x28f028e028d028c, 0x293029202910290, + 0xffffffffffffffff, 0xffffffffffffffff, 0x297029602950294, + 0x29b029a02990298, 0x29f029e029d029c, 0xffffffff02a102a0, + 0x2a502a402a302a2, 0x2a902a802a702a6, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2ad02ac02ab02aa, 0x2b102b002af02ae, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2b502b402b302b2, + 0x2b902b802b702b6, 0xffffffffffffffff, 0xffffffffffffffff, + 0x2bcffff02bb02ba, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff02bdffffffff, 0x2beffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff02c002bf, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffff02c202c1, 0xffffffff02c3ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x2c4ffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffff02c5ffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2c902c802c702c6, + 0x2cd02cc02cb02ca, 0x2d102d002cf02ce, 0x2d502d402d302d2, + 0xffffffffffffffff, 0xffffffffffff02d6, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2da02d902d802d7, + 0x2de02dd02dc02db, 0x2e202e102e002df, 0x2e602e502e402e3, + 0x2ea02e902e802e7, 0x2ee02ed02ec02eb, 0xffffffff02f002ef, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x2f402f302f202f1, + 0x2f802f702f602f5, 0x2fc02fb02fa02f9, 0x30002ff02fe02fd, + 0x304030303020301, 0x308030703060305, 0x30c030b030a0309, + 0x310030f030e030d, 0x314031303120311, 0x318031703160315, + 0x31c031b031a0319, 0xffff031f031e031d, 0xffffffff0320ffff, + 0xffff03220321ffff, 0xffff0324ffff0323, 0xffffffffffff0325, + 0x326ffffffffffff, 0xffff0327ffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x329ffff0328ffff, 0x32bffff032affff, + 0x32dffff032cffff, 0x32fffff032effff, 0x331ffff0330ffff, + 0x333ffff0332ffff, 0x335ffff0334ffff, 0x337ffff0336ffff, + 0x339ffff0338ffff, 0x33bffff033affff, 0x33dffff033cffff, + 0x33fffff033effff, 0x341ffff0340ffff, 0x343ffff0342ffff, + 0x345ffff0344ffff, 0x347ffff0346ffff, 0x349ffff0348ffff, + 0x34bffff034affff, 0x34dffff034cffff, 0x34fffff034effff, + 0x351ffff0350ffff, 0x353ffff0352ffff, 0x355ffff0354ffff, + 0x357ffff0356ffff, 0x359ffff0358ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffff035bffff035a, 0x35cffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x360035f035e035d, 0x364036303620361, 0x368036703660365, + 0x36c036b036a0369, 0x370036f036e036d, 0x374037303720371, + 0x378037703760375, 0x37c037b037a0379, 0x380037f037e037d, + 0x383ffff03820381, 0xffffffffffffffff, 0xffffffff0384ffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x386ffff0385ffff, 0x388ffff0387ffff, + 0x38affff0389ffff, 0x38cffff038bffff, 0x38effff038dffff, + 0x390ffff038fffff, 0x392ffff0391ffff, 0x394ffff0393ffff, + 0x396ffff0395ffff, 0x398ffff0397ffff, 0x39affff0399ffff, + 0xffffffff039bffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x39dffff039cffff, + 0x39fffff039effff, 0x3a1ffff03a0ffff, 0x3a3ffff03a2ffff, + 0x3a5ffff03a4ffff, 0x3a7ffff03a6ffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3a8ffffffffffff, + 0x3aaffff03a9ffff, 0x3acffff03abffff, 0x3aeffff03adffff, + 0x3afffffffffffff, 0x3b1ffff03b0ffff, 0x3b3ffff03b2ffff, + 0x3b5ffff03b4ffff, 0x3b7ffff03b6ffff, 0x3b9ffff03b8ffff, + 0x3bbffff03baffff, 0x3bdffff03bcffff, 0x3bfffff03beffff, + 0x3c1ffff03c0ffff, 0x3c3ffff03c2ffff, 0x3c5ffff03c4ffff, + 0x3c7ffff03c6ffff, 0x3c9ffff03c8ffff, 0x3cbffff03caffff, + 0x3cdffff03ccffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffff03ceffffffff, 0x3d0ffffffff03cf, 0x3d2ffff03d1ffff, + 0x3d4ffff03d3ffff, 0xffffffffffffffff, 0xffffffffffff03d5, + 0x3d7ffff03d6ffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0x3d9ffff03d8ffff, 0x3dbffff03daffff, + 0xffffffff03dcffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0x3df03de03ddffff, 0x3e303e203e103e0, 0x3e703e603e503e4, + 0x3eb03ea03e903e8, 0x3ef03ee03ed03ec, 0x3f303f203f103f0, + 0xffff03f603f503f4, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0x3fa03f903f803f7, + 0x3fe03fd03fc03fb, 0x4020401040003ff, 0x406040504040403, + 0x40a040904080407, 0x40e040d040c040b, 0x41204110410040f, + 0x416041504140413, 0x41a041904180417, 0x41e041d041c041b, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]); @property { -private alias _IUA = immutable(uint[]); -_IUA toUpperTable() { static _IUA t = [ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x49, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c4, 0x1c4, 0x1c7, 0x1c7, 0x1ca, 0x1ca, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f1, 0x1f1, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, 0x18a, 0x18f, 0x190, 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, 0x19c, 0x2c6e, 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, 0x1b1, 0x1b2, 0x245, 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, 0x3fe, 0x3ff, 0x386, 0x388, 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, 0x526, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0xa77d, 0x2c63, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c60, 0x23a, 0x23e, 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa790, 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x2000053, 0x53, 0x130, 0x2000046, 0x46, 0x2000046, 0x49, 0x2000046, 0x4c, 0x3000046, 0x46, 0x49, 0x3000046, 0x46, 0x4c, 0x2000053, 0x54, 0x2000053, 0x54, 0x2000535, 0x552, 0x2000544, 0x546, 0x2000544, 0x535, 0x2000544, 0x53b, 0x200054e, 0x546, 0x2000544, 0x53d, 0x20002bc, 0x4e, 0x3000399, 0x308, 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, 0x2000048, 0x331, 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, 0x2000041, 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, 0x313, 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, 0x342, 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, 0x342, 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, 0x308, 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, 0x342, 0x20003a9, 0x342, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, 0x399, 0x2001f0b, 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, 0x399, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, 0x399, 0x2001f0b, 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, 0x399, 0x2001f6c, 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, 0x399, 0x2001f6c, 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, 0x399, 0x2000391, 0x399, 0x2000391, 0x399, 0x2000397, 0x399, 0x2000397, 0x399, 0x20003a9, 0x399, 0x20003a9, 0x399, 0x2001fba, 0x399, 0x2000386, 0x399, 0x2001fca, 0x399, 0x2000389, 0x399, 0x2001ffa, 0x399, 0x200038f, 0x399, 0x3000391, 0x342, 0x399, 0x3000397, 0x342, 0x399, 0x30003a9, 0x342, 0x399]; return t; } -_IUA toLowerTable() { static _IUA t = [ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x69, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0xff, 0x17a, 0x17c, 0x17e, 0x253, 0x183, 0x185, 0x254, 0x188, 0x256, 0x257, 0x18c, 0x1dd, 0x259, 0x25b, 0x192, 0x260, 0x263, 0x269, 0x268, 0x199, 0x26f, 0x272, 0x275, 0x1a1, 0x1a3, 0x1a5, 0x280, 0x1a8, 0x283, 0x1ad, 0x288, 0x1b0, 0x28a, 0x28b, 0x1b4, 0x1b6, 0x292, 0x1b9, 0x1bd, 0x1c6, 0x1c6, 0x1c9, 0x1c9, 0x1cc, 0x1cc, 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f3, 0x1f3, 0x1f5, 0x195, 0x1bf, 0x1f9, 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, 0x207, 0x209, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x21f, 0x19e, 0x223, 0x225, 0x227, 0x229, 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x2c65, 0x23c, 0x19a, 0x2c66, 0x242, 0x180, 0x289, 0x28c, 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x371, 0x373, 0x377, 0x3ac, 0x3ad, 0x3ae, 0x3af, 0x3cc, 0x3cd, 0x3ce, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf, 0x3c0, 0x3c1, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3d7, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3b8, 0x3f8, 0x3f2, 0x3fb, 0x37b, 0x37c, 0x37d, 0x450, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45a, 0x45b, 0x45c, 0x45d, 0x45e, 0x45f, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43a, 0x43b, 0x43c, 0x43d, 0x43e, 0x43f, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44a, 0x44b, 0x44c, 0x44d, 0x44e, 0x44f, 0x461, 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, 0x4b9, 0x4bb, 0x4bd, 0x4bf, 0x4cf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4cc, 0x4ce, 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, 0x4df, 0x4e1, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, 0x50d, 0x50f, 0x511, 0x513, 0x515, 0x517, 0x519, 0x51b, 0x51d, 0x51f, 0x521, 0x523, 0x525, 0x527, 0x561, 0x562, 0x563, 0x564, 0x565, 0x566, 0x567, 0x568, 0x569, 0x56a, 0x56b, 0x56c, 0x56d, 0x56e, 0x56f, 0x570, 0x571, 0x572, 0x573, 0x574, 0x575, 0x576, 0x577, 0x578, 0x579, 0x57a, 0x57b, 0x57c, 0x57d, 0x57e, 0x57f, 0x580, 0x581, 0x582, 0x583, 0x584, 0x585, 0x586, 0x2d00, 0x2d01, 0x2d02, 0x2d03, 0x2d04, 0x2d05, 0x2d06, 0x2d07, 0x2d08, 0x2d09, 0x2d0a, 0x2d0b, 0x2d0c, 0x2d0d, 0x2d0e, 0x2d0f, 0x2d10, 0x2d11, 0x2d12, 0x2d13, 0x2d14, 0x2d15, 0x2d16, 0x2d17, 0x2d18, 0x2d19, 0x2d1a, 0x2d1b, 0x2d1c, 0x2d1d, 0x2d1e, 0x2d1f, 0x2d20, 0x2d21, 0x2d22, 0x2d23, 0x2d24, 0x2d25, 0x2d27, 0x2d2d, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0xdf, 0x1ea1, 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x1efb, 0x1efd, 0x1eff, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x3c9, 0x6b, 0xe5, 0x214e, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2184, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x2c30, 0x2c31, 0x2c32, 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, 0x2c3b, 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53, 0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59, 0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c61, 0x26b, 0x1d7d, 0x27d, 0x2c68, 0x2c6a, 0x2c6c, 0x251, 0x271, 0x250, 0x252, 0x2c73, 0x2c76, 0x23f, 0x240, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2cdf, 0x2ce1, 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641, 0xa643, 0xa645, 0xa647, 0xa649, 0xa64b, 0xa64d, 0xa64f, 0xa651, 0xa653, 0xa655, 0xa657, 0xa659, 0xa65b, 0xa65d, 0xa65f, 0xa661, 0xa663, 0xa665, 0xa667, 0xa669, 0xa66b, 0xa66d, 0xa681, 0xa683, 0xa685, 0xa687, 0xa689, 0xa68b, 0xa68d, 0xa68f, 0xa691, 0xa693, 0xa695, 0xa697, 0xa723, 0xa725, 0xa727, 0xa729, 0xa72b, 0xa72d, 0xa72f, 0xa733, 0xa735, 0xa737, 0xa739, 0xa73b, 0xa73d, 0xa73f, 0xa741, 0xa743, 0xa745, 0xa747, 0xa749, 0xa74b, 0xa74d, 0xa74f, 0xa751, 0xa753, 0xa755, 0xa757, 0xa759, 0xa75b, 0xa75d, 0xa75f, 0xa761, 0xa763, 0xa765, 0xa767, 0xa769, 0xa76b, 0xa76d, 0xa76f, 0xa77a, 0xa77c, 0x1d79, 0xa77f, 0xa781, 0xa783, 0xa785, 0xa787, 0xa78c, 0x265, 0xa791, 0xa793, 0xa7a1, 0xa7a3, 0xa7a5, 0xa7a7, 0xa7a9, 0x266, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0x10428, 0x10429, 0x1042a, 0x1042b, 0x1042c, 0x1042d, 0x1042e, 0x1042f, 0x10430, 0x10431, 0x10432, 0x10433, 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, 0x1043a, 0x1043b, 0x1043c, 0x1043d, 0x1043e, 0x1043f, 0x10440, 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447, 0x10448, 0x10449, 0x1044a, 0x1044b, 0x1044c, 0x1044d, 0x1044e, 0x1044f, 0xdf, 0x2000069, 0x307, 0xfb00, 0xfb01, 0xfb02, 0xfb03, 0xfb04, 0xfb05, 0xfb06, 0x587, 0xfb13, 0xfb14, 0xfb15, 0xfb16, 0xfb17, 0x149, 0x390, 0x3b0, 0x1f0, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1fb6, 0x1fc6, 0x1fd2, 0x1fd3, 0x1fd6, 0x1fd7, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe6, 0x1fe7, 0x1ff6, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb3, 0x1fb3, 0x1fc3, 0x1fc3, 0x1ff3, 0x1ff3, 0x1fb2, 0x1fb4, 0x1fc2, 0x1fc4, 0x1ff2, 0x1ff4, 0x1fb7, 0x1fc7, 0x1ff7]; return t; } -_IUA toTitleTable() { static _IUA t = [ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x49, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c5, 0x1c5, 0x1c5, 0x1c8, 0x1c8, 0x1c8, 0x1cb, 0x1cb, 0x1cb, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f2, 0x1f2, 0x1f2, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, 0x18a, 0x18f, 0x190, 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, 0x19c, 0x2c6e, 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, 0x1b1, 0x1b2, 0x245, 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, 0x3fe, 0x3ff, 0x386, 0x388, 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, 0x526, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0xa77d, 0x2c63, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c60, 0x23a, 0x23e, 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa790, 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x2000053, 0x73, 0x130, 0x2000046, 0x66, 0x2000046, 0x69, 0x2000046, 0x6c, 0x3000046, 0x66, 0x69, 0x3000046, 0x66, 0x6c, 0x2000053, 0x74, 0x2000053, 0x74, 0x2000535, 0x582, 0x2000544, 0x576, 0x2000544, 0x565, 0x2000544, 0x56b, 0x200054e, 0x576, 0x2000544, 0x56d, 0x20002bc, 0x4e, 0x3000399, 0x308, 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, 0x2000048, 0x331, 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, 0x2000041, 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, 0x313, 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, 0x342, 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, 0x342, 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, 0x308, 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, 0x342, 0x20003a9, 0x342, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fbc, 0x1fcc, 0x1fcc, 0x1ffc, 0x1ffc, 0x2001fba, 0x345, 0x2000386, 0x345, 0x2001fca, 0x345, 0x2000389, 0x345, 0x2001ffa, 0x345, 0x200038f, 0x345, 0x3000391, 0x342, 0x345, 0x3000397, 0x342, 0x345, 0x30003a9, 0x342, 0x345]; return t; } -} + private alias _IUA = immutable(uint[]); + _IUA toUpperTable() + { + static _IUA t = [ + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, + 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, + 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x49, + 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, + 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, + 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, + 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, + 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, + 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, + 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c4, 0x1c4, 0x1c7, 0x1c7, + 0x1ca, 0x1ca, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, + 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, + 0x1ec, 0x1ee, 0x1f1, 0x1f1, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, + 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, + 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, + 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x2c7e, + 0x2c7f, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x2c6f, 0x2c6d, + 0x2c70, 0x181, 0x186, 0x189, 0x18a, 0x18f, 0x190, 0x193, 0x194, + 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, 0x19c, 0x2c6e, 0x19d, 0x19f, + 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, 0x1b1, 0x1b2, 0x245, 0x1b7, + 0x399, 0x370, 0x372, 0x376, 0x3fd, 0x3fe, 0x3ff, 0x386, 0x388, + 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, + 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, + 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, + 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, + 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, + 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, 0x3a1, 0x3f9, 0x395, 0x3f7, + 0x3fa, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, + 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, + 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, + 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x400, 0x401, 0x402, + 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, + 0x40c, 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, 0x466, 0x468, + 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, + 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, + 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, + 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, + 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, + 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, + 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, + 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, + 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, + 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, + 0x526, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, + 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, + 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, + 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, + 0x554, 0x555, 0x556, 0xa77d, 0x2c63, 0x1e00, 0x1e02, 0x1e04, + 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, + 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, + 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, + 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, + 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, + 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, + 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, + 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, + 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, + 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, + 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, + 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, + 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, + 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, + 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, + 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, + 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, + 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, + 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, + 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f59, 0x1f5b, 0x1f5d, + 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, + 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, + 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x1f88, + 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, + 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, + 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, + 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, 0x1fd9, 0x1fe8, 0x1fe9, + 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, + 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, + 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, + 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, + 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, + 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, + 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, + 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, + 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, + 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, 0x2c29, + 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c60, 0x23a, 0x23e, + 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, 0x2c82, 0x2c84, + 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, + 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, + 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, + 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, + 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, + 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, + 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, + 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, + 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, + 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, + 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, + 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, + 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, + 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, + 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, + 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa722, 0xa724, 0xa726, + 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, + 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, + 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, + 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, + 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, + 0xa784, 0xa786, 0xa78b, 0xa790, 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, + 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, + 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, + 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, + 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, 0x10403, + 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, + 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, + 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, + 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, + 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, + 0x10427, 0x2000053, 0x53, 0x130, 0x2000046, 0x46, 0x2000046, 0x49, + 0x2000046, 0x4c, 0x3000046, 0x46, 0x49, 0x3000046, 0x46, 0x4c, + 0x2000053, 0x54, 0x2000053, 0x54, 0x2000535, 0x552, 0x2000544, + 0x546, 0x2000544, 0x535, 0x2000544, 0x53b, 0x200054e, 0x546, + 0x2000544, 0x53d, 0x20002bc, 0x4e, 0x3000399, 0x308, 0x301, + 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, 0x2000048, 0x331, + 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, 0x2000041, + 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, 0x313, + 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, 0x342, + 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, 0x342, + 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, 0x308, + 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, 0x342, + 0x20003a9, 0x342, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, + 0x399, 0x2001f0b, 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, + 0x2001f0e, 0x399, 0x2001f0f, 0x399, 0x2001f08, 0x399, 0x2001f09, + 0x399, 0x2001f0a, 0x399, 0x2001f0b, 0x399, 0x2001f0c, 0x399, + 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, 0x399, 0x2001f28, + 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, 0x399, + 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, + 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, + 0x2001f2b, 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, + 0x399, 0x2001f2f, 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, + 0x2001f6a, 0x399, 0x2001f6b, 0x399, 0x2001f6c, 0x399, 0x2001f6d, + 0x399, 0x2001f6e, 0x399, 0x2001f6f, 0x399, 0x2001f68, 0x399, + 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, 0x399, 0x2001f6c, + 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, 0x399, + 0x2000391, 0x399, 0x2000391, 0x399, 0x2000397, 0x399, 0x2000397, + 0x399, 0x20003a9, 0x399, 0x20003a9, 0x399, 0x2001fba, 0x399, + 0x2000386, 0x399, 0x2001fca, 0x399, 0x2000389, 0x399, 0x2001ffa, + 0x399, 0x200038f, 0x399, 0x3000391, 0x342, 0x399, 0x3000397, 0x342, + 0x399, 0x30003a9, 0x342, 0x399 + ]; + return t; + } + + _IUA toLowerTable() + { + static _IUA t = [ + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + 0xfe, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, + 0x111, 0x113, 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, + 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x69, 0x133, + 0x135, 0x137, 0x13a, 0x13c, 0x13e, 0x140, 0x142, 0x144, 0x146, + 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, 0x155, 0x157, 0x159, + 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16b, + 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0xff, 0x17a, 0x17c, + 0x17e, 0x253, 0x183, 0x185, 0x254, 0x188, 0x256, 0x257, 0x18c, + 0x1dd, 0x259, 0x25b, 0x192, 0x260, 0x263, 0x269, 0x268, 0x199, + 0x26f, 0x272, 0x275, 0x1a1, 0x1a3, 0x1a5, 0x280, 0x1a8, 0x283, + 0x1ad, 0x288, 0x1b0, 0x28a, 0x28b, 0x1b4, 0x1b6, 0x292, 0x1b9, + 0x1bd, 0x1c6, 0x1c6, 0x1c9, 0x1c9, 0x1cc, 0x1cc, 0x1ce, 0x1d0, + 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, 0x1e3, + 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f3, 0x1f3, 0x1f5, + 0x195, 0x1bf, 0x1f9, 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, + 0x207, 0x209, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, + 0x219, 0x21b, 0x21d, 0x21f, 0x19e, 0x223, 0x225, 0x227, 0x229, + 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x2c65, 0x23c, 0x19a, 0x2c66, + 0x242, 0x180, 0x289, 0x28c, 0x247, 0x249, 0x24b, 0x24d, 0x24f, + 0x371, 0x373, 0x377, 0x3ac, 0x3ad, 0x3ae, 0x3af, 0x3cc, 0x3cd, + 0x3ce, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, + 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf, 0x3c0, 0x3c1, + 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, + 0x3d7, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e5, 0x3e7, + 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3b8, 0x3f8, 0x3f2, 0x3fb, 0x37b, + 0x37c, 0x37d, 0x450, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, + 0x457, 0x458, 0x459, 0x45a, 0x45b, 0x45c, 0x45d, 0x45e, 0x45f, + 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, + 0x439, 0x43a, 0x43b, 0x43c, 0x43d, 0x43e, 0x43f, 0x440, 0x441, + 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44a, + 0x44b, 0x44c, 0x44d, 0x44e, 0x44f, 0x461, 0x463, 0x465, 0x467, + 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, 0x477, 0x479, + 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, 0x493, + 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, + 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, + 0x4b9, 0x4bb, 0x4bd, 0x4bf, 0x4cf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, + 0x4ca, 0x4cc, 0x4ce, 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, + 0x4dd, 0x4df, 0x4e1, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, + 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x4fd, 0x4ff, + 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, 0x50d, 0x50f, 0x511, + 0x513, 0x515, 0x517, 0x519, 0x51b, 0x51d, 0x51f, 0x521, 0x523, + 0x525, 0x527, 0x561, 0x562, 0x563, 0x564, 0x565, 0x566, 0x567, + 0x568, 0x569, 0x56a, 0x56b, 0x56c, 0x56d, 0x56e, 0x56f, 0x570, + 0x571, 0x572, 0x573, 0x574, 0x575, 0x576, 0x577, 0x578, 0x579, + 0x57a, 0x57b, 0x57c, 0x57d, 0x57e, 0x57f, 0x580, 0x581, 0x582, + 0x583, 0x584, 0x585, 0x586, 0x2d00, 0x2d01, 0x2d02, 0x2d03, 0x2d04, + 0x2d05, 0x2d06, 0x2d07, 0x2d08, 0x2d09, 0x2d0a, 0x2d0b, 0x2d0c, + 0x2d0d, 0x2d0e, 0x2d0f, 0x2d10, 0x2d11, 0x2d12, 0x2d13, 0x2d14, + 0x2d15, 0x2d16, 0x2d17, 0x2d18, 0x2d19, 0x2d1a, 0x2d1b, 0x2d1c, + 0x2d1d, 0x2d1e, 0x2d1f, 0x2d20, 0x2d21, 0x2d22, 0x2d23, 0x2d24, + 0x2d25, 0x2d27, 0x2d2d, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, + 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, + 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, + 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, + 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, + 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, + 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, + 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, + 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, + 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0xdf, 0x1ea1, + 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, + 0x1eb3, 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, + 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, + 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, + 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, + 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x1efb, 0x1efd, 0x1eff, 0x1f00, + 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f10, + 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f20, 0x1f21, 0x1f22, + 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, + 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f40, 0x1f41, 0x1f42, + 0x1f43, 0x1f44, 0x1f45, 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, + 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f80, + 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, + 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, + 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb0, + 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1f72, 0x1f73, 0x1f74, 0x1f75, + 0x1fc3, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fe0, 0x1fe1, 0x1f7a, + 0x1f7b, 0x1fe5, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x3c9, + 0x6b, 0xe5, 0x214e, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, + 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, + 0x217e, 0x217f, 0x2184, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, + 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, + 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, + 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x2c30, 0x2c31, 0x2c32, + 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, + 0x2c3b, 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, + 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, + 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, + 0x2c53, 0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59, 0x2c5a, + 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c61, 0x26b, 0x1d7d, 0x27d, + 0x2c68, 0x2c6a, 0x2c6c, 0x251, 0x271, 0x250, 0x252, 0x2c73, 0x2c76, + 0x23f, 0x240, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, + 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, + 0x2c9d, 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, + 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, + 0x2cbd, 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, + 0x2ccd, 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, + 0x2cdd, 0x2cdf, 0x2ce1, 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641, + 0xa643, 0xa645, 0xa647, 0xa649, 0xa64b, 0xa64d, 0xa64f, 0xa651, + 0xa653, 0xa655, 0xa657, 0xa659, 0xa65b, 0xa65d, 0xa65f, 0xa661, + 0xa663, 0xa665, 0xa667, 0xa669, 0xa66b, 0xa66d, 0xa681, 0xa683, + 0xa685, 0xa687, 0xa689, 0xa68b, 0xa68d, 0xa68f, 0xa691, 0xa693, + 0xa695, 0xa697, 0xa723, 0xa725, 0xa727, 0xa729, 0xa72b, 0xa72d, + 0xa72f, 0xa733, 0xa735, 0xa737, 0xa739, 0xa73b, 0xa73d, 0xa73f, + 0xa741, 0xa743, 0xa745, 0xa747, 0xa749, 0xa74b, 0xa74d, 0xa74f, + 0xa751, 0xa753, 0xa755, 0xa757, 0xa759, 0xa75b, 0xa75d, 0xa75f, + 0xa761, 0xa763, 0xa765, 0xa767, 0xa769, 0xa76b, 0xa76d, 0xa76f, + 0xa77a, 0xa77c, 0x1d79, 0xa77f, 0xa781, 0xa783, 0xa785, 0xa787, + 0xa78c, 0x265, 0xa791, 0xa793, 0xa7a1, 0xa7a3, 0xa7a5, 0xa7a7, + 0xa7a9, 0x266, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, + 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, + 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, + 0xff57, 0xff58, 0xff59, 0xff5a, 0x10428, 0x10429, 0x1042a, 0x1042b, + 0x1042c, 0x1042d, 0x1042e, 0x1042f, 0x10430, 0x10431, 0x10432, + 0x10433, 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, + 0x1043a, 0x1043b, 0x1043c, 0x1043d, 0x1043e, 0x1043f, 0x10440, + 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447, + 0x10448, 0x10449, 0x1044a, 0x1044b, 0x1044c, 0x1044d, 0x1044e, + 0x1044f, 0xdf, 0x2000069, 0x307, 0xfb00, 0xfb01, 0xfb02, 0xfb03, + 0xfb04, 0xfb05, 0xfb06, 0x587, 0xfb13, 0xfb14, 0xfb15, 0xfb16, + 0xfb17, 0x149, 0x390, 0x3b0, 0x1f0, 0x1e96, 0x1e97, 0x1e98, 0x1e99, + 0x1e9a, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1fb6, 0x1fc6, 0x1fd2, + 0x1fd3, 0x1fd6, 0x1fd7, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe6, 0x1fe7, + 0x1ff6, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, + 0x1f87, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, + 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, + 0x1f97, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, + 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, + 0x1fa7, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, + 0x1fa7, 0x1fb3, 0x1fb3, 0x1fc3, 0x1fc3, 0x1ff3, 0x1ff3, 0x1fb2, + 0x1fb4, 0x1fc2, 0x1fc4, 0x1ff2, 0x1ff4, 0x1fb7, 0x1fc7, 0x1ff7 + ]; + return t; + } + _IUA toTitleTable() + { + static _IUA t = [ + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, + 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, + 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x49, + 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, + 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, + 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, + 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, + 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, + 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, + 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c5, 0x1c5, 0x1c5, 0x1c8, + 0x1c8, 0x1c8, 0x1cb, 0x1cb, 0x1cb, 0x1cd, 0x1cf, 0x1d1, 0x1d3, + 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, + 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f2, 0x1f2, 0x1f2, 0x1f4, + 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, + 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, + 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, + 0x230, 0x232, 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, 0x24a, + 0x24c, 0x24e, 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, 0x18a, + 0x18f, 0x190, 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, + 0x19c, 0x2c6e, 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, + 0x1b1, 0x1b2, 0x245, 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, + 0x3fe, 0x3ff, 0x386, 0x388, 0x389, 0x38a, 0x391, 0x392, 0x393, + 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, + 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, + 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, + 0x392, 0x398, 0x3a6, 0x3a0, 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, + 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, + 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, 0x411, 0x412, 0x413, + 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, + 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, + 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, + 0x42f, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, + 0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x460, + 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, + 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, + 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, + 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, + 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, + 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, + 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, + 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, + 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, + 0x50c, 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, + 0x51e, 0x520, 0x522, 0x524, 0x526, 0x531, 0x532, 0x533, 0x534, + 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, + 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, 0x546, + 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, + 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0xa77d, 0x2c63, + 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, + 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, + 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, + 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, + 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, + 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, + 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, + 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, + 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, + 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, + 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, + 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, + 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, + 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, + 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, + 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, + 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, + 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, + 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, + 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, + 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, + 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, + 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, + 0x1ffa, 0x1ffb, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, + 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, + 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, + 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, + 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, + 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, + 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, + 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, + 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, + 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, + 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, + 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, + 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, + 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, + 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, + 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, + 0x2c60, 0x23a, 0x23e, 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, + 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, + 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, + 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, + 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, + 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, + 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, + 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, + 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, + 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, + 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, + 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, + 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, + 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, + 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, + 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, + 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, + 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, + 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, + 0xa744, 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, + 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, + 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, + 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa790, 0xa792, + 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, + 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, + 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, + 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, + 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, + 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, + 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, + 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, + 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, + 0x10424, 0x10425, 0x10426, 0x10427, 0x2000053, 0x73, 0x130, + 0x2000046, 0x66, 0x2000046, 0x69, 0x2000046, 0x6c, 0x3000046, 0x66, + 0x69, 0x3000046, 0x66, 0x6c, 0x2000053, 0x74, 0x2000053, 0x74, + 0x2000535, 0x582, 0x2000544, 0x576, 0x2000544, 0x565, 0x2000544, + 0x56b, 0x200054e, 0x576, 0x2000544, 0x56d, 0x20002bc, 0x4e, + 0x3000399, 0x308, 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, + 0x2000048, 0x331, 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, + 0x30a, 0x2000041, 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, + 0x30003a5, 0x313, 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, + 0x2000397, 0x342, 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, + 0x2000399, 0x342, 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, + 0x30003a5, 0x308, 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, + 0x30003a5, 0x308, 0x342, 0x20003a9, 0x342, 0x1f88, 0x1f89, 0x1f8a, + 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, + 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, + 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, + 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, + 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, + 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fbc, 0x1fcc, + 0x1fcc, 0x1ffc, 0x1ffc, 0x2001fba, 0x345, 0x2000386, 0x345, + 0x2001fca, 0x345, 0x2000389, 0x345, 0x2001ffa, 0x345, 0x200038f, + 0x345, 0x3000391, 0x342, 0x345, 0x3000397, 0x342, 0x345, 0x30003a9, 0x342, + 0x345 + ]; + return t; + } } +} -static if(size_t.sizeof == 4) { +static if (size_t.sizeof == 4) +{ //1536 bytes -enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([ 0x0, 0x40, 0x80], [ 0x100, 0x80, 0x2000], [ 0x2020100, 0x4020302, 0x2020205, 0x2060202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x50004, 0x30006, 0x30007, 0x30003, 0x30008, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x90003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xa0003, 0xb0003, 0x30003, 0x3000c, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xe000d, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x4200400, 0x80000000, 0xff7fffff, 0xaaaaaaaa, 0x55aaaaaa, 0xaaaaab55, 0xd4aaaaaa, 0x4e243129, 0xe6512d2a, 0xb5555240, 0xaa29aaaa, 0xaaaaaaaa, 0x93faaaaa, 0xffffaa85, 0xffffffff, 0xffefffff, 0x1ffffff, 0x3, 0x1f, 0x0, 0x0, 0x20, 0x3c8a0000, 0x10000, 0xfffff000, 0xaae37fff, 0x192faaaa, 0x0, 0xffff0000, 0xffffffff, 0xaaaaaaaa, 0xaaaaa802, 0xaaaaaaaa, 0xaaaad554, 0xaaaaaaaa, 0xaaaaaaaa, 0xaa, 0x0, 0xfffffffe, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, 0x0, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xbfeaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0x3f00ff, 0xff00ff, 0xff003f, 0x3fff00ff, 0xff00ff, 0x40df00ff, 0xcf00dc, 0xdc00ff, 0x0, 0x0, 0x0, 0x80020000, 0x1fff0000, 0x0, 0x0, 0x0, 0x8c400, 0x32108000, 0x43c0, 0xffff0000, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x3ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x7fffffff, 0x3fda1562, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0x8501a, 0xffffffff, 0x20bf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaaaaaaaa, 0x2aaa, 0xaaaaaa, 0x0, 0x0, 0x0, 0x0, 0xaaabaaa8, 0xaaaaaaaa, 0x95ffaaaa, 0xa50aa, 0x2aa, 0x0, 0x7000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf8007f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffff00, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfc000000, 0xfffff, 0xffdfc000, 0xff, 0xffffffc, 0xebc00000, 0xffef, 0xfffffc00, 0xc000000f, 0xffffff, 0xfffc0000, 0xfff, 0xffffffc0, 0xfc000000, 0xfffff, 0xffffc000, 0xff, 0xffffffc, 0xffc00000, 0xffff, 0xfffffc00, 0x3f, 0xf7fffffc, 0xf0000003, 0xfdfffff, 0xffc00000, 0x3f7fff, 0xffff0000, 0xfdff, 0xfffffc00, 0xbf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([0x0, 0x40, 0x80], + [0x100, 0x80, 0x2000], [0x2020100, 0x4020302, 0x2020205, 0x2060202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x10000, 0x30002, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x50004, 0x30006, 0x30007, 0x30003, 0x30008, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x90003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xa0003, + 0xb0003, 0x30003, 0x3000c, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xe000d, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x4200400, 0x80000000, + 0xff7fffff, 0xaaaaaaaa, 0x55aaaaaa, 0xaaaaab55, 0xd4aaaaaa, 0x4e243129, + 0xe6512d2a, 0xb5555240, 0xaa29aaaa, 0xaaaaaaaa, 0x93faaaaa, 0xffffaa85, + 0xffffffff, 0xffefffff, 0x1ffffff, 0x3, 0x1f, 0x0, 0x0, 0x20, + 0x3c8a0000, 0x10000, 0xfffff000, 0xaae37fff, 0x192faaaa, 0x0, + 0xffff0000, 0xffffffff, 0xaaaaaaaa, 0xaaaaa802, 0xaaaaaaaa, 0xaaaad554, + 0xaaaaaaaa, 0xaaaaaaaa, 0xaa, 0x0, 0xfffffffe, 0xff, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, 0x0, + 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xbfeaaaaa, 0xaaaaaaaa, + 0xaaaaaaaa, 0xaaaaaaaa, 0x3f00ff, 0xff00ff, 0xff003f, 0x3fff00ff, + 0xff00ff, 0x40df00ff, 0xcf00dc, 0xdc00ff, 0x0, 0x0, 0x0, 0x80020000, + 0x1fff0000, 0x0, 0x0, 0x0, 0x8c400, 0x32108000, 0x43c0, 0xffff0000, + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x3ff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x7fffffff, + 0x3fda1562, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0x8501a, 0xffffffff, + 0x20bf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaaaaaaaa, 0x2aaa, + 0xaaaaaa, 0x0, 0x0, 0x0, 0x0, 0xaaabaaa8, 0xaaaaaaaa, 0x95ffaaaa, + 0xa50aa, 0x2aa, 0x0, 0x7000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xf8007f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffff00, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xfc000000, 0xfffff, 0xffdfc000, 0xff, 0xffffffc, + 0xebc00000, 0xffef, 0xfffffc00, 0xc000000f, 0xffffff, 0xfffc0000, + 0xfff, 0xffffffc0, 0xfc000000, 0xfffff, 0xffffc000, 0xff, 0xffffffc, + 0xffc00000, 0xffff, 0xfffffc00, 0x3f, 0xf7fffffc, 0xf0000003, + 0xfdfffff, 0xffc00000, 0x3f7fff, 0xffff0000, 0xfdff, 0xfffffc00, 0xbf7, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0]); //1472 bytes -enum upperCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([ 0x0, 0x40, 0x80], [ 0x100, 0x80, 0x1e00], [ 0x2020100, 0x4020302, 0x2020205, 0x2060202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x30003, 0x30003, 0x30004, 0x30003, 0x30003, 0x50003, 0x30006, 0x30007, 0x30003, 0x30008, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x90003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xa0003, 0x30003, 0x3000b, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xd000c, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0x0, 0x7f7fffff, 0x0, 0x55555555, 0xaa555555, 0x555554aa, 0x2b555555, 0xb1dbced6, 0x11aed2d5, 0x4aaaa490, 0x55d25555, 0x55555555, 0x6c055555, 0x557a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x450000, 0xfffed740, 0xffb, 0x551c8000, 0xe6905555, 0xffffffff, 0xffff, 0x0, 0x55555555, 0x55555401, 0x55555555, 0x55552aab, 0x55555555, 0x55555555, 0xfffe0055, 0x7fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x20bf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55555555, 0x55555555, 0x55555555, 0x55555555, 0x40155555, 0x55555555, 0x55555555, 0x55555555, 0x3f00ff00, 0xff00ff00, 0xaa003f00, 0xff00, 0x0, 0xf000000, 0xf000f00, 0xf001f00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e273884, 0xc00f3d50, 0x20, 0xffff, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffc00000, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x7fff, 0x0, 0xc025ea9d, 0x55555555, 0x55555555, 0x55555555, 0x42805, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55555555, 0x1555, 0x555555, 0x0, 0x0, 0x0, 0x0, 0x55545554, 0x55555555, 0x6a005555, 0x52855, 0x555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ffffff, 0xfff00000, 0x3fff, 0xffffff00, 0xd0000003, 0x3fde64, 0xffff0000, 0x3ff, 0x1fdfe7b0, 0x7b000000, 0x1fc5f, 0xfffff000, 0x3f, 0x3ffffff, 0xfff00000, 0x3fff, 0xffffff00, 0xf0000003, 0x3fffff, 0xffff0000, 0x3ff, 0xffffff00, 0x1, 0x7fffffc, 0xf0000000, 0x1fffff, 0xffc00000, 0x7fff, 0xffff0000, 0x1ff, 0x400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum upperCaseTrieEntries = TrieEntry!(bool, 8, 4, 9)([0x0, 0x40, 0x80], + [0x100, 0x80, 0x1e00], [0x2020100, 0x4020302, 0x2020205, 0x2060202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x10000, 0x30002, 0x30003, 0x30003, 0x30004, 0x30003, 0x30003, + 0x50003, 0x30006, 0x30007, 0x30003, 0x30008, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x90003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0xa0003, 0x30003, 0x3000b, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0xd000c, 0x30003, + 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, 0x30003, + 0x30003, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0x0, 0x7f7fffff, 0x0, + 0x55555555, 0xaa555555, 0x555554aa, 0x2b555555, 0xb1dbced6, 0x11aed2d5, + 0x4aaaa490, 0x55d25555, 0x55555555, 0x6c055555, 0x557a, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x450000, 0xfffed740, 0xffb, 0x551c8000, + 0xe6905555, 0xffffffff, 0xffff, 0x0, 0x55555555, 0x55555401, + 0x55555555, 0x55552aab, 0x55555555, 0x55555555, 0xfffe0055, 0x7fffff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0x20bf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55555555, + 0x55555555, 0x55555555, 0x55555555, 0x40155555, 0x55555555, 0x55555555, + 0x55555555, 0x3f00ff00, 0xff00ff00, 0xaa003f00, 0xff00, 0x0, 0xf000000, + 0xf000f00, 0xf001f00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3e273884, 0xc00f3d50, 0x20, 0xffff, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xffc00000, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xffffffff, 0x7fff, 0x0, 0xc025ea9d, 0x55555555, 0x55555555, + 0x55555555, 0x42805, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x55555555, 0x1555, 0x555555, 0x0, 0x0, 0x0, 0x0, 0x55545554, + 0x55555555, 0x6a005555, 0x52855, 0x555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x3ffffff, 0xfff00000, 0x3fff, 0xffffff00, + 0xd0000003, 0x3fde64, 0xffff0000, 0x3ff, 0x1fdfe7b0, 0x7b000000, + 0x1fc5f, 0xfffff000, 0x3f, 0x3ffffff, 0xfff00000, 0x3fff, 0xffffff00, + 0xf0000003, 0x3fffff, 0xffff0000, 0x3ff, 0xffffff00, 0x1, 0x7fffffc, + 0xf0000000, 0x1fffff, 0xffc00000, 0x7fff, 0xffff0000, 0x1ff, 0x400, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0]); //8704 bytes -enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xd00], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, 0x0, 0x190018, 0x1b001a, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x210020, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x240023, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260025, 0x280027, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a0000, 0x2b, 0x2d002c, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30002f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x320031, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x20ffff, 0x240022, 0x280026, 0x2c002a, 0x30002e, 0x72f0032, 0x390037, 0x3d003b, 0x41003f, 0x1b00043, 0x4a0048, 0x4e004c, 0x520050, 0xffff0054, 0xffffffff, 0xffffffff, 0x21ffff, 0x250023, 0x290027, 0x2d002b, 0x31002f, 0x7300033, 0x3a0038, 0x3e003c, 0x420040, 0x1b10044, 0x4b0049, 0x4f004d, 0x530051, 0xffff0055, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x43fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xc800c6, 0xcc0498, 0x14904aa, 0xd500d3, 0xd900d7, 0xdd00db, 0xe100df, 0xe500e3, 0xe900e7, 0xed00eb, 0xf100ef, 0xffff00f3, 0xf700f5, 0xfb00f9, 0xff00fd, 0x6be0101, 0xc900c7, 0xcd0499, 0x14a04ab, 0xd600d4, 0xda00d8, 0xde00dc, 0xe200e0, 0xe600e4, 0xea00e8, 0xee00ec, 0xf200f0, 0xffff00f4, 0xf800f6, 0xfc00fa, 0x10000fe, 0x1a80102, 0x1160115, 0x1180117, 0x11c011b, 0x11e011d, 0x120011f, 0x1240123, 0x1260125, 0x1280127, 0x12c012b, 0x12e012d, 0x130012f, 0x1340133, 0x1360135, 0x1380137, 0x13a0139, 0x13c013b, 0x13e013d, 0x140013f, 0x1420141, 0x1440143, 0x1460145, 0x1480147, 0x14d014c, 0x14f014e, 0xffffffff, 0x1510150, 0x1530152, 0x1550154, 0x156ffff, 0x1580157, 0x15c0159, 0x15e015d, 0x160015f, 0x1620161, 0x1640163, 0x1660165, 0xffff0167, 0x1690168, 0x16b016a, 0x16d016c, 0x16f016e, 0x1710170, 0x1730172, 0x1750174, 0x1770176, 0x1790178, 0x17b017a, 0x17d017c, 0x17f017e, 0x1830182, 0x1870186, 0x18b018a, 0x18f018e, 0x1930192, 0x1970196, 0x19b019a, 0x19f019e, 0x1a301a2, 0x1a501a4, 0x1a701a6, 0x1aa01a9, 0x1ac01ab, 0x1ae01ad, 0x1b201af, 0x1b3028b, 0x1b601b5, 0x1ba01b9, 0x1bd01bb, 0x1bf01be, 0x1c301c1, 0xffff01c4, 0x1c701c5, 0x1cb01c9, 0x1cd01cc, 0x23b01cf, 0x1d301d1, 0x1d601d5, 0xffff0283, 0x1d901d7, 0x1db0269, 0x1de01dd, 0x1e001df, 0x1e201e1, 0x1e501e3, 0x1e701e6, 0xffffffff, 0x1ea01e9, 0x1ed01eb, 0x1ef01ee, 0x1f301f1, 0x1f501f4, 0x1f701f6, 0x1fa01f9, 0xffffffff, 0x1fc01fb, 0x23dffff, 0xffffffff, 0xffffffff, 0x2010200, 0x2060202, 0x2080207, 0x20d020c, 0x20f020e, 0x2110210, 0x2130212, 0x2150214, 0x2170216, 0x2190218, 0x21b021a, 0x21d021c, 0x1c6021e, 0x220021f, 0x2240223, 0x2260225, 0x2280227, 0x22a0229, 0x22c022b, 0x22e022d, 0x230022f, 0x2320231, 0x236ffff, 0x2380237, 0x23a0239, 0x23e023c, 0x240023f, 0x2440243, 0x2460245, 0x2480247, 0x24a0249, 0x24c024b, 0x24e024d, 0x250024f, 0x2520251, 0x2540253, 0x2560255, 0x2580257, 0x25a0259, 0x25c025b, 0x25e025d, 0x260025f, 0x2620261, 0x2640263, 0x2660265, 0x2680267, 0xffff026a, 0x26c026b, 0x26e026d, 0x270026f, 0x2720271, 0x2740273, 0x2760275, 0x2780277, 0x27a0279, 0x27c027b, 0xffffffff, 0xffffffff, 0xffffffff, 0x281027f, 0x2840282, 0x2d70285, 0x2870482, 0x28c0288, 0x28f028d, 0x2920291, 0x2940293, 0x2960295, 0x2980297, 0x29c029b, 0x466046a, 0x1b402b7, 0xffff01bc, 0x1c201c0, 0x1c8ffff, 0x1caffff, 0xffffffff, 0xffffffff, 0xffff01ce, 0x1d0ffff, 0x748ffff, 0xffff05fa, 0x1d201d4, 0x528ffff, 0xffffffff, 0x1d8ffff, 0x2b3ffff, 0xffff01da, 0x1dcffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2a3ffff, 0xffffffff, 0xffff01e4, 0x1e8ffff, 0xffffffff, 0xffffffff, 0x28e01ec, 0x1f201f0, 0xffff0290, 0xffffffff, 0xffffffff, 0xffff01f8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x83affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x31e031d, 0x320031f, 0xffffffff, 0x3240323, 0xffffffff, 0x3d5ffff, 0x3d903d7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0329, 0x32f032d, 0xffff0331, 0xffff0333, 0x3370335, 0x339ffff, 0x33e0395, 0x3cc0340, 0x3470345, 0x83b03c8, 0x35403c2, 0x3590440, 0x35d035b, 0x3c5039f, 0x388ffff, 0x36a0368, 0x36f039c, 0x7100371, 0x3780376, 0x32e032a, 0x3320330, 0x33affff, 0x33f0396, 0x3cd0341, 0x3480346, 0x83c03c9, 0x35503c3, 0x35a0441, 0x35e035c, 0x3c603a0, 0x38a0389, 0x36b0369, 0x370039d, 0x7110372, 0x3790377, 0x3360334, 0x3930338, 0x3ca0397, 0xffffffff, 0x39effff, 0x39403a1, 0x3a303a2, 0x3a703a6, 0x3a903a8, 0x3ab03aa, 0x3ad03ac, 0x3af03ae, 0x3b103b0, 0x3b503b4, 0x3b903b8, 0x3bd03bc, 0x3bf03be, 0x3c103c0, 0x3c703c4, 0xffff03d1, 0x3ce03cb, 0x3cfffff, 0x3d203d0, 0x3d403d3, 0x3d6ffff, 0x3da03d8, 0x3dd03db, 0x3e103df, 0x3e503e3, 0x3e903e7, 0x3ed03eb, 0x3f103ef, 0x3f503f3, 0x3f903f7, 0x3fd03fb, 0x40103ff, 0x4050403, 0x4090407, 0x40d040b, 0x411040f, 0x4150413, 0x4190417, 0x41d041b, 0x421041f, 0x4250423, 0x4290427, 0x42d042b, 0x431042f, 0x4350433, 0x4390437, 0x3fe03fc, 0x4020400, 0x4060404, 0x40a0408, 0x40e040c, 0x4120410, 0x4160414, 0x41a0418, 0x41e041c, 0x4220420, 0x4260424, 0x42a0428, 0x42e042c, 0x4320430, 0x4360434, 0x43a0438, 0x3de03dc, 0x3e203e0, 0x3e603e4, 0x3ea03e8, 0x3ee03ec, 0x3f203f0, 0x3f603f4, 0x3fa03f8, 0x4510450, 0x4530452, 0x4570456, 0x4590458, 0x45d045c, 0x4610460, 0x4650464, 0x4690468, 0x46d046c, 0x4710470, 0x4730472, 0x4770476, 0x4790478, 0x47b047a, 0x47d047c, 0x4810480, 0x4850484, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4950494, 0x4970496, 0x49b049a, 0x49d049c, 0x49f049e, 0x4a304a2, 0x4a704a6, 0x4a904a8, 0x4ad04ac, 0x4b104b0, 0x4b304b2, 0x4b704b6, 0x4b904b8, 0x4bb04ba, 0x4bf04be, 0x4c104c0, 0x4c504c4, 0x4c904c8, 0x4cd04cc, 0x4cf04ce, 0x4d304d2, 0x4d504d4, 0x4d704d6, 0x4db04da, 0x4df04de, 0x4e304e2, 0x4e704e6, 0x4ec04ea, 0x4f004ed, 0x4f404f1, 0x4f804f5, 0x4fc04f9, 0x50004fd, 0x5040501, 0x4eb0505, 0x50b050a, 0x50d050c, 0x50f050e, 0x5130512, 0x5170516, 0x5190518, 0x51d051c, 0x51f051e, 0x5210520, 0x5250524, 0x5270526, 0x52b052a, 0x52d052c, 0x52f052e, 0x5330532, 0x5370536, 0x5390538, 0x53d053c, 0x53f053e, 0x5410540, 0x5430542, 0x5470546, 0x5490548, 0x54b054a, 0x54d054c, 0x54f054e, 0x5510550, 0x5550554, 0x5570556, 0x5590558, 0x55b055a, 0x55d055c, 0x55f055e, 0x5630562, 0x5650564, 0x5670566, 0x5690568, 0x56b056a, 0x56f056e, 0x5730572, 0x5750574, 0x5770576, 0x5790578, 0x57b057a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x580ffff, 0x5840582, 0x5880586, 0x58c058a, 0x590058e, 0x5940592, 0x5980596, 0x59c059a, 0x5a0059e, 0x5a405a2, 0x5a805a6, 0x5ac05aa, 0x5b005ae, 0x5b405b2, 0x5b805b6, 0x5bc05ba, 0x5c005be, 0x5c405c2, 0x5c805c6, 0xffff05ca, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x581ffff, 0x5850583, 0x5890587, 0x58d058b, 0x591058f, 0x5950593, 0x5990597, 0x59d059b, 0x5a1059f, 0x5a505a3, 0x5a905a7, 0x5ad05ab, 0x5b105af, 0x5b505b3, 0x5b905b7, 0x5bd05bb, 0x5c105bf, 0x5c505c3, 0x5c905c7, 0xffff05cb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x880086, 0x8c008a, 0x90008e, 0x940092, 0x980096, 0x9c009a, 0xa0009e, 0xa400a2, 0xa800a6, 0xac00aa, 0xb000ae, 0xb400b2, 0xb800b6, 0xbc00ba, 0xc000be, 0xc400c2, 0x48e0486, 0x4a000ca, 0x4b400ce, 0x4c6ffff, 0xffffffff, 0xffffffff, 0x508ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7e8ffff, 0xffffffff, 0x454ffff, 0xffffffff, 0x5fd05fc, 0x5ff05fe, 0x6010600, 0x6050604, 0x6090608, 0x60b060a, 0x60f060e, 0x6110610, 0x6130612, 0x6170616, 0x6190618, 0x61d061c, 0x61f061e, 0x6210620, 0x6230622, 0x6270626, 0x6290628, 0x62b062a, 0x62d062c, 0x62f062e, 0x6310630, 0x6350634, 0x6370636, 0x6390638, 0x63b063a, 0x63d063c, 0x63f063e, 0x6430642, 0x6450644, 0x6470646, 0x6490648, 0x64b064a, 0x64d064c, 0x6510650, 0x6530652, 0x6550654, 0x6590658, 0x65d065c, 0x65f065e, 0x6630662, 0x6650664, 0x6670666, 0x6690668, 0x66b066a, 0x66d066c, 0x6710670, 0x6730672, 0x6750674, 0x6bc06bb, 0x67a0679, 0x67c067b, 0x680067f, 0x6820681, 0x6840683, 0x6860685, 0x6880687, 0x68a0689, 0x68e068d, 0x690068f, 0x6920691, 0x6960695, 0x6980697, 0x69a0699, 0x69e069d, 0x6a0069f, 0x6a206a1, 0x6a406a3, 0x6a606a5, 0x6a806a7, 0x6ac06ab, 0x6ae06ad, 0x6b006af, 0x6b206b1, 0x6b406b3, 0x6b606b5, 0xffffffff, 0xffffffff, 0x6bdffff, 0xffffffff, 0xffff06bf, 0x6c106c0, 0x6c306c2, 0x6c506c4, 0x6c906c8, 0x6cb06ca, 0x6cd06cc, 0x6cf06ce, 0x6d106d0, 0x6d506d4, 0x6d706d6, 0x6db06da, 0x6dd06dc, 0x6df06de, 0x6e106e0, 0x6e306e2, 0x6e506e4, 0x6e906e8, 0x6eb06ea, 0x6ef06ee, 0x6f106f0, 0x6f306f2, 0x6f506f4, 0x6f706f6, 0x6f906f8, 0x6fb06fa, 0x6fd06fc, 0x6ff06fe, 0x7010700, 0x7030702, 0x7050704, 0x7070706, 0x7090708, 0x70b070a, 0x70d070c, 0x70f070e, 0x7140713, 0x7160715, 0x7180717, 0x71c071b, 0x71e071d, 0x720071f, 0x7220721, 0x7240723, 0x7260725, 0x7280727, 0x72a0729, 0x72e072d, 0x7330732, 0x7380736, 0x73c073a, 0x740073e, 0x7440742, 0x7390737, 0x73d073b, 0x741073f, 0x7450743, 0x74c074a, 0x750074e, 0x7540752, 0xffffffff, 0x74d074b, 0x751074f, 0x7550753, 0xffffffff, 0x7660764, 0x76a0768, 0x76e076c, 0x7720770, 0x7670765, 0x76b0769, 0x76f076d, 0x7730771, 0x7860784, 0x78a0788, 0x78e078c, 0x7920790, 0x7870785, 0x78b0789, 0x78f078d, 0x7930791, 0x79e079c, 0x7a207a0, 0x7a607a4, 0xffffffff, 0x79f079d, 0x7a307a1, 0x7a707a5, 0xffffffff, 0x7b6ffff, 0x7baffff, 0x7beffff, 0x7c2ffff, 0x7b7ffff, 0x7bbffff, 0x7bfffff, 0x7c3ffff, 0x7d207d0, 0x7d607d4, 0x7da07d8, 0x7de07dc, 0x7d307d1, 0x7d707d5, 0x7db07d9, 0x7df07dd, 0x8360834, 0x840083e, 0x8440842, 0x84e084c, 0x8620860, 0x8580856, 0x8660864, 0xffffffff, 0x7f607f4, 0x7fa07f8, 0x7fe07fc, 0x8020800, 0x7f707f5, 0x7fb07f9, 0x7ff07fd, 0x8030801, 0x80a0808, 0x80e080c, 0x8120810, 0x8160814, 0x80b0809, 0x80f080d, 0x8130811, 0x8170815, 0x8220820, 0x8260824, 0x82a0828, 0x82e082c, 0x8230821, 0x8270825, 0x82b0829, 0x82f082d, 0x8320830, 0x838ffff, 0xffffffff, 0xffffffff, 0x8330831, 0x8370835, 0xffff0839, 0xffff083d, 0xffffffff, 0x846ffff, 0xffffffff, 0xffffffff, 0x841083f, 0x8450843, 0xffff0847, 0xffffffff, 0x84a0848, 0xffffffff, 0xffffffff, 0xffffffff, 0x84b0849, 0x84f084d, 0xffffffff, 0xffffffff, 0x8540852, 0xffffffff, 0x85affff, 0xffffffff, 0x8550853, 0x8590857, 0xffff085b, 0xffffffff, 0xffffffff, 0x868ffff, 0xffffffff, 0xffffffff, 0x8630861, 0x8670865, 0xffff0869, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0712, 0xffffffff, 0x14b0731, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0530, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0531, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x180029f, 0x18402af, 0x18802c1, 0x18c005e, 0x1900064, 0x194006c, 0x1980076, 0x19c007e, 0x18102a0, 0x18502b0, 0x18902c2, 0x18d005f, 0x1910065, 0x195006d, 0x1990077, 0x19d007f, 0xffffffff, 0x1b7ffff, 0xffff01b8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4d80444, 0x4dc0446, 0x4e0044c, 0x4e4045e, 0x4e80474, 0x2d3086a, 0x204ee, 0x6c604f2, 0x6e04f6, 0x37a04fa, 0x10d04fe, 0x61a0502, 0x51a0506, 0x4d90445, 0x4dd0447, 0x4e1044d, 0x4e5045f, 0x4e90475, 0x2d4086b, 0x304ef, 0x6c704f3, 0x6f04f7, 0x37b04fb, 0x10e04ff, 0x61b0503, 0x51b0507, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x40000, 0xa0008, 0xe000c, 0x160010, 0x1a0018, 0x2bb001c, 0x2d102c7, 0x5602e7, 0x600058, 0x660062, 0x70006a, 0x780074, 0x80007c, 0x2990082, 0x607e0, 0x6020084, 0x2a7057c, 0x5d005ce, 0x10305de, 0x1070105, 0x10f0109, 0x1190113, 0x1290121, 0xffff0131, 0x50001, 0xb0009, 0xf000d, 0x170011, 0x1b0019, 0x2bc001d, 0x2d202c8, 0x5702e8, 0x610059, 0x670063, 0x71006b, 0x790075, 0x81007d, 0x29a0083, 0x707e1, 0x6030085, 0x2a8057d, 0x5d105cf, 0x10405df, 0x1080106, 0x110010a, 0x11a0114, 0x12a0122, 0xffff0132, 0x4c304c2, 0x4550529, 0x28002a4, 0x45a0286, 0x2a9045b, 0x46202aa, 0x4670463, 0x46b02b4, 0xffff02b8, 0x2ba02b9, 0x2bfffff, 0xffff02c0, 0xffffffff, 0xffffffff, 0xffffffff, 0x48302d8, 0x2e202e1, 0x4890488, 0x48b048a, 0x48d048c, 0x4910490, 0x2fe02fd, 0x3040303, 0x30e030d, 0x3160315, 0x31a0319, 0x3260325, 0x3280327, 0x2fc02fb, 0x6ed06ec, 0x3810380, 0x3830382, 0x3870386, 0x3920391, 0x3a503a4, 0x3b303b2, 0x56d056c, 0x5cd05cc, 0x5db05da, 0x5ed05ec, 0x60d060c, 0x6570656, 0x43e043d, 0x6e706e6, 0x72c072b, 0x7830782, 0x7e307e2, 0x6940693, 0x65b065a, 0x150014, 0x5d005c, 0x4bd04bc, 0x4d104d0, 0x5d505d4, 0x1a101a0, 0x5110510, 0x5230522, 0x5350534, 0x5450544, 0x5530552, 0x5610560, 0x5710570, 0x57f057e, 0x15b015a, 0x37d037c, 0x3bb03ba, 0xffffffff, 0xffffffff, 0xffffffff, 0x5d2ffff, 0x5d805d3, 0xffff05d9, 0xffffffff, 0x5e305e2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x890087, 0x8d008b, 0x91008f, 0x950093, 0x990097, 0x9d009b, 0xa1009f, 0xa500a3, 0xa900a7, 0xad00ab, 0xb100af, 0xb500b3, 0xb900b7, 0xbd00bb, 0xc100bf, 0xc500c3, 0x48f0487, 0x4a100cb, 0x4b500cf, 0x4c7ffff, 0xffffffff, 0xffffffff, 0x509ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c402c3, 0x5d705d6, 0x5dd05dc, 0x5e105e0, 0x5e705e6, 0x5e905e8, 0x5eb05ea, 0x5ef05ee, 0x5f105f0, 0x5f505f4, 0x5f905f8, 0x3080307, 0x6150614, 0x6250624, 0x6330632, 0x6410640, 0x64f064e, 0x6610660, 0x66f066e, 0x67e067d, 0x68c068b, 0x69c069b, 0x6aa06a9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7350734, 0x690068, 0x27e027d, 0x75f075e, 0x7770776, 0x390038f, 0x1f001e, 0x7b107b0, 0x7c707c6, 0x2a202a1, 0x7e507e4, 0x6b806b7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7590758, 0x75d075c, 0x7610760, 0x2d602d5, 0x2e002df, 0x2e602e5, 0x2ee02ed, 0xffffffff, 0x7790778, 0x77d077c, 0x7810780, 0x30c030b, 0x3140313, 0x3180317, 0x3220321, 0x7950794, 0x7970796, 0x7990798, 0x79b079a, 0x37f037e, 0x3850384, 0x38e038d, 0x7a907a8, 0x7ab07aa, 0x7ad07ac, 0x7af07ae, 0x7b307b2, 0x7b507b4, 0x7b907b8, 0x7bd07bc, 0x7c107c0, 0x7c507c4, 0x7c907c8, 0x7cd07cc, 0x7cf07ce, 0x46f046e, 0x47f047e, 0x4930492, 0x4a504a4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x514ffff, 0x7e60515, 0x7e907e7, 0x7eb07ea, 0x7ed07ec, 0x7ef07ee, 0x7f107f0, 0x7f307f2, 0xffffffff, 0x5f2ffff, 0x74905f3, 0xffffffff, 0x8050804, 0x8070806, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x8190818, 0x81b081a, 0x81d081c, 0x81f081e, 0x5f705f6, 0xffff05fb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x756ffff, 0x75a02c5, 0x2cd02cb, 0x76202cf, 0x2db06d2, 0x2e30719, 0x2e90448, 0x2f107ca, 0x2f30774, 0x77a02f5, 0x77e02f9, 0x3050221, 0x30f007a, 0xffff043b, 0xffffffff, 0xffffffff, 0x757ffff, 0x75b02c6, 0x2ce02cc, 0x76302d0, 0x2dc06d3, 0x2e4071a, 0x2ea0449, 0x2f207cb, 0x2f40775, 0x77b02f6, 0x77f02fa, 0x3060222, 0x310007b, 0xffff043c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x85c0012, 0x72005a, 0x32b0311, 0x11106b9, 0x2ab05e4, 0x2dd029d, 0x2ef085e, 0x10b0606, 0x2d902a5, 0x4ca0289, 0x2b502ad, 0x2c902bd, 0x2eb0746, 0x30902f7, 0x241031b, 0x38b02b1, 0x44a03b6, 0x442053a, 0x6d8044e, 0x85004ae, 0x85d0013, 0x73005b, 0x32c0312, 0x11206ba, 0x2ac05e5, 0x2de029e, 0x2f0085f, 0x10c0607, 0x2da02a6, 0x4cb028a, 0x2b602ae, 0x2ca02be, 0x2ec0747, 0x30a02f8, 0x242031c, 0x38c02b2, 0x44b03b7, 0x443053b, 0x6d9044f, 0x85104af, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, 0x200], + [0x100, 0x380, 0xd00], [0x2020100, 0x4020302, 0x2020205, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, + 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, + 0x0, 0x190018, 0x1b001a, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x210020, 0x22, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x240023, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260025, 0x280027, 0x29, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2a0000, 0x2b, 0x2d002c, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x30002f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x320031, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x20ffff, 0x240022, 0x280026, 0x2c002a, 0x30002e, + 0x72f0032, 0x390037, 0x3d003b, 0x41003f, 0x1b00043, 0x4a0048, 0x4e004c, + 0x520050, 0xffff0054, 0xffffffff, 0xffffffff, 0x21ffff, 0x250023, + 0x290027, 0x2d002b, 0x31002f, 0x7300033, 0x3a0038, 0x3e003c, 0x420040, + 0x1b10044, 0x4b0049, 0x4f004d, 0x530051, 0xffff0055, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x43fffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xc800c6, 0xcc0498, 0x14904aa, + 0xd500d3, 0xd900d7, 0xdd00db, 0xe100df, 0xe500e3, 0xe900e7, 0xed00eb, + 0xf100ef, 0xffff00f3, 0xf700f5, 0xfb00f9, 0xff00fd, 0x6be0101, + 0xc900c7, 0xcd0499, 0x14a04ab, 0xd600d4, 0xda00d8, 0xde00dc, 0xe200e0, + 0xe600e4, 0xea00e8, 0xee00ec, 0xf200f0, 0xffff00f4, 0xf800f6, 0xfc00fa, + 0x10000fe, 0x1a80102, 0x1160115, 0x1180117, 0x11c011b, 0x11e011d, + 0x120011f, 0x1240123, 0x1260125, 0x1280127, 0x12c012b, 0x12e012d, + 0x130012f, 0x1340133, 0x1360135, 0x1380137, 0x13a0139, 0x13c013b, + 0x13e013d, 0x140013f, 0x1420141, 0x1440143, 0x1460145, 0x1480147, + 0x14d014c, 0x14f014e, 0xffffffff, 0x1510150, 0x1530152, 0x1550154, + 0x156ffff, 0x1580157, 0x15c0159, 0x15e015d, 0x160015f, 0x1620161, + 0x1640163, 0x1660165, 0xffff0167, 0x1690168, 0x16b016a, 0x16d016c, + 0x16f016e, 0x1710170, 0x1730172, 0x1750174, 0x1770176, 0x1790178, + 0x17b017a, 0x17d017c, 0x17f017e, 0x1830182, 0x1870186, 0x18b018a, + 0x18f018e, 0x1930192, 0x1970196, 0x19b019a, 0x19f019e, 0x1a301a2, + 0x1a501a4, 0x1a701a6, 0x1aa01a9, 0x1ac01ab, 0x1ae01ad, 0x1b201af, + 0x1b3028b, 0x1b601b5, 0x1ba01b9, 0x1bd01bb, 0x1bf01be, 0x1c301c1, + 0xffff01c4, 0x1c701c5, 0x1cb01c9, 0x1cd01cc, 0x23b01cf, 0x1d301d1, + 0x1d601d5, 0xffff0283, 0x1d901d7, 0x1db0269, 0x1de01dd, 0x1e001df, + 0x1e201e1, 0x1e501e3, 0x1e701e6, 0xffffffff, 0x1ea01e9, 0x1ed01eb, + 0x1ef01ee, 0x1f301f1, 0x1f501f4, 0x1f701f6, 0x1fa01f9, 0xffffffff, + 0x1fc01fb, 0x23dffff, 0xffffffff, 0xffffffff, 0x2010200, 0x2060202, + 0x2080207, 0x20d020c, 0x20f020e, 0x2110210, 0x2130212, 0x2150214, + 0x2170216, 0x2190218, 0x21b021a, 0x21d021c, 0x1c6021e, 0x220021f, + 0x2240223, 0x2260225, 0x2280227, 0x22a0229, 0x22c022b, 0x22e022d, + 0x230022f, 0x2320231, 0x236ffff, 0x2380237, 0x23a0239, 0x23e023c, + 0x240023f, 0x2440243, 0x2460245, 0x2480247, 0x24a0249, 0x24c024b, + 0x24e024d, 0x250024f, 0x2520251, 0x2540253, 0x2560255, 0x2580257, + 0x25a0259, 0x25c025b, 0x25e025d, 0x260025f, 0x2620261, 0x2640263, + 0x2660265, 0x2680267, 0xffff026a, 0x26c026b, 0x26e026d, 0x270026f, + 0x2720271, 0x2740273, 0x2760275, 0x2780277, 0x27a0279, 0x27c027b, + 0xffffffff, 0xffffffff, 0xffffffff, 0x281027f, 0x2840282, 0x2d70285, + 0x2870482, 0x28c0288, 0x28f028d, 0x2920291, 0x2940293, 0x2960295, + 0x2980297, 0x29c029b, 0x466046a, 0x1b402b7, 0xffff01bc, 0x1c201c0, + 0x1c8ffff, 0x1caffff, 0xffffffff, 0xffffffff, 0xffff01ce, 0x1d0ffff, + 0x748ffff, 0xffff05fa, 0x1d201d4, 0x528ffff, 0xffffffff, 0x1d8ffff, + 0x2b3ffff, 0xffff01da, 0x1dcffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x2a3ffff, 0xffffffff, 0xffff01e4, 0x1e8ffff, 0xffffffff, 0xffffffff, + 0x28e01ec, 0x1f201f0, 0xffff0290, 0xffffffff, 0xffffffff, 0xffff01f8, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x83affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x31e031d, 0x320031f, + 0xffffffff, 0x3240323, 0xffffffff, 0x3d5ffff, 0x3d903d7, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0329, 0x32f032d, 0xffff0331, + 0xffff0333, 0x3370335, 0x339ffff, 0x33e0395, 0x3cc0340, 0x3470345, + 0x83b03c8, 0x35403c2, 0x3590440, 0x35d035b, 0x3c5039f, 0x388ffff, + 0x36a0368, 0x36f039c, 0x7100371, 0x3780376, 0x32e032a, 0x3320330, + 0x33affff, 0x33f0396, 0x3cd0341, 0x3480346, 0x83c03c9, 0x35503c3, + 0x35a0441, 0x35e035c, 0x3c603a0, 0x38a0389, 0x36b0369, 0x370039d, + 0x7110372, 0x3790377, 0x3360334, 0x3930338, 0x3ca0397, 0xffffffff, + 0x39effff, 0x39403a1, 0x3a303a2, 0x3a703a6, 0x3a903a8, 0x3ab03aa, + 0x3ad03ac, 0x3af03ae, 0x3b103b0, 0x3b503b4, 0x3b903b8, 0x3bd03bc, + 0x3bf03be, 0x3c103c0, 0x3c703c4, 0xffff03d1, 0x3ce03cb, 0x3cfffff, + 0x3d203d0, 0x3d403d3, 0x3d6ffff, 0x3da03d8, 0x3dd03db, 0x3e103df, + 0x3e503e3, 0x3e903e7, 0x3ed03eb, 0x3f103ef, 0x3f503f3, 0x3f903f7, + 0x3fd03fb, 0x40103ff, 0x4050403, 0x4090407, 0x40d040b, 0x411040f, + 0x4150413, 0x4190417, 0x41d041b, 0x421041f, 0x4250423, 0x4290427, + 0x42d042b, 0x431042f, 0x4350433, 0x4390437, 0x3fe03fc, 0x4020400, + 0x4060404, 0x40a0408, 0x40e040c, 0x4120410, 0x4160414, 0x41a0418, + 0x41e041c, 0x4220420, 0x4260424, 0x42a0428, 0x42e042c, 0x4320430, + 0x4360434, 0x43a0438, 0x3de03dc, 0x3e203e0, 0x3e603e4, 0x3ea03e8, + 0x3ee03ec, 0x3f203f0, 0x3f603f4, 0x3fa03f8, 0x4510450, 0x4530452, + 0x4570456, 0x4590458, 0x45d045c, 0x4610460, 0x4650464, 0x4690468, + 0x46d046c, 0x4710470, 0x4730472, 0x4770476, 0x4790478, 0x47b047a, + 0x47d047c, 0x4810480, 0x4850484, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x4950494, 0x4970496, 0x49b049a, 0x49d049c, 0x49f049e, + 0x4a304a2, 0x4a704a6, 0x4a904a8, 0x4ad04ac, 0x4b104b0, 0x4b304b2, + 0x4b704b6, 0x4b904b8, 0x4bb04ba, 0x4bf04be, 0x4c104c0, 0x4c504c4, + 0x4c904c8, 0x4cd04cc, 0x4cf04ce, 0x4d304d2, 0x4d504d4, 0x4d704d6, + 0x4db04da, 0x4df04de, 0x4e304e2, 0x4e704e6, 0x4ec04ea, 0x4f004ed, + 0x4f404f1, 0x4f804f5, 0x4fc04f9, 0x50004fd, 0x5040501, 0x4eb0505, + 0x50b050a, 0x50d050c, 0x50f050e, 0x5130512, 0x5170516, 0x5190518, + 0x51d051c, 0x51f051e, 0x5210520, 0x5250524, 0x5270526, 0x52b052a, + 0x52d052c, 0x52f052e, 0x5330532, 0x5370536, 0x5390538, 0x53d053c, + 0x53f053e, 0x5410540, 0x5430542, 0x5470546, 0x5490548, 0x54b054a, + 0x54d054c, 0x54f054e, 0x5510550, 0x5550554, 0x5570556, 0x5590558, + 0x55b055a, 0x55d055c, 0x55f055e, 0x5630562, 0x5650564, 0x5670566, + 0x5690568, 0x56b056a, 0x56f056e, 0x5730572, 0x5750574, 0x5770576, + 0x5790578, 0x57b057a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x580ffff, 0x5840582, 0x5880586, 0x58c058a, 0x590058e, 0x5940592, + 0x5980596, 0x59c059a, 0x5a0059e, 0x5a405a2, 0x5a805a6, 0x5ac05aa, + 0x5b005ae, 0x5b405b2, 0x5b805b6, 0x5bc05ba, 0x5c005be, 0x5c405c2, + 0x5c805c6, 0xffff05ca, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x581ffff, 0x5850583, 0x5890587, 0x58d058b, 0x591058f, 0x5950593, + 0x5990597, 0x59d059b, 0x5a1059f, 0x5a505a3, 0x5a905a7, 0x5ad05ab, + 0x5b105af, 0x5b505b3, 0x5b905b7, 0x5bd05bb, 0x5c105bf, 0x5c505c3, + 0x5c905c7, 0xffff05cb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x880086, 0x8c008a, + 0x90008e, 0x940092, 0x980096, 0x9c009a, 0xa0009e, 0xa400a2, 0xa800a6, + 0xac00aa, 0xb000ae, 0xb400b2, 0xb800b6, 0xbc00ba, 0xc000be, 0xc400c2, + 0x48e0486, 0x4a000ca, 0x4b400ce, 0x4c6ffff, 0xffffffff, 0xffffffff, + 0x508ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7e8ffff, 0xffffffff, 0x454ffff, 0xffffffff, 0x5fd05fc, 0x5ff05fe, + 0x6010600, 0x6050604, 0x6090608, 0x60b060a, 0x60f060e, 0x6110610, + 0x6130612, 0x6170616, 0x6190618, 0x61d061c, 0x61f061e, 0x6210620, + 0x6230622, 0x6270626, 0x6290628, 0x62b062a, 0x62d062c, 0x62f062e, + 0x6310630, 0x6350634, 0x6370636, 0x6390638, 0x63b063a, 0x63d063c, + 0x63f063e, 0x6430642, 0x6450644, 0x6470646, 0x6490648, 0x64b064a, + 0x64d064c, 0x6510650, 0x6530652, 0x6550654, 0x6590658, 0x65d065c, + 0x65f065e, 0x6630662, 0x6650664, 0x6670666, 0x6690668, 0x66b066a, + 0x66d066c, 0x6710670, 0x6730672, 0x6750674, 0x6bc06bb, 0x67a0679, + 0x67c067b, 0x680067f, 0x6820681, 0x6840683, 0x6860685, 0x6880687, + 0x68a0689, 0x68e068d, 0x690068f, 0x6920691, 0x6960695, 0x6980697, + 0x69a0699, 0x69e069d, 0x6a0069f, 0x6a206a1, 0x6a406a3, 0x6a606a5, + 0x6a806a7, 0x6ac06ab, 0x6ae06ad, 0x6b006af, 0x6b206b1, 0x6b406b3, + 0x6b606b5, 0xffffffff, 0xffffffff, 0x6bdffff, 0xffffffff, 0xffff06bf, + 0x6c106c0, 0x6c306c2, 0x6c506c4, 0x6c906c8, 0x6cb06ca, 0x6cd06cc, + 0x6cf06ce, 0x6d106d0, 0x6d506d4, 0x6d706d6, 0x6db06da, 0x6dd06dc, + 0x6df06de, 0x6e106e0, 0x6e306e2, 0x6e506e4, 0x6e906e8, 0x6eb06ea, + 0x6ef06ee, 0x6f106f0, 0x6f306f2, 0x6f506f4, 0x6f706f6, 0x6f906f8, + 0x6fb06fa, 0x6fd06fc, 0x6ff06fe, 0x7010700, 0x7030702, 0x7050704, + 0x7070706, 0x7090708, 0x70b070a, 0x70d070c, 0x70f070e, 0x7140713, + 0x7160715, 0x7180717, 0x71c071b, 0x71e071d, 0x720071f, 0x7220721, + 0x7240723, 0x7260725, 0x7280727, 0x72a0729, 0x72e072d, 0x7330732, + 0x7380736, 0x73c073a, 0x740073e, 0x7440742, 0x7390737, 0x73d073b, + 0x741073f, 0x7450743, 0x74c074a, 0x750074e, 0x7540752, 0xffffffff, + 0x74d074b, 0x751074f, 0x7550753, 0xffffffff, 0x7660764, 0x76a0768, + 0x76e076c, 0x7720770, 0x7670765, 0x76b0769, 0x76f076d, 0x7730771, + 0x7860784, 0x78a0788, 0x78e078c, 0x7920790, 0x7870785, 0x78b0789, + 0x78f078d, 0x7930791, 0x79e079c, 0x7a207a0, 0x7a607a4, 0xffffffff, + 0x79f079d, 0x7a307a1, 0x7a707a5, 0xffffffff, 0x7b6ffff, 0x7baffff, + 0x7beffff, 0x7c2ffff, 0x7b7ffff, 0x7bbffff, 0x7bfffff, 0x7c3ffff, + 0x7d207d0, 0x7d607d4, 0x7da07d8, 0x7de07dc, 0x7d307d1, 0x7d707d5, + 0x7db07d9, 0x7df07dd, 0x8360834, 0x840083e, 0x8440842, 0x84e084c, + 0x8620860, 0x8580856, 0x8660864, 0xffffffff, 0x7f607f4, 0x7fa07f8, + 0x7fe07fc, 0x8020800, 0x7f707f5, 0x7fb07f9, 0x7ff07fd, 0x8030801, + 0x80a0808, 0x80e080c, 0x8120810, 0x8160814, 0x80b0809, 0x80f080d, + 0x8130811, 0x8170815, 0x8220820, 0x8260824, 0x82a0828, 0x82e082c, + 0x8230821, 0x8270825, 0x82b0829, 0x82f082d, 0x8320830, 0x838ffff, + 0xffffffff, 0xffffffff, 0x8330831, 0x8370835, 0xffff0839, 0xffff083d, + 0xffffffff, 0x846ffff, 0xffffffff, 0xffffffff, 0x841083f, 0x8450843, + 0xffff0847, 0xffffffff, 0x84a0848, 0xffffffff, 0xffffffff, 0xffffffff, + 0x84b0849, 0x84f084d, 0xffffffff, 0xffffffff, 0x8540852, 0xffffffff, + 0x85affff, 0xffffffff, 0x8550853, 0x8590857, 0xffff085b, 0xffffffff, + 0xffffffff, 0x868ffff, 0xffffffff, 0xffffffff, 0x8630861, 0x8670865, + 0xffff0869, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0712, 0xffffffff, 0x14b0731, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0530, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0531, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x180029f, 0x18402af, 0x18802c1, 0x18c005e, + 0x1900064, 0x194006c, 0x1980076, 0x19c007e, 0x18102a0, 0x18502b0, + 0x18902c2, 0x18d005f, 0x1910065, 0x195006d, 0x1990077, 0x19d007f, + 0xffffffff, 0x1b7ffff, 0xffff01b8, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4d80444, + 0x4dc0446, 0x4e0044c, 0x4e4045e, 0x4e80474, 0x2d3086a, 0x204ee, + 0x6c604f2, 0x6e04f6, 0x37a04fa, 0x10d04fe, 0x61a0502, 0x51a0506, + 0x4d90445, 0x4dd0447, 0x4e1044d, 0x4e5045f, 0x4e90475, 0x2d4086b, + 0x304ef, 0x6c704f3, 0x6f04f7, 0x37b04fb, 0x10e04ff, 0x61b0503, + 0x51b0507, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x40000, 0xa0008, 0xe000c, 0x160010, 0x1a0018, 0x2bb001c, 0x2d102c7, + 0x5602e7, 0x600058, 0x660062, 0x70006a, 0x780074, 0x80007c, 0x2990082, + 0x607e0, 0x6020084, 0x2a7057c, 0x5d005ce, 0x10305de, 0x1070105, + 0x10f0109, 0x1190113, 0x1290121, 0xffff0131, 0x50001, 0xb0009, 0xf000d, + 0x170011, 0x1b0019, 0x2bc001d, 0x2d202c8, 0x5702e8, 0x610059, 0x670063, + 0x71006b, 0x790075, 0x81007d, 0x29a0083, 0x707e1, 0x6030085, 0x2a8057d, + 0x5d105cf, 0x10405df, 0x1080106, 0x110010a, 0x11a0114, 0x12a0122, + 0xffff0132, 0x4c304c2, 0x4550529, 0x28002a4, 0x45a0286, 0x2a9045b, + 0x46202aa, 0x4670463, 0x46b02b4, 0xffff02b8, 0x2ba02b9, 0x2bfffff, + 0xffff02c0, 0xffffffff, 0xffffffff, 0xffffffff, 0x48302d8, 0x2e202e1, + 0x4890488, 0x48b048a, 0x48d048c, 0x4910490, 0x2fe02fd, 0x3040303, + 0x30e030d, 0x3160315, 0x31a0319, 0x3260325, 0x3280327, 0x2fc02fb, + 0x6ed06ec, 0x3810380, 0x3830382, 0x3870386, 0x3920391, 0x3a503a4, + 0x3b303b2, 0x56d056c, 0x5cd05cc, 0x5db05da, 0x5ed05ec, 0x60d060c, + 0x6570656, 0x43e043d, 0x6e706e6, 0x72c072b, 0x7830782, 0x7e307e2, + 0x6940693, 0x65b065a, 0x150014, 0x5d005c, 0x4bd04bc, 0x4d104d0, + 0x5d505d4, 0x1a101a0, 0x5110510, 0x5230522, 0x5350534, 0x5450544, + 0x5530552, 0x5610560, 0x5710570, 0x57f057e, 0x15b015a, 0x37d037c, + 0x3bb03ba, 0xffffffff, 0xffffffff, 0xffffffff, 0x5d2ffff, 0x5d805d3, + 0xffff05d9, 0xffffffff, 0x5e305e2, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x890087, 0x8d008b, 0x91008f, + 0x950093, 0x990097, 0x9d009b, 0xa1009f, 0xa500a3, 0xa900a7, 0xad00ab, + 0xb100af, 0xb500b3, 0xb900b7, 0xbd00bb, 0xc100bf, 0xc500c3, 0x48f0487, + 0x4a100cb, 0x4b500cf, 0x4c7ffff, 0xffffffff, 0xffffffff, 0x509ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2c402c3, 0x5d705d6, 0x5dd05dc, + 0x5e105e0, 0x5e705e6, 0x5e905e8, 0x5eb05ea, 0x5ef05ee, 0x5f105f0, + 0x5f505f4, 0x5f905f8, 0x3080307, 0x6150614, 0x6250624, 0x6330632, + 0x6410640, 0x64f064e, 0x6610660, 0x66f066e, 0x67e067d, 0x68c068b, + 0x69c069b, 0x6aa06a9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7350734, + 0x690068, 0x27e027d, 0x75f075e, 0x7770776, 0x390038f, 0x1f001e, + 0x7b107b0, 0x7c707c6, 0x2a202a1, 0x7e507e4, 0x6b806b7, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7590758, 0x75d075c, 0x7610760, 0x2d602d5, 0x2e002df, 0x2e602e5, + 0x2ee02ed, 0xffffffff, 0x7790778, 0x77d077c, 0x7810780, 0x30c030b, + 0x3140313, 0x3180317, 0x3220321, 0x7950794, 0x7970796, 0x7990798, + 0x79b079a, 0x37f037e, 0x3850384, 0x38e038d, 0x7a907a8, 0x7ab07aa, + 0x7ad07ac, 0x7af07ae, 0x7b307b2, 0x7b507b4, 0x7b907b8, 0x7bd07bc, + 0x7c107c0, 0x7c507c4, 0x7c907c8, 0x7cd07cc, 0x7cf07ce, 0x46f046e, + 0x47f047e, 0x4930492, 0x4a504a4, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x514ffff, 0x7e60515, 0x7e907e7, 0x7eb07ea, 0x7ed07ec, + 0x7ef07ee, 0x7f107f0, 0x7f307f2, 0xffffffff, 0x5f2ffff, 0x74905f3, + 0xffffffff, 0x8050804, 0x8070806, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x8190818, 0x81b081a, 0x81d081c, + 0x81f081e, 0x5f705f6, 0xffff05fb, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x756ffff, + 0x75a02c5, 0x2cd02cb, 0x76202cf, 0x2db06d2, 0x2e30719, 0x2e90448, + 0x2f107ca, 0x2f30774, 0x77a02f5, 0x77e02f9, 0x3050221, 0x30f007a, + 0xffff043b, 0xffffffff, 0xffffffff, 0x757ffff, 0x75b02c6, 0x2ce02cc, + 0x76302d0, 0x2dc06d3, 0x2e4071a, 0x2ea0449, 0x2f207cb, 0x2f40775, + 0x77b02f6, 0x77f02fa, 0x3060222, 0x310007b, 0xffff043c, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x85c0012, + 0x72005a, 0x32b0311, 0x11106b9, 0x2ab05e4, 0x2dd029d, 0x2ef085e, + 0x10b0606, 0x2d902a5, 0x4ca0289, 0x2b502ad, 0x2c902bd, 0x2eb0746, + 0x30902f7, 0x241031b, 0x38b02b1, 0x44a03b6, 0x442053a, 0x6d8044e, + 0x85004ae, 0x85d0013, 0x73005b, 0x32c0312, 0x11206ba, 0x2ac05e5, + 0x2de029e, 0x2f0085f, 0x10c0607, 0x2da02a6, 0x4cb028a, 0x2b602ae, + 0x2ca02be, 0x2ec0747, 0x30a02f8, 0x242031c, 0x38c02b2, 0x44b03b7, + 0x443053b, 0x6d9044f, 0x85104af, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //8832 bytes -enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xd40], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, 0x0, 0x190018, 0x1b001a, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x210020, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x240023, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260025, 0x280027, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a0000, 0x2b, 0x2d002c, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x310030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330032, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x20ffff, 0x240022, 0x280026, 0x2c002a, 0x30002e, 0x7810032, 0x390037, 0x3d003b, 0x41003f, 0x1b90043, 0x4a0048, 0x4e004c, 0x520050, 0xffff0054, 0xffffffff, 0xffffffff, 0x21ffff, 0x250023, 0x290027, 0x2d002b, 0x31002f, 0x7820033, 0x3a0038, 0x3e003c, 0x420040, 0x1ba0044, 0x4b0049, 0x4f004d, 0x530051, 0xffff0055, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x470ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xc800c6, 0xcc04c9, 0x14e04db, 0xd500d3, 0xd900d7, 0xdd00db, 0xe100df, 0xe500e3, 0xe900e7, 0xed00eb, 0xf100ef, 0xffff00f3, 0xf700f5, 0xfb00f9, 0xff00fd, 0x70f0101, 0xc900c7, 0xcd04ca, 0x14f04dc, 0xd600d4, 0xda00d8, 0xde00dc, 0xe200e0, 0xe600e4, 0xea00e8, 0xee00ec, 0xf200f0, 0xffff00f4, 0xf800f6, 0xfc00fa, 0x10000fe, 0x1b10102, 0x1190118, 0x11b011a, 0x11f011e, 0x1210120, 0x1230122, 0x1270126, 0x1290128, 0x12b012a, 0x12f012e, 0x1310130, 0x1330132, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, 0x1520151, 0x1540153, 0xffff0155, 0x1580157, 0x15a0159, 0x15c015b, 0x15dffff, 0x15f015e, 0x1630160, 0x1650164, 0x1670166, 0x1690168, 0x16b016a, 0x16d016c, 0x16f016e, 0x1720171, 0x1740173, 0x1760175, 0x1780177, 0x17a0179, 0x17c017b, 0x17e017d, 0x180017f, 0x1820181, 0x1840183, 0x1860185, 0x1880187, 0x18c018b, 0x190018f, 0x1940193, 0x1980197, 0x19c019b, 0x1a0019f, 0x1a401a3, 0x1a801a7, 0x1ac01ab, 0x1ae01ad, 0x1b001af, 0x1b301b2, 0x1b501b4, 0x1b701b6, 0x1bb01b8, 0x1bc029c, 0x1bf01be, 0x1c301c2, 0x1c601c4, 0x1c801c7, 0x1cc01ca, 0xffff01cd, 0x1d001ce, 0x1d401d2, 0x1d601d5, 0x24801d8, 0x1dc01da, 0x1df01de, 0xffff0294, 0x1e201e0, 0x1e60278, 0x1e901e8, 0x1eb01ea, 0x1ed01ec, 0x1f001ee, 0x1f201f1, 0xffffffff, 0x1f501f4, 0x1f801f6, 0x1fa01f9, 0x1fe01fc, 0x20001ff, 0x2020201, 0x2050204, 0xffffffff, 0x2070206, 0x24affff, 0xffffffff, 0xffffffff, 0x20c020b, 0x211020d, 0x2130212, 0x2180217, 0x21a0219, 0x21c021b, 0x21e021d, 0x220021f, 0x2220221, 0x2240223, 0x2260225, 0x2280227, 0x1cf0229, 0x22b022a, 0x22f022e, 0x2310230, 0x2330232, 0x2350234, 0x2370236, 0x2390238, 0x23b023a, 0x23d023c, 0x243023e, 0x2450244, 0x2470246, 0x24b0249, 0x24d024c, 0x2510250, 0x2530252, 0x2550254, 0x2570256, 0x2590258, 0x25b025a, 0x25d025c, 0x2610260, 0x2630262, 0x2650264, 0x2670266, 0x2690268, 0x26b026a, 0x26d026c, 0x26f026e, 0x2710270, 0x2730272, 0x2750274, 0x2770276, 0xffff0279, 0x27b027a, 0x27d027c, 0x27f027e, 0x2810280, 0x2850284, 0x2870286, 0x2890288, 0x28b028a, 0x28d028c, 0xffffffff, 0xffffffff, 0xffffffff, 0x2920290, 0x2950293, 0x2ec0296, 0x29804b3, 0x29d0299, 0x2a0029e, 0x2a302a2, 0x2a502a4, 0x2a702a6, 0x2a902a8, 0x2ad02ac, 0x497049b, 0x1bd02ca, 0xffff01c5, 0x1cb01c9, 0x1d1ffff, 0x1d3ffff, 0xffffffff, 0xffffffff, 0xffff01d7, 0x1d9ffff, 0x79affff, 0xffff0643, 0x1db01dd, 0x559ffff, 0xffffffff, 0x1e1ffff, 0x2c6ffff, 0xffff01e3, 0x1e7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2b4ffff, 0xffffffff, 0xffff01ef, 0x1f3ffff, 0xffffffff, 0xffffffff, 0x29f01f7, 0x1fd01fb, 0xffff02a1, 0xffffffff, 0xffffffff, 0xffff0203, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x8e4ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3450344, 0x3470346, 0xffffffff, 0x34b034a, 0xffffffff, 0x406ffff, 0x40a0408, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0350, 0x3560354, 0xffff0358, 0xffff035a, 0x35e035c, 0x3630902, 0x36803c2, 0x3fd036a, 0x371036f, 0x8e503f9, 0x37e03f3, 0x3830471, 0x3870385, 0x3f603cc, 0x3b5ffff, 0x3940392, 0x39903c9, 0x762039b, 0x3a203a0, 0x3550351, 0x3590357, 0x3640915, 0x36903c3, 0x3fe036b, 0x3720370, 0x8e603fa, 0x37f03f4, 0x3840472, 0x3880386, 0x3f703cd, 0x3b703b6, 0x3950393, 0x39a03ca, 0x763039c, 0x3a303a1, 0x35d035b, 0x3c0035f, 0x3fb03c4, 0xffffffff, 0x3cbffff, 0x3c103ce, 0x3d003cf, 0x3d403d3, 0x3d603d5, 0x3d803d7, 0x3da03d9, 0x3de03dd, 0x3e003df, 0x3e403e3, 0x3e803e7, 0x3ec03eb, 0x3ee03ed, 0x3f203f1, 0x3f803f5, 0xffff0402, 0x3ff03fc, 0x400ffff, 0x4030401, 0x4050404, 0x407ffff, 0x40b0409, 0x40e040c, 0x4120410, 0x4160414, 0x41a0418, 0x41e041c, 0x4220420, 0x4260424, 0x42a0428, 0x42e042c, 0x4320430, 0x4360434, 0x43a0438, 0x43e043c, 0x4420440, 0x4460444, 0x44a0448, 0x44e044c, 0x4520450, 0x4560454, 0x45a0458, 0x45e045c, 0x4620460, 0x4660464, 0x46a0468, 0x42f042d, 0x4330431, 0x4370435, 0x43b0439, 0x43f043d, 0x4430441, 0x4470445, 0x44b0449, 0x44f044d, 0x4530451, 0x4570455, 0x45b0459, 0x45f045d, 0x4630461, 0x4670465, 0x46b0469, 0x40f040d, 0x4130411, 0x4170415, 0x41b0419, 0x41f041d, 0x4230421, 0x4270425, 0x42b0429, 0x4820481, 0x4840483, 0x4880487, 0x48a0489, 0x48e048d, 0x4920491, 0x4960495, 0x49a0499, 0x49e049d, 0x4a204a1, 0x4a404a3, 0x4a804a7, 0x4aa04a9, 0x4ac04ab, 0x4ae04ad, 0x4b204b1, 0x4b604b5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4c604c5, 0x4c804c7, 0x4cc04cb, 0x4ce04cd, 0x4d004cf, 0x4d404d3, 0x4d804d7, 0x4da04d9, 0x4de04dd, 0x4e204e1, 0x4e404e3, 0x4e804e7, 0x4ea04e9, 0x4ec04eb, 0x4f004ef, 0x4f204f1, 0x4f604f5, 0x4fa04f9, 0x4fe04fd, 0x50004ff, 0x5040503, 0x5060505, 0x5080507, 0x50c050b, 0x510050f, 0x5140513, 0x5180517, 0x51d051b, 0x521051e, 0x5250522, 0x5290526, 0x52d052a, 0x531052e, 0x5350532, 0x51c0536, 0x53c053b, 0x53e053d, 0x540053f, 0x5440543, 0x5480547, 0x54a0549, 0x54e054d, 0x550054f, 0x5520551, 0x5560555, 0x5580557, 0x55c055b, 0x55e055d, 0x560055f, 0x5640563, 0x5680567, 0x56a0569, 0x56e056d, 0x570056f, 0x5720571, 0x5740573, 0x5780577, 0x57a0579, 0x57c057b, 0x57e057d, 0x5820581, 0x5840583, 0x5880587, 0x58a0589, 0x58c058b, 0x58e058d, 0x5920591, 0x5940593, 0x5980597, 0x59a0599, 0x59c059b, 0x59e059d, 0x5a205a1, 0x5a605a5, 0x5aa05a9, 0x5ac05ab, 0x5ae05ad, 0x5b005af, 0x5b405b3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5b9ffff, 0x5bd05bb, 0x5c105bf, 0x5c505c3, 0x5c905c7, 0x5cd05cb, 0x5d105cf, 0x5d505d3, 0x5d905d7, 0x5dd05db, 0x5e105df, 0x5e505e3, 0x5e905e7, 0x5ed05eb, 0x5f105ef, 0x5f505f3, 0x5f905f7, 0x5fd05fb, 0x60105ff, 0xffff0603, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5baffff, 0x5be05bc, 0x5c205c0, 0x5c605c4, 0x5ca05c8, 0x5ce05cc, 0x5d205d0, 0x5d605d4, 0x5da05d8, 0x5de05dc, 0x5e205e0, 0x5e605e4, 0x5ea05e8, 0x5ee05ec, 0x5f205f0, 0x5f605f4, 0x5fa05f8, 0x5fe05fc, 0x6020600, 0x6130604, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x880086, 0x8c008a, 0x90008e, 0x940092, 0x980096, 0x9c009a, 0xa0009e, 0xa400a2, 0xa800a6, 0xac00aa, 0xb000ae, 0xb400b2, 0xb800b6, 0xbc00ba, 0xc000be, 0xc400c2, 0x4bf04b7, 0x4d100ca, 0x4e500ce, 0x4f7ffff, 0xffffffff, 0xffffffff, 0x539ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x83affff, 0xffffffff, 0x485ffff, 0xffffffff, 0x6460645, 0x6480647, 0x64a0649, 0x64e064d, 0x6520651, 0x6540653, 0x6580657, 0x65a0659, 0x65c065b, 0x660065f, 0x6620661, 0x6660665, 0x6680667, 0x66a0669, 0x66c066b, 0x670066f, 0x6720671, 0x6740673, 0x6760675, 0x6780677, 0x67a0679, 0x67e067d, 0x680067f, 0x6820681, 0x6840683, 0x6860685, 0x6880687, 0x68c068b, 0x68e068d, 0x690068f, 0x6920691, 0x6940693, 0x6960695, 0x69a0699, 0x69c069b, 0x69e069d, 0x6a206a1, 0x6a606a5, 0x6a806a7, 0x6ac06ab, 0x6ae06ad, 0x6b006af, 0x6b206b1, 0x6b406b3, 0x6b606b5, 0x6ba06b9, 0x6bc06bb, 0x6be06bd, 0x70d070c, 0x6c306c2, 0x6c706c6, 0x6cb06ca, 0x6cd06cc, 0x6cf06ce, 0x6d106d0, 0x6d306d2, 0x6d506d4, 0x6d906d8, 0x6db06da, 0x6dd06dc, 0x6e106e0, 0x6e306e2, 0x6e506e4, 0x6e906e8, 0x6eb06ea, 0x6ed06ec, 0x6ef06ee, 0x6f106f0, 0x6f306f2, 0x6f706f6, 0x6f906f8, 0x6fb06fa, 0x6fd06fc, 0x6ff06fe, 0x7010700, 0x7040702, 0x7080706, 0x70e070a, 0xffffffff, 0xffff0710, 0x7130712, 0x7150714, 0x7170716, 0x71b071a, 0x71d071c, 0x71f071e, 0x7210720, 0x7230722, 0x7270726, 0x7290728, 0x72d072c, 0x72f072e, 0x7310730, 0x7330732, 0x7350734, 0x7370736, 0x73b073a, 0x73d073c, 0x7410740, 0x7430742, 0x7450744, 0x7470746, 0x7490748, 0x74b074a, 0x74d074c, 0x74f074e, 0x7510750, 0x7530752, 0x7550754, 0x7570756, 0x7590758, 0x75b075a, 0x75d075c, 0x75f075e, 0x7610760, 0x7660765, 0x7680767, 0x76a0769, 0x76e076d, 0x770076f, 0x7720771, 0x7740773, 0x7760775, 0x7780777, 0x77a0779, 0x77c077b, 0x780077f, 0x7850784, 0x78a0788, 0x78e078c, 0x7920790, 0x7960794, 0x78b0789, 0x78f078d, 0x7930791, 0x7970795, 0x79e079c, 0x7a207a0, 0x7a607a4, 0xffffffff, 0x79f079d, 0x7a307a1, 0x7a707a5, 0xffffffff, 0x7b807b6, 0x7bc07ba, 0x7c007be, 0x7c407c2, 0x7b907b7, 0x7bd07bb, 0x7c107bf, 0x7c507c3, 0x7d807d6, 0x7dc07da, 0x7e007de, 0x7e407e2, 0x7d907d7, 0x7dd07db, 0x7e107df, 0x7e507e3, 0x7f007ee, 0x7f407f2, 0x7f807f6, 0xffffffff, 0x7f107ef, 0x7f507f3, 0x7f907f7, 0xffffffff, 0x80807fc, 0x80c07fe, 0x8100800, 0x8140804, 0x809ffff, 0x80dffff, 0x811ffff, 0x815ffff, 0x8240822, 0x8280826, 0x82c082a, 0x830082e, 0x8250823, 0x8290827, 0x82d082b, 0x831082f, 0x8df08dd, 0x8f708f5, 0x8fb08f9, 0x90f090d, 0x9370935, 0x9240922, 0x93b0939, 0xffffffff, 0x8590856, 0x85f085c, 0x8650862, 0x86b0868, 0x85a0857, 0x860085d, 0x8660863, 0x86c0869, 0x8890886, 0x88f088c, 0x8950892, 0x89b0898, 0x88a0887, 0x890088d, 0x8960893, 0x89c0899, 0x8b908b6, 0x8bf08bc, 0x8c508c2, 0x8cb08c8, 0x8ba08b7, 0x8c008bd, 0x8c608c3, 0x8cc08c9, 0x8db08d9, 0x8e108ce, 0xffff08d3, 0x8d708d5, 0x8dc08da, 0x8e008de, 0xffff08e2, 0xffff08e7, 0xffffffff, 0x8fd08e8, 0xffff08ed, 0x8f308f1, 0x8f808f6, 0x8fc08fa, 0xffff08fe, 0xffffffff, 0x90b0909, 0x9030900, 0xffffffff, 0x9070905, 0x90c090a, 0x910090e, 0xffffffff, 0xffffffff, 0x920091e, 0x9160913, 0x9260918, 0x91c091a, 0x921091f, 0x9250923, 0xffff0927, 0xffffffff, 0xffffffff, 0x93d092a, 0xffff092f, 0x9330931, 0x9380936, 0x93c093a, 0xffff093e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0764, 0xffffffff, 0x1500783, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0561, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0562, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x18902b0, 0x18d02c2, 0x19102d6, 0x195005e, 0x1990064, 0x19d006c, 0x1a10076, 0x1a5007e, 0x18a02b1, 0x18e02c3, 0x19202d7, 0x196005f, 0x19a0065, 0x19e006d, 0x1a20077, 0x1a6007f, 0xffffffff, 0x1c0ffff, 0xffff01c1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5090475, 0x50d0477, 0x511047d, 0x515048f, 0x51904a5, 0x2e80940, 0x2051f, 0x7180523, 0x6e0527, 0x3a4052b, 0x110052f, 0x6630533, 0x54b0537, 0x50a0476, 0x50e0478, 0x512047e, 0x5160490, 0x51a04a6, 0x2e90941, 0x30520, 0x7190524, 0x6f0528, 0x3a5052c, 0x1110530, 0x6640534, 0x54c0538, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x40000, 0xa0008, 0xe000c, 0x160010, 0x1a0018, 0x2ce001c, 0x2e602dc, 0x560308, 0x600058, 0x660062, 0x70006a, 0x780074, 0x80007c, 0x2aa0082, 0x60832, 0x64b0084, 0x2b805b5, 0x60d0609, 0x629061d, 0x1080106, 0x112010a, 0x11c0116, 0x12c0124, 0xffff0134, 0x50001, 0xb0009, 0xf000d, 0x170011, 0x1b0019, 0x2cf001d, 0x2e702dd, 0x570309, 0x610059, 0x670063, 0x71006b, 0x790075, 0x81007d, 0x2ab0083, 0x70833, 0x64c0085, 0x2b905b6, 0x60e060a, 0x62a061e, 0x1090107, 0x113010b, 0x11d0117, 0x12d0125, 0xffff0135, 0x4f404f3, 0x486055a, 0x29102b5, 0x48b0297, 0x2ba048c, 0x49302bb, 0x4980494, 0x49c02c7, 0xffff02cb, 0x2cd02cc, 0x2d4ffff, 0xffff02d5, 0xffffffff, 0xffffffff, 0xffffffff, 0x4b402ed, 0x2f902f8, 0x4ba04b9, 0x4bc04bb, 0x4be04bd, 0x4c204c1, 0x3250324, 0x32b032a, 0x3350334, 0x33d033c, 0x3410340, 0x34d034c, 0x34f034e, 0x3230322, 0x73f073e, 0x3ae03ad, 0x3b003af, 0x3b403b3, 0x3bf03be, 0x3d203d1, 0x3e203e1, 0x5a405a3, 0x6060605, 0x61a0619, 0x6320631, 0x6560655, 0x6a0069f, 0x46f046e, 0x7390738, 0x77e077d, 0x7d507d4, 0x8350834, 0x6df06de, 0x6a406a3, 0x150014, 0x5d005c, 0x4ee04ed, 0x5020501, 0x6120611, 0x1aa01a9, 0x5420541, 0x5540553, 0x5660565, 0x5760575, 0x5860585, 0x5960595, 0x5a805a7, 0x5b805b7, 0x1620161, 0x3a703a6, 0x3ea03e9, 0xffffffff, 0xffffffff, 0xffffffff, 0x60fffff, 0x6170610, 0xffff0618, 0xffffffff, 0x6240623, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x890087, 0x8d008b, 0x91008f, 0x950093, 0x990097, 0x9d009b, 0xa1009f, 0xa500a3, 0xa900a7, 0xad00ab, 0xb100af, 0xb500b3, 0xb900b7, 0xbd00bb, 0xc100bf, 0xc500c3, 0x4c004b8, 0x4d200cb, 0x4e600cf, 0x4f8ffff, 0xffffffff, 0xffffffff, 0x53affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d902d8, 0x6160615, 0x61c061b, 0x6220621, 0x6280627, 0x1e501e4, 0x62e062d, 0x6340633, 0x6380637, 0x63e063d, 0x6420641, 0x32f032e, 0x65e065d, 0x66e066d, 0x67c067b, 0x68a0689, 0x6980697, 0x6aa06a9, 0x6b806b7, 0x6c906c8, 0x6d706d6, 0x6e706e6, 0x6f506f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7870786, 0x690068, 0x28f028e, 0x7b107b0, 0x7c907c8, 0x3bd03bc, 0x1f001e, 0x8030802, 0x8190818, 0x2b302b2, 0x8370836, 0x2d302d2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ab07aa, 0x7af07ae, 0x7b307b2, 0x2eb02ea, 0x2f502f4, 0x3070306, 0x3110310, 0xffffffff, 0x7cb07ca, 0x7cf07ce, 0x7d307d2, 0x3330332, 0x33b033a, 0x33f033e, 0x3490348, 0x7e707e6, 0x7e907e8, 0x7eb07ea, 0x7ed07ec, 0x3ac03ab, 0x3b203b1, 0x3bb03ba, 0x7fb07fa, 0x3dc03db, 0x3f003ef, 0x620061f, 0x2830282, 0x8070806, 0x80b080a, 0x80f080e, 0x8130812, 0x8170816, 0x81b081a, 0x81f081e, 0x8210820, 0x4a0049f, 0x4b004af, 0x4c404c3, 0x4d604d5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x545ffff, 0x8380546, 0x83b0839, 0x83d083c, 0x580057f, 0x590058f, 0x5a0059f, 0x5b205b1, 0xffffffff, 0x63bffff, 0x79b063c, 0xffffffff, 0x6080607, 0x60c060b, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x62c062b, 0x630062f, 0x6360635, 0x63a0639, 0x640063f, 0xffff0644, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x25e02f6, 0x2fc02fa, 0x30302fe, 0xffff0304, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x30cffff, 0x2c0030e, 0x3140312, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7a8ffff, 0x7ac02da, 0x2e202e0, 0x7b402e4, 0x2f00724, 0x144076b, 0x30a0479, 0x318081c, 0x31a07c6, 0x7cc031c, 0x7d00320, 0x32c022c, 0x336007a, 0xffff046c, 0xffffffff, 0xffffffff, 0x7a9ffff, 0x7ad02db, 0x2e302e1, 0x7b502e5, 0x2f10725, 0x145076c, 0x30b047a, 0x319081d, 0x31b07c7, 0x7cd031d, 0x7d10321, 0x32d022d, 0x337007b, 0xffff046d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x9280012, 0x72005a, 0x3520338, 0x114010c, 0x2bc0625, 0x2f202ae, 0x31608ef, 0x10e064f, 0x2ee02b6, 0x4fb029a, 0x2c802be, 0x2de02d0, 0x47f0798, 0x330031e, 0x24e0342, 0x3b802c4, 0x47b03e5, 0x473056b, 0x72a06c4, 0x91104df, 0x9290013, 0x73005b, 0x3530339, 0x115010d, 0x2bd0626, 0x2f302af, 0x31708f0, 0x10f0650, 0x2ef02b7, 0x4fc029b, 0x2c902bf, 0x2df02d1, 0x4800799, 0x331031f, 0x24f0343, 0x3b902c5, 0x47c03e6, 0x474056c, 0x72b06c5, 0x91204e0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, 0x200], + [0x100, 0x380, 0xd40], [0x2020100, 0x4020302, 0x2020205, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, + 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x160015, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x170000, + 0x0, 0x190018, 0x1b001a, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x210020, 0x22, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x240023, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260025, 0x280027, 0x29, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2a0000, 0x2b, 0x2d002c, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x310030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x330032, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x20ffff, 0x240022, 0x280026, 0x2c002a, 0x30002e, + 0x7810032, 0x390037, 0x3d003b, 0x41003f, 0x1b90043, 0x4a0048, 0x4e004c, + 0x520050, 0xffff0054, 0xffffffff, 0xffffffff, 0x21ffff, 0x250023, + 0x290027, 0x2d002b, 0x31002f, 0x7820033, 0x3a0038, 0x3e003c, 0x420040, + 0x1ba0044, 0x4b0049, 0x4f004d, 0x530051, 0xffff0055, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x470ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xc800c6, 0xcc04c9, 0x14e04db, + 0xd500d3, 0xd900d7, 0xdd00db, 0xe100df, 0xe500e3, 0xe900e7, 0xed00eb, + 0xf100ef, 0xffff00f3, 0xf700f5, 0xfb00f9, 0xff00fd, 0x70f0101, + 0xc900c7, 0xcd04ca, 0x14f04dc, 0xd600d4, 0xda00d8, 0xde00dc, 0xe200e0, + 0xe600e4, 0xea00e8, 0xee00ec, 0xf200f0, 0xffff00f4, 0xf800f6, 0xfc00fa, + 0x10000fe, 0x1b10102, 0x1190118, 0x11b011a, 0x11f011e, 0x1210120, + 0x1230122, 0x1270126, 0x1290128, 0x12b012a, 0x12f012e, 0x1310130, + 0x1330132, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, + 0x1410140, 0x1430142, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, + 0x1520151, 0x1540153, 0xffff0155, 0x1580157, 0x15a0159, 0x15c015b, + 0x15dffff, 0x15f015e, 0x1630160, 0x1650164, 0x1670166, 0x1690168, + 0x16b016a, 0x16d016c, 0x16f016e, 0x1720171, 0x1740173, 0x1760175, + 0x1780177, 0x17a0179, 0x17c017b, 0x17e017d, 0x180017f, 0x1820181, + 0x1840183, 0x1860185, 0x1880187, 0x18c018b, 0x190018f, 0x1940193, + 0x1980197, 0x19c019b, 0x1a0019f, 0x1a401a3, 0x1a801a7, 0x1ac01ab, + 0x1ae01ad, 0x1b001af, 0x1b301b2, 0x1b501b4, 0x1b701b6, 0x1bb01b8, + 0x1bc029c, 0x1bf01be, 0x1c301c2, 0x1c601c4, 0x1c801c7, 0x1cc01ca, + 0xffff01cd, 0x1d001ce, 0x1d401d2, 0x1d601d5, 0x24801d8, 0x1dc01da, + 0x1df01de, 0xffff0294, 0x1e201e0, 0x1e60278, 0x1e901e8, 0x1eb01ea, + 0x1ed01ec, 0x1f001ee, 0x1f201f1, 0xffffffff, 0x1f501f4, 0x1f801f6, + 0x1fa01f9, 0x1fe01fc, 0x20001ff, 0x2020201, 0x2050204, 0xffffffff, + 0x2070206, 0x24affff, 0xffffffff, 0xffffffff, 0x20c020b, 0x211020d, + 0x2130212, 0x2180217, 0x21a0219, 0x21c021b, 0x21e021d, 0x220021f, + 0x2220221, 0x2240223, 0x2260225, 0x2280227, 0x1cf0229, 0x22b022a, + 0x22f022e, 0x2310230, 0x2330232, 0x2350234, 0x2370236, 0x2390238, + 0x23b023a, 0x23d023c, 0x243023e, 0x2450244, 0x2470246, 0x24b0249, + 0x24d024c, 0x2510250, 0x2530252, 0x2550254, 0x2570256, 0x2590258, + 0x25b025a, 0x25d025c, 0x2610260, 0x2630262, 0x2650264, 0x2670266, + 0x2690268, 0x26b026a, 0x26d026c, 0x26f026e, 0x2710270, 0x2730272, + 0x2750274, 0x2770276, 0xffff0279, 0x27b027a, 0x27d027c, 0x27f027e, + 0x2810280, 0x2850284, 0x2870286, 0x2890288, 0x28b028a, 0x28d028c, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2920290, 0x2950293, 0x2ec0296, + 0x29804b3, 0x29d0299, 0x2a0029e, 0x2a302a2, 0x2a502a4, 0x2a702a6, + 0x2a902a8, 0x2ad02ac, 0x497049b, 0x1bd02ca, 0xffff01c5, 0x1cb01c9, + 0x1d1ffff, 0x1d3ffff, 0xffffffff, 0xffffffff, 0xffff01d7, 0x1d9ffff, + 0x79affff, 0xffff0643, 0x1db01dd, 0x559ffff, 0xffffffff, 0x1e1ffff, + 0x2c6ffff, 0xffff01e3, 0x1e7ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x2b4ffff, 0xffffffff, 0xffff01ef, 0x1f3ffff, 0xffffffff, 0xffffffff, + 0x29f01f7, 0x1fd01fb, 0xffff02a1, 0xffffffff, 0xffffffff, 0xffff0203, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x8e4ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3450344, 0x3470346, + 0xffffffff, 0x34b034a, 0xffffffff, 0x406ffff, 0x40a0408, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0350, 0x3560354, 0xffff0358, + 0xffff035a, 0x35e035c, 0x3630902, 0x36803c2, 0x3fd036a, 0x371036f, + 0x8e503f9, 0x37e03f3, 0x3830471, 0x3870385, 0x3f603cc, 0x3b5ffff, + 0x3940392, 0x39903c9, 0x762039b, 0x3a203a0, 0x3550351, 0x3590357, + 0x3640915, 0x36903c3, 0x3fe036b, 0x3720370, 0x8e603fa, 0x37f03f4, + 0x3840472, 0x3880386, 0x3f703cd, 0x3b703b6, 0x3950393, 0x39a03ca, + 0x763039c, 0x3a303a1, 0x35d035b, 0x3c0035f, 0x3fb03c4, 0xffffffff, + 0x3cbffff, 0x3c103ce, 0x3d003cf, 0x3d403d3, 0x3d603d5, 0x3d803d7, + 0x3da03d9, 0x3de03dd, 0x3e003df, 0x3e403e3, 0x3e803e7, 0x3ec03eb, + 0x3ee03ed, 0x3f203f1, 0x3f803f5, 0xffff0402, 0x3ff03fc, 0x400ffff, + 0x4030401, 0x4050404, 0x407ffff, 0x40b0409, 0x40e040c, 0x4120410, + 0x4160414, 0x41a0418, 0x41e041c, 0x4220420, 0x4260424, 0x42a0428, + 0x42e042c, 0x4320430, 0x4360434, 0x43a0438, 0x43e043c, 0x4420440, + 0x4460444, 0x44a0448, 0x44e044c, 0x4520450, 0x4560454, 0x45a0458, + 0x45e045c, 0x4620460, 0x4660464, 0x46a0468, 0x42f042d, 0x4330431, + 0x4370435, 0x43b0439, 0x43f043d, 0x4430441, 0x4470445, 0x44b0449, + 0x44f044d, 0x4530451, 0x4570455, 0x45b0459, 0x45f045d, 0x4630461, + 0x4670465, 0x46b0469, 0x40f040d, 0x4130411, 0x4170415, 0x41b0419, + 0x41f041d, 0x4230421, 0x4270425, 0x42b0429, 0x4820481, 0x4840483, + 0x4880487, 0x48a0489, 0x48e048d, 0x4920491, 0x4960495, 0x49a0499, + 0x49e049d, 0x4a204a1, 0x4a404a3, 0x4a804a7, 0x4aa04a9, 0x4ac04ab, + 0x4ae04ad, 0x4b204b1, 0x4b604b5, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x4c604c5, 0x4c804c7, 0x4cc04cb, 0x4ce04cd, 0x4d004cf, + 0x4d404d3, 0x4d804d7, 0x4da04d9, 0x4de04dd, 0x4e204e1, 0x4e404e3, + 0x4e804e7, 0x4ea04e9, 0x4ec04eb, 0x4f004ef, 0x4f204f1, 0x4f604f5, + 0x4fa04f9, 0x4fe04fd, 0x50004ff, 0x5040503, 0x5060505, 0x5080507, + 0x50c050b, 0x510050f, 0x5140513, 0x5180517, 0x51d051b, 0x521051e, + 0x5250522, 0x5290526, 0x52d052a, 0x531052e, 0x5350532, 0x51c0536, + 0x53c053b, 0x53e053d, 0x540053f, 0x5440543, 0x5480547, 0x54a0549, + 0x54e054d, 0x550054f, 0x5520551, 0x5560555, 0x5580557, 0x55c055b, + 0x55e055d, 0x560055f, 0x5640563, 0x5680567, 0x56a0569, 0x56e056d, + 0x570056f, 0x5720571, 0x5740573, 0x5780577, 0x57a0579, 0x57c057b, + 0x57e057d, 0x5820581, 0x5840583, 0x5880587, 0x58a0589, 0x58c058b, + 0x58e058d, 0x5920591, 0x5940593, 0x5980597, 0x59a0599, 0x59c059b, + 0x59e059d, 0x5a205a1, 0x5a605a5, 0x5aa05a9, 0x5ac05ab, 0x5ae05ad, + 0x5b005af, 0x5b405b3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x5b9ffff, 0x5bd05bb, 0x5c105bf, 0x5c505c3, 0x5c905c7, 0x5cd05cb, + 0x5d105cf, 0x5d505d3, 0x5d905d7, 0x5dd05db, 0x5e105df, 0x5e505e3, + 0x5e905e7, 0x5ed05eb, 0x5f105ef, 0x5f505f3, 0x5f905f7, 0x5fd05fb, + 0x60105ff, 0xffff0603, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x5baffff, 0x5be05bc, 0x5c205c0, 0x5c605c4, 0x5ca05c8, 0x5ce05cc, + 0x5d205d0, 0x5d605d4, 0x5da05d8, 0x5de05dc, 0x5e205e0, 0x5e605e4, + 0x5ea05e8, 0x5ee05ec, 0x5f205f0, 0x5f605f4, 0x5fa05f8, 0x5fe05fc, + 0x6020600, 0x6130604, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x880086, 0x8c008a, + 0x90008e, 0x940092, 0x980096, 0x9c009a, 0xa0009e, 0xa400a2, 0xa800a6, + 0xac00aa, 0xb000ae, 0xb400b2, 0xb800b6, 0xbc00ba, 0xc000be, 0xc400c2, + 0x4bf04b7, 0x4d100ca, 0x4e500ce, 0x4f7ffff, 0xffffffff, 0xffffffff, + 0x539ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x83affff, 0xffffffff, 0x485ffff, 0xffffffff, 0x6460645, 0x6480647, + 0x64a0649, 0x64e064d, 0x6520651, 0x6540653, 0x6580657, 0x65a0659, + 0x65c065b, 0x660065f, 0x6620661, 0x6660665, 0x6680667, 0x66a0669, + 0x66c066b, 0x670066f, 0x6720671, 0x6740673, 0x6760675, 0x6780677, + 0x67a0679, 0x67e067d, 0x680067f, 0x6820681, 0x6840683, 0x6860685, + 0x6880687, 0x68c068b, 0x68e068d, 0x690068f, 0x6920691, 0x6940693, + 0x6960695, 0x69a0699, 0x69c069b, 0x69e069d, 0x6a206a1, 0x6a606a5, + 0x6a806a7, 0x6ac06ab, 0x6ae06ad, 0x6b006af, 0x6b206b1, 0x6b406b3, + 0x6b606b5, 0x6ba06b9, 0x6bc06bb, 0x6be06bd, 0x70d070c, 0x6c306c2, + 0x6c706c6, 0x6cb06ca, 0x6cd06cc, 0x6cf06ce, 0x6d106d0, 0x6d306d2, + 0x6d506d4, 0x6d906d8, 0x6db06da, 0x6dd06dc, 0x6e106e0, 0x6e306e2, + 0x6e506e4, 0x6e906e8, 0x6eb06ea, 0x6ed06ec, 0x6ef06ee, 0x6f106f0, + 0x6f306f2, 0x6f706f6, 0x6f906f8, 0x6fb06fa, 0x6fd06fc, 0x6ff06fe, + 0x7010700, 0x7040702, 0x7080706, 0x70e070a, 0xffffffff, 0xffff0710, + 0x7130712, 0x7150714, 0x7170716, 0x71b071a, 0x71d071c, 0x71f071e, + 0x7210720, 0x7230722, 0x7270726, 0x7290728, 0x72d072c, 0x72f072e, + 0x7310730, 0x7330732, 0x7350734, 0x7370736, 0x73b073a, 0x73d073c, + 0x7410740, 0x7430742, 0x7450744, 0x7470746, 0x7490748, 0x74b074a, + 0x74d074c, 0x74f074e, 0x7510750, 0x7530752, 0x7550754, 0x7570756, + 0x7590758, 0x75b075a, 0x75d075c, 0x75f075e, 0x7610760, 0x7660765, + 0x7680767, 0x76a0769, 0x76e076d, 0x770076f, 0x7720771, 0x7740773, + 0x7760775, 0x7780777, 0x77a0779, 0x77c077b, 0x780077f, 0x7850784, + 0x78a0788, 0x78e078c, 0x7920790, 0x7960794, 0x78b0789, 0x78f078d, + 0x7930791, 0x7970795, 0x79e079c, 0x7a207a0, 0x7a607a4, 0xffffffff, + 0x79f079d, 0x7a307a1, 0x7a707a5, 0xffffffff, 0x7b807b6, 0x7bc07ba, + 0x7c007be, 0x7c407c2, 0x7b907b7, 0x7bd07bb, 0x7c107bf, 0x7c507c3, + 0x7d807d6, 0x7dc07da, 0x7e007de, 0x7e407e2, 0x7d907d7, 0x7dd07db, + 0x7e107df, 0x7e507e3, 0x7f007ee, 0x7f407f2, 0x7f807f6, 0xffffffff, + 0x7f107ef, 0x7f507f3, 0x7f907f7, 0xffffffff, 0x80807fc, 0x80c07fe, + 0x8100800, 0x8140804, 0x809ffff, 0x80dffff, 0x811ffff, 0x815ffff, + 0x8240822, 0x8280826, 0x82c082a, 0x830082e, 0x8250823, 0x8290827, + 0x82d082b, 0x831082f, 0x8df08dd, 0x8f708f5, 0x8fb08f9, 0x90f090d, + 0x9370935, 0x9240922, 0x93b0939, 0xffffffff, 0x8590856, 0x85f085c, + 0x8650862, 0x86b0868, 0x85a0857, 0x860085d, 0x8660863, 0x86c0869, + 0x8890886, 0x88f088c, 0x8950892, 0x89b0898, 0x88a0887, 0x890088d, + 0x8960893, 0x89c0899, 0x8b908b6, 0x8bf08bc, 0x8c508c2, 0x8cb08c8, + 0x8ba08b7, 0x8c008bd, 0x8c608c3, 0x8cc08c9, 0x8db08d9, 0x8e108ce, + 0xffff08d3, 0x8d708d5, 0x8dc08da, 0x8e008de, 0xffff08e2, 0xffff08e7, + 0xffffffff, 0x8fd08e8, 0xffff08ed, 0x8f308f1, 0x8f808f6, 0x8fc08fa, + 0xffff08fe, 0xffffffff, 0x90b0909, 0x9030900, 0xffffffff, 0x9070905, + 0x90c090a, 0x910090e, 0xffffffff, 0xffffffff, 0x920091e, 0x9160913, + 0x9260918, 0x91c091a, 0x921091f, 0x9250923, 0xffff0927, 0xffffffff, + 0xffffffff, 0x93d092a, 0xffff092f, 0x9330931, 0x9380936, 0x93c093a, + 0xffff093e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0764, 0xffffffff, 0x1500783, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0561, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0562, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x18902b0, 0x18d02c2, 0x19102d6, 0x195005e, + 0x1990064, 0x19d006c, 0x1a10076, 0x1a5007e, 0x18a02b1, 0x18e02c3, + 0x19202d7, 0x196005f, 0x19a0065, 0x19e006d, 0x1a20077, 0x1a6007f, + 0xffffffff, 0x1c0ffff, 0xffff01c1, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5090475, + 0x50d0477, 0x511047d, 0x515048f, 0x51904a5, 0x2e80940, 0x2051f, + 0x7180523, 0x6e0527, 0x3a4052b, 0x110052f, 0x6630533, 0x54b0537, + 0x50a0476, 0x50e0478, 0x512047e, 0x5160490, 0x51a04a6, 0x2e90941, + 0x30520, 0x7190524, 0x6f0528, 0x3a5052c, 0x1110530, 0x6640534, + 0x54c0538, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x40000, 0xa0008, 0xe000c, 0x160010, 0x1a0018, 0x2ce001c, 0x2e602dc, + 0x560308, 0x600058, 0x660062, 0x70006a, 0x780074, 0x80007c, 0x2aa0082, + 0x60832, 0x64b0084, 0x2b805b5, 0x60d0609, 0x629061d, 0x1080106, + 0x112010a, 0x11c0116, 0x12c0124, 0xffff0134, 0x50001, 0xb0009, 0xf000d, + 0x170011, 0x1b0019, 0x2cf001d, 0x2e702dd, 0x570309, 0x610059, 0x670063, + 0x71006b, 0x790075, 0x81007d, 0x2ab0083, 0x70833, 0x64c0085, 0x2b905b6, + 0x60e060a, 0x62a061e, 0x1090107, 0x113010b, 0x11d0117, 0x12d0125, + 0xffff0135, 0x4f404f3, 0x486055a, 0x29102b5, 0x48b0297, 0x2ba048c, + 0x49302bb, 0x4980494, 0x49c02c7, 0xffff02cb, 0x2cd02cc, 0x2d4ffff, + 0xffff02d5, 0xffffffff, 0xffffffff, 0xffffffff, 0x4b402ed, 0x2f902f8, + 0x4ba04b9, 0x4bc04bb, 0x4be04bd, 0x4c204c1, 0x3250324, 0x32b032a, + 0x3350334, 0x33d033c, 0x3410340, 0x34d034c, 0x34f034e, 0x3230322, + 0x73f073e, 0x3ae03ad, 0x3b003af, 0x3b403b3, 0x3bf03be, 0x3d203d1, + 0x3e203e1, 0x5a405a3, 0x6060605, 0x61a0619, 0x6320631, 0x6560655, + 0x6a0069f, 0x46f046e, 0x7390738, 0x77e077d, 0x7d507d4, 0x8350834, + 0x6df06de, 0x6a406a3, 0x150014, 0x5d005c, 0x4ee04ed, 0x5020501, + 0x6120611, 0x1aa01a9, 0x5420541, 0x5540553, 0x5660565, 0x5760575, + 0x5860585, 0x5960595, 0x5a805a7, 0x5b805b7, 0x1620161, 0x3a703a6, + 0x3ea03e9, 0xffffffff, 0xffffffff, 0xffffffff, 0x60fffff, 0x6170610, + 0xffff0618, 0xffffffff, 0x6240623, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x890087, 0x8d008b, 0x91008f, + 0x950093, 0x990097, 0x9d009b, 0xa1009f, 0xa500a3, 0xa900a7, 0xad00ab, + 0xb100af, 0xb500b3, 0xb900b7, 0xbd00bb, 0xc100bf, 0xc500c3, 0x4c004b8, + 0x4d200cb, 0x4e600cf, 0x4f8ffff, 0xffffffff, 0xffffffff, 0x53affff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2d902d8, 0x6160615, 0x61c061b, + 0x6220621, 0x6280627, 0x1e501e4, 0x62e062d, 0x6340633, 0x6380637, + 0x63e063d, 0x6420641, 0x32f032e, 0x65e065d, 0x66e066d, 0x67c067b, + 0x68a0689, 0x6980697, 0x6aa06a9, 0x6b806b7, 0x6c906c8, 0x6d706d6, + 0x6e706e6, 0x6f506f4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7870786, + 0x690068, 0x28f028e, 0x7b107b0, 0x7c907c8, 0x3bd03bc, 0x1f001e, + 0x8030802, 0x8190818, 0x2b302b2, 0x8370836, 0x2d302d2, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7ab07aa, 0x7af07ae, 0x7b307b2, 0x2eb02ea, 0x2f502f4, 0x3070306, + 0x3110310, 0xffffffff, 0x7cb07ca, 0x7cf07ce, 0x7d307d2, 0x3330332, + 0x33b033a, 0x33f033e, 0x3490348, 0x7e707e6, 0x7e907e8, 0x7eb07ea, + 0x7ed07ec, 0x3ac03ab, 0x3b203b1, 0x3bb03ba, 0x7fb07fa, 0x3dc03db, + 0x3f003ef, 0x620061f, 0x2830282, 0x8070806, 0x80b080a, 0x80f080e, + 0x8130812, 0x8170816, 0x81b081a, 0x81f081e, 0x8210820, 0x4a0049f, + 0x4b004af, 0x4c404c3, 0x4d604d5, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x545ffff, 0x8380546, 0x83b0839, 0x83d083c, 0x580057f, + 0x590058f, 0x5a0059f, 0x5b205b1, 0xffffffff, 0x63bffff, 0x79b063c, + 0xffffffff, 0x6080607, 0x60c060b, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x62c062b, 0x630062f, 0x6360635, + 0x63a0639, 0x640063f, 0xffff0644, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x25e02f6, 0x2fc02fa, 0x30302fe, 0xffff0304, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x30cffff, 0x2c0030e, + 0x3140312, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x7a8ffff, 0x7ac02da, 0x2e202e0, 0x7b402e4, 0x2f00724, + 0x144076b, 0x30a0479, 0x318081c, 0x31a07c6, 0x7cc031c, 0x7d00320, + 0x32c022c, 0x336007a, 0xffff046c, 0xffffffff, 0xffffffff, 0x7a9ffff, + 0x7ad02db, 0x2e302e1, 0x7b502e5, 0x2f10725, 0x145076c, 0x30b047a, + 0x319081d, 0x31b07c7, 0x7cd031d, 0x7d10321, 0x32d022d, 0x337007b, + 0xffff046d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x9280012, 0x72005a, 0x3520338, 0x114010c, 0x2bc0625, + 0x2f202ae, 0x31608ef, 0x10e064f, 0x2ee02b6, 0x4fb029a, 0x2c802be, + 0x2de02d0, 0x47f0798, 0x330031e, 0x24e0342, 0x3b802c4, 0x47b03e5, + 0x473056b, 0x72a06c4, 0x91104df, 0x9290013, 0x73005b, 0x3530339, + 0x115010d, 0x2bd0626, 0x2f302af, 0x31708f0, 0x10f0650, 0x2ef02b7, + 0x4fc029b, 0x2c902bf, 0x2df02d1, 0x4800799, 0x331031f, 0x24f0343, + 0x3b902c5, 0x47c03e6, 0x474056c, 0x72b06c5, 0x91204e0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //4000 bytes -enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0x160], [ 0x100, 0x240, 0x5100], [ 0x3020100, 0x7060504, 0xb0a0908, 0xe0d0c0a, 0x3030303, 0x100a0f03, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0xf000e, 0x10010, 0x120011, 0x10013, 0x150014, 0x170016, 0x190018, 0x1b001a, 0x1c0001, 0x1e001d, 0x1f001f, 0x1f0020, 0x1f001f, 0x1f001f, 0x1f001f, 0x220021, 0x1f0023, 0x250024, 0x1f001f, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x260001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x270001, 0x10001, 0x10001, 0x10028, 0x2a0029, 0x2c002b, 0x2e002d, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x2f0001, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1001f, 0x310030, 0x320001, 0x340033, 0x360035, 0x380037, 0x1f0039, 0x1f001f, 0x3b003a, 0x3d003c, 0x1f003e, 0x1f001f, 0x40003f, 0x1f001f, 0x1f001f, 0x1f0041, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x420001, 0x1f0043, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x10001, 0x1f0044, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x1f0045, 0x1f001f, 0x46001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f0047, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x490048, 0x4b004a, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f004c, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x10001, 0x10001, 0x1004d, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x4e0001, 0x1f004f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x1f004f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x0, 0x0, 0x7fffffe, 0x7fffffe, 0x0, 0x4200400, 0xff7fffff, 0xff7fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3ffc3, 0x501f, 0x0, 0x0, 0x20, 0x3cdf0000, 0xffffd740, 0xfffffffb, 0xffffffff, 0xffbfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffc03, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffe00ff, 0x27fffff, 0xfffffffe, 0xff, 0xbfff0000, 0xffff00b6, 0x707ff, 0x7ff0000, 0xffffffff, 0xfeffffff, 0xffffc000, 0xffffffff, 0xffffffff, 0x1fefffff, 0x9c00e1fe, 0xffff0000, 0xffffffff, 0xffffe000, 0xffffffff, 0xffffffff, 0x3ffff, 0xfffffc00, 0x43007ff, 0xfcffffff, 0x1fff, 0x1ffffff, 0x0, 0x0, 0x1ffd, 0x0, 0x7fff03f0, 0xffffffff, 0xefffffff, 0xffe1dfff, 0xfefe000f, 0xfff99fee, 0xe3c5fdff, 0xb080599f, 0x3000f, 0xfff987ee, 0xc36dfdff, 0x5e021987, 0x3f0000, 0xfffbbfee, 0xe3edfdff, 0x11bbf, 0xf, 0xfff99fee, 0xe3edfdff, 0xb0c0199f, 0x2000f, 0xd63dc7ec, 0xc3ffc718, 0x811dc7, 0x0, 0xfffddfee, 0xe3effdff, 0x3601ddf, 0xf, 0xfffddfec, 0xe3effdff, 0x40601ddf, 0x6000f, 0xfffddfec, 0xe7ffffff, 0x805ddf, 0xfc00000f, 0xfc7fffec, 0x2ffbffff, 0xff5f807f, 0xc0000, 0xfffffffe, 0x7ffffff, 0x207f, 0x0, 0xfef02596, 0x3bffecae, 0xf000205f, 0x0, 0x1, 0x0, 0xfffffeff, 0xfffe1fff, 0xfeffff03, 0x1fffffff, 0x0, 0x0, 0xffffffff, 0xf97fffff, 0xffff0000, 0xffffc1e7, 0x3000407f, 0xffffffff, 0xffff20bf, 0xf7ffffff, 0xffffffff, 0xffffffff, 0x3d7f3dff, 0xffffffff, 0xffff3dff, 0x7f3dffff, 0xff7fff3d, 0xffffffff, 0xff3dffff, 0xffffffff, 0x87ffffff, 0x0, 0xffff, 0xffffffff, 0xffffffff, 0x1fffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff9fff, 0x7fffffe, 0xffffffff, 0xffffffff, 0x1c7ff, 0xfdfff, 0xfffff, 0xfffff, 0xddfff, 0xffffffff, 0xffcfffff, 0x108001ff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffff, 0xffffffff, 0xffff07ff, 0xffffffff, 0x3fffff, 0x1fffffff, 0x1ff0fff, 0xffff0000, 0x1f3fff, 0xffffffff, 0xffff0fff, 0x3ff, 0x0, 0xfffffff, 0xffffffff, 0x7fffffff, 0x1ffffe, 0x0, 0x80, 0x0, 0x0, 0xffffffff, 0xffefffff, 0xfef, 0x0, 0xffffffff, 0xfc00f3ff, 0xffffffff, 0x3ffbf, 0xffffffff, 0x3fffff, 0xfc00e000, 0x3fffffff, 0x0, 0x0, 0x0, 0x6fde00, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0, 0x0, 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, 0x5fdfffff, 0xfcf1fdc, 0x1fdc1fff, 0x0, 0x0, 0x0, 0x80020000, 0x1fff0000, 0x0, 0x0, 0x0, 0x3e2ffc84, 0xf3ffbd50, 0x43e0, 0xffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffc00000, 0xffffffff, 0x3ff, 0xffffffff, 0xffff7fff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xc781f, 0xffffffff, 0xffff20bf, 0xffffffff, 0x80ff, 0x7fffff, 0x7f7f7f7f, 0x7f7f7f7f, 0xffffffff, 0x0, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0, 0x1f3e03fe, 0xfffffffe, 0xffffffff, 0xe07fffff, 0xfffffffe, 0xffffffff, 0xf7ffffff, 0xffffffe0, 0xfffe3fff, 0xffffffff, 0xffffffff, 0x7fff, 0x7ffffff, 0x0, 0xffff0000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fff, 0x0, 0xffff0000, 0x3fffffff, 0xffff1fff, 0xc00, 0xffffffff, 0x8ff07fff, 0x80ffffff, 0xffffffff, 0xffffffff, 0xffff, 0xff800000, 0xfffffffc, 0xffffffff, 0xffffffff, 0xf79ff, 0x7ff, 0x0, 0xff000000, 0xfffff7bb, 0xff, 0xffffffff, 0xfffff, 0xffffffff, 0xffffffff, 0xf, 0x8fc0000, 0xfffffc00, 0xffff07ff, 0x7ffff, 0x1fffffff, 0xffffffff, 0xfff7ffff, 0x8000, 0x0, 0xffffffff, 0x7fffff, 0x3fff, 0x47fffff, 0xffffffff, 0x7fffffff, 0x38000005, 0x3cffff, 0x7e7e7e, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x7ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff000f, 0xfffff87f, 0xfffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xe0f8007f, 0x5f7ffdff, 0xffffffdb, 0xffffffff, 0xffffffff, 0x3ffff, 0xfff80000, 0xffffffff, 0xffffffff, 0x3fffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, 0xfff0000, 0x0, 0x0, 0x0, 0xffdf0000, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0x0, 0x7fffffe, 0x7fffffe, 0xffffffc0, 0xffffffff, 0x7fffffff, 0x1cfcfcfc, 0x0, 0xffffefff, 0xb7ffff7f, 0x3fff3fff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffff, 0x0, 0x0, 0xffffffff, 0x1fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1fffffff, 0xffffffff, 0x1ffff, 0x0, 0x7fffffff, 0xffff0000, 0x7ff, 0x0, 0x3fffffff, 0xffffffff, 0x3eff0f, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0xfffffd3f, 0x91bfffff, 0x3fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fffff, 0x3ffffff, 0x0, 0x0, 0xffffffff, 0xc0ffffff, 0x0, 0x0, 0xfeeff06f, 0xfffff, 0x0, 0x1fffffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x3fffff, 0x3fffff, 0x7ffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x3f, 0x0, 0xfffffffc, 0x1ffffff, 0xffff0000, 0x1ff, 0xffffffff, 0x7ffff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x1e, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x3fffff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x1ffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffff001f, 0x7fffffff, 0xfff80000, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, 0xffffffef, 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff3f, 0xf7fffffd, 0xf7ffffff, 0xffdfffff, 0xffdfffff, 0xffff7fff, 0xffff7fff, 0xfffffdff, 0xfffffdff, 0xff7, 0x0, 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, 0xffffbff, 0xffffbee, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffff, 0x0, 0xffffffff, 0x1fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0x160], [0x100, + 0x240, 0x5100], [0x3020100, 0x7060504, 0xb0a0908, 0xe0d0c0a, 0x3030303, + 0x100a0f03, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0xf000e, + 0x10010, 0x120011, 0x10013, 0x150014, 0x170016, 0x190018, 0x1b001a, + 0x1c0001, 0x1e001d, 0x1f001f, 0x1f0020, 0x1f001f, 0x1f001f, 0x1f001f, + 0x220021, 0x1f0023, 0x250024, 0x1f001f, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x260001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x270001, 0x10001, 0x10001, 0x10028, + 0x2a0029, 0x2c002b, 0x2e002d, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x2f0001, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1001f, 0x310030, 0x320001, + 0x340033, 0x360035, 0x380037, 0x1f0039, 0x1f001f, 0x3b003a, 0x3d003c, + 0x1f003e, 0x1f001f, 0x40003f, 0x1f001f, 0x1f001f, 0x1f0041, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x420001, 0x1f0043, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x10001, 0x1f0044, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x10001, 0x1f0045, 0x1f001f, + 0x46001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f0047, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x490048, 0x4b004a, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f004c, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x10001, 0x10001, 0x10001, 0x1004d, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x4e0001, 0x1f004f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x10001, 0x1f004f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, 0x1f001f, + 0x0, 0x0, 0x7fffffe, 0x7fffffe, 0x0, 0x4200400, 0xff7fffff, 0xff7fffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x3ffc3, 0x501f, 0x0, 0x0, 0x20, 0x3cdf0000, + 0xffffd740, 0xfffffffb, 0xffffffff, 0xffbfffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xfffffc03, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfffe00ff, 0x27fffff, 0xfffffffe, 0xff, 0xbfff0000, + 0xffff00b6, 0x707ff, 0x7ff0000, 0xffffffff, 0xfeffffff, 0xffffc000, + 0xffffffff, 0xffffffff, 0x1fefffff, 0x9c00e1fe, 0xffff0000, 0xffffffff, + 0xffffe000, 0xffffffff, 0xffffffff, 0x3ffff, 0xfffffc00, 0x43007ff, + 0xfcffffff, 0x1fff, 0x1ffffff, 0x0, 0x0, 0x1ffd, 0x0, 0x7fff03f0, + 0xffffffff, 0xefffffff, 0xffe1dfff, 0xfefe000f, 0xfff99fee, 0xe3c5fdff, + 0xb080599f, 0x3000f, 0xfff987ee, 0xc36dfdff, 0x5e021987, 0x3f0000, + 0xfffbbfee, 0xe3edfdff, 0x11bbf, 0xf, 0xfff99fee, 0xe3edfdff, + 0xb0c0199f, 0x2000f, 0xd63dc7ec, 0xc3ffc718, 0x811dc7, 0x0, 0xfffddfee, + 0xe3effdff, 0x3601ddf, 0xf, 0xfffddfec, 0xe3effdff, 0x40601ddf, + 0x6000f, 0xfffddfec, 0xe7ffffff, 0x805ddf, 0xfc00000f, 0xfc7fffec, + 0x2ffbffff, 0xff5f807f, 0xc0000, 0xfffffffe, 0x7ffffff, 0x207f, 0x0, + 0xfef02596, 0x3bffecae, 0xf000205f, 0x0, 0x1, 0x0, 0xfffffeff, + 0xfffe1fff, 0xfeffff03, 0x1fffffff, 0x0, 0x0, 0xffffffff, 0xf97fffff, + 0xffff0000, 0xffffc1e7, 0x3000407f, 0xffffffff, 0xffff20bf, 0xf7ffffff, + 0xffffffff, 0xffffffff, 0x3d7f3dff, 0xffffffff, 0xffff3dff, 0x7f3dffff, + 0xff7fff3d, 0xffffffff, 0xff3dffff, 0xffffffff, 0x87ffffff, 0x0, + 0xffff, 0xffffffff, 0xffffffff, 0x1fffff, 0xfffffffe, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff9fff, 0x7fffffe, 0xffffffff, + 0xffffffff, 0x1c7ff, 0xfdfff, 0xfffff, 0xfffff, 0xddfff, 0xffffffff, + 0xffcfffff, 0x108001ff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffff, + 0xffffffff, 0xffff07ff, 0xffffffff, 0x3fffff, 0x1fffffff, 0x1ff0fff, + 0xffff0000, 0x1f3fff, 0xffffffff, 0xffff0fff, 0x3ff, 0x0, 0xfffffff, + 0xffffffff, 0x7fffffff, 0x1ffffe, 0x0, 0x80, 0x0, 0x0, 0xffffffff, + 0xffefffff, 0xfef, 0x0, 0xffffffff, 0xfc00f3ff, 0xffffffff, 0x3ffbf, + 0xffffffff, 0x3fffff, 0xfc00e000, 0x3fffffff, 0x0, 0x0, 0x0, 0x6fde00, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x0, 0x0, 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, + 0x5fdfffff, 0xfcf1fdc, 0x1fdc1fff, 0x0, 0x0, 0x0, 0x80020000, + 0x1fff0000, 0x0, 0x0, 0x0, 0x3e2ffc84, 0xf3ffbd50, 0x43e0, 0xffffffff, + 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xffc00000, 0xffffffff, 0x3ff, 0xffffffff, 0xffff7fff, + 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xc781f, + 0xffffffff, 0xffff20bf, 0xffffffff, 0x80ff, 0x7fffff, 0x7f7f7f7f, + 0x7f7f7f7f, 0xffffffff, 0x0, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe0, 0x1f3e03fe, 0xfffffffe, 0xffffffff, 0xe07fffff, 0xfffffffe, + 0xffffffff, 0xf7ffffff, 0xffffffe0, 0xfffe3fff, 0xffffffff, 0xffffffff, + 0x7fff, 0x7ffffff, 0x0, 0xffff0000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x3fffff, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fff, 0x0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fff, 0x0, + 0xffff0000, 0x3fffffff, 0xffff1fff, 0xc00, 0xffffffff, 0x8ff07fff, + 0x80ffffff, 0xffffffff, 0xffffffff, 0xffff, 0xff800000, 0xfffffffc, + 0xffffffff, 0xffffffff, 0xf79ff, 0x7ff, 0x0, 0xff000000, 0xfffff7bb, + 0xff, 0xffffffff, 0xfffff, 0xffffffff, 0xffffffff, 0xf, 0x8fc0000, + 0xfffffc00, 0xffff07ff, 0x7ffff, 0x1fffffff, 0xffffffff, 0xfff7ffff, + 0x8000, 0x0, 0xffffffff, 0x7fffff, 0x3fff, 0x47fffff, 0xffffffff, + 0x7fffffff, 0x38000005, 0x3cffff, 0x7e7e7e, 0x7f7f, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0x7ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff000f, 0xfffff87f, 0xfffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, + 0xe0f8007f, 0x5f7ffdff, 0xffffffdb, 0xffffffff, 0xffffffff, 0x3ffff, + 0xfff80000, 0xffffffff, 0xffffffff, 0x3fffffff, 0xffff0000, 0xffffffff, + 0xfffcffff, 0xffffffff, 0xff, 0xfff0000, 0x0, 0x0, 0x0, 0xffdf0000, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0x0, 0x7fffffe, + 0x7fffffe, 0xffffffc0, 0xffffffff, 0x7fffffff, 0x1cfcfcfc, 0x0, + 0xffffefff, 0xb7ffff7f, 0x3fff3fff, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0x7ffffff, 0x0, 0x0, 0xffffffff, 0x1fffff, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1fffffff, 0xffffffff, 0x1ffff, 0x0, + 0x7fffffff, 0xffff0000, 0x7ff, 0x0, 0x3fffffff, 0xffffffff, 0x3eff0f, + 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x0, + 0x0, 0x0, 0xfffffd3f, 0x91bfffff, 0x3fffff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3fffff, 0x3ffffff, 0x0, 0x0, 0xffffffff, 0xc0ffffff, 0x0, 0x0, + 0xfeeff06f, 0xfffff, 0x0, 0x1fffffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0x3fffff, 0x3fffff, 0x7ffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0x3f, 0x0, 0xfffffffc, 0x1ffffff, 0xffff0000, 0x1ff, 0xffffffff, + 0x7ffff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x1e, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xffffffff, 0x3fffff, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0x7, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xffffffff, 0x1ffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0xffff001f, 0x7fffffff, 0xfff80000, 0x0, 0x0, + 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, 0xffffffef, 0xffffffff, + 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffff3f, 0xf7fffffd, 0xf7ffffff, 0xffdfffff, 0xffdfffff, + 0xffff7fff, 0xffff7fff, 0xfffffdff, 0xfffffdff, 0xff7, 0x0, 0xffffffef, + 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, 0xffffbff, 0xffffbee, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7fffff, 0x0, 0xffffffff, 0x1fffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //2304 bytes -enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xe0], [ 0x100, 0x140, 0x2c00], [ 0x2020100, 0x4020302, 0x6020205, 0x2070202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020208, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0xe, 0xf0000, 0x0, 0x100000, 0x120011, 0x140013, 0x160015, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x190018, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x21, 0x220000, 0x0, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x250024, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x270000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x280000, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffe0000, 0xbfffffff, 0xb6, 0x0, 0x7ff0000, 0x0, 0xfffff800, 0x10000, 0x0, 0x0, 0x9fc00000, 0x3d9f, 0x20000, 0xffff0000, 0x7ff, 0x0, 0x0, 0x1ffc0, 0x0, 0xff800, 0xfbc00000, 0x3eef, 0xe000000, 0x0, 0x0, 0x0, 0x0, 0x7ffffff0, 0xf, 0xdc000000, 0xfeffff, 0xc, 0xe, 0xd0000000, 0x80399f, 0xc, 0xe, 0xd0000000, 0x23987, 0x230000, 0xe, 0xd0000000, 0x3bbf, 0xc, 0xe, 0xd0000000, 0xc0399f, 0xc, 0x4, 0xc0000000, 0x803dc7, 0x0, 0xe, 0xc0000000, 0x603ddf, 0xc, 0xc, 0xd0000000, 0x603ddf, 0xc, 0xc, 0xc0000000, 0x803ddf, 0xc, 0xc, 0x0, 0xff5f8400, 0xc0000, 0x0, 0x7f20000, 0x7f80, 0x0, 0x0, 0x1bf20000, 0x3f00, 0x0, 0x3000000, 0xc2a00000, 0x0, 0xfffe0000, 0xfeffe0df, 0x1fffffff, 0x40, 0x0, 0x0, 0x7ffff800, 0xc3c00000, 0x1e3f9d, 0x3c00bffc, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c0000, 0x1c0000, 0xc0000, 0xc0000, 0x0, 0xfff00000, 0x200fffff, 0x0, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0x0, 0xfff0fff, 0x0, 0x0, 0x0, 0xffff0000, 0x301, 0x0, 0xf800000, 0x0, 0x7fe00000, 0x9fffffff, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xfff00000, 0x1f, 0xff800, 0x7, 0x3ffe, 0x0, 0xfffc0, 0x0, 0xfffff0, 0x0, 0x0, 0x0, 0x0, 0xfff70000, 0x1c21ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xf000007f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x1ffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38000, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0xffffffff, 0x0, 0xfc00, 0x0, 0x0, 0x6000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff78000, 0x80000000, 0x0, 0x0, 0x30000, 0x844, 0xf8, 0x0, 0x0, 0x3, 0xfff00000, 0x1f, 0x3ffff, 0x0, 0x3fc0, 0xfff80, 0x0, 0xf, 0xfff80000, 0x1, 0x0, 0x0, 0x7ffe00, 0x3008, 0x8000000, 0x0, 0xc19d0000, 0x2, 0x60f800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37f8, 0x40000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000, 0xf06e, 0x87000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xff000000, 0x7f, 0x0, 0x7, 0x7ff0000, 0x0, 0x0, 0x7, 0x1fff80, 0x0, 0x0, 0x7, 0xfff80000, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfff800, 0x0, 0x0, 0x0, 0x0, 0xfffe0000, 0x7fffffff, 0x78000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf807e3e0, 0xfe7, 0x3c00, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xe0], [0x100, + 0x140, 0x2c00], [0x2020100, 0x4020302, 0x6020205, 0x2070202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020208, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0xe, + 0xf0000, 0x0, 0x100000, 0x120011, 0x140013, 0x160015, 0x0, 0x17, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x190018, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1d001c, 0x1f001e, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x21, 0x220000, 0x0, 0x0, + 0x0, 0x0, 0x23, 0x0, 0x0, 0x250024, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x270000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x280000, + 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a0000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xfffe0000, 0xbfffffff, 0xb6, 0x0, 0x7ff0000, 0x0, + 0xfffff800, 0x10000, 0x0, 0x0, 0x9fc00000, 0x3d9f, 0x20000, 0xffff0000, + 0x7ff, 0x0, 0x0, 0x1ffc0, 0x0, 0xff800, 0xfbc00000, 0x3eef, 0xe000000, + 0x0, 0x0, 0x0, 0x0, 0x7ffffff0, 0xf, 0xdc000000, 0xfeffff, 0xc, 0xe, + 0xd0000000, 0x80399f, 0xc, 0xe, 0xd0000000, 0x23987, 0x230000, 0xe, + 0xd0000000, 0x3bbf, 0xc, 0xe, 0xd0000000, 0xc0399f, 0xc, 0x4, + 0xc0000000, 0x803dc7, 0x0, 0xe, 0xc0000000, 0x603ddf, 0xc, 0xc, + 0xd0000000, 0x603ddf, 0xc, 0xc, 0xc0000000, 0x803ddf, 0xc, 0xc, 0x0, + 0xff5f8400, 0xc0000, 0x0, 0x7f20000, 0x7f80, 0x0, 0x0, 0x1bf20000, + 0x3f00, 0x0, 0x3000000, 0xc2a00000, 0x0, 0xfffe0000, 0xfeffe0df, + 0x1fffffff, 0x40, 0x0, 0x0, 0x7ffff800, 0xc3c00000, 0x1e3f9d, + 0x3c00bffc, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe0000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1c0000, 0x1c0000, 0xc0000, 0xc0000, 0x0, 0xfff00000, 0x200fffff, + 0x0, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0x0, 0xfff0fff, 0x0, + 0x0, 0x0, 0xffff0000, 0x301, 0x0, 0xf800000, 0x0, 0x7fe00000, + 0x9fffffff, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xfff00000, 0x1f, 0xff800, 0x7, + 0x3ffe, 0x0, 0xfffc0, 0x0, 0xfffff0, 0x0, 0x0, 0x0, 0x0, 0xfff70000, + 0x1c21ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xf000007f, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0x1ffff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x38000, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, + 0xffffffff, 0x0, 0xfc00, 0x0, 0x0, 0x6000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x3ff78000, 0x80000000, 0x0, 0x0, 0x30000, 0x844, 0xf8, 0x0, 0x0, + 0x3, 0xfff00000, 0x1f, 0x3ffff, 0x0, 0x3fc0, 0xfff80, 0x0, 0xf, + 0xfff80000, 0x1, 0x0, 0x0, 0x7ffe00, 0x3008, 0x8000000, 0x0, + 0xc19d0000, 0x2, 0x60f800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37f8, + 0x40000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff, 0x7f, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000, + 0xf06e, 0x87000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xff000000, + 0x7f, 0x0, 0x7, 0x7ff0000, 0x0, 0x0, 0x7, 0x1fff80, 0x0, 0x0, 0x7, + 0xfff80000, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfff800, 0x0, 0x0, 0x0, + 0x0, 0xfffe0000, 0x7fffffff, 0x78000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xf807e3e0, 0xfe7, 0x3c00, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //2384 bytes -enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)([ 0x0, 0x40, 0x180], [ 0x100, 0x280, 0x1a80], [ 0x2020100, 0x4020302, 0x2020605, 0x8070202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x40003, 0x50002, 0x20002, 0x70006, 0x60006, 0x90008, 0x6000a, 0x2000b, 0xc000c, 0x2000d, 0xe0005, 0x20002, 0x20002, 0x2000f, 0x20002, 0x20002, 0x100002, 0x110002, 0x2000e, 0x130012, 0x140002, 0xc, 0x20015, 0x20002, 0x20002, 0x20002, 0x170016, 0x190018, 0x20002, 0x20002, 0x1b001a, 0x20002, 0x20002, 0x1d001c, 0x20002, 0x20002, 0x20002, 0x20002, 0x1e0002, 0x20002, 0x20002, 0x20002, 0x2001f, 0x200002, 0x220021, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x60023, 0x20002, 0xc0024, 0xc0017, 0x2000c, 0x40002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x2000e, 0x20002, 0x260025, 0x20002, 0x280027, 0x230002, 0x20002, 0x20002, 0x20002, 0x20029, 0x2002a, 0x2002b, 0x2002c, 0x20002, 0x20002, 0x2002d, 0x20002, 0x4002e, 0xc002f, 0x20002, 0x20002, 0x20002, 0x20002, 0x50002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20030, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20031, 0x20002, 0x20002, 0x20002, 0x320002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20033, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x0, 0x3ff0000, 0x0, 0x0, 0x0, 0x720c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0x0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x3ff, 0x0, 0x0, 0x0, 0x0, 0xffc0, 0x0, 0x0, 0x0, 0x3f0ffc0, 0x0, 0x0, 0x0, 0xfcffc0, 0x0, 0x0, 0x0, 0x7ffc0, 0x0, 0x0, 0x0, 0x7f00ffc0, 0x0, 0x0, 0x0, 0x3fffc0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0xfffff, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ffffe00, 0x0, 0x0, 0x0, 0x1c000, 0x0, 0x0, 0x0, 0x3ff03ff, 0x0, 0x0, 0xffc0, 0x0, 0x0, 0x0, 0x7ff0000, 0x0, 0x3ff03ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff03ff, 0x0, 0x0, 0x0, 0x0, 0x3f10000, 0x3ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0xffffffff, 0x3e7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xfffffff, 0x0, 0x0, 0xfffffc00, 0x0, 0x0, 0x0, 0xffc00000, 0xfffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000, 0x80, 0x70003fe, 0x0, 0x0, 0x3c0000, 0x0, 0x0, 0x0, 0x0, 0x3ff, 0xfffeff00, 0x0, 0x3ff, 0xfffe0000, 0x0, 0x0, 0x0, 0x3ff, 0x0, 0x0, 0x0, 0x3f0000, 0x0, 0x0, 0xffffff80, 0xfffff, 0xffffffff, 0x1ffffff, 0x400, 0x0, 0x0, 0x0, 0x0, 0xf, 0x402, 0x0, 0x0, 0x0, 0x3e0000, 0x0, 0x0, 0x0, 0xff000000, 0x0, 0xfc00000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x60000000, 0x0, 0x0, 0xff000000, 0xff000000, 0x0, 0x0, 0x0, 0x7fffffff, 0x0, 0x0, 0xfffc0000, 0xffff, 0x0, 0xffc00000, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7, 0x0, 0x0, 0x0, 0x3ffff, 0x0, 0x0, 0xffffc000, 0xffffffff, 0x7ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)([0x0, 0x40, 0x180], [0x100, + 0x280, 0x1a80], [0x2020100, 0x4020302, 0x2020605, 0x8070202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x40003, 0x50002, + 0x20002, 0x70006, 0x60006, 0x90008, 0x6000a, 0x2000b, 0xc000c, 0x2000d, + 0xe0005, 0x20002, 0x20002, 0x2000f, 0x20002, 0x20002, 0x100002, + 0x110002, 0x2000e, 0x130012, 0x140002, 0xc, 0x20015, 0x20002, 0x20002, + 0x20002, 0x170016, 0x190018, 0x20002, 0x20002, 0x1b001a, 0x20002, + 0x20002, 0x1d001c, 0x20002, 0x20002, 0x20002, 0x20002, 0x1e0002, + 0x20002, 0x20002, 0x20002, 0x2001f, 0x200002, 0x220021, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x60023, 0x20002, 0xc0024, 0xc0017, 0x2000c, 0x40002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x2000e, 0x20002, 0x260025, 0x20002, + 0x280027, 0x230002, 0x20002, 0x20002, 0x20002, 0x20029, 0x2002a, + 0x2002b, 0x2002c, 0x20002, 0x20002, 0x2002d, 0x20002, 0x4002e, 0xc002f, + 0x20002, 0x20002, 0x20002, 0x20002, 0x50002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20030, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20031, 0x20002, 0x20002, 0x20002, 0x320002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20033, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, + 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x20002, 0x0, + 0x3ff0000, 0x0, 0x0, 0x0, 0x720c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x3ff, 0x0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x3ff, 0x0, + 0x0, 0x0, 0x0, 0xffc0, 0x0, 0x0, 0x0, 0x3f0ffc0, 0x0, 0x0, 0x0, + 0xfcffc0, 0x0, 0x0, 0x0, 0x7ffc0, 0x0, 0x0, 0x0, 0x7f00ffc0, 0x0, 0x0, + 0x0, 0x3fffc0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0xfffff, 0x0, 0x0, + 0x3ff0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ffffe00, 0x0, 0x0, 0x0, + 0x1c000, 0x0, 0x0, 0x0, 0x3ff03ff, 0x0, 0x0, 0xffc0, 0x0, 0x0, 0x0, + 0x7ff0000, 0x0, 0x3ff03ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff03ff, 0x0, + 0x0, 0x0, 0x0, 0x3f10000, 0x3ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffff0000, + 0xffffffff, 0x3e7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xfffffff, + 0x0, 0x0, 0xfffffc00, 0x0, 0x0, 0x0, 0xffc00000, 0xfffff, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x20000000, 0x80, 0x70003fe, 0x0, 0x0, 0x3c0000, + 0x0, 0x0, 0x0, 0x0, 0x3ff, 0xfffeff00, 0x0, 0x3ff, 0xfffe0000, 0x0, + 0x0, 0x0, 0x3ff, 0x0, 0x0, 0x0, 0x3f0000, 0x0, 0x0, 0xffffff80, + 0xfffff, 0xffffffff, 0x1ffffff, 0x400, 0x0, 0x0, 0x0, 0x0, 0xf, 0x402, + 0x0, 0x0, 0x0, 0x3e0000, 0x0, 0x0, 0x0, 0xff000000, 0x0, 0xfc00000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x60000000, 0x0, 0x0, 0xff000000, + 0xff000000, 0x0, 0x0, 0x0, 0x7fffffff, 0x0, 0x0, 0xfffc0000, 0xffff, + 0x0, 0xffc00000, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7, + 0x0, 0x0, 0x0, 0x3ffff, 0x0, 0x0, 0xffffc000, 0xffffffff, 0x7ff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //2336 bytes -enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xc0], [ 0x100, 0x100, 0x3100], [ 0x2020100, 0x4020302, 0x2020605, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x20001, 0x30001, 0x50004, 0x70006, 0x10008, 0x90001, 0xb000a, 0x1000c, 0xd0001, 0x1000e, 0x10000f, 0x120011, 0x140013, 0x10015, 0x10001, 0x10016, 0x170001, 0x10001, 0x180001, 0x190001, 0x10001, 0x1b001a, 0x1001c, 0x1001d, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x1001e, 0x1001f, 0x210020, 0x230022, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x240001, 0x260025, 0x270001, 0x280001, 0x10001, 0x10001, 0x2a0029, 0x2c002b, 0x10001, 0x10001, 0x2e002d, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x1002f, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x0, 0x8c00f7ee, 0xb8000001, 0x28000000, 0x0, 0x88c00882, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfc000000, 0x0, 0x600, 0x40000000, 0x49, 0x180000, 0xc8003600, 0x0, 0x0, 0x3c00, 0x0, 0x0, 0x100000, 0x0, 0x3fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3800000, 0x0, 0x7fff0000, 0x40000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000, 0x0, 0x0, 0xc008000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17fff0, 0x3c000000, 0x0, 0x0, 0x20, 0x0, 0x61f0000, 0x0, 0x0, 0x0, 0xfc00, 0x0, 0x0, 0x0, 0x0, 0x8000000, 0x0, 0x0, 0x0, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6000, 0x18000000, 0x0, 0x0, 0x3800, 0x0, 0x600000, 0x0, 0x0, 0x0, 0x0, 0x7700000, 0x0, 0x7ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, 0x3f7f, 0x0, 0x0, 0x0, 0x0, 0xfc000000, 0x1, 0x0, 0x0, 0x0, 0xf0000000, 0x0, 0xf8000000, 0x0, 0xc0000000, 0x0, 0x0, 0x800ff, 0x0, 0xffff0000, 0xffff00ff, 0x7ffbffef, 0x60000000, 0x6000, 0x0, 0x0, 0x0, 0xf00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fff00, 0x0, 0x0, 0x60, 0xffc0, 0x0, 0x0, 0x0, 0x0, 0x1fffff8, 0x0, 0xf000000, 0x30000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xde000000, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xfff7fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfff3ff0e, 0x20010000, 0x0, 0x0, 0x0, 0x1, 0x0, 0x8000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0xe000, 0x0, 0x0, 0x40080000, 0x0, 0x0, 0x0, 0xfc0000, 0x0, 0x0, 0x0, 0xf00000, 0x0, 0x0, 0xc000, 0x7000000, 0x0, 0xc000, 0x80000000, 0x0, 0x0, 0x0, 0xc0003ffe, 0x0, 0x0, 0x0, 0xf0000000, 0x0, 0x0, 0x0, 0xc0000000, 0x30000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff0000, 0xffff0000, 0xfff7ffff, 0xd0b, 0x0, 0x0, 0x0, 0x0, 0x8c00f7ee, 0xb8000001, 0xa8000000, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ff0000, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f80, 0x0, 0x0, 0xd8000000, 0x3, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x1e0, 0x0, 0x0, 0x0, 0x0, 0xf0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xc0], + [0x100, 0x100, 0x3100], [0x2020100, 0x4020302, 0x2020605, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x10000, 0x20001, 0x30001, 0x50004, 0x70006, 0x10008, 0x90001, + 0xb000a, 0x1000c, 0xd0001, 0x1000e, 0x10000f, 0x120011, 0x140013, + 0x10015, 0x10001, 0x10016, 0x170001, 0x10001, 0x180001, 0x190001, + 0x10001, 0x1b001a, 0x1001c, 0x1001d, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x1001e, 0x1001f, + 0x210020, 0x230022, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x240001, 0x260025, 0x270001, 0x280001, + 0x10001, 0x10001, 0x2a0029, 0x2c002b, 0x10001, 0x10001, 0x2e002d, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x1002f, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x0, + 0x8c00f7ee, 0xb8000001, 0x28000000, 0x0, 0x88c00882, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, 0x80, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xfc000000, 0x0, 0x600, 0x40000000, 0x49, + 0x180000, 0xc8003600, 0x0, 0x0, 0x3c00, 0x0, 0x0, 0x100000, 0x0, + 0x3fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3800000, 0x0, 0x7fff0000, + 0x40000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10030, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x100000, 0x0, 0x0, 0xc008000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x17fff0, 0x3c000000, 0x0, 0x0, 0x20, 0x0, 0x61f0000, 0x0, 0x0, + 0x0, 0xfc00, 0x0, 0x0, 0x0, 0x0, 0x8000000, 0x0, 0x0, 0x0, 0x1ff, 0x0, + 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x6000, 0x18000000, 0x0, 0x0, 0x3800, 0x0, 0x600000, 0x0, 0x0, 0x0, + 0x0, 0x7700000, 0x0, 0x7ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, + 0x3f7f, 0x0, 0x0, 0x0, 0x0, 0xfc000000, 0x1, 0x0, 0x0, 0x0, 0xf0000000, + 0x0, 0xf8000000, 0x0, 0xc0000000, 0x0, 0x0, 0x800ff, 0x0, 0xffff0000, + 0xffff00ff, 0x7ffbffef, 0x60000000, 0x6000, 0x0, 0x0, 0x0, 0xf00, + 0x600, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3fff00, 0x0, 0x0, + 0x60, 0xffc0, 0x0, 0x0, 0x0, 0x0, 0x1fffff8, 0x0, 0xf000000, + 0x30000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xde000000, 0x0, 0x0, + 0x0, 0x10000, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xfff7fff, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xfff3ff0e, 0x20010000, 0x0, 0x0, 0x0, 0x1, 0x0, + 0x8000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0xe000, 0x0, + 0x0, 0x40080000, 0x0, 0x0, 0x0, 0xfc0000, 0x0, 0x0, 0x0, 0xf00000, 0x0, + 0x0, 0xc000, 0x7000000, 0x0, 0xc000, 0x80000000, 0x0, 0x0, 0x0, + 0xc0003ffe, 0x0, 0x0, 0x0, 0xf0000000, 0x0, 0x0, 0x0, 0xc0000000, + 0x30000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800, 0x0, 0xc0000000, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff0000, 0xffff0000, 0xfff7ffff, 0xd0b, + 0x0, 0x0, 0x0, 0x0, 0x8c00f7ee, 0xb8000001, 0xa8000000, 0x3f, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80000000, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x800000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x80000000, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1ff0000, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f80, 0x0, 0x0, 0xd8000000, 0x3, 0x0, + 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x1e0, 0x0, 0x0, 0x0, 0x0, 0xf0000, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //2848 bytes -enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0xe0], [ 0x100, 0x140, 0x3d00], [ 0x3020100, 0x5030403, 0x3030306, 0x8070303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x80001, 0xa0009, 0xc000b, 0xe000d, 0x1000f, 0x100001, 0x10001, 0x110001, 0x120001, 0x130001, 0x10001, 0x140001, 0x160015, 0x180017, 0x170019, 0x1a0017, 0x1b0017, 0x1c0017, 0x1001d, 0x1f001e, 0x210020, 0x170022, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x230001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10024, 0x250001, 0x10026, 0x10027, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x280001, 0x290001, 0x2b002a, 0x2c0001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x2e002d, 0x30002f, 0x10001, 0x320031, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10033, 0x350034, 0x370036, 0x390038, 0x3b003a, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x0, 0x70000810, 0x40000000, 0x50000001, 0x0, 0x113d37c, 0x800000, 0x800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffc003c, 0xffffafe0, 0x0, 0x0, 0x0, 0x200000, 0x30, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0x0, 0xc9c0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000, 0x60000200, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, 0x7f80000, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfce8000e, 0x1500000, 0x0, 0x0, 0x0, 0xc0000000, 0x1e0dfbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc0000000, 0xffffffff, 0x0, 0x0, 0x0, 0x1ff007fe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0000000, 0xe000e003, 0x6000e000, 0x0, 0x0, 0x40010, 0x1c000000, 0x1c00, 0x7ffffff, 0x0, 0x0, 0xc1d0037b, 0xc0042af, 0xbc1f, 0x0, 0xffff0000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff0ff, 0xfffff9ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff, 0xffffffff, 0x7f, 0x7ff, 0x0, 0xf0000000, 0xffffffff, 0xffffffff, 0x3ff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xff, 0xfff00000, 0xffffffff, 0xffffff9f, 0xffff003f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfe000007, 0xffffffff, 0xf0ffffff, 0xcfffffff, 0xffffffff, 0xffffffff, 0x3ff1fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e0, 0x0, 0x0, 0x0, 0x0, 0xfbffffff, 0xffffffff, 0xffffffff, 0xfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xfff0000, 0xc0010, 0xc0c00001, 0x0, 0x0, 0x18000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffc30000, 0x0, 0xffffffff, 0xf, 0x7fffffff, 0xfffffc00, 0x100ff, 0xffffffff, 0xfffffc00, 0x1ffff, 0xffffffff, 0x7fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x0, 0xffff0000, 0xffffffff, 0x7f, 0x0, 0x7fffff, 0x3, 0x0, 0x0, 0x600, 0x0, 0x0, 0x0, 0x0, 0x3c00f00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0x0, 0xfffc0000, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30000000, 0x0, 0x0, 0x0, 0x274, 0x0, 0x0, 0x0, 0x0, 0x70000810, 0x40000000, 0x50000001, 0x0, 0x0, 0x0, 0x0, 0x30007f7f, 0x0, 0xff800000, 0x0, 0xfe000000, 0xfff03ff, 0x0, 0xffff0000, 0x1fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xffffffff, 0xfffffe7f, 0xffffffff, 0x1c1f, 0xfffff018, 0xffffc3ff, 0x3fffffff, 0x0, 0xffffffff, 0xffffffff, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x7fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000002, 0x8000000, 0x200000, 0x200000, 0x8000, 0x8000, 0x200, 0x200, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30000, 0xffffffff, 0xffff0fff, 0xffffffff, 0xffffffff, 0xfffff, 0x7ffe7fff, 0xfffefffe, 0x0, 0xffff0000, 0xffff7fff, 0xffffffff, 0xffff0fff, 0x7ffffff, 0x0, 0x0, 0xffffffc0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffbf0001, 0xffffffff, 0x1fffffff, 0xfffff, 0xffffffff, 0x7df, 0x1ffff, 0xffffffff, 0x7fffffff, 0xfffffffd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1effffff, 0xffffffff, 0x3fffffff, 0xffff000f, 0xff, 0x0, 0x0, 0x0, 0xf8000000, 0xffffffff, 0xffffffff, 0xffe1, 0x0, 0xffffffff, 0xffffffff, 0x3f, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0xe0], [0x100, + 0x140, 0x3d00], [0x3020100, 0x5030403, 0x3030306, 0x8070303, 0x3030303, + 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, + 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, + 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, + 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, + 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x3030303, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x10000, 0x30002, 0x50004, 0x70006, 0x80001, 0xa0009, 0xc000b, 0xe000d, + 0x1000f, 0x100001, 0x10001, 0x110001, 0x120001, 0x130001, 0x10001, + 0x140001, 0x160015, 0x180017, 0x170019, 0x1a0017, 0x1b0017, 0x1c0017, + 0x1001d, 0x1f001e, 0x210020, 0x170022, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x230001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10024, + 0x250001, 0x10026, 0x10027, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x280001, 0x290001, 0x2b002a, 0x2c0001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x2e002d, 0x30002f, 0x10001, 0x320031, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10033, 0x350034, 0x370036, 0x390038, 0x3b003a, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x0, 0x70000810, 0x40000000, 0x50000001, 0x0, + 0x113d37c, 0x800000, 0x800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffc003c, 0xffffafe0, 0x0, 0x0, 0x0, + 0x200000, 0x30, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0x0, 0xc9c0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x40000000, 0x60000200, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x400000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0c0000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x20000, 0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x0, + 0x7f80000, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x2000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xfce8000e, 0x1500000, 0x0, 0x0, 0x0, 0xc0000000, + 0x1e0dfbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0000000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x3ff0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x8000000, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc0000000, 0xffffffff, + 0x0, 0x0, 0x0, 0x1ff007fe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xa0000000, 0xe000e003, 0x6000e000, 0x0, 0x0, 0x40010, 0x1c000000, + 0x1c00, 0x7ffffff, 0x0, 0x0, 0xc1d0037b, 0xc0042af, 0xbc1f, 0x0, + 0xffff0000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffff0ff, 0xfffff9ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfffff, 0xffffffff, 0x7f, 0x7ff, 0x0, 0xf0000000, + 0xffffffff, 0xffffffff, 0x3ff, 0xfffffffe, 0xffffffff, 0xffffffff, + 0xff, 0xfff00000, 0xffffffff, 0xffffff9f, 0xffff003f, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfe000007, 0xffffffff, 0xf0ffffff, + 0xcfffffff, 0xffffffff, 0xffffffff, 0x3ff1fff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e0, 0x0, 0x0, 0x0, 0x0, + 0xfbffffff, 0xffffffff, 0xffffffff, 0xfffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xfff0000, + 0xc0010, 0xc0c00001, 0x0, 0x0, 0x18000000, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xffc30000, 0x0, 0xffffffff, 0xf, 0x7fffffff, 0xfffffc00, + 0x100ff, 0xffffffff, 0xfffffc00, 0x1ffff, 0xffffffff, 0x7fffffff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x0, 0x0, 0x0, 0x0, + 0xffff0000, 0xffffffff, 0x7f, 0x0, 0x7fffff, 0x3, 0x0, 0x0, 0x600, 0x0, + 0x0, 0x0, 0x0, 0x3c00f00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0x0, 0xfffc0000, + 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30000000, 0x0, 0x0, 0x0, + 0x274, 0x0, 0x0, 0x0, 0x0, 0x70000810, 0x40000000, 0x50000001, 0x0, + 0x0, 0x0, 0x0, 0x30007f7f, 0x0, 0xff800000, 0x0, 0xfe000000, 0xfff03ff, + 0x0, 0xffff0000, 0x1fffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xffffffff, + 0xfffffe7f, 0xffffffff, 0x1c1f, 0xfffff018, 0xffffc3ff, 0x3fffffff, + 0x0, 0xffffffff, 0xffffffff, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0x7fffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x8000002, 0x8000000, 0x200000, 0x200000, 0x8000, 0x8000, 0x200, + 0x200, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30000, + 0xffffffff, 0xffff0fff, 0xffffffff, 0xffffffff, 0xfffff, 0x7ffe7fff, + 0xfffefffe, 0x0, 0xffff0000, 0xffff7fff, 0xffffffff, 0xffff0fff, + 0x7ffffff, 0x0, 0x0, 0xffffffc0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffbf0001, 0xffffffff, 0x1fffffff, + 0xfffff, 0xffffffff, 0x7df, 0x1ffff, 0xffffffff, 0x7fffffff, + 0xfffffffd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1effffff, + 0xffffffff, 0x3fffffff, 0xffff000f, 0xff, 0x0, 0x0, 0x0, 0xf8000000, + 0xffffffff, 0xffffffff, 0xffe1, 0x0, 0xffffffff, 0xffffffff, 0x3f, 0x0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //4576 bytes -enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)([ 0x0, 0x40, 0x170], [ 0x100, 0x260, 0x6100], [ 0x3020100, 0x7060504, 0xb0a0908, 0xe0d0c0a, 0x3030303, 0x100a0f03, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a11, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x20001, 0x30001, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0x1000e, 0x10000f, 0x10001, 0x120011, 0x140013, 0x160015, 0x180017, 0x190001, 0x1b001a, 0x1c0001, 0x1001d, 0x1e0001, 0x10001, 0x1f0001, 0x210020, 0x230022, 0x250024, 0x10026, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x270001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x280001, 0x10001, 0x10001, 0x10029, 0x2b002a, 0x2d002c, 0x2f002e, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x300001, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10031, 0x330032, 0x340001, 0x360035, 0x380037, 0x3a0039, 0x31003b, 0x310031, 0x3d003c, 0x3f003e, 0x310040, 0x310041, 0x430042, 0x310031, 0x310031, 0x310044, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x450001, 0x310046, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x10001, 0x310047, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x310048, 0x310031, 0x490031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x31004a, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x4c004b, 0x4e004d, 0x50004f, 0x520051, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310053, 0x550054, 0x570056, 0x590058, 0x5b005a, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x10001, 0x10001, 0x1005c, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x5d0001, 0x31005e, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x31005e, 0x310031, 0x310031, 0x5f0031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x0, 0xffffffff, 0xffffffff, 0x7fffffff, 0x0, 0xffffdfff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7cffffff, 0xffffd7f0, 0xfffffffb, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffe00ff, 0xfe7fffff, 0xfffffffe, 0xfffe86ff, 0xffffffff, 0xffff00ff, 0x1f07ff, 0xcfffffc0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xdfffffff, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffe7ff, 0xffffffff, 0xffffffff, 0x3ffff, 0xffffffff, 0x7ffffff, 0xffffffff, 0x7fff3fff, 0x4fffffff, 0x0, 0x0, 0x1ffd, 0x0, 0x7ffffff0, 0xffffffff, 0xffffffff, 0xffffffff, 0xfeffffff, 0xfff99fee, 0xf3c5fdff, 0xb080799f, 0xfffffcf, 0xfff987ee, 0xd36dfdff, 0x5e023987, 0x3fffc0, 0xfffbbfee, 0xf3edfdff, 0x13bbf, 0x3ffcf, 0xfff99fee, 0xf3edfdff, 0xb0c0399f, 0xffffcf, 0xd63dc7ec, 0xc3ffc718, 0x813dc7, 0x7ffffc0, 0xfffddfee, 0xe3effdff, 0x3603ddf, 0xff00ffcf, 0xfffddfec, 0xf3effdff, 0x40603ddf, 0x6ffcf, 0xfffddfec, 0xe7ffffff, 0x807ddf, 0xfe3fffcf, 0xfc7fffec, 0x2ffbffff, 0xff5f847f, 0x1c0000, 0xfffffffe, 0x87ffffff, 0xfffffff, 0x0, 0xfef02596, 0x3bffecae, 0xf3ff3f5f, 0x0, 0xffffffff, 0xffffffff, 0xfffffeff, 0xfffe1fff, 0xfeffffff, 0xdfffffff, 0x7ffdfff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff20bf, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d7f3dff, 0xffffffff, 0xffff3dff, 0x7f3dffff, 0xff7fff3d, 0xffffffff, 0xff3dffff, 0xffffffff, 0xe7ffffff, 0x1fffffff, 0x3ffffff, 0xffffffff, 0xffffffff, 0x1fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xffffffff, 0xffffffff, 0x1ffff, 0x1fdfff, 0x7fffff, 0xfffff, 0xddfff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x3ff03ff, 0x3ff3fff, 0xffffffff, 0xffffffff, 0xffffff, 0xffffffff, 0xffff07ff, 0xffffffff, 0x3fffff, 0x1fffffff, 0xfff0fff, 0xfffffff1, 0x1f3fff, 0xffffffff, 0xffff0fff, 0xc7ff03ff, 0xffffffff, 0xcfffffff, 0xffffffff, 0x7fffffff, 0x9fffffff, 0x3ff03ff, 0x3fff, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffff0fff, 0x1fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf00fffff, 0xffffffff, 0xf8ffffff, 0xffffe3ff, 0xffffffff, 0x0, 0x0, 0xffff00ff, 0x7fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf000007f, 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, 0xffdfffff, 0xefcfffdf, 0x7fdcffff, 0xffff07ff, 0xffff80ff, 0xffffffff, 0xfff30000, 0x1fff7fff, 0x7ffffff, 0xffff0000, 0x1ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff, 0xffffffff, 0x7f, 0x7ff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3ff1fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffff7fff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfe0fffff, 0xffffffff, 0xffff20bf, 0xffffffff, 0x800180ff, 0x7fffff, 0x7f7f7f7f, 0x7f7f7f7f, 0xffffffff, 0xffffffff, 0xfffffff, 0x0, 0x0, 0xfbffffff, 0xffffffff, 0xffffffff, 0xfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xfff0000, 0xffffffff, 0xffffffff, 0xfffffffe, 0xffffffff, 0xfe7fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffe0, 0xfffe3fff, 0xffffffff, 0xffffffff, 0xffff7fff, 0x7ffffff, 0xffffffff, 0xffff000f, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff1fff, 0xffffffff, 0xffff007f, 0xffffffff, 0xffffffff, 0xfff, 0xffffffff, 0xffffffff, 0x80ffffff, 0xffffffff, 0xffffffff, 0xffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf7fff, 0x7ff, 0x0, 0xff000000, 0xffffffff, 0x3ff0fff, 0xffffffff, 0xffffff, 0xffffffff, 0xffffffff, 0x3ffc01f, 0xfffffff, 0xffffffff, 0xffffffff, 0x800fffff, 0x1fffffff, 0xffffffff, 0xffffffff, 0xc3ffbfff, 0x0, 0xffffffff, 0x7fffff, 0xf3ff3fff, 0xfffffff, 0xffffffff, 0xffffffff, 0xf8000007, 0x7fffff, 0x7e7e7e, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x3ff3fff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff000f, 0xfffff87f, 0xfffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff3fff, 0xffffffff, 0xffffffff, 0x3ffffff, 0x0, 0xe0f8007f, 0x5f7fffff, 0xffffffdb, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff80003, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, 0x3fff0000, 0x3ffffff, 0xffff007f, 0xfff7ffff, 0xffdf0f7f, 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0x1cfcfcfc, 0x30007f7f, 0xffffefff, 0xb7ffff7f, 0x3fff3fff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffff, 0xffffff87, 0xff8fffff, 0xffffffff, 0xffffffff, 0xfff07ff, 0x0, 0xffff0000, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x1fffffff, 0xffffffff, 0x1ffff, 0x0, 0x7fffffff, 0xffff000f, 0x7ff, 0x0, 0xbfffffff, 0xffffffff, 0x3fff0f, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x3ff, 0x0, 0x0, 0xfffffd3f, 0x91bfffff, 0xffbfffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8fffffff, 0x83ffffff, 0x0, 0x0, 0xffffffff, 0xc0ffffff, 0x0, 0x0, 0xfeeff06f, 0x870fffff, 0x1ff00ff, 0xffffffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xfe3fffff, 0xff3fffff, 0xff07ffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xfffc3fff, 0xffff, 0xffffffff, 0xdfffffff, 0xffff0003, 0x3ff01ff, 0xffffffff, 0xffdfffff, 0xf, 0x0, 0xffffffff, 0xffffffff, 0x3ff01ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffff, 0x3ff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xf0007, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x1ffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffff001f, 0x7fffffff, 0xffff8000, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xffffffff, 0xfffffe7f, 0xffffffff, 0xf807ffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x0, 0xffffffff, 0xffffffff, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0x7fffff, 0x3ffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, 0xffffffef, 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff3f, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffcfff, 0xffffffff, 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, 0xffffbff, 0xffffbee, 0x0, 0x30000, 0xffffffff, 0xffff0fff, 0xffffffff, 0xffffffff, 0xfffff, 0x7ffe7fff, 0xfffefffe, 0x0, 0xffff07ff, 0xffff7fff, 0xffffffff, 0xffff0fff, 0x7ffffff, 0x0, 0x0, 0xffffffc0, 0xffff0007, 0x7ffffff, 0x301ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffbf0001, 0xffffffff, 0x1fffffff, 0xfffff, 0xffffffff, 0x7df, 0x1ffff, 0xffffffff, 0x7fffffff, 0xfffffffd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1effffff, 0xffffffff, 0x3fffffff, 0xffff000f, 0xff, 0x0, 0x0, 0x0, 0xf8000000, 0xffffffff, 0xffffffff, 0xffe1, 0x0, 0xffffffff, 0xffffffff, 0x3f, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffff, 0x0, 0xffffffff, 0x1fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); +enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)([0x0, 0x40, 0x170], + [0x100, 0x260, 0x6100], [0x3020100, 0x7060504, 0xb0a0908, 0xe0d0c0a, + 0x3030303, 0x100a0f03, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, + 0xa0a0a11, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0xa0a0a0a, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x10000, 0x20001, 0x30001, 0x50004, 0x70006, 0x90008, 0xb000a, + 0xd000c, 0x1000e, 0x10000f, 0x10001, 0x120011, 0x140013, 0x160015, + 0x180017, 0x190001, 0x1b001a, 0x1c0001, 0x1001d, 0x1e0001, 0x10001, + 0x1f0001, 0x210020, 0x230022, 0x250024, 0x10026, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x270001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x280001, 0x10001, 0x10001, + 0x10029, 0x2b002a, 0x2d002c, 0x2f002e, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, + 0x10001, 0x10001, 0x300001, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10031, 0x330032, + 0x340001, 0x360035, 0x380037, 0x3a0039, 0x31003b, 0x310031, 0x3d003c, + 0x3f003e, 0x310040, 0x310041, 0x430042, 0x310031, 0x310031, 0x310044, + 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x450001, 0x310046, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x10001, + 0x310047, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x10001, 0x310048, + 0x310031, 0x490031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x31004a, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x4c004b, + 0x4e004d, 0x50004f, 0x520051, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310053, 0x550054, 0x570056, 0x590058, 0x5b005a, 0x310031, 0x310031, + 0x310031, 0x310031, 0x10001, 0x10001, 0x10001, 0x1005c, 0x10001, + 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x10001, 0x5d0001, + 0x31005e, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x10001, 0x31005e, 0x310031, 0x310031, 0x5f0031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, 0x310031, + 0x310031, 0x310031, 0x310031, 0x0, 0xffffffff, 0xffffffff, 0x7fffffff, + 0x0, 0xffffdfff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x7cffffff, 0xffffd7f0, 0xfffffffb, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffe00ff, 0xfe7fffff, 0xfffffffe, + 0xfffe86ff, 0xffffffff, 0xffff00ff, 0x1f07ff, 0xcfffffc0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xdfffffff, 0xffffffff, + 0xffff3fff, 0xffffffff, 0xffffe7ff, 0xffffffff, 0xffffffff, 0x3ffff, + 0xffffffff, 0x7ffffff, 0xffffffff, 0x7fff3fff, 0x4fffffff, 0x0, 0x0, + 0x1ffd, 0x0, 0x7ffffff0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfeffffff, 0xfff99fee, 0xf3c5fdff, 0xb080799f, 0xfffffcf, 0xfff987ee, + 0xd36dfdff, 0x5e023987, 0x3fffc0, 0xfffbbfee, 0xf3edfdff, 0x13bbf, + 0x3ffcf, 0xfff99fee, 0xf3edfdff, 0xb0c0399f, 0xffffcf, 0xd63dc7ec, + 0xc3ffc718, 0x813dc7, 0x7ffffc0, 0xfffddfee, 0xe3effdff, 0x3603ddf, + 0xff00ffcf, 0xfffddfec, 0xf3effdff, 0x40603ddf, 0x6ffcf, 0xfffddfec, + 0xe7ffffff, 0x807ddf, 0xfe3fffcf, 0xfc7fffec, 0x2ffbffff, 0xff5f847f, + 0x1c0000, 0xfffffffe, 0x87ffffff, 0xfffffff, 0x0, 0xfef02596, + 0x3bffecae, 0xf3ff3f5f, 0x0, 0xffffffff, 0xffffffff, 0xfffffeff, + 0xfffe1fff, 0xfeffffff, 0xdfffffff, 0x7ffdfff, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff20bf, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3d7f3dff, 0xffffffff, 0xffff3dff, + 0x7f3dffff, 0xff7fff3d, 0xffffffff, 0xff3dffff, 0xffffffff, 0xe7ffffff, + 0x1fffffff, 0x3ffffff, 0xffffffff, 0xffffffff, 0x1fffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1fffffff, 0xffffffff, 0xffffffff, + 0x1ffff, 0x1fdfff, 0x7fffff, 0xfffff, 0xddfff, 0xffffffff, 0xffffffff, + 0x3fffffff, 0x3ff03ff, 0x3ff3fff, 0xffffffff, 0xffffffff, 0xffffff, + 0xffffffff, 0xffff07ff, 0xffffffff, 0x3fffff, 0x1fffffff, 0xfff0fff, + 0xfffffff1, 0x1f3fff, 0xffffffff, 0xffff0fff, 0xc7ff03ff, 0xffffffff, + 0xcfffffff, 0xffffffff, 0x7fffffff, 0x9fffffff, 0x3ff03ff, 0x3fff, 0x0, + 0x0, 0xffffffff, 0xffffffff, 0xffff0fff, 0x1fffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xf00fffff, 0xffffffff, 0xf8ffffff, 0xffffe3ff, + 0xffffffff, 0x0, 0x0, 0xffff00ff, 0x7fffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf000007f, + 0x3f3fffff, 0xffffffff, 0xaaff3f3f, 0x3fffffff, 0xffffffff, 0xffdfffff, + 0xefcfffdf, 0x7fdcffff, 0xffff07ff, 0xffff80ff, 0xffffffff, 0xfff30000, + 0x1fff7fff, 0x7ffffff, 0xffff0000, 0x1ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff03ff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xfffff, 0xffffffff, 0x7f, 0x7ff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3ff1fff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffff7fff, + 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfe0fffff, + 0xffffffff, 0xffff20bf, 0xffffffff, 0x800180ff, 0x7fffff, 0x7f7f7f7f, + 0x7f7f7f7f, 0xffffffff, 0xffffffff, 0xfffffff, 0x0, 0x0, 0xfbffffff, + 0xffffffff, 0xffffffff, 0xfffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xfff0000, 0xffffffff, + 0xffffffff, 0xfffffffe, 0xffffffff, 0xfe7fffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffe0, 0xfffe3fff, 0xffffffff, 0xffffffff, 0xffff7fff, + 0x7ffffff, 0xffffffff, 0xffff000f, 0x7fffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x1fff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff1fff, 0xffffffff, 0xffff007f, 0xffffffff, 0xffffffff, + 0xfff, 0xffffffff, 0xffffffff, 0x80ffffff, 0xffffffff, 0xffffffff, + 0xffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf7fff, + 0x7ff, 0x0, 0xff000000, 0xffffffff, 0x3ff0fff, 0xffffffff, 0xffffff, + 0xffffffff, 0xffffffff, 0x3ffc01f, 0xfffffff, 0xffffffff, 0xffffffff, + 0x800fffff, 0x1fffffff, 0xffffffff, 0xffffffff, 0xc3ffbfff, 0x0, + 0xffffffff, 0x7fffff, 0xf3ff3fff, 0xfffffff, 0xffffffff, 0xffffffff, + 0xf8000007, 0x7fffff, 0x7e7e7e, 0x7f7f, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0x3ff3fff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff000f, 0xfffff87f, 0xfffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff3fff, 0xffffffff, + 0xffffffff, 0x3ffffff, 0x0, 0xe0f8007f, 0x5f7fffff, 0xffffffdb, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfff80003, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0000, 0xffffffff, 0xfffcffff, 0xffffffff, 0xff, + 0x3fff0000, 0x3ffffff, 0xffff007f, 0xfff7ffff, 0xffdf0f7f, 0xffffffff, + 0xffffffff, 0xffffffff, 0x1fffffff, 0xfffffffe, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x7fffffff, 0x1cfcfcfc, 0x30007f7f, 0xffffefff, + 0xb7ffff7f, 0x3fff3fff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7ffffff, 0xffffff87, 0xff8fffff, 0xffffffff, 0xffffffff, 0xfff07ff, + 0x0, 0xffff0000, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x1fffffff, + 0xffffffff, 0x1ffff, 0x0, 0x7fffffff, 0xffff000f, 0x7ff, 0x0, + 0xbfffffff, 0xffffffff, 0x3fff0f, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x3fffffff, 0x3ff, 0x0, 0x0, 0xfffffd3f, + 0x91bfffff, 0xffbfffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8fffffff, + 0x83ffffff, 0x0, 0x0, 0xffffffff, 0xc0ffffff, 0x0, 0x0, 0xfeeff06f, + 0x870fffff, 0x1ff00ff, 0xffffffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xfe3fffff, 0xff3fffff, 0xff07ffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0x1ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7fffffff, + 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xfffc3fff, 0xffff, + 0xffffffff, 0xdfffffff, 0xffff0003, 0x3ff01ff, 0xffffffff, 0xffdfffff, + 0xf, 0x0, 0xffffffff, 0xffffffff, 0x3ff01ff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffff, 0x3ff, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0x7fff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xf0007, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0x7fff, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xffffffff, 0x1ffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0xffff001f, 0x7fffffff, 0xffff8000, 0x0, 0x0, + 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffff, + 0xffffffff, 0xfffffe7f, 0xffffffff, 0xf807ffff, 0xffffffff, 0xffffffff, + 0x3fffffff, 0x0, 0xffffffff, 0xffffffff, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0x7fffff, 0x3ffff, 0x0, 0x0, 0x0, 0x0, + 0xffffffff, 0xffffffff, 0xffdfffff, 0xffffffff, 0xdfffffff, 0xebffde64, + 0xffffffef, 0xffffffff, 0xdfdfe7bf, 0x7bffffff, 0xfffdfc5f, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff3f, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffcfff, 0xffffffff, 0xffffffef, 0xaf7fe96, 0xaa96ea84, 0x5ef7f796, + 0xffffbff, 0xffffbee, 0x0, 0x30000, 0xffffffff, 0xffff0fff, 0xffffffff, + 0xffffffff, 0xfffff, 0x7ffe7fff, 0xfffefffe, 0x0, 0xffff07ff, + 0xffff7fff, 0xffffffff, 0xffff0fff, 0x7ffffff, 0x0, 0x0, 0xffffffc0, + 0xffff0007, 0x7ffffff, 0x301ff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffbf0001, 0xffffffff, 0x1fffffff, 0xfffff, 0xffffffff, 0x7df, + 0x1ffff, 0xffffffff, 0x7fffffff, 0xfffffffd, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x1effffff, 0xffffffff, 0x3fffffff, 0xffff000f, + 0xff, 0x0, 0x0, 0x0, 0xf8000000, 0xffffffff, 0xffffffff, 0xffe1, 0x0, + 0xffffffff, 0xffffffff, 0x3f, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xfffff, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffff, 0x0, 0xffffffff, + 0x1fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3fffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); //3664 bytes -enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)([ 0x0, 0x20, 0x98, 0x208], [ 0x80, 0xf0, 0x2e0, 0x3180], [ 0x3020100, 0x7060504, 0xa090808, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0c, 0xd080808, 0xd080808, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xb000a, 0xd000c, 0xd000d, 0xd000d, 0xe000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xf000d, 0x10000d, 0xd0011, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x12000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x140013, 0x160015, 0x180017, 0x1a0019, 0x1b001b, 0x1d001c, 0x1b001b, 0x1e000d, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x20001f, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b0021, 0x1b001b, 0x1b001b, 0x1b001b, 0x230022, 0x1b001b, 0x1b001b, 0x24001b, 0x260025, 0x1b001b, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x27000d, 0xd000d, 0x28000d, 0x1b0029, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b002a, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b002b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x2c000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x2c000d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x2, 0x0, 0x0, 0x40003, 0x60005, 0x7, 0x0, 0x90008, 0xb000a, 0xd000c, 0xf000e, 0x100000, 0x120011, 0x140013, 0x160015, 0x180017, 0x1a0019, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, 0x270000, 0x290028, 0x0, 0x2a0000, 0x0, 0x0, 0x2b0000, 0x2d002c, 0x2f002e, 0x310030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x330032, 0x350034, 0x360000, 0x380037, 0x3a0039, 0x3c003b, 0x3e003d, 0x40003f, 0x420041, 0x430000, 0x440000, 0x460045, 0x470042, 0x0, 0x480000, 0x0, 0x0, 0x4a0049, 0x4c004b, 0x4d0000, 0x4f004e, 0x0, 0x50, 0x0, 0x0, 0x0, 0x510000, 0x530052, 0x0, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x550000, 0x420042, 0x570056, 0x580000, 0x5a0059, 0x5c005b, 0x42005d, 0x51005e, 0x0, 0x5f0000, 0x540000, 0x60, 0x61, 0x630062, 0x57, 0x640000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x650000, 0x0, 0x670066, 0x0, 0x0, 0x68, 0x380069, 0x0, 0x6b006a, 0x38006c, 0x6d0000, 0x6e0000, 0x6f0000, 0x710070, 0x720000, 0x420073, 0x740042, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x750063, 0x0, 0x0, 0x0, 0x0, 0x760000, 0x770000, 0x790078, 0x7a0000, 0x0, 0x0, 0x7b0000, 0x7d007c, 0x7f007e, 0x800000, 0x54, 0x810064, 0x830082, 0xb0000, 0x84, 0x860085, 0x420042, 0x870032, 0x890088, 0x8b008a, 0x0, 0x42008c, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x8e008d, 0x420042, 0x42008f, 0x420090, 0x920091, 0x420042, 0x940093, 0x420042, 0x950000, 0x420042, 0x420042, 0x420042, 0x960042, 0x420042, 0x420042, 0x420042, 0x970000, 0x980000, 0x99004b, 0x9a0000, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x9b0038, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9c0000, 0x420042, 0x9d0000, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x42009c, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x0, 0x0, 0x0, 0x0, 0x42009e, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x9f0000, 0x4200a0, 0x4200a1, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x0, 0x3a0000, 0xa2, 0xa30000, 0xa40000, 0x420042, 0xa50000, 0x420042, 0xa60000, 0xa800a7, 0xaa00a9, 0x0, 0x0, 0xab, 0x0, 0xac0000, 0x420042, 0x420042, 0x420042, 0x420042, 0xae00ad, 0xb000af, 0x420042, 0x420042, 0x3d, 0xb200b1, 0x3d00b3, 0xb500b4, 0xb700b6, 0x420042, 0xb900b8, 0xbb00ba, 0xbc0064, 0xbd0000, 0xbf00be, 0xc00042, 0xc10000, 0xa40000, 0x510000, 0x420042, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc20000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x0, 0x4200a3, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x0, 0x0, 0x0, 0x0, 0x4200a3, 0x420042, 0x420042, 0x420042, 0xc3, 0x420042, 0x0, 0xc40000, 0x420042, 0x420042, 0x420042, 0x420042, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe0000, 0x0, 0x0, 0x0, 0x83000000, 0x280f, 0x4, 0x0, 0x1ff00, 0x1800000, 0x1, 0x17900, 0x0, 0xff00, 0xffe0f800, 0x20000020, 0x0, 0x4000, 0x0, 0x1800, 0x0, 0x0, 0xfffc0000, 0x0, 0xf8000000, 0x0, 0x8000c000, 0xb0000000, 0xffffffff, 0xffffffff, 0xffffe002, 0xffffffff, 0x8000000f, 0x0, 0x1000000, 0x66011, 0xc3a0200, 0x4f7f8660, 0xf0000030, 0x67811, 0x2c920200, 0xa1fdc678, 0xffc0003f, 0x44011, 0xc120200, 0xfffec440, 0xfffc0030, 0x66011, 0xc120200, 0x4f3fc660, 0xff000030, 0x29c23813, 0x3c0038e7, 0xff7ec238, 0xf800003f, 0x22011, 0x1c100200, 0xfc9fc220, 0xff0030, 0x22013, 0xc100200, 0xbf9fc220, 0xfff90030, 0x22013, 0x18000000, 0xff7f8220, 0x1c00030, 0x3800013, 0xd0040000, 0xa07b80, 0xffe3ffff, 0x1, 0x78000000, 0xf0000000, 0xffffffff, 0x10fda69, 0xc4001351, 0xc00c0a0, 0xffffffff, 0x100, 0x1e000, 0x1000000, 0x20000000, 0xf8002000, 0xffffffff, 0xdf40, 0x0, 0xc280c200, 0x0, 0xc200, 0x80c20000, 0x8000c2, 0x0, 0xc20000, 0x0, 0x18000000, 0xe0000000, 0xfc000000, 0x0, 0x0, 0xffe00000, 0xe0000000, 0x0, 0x0, 0xfffe0000, 0xffe02000, 0xff800000, 0xfff00000, 0xfff22000, 0xc0000000, 0xfc00fc00, 0xfc008000, 0x0, 0x0, 0xff000000, 0x0, 0xf800, 0x0, 0xffc00000, 0xe0000000, 0xf000f000, 0xe, 0xffe0c000, 0x0, 0xf000, 0x3800fc00, 0x0, 0x30000000, 0x0, 0x80000000, 0x60000000, 0xfc00fc00, 0xffffc000, 0xffffffff, 0xffffffff, 0xf000, 0xe0000000, 0x0, 0xff00000, 0x0, 0x7000000, 0x1c00, 0x0, 0xff00, 0xff800000, 0x0, 0xfffff80, 0xc0c00000, 0x0, 0x5500c0c0, 0xc0000000, 0x0, 0x200000, 0x10300020, 0x80230000, 0x0, 0xc0020, 0xe0008000, 0xf8000000, 0xffff, 0xfffe0000, 0xfc00, 0x0, 0x0, 0xfff00000, 0x0, 0xffffff80, 0xfffff800, 0x0, 0x1, 0x0, 0xfc00e000, 0xffffffff, 0x0, 0x8000, 0x80000000, 0x0, 0x0, 0x1f00000, 0x0, 0xdf40, 0x0, 0x7ffe7f00, 0xff800000, 0x80808080, 0x80808080, 0x0, 0x0, 0xf0000000, 0x4000000, 0x0, 0xffc00000, 0xf000ffff, 0x1800000, 0x0, 0x1f, 0x1c000, 0x8000, 0xf8000000, 0x0, 0xfff0, 0x0, 0x80000000, 0xffffe000, 0xffffffff, 0xe000, 0x0, 0xff80, 0x0, 0x0, 0xfffff000, 0x7f000000, 0x0, 0xfff08000, 0xfffff800, 0xffffffff, 0xffffff, 0x0, 0xfc00f000, 0xfc003fe0, 0xf0000000, 0x7ff00000, 0xe0000000, 0x3c004000, 0xffffffff, 0x0, 0xff800000, 0xc00c000, 0xf0000000, 0x7fffff8, 0xff800000, 0xff818181, 0xffff8080, 0x0, 0xfc00c000, 0x780, 0xf0000000, 0x0, 0xc000, 0xfc000000, 0xffffffff, 0x1f07ff80, 0xa0800000, 0x24, 0x0, 0x7fffc, 0x0, 0xffff, 0x0, 0x30000, 0x0, 0xffffff00, 0xc000ffff, 0xfc000000, 0xff80, 0x80000, 0x20f080, 0x0, 0x60000000, 0xe3030303, 0xc1ff8080, 0x1000, 0x48000080, 0xc000c000, 0xffffffff, 0x78, 0x700000, 0xf000f800, 0xffffffff, 0xffff, 0xc0000000, 0xfffe0000, 0xffffffff, 0x80000000, 0xfff0, 0xfffff800, 0xffffffff, 0x40000000, 0x0, 0xffc000f0, 0xffffffff, 0xc0000000, 0xfffffc00, 0x2c0, 0x6e400000, 0x400000, 0xffffffff, 0x70000000, 0x7c000000, 0x0, 0x3f000000, 0x1100f90, 0x78f00000, 0xfe00ff00, 0x0, 0x0, 0x1c00000, 0xc00000, 0xf80000, 0xfffffe00, 0xffffffff, 0xffffffff, 0x80000000, 0x3c000, 0xffff0000, 0xfffc, 0xfc00fe00, 0xfffffff0, 0xffffffff, 0xfc00fe00, 0xffffffff, 0xfffffc00, 0xffffffff, 0x0, 0xffff8000, 0x0, 0xfff0fff8, 0x0, 0xfe000000, 0xffe0, 0x80000000, 0x7fff, 0xffffffff, 0xfffffffc, 0xffffffff, 0x0, 0x180, 0xc0000000, 0xffffffff, 0xffffffc0, 0xffffffff, 0xff800000, 0xfffc0000, 0x200000, 0x0, 0x20000000, 0x1400219b, 0x10, 0x0, 0x20201840, 0x84000000, 0x203a0, 0x0, 0x0, 0xc0, 0x3000, 0x0, 0x10, 0xf5080169, 0x5569157b, 0xa1080869, 0xf0000400, 0xf0000411, 0xffffffff, 0xfffcffff, 0xfff00000, 0x80018000, 0x10001, 0xffffffff, 0xf800, 0x8000, 0xf8000000, 0xffffffff, 0xffffffff, 0x3f, 0xfff8, 0xf8000000, 0xfffcfe00, 0xffffffff, 0x0, 0x40fffe, 0x0, 0xe0000000, 0xfff00000, 0x0, 0xfffff820, 0xfffe0000, 0x2, 0x0, 0x0, 0xe1000000, 0x0, 0xc0000000, 0xfff0, 0xffffff00, 0xffffffff, 0x7ffffff, 0xffff001e, 0xffffffff, 0xff800000, 0xffffffff, 0xfffffffd, 0x0, 0x0, 0xffff0000, 0x0, 0xc0000000]); +enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)([0x0, 0x20, 0x98, + 0x208], [0x80, 0xf0, 0x2e0, 0x3180], [0x3020100, 0x7060504, 0xa090808, + 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, + 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0b, 0xb0b0b0c, + 0xd080808, 0xd080808, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, + 0xb000a, 0xd000c, 0xd000d, 0xd000d, 0xe000d, 0xd000d, 0xd000d, 0xd000d, + 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xf000d, + 0x10000d, 0xd0011, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x12000d, + 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0x140013, 0x160015, 0x180017, + 0x1a0019, 0x1b001b, 0x1d001c, 0x1b001b, 0x1e000d, 0x1b001b, 0x1b001b, + 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x20001f, 0x1b001b, 0x1b001b, + 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b0021, + 0x1b001b, 0x1b001b, 0x1b001b, 0x230022, 0x1b001b, 0x1b001b, 0x24001b, + 0x260025, 0x1b001b, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, + 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, + 0x27000d, 0xd000d, 0x28000d, 0x1b0029, 0x1b001b, 0x1b001b, 0x1b001b, + 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b002a, 0x1b001b, 0x1b001b, + 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b002b, + 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, 0x1b001b, + 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, + 0x2c000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, 0xd000d, + 0xd000d, 0x2c000d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000, 0x2, 0x0, + 0x0, 0x40003, 0x60005, 0x7, 0x0, 0x90008, 0xb000a, 0xd000c, 0xf000e, + 0x100000, 0x120011, 0x140013, 0x160015, 0x180017, 0x1a0019, 0x1c001b, + 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, 0x270000, 0x290028, + 0x0, 0x2a0000, 0x0, 0x0, 0x2b0000, 0x2d002c, 0x2f002e, 0x310030, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x330032, 0x350034, 0x360000, 0x380037, 0x3a0039, + 0x3c003b, 0x3e003d, 0x40003f, 0x420041, 0x430000, 0x440000, 0x460045, + 0x470042, 0x0, 0x480000, 0x0, 0x0, 0x4a0049, 0x4c004b, 0x4d0000, + 0x4f004e, 0x0, 0x50, 0x0, 0x0, 0x0, 0x510000, 0x530052, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x550000, 0x420042, + 0x570056, 0x580000, 0x5a0059, 0x5c005b, 0x42005d, 0x51005e, 0x0, + 0x5f0000, 0x540000, 0x60, 0x61, 0x630062, 0x57, 0x640000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x650000, 0x0, 0x670066, + 0x0, 0x0, 0x68, 0x380069, 0x0, 0x6b006a, 0x38006c, 0x6d0000, 0x6e0000, + 0x6f0000, 0x710070, 0x720000, 0x420073, 0x740042, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x750063, 0x0, 0x0, 0x0, 0x0, 0x760000, 0x770000, + 0x790078, 0x7a0000, 0x0, 0x0, 0x7b0000, 0x7d007c, 0x7f007e, 0x800000, + 0x54, 0x810064, 0x830082, 0xb0000, 0x84, 0x860085, 0x420042, 0x870032, + 0x890088, 0x8b008a, 0x0, 0x42008c, 0x420042, 0x420042, 0x420042, + 0x420042, 0x420042, 0x420042, 0x8e008d, 0x420042, 0x42008f, 0x420090, + 0x920091, 0x420042, 0x940093, 0x420042, 0x950000, 0x420042, 0x420042, + 0x420042, 0x960042, 0x420042, 0x420042, 0x420042, 0x970000, 0x980000, + 0x99004b, 0x9a0000, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, + 0x420042, 0x420042, 0x420042, 0x420042, 0x9b0038, 0x420042, 0x420042, + 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, + 0x420042, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9c0000, 0x420042, 0x9d0000, + 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, + 0x42009c, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, + 0x420042, 0x0, 0x0, 0x0, 0x0, 0x42009e, 0x420042, 0x420042, 0x420042, + 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x9f0000, + 0x4200a0, 0x4200a1, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, + 0x420042, 0x420042, 0x0, 0x3a0000, 0xa2, 0xa30000, 0xa40000, 0x420042, + 0xa50000, 0x420042, 0xa60000, 0xa800a7, 0xaa00a9, 0x0, 0x0, 0xab, 0x0, + 0xac0000, 0x420042, 0x420042, 0x420042, 0x420042, 0xae00ad, 0xb000af, + 0x420042, 0x420042, 0x3d, 0xb200b1, 0x3d00b3, 0xb500b4, 0xb700b6, + 0x420042, 0xb900b8, 0xbb00ba, 0xbc0064, 0xbd0000, 0xbf00be, 0xc00042, + 0xc10000, 0xa40000, 0x510000, 0x420042, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xc20000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x0, 0x4200a3, + 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, 0x420042, + 0x0, 0x0, 0x0, 0x0, 0x4200a3, 0x420042, 0x420042, 0x420042, 0xc3, + 0x420042, 0x0, 0xc40000, 0x420042, 0x420042, 0x420042, 0x420042, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbe0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xbe0000, 0x0, 0x0, 0x0, 0x83000000, 0x280f, 0x4, 0x0, 0x1ff00, + 0x1800000, 0x1, 0x17900, 0x0, 0xff00, 0xffe0f800, 0x20000020, 0x0, + 0x4000, 0x0, 0x1800, 0x0, 0x0, 0xfffc0000, 0x0, 0xf8000000, 0x0, + 0x8000c000, 0xb0000000, 0xffffffff, 0xffffffff, 0xffffe002, 0xffffffff, + 0x8000000f, 0x0, 0x1000000, 0x66011, 0xc3a0200, 0x4f7f8660, 0xf0000030, + 0x67811, 0x2c920200, 0xa1fdc678, 0xffc0003f, 0x44011, 0xc120200, + 0xfffec440, 0xfffc0030, 0x66011, 0xc120200, 0x4f3fc660, 0xff000030, + 0x29c23813, 0x3c0038e7, 0xff7ec238, 0xf800003f, 0x22011, 0x1c100200, + 0xfc9fc220, 0xff0030, 0x22013, 0xc100200, 0xbf9fc220, 0xfff90030, + 0x22013, 0x18000000, 0xff7f8220, 0x1c00030, 0x3800013, 0xd0040000, + 0xa07b80, 0xffe3ffff, 0x1, 0x78000000, 0xf0000000, 0xffffffff, + 0x10fda69, 0xc4001351, 0xc00c0a0, 0xffffffff, 0x100, 0x1e000, + 0x1000000, 0x20000000, 0xf8002000, 0xffffffff, 0xdf40, 0x0, 0xc280c200, + 0x0, 0xc200, 0x80c20000, 0x8000c2, 0x0, 0xc20000, 0x0, 0x18000000, + 0xe0000000, 0xfc000000, 0x0, 0x0, 0xffe00000, 0xe0000000, 0x0, 0x0, + 0xfffe0000, 0xffe02000, 0xff800000, 0xfff00000, 0xfff22000, 0xc0000000, + 0xfc00fc00, 0xfc008000, 0x0, 0x0, 0xff000000, 0x0, 0xf800, 0x0, + 0xffc00000, 0xe0000000, 0xf000f000, 0xe, 0xffe0c000, 0x0, 0xf000, + 0x3800fc00, 0x0, 0x30000000, 0x0, 0x80000000, 0x60000000, 0xfc00fc00, + 0xffffc000, 0xffffffff, 0xffffffff, 0xf000, 0xe0000000, 0x0, 0xff00000, + 0x0, 0x7000000, 0x1c00, 0x0, 0xff00, 0xff800000, 0x0, 0xfffff80, + 0xc0c00000, 0x0, 0x5500c0c0, 0xc0000000, 0x0, 0x200000, 0x10300020, + 0x80230000, 0x0, 0xc0020, 0xe0008000, 0xf8000000, 0xffff, 0xfffe0000, + 0xfc00, 0x0, 0x0, 0xfff00000, 0x0, 0xffffff80, 0xfffff800, 0x0, 0x1, + 0x0, 0xfc00e000, 0xffffffff, 0x0, 0x8000, 0x80000000, 0x0, 0x0, + 0x1f00000, 0x0, 0xdf40, 0x0, 0x7ffe7f00, 0xff800000, 0x80808080, + 0x80808080, 0x0, 0x0, 0xf0000000, 0x4000000, 0x0, 0xffc00000, + 0xf000ffff, 0x1800000, 0x0, 0x1f, 0x1c000, 0x8000, 0xf8000000, 0x0, + 0xfff0, 0x0, 0x80000000, 0xffffe000, 0xffffffff, 0xe000, 0x0, 0xff80, + 0x0, 0x0, 0xfffff000, 0x7f000000, 0x0, 0xfff08000, 0xfffff800, + 0xffffffff, 0xffffff, 0x0, 0xfc00f000, 0xfc003fe0, 0xf0000000, + 0x7ff00000, 0xe0000000, 0x3c004000, 0xffffffff, 0x0, 0xff800000, + 0xc00c000, 0xf0000000, 0x7fffff8, 0xff800000, 0xff818181, 0xffff8080, + 0x0, 0xfc00c000, 0x780, 0xf0000000, 0x0, 0xc000, 0xfc000000, + 0xffffffff, 0x1f07ff80, 0xa0800000, 0x24, 0x0, 0x7fffc, 0x0, 0xffff, + 0x0, 0x30000, 0x0, 0xffffff00, 0xc000ffff, 0xfc000000, 0xff80, 0x80000, + 0x20f080, 0x0, 0x60000000, 0xe3030303, 0xc1ff8080, 0x1000, 0x48000080, + 0xc000c000, 0xffffffff, 0x78, 0x700000, 0xf000f800, 0xffffffff, 0xffff, + 0xc0000000, 0xfffe0000, 0xffffffff, 0x80000000, 0xfff0, 0xfffff800, + 0xffffffff, 0x40000000, 0x0, 0xffc000f0, 0xffffffff, 0xc0000000, + 0xfffffc00, 0x2c0, 0x6e400000, 0x400000, 0xffffffff, 0x70000000, + 0x7c000000, 0x0, 0x3f000000, 0x1100f90, 0x78f00000, 0xfe00ff00, 0x0, + 0x0, 0x1c00000, 0xc00000, 0xf80000, 0xfffffe00, 0xffffffff, 0xffffffff, + 0x80000000, 0x3c000, 0xffff0000, 0xfffc, 0xfc00fe00, 0xfffffff0, + 0xffffffff, 0xfc00fe00, 0xffffffff, 0xfffffc00, 0xffffffff, 0x0, + 0xffff8000, 0x0, 0xfff0fff8, 0x0, 0xfe000000, 0xffe0, 0x80000000, + 0x7fff, 0xffffffff, 0xfffffffc, 0xffffffff, 0x0, 0x180, 0xc0000000, + 0xffffffff, 0xffffffc0, 0xffffffff, 0xff800000, 0xfffc0000, 0x200000, + 0x0, 0x20000000, 0x1400219b, 0x10, 0x0, 0x20201840, 0x84000000, + 0x203a0, 0x0, 0x0, 0xc0, 0x3000, 0x0, 0x10, 0xf5080169, 0x5569157b, + 0xa1080869, 0xf0000400, 0xf0000411, 0xffffffff, 0xfffcffff, 0xfff00000, + 0x80018000, 0x10001, 0xffffffff, 0xf800, 0x8000, 0xf8000000, + 0xffffffff, 0xffffffff, 0x3f, 0xfff8, 0xf8000000, 0xfffcfe00, + 0xffffffff, 0x0, 0x40fffe, 0x0, 0xe0000000, 0xfff00000, 0x0, + 0xfffff820, 0xfffe0000, 0x2, 0x0, 0x0, 0xe1000000, 0x0, 0xc0000000, + 0xfff0, 0xffffff00, 0xffffffff, 0x7ffffff, 0xffff001e, 0xffffffff, + 0xff800000, 0xffffffff, 0xfffffffd, 0x0, 0x0, 0xffff0000, 0x0, 0xc0000000]); enum MAX_SIMPLE_LOWER = 1043; enum MAX_SIMPLE_UPPER = 1051; enum MAX_SIMPLE_TITLE = 1055; //8192 bytes -enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xc00], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, 0x1e0000, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x41bffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, 0xffff0031, 0x330032, 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, 0x3cffff, 0x3dffff, 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, 0x43ffff, 0x44ffff, 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, 0x4affff, 0x4bffff, 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, 0x51ffff, 0x52041d, 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, 0xffff0056, 0xffff0057, 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, 0xffff005c, 0x43a005d, 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, 0x62ffff, 0x63ffff, 0x64ffff, 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, 0x69ffff, 0x6affff, 0x6bffff, 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, 0x70ffff, 0x71ffff, 0x72ffff, 0x73ffff, 0x74ffff, 0xffffffff, 0xffff0075, 0xffff0076, 0x780077, 0xffff0079, 0x7affff, 0x7bffff, 0xffffffff, 0xffff007c, 0xffffffff, 0xffff007d, 0xffffffff, 0xffffffff, 0xffff007e, 0x7fffff, 0xffffffff, 0x80ffff, 0xffff0081, 0xffffffff, 0xffff0082, 0x83ffff, 0x84ffff, 0x85ffff, 0xffffffff, 0xffff0086, 0xffffffff, 0x87ffff, 0xffffffff, 0xffff0088, 0xffffffff, 0xffff0089, 0xffff008a, 0x8bffff, 0xffffffff, 0x8cffff, 0x8dffff, 0xffffffff, 0xffffffff, 0x8effff, 0xffff008f, 0x910090, 0x92ffff, 0xffff0093, 0xffff0094, 0xffff0095, 0xffff0096, 0xffff0097, 0xffff0098, 0xffff0099, 0xffff009a, 0x9c009b, 0x9dffff, 0x9effff, 0x9fffff, 0xa0ffff, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xffff0442, 0xa700a6, 0xa8ffff, 0xffffffff, 0xa9ffff, 0xaaffff, 0xabffff, 0xacffff, 0xadffff, 0xaeffff, 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, 0xffffffff, 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, 0xc1ffff, 0xc2ffff, 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00c6, 0xc7ffff, 0xffff00c8, 0xffff00c9, 0xffffffff, 0xcaffff, 0xcbffff, 0xccffff, 0xcdffff, 0xceffff, 0xd000cf, 0xd200d1, 0xffff00d3, 0xd500d4, 0xd6ffff, 0xd7ffff, 0xffffffff, 0xffffffff, 0xffff00d8, 0xd9ffff, 0xdaffff, 0xffff00db, 0xdd00dc, 0xdeffff, 0xffffffff, 0xdfffff, 0xe0ffff, 0xffff00e1, 0xe2ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xe3ffff, 0xffffffff, 0xffff00e4, 0xe5ffff, 0xffffffff, 0xffffffff, 0xe700e6, 0xe900e8, 0xffff00ea, 0xffffffff, 0xffffffff, 0xffff00eb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xecffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xedffff, 0xeeffff, 0xffffffff, 0xefffff, 0xffffffff, 0xf0ffff, 0xf200f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff043c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf400f3, 0xf600f5, 0xf7043f, 0xf900f8, 0xfb00fa, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, 0x1110110, 0x1130112, 0xffff0114, 0x1160115, 0xffffffff, 0x117ffff, 0x1190118, 0x11affff, 0x11bffff, 0x11cffff, 0x11dffff, 0x11effff, 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, 0x123ffff, 0x124ffff, 0x125ffff, 0x1270126, 0xffff0128, 0x129ffff, 0xffffffff, 0xffff012a, 0x12bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x12d012c, 0x12f012e, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, 0x1530152, 0x1550154, 0x1570156, 0x1590158, 0x15b015a, 0x15cffff, 0x15dffff, 0x15effff, 0x15fffff, 0x160ffff, 0x161ffff, 0x162ffff, 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, 0x187ffff, 0xffffffff, 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, 0xffff018d, 0x18f018e, 0x190ffff, 0x191ffff, 0x192ffff, 0x193ffff, 0x194ffff, 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, 0x1b9ffff, 0x1baffff, 0x1bbffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1bcffff, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, 0x42e01e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1e2ffff, 0xffffffff, 0x1e3ffff, 0xffffffff, 0x1e4ffff, 0x1e5ffff, 0x1e6ffff, 0x1e7ffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, 0x4460444, 0x44a0448, 0x22f044c, 0xffffffff, 0xffffffff, 0x230ffff, 0x231ffff, 0x232ffff, 0x233ffff, 0x234ffff, 0x235ffff, 0x236ffff, 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, 0x25fffff, 0x2610260, 0x2630262, 0x2650264, 0x2670266, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2690268, 0x26b026a, 0x26d026c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x26f026e, 0x2710270, 0x2730272, 0x2750274, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2770276, 0x2790278, 0x27b027a, 0x27d027c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27f027e, 0x2810280, 0x2830282, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x284044e, 0x2850450, 0x2860453, 0x2870456, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2890288, 0x28b028a, 0x28d028c, 0x28f028e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2910290, 0x2930292, 0x2950294, 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, 0xffffffff, 0x4790477, 0x47d047b, 0x481047f, 0x4850483, 0x4890487, 0x48d048b, 0x491048f, 0x4950493, 0x4990497, 0x49d049b, 0x4a1049f, 0x4a504a3, 0x4a904a7, 0x4ad04ab, 0x4b104af, 0x4b504b3, 0x4b904b7, 0x4bd04bb, 0x4c104bf, 0x4c504c3, 0x4c904c7, 0x4cd04cb, 0x4d104cf, 0x4d504d3, 0x2b702b6, 0x4d704e3, 0xffff04e5, 0x4ef0459, 0xffffffff, 0xffffffff, 0xffff04d9, 0xffff02b9, 0xffffffff, 0x4db04e7, 0xffff04e9, 0x4f2045b, 0xffffffff, 0xffffffff, 0xffff04dd, 0xffffffff, 0x2bc02bb, 0x460045d, 0xffffffff, 0x4650463, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2be02bd, 0x46b0468, 0x2bf046e, 0x4720470, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4df04eb, 0xffff04ed, 0x4f50475, 0xffffffff, 0xffffffff, 0xffff04e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02c1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c302c2, 0x2c502c4, 0x2c702c6, 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, 0x2d102d0, 0xffffffff, 0xffffffff, 0xffff02d2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d402d3, 0x2d602d5, 0x2d802d7, 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ee02ed, 0x2f002ef, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0xffff031b, 0x31cffff, 0xffffffff, 0x31dffff, 0xffff031e, 0xffff031f, 0xffff0320, 0xffff0321, 0xffffffff, 0xffffffff, 0x322ffff, 0xffffffff, 0xffff0323, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x324ffff, 0x325ffff, 0x326ffff, 0x327ffff, 0x328ffff, 0x329ffff, 0x32affff, 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, 0x355ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0356, 0xffff0357, 0xffffffff, 0x358ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x35a0359, 0x35c035b, 0x35e035d, 0x360035f, 0x3620361, 0x3640363, 0x3660365, 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, 0x370036f, 0x3720371, 0x3740373, 0x3760375, 0x3780377, 0x37a0379, 0x37c037b, 0x37e037d, 0x37fffff, 0xffffffff, 0xffffffff, 0x380ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x381ffff, 0x382ffff, 0x383ffff, 0x384ffff, 0x385ffff, 0x386ffff, 0x387ffff, 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x398ffff, 0x399ffff, 0x39affff, 0x39bffff, 0x39cffff, 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, 0x3a3ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0xffffffff, 0x3abffff, 0x3acffff, 0x3adffff, 0x3aeffff, 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ca, 0xffff03cb, 0x3ccffff, 0x3cdffff, 0x3ceffff, 0x3cfffff, 0x3d0ffff, 0xffffffff, 0xffffffff, 0xffff03d1, 0xffffffff, 0x3d2ffff, 0x3d3ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d4ffff, 0x3d5ffff, 0x3d6ffff, 0x3d7ffff, 0x3d8ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x420041e, 0x4240422, 0x42a0427, 0xffff042c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x430ffff, 0x4340432, 0x4380436, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d9ffff, 0x3db03da, 0x3dd03dc, 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0x3eb03ea, 0x3ed03ec, 0x3ef03ee, 0x3f103f0, 0xffff03f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0x4140413, 0x4160415, 0x4180417, 0x41a0419, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, 0x200], + [0x100, 0x380, 0xc00], [0x2020100, 0x4020302, 0x2020205, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, + 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, + 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, 0x1e0000, 0x1f, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x25, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, + 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, + 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, + 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x41bffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, + 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, 0xffff0031, 0x330032, + 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, 0x3cffff, 0x3dffff, + 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, 0x43ffff, 0x44ffff, + 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, 0x4affff, 0x4bffff, + 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, 0x51ffff, 0x52041d, + 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, 0xffff0056, 0xffff0057, + 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, 0xffff005c, 0x43a005d, + 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, 0x62ffff, 0x63ffff, 0x64ffff, + 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, 0x69ffff, 0x6affff, 0x6bffff, + 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, 0x70ffff, 0x71ffff, 0x72ffff, + 0x73ffff, 0x74ffff, 0xffffffff, 0xffff0075, 0xffff0076, 0x780077, + 0xffff0079, 0x7affff, 0x7bffff, 0xffffffff, 0xffff007c, 0xffffffff, + 0xffff007d, 0xffffffff, 0xffffffff, 0xffff007e, 0x7fffff, 0xffffffff, + 0x80ffff, 0xffff0081, 0xffffffff, 0xffff0082, 0x83ffff, 0x84ffff, + 0x85ffff, 0xffffffff, 0xffff0086, 0xffffffff, 0x87ffff, 0xffffffff, + 0xffff0088, 0xffffffff, 0xffff0089, 0xffff008a, 0x8bffff, 0xffffffff, + 0x8cffff, 0x8dffff, 0xffffffff, 0xffffffff, 0x8effff, 0xffff008f, + 0x910090, 0x92ffff, 0xffff0093, 0xffff0094, 0xffff0095, 0xffff0096, + 0xffff0097, 0xffff0098, 0xffff0099, 0xffff009a, 0x9c009b, 0x9dffff, + 0x9effff, 0x9fffff, 0xa0ffff, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, + 0xa5ffff, 0xffff0442, 0xa700a6, 0xa8ffff, 0xffffffff, 0xa9ffff, + 0xaaffff, 0xabffff, 0xacffff, 0xadffff, 0xaeffff, 0xafffff, 0xb0ffff, + 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, 0xb6ffff, 0xb7ffff, + 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, 0xffffffff, 0xbdffff, + 0xbeffff, 0xbfffff, 0xc0ffff, 0xc1ffff, 0xc2ffff, 0xc3ffff, 0xc4ffff, + 0xc5ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00c6, + 0xc7ffff, 0xffff00c8, 0xffff00c9, 0xffffffff, 0xcaffff, 0xcbffff, + 0xccffff, 0xcdffff, 0xceffff, 0xd000cf, 0xd200d1, 0xffff00d3, 0xd500d4, + 0xd6ffff, 0xd7ffff, 0xffffffff, 0xffffffff, 0xffff00d8, 0xd9ffff, + 0xdaffff, 0xffff00db, 0xdd00dc, 0xdeffff, 0xffffffff, 0xdfffff, + 0xe0ffff, 0xffff00e1, 0xe2ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xe3ffff, 0xffffffff, 0xffff00e4, 0xe5ffff, 0xffffffff, 0xffffffff, + 0xe700e6, 0xe900e8, 0xffff00ea, 0xffffffff, 0xffffffff, 0xffff00eb, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xecffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xedffff, 0xeeffff, + 0xffffffff, 0xefffff, 0xffffffff, 0xf0ffff, 0xf200f1, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff043c, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf400f3, 0xf600f5, + 0xf7043f, 0xf900f8, 0xfb00fa, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, + 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, + 0x1110110, 0x1130112, 0xffff0114, 0x1160115, 0xffffffff, 0x117ffff, + 0x1190118, 0x11affff, 0x11bffff, 0x11cffff, 0x11dffff, 0x11effff, + 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, 0x123ffff, 0x124ffff, + 0x125ffff, 0x1270126, 0xffff0128, 0x129ffff, 0xffffffff, 0xffff012a, + 0x12bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x12d012c, 0x12f012e, 0x1310130, + 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, + 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, + 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, 0x1530152, 0x1550154, + 0x1570156, 0x1590158, 0x15b015a, 0x15cffff, 0x15dffff, 0x15effff, + 0x15fffff, 0x160ffff, 0x161ffff, 0x162ffff, 0x163ffff, 0x164ffff, + 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, 0x169ffff, 0x16affff, + 0x16bffff, 0x16cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, 0x171ffff, 0x172ffff, + 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, 0x177ffff, 0x178ffff, + 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0x17effff, + 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, 0x183ffff, 0x184ffff, + 0x185ffff, 0x186ffff, 0x187ffff, 0xffffffff, 0xffff0188, 0xffff0189, + 0xffff018a, 0xffff018b, 0xffff018c, 0xffff018d, 0x18f018e, 0x190ffff, + 0x191ffff, 0x192ffff, 0x193ffff, 0x194ffff, 0x195ffff, 0x196ffff, + 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, 0x19bffff, 0x19cffff, + 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, 0x1a1ffff, 0x1a2ffff, + 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, 0x1a7ffff, 0x1a8ffff, + 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, 0x1adffff, 0x1aeffff, + 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, 0x1b3ffff, 0x1b4ffff, + 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, 0x1b9ffff, 0x1baffff, + 0x1bbffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1bcffff, + 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, + 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, + 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, + 0x42e01e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1e2ffff, 0xffffffff, 0x1e3ffff, + 0xffffffff, 0x1e4ffff, 0x1e5ffff, 0x1e6ffff, 0x1e7ffff, 0x1e8ffff, + 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, 0x1edffff, 0x1eeffff, + 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, 0x1f3ffff, 0x1f4ffff, + 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, 0x1f9ffff, 0x1faffff, + 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, 0x1ffffff, 0x200ffff, + 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, 0x205ffff, 0x206ffff, + 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, 0x20bffff, 0x20cffff, + 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, 0x211ffff, 0x212ffff, + 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, 0x217ffff, 0x218ffff, + 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, 0x21dffff, 0x21effff, + 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, 0x223ffff, 0x224ffff, + 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, 0x229ffff, 0x22affff, + 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, 0x4460444, 0x44a0448, + 0x22f044c, 0xffffffff, 0xffffffff, 0x230ffff, 0x231ffff, 0x232ffff, + 0x233ffff, 0x234ffff, 0x235ffff, 0x236ffff, 0x237ffff, 0x238ffff, + 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, 0x23dffff, 0x23effff, + 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, 0x243ffff, 0x244ffff, + 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, 0x249ffff, 0x24affff, + 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, 0x24fffff, 0x250ffff, + 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, 0x255ffff, 0x256ffff, + 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, 0x25bffff, 0x25cffff, + 0x25dffff, 0x25effff, 0x25fffff, 0x2610260, 0x2630262, 0x2650264, + 0x2670266, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2690268, + 0x26b026a, 0x26d026c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x26f026e, 0x2710270, 0x2730272, 0x2750274, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2770276, 0x2790278, 0x27b027a, + 0x27d027c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27f027e, + 0x2810280, 0x2830282, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x284044e, 0x2850450, 0x2860453, 0x2870456, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2890288, 0x28b028a, 0x28d028c, + 0x28f028e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2910290, + 0x2930292, 0x2950294, 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, + 0xffffffff, 0x4790477, 0x47d047b, 0x481047f, 0x4850483, 0x4890487, + 0x48d048b, 0x491048f, 0x4950493, 0x4990497, 0x49d049b, 0x4a1049f, + 0x4a504a3, 0x4a904a7, 0x4ad04ab, 0x4b104af, 0x4b504b3, 0x4b904b7, + 0x4bd04bb, 0x4c104bf, 0x4c504c3, 0x4c904c7, 0x4cd04cb, 0x4d104cf, + 0x4d504d3, 0x2b702b6, 0x4d704e3, 0xffff04e5, 0x4ef0459, 0xffffffff, + 0xffffffff, 0xffff04d9, 0xffff02b9, 0xffffffff, 0x4db04e7, 0xffff04e9, + 0x4f2045b, 0xffffffff, 0xffffffff, 0xffff04dd, 0xffffffff, 0x2bc02bb, + 0x460045d, 0xffffffff, 0x4650463, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2be02bd, 0x46b0468, 0x2bf046e, 0x4720470, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4df04eb, 0xffff04ed, + 0x4f50475, 0xffffffff, 0xffffffff, 0xffff04e1, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff02c1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c302c2, + 0x2c502c4, 0x2c702c6, 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, + 0x2d102d0, 0xffffffff, 0xffffffff, 0xffff02d2, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d402d3, + 0x2d602d5, 0x2d802d7, 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, + 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ee02ed, + 0x2f002ef, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, + 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0x3040303, 0x3060305, + 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, + 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0xffff031b, 0x31cffff, + 0xffffffff, 0x31dffff, 0xffff031e, 0xffff031f, 0xffff0320, 0xffff0321, + 0xffffffff, 0xffffffff, 0x322ffff, 0xffffffff, 0xffff0323, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x324ffff, 0x325ffff, 0x326ffff, + 0x327ffff, 0x328ffff, 0x329ffff, 0x32affff, 0x32bffff, 0x32cffff, + 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, 0x331ffff, 0x332ffff, + 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, 0x337ffff, 0x338ffff, + 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, 0x33dffff, 0x33effff, + 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, 0x343ffff, 0x344ffff, + 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, 0x349ffff, 0x34affff, + 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, 0x34fffff, 0x350ffff, + 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, 0x355ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0356, 0xffff0357, 0xffffffff, + 0x358ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x35a0359, 0x35c035b, 0x35e035d, 0x360035f, 0x3620361, + 0x3640363, 0x3660365, 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, + 0x370036f, 0x3720371, 0x3740373, 0x3760375, 0x3780377, 0x37a0379, + 0x37c037b, 0x37e037d, 0x37fffff, 0xffffffff, 0xffffffff, 0x380ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x381ffff, 0x382ffff, 0x383ffff, + 0x384ffff, 0x385ffff, 0x386ffff, 0x387ffff, 0x388ffff, 0x389ffff, + 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, 0x38effff, 0x38fffff, + 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, 0x394ffff, 0x395ffff, + 0x396ffff, 0x397ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x398ffff, + 0x399ffff, 0x39affff, 0x39bffff, 0x39cffff, 0x39dffff, 0x39effff, + 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, 0x3a3ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, 0x3a8ffff, 0x3a9ffff, + 0x3aaffff, 0xffffffff, 0x3abffff, 0x3acffff, 0x3adffff, 0x3aeffff, + 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, 0x3b3ffff, 0x3b4ffff, + 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, 0x3b9ffff, 0x3baffff, + 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, 0x3bfffff, 0x3c0ffff, + 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, 0x3c5ffff, 0x3c6ffff, + 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff03ca, 0xffff03cb, 0x3ccffff, 0x3cdffff, + 0x3ceffff, 0x3cfffff, 0x3d0ffff, 0xffffffff, 0xffffffff, 0xffff03d1, + 0xffffffff, 0x3d2ffff, 0x3d3ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3d4ffff, 0x3d5ffff, 0x3d6ffff, + 0x3d7ffff, 0x3d8ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x420041e, 0x4240422, 0x42a0427, 0xffff042c, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x430ffff, 0x4340432, + 0x4380436, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3d9ffff, 0x3db03da, 0x3dd03dc, + 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, + 0x3eb03ea, 0x3ed03ec, 0x3ef03ee, 0x3f103f0, 0xffff03f2, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, + 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, + 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0x4140413, + 0x4160415, 0x4180417, 0x41a0419, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //8064 bytes -enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xbc0], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x20000, 0x40003, 0x60005, 0x80007, 0x0, 0x90000, 0xb000a, 0xd000c, 0xf000e, 0x110010, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x140013, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x160015, 0x180017, 0x1a0019, 0x1c001b, 0x0, 0x0, 0x1e001d, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x210020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x230022, 0x250024, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b001a, 0x1d001c, 0x1f001e, 0x210020, 0x230022, 0x250024, 0x270026, 0x290028, 0x2b002a, 0x2d002c, 0x2f002e, 0xffff0030, 0x320031, 0x340033, 0x360035, 0x4130037, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0038, 0xffff0039, 0xffff003a, 0xffff003b, 0xffff003c, 0xffff003d, 0xffff003e, 0xffff003f, 0xffff0040, 0xffff0041, 0xffff0042, 0xffff0043, 0xffff0044, 0xffff0045, 0xffff0046, 0xffff0047, 0xffff0048, 0xffff0049, 0xffff004a, 0xffff004b, 0xffff004c, 0xffff004d, 0xffff004e, 0xffff004f, 0xffff0414, 0xffff0051, 0xffff0052, 0xffff0053, 0x54ffff, 0x55ffff, 0x56ffff, 0x57ffff, 0x58ffff, 0x59ffff, 0x5affff, 0x5bffff, 0x423ffff, 0xffff005c, 0xffff005d, 0xffff005e, 0xffff005f, 0xffff0060, 0xffff0061, 0xffff0062, 0xffff0063, 0xffff0064, 0xffff0065, 0xffff0066, 0xffff0067, 0xffff0068, 0xffff0069, 0xffff006a, 0xffff006b, 0xffff006c, 0xffff006d, 0xffff006e, 0xffff006f, 0xffff0070, 0xffff0071, 0xffff0072, 0x740073, 0x75ffff, 0x76ffff, 0xffffffff, 0x77ffff, 0xffff0078, 0xffff0079, 0x7b007a, 0x7cffff, 0x7e007d, 0xffffffff, 0x80007f, 0x820081, 0x83ffff, 0xffff0084, 0x860085, 0xffff0087, 0xffffffff, 0x890088, 0x8affff, 0xffff008b, 0xffff008c, 0xffff008d, 0x8f008e, 0x90ffff, 0xffffffff, 0xffff0091, 0x930092, 0x94ffff, 0x960095, 0x97ffff, 0x98ffff, 0xffff0099, 0xffffffff, 0xffff009a, 0xffffffff, 0xffffffff, 0xffffffff, 0x9c009b, 0x9dffff, 0xffff009e, 0xa0009f, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xffffffff, 0xffff00a9, 0xffff00aa, 0xffff00ab, 0xffff00ac, 0xffff00ad, 0xffff00ae, 0xffff00af, 0xffff00b0, 0xffff00b1, 0xb20426, 0xffff00b3, 0xffff00b4, 0xb600b5, 0xffff00b7, 0xffff00b8, 0xffff00b9, 0xffff00ba, 0xffff00bb, 0xffff00bc, 0xffff00bd, 0xffff00be, 0xffff00bf, 0xffff00c0, 0xffff00c1, 0xffff00c2, 0xffff00c3, 0xffff00c4, 0xffff00c5, 0xffff00c6, 0xffff00c7, 0xffff00c8, 0xffff00c9, 0xffff00ca, 0xffff00cb, 0xffff00cc, 0xffff00cd, 0xffff00ce, 0xffff00cf, 0xffff00d0, 0xffff00d1, 0xffff00d2, 0xffff00d3, 0xffff00d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xd600d5, 0xd7ffff, 0xffff00d8, 0xd9ffff, 0xdaffff, 0xdc00db, 0xffff00dd, 0xffff00de, 0xffff00df, 0xffff00e0, 0xffff00e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00e2, 0xffff00e3, 0xffffffff, 0xffff00e4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00e5, 0xe700e6, 0xffff00e8, 0xffff00e9, 0xeb00ea, 0xec0424, 0xee00ed, 0xf000ef, 0xf200f1, 0xf400f3, 0xf600f5, 0xf800f7, 0xfa00f9, 0xfc00fb, 0xfdffff, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0xffffffff, 0xffffffff, 0xffff0425, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x106ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0107, 0xffff0108, 0xffff0109, 0xffff010a, 0xffff010b, 0xffff010c, 0xffff010d, 0xffff010e, 0xffff010f, 0xffff0110, 0xffff0111, 0xffff0112, 0xffffffff, 0xffffffff, 0xffff0113, 0x114ffff, 0x115ffff, 0xffff0116, 0x117ffff, 0x1190118, 0x11b011a, 0x11d011c, 0x11f011e, 0x1210120, 0x1230122, 0x1250124, 0x1270126, 0x1290128, 0x12b012a, 0x12d012c, 0x12f012e, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff014a, 0xffff014b, 0xffff014c, 0xffff014d, 0xffff014e, 0xffff014f, 0xffff0150, 0xffff0151, 0xffff0152, 0xffff0153, 0xffff0154, 0xffff0155, 0xffff0156, 0xffff0157, 0xffff0158, 0xffff0159, 0xffff015a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff015b, 0xffff015c, 0xffff015d, 0xffff015e, 0xffff015f, 0xffff0160, 0xffff0161, 0xffff0162, 0xffff0163, 0xffff0164, 0xffff0165, 0xffff0166, 0xffff0167, 0xffff0168, 0xffff0169, 0xffff016a, 0xffff016b, 0xffff016c, 0xffff016d, 0xffff016e, 0xffff016f, 0xffff0170, 0xffff0171, 0xffff0172, 0xffff0173, 0xffff0174, 0xffff0175, 0x1770176, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0xffffffff, 0xffff017e, 0xffff017f, 0xffff0180, 0xffff0181, 0xffff0182, 0xffff0183, 0xffff0184, 0xffff0185, 0xffff0186, 0xffff0187, 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0xffff0192, 0xffff0193, 0xffff0194, 0xffff0195, 0xffff0196, 0xffff0197, 0xffff0198, 0xffff0199, 0xffff019a, 0xffff019b, 0xffff019c, 0xffff019d, 0xffff019e, 0xffff019f, 0xffff01a0, 0xffff01a1, 0xffff01a2, 0xffff01a3, 0xffff01a4, 0xffff01a5, 0xffff01a6, 0xffff01a7, 0xffff01a8, 0xffff01a9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1aaffff, 0x1ac01ab, 0x1ae01ad, 0x1b001af, 0x1b201b1, 0x1b401b3, 0x1b601b5, 0x1b801b7, 0x1ba01b9, 0x1bc01bb, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0xffff01cf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x41dffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1d101d0, 0x1d301d2, 0x1d501d4, 0x1d701d6, 0x1d901d8, 0x1db01da, 0x1dd01dc, 0x1df01de, 0x1e101e0, 0x1e301e2, 0x1e501e4, 0x1e701e6, 0x1e901e8, 0x1eb01ea, 0x1ed01ec, 0x1ef01ee, 0x1f101f0, 0x1f301f2, 0x1f501f4, 0x1f6ffff, 0xffffffff, 0xffffffff, 0x1f7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff01f8, 0xffff01f9, 0xffff01fa, 0xffff01fb, 0xffff01fc, 0xffff01fd, 0xffff01fe, 0xffff01ff, 0xffff0200, 0xffff0201, 0xffff0202, 0xffff0203, 0xffff0204, 0xffff0205, 0xffff0206, 0xffff0207, 0xffff0208, 0xffff0209, 0xffff020a, 0xffff020b, 0xffff020c, 0xffff020d, 0xffff020e, 0xffff020f, 0xffff0210, 0xffff0211, 0xffff0212, 0xffff0213, 0xffff0214, 0xffff0215, 0xffff0216, 0xffff0217, 0xffff0218, 0xffff0219, 0xffff021a, 0xffff021b, 0xffff021c, 0xffff021d, 0xffff021e, 0xffff021f, 0xffff0220, 0xffff0221, 0xffff0222, 0xffff0223, 0xffff0224, 0xffff0225, 0xffff0226, 0xffff0227, 0xffff0228, 0xffff0229, 0xffff022a, 0xffff022b, 0xffff022c, 0xffff022d, 0xffff022e, 0xffff022f, 0xffff0230, 0xffff0231, 0xffff0232, 0xffff0233, 0xffff0234, 0xffff0235, 0xffff0236, 0xffff0237, 0xffff0238, 0xffff0239, 0xffff023a, 0xffff023b, 0xffff023c, 0xffff023d, 0xffff023e, 0xffff023f, 0xffff0240, 0xffff0241, 0xffff0242, 0x4280427, 0x42a0429, 0xffff042b, 0xffffffff, 0xffff0243, 0xffff0244, 0xffff0245, 0xffff0246, 0xffff0247, 0xffff0248, 0xffff0249, 0xffff024a, 0xffff024b, 0xffff024c, 0xffff024d, 0xffff024e, 0xffff024f, 0xffff0250, 0xffff0251, 0xffff0252, 0xffff0253, 0xffff0254, 0xffff0255, 0xffff0256, 0xffff0257, 0xffff0258, 0xffff0259, 0xffff025a, 0xffff025b, 0xffff025c, 0xffff025d, 0xffff025e, 0xffff025f, 0xffff0260, 0xffff0261, 0xffff0262, 0xffff0263, 0xffff0264, 0xffff0265, 0xffff0266, 0xffff0267, 0xffff0268, 0xffff0269, 0xffff026a, 0xffff026b, 0xffff026c, 0xffff026d, 0xffff026e, 0xffff026f, 0xffff0270, 0xffff0271, 0xffff0272, 0xffff0273, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2750274, 0x2770276, 0x2790278, 0x27b027a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27d027c, 0x27f027e, 0x2810280, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, 0x2850284, 0x2870286, 0x2890288, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x28b028a, 0x28d028c, 0x28f028e, 0x2910290, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2930292, 0x2950294, 0x2970296, 0xffffffff, 0xffff042c, 0xffff042d, 0xffff042e, 0xffff042f, 0x298ffff, 0x299ffff, 0x29affff, 0x29bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x29d029c, 0x29f029e, 0x2a102a0, 0x2a302a2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x43d043c, 0x43f043e, 0x4410440, 0x4430442, 0x4450444, 0x4470446, 0x4490448, 0x44b044a, 0x44d044c, 0x44f044e, 0x4510450, 0x4530452, 0x4550454, 0x4570456, 0x4590458, 0x45b045a, 0x45d045c, 0x45f045e, 0x4610460, 0x4630462, 0x4650464, 0x4670466, 0x4690468, 0x46b046a, 0xffffffff, 0x46c0472, 0xffff0473, 0x4780430, 0x2bd02bc, 0x2bf02be, 0xffff046d, 0xffffffff, 0xffffffff, 0x46e0474, 0xffff0475, 0x4790431, 0x2c202c1, 0x2c402c3, 0xffff046f, 0xffffffff, 0xffffffff, 0x4330432, 0xffffffff, 0x4350434, 0x2c702c6, 0x2c902c8, 0xffffffff, 0xffffffff, 0xffffffff, 0x4370436, 0xffff0438, 0x43a0439, 0x2cb02ca, 0x2cd02cc, 0xffff02ce, 0xffffffff, 0xffffffff, 0x4700476, 0xffff0477, 0x47a043b, 0x2d002cf, 0x2d202d1, 0xffff0471, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02d4, 0xffffffff, 0x2d602d5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02d7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d902d8, 0x2db02da, 0x2dd02dc, 0x2df02de, 0x2e102e0, 0x2e302e2, 0x2e502e4, 0x2e702e6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2e8ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, 0x320031f, 0x3220321, 0x3240323, 0x3260325, 0x3280327, 0x32a0329, 0x32c032b, 0x32e032d, 0x330032f, 0xffff0331, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0332, 0x3340333, 0xffff0335, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33b033a, 0xffff033c, 0xffff033d, 0x33effff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x340033f, 0xffff0341, 0xffff0342, 0xffff0343, 0xffff0344, 0xffff0345, 0xffff0346, 0xffff0347, 0xffff0348, 0xffff0349, 0xffff034a, 0xffff034b, 0xffff034c, 0xffff034d, 0xffff034e, 0xffff034f, 0xffff0350, 0xffff0351, 0xffff0352, 0xffff0353, 0xffff0354, 0xffff0355, 0xffff0356, 0xffff0357, 0xffff0358, 0xffff0359, 0xffff035a, 0xffff035b, 0xffff035c, 0xffff035d, 0xffff035e, 0xffff035f, 0xffff0360, 0xffff0361, 0xffff0362, 0xffff0363, 0xffff0364, 0xffff0365, 0xffff0366, 0xffff0367, 0xffff0368, 0xffff0369, 0xffff036a, 0xffff036b, 0xffff036c, 0xffff036d, 0xffff036e, 0xffff036f, 0xffff0370, 0xffff0371, 0xffff0372, 0xffffffff, 0xffffffff, 0xffffffff, 0x373ffff, 0x374ffff, 0xffffffff, 0xffffffff, 0xffff0375, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0376, 0xffff0377, 0xffff0378, 0xffff0379, 0xffff037a, 0xffff037b, 0xffff037c, 0xffff037d, 0xffff037e, 0xffff037f, 0xffff0380, 0xffff0381, 0xffff0382, 0xffff0383, 0xffff0384, 0xffff0385, 0xffff0386, 0xffff0387, 0xffff0388, 0xffff0389, 0xffff038a, 0xffff038b, 0xffff038c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff038d, 0xffff038e, 0xffff038f, 0xffff0390, 0xffff0391, 0xffff0392, 0xffff0393, 0xffff0394, 0xffff0395, 0xffff0396, 0xffff0397, 0xffff0398, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0399, 0xffff039a, 0xffff039b, 0xffff039c, 0xffff039d, 0xffff039e, 0xffff039f, 0xffffffff, 0xffff03a0, 0xffff03a1, 0xffff03a2, 0xffff03a3, 0xffff03a4, 0xffff03a5, 0xffff03a6, 0xffff03a7, 0xffff03a8, 0xffff03a9, 0xffff03aa, 0xffff03ab, 0xffff03ac, 0xffff03ad, 0xffff03ae, 0xffff03af, 0xffff03b0, 0xffff03b1, 0xffff03b2, 0xffff03b3, 0xffff03b4, 0xffff03b5, 0xffff03b6, 0xffff03b7, 0xffff03b8, 0xffff03b9, 0xffff03ba, 0xffff03bb, 0xffff03bc, 0xffff03bd, 0xffff03be, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0xffff03c2, 0xffff03c3, 0xffff03c4, 0xffff03c5, 0xffff03c6, 0xffffffff, 0x3c7ffff, 0x3c8ffff, 0xffffffff, 0xffff03c9, 0xffff03ca, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03cb, 0xffff03cc, 0xffff03cd, 0xffff03ce, 0xffff03cf, 0xffff03d0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4170416, 0x4190418, 0x41b041a, 0xffff041c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x41effff, 0x420041f, 0x4220421, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d1ffff, 0x3d303d2, 0x3d503d4, 0x3d703d6, 0x3d903d8, 0x3db03da, 0x3dd03dc, 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0xffff03ea, 0xffffffff, 0xffffffff, 0x3ec03eb, 0x3ee03ed, 0x3f003ef, 0x3f203f1, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, 0x200], + [0x100, 0x380, 0xbc0], [0x2020100, 0x4020302, 0x2020205, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x10000, 0x20000, 0x40003, 0x60005, 0x80007, 0x0, 0x90000, 0xb000a, + 0xd000c, 0xf000e, 0x110010, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x140013, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x160015, 0x180017, 0x1a0019, 0x1c001b, 0x0, 0x0, 0x1e001d, 0x1f, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x210020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x230022, 0x250024, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, + 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x2d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, + 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x1b001a, 0x1d001c, 0x1f001e, 0x210020, 0x230022, 0x250024, 0x270026, + 0x290028, 0x2b002a, 0x2d002c, 0x2f002e, 0xffff0030, 0x320031, 0x340033, + 0x360035, 0x4130037, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0038, 0xffff0039, 0xffff003a, 0xffff003b, 0xffff003c, 0xffff003d, + 0xffff003e, 0xffff003f, 0xffff0040, 0xffff0041, 0xffff0042, 0xffff0043, + 0xffff0044, 0xffff0045, 0xffff0046, 0xffff0047, 0xffff0048, 0xffff0049, + 0xffff004a, 0xffff004b, 0xffff004c, 0xffff004d, 0xffff004e, 0xffff004f, + 0xffff0414, 0xffff0051, 0xffff0052, 0xffff0053, 0x54ffff, 0x55ffff, + 0x56ffff, 0x57ffff, 0x58ffff, 0x59ffff, 0x5affff, 0x5bffff, 0x423ffff, + 0xffff005c, 0xffff005d, 0xffff005e, 0xffff005f, 0xffff0060, 0xffff0061, + 0xffff0062, 0xffff0063, 0xffff0064, 0xffff0065, 0xffff0066, 0xffff0067, + 0xffff0068, 0xffff0069, 0xffff006a, 0xffff006b, 0xffff006c, 0xffff006d, + 0xffff006e, 0xffff006f, 0xffff0070, 0xffff0071, 0xffff0072, 0x740073, + 0x75ffff, 0x76ffff, 0xffffffff, 0x77ffff, 0xffff0078, 0xffff0079, + 0x7b007a, 0x7cffff, 0x7e007d, 0xffffffff, 0x80007f, 0x820081, 0x83ffff, + 0xffff0084, 0x860085, 0xffff0087, 0xffffffff, 0x890088, 0x8affff, + 0xffff008b, 0xffff008c, 0xffff008d, 0x8f008e, 0x90ffff, 0xffffffff, + 0xffff0091, 0x930092, 0x94ffff, 0x960095, 0x97ffff, 0x98ffff, + 0xffff0099, 0xffffffff, 0xffff009a, 0xffffffff, 0xffffffff, 0xffffffff, + 0x9c009b, 0x9dffff, 0xffff009e, 0xa0009f, 0xa1ffff, 0xa2ffff, 0xa3ffff, + 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xffffffff, + 0xffff00a9, 0xffff00aa, 0xffff00ab, 0xffff00ac, 0xffff00ad, 0xffff00ae, + 0xffff00af, 0xffff00b0, 0xffff00b1, 0xb20426, 0xffff00b3, 0xffff00b4, + 0xb600b5, 0xffff00b7, 0xffff00b8, 0xffff00b9, 0xffff00ba, 0xffff00bb, + 0xffff00bc, 0xffff00bd, 0xffff00be, 0xffff00bf, 0xffff00c0, 0xffff00c1, + 0xffff00c2, 0xffff00c3, 0xffff00c4, 0xffff00c5, 0xffff00c6, 0xffff00c7, + 0xffff00c8, 0xffff00c9, 0xffff00ca, 0xffff00cb, 0xffff00cc, 0xffff00cd, + 0xffff00ce, 0xffff00cf, 0xffff00d0, 0xffff00d1, 0xffff00d2, 0xffff00d3, + 0xffff00d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xd600d5, 0xd7ffff, + 0xffff00d8, 0xd9ffff, 0xdaffff, 0xdc00db, 0xffff00dd, 0xffff00de, + 0xffff00df, 0xffff00e0, 0xffff00e1, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00e2, 0xffff00e3, 0xffffffff, + 0xffff00e4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff00e5, 0xe700e6, 0xffff00e8, 0xffff00e9, + 0xeb00ea, 0xec0424, 0xee00ed, 0xf000ef, 0xf200f1, 0xf400f3, 0xf600f5, + 0xf800f7, 0xfa00f9, 0xfc00fb, 0xfdffff, 0xff00fe, 0x1010100, 0x1030102, + 0x1050104, 0xffffffff, 0xffffffff, 0xffff0425, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x106ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0107, + 0xffff0108, 0xffff0109, 0xffff010a, 0xffff010b, 0xffff010c, 0xffff010d, + 0xffff010e, 0xffff010f, 0xffff0110, 0xffff0111, 0xffff0112, 0xffffffff, + 0xffffffff, 0xffff0113, 0x114ffff, 0x115ffff, 0xffff0116, 0x117ffff, + 0x1190118, 0x11b011a, 0x11d011c, 0x11f011e, 0x1210120, 0x1230122, + 0x1250124, 0x1270126, 0x1290128, 0x12b012a, 0x12d012c, 0x12f012e, + 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, + 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, + 0x1490148, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff014a, 0xffff014b, 0xffff014c, 0xffff014d, 0xffff014e, + 0xffff014f, 0xffff0150, 0xffff0151, 0xffff0152, 0xffff0153, 0xffff0154, + 0xffff0155, 0xffff0156, 0xffff0157, 0xffff0158, 0xffff0159, 0xffff015a, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff015b, 0xffff015c, + 0xffff015d, 0xffff015e, 0xffff015f, 0xffff0160, 0xffff0161, 0xffff0162, + 0xffff0163, 0xffff0164, 0xffff0165, 0xffff0166, 0xffff0167, 0xffff0168, + 0xffff0169, 0xffff016a, 0xffff016b, 0xffff016c, 0xffff016d, 0xffff016e, + 0xffff016f, 0xffff0170, 0xffff0171, 0xffff0172, 0xffff0173, 0xffff0174, + 0xffff0175, 0x1770176, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, + 0x17cffff, 0x17dffff, 0xffffffff, 0xffff017e, 0xffff017f, 0xffff0180, + 0xffff0181, 0xffff0182, 0xffff0183, 0xffff0184, 0xffff0185, 0xffff0186, + 0xffff0187, 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, + 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0xffff0192, + 0xffff0193, 0xffff0194, 0xffff0195, 0xffff0196, 0xffff0197, 0xffff0198, + 0xffff0199, 0xffff019a, 0xffff019b, 0xffff019c, 0xffff019d, 0xffff019e, + 0xffff019f, 0xffff01a0, 0xffff01a1, 0xffff01a2, 0xffff01a3, 0xffff01a4, + 0xffff01a5, 0xffff01a6, 0xffff01a7, 0xffff01a8, 0xffff01a9, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1aaffff, 0x1ac01ab, 0x1ae01ad, + 0x1b001af, 0x1b201b1, 0x1b401b3, 0x1b601b5, 0x1b801b7, 0x1ba01b9, + 0x1bc01bb, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, + 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0xffff01cf, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x41dffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x1d101d0, 0x1d301d2, 0x1d501d4, 0x1d701d6, 0x1d901d8, + 0x1db01da, 0x1dd01dc, 0x1df01de, 0x1e101e0, 0x1e301e2, 0x1e501e4, + 0x1e701e6, 0x1e901e8, 0x1eb01ea, 0x1ed01ec, 0x1ef01ee, 0x1f101f0, + 0x1f301f2, 0x1f501f4, 0x1f6ffff, 0xffffffff, 0xffffffff, 0x1f7ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff01f8, 0xffff01f9, 0xffff01fa, 0xffff01fb, 0xffff01fc, + 0xffff01fd, 0xffff01fe, 0xffff01ff, 0xffff0200, 0xffff0201, 0xffff0202, + 0xffff0203, 0xffff0204, 0xffff0205, 0xffff0206, 0xffff0207, 0xffff0208, + 0xffff0209, 0xffff020a, 0xffff020b, 0xffff020c, 0xffff020d, 0xffff020e, + 0xffff020f, 0xffff0210, 0xffff0211, 0xffff0212, 0xffff0213, 0xffff0214, + 0xffff0215, 0xffff0216, 0xffff0217, 0xffff0218, 0xffff0219, 0xffff021a, + 0xffff021b, 0xffff021c, 0xffff021d, 0xffff021e, 0xffff021f, 0xffff0220, + 0xffff0221, 0xffff0222, 0xffff0223, 0xffff0224, 0xffff0225, 0xffff0226, + 0xffff0227, 0xffff0228, 0xffff0229, 0xffff022a, 0xffff022b, 0xffff022c, + 0xffff022d, 0xffff022e, 0xffff022f, 0xffff0230, 0xffff0231, 0xffff0232, + 0xffff0233, 0xffff0234, 0xffff0235, 0xffff0236, 0xffff0237, 0xffff0238, + 0xffff0239, 0xffff023a, 0xffff023b, 0xffff023c, 0xffff023d, 0xffff023e, + 0xffff023f, 0xffff0240, 0xffff0241, 0xffff0242, 0x4280427, 0x42a0429, + 0xffff042b, 0xffffffff, 0xffff0243, 0xffff0244, 0xffff0245, 0xffff0246, + 0xffff0247, 0xffff0248, 0xffff0249, 0xffff024a, 0xffff024b, 0xffff024c, + 0xffff024d, 0xffff024e, 0xffff024f, 0xffff0250, 0xffff0251, 0xffff0252, + 0xffff0253, 0xffff0254, 0xffff0255, 0xffff0256, 0xffff0257, 0xffff0258, + 0xffff0259, 0xffff025a, 0xffff025b, 0xffff025c, 0xffff025d, 0xffff025e, + 0xffff025f, 0xffff0260, 0xffff0261, 0xffff0262, 0xffff0263, 0xffff0264, + 0xffff0265, 0xffff0266, 0xffff0267, 0xffff0268, 0xffff0269, 0xffff026a, + 0xffff026b, 0xffff026c, 0xffff026d, 0xffff026e, 0xffff026f, 0xffff0270, + 0xffff0271, 0xffff0272, 0xffff0273, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2750274, 0x2770276, 0x2790278, 0x27b027a, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x27d027c, 0x27f027e, 0x2810280, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, + 0x2850284, 0x2870286, 0x2890288, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x28b028a, 0x28d028c, 0x28f028e, 0x2910290, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2930292, 0x2950294, 0x2970296, + 0xffffffff, 0xffff042c, 0xffff042d, 0xffff042e, 0xffff042f, 0x298ffff, + 0x299ffff, 0x29affff, 0x29bffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x29d029c, 0x29f029e, 0x2a102a0, 0x2a302a2, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x43d043c, 0x43f043e, 0x4410440, 0x4430442, 0x4450444, + 0x4470446, 0x4490448, 0x44b044a, 0x44d044c, 0x44f044e, 0x4510450, + 0x4530452, 0x4550454, 0x4570456, 0x4590458, 0x45b045a, 0x45d045c, + 0x45f045e, 0x4610460, 0x4630462, 0x4650464, 0x4670466, 0x4690468, + 0x46b046a, 0xffffffff, 0x46c0472, 0xffff0473, 0x4780430, 0x2bd02bc, + 0x2bf02be, 0xffff046d, 0xffffffff, 0xffffffff, 0x46e0474, 0xffff0475, + 0x4790431, 0x2c202c1, 0x2c402c3, 0xffff046f, 0xffffffff, 0xffffffff, + 0x4330432, 0xffffffff, 0x4350434, 0x2c702c6, 0x2c902c8, 0xffffffff, + 0xffffffff, 0xffffffff, 0x4370436, 0xffff0438, 0x43a0439, 0x2cb02ca, + 0x2cd02cc, 0xffff02ce, 0xffffffff, 0xffffffff, 0x4700476, 0xffff0477, + 0x47a043b, 0x2d002cf, 0x2d202d1, 0xffff0471, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff02d4, 0xffffffff, 0x2d602d5, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff02d7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d902d8, + 0x2db02da, 0x2dd02dc, 0x2df02de, 0x2e102e0, 0x2e302e2, 0x2e502e4, + 0x2e702e6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2e8ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, + 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, + 0x2fe02fd, 0x30002ff, 0x3020301, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3040303, 0x3060305, 0x3080307, + 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, + 0x3160315, 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, 0x320031f, + 0x3220321, 0x3240323, 0x3260325, 0x3280327, 0x32a0329, 0x32c032b, + 0x32e032d, 0x330032f, 0xffff0331, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0332, 0x3340333, 0xffff0335, + 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33b033a, 0xffff033c, + 0xffff033d, 0x33effff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x340033f, 0xffff0341, 0xffff0342, 0xffff0343, 0xffff0344, 0xffff0345, + 0xffff0346, 0xffff0347, 0xffff0348, 0xffff0349, 0xffff034a, 0xffff034b, + 0xffff034c, 0xffff034d, 0xffff034e, 0xffff034f, 0xffff0350, 0xffff0351, + 0xffff0352, 0xffff0353, 0xffff0354, 0xffff0355, 0xffff0356, 0xffff0357, + 0xffff0358, 0xffff0359, 0xffff035a, 0xffff035b, 0xffff035c, 0xffff035d, + 0xffff035e, 0xffff035f, 0xffff0360, 0xffff0361, 0xffff0362, 0xffff0363, + 0xffff0364, 0xffff0365, 0xffff0366, 0xffff0367, 0xffff0368, 0xffff0369, + 0xffff036a, 0xffff036b, 0xffff036c, 0xffff036d, 0xffff036e, 0xffff036f, + 0xffff0370, 0xffff0371, 0xffff0372, 0xffffffff, 0xffffffff, 0xffffffff, + 0x373ffff, 0x374ffff, 0xffffffff, 0xffffffff, 0xffff0375, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0376, + 0xffff0377, 0xffff0378, 0xffff0379, 0xffff037a, 0xffff037b, 0xffff037c, + 0xffff037d, 0xffff037e, 0xffff037f, 0xffff0380, 0xffff0381, 0xffff0382, + 0xffff0383, 0xffff0384, 0xffff0385, 0xffff0386, 0xffff0387, 0xffff0388, + 0xffff0389, 0xffff038a, 0xffff038b, 0xffff038c, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff038d, 0xffff038e, 0xffff038f, 0xffff0390, 0xffff0391, + 0xffff0392, 0xffff0393, 0xffff0394, 0xffff0395, 0xffff0396, 0xffff0397, + 0xffff0398, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff0399, 0xffff039a, 0xffff039b, 0xffff039c, + 0xffff039d, 0xffff039e, 0xffff039f, 0xffffffff, 0xffff03a0, 0xffff03a1, + 0xffff03a2, 0xffff03a3, 0xffff03a4, 0xffff03a5, 0xffff03a6, 0xffff03a7, + 0xffff03a8, 0xffff03a9, 0xffff03aa, 0xffff03ab, 0xffff03ac, 0xffff03ad, + 0xffff03ae, 0xffff03af, 0xffff03b0, 0xffff03b1, 0xffff03b2, 0xffff03b3, + 0xffff03b4, 0xffff03b5, 0xffff03b6, 0xffff03b7, 0xffff03b8, 0xffff03b9, + 0xffff03ba, 0xffff03bb, 0xffff03bc, 0xffff03bd, 0xffff03be, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, + 0xffff03c2, 0xffff03c3, 0xffff03c4, 0xffff03c5, 0xffff03c6, 0xffffffff, + 0x3c7ffff, 0x3c8ffff, 0xffffffff, 0xffff03c9, 0xffff03ca, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03cb, + 0xffff03cc, 0xffff03cd, 0xffff03ce, 0xffff03cf, 0xffff03d0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x4170416, 0x4190418, 0x41b041a, + 0xffff041c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x41effff, 0x420041f, 0x4220421, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3d1ffff, 0x3d303d2, 0x3d503d4, + 0x3d703d6, 0x3d903d8, 0x3db03da, 0x3dd03dc, 0x3df03de, 0x3e103e0, + 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0xffff03ea, 0xffffffff, + 0xffffffff, 0x3ec03eb, 0x3ee03ed, 0x3f003ef, 0x3f203f1, 0x3f403f3, + 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, + 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, + 0x40e040d, 0x410040f, 0x4120411, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //8192 bytes -enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xc00], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, 0x1e0000, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x41fffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, 0xffff0031, 0x330032, 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, 0x3cffff, 0x3dffff, 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, 0x43ffff, 0x44ffff, 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, 0x4affff, 0x4bffff, 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, 0x51ffff, 0x520421, 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, 0xffff0056, 0xffff0057, 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, 0xffff005c, 0x43e005d, 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, 0x62ffff, 0x63ffff, 0x64ffff, 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, 0x69ffff, 0x6affff, 0x6bffff, 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, 0x70ffff, 0x71ffff, 0x72ffff, 0x73ffff, 0x74ffff, 0xffffffff, 0xffff0075, 0xffff0076, 0x780077, 0xffff0079, 0x7affff, 0x7bffff, 0xffffffff, 0xffff007c, 0xffffffff, 0xffff007d, 0xffffffff, 0xffffffff, 0xffff007e, 0x7fffff, 0xffffffff, 0x80ffff, 0xffff0081, 0xffffffff, 0xffff0082, 0x83ffff, 0x84ffff, 0x85ffff, 0xffffffff, 0xffff0086, 0xffffffff, 0x87ffff, 0xffffffff, 0xffff0088, 0xffffffff, 0xffff0089, 0xffff008a, 0x8bffff, 0xffffffff, 0x8cffff, 0x8dffff, 0xffffffff, 0xffffffff, 0x8f008e, 0x910090, 0x930092, 0x950094, 0xffff0096, 0xffff0097, 0xffff0098, 0xffff0099, 0xffff009a, 0xffff009b, 0xffff009c, 0xffff009d, 0x9f009e, 0xa0ffff, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xa90446, 0xab00aa, 0xacffff, 0xffffffff, 0xadffff, 0xaeffff, 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, 0xffffffff, 0xc1ffff, 0xc2ffff, 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xc6ffff, 0xc7ffff, 0xc8ffff, 0xc9ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00ca, 0xcbffff, 0xffff00cc, 0xffff00cd, 0xffffffff, 0xceffff, 0xcfffff, 0xd0ffff, 0xd1ffff, 0xd2ffff, 0xd400d3, 0xd600d5, 0xffff00d7, 0xd900d8, 0xdaffff, 0xdbffff, 0xffffffff, 0xffffffff, 0xffff00dc, 0xddffff, 0xdeffff, 0xffff00df, 0xe100e0, 0xe2ffff, 0xffffffff, 0xe3ffff, 0xe4ffff, 0xffff00e5, 0xe6ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xe7ffff, 0xffffffff, 0xffff00e8, 0xe9ffff, 0xffffffff, 0xffffffff, 0xeb00ea, 0xed00ec, 0xffff00ee, 0xffffffff, 0xffffffff, 0xffff00ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf0ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf1ffff, 0xf2ffff, 0xffffffff, 0xf3ffff, 0xffffffff, 0xf4ffff, 0xf600f5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0440, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf800f7, 0xfa00f9, 0xfb0443, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, 0x1110110, 0x1130112, 0x1150114, 0x1170116, 0xffff0118, 0x11a0119, 0xffffffff, 0x11bffff, 0x11d011c, 0x11effff, 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, 0x123ffff, 0x124ffff, 0x125ffff, 0x126ffff, 0x127ffff, 0x128ffff, 0x129ffff, 0x12b012a, 0xffff012c, 0x12dffff, 0xffffffff, 0xffff012e, 0x12fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, 0x1530152, 0x1550154, 0x1570156, 0x1590158, 0x15b015a, 0x15d015c, 0x15f015e, 0x160ffff, 0x161ffff, 0x162ffff, 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, 0x187ffff, 0x188ffff, 0x189ffff, 0x18affff, 0x18bffff, 0xffffffff, 0xffff018c, 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0x1930192, 0x194ffff, 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, 0x1b9ffff, 0x1baffff, 0x1bbffff, 0x1bcffff, 0x1bdffff, 0x1beffff, 0x1bfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1c0ffff, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, 0x1e201e1, 0x1e401e3, 0x43201e5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1e6ffff, 0xffffffff, 0x1e7ffff, 0xffffffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, 0x22fffff, 0x230ffff, 0x231ffff, 0x232ffff, 0x44a0448, 0x44e044c, 0x2330450, 0xffffffff, 0xffffffff, 0x234ffff, 0x235ffff, 0x236ffff, 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, 0x25fffff, 0x260ffff, 0x261ffff, 0x262ffff, 0x263ffff, 0x2650264, 0x2670266, 0x2690268, 0x26b026a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x26d026c, 0x26f026e, 0x2710270, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2730272, 0x2750274, 0x2770276, 0x2790278, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27b027a, 0x27d027c, 0x27f027e, 0x2810280, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, 0x2850284, 0x2870286, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2880452, 0x2890454, 0x28a0457, 0x28b045a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x28d028c, 0x28f028e, 0x2910290, 0x2930292, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2950294, 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, 0x29f029e, 0x2a102a0, 0xffffffff, 0x47c047b, 0x47e047d, 0x480047f, 0x4820481, 0x4840483, 0x4860485, 0x4880487, 0x48a0489, 0x48c048b, 0x48e048d, 0x490048f, 0x4920491, 0x4940493, 0x4960495, 0x4980497, 0x49a0499, 0x49c049b, 0x49e049d, 0x4a0049f, 0x4a204a1, 0x4a404a3, 0x4a604a5, 0x4a804a7, 0x4aa04a9, 0x2bb02ba, 0x4ab04b1, 0xffff04b3, 0x4bd045d, 0xffffffff, 0xffffffff, 0xffff04ac, 0xffff02bd, 0xffffffff, 0x4ad04b5, 0xffff04b7, 0x4c0045f, 0xffffffff, 0xffffffff, 0xffff04ae, 0xffffffff, 0x2c002bf, 0x4640461, 0xffffffff, 0x4690467, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c202c1, 0x46f046c, 0x2c30472, 0x4760474, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4af04b9, 0xffff04bb, 0x4c30479, 0xffffffff, 0xffffffff, 0xffff04b0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02c5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c702c6, 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, 0x2d102d0, 0x2d302d2, 0x2d502d4, 0xffffffff, 0xffffffff, 0xffff02d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d802d7, 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, 0xffff031f, 0x320ffff, 0xffffffff, 0x321ffff, 0xffff0322, 0xffff0323, 0xffff0324, 0xffff0325, 0xffffffff, 0xffffffff, 0x326ffff, 0xffffffff, 0xffff0327, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x328ffff, 0x329ffff, 0x32affff, 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, 0x355ffff, 0x356ffff, 0x357ffff, 0x358ffff, 0x359ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff035a, 0xffff035b, 0xffffffff, 0x35cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x35e035d, 0x360035f, 0x3620361, 0x3640363, 0x3660365, 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, 0x370036f, 0x3720371, 0x3740373, 0x3760375, 0x3780377, 0x37a0379, 0x37c037b, 0x37e037d, 0x380037f, 0x3820381, 0x383ffff, 0xffffffff, 0xffffffff, 0x384ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x385ffff, 0x386ffff, 0x387ffff, 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, 0x398ffff, 0x399ffff, 0x39affff, 0x39bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x39cffff, 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, 0x3a3ffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0x3abffff, 0x3acffff, 0x3adffff, 0x3aeffff, 0xffffffff, 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0x3caffff, 0x3cbffff, 0x3ccffff, 0x3cdffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ce, 0xffff03cf, 0x3d0ffff, 0x3d1ffff, 0x3d2ffff, 0x3d3ffff, 0x3d4ffff, 0xffffffff, 0xffffffff, 0xffff03d5, 0xffffffff, 0x3d6ffff, 0x3d7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d8ffff, 0x3d9ffff, 0x3daffff, 0x3dbffff, 0x3dcffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4240422, 0x4280426, 0x42e042b, 0xffff0430, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x434ffff, 0x4380436, 0x43c043a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3ddffff, 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0x3eb03ea, 0x3ed03ec, 0x3ef03ee, 0x3f103f0, 0x3f303f2, 0x3f503f4, 0xffff03f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0x4140413, 0x4160415, 0x4180417, 0x41a0419, 0x41c041b, 0x41e041d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, 0x200], + [0x100, 0x380, 0xc00], [0x2020100, 0x4020302, 0x2020205, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, + 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, + 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, 0x1e0000, 0x1f, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x25, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, + 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2c0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2e002d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, + 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, + 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x41fffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, + 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, 0xffff0031, 0x330032, + 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, 0x3cffff, 0x3dffff, + 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, 0x43ffff, 0x44ffff, + 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, 0x4affff, 0x4bffff, + 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, 0x51ffff, 0x520421, + 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, 0xffff0056, 0xffff0057, + 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, 0xffff005c, 0x43e005d, + 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, 0x62ffff, 0x63ffff, 0x64ffff, + 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, 0x69ffff, 0x6affff, 0x6bffff, + 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, 0x70ffff, 0x71ffff, 0x72ffff, + 0x73ffff, 0x74ffff, 0xffffffff, 0xffff0075, 0xffff0076, 0x780077, + 0xffff0079, 0x7affff, 0x7bffff, 0xffffffff, 0xffff007c, 0xffffffff, + 0xffff007d, 0xffffffff, 0xffffffff, 0xffff007e, 0x7fffff, 0xffffffff, + 0x80ffff, 0xffff0081, 0xffffffff, 0xffff0082, 0x83ffff, 0x84ffff, + 0x85ffff, 0xffffffff, 0xffff0086, 0xffffffff, 0x87ffff, 0xffffffff, + 0xffff0088, 0xffffffff, 0xffff0089, 0xffff008a, 0x8bffff, 0xffffffff, + 0x8cffff, 0x8dffff, 0xffffffff, 0xffffffff, 0x8f008e, 0x910090, + 0x930092, 0x950094, 0xffff0096, 0xffff0097, 0xffff0098, 0xffff0099, + 0xffff009a, 0xffff009b, 0xffff009c, 0xffff009d, 0x9f009e, 0xa0ffff, + 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, + 0xa8ffff, 0xa90446, 0xab00aa, 0xacffff, 0xffffffff, 0xadffff, 0xaeffff, + 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, + 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, + 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, 0xffffffff, 0xc1ffff, 0xc2ffff, + 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xc6ffff, 0xc7ffff, 0xc8ffff, 0xc9ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00ca, 0xcbffff, + 0xffff00cc, 0xffff00cd, 0xffffffff, 0xceffff, 0xcfffff, 0xd0ffff, + 0xd1ffff, 0xd2ffff, 0xd400d3, 0xd600d5, 0xffff00d7, 0xd900d8, 0xdaffff, + 0xdbffff, 0xffffffff, 0xffffffff, 0xffff00dc, 0xddffff, 0xdeffff, + 0xffff00df, 0xe100e0, 0xe2ffff, 0xffffffff, 0xe3ffff, 0xe4ffff, + 0xffff00e5, 0xe6ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xe7ffff, + 0xffffffff, 0xffff00e8, 0xe9ffff, 0xffffffff, 0xffffffff, 0xeb00ea, + 0xed00ec, 0xffff00ee, 0xffffffff, 0xffffffff, 0xffff00ef, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf0ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf1ffff, 0xf2ffff, 0xffffffff, + 0xf3ffff, 0xffffffff, 0xf4ffff, 0xf600f5, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0440, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xf800f7, 0xfa00f9, 0xfb0443, + 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, + 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, 0x1110110, 0x1130112, + 0x1150114, 0x1170116, 0xffff0118, 0x11a0119, 0xffffffff, 0x11bffff, + 0x11d011c, 0x11effff, 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, + 0x123ffff, 0x124ffff, 0x125ffff, 0x126ffff, 0x127ffff, 0x128ffff, + 0x129ffff, 0x12b012a, 0xffff012c, 0x12dffff, 0xffffffff, 0xffff012e, + 0x12fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1310130, 0x1330132, 0x1350134, + 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, + 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, + 0x14f014e, 0x1510150, 0x1530152, 0x1550154, 0x1570156, 0x1590158, + 0x15b015a, 0x15d015c, 0x15f015e, 0x160ffff, 0x161ffff, 0x162ffff, + 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, + 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, 0x16dffff, 0x16effff, + 0x16fffff, 0x170ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, + 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, + 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, + 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, 0x187ffff, 0x188ffff, + 0x189ffff, 0x18affff, 0x18bffff, 0xffffffff, 0xffff018c, 0xffff018d, + 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0x1930192, 0x194ffff, + 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, + 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, + 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, + 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, + 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, + 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, + 0x1b9ffff, 0x1baffff, 0x1bbffff, 0x1bcffff, 0x1bdffff, 0x1beffff, + 0x1bfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1c0ffff, + 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, + 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, 0x1d601d5, 0x1d801d7, + 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, 0x1e201e1, 0x1e401e3, + 0x43201e5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1e6ffff, 0xffffffff, 0x1e7ffff, + 0xffffffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, + 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, + 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, + 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, + 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, + 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, + 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, + 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, + 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, + 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, + 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, + 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, + 0x22fffff, 0x230ffff, 0x231ffff, 0x232ffff, 0x44a0448, 0x44e044c, + 0x2330450, 0xffffffff, 0xffffffff, 0x234ffff, 0x235ffff, 0x236ffff, + 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, + 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, + 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, + 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, + 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, + 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, + 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, 0x25fffff, 0x260ffff, + 0x261ffff, 0x262ffff, 0x263ffff, 0x2650264, 0x2670266, 0x2690268, + 0x26b026a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x26d026c, + 0x26f026e, 0x2710270, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2730272, 0x2750274, 0x2770276, 0x2790278, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x27b027a, 0x27d027c, 0x27f027e, + 0x2810280, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, + 0x2850284, 0x2870286, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2880452, 0x2890454, 0x28a0457, 0x28b045a, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x28d028c, 0x28f028e, 0x2910290, + 0x2930292, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2950294, + 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, 0x29f029e, 0x2a102a0, + 0xffffffff, 0x47c047b, 0x47e047d, 0x480047f, 0x4820481, 0x4840483, + 0x4860485, 0x4880487, 0x48a0489, 0x48c048b, 0x48e048d, 0x490048f, + 0x4920491, 0x4940493, 0x4960495, 0x4980497, 0x49a0499, 0x49c049b, + 0x49e049d, 0x4a0049f, 0x4a204a1, 0x4a404a3, 0x4a604a5, 0x4a804a7, + 0x4aa04a9, 0x2bb02ba, 0x4ab04b1, 0xffff04b3, 0x4bd045d, 0xffffffff, + 0xffffffff, 0xffff04ac, 0xffff02bd, 0xffffffff, 0x4ad04b5, 0xffff04b7, + 0x4c0045f, 0xffffffff, 0xffffffff, 0xffff04ae, 0xffffffff, 0x2c002bf, + 0x4640461, 0xffffffff, 0x4690467, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2c202c1, 0x46f046c, 0x2c30472, 0x4760474, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x4af04b9, 0xffff04bb, + 0x4c30479, 0xffffffff, 0xffffffff, 0xffff04b0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff02c5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c702c6, + 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, 0x2d102d0, 0x2d302d2, + 0x2d502d4, 0xffffffff, 0xffffffff, 0xffff02d6, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d802d7, + 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, 0x2e202e1, 0x2e402e3, + 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2f202f1, + 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, + 0x30002ff, 0x3020301, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, + 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, + 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, 0xffff031f, 0x320ffff, + 0xffffffff, 0x321ffff, 0xffff0322, 0xffff0323, 0xffff0324, 0xffff0325, + 0xffffffff, 0xffffffff, 0x326ffff, 0xffffffff, 0xffff0327, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x328ffff, 0x329ffff, 0x32affff, + 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, + 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, + 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, + 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, + 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, + 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, + 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, + 0x355ffff, 0x356ffff, 0x357ffff, 0x358ffff, 0x359ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff035a, 0xffff035b, 0xffffffff, + 0x35cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x35e035d, 0x360035f, 0x3620361, 0x3640363, 0x3660365, + 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, 0x370036f, 0x3720371, + 0x3740373, 0x3760375, 0x3780377, 0x37a0379, 0x37c037b, 0x37e037d, + 0x380037f, 0x3820381, 0x383ffff, 0xffffffff, 0xffffffff, 0x384ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x385ffff, 0x386ffff, 0x387ffff, + 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, + 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, + 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, 0x398ffff, 0x399ffff, + 0x39affff, 0x39bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x39cffff, + 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, + 0x3a3ffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0x3abffff, 0x3acffff, 0x3adffff, + 0x3aeffff, 0xffffffff, 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, + 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, + 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, + 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, + 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0x3caffff, + 0x3cbffff, 0x3ccffff, 0x3cdffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff03ce, 0xffff03cf, 0x3d0ffff, 0x3d1ffff, + 0x3d2ffff, 0x3d3ffff, 0x3d4ffff, 0xffffffff, 0xffffffff, 0xffff03d5, + 0xffffffff, 0x3d6ffff, 0x3d7ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3d8ffff, 0x3d9ffff, 0x3daffff, + 0x3dbffff, 0x3dcffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x4240422, 0x4280426, 0x42e042b, 0xffff0430, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x434ffff, 0x4380436, + 0x43c043a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3ddffff, 0x3df03de, 0x3e103e0, + 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0x3eb03ea, 0x3ed03ec, + 0x3ef03ee, 0x3f103f0, 0x3f303f2, 0x3f503f4, 0xffff03f6, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, + 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, + 0x40e040d, 0x410040f, 0x4120411, 0x4140413, 0x4160415, 0x4180417, + 0x41a0419, 0x41c041b, 0x41e041d, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //8064 bytes -enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xbc0], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, 0x1e0000, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, 0xffff0031, 0x330032, 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, 0x3cffff, 0x3dffff, 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, 0x43ffff, 0x44ffff, 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, 0x4affff, 0x4bffff, 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, 0x51ffff, 0x52ffff, 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, 0xffff0056, 0xffff0057, 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, 0xffff005c, 0xffff005d, 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, 0x62ffff, 0x63ffff, 0x64ffff, 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, 0x69ffff, 0x6affff, 0x6bffff, 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, 0x70ffff, 0x71ffff, 0x72ffff, 0x73ffff, 0x74ffff, 0xffffffff, 0xffff0075, 0xffff0076, 0x780077, 0xffff0079, 0x7affff, 0x7bffff, 0xffffffff, 0xffff007c, 0xffffffff, 0xffff007d, 0xffffffff, 0xffffffff, 0xffff007e, 0x7fffff, 0xffffffff, 0x80ffff, 0xffff0081, 0xffffffff, 0xffff0082, 0x83ffff, 0x84ffff, 0x85ffff, 0xffffffff, 0xffff0086, 0xffffffff, 0x87ffff, 0xffffffff, 0xffff0088, 0xffffffff, 0xffff0089, 0xffff008a, 0x8bffff, 0xffffffff, 0x8cffff, 0x8dffff, 0xffffffff, 0xffffffff, 0x8effff, 0xffff008f, 0x910090, 0x92ffff, 0xffff0093, 0xffff0094, 0xffff0095, 0xffff0096, 0xffff0097, 0xffff0098, 0xffff0099, 0xffff009a, 0x9c009b, 0x9dffff, 0x9effff, 0x9fffff, 0xa0ffff, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xffffffff, 0xa700a6, 0xa8ffff, 0xffffffff, 0xa9ffff, 0xaaffff, 0xabffff, 0xacffff, 0xadffff, 0xaeffff, 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, 0xffffffff, 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, 0xc1ffff, 0xc2ffff, 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00c6, 0xc7ffff, 0xffff00c8, 0xffff00c9, 0xffffffff, 0xcaffff, 0xcbffff, 0xccffff, 0xcdffff, 0xceffff, 0xd000cf, 0xd200d1, 0xffff00d3, 0xd500d4, 0xd6ffff, 0xd7ffff, 0xffffffff, 0xffffffff, 0xffff00d8, 0xd9ffff, 0xdaffff, 0xffff00db, 0xdd00dc, 0xdeffff, 0xffffffff, 0xdfffff, 0xe0ffff, 0xffff00e1, 0xe2ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xe3ffff, 0xffffffff, 0xffff00e4, 0xe5ffff, 0xffffffff, 0xffffffff, 0xe700e6, 0xe900e8, 0xffff00ea, 0xffffffff, 0xffffffff, 0xffff00eb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xecffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xedffff, 0xeeffff, 0xffffffff, 0xefffff, 0xffffffff, 0xf0ffff, 0xf200f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf400f3, 0xf600f5, 0xf7ffff, 0xf900f8, 0xfb00fa, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, 0x1110110, 0x1130112, 0xffff0114, 0x1160115, 0xffffffff, 0x117ffff, 0x1190118, 0x11affff, 0x11bffff, 0x11cffff, 0x11dffff, 0x11effff, 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, 0x123ffff, 0x124ffff, 0x125ffff, 0x1270126, 0xffff0128, 0x129ffff, 0xffffffff, 0xffff012a, 0x12bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x12d012c, 0x12f012e, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, 0x1530152, 0x1550154, 0x1570156, 0x1590158, 0x15b015a, 0x15cffff, 0x15dffff, 0x15effff, 0x15fffff, 0x160ffff, 0x161ffff, 0x162ffff, 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, 0x187ffff, 0xffffffff, 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, 0xffff018d, 0x18f018e, 0x190ffff, 0x191ffff, 0x192ffff, 0x193ffff, 0x194ffff, 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, 0x1b9ffff, 0x1baffff, 0x1bbffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1bcffff, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, 0xffff01e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1e2ffff, 0xffffffff, 0x1e3ffff, 0xffffffff, 0x1e4ffff, 0x1e5ffff, 0x1e6ffff, 0x1e7ffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, 0xffffffff, 0xffffffff, 0x22fffff, 0xffffffff, 0xffffffff, 0x230ffff, 0x231ffff, 0x232ffff, 0x233ffff, 0x234ffff, 0x235ffff, 0x236ffff, 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, 0x25fffff, 0x2610260, 0x2630262, 0x2650264, 0x2670266, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2690268, 0x26b026a, 0x26d026c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x26f026e, 0x2710270, 0x2730272, 0x2750274, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2770276, 0x2790278, 0x27b027a, 0x27d027c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27f027e, 0x2810280, 0x2830282, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x284ffff, 0x285ffff, 0x286ffff, 0x287ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2890288, 0x28b028a, 0x28d028c, 0x28f028e, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2910290, 0x2930292, 0x2950294, 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, 0xffffffff, 0x29f029e, 0x2a102a0, 0x2a302a2, 0x2a502a4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2a702a6, 0x2a902a8, 0x2ab02aa, 0x2ad02ac, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2af02ae, 0x2b102b0, 0x2b302b2, 0x2b502b4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2b702b6, 0x2b8ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02b9, 0xffffffff, 0x2baffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2bc02bb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2be02bd, 0xffffffff, 0x2bfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c0ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02c1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c302c2, 0x2c502c4, 0x2c702c6, 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, 0x2d102d0, 0xffffffff, 0xffffffff, 0xffff02d2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d402d3, 0x2d602d5, 0x2d802d7, 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ee02ed, 0x2f002ef, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0xffff031b, 0x31cffff, 0xffffffff, 0x31dffff, 0xffff031e, 0xffff031f, 0xffff0320, 0xffff0321, 0xffffffff, 0xffffffff, 0x322ffff, 0xffffffff, 0xffff0323, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x324ffff, 0x325ffff, 0x326ffff, 0x327ffff, 0x328ffff, 0x329ffff, 0x32affff, 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, 0x355ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0356, 0xffff0357, 0xffffffff, 0x358ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x35a0359, 0x35c035b, 0x35e035d, 0x360035f, 0x3620361, 0x3640363, 0x3660365, 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, 0x370036f, 0x3720371, 0x3740373, 0x3760375, 0x3780377, 0x37a0379, 0x37c037b, 0x37e037d, 0x37fffff, 0xffffffff, 0xffffffff, 0x380ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x381ffff, 0x382ffff, 0x383ffff, 0x384ffff, 0x385ffff, 0x386ffff, 0x387ffff, 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x398ffff, 0x399ffff, 0x39affff, 0x39bffff, 0x39cffff, 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, 0x3a3ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0xffffffff, 0x3abffff, 0x3acffff, 0x3adffff, 0x3aeffff, 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ca, 0xffff03cb, 0x3ccffff, 0x3cdffff, 0x3ceffff, 0x3cfffff, 0x3d0ffff, 0xffffffff, 0xffffffff, 0xffff03d1, 0xffffffff, 0x3d2ffff, 0x3d3ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d4ffff, 0x3d5ffff, 0x3d6ffff, 0x3d7ffff, 0x3d8ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d9ffff, 0x3db03da, 0x3dd03dc, 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0x3eb03ea, 0x3ed03ec, 0x3ef03ee, 0x3f103f0, 0xffff03f2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0x4140413, 0x4160415, 0x4180417, 0x41a0419, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, + 0x200], [0x100, 0x380, 0xbc0], [0x2020100, 0x4020302, 0x2020205, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, + 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x150000, 0x0, 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, + 0x1e0000, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, + 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, + 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, + 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, + 0x240023, 0x260025, 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, + 0xffff0031, 0x330032, 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, + 0x3cffff, 0x3dffff, 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, + 0x43ffff, 0x44ffff, 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, + 0x4affff, 0x4bffff, 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, + 0x51ffff, 0x52ffff, 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, + 0xffff0056, 0xffff0057, 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, + 0xffff005c, 0xffff005d, 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, + 0x62ffff, 0x63ffff, 0x64ffff, 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, + 0x69ffff, 0x6affff, 0x6bffff, 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, + 0x70ffff, 0x71ffff, 0x72ffff, 0x73ffff, 0x74ffff, 0xffffffff, + 0xffff0075, 0xffff0076, 0x780077, 0xffff0079, 0x7affff, 0x7bffff, + 0xffffffff, 0xffff007c, 0xffffffff, 0xffff007d, 0xffffffff, 0xffffffff, + 0xffff007e, 0x7fffff, 0xffffffff, 0x80ffff, 0xffff0081, 0xffffffff, + 0xffff0082, 0x83ffff, 0x84ffff, 0x85ffff, 0xffffffff, 0xffff0086, + 0xffffffff, 0x87ffff, 0xffffffff, 0xffff0088, 0xffffffff, 0xffff0089, + 0xffff008a, 0x8bffff, 0xffffffff, 0x8cffff, 0x8dffff, 0xffffffff, + 0xffffffff, 0x8effff, 0xffff008f, 0x910090, 0x92ffff, 0xffff0093, + 0xffff0094, 0xffff0095, 0xffff0096, 0xffff0097, 0xffff0098, 0xffff0099, + 0xffff009a, 0x9c009b, 0x9dffff, 0x9effff, 0x9fffff, 0xa0ffff, 0xa1ffff, + 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xffffffff, 0xa700a6, 0xa8ffff, + 0xffffffff, 0xa9ffff, 0xaaffff, 0xabffff, 0xacffff, 0xadffff, 0xaeffff, + 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, + 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, + 0xffffffff, 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, 0xc1ffff, 0xc2ffff, + 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff00c6, 0xc7ffff, 0xffff00c8, 0xffff00c9, 0xffffffff, + 0xcaffff, 0xcbffff, 0xccffff, 0xcdffff, 0xceffff, 0xd000cf, 0xd200d1, + 0xffff00d3, 0xd500d4, 0xd6ffff, 0xd7ffff, 0xffffffff, 0xffffffff, + 0xffff00d8, 0xd9ffff, 0xdaffff, 0xffff00db, 0xdd00dc, 0xdeffff, + 0xffffffff, 0xdfffff, 0xe0ffff, 0xffff00e1, 0xe2ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xe3ffff, 0xffffffff, 0xffff00e4, 0xe5ffff, + 0xffffffff, 0xffffffff, 0xe700e6, 0xe900e8, 0xffff00ea, 0xffffffff, + 0xffffffff, 0xffff00eb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xecffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xedffff, 0xeeffff, 0xffffffff, 0xefffff, 0xffffffff, 0xf0ffff, + 0xf200f1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xf400f3, 0xf600f5, 0xf7ffff, 0xf900f8, 0xfb00fa, 0xfd00fc, 0xff00fe, + 0x1010100, 0x1030102, 0x1050104, 0x1070106, 0x1090108, 0x10b010a, + 0x10d010c, 0x10f010e, 0x1110110, 0x1130112, 0xffff0114, 0x1160115, + 0xffffffff, 0x117ffff, 0x1190118, 0x11affff, 0x11bffff, 0x11cffff, + 0x11dffff, 0x11effff, 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, + 0x123ffff, 0x124ffff, 0x125ffff, 0x1270126, 0xffff0128, 0x129ffff, + 0xffffffff, 0xffff012a, 0x12bffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x12d012c, + 0x12f012e, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, + 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, + 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, + 0x1530152, 0x1550154, 0x1570156, 0x1590158, 0x15b015a, 0x15cffff, + 0x15dffff, 0x15effff, 0x15fffff, 0x160ffff, 0x161ffff, 0x162ffff, + 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, + 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, + 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, + 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, + 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, + 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, 0x187ffff, 0xffffffff, + 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, 0xffff018d, + 0x18f018e, 0x190ffff, 0x191ffff, 0x192ffff, 0x193ffff, 0x194ffff, + 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, + 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, + 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, + 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, + 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, + 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, + 0x1b9ffff, 0x1baffff, 0x1bbffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x1bcffff, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, + 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, + 0x1d201d1, 0x1d401d3, 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, + 0x1de01dd, 0x1e001df, 0xffff01e1, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1e2ffff, + 0xffffffff, 0x1e3ffff, 0xffffffff, 0x1e4ffff, 0x1e5ffff, 0x1e6ffff, + 0x1e7ffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, + 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, + 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, + 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, + 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, + 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, + 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, + 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, + 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, + 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, + 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, + 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, + 0xffffffff, 0xffffffff, 0x22fffff, 0xffffffff, 0xffffffff, 0x230ffff, + 0x231ffff, 0x232ffff, 0x233ffff, 0x234ffff, 0x235ffff, 0x236ffff, + 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, + 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, + 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, + 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, + 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, + 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, + 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, 0x25fffff, 0x2610260, + 0x2630262, 0x2650264, 0x2670266, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2690268, 0x26b026a, 0x26d026c, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x26f026e, 0x2710270, 0x2730272, + 0x2750274, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2770276, + 0x2790278, 0x27b027a, 0x27d027c, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x27f027e, 0x2810280, 0x2830282, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x284ffff, 0x285ffff, 0x286ffff, + 0x287ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2890288, + 0x28b028a, 0x28d028c, 0x28f028e, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2910290, 0x2930292, 0x2950294, 0x2970296, 0x2990298, + 0x29b029a, 0x29d029c, 0xffffffff, 0x29f029e, 0x2a102a0, 0x2a302a2, + 0x2a502a4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2a702a6, + 0x2a902a8, 0x2ab02aa, 0x2ad02ac, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2af02ae, 0x2b102b0, 0x2b302b2, 0x2b502b4, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2b702b6, 0x2b8ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02b9, 0xffffffff, + 0x2baffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2bc02bb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2be02bd, 0xffffffff, 0x2bfffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x2c0ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff02c1, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2c302c2, 0x2c502c4, 0x2c702c6, 0x2c902c8, 0x2cb02ca, + 0x2cd02cc, 0x2cf02ce, 0x2d102d0, 0xffffffff, 0xffffffff, 0xffff02d2, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2d402d3, 0x2d602d5, 0x2d802d7, 0x2da02d9, 0x2dc02db, + 0x2de02dd, 0x2e002df, 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, + 0x2ea02e9, 0x2ec02eb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2ee02ed, 0x2f002ef, 0x2f202f1, 0x2f402f3, 0x2f602f5, + 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, + 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, + 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, + 0xffff031b, 0x31cffff, 0xffffffff, 0x31dffff, 0xffff031e, 0xffff031f, + 0xffff0320, 0xffff0321, 0xffffffff, 0xffffffff, 0x322ffff, 0xffffffff, + 0xffff0323, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x324ffff, + 0x325ffff, 0x326ffff, 0x327ffff, 0x328ffff, 0x329ffff, 0x32affff, + 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, + 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, + 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, + 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, + 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, + 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, + 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, + 0x355ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0356, + 0xffff0357, 0xffffffff, 0x358ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x35a0359, 0x35c035b, 0x35e035d, + 0x360035f, 0x3620361, 0x3640363, 0x3660365, 0x3680367, 0x36a0369, + 0x36c036b, 0x36e036d, 0x370036f, 0x3720371, 0x3740373, 0x3760375, + 0x3780377, 0x37a0379, 0x37c037b, 0x37e037d, 0x37fffff, 0xffffffff, + 0xffffffff, 0x380ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x381ffff, + 0x382ffff, 0x383ffff, 0x384ffff, 0x385ffff, 0x386ffff, 0x387ffff, + 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, + 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, + 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x398ffff, 0x399ffff, 0x39affff, 0x39bffff, 0x39cffff, + 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, + 0x3a3ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, + 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0xffffffff, 0x3abffff, 0x3acffff, + 0x3adffff, 0x3aeffff, 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, + 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, + 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, + 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, + 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ca, 0xffff03cb, + 0x3ccffff, 0x3cdffff, 0x3ceffff, 0x3cfffff, 0x3d0ffff, 0xffffffff, + 0xffffffff, 0xffff03d1, 0xffffffff, 0x3d2ffff, 0x3d3ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d4ffff, + 0x3d5ffff, 0x3d6ffff, 0x3d7ffff, 0x3d8ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3d9ffff, 0x3db03da, 0x3dd03dc, + 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, + 0x3eb03ea, 0x3ed03ec, 0x3ef03ee, 0x3f103f0, 0xffff03f2, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, + 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, + 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0x4140413, + 0x4160415, 0x4180417, 0x41a0419, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //7808 bytes -enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xb40], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x20000, 0x40003, 0x60005, 0x80007, 0x0, 0x90000, 0xb000a, 0xd000c, 0xf000e, 0x110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150014, 0x170016, 0x190018, 0x1b001a, 0x0, 0x0, 0x1d001c, 0x1e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20001f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x250000, 0x26, 0x280027, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1b001a, 0x1d001c, 0x1f001e, 0x210020, 0x230022, 0x250024, 0x270026, 0x290028, 0x2b002a, 0x2d002c, 0x2f002e, 0xffff0030, 0x320031, 0x340033, 0x360035, 0xffff0037, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0038, 0xffff0039, 0xffff003a, 0xffff003b, 0xffff003c, 0xffff003d, 0xffff003e, 0xffff003f, 0xffff0040, 0xffff0041, 0xffff0042, 0xffff0043, 0xffff0044, 0xffff0045, 0xffff0046, 0xffff0047, 0xffff0048, 0xffff0049, 0xffff004a, 0xffff004b, 0xffff004c, 0xffff004d, 0xffff004e, 0xffff004f, 0xffff0050, 0xffff0051, 0xffff0052, 0xffff0053, 0x54ffff, 0x55ffff, 0x56ffff, 0x57ffff, 0x58ffff, 0x59ffff, 0x5affff, 0x5bffff, 0xffffffff, 0xffff005c, 0xffff005d, 0xffff005e, 0xffff005f, 0xffff0060, 0xffff0061, 0xffff0062, 0xffff0063, 0xffff0064, 0xffff0065, 0xffff0066, 0xffff0067, 0xffff0068, 0xffff0069, 0xffff006a, 0xffff006b, 0xffff006c, 0xffff006d, 0xffff006e, 0xffff006f, 0xffff0070, 0xffff0071, 0xffff0072, 0x740073, 0x75ffff, 0x76ffff, 0xffffffff, 0x77ffff, 0xffff0078, 0xffff0079, 0x7b007a, 0x7cffff, 0x7e007d, 0xffffffff, 0x80007f, 0x820081, 0x83ffff, 0xffff0084, 0x860085, 0xffff0087, 0xffffffff, 0x890088, 0x8affff, 0xffff008b, 0xffff008c, 0xffff008d, 0x8f008e, 0x90ffff, 0xffffffff, 0xffff0091, 0x930092, 0x94ffff, 0x960095, 0x97ffff, 0x98ffff, 0xffff0099, 0xffffffff, 0xffff009a, 0xffffffff, 0xffffffff, 0xffffffff, 0x9c009b, 0x9dffff, 0xffff009e, 0xa0009f, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xffffffff, 0xffff00a9, 0xffff00aa, 0xffff00ab, 0xffff00ac, 0xffff00ad, 0xffff00ae, 0xffff00af, 0xffff00b0, 0xffff00b1, 0xb2ffff, 0xffff00b3, 0xffff00b4, 0xb600b5, 0xffff00b7, 0xffff00b8, 0xffff00b9, 0xffff00ba, 0xffff00bb, 0xffff00bc, 0xffff00bd, 0xffff00be, 0xffff00bf, 0xffff00c0, 0xffff00c1, 0xffff00c2, 0xffff00c3, 0xffff00c4, 0xffff00c5, 0xffff00c6, 0xffff00c7, 0xffff00c8, 0xffff00c9, 0xffff00ca, 0xffff00cb, 0xffff00cc, 0xffff00cd, 0xffff00ce, 0xffff00cf, 0xffff00d0, 0xffff00d1, 0xffff00d2, 0xffff00d3, 0xffff00d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xd600d5, 0xd7ffff, 0xffff00d8, 0xd9ffff, 0xdaffff, 0xdc00db, 0xffff00dd, 0xffff00de, 0xffff00df, 0xffff00e0, 0xffff00e1, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00e2, 0xffff00e3, 0xffffffff, 0xffff00e4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00e5, 0xe700e6, 0xffff00e8, 0xffff00e9, 0xeb00ea, 0xecffff, 0xee00ed, 0xf000ef, 0xf200f1, 0xf400f3, 0xf600f5, 0xf800f7, 0xfa00f9, 0xfc00fb, 0xfdffff, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x106ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0107, 0xffff0108, 0xffff0109, 0xffff010a, 0xffff010b, 0xffff010c, 0xffff010d, 0xffff010e, 0xffff010f, 0xffff0110, 0xffff0111, 0xffff0112, 0xffffffff, 0xffffffff, 0xffff0113, 0x114ffff, 0x115ffff, 0xffff0116, 0x117ffff, 0x1190118, 0x11b011a, 0x11d011c, 0x11f011e, 0x1210120, 0x1230122, 0x1250124, 0x1270126, 0x1290128, 0x12b012a, 0x12d012c, 0x12f012e, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff014a, 0xffff014b, 0xffff014c, 0xffff014d, 0xffff014e, 0xffff014f, 0xffff0150, 0xffff0151, 0xffff0152, 0xffff0153, 0xffff0154, 0xffff0155, 0xffff0156, 0xffff0157, 0xffff0158, 0xffff0159, 0xffff015a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff015b, 0xffff015c, 0xffff015d, 0xffff015e, 0xffff015f, 0xffff0160, 0xffff0161, 0xffff0162, 0xffff0163, 0xffff0164, 0xffff0165, 0xffff0166, 0xffff0167, 0xffff0168, 0xffff0169, 0xffff016a, 0xffff016b, 0xffff016c, 0xffff016d, 0xffff016e, 0xffff016f, 0xffff0170, 0xffff0171, 0xffff0172, 0xffff0173, 0xffff0174, 0xffff0175, 0x1770176, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0xffffffff, 0xffff017e, 0xffff017f, 0xffff0180, 0xffff0181, 0xffff0182, 0xffff0183, 0xffff0184, 0xffff0185, 0xffff0186, 0xffff0187, 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0xffff0192, 0xffff0193, 0xffff0194, 0xffff0195, 0xffff0196, 0xffff0197, 0xffff0198, 0xffff0199, 0xffff019a, 0xffff019b, 0xffff019c, 0xffff019d, 0xffff019e, 0xffff019f, 0xffff01a0, 0xffff01a1, 0xffff01a2, 0xffff01a3, 0xffff01a4, 0xffff01a5, 0xffff01a6, 0xffff01a7, 0xffff01a8, 0xffff01a9, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1aaffff, 0x1ac01ab, 0x1ae01ad, 0x1b001af, 0x1b201b1, 0x1b401b3, 0x1b601b5, 0x1b801b7, 0x1ba01b9, 0x1bc01bb, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0xffff01cf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1d101d0, 0x1d301d2, 0x1d501d4, 0x1d701d6, 0x1d901d8, 0x1db01da, 0x1dd01dc, 0x1df01de, 0x1e101e0, 0x1e301e2, 0x1e501e4, 0x1e701e6, 0x1e901e8, 0x1eb01ea, 0x1ed01ec, 0x1ef01ee, 0x1f101f0, 0x1f301f2, 0x1f501f4, 0x1f6ffff, 0xffffffff, 0xffffffff, 0x1f7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff01f8, 0xffff01f9, 0xffff01fa, 0xffff01fb, 0xffff01fc, 0xffff01fd, 0xffff01fe, 0xffff01ff, 0xffff0200, 0xffff0201, 0xffff0202, 0xffff0203, 0xffff0204, 0xffff0205, 0xffff0206, 0xffff0207, 0xffff0208, 0xffff0209, 0xffff020a, 0xffff020b, 0xffff020c, 0xffff020d, 0xffff020e, 0xffff020f, 0xffff0210, 0xffff0211, 0xffff0212, 0xffff0213, 0xffff0214, 0xffff0215, 0xffff0216, 0xffff0217, 0xffff0218, 0xffff0219, 0xffff021a, 0xffff021b, 0xffff021c, 0xffff021d, 0xffff021e, 0xffff021f, 0xffff0220, 0xffff0221, 0xffff0222, 0xffff0223, 0xffff0224, 0xffff0225, 0xffff0226, 0xffff0227, 0xffff0228, 0xffff0229, 0xffff022a, 0xffff022b, 0xffff022c, 0xffff022d, 0xffff022e, 0xffff022f, 0xffff0230, 0xffff0231, 0xffff0232, 0xffff0233, 0xffff0234, 0xffff0235, 0xffff0236, 0xffff0237, 0xffff0238, 0xffff0239, 0xffff023a, 0xffff023b, 0xffff023c, 0xffff023d, 0xffff023e, 0xffff023f, 0xffff0240, 0xffff0241, 0xffff0242, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0243, 0xffff0244, 0xffff0245, 0xffff0246, 0xffff0247, 0xffff0248, 0xffff0249, 0xffff024a, 0xffff024b, 0xffff024c, 0xffff024d, 0xffff024e, 0xffff024f, 0xffff0250, 0xffff0251, 0xffff0252, 0xffff0253, 0xffff0254, 0xffff0255, 0xffff0256, 0xffff0257, 0xffff0258, 0xffff0259, 0xffff025a, 0xffff025b, 0xffff025c, 0xffff025d, 0xffff025e, 0xffff025f, 0xffff0260, 0xffff0261, 0xffff0262, 0xffff0263, 0xffff0264, 0xffff0265, 0xffff0266, 0xffff0267, 0xffff0268, 0xffff0269, 0xffff026a, 0xffff026b, 0xffff026c, 0xffff026d, 0xffff026e, 0xffff026f, 0xffff0270, 0xffff0271, 0xffff0272, 0xffff0273, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2750274, 0x2770276, 0x2790278, 0x27b027a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27d027c, 0x27f027e, 0x2810280, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, 0x2850284, 0x2870286, 0x2890288, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x28b028a, 0x28d028c, 0x28f028e, 0x2910290, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2930292, 0x2950294, 0x2970296, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x298ffff, 0x299ffff, 0x29affff, 0x29bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x29d029c, 0x29f029e, 0x2a102a0, 0x2a302a2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2a502a4, 0x2a702a6, 0x2a902a8, 0x2ab02aa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ad02ac, 0x2af02ae, 0x2b102b0, 0x2b302b2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2b502b4, 0x2b702b6, 0x2b902b8, 0x2bb02ba, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2bd02bc, 0x2bf02be, 0xffff02c0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c202c1, 0x2c402c3, 0xffff02c5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c702c6, 0x2c902c8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2cb02ca, 0x2cd02cc, 0xffff02ce, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d002cf, 0x2d202d1, 0xffff02d3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02d4, 0xffffffff, 0x2d602d5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02d7, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d902d8, 0x2db02da, 0x2dd02dc, 0x2df02de, 0x2e102e0, 0x2e302e2, 0x2e502e4, 0x2e702e6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2e8ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, 0x320031f, 0x3220321, 0x3240323, 0x3260325, 0x3280327, 0x32a0329, 0x32c032b, 0x32e032d, 0x330032f, 0xffff0331, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0332, 0x3340333, 0xffff0335, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33b033a, 0xffff033c, 0xffff033d, 0x33effff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x340033f, 0xffff0341, 0xffff0342, 0xffff0343, 0xffff0344, 0xffff0345, 0xffff0346, 0xffff0347, 0xffff0348, 0xffff0349, 0xffff034a, 0xffff034b, 0xffff034c, 0xffff034d, 0xffff034e, 0xffff034f, 0xffff0350, 0xffff0351, 0xffff0352, 0xffff0353, 0xffff0354, 0xffff0355, 0xffff0356, 0xffff0357, 0xffff0358, 0xffff0359, 0xffff035a, 0xffff035b, 0xffff035c, 0xffff035d, 0xffff035e, 0xffff035f, 0xffff0360, 0xffff0361, 0xffff0362, 0xffff0363, 0xffff0364, 0xffff0365, 0xffff0366, 0xffff0367, 0xffff0368, 0xffff0369, 0xffff036a, 0xffff036b, 0xffff036c, 0xffff036d, 0xffff036e, 0xffff036f, 0xffff0370, 0xffff0371, 0xffff0372, 0xffffffff, 0xffffffff, 0xffffffff, 0x373ffff, 0x374ffff, 0xffffffff, 0xffffffff, 0xffff0375, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0376, 0xffff0377, 0xffff0378, 0xffff0379, 0xffff037a, 0xffff037b, 0xffff037c, 0xffff037d, 0xffff037e, 0xffff037f, 0xffff0380, 0xffff0381, 0xffff0382, 0xffff0383, 0xffff0384, 0xffff0385, 0xffff0386, 0xffff0387, 0xffff0388, 0xffff0389, 0xffff038a, 0xffff038b, 0xffff038c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff038d, 0xffff038e, 0xffff038f, 0xffff0390, 0xffff0391, 0xffff0392, 0xffff0393, 0xffff0394, 0xffff0395, 0xffff0396, 0xffff0397, 0xffff0398, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0399, 0xffff039a, 0xffff039b, 0xffff039c, 0xffff039d, 0xffff039e, 0xffff039f, 0xffffffff, 0xffff03a0, 0xffff03a1, 0xffff03a2, 0xffff03a3, 0xffff03a4, 0xffff03a5, 0xffff03a6, 0xffff03a7, 0xffff03a8, 0xffff03a9, 0xffff03aa, 0xffff03ab, 0xffff03ac, 0xffff03ad, 0xffff03ae, 0xffff03af, 0xffff03b0, 0xffff03b1, 0xffff03b2, 0xffff03b3, 0xffff03b4, 0xffff03b5, 0xffff03b6, 0xffff03b7, 0xffff03b8, 0xffff03b9, 0xffff03ba, 0xffff03bb, 0xffff03bc, 0xffff03bd, 0xffff03be, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0xffff03c2, 0xffff03c3, 0xffff03c4, 0xffff03c5, 0xffff03c6, 0xffffffff, 0x3c7ffff, 0x3c8ffff, 0xffffffff, 0xffff03c9, 0xffff03ca, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03cb, 0xffff03cc, 0xffff03cd, 0xffff03ce, 0xffff03cf, 0xffff03d0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d1ffff, 0x3d303d2, 0x3d503d4, 0x3d703d6, 0x3d903d8, 0x3db03da, 0x3dd03dc, 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0xffff03ea, 0xffffffff, 0xffffffff, 0x3ec03eb, 0x3ee03ed, 0x3f003ef, 0x3f203f1, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, + 0x200], [0x100, 0x380, 0xb40], [0x2020100, 0x4020302, 0x2020205, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x10000, 0x20000, 0x40003, 0x60005, 0x80007, 0x0, 0x90000, + 0xb000a, 0xd000c, 0xf000e, 0x110010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x130012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x150014, 0x170016, 0x190018, 0x1b001a, 0x0, 0x0, 0x1d001c, 0x1e, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20001f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x250000, 0x26, 0x280027, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x2b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, + 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x1b001a, 0x1d001c, 0x1f001e, 0x210020, 0x230022, 0x250024, 0x270026, + 0x290028, 0x2b002a, 0x2d002c, 0x2f002e, 0xffff0030, 0x320031, 0x340033, + 0x360035, 0xffff0037, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0038, 0xffff0039, 0xffff003a, 0xffff003b, 0xffff003c, 0xffff003d, + 0xffff003e, 0xffff003f, 0xffff0040, 0xffff0041, 0xffff0042, 0xffff0043, + 0xffff0044, 0xffff0045, 0xffff0046, 0xffff0047, 0xffff0048, 0xffff0049, + 0xffff004a, 0xffff004b, 0xffff004c, 0xffff004d, 0xffff004e, 0xffff004f, + 0xffff0050, 0xffff0051, 0xffff0052, 0xffff0053, 0x54ffff, 0x55ffff, + 0x56ffff, 0x57ffff, 0x58ffff, 0x59ffff, 0x5affff, 0x5bffff, 0xffffffff, + 0xffff005c, 0xffff005d, 0xffff005e, 0xffff005f, 0xffff0060, 0xffff0061, + 0xffff0062, 0xffff0063, 0xffff0064, 0xffff0065, 0xffff0066, 0xffff0067, + 0xffff0068, 0xffff0069, 0xffff006a, 0xffff006b, 0xffff006c, 0xffff006d, + 0xffff006e, 0xffff006f, 0xffff0070, 0xffff0071, 0xffff0072, 0x740073, + 0x75ffff, 0x76ffff, 0xffffffff, 0x77ffff, 0xffff0078, 0xffff0079, + 0x7b007a, 0x7cffff, 0x7e007d, 0xffffffff, 0x80007f, 0x820081, 0x83ffff, + 0xffff0084, 0x860085, 0xffff0087, 0xffffffff, 0x890088, 0x8affff, + 0xffff008b, 0xffff008c, 0xffff008d, 0x8f008e, 0x90ffff, 0xffffffff, + 0xffff0091, 0x930092, 0x94ffff, 0x960095, 0x97ffff, 0x98ffff, + 0xffff0099, 0xffffffff, 0xffff009a, 0xffffffff, 0xffffffff, 0xffffffff, + 0x9c009b, 0x9dffff, 0xffff009e, 0xa0009f, 0xa1ffff, 0xa2ffff, 0xa3ffff, + 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xffffffff, + 0xffff00a9, 0xffff00aa, 0xffff00ab, 0xffff00ac, 0xffff00ad, 0xffff00ae, + 0xffff00af, 0xffff00b0, 0xffff00b1, 0xb2ffff, 0xffff00b3, 0xffff00b4, + 0xb600b5, 0xffff00b7, 0xffff00b8, 0xffff00b9, 0xffff00ba, 0xffff00bb, + 0xffff00bc, 0xffff00bd, 0xffff00be, 0xffff00bf, 0xffff00c0, 0xffff00c1, + 0xffff00c2, 0xffff00c3, 0xffff00c4, 0xffff00c5, 0xffff00c6, 0xffff00c7, + 0xffff00c8, 0xffff00c9, 0xffff00ca, 0xffff00cb, 0xffff00cc, 0xffff00cd, + 0xffff00ce, 0xffff00cf, 0xffff00d0, 0xffff00d1, 0xffff00d2, 0xffff00d3, + 0xffff00d4, 0xffffffff, 0xffffffff, 0xffffffff, 0xd600d5, 0xd7ffff, + 0xffff00d8, 0xd9ffff, 0xdaffff, 0xdc00db, 0xffff00dd, 0xffff00de, + 0xffff00df, 0xffff00e0, 0xffff00e1, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00e2, 0xffff00e3, 0xffffffff, + 0xffff00e4, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff00e5, 0xe700e6, 0xffff00e8, 0xffff00e9, + 0xeb00ea, 0xecffff, 0xee00ed, 0xf000ef, 0xf200f1, 0xf400f3, 0xf600f5, + 0xf800f7, 0xfa00f9, 0xfc00fb, 0xfdffff, 0xff00fe, 0x1010100, 0x1030102, + 0x1050104, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x106ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0107, + 0xffff0108, 0xffff0109, 0xffff010a, 0xffff010b, 0xffff010c, 0xffff010d, + 0xffff010e, 0xffff010f, 0xffff0110, 0xffff0111, 0xffff0112, 0xffffffff, + 0xffffffff, 0xffff0113, 0x114ffff, 0x115ffff, 0xffff0116, 0x117ffff, + 0x1190118, 0x11b011a, 0x11d011c, 0x11f011e, 0x1210120, 0x1230122, + 0x1250124, 0x1270126, 0x1290128, 0x12b012a, 0x12d012c, 0x12f012e, + 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, + 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, + 0x1490148, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff014a, 0xffff014b, 0xffff014c, 0xffff014d, 0xffff014e, + 0xffff014f, 0xffff0150, 0xffff0151, 0xffff0152, 0xffff0153, 0xffff0154, + 0xffff0155, 0xffff0156, 0xffff0157, 0xffff0158, 0xffff0159, 0xffff015a, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff015b, 0xffff015c, + 0xffff015d, 0xffff015e, 0xffff015f, 0xffff0160, 0xffff0161, 0xffff0162, + 0xffff0163, 0xffff0164, 0xffff0165, 0xffff0166, 0xffff0167, 0xffff0168, + 0xffff0169, 0xffff016a, 0xffff016b, 0xffff016c, 0xffff016d, 0xffff016e, + 0xffff016f, 0xffff0170, 0xffff0171, 0xffff0172, 0xffff0173, 0xffff0174, + 0xffff0175, 0x1770176, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, + 0x17cffff, 0x17dffff, 0xffffffff, 0xffff017e, 0xffff017f, 0xffff0180, + 0xffff0181, 0xffff0182, 0xffff0183, 0xffff0184, 0xffff0185, 0xffff0186, + 0xffff0187, 0xffff0188, 0xffff0189, 0xffff018a, 0xffff018b, 0xffff018c, + 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0xffff0192, + 0xffff0193, 0xffff0194, 0xffff0195, 0xffff0196, 0xffff0197, 0xffff0198, + 0xffff0199, 0xffff019a, 0xffff019b, 0xffff019c, 0xffff019d, 0xffff019e, + 0xffff019f, 0xffff01a0, 0xffff01a1, 0xffff01a2, 0xffff01a3, 0xffff01a4, + 0xffff01a5, 0xffff01a6, 0xffff01a7, 0xffff01a8, 0xffff01a9, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x1aaffff, 0x1ac01ab, 0x1ae01ad, + 0x1b001af, 0x1b201b1, 0x1b401b3, 0x1b601b5, 0x1b801b7, 0x1ba01b9, + 0x1bc01bb, 0x1be01bd, 0x1c001bf, 0x1c201c1, 0x1c401c3, 0x1c601c5, + 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0xffff01cf, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1d101d0, + 0x1d301d2, 0x1d501d4, 0x1d701d6, 0x1d901d8, 0x1db01da, 0x1dd01dc, + 0x1df01de, 0x1e101e0, 0x1e301e2, 0x1e501e4, 0x1e701e6, 0x1e901e8, + 0x1eb01ea, 0x1ed01ec, 0x1ef01ee, 0x1f101f0, 0x1f301f2, 0x1f501f4, + 0x1f6ffff, 0xffffffff, 0xffffffff, 0x1f7ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff01f8, + 0xffff01f9, 0xffff01fa, 0xffff01fb, 0xffff01fc, 0xffff01fd, 0xffff01fe, + 0xffff01ff, 0xffff0200, 0xffff0201, 0xffff0202, 0xffff0203, 0xffff0204, + 0xffff0205, 0xffff0206, 0xffff0207, 0xffff0208, 0xffff0209, 0xffff020a, + 0xffff020b, 0xffff020c, 0xffff020d, 0xffff020e, 0xffff020f, 0xffff0210, + 0xffff0211, 0xffff0212, 0xffff0213, 0xffff0214, 0xffff0215, 0xffff0216, + 0xffff0217, 0xffff0218, 0xffff0219, 0xffff021a, 0xffff021b, 0xffff021c, + 0xffff021d, 0xffff021e, 0xffff021f, 0xffff0220, 0xffff0221, 0xffff0222, + 0xffff0223, 0xffff0224, 0xffff0225, 0xffff0226, 0xffff0227, 0xffff0228, + 0xffff0229, 0xffff022a, 0xffff022b, 0xffff022c, 0xffff022d, 0xffff022e, + 0xffff022f, 0xffff0230, 0xffff0231, 0xffff0232, 0xffff0233, 0xffff0234, + 0xffff0235, 0xffff0236, 0xffff0237, 0xffff0238, 0xffff0239, 0xffff023a, + 0xffff023b, 0xffff023c, 0xffff023d, 0xffff023e, 0xffff023f, 0xffff0240, + 0xffff0241, 0xffff0242, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0243, 0xffff0244, 0xffff0245, 0xffff0246, 0xffff0247, 0xffff0248, + 0xffff0249, 0xffff024a, 0xffff024b, 0xffff024c, 0xffff024d, 0xffff024e, + 0xffff024f, 0xffff0250, 0xffff0251, 0xffff0252, 0xffff0253, 0xffff0254, + 0xffff0255, 0xffff0256, 0xffff0257, 0xffff0258, 0xffff0259, 0xffff025a, + 0xffff025b, 0xffff025c, 0xffff025d, 0xffff025e, 0xffff025f, 0xffff0260, + 0xffff0261, 0xffff0262, 0xffff0263, 0xffff0264, 0xffff0265, 0xffff0266, + 0xffff0267, 0xffff0268, 0xffff0269, 0xffff026a, 0xffff026b, 0xffff026c, + 0xffff026d, 0xffff026e, 0xffff026f, 0xffff0270, 0xffff0271, 0xffff0272, + 0xffff0273, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2750274, + 0x2770276, 0x2790278, 0x27b027a, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x27d027c, 0x27f027e, 0x2810280, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, 0x2850284, 0x2870286, + 0x2890288, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x28b028a, + 0x28d028c, 0x28f028e, 0x2910290, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2930292, 0x2950294, 0x2970296, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x298ffff, 0x299ffff, 0x29affff, + 0x29bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x29d029c, + 0x29f029e, 0x2a102a0, 0x2a302a2, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2a502a4, 0x2a702a6, 0x2a902a8, + 0x2ab02aa, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ad02ac, + 0x2af02ae, 0x2b102b0, 0x2b302b2, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2b502b4, 0x2b702b6, 0x2b902b8, 0x2bb02ba, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2bd02bc, 0x2bf02be, 0xffff02c0, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c202c1, + 0x2c402c3, 0xffff02c5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2c702c6, 0x2c902c8, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2cb02ca, 0x2cd02cc, 0xffff02ce, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d002cf, + 0x2d202d1, 0xffff02d3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02d4, 0xffffffff, + 0x2d602d5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02d7, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2d902d8, 0x2db02da, 0x2dd02dc, + 0x2df02de, 0x2e102e0, 0x2e302e2, 0x2e502e4, 0x2e702e6, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x2e8ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, 0x2f202f1, 0x2f402f3, + 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, + 0x3020301, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, + 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, + 0x31a0319, 0x31c031b, 0x31e031d, 0x320031f, 0x3220321, 0x3240323, + 0x3260325, 0x3280327, 0x32a0329, 0x32c032b, 0x32e032d, 0x330032f, + 0xffff0331, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff0332, 0x3340333, 0xffff0335, 0x336ffff, 0x337ffff, + 0x338ffff, 0x339ffff, 0x33b033a, 0xffff033c, 0xffff033d, 0x33effff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x340033f, 0xffff0341, + 0xffff0342, 0xffff0343, 0xffff0344, 0xffff0345, 0xffff0346, 0xffff0347, + 0xffff0348, 0xffff0349, 0xffff034a, 0xffff034b, 0xffff034c, 0xffff034d, + 0xffff034e, 0xffff034f, 0xffff0350, 0xffff0351, 0xffff0352, 0xffff0353, + 0xffff0354, 0xffff0355, 0xffff0356, 0xffff0357, 0xffff0358, 0xffff0359, + 0xffff035a, 0xffff035b, 0xffff035c, 0xffff035d, 0xffff035e, 0xffff035f, + 0xffff0360, 0xffff0361, 0xffff0362, 0xffff0363, 0xffff0364, 0xffff0365, + 0xffff0366, 0xffff0367, 0xffff0368, 0xffff0369, 0xffff036a, 0xffff036b, + 0xffff036c, 0xffff036d, 0xffff036e, 0xffff036f, 0xffff0370, 0xffff0371, + 0xffff0372, 0xffffffff, 0xffffffff, 0xffffffff, 0x373ffff, 0x374ffff, + 0xffffffff, 0xffffffff, 0xffff0375, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff0376, 0xffff0377, 0xffff0378, + 0xffff0379, 0xffff037a, 0xffff037b, 0xffff037c, 0xffff037d, 0xffff037e, + 0xffff037f, 0xffff0380, 0xffff0381, 0xffff0382, 0xffff0383, 0xffff0384, + 0xffff0385, 0xffff0386, 0xffff0387, 0xffff0388, 0xffff0389, 0xffff038a, + 0xffff038b, 0xffff038c, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff038d, + 0xffff038e, 0xffff038f, 0xffff0390, 0xffff0391, 0xffff0392, 0xffff0393, + 0xffff0394, 0xffff0395, 0xffff0396, 0xffff0397, 0xffff0398, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffff0399, 0xffff039a, 0xffff039b, 0xffff039c, 0xffff039d, 0xffff039e, + 0xffff039f, 0xffffffff, 0xffff03a0, 0xffff03a1, 0xffff03a2, 0xffff03a3, + 0xffff03a4, 0xffff03a5, 0xffff03a6, 0xffff03a7, 0xffff03a8, 0xffff03a9, + 0xffff03aa, 0xffff03ab, 0xffff03ac, 0xffff03ad, 0xffff03ae, 0xffff03af, + 0xffff03b0, 0xffff03b1, 0xffff03b2, 0xffff03b3, 0xffff03b4, 0xffff03b5, + 0xffff03b6, 0xffff03b7, 0xffff03b8, 0xffff03b9, 0xffff03ba, 0xffff03bb, + 0xffff03bc, 0xffff03bd, 0xffff03be, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0xffff03c2, 0xffff03c3, + 0xffff03c4, 0xffff03c5, 0xffff03c6, 0xffffffff, 0x3c7ffff, 0x3c8ffff, + 0xffffffff, 0xffff03c9, 0xffff03ca, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03cb, 0xffff03cc, 0xffff03cd, + 0xffff03ce, 0xffff03cf, 0xffff03d0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d1ffff, + 0x3d303d2, 0x3d503d4, 0x3d703d6, 0x3d903d8, 0x3db03da, 0x3dd03dc, + 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, + 0xffff03ea, 0xffffffff, 0xffffffff, 0x3ec03eb, 0x3ee03ed, 0x3f003ef, + 0x3f203f1, 0x3f403f3, 0x3f603f5, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, + 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, + 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); //8064 bytes -enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([ 0x0, 0x40, 0x200], [ 0x100, 0x380, 0xbc0], [ 0x2020100, 0x4020302, 0x2020205, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x150000, 0x0, 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, 0x1e0000, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, 0x240023, 0x260025, 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, 0xffff0031, 0x330032, 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, 0x3cffff, 0x3dffff, 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, 0x43ffff, 0x44ffff, 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, 0x4affff, 0x4bffff, 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, 0x51ffff, 0x52ffff, 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, 0xffff0056, 0xffff0057, 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, 0xffff005c, 0xffff005d, 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, 0x62ffff, 0x63ffff, 0x64ffff, 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, 0x69ffff, 0x6affff, 0x6bffff, 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, 0x70ffff, 0x71ffff, 0x72ffff, 0x73ffff, 0x74ffff, 0xffffffff, 0xffff0075, 0xffff0076, 0x780077, 0xffff0079, 0x7affff, 0x7bffff, 0xffffffff, 0xffff007c, 0xffffffff, 0xffff007d, 0xffffffff, 0xffffffff, 0xffff007e, 0x7fffff, 0xffffffff, 0x80ffff, 0xffff0081, 0xffffffff, 0xffff0082, 0x83ffff, 0x84ffff, 0x85ffff, 0xffffffff, 0xffff0086, 0xffffffff, 0x87ffff, 0xffffffff, 0xffff0088, 0xffffffff, 0xffff0089, 0xffff008a, 0x8bffff, 0xffffffff, 0x8cffff, 0x8dffff, 0xffffffff, 0xffffffff, 0x8f008e, 0x910090, 0x930092, 0x950094, 0xffff0096, 0xffff0097, 0xffff0098, 0xffff0099, 0xffff009a, 0xffff009b, 0xffff009c, 0xffff009d, 0x9f009e, 0xa0ffff, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xa9ffff, 0xab00aa, 0xacffff, 0xffffffff, 0xadffff, 0xaeffff, 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, 0xb3ffff, 0xb4ffff, 0xb5ffff, 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, 0xbaffff, 0xbbffff, 0xbcffff, 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, 0xffffffff, 0xc1ffff, 0xc2ffff, 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xc6ffff, 0xc7ffff, 0xc8ffff, 0xc9ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff00ca, 0xcbffff, 0xffff00cc, 0xffff00cd, 0xffffffff, 0xceffff, 0xcfffff, 0xd0ffff, 0xd1ffff, 0xd2ffff, 0xd400d3, 0xd600d5, 0xffff00d7, 0xd900d8, 0xdaffff, 0xdbffff, 0xffffffff, 0xffffffff, 0xffff00dc, 0xddffff, 0xdeffff, 0xffff00df, 0xe100e0, 0xe2ffff, 0xffffffff, 0xe3ffff, 0xe4ffff, 0xffff00e5, 0xe6ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xe7ffff, 0xffffffff, 0xffff00e8, 0xe9ffff, 0xffffffff, 0xffffffff, 0xeb00ea, 0xed00ec, 0xffff00ee, 0xffffffff, 0xffffffff, 0xffff00ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf0ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf1ffff, 0xf2ffff, 0xffffffff, 0xf3ffff, 0xffffffff, 0xf4ffff, 0xf600f5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xf800f7, 0xfa00f9, 0xfbffff, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, 0x1110110, 0x1130112, 0x1150114, 0x1170116, 0xffff0118, 0x11a0119, 0xffffffff, 0x11bffff, 0x11d011c, 0x11effff, 0x11fffff, 0x120ffff, 0x121ffff, 0x122ffff, 0x123ffff, 0x124ffff, 0x125ffff, 0x126ffff, 0x127ffff, 0x128ffff, 0x129ffff, 0x12b012a, 0xffff012c, 0x12dffff, 0xffffffff, 0xffff012e, 0x12fffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1310130, 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, 0x1530152, 0x1550154, 0x1570156, 0x1590158, 0x15b015a, 0x15d015c, 0x15f015e, 0x160ffff, 0x161ffff, 0x162ffff, 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, 0x167ffff, 0x168ffff, 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, 0x175ffff, 0x176ffff, 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, 0x17bffff, 0x17cffff, 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, 0x181ffff, 0x182ffff, 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, 0x187ffff, 0x188ffff, 0x189ffff, 0x18affff, 0x18bffff, 0xffffffff, 0xffff018c, 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, 0x1930192, 0x194ffff, 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, 0x199ffff, 0x19affff, 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, 0x19fffff, 0x1a0ffff, 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, 0x1a5ffff, 0x1a6ffff, 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, 0x1abffff, 0x1acffff, 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, 0x1b1ffff, 0x1b2ffff, 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, 0x1b7ffff, 0x1b8ffff, 0x1b9ffff, 0x1baffff, 0x1bbffff, 0x1bcffff, 0x1bdffff, 0x1beffff, 0x1bfffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1c0ffff, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, 0x1e201e1, 0x1e401e3, 0xffff01e5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1e6ffff, 0xffffffff, 0x1e7ffff, 0xffffffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, 0x1ebffff, 0x1ecffff, 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, 0x1f1ffff, 0x1f2ffff, 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, 0x1f7ffff, 0x1f8ffff, 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, 0x1fdffff, 0x1feffff, 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, 0x203ffff, 0x204ffff, 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, 0x209ffff, 0x20affff, 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, 0x20fffff, 0x210ffff, 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, 0x215ffff, 0x216ffff, 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, 0x21bffff, 0x21cffff, 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, 0x221ffff, 0x222ffff, 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, 0x227ffff, 0x228ffff, 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, 0x22dffff, 0x22effff, 0x22fffff, 0x230ffff, 0x231ffff, 0x232ffff, 0xffffffff, 0xffffffff, 0x233ffff, 0xffffffff, 0xffffffff, 0x234ffff, 0x235ffff, 0x236ffff, 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, 0x23bffff, 0x23cffff, 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, 0x241ffff, 0x242ffff, 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, 0x247ffff, 0x248ffff, 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, 0x24dffff, 0x24effff, 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, 0x253ffff, 0x254ffff, 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, 0x259ffff, 0x25affff, 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, 0x25fffff, 0x260ffff, 0x261ffff, 0x262ffff, 0x263ffff, 0x2650264, 0x2670266, 0x2690268, 0x26b026a, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x26d026c, 0x26f026e, 0x2710270, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2730272, 0x2750274, 0x2770276, 0x2790278, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27b027a, 0x27d027c, 0x27f027e, 0x2810280, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2830282, 0x2850284, 0x2870286, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x288ffff, 0x289ffff, 0x28affff, 0x28bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x28d028c, 0x28f028e, 0x2910290, 0x2930292, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2950294, 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, 0x29f029e, 0x2a102a0, 0xffffffff, 0x2a302a2, 0x2a502a4, 0x2a702a6, 0x2a902a8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ab02aa, 0x2ad02ac, 0x2af02ae, 0x2b102b0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2b302b2, 0x2b502b4, 0x2b702b6, 0x2b902b8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2bb02ba, 0x2bcffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02bd, 0xffffffff, 0x2beffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c002bf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c202c1, 0xffffffff, 0x2c3ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c4ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02c5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2c702c6, 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, 0x2d102d0, 0x2d302d2, 0x2d502d4, 0xffffffff, 0xffffffff, 0xffff02d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2d802d7, 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, 0x2ee02ed, 0x2f002ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0x3040303, 0x3060305, 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, 0xffff031f, 0x320ffff, 0xffffffff, 0x321ffff, 0xffff0322, 0xffff0323, 0xffff0324, 0xffff0325, 0xffffffff, 0xffffffff, 0x326ffff, 0xffffffff, 0xffff0327, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x328ffff, 0x329ffff, 0x32affff, 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, 0x32fffff, 0x330ffff, 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, 0x335ffff, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, 0x33bffff, 0x33cffff, 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, 0x341ffff, 0x342ffff, 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, 0x347ffff, 0x348ffff, 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, 0x34dffff, 0x34effff, 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, 0x353ffff, 0x354ffff, 0x355ffff, 0x356ffff, 0x357ffff, 0x358ffff, 0x359ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff035a, 0xffff035b, 0xffffffff, 0x35cffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x35e035d, 0x360035f, 0x3620361, 0x3640363, 0x3660365, 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, 0x370036f, 0x3720371, 0x3740373, 0x3760375, 0x3780377, 0x37a0379, 0x37c037b, 0x37e037d, 0x380037f, 0x3820381, 0x383ffff, 0xffffffff, 0xffffffff, 0x384ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x385ffff, 0x386ffff, 0x387ffff, 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, 0x38cffff, 0x38dffff, 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, 0x392ffff, 0x393ffff, 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, 0x398ffff, 0x399ffff, 0x39affff, 0x39bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x39cffff, 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, 0x3a1ffff, 0x3a2ffff, 0x3a3ffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, 0x3a7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0x3abffff, 0x3acffff, 0x3adffff, 0x3aeffff, 0xffffffff, 0x3afffff, 0x3b0ffff, 0x3b1ffff, 0x3b2ffff, 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, 0x3b7ffff, 0x3b8ffff, 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, 0x3bdffff, 0x3beffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, 0x3c3ffff, 0x3c4ffff, 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, 0x3c9ffff, 0x3caffff, 0x3cbffff, 0x3ccffff, 0x3cdffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ce, 0xffff03cf, 0x3d0ffff, 0x3d1ffff, 0x3d2ffff, 0x3d3ffff, 0x3d4ffff, 0xffffffff, 0xffffffff, 0xffff03d5, 0xffffffff, 0x3d6ffff, 0x3d7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d8ffff, 0x3d9ffff, 0x3daffff, 0x3dbffff, 0x3dcffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3ddffff, 0x3df03de, 0x3e103e0, 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0x3eb03ea, 0x3ed03ec, 0x3ef03ee, 0x3f103f0, 0x3f303f2, 0x3f503f4, 0xffff03f6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, 0x40e040d, 0x410040f, 0x4120411, 0x4140413, 0x4160415, 0x4180417, 0x41a0419, 0x41c041b, 0x41e041d, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); +enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)([0x0, 0x40, + 0x200], [0x100, 0x380, 0xbc0], [0x2020100, 0x4020302, 0x2020205, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, 0x2020202, + 0x2020202, 0x10000, 0x30002, 0x50004, 0x70006, 0x90008, 0xa, 0xb0000, + 0xd000c, 0xf000e, 0x110010, 0x130012, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x150000, 0x0, 0x170016, 0x190018, 0x1b001a, 0x1d001c, 0x0, 0x0, + 0x1e0000, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x220021, 0x240023, + 0x25, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x260000, 0x27, 0x290028, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2d002c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff, + 0x20001, 0x40003, 0x60005, 0x80007, 0xa0009, 0xc000b, 0xe000d, + 0x10000f, 0x120011, 0x140013, 0x160015, 0x180017, 0xffff0019, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1affff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x1c001b, 0x1e001d, 0x20001f, 0x220021, + 0x240023, 0x260025, 0x280027, 0x2a0029, 0x2c002b, 0x2e002d, 0x30002f, + 0xffff0031, 0x330032, 0x350034, 0x370036, 0x390038, 0x3affff, 0x3bffff, + 0x3cffff, 0x3dffff, 0x3effff, 0x3fffff, 0x40ffff, 0x41ffff, 0x42ffff, + 0x43ffff, 0x44ffff, 0x45ffff, 0x46ffff, 0x47ffff, 0x48ffff, 0x49ffff, + 0x4affff, 0x4bffff, 0x4cffff, 0x4dffff, 0x4effff, 0x4fffff, 0x50ffff, + 0x51ffff, 0x52ffff, 0x53ffff, 0x54ffff, 0x55ffff, 0xffffffff, + 0xffff0056, 0xffff0057, 0xffff0058, 0xffff0059, 0xffff005a, 0xffff005b, + 0xffff005c, 0xffff005d, 0x5effff, 0x5fffff, 0x60ffff, 0x61ffff, + 0x62ffff, 0x63ffff, 0x64ffff, 0x65ffff, 0x66ffff, 0x67ffff, 0x68ffff, + 0x69ffff, 0x6affff, 0x6bffff, 0x6cffff, 0x6dffff, 0x6effff, 0x6fffff, + 0x70ffff, 0x71ffff, 0x72ffff, 0x73ffff, 0x74ffff, 0xffffffff, + 0xffff0075, 0xffff0076, 0x780077, 0xffff0079, 0x7affff, 0x7bffff, + 0xffffffff, 0xffff007c, 0xffffffff, 0xffff007d, 0xffffffff, 0xffffffff, + 0xffff007e, 0x7fffff, 0xffffffff, 0x80ffff, 0xffff0081, 0xffffffff, + 0xffff0082, 0x83ffff, 0x84ffff, 0x85ffff, 0xffffffff, 0xffff0086, + 0xffffffff, 0x87ffff, 0xffffffff, 0xffff0088, 0xffffffff, 0xffff0089, + 0xffff008a, 0x8bffff, 0xffffffff, 0x8cffff, 0x8dffff, 0xffffffff, + 0xffffffff, 0x8f008e, 0x910090, 0x930092, 0x950094, 0xffff0096, + 0xffff0097, 0xffff0098, 0xffff0099, 0xffff009a, 0xffff009b, 0xffff009c, + 0xffff009d, 0x9f009e, 0xa0ffff, 0xa1ffff, 0xa2ffff, 0xa3ffff, 0xa4ffff, + 0xa5ffff, 0xa6ffff, 0xa7ffff, 0xa8ffff, 0xa9ffff, 0xab00aa, 0xacffff, + 0xffffffff, 0xadffff, 0xaeffff, 0xafffff, 0xb0ffff, 0xb1ffff, 0xb2ffff, + 0xb3ffff, 0xb4ffff, 0xb5ffff, 0xb6ffff, 0xb7ffff, 0xb8ffff, 0xb9ffff, + 0xbaffff, 0xbbffff, 0xbcffff, 0xbdffff, 0xbeffff, 0xbfffff, 0xc0ffff, + 0xffffffff, 0xc1ffff, 0xc2ffff, 0xc3ffff, 0xc4ffff, 0xc5ffff, 0xc6ffff, + 0xc7ffff, 0xc8ffff, 0xc9ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffff00ca, 0xcbffff, 0xffff00cc, 0xffff00cd, 0xffffffff, + 0xceffff, 0xcfffff, 0xd0ffff, 0xd1ffff, 0xd2ffff, 0xd400d3, 0xd600d5, + 0xffff00d7, 0xd900d8, 0xdaffff, 0xdbffff, 0xffffffff, 0xffffffff, + 0xffff00dc, 0xddffff, 0xdeffff, 0xffff00df, 0xe100e0, 0xe2ffff, + 0xffffffff, 0xe3ffff, 0xe4ffff, 0xffff00e5, 0xe6ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xe7ffff, 0xffffffff, 0xffff00e8, 0xe9ffff, + 0xffffffff, 0xffffffff, 0xeb00ea, 0xed00ec, 0xffff00ee, 0xffffffff, + 0xffffffff, 0xffff00ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xf0ffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xf1ffff, 0xf2ffff, 0xffffffff, 0xf3ffff, 0xffffffff, 0xf4ffff, + 0xf600f5, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xf800f7, 0xfa00f9, 0xfbffff, 0xfd00fc, 0xff00fe, 0x1010100, 0x1030102, + 0x1050104, 0x1070106, 0x1090108, 0x10b010a, 0x10d010c, 0x10f010e, + 0x1110110, 0x1130112, 0x1150114, 0x1170116, 0xffff0118, 0x11a0119, + 0xffffffff, 0x11bffff, 0x11d011c, 0x11effff, 0x11fffff, 0x120ffff, + 0x121ffff, 0x122ffff, 0x123ffff, 0x124ffff, 0x125ffff, 0x126ffff, + 0x127ffff, 0x128ffff, 0x129ffff, 0x12b012a, 0xffff012c, 0x12dffff, + 0xffffffff, 0xffff012e, 0x12fffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1310130, + 0x1330132, 0x1350134, 0x1370136, 0x1390138, 0x13b013a, 0x13d013c, + 0x13f013e, 0x1410140, 0x1430142, 0x1450144, 0x1470146, 0x1490148, + 0x14b014a, 0x14d014c, 0x14f014e, 0x1510150, 0x1530152, 0x1550154, + 0x1570156, 0x1590158, 0x15b015a, 0x15d015c, 0x15f015e, 0x160ffff, + 0x161ffff, 0x162ffff, 0x163ffff, 0x164ffff, 0x165ffff, 0x166ffff, + 0x167ffff, 0x168ffff, 0x169ffff, 0x16affff, 0x16bffff, 0x16cffff, + 0x16dffff, 0x16effff, 0x16fffff, 0x170ffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x171ffff, 0x172ffff, 0x173ffff, 0x174ffff, + 0x175ffff, 0x176ffff, 0x177ffff, 0x178ffff, 0x179ffff, 0x17affff, + 0x17bffff, 0x17cffff, 0x17dffff, 0x17effff, 0x17fffff, 0x180ffff, + 0x181ffff, 0x182ffff, 0x183ffff, 0x184ffff, 0x185ffff, 0x186ffff, + 0x187ffff, 0x188ffff, 0x189ffff, 0x18affff, 0x18bffff, 0xffffffff, + 0xffff018c, 0xffff018d, 0xffff018e, 0xffff018f, 0xffff0190, 0xffff0191, + 0x1930192, 0x194ffff, 0x195ffff, 0x196ffff, 0x197ffff, 0x198ffff, + 0x199ffff, 0x19affff, 0x19bffff, 0x19cffff, 0x19dffff, 0x19effff, + 0x19fffff, 0x1a0ffff, 0x1a1ffff, 0x1a2ffff, 0x1a3ffff, 0x1a4ffff, + 0x1a5ffff, 0x1a6ffff, 0x1a7ffff, 0x1a8ffff, 0x1a9ffff, 0x1aaffff, + 0x1abffff, 0x1acffff, 0x1adffff, 0x1aeffff, 0x1afffff, 0x1b0ffff, + 0x1b1ffff, 0x1b2ffff, 0x1b3ffff, 0x1b4ffff, 0x1b5ffff, 0x1b6ffff, + 0x1b7ffff, 0x1b8ffff, 0x1b9ffff, 0x1baffff, 0x1bbffff, 0x1bcffff, + 0x1bdffff, 0x1beffff, 0x1bfffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x1c0ffff, 0x1c201c1, 0x1c401c3, 0x1c601c5, 0x1c801c7, + 0x1ca01c9, 0x1cc01cb, 0x1ce01cd, 0x1d001cf, 0x1d201d1, 0x1d401d3, + 0x1d601d5, 0x1d801d7, 0x1da01d9, 0x1dc01db, 0x1de01dd, 0x1e001df, + 0x1e201e1, 0x1e401e3, 0xffff01e5, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1e6ffff, + 0xffffffff, 0x1e7ffff, 0xffffffff, 0x1e8ffff, 0x1e9ffff, 0x1eaffff, + 0x1ebffff, 0x1ecffff, 0x1edffff, 0x1eeffff, 0x1efffff, 0x1f0ffff, + 0x1f1ffff, 0x1f2ffff, 0x1f3ffff, 0x1f4ffff, 0x1f5ffff, 0x1f6ffff, + 0x1f7ffff, 0x1f8ffff, 0x1f9ffff, 0x1faffff, 0x1fbffff, 0x1fcffff, + 0x1fdffff, 0x1feffff, 0x1ffffff, 0x200ffff, 0x201ffff, 0x202ffff, + 0x203ffff, 0x204ffff, 0x205ffff, 0x206ffff, 0x207ffff, 0x208ffff, + 0x209ffff, 0x20affff, 0x20bffff, 0x20cffff, 0x20dffff, 0x20effff, + 0x20fffff, 0x210ffff, 0x211ffff, 0x212ffff, 0x213ffff, 0x214ffff, + 0x215ffff, 0x216ffff, 0x217ffff, 0x218ffff, 0x219ffff, 0x21affff, + 0x21bffff, 0x21cffff, 0x21dffff, 0x21effff, 0x21fffff, 0x220ffff, + 0x221ffff, 0x222ffff, 0x223ffff, 0x224ffff, 0x225ffff, 0x226ffff, + 0x227ffff, 0x228ffff, 0x229ffff, 0x22affff, 0x22bffff, 0x22cffff, + 0x22dffff, 0x22effff, 0x22fffff, 0x230ffff, 0x231ffff, 0x232ffff, + 0xffffffff, 0xffffffff, 0x233ffff, 0xffffffff, 0xffffffff, 0x234ffff, + 0x235ffff, 0x236ffff, 0x237ffff, 0x238ffff, 0x239ffff, 0x23affff, + 0x23bffff, 0x23cffff, 0x23dffff, 0x23effff, 0x23fffff, 0x240ffff, + 0x241ffff, 0x242ffff, 0x243ffff, 0x244ffff, 0x245ffff, 0x246ffff, + 0x247ffff, 0x248ffff, 0x249ffff, 0x24affff, 0x24bffff, 0x24cffff, + 0x24dffff, 0x24effff, 0x24fffff, 0x250ffff, 0x251ffff, 0x252ffff, + 0x253ffff, 0x254ffff, 0x255ffff, 0x256ffff, 0x257ffff, 0x258ffff, + 0x259ffff, 0x25affff, 0x25bffff, 0x25cffff, 0x25dffff, 0x25effff, + 0x25fffff, 0x260ffff, 0x261ffff, 0x262ffff, 0x263ffff, 0x2650264, + 0x2670266, 0x2690268, 0x26b026a, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x26d026c, 0x26f026e, 0x2710270, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2730272, 0x2750274, 0x2770276, + 0x2790278, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x27b027a, + 0x27d027c, 0x27f027e, 0x2810280, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2830282, 0x2850284, 0x2870286, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x288ffff, 0x289ffff, 0x28affff, + 0x28bffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x28d028c, + 0x28f028e, 0x2910290, 0x2930292, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2950294, 0x2970296, 0x2990298, 0x29b029a, 0x29d029c, + 0x29f029e, 0x2a102a0, 0xffffffff, 0x2a302a2, 0x2a502a4, 0x2a702a6, + 0x2a902a8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x2ab02aa, + 0x2ad02ac, 0x2af02ae, 0x2b102b0, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2b302b2, 0x2b502b4, 0x2b702b6, 0x2b902b8, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2bb02ba, 0x2bcffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff02bd, 0xffffffff, + 0x2beffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2c002bf, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x2c202c1, 0xffffffff, 0x2c3ffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x2c4ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffff02c5, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2c702c6, 0x2c902c8, 0x2cb02ca, 0x2cd02cc, 0x2cf02ce, + 0x2d102d0, 0x2d302d2, 0x2d502d4, 0xffffffff, 0xffffffff, 0xffff02d6, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2d802d7, 0x2da02d9, 0x2dc02db, 0x2de02dd, 0x2e002df, + 0x2e202e1, 0x2e402e3, 0x2e602e5, 0x2e802e7, 0x2ea02e9, 0x2ec02eb, + 0x2ee02ed, 0x2f002ef, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x2f202f1, 0x2f402f3, 0x2f602f5, 0x2f802f7, 0x2fa02f9, + 0x2fc02fb, 0x2fe02fd, 0x30002ff, 0x3020301, 0x3040303, 0x3060305, + 0x3080307, 0x30a0309, 0x30c030b, 0x30e030d, 0x310030f, 0x3120311, + 0x3140313, 0x3160315, 0x3180317, 0x31a0319, 0x31c031b, 0x31e031d, + 0xffff031f, 0x320ffff, 0xffffffff, 0x321ffff, 0xffff0322, 0xffff0323, + 0xffff0324, 0xffff0325, 0xffffffff, 0xffffffff, 0x326ffff, 0xffffffff, + 0xffff0327, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x328ffff, + 0x329ffff, 0x32affff, 0x32bffff, 0x32cffff, 0x32dffff, 0x32effff, + 0x32fffff, 0x330ffff, 0x331ffff, 0x332ffff, 0x333ffff, 0x334ffff, + 0x335ffff, 0x336ffff, 0x337ffff, 0x338ffff, 0x339ffff, 0x33affff, + 0x33bffff, 0x33cffff, 0x33dffff, 0x33effff, 0x33fffff, 0x340ffff, + 0x341ffff, 0x342ffff, 0x343ffff, 0x344ffff, 0x345ffff, 0x346ffff, + 0x347ffff, 0x348ffff, 0x349ffff, 0x34affff, 0x34bffff, 0x34cffff, + 0x34dffff, 0x34effff, 0x34fffff, 0x350ffff, 0x351ffff, 0x352ffff, + 0x353ffff, 0x354ffff, 0x355ffff, 0x356ffff, 0x357ffff, 0x358ffff, + 0x359ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff035a, + 0xffff035b, 0xffffffff, 0x35cffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x35e035d, 0x360035f, 0x3620361, + 0x3640363, 0x3660365, 0x3680367, 0x36a0369, 0x36c036b, 0x36e036d, + 0x370036f, 0x3720371, 0x3740373, 0x3760375, 0x3780377, 0x37a0379, + 0x37c037b, 0x37e037d, 0x380037f, 0x3820381, 0x383ffff, 0xffffffff, + 0xffffffff, 0x384ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x385ffff, + 0x386ffff, 0x387ffff, 0x388ffff, 0x389ffff, 0x38affff, 0x38bffff, + 0x38cffff, 0x38dffff, 0x38effff, 0x38fffff, 0x390ffff, 0x391ffff, + 0x392ffff, 0x393ffff, 0x394ffff, 0x395ffff, 0x396ffff, 0x397ffff, + 0x398ffff, 0x399ffff, 0x39affff, 0x39bffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x39cffff, 0x39dffff, 0x39effff, 0x39fffff, 0x3a0ffff, + 0x3a1ffff, 0x3a2ffff, 0x3a3ffff, 0x3a4ffff, 0x3a5ffff, 0x3a6ffff, + 0x3a7ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x3a8ffff, 0x3a9ffff, 0x3aaffff, 0x3abffff, + 0x3acffff, 0x3adffff, 0x3aeffff, 0xffffffff, 0x3afffff, 0x3b0ffff, + 0x3b1ffff, 0x3b2ffff, 0x3b3ffff, 0x3b4ffff, 0x3b5ffff, 0x3b6ffff, + 0x3b7ffff, 0x3b8ffff, 0x3b9ffff, 0x3baffff, 0x3bbffff, 0x3bcffff, + 0x3bdffff, 0x3beffff, 0x3bfffff, 0x3c0ffff, 0x3c1ffff, 0x3c2ffff, + 0x3c3ffff, 0x3c4ffff, 0x3c5ffff, 0x3c6ffff, 0x3c7ffff, 0x3c8ffff, + 0x3c9ffff, 0x3caffff, 0x3cbffff, 0x3ccffff, 0x3cdffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff03ce, 0xffff03cf, + 0x3d0ffff, 0x3d1ffff, 0x3d2ffff, 0x3d3ffff, 0x3d4ffff, 0xffffffff, + 0xffffffff, 0xffff03d5, 0xffffffff, 0x3d6ffff, 0x3d7ffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3d8ffff, + 0x3d9ffff, 0x3daffff, 0x3dbffff, 0x3dcffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x3ddffff, 0x3df03de, 0x3e103e0, + 0x3e303e2, 0x3e503e4, 0x3e703e6, 0x3e903e8, 0x3eb03ea, 0x3ed03ec, + 0x3ef03ee, 0x3f103f0, 0x3f303f2, 0x3f503f4, 0xffff03f6, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3f803f7, 0x3fa03f9, 0x3fc03fb, 0x3fe03fd, 0x40003ff, + 0x4020401, 0x4040403, 0x4060405, 0x4080407, 0x40a0409, 0x40c040b, + 0x40e040d, 0x410040f, 0x4120411, 0x4140413, 0x4160415, 0x4180417, + 0x41a0419, 0x41c041b, 0x41e041d, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]); @property { private alias _IUA = immutable(uint[]); -_IUA toUpperTable() { static _IUA t = [ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x49, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c4, 0x1c4, 0x1c7, 0x1c7, 0x1ca, 0x1ca, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f1, 0x1f1, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, 0x18a, 0x18f, 0x190, 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, 0x19c, 0x2c6e, 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, 0x1b1, 0x1b2, 0x245, 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, 0x3fe, 0x3ff, 0x386, 0x388, 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, 0x526, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0xa77d, 0x2c63, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c60, 0x23a, 0x23e, 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa790, 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x2000053, 0x53, 0x130, 0x2000046, 0x46, 0x2000046, 0x49, 0x2000046, 0x4c, 0x3000046, 0x46, 0x49, 0x3000046, 0x46, 0x4c, 0x2000053, 0x54, 0x2000053, 0x54, 0x2000535, 0x552, 0x2000544, 0x546, 0x2000544, 0x535, 0x2000544, 0x53b, 0x200054e, 0x546, 0x2000544, 0x53d, 0x20002bc, 0x4e, 0x3000399, 0x308, 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, 0x2000048, 0x331, 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, 0x2000041, 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, 0x313, 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, 0x342, 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, 0x342, 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, 0x308, 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, 0x342, 0x20003a9, 0x342, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, 0x399, 0x2001f0b, 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, 0x399, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, 0x399, 0x2001f0b, 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, 0x399, 0x2001f6c, 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, 0x399, 0x2001f6c, 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, 0x399, 0x2000391, 0x399, 0x2000391, 0x399, 0x2000397, 0x399, 0x2000397, 0x399, 0x20003a9, 0x399, 0x20003a9, 0x399, 0x2001fba, 0x399, 0x2000386, 0x399, 0x2001fca, 0x399, 0x2000389, 0x399, 0x2001ffa, 0x399, 0x200038f, 0x399, 0x3000391, 0x342, 0x399, 0x3000397, 0x342, 0x399, 0x30003a9, 0x342, 0x399]; return t; } -_IUA toLowerTable() { static _IUA t = [ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x69, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0xff, 0x17a, 0x17c, 0x17e, 0x253, 0x183, 0x185, 0x254, 0x188, 0x256, 0x257, 0x18c, 0x1dd, 0x259, 0x25b, 0x192, 0x260, 0x263, 0x269, 0x268, 0x199, 0x26f, 0x272, 0x275, 0x1a1, 0x1a3, 0x1a5, 0x280, 0x1a8, 0x283, 0x1ad, 0x288, 0x1b0, 0x28a, 0x28b, 0x1b4, 0x1b6, 0x292, 0x1b9, 0x1bd, 0x1c6, 0x1c6, 0x1c9, 0x1c9, 0x1cc, 0x1cc, 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f3, 0x1f3, 0x1f5, 0x195, 0x1bf, 0x1f9, 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, 0x207, 0x209, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x21f, 0x19e, 0x223, 0x225, 0x227, 0x229, 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x2c65, 0x23c, 0x19a, 0x2c66, 0x242, 0x180, 0x289, 0x28c, 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x371, 0x373, 0x377, 0x3ac, 0x3ad, 0x3ae, 0x3af, 0x3cc, 0x3cd, 0x3ce, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf, 0x3c0, 0x3c1, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3d7, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3b8, 0x3f8, 0x3f2, 0x3fb, 0x37b, 0x37c, 0x37d, 0x450, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45a, 0x45b, 0x45c, 0x45d, 0x45e, 0x45f, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43a, 0x43b, 0x43c, 0x43d, 0x43e, 0x43f, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44a, 0x44b, 0x44c, 0x44d, 0x44e, 0x44f, 0x461, 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, 0x4b9, 0x4bb, 0x4bd, 0x4bf, 0x4cf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4cc, 0x4ce, 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, 0x4df, 0x4e1, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, 0x50d, 0x50f, 0x511, 0x513, 0x515, 0x517, 0x519, 0x51b, 0x51d, 0x51f, 0x521, 0x523, 0x525, 0x527, 0x561, 0x562, 0x563, 0x564, 0x565, 0x566, 0x567, 0x568, 0x569, 0x56a, 0x56b, 0x56c, 0x56d, 0x56e, 0x56f, 0x570, 0x571, 0x572, 0x573, 0x574, 0x575, 0x576, 0x577, 0x578, 0x579, 0x57a, 0x57b, 0x57c, 0x57d, 0x57e, 0x57f, 0x580, 0x581, 0x582, 0x583, 0x584, 0x585, 0x586, 0x2d00, 0x2d01, 0x2d02, 0x2d03, 0x2d04, 0x2d05, 0x2d06, 0x2d07, 0x2d08, 0x2d09, 0x2d0a, 0x2d0b, 0x2d0c, 0x2d0d, 0x2d0e, 0x2d0f, 0x2d10, 0x2d11, 0x2d12, 0x2d13, 0x2d14, 0x2d15, 0x2d16, 0x2d17, 0x2d18, 0x2d19, 0x2d1a, 0x2d1b, 0x2d1c, 0x2d1d, 0x2d1e, 0x2d1f, 0x2d20, 0x2d21, 0x2d22, 0x2d23, 0x2d24, 0x2d25, 0x2d27, 0x2d2d, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0xdf, 0x1ea1, 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x1efb, 0x1efd, 0x1eff, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x3c9, 0x6b, 0xe5, 0x214e, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2184, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x2c30, 0x2c31, 0x2c32, 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, 0x2c3b, 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53, 0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59, 0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c61, 0x26b, 0x1d7d, 0x27d, 0x2c68, 0x2c6a, 0x2c6c, 0x251, 0x271, 0x250, 0x252, 0x2c73, 0x2c76, 0x23f, 0x240, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2cdf, 0x2ce1, 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641, 0xa643, 0xa645, 0xa647, 0xa649, 0xa64b, 0xa64d, 0xa64f, 0xa651, 0xa653, 0xa655, 0xa657, 0xa659, 0xa65b, 0xa65d, 0xa65f, 0xa661, 0xa663, 0xa665, 0xa667, 0xa669, 0xa66b, 0xa66d, 0xa681, 0xa683, 0xa685, 0xa687, 0xa689, 0xa68b, 0xa68d, 0xa68f, 0xa691, 0xa693, 0xa695, 0xa697, 0xa723, 0xa725, 0xa727, 0xa729, 0xa72b, 0xa72d, 0xa72f, 0xa733, 0xa735, 0xa737, 0xa739, 0xa73b, 0xa73d, 0xa73f, 0xa741, 0xa743, 0xa745, 0xa747, 0xa749, 0xa74b, 0xa74d, 0xa74f, 0xa751, 0xa753, 0xa755, 0xa757, 0xa759, 0xa75b, 0xa75d, 0xa75f, 0xa761, 0xa763, 0xa765, 0xa767, 0xa769, 0xa76b, 0xa76d, 0xa76f, 0xa77a, 0xa77c, 0x1d79, 0xa77f, 0xa781, 0xa783, 0xa785, 0xa787, 0xa78c, 0x265, 0xa791, 0xa793, 0xa7a1, 0xa7a3, 0xa7a5, 0xa7a7, 0xa7a9, 0x266, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0x10428, 0x10429, 0x1042a, 0x1042b, 0x1042c, 0x1042d, 0x1042e, 0x1042f, 0x10430, 0x10431, 0x10432, 0x10433, 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, 0x1043a, 0x1043b, 0x1043c, 0x1043d, 0x1043e, 0x1043f, 0x10440, 0x10441, 0x10442, 0x10443, 0x10444, 0x10445, 0x10446, 0x10447, 0x10448, 0x10449, 0x1044a, 0x1044b, 0x1044c, 0x1044d, 0x1044e, 0x1044f, 0xdf, 0x2000069, 0x307, 0xfb00, 0xfb01, 0xfb02, 0xfb03, 0xfb04, 0xfb05, 0xfb06, 0x587, 0xfb13, 0xfb14, 0xfb15, 0xfb16, 0xfb17, 0x149, 0x390, 0x3b0, 0x1f0, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1fb6, 0x1fc6, 0x1fd2, 0x1fd3, 0x1fd6, 0x1fd7, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe6, 0x1fe7, 0x1ff6, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb3, 0x1fb3, 0x1fc3, 0x1fc3, 0x1ff3, 0x1ff3, 0x1fb2, 0x1fb4, 0x1fc2, 0x1fc4, 0x1ff2, 0x1ff4, 0x1fb7, 0x1fc7, 0x1ff7]; return t; } -_IUA toTitleTable() { static _IUA t = [ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x49, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c5, 0x1c5, 0x1c5, 0x1c8, 0x1c8, 0x1c8, 0x1cb, 0x1cb, 0x1cb, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f2, 0x1f2, 0x1f2, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, 0x18a, 0x18f, 0x190, 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, 0x19c, 0x2c6e, 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, 0x1b1, 0x1b2, 0x245, 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, 0x3fe, 0x3ff, 0x386, 0x388, 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, 0x526, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0xa77d, 0x2c63, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c60, 0x23a, 0x23e, 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa790, 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x2000053, 0x73, 0x130, 0x2000046, 0x66, 0x2000046, 0x69, 0x2000046, 0x6c, 0x3000046, 0x66, 0x69, 0x3000046, 0x66, 0x6c, 0x2000053, 0x74, 0x2000053, 0x74, 0x2000535, 0x582, 0x2000544, 0x576, 0x2000544, 0x565, 0x2000544, 0x56b, 0x200054e, 0x576, 0x2000544, 0x56d, 0x20002bc, 0x4e, 0x3000399, 0x308, 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, 0x2000048, 0x331, 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, 0x2000041, 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, 0x313, 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, 0x342, 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, 0x342, 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, 0x308, 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, 0x342, 0x20003a9, 0x342, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fbc, 0x1fcc, 0x1fcc, 0x1ffc, 0x1ffc, 0x2001fba, 0x345, 0x2000386, 0x345, 0x2001fca, 0x345, 0x2000389, 0x345, 0x2001ffa, 0x345, 0x200038f, 0x345, 0x3000391, 0x342, 0x345, 0x3000397, 0x342, 0x345, 0x30003a9, 0x342, 0x345]; return t; } +_IUA toUpperTable() +{ + static _IUA t = [ + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178, + 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, + 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, + 0x128, 0x12a, 0x12c, 0x12e, 0x49, 0x132, 0x134, 0x136, 0x139, 0x13b, + 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, + 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, + 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, + 0x17b, 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, + 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, + 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c4, 0x1c4, 0x1c7, 0x1c7, 0x1ca, 0x1ca, + 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x18e, 0x1de, + 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f1, 0x1f1, + 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, + 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, + 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, + 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, + 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, 0x18a, 0x18f, 0x190, + 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, 0x2c62, 0x19c, 0x2c6e, + 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, 0x244, 0x1b1, 0x1b2, 0x245, + 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, 0x3fe, 0x3ff, 0x386, 0x388, + 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, + 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, + 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x38c, + 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, 0x3cf, 0x3d8, 0x3da, 0x3dc, + 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x39a, + 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, 0x411, 0x412, 0x413, 0x414, + 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, + 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, + 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x400, 0x401, 0x402, + 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, 0x40c, + 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, + 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, + 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, + 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, + 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, + 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, + 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, + 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, + 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x514, + 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, 0x526, 0x531, + 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, + 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, + 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, + 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0xa77d, 0x2c63, + 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, + 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, + 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, + 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, + 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, + 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, + 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, + 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, + 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, + 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, + 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, + 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, + 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, + 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, + 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, + 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, + 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, + 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f59, 0x1f5b, + 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, + 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, + 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x1f88, 0x1f89, 0x1f8a, + 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, + 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, + 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, 0x399, 0x1fcc, 0x1fd8, + 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, 0x2160, 0x2161, 0x2162, + 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, + 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, + 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, + 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, + 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, + 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, + 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, + 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, + 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, + 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c60, 0x23a, 0x23e, + 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, + 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, + 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, + 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, + 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, + 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, + 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, + 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, + 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, + 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, + 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0xa640, + 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, + 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, + 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, + 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa722, 0xa724, + 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, + 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, 0xa74a, + 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, + 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, + 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa790, + 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xff21, 0xff22, 0xff23, + 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, + 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, + 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, + 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, + 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, + 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, + 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, + 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x2000053, 0x53, 0x130, + 0x2000046, 0x46, 0x2000046, 0x49, 0x2000046, 0x4c, 0x3000046, 0x46, + 0x49, 0x3000046, 0x46, 0x4c, 0x2000053, 0x54, 0x2000053, 0x54, + 0x2000535, 0x552, 0x2000544, 0x546, 0x2000544, 0x535, 0x2000544, 0x53b, + 0x200054e, 0x546, 0x2000544, 0x53d, 0x20002bc, 0x4e, 0x3000399, 0x308, + 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, 0x2000048, 0x331, + 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, 0x2000041, 0x2be, + 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, 0x313, 0x301, + 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, 0x342, 0x3000399, + 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, 0x342, 0x3000399, + 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, 0x308, 0x301, + 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, 0x342, 0x20003a9, + 0x342, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, 0x399, 0x2001f0b, + 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, + 0x399, 0x2001f08, 0x399, 0x2001f09, 0x399, 0x2001f0a, 0x399, 0x2001f0b, + 0x399, 0x2001f0c, 0x399, 0x2001f0d, 0x399, 0x2001f0e, 0x399, 0x2001f0f, + 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, + 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, + 0x399, 0x2001f28, 0x399, 0x2001f29, 0x399, 0x2001f2a, 0x399, 0x2001f2b, + 0x399, 0x2001f2c, 0x399, 0x2001f2d, 0x399, 0x2001f2e, 0x399, 0x2001f2f, + 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, + 0x399, 0x2001f6c, 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, + 0x399, 0x2001f68, 0x399, 0x2001f69, 0x399, 0x2001f6a, 0x399, 0x2001f6b, + 0x399, 0x2001f6c, 0x399, 0x2001f6d, 0x399, 0x2001f6e, 0x399, 0x2001f6f, + 0x399, 0x2000391, 0x399, 0x2000391, 0x399, 0x2000397, 0x399, 0x2000397, + 0x399, 0x20003a9, 0x399, 0x20003a9, 0x399, 0x2001fba, 0x399, 0x2000386, + 0x399, 0x2001fca, 0x399, 0x2000389, 0x399, 0x2001ffa, 0x399, 0x200038f, + 0x399, 0x3000391, 0x342, 0x399, 0x3000397, 0x342, 0x399, 0x30003a9, 0x342, + 0x399 + ]; + return t; } - +_IUA toLowerTable() +{ + static _IUA t = [ + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x101, 0x103, 0x105, + 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, 0x117, 0x119, + 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, + 0x12f, 0x69, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, 0x140, 0x142, + 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, 0x155, 0x157, + 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16b, + 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0xff, 0x17a, 0x17c, 0x17e, + 0x253, 0x183, 0x185, 0x254, 0x188, 0x256, 0x257, 0x18c, 0x1dd, 0x259, + 0x25b, 0x192, 0x260, 0x263, 0x269, 0x268, 0x199, 0x26f, 0x272, 0x275, + 0x1a1, 0x1a3, 0x1a5, 0x280, 0x1a8, 0x283, 0x1ad, 0x288, 0x1b0, 0x28a, + 0x28b, 0x1b4, 0x1b6, 0x292, 0x1b9, 0x1bd, 0x1c6, 0x1c6, 0x1c9, 0x1c9, + 0x1cc, 0x1cc, 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, + 0x1df, 0x1e1, 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f3, + 0x1f3, 0x1f5, 0x195, 0x1bf, 0x1f9, 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, + 0x205, 0x207, 0x209, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, + 0x219, 0x21b, 0x21d, 0x21f, 0x19e, 0x223, 0x225, 0x227, 0x229, 0x22b, + 0x22d, 0x22f, 0x231, 0x233, 0x2c65, 0x23c, 0x19a, 0x2c66, 0x242, 0x180, + 0x289, 0x28c, 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x371, 0x373, 0x377, + 0x3ac, 0x3ad, 0x3ae, 0x3af, 0x3cc, 0x3cd, 0x3ce, 0x3b1, 0x3b2, 0x3b3, + 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, + 0x3be, 0x3bf, 0x3c0, 0x3c1, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, + 0x3c9, 0x3ca, 0x3cb, 0x3d7, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, + 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3b8, 0x3f8, 0x3f2, 0x3fb, + 0x37b, 0x37c, 0x37d, 0x450, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, + 0x457, 0x458, 0x459, 0x45a, 0x45b, 0x45c, 0x45d, 0x45e, 0x45f, 0x430, + 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43a, + 0x43b, 0x43c, 0x43d, 0x43e, 0x43f, 0x440, 0x441, 0x442, 0x443, 0x444, + 0x445, 0x446, 0x447, 0x448, 0x449, 0x44a, 0x44b, 0x44c, 0x44d, 0x44e, + 0x44f, 0x461, 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, + 0x473, 0x475, 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, + 0x48f, 0x491, 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, + 0x4a3, 0x4a5, 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, + 0x4b7, 0x4b9, 0x4bb, 0x4bd, 0x4bf, 0x4cf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, + 0x4ca, 0x4cc, 0x4ce, 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, + 0x4df, 0x4e1, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, + 0x4f3, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, + 0x507, 0x509, 0x50b, 0x50d, 0x50f, 0x511, 0x513, 0x515, 0x517, 0x519, + 0x51b, 0x51d, 0x51f, 0x521, 0x523, 0x525, 0x527, 0x561, 0x562, 0x563, + 0x564, 0x565, 0x566, 0x567, 0x568, 0x569, 0x56a, 0x56b, 0x56c, 0x56d, + 0x56e, 0x56f, 0x570, 0x571, 0x572, 0x573, 0x574, 0x575, 0x576, 0x577, + 0x578, 0x579, 0x57a, 0x57b, 0x57c, 0x57d, 0x57e, 0x57f, 0x580, 0x581, + 0x582, 0x583, 0x584, 0x585, 0x586, 0x2d00, 0x2d01, 0x2d02, 0x2d03, + 0x2d04, 0x2d05, 0x2d06, 0x2d07, 0x2d08, 0x2d09, 0x2d0a, 0x2d0b, 0x2d0c, + 0x2d0d, 0x2d0e, 0x2d0f, 0x2d10, 0x2d11, 0x2d12, 0x2d13, 0x2d14, 0x2d15, + 0x2d16, 0x2d17, 0x2d18, 0x2d19, 0x2d1a, 0x2d1b, 0x2d1c, 0x2d1d, 0x2d1e, + 0x2d1f, 0x2d20, 0x2d21, 0x2d22, 0x2d23, 0x2d24, 0x2d25, 0x2d27, 0x2d2d, + 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, + 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, + 0x1e25, 0x1e27, 0x1e29, 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, + 0x1e37, 0x1e39, 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, + 0x1e49, 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, + 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, 0x1e6b, + 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e7b, 0x1e7d, + 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, 0x1e8b, 0x1e8d, 0x1e8f, + 0x1e91, 0x1e93, 0x1e95, 0xdf, 0x1ea1, 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, + 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, + 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, + 0x1ecf, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, + 0x1ee1, 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, + 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x1efb, 0x1efd, 0x1eff, 0x1f00, 0x1f01, + 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f10, 0x1f11, 0x1f12, + 0x1f13, 0x1f14, 0x1f15, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, + 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, + 0x1f37, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f51, 0x1f53, + 0x1f55, 0x1f57, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, + 0x1f67, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, + 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, + 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb0, 0x1fb1, + 0x1f70, 0x1f71, 0x1fb3, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fd0, + 0x1fd1, 0x1f76, 0x1f77, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1f78, + 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x3c9, 0x6b, 0xe5, 0x214e, 0x2170, + 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, + 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2184, 0x24d0, 0x24d1, + 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, + 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, + 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x2c30, 0x2c31, 0x2c32, + 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, 0x2c3b, + 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, + 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, + 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53, 0x2c54, 0x2c55, 0x2c56, + 0x2c57, 0x2c58, 0x2c59, 0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c61, + 0x26b, 0x1d7d, 0x27d, 0x2c68, 0x2c6a, 0x2c6c, 0x251, 0x271, 0x250, + 0x252, 0x2c73, 0x2c76, 0x23f, 0x240, 0x2c81, 0x2c83, 0x2c85, 0x2c87, + 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, + 0x2c9b, 0x2c9d, 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, + 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, + 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, 0x2ccf, + 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2cdf, 0x2ce1, + 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641, 0xa643, 0xa645, 0xa647, 0xa649, + 0xa64b, 0xa64d, 0xa64f, 0xa651, 0xa653, 0xa655, 0xa657, 0xa659, 0xa65b, + 0xa65d, 0xa65f, 0xa661, 0xa663, 0xa665, 0xa667, 0xa669, 0xa66b, 0xa66d, + 0xa681, 0xa683, 0xa685, 0xa687, 0xa689, 0xa68b, 0xa68d, 0xa68f, 0xa691, + 0xa693, 0xa695, 0xa697, 0xa723, 0xa725, 0xa727, 0xa729, 0xa72b, 0xa72d, + 0xa72f, 0xa733, 0xa735, 0xa737, 0xa739, 0xa73b, 0xa73d, 0xa73f, 0xa741, + 0xa743, 0xa745, 0xa747, 0xa749, 0xa74b, 0xa74d, 0xa74f, 0xa751, 0xa753, + 0xa755, 0xa757, 0xa759, 0xa75b, 0xa75d, 0xa75f, 0xa761, 0xa763, 0xa765, + 0xa767, 0xa769, 0xa76b, 0xa76d, 0xa76f, 0xa77a, 0xa77c, 0x1d79, 0xa77f, + 0xa781, 0xa783, 0xa785, 0xa787, 0xa78c, 0x265, 0xa791, 0xa793, 0xa7a1, + 0xa7a3, 0xa7a5, 0xa7a7, 0xa7a9, 0x266, 0xff41, 0xff42, 0xff43, 0xff44, + 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, + 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, + 0xff57, 0xff58, 0xff59, 0xff5a, 0x10428, 0x10429, 0x1042a, 0x1042b, + 0x1042c, 0x1042d, 0x1042e, 0x1042f, 0x10430, 0x10431, 0x10432, 0x10433, + 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, 0x1043a, 0x1043b, + 0x1043c, 0x1043d, 0x1043e, 0x1043f, 0x10440, 0x10441, 0x10442, 0x10443, + 0x10444, 0x10445, 0x10446, 0x10447, 0x10448, 0x10449, 0x1044a, 0x1044b, + 0x1044c, 0x1044d, 0x1044e, 0x1044f, 0xdf, 0x2000069, 0x307, 0xfb00, + 0xfb01, 0xfb02, 0xfb03, 0xfb04, 0xfb05, 0xfb06, 0x587, 0xfb13, 0xfb14, + 0xfb15, 0xfb16, 0xfb17, 0x149, 0x390, 0x3b0, 0x1f0, 0x1e96, 0x1e97, + 0x1e98, 0x1e99, 0x1e9a, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1fb6, 0x1fc6, + 0x1fd2, 0x1fd3, 0x1fd6, 0x1fd7, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe6, 0x1fe7, + 0x1ff6, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, + 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, + 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1f90, 0x1f91, + 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, + 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, + 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb3, 0x1fb3, 0x1fc3, 0x1fc3, 0x1ff3, + 0x1ff3, 0x1fb2, 0x1fb4, 0x1fc2, 0x1fc4, 0x1ff2, 0x1ff4, 0x1fb7, 0x1fc7, 0x1ff7 + ]; + return t; } +_IUA toTitleTable() +{ + static _IUA t = [ + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x39c, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178, + 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, + 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, + 0x128, 0x12a, 0x12c, 0x12e, 0x49, 0x132, 0x134, 0x136, 0x139, 0x13b, + 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, + 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, + 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, + 0x17b, 0x17d, 0x53, 0x243, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x1f6, + 0x198, 0x23d, 0x220, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, + 0x1b5, 0x1b8, 0x1bc, 0x1f7, 0x1c5, 0x1c5, 0x1c5, 0x1c8, 0x1c8, 0x1c8, + 0x1cb, 0x1cb, 0x1cb, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, + 0x1db, 0x18e, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, + 0x1ee, 0x1f2, 0x1f2, 0x1f2, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, + 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, + 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, + 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x2c7e, 0x2c7f, 0x241, 0x246, 0x248, + 0x24a, 0x24c, 0x24e, 0x2c6f, 0x2c6d, 0x2c70, 0x181, 0x186, 0x189, + 0x18a, 0x18f, 0x190, 0x193, 0x194, 0xa78d, 0xa7aa, 0x197, 0x196, + 0x2c62, 0x19c, 0x2c6e, 0x19d, 0x19f, 0x2c64, 0x1a6, 0x1a9, 0x1ae, + 0x244, 0x1b1, 0x1b2, 0x245, 0x1b7, 0x399, 0x370, 0x372, 0x376, 0x3fd, + 0x3fe, 0x3ff, 0x386, 0x388, 0x389, 0x38a, 0x391, 0x392, 0x393, 0x394, + 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, + 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, + 0x3a9, 0x3aa, 0x3ab, 0x38c, 0x38e, 0x38f, 0x392, 0x398, 0x3a6, 0x3a0, + 0x3cf, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, + 0x3ea, 0x3ec, 0x3ee, 0x39a, 0x3a1, 0x3f9, 0x395, 0x3f7, 0x3fa, 0x410, + 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, + 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, + 0x425, 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, + 0x42f, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, + 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x460, 0x462, 0x464, + 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, + 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, + 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, + 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, + 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4c0, 0x4d0, + 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, + 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, + 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, + 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, + 0x522, 0x524, 0x526, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, + 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, + 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, + 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, + 0x556, 0xa77d, 0x2c63, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, + 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, + 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, + 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, + 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, + 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, + 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, + 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, + 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1e60, 0x1ea0, 0x1ea2, + 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, + 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, + 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, + 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, + 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, + 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, + 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, + 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, + 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, + 0x1f4d, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, + 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, + 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, + 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, + 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, + 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fbc, + 0x399, 0x1fcc, 0x1fd8, 0x1fd9, 0x1fe8, 0x1fe9, 0x1fec, 0x1ffc, 0x2132, + 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, + 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, + 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, + 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, + 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, + 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, + 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, + 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, + 0x2c26, 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, + 0x2c60, 0x23a, 0x23e, 0x2c67, 0x2c69, 0x2c6b, 0x2c72, 0x2c75, 0x2c80, + 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, + 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, + 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, + 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, + 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, + 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0x10a0, 0x10a1, + 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, + 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, + 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, + 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, + 0x10c7, 0x10cd, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, + 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, + 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, + 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, + 0xa696, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, + 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, + 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, + 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, + 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77e, 0xa780, 0xa782, 0xa784, + 0xa786, 0xa78b, 0xa790, 0xa792, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, + 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, + 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, + 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, + 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, + 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, + 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, + 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, + 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, + 0x2000053, 0x73, 0x130, 0x2000046, 0x66, 0x2000046, 0x69, 0x2000046, + 0x6c, 0x3000046, 0x66, 0x69, 0x3000046, 0x66, 0x6c, 0x2000053, 0x74, + 0x2000053, 0x74, 0x2000535, 0x582, 0x2000544, 0x576, 0x2000544, 0x565, + 0x2000544, 0x56b, 0x200054e, 0x576, 0x2000544, 0x56d, 0x20002bc, 0x4e, + 0x3000399, 0x308, 0x301, 0x30003a5, 0x308, 0x301, 0x200004a, 0x30c, + 0x2000048, 0x331, 0x2000054, 0x308, 0x2000057, 0x30a, 0x2000059, 0x30a, + 0x2000041, 0x2be, 0x20003a5, 0x313, 0x30003a5, 0x313, 0x300, 0x30003a5, + 0x313, 0x301, 0x30003a5, 0x313, 0x342, 0x2000391, 0x342, 0x2000397, + 0x342, 0x3000399, 0x308, 0x300, 0x3000399, 0x308, 0x301, 0x2000399, + 0x342, 0x3000399, 0x308, 0x342, 0x30003a5, 0x308, 0x300, 0x30003a5, + 0x308, 0x301, 0x20003a1, 0x313, 0x20003a5, 0x342, 0x30003a5, 0x308, + 0x342, 0x20003a9, 0x342, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, + 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, + 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, + 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, + 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, + 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fbc, + 0x1fcc, 0x1fcc, 0x1ffc, 0x1ffc, 0x2001fba, 0x345, 0x2000386, 0x345, + 0x2001fca, 0x345, 0x2000389, 0x345, 0x2001ffa, 0x345, 0x200038f, 0x345, + 0x3000391, 0x342, 0x345, 0x3000397, 0x342, 0x345, 0x30003a9, 0x342, 0x345 + ]; + return t; +} +} +} \ No newline at end of file diff --git a/std/internal/windows/advapi32.d b/std/internal/windows/advapi32.d index 86147706e56..d5d04fd1b9b 100644 --- a/std/internal/windows/advapi32.d +++ b/std/internal/windows/advapi32.d @@ -4,7 +4,7 @@ * The only purpose of this module is to do the static construction for * std.windows.registry, to eliminate cyclic construction errors. * - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Kenji Hara * Source: $(PHOBOSSRC std/internal/windows/_advapi32.d) */ @@ -22,7 +22,7 @@ shared static this() { // WOW64 is the x86 emulator that allows 32-bit Windows-based applications to run seamlessly on 64-bit Windows // IsWow64Process Function - Minimum supported client - Windows Vista, Windows XP with SP2 - alias extern(Windows) BOOL function(HANDLE, PBOOL) fptr_t; + alias fptr_t = extern(Windows) BOOL function(HANDLE, PBOOL); auto hKernel = GetModuleHandleA("kernel32"); auto IsWow64Process = cast(fptr_t) GetProcAddress(hKernel, "IsWow64Process"); BOOL bIsWow64; diff --git a/std/json.d b/std/json.d index b81b126515d..daf5643b327 100644 --- a/std/json.d +++ b/std/json.d @@ -3,60 +3,80 @@ /** JavaScript Object Notation -Synopsis: ----- - //parse a file or string of json into a usable structure - string s = "{ \"language\": \"D\", \"rating\": 3.14, \"code\": \"42\" }"; - JSONValue j = parseJSON(s); - writeln("Language: ", j["language"].str(), - " Rating: ", j["rating"].floating() - ); +Copyright: Copyright Jeremie Pelletier 2008 - 2009. +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: Jeremie Pelletier, David Herberth +References: $(LINK http://json.org/) +Source: $(PHOBOSSRC std/_json.d) +*/ +/* + Copyright Jeremie Pelletier 2008 - 2009. +Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +module std.json; +import std.conv; +import std.range.primitives; +import std.array; +import std.traits; + +/// +@system unittest +{ + import std.conv : to; + + // parse a file or string of json into a usable structure + string s = `{ "language": "D", "rating": 3.5, "code": "42" }`; + JSONValue j = parseJSON(s); // j and j["language"] return JSONValue, // j["language"].str returns a string + assert(j["language"].str == "D"); + assert(j["rating"].floating == 3.5); - //check a type + // check a type long x; - if (j["code"].type() == JSON_TYPE.INTEGER) - { - x = j["code"].integer; - } - else + if (const(JSONValue)* code = "code" in j) { - x = to!int(j["code"].str); + if (code.type() == JSON_TYPE.INTEGER) + x = code.integer; + else + x = to!int(code.str); } // create a json struct JSONValue jj = [ "language": "D" ]; // rating doesnt exist yet, so use .object to assign - jj.object["rating"] = JSONValue(3.14); + jj.object["rating"] = JSONValue(3.5); // create an array to assign to list jj.object["list"] = JSONValue( ["a", "b", "c"] ); // list already exists, so .object optional jj["list"].array ~= JSONValue("D"); - s = j.toString(); - writeln(s); ----- + string jjStr = `{"language":"D","list":["a","b","c","D"],"rating":3.5}`; + assert(jj.toString == jjStr); +} -Copyright: Copyright Jeremie Pelletier 2008 - 2009. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: Jeremie Pelletier, David Herberth -References: $(LINK http://json.org/) -Source: $(PHOBOSSRC std/_json.d) -*/ -/* - Copyright Jeremie Pelletier 2008 - 2009. -Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt) +/** +String literals used to represent special float values within JSON strings. */ -module std.json; +enum JSONFloatLiteral : string +{ + nan = "NaN", /// string representation of floating-point NaN + inf = "Infinite", /// string representation of floating-point Infinity + negativeInf = "-Infinite", /// string representation of floating-point negative Infinity +} -import std.conv; -import std.range.primitives; -import std.array; -import std.traits; +/** +Flags that control how json is encoded and parsed. +*/ +enum JSONOptions +{ + none, /// standard parsing + specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings + escapeNonAsciiChars = 0x2 /// encode non ascii characters with an unicode escape sequence +} /** JSON type enumeration @@ -97,12 +117,12 @@ struct JSONValue /** Returns the JSON_TYPE of the value stored in this structure. */ - @property JSON_TYPE type() const + @property JSON_TYPE type() const pure nothrow @safe @nogc { return type_tag; } /// - unittest + @safe unittest { string s = "{ \"language\": \"D\" }"; JSONValue j = parseJSON(s); @@ -110,65 +130,25 @@ struct JSONValue assert(j["language"].type == JSON_TYPE.STRING); } - /** - $(RED Deprecated. Instead, please assign the value with the adequate - type to $(D JSONValue) directly. This will be removed in - June 2015.) - - Sets the _type of this $(D JSONValue). Previous content is cleared. - */ - deprecated("Please assign the value with the adequate type to JSONValue directly.") - @property JSON_TYPE type(JSON_TYPE newType) - { - if (type_tag != newType - && ((type_tag != JSON_TYPE.INTEGER && type_tag != JSON_TYPE.UINTEGER) - || (newType != JSON_TYPE.INTEGER && newType != JSON_TYPE.UINTEGER))) - { - final switch (newType) - { - case JSON_TYPE.STRING: - store.str = store.str.init; - break; - case JSON_TYPE.INTEGER: - store.integer = store.integer.init; - break; - case JSON_TYPE.UINTEGER: - store.uinteger = store.uinteger.init; - break; - case JSON_TYPE.FLOAT: - store.floating = store.floating.init; - break; - case JSON_TYPE.OBJECT: - store.object = store.object.init; - break; - case JSON_TYPE.ARRAY: - store.array = store.array.init; - break; - case JSON_TYPE.TRUE: - case JSON_TYPE.FALSE: - case JSON_TYPE.NULL: - break; - } - } - return type_tag = newType; - } - - /// Value getter/setter for $(D JSON_TYPE.STRING). - /// Throws $(D JSONException) for read access if $(D type) is not $(D JSON_TYPE.STRING). - @property inout(string) str() inout + /*** + * Value getter/setter for $(D JSON_TYPE.STRING). + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.STRING). + */ + @property string str() const pure @trusted { enforce!JSONException(type == JSON_TYPE.STRING, "JSONValue is not a string"); return store.str; } /// ditto - @property string str(string v) + @property string str(string v) pure nothrow @nogc @safe { assign(v); - return store.str; + return v; } /// - unittest + @safe unittest { JSONValue j = [ "language": "D" ]; @@ -180,148 +160,234 @@ struct JSONValue assert(j["language"].str == "Perl"); } - /// Value getter/setter for $(D JSON_TYPE.INTEGER). - /// Throws $(D JSONException) for read access if $(D type) is not $(D JSON_TYPE.INTEGER). - @property inout(long) integer() inout + /*** + * Value getter/setter for $(D JSON_TYPE.INTEGER). + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.INTEGER). + */ + @property inout(long) integer() inout pure @safe { enforce!JSONException(type == JSON_TYPE.INTEGER, "JSONValue is not an integer"); return store.integer; } /// ditto - @property long integer(long v) + @property long integer(long v) pure nothrow @safe @nogc { assign(v); return store.integer; } - /// Value getter/setter for $(D JSON_TYPE.UINTEGER). - /// Throws $(D JSONException) for read access if $(D type) is not $(D JSON_TYPE.UINTEGER). - @property inout(ulong) uinteger() inout + /*** + * Value getter/setter for $(D JSON_TYPE.UINTEGER). + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.UINTEGER). + */ + @property inout(ulong) uinteger() inout pure @safe { enforce!JSONException(type == JSON_TYPE.UINTEGER, "JSONValue is not an unsigned integer"); return store.uinteger; } /// ditto - @property ulong uinteger(ulong v) + @property ulong uinteger(ulong v) pure nothrow @safe @nogc { assign(v); return store.uinteger; } - /// Value getter/setter for $(D JSON_TYPE.FLOAT). - /// Throws $(D JSONException) for read access if $(D type) is not $(D JSON_TYPE.FLOAT). - @property inout(double) floating() inout + /*** + * Value getter/setter for $(D JSON_TYPE.FLOAT). Note that despite + * the name, this is a $(B 64)-bit `double`, not a 32-bit `float`. + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.FLOAT). + */ + @property inout(double) floating() inout pure @safe { enforce!JSONException(type == JSON_TYPE.FLOAT, "JSONValue is not a floating type"); return store.floating; } /// ditto - @property double floating(double v) + @property double floating(double v) pure nothrow @safe @nogc { assign(v); return store.floating; } - /// Value getter/setter for $(D JSON_TYPE.OBJECT). - /// Throws $(D JSONException) for read access if $(D type) is not $(D JSON_TYPE.OBJECT). - @property ref inout(JSONValue[string]) object() inout + /*** + * Value getter/setter for $(D JSON_TYPE.OBJECT). + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.OBJECT). + * Note: this is @system because of the following pattern: + --- + auto a = &(json.object()); + json.uinteger = 0; // overwrite AA pointer + (*a)["hello"] = "world"; // segmentation fault + --- + */ + @property ref inout(JSONValue[string]) object() inout pure @system { enforce!JSONException(type == JSON_TYPE.OBJECT, "JSONValue is not an object"); return store.object; } /// ditto - @property JSONValue[string] object(JSONValue[string] v) + @property JSONValue[string] object(JSONValue[string] v) pure nothrow @nogc @safe { assign(v); + return v; + } + + /*** + * Value getter for $(D JSON_TYPE.OBJECT). + * Unlike $(D object), this retrieves the object by value and can be used in @safe code. + * + * A caveat is that, if the returned value is null, modifications will not be visible: + * --- + * JSONValue json; + * json.object = null; + * json.objectNoRef["hello"] = JSONValue("world"); + * assert("hello" !in json.object); + * --- + * + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.OBJECT). + */ + @property inout(JSONValue[string]) objectNoRef() inout pure @trusted + { + enforce!JSONException(type == JSON_TYPE.OBJECT, + "JSONValue is not an object"); return store.object; } - /// Value getter/setter for $(D JSON_TYPE.ARRAY). - /// Throws $(D JSONException) for read access if $(D type) is not $(D JSON_TYPE.ARRAY). - @property ref inout(JSONValue[]) array() inout + /*** + * Value getter/setter for $(D JSON_TYPE.ARRAY). + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.ARRAY). + * Note: this is @system because of the following pattern: + --- + auto a = &(json.array()); + json.uinteger = 0; // overwrite array pointer + (*a)[0] = "world"; // segmentation fault + --- + */ + @property ref inout(JSONValue[]) array() inout pure @system { enforce!JSONException(type == JSON_TYPE.ARRAY, "JSONValue is not an array"); return store.array; } /// ditto - @property JSONValue[] array(JSONValue[] v) + @property JSONValue[] array(JSONValue[] v) pure nothrow @nogc @safe { assign(v); + return v; + } + + /*** + * Value getter for $(D JSON_TYPE.ARRAY). + * Unlike $(D array), this retrieves the array by value and can be used in @safe code. + * + * A caveat is that, if you append to the returned array, the new values aren't visible in the + * JSONValue: + * --- + * JSONValue json; + * json.array = [JSONValue("hello")]; + * json.arrayNoRef ~= JSONValue("world"); + * assert(json.array.length == 1); + * --- + * + * Throws: $(D JSONException) for read access if $(D type) is not + * $(D JSON_TYPE.ARRAY). + */ + @property inout(JSONValue[]) arrayNoRef() inout pure @trusted + { + enforce!JSONException(type == JSON_TYPE.ARRAY, + "JSONValue is not an array"); return store.array; } /// Test whether the type is $(D JSON_TYPE.NULL) - @property bool isNull() const + @property bool isNull() const pure nothrow @safe @nogc { return type == JSON_TYPE.NULL; } - private void assign(T)(T arg) + private void assign(T)(T arg) @safe { - static if(is(T : typeof(null))) + static if (is(T : typeof(null))) { type_tag = JSON_TYPE.NULL; } - else static if(is(T : string)) + else static if (is(T : string)) + { + type_tag = JSON_TYPE.STRING; + string t = arg; + () @trusted { store.str = t; }(); + } + else static if (isSomeString!T) // issue 15884 { type_tag = JSON_TYPE.STRING; - store.str = arg; + // FIXME: std.array.array(Range) is not deduced as 'pure' + () @trusted { + import std.utf : byUTF; + store.str = cast(immutable)(arg.byUTF!char.array); + }(); } - else static if(is(T : bool)) + else static if (is(T : bool)) { type_tag = arg ? JSON_TYPE.TRUE : JSON_TYPE.FALSE; } - else static if(is(T : ulong) && isUnsigned!T) + else static if (is(T : ulong) && isUnsigned!T) { type_tag = JSON_TYPE.UINTEGER; store.uinteger = arg; } - else static if(is(T : long)) + else static if (is(T : long)) { type_tag = JSON_TYPE.INTEGER; store.integer = arg; } - else static if(isFloatingPoint!T) + else static if (isFloatingPoint!T) { type_tag = JSON_TYPE.FLOAT; store.floating = arg; } - else static if(is(T : Value[Key], Key, Value)) + else static if (is(T : Value[Key], Key, Value)) { static assert(is(Key : string), "AA key must be string"); type_tag = JSON_TYPE.OBJECT; - static if(is(Value : JSONValue)) { - store.object = arg; + static if (is(Value : JSONValue)) + { + JSONValue[string] t = arg; + () @trusted { store.object = t; }(); } else { JSONValue[string] aa; - foreach(key, value; arg) + foreach (key, value; arg) aa[key] = JSONValue(value); - store.object = aa; + () @trusted { store.object = aa; }(); } } - else static if(isArray!T) + else static if (isArray!T) { type_tag = JSON_TYPE.ARRAY; - static if(is(ElementEncodingType!T : JSONValue)) + static if (is(ElementEncodingType!T : JSONValue)) { - store.array = arg; + JSONValue[] t = arg; + () @trusted { store.array = t; }(); } else { JSONValue[] new_arg = new JSONValue[arg.length]; - foreach(i, e; arg) + foreach (i, e; arg) new_arg[i] = JSONValue(e); - store.array = new_arg; + () @trusted { store.array = new_arg; }(); } } - else static if(is(T : JSONValue)) + else static if (is(T : JSONValue)) { type_tag = arg.type; store = arg.store; @@ -332,17 +398,17 @@ struct JSONValue } } - private void assignRef(T)(ref T arg) if(isStaticArray!T) + private void assignRef(T)(ref T arg) if (isStaticArray!T) { type_tag = JSON_TYPE.ARRAY; - static if(is(ElementEncodingType!T : JSONValue)) + static if (is(ElementEncodingType!T : JSONValue)) { store.array = arg; } else { JSONValue[] new_arg = new JSONValue[arg.length]; - foreach(i, e; arg) + foreach (i, e; arg) new_arg[i] = JSONValue(e); store.array = new_arg; } @@ -359,13 +425,13 @@ struct JSONValue * $(D long), $(D double), an associative array $(D V[K]) for any $(D V) * and $(D K) i.e. a JSON object, any array or $(D bool). The type will * be set accordingly. - */ - this(T)(T arg) if(!isStaticArray!T) + */ + this(T)(T arg) if (!isStaticArray!T) { assign(arg); } /// Ditto - this(T)(ref T arg) if(isStaticArray!T) + this(T)(ref T arg) if (isStaticArray!T) { assignRef(arg); } @@ -376,7 +442,7 @@ struct JSONValue type_tag = arg.type; } /// - unittest + @safe unittest { JSONValue j = JSONValue( "a string" ); j = JSONValue(42); @@ -388,106 +454,108 @@ struct JSONValue assert(j.type == JSON_TYPE.OBJECT); } - void opAssign(T)(T arg) if(!isStaticArray!T && !is(T : JSONValue)) + void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue)) { assign(arg); } - void opAssign(T)(ref T arg) if(isStaticArray!T) + void opAssign(T)(ref T arg) if (isStaticArray!T) { assignRef(arg); } - /// Array syntax for json arrays. - /// Throws $(D JSONException) if $(D type) is not $(D JSON_TYPE.ARRAY). - ref inout(JSONValue) opIndex(size_t i) inout + /*** + * Array syntax for json arrays. + * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.ARRAY). + */ + ref inout(JSONValue) opIndex(size_t i) inout pure @safe { - enforce!JSONException(type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); - enforceEx!JSONException(i < store.array.length, + auto a = this.arrayNoRef; + enforceEx!JSONException(i < a.length, "JSONValue array index is out of range"); - return store.array[i]; + return a[i]; } /// - unittest + @safe unittest { JSONValue j = JSONValue( [42, 43, 44] ); assert( j[0].integer == 42 ); assert( j[1].integer == 43 ); } - /// Hash syntax for json objects. - /// Throws $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT). - ref inout(JSONValue) opIndex(string k) inout + /*** + * Hash syntax for json objects. + * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT). + */ + ref inout(JSONValue) opIndex(string k) inout pure @safe { - enforce!JSONException(type == JSON_TYPE.OBJECT, - "JSONValue is not an object"); - return *enforce!JSONException(k in store.object, + auto o = this.objectNoRef; + return *enforce!JSONException(k in o, "Key not found: " ~ k); } /// - unittest + @safe unittest { JSONValue j = JSONValue( ["language": "D"] ); assert( j["language"].str == "D" ); } - /// Operator sets $(D value) for element of JSON object by $(D key) - /// If JSON value is null, then operator initializes it with object and then - /// sets $(D value) for it. - /// Throws $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT) - /// or $(D JSON_TYPE.NULL). - void opIndexAssign(T)(auto ref T value, string key) + /*** + * Operator sets $(D value) for element of JSON object by $(D key). + * + * If JSON value is null, then operator initializes it with object and then + * sets $(D value) for it. + * + * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT) + * or $(D JSON_TYPE.NULL). + */ + void opIndexAssign(T)(auto ref T value, string key) pure { enforceEx!JSONException(type == JSON_TYPE.OBJECT || type == JSON_TYPE.NULL, "JSONValue must be object or null"); + JSONValue[string] aa = null; + if (type == JSON_TYPE.OBJECT) + { + aa = this.objectNoRef; + } - if(type == JSON_TYPE.NULL) - this = (JSONValue[string]).init; - - store.object[key] = value; + aa[key] = value; + this.object = aa; } /// - unittest + @safe unittest { JSONValue j = JSONValue( ["language": "D"] ); j["language"].str = "Perl"; assert( j["language"].str == "Perl" ); } - void opIndexAssign(T)(T arg, size_t i) + void opIndexAssign(T)(T arg, size_t i) pure { - enforceEx!JSONException(type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); - enforceEx!JSONException(i < store.array.length, + auto a = this.arrayNoRef; + enforceEx!JSONException(i < a.length, "JSONValue array index is out of range"); - store.array[i] = arg; + a[i] = arg; + this.array = a; } /// - unittest + @safe unittest { JSONValue j = JSONValue( ["Perl", "C"] ); j[1].str = "D"; assert( j[1].str == "D" ); } - JSONValue opBinary(string op : "~", T)(T arg) + JSONValue opBinary(string op : "~", T)(T arg) @safe { - enforceEx!JSONException(type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); - static if(isArray!T) + auto a = this.arrayNoRef; + static if (isArray!T) { - JSONValue newArray = JSONValue(this.store.array.dup); - newArray.store.array ~= JSONValue(arg).store.array; - return newArray; + return JSONValue(a ~ JSONValue(arg).arrayNoRef); } - else static if(is(T : JSONValue)) + else static if (is(T : JSONValue)) { - enforceEx!JSONException(arg.type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); - JSONValue newArray = JSONValue(this.store.array.dup); - newArray.store.array ~= arg.store.array; - return newArray; + return JSONValue(a ~ arg.arrayNoRef); } else { @@ -495,48 +563,57 @@ struct JSONValue } } - void opOpAssign(string op : "~", T)(T arg) + void opOpAssign(string op : "~", T)(T arg) @safe { - enforceEx!JSONException(type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); - static if(isArray!T) + auto a = this.arrayNoRef; + static if (isArray!T) { - store.array ~= JSONValue(arg).store.array; + a ~= JSONValue(arg).arrayNoRef; } - else static if(is(T : JSONValue)) + else static if (is(T : JSONValue)) { - enforceEx!JSONException(arg.type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); - store.array ~= arg.store.array; + a ~= arg.arrayNoRef; } else { static assert(false, "argument is not an array or a JSONValue array"); } + this.array = a; } - auto opBinaryRight(string op : "in")(string k) const + /** + * Support for the $(D in) operator. + * + * Tests wether a key can be found in an object. + * + * Returns: + * when found, the $(D const(JSONValue)*) that matches to the key, + * otherwise $(D null). + * + * Throws: $(D JSONException) if the right hand side argument $(D JSON_TYPE) + * is not $(D OBJECT). + */ + auto opBinaryRight(string op : "in")(string k) const @safe { - enforce!JSONException(type == JSON_TYPE.OBJECT, - "JSONValue is not an object"); - return k in store.object; + return k in this.objectNoRef; } /// - unittest + @safe unittest { JSONValue j = [ "language": "D", "author": "walter" ]; string a = ("author" in j).str; } - bool opEquals(const JSONValue rhs) const + bool opEquals(const JSONValue rhs) const @nogc nothrow pure @safe { return opEquals(rhs); } - bool opEquals(ref const JSONValue rhs) const + bool opEquals(ref const JSONValue rhs) const @nogc nothrow pure @trusted { // Default doesn't work well since store is a union. Compare only // what should be in store. + // This is @trusted to remain nogc, nothrow, fast, and usable from @safe code. if (type_tag != rhs.type_tag) return false; final switch (type_tag) @@ -561,16 +638,14 @@ struct JSONValue } /// Implements the foreach $(D opApply) interface for json arrays. - int opApply(int delegate(size_t index, ref JSONValue) dg) + int opApply(scope int delegate(size_t index, ref JSONValue) dg) @system { - enforce!JSONException(type == JSON_TYPE.ARRAY, - "JSONValue is not an array"); int result; - foreach(size_t index, ref value; store.array) + foreach (size_t index, ref value; array) { result = dg(index, value); - if(result) + if (result) break; } @@ -578,48 +653,63 @@ struct JSONValue } /// Implements the foreach $(D opApply) interface for json objects. - int opApply(int delegate(string key, ref JSONValue) dg) + int opApply(scope int delegate(string key, ref JSONValue) dg) @system { enforce!JSONException(type == JSON_TYPE.OBJECT, "JSONValue is not an object"); int result; - foreach(string key, ref value; store.object) + foreach (string key, ref value; object) { result = dg(key, value); - if(result) + if (result) break; } return result; } - /// Implicitly calls $(D toJSON) on this JSONValue. - string toString() const + /*** + * Implicitly calls $(D toJSON) on this JSONValue. + * + * $(I options) can be used to tweak the conversion behavior. + */ + string toString(in JSONOptions options = JSONOptions.none) const @safe { - return toJSON(&this); + return toJSON(this, false, options); } - /// Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but - /// also passes $(I true) as $(I pretty) argument. - string toPrettyString() const + /*** + * Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but + * also passes $(I true) as $(I pretty) argument. + * + * $(I options) can be used to tweak the conversion behavior + */ + string toPrettyString(in JSONOptions options = JSONOptions.none) const @safe { - return toJSON(&this, true); + return toJSON(this, true, options); } } /** Parses a serialized string and returns a tree of JSON values. +Throws: $(LREF JSONException) if the depth exceeds the max depth. +Params: + json = json-formatted string to parse + maxDepth = maximum depth of nesting allowed, -1 disables depth checking + options = enable decoding string representations of NaN/Inf as float values */ -JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) +JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none) +if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) { import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower; - import std.utf : toUTF8; + import std.typecons : Yes; + import std.utf : encode; - JSONValue root = void; + JSONValue root; root.type_tag = JSON_TYPE.NULL; - if(json.empty) return root; + if (json.empty) return root; int depth = -1; dchar next = 0; @@ -636,7 +726,7 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) dchar c = json.front; json.popFront(); - if(c == '\n') + if (c == '\n') { line++; pos = 0; @@ -651,9 +741,9 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) dchar peekChar() { - if(!next) + if (!next) { - if(json.empty) return '\0'; + if (json.empty) return '\0'; next = popChar(); } return next; @@ -661,15 +751,15 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) void skipWhitespace() { - while(isWhite(peekChar())) next = 0; + while (isWhite(peekChar())) next = 0; } dchar getChar(bool SkipWhitespace = false)() { - static if(SkipWhitespace) skipWhitespace(); + static if (SkipWhitespace) skipWhitespace(); - dchar c = void; - if(next) + dchar c; + if (next) { c = next; next = 0; @@ -682,20 +772,20 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) { - static if(SkipWhitespace) skipWhitespace(); + static if (SkipWhitespace) skipWhitespace(); auto c2 = getChar(); - static if(!CaseSensitive) c2 = toLower(c2); + static if (!CaseSensitive) c2 = toLower(c2); - if(c2 != c) error(text("Found '", c2, "' when expecting '", c, "'.")); + if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'.")); } bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) { - static if(SkipWhitespace) skipWhitespace(); + static if (SkipWhitespace) skipWhitespace(); auto c2 = peekChar(); static if (!CaseSensitive) c2 = toLower(c2); - if(c2 != c) return false; + if (c2 != c) return false; getChar(); return true; @@ -706,7 +796,7 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) auto str = appender!string(); Next: - switch(peekChar()) + switch (peekChar()) { case '"': getChar(); @@ -715,7 +805,7 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) case '\\': getChar(); auto c = getChar(); - switch(c) + switch (c) { case '"': str.put('"'); break; case '\\': str.put('\\'); break; @@ -727,14 +817,15 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) case 't': str.put('\t'); break; case 'u': dchar val = 0; - foreach_reverse(i; 0 .. 4) + foreach_reverse (i; 0 .. 4) { auto hex = toUpper(getChar()); - if(!isHexDigit(hex)) error("Expecting hex character"); + if (!isHexDigit(hex)) error("Expecting hex character"); val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i); } - char[4] buf = void; - str.put(toUTF8(buf, val)); + char[4] buf; + immutable len = encode!(Yes.useReplacementDchar)(buf, val); + str.put(buf[0 .. len]); break; default: @@ -744,68 +835,97 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) default: auto c = getChar(); - appendJSONChar(&str, c, &error); + appendJSONChar(str, c, options, &error); goto Next; } return str.data.length ? str.data : ""; } - void parseValue(JSONValue* value) + bool tryGetSpecialFloat(string str, out double val) { + switch (str) + { + case JSONFloatLiteral.nan: + val = double.nan; + return true; + case JSONFloatLiteral.inf: + val = double.infinity; + return true; + case JSONFloatLiteral.negativeInf: + val = -double.infinity; + return true; + default: + return false; + } + } + + void parseValue(ref JSONValue value) { depth++; - if(maxDepth != -1 && depth > maxDepth) error("Nesting too deep."); + if (maxDepth != -1 && depth > maxDepth) error("Nesting too deep."); auto c = getChar!true(); - switch(c) + switch (c) { case '{': - value.type_tag = JSON_TYPE.OBJECT; - value.store.object = null; - - if(testChar('}')) break; + if (testChar('}')) + { + value.object = null; + break; + } + JSONValue[string] obj; do { checkChar('"'); string name = parseString(); checkChar(':'); - JSONValue member = void; - parseValue(&member); - value.store.object[name] = member; + JSONValue member; + parseValue(member); + obj[name] = member; } - while(testChar(',')); + while (testChar(',')); + value.object = obj; checkChar('}'); break; case '[': - value.type_tag = JSON_TYPE.ARRAY; - - if(testChar(']')) + if (testChar(']')) { - value.store.array = cast(JSONValue[]) ""; + value.type_tag = JSON_TYPE.ARRAY; break; } - value.store.array = null; - + JSONValue[] arr; do { - JSONValue element = void; - parseValue(&element); - value.store.array ~= element; + JSONValue element; + parseValue(element); + arr ~= element; } - while(testChar(',')); + while (testChar(',')); checkChar(']'); + value.array = arr; break; case '"': + auto str = parseString(); + + // if special float parsing is enabled, check if string represents NaN/Inf + if ((options & JSONOptions.specialFloatLiterals) && + tryGetSpecialFloat(str, value.store.floating)) + { + // found a special float, its value was placed in value.store.floating + value.type_tag = JSON_TYPE.FLOAT; + break; + } + value.type_tag = JSON_TYPE.STRING; - value.store.str = parseString(); + value.store.str = str; break; case '0': .. case '9': @@ -815,18 +935,18 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) void readInteger() { - if(!isDigit(c)) error("Digit expected"); + if (!isDigit(c)) error("Digit expected"); Next: number.put(c); - if(isDigit(peekChar())) + if (isDigit(peekChar())) { c = getChar(); goto Next; } } - if(c == '-') + if (c == '-') { number.put('-'); c = getChar(); @@ -835,25 +955,25 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) readInteger(); - if(testChar('.')) + if (testChar('.')) { isFloat = true; number.put('.'); c = getChar(); readInteger(); } - if(testChar!(false, false)('e')) + if (testChar!(false, false)('e')) { isFloat = true; number.put('e'); - if(testChar('+')) number.put('+'); - else if(testChar('-')) number.put('-'); + if (testChar('+')) number.put('+'); + else if (testChar('-')) number.put('-'); c = getChar(); readInteger(); } string data = number.data; - if(isFloat) + if (isFloat) { value.type_tag = JSON_TYPE.FLOAT; value.store.floating = parse!double(data); @@ -865,7 +985,8 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) else value.store.uinteger = parse!ulong(data); - value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ? JSON_TYPE.UINTEGER : JSON_TYPE.INTEGER; + value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ? + JSON_TYPE.UINTEGER : JSON_TYPE.INTEGER; } break; @@ -901,10 +1022,67 @@ JSONValue parseJSON(T)(T json, int maxDepth = -1) if(isInputRange!T) depth--; } - parseValue(&root); + parseValue(root); return root; } +@safe unittest +{ + enum issue15742objectOfObject = `{ "key1": { "key2": 1 }}`; + static assert(parseJSON(issue15742objectOfObject).type == JSON_TYPE.OBJECT); + + enum issue15742arrayOfArray = `[[1]]`; + static assert(parseJSON(issue15742arrayOfArray).type == JSON_TYPE.ARRAY); +} + +@safe unittest +{ + // Ensure we can parse and use JSON from @safe code + auto a = `{ "key1": { "key2": 1 }}`.parseJSON; + assert(a["key1"]["key2"].integer == 1); + assert(a.toString == `{"key1":{"key2":1}}`); +} + +@system unittest +{ + // Ensure we can parse JSON from a @system range. + struct Range + { + string s; + size_t index; + @system + { + bool empty() { return index >= s.length; } + void popFront() { index++; } + char front() { return s[index]; } + } + } + auto s = Range(`{ "key1": { "key2": 1 }}`); + auto json = parseJSON(s); + assert(json["key1"]["key2"].integer == 1); +} + +/** +Parses a serialized string and returns a tree of JSON values. +Throws: $(REF JSONException, std,json) if the depth exceeds the max depth. +Params: + json = json-formatted string to parse + options = enable decoding string representations of NaN/Inf as float values +*/ +JSONValue parseJSON(T)(T json, JSONOptions options) +if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) +{ + return parseJSON!T(json, -1, options); +} + +deprecated( + "Please use the overload that takes a ref JSONValue rather than a pointer. This overload will " + ~ "be removed in November 2017.") +string toJSON(in JSONValue* root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe +{ + return toJSON(*root, pretty, options); +} + /** Takes a tree of JSON values and returns the serialized string. @@ -912,18 +1090,19 @@ Any Object types will be serialized in a key-sorted order. If $(D pretty) is false no whitespaces are generated. If $(D pretty) is true serialized string is formatted to be human-readable. +Set the $(specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings. */ -string toJSON(in JSONValue* root, in bool pretty = false) +string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe { auto json = appender!string(); - void toString(string str) + void toString(string str) @safe { json.put('"'); foreach (dchar c; str) { - switch(c) + switch (c) { case '"': json.put("\\\""); break; case '\\': json.put("\\\\"); break; @@ -934,7 +1113,7 @@ string toJSON(in JSONValue* root, in bool pretty = false) case '\r': json.put("\\r"); break; case '\t': json.put("\\t"); break; default: - appendJSONChar(&json, c, + appendJSONChar(json, c, options, (msg) { throw new JSONException(msg); }); } } @@ -942,17 +1121,17 @@ string toJSON(in JSONValue* root, in bool pretty = false) json.put('"'); } - void toValue(in JSONValue* value, ulong indentLevel) + void toValue(ref in JSONValue value, ulong indentLevel) @safe { void putTabs(ulong additionalIndent = 0) { - if(pretty) - foreach(i; 0 .. indentLevel + additionalIndent) + if (pretty) + foreach (i; 0 .. indentLevel + additionalIndent) json.put(" "); } void putEOL() { - if(pretty) + if (pretty) json.put('\n'); } void putCharAndEOL(char ch) @@ -961,10 +1140,11 @@ string toJSON(in JSONValue* root, in bool pretty = false) putEOL(); } - final switch(value.type) + final switch (value.type) { case JSON_TYPE.OBJECT: - if(!value.store.object.length) + auto obj = value.objectNoRef; + if (!obj.length) { json.put("{}"); } @@ -977,21 +1157,29 @@ string toJSON(in JSONValue* root, in bool pretty = false) { foreach (name; names) { - auto member = value.store.object[name]; - if(!first) + auto member = obj[name]; + if (!first) putCharAndEOL(','); first = false; putTabs(1); toString(name); json.put(':'); - if(pretty) + if (pretty) json.put(' '); - toValue(&member, indentLevel + 1); + toValue(member, indentLevel + 1); } } - import std.algorithm : sort; - auto names = value.store.object.keys; + import std.algorithm.sorting : sort; + // @@@BUG@@@ 14439 + // auto names = obj.keys; // aa.keys can't be called in @safe code + auto names = new string[obj.length]; + size_t i = 0; + foreach (k, v; obj) + { + names[i] = k; + i++; + } sort(names); emit(names); @@ -1002,19 +1190,20 @@ string toJSON(in JSONValue* root, in bool pretty = false) break; case JSON_TYPE.ARRAY: - if(value.store.array.empty) + auto arr = value.arrayNoRef; + if (arr.empty) { json.put("[]"); } else { putCharAndEOL('['); - foreach (i, ref el; value.store.array) + foreach (i, el; arr) { - if(i) + if (i) putCharAndEOL(','); putTabs(1); - toValue(&el, indentLevel + 1); + toValue(el, indentLevel + 1); } putEOL(); putTabs(); @@ -1023,7 +1212,7 @@ string toJSON(in JSONValue* root, in bool pretty = false) break; case JSON_TYPE.STRING: - toString(value.store.str); + toString(value.str); break; case JSON_TYPE.INTEGER: @@ -1035,7 +1224,43 @@ string toJSON(in JSONValue* root, in bool pretty = false) break; case JSON_TYPE.FLOAT: - json.put(to!string(value.store.floating)); + import std.math : isNaN, isInfinity; + + auto val = value.store.floating; + + if (val.isNaN) + { + if (options & JSONOptions.specialFloatLiterals) + { + toString(JSONFloatLiteral.nan); + } + else + { + throw new JSONException( + "Cannot encode NaN. Consider passing the specialFloatLiterals flag."); + } + } + else if (val.isInfinity) + { + if (options & JSONOptions.specialFloatLiterals) + { + toString((val > 0) ? JSONFloatLiteral.inf : JSONFloatLiteral.negativeInf); + } + else + { + throw new JSONException( + "Cannot encode Infinity. Consider passing the specialFloatLiterals flag."); + } + } + else + { + import std.format : format; + // The correct formula for the number of decimal digits needed for lossless round + // trips is actually: + // ceil(log(pow(2.0, double.mant_dig - 1)) / log(10.0) + 1) == (double.dig + 2) + // Anything less will round off (1 + double.epsilon) + json.put("%.18g".format(val)); + } break; case JSON_TYPE.TRUE: @@ -1056,14 +1281,40 @@ string toJSON(in JSONValue* root, in bool pretty = false) return json.data; } -private void appendJSONChar(Appender!string* dst, dchar c, - scope void delegate(string) error) +private void appendJSONChar(ref Appender!string dst, dchar c, JSONOptions opts, + scope void delegate(string) error) @safe { import std.uni : isControl; - if(isControl(c)) - error("Illegal control character."); - dst.put(c); + with (JSONOptions) if (isControl(c) || + ((opts & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80)) + { + dst.put("\\u"); + foreach_reverse (i; 0 .. 4) + { + char ch = (c >>> (4 * i)) & 0x0f; + ch += ch < 10 ? '0' : 'A' - 10; + dst.put(ch); + } + } + else + { + dst.put(c); + } +} + +@safe unittest // bugzilla 12897 +{ + JSONValue jv0 = JSONValue("test测试"); + assert(toJSON(jv0, false, JSONOptions.escapeNonAsciiChars) == `"test\u6D4B\u8BD5"`); + JSONValue jv00 = JSONValue("test\u6D4B\u8BD5"); + assert(toJSON(jv00, false, JSONOptions.none) == `"test测试"`); + assert(toJSON(jv0, false, JSONOptions.none) == `"test测试"`); + JSONValue jv1 = JSONValue("été"); + assert(toJSON(jv1, false, JSONOptions.escapeNonAsciiChars) == `"\u00E9t\u00E9"`); + JSONValue jv11 = JSONValue("\u00E9t\u00E9"); + assert(toJSON(jv11, false, JSONOptions.none) == `"été"`); + assert(toJSON(jv1, false, JSONOptions.none) == `"été"`); } /** @@ -1071,22 +1322,22 @@ Exception thrown on JSON errors */ class JSONException : Exception { - this(string msg, int line = 0, int pos = 0) + this(string msg, int line = 0, int pos = 0) pure nothrow @safe { - if(line) + if (line) super(text(msg, " (Line ", line, ":", pos, ")")); else super(msg); } - this(string msg, string file, size_t line) + this(string msg, string file, size_t line) pure nothrow @safe { super(msg, file, line); } } -unittest +@system unittest { import std.exception; JSONValue jv = "123"; @@ -1104,11 +1355,11 @@ unittest assert(jv.type == JSON_TYPE.INTEGER); assertNotThrown(jv.integer); - jv = cast(uint)3; + jv = cast(uint) 3; assert(jv.type == JSON_TYPE.UINTEGER); assertNotThrown(jv.uinteger); - jv = 3.0f; + jv = 3.0; assert(jv.type == JSON_TYPE.FLOAT); assertNotThrown(jv.floating); @@ -1123,7 +1374,7 @@ unittest assert("key" in cjv); assertThrown!JSONException(cjv["notAnElement"]); - foreach(string key, value; jv) + foreach (string key, value; jv) { static assert(is(typeof(value) == JSONValue)); assert(key == "key"); @@ -1136,7 +1387,7 @@ unittest assert(jv.type == JSON_TYPE.ARRAY); assertNotThrown(jv.array); assertNotThrown(jv[2]); - foreach(size_t index, value; jv) + foreach (size_t index, value; jv) { static assert(is(typeof(value) == JSONValue)); assert(value.type == JSON_TYPE.INTEGER); @@ -1157,9 +1408,13 @@ unittest JSONValue jv2 = JSONValue("value"); assert(jv2.type == JSON_TYPE.STRING); assert(jv2.str == "value"); + + JSONValue jv3 = JSONValue("\u001c"); + assert(jv3.type == JSON_TYPE.STRING); + assert(jv3.str == "\u001C"); } -unittest +@system unittest { // Bugzilla 11504 @@ -1178,9 +1433,9 @@ unittest assert(jv.type == JSON_TYPE.UINTEGER); assert(jv.uinteger == 2u); - jv.floating = 1.5f; + jv.floating = 1.5; assert(jv.type == JSON_TYPE.FLOAT); - assert(jv.floating == 1.5f); + assert(jv.floating == 1.5); jv.object = ["key" : JSONValue("value")]; assert(jv.type == JSON_TYPE.OBJECT); @@ -1201,32 +1456,32 @@ unittest assert(jv.type == JSON_TYPE.TRUE); } -unittest +@system pure unittest { // Adding new json element via array() / object() directly JSONValue jarr = JSONValue([10]); - foreach (i; 0..9) + foreach (i; 0 .. 9) jarr.array ~= JSONValue(i); assert(jarr.array.length == 10); JSONValue jobj = JSONValue(["key" : JSONValue("value")]); - foreach (i; 0..9) + foreach (i; 0 .. 9) jobj.object[text("key", i)] = JSONValue(text("value", i)); assert(jobj.object.length == 10); } -unittest +@system pure unittest { // Adding new json element without array() / object() access JSONValue jarr = JSONValue([10]); - foreach (i; 0..9) + foreach (i; 0 .. 9) jarr ~= [JSONValue(i)]; assert(jarr.array.length == 10); JSONValue jobj = JSONValue(["key" : JSONValue("value")]); - foreach (i; 0..9) + foreach (i; 0 .. 9) jobj[text("key", i)] = JSONValue(text("value", i)); assert(jobj.object.length == 10); @@ -1236,8 +1491,9 @@ unittest assert(jarr[0] == JSONValue(10)); } -unittest +@system unittest { + // @system because JSONValue.array is @system import std.exception; // An overly simple test suite, if it can parse a serializated string and @@ -1251,8 +1507,8 @@ unittest `0`, `123`, `-4321`, - `0.23`, - `-0.23`, + `0.25`, + `-0.25`, `""`, `"hello\nworld"`, `"\"\\\/\b\f\n\r\t"`, @@ -1260,13 +1516,15 @@ unittest `[12,"foo",true,false]`, `{}`, `{"a":1,"b":null}`, - `{"goodbye":[true,"or",false,["test",42,{"nested":{"a":23.54,"b":0.0012}}]],"hello":{"array":[12,null,{}],"json":"is great"}}`, + `{"goodbye":[true,"or",false,["test",42,{"nested":{"a":23.5,"b":0.140625}}]],` + ~`"hello":{"array":[12,null,{}],"json":"is great"}}`, ]; + enum dbl1_844 = `1.8446744073709568`; version (MinGW) - jsons ~= `1.223e+024`; + jsons ~= dbl1_844 ~ `e+019`; else - jsons ~= `1.223e+24`; + jsons ~= dbl1_844 ~ `e+19`; JSONValue val; string result; @@ -1276,7 +1534,7 @@ unittest { val = parseJSON(json); enum pretty = false; - result = toJSON(&val, pretty); + result = toJSON(val, pretty); assert(result == json, text(result, " should be ", json)); } catch (JSONException e) @@ -1288,26 +1546,28 @@ unittest // Should be able to correctly interpret unicode entities val = parseJSON(`"\u003C\u003E"`); - assert(toJSON(&val) == "\"\<\>\""); + assert(toJSON(val) == "\"\<\>\""); assert(val.to!string() == "\"\<\>\""); val = parseJSON(`"\u0391\u0392\u0393"`); - assert(toJSON(&val) == "\"\Α\Β\Γ\""); + assert(toJSON(val) == "\"\Α\Β\Γ\""); assert(val.to!string() == "\"\Α\Β\Γ\""); val = parseJSON(`"\u2660\u2666"`); - assert(toJSON(&val) == "\"\♠\♦\""); + assert(toJSON(val) == "\"\♠\♦\""); assert(val.to!string() == "\"\♠\♦\""); //0x7F is a control character (see Unicode spec) - assertThrown(parseJSON(`{ "foo": "` ~ "\u007F" ~ `"}`)); + val = parseJSON(`"\u007F"`); + assert(toJSON(val) == "\"\\u007F\""); + assert(val.to!string() == "\"\\u007F\""); with(parseJSON(`""`)) assert(str == "" && str !is null); with(parseJSON(`[]`)) - assert(!array.length && array !is null); + assert(!array.length); // Formatting val = parseJSON(`{"a":[null,{"x":1},{},[]]}`); - assert(toJSON(&val, true) == `{ + assert(toJSON(val, true) == `{ "a": [ null, { @@ -1319,39 +1579,15 @@ unittest }`); } -unittest { +@safe unittest +{ auto json = `"hello\nworld"`; const jv = parseJSON(json); assert(jv.toString == json); assert(jv.toPrettyString == json); } -deprecated unittest -{ - // Bugzilla 12332 - import std.exception; - - JSONValue jv; - jv.type = JSON_TYPE.INTEGER; - jv = 1; - assert(jv.type == JSON_TYPE.INTEGER); - assert(jv.integer == 1); - jv.type = JSON_TYPE.UINTEGER; - assert(jv.uinteger == 1); - - jv.type = JSON_TYPE.STRING; - assertThrown!JSONException(jv.integer == 1); - assert(jv.str is null); - jv.str = "123"; - assert(jv.str == "123"); - jv.type = JSON_TYPE.STRING; - assert(jv.str == "123"); - - jv.type = JSON_TYPE.TRUE; - assert(jv.type == JSON_TYPE.TRUE); -} - -unittest +@system pure unittest { // Bugzilla 12969 @@ -1381,7 +1617,7 @@ unittest assert( jv[3].integer == 2 ); } -unittest +@safe unittest { auto s = q"EOF [ @@ -1397,3 +1633,102 @@ EOF"; auto e = collectException!JSONException(parseJSON(s)); assert(e.msg == "Unexpected character 'p'. (Line 5:3)", e.msg); } + +// handling of special float values (NaN, Inf, -Inf) +@safe unittest +{ + import std.math : isNaN, isInfinity; + import std.exception : assertThrown; + + // expected representations of NaN and Inf + enum { + nanString = '"' ~ JSONFloatLiteral.nan ~ '"', + infString = '"' ~ JSONFloatLiteral.inf ~ '"', + negativeInfString = '"' ~ JSONFloatLiteral.negativeInf ~ '"', + } + + // with the specialFloatLiterals option, encode NaN/Inf as strings + assert(JSONValue(float.nan).toString(JSONOptions.specialFloatLiterals) == nanString); + assert(JSONValue(double.infinity).toString(JSONOptions.specialFloatLiterals) == infString); + assert(JSONValue(-real.infinity).toString(JSONOptions.specialFloatLiterals) == negativeInfString); + + // without the specialFloatLiterals option, throw on encoding NaN/Inf + assertThrown!JSONException(JSONValue(float.nan).toString); + assertThrown!JSONException(JSONValue(double.infinity).toString); + assertThrown!JSONException(JSONValue(-real.infinity).toString); + + // when parsing json with specialFloatLiterals option, decode special strings as floats + JSONValue jvNan = parseJSON(nanString, JSONOptions.specialFloatLiterals); + JSONValue jvInf = parseJSON(infString, JSONOptions.specialFloatLiterals); + JSONValue jvNegInf = parseJSON(negativeInfString, JSONOptions.specialFloatLiterals); + + assert(jvNan.floating.isNaN); + assert(jvInf.floating.isInfinity && jvInf.floating > 0); + assert(jvNegInf.floating.isInfinity && jvNegInf.floating < 0); + + // when parsing json without the specialFloatLiterals option, decode special strings as strings + jvNan = parseJSON(nanString); + jvInf = parseJSON(infString); + jvNegInf = parseJSON(negativeInfString); + + assert(jvNan.str == JSONFloatLiteral.nan); + assert(jvInf.str == JSONFloatLiteral.inf); + assert(jvNegInf.str == JSONFloatLiteral.negativeInf); +} + +pure nothrow @safe @nogc unittest +{ + JSONValue testVal; + testVal = "test"; + testVal = 10; + testVal = 10u; + testVal = 1.0; + testVal = (JSONValue[string]).init; + testVal = JSONValue[].init; + testVal = null; + assert(testVal.isNull); +} + +pure nothrow @safe unittest // issue 15884 +{ + import std.typecons; + void Test(C)() { + C[] a = ['x']; + JSONValue testVal = a; + assert(testVal.type == JSON_TYPE.STRING); + testVal = a.idup; + assert(testVal.type == JSON_TYPE.STRING); + } + Test!char(); + Test!wchar(); + Test!dchar(); +} + +@safe unittest // issue 15885 +{ + static bool test(const double num0) + { + import std.math : feqrel; + const json0 = JSONValue(num0); + const num1 = to!double(toJSON(json0)); + version(Win32) + return feqrel(num1, num0) >= (double.mant_dig - 1); + else + return num1 == num0; + } + + assert(test( 0.23)); + assert(test(-0.23)); + assert(test(1.223e+24)); + assert(test(23.4)); + assert(test(0.0012)); + assert(test(30738.22)); + + assert(test(1 + double.epsilon)); + assert(test(-double.max)); + assert(test(double.min_normal)); + + const minSub = double.min_normal * double.epsilon; + assert(test(minSub)); + assert(test(3*minSub)); +} diff --git a/std/math.d b/std/math.d index 8518185d9ba..8c394ab1730 100644 --- a/std/math.d +++ b/std/math.d @@ -17,7 +17,8 @@ $(TR $(TDNW Constants) $(TD $(MYREF SQRT2) $(MYREF SQRT1_2) )) $(TR $(TDNW Classics) $(TD - $(MYREF abs) $(MYREF fabs) $(MYREF sqrt) $(MYREF cbrt) $(MYREF hypot) $(MYREF poly) + $(MYREF abs) $(MYREF fabs) $(MYREF sqrt) $(MYREF cbrt) $(MYREF hypot) + $(MYREF poly) $(MYREF nextPow2) $(MYREF truncPow2) )) $(TR $(TDNW Trigonometry) $(TD $(MYREF sin) $(MYREF cos) $(MYREF tan) $(MYREF asin) $(MYREF acos) @@ -27,7 +28,7 @@ $(TR $(TDNW Trigonometry) $(TD $(TR $(TDNW Rounding) $(TD $(MYREF ceil) $(MYREF floor) $(MYREF round) $(MYREF lround) $(MYREF trunc) $(MYREF rint) $(MYREF lrint) $(MYREF nearbyint) - $(MYREF rndtol) + $(MYREF rndtol) $(MYREF quantize) )) $(TR $(TDNW Exponentiation & Logarithms) $(TD $(MYREF pow) $(MYREF exp) $(MYREF exp2) $(MYREF expm1) $(MYREF ldexp) @@ -41,11 +42,12 @@ $(TR $(TDNW Floating-point operations) $(TD $(MYREF approxEqual) $(MYREF feqrel) $(MYREF fdim) $(MYREF fmax) $(MYREF fmin) $(MYREF fma) $(MYREF nextDown) $(MYREF nextUp) $(MYREF nextafter) $(MYREF NaN) $(MYREF getNaNPayload) + $(MYREF cmp) )) $(TR $(TDNW Introspection) $(TD $(MYREF isFinite) $(MYREF isIdentical) $(MYREF isInfinity) $(MYREF isNaN) $(MYREF isNormal) $(MYREF isSubnormal) $(MYREF signbit) $(MYREF sgn) - $(MYREF copysign) + $(MYREF copysign) $(MYREF isPowerOf2) )) $(TR $(TDNW Complex Numbers) $(TD $(MYREF abs) $(MYREF conj) $(MYREF sin) $(MYREF cos) $(MYREF expi) @@ -77,9 +79,7 @@ $(TR $(TDNW Hardware Control) $(TD * The semantics and names of feqrel and approxEqual will be revised. * * Macros: - * WIKI = Phobos/StdMath - * - * TABLE_SV = + * TABLE_SV =
* * $0
Special Values
* SVH = $(TR $(TH $1) $(TH $2)) @@ -109,13 +109,13 @@ $(TR $(TDNW Hardware Control) $(TD * Copyright: Copyright Digital Mars 2000 - 2011. * D implementations of tan, atan, atan2, exp, expm1, exp2, log, log10, log1p, * log2, floor, ceil and lrint functions are based on the CEPHES math library, - * which is Copyright (C) 2001 Stephen L. Moshier + * which is Copyright (C) 2001 Stephen L. Moshier $(LT)steve@moshier.net$(GT) * and are incorporated herein by permission of the author. The author * reserves the right to distribute this material elsewhere under different * copying permissions. These modifications are distributed here under * the following terms: - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright), Don Clugston, + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, * Conversion of CEPHES math library to D by Iain Buclaw * Source: $(PHOBOSSRC std/_math.d) */ @@ -127,8 +127,9 @@ version (Win64) version = Win64_DMD_InlineAsm; } -import core.stdc.math; -import std.traits; +static import core.math; +static import core.stdc.math; +import std.traits; // CommonType, isFloatingPoint, isIntegral, isSigned, isUnsigned, Largest, Unqual version(LDC) { @@ -154,12 +155,24 @@ else version(D_InlineAsm_X86_64) version = InlineAsm_X86_Any; } +version (X86_64) version = StaticallyHaveSSE; +version (X86) version (OSX) version = StaticallyHaveSSE; + +version (StaticallyHaveSSE) +{ + private enum bool haveSSE = true; +} +else +{ + static import core.cpuid; + private alias haveSSE = core.cpuid.sse; +} version(unittest) { - import core.stdc.stdio; + import core.stdc.stdio; // : sprintf; - static if(real.sizeof > double.sizeof) + static if (real.sizeof > double.sizeof) enum uint useDigits = 16; else enum uint useDigits = 15; @@ -193,9 +206,9 @@ version(unittest) int ix; int iy; version(CRuntime_Microsoft) - alias double real_t; + alias real_t = double; else - alias real real_t; + alias real_t = real; ix = sprintf(bufx.ptr, "%.*Lg", ndigits, cast(real_t) x); iy = sprintf(bufy.ptr, "%.*Lg", ndigits, cast(real_t) y); assert(ix < bufx.length && ix > 0); @@ -207,7 +220,7 @@ version(unittest) -private: +package: // The following IEEE 'real' formats are currently supported. version(LittleEndian) { @@ -241,6 +254,7 @@ enum RealFormat template floatTraits(T) { // EXPMASK is a ushort mask to select the exponent portion (without sign) + // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort // EXPPOS_SHORT is the index of the exponent when represented as a ushort array. // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array. // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal @@ -249,6 +263,7 @@ template floatTraits(T) { // Single precision float enum ushort EXPMASK = 0x7F80; + enum ushort EXPSHIFT = 7; enum ushort EXPBIAS = 0x3F00; enum uint EXPMASK_INT = 0x7F80_0000; enum uint MANTISSAMASK_INT = 0x007F_FFFF; @@ -270,6 +285,7 @@ template floatTraits(T) { // Double precision float, or real == double enum ushort EXPMASK = 0x7FF0; + enum ushort EXPSHIFT = 4; enum ushort EXPBIAS = 0x3FE0; enum uint EXPMASK_INT = 0x7FF0_0000; enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only @@ -289,6 +305,7 @@ template floatTraits(T) { // Intel extended real80 rounded to double enum ushort EXPMASK = 0x7FFF; + enum ushort EXPSHIFT = 0; enum ushort EXPBIAS = 0x3FFE; enum realFormat = RealFormat.ieeeExtended53; version(LittleEndian) @@ -309,6 +326,7 @@ template floatTraits(T) { // Intel extended real80 enum ushort EXPMASK = 0x7FFF; + enum ushort EXPSHIFT = 0; enum ushort EXPBIAS = 0x3FFE; enum realFormat = RealFormat.ieeeExtended; version(LittleEndian) @@ -326,6 +344,8 @@ template floatTraits(T) { // Quadruple precision float enum ushort EXPMASK = 0x7FFF; + enum ushort EXPSHIFT = 0; + enum ushort EXPBIAS = 0x3FFF; enum realFormat = RealFormat.ieeeQuadruple; version(LittleEndian) { @@ -342,6 +362,7 @@ template floatTraits(T) { // IBM Extended doubledouble enum ushort EXPMASK = 0x7FF0; + enum ushort EXPSHIFT = 4; enum realFormat = RealFormat.ibmExtended; // the exponent byte is not unique version(LittleEndian) @@ -498,28 +519,28 @@ enum real SQRT1_2 = SQRT2/2; /** $(SQRT)$(HALF) * = hypot(z.re, z.im). */ Num abs(Num)(Num x) @safe pure nothrow - if (is(typeof(Num.init >= 0)) && is(typeof(-Num.init)) && - !(is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) - || is(Num* : const(ireal*)))) +if (is(typeof(Num.init >= 0)) && is(typeof(-Num.init)) && + !(is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) + || is(Num* : const(ireal*)))) { static if (isFloatingPoint!(Num)) return fabs(x); else - return x>=0 ? x : -x; + return x >= 0 ? x : -x; } /// ditto auto abs(Num)(Num z) @safe pure nothrow @nogc - if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*)) - || is(Num* : const(creal*))) +if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*)) + || is(Num* : const(creal*))) { return hypot(z.re, z.im); } /// ditto auto abs(Num)(Num y) @safe pure nothrow @nogc - if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) - || is(Num* : const(ireal*))) +if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) + || is(Num* : const(ireal*))) { return fabs(y.im); } @@ -539,14 +560,14 @@ auto abs(Num)(Num y) @safe pure nothrow @nogc @safe pure nothrow @nogc unittest { - import std.typetuple; - foreach (T; TypeTuple!(float, double, real)) + import std.meta : AliasSeq; + foreach (T; AliasSeq!(float, double, real)) { T f = 3; assert(abs(f) == f); assert(abs(-f) == f); } - foreach (T; TypeTuple!(cfloat, cdouble, creal)) + foreach (T; AliasSeq!(cfloat, cdouble, creal)) { T f = -12+3i; assert(abs(f) == hypot(f.re, f.im)); @@ -563,21 +584,21 @@ auto abs(Num)(Num y) @safe pure nothrow @nogc * is always a real number */ auto conj(Num)(Num z) @safe pure nothrow @nogc - if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*)) - || is(Num* : const(creal*))) +if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*)) + || is(Num* : const(creal*))) { //FIXME //Issue 14206 - static if(is(Num* : const(cdouble*))) - return cast(cdouble) conj(cast(creal)z); + static if (is(Num* : const(cdouble*))) + return cast(cdouble) conj(cast(creal) z); else return z.re - z.im*1fi; } /** ditto */ auto conj(Num)(Num y) @safe pure nothrow @nogc - if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) - || is(Num* : const(ireal*))) +if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) + || is(Num* : const(ireal*))) { return -y; } @@ -619,10 +640,22 @@ auto conj(Num)(Num y) @safe pure nothrow @nogc * Results are undefined if |x| >= $(POWER 2,64). */ -real cos(real x) @safe pure nothrow @nogc; /* intrinsic */ +real cos(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.cos(x); } +//FIXME +///ditto +double cos(double x) @safe pure nothrow @nogc { return cos(cast(real) x); } +//FIXME +///ditto +float cos(float x) @safe pure nothrow @nogc { return cos(cast(real) x); } + +@safe unittest +{ + real function(real) pcos = &cos; + assert(pcos != null); +} /*********************************** - * Returns $(WEB en.wikipedia.org/wiki/Sine, sine) of x. x is in $(WEB en.wikipedia.org/wiki/Radian, radians). + * Returns $(HTTP en.wikipedia.org/wiki/Sine, sine) of x. x is in $(HTTP en.wikipedia.org/wiki/Radian, radians). * * $(TABLE_SV * $(TH3 x , sin(x) , invalid?) @@ -641,10 +674,16 @@ real cos(real x) @safe pure nothrow @nogc; /* intrinsic */ * Results are undefined if |x| >= $(POWER 2,64). */ -real sin(real x) @safe pure nothrow @nogc; /* intrinsic */ +real sin(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.sin(x); } +//FIXME +///ditto +double sin(double x) @safe pure nothrow @nogc { return sin(cast(real) x); } +//FIXME +///ditto +float sin(float x) @safe pure nothrow @nogc { return sin(cast(real) x); } /// -unittest +@safe unittest { import std.math : sin, PI; import std.stdio : writefln; @@ -657,6 +696,12 @@ unittest } } +@safe unittest +{ + real function(real) psin = &sin; + assert(psin != null); +} + /*********************************** * Returns sine for complex and imaginary arguments. * @@ -667,8 +712,8 @@ unittest */ creal sin(creal z) @safe pure nothrow @nogc { - creal cs = expi(z.re); - creal csh = coshisinh(z.im); + const creal cs = expi(z.re); + const creal csh = coshisinh(z.im); return cs.im * csh.re + cs.re * csh.im * 1i; } @@ -692,8 +737,8 @@ ireal sin(ireal y) @safe pure nothrow @nogc */ creal cos(creal z) @safe pure nothrow @nogc { - creal cs = expi(z.re); - creal csh = coshisinh(z.im); + const creal cs = expi(z.re); + const creal csh = coshisinh(z.im); return cs.re * csh.re - cs.im * csh.im * 1i; } @@ -859,7 +904,7 @@ Lret: {} } z = ((x - y * P1) - y * P2) - y * P3; - real zz = z * z; + const real zz = z * z; if (zz > 1.0e-20L) y = z + z * (zz * poly(zz, P) / poly(zz, Q)); @@ -918,7 +963,7 @@ Lret: {} r = -r; t = tan(x); //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r); - if (!isIdentical(r, t) && !(r!=r && t!=t)) assert(fabs(r-t) <= .0000001); + if (!isIdentical(r, t) && !(r != r && t != t)) assert(fabs(r-t) <= .0000001); } // overflow assert(isNaN(tan(real.infinity))); @@ -927,7 +972,7 @@ Lret: {} assert(isIdentical( tan(NaN(0x0123L)), NaN(0x0123L) )); } -unittest +@system unittest { assert(equalsDigit(tan(PI / 3), std.math.sqrt(3.0), useDigits)); } @@ -949,12 +994,12 @@ real acos(real x) @safe pure nothrow @nogc } /// ditto -double acos(double x) @safe pure nothrow @nogc { return acos(cast(real)x); } +double acos(double x) @safe pure nothrow @nogc { return acos(cast(real) x); } /// ditto -float acos(float x) @safe pure nothrow @nogc { return acos(cast(real)x); } +float acos(float x) @safe pure nothrow @nogc { return acos(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(acos(0.5), std.math.PI / 3, useDigits)); } @@ -976,12 +1021,12 @@ real asin(real x) @safe pure nothrow @nogc } /// ditto -double asin(double x) @safe pure nothrow @nogc { return asin(cast(real)x); } +double asin(double x) @safe pure nothrow @nogc { return asin(cast(real) x); } /// ditto -float asin(float x) @safe pure nothrow @nogc { return asin(cast(real)x); } +float asin(float x) @safe pure nothrow @nogc { return asin(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(asin(0.5), PI / 6, useDigits)); } @@ -1056,7 +1101,7 @@ real atan(real x) @safe pure nothrow @nogc y = 0.0; // Rational form in x^^2. - real z = x * x; + const real z = x * x; y = y + (poly(z, P) / poly(z, Q)) * z * x + x; return (sign) ? -y : y; @@ -1064,12 +1109,12 @@ real atan(real x) @safe pure nothrow @nogc } /// ditto -double atan(double x) @safe pure nothrow @nogc { return atan(cast(real)x); } +double atan(double x) @safe pure nothrow @nogc { return atan(cast(real) x); } /// ditto -float atan(float x) @safe pure nothrow @nogc { return atan(cast(real)x); } +float atan(float x) @safe pure nothrow @nogc { return atan(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(atan(std.math.sqrt(3.0)), PI / 3, useDigits)); } @@ -1173,16 +1218,16 @@ real atan2(real y, real x) @trusted pure nothrow @nogc /// ditto double atan2(double y, double x) @safe pure nothrow @nogc { - return atan2(cast(real)y, cast(real)x); + return atan2(cast(real) y, cast(real) x); } /// ditto float atan2(float y, float x) @safe pure nothrow @nogc { - return atan2(cast(real)y, cast(real)x); + return atan2(cast(real) y, cast(real) x); } -unittest +@system unittest { assert(equalsDigit(atan2(1.0L, std.math.sqrt(3.0L)), PI / 6, useDigits)); } @@ -1199,17 +1244,17 @@ real cosh(real x) @safe pure nothrow @nogc { // cosh = (exp(x)+exp(-x))/2. // The naive implementation works correctly. - real y = exp(x); + const real y = exp(x); return (y + 1.0/y) * 0.5; } /// ditto -double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real)x); } +double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real) x); } /// ditto -float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real)x); } +float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(cosh(1.0), (E + 1.0 / E) / 2, useDigits)); } @@ -1234,17 +1279,17 @@ real sinh(real x) @safe pure nothrow @nogc return copysign(0.5 * exp(fabs(x)), x); } - real y = expm1(x); + const real y = expm1(x); return 0.5 * y / (y+1) * (y+2); } /// ditto -double sinh(double x) @safe pure nothrow @nogc { return sinh(cast(real)x); } +double sinh(double x) @safe pure nothrow @nogc { return sinh(cast(real) x); } /// ditto -float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real)x); } +float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(sinh(1.0), (E - 1.0 / E) / 2, useDigits)); } @@ -1266,17 +1311,17 @@ real tanh(real x) @safe pure nothrow @nogc return copysign(1, x); } - real y = expm1(2*x); + const real y = expm1(2*x); return y / (y + 2); } /// ditto -double tanh(double x) @safe pure nothrow @nogc { return tanh(cast(real)x); } +double tanh(double x) @safe pure nothrow @nogc { return tanh(cast(real) x); } /// ditto -float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real)x); } +float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(tanh(1.0), sinh(1.0) / cosh(1.0), 15)); } @@ -1291,12 +1336,12 @@ creal coshisinh(real x) @safe pure nothrow @nogc // See comments for cosh, sinh. if (fabs(x) > real.mant_dig * LN2) { - real y = exp(fabs(x)); + const real y = exp(fabs(x)); return y * 0.5 + 0.5i * copysign(y, x); } else { - real y = expm1(x); + const real y = expm1(x); return (y + 1.0 + 1.0/(y + 1.0)) * 0.5 + 0.5i * y / (y+1) * (y+2); } } @@ -1317,7 +1362,7 @@ public: * * $(TABLE_DOMRG * $(DOMAIN 1..$(INFIN)) - * $(RANGE 1..log(real.max), $(INFIN)) ) + * $(RANGE 1 .. log(real.max), $(INFIN)) ) * $(TABLE_SV * $(SVH x, acosh(x) ) * $(SV $(NAN), $(NAN) ) @@ -1335,13 +1380,13 @@ real acosh(real x) @safe pure nothrow @nogc } /// ditto -double acosh(double x) @safe pure nothrow @nogc { return acosh(cast(real)x); } +double acosh(double x) @safe pure nothrow @nogc { return acosh(cast(real) x); } /// ditto -float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real)x); } +float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real) x); } -unittest +@system unittest { assert(isNaN(acosh(0.9))); assert(isNaN(acosh(real.nan))); @@ -1377,12 +1422,12 @@ real asinh(real x) @safe pure nothrow @nogc } /// ditto -double asinh(double x) @safe pure nothrow @nogc { return asinh(cast(real)x); } +double asinh(double x) @safe pure nothrow @nogc { return asinh(cast(real) x); } /// ditto -float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real)x); } +float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real) x); } -unittest +@system unittest { assert(isIdentical(asinh(0.0), 0.0)); assert(isIdentical(asinh(-0.0), -0.0)); @@ -1401,7 +1446,7 @@ unittest * * $(TABLE_DOMRG * $(DOMAIN -$(INFIN)..$(INFIN)) - * $(RANGE -1..1) ) + * $(RANGE -1 .. 1) ) * $(TABLE_SV * $(SVH x, acosh(x) ) * $(SV $(NAN), $(NAN) ) @@ -1416,13 +1461,13 @@ real atanh(real x) @safe pure nothrow @nogc } /// ditto -double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real)x); } +double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real) x); } /// ditto -float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real)x); } +float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real) x); } -unittest +@system unittest { assert(isIdentical(atanh(0.0), 0.0)); assert(isIdentical(atanh(-0.0),-0.0)); @@ -1438,8 +1483,19 @@ unittest * greater than long.max, the result is * indeterminate. */ -long rndtol(real x) @nogc @safe pure nothrow; /* intrinsic */ +long rndtol(real x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.rndtol(x); } +//FIXME +///ditto +long rndtol(double x) @safe pure nothrow @nogc { return rndtol(cast(real) x); } +//FIXME +///ditto +long rndtol(float x) @safe pure nothrow @nogc { return rndtol(cast(real) x); } +@safe unittest +{ + long function(real) prndtol = &rndtol; + assert(prndtol != null); +} /***************************************** * Returns x rounded to a long value using the FE_TONEAREST rounding mode. @@ -1459,13 +1515,13 @@ extern (C) real rndtonl(real x); * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no)) * ) */ -float sqrt(float x) @nogc @safe pure nothrow; /* intrinsic */ +float sqrt(float x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); } /// ditto -double sqrt(double x) @nogc @safe pure nothrow; /* intrinsic */ +double sqrt(double x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); } /// ditto -real sqrt(real x) @nogc @safe pure nothrow; /* intrinsic */ +real sqrt(real x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); } @safe pure nothrow @nogc unittest { @@ -1473,6 +1529,20 @@ real sqrt(real x) @nogc @safe pure nothrow; /* intrinsic */ enum ZX80 = sqrt(7.0f); enum ZX81 = sqrt(7.0); enum ZX82 = sqrt(7.0L); + + assert(isNaN(sqrt(-1.0f))); + assert(isNaN(sqrt(-1.0))); + assert(isNaN(sqrt(-1.0L))); +} + +@safe unittest +{ + float function(float) psqrtf = &sqrt; + assert(psqrtf != null); + double function(double) psqrtd = &sqrt; + assert(psqrtd != null); + real function(real) psqrtr = &sqrt; + assert(psqrtr != null); } creal sqrt(creal z) @nogc @safe pure nothrow @@ -1486,8 +1556,8 @@ creal sqrt(creal z) @nogc @safe pure nothrow } else { - real z_re = z.re; - real z_im = z.im; + const real z_re = z.re; + const real z_im = z.im; x = fabs(z_re); y = fabs(z_im); @@ -1544,46 +1614,86 @@ real exp(real x) @trusted pure nothrow @nogc } else { - // Coefficients for exp(x) - static immutable real[3] P = [ - 9.9999999999999999991025E-1L, - 3.0299440770744196129956E-2L, - 1.2617719307481059087798E-4L, - ]; - static immutable real[4] Q = [ - 2.0000000000000000000897E0L, - 2.2726554820815502876593E-1L, - 2.5244834034968410419224E-3L, - 3.0019850513866445504159E-6L, - ]; + alias F = floatTraits!real; + static if (F.realFormat == RealFormat.ieeeDouble) + { + // Coefficients for exp(x) + static immutable real[3] P = [ + 9.99999999999999999910E-1L, + 3.02994407707441961300E-2L, + 1.26177193074810590878E-4L, + ]; + static immutable real[4] Q = [ + 2.00000000000000000009E0L, + 2.27265548208155028766E-1L, + 2.52448340349684104192E-3L, + 3.00198505138664455042E-6L, + ]; - // C1 + C2 = LN2. - enum real C1 = 6.9314575195312500000000E-1L; - enum real C2 = 1.428606820309417232121458176568075500134E-6L; + // C1 + C2 = LN2. + enum real C1 = 6.93145751953125E-1; + enum real C2 = 1.42860682030941723212E-6; - // Overflow and Underflow limits. - enum real OF = 1.1356523406294143949492E4L; - enum real UF = -1.1432769596155737933527E4L; + // Overflow and Underflow limits. + enum real OF = 7.09782712893383996732E2; // ln((1-2^-53) * 2^1024) + enum real UF = -7.451332191019412076235E2; // ln(2^-1075) + } + else static if (F.realFormat == RealFormat.ieeeExtended) + { + // Coefficients for exp(x) + static immutable real[3] P = [ + 9.9999999999999999991025E-1L, + 3.0299440770744196129956E-2L, + 1.2617719307481059087798E-4L, + ]; + static immutable real[4] Q = [ + 2.0000000000000000000897E0L, + 2.2726554820815502876593E-1L, + 2.5244834034968410419224E-3L, + 3.0019850513866445504159E-6L, + ]; - // Special cases. + // C1 + C2 = LN2. + enum real C1 = 6.9314575195312500000000E-1L; + enum real C2 = 1.4286068203094172321215E-6L; + + // Overflow and Underflow limits. + enum real OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384) + enum real UF = -1.13994985314888605586758E4L; // ln(2^-16446) + } + else + static assert(0, "Not implemented for this architecture"); + + // Special cases. Raises an overflow or underflow flag accordingly, + // except in the case for CTFE, where there are no hardware controls. if (isNaN(x)) return x; if (x > OF) - return real.infinity; + { + if (__ctfe) + return real.infinity; + else + return real.max * copysign(real.max, real.infinity); + } if (x < UF) - return 0.0; + { + if (__ctfe) + return 0.0; + else + return real.min_normal * copysign(real.min_normal, 0.0); + } // Express: e^^x = e^^g * 2^^n // = e^^g * e^^(n * LOG2E) // = e^^(g + n * LOG2E) - int n = cast(int)floor(LOG2E * x + 0.5); + int n = cast(int) floor(LOG2E * x + 0.5); x -= n * C1; x -= n * C2; // Rational approximation for exponential of the fractional part: // e^^x = 1 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) - real xx = x * x; - real px = x * poly(xx, P); + const real xx = x * x; + const real px = x * poly(xx, P); x = px / (poly(xx, Q) - px); x = 1.0 + ldexp(x, 1); @@ -1595,12 +1705,12 @@ real exp(real x) @trusted pure nothrow @nogc } /// ditto -double exp(double x) @safe pure nothrow @nogc { return exp(cast(real)x); } +double exp(double x) @safe pure nothrow @nogc { return exp(cast(real) x); } /// ditto -float exp(float x) @safe pure nothrow @nogc { return exp(cast(real)x); } +float exp(float x) @safe pure nothrow @nogc { return exp(cast(real) x); } -unittest +@system unittest { assert(equalsDigit(exp(3.0L), E * E * E, useDigits)); } @@ -1672,7 +1782,7 @@ L_extreme: // Extreme exponent. X is very large positive, very // large negative, infinity, or NaN. fxam; fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x!=0 + test AX, 0x0400; // NaN_or_zero, but we already know x != 0 jz L_was_nan; // if x is NaN, returns x test AX, 0x0200; jnz L_largenegative; @@ -1760,7 +1870,7 @@ L_extreme: // Extreme exponent. X is very large positive, very // large negative, infinity, or NaN. fxam; fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x!=0 + test AX, 0x0400; // NaN_or_zero, but we already know x != 0 jz L_was_nan; // if x is NaN, returns x test AX, 0x0200; jnz L_largenegative; @@ -1810,16 +1920,22 @@ L_largenegative: enum real OF = 1.1356523406294143949492E4L; enum real UF = -4.5054566736396445112120088E1L; - // Special cases. + // Special cases. Raises an overflow flag, except in the case + // for CTFE, where there are no hardware controls. if (x > OF) - return real.infinity; + { + if (__ctfe) + return real.infinity; + else + return real.max * copysign(real.max, real.infinity); + } if (x == 0.0) return x; if (x < UF) return -1.0; // Express x = LN2 (n + remainder), remainder not exceeding 1/2. - int n = cast(int)floor(0.5 + x / LN2); + int n = cast(int) floor(0.5 + x / LN2); x -= n * C1; x -= n * C2; @@ -1827,7 +1943,7 @@ L_largenegative: // exp(x) - 1 = x + 0.5 x^^2 + x^^3 P(x) / Q(x) real px = x * poly(x, P); real qx = poly(x, Q); - real xx = x * x; + const real xx = x * x; qx = x + (0.5 * xx + xx * px / qx); // We have qx = exp(remainder LN2) - 1, so: @@ -1919,7 +2035,7 @@ L_extreme: // Extreme exponent. X is very large positive, very // large negative, infinity, or NaN. fxam; fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x!=0 + test AX, 0x0400; // NaN_or_zero, but we already know x != 0 jz L_was_nan; // if x is NaN, returns x // set scratchreal = real.min_normal // squaring it will return 0, setting underflow flag @@ -2020,7 +2136,7 @@ L_extreme: // Extreme exponent. X is very large positive, very // large negative, infinity, or NaN. fxam; fstsw AX; - test AX, 0x0400; // NaN_or_zero, but we already know x!=0 + test AX, 0x0400; // NaN_or_zero, but we already know x != 0 jz L_was_nan; // if x is NaN, returns x // set scratchreal = real.min // squaring it will return 0, setting underflow flag @@ -2056,25 +2172,36 @@ L_was_nan: ]; // Overflow and Underflow limits. - enum real OF = 16384.0L; - enum real UF = -16382.0L; + enum real OF = 16_384.0L; + enum real UF = -16_382.0L; - // Special cases. + // Special cases. Raises an overflow or underflow flag accordingly, + // except in the case for CTFE, where there are no hardware controls. if (isNaN(x)) return x; if (x > OF) - return real.infinity; + { + if (__ctfe) + return real.infinity; + else + return real.max * copysign(real.max, real.infinity); + } if (x < UF) - return 0.0; + { + if (__ctfe) + return 0.0; + else + return real.min_normal * copysign(real.min_normal, 0.0); + } // Separate into integer and fractional parts. - int n = cast(int)floor(x + 0.5); + int n = cast(int) floor(x + 0.5); x -= n; // Rational approximation: // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2)) - real xx = x * x; - real px = x * poly(xx, P); + const real xx = x * x; + const real px = x * poly(xx, P); x = px / (poly(xx, Q) - px); x = 1.0 + ldexp(x, 1); @@ -2086,14 +2213,14 @@ L_was_nan: } /// -unittest +@safe unittest { assert(feqrel(exp2(0.5L), SQRT2) >= real.mant_dig -1); assert(exp2(8.0L) == 256.0); assert(exp2(-9.0L)== 1.0L/512.0); } -unittest +@safe unittest { version(CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented { @@ -2103,46 +2230,80 @@ unittest } } -unittest +@system unittest { FloatingPointControl ctrl; - if(FloatingPointControl.hasExceptionTraps) + if (FloatingPointControl.hasExceptionTraps) ctrl.disableExceptions(FloatingPointControl.allExceptions); ctrl.rounding = FloatingPointControl.roundToNearest; - // @@BUG@@: Non-immutable array literals are ridiculous. - // Note that these are only valid for 80-bit reals: overflow will be different for 64-bit reals. - static const real [2][] exptestpoints = - [ // x, exp(x) - [1.0L, E ], - [0.5L, 0x1.A612_98E1_E069_BC97p+0L ], - [3.0L, E*E*E ], - [0x1.1p13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow - [-0x1.18p13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow - [-0x1.625p13L, 0x1.a6bd68a39d11f35cp-16358L], - [-0x1p30L, 0 ], // underflow - subnormal - [-0x1.62DAFp13L, 0x1.96c53d30277021dp-16383L ], - [-0x1.643p13L, 0x1p-16444L ], - [-0x1.645p13L, 0 ], // underflow to zero - [0x1p80L, real.infinity ], // far overflow - [real.infinity, real.infinity ], - [0x1.7p13L, real.infinity ] // close overflow - ]; + static if (real.mant_dig == 64) // 80-bit reals + { + static immutable real[2][] exptestpoints = + [ // x exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61298e1e069bc97p+0L ], + [ 3.0L, E*E*E ], + [ 0x1.1p+13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow + [ 0x1.7p+13L, real.infinity ], // close overflow + [ 0x1p+80L, real.infinity ], // far overflow + [ real.infinity, real.infinity ], + [-0x1.18p+13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow + [-0x1.625p+13L, 0x1.a6bd68a39d11f35cp-16358L ], // ditto + [-0x1.62dafp+13L, 0x1.96c53d30277021dp-16383L ], // near underflow - subnormal + [-0x1.643p+13L, 0x1p-16444L ], // ditto + [-0x1.645p+13L, 0 ], // close underflow + [-0x1p+30L, 0 ], // far underflow + ]; + } + else static if (real.mant_dig == 53) // 64-bit reals + { + static immutable real[2][] exptestpoints = + [ // x, exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61298e1e069cp+0L ], + [ 3.0L, E*E*E ], + [ 0x1.6p+9L, 0x1.93bf4ec282efbp+1015L ], // near overflow + [ 0x1.7p+9L, real.infinity ], // close overflow + [ 0x1p+80L, real.infinity ], // far overflow + [ real.infinity, real.infinity ], + [-0x1.6p+9L, 0x1.44a3824e5285fp-1016L ], // near underflow + [-0x1.64p+9L, 0x0.06f84920bb2d4p-1022L ], // near underflow - subnormal + [-0x1.743p+9L, 0x0.0000000000001p-1022L ], // ditto + [-0x1.8p+9L, 0 ], // close underflow + [-0x1p30L, 0 ], // far underflow + ]; + } + else + static assert(0, "No exp() tests for real type!"); + + const minEqualMantissaBits = real.mant_dig - 2; real x; IeeeFlags f; - for (int i=0; i= minEqualMantissaBits); + + version (IeeeFlagsSupport) + { + // Check the overflow bit + if (x == real.infinity) + { + // don't care about the overflow bit if input was inf + // (e.g., the LLVM intrinsic doesn't set it on Linux x86_64) + assert(pair[0] == real.infinity || f.overflow); + } + else + assert(!f.overflow); + // Check the underflow bit + assert(f.underflow == (fabs(x) < real.min_normal)); + // Invalid and div by zero shouldn't be affected. + assert(!f.invalid); + assert(!f.divByZero); + } } // Ideally, exp(0) would not set the inexact flag. // Unfortunately, fldl2e sets it! @@ -2234,11 +2395,11 @@ creal expi(real y) @trusted pure nothrow @nogc * ) */ T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc - if(isFloatingPoint!T) +if (isFloatingPoint!T) { Unqual!T vf = value; ushort* vu = cast(ushort*)&vf; - static if(is(Unqual!T == float)) + static if (is(Unqual!T == float)) int* vi = cast(int*)&vf; else long* vl = cast(long*)&vf; @@ -2281,7 +2442,7 @@ T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc vf *= F.RECIP_EPSILON; ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; exp = ex - F.EXPBIAS - T.mant_dig + 1; - vu[F.EXPPOS_SHORT] = (0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE; + vu[F.EXPPOS_SHORT] = (~F.EXPMASK & vu[F.EXPPOS_SHORT]) | 0x3FFE; } return vf; } @@ -2324,7 +2485,7 @@ T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; exp = ex - F.EXPBIAS - T.mant_dig + 1; vu[F.EXPPOS_SHORT] = - cast(ushort)((0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE); + cast(ushort)((~F.EXPMASK & vu[F.EXPPOS_SHORT]) | 0x3FFE); } return vf; } @@ -2364,7 +2525,7 @@ T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; exp = ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1; vu[F.EXPPOS_SHORT] = - cast(ushort)((0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FE0); + cast(ushort)((~F.EXPMASK & vu[F.EXPPOS_SHORT]) | 0x3FE0); } return vf; } @@ -2404,24 +2565,24 @@ T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; exp = ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1; vu[F.EXPPOS_SHORT] = - cast(ushort)((0x8000 & vu[F.EXPPOS_SHORT]) | 0x3F00); + cast(ushort)((~F.EXPMASK & vu[F.EXPPOS_SHORT]) | 0x3F00); } return vf; } else // static if (F.realFormat == RealFormat.ibmExtended) { - assert (0, "frexp not implemented"); + assert(0, "frexp not implemented"); } } /// -unittest +@system unittest { int exp; real mantissa = frexp(123.456L, exp); // check if values are equal to 19 decimal digits of precision - assert(equalsDigit(mantissa * pow(2.0L, cast(real)exp), 123.456L, 19)); + assert(equalsDigit(mantissa * pow(2.0L, cast(real) exp), 123.456L, 19)); assert(frexp(-real.nan, exp) && exp == int.min); assert(frexp(real.nan, exp) && exp == int.min); @@ -2431,11 +2592,12 @@ unittest assert(frexp(0.0, exp) == 0.0 && exp == 0); } -unittest +@safe unittest { - import std.typetuple, std.typecons; + import std.meta : AliasSeq; + import std.typecons : tuple, Tuple; - foreach (T; TypeTuple!(real, double, float)) + foreach (T; AliasSeq!(real, double, float)) { Tuple!(T, T, int)[] vals = // x,frexp,exp [ @@ -2449,9 +2611,12 @@ unittest tuple(-T.infinity, -T.infinity, int.min), tuple(T.nan, T.nan, int.min), tuple(-T.nan, -T.nan, int.min), + + // Phobos issue #16026: + tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2) ]; - foreach(elem; vals) + foreach (elem; vals) { T x = elem[0]; T e = elem[1]; @@ -2466,16 +2631,16 @@ unittest static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended) { static T[3][] extendedvals = [ // x,frexp,exp - [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal - [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063], - [T.min_normal, .5, -16381], - [T.min_normal/2.0L, .5, -16382] // subnormal + [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal + [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063], + [T.min_normal, .5, -16381], + [T.min_normal/2.0L, .5, -16382] // subnormal ]; - foreach(elem; extendedvals) + foreach (elem; extendedvals) { T x = elem[0]; T e = elem[1]; - int exp = cast(int)elem[2]; + int exp = cast(int) elem[2]; int eptr; T v = frexp(x, eptr); assert(isIdentical(e, v)); @@ -2486,11 +2651,11 @@ unittest } } -unittest +@safe unittest { - import std.typetuple: TypeTuple; + import std.meta : AliasSeq; void foo() { - foreach (T; TypeTuple!(real, double, float)) + foreach (T; AliasSeq!(real, double, float)) { int exp; const T a = 1; @@ -2505,7 +2670,7 @@ unittest * Extracts the exponent of x as a signed integral value. * * If x is not a special value, the result is the same as - * $(D cast(int)logb(x)). + * $(D cast(int) logb(x)). * * $(TABLE_SV * $(TR $(TH x) $(TH ilogb(x)) $(TH Range error?)) @@ -2514,130 +2679,318 @@ unittest * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no)) * ) */ -int ilogb(real x) @trusted nothrow @nogc +int ilogb(T)(const T x) @trusted pure nothrow @nogc +if (isFloatingPoint!T) { - version (Win64_DMD_InlineAsm) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; - fxam ; - fstsw AX ; - and AH,0x45 ; - cmp AH,0x40 ; - jz Lzeronan ; - cmp AH,5 ; - jz Linfinity ; - cmp AH,1 ; - jz Lzeronan ; - fxtract ; - fstp ST(0) ; - fistp dword ptr 8[RSP] ; - mov EAX,8[RSP] ; - ret ; + import core.bitop : bsr; + alias F = floatTraits!T; - Lzeronan: - mov EAX,0x80000000 ; - fstp ST(0) ; - ret ; + union floatBits + { + T rv; + ushort[T.sizeof/2] vu; + uint[T.sizeof/4] vui; + static if (T.sizeof >= 8) + ulong[T.sizeof/8] vul; + } + floatBits y = void; + y.rv = x; - Linfinity: - mov EAX,0x7FFFFFFF ; - fstp ST(0) ; - ret ; + int ex = y.vu[F.EXPPOS_SHORT] & F.EXPMASK; + static if (F.realFormat == RealFormat.ieeeExtended) + { + if (ex) + { + // If exponent is non-zero + if (ex == F.EXPMASK) // infinity or NaN + { + if (y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) // NaN + return FP_ILOGBNAN; + else // +-infinity + return int.max; + } + else + { + return ex - F.EXPBIAS - 1; + } + } + else if (!y.vul[0]) + { + // vf is +-0.0 + return FP_ILOGB0; + } + else + { + // subnormal + return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(y.vul[0]); } } - else version (CRuntime_Microsoft) + else static if (F.realFormat == RealFormat.ieeeQuadruple) { - int res; - asm pure nothrow @nogc + if (ex) // If exponent is non-zero { - naked ; - fld real ptr [x] ; - fxam ; - fstsw AX ; - and AH,0x45 ; - cmp AH,0x40 ; - jz Lzeronan ; - cmp AH,5 ; - jz Linfinity ; - cmp AH,1 ; - jz Lzeronan ; - fxtract ; - fstp ST(0) ; - fistp res ; - mov EAX,res ; - jmp Ldone ; - - Lzeronan: - mov EAX,0x80000000 ; - fstp ST(0) ; - - Linfinity: - mov EAX,0x7FFFFFFF ; - fstp ST(0) ; - Ldone: ; + if (ex == F.EXPMASK) + { + // infinity or NaN + if (y.vul[MANTISSA_LSB] | ( y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN + return FP_ILOGBNAN; + else // +- infinity + return int.max; + } + else + { + return ex - F.EXPBIAS - 1; + } + } + else if ((y.vul[MANTISSA_LSB] | (y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0) + { + // vf is +-0.0 + return FP_ILOGB0; + } + else + { + // subnormal + const ulong msb = y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF; + const ulong lsb = y.vul[MANTISSA_LSB]; + if (msb) + return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(msb) + 64; + else + return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(lsb); + } + } + else static if (F.realFormat == RealFormat.ieeeDouble) + { + if (ex) // If exponent is non-zero + { + if (ex == F.EXPMASK) // infinity or NaN + { + if ((y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF0_0000_0000_0000) // +- infinity + return int.max; + else // NaN + return FP_ILOGBNAN; + } + else + { + return ((ex - F.EXPBIAS) >> 4) - 1; + } + } + else if (!(y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF)) + { + // vf is +-0.0 + return FP_ILOGB0; + } + else + { + // subnormal + enum MANTISSAMASK_64 = ((cast(ulong) F.MANTISSAMASK_INT) << 32) | 0xFFFF_FFFF; + return ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1 + bsr(y.vul[0] & MANTISSAMASK_64); + } + } + else static if (F.realFormat == RealFormat.ieeeSingle) + { + if (ex) // If exponent is non-zero + { + if (ex == F.EXPMASK) // infinity or NaN + { + if ((y.vui[0] & 0x7FFF_FFFF) == 0x7F80_0000) // +- infinity + return int.max; + else // NaN + return FP_ILOGBNAN; + } + else + { + return ((ex - F.EXPBIAS) >> 7) - 1; + } + } + else if (!(y.vui[0] & 0x7FFF_FFFF)) + { + // vf is +-0.0 + return FP_ILOGB0; + } + else + { + // subnormal + const uint mantissa = y.vui[0] & F.MANTISSAMASK_INT; + return ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1 + bsr(mantissa); } } + else // static if (F.realFormat == RealFormat.ibmExtended) + { + core.stdc.math.ilogbl(x); + } +} +/// ditto +int ilogb(T)(const T x) @safe pure nothrow @nogc +if (isIntegral!T && isUnsigned!T) +{ + import core.bitop : bsr; + if (x == 0) + return FP_ILOGB0; else - return core.stdc.math.ilogbl(x); + { + static assert(T.sizeof <= ulong.sizeof, "integer size too large for the current ilogb implementation"); + return bsr(x); + } +} +/// ditto +int ilogb(T)(const T x) @safe pure nothrow @nogc +if (isIntegral!T && isSigned!T) +{ + import std.traits : Unsigned; + // Note: abs(x) can not be used because the return type is not Unsigned and + // the return value would be wrong for x == int.min + Unsigned!T absx = x >= 0 ? x : -x; + return ilogb(absx); } alias FP_ILOGB0 = core.stdc.math.FP_ILOGB0; alias FP_ILOGBNAN = core.stdc.math.FP_ILOGBNAN; +@system nothrow @nogc unittest +{ + import std.meta : AliasSeq; + import std.typecons : Tuple; + foreach (F; AliasSeq!(float, double, real)) + { + alias T = Tuple!(F, int); + T[13] vals = // x, ilogb(x) + [ + T( F.nan , FP_ILOGBNAN ), + T( -F.nan , FP_ILOGBNAN ), + T( F.infinity, int.max ), + T( -F.infinity, int.max ), + T( 0.0 , FP_ILOGB0 ), + T( -0.0 , FP_ILOGB0 ), + T( 2.0 , 1 ), + T( 2.0001 , 1 ), + T( 1.9999 , 0 ), + T( 0.5 , -1 ), + T( 123.123 , 6 ), + T( -123.123 , 6 ), + T( 0.123 , -4 ), + ]; + + foreach (elem; vals) + { + assert(ilogb(elem[0]) == elem[1]); + } + } + + // min_normal and subnormals + assert(ilogb(-float.min_normal) == -126); + assert(ilogb(nextUp(-float.min_normal)) == -127); + assert(ilogb(nextUp(-float(0.0))) == -149); + assert(ilogb(-double.min_normal) == -1022); + assert(ilogb(nextUp(-double.min_normal)) == -1023); + assert(ilogb(nextUp(-double(0.0))) == -1074); + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) + { + assert(ilogb(-real.min_normal) == -16382); + assert(ilogb(nextUp(-real.min_normal)) == -16383); + assert(ilogb(nextUp(-real(0.0))) == -16445); + } + else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) + { + assert(ilogb(-real.min_normal) == -1022); + assert(ilogb(nextUp(-real.min_normal)) == -1023); + assert(ilogb(nextUp(-real(0.0))) == -1074); + } + + // test integer types + assert(ilogb(0) == FP_ILOGB0); + assert(ilogb(int.max) == 30); + assert(ilogb(int.min) == 31); + assert(ilogb(uint.max) == 31); + assert(ilogb(long.max) == 62); + assert(ilogb(long.min) == 63); + assert(ilogb(ulong.max) == 63); +} /******************************************* * Compute n * 2$(SUPERSCRIPT exp) * References: frexp */ -real ldexp(real n, int exp) @nogc @safe pure nothrow; /* intrinsic */ +real ldexp(real n, int exp) @nogc @safe pure nothrow { pragma(inline, true); return core.math.ldexp(n, exp); } +//FIXME +///ditto +double ldexp(double n, int exp) @safe pure nothrow @nogc { return ldexp(cast(real) n, exp); } +//FIXME +///ditto +float ldexp(float n, int exp) @safe pure nothrow @nogc { return ldexp(cast(real) n, exp); } /// @nogc @safe pure nothrow unittest { - real r; + import std.meta : AliasSeq; + foreach (T; AliasSeq!(float, double, real)) + { + T r; - r = ldexp(3.0L, 3); - assert(r == 24); + r = ldexp(3.0L, 3); + assert(r == 24); - r = ldexp(cast(real) 3.0, cast(int) 3); - assert(r == 24); + r = ldexp(cast(T) 3.0, cast(int) 3); + assert(r == 24); - real n = 3.0; - int exp = 3; - r = ldexp(n, exp); - assert(r == 24); + T n = 3.0; + int exp = 3; + r = ldexp(n, exp); + assert(r == 24); + } } @safe pure nothrow @nogc unittest { static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { - assert(ldexp(1, -16384) == 0x1p-16384L); - assert(ldexp(1, -16382) == 0x1p-16382L); + assert(ldexp(1.0L, -16384) == 0x1p-16384L); + assert(ldexp(1.0L, -16382) == 0x1p-16382L); int x; real n = frexp(0x1p-16384L, x); - assert(n==0.5L); + assert(n == 0.5L); assert(x==-16383); assert(ldexp(n, x)==0x1p-16384L); } else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) { - assert(ldexp(1, -1024) == 0x1p-1024L); - assert(ldexp(1, -1022) == 0x1p-1022L); + assert(ldexp(1.0L, -1024) == 0x1p-1024L); + assert(ldexp(1.0L, -1022) == 0x1p-1022L); int x; real n = frexp(0x1p-1024L, x); - assert(n==0.5L); + assert(n == 0.5L); assert(x==-1023); assert(ldexp(n, x)==0x1p-1024L); } else static assert(false, "Floating point type real not supported"); } -unittest +/* workaround Issue 14718, float parsing depends on platform strtold +typed_allocator.d +@safe pure nothrow @nogc unittest +{ + assert(ldexp(1.0, -1024) == 0x1p-1024); + assert(ldexp(1.0, -1022) == 0x1p-1022); + int x; + double n = frexp(0x1p-1024, x); + assert(n == 0.5); + assert(x==-1023); + assert(ldexp(n, x)==0x1p-1024); +} + +@safe pure nothrow @nogc unittest +{ + assert(ldexp(1.0f, -128) == 0x1p-128f); + assert(ldexp(1.0f, -126) == 0x1p-126f); + int x; + float n = frexp(0x1p-128f, x); + assert(n == 0.5f); + assert(x==-127); + assert(ldexp(n, x)==0x1p-128f); +} +*/ + +@system unittest { static real[3][] vals = // value,exp,ldexp [ @@ -2655,12 +3008,15 @@ unittest for (i = 0; i < vals.length; i++) { real x = vals[i][0]; - int exp = cast(int)vals[i][1]; + int exp = cast(int) vals[i][1]; real z = vals[i][2]; real l = ldexp(x, exp); assert(equalsDigit(z, l, 7)); } + + real function(real, int) pldexp = &ldexp; + assert(pldexp != null); } /************************************** @@ -2676,7 +3032,7 @@ unittest real log(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) - return yl2x(x, LN2); + return core.math.yl2x(x, LN2); else { // Coefficients for log(1 + x) @@ -2736,9 +3092,9 @@ real log(real x) @safe pure nothrow @nogc // Logarithm using log(x) = z + z^^3 P(z) / Q(z), // where z = 2(x - 1)/(x + 1) - if((exp > 2) || (exp < -2)) + if ((exp > 2) || (exp < -2)) { - if(x < SQRT1_2) + if (x < SQRT1_2) { // 2(2x - 1)/(2x + 1) exp -= 1; z = x - 0.5; @@ -2803,7 +3159,7 @@ real log(real x) @safe pure nothrow @nogc real log10(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) - return yl2x(x, LOG2); + return core.math.yl2x(x, LOG2); else { // Coefficients for log(1 + x) @@ -2867,9 +3223,9 @@ real log10(real x) @safe pure nothrow @nogc // Logarithm using log(x) = z + z^^3 P(z) / Q(z), // where z = 2(x - 1)/(x + 1) - if((exp > 2) || (exp < -2)) + if ((exp > 2) || (exp < -2)) { - if(x < SQRT1_2) + if (x < SQRT1_2) { // 2(2x - 1)/(2x + 1) exp -= 1; z = x - 0.5; @@ -2940,8 +3296,8 @@ real log1p(real x) @safe pure nothrow @nogc version(INLINE_YL2X) { // On x87, yl2xp1 is valid if and only if -0.5 <= lg(x) <= 0.5, - // ie if -0.29<=x<=0.414 - return (fabs(x) <= 0.25) ? yl2xp1(x, LN2) : yl2x(x+1, LN2); + // ie if -0.29 <= x <= 0.414 + return (fabs(x) <= 0.25) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2); } else { @@ -2973,7 +3329,7 @@ real log1p(real x) @safe pure nothrow @nogc real log2(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) - return yl2x(x, 1); + return core.math.yl2x(x, 1); else { // Coefficients for log(1 + x) @@ -3029,9 +3385,9 @@ real log2(real x) @safe pure nothrow @nogc // Logarithm using log(x) = z + z^^3 P(z) / Q(z), // where z = 2(x - 1)/(x + 1) - if((exp > 2) || (exp < -2)) + if ((exp > 2) || (exp < -2)) { - if(x < SQRT1_2) + if (x < SQRT1_2) { // 2(2x - 1)/(2x + 1) exp -= 1; z = x - 0.5; @@ -3077,7 +3433,7 @@ real log2(real x) @safe pure nothrow @nogc } /// -unittest +@system unittest { // check if values are equal to 19 decimal digits of precision assert(equalsDigit(log2(1024.0L), 10, 19)); @@ -3183,7 +3539,8 @@ real modf(real x, ref real i) @trusted nothrow @nogc */ real scalbn(real x, int n) @trusted nothrow @nogc { - version(InlineAsm_X86_Any) { + version(InlineAsm_X86_Any) + { // scalbnl is not supported on DMD-Windows, so use asm pure nothrow @nogc. version (Win64) { @@ -3234,7 +3591,7 @@ real cbrt(real x) @trusted nothrow @nogc version (CRuntime_Microsoft) { version (INLINE_YL2X) - return copysign(exp2(yl2x(fabs(x), 1.0L/3.0L)), x); + return copysign(exp2(core.math.yl2x(fabs(x), 1.0L/3.0L)), x); else return core.stdc.math.cbrtl(x); } @@ -3252,14 +3609,19 @@ real cbrt(real x) @trusted nothrow @nogc * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) ) * ) */ -real fabs(real x) @safe pure nothrow @nogc; /* intrinsic */ +real fabs(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.fabs(x); } //FIXME ///ditto -double fabs(double x) @safe pure nothrow @nogc { return fabs(cast(real)x); } +double fabs(double x) @safe pure nothrow @nogc { return fabs(cast(real) x); } //FIXME ///ditto -float fabs(float x) @safe pure nothrow @nogc { return fabs(cast(real)x); } +float fabs(float x) @safe pure nothrow @nogc { return fabs(cast(real) x); } +@safe unittest +{ + real function(real) pfabs = &fabs; + assert(pfabs != null); +} /*********************************************************************** * Calculates the length of the @@ -3334,7 +3696,7 @@ real hypot(real x, real y) @safe pure nothrow @nogc return sqrt(u*u + v*v); } -unittest +@safe unittest { static real[3][] vals = // x,y,hypot [ @@ -3610,32 +3972,135 @@ float floor(float x) @trusted pure nothrow @nogc assert(isNaN(floor(float.init))); } -/****************************************** - * Rounds x to the nearest integer value, using the current rounding - * mode. - * - * Unlike the rint functions, nearbyint does not raise the - * FE_INEXACT exception. +/** + * Round `val` to a multiple of `unit`. `rfunc` specifies the rounding + * function to use; by default this is `rint`, which uses the current + * rounding mode. */ -real nearbyint(real x) @trusted nothrow @nogc +Unqual!F quantize(alias rfunc = rint, F)(const F val, const F unit) +if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F) { - version (CRuntime_Microsoft) + typeof(return) ret = val; + if (unit != 0) { - assert(0); // not implemented in C library + const scaled = val / unit; + if (!scaled.isInfinity) + ret = rfunc(scaled) * unit; } - else - return core.stdc.math.nearbyintl(x); + return ret; } -/********************************** - * Rounds x to the nearest integer value, using the current rounding - * mode. - * If the return value is not equal to x, the FE_INEXACT - * exception is raised. - * $(B nearbyint) performs +/// +@safe pure nothrow @nogc unittest +{ + assert(12345.6789L.quantize(0.01L) == 12345.68L); + assert(12345.6789L.quantize!floor(0.01L) == 12345.67L); + assert(12345.6789L.quantize(22.0L) == 12342.0L); +} + +/// +@safe pure nothrow @nogc unittest +{ + assert(12345.6789L.quantize(0) == 12345.6789L); + assert(12345.6789L.quantize(real.infinity).isNaN); + assert(12345.6789L.quantize(real.nan).isNaN); + assert(real.infinity.quantize(0.01L) == real.infinity); + assert(real.infinity.quantize(real.nan).isNaN); + assert(real.nan.quantize(0.01L).isNaN); + assert(real.nan.quantize(real.infinity).isNaN); + assert(real.nan.quantize(real.nan).isNaN); +} + +/** + * Round `val` to a multiple of `pow(base, exp)`. `rfunc` specifies the + * rounding function to use; by default this is `rint`, which uses the + * current rounding mode. + */ +Unqual!F quantize(real base, alias rfunc = rint, F, E)(const F val, const E exp) +if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F && isIntegral!E) +{ + // TODO: Compile-time optimization for power-of-two bases? + return quantize!rfunc(val, pow(cast(F) base, exp)); +} + +/// ditto +Unqual!F quantize(real base, long exp = 1, alias rfunc = rint, F)(const F val) +if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F) +{ + enum unit = cast(F) pow(base, exp); + return quantize!rfunc(val, unit); +} + +/// +@safe pure nothrow @nogc unittest +{ + assert(12345.6789L.quantize!10(-2) == 12345.68L); + assert(12345.6789L.quantize!(10, -2) == 12345.68L); + assert(12345.6789L.quantize!(10, floor)(-2) == 12345.67L); + assert(12345.6789L.quantize!(10, -2, floor) == 12345.67L); + + assert(12345.6789L.quantize!22(1) == 12342.0L); + assert(12345.6789L.quantize!22 == 12342.0L); +} + +@safe pure nothrow @nogc unittest +{ + import std.meta : AliasSeq; + + foreach (F; AliasSeq!(real, double, float)) + { + const maxL10 = cast(int) F.max.log10.floor; + const maxR10 = pow(cast(F) 10, maxL10); + assert((cast(F) 0.9L * maxR10).quantize!10(maxL10) == maxR10); + assert((cast(F)-0.9L * maxR10).quantize!10(maxL10) == -maxR10); + + assert(F.max.quantize(F.min_normal) == F.max); + assert((-F.max).quantize(F.min_normal) == -F.max); + assert(F.min_normal.quantize(F.max) == 0); + assert((-F.min_normal).quantize(F.max) == 0); + assert(F.min_normal.quantize(F.min_normal) == F.min_normal); + assert((-F.min_normal).quantize(F.min_normal) == -F.min_normal); + } +} + +/****************************************** + * Rounds x to the nearest integer value, using the current rounding + * mode. + * + * Unlike the rint functions, nearbyint does not raise the + * FE_INEXACT exception. + */ +real nearbyint(real x) @trusted nothrow @nogc +{ + version (CRuntime_Microsoft) + { + assert(0); // not implemented in C library + } + else + return core.stdc.math.nearbyintl(x); +} + +/********************************** + * Rounds x to the nearest integer value, using the current rounding + * mode. + * If the return value is not equal to x, the FE_INEXACT + * exception is raised. + * $(B nearbyint) performs * the same operation, but does not set the FE_INEXACT exception. */ -real rint(real x) @safe pure nothrow @nogc; /* intrinsic */ +real rint(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.rint(x); } +//FIXME +///ditto +double rint(double x) @safe pure nothrow @nogc { return rint(cast(real) x); } +//FIXME +///ditto +float rint(float x) @safe pure nothrow @nogc { return rint(cast(real) x); } + +@safe unittest +{ + real function(real) print = &rint; + assert(print != null); +} /*************************************** * Rounds x to the nearest integer value, using the current rounding @@ -3689,7 +4154,7 @@ long lrint(real x) @trusted pure nothrow @nogc uint msb = vi[MANTISSA_MSB]; uint lsb = vi[MANTISSA_LSB]; int exp = ((msb >> 20) & 0x7ff) - 0x3ff; - int sign = msb >> 31; + const int sign = msb >> 31; msb &= 0xfffff; msb |= 0x100000; @@ -3700,7 +4165,7 @@ long lrint(real x) @trusted pure nothrow @nogc else { // Adjust x and check result. - real j = sign ? -OF : OF; + const real j = sign ? -OF : OF; x = (j + x) - j; msb = vi[MANTISSA_MSB]; lsb = vi[MANTISSA_LSB]; @@ -3738,12 +4203,12 @@ long lrint(real x) @trusted pure nothrow @nogc // Find the exponent and sign int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; + const int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; if (exp < 63) { // Adjust x and check result. - real j = sign ? -OF : OF; + const real j = sign ? -OF : OF; x = (j + x) - j; exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; @@ -3798,15 +4263,17 @@ long lrint(real x) @trusted pure nothrow @nogc /******************************************* * Return the value of x rounded to the nearest integer. - * If the fractional part of x is exactly 0.5, the return value is rounded to - * the even integer. + * If the fractional part of x is exactly 0.5, the return value is + * rounded away from zero. */ real round(real x) @trusted nothrow @nogc { version (CRuntime_Microsoft) { auto old = FloatingPointControl.getControlState(); - FloatingPointControl.setControlState((old & ~FloatingPointControl.ROUNDING_MASK) | FloatingPointControl.roundToZero); + FloatingPointControl.setControlState( + (old & ~FloatingPointControl.ROUNDING_MASK) | FloatingPointControl.roundToZero + ); x = rint((x >= 0) ? x + 0.5 : x - 0.5); FloatingPointControl.setControlState(old); return x; @@ -3826,7 +4293,7 @@ long lround(real x) @trusted nothrow @nogc version (Posix) return core.stdc.math.llroundl(x); else - assert (0, "lround not implemented"); + assert(0, "lround not implemented"); } version(Posix) @@ -3925,7 +4392,7 @@ real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto version (Posix) return core.stdc.math.remquol(x, y, &n); else - assert (0, "remquo not implemented"); + assert(0, "remquo not implemented"); } /** IEEE exception status flags ('sticky bits') @@ -3951,7 +4418,9 @@ private: UNDERFLOW_MASK = 0x10, OVERFLOW_MASK = 0x08, DIVBYZERO_MASK = 0x04, - INVALID_MASK = 0x01 + INVALID_MASK = 0x01, + + EXCEPTIONS_MASK = 0b11_1111 } // Don't bother about subnormals, they are not supported on most CPUs. // SUBNORMAL_MASK = 0x02; @@ -3973,11 +4442,11 @@ private: // ARM FPSCR is a 32bit register enum : int { - INEXACT_MASK = 0x1000, - UNDERFLOW_MASK = 0x0800, - OVERFLOW_MASK = 0x0400, - DIVBYZERO_MASK = 0x0200, - INVALID_MASK = 0x0100 + INEXACT_MASK = 0x10, + UNDERFLOW_MASK = 0x08, + OVERFLOW_MASK = 0x04, + DIVBYZERO_MASK = 0x02, + INVALID_MASK = 0x01 } } else version(SPARC) @@ -3998,27 +4467,19 @@ private: private: static uint getIeeeFlags() { - version(D_InlineAsm_X86) - { - asm pure nothrow @nogc - { - fstsw AX; - // NOTE: If compiler supports SSE2, need to OR the result with - // the SSE2 status register. - // Clear all irrelevant bits - and EAX, 0x03D; - } - } - else version(D_InlineAsm_X86_64) + version(InlineAsm_X86_Any) { - asm pure nothrow @nogc + ushort sw; + asm pure nothrow @nogc { fstsw sw; } + + // OR the result with the SSE2 status register (MXCSR). + if (haveSSE) { - fstsw AX; - // NOTE: If compiler supports SSE2, need to OR the result with - // the SSE2 status register. - // Clear all irrelevant bits - and RAX, 0x03D; + uint mxcsr; + asm pure nothrow @nogc { stmxcsr mxcsr; } + return (sw | mxcsr) & EXCEPTIONS_MASK; } + else return sw & EXCEPTIONS_MASK; } else version (SPARC) { @@ -4036,7 +4497,7 @@ private: else assert(0, "Not yet supported"); } - static void resetIeeeFlags() + static void resetIeeeFlags() @nogc { version(InlineAsm_X86_Any) { @@ -4044,6 +4505,15 @@ private: { fnclex; } + + // Also clear exception flags in MXCSR, SSE's control register. + if (haveSSE) + { + uint mxcsr; + asm nothrow @nogc { stmxcsr mxcsr; } + mxcsr &= ~EXCEPTIONS_MASK; + asm nothrow @nogc { ldmxcsr mxcsr; } + } } else { @@ -4057,29 +4527,44 @@ private: } } public: - version (IeeeFlagsSupport) { - - /// The result cannot be represented exactly, so rounding occurred. - /// (example: x = sin(0.1); ) - @property bool inexact() { return (flags & INEXACT_MASK) != 0; } - - /// A zero was generated by underflow (example: x = real.min*real.epsilon/2;) - @property bool underflow() { return (flags & UNDERFLOW_MASK) != 0; } - - /// An infinity was generated by overflow (example: x = real.max*2;) - @property bool overflow() { return (flags & OVERFLOW_MASK) != 0; } - - /// An infinity was generated by division by zero (example: x = 3/0.0; ) - @property bool divByZero() { return (flags & DIVBYZERO_MASK) != 0; } - - /// A machine NaN was generated. (example: x = real.infinity * 0.0; ) - @property bool invalid() { return (flags & INVALID_MASK) != 0; } + version (IeeeFlagsSupport) + { + + /** + * The result cannot be represented exactly, so rounding occurred. + * Example: `x = sin(0.1);` + */ + @property bool inexact() const { return (flags & INEXACT_MASK) != 0; } + + /** + * A zero was generated by underflow + * Example: `x = real.min*real.epsilon/2;` + */ + @property bool underflow() const { return (flags & UNDERFLOW_MASK) != 0; } + + /** + * An infinity was generated by overflow + * Example: `x = real.max*2;` + */ + @property bool overflow() const { return (flags & OVERFLOW_MASK) != 0; } + + /** + * An infinity was generated by division by zero + * Example: `x = 3/0.0;` + */ + @property bool divByZero() const { return (flags & DIVBYZERO_MASK) != 0; } + + /** + * A machine NaN was generated. + * Example: `x = real.infinity * 0.0;` + */ + @property bool invalid() const { return (flags & INVALID_MASK) != 0; } } } /// -unittest +@system unittest { static void func() { int a = 10 * 10; @@ -4091,7 +4576,7 @@ unittest assert(!ieeeFlags.divByZero); // Perform a division by zero. a/=0.0L; - assert(a==real.infinity); + assert(a == real.infinity); assert(ieeeFlags.divByZero); // Create a NaN a*=0.0L; @@ -4105,6 +4590,52 @@ unittest assert(ieeeFlags == f); } +@system unittest +{ + import std.meta : AliasSeq; + + static struct Test + { + void delegate() action; + bool function() ieeeCheck; + } + + foreach (T; AliasSeq!(float, double, real)) + { + T x; /* Needs to be here to trick -O. It would optimize away the + calculations if x were local to the function literals. */ + auto tests = [ + Test( + () { x = 1; x += 0.1; }, + () => ieeeFlags.inexact + ), + Test( + () { x = T.min_normal; x /= T.max; }, + () => ieeeFlags.underflow + ), + Test( + () { x = T.max; x += T.max; }, + () => ieeeFlags.overflow + ), + Test( + () { x = 1; x /= 0; }, + () => ieeeFlags.divByZero + ), + Test( + () { x = 0; x /= 0; }, + () => ieeeFlags.invalid + ) + ]; + foreach (test; tests) + { + resetIeeeFlags(); + assert(!test.ieeeCheck()); + test.action(); + assert(test.ieeeCheck()); + } + } +} + version(X86_Any) { version = IeeeFlagsSupport; @@ -4115,9 +4646,9 @@ else version(ARM) } /// Set all of the floating-point status flags to false. -void resetIeeeFlags() { IeeeFlags.resetIeeeFlags(); } +void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); } -/// Return a snapshot of the current state of the floating-point status flags. +/// Returns: snapshot of the current state of the floating-point status flags @property IeeeFlags ieeeFlags() { return IeeeFlags(IeeeFlags.getIeeeFlags()); @@ -4184,8 +4715,8 @@ struct FloatingPointControl enum : RoundingMode { roundToNearest = 0x000000, - roundDown = 0x400000, - roundUp = 0x800000, + roundDown = 0x800000, + roundUp = 0x400000, roundToZero = 0xC00000 } } @@ -4289,7 +4820,7 @@ private: static assert(false, "Architecture not supported"); public: - /// Returns true if the current FPU supports exception trapping + /// Returns: true if the current FPU supports exception trapping @property static bool hasExceptionTraps() @safe nothrow @nogc { version(X86_Any) @@ -4302,7 +4833,7 @@ public: // If exceptions are not supported, we set the bit but read it back as zero // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html setControlState(oldState | (divByZeroException & EXCEPTION_MASK)); - bool result = (getControlState() & EXCEPTION_MASK) != 0; + immutable result = (getControlState() & EXCEPTION_MASK) != 0; setControlState(oldState); return result; } @@ -4339,7 +4870,7 @@ public: setControlState((getControlState() & ~ROUNDING_MASK) | (newMode & ROUNDING_MASK)); } - /// Return the exceptions which are currently enabled (unmasked) + /// Returns: the exceptions which are currently enabled (unmasked) @property static uint enabledExceptions() @nogc { assert(hasExceptionTraps); @@ -4349,7 +4880,7 @@ public: return (getControlState() & EXCEPTION_MASK); } - /// Return the currently active rounding mode + /// Returns: the currently active rounding mode @property static RoundingMode rounding() @nogc { return cast(RoundingMode)(getControlState() & ROUNDING_MASK); @@ -4393,15 +4924,7 @@ private: // Clear all pending exceptions static void clearExceptions() @nogc { - version (InlineAsm_X86_Any) - { - asm nothrow @nogc - { - fclex; - } - } - else - assert(0, "Not yet supported"); + resetIeeeFlags(); } // Read from the control register @@ -4437,24 +4960,33 @@ private: { version (InlineAsm_X86_Any) { - version (Win64) + asm nothrow @nogc { - asm nothrow @nogc - { - naked; - mov 8[RSP],RCX; - fclex; - fldcw 8[RSP]; - ret; - } + fclex; + fldcw newState; } - else + + // Also update MXCSR, SSE's control register. + if (haveSSE) { - asm nothrow @nogc - { - fclex; - fldcw newState; - } + uint mxcsr; + asm nothrow @nogc { stmxcsr mxcsr; } + + /* In the FPU control register, rounding mode is in bits 10 and + 11. In MXCSR it's in bits 13 and 14. */ + enum ROUNDING_MASK_SSE = ROUNDING_MASK << 3; + immutable newRoundingModeSSE = (newState & ROUNDING_MASK) << 3; + mxcsr &= ~ROUNDING_MASK_SSE; // delete old rounding mode + mxcsr |= newRoundingModeSSE; // write new rounding mode + + /* In the FPU control register, masks are bits 0 through 5. + In MXCSR they're 7 through 12. */ + enum EXCEPTION_MASK_SSE = EXCEPTION_MASK << 7; + immutable newExceptionMasks = (newState & EXCEPTION_MASK) << 7; + mxcsr &= ~EXCEPTION_MASK_SSE; // delete old masks + mxcsr |= newExceptionMasks; // write new exception masks + + asm nothrow @nogc { ldmxcsr mxcsr; } } } else @@ -4462,13 +4994,13 @@ private: } } -unittest +@system unittest { void ensureDefaults() { assert(FloatingPointControl.rounding == FloatingPointControl.roundToNearest); - if(FloatingPointControl.hasExceptionTraps) + if (FloatingPointControl.hasExceptionTraps) assert(FloatingPointControl.enabledExceptions == 0); } @@ -4477,14 +5009,17 @@ unittest } ensureDefaults(); + version(D_HardFloat) { - FloatingPointControl ctrl; - ctrl.rounding = FloatingPointControl.roundDown; - assert(FloatingPointControl.rounding == FloatingPointControl.roundDown); + { + FloatingPointControl ctrl; + ctrl.rounding = FloatingPointControl.roundDown; + assert(FloatingPointControl.rounding == FloatingPointControl.roundDown); + } + ensureDefaults(); } - ensureDefaults(); - if(FloatingPointControl.hasExceptionTraps) + if (FloatingPointControl.hasExceptionTraps) { FloatingPointControl ctrl; ctrl.enableExceptions(FloatingPointControl.divByZeroException @@ -4499,39 +5034,84 @@ unittest ensureDefaults(); } +@system unittest // rounding +{ + import std.meta : AliasSeq; + + foreach (T; AliasSeq!(float, double, real)) + { + FloatingPointControl fpctrl; + + fpctrl.rounding = FloatingPointControl.roundUp; + T u = 1; + u += 0.1; + + fpctrl.rounding = FloatingPointControl.roundDown; + T d = 1; + d += 0.1; + + fpctrl.rounding = FloatingPointControl.roundToZero; + T z = 1; + z += 0.1; + + assert(u > d); + assert(z == d); + + fpctrl.rounding = FloatingPointControl.roundUp; + u = -1; + u -= 0.1; + + fpctrl.rounding = FloatingPointControl.roundDown; + d = -1; + d -= 0.1; + + fpctrl.rounding = FloatingPointControl.roundToZero; + z = -1; + z -= 0.1; + + assert(u > d); + assert(z == u); + } +} + /********************************* - * Returns !=0 if e is a NaN. + * Determines if $(D_PARAM x) is NaN. + * params: + * x = a floating point number. + * returns: + * $(D true) if $(D_PARAM x) is Nan. */ bool isNaN(X)(X x) @nogc @trusted pure nothrow - if (isFloatingPoint!(X)) +if (isFloatingPoint!(X)) { alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { - uint* p = cast(uint *)&x; - return ((*p & 0x7F80_0000) == 0x7F80_0000) - && *p & 0x007F_FFFF; // not infinity + const uint p = *cast(uint *)&x; + return ((p & 0x7F80_0000) == 0x7F80_0000) + && p & 0x007F_FFFF; // not infinity } else static if (F.realFormat == RealFormat.ieeeDouble) { - ulong* p = cast(ulong *)&x; - return ((*p & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000) - && *p & 0x000F_FFFF_FFFF_FFFF; // not infinity + const ulong p = *cast(ulong *)&x; + return ((p & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000) + && p & 0x000F_FFFF_FFFF_FFFF; // not infinity } else static if (F.realFormat == RealFormat.ieeeExtended) { - ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - ulong* ps = cast(ulong *)&x; + const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; + const ulong ps = *cast(ulong *)&x; return e == F.EXPMASK && - *ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity + ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity } else static if (F.realFormat == RealFormat.ieeeQuadruple) { - ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - ulong* ps = cast(ulong *)&x; + const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; + const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB]; + const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB]; return e == F.EXPMASK && - (ps[MANTISSA_LSB] | (ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0; + (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0; } else { @@ -4546,22 +5126,15 @@ bool isNaN(X)(X x) @nogc @trusted pure nothrow assert( isNaN(-double.init)); assert( isNaN(real.nan)); assert( isNaN(-real.nan)); - assert(!isNaN(cast(float)53.6)); + assert(!isNaN(cast(float) 53.6)); assert(!isNaN(cast(real)-53.6)); } -deprecated("isNaN is not defined for integer types") -bool isNaN(X)(X x) @nogc @trusted pure nothrow - if (isIntegral!(X)) -{ - return isNaN(cast(float)x); -} - @safe pure nothrow @nogc unittest { - import std.typetuple; + import std.meta : AliasSeq; - foreach(T; TypeTuple!(float, double, real)) + foreach (T; AliasSeq!(float, double, real)) { // CTFE-able tests assert(isNaN(T.init)); @@ -4570,7 +5143,7 @@ bool isNaN(X)(X x) @nogc @trusted pure nothrow assert(isNaN(-T.nan)); assert(!isNaN(T.infinity)); assert(!isNaN(-T.infinity)); - assert(!isNaN(cast(T)53.6)); + assert(!isNaN(cast(T) 53.6)); assert(!isNaN(cast(T)-53.6)); // Runtime tests @@ -4584,19 +5157,23 @@ bool isNaN(X)(X x) @nogc @trusted pure nothrow f = T.infinity; assert(!isNaN(f)); assert(!isNaN(-f)); - f = cast(T)53.6; + f = cast(T) 53.6; assert(!isNaN(f)); assert(!isNaN(-f)); } } /********************************* - * Returns !=0 if e is finite (not infinite or $(NAN)). + * Determines if $(D_PARAM x) is finite. + * params: + * x = a floating point number. + * returns: + * $(D true) if $(D_PARAM x) is finite. */ -int isFinite(X)(X e) @trusted pure nothrow @nogc +bool isFinite(X)(X x) @trusted pure nothrow @nogc { alias F = floatTraits!(X); - ushort* pe = cast(ushort *)&e; + ushort* pe = cast(ushort *)&x; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK; } @@ -4625,21 +5202,22 @@ int isFinite(X)(X e) @trusted pure nothrow @nogc assert(!isFinite(real.infinity)); } -deprecated("isFinite is not defined for integer types") -int isFinite(X)(X x) @trusted pure nothrow @nogc - if (isIntegral!(X)) -{ - return isFinite(cast(float)x); -} /********************************* - * Returns !=0 if x is normalized (not zero, subnormal, infinite, or $(NAN)). + * Determines if $(D_PARAM x) is normalized. + * + * A normalized number must not be zero, subnormal, infinite nor $(NAN). + * + * params: + * x = a floating point number. + * returns: + * $(D true) if $(D_PARAM x) is normalized. */ /* Need one for each format because subnormal floats might * be converted to normal reals. */ -int isNormal(X)(X x) @trusted pure nothrow @nogc +bool isNormal(X)(X x) @trusted pure nothrow @nogc { alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ibmExtended) @@ -4675,14 +5253,22 @@ int isNormal(X)(X x) @trusted pure nothrow @nogc } /********************************* - * Is number subnormal? (Also called "denormal".) - * Subnormals have a 0 exponent and a 0 most significant mantissa bit. + * Determines if $(D_PARAM x) is subnormal. * - * Need one for each format because subnormal floats might - * be converted to normal reals. + * Subnormals (also known as "denormal number"), have a 0 exponent + * and a 0 most significant mantissa bit. + * + * params: + * x = a floating point number. + * returns: + * $(D true) if $(D_PARAM x) is a denormal number. */ -int isSubnormal(X)(X x) @trusted pure nothrow @nogc +bool isSubnormal(X)(X x) @trusted pure nothrow @nogc { + /* + Need one for each format because subnormal floats might + be converted to normal reals. + */ alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) { @@ -4700,7 +5286,7 @@ int isSubnormal(X)(X x) @trusted pure nothrow @nogc ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; long* ps = cast(long *)&x; return (e == 0 && - (((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF))) != 0)); + ((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0)); } else static if (F.realFormat == RealFormat.ieeeExtended) { @@ -4722,9 +5308,9 @@ int isSubnormal(X)(X x) @trusted pure nothrow @nogc /// @safe pure nothrow @nogc unittest { - import std.typetuple; + import std.meta : AliasSeq; - foreach (T; TypeTuple!(float, double, real)) + foreach (T; AliasSeq!(float, double, real)) { T f; for (f = 1.0; !isSubnormal(f); f /= 2) @@ -4732,18 +5318,15 @@ int isSubnormal(X)(X x) @trusted pure nothrow @nogc } } -deprecated("isSubnormal is not defined for integer types") -int isSubnormal(X)(X x) @trusted pure nothrow @nogc - if (isIntegral!X) -{ - return isSubnormal(cast(double)x); -} - /********************************* - * Return !=0 if e is $(PLUSMN)$(INFIN). + * Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN). + * params: + * x = a floating point number. + * returns: + * $(D true) if $(D_PARAM x) is $(PLUSMN)$(INFIN). */ bool isInfinity(X)(X x) @nogc @trusted pure nothrow - if (isFloatingPoint!(X)) +if (isFloatingPoint!(X)) { alias F = floatTraits!(X); static if (F.realFormat == RealFormat.ieeeSingle) @@ -4757,11 +5340,11 @@ bool isInfinity(X)(X x) @nogc @trusted pure nothrow } else static if (F.realFormat == RealFormat.ieeeExtended) { - ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); - ulong* ps = cast(ulong *)&x; + const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); + const ulong ps = *cast(ulong *)&x; // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. - return e == F.EXPMASK && (*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; + return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; } else static if (F.realFormat == RealFormat.ibmExtended) { @@ -4770,13 +5353,14 @@ bool isInfinity(X)(X x) @nogc @trusted pure nothrow } else static if (F.realFormat == RealFormat.ieeeQuadruple) { - long* ps = cast(long *)&x; - return (ps[MANTISSA_LSB] == 0) - && (ps[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; + const long psLsb = (cast(long *)&x)[MANTISSA_LSB]; + const long psMsb = (cast(long *)&x)[MANTISSA_MSB]; + return (psLsb == 0) + && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; } else { - return (x - 1) == x; + return (x < -X.max) || (X.max < x); } } @@ -4893,7 +5477,6 @@ int signbit(X)(X x) @nogc @trusted pure nothrow /// @nogc @safe pure nothrow unittest { - debug (math) printf("math.signbit.unittest\n"); assert(!signbit(float.nan)); assert(signbit(-float.nan)); assert(!signbit(168.1234f)); @@ -4923,18 +5506,11 @@ int signbit(X)(X x) @nogc @trusted pure nothrow } -deprecated("signbit is not defined for integer types") -int signbit(X)(X x) @nogc @trusted pure nothrow - if (isIntegral!X) -{ - return signbit(cast(float)x); -} - /********************************* * Return a value composed of to with from's sign bit. */ R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc - if (isFloatingPoint!(R) && isFloatingPoint!(X)) +if (isFloatingPoint!(R) && isFloatingPoint!(X)) { ubyte* pto = cast(ubyte *)&to; const ubyte* pfrom = cast(ubyte *)&from; @@ -4948,18 +5524,18 @@ R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc // ditto R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc - if (isIntegral!(X) && isFloatingPoint!(R)) +if (isIntegral!(X) && isFloatingPoint!(R)) { - return copysign(cast(R)to, from); + return copysign(cast(R) to, from); } @safe pure nothrow @nogc unittest { - import std.typetuple; + import std.meta : AliasSeq; - foreach (X; TypeTuple!(float, double, real, int, long)) + foreach (X; AliasSeq!(float, double, real, int, long)) { - foreach (Y; TypeTuple!(float, double, real)) + foreach (Y; AliasSeq!(float, double, real)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 X x = 21; Y y = 23.8; @@ -4989,13 +5565,6 @@ R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc } } -deprecated("copysign : from can't be of integer type") -R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc - if (isIntegral!R) -{ - return copysign(to, cast(float)from); -} - /********************************* Returns $(D -1) if $(D x < 0), $(D x) if $(D x == 0), $(D 1) if $(D x > 0), and $(NAN) if x==$(NAN). @@ -5127,7 +5696,7 @@ ulong getNaNPayload(real x) @trusted pure nothrow @nogc // Make it look like an 80-bit significand. // Skip exponent, and quiet bit m &= 0x0007_FFFF_FFFF_FFFF; - m <<= 10; + m <<= 11; } else static if (F.realFormat == RealFormat.ieeeQuadruple) { @@ -5149,7 +5718,7 @@ ulong getNaNPayload(real x) @trusted pure nothrow @nogc // ignore implicit bit and quiet bit - ulong f = m & 0x3FFF_FF00_0000_0000L; + const ulong f = m & 0x3FFF_FF00_0000_0000L; ulong w = f >>> 40; w |= (m & 0x00FF_FFFF_F800L) << (22 - 11); @@ -5165,20 +5734,20 @@ debug(UnitTest) static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended || floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) { - assert (getNaNPayload(nan4) == 0x789_ABCD_EF12_3456); + assert(getNaNPayload(nan4) == 0x789_ABCD_EF12_3456); } else { - assert (getNaNPayload(nan4) == 0x1_ABCD_EF12_3456); + assert(getNaNPayload(nan4) == 0x1_ABCD_EF12_3456); } double nan5 = nan4; - assert (getNaNPayload(nan5) == 0x1_ABCD_EF12_3456); + assert(getNaNPayload(nan5) == 0x1_ABCD_EF12_3456); float nan6 = nan4; - assert (getNaNPayload(nan6) == 0x12_3456); + assert(getNaNPayload(nan6) == 0x12_3456); nan4 = NaN(0xFABCD); - assert (getNaNPayload(nan4) == 0xFABCD); + assert(getNaNPayload(nan4) == 0xFABCD); nan6 = nan4; - assert (getNaNPayload(nan6) == 0xFABCD); + assert(getNaNPayload(nan6) == 0xFABCD); nan5 = NaN(0x100_0000_0000_3456); assert(getNaNPayload(nan5) == 0x0000_0000_3456); } @@ -5204,7 +5773,7 @@ real nextUp(real x) @trusted pure nothrow @nogc alias F = floatTraits!(real); static if (F.realFormat == RealFormat.ieeeDouble) { - return nextUp(cast(double)x); + return nextUp(cast(double) x); } else static if (F.realFormat == RealFormat.ieeeQuadruple) { @@ -5294,7 +5863,7 @@ real nextUp(real x) @trusted pure nothrow @nogc } else // static if (F.realFormat == RealFormat.ibmExtended) { - assert (0, "nextUp not implemented"); + assert(0, "nextUp not implemented"); } } @@ -5533,8 +6102,9 @@ real fma(real x, real y, real z) @safe pure nothrow @nogc { return (x * y) + z; * Compute the value of x $(SUPERSCRIPT n), where n is an integer */ Unqual!F pow(F, G)(F x, G n) @nogc @trusted pure nothrow - if (isFloatingPoint!(F) && isIntegral!(G)) +if (isFloatingPoint!(F) && isIntegral!(G)) { + import std.traits : Unsigned; real p = 1.0, v = void; Unsigned!(Unqual!G) m = n; if (n < 0) @@ -5622,7 +6192,7 @@ Unqual!F pow(F, G)(F x, G n) @nogc @trusted pure nothrow assert(feqrel(pow(x, neg3), 1 / (x * x * x)) >= real.mant_dig - 1); } -unittest +@system unittest { assert(equalsDigit(pow(2.0L, 10.0L), 1024, 19)); } @@ -5635,7 +6205,7 @@ unittest * regardless of the value of x. */ typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pure nothrow - if (isIntegral!(F) && isIntegral!(G)) +if (isIntegral!(F) && isIntegral!(G)) { if (n<0) return x/0; // Only support positive powers typeof(return) p, v = void; @@ -5658,7 +6228,8 @@ typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pur default: v = x; p = 1; - while (1){ + while (1) + { if (m & 1) p *= v; m >>= 1; @@ -5691,7 +6262,7 @@ typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pur /**Computes integer to floating point powers.*/ real pow(I, F)(I x, F y) @nogc @trusted pure nothrow - if(isIntegral!I && isFloatingPoint!F) +if (isIntegral!I && isFloatingPoint!F) { return pow(cast(real) x, cast(Unqual!F) y); } @@ -5739,7 +6310,7 @@ real pow(I, F)(I x, F y) @nogc @trusted pure nothrow * ) */ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow - if (isFloatingPoint!(F) && isFloatingPoint!(G)) +if (isFloatingPoint!(F) && isFloatingPoint!(G)) { alias Float = typeof(return); @@ -5782,7 +6353,7 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow { if (signbit(x)) { - long i = cast(long)y; + long i = cast(long) y; if (y > 0.0) { if (i == y && i & 1) @@ -5811,7 +6382,7 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow { if (signbit(x)) { - long i = cast(long)y; + long i = cast(long) y; if (y > 0.0) { if (i == y && i & 1) @@ -5862,7 +6433,7 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow } if (x <= -F.max) { - long i = cast(long)y; + long i = cast(long) y; if (y > 0.0) { if (i == y && i & 1) @@ -5880,21 +6451,39 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow } // Integer power of x. - long iy = cast(long)y; - if (iy == y && fabs(y) < 32768.0) + long iy = cast(long) y; + if (iy == y && fabs(y) < 32_768.0) return pow(x, iy); - double sign = 1.0; + real sign = 1.0; if (x < 0) { // Result is real only if y is an integer // Check for a non-zero fractional part - if (y > -1.0 / real.epsilon && y < 1.0 / real.epsilon) + enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L; + static if (maxOdd > ulong.max) { - long w = cast(long)y; - if (w != y) + // Generic method, for any FP type + if (floor(y) != y) return sqrt(x); // Complex result -- create a NaN - if (w & 1) sign = -1.0; + + const hy = ldexp(y, -1); + if (floor(hy) != hy) + sign = -1.0; + } + else + { + // Much faster, if ulong has enough precision + const absY = fabs(y); + if (absY <= maxOdd) + { + const uy = cast(ulong) absY; + if (uy != absY) + return sqrt(x); // Complex result -- create a NaN + + if (uy & 1) + sign = -1.0; + } } x = -x; } @@ -5905,7 +6494,7 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow // (though complicated) method is described in: // "An efficient rounding boundary test for pow(x, y) // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007). - return sign * exp2( yl2x(x, y) ); + return sign * exp2( core.math.yl2x(x, y) ); } else { @@ -5970,6 +6559,15 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow assert(isIdentical(pow(0.0, 6.0), 0.0)); assert(isIdentical(pow(-0.0, 6.0), 0.0)); + // Issue #14786 fixed + immutable real maxOdd = pow(2.0L, real.mant_dig) - 1.0L; + assert(pow(-1.0L, maxOdd) == -1.0L); + assert(pow(-1.0L, -maxOdd) == -1.0L); + assert(pow(-1.0L, maxOdd + 1.0L) == 1.0L); + assert(pow(-1.0L, -maxOdd + 1.0L) == 1.0L); + assert(pow(-1.0L, maxOdd - 1.0L) == 1.0L); + assert(pow(-1.0L, -maxOdd - 1.0L) == 1.0L); + // Now, actual numbers. assert(approxEqual(pow(two, three), 8.0)); assert(approxEqual(pow(two, -2.5), 0.1767767)); @@ -5995,7 +6593,7 @@ Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow * ) */ int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc - if (isFloatingPoint!(X)) +if (isFloatingPoint!(X)) { /* Public Domain. Author: Don Clugston, 18 Aug 2005. */ @@ -6016,13 +6614,13 @@ int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc } else { - static assert (F.realFormat == RealFormat.ieeeSingle + static assert(F.realFormat == RealFormat.ieeeSingle || F.realFormat == RealFormat.ieeeDouble || F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeQuadruple); if (x == y) - return X.mant_dig; // ensure diff!=0, cope with INF. + return X.mant_dig; // ensure diff != 0, cope with INF. Unqual!X diff = fabs(x - y); @@ -6038,55 +6636,31 @@ int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc // AND with 0x7FFF to form the absolute value. // To avoid out-by-1 errors, we subtract 1 so it rounds down // if the exponents were different. This means 'bitsdiff' is - // always 1 lower than we want, except that if bitsdiff==0, + // always 1 lower than we want, except that if bitsdiff == 0, // they could have 0 or 1 bits in common. - static if (F.realFormat == RealFormat.ieeeExtended - || F.realFormat == RealFormat.ieeeQuadruple) - { - int bitsdiff = ( ((pa[F.EXPPOS_SHORT] & F.EXPMASK) - + (pb[F.EXPPOS_SHORT] & F.EXPMASK) - 1) >> 1) - - pd[F.EXPPOS_SHORT]; - } - else static if (F.realFormat == RealFormat.ieeeDouble) - { - int bitsdiff = (( ((pa[F.EXPPOS_SHORT]&0x7FF0) - + (pb[F.EXPPOS_SHORT]&0x7FF0)-0x10)>>1) - - (pd[F.EXPPOS_SHORT]&0x7FF0))>>4; - } - else static if (F.realFormat == RealFormat.ieeeSingle) - { - int bitsdiff = (( ((pa[F.EXPPOS_SHORT]&0x7F80) - + (pb[F.EXPPOS_SHORT]&0x7F80)-0x80)>>1) - - (pd[F.EXPPOS_SHORT]&0x7F80))>>7; - } + int bitsdiff = ((( (pa[F.EXPPOS_SHORT] & F.EXPMASK) + + (pb[F.EXPPOS_SHORT] & F.EXPMASK) + - (1 << F.EXPSHIFT)) >> 1) + - (pd[F.EXPPOS_SHORT] & F.EXPMASK)) >> F.EXPSHIFT; if ( (pd[F.EXPPOS_SHORT] & F.EXPMASK) == 0) { // Difference is subnormal // For subnormals, we need to add the number of zeros that // lie at the start of diff's significand. // We do this by multiplying by 2^^real.mant_dig diff *= F.RECIP_EPSILON; - return bitsdiff + X.mant_dig - pd[F.EXPPOS_SHORT]; + return bitsdiff + X.mant_dig - ((pd[F.EXPPOS_SHORT] & F.EXPMASK) >> F.EXPSHIFT); } if (bitsdiff > 0) return bitsdiff + 1; // add the 1 we subtracted before // Avoid out-by-1 errors when factor is almost 2. - static if (F.realFormat == RealFormat.ieeeExtended - || F.realFormat == RealFormat.ieeeQuadruple) + if (bitsdiff == 0 + && ((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK) == 0) { - return (bitsdiff == 0) ? (pa[F.EXPPOS_SHORT] == pb[F.EXPPOS_SHORT]) : 0; - } - else static if (F.realFormat == RealFormat.ieeeDouble - || F.realFormat == RealFormat.ieeeSingle) - { - if (bitsdiff == 0 - && !((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK)) - { - return 1; - } else return 0; - } + return 1; + } else return 0; } } @@ -6134,6 +6708,7 @@ int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc assert(feqrel(F.infinity, -F.infinity) == 0); assert(feqrel(F.max, -F.max) == 0); + assert(feqrel(F.min_normal / 8, F.min_normal / 17) == 3); const F Const = 2; immutable F Immutable = 2; @@ -6141,10 +6716,6 @@ int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc } assert(feqrel(7.1824L, 7.1824L) == real.mant_dig); - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) - { - assert(feqrel(real.min_normal / 8, real.min_normal / 17) == 3); - } testFeqrel!(real)(); testFeqrel!(double)(); @@ -6178,7 +6749,7 @@ body { // Runtime behaviour for contract violation: // If signs are opposite, or one is a NaN, return 0. - if (!((x>=0 && y>=0) || (x<=0 && y<=0))) return 0.0; + if (!((x >= 0 && y >= 0) || (x <= 0 && y <= 0))) return 0.0; // The implementation is simple: cast x and y to integers, // average them (avoiding overflow), and cast the result back to a floating-point number. @@ -6209,7 +6780,7 @@ body m &= 0x7FFF_FFFF_FFFF_FFFFL; } // Now do a multi-byte right shift - uint c = e & 1; // carry + const uint c = e & 1; // carry e >>= 1; m >>>= 1; if (c) @@ -6309,14 +6880,14 @@ public: * A = array of coefficients $(SUB a, 0), $(SUB a, 1), etc. */ Unqual!(CommonType!(T1, T2)) poly(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc - if (isFloatingPoint!T1 && isFloatingPoint!T2) +if (isFloatingPoint!T1 && isFloatingPoint!T2) in { assert(A.length > 0); } body { - static if(is(Unqual!T2 == real)) + static if (is(Unqual!T2 == real)) { return polyImpl(x, A); } @@ -6329,7 +6900,7 @@ body /// @safe nothrow @nogc unittest { - double x = 3.1; + real x = 3.1; static real[] pp = [56.1, 32.7, 6]; assert(poly(x, pp) == (56.1L + (32.7L + 6.0L * x) * x)); @@ -6347,12 +6918,13 @@ body assert(poly(x, pp) == y); } -unittest { +@safe unittest +{ static assert(poly(3.0, [1.0, 2.0, 3.0]) == 34); } private Unqual!(CommonType!(T1, T2)) polyImplBase(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc - if (isFloatingPoint!T1 && isFloatingPoint!T2) +if (isFloatingPoint!T1 && isFloatingPoint!T2) { ptrdiff_t i = A.length - 1; typeof(return) r = A[i]; @@ -6368,7 +6940,7 @@ private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc { version (D_InlineAsm_X86) { - if(__ctfe) + if (__ctfe) { return polyImplBase(x, A); } @@ -6485,7 +7057,7 @@ private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc ; } } - else version (Android) + else version (Solaris) { asm pure nothrow @nogc // assembler by W. Bright { @@ -6526,17 +7098,27 @@ private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc /** - Computes whether $(D lhs) is approximately equal to $(D rhs) - admitting a maximum relative difference $(D maxRelDiff) and a - maximum absolute difference $(D maxAbsDiff). - - If the two inputs are ranges, $(D approxEqual) returns true if and - only if the ranges have the same number of elements and if $(D - approxEqual) evaluates to $(D true) for each pair of elements. + Computes whether two values are approximately equal, admitting a maximum + relative difference, and a maximum absolute difference. + + Params: + lhs = First item to compare. + rhs = Second item to compare. + maxRelDiff = Maximum allowable difference relative to `rhs`. + maxAbsDiff = Maximum absolute difference. + + Returns: + `true` if the two items are approximately equal under either criterium. + If one item is a range, and the other is a single value, then the result + is the logical and-ing of calling `approxEqual` on each element of the + ranged item against the single item. If both items are ranges, then + `approxEqual` returns `true` if and only if the ranges have the same + number of elements and if `approxEqual` evaluates to `true` for each + pair of elements. */ bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-5) { - import std.range; + import std.range.primitives : empty, front, isInputRange, popFront; static if (isInputRange!T) { static if (isInputRange!U) @@ -6550,6 +7132,11 @@ bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-5) return false; } } + else static if (isIntegral!U) + { + // convert rhs to real + return approxEqual(lhs, real(rhs), maxRelDiff, maxAbsDiff); + } else { // lhs is range, rhs is number @@ -6565,8 +7152,18 @@ bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-5) { static if (isInputRange!U) { - // lhs is number, rhs is array - return approxEqual(rhs, lhs, maxRelDiff, maxAbsDiff); + // lhs is number, rhs is range + for (; !rhs.empty; rhs.popFront()) + { + if (!approxEqual(lhs, rhs.front, maxRelDiff, maxAbsDiff)) + return false; + } + return true; + } + else static if (isIntegral!T || isIntegral!U) + { + // convert both lhs and rhs to real + return approxEqual(real(lhs), real(rhs), maxRelDiff, maxAbsDiff); } else { @@ -6610,30 +7207,14 @@ bool approxEqual(T, U)(T lhs, U rhs) num = -real.infinity; assert(num == -real.infinity); // Passes. assert(approxEqual(num, -real.infinity)); // Fails. -} - -// Included for backwards compatibility with Phobos1 -deprecated("Phobos1 math functions are deprecated, use isNaN") alias isnan = isNaN; -deprecated("Phobos1 math functions are deprecated, use isFinite ") alias isfinite = isFinite; -deprecated("Phobos1 math functions are deprecated, use isNormal ") alias isnormal = isNormal; -deprecated("Phobos1 math functions are deprecated, use isSubnormal ") alias issubnormal = isSubnormal; -deprecated("Phobos1 math functions are deprecated, use isInfinity ") alias isinf = isInfinity; - -/* ********************************** - * Building block functions, they - * translate to a single x87 instruction. - */ - -real yl2x(real x, real y) @nogc @safe pure nothrow; // y * log2(x) -real yl2xp1(real x, real y) @nogc @safe pure nothrow; // y * log2(x + 1) -@safe pure nothrow @nogc unittest -{ - version (INLINE_YL2X) - { - assert(yl2x(1024, 1) == 10); - assert(yl2xp1(1023, 1) == 10); - } + assert(!approxEqual(3, 0)); + assert(approxEqual(3, 3)); + assert(approxEqual(3.0, 3)); + assert(approxEqual([3, 3, 3], 3.0)); + assert(approxEqual([3.0, 3.0, 3.0], 3)); + int a = 10; + assert(approxEqual(10, a)); } @safe pure nothrow @nogc unittest @@ -6710,3 +7291,638 @@ real yl2xp1(real x, real y) @nogc @safe pure nothrow; // y * log2(x + 1) auto x = floor(1.2); auto y = ceil(1.2); } + +@safe pure nothrow unittest +{ + // relative comparison depends on rhs, make sure proper side is used when + // comparing range to single value. Based on bugzilla issue 15763 + auto a = [2e-3 - 1e-5]; + auto b = 2e-3 + 1e-5; + assert(a[0].approxEqual(b)); + assert(!b.approxEqual(a[0])); + assert(a.approxEqual(b)); + assert(!b.approxEqual(a)); +} + +/*********************************** + * Defines a total order on all floating-point numbers. + * + * The order is defined as follows: + * $(UL + * $(LI All numbers in [-$(INFIN), +$(INFIN)] are ordered + * the same way as by built-in comparison, with the exception of + * -0.0, which is less than +0.0;) + * $(LI If the sign bit is set (that is, it's 'negative'), $(NAN) is less + * than any number; if the sign bit is not set (it is 'positive'), + * $(NAN) is greater than any number;) + * $(LI $(NAN)s of the same sign are ordered by the payload ('negative' + * ones - in reverse order).) + * ) + * + * Returns: + * negative value if $(D x) precedes $(D y) in the order specified above; + * 0 if $(D x) and $(D y) are identical, and positive value otherwise. + * + * See_Also: + * $(MYREF isIdentical) + * Standards: Conforms to IEEE 754-2008 + */ +int cmp(T)(const(T) x, const(T) y) @nogc @trusted pure nothrow +if (isFloatingPoint!T) +{ + alias F = floatTraits!T; + + static if (F.realFormat == RealFormat.ieeeSingle + || F.realFormat == RealFormat.ieeeDouble) + { + static if (T.sizeof == 4) + alias UInt = uint; + else + alias UInt = ulong; + + union Repainter + { + T number; + UInt bits; + } + + enum msb = ~(UInt.max >>> 1); + + import std.typecons : Tuple; + Tuple!(Repainter, Repainter) vars = void; + vars[0].number = x; + vars[1].number = y; + + foreach (ref var; vars) + if (var.bits & msb) + var.bits = ~var.bits; + else + var.bits |= msb; + + if (vars[0].bits < vars[1].bits) + return -1; + else if (vars[0].bits > vars[1].bits) + return 1; + else + return 0; + } + else static if (F.realFormat == RealFormat.ieeeExtended53 + || F.realFormat == RealFormat.ieeeExtended + || F.realFormat == RealFormat.ieeeQuadruple) + { + static if (F.realFormat == RealFormat.ieeeQuadruple) + alias RemT = ulong; + else + alias RemT = ushort; + + struct Bits + { + ulong bulk; + RemT rem; + } + + union Repainter + { + T number; + Bits bits; + ubyte[T.sizeof] bytes; + } + + import std.typecons : Tuple; + Tuple!(Repainter, Repainter) vars = void; + vars[0].number = x; + vars[1].number = y; + + foreach (ref var; vars) + if (var.bytes[F.SIGNPOS_BYTE] & 0x80) + { + var.bits.bulk = ~var.bits.bulk; + var.bits.rem = ~var.bits.rem; + } + else + { + var.bytes[F.SIGNPOS_BYTE] |= 0x80; + } + + version(LittleEndian) + { + if (vars[0].bits.rem < vars[1].bits.rem) + return -1; + else if (vars[0].bits.rem > vars[1].bits.rem) + return 1; + else if (vars[0].bits.bulk < vars[1].bits.bulk) + return -1; + else if (vars[0].bits.bulk > vars[1].bits.bulk) + return 1; + else + return 0; + } + else + { + if (vars[0].bits.bulk < vars[1].bits.bulk) + return -1; + else if (vars[0].bits.bulk > vars[1].bits.bulk) + return 1; + else if (vars[0].bits.rem < vars[1].bits.rem) + return -1; + else if (vars[0].bits.rem > vars[1].bits.rem) + return 1; + else + return 0; + } + } + else + { + // IBM Extended doubledouble does not follow the general + // sign-exponent-significand layout, so has to be handled generically + + const int xSign = signbit(x), + ySign = signbit(y); + + if (xSign == 1 && ySign == 1) + return cmp(-y, -x); + else if (xSign == 1) + return -1; + else if (ySign == 1) + return 1; + else if (x < y) + return -1; + else if (x == y) + return 0; + else if (x > y) + return 1; + else if (isNaN(x) && !isNaN(y)) + return 1; + else if (isNaN(y) && !isNaN(x)) + return -1; + else if (getNaNPayload(x) < getNaNPayload(y)) + return -1; + else if (getNaNPayload(x) > getNaNPayload(y)) + return 1; + else + return 0; + } +} + +/// Most numbers are ordered naturally. +@safe unittest +{ + assert(cmp(-double.infinity, -double.max) < 0); + assert(cmp(-double.max, -100.0) < 0); + assert(cmp(-100.0, -0.5) < 0); + assert(cmp(-0.5, 0.0) < 0); + assert(cmp(0.0, 0.5) < 0); + assert(cmp(0.5, 100.0) < 0); + assert(cmp(100.0, double.max) < 0); + assert(cmp(double.max, double.infinity) < 0); + + assert(cmp(1.0, 1.0) == 0); +} + +/// Positive and negative zeroes are distinct. +@safe unittest +{ + assert(cmp(-0.0, +0.0) < 0); + assert(cmp(+0.0, -0.0) > 0); +} + +/// Depending on the sign, $(NAN)s go to either end of the spectrum. +@safe unittest +{ + assert(cmp(-double.nan, -double.infinity) < 0); + assert(cmp(double.infinity, double.nan) < 0); + assert(cmp(-double.nan, double.nan) < 0); +} + +/// $(NAN)s of the same sign are ordered by the payload. +@safe unittest +{ + assert(cmp(NaN(10), NaN(20)) < 0); + assert(cmp(-NaN(20), -NaN(10)) < 0); +} + +@safe unittest +{ + import std.meta : AliasSeq; + foreach (T; AliasSeq!(float, double, real)) + { + T[] values = [-cast(T) NaN(20), -cast(T) NaN(10), -T.nan, -T.infinity, + -T.max, -T.max / 2, T(-16.0), T(-1.0).nextDown, + T(-1.0), T(-1.0).nextUp, + T(-0.5), -T.min_normal, (-T.min_normal).nextUp, + -2 * T.min_normal * T.epsilon, + -T.min_normal * T.epsilon, + T(-0.0), T(0.0), + T.min_normal * T.epsilon, + 2 * T.min_normal * T.epsilon, + T.min_normal.nextDown, T.min_normal, T(0.5), + T(1.0).nextDown, T(1.0), + T(1.0).nextUp, T(16.0), T.max / 2, T.max, + T.infinity, T.nan, cast(T) NaN(10), cast(T) NaN(20)]; + + foreach (i, x; values) + { + foreach (y; values[i + 1 .. $]) + { + assert(cmp(x, y) < 0); + assert(cmp(y, x) > 0); + } + assert(cmp(x, x) == 0); + } + } +} + +private enum PowType +{ + floor, + ceil +} + +pragma(inline, true) +private T powIntegralImpl(PowType type, T)(T val) +{ + import core.bitop : bsr; + + if (val == 0 || (type == PowType.ceil && (val > T.max / 2 || val == T.min))) + return 0; + else + { + static if (isSigned!T) + return cast(Unqual!T) (val < 0 ? -(T(1) << bsr(-val) + type) : T(1) << bsr(val) + type); + else + return cast(Unqual!T) (T(1) << bsr(val) + type); + } +} + +private T powFloatingPointImpl(PowType type, T)(T x) +{ + if (!x.isFinite) + return x; + + if (!x) + return x; + + int exp; + auto y = frexp(x, exp); + + static if (type == PowType.ceil) + y = ldexp(cast(T) 0.5, exp + 1); + else + y = ldexp(cast(T) 0.5, exp); + + if (!y.isFinite) + return cast(T) 0.0; + + y = copysign(y, x); + + return y; +} + +/** + * Gives the next power of two after $(D val). `T` can be any built-in + * numerical type. + * + * If the operation would lead to an over/underflow, this function will + * return `0`. + * + * Params: + * val = any number + * + * Returns: + * the next power of two after $(D val) + */ +T nextPow2(T)(const T val) +if (isIntegral!T) +{ + return powIntegralImpl!(PowType.ceil)(val); +} + +/// ditto +T nextPow2(T)(const T val) +if (isFloatingPoint!T) +{ + return powFloatingPointImpl!(PowType.ceil)(val); +} + +/// +@safe @nogc pure nothrow unittest +{ + assert(nextPow2(2) == 4); + assert(nextPow2(10) == 16); + assert(nextPow2(4000) == 4096); + + assert(nextPow2(-2) == -4); + assert(nextPow2(-10) == -16); + + assert(nextPow2(uint.max) == 0); + assert(nextPow2(uint.min) == 0); + assert(nextPow2(size_t.max) == 0); + assert(nextPow2(size_t.min) == 0); + + assert(nextPow2(int.max) == 0); + assert(nextPow2(int.min) == 0); + assert(nextPow2(long.max) == 0); + assert(nextPow2(long.min) == 0); +} + +/// +@safe @nogc pure nothrow unittest +{ + assert(nextPow2(2.1) == 4.0); + assert(nextPow2(-2.0) == -4.0); + assert(nextPow2(0.25) == 0.5); + assert(nextPow2(-4.0) == -8.0); + + assert(nextPow2(double.max) == 0.0); + assert(nextPow2(double.infinity) == double.infinity); +} + +@safe @nogc pure nothrow unittest +{ + assert(nextPow2(ubyte(2)) == 4); + assert(nextPow2(ubyte(10)) == 16); + + assert(nextPow2(byte(2)) == 4); + assert(nextPow2(byte(10)) == 16); + + assert(nextPow2(short(2)) == 4); + assert(nextPow2(short(10)) == 16); + assert(nextPow2(short(4000)) == 4096); + + assert(nextPow2(ushort(2)) == 4); + assert(nextPow2(ushort(10)) == 16); + assert(nextPow2(ushort(4000)) == 4096); +} + +@safe @nogc pure nothrow unittest +{ + foreach (ulong i; 1 .. 62) + { + assert(nextPow2(1UL << i) == 2UL << i); + assert(nextPow2((1UL << i) - 1) == 1UL << i); + assert(nextPow2((1UL << i) + 1) == 2UL << i); + assert(nextPow2((1UL << i) + (1UL<<(i-1))) == 2UL << i); + } +} + +@safe @nogc pure nothrow unittest +{ + import std.meta : AliasSeq; + + foreach (T; AliasSeq!(float, double, real)) + { + enum T subNormal = T.min_normal / 2; + + static if (subNormal) assert(nextPow2(subNormal) == T.min_normal); + + assert(nextPow2(T(0.0)) == 0.0); + + assert(nextPow2(T(2.0)) == 4.0); + assert(nextPow2(T(2.1)) == 4.0); + assert(nextPow2(T(3.1)) == 4.0); + assert(nextPow2(T(4.0)) == 8.0); + assert(nextPow2(T(0.25)) == 0.5); + + assert(nextPow2(T(-2.0)) == -4.0); + assert(nextPow2(T(-2.1)) == -4.0); + assert(nextPow2(T(-3.1)) == -4.0); + assert(nextPow2(T(-4.0)) == -8.0); + assert(nextPow2(T(-0.25)) == -0.5); + + assert(nextPow2(T.max) == 0); + assert(nextPow2(-T.max) == 0); + + assert(nextPow2(T.infinity) == T.infinity); + assert(nextPow2(T.init).isNaN); + } +} + +@safe @nogc pure nothrow unittest // Issue 15973 +{ + assert(nextPow2(uint.max / 2) == uint.max / 2 + 1); + assert(nextPow2(uint.max / 2 + 2) == 0); + assert(nextPow2(int.max / 2) == int.max / 2 + 1); + assert(nextPow2(int.max / 2 + 2) == 0); + assert(nextPow2(int.min + 1) == int.min); +} + +/** + * Gives the last power of two before $(D val). $(T) can be any built-in + * numerical type. + * + * Params: + * val = any number + * + * Returns: + * the last power of two before $(D val) + */ +T truncPow2(T)(const T val) +if (isIntegral!T) +{ + return powIntegralImpl!(PowType.floor)(val); +} + +/// ditto +T truncPow2(T)(const T val) +if (isFloatingPoint!T) +{ + return powFloatingPointImpl!(PowType.floor)(val); +} + +/// +@safe @nogc pure nothrow unittest +{ + assert(truncPow2(3) == 2); + assert(truncPow2(4) == 4); + assert(truncPow2(10) == 8); + assert(truncPow2(4000) == 2048); + + assert(truncPow2(-5) == -4); + assert(truncPow2(-20) == -16); + + assert(truncPow2(uint.max) == int.max + 1); + assert(truncPow2(uint.min) == 0); + assert(truncPow2(ulong.max) == long.max + 1); + assert(truncPow2(ulong.min) == 0); + + assert(truncPow2(int.max) == (int.max / 2) + 1); + assert(truncPow2(int.min) == int.min); + assert(truncPow2(long.max) == (long.max / 2) + 1); + assert(truncPow2(long.min) == long.min); +} + +/// +@safe @nogc pure nothrow unittest +{ + assert(truncPow2(2.1) == 2.0); + assert(truncPow2(7.0) == 4.0); + assert(truncPow2(-1.9) == -1.0); + assert(truncPow2(0.24) == 0.125); + assert(truncPow2(-7.0) == -4.0); + + assert(truncPow2(double.infinity) == double.infinity); +} + +@safe @nogc pure nothrow unittest +{ + assert(truncPow2(ubyte(3)) == 2); + assert(truncPow2(ubyte(4)) == 4); + assert(truncPow2(ubyte(10)) == 8); + + assert(truncPow2(byte(3)) == 2); + assert(truncPow2(byte(4)) == 4); + assert(truncPow2(byte(10)) == 8); + + assert(truncPow2(ushort(3)) == 2); + assert(truncPow2(ushort(4)) == 4); + assert(truncPow2(ushort(10)) == 8); + assert(truncPow2(ushort(4000)) == 2048); + + assert(truncPow2(short(3)) == 2); + assert(truncPow2(short(4)) == 4); + assert(truncPow2(short(10)) == 8); + assert(truncPow2(short(4000)) == 2048); +} + +@safe @nogc pure nothrow unittest +{ + foreach (ulong i; 1 .. 62) + { + assert(truncPow2(2UL << i) == 2UL << i); + assert(truncPow2((2UL << i) + 1) == 2UL << i); + assert(truncPow2((2UL << i) - 1) == 1UL << i); + assert(truncPow2((2UL << i) - (2UL<<(i-1))) == 1UL << i); + } +} + +@safe @nogc pure nothrow unittest +{ + import std.meta : AliasSeq; + + foreach (T; AliasSeq!(float, double, real)) + { + assert(truncPow2(T(0.0)) == 0.0); + + assert(truncPow2(T(4.0)) == 4.0); + assert(truncPow2(T(2.1)) == 2.0); + assert(truncPow2(T(3.5)) == 2.0); + assert(truncPow2(T(7.0)) == 4.0); + assert(truncPow2(T(0.24)) == 0.125); + + assert(truncPow2(T(-2.0)) == -2.0); + assert(truncPow2(T(-2.1)) == -2.0); + assert(truncPow2(T(-3.1)) == -2.0); + assert(truncPow2(T(-7.0)) == -4.0); + assert(truncPow2(T(-0.24)) == -0.125); + + assert(truncPow2(T.infinity) == T.infinity); + assert(truncPow2(T.init).isNaN); + } +} + +/** +Check whether a number is an integer power of two. + +Note that only positive numbers can be integer powers of two. This +function always return `false` if `x` is negative or zero. + +Params: + x = the number to test + +Returns: + `true` if `x` is an integer power of two. +*/ +bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc +if (isNumeric!X) +{ + static if (isFloatingPoint!X) + { + int exp; + const X sig = frexp(x, exp); + + return (exp != int.min) && (sig is cast(X) 0.5L); + } + else + { + static if (isSigned!X) + { + auto y = cast(typeof(x + 0))x; + return y > 0 && !(y & (y - 1)); + } + else + { + auto y = cast(typeof(x + 0u))x; + return (y & -y) > (y - 1); + } + } +} +/// +@safe unittest +{ + assert( isPowerOf2(1.0L)); + assert( isPowerOf2(2.0L)); + assert( isPowerOf2(0.5L)); + assert( isPowerOf2(pow(2.0L, 96))); + assert( isPowerOf2(pow(2.0L, -77))); + + assert(!isPowerOf2(-2.0L)); + assert(!isPowerOf2(-0.5L)); + assert(!isPowerOf2(0.0L)); + assert(!isPowerOf2(4.315)); + assert(!isPowerOf2(1.0L / 3.0L)); + + assert(!isPowerOf2(real.nan)); + assert(!isPowerOf2(real.infinity)); +} +/// +@safe unittest +{ + assert( isPowerOf2(1)); + assert( isPowerOf2(2)); + assert( isPowerOf2(1uL << 63)); + + assert(!isPowerOf2(-4)); + assert(!isPowerOf2(0)); + assert(!isPowerOf2(1337u)); +} + +@safe unittest +{ + import std.meta : AliasSeq; + + immutable smallP2 = pow(2.0L, -62); + immutable bigP2 = pow(2.0L, 50); + immutable smallP7 = pow(7.0L, -35); + immutable bigP7 = pow(7.0L, 30); + + foreach (X; AliasSeq!(float, double, real)) + { + immutable min_sub = X.min_normal * X.epsilon; + + foreach (x; AliasSeq!(smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L, + 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2)) + { + assert( isPowerOf2(cast(X) x)); + assert(!isPowerOf2(cast(X)-x)); + } + + foreach (x; AliasSeq!(0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity)) + { + assert(!isPowerOf2(cast(X) x)); + assert(!isPowerOf2(cast(X)-x)); + } + } + + foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) + { + foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1]) + { + assert( isPowerOf2(cast(X) x)); + static if (isSigned!X) + assert(!isPowerOf2(cast(X)-x)); + } + + foreach (x; [0, 3, 5, 13, 77, X.min, X.max]) + assert(!isPowerOf2(cast(X) x)); + } +} diff --git a/std/mathspecial.d b/std/mathspecial.d index 353642faf26..896b035c12b 100644 --- a/std/mathspecial.d +++ b/std/mathspecial.d @@ -20,9 +20,7 @@ * is not yet finalized and will probably change. * * Macros: - * WIKI = Phobos/StdMathSpecial - * - * TABLE_SV = + * TABLE_SV =
* * $0
Special Values
* SVH = $(TR $(TH $1) $(TH $2)) @@ -50,7 +48,7 @@ * * Copyright: Based on the CEPHES math library, which is * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Stephen L. Moshier (original C code). Conversion to D by Don Clugston * Source: $(PHOBOSSRC std/_mathspecial.d) */ @@ -130,13 +128,15 @@ real sgnGamma(real x) return real.nan; } long n = rndtol(x); - if (x == n) { + if (x == n) + { return x == 0 ? copysign(1, x) : real.nan; } return n & 1 ? 1.0 : -1.0; } -unittest { +@safe unittest +{ assert(sgnGamma(5.0) == 1.0); assert(isNaN(sgnGamma(-3.0))); assert(sgnGamma(-0.1) == -1.0); @@ -153,12 +153,14 @@ unittest { */ real beta(real x, real y) { - if ((x+y)> MAXGAMMA) { + if ((x+y)> MAXGAMMA) + { return exp(logGamma(x) + logGamma(y) - logGamma(x+y)); } else return gamma(x) * gamma(y) / gamma(x+y); } -unittest { +@safe unittest +{ assert(isIdentical(beta(NaN(0xABC), 4), NaN(0xABC))); assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC))); } @@ -179,7 +181,7 @@ real digamma(real x) /** Log Minus Digamma function * * logmdigamma(x) = log(x) - digamma(x) - * + * * See_Also: $(LREF digamma), $(LREF logmdigammaInverse). */ real logmdigamma(real x) @@ -188,9 +190,9 @@ real logmdigamma(real x) } /** Inverse of the Log Minus Digamma function - * + * * Given y, the function finds x such log(x) - digamma(x) = y. - * + * * See_Also: $(LREF logmdigamma), $(LREF digamma). */ real logmdigammaInverse(real x) @@ -349,7 +351,7 @@ real normalDistribution(real x) */ real normalDistributionInverse(real p) in { - assert(p>=0.0L && p<=1.0L, "Domain error"); + assert(p >= 0.0L && p <= 1.0L, "Domain error"); } body { diff --git a/std/meta.d b/std/meta.d new file mode 100644 index 00000000000..9e7a061f887 --- /dev/null +++ b/std/meta.d @@ -0,0 +1,1619 @@ +// Written in the D programming language. + +/** + * Templates to manipulate template argument lists (also known as type lists). + * + * Some operations on alias sequences are built in to the language, + * such as TL[$(I n)] which gets the $(I n)th type from the + * alias sequence. TL[$(I lwr) .. $(I upr)] returns a new type + * list that is a slice of the old one. + * + * Several templates in this module use or operate on eponymous templates that + * take a single argument and evaluate to a boolean constant. Such templates + * are referred to as $(I template predicates). + * + * $(SCRIPT inhibitQuickIndex = 1;) + * $(DIVC quickindex, + * $(BOOKTABLE , + * $(TR $(TH Category) $(TH Templates)) + * $(TR $(TD Building blocks) $(TD + * $(LREF Alias) + * $(LREF AliasSeq) + * $(LREF aliasSeqOf) + * )) + * $(TR $(TD Alias sequence filtering) $(TD + * $(LREF Erase) + * $(LREF EraseAll) + * $(LREF Filter) + * $(LREF NoDuplicates) + * )) + * $(TR $(TD Alias sequence type hierarchy) $(TD + * $(LREF DerivedToFront) + * $(LREF MostDerived) + * )) + * $(TR $(TD Alias sequence transformation) $(TD + * $(LREF Repeat) + * $(LREF Replace) + * $(LREF ReplaceAll) + * $(LREF Reverse) + * $(LREF staticMap) + * $(LREF staticSort) + * )) + * $(TR $(TD Alias sequence searching) $(TD + * $(LREF allSatisfy) + * $(LREF anySatisfy) + * $(LREF staticIndexOf) + * )) + * $(TR $(TD Template predicates) $(TD + * $(LREF templateAnd) + * $(LREF templateNot) + * $(LREF templateOr) + * $(LREF staticIsSorted) + * )) + * $(TR $(TD Template instantiation) $(TD + * $(LREF ApplyLeft) + * $(LREF ApplyRight) + * )) + * )) + * + * References: + * Based on ideas in Table 3.1 from + * $(LINK2 http://amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768, + * Modern C++ Design), + * Andrei Alexandrescu (Addison-Wesley Professional, 2001) + * Copyright: Copyright Digital Mars 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * $(HTTP digitalmars.com, Walter Bright), + * $(HTTP klickverbot.at, David Nadlinger) + * Source: $(PHOBOSSRC std/_meta.d) + */ + +module std.meta; + +/** + * Creates a sequence of zero or more aliases. This is most commonly + * used as template parameters or arguments. + */ +template AliasSeq(TList...) +{ + alias AliasSeq = TList; +} + +/// +@safe unittest +{ + import std.meta; + alias TL = AliasSeq!(int, double); + + int foo(TL td) // same as int foo(int, double); + { + return td[0] + cast(int) td[1]; + } +} + +/// +@safe unittest +{ + alias TL = AliasSeq!(int, double); + + alias Types = AliasSeq!(TL, char); + static assert(is(Types == AliasSeq!(int, double, char))); +} + + +/** + Returns an `AliasSeq` expression of `Func` being + applied to every variadic template argument. + */ + +/// +@safe unittest +{ + auto ref ArgCall(alias Func, alias arg)() + { + return Func(arg); + } + + template Map(alias Func, args...) + { + static if (args.length > 1) + { + alias Map = AliasSeq!(ArgCall!(Func, args[0]), Map!(Func, args[1 .. $])); + } + else + { + alias Map = ArgCall!(Func, args[0]); + } + } + + static int square(int arg) + { + return arg * arg; + } + + static int refSquare(ref int arg) + { + arg *= arg; + return arg; + } + + static ref int refRetSquare(ref int arg) + { + arg *= arg; + return arg; + } + + static void test(int a, int b) + { + assert(a == 4); + assert(b == 16); + } + + static void testRef(ref int a, ref int b) + { + assert(a++ == 16); + assert(b++ == 256); + } + + static int a = 2; + static int b = 4; + + test(Map!(square, a, b)); + + test(Map!(refSquare, a, b)); + assert(a == 4); + assert(b == 16); + + testRef(Map!(refRetSquare, a, b)); + assert(a == 17); + assert(b == 257); +} + +/** + * Allows `alias`ing of any single symbol, type or compile-time expression. + * + * Not everything can be directly aliased. An alias cannot be declared + * of - for example - a literal: + * + * `alias a = 4; //Error` + * + * With this template any single entity can be aliased: + * + * `alias b = Alias!4; //OK` + * + * See_Also: + * To alias more than one thing at once, use $(LREF AliasSeq) + */ +alias Alias(alias a) = a; + +/// Ditto +alias Alias(T) = T; + +/// +@safe unittest +{ + // Without Alias this would fail if Args[0] was e.g. a value and + // some logic would be needed to detect when to use enum instead + alias Head(Args ...) = Alias!(Args[0]); + alias Tail(Args ...) = Args[1 .. $]; + + alias Blah = AliasSeq!(3, int, "hello"); + static assert(Head!Blah == 3); + static assert(is(Head!(Tail!Blah) == int)); + static assert((Tail!Blah)[1] == "hello"); +} + +/// +@safe unittest +{ + alias a = Alias!(123); + static assert(a == 123); + + enum abc = 1; + alias b = Alias!(abc); + static assert(b == 1); + + alias c = Alias!(3 + 4); + static assert(c == 7); + + alias concat = (s0, s1) => s0 ~ s1; + alias d = Alias!(concat("Hello", " World!")); + static assert(d == "Hello World!"); + + alias e = Alias!(int); + static assert(is(e == int)); + + alias f = Alias!(AliasSeq!(int)); + static assert(!is(typeof(f[0]))); //not an AliasSeq + static assert(is(f == int)); + + auto g = 6; + alias h = Alias!g; + ++h; + assert(g == 7); +} + +package template OldAlias(alias a) +{ + static if (__traits(compiles, { alias x = a; })) + alias OldAlias = a; + else static if (__traits(compiles, { enum x = a; })) + enum OldAlias = a; + else + static assert(0, "Cannot alias " ~ a.stringof); +} + +import std.traits : isAggregateType, Unqual; + +package template OldAlias(T) +if (!isAggregateType!T || is(Unqual!T == T)) +{ + alias OldAlias = T; +} + +@safe unittest +{ + static struct Foo {} + static assert(is(OldAlias!(const(Foo)) == Foo)); + static assert(is(OldAlias!(const(int)) == const(int))); + static assert(OldAlias!123 == 123); + enum abc = 123; + static assert(OldAlias!abc == 123); +} + +/** + * Returns the index of the first occurrence of type T in the + * sequence of zero or more types TList. + * If not found, -1 is returned. + */ +template staticIndexOf(T, TList...) +{ + enum staticIndexOf = genericIndexOf!(T, TList).index; +} + +/// Ditto +template staticIndexOf(alias T, TList...) +{ + enum staticIndexOf = genericIndexOf!(T, TList).index; +} + +/// +@safe unittest +{ + import std.stdio; + + void foo() + { + writefln("The index of long is %s", + staticIndexOf!(long, AliasSeq!(int, long, double))); + // prints: The index of long is 1 + } +} + +// [internal] +private template genericIndexOf(args...) +if (args.length >= 1) +{ + alias e = OldAlias!(args[0]); + alias tuple = args[1 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + + static if (isSame!(e, head)) + { + enum index = 0; + } + else + { + enum next = genericIndexOf!(e, tail).index; + enum index = (next == -1) ? -1 : 1 + next; + } + } + else + { + enum index = -1; + } +} + +@safe unittest +{ + static assert(staticIndexOf!( byte, byte, short, int, long) == 0); + static assert(staticIndexOf!(short, byte, short, int, long) == 1); + static assert(staticIndexOf!( int, byte, short, int, long) == 2); + static assert(staticIndexOf!( long, byte, short, int, long) == 3); + static assert(staticIndexOf!( char, byte, short, int, long) == -1); + static assert(staticIndexOf!( -1, byte, short, int, long) == -1); + static assert(staticIndexOf!(void) == -1); + + static assert(staticIndexOf!("abc", "abc", "def", "ghi", "jkl") == 0); + static assert(staticIndexOf!("def", "abc", "def", "ghi", "jkl") == 1); + static assert(staticIndexOf!("ghi", "abc", "def", "ghi", "jkl") == 2); + static assert(staticIndexOf!("jkl", "abc", "def", "ghi", "jkl") == 3); + static assert(staticIndexOf!("mno", "abc", "def", "ghi", "jkl") == -1); + static assert(staticIndexOf!( void, "abc", "def", "ghi", "jkl") == -1); + static assert(staticIndexOf!(42) == -1); + + static assert(staticIndexOf!(void, 0, "void", void) == 2); + static assert(staticIndexOf!("void", 0, void, "void") == 2); +} + +/** + * Returns a typetuple created from TList with the first occurrence, + * if any, of T removed. + */ +template Erase(T, TList...) +{ + alias Erase = GenericErase!(T, TList).result; +} + +/// Ditto +template Erase(alias T, TList...) +{ + alias Erase = GenericErase!(T, TList).result; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, double, char); + alias TL = Erase!(long, Types); + static assert(is(TL == AliasSeq!(int, double, char))); +} + +// [internal] +private template GenericErase(args...) +if (args.length >= 1) +{ + alias e = OldAlias!(args[0]); + alias tuple = args[1 .. $] ; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + + static if (isSame!(e, head)) + alias result = tail; + else + alias result = AliasSeq!(head, GenericErase!(e, tail).result); + } + else + { + alias result = AliasSeq!(); + } +} + +@safe unittest +{ + static assert(Pack!(Erase!(int, + short, int, int, 4)). + equals!(short, int, 4)); + + static assert(Pack!(Erase!(1, + real, 3, 1, 4, 1, 5, 9)). + equals!(real, 3, 4, 1, 5, 9)); +} + + +/** + * Returns a typetuple created from TList with the all occurrences, + * if any, of T removed. + */ +template EraseAll(T, TList...) +{ + alias EraseAll = GenericEraseAll!(T, TList).result; +} + +/// Ditto +template EraseAll(alias T, TList...) +{ + alias EraseAll = GenericEraseAll!(T, TList).result; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int); + + alias TL = EraseAll!(long, Types); + static assert(is(TL == AliasSeq!(int, int))); +} + +// [internal] +private template GenericEraseAll(args...) +if (args.length >= 1) +{ + alias e = OldAlias!(args[0]); + alias tuple = args[1 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + alias next = AliasSeq!( + GenericEraseAll!(e, tail[0..$/2]).result, + GenericEraseAll!(e, tail[$/2..$]).result + ); + + static if (isSame!(e, head)) + alias result = next; + else + alias result = AliasSeq!(head, next); + } + else + { + alias result = AliasSeq!(); + } +} + +@safe unittest +{ + static assert(Pack!(EraseAll!(int, + short, int, int, 4)). + equals!(short, 4)); + + static assert(Pack!(EraseAll!(1, + real, 3, 1, 4, 1, 5, 9)). + equals!(real, 3, 4, 5, 9)); +} + + +/** + * Returns a typetuple created from TList with the all duplicate + * types removed. + */ +template NoDuplicates(TList...) +{ + template EraseAllN(uint N, T...) + { + static if (N <= 1) + { + alias EraseAllN = T; + } + else + { + alias EraseAllN = EraseAllN!(N-1, T[1 .. N], EraseAll!(T[0], T[N..$])); + } + } + static if (TList.length > 500) + { + enum steps = 16; + alias first = NoDuplicates!(TList[0 .. steps]); + alias NoDuplicates = NoDuplicates!(EraseAllN!(first.length, first, TList[steps..$])); + } + else static if (TList.length == 0) + { + alias NoDuplicates = TList; + } + else + { + alias NoDuplicates = + AliasSeq!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $]))); + } +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = NoDuplicates!(Types); + static assert(is(TL == AliasSeq!(int, long, float))); + + // Bugzilla 14561: huge enums + alias LongList = Repeat!(1500, int); + static assert(NoDuplicates!LongList.length == 1); +} + +@safe unittest +{ + static assert( + Pack!( + NoDuplicates!(1, int, 1, NoDuplicates, int, NoDuplicates, real)) + .equals!(1, int, NoDuplicates, real)); +} + + +/** + * Returns a typetuple created from TList with the first occurrence + * of type T, if found, replaced with type U. + */ +template Replace(T, U, TList...) +{ + alias Replace = GenericReplace!(T, U, TList).result; +} + +/// Ditto +template Replace(alias T, U, TList...) +{ + alias Replace = GenericReplace!(T, U, TList).result; +} + +/// Ditto +template Replace(T, alias U, TList...) +{ + alias Replace = GenericReplace!(T, U, TList).result; +} + +/// Ditto +template Replace(alias T, alias U, TList...) +{ + alias Replace = GenericReplace!(T, U, TList).result; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = Replace!(long, char, Types); + static assert(is(TL == AliasSeq!(int, char, long, int, float))); +} + +// [internal] +private template GenericReplace(args...) +if (args.length >= 2) +{ + alias from = OldAlias!(args[0]); + alias to = OldAlias!(args[1]); + alias tuple = args[2 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + + static if (isSame!(from, head)) + alias result = AliasSeq!(to, tail); + else + alias result = AliasSeq!(head, + GenericReplace!(from, to, tail).result); + } + else + { + alias result = AliasSeq!(); + } + } + +@safe unittest +{ + static assert(Pack!(Replace!(byte, ubyte, + short, byte, byte, byte)). + equals!(short, ubyte, byte, byte)); + + static assert(Pack!(Replace!(1111, byte, + 2222, 1111, 1111, 1111)). + equals!(2222, byte, 1111, 1111)); + + static assert(Pack!(Replace!(byte, 1111, + short, byte, byte, byte)). + equals!(short, 1111, byte, byte)); + + static assert(Pack!(Replace!(1111, "11", + 2222, 1111, 1111, 1111)). + equals!(2222, "11", 1111, 1111)); +} + +/** + * Returns a typetuple created from TList with all occurrences + * of type T, if found, replaced with type U. + */ +template ReplaceAll(T, U, TList...) +{ + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; +} + +/// Ditto +template ReplaceAll(alias T, U, TList...) +{ + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; +} + +/// Ditto +template ReplaceAll(T, alias U, TList...) +{ + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; +} + +/// Ditto +template ReplaceAll(alias T, alias U, TList...) +{ + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = ReplaceAll!(long, char, Types); + static assert(is(TL == AliasSeq!(int, char, char, int, float))); +} + +// [internal] +private template GenericReplaceAll(args...) +if (args.length >= 2) +{ + alias from = OldAlias!(args[0]); + alias to = OldAlias!(args[1]); + alias tuple = args[2 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + alias next = GenericReplaceAll!(from, to, tail).result; + + static if (isSame!(from, head)) + alias result = AliasSeq!(to, next); + else + alias result = AliasSeq!(head, next); + } + else + { + alias result = AliasSeq!(); + } +} + +@safe unittest +{ + static assert(Pack!(ReplaceAll!(byte, ubyte, + byte, short, byte, byte)). + equals!(ubyte, short, ubyte, ubyte)); + + static assert(Pack!(ReplaceAll!(1111, byte, + 1111, 2222, 1111, 1111)). + equals!(byte, 2222, byte, byte)); + + static assert(Pack!(ReplaceAll!(byte, 1111, + byte, short, byte, byte)). + equals!(1111, short, 1111, 1111)); + + static assert(Pack!(ReplaceAll!(1111, "11", + 1111, 2222, 1111, 1111)). + equals!("11", 2222, "11", "11")); +} + +/** + * Returns a typetuple created from TList with the order reversed. + */ +template Reverse(TList...) +{ + static if (TList.length <= 1) + { + alias Reverse = TList; + } + else + { + alias Reverse = + AliasSeq!( + Reverse!(TList[$/2 .. $ ]), + Reverse!(TList[ 0 .. $/2])); + } +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = Reverse!(Types); + static assert(is(TL == AliasSeq!(float, int, long, long, int))); +} + +/** + * Returns the type from TList that is the most derived from type T. + * If none are found, T is returned. + */ +template MostDerived(T, TList...) +{ + static if (TList.length == 0) + alias MostDerived = T; + else static if (is(TList[0] : T)) + alias MostDerived = MostDerived!(TList[0], TList[1 .. $]); + else + alias MostDerived = MostDerived!(T, TList[1 .. $]); +} + +/// +@safe unittest +{ + class A { } + class B : A { } + class C : B { } + alias Types = AliasSeq!(A, C, B); + + MostDerived!(Object, Types) x; // x is declared as type C + static assert(is(typeof(x) == C)); +} + +/** + * Returns the typetuple TList with the types sorted so that the most + * derived types come first. + */ +template DerivedToFront(TList...) +{ + static if (TList.length == 0) + alias DerivedToFront = TList; + else + alias DerivedToFront = + AliasSeq!(MostDerived!(TList[0], TList[1 .. $]), + DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]), + TList[0], + TList[1 .. $]))); +} + +/// +@safe unittest +{ + class A { } + class B : A { } + class C : B { } + alias Types = AliasSeq!(A, C, B); + + alias TL = DerivedToFront!(Types); + static assert(is(TL == AliasSeq!(C, B, A))); +} + +/** +Evaluates to $(D AliasSeq!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))). + */ +template staticMap(alias F, T...) +{ + static if (T.length == 0) + { + alias staticMap = AliasSeq!(); + } + else static if (T.length == 1) + { + alias staticMap = AliasSeq!(F!(T[0])); + } + else + { + alias staticMap = + AliasSeq!( + staticMap!(F, T[ 0 .. $/2]), + staticMap!(F, T[$/2 .. $ ])); + } +} + +/// +@safe unittest +{ + import std.traits : Unqual; + alias TL = staticMap!(Unqual, int, const int, immutable int); + static assert(is(TL == AliasSeq!(int, int, int))); +} + +@safe unittest +{ + import std.traits : Unqual; + + // empty + alias Empty = staticMap!(Unqual); + static assert(Empty.length == 0); + + // single + alias Single = staticMap!(Unqual, const int); + static assert(is(Single == AliasSeq!int)); + + alias T = staticMap!(Unqual, int, const int, immutable int); + static assert(is(T == AliasSeq!(int, int, int))); +} + +/** +Tests whether all given items satisfy a template predicate, i.e. evaluates to +$(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])). + +Evaluation is $(I not) short-circuited if a false result is encountered; the +template predicate must be instantiable with all the given items. + */ +template allSatisfy(alias F, T...) +{ + static if (T.length == 0) + { + enum allSatisfy = true; + } + else static if (T.length == 1) + { + enum allSatisfy = F!(T[0]); + } + else + { + enum allSatisfy = + allSatisfy!(F, T[ 0 .. $/2]) && + allSatisfy!(F, T[$/2 .. $ ]); + } +} + +/// +@safe unittest +{ + import std.traits : isIntegral; + + static assert(!allSatisfy!(isIntegral, int, double)); + static assert( allSatisfy!(isIntegral, int, long)); +} + +/** +Tests whether any given items satisfy a template predicate, i.e. evaluates to +$(D F!(T[0]) || F!(T[1]) || ... || F!(T[$ - 1])). + +Evaluation is short-circuited if a true result is encountered; the +template predicate must be instantiable with one of the given items. + */ +template anySatisfy(alias F, T...) +{ + static if (T.length == 0) + { + enum anySatisfy = false; + } + else static if (T.length == 1) + { + enum anySatisfy = F!(T[0]); + } + else + { + enum anySatisfy = + anySatisfy!(F, T[ 0 .. $/2]) || + anySatisfy!(F, T[$/2 .. $ ]); + } +} + +/// +@safe unittest +{ + import std.traits : isIntegral; + + static assert(!anySatisfy!(isIntegral, string, double)); + static assert( anySatisfy!(isIntegral, int, double)); +} + + +/** + * Filters an $(D AliasSeq) using a template predicate. Returns a + * $(D AliasSeq) of the elements which satisfy the predicate. + */ +template Filter(alias pred, TList...) +{ + static if (TList.length == 0) + { + alias Filter = AliasSeq!(); + } + else static if (TList.length == 1) + { + static if (pred!(TList[0])) + alias Filter = AliasSeq!(TList[0]); + else + alias Filter = AliasSeq!(); + } + else + { + alias Filter = + AliasSeq!( + Filter!(pred, TList[ 0 .. $/2]), + Filter!(pred, TList[$/2 .. $ ])); + } +} + +/// +@safe unittest +{ + import std.traits : isNarrowString, isUnsigned; + + alias Types1 = AliasSeq!(string, wstring, dchar[], char[], dstring, int); + alias TL1 = Filter!(isNarrowString, Types1); + static assert(is(TL1 == AliasSeq!(string, wstring, char[]))); + + alias Types2 = AliasSeq!(int, byte, ubyte, dstring, dchar, uint, ulong); + alias TL2 = Filter!(isUnsigned, Types2); + static assert(is(TL2 == AliasSeq!(ubyte, uint, ulong))); +} + +@safe unittest +{ + import std.traits : isPointer; + + static assert(is(Filter!(isPointer, int, void*, char[], int*) == AliasSeq!(void*, int*))); + static assert(is(Filter!isPointer == AliasSeq!())); +} + + +// Used in template predicate unit tests below. +private version (unittest) +{ + template testAlways(T...) + { + enum testAlways = true; + } + + template testNever(T...) + { + enum testNever = false; + } + + template testError(T...) + { + static assert(false, "Should never be instantiated."); + } +} + + +/** + * Negates the passed template predicate. + */ +template templateNot(alias pred) +{ + enum templateNot(T...) = !pred!T; +} + +/// +@safe unittest +{ + import std.traits : isPointer; + + alias isNoPointer = templateNot!isPointer; + static assert(!isNoPointer!(int*)); + static assert(allSatisfy!(isNoPointer, string, char, float)); +} + +@safe unittest +{ + foreach (T; AliasSeq!(int, staticMap, 42)) + { + static assert(!Instantiate!(templateNot!testAlways, T)); + static assert(Instantiate!(templateNot!testNever, T)); + } +} + + +/** + * Combines several template predicates using logical AND, i.e. constructs a new + * predicate which evaluates to true for a given input T if and only if all of + * the passed predicates are true for T. + * + * The predicates are evaluated from left to right, aborting evaluation in a + * short-cut manner if a false result is encountered, in which case the latter + * instantiations do not need to compile. + */ +template templateAnd(Preds...) +{ + template templateAnd(T...) + { + static if (Preds.length == 0) + { + enum templateAnd = true; + } + else + { + static if (Instantiate!(Preds[0], T)) + alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T); + else + enum templateAnd = false; + } + } +} + +/// +@safe unittest +{ + import std.traits : isNumeric, isUnsigned; + + alias storesNegativeNumbers = templateAnd!(isNumeric, templateNot!isUnsigned); + static assert(storesNegativeNumbers!int); + static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint); + + // An empty list of predicates always yields true. + alias alwaysTrue = templateAnd!(); + static assert(alwaysTrue!int); +} + +@safe unittest +{ + foreach (T; AliasSeq!(int, staticMap, 42)) + { + static assert( Instantiate!(templateAnd!(), T)); + static assert( Instantiate!(templateAnd!(testAlways), T)); + static assert( Instantiate!(templateAnd!(testAlways, testAlways), T)); + static assert(!Instantiate!(templateAnd!(testNever), T)); + static assert(!Instantiate!(templateAnd!(testAlways, testNever), T)); + static assert(!Instantiate!(templateAnd!(testNever, testAlways), T)); + + static assert(!Instantiate!(templateAnd!(testNever, testError), T)); + static assert(!is(typeof(Instantiate!(templateAnd!(testAlways, testError), T)))); + } +} + + +/** + * Combines several template predicates using logical OR, i.e. constructs a new + * predicate which evaluates to true for a given input T if and only at least + * one of the passed predicates is true for T. + * + * The predicates are evaluated from left to right, aborting evaluation in a + * short-cut manner if a true result is encountered, in which case the latter + * instantiations do not need to compile. + */ +template templateOr(Preds...) +{ + template templateOr(T...) + { + static if (Preds.length == 0) + { + enum templateOr = false; + } + else + { + static if (Instantiate!(Preds[0], T)) + enum templateOr = true; + else + alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T); + } + } +} + +/// +@safe unittest +{ + import std.traits : isPointer, isUnsigned; + + alias isPtrOrUnsigned = templateOr!(isPointer, isUnsigned); + static assert( isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*)); + static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!(string)); + + // An empty list of predicates never yields true. + alias alwaysFalse = templateOr!(); + static assert(!alwaysFalse!int); +} + +@safe unittest +{ + foreach (T; AliasSeq!(int, staticMap, 42)) + { + static assert( Instantiate!(templateOr!(testAlways), T)); + static assert( Instantiate!(templateOr!(testAlways, testAlways), T)); + static assert( Instantiate!(templateOr!(testAlways, testNever), T)); + static assert( Instantiate!(templateOr!(testNever, testAlways), T)); + static assert(!Instantiate!(templateOr!(), T)); + static assert(!Instantiate!(templateOr!(testNever), T)); + + static assert( Instantiate!(templateOr!(testAlways, testError), T)); + static assert( Instantiate!(templateOr!(testNever, testAlways, testError), T)); + // DMD @@BUG@@: Assertion fails for int, seems like a error gagging + // problem. The bug goes away when removing some of the other template + // instantiations in the module. + // static assert(!is(typeof(Instantiate!(templateOr!(testNever, testError), T)))); + } +} + +/** + * Converts an input range $(D range) to an alias sequence. + */ +template aliasSeqOf(alias range) +{ + import std.traits : isArray, isNarrowString; + + alias ArrT = typeof(range); + static if (isArray!ArrT && !isNarrowString!ArrT) + { + static if (range.length == 0) + { + alias aliasSeqOf = AliasSeq!(); + } + else static if (range.length == 1) + { + alias aliasSeqOf = AliasSeq!(range[0]); + } + else + { + alias aliasSeqOf = AliasSeq!(aliasSeqOf!(range[0 .. $/2]), aliasSeqOf!(range[$/2 .. $])); + } + } + else + { + import std.range.primitives : isInputRange; + static if (isInputRange!ArrT) + { + import std.array : array; + alias aliasSeqOf = aliasSeqOf!(array(range)); + } + else + { + static assert(false, "Cannot transform range of type " ~ ArrT.stringof ~ " into a AliasSeq."); + } + } +} + +/// +@safe unittest +{ + import std.algorithm.iteration : map; + import std.algorithm.sorting : sort; + import std.string : capitalize; + + struct S + { + int a; + int c; + int b; + } + + alias capMembers = aliasSeqOf!([__traits(allMembers, S)].sort().map!capitalize()); + static assert(capMembers[0] == "A"); + static assert(capMembers[1] == "B"); + static assert(capMembers[2] == "C"); +} + +/// +@safe unittest +{ + static immutable REF = [0, 1, 2, 3]; + foreach (I, V; aliasSeqOf!([0, 1, 2, 3])) + { + static assert(V == I); + static assert(V == REF[I]); + } +} + +@safe unittest +{ + import std.range : iota; + import std.conv : to, octal; + //Testing compile time octal + foreach (I2; aliasSeqOf!(iota(0, 8))) + foreach (I1; aliasSeqOf!(iota(0, 8))) + { + enum oct = I2 * 8 + I1; + enum dec = I2 * 10 + I1; + enum str = to!string(dec); + static assert(octal!dec == oct); + static assert(octal!str == oct); + } +} + +@safe unittest +{ + enum REF = "日本語"d; + foreach (I, V; aliasSeqOf!"日本語"c) + { + static assert(V == REF[I]); + } +} + +/** + * $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially applies) + * $(D_PARAM Template) by binding its first (left) or last (right) arguments + * to $(D_PARAM args). + * + * Behaves like the identity function when $(D_PARAM args) is empty. + * Params: + * Template = template to partially apply + * args = arguments to bind + * Returns: + * _Template with arity smaller than or equal to $(D_PARAM Template) + */ +template ApplyLeft(alias Template, args...) +{ + alias ApplyLeft(right...) = SmartAlias!(Template!(args, right)); +} + +/// Ditto +template ApplyRight(alias Template, args...) +{ + alias ApplyRight(left...) = SmartAlias!(Template!(left, args)); +} + +/// +@safe unittest +{ + // enum bool isImplicitlyConvertible(From, To) + import std.traits : isImplicitlyConvertible; + + static assert(allSatisfy!( + ApplyLeft!(isImplicitlyConvertible, ubyte), + short, ushort, int, uint, long, ulong)); + + static assert(is(Filter!(ApplyRight!(isImplicitlyConvertible, short), + ubyte, string, short, float, int) == AliasSeq!(ubyte, short))); +} + +/// +@safe unittest +{ + import std.traits : hasMember, ifTestable; + + struct T1 + { + bool foo; + } + + struct T2 + { + struct Test + { + bool opCast(T : bool)() { return true; } + } + + Test foo; + } + + static assert(allSatisfy!(ApplyRight!(hasMember, "foo"), T1, T2)); + static assert(allSatisfy!(ApplyRight!(ifTestable, a => a.foo), T1, T2)); +} + +/// +@safe unittest +{ + import std.traits : Largest; + + alias Types = AliasSeq!(byte, short, int, long); + + static assert(is(staticMap!(ApplyLeft!(Largest, short), Types) == + AliasSeq!(short, short, int, long))); + static assert(is(staticMap!(ApplyLeft!(Largest, int), Types) == + AliasSeq!(int, int, int, long))); +} + +/// +@safe unittest +{ + import std.traits : FunctionAttribute, SetFunctionAttributes; + + static void foo() @system; + static int bar(int) @system; + + alias SafeFunctions = AliasSeq!( + void function() @safe, + int function(int) @safe); + + static assert(is(staticMap!(ApplyRight!( + SetFunctionAttributes, "D", FunctionAttribute.safe), + typeof(&foo), typeof(&bar)) == SafeFunctions)); +} + +private template SmartAlias(T...) +{ + static if (T.length == 1) + { + alias SmartAlias = Alias!T; + } + else + { + alias SmartAlias = AliasSeq!T; + } +} + +@safe unittest +{ + static assert(is(typeof({ + alias T(T0, int a, double b, alias T1, string c) = AliasSeq!(T0, a, b, T1, c); + alias T0 = ApplyRight!(ApplyLeft, ApplyRight); + alias T1 = T0!ApplyLeft; + alias T2 = T1!T; + alias T3 = T2!(3, "foo"); + alias T4 = T3!(short, 3, 3.3); + static assert(Pack!T4.equals!(short, 3, 3.3, 3, "foo")); + + import std.traits : isImplicitlyConvertible; + alias U1 = ApplyLeft!(ApplyRight, isImplicitlyConvertible); + alias U2 = U1!int; + enum U3 = U2!short; + static assert(U3); + }))); +} + +/** + * Creates an `AliasSeq` which repeats a type or an `AliasSeq` exactly `n` times. + */ +template Repeat(size_t n, TList...) +if (n > 0) +{ + static if (n == 1) + { + alias Repeat = AliasSeq!TList; + } + else static if (n == 2) + { + alias Repeat = AliasSeq!(TList, TList); + } + else + { + alias R = Repeat!((n - 1) / 2, TList); + static if ((n - 1) % 2 == 0) + { + alias Repeat = AliasSeq!(TList, R, R); + } + else + { + alias Repeat = AliasSeq!(TList, TList, R, R); + } + } +} + +/// +@safe unittest +{ + alias ImInt1 = Repeat!(1, immutable(int)); + static assert(is(ImInt1 == AliasSeq!(immutable(int)))); + + alias Real3 = Repeat!(3, real); + static assert(is(Real3 == AliasSeq!(real, real, real))); + + alias Real12 = Repeat!(4, Real3); + static assert(is(Real12 == AliasSeq!(real, real, real, real, real, real, + real, real, real, real, real, real))); + + alias Composite = AliasSeq!(uint, int); + alias Composite2 = Repeat!(2, Composite); + static assert(is(Composite2 == AliasSeq!(uint, int, uint, int))); +} + + +/// +@safe unittest +{ + auto staticArray(T, size_t n)(Repeat!(n, T) elems) + { + T[n] a = [elems]; + return a; + } + + auto a = staticArray!(long, 3)(3, 1, 4); + assert(is(typeof(a) == long[3])); + assert(a == [3, 1, 4]); +} + +/** + * Sorts a $(LREF AliasSeq) using $(D cmp). + * + * Parameters: + * cmp = A template that returns a $(D bool) (if its first argument is less than the second one) + * or an $(D int) (-1 means less than, 0 means equal, 1 means greater than) + * + * Seq = The $(LREF AliasSeq) to sort + * + * Returns: The sorted alias sequence + */ +template staticSort(alias cmp, Seq...) +{ + static if (Seq.length < 2) + { + alias staticSort = Seq; + } + else + { + private alias btm = staticSort!(cmp, Seq[0 .. $ / 2]); + private alias top = staticSort!(cmp, Seq[$ / 2 .. $]); + + static if (isLessEq!(cmp, btm[$ - 1], top[0])) + alias staticSort = AliasSeq!(btm, top); // already ascending + else static if (isLessEq!(cmp, top[$ - 1], btm[0])) + alias staticSort = AliasSeq!(top, btm); // already descending + else + alias staticSort = staticMerge!(cmp, Seq.length / 2, btm, top); + } +} + +/// +@safe unittest +{ + alias Nums = AliasSeq!(7, 2, 3, 23); + enum Comp(int N1, int N2) = N1 < N2; + static assert(AliasSeq!(2, 3, 7, 23) == staticSort!(Comp, Nums)); +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(uint, short, ubyte, long, ulong); + enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1); + static assert(is(AliasSeq!(uint, ubyte, ulong, short, long) == staticSort!(Comp, + Types))); +} + +private template staticMerge(alias cmp, int half, Seq...) +{ + static if (half == 0 || half == Seq.length) + { + alias staticMerge = Seq; + } + else + { + static if (isLessEq!(cmp, Seq[0], Seq[half])) + { + alias staticMerge = AliasSeq!(Seq[0], + staticMerge!(cmp, half - 1, Seq[1 .. $])); + } + else + { + alias staticMerge = AliasSeq!(Seq[half], + staticMerge!(cmp, half, Seq[0 .. half], Seq[half + 1 .. $])); + } + } +} + +private template isLessEq(alias cmp, Seq...) +if (Seq.length == 2) +{ + private enum Result = cmp!(Seq[1], Seq[0]); + static if (is(typeof(Result) == bool)) + enum isLessEq = !Result; + else static if (is(typeof(Result) : int)) + enum isLessEq = Result >= 0; + else + static assert(0, typeof(Result).stringof ~ " is not a value comparison type"); +} + +/** + * Checks if an $(LREF AliasSeq) is sorted according to $(D cmp). + * + * Parameters: + * cmp = A template that returns a $(D bool) (if its first argument is less than the second one) + * or an $(D int) (-1 means less than, 0 means equal, 1 means greater than) + * + * Seq = The $(LREF AliasSeq) to check + * + * Returns: `true` if `Seq` is sorted; otherwise `false` + */ +template staticIsSorted(alias cmp, Seq...) +{ + static if (Seq.length <= 1) + enum staticIsSorted = true; + else static if (Seq.length == 2) + enum staticIsSorted = isLessEq!(cmp, Seq[0], Seq[1]); + else + { + enum staticIsSorted = + isLessEq!(cmp, Seq[($ / 2) - 1], Seq[$ / 2]) && + staticIsSorted!(cmp, Seq[0 .. $ / 2]) && + staticIsSorted!(cmp, Seq[$ / 2 .. $]); + } +} + +/// +@safe unittest +{ + enum Comp(int N1, int N2) = N1 < N2; + static assert( staticIsSorted!(Comp, 2, 2)); + static assert( staticIsSorted!(Comp, 2, 3, 7, 23)); + static assert(!staticIsSorted!(Comp, 7, 2, 3, 23)); +} + +/// +@safe unittest +{ + enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1); + static assert( staticIsSorted!(Comp, uint, ubyte, ulong, short, long)); + static assert(!staticIsSorted!(Comp, uint, short, ubyte, long, ulong)); +} + +// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : // +private: + +/* + * [internal] Returns true if a and b are the same thing, or false if + * not. Both a and b can be types, literals, or symbols. + * + * How: When: + * is(a == b) - both are types + * a == b - both are literals (true literals, enums) + * __traits(isSame, a, b) - other cases (variables, functions, + * templates, etc.) + */ +private template isSame(ab...) +if (ab.length == 2) +{ + static if (__traits(compiles, expectType!(ab[0]), + expectType!(ab[1]))) + { + enum isSame = is(ab[0] == ab[1]); + } + else static if (!__traits(compiles, expectType!(ab[0])) && + !__traits(compiles, expectType!(ab[1])) && + __traits(compiles, expectBool!(ab[0] == ab[1]))) + { + static if (!__traits(compiles, &ab[0]) || + !__traits(compiles, &ab[1])) + enum isSame = (ab[0] == ab[1]); + else + enum isSame = __traits(isSame, ab[0], ab[1]); + } + else + { + enum isSame = __traits(isSame, ab[0], ab[1]); + } +} +private template expectType(T) {} +private template expectBool(bool b) {} + +@safe unittest +{ + static assert( isSame!(int, int)); + static assert(!isSame!(int, short)); + + enum a = 1, b = 1, c = 2, s = "a", t = "a"; + static assert( isSame!(1, 1)); + static assert( isSame!(a, 1)); + static assert( isSame!(a, b)); + static assert(!isSame!(b, c)); + static assert( isSame!("a", "a")); + static assert( isSame!(s, "a")); + static assert( isSame!(s, t)); + static assert(!isSame!(1, "1")); + static assert(!isSame!(a, "a")); + static assert( isSame!(isSame, isSame)); + static assert(!isSame!(isSame, a)); + + static assert(!isSame!(byte, a)); + static assert(!isSame!(short, isSame)); + static assert(!isSame!(a, int)); + static assert(!isSame!(long, isSame)); + + static immutable X = 1, Y = 1, Z = 2; + static assert( isSame!(X, X)); + static assert(!isSame!(X, Y)); + static assert(!isSame!(Y, Z)); + + int foo(); + int bar(); + real baz(int); + static assert( isSame!(foo, foo)); + static assert(!isSame!(foo, bar)); + static assert(!isSame!(bar, baz)); + static assert( isSame!(baz, baz)); + static assert(!isSame!(foo, 0)); + + int x, y; + real z; + static assert( isSame!(x, x)); + static assert(!isSame!(x, y)); + static assert(!isSame!(y, z)); + static assert( isSame!(z, z)); + static assert(!isSame!(x, 0)); +} + +/* + * [internal] Confines a tuple within a template. + */ +private template Pack(T...) +{ + alias tuple = T; + + // For convenience + template equals(U...) + { + static if (T.length == U.length) + { + static if (T.length == 0) + enum equals = true; + else + enum equals = isSame!(T[0], U[0]) && + Pack!(T[1 .. $]).equals!(U[1 .. $]); + } + else + { + enum equals = false; + } + } +} + +@safe unittest +{ + static assert( Pack!(1, int, "abc").equals!(1, int, "abc")); + static assert(!Pack!(1, int, "abc").equals!(1, int, "cba")); +} + +/* + * Instantiates the given template with the given list of parameters. + * + * Used to work around syntactic limitations of D with regard to instantiating + * a template from an alias sequence (e.g. T[0]!(...) is not valid) or a template + * returning another template (e.g. Foo!(Bar)!(Baz) is not allowed). + */ +// TODO: Consider publicly exposing this, maybe even if only for better +// understandability of error messages. +alias Instantiate(alias Template, Params...) = Template!Params; diff --git a/std/metastrings.d b/std/metastrings.d deleted file mode 100644 index ce2d6146f05..00000000000 --- a/std/metastrings.d +++ /dev/null @@ -1,3 +0,0 @@ -// Explicitly undocumented. It will be removed in April 2015. -deprecated("Please use std.string.format, std.conv.to or std.conv.parse instead") -module std.metastrings; diff --git a/std/mmfile.d b/std/mmfile.d index 7cc64866c50..4313b01bcd5 100644 --- a/std/mmfile.d +++ b/std/mmfile.d @@ -2,14 +2,13 @@ /** * Read and write memory mapped files. - * Macros: - * WIKI=Phobos/StdMmfile - * * Copyright: Copyright Digital Mars 2004 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright), + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright), * Matthew Wilson * Source: $(PHOBOSSRC std/_mmfile.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) */ /* Copyright Digital Mars 2004 - 2009. * Distributed under the Boost Software License, Version 1.0. @@ -78,6 +77,8 @@ class MmFile version(linux) this(File file, Mode mode = Mode.read, ulong size = 0, void* address = null, size_t window = 0) { + // Save a copy of the File to make sure the fd stays open. + this.file = file; this(file.fileno, mode, size, address, window); } @@ -140,9 +141,10 @@ class MmFile // Map the file into memory! size_t initial_map = (window && 2*window>32); + scope(failure) + { + if (hFile != INVALID_HANDLE_VALUE) + { + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + } + } + + int hi = cast(int)(size >> 32); hFileMap = CreateFileMappingW(hFile, null, flProtect, - hi, cast(uint)size, null); + hi, cast(uint) size, null); wenforce(hFileMap, "CreateFileMapping"); - scope(failure) CloseHandle(hFileMap); + scope(failure) + { + CloseHandle(hFileMap); + hFileMap = null; + } if (size == 0 && filename != null) { @@ -248,12 +262,12 @@ class MmFile uint sizelow = GetFileSize(hFile, &sizehi); wenforce(sizelow != INVALID_FILE_SIZE || GetLastError() != ERROR_SUCCESS, "GetFileSize"); - size = (cast(ulong)sizehi << 32) + sizelow; + size = (cast(ulong) sizehi << 32) + sizelow; } this.size = size; size_t initial_map = (window && 2*window size) len = cast(size_t)(size-start); - version(Windows) { - uint hi = cast(uint)(start>>32); - p = MapViewOfFileEx(hFileMap, dwDesiredAccess, hi, cast(uint)start, len, address); + version(Windows) + { + uint hi = cast(uint)(start >> 32); + p = MapViewOfFileEx(hFileMap, dwDesiredAccess, hi, cast(uint) start, len, address); wenforce(p, "MapViewOfFileEx"); - } else { - p = mmap(address, len, prot, flags, fd, cast(off_t)start); + } + else + { + p = mmap(address, len, prot, flags, fd, cast(off_t) start); errnoEnforce(p != MAP_FAILED); } data = p[0 .. len]; @@ -508,11 +542,15 @@ class MmFile private void ensureMapped(ulong i) { debug (MMFILE) printf("MmFile.ensureMapped(%lld)\n", i); - if (!mapped(i)) { + if (!mapped(i)) + { unmap(); - if (window == 0) { - map(0,cast(size_t)size); - } else { + if (window == 0) + { + map(0,cast(size_t) size); + } + else + { ulong block = i/window; if (block == 0) map(0,2*window); @@ -526,16 +564,23 @@ class MmFile private void ensureMapped(ulong i, ulong j) { debug (MMFILE) printf("MmFile.ensureMapped(%lld, %lld)\n", i, j); - if (!mapped(i) || !mapped(j-1)) { + if (!mapped(i) || !mapped(j-1)) + { unmap(); - if (window == 0) { - map(0,cast(size_t)size); - } else { + if (window == 0) + { + map(0,cast(size_t) size); + } + else + { ulong iblock = i/window; ulong jblock = (j-1)/window; - if (iblock == 0) { + if (iblock == 0) + { map(0,cast(size_t)(window*(jblock+2))); - } else { + } + else + { map(window*(iblock-1),cast(size_t)(window*(jblock-iblock+3))); } } @@ -550,6 +595,7 @@ private: ulong size; Mode mMode; void* address; + version (linux) File file; version (Windows) { @@ -587,40 +633,89 @@ private: // } } -unittest +@system unittest { import std.file : deleteme; + import core.memory : GC; const size_t K = 1024; size_t win = 64*K; // assume the page size is 64K - version(Windows) { + version(Windows) + { /+ these aren't defined in core.sys.windows.windows so let's use default SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); win = sysinfo.dwAllocationGranularity; +/ - } else version (linux) { + } + else version (linux) + { // getpagesize() is not defined in the unix D headers so use the guess } string test_file = std.file.deleteme ~ "-testing.txt"; MmFile mf = new MmFile(test_file,MmFile.Mode.readWriteNew, 100*K,null,win); ubyte[] str = cast(ubyte[])"1234567890"; - ubyte[] data = cast(ubyte[])mf[0 .. 10]; + ubyte[] data = cast(ubyte[]) mf[0 .. 10]; data[] = str[]; assert( mf[0 .. 10] == str ); - data = cast(ubyte[])mf[50 .. 60]; + data = cast(ubyte[]) mf[50 .. 60]; data[] = str[]; assert( mf[50 .. 60] == str ); - ubyte[] data2 = cast(ubyte[])mf[20*K .. 60*K]; + ubyte[] data2 = cast(ubyte[]) mf[20*K .. 60*K]; assert( data2.length == 40*K ); assert( data2[$-1] == 0 ); mf[100*K-1] = cast(ubyte)'b'; - data2 = cast(ubyte[])mf[21*K .. 100*K]; + data2 = cast(ubyte[]) mf[21*K .. 100*K]; assert( data2.length == 79*K ); assert( data2[$-1] == 'b' ); - delete mf; + + destroy(mf); + GC.free(&mf); + std.file.remove(test_file); // Create anonymous mapping auto test = new MmFile(null, MmFile.Mode.readWriteNew, 1024*1024, null); } + +version(linux) +@system unittest // Issue 14868 +{ + import std.typecons : scoped; + import std.file : deleteme; + + // Test retaining ownership of File/fd + + auto fn = std.file.deleteme ~ "-testing.txt"; + scope(exit) std.file.remove(fn); + File(fn, "wb").writeln("Testing!"); + scoped!MmFile(File(fn)); + + // Test that unique ownership of File actually leads to the fd being closed + + auto f = File(fn); + auto fd = f.fileno; + { + auto mf = scoped!MmFile(f); + f = File.init; + } + assert(.close(fd) == -1); +} + +@system unittest // Issue 14994, 14995 +{ + import std.file : deleteme; + import std.typecons : scoped; + + // Zero-length map may or may not be valid on OSX and NetBSD + version (OSX) + import std.exception : verifyThrown = collectException; + version (NetBSD) + import std.exception : verifyThrown = collectException; + else + import std.exception : verifyThrown = assertThrown; + + auto fn = std.file.deleteme ~ "-testing.txt"; + scope(exit) std.file.remove(fn); + verifyThrown(scoped!MmFile(fn, MmFile.Mode.readWrite, 0, null)); +} diff --git a/std/net/curl.d b/std/net/curl.d index bab82be9caa..711c49819c6 100644 --- a/std/net/curl.d +++ b/std/net/curl.d @@ -1,7 +1,7 @@ // Written in the D programming language. /** -Networking client functionality as provided by $(WEB _curl.haxx.se/libcurl, +Networking client functionality as provided by $(HTTP _curl.haxx.se/libcurl, libcurl). The libcurl library must be installed on the system in order to use this module. @@ -58,18 +58,18 @@ upload("/tmp/downloaded-ftp-file", "ftp.digitalmars.com/sieve.ds");) uploads file from file system to URL.) ) $(TR $(TDNW $(LREF get)) $(TD $(D -get("dlang.org")) returns a string containing the dlang.org web page.) +get("dlang.org")) returns a char[] containing the dlang.org web page.) ) $(TR $(TDNW $(LREF put)) $(TD $(D -put("dlang.org", "Hi")) returns a string containing +put("dlang.org", "Hi")) returns a char[] containing the dlang.org web page. after a HTTP PUT of "hi") ) $(TR $(TDNW $(LREF post)) $(TD $(D -post("dlang.org", "Hi")) returns a string containing +post("dlang.org", "Hi")) returns a char[] containing the dlang.org web page. after a HTTP POST of "hi") ) $(TR $(TDNW $(LREF byLine)) $(TD $(D -byLine("dlang.org")) returns a range of strings containing the +byLine("dlang.org")) returns a range of char[] containing the dlang.org web page.) ) $(TR $(TDNW $(LREF byChunk)) $(TD $(D @@ -77,7 +77,7 @@ byChunk("dlang.org", 10)) returns a range of ubyte[10] containing the dlang.org web page.) ) $(TR $(TDNW $(LREF byLineAsync)) $(TD $(D -byLineAsync("dlang.org")) returns a range of strings containing the dlang.org web +byLineAsync("dlang.org")) returns a range of char[] containing the dlang.org web page asynchronously.) ) $(TR $(TDNW $(LREF byChunkAsync)) $(TD $(D @@ -96,14 +96,14 @@ Example: --- import std.net.curl, std.stdio; -// Return a string containing the content specified by an URL -string content = get("dlang.org"); +// Return a char[] containing the content specified by a URL +auto content = get("dlang.org"); -// Post data and return a string containing the content specified by an URL -string content = post("mydomain.com/here.cgi", "post data"); +// Post data and return a char[] containing the content specified by a URL +auto content = post("mydomain.com/here.cgi", ["name1" : "value1", "name2" : "value2"]); // Get content of file from ftp server -string content = get("ftp.digitalmars.com/sieve.ds"); +auto content = get("ftp.digitalmars.com/sieve.ds"); // Post and print out content line by line. The request is done in another thread. foreach (line; byLineAsync("dlang.org", "Post data")) @@ -142,10 +142,10 @@ synchronous. Source: $(PHOBOSSRC std/net/_curl.d) Copyright: Copyright Jonas Drewsen 2011-2012 -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao. -Credits: The functionally is based on $(WEB _curl.haxx.se/libcurl, libcurl). +Credits: The functionally is based on $(HTTP _curl.haxx.se/libcurl, libcurl). LibCurl is licensed under an MIT/X derivative license. */ /* @@ -158,20 +158,14 @@ module std.net.curl; import core.thread; import etc.c.curl; -import std.algorithm; -import std.array; import std.concurrency; -import std.conv; -import std.datetime; import std.encoding; import std.exception; -import std.regex; +import std.meta; +import std.range.primitives; import std.socket : InternetAddress; -import std.stream; -import std.string; import std.traits; import std.typecons; -import std.typetuple; import std.internal.cstring; @@ -183,25 +177,141 @@ version(unittest) // allow net traffic import std.stdio; import std.range; - import std.process : environment; - import std.file : tempDir; - import std.path : buildPath; - - // Source code for the test service is available at - // https://github.com/jcd/d-lang-testservice - enum testService = "d-lang.appspot.com"; - - enum testUrl1 = "http://"~testService~"/testUrl1"; - enum testUrl2 = "http://"~testService~"/testUrl2"; - enum testUrl3 = "ftp://ftp.digitalmars.com/sieve.ds"; - enum testUrl4 = testService~"/testUrl1"; - enum testUrl5 = "http://"~testService~"/testUrl3"; + + import std.socket : Address, INADDR_LOOPBACK, Socket, TcpSocket; + + private struct TestServer + { + string addr() { return _addr; } + + void handle(void function(Socket s) dg) + { + tid.send(dg); + } + + private: + string _addr; + Tid tid; + + static void loop(shared TcpSocket listener) + { + try while (true) + { + void function(Socket) handler = void; + try + handler = receiveOnly!(typeof(handler)); + catch (OwnerTerminated) + return; + handler((cast() listener).accept); + } + catch (Throwable e) + { + import core.stdc.stdlib : exit, EXIT_FAILURE; + stderr.writeln(e); + exit(EXIT_FAILURE); // Bugzilla 7018 + } + } + } + + private TestServer startServer() + { + auto sock = new TcpSocket; + sock.bind(new InternetAddress(INADDR_LOOPBACK, InternetAddress.PORT_ANY)); + sock.listen(1); + auto addr = sock.localAddress.toString(); + auto tid = spawn(&TestServer.loop, cast(shared) sock); + return TestServer(addr, tid); + } + + private ref TestServer testServer() + { + __gshared TestServer server; + return initOnce!server(startServer()); + } + + private struct Request(T) + { + string hdrs; + immutable(T)[] bdy; + } + + private Request!T recvReq(T=char)(Socket s) + { + import std.algorithm.comparison : min; + import std.algorithm.searching : find, canFind; + import std.conv : to; + import std.regex : ctRegex, matchFirst; + + ubyte[1024] tmp=void; + ubyte[] buf; + + while (true) + { + auto nbytes = s.receive(tmp[]); + assert(nbytes >= 0); + + immutable beg = buf.length > 3 ? buf.length - 3 : 0; + buf ~= tmp[0 .. nbytes]; + auto bdy = buf[beg .. $].find(cast(ubyte[])"\r\n\r\n"); + if (bdy.empty) + continue; + + auto hdrs = cast(string) buf[0 .. $ - bdy.length]; + bdy.popFrontN(4); + // no support for chunked transfer-encoding + if (auto m = hdrs.matchFirst(ctRegex!(`Content-Length: ([0-9]+)`, "i"))) + { + import std.uni : asUpperCase; + if (hdrs.asUpperCase.canFind("EXPECT: 100-CONTINUE")) + s.send(httpContinue); + + size_t remain = m.captures[1].to!size_t - bdy.length; + while (remain) + { + nbytes = s.receive(tmp[0 .. min(remain, $)]); + assert(nbytes >= 0); + buf ~= tmp[0 .. nbytes]; + remain -= nbytes; + } + } + else + { + assert(bdy.empty); + } + bdy = buf[hdrs.length + 4 .. $]; + return typeof(return)(hdrs, cast(immutable(T)[])bdy); + } + } + + private string httpOK(string msg) + { + import std.conv : to; + + return "HTTP/1.1 200 OK\r\n"~ + "Content-Type: text/plain\r\n"~ + "Content-Length: "~msg.length.to!string~"\r\n"~ + "\r\n"~ + msg; + } + + private string httpOK() + { + return "HTTP/1.1 200 OK\r\n"~ + "Content-Length: 0\r\n"~ + "\r\n"; + } + + private string httpNotFound() + { + return "HTTP/1.1 404 Not Found\r\n"~ + "Content-Length: 0\r\n"~ + "\r\n"; + } + + private enum httpContinue = "HTTP/1.1 100 Continue\r\n\r\n"; } version(StdDdoc) import std.stdio; -version (Windows) pragma(lib, "curl"); -extern (C) void exit(int); - // Default data timeout for Protocols private enum _defaultDataTimeout = dur!"minutes"(2); @@ -250,9 +360,9 @@ CALLBACK_PARAMS = $(TABLE , * // Guess connection type by looking at the URL * content = get!AutoProtocol("ftp://foo.com/file"); * // and since AutoProtocol is default this is the same as - * connect = get("ftp://foo.com/file"); + * content = get("ftp://foo.com/file"); * // and will end up detecting FTP from the url and be the same as - * connect = get!FTP("ftp://foo.com/file"); + * content = get!FTP("ftp://foo.com/file"); * --- */ struct AutoProtocol { } @@ -260,6 +370,9 @@ struct AutoProtocol { } // Returns true if the url points to an FTP resource private bool isFTPUrl(const(char)[] url) { + import std.algorithm.searching : startsWith; + import std.uni : toLower; + return startsWith(url.toLower(), "ftp://", "ftps://", "ftp.") != 0; } @@ -281,19 +394,18 @@ private template isCurlConn(Conn) * Example: * ---- * import std.net.curl; - * download("ftp.digitalmars.com/sieve.ds", "/tmp/downloaded-ftp-file"); * download("d-lang.appspot.com/testUrl2", "/tmp/downloaded-http-file"); * ---- */ void download(Conn = AutoProtocol)(const(char)[] url, string saveToPath, Conn conn = Conn()) - if (isCurlConn!Conn) +if (isCurlConn!Conn) { static if (is(Conn : HTTP) || is(Conn : FTP)) { + import std.stdio : File; conn.url = url; - auto f = new std.stream.BufferedFile(saveToPath, FileMode.OutNew); - scope (exit) f.close(); - conn.onReceive = (ubyte[] data) { return f.write(data); }; + auto f = File(saveToPath, "wb"); + conn.onReceive = (ubyte[] data) { f.rawWrite(data); return data.length; }; conn.perform(); } else @@ -305,11 +417,22 @@ void download(Conn = AutoProtocol)(const(char)[] url, string saveToPath, Conn co } } -unittest +@system unittest { - if (!netAllowed()) return; - download("ftp.digitalmars.com/sieve.ds", buildPath(tempDir(), "downloaded-ftp-file")); - download("d-lang.appspot.com/testUrl1", buildPath(tempDir(), "downloaded-http-file")); + import std.algorithm.searching : canFind; + static import std.file; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + assert(s.recvReq.hdrs.canFind("GET /")); + s.send(httpOK("Hello world")); + }); + auto fn = std.file.deleteme; + scope (exit) std.file.remove(fn); + download(host, fn); + assert(std.file.readText(fn) == "Hello world"); + } } /** Upload file from local files system using the HTTP or FTP protocol. @@ -328,7 +451,7 @@ unittest * ---- */ void upload(Conn = AutoProtocol)(string loadFromPath, const(char)[] url, Conn conn = Conn()) - if (isCurlConn!Conn) +if (isCurlConn!Conn) { static if (is(Conn : HTTP)) { @@ -350,22 +473,34 @@ void upload(Conn = AutoProtocol)(string loadFromPath, const(char)[] url, Conn co static if (is(Conn : HTTP) || is(Conn : FTP)) { - auto f = new std.stream.BufferedFile(loadFromPath, FileMode.In); - scope (exit) f.close(); - conn.onSend = (void[] data) - { - return f.read(cast(ubyte[])data); - }; - conn.contentLength = cast(size_t)f.size; + import std.stdio : File; + auto f = File(loadFromPath, "rb"); + conn.onSend = buf => f.rawRead(buf).length; + immutable sz = f.size; + if (sz != ulong.max) + conn.contentLength = sz; conn.perform(); } } -unittest +@system unittest { - if (!netAllowed()) return; - // upload(buildPath(tempDir(), "downloaded-ftp-file"), "ftp.digitalmars.com/sieve.ds"); - upload(buildPath(tempDir(), "downloaded-http-file"), "d-lang.appspot.com/testUrl2"); + import std.algorithm.searching : canFind; + static import std.file; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + auto fn = std.file.deleteme; + scope (exit) std.file.remove(fn); + std.file.write(fn, "upload data\n"); + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("PUT /path")); + assert(req.bdy.canFind("upload data")); + s.send(httpOK()); + }); + upload(fn, host ~ "/path"); + } } /** HTTP/FTP get content. @@ -384,7 +519,7 @@ unittest * Example: * ---- * import std.net.curl; - * string content = get("d-lang.appspot.com/testUrl2"); + * auto content = get("d-lang.appspot.com/testUrl2"); * ---- * * Returns: @@ -397,7 +532,7 @@ unittest * See_Also: $(LREF HTTP.Method) */ T[] get(Conn = AutoProtocol, T = char)(const(char)[] url, Conn conn = Conn()) - if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) ) +if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) ) { static if (is(Conn : HTTP)) { @@ -418,41 +553,45 @@ T[] get(Conn = AutoProtocol, T = char)(const(char)[] url, Conn conn = Conn()) } } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = get(testUrl1); - assert(res == "Hello world\n", - "get!HTTP() returns unexpected content " ~ res); - res = get(testUrl4); - assert(res == "Hello world\n", - "get!HTTP() returns unexpected content: " ~ res); - res = get(testUrl3); - assert(res.startsWith("\r\n/* Eratosthenes Sieve prime number calculation. */"), - "get!FTP() returns unexpected content"); + import std.algorithm.searching : canFind; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + assert(s.recvReq.hdrs.canFind("GET /path")); + s.send(httpOK("GETRESPONSE")); + }); + auto res = get(host ~ "/path"); + assert(res == "GETRESPONSE"); + } } /** HTTP post content. * * Params: - * url = resource to post to - * postData = data to send as the body of the request. An array - * of an arbitrary type is accepted and will be cast to ubyte[] - * before sending it. - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. + * url = resource to post to + * postDict = data to send as the body of the request. An associative array + * of $(D string) is accepted and will be encoded using + * www-form-urlencoding + * postData = data to send as the body of the request. An array + * of an arbitrary type is accepted and will be cast to ubyte[] + * before sending it. + * conn = HTTP connection to use + * T = The template parameter $(D T) specifies the type to return. Possible values + * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). If asking + * for $(D char), content will be converted from the connection character set + * (specified in HTTP response headers or FTP connection properties, both ISO-8859-1 + * by default) to UTF-8. * - * The template parameter $(D T) specifies the type to return. Possible values - * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). If asking - * for $(D char), content will be converted from the connection character set - * (specified in HTTP response headers or FTP connection properties, both ISO-8859-1 - * by default) to UTF-8. - * - * Example: + * Examples: * ---- * import std.net.curl; - * string content = post("d-lang.appspot.com/testUrl2", [1,2,3,4]); + * + * auto content1 = post("d-lang.appspot.com/testUrl2", ["name1" : "value1", "name2" : "value2"]); + * auto content2 = post("d-lang.appspot.com/testUrl2", [1,2,3,4]); * ---- * * Returns: @@ -467,35 +606,63 @@ if (is(T == char) || is(T == ubyte)) return _basicHTTP!(T)(url, postData, conn); } -unittest +@system unittest { - if (!netAllowed()) return; + import std.algorithm.searching : canFind; + foreach (host; [testServer.addr, "http://"~testServer.addr]) { - string data = "Hello world"; - auto res = post(testUrl2, data); - assert(res == data, - "post!HTTP() returns unexpected content " ~ res); + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("POST /path")); + assert(req.bdy.canFind("POSTBODY")); + s.send(httpOK("POSTRESPONSE")); + }); + auto res = post(host ~ "/path", "POSTBODY"); + assert(res == "POSTRESPONSE"); } +} - { - ubyte[] data; - foreach (n; 0..256) - data ~= cast(ubyte)n; - auto res = post!ubyte(testUrl2, data); - assert(res == data, - "post!HTTP() with binary data returns unexpected content (" ~ text(res.length) ~ " bytes)"); - } +@system unittest +{ + import std.algorithm.searching : canFind; + + auto data = new ubyte[](256); + foreach (i, ref ub; data) + ub = cast(ubyte) i; + + testServer.handle((s) { + auto req = s.recvReq!ubyte; + assert(req.bdy.canFind(cast(ubyte[])[0, 1, 2, 3, 4])); + assert(req.bdy.canFind(cast(ubyte[])[253, 254, 255])); + s.send(httpOK(cast(ubyte[])[17, 27, 35, 41])); + }); + auto res = post!ubyte(testServer.addr, data); + assert(res == cast(ubyte[])[17, 27, 35, 41]); +} + +/// ditto +T[] post(T = char)(const(char)[] url, string[string] postDict, HTTP conn = HTTP()) +if (is(T == char) || is(T == ubyte)) +{ + import std.uri : urlEncode; + + return post(url, urlEncode(postDict), conn); +} +@system unittest +{ + foreach (host; [testServer.addr, "http://" ~ testServer.addr]) { - string data = "Hello world"; - auto res = post(testUrl5, data); - assert(res == data, - "post!HTTP() returns unexpected content after redirect " ~ res); + testServer.handle((s) { + auto req = s.recvReq!char; + s.send(httpOK(req.bdy)); + }); + auto res = post(host ~ "/path", ["name1" : "value1", "name2" : "value2"]); + assert(res == "name1=value1&name2=value2"); } } - /** HTTP/FTP put content. * * Params: @@ -515,7 +682,7 @@ unittest * Example: * ---- * import std.net.curl; - * string content = put("d-lang.appspot.com/testUrl2", + * auto content = put("d-lang.appspot.com/testUrl2", * "Putting this data"); * ---- * @@ -526,7 +693,7 @@ unittest */ T[] put(Conn = AutoProtocol, T = char, PutUnit)(const(char)[] url, const(PutUnit)[] putData, Conn conn = Conn()) - if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) ) +if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) ) { static if (is(Conn : HTTP)) { @@ -546,17 +713,21 @@ T[] put(Conn = AutoProtocol, T = char, PutUnit)(const(char)[] url, const(PutUnit } } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = put(testUrl2, "Hello world"); - assert(res == "Hello world", - "put!HTTP() returns unexpected content " ~ res); - - // TODO: need ftp server to test with - // res = get(testUrl3); - // assert(res.startsWith("\r\n/* Eratosthenes Sieve prime number calculation. */"), - // "get!FTP() returns unexpected content"); + import std.algorithm.searching : canFind; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("PUT /path")); + assert(req.bdy.canFind("PUTBODY")); + s.send(httpOK("PUTRESPONSE")); + }); + auto res = put(host ~ "/path", "PUTBODY"); + assert(res == "PUTRESPONSE"); + } } @@ -576,7 +747,7 @@ unittest * See_Also: $(LREF HTTP.Method) */ void del(Conn = AutoProtocol)(const(char)[] url, Conn conn = Conn()) - if (isCurlConn!Conn) +if (isCurlConn!Conn) { static if (is(Conn : HTTP)) { @@ -585,6 +756,9 @@ void del(Conn = AutoProtocol)(const(char)[] url, Conn conn = Conn()) } else static if (is(Conn : FTP)) { + import std.algorithm.searching : findSplitAfter; + import std.conv : text; + auto trimmed = url.findSplitAfter("ftp://")[1]; auto t = trimmed.findSplitAfter("/"); enum minDomainNameLength = 3; @@ -606,10 +780,19 @@ void del(Conn = AutoProtocol)(const(char)[] url, Conn conn = Conn()) } } -unittest +@system unittest { - if (!netAllowed()) return; - del(testUrl1); + import std.algorithm.searching : canFind; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("DELETE /path")); + s.send(httpOK()); + }); + del(host ~ "/path"); + } } @@ -617,22 +800,17 @@ unittest * * Params: * url = resource make a option call to - * optionsData = options data to send as the body of the request. An array - * of an arbitrary type is accepted and will be cast to ubyte[] - * before sending it. * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will * guess connection type and create a new instance for this call only. * * The template parameter $(D T) specifies the type to return. Possible values * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). - * Currently the HTTP RFC does not specify any usage of the optionsData and - * for this reason the example below does not send optionsData to the server. * * Example: * ---- * import std.net.curl; * auto http = HTTP(); - * options("d-lang.appspot.com/testUrl2", null, http); + * options("d-lang.appspot.com/testUrl2", http); * writeln("Allow set to " ~ http.responseHeaders["Allow"]); * ---- * @@ -641,29 +819,24 @@ unittest * * See_Also: $(LREF HTTP.Method) */ -T[] options(T = char, OptionsUnit)(const(char)[] url, - const(OptionsUnit)[] optionsData = null, - HTTP conn = HTTP()) +T[] options(T = char)(const(char)[] url, HTTP conn = HTTP()) if (is(T == char) || is(T == ubyte)) { conn.method = HTTP.Method.options; - return _basicHTTP!(T)(url, optionsData, conn); -} - -unittest -{ - if (!netAllowed()) return; - auto res = options(testUrl2, "Hello world"); - assert(res == "Hello world", - "options!HTTP() returns unexpected content " ~ res); + return _basicHTTP!(T)(url, null, conn); } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = options(testUrl1, []); - assert(res == "Hello world\n", - "options!HTTP() returns unexpected content " ~ res); + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("OPTIONS /path")); + s.send(httpOK("OPTIONSRESPONSE")); + }); + auto res = options(testServer.addr ~ "/path"); + assert(res == "OPTIONSRESPONSE"); } @@ -689,18 +862,23 @@ unittest * See_Also: $(LREF HTTP.Method) */ T[] trace(T = char)(const(char)[] url, HTTP conn = HTTP()) - if (is(T == char) || is(T == ubyte)) +if (is(T == char) || is(T == ubyte)) { conn.method = HTTP.Method.trace; return _basicHTTP!(T)(url, cast(void[]) null, conn); } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = trace(testUrl1); - assert(res == "Hello world\n", - "trace!HTTP() returns unexpected content " ~ res); + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("TRACE /path")); + s.send(httpOK("TRACERESPONSE")); + }); + auto res = trace(testServer.addr ~ "/path"); + assert(res == "TRACERESPONSE"); } @@ -708,8 +886,7 @@ unittest * * Params: * url = resource make a connect to - * conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will - * guess connection type and create a new instance for this call only. + * conn = HTTP connection to use * * The template parameter $(D T) specifies the type to return. Possible values * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). @@ -726,19 +903,70 @@ unittest * See_Also: $(LREF HTTP.Method) */ T[] connect(T = char)(const(char)[] url, HTTP conn = HTTP()) - if (is(T == char) || is(T == ubyte)) +if (is(T == char) || is(T == ubyte)) { conn.method = HTTP.Method.connect; return _basicHTTP!(T)(url, cast(void[]) null, conn); } -unittest +@system unittest { - // google appspot does not allow connect method. - // if (!netAllowed()) return; - // auto res = connect(testUrl1); - // assert(res == "Hello world\n", - // "connect!HTTP() returns unexpected content " ~ res); + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("CONNECT /path")); + s.send(httpOK("CONNECTRESPONSE")); + }); + auto res = connect(testServer.addr ~ "/path"); + assert(res == "CONNECTRESPONSE"); +} + + +/** HTTP patch content. + * + * Params: + * url = resource to patch + * patchData = data to send as the body of the request. An array + * of an arbitrary type is accepted and will be cast to ubyte[] + * before sending it. + * conn = HTTP connection to use + * + * The template parameter $(D T) specifies the type to return. Possible values + * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). + * + * Example: + * ---- + * auto http = HTTP(); + * http.addRequestHeader("Content-Type", "application/json"); + * auto content = patch("d-lang.appspot.com/testUrl2", `{"title": "Patched Title"}`, http); + * ---- + * + * Returns: + * A T[] range containing the content of the resource pointed to by the URL. + * + * See_Also: $(LREF HTTP.Method) + */ +T[] patch(T = char, PatchUnit)(const(char)[] url, const(PatchUnit)[] patchData, + HTTP conn = HTTP()) +if (is(T == char) || is(T == ubyte)) +{ + conn.method = HTTP.Method.patch; + return _basicHTTP!(T)(url, patchData, conn); +} + +@system unittest +{ + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("PATCH /path")); + assert(req.bdy.canFind("PATCHBODY")); + s.send(httpOK("PATCHRESPONSE")); + }); + auto res = patch(testServer.addr ~ "/path", "PATCHBODY"); + assert(res == "PATCHRESPONSE"); } @@ -750,31 +978,38 @@ unittest */ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP client) { + import std.algorithm.comparison : min; + import std.format : format; + + immutable doSend = sendData !is null && + (client.method == HTTP.Method.post || + client.method == HTTP.Method.put || + client.method == HTTP.Method.patch); + scope (exit) { client.onReceiveHeader = null; client.onReceiveStatusLine = null; client.onReceive = null; - if (sendData !is null && - (client.method == HTTP.Method.post || client.method == HTTP.Method.put)) + if (doSend) { client.onSend = null; client.handle.onSeek = null; + client.contentLength = 0; } } client.url = url; HTTP.StatusLine statusLine; - ubyte[] content; - string[string] headers; + import std.array : appender; + auto content = appender!(ubyte[])(); client.onReceive = (ubyte[] data) { content ~= data; return data.length; }; - if (sendData !is null && - (client.method == HTTP.Method.post || client.method == HTTP.Method.put)) + if (doSend) { client.contentLength = sendData.length; auto remainingData = sendData; @@ -782,7 +1017,7 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien { size_t minLen = min(buf.length, remainingData.length); if (minLen == 0) return 0; - buf[0..minLen] = remainingData[0..minLen]; + buf[0 .. minLen] = remainingData[0 .. minLen]; remainingData = remainingData[minLen..$]; return minLen; }; @@ -791,7 +1026,7 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien switch (mode) { case CurlSeekPos.set: - remainingData = sendData[cast(size_t)offset..$]; + remainingData = sendData[cast(size_t) offset..$]; return CurlSeek.ok; default: // As of curl 7.18.0, libcurl will not pass @@ -804,32 +1039,83 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien client.onReceiveHeader = (in char[] key, in char[] value) { - if (auto v = key in headers) + if (key == "content-length") { - *v ~= ", "; - *v ~= value; + import std.conv : to; + content.reserve(value.to!size_t); } - else - headers[key] = value.idup; }; client.onReceiveStatusLine = (HTTP.StatusLine l) { statusLine = l; }; client.perform(); enforce!CurlException(statusLine.code / 100 == 2, - format("HTTP request returned status code %s", - statusLine.code)); + format("HTTP request returned status code %d (%s)", + statusLine.code, statusLine.reason)); - // Default charset defined in HTTP RFC - auto charset = "ISO-8859-1"; - if (auto v = "content-type" in headers) - { - auto m = match(cast(char[]) (*v), regex("charset=([^;,]*)")); - if (!m.empty && m.captures.length > 1) - { - charset = m.captures[1].idup; - } - } + return _decodeContent!T(content.data, client.p.charset); +} + +@system unittest +{ + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("GET /path")); + s.send(httpNotFound()); + }); + auto e = collectException!CurlException(get(testServer.addr ~ "/path")); + assert(e.msg == "HTTP request returned status code 404 (Not Found)"); +} - return _decodeContent!T(content, charset); +// Bugzilla 14760 - content length must be reset after post +@system unittest +{ + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("POST /")); + assert(req.bdy.canFind("POSTBODY")); + s.send(httpOK("POSTRESPONSE")); + + req = s.recvReq; + assert(req.hdrs.canFind("TRACE /")); + assert(req.bdy.empty); + s.blocking = false; + ubyte[6] buf = void; + assert(s.receive(buf[]) < 0); + s.send(httpOK("TRACERESPONSE")); + }); + auto http = HTTP(); + auto res = post(testServer.addr, "POSTBODY", http); + assert(res == "POSTRESPONSE"); + res = trace(testServer.addr, http); + assert(res == "TRACERESPONSE"); +} + +@system unittest // charset detection and transcoding to T +{ + testServer.handle((s) { + s.send("HTTP/1.1 200 OK\r\n"~ + "Content-Length: 4\r\n"~ + "Content-Type: text/plain; charset=utf-8\r\n" ~ + "\r\n" ~ + "äbc"); + }); + auto client = HTTP(); + auto result = _basicHTTP!char(testServer.addr, "", client); + assert(result == "äbc"); + + testServer.handle((s) { + s.send("HTTP/1.1 200 OK\r\n"~ + "Content-Length: 3\r\n"~ + "Content-Type: text/plain; charset=iso-8859-1\r\n" ~ + "\r\n" ~ + 0xE4 ~ "bc"); + }); + client = HTTP(); + result = _basicHTTP!char(testServer.addr, "", client); + assert(result == "äbc"); } /* @@ -840,6 +1126,8 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien */ private auto _basicFTP(T)(const(char)[] url, const(void)[] sendData, FTP client) { + import std.algorithm.comparison : min; + scope (exit) { client.onReceive = null; @@ -866,7 +1154,7 @@ private auto _basicFTP(T)(const(char)[] url, const(void)[] sendData, FTP client) { size_t minLen = min(buf.length, sendData.length); if (minLen == 0) return 0; - buf[0..minLen] = sendData[0..minLen]; + buf[0 .. minLen] = sendData[0 .. minLen]; sendData = sendData[minLen..$]; return minLen; }; @@ -888,6 +1176,8 @@ private auto _decodeContent(T)(ubyte[] content, string encoding) } else { + import std.format : format; + // Optimally just return the utf8 encoded content if (encoding == "UTF-8") return cast(char[])(content); @@ -953,7 +1243,7 @@ struct ByLineBuffer(Char) * * Params: * url = The url to receive content from - * keepTerminator = KeepTerminator.yes signals that the line terminator should be + * keepTerminator = $(D Yes.keepTerminator) signals that the line terminator should be * returned as part of the lines in the range. * terminator = The character that terminates a line * conn = The connection to use e.g. HTTP or FTP. @@ -962,7 +1252,7 @@ struct ByLineBuffer(Char) * A range of Char[] with the content of the resource pointer to by the URL */ auto byLine(Conn = AutoProtocol, Terminator = char, Char = char) - (const(char)[] url, KeepTerminator keepTerminator = KeepTerminator.no, + (const(char)[] url, KeepTerminator keepTerminator = No.keepTerminator, Terminator terminator = '\n', Conn conn = Conn()) if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) { @@ -997,6 +1287,8 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) void popFront() { + import std.algorithm.searching : findSplitAfter, findSplit; + enforce!CurlException(currentValid, "Cannot call popFront() on empty range"); if (lines.empty) { @@ -1028,21 +1320,21 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) } auto result = _getForRange!Char(url, conn); - return SyncLineInputRange(result, keepTerminator == KeepTerminator.yes, terminator); + return SyncLineInputRange(result, keepTerminator == Yes.keepTerminator, terminator); } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = byLine(testUrl1); - auto line = res.front; - assert(line == "Hello world", - "byLine!HTTP() returns unexpected content: " ~ line); - - auto res2 = byLine(testUrl1, KeepTerminator.no, '\n', HTTP()); - line = res2.front; - assert(line == "Hello world", - "byLine!HTTP() returns unexpected content: " ~ line); + import std.algorithm.comparison : equal; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + auto req = s.recvReq; + s.send(httpOK("Line1\nLine2\nLine3")); + }); + assert(byLine(host).equal(["Line1", "Line2", "Line3"])); + } } /** HTTP/FTP fetch content as a range of chunks. @@ -1068,7 +1360,7 @@ unittest */ auto byChunk(Conn = AutoProtocol) (const(char)[] url, size_t chunkSize = 1024, Conn conn = Conn()) - if (isCurlConn!(Conn)) +if (isCurlConn!(Conn)) { static struct SyncChunkInputRange { @@ -1091,7 +1383,7 @@ auto byChunk(Conn = AutoProtocol) { size_t nextOffset = offset + chunkSize; if (nextOffset > _bytes.length) nextOffset = _bytes.length; - return _bytes[offset..nextOffset]; + return _bytes[offset .. nextOffset]; } @safe void popFront() @@ -1105,19 +1397,18 @@ auto byChunk(Conn = AutoProtocol) return SyncChunkInputRange(result, chunkSize); } -unittest +@system unittest { - if (!netAllowed()) return; - - auto res = byChunk(testUrl1); - auto line = res.front; - assert(line == cast(ubyte[])"Hello world\n", - "byLineAsync!HTTP() returns unexpected content " ~ to!string(line)); + import std.algorithm.comparison : equal; - auto res2 = byChunk(testUrl1, 1024, HTTP()); - line = res2.front; - assert(line == cast(ubyte[])"Hello world\n", - "byLineAsync!HTTP() returns unexpected content: " ~ to!string(line)); + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + auto req = s.recvReq; + s.send(httpOK(cast(ubyte[])[0, 1, 2, 3, 4, 5])); + }); + assert(byChunk(host, 2).equal([[0, 1], [2, 3], [4, 5]])); + } } private T[] _getForRange(T,Conn)(const(char)[] url, Conn conn) @@ -1154,6 +1445,7 @@ private mixin template WorkerThreadProtocol(Unit, alias units) @property Unit[] front() { + import std.format : format; tryEnsureUnits(); assert(state == State.gotUnits, format("Expected %s but got $s", @@ -1163,6 +1455,7 @@ private mixin template WorkerThreadProtocol(Unit, alias units) void popFront() { + import std.format : format; tryEnsureUnits(); assert(state == State.gotUnits, format("Expected %s but got $s", @@ -1178,6 +1471,8 @@ private mixin template WorkerThreadProtocol(Unit, alias units) */ bool wait(Duration d) { + import std.datetime : StopWatch; + if (state == State.gotUnits) return true; @@ -1258,12 +1553,10 @@ private mixin template WorkerThreadProtocol(Unit, alias units) } } -// Workaround bug #2458 -// It should really be defined inside the byLineAsync method. -// Do not create instances of this struct since it will be -// moved when the bug has been fixed. +// @@@@BUG 15831@@@@ +// this should be inside byLineAsync // Range that reads one line at a time asynchronously. -static struct AsyncLineInputRange(Char) +private static struct AsyncLineInputRange(Char) { private Char[] line; mixin WorkerThreadProtocol!(Char, line); @@ -1279,7 +1572,7 @@ static struct AsyncLineInputRange(Char) // Send buffers to other thread for it to use. Since no mechanism is in // place for moving ownership a cast to shared is done here and casted // back to non-shared in the receiving end. - foreach (i ; 0..transmitBuffers) + foreach (i ; 0 .. transmitBuffers) { auto arr = new Char[](bufferSize); workerTid.send(cast(immutable(Char[]))arr); @@ -1287,7 +1580,6 @@ static struct AsyncLineInputRange(Char) } } - /** HTTP/FTP fetch content as a range of lines asynchronously. * * A range of lines is returned immediately and the request that fetches the @@ -1327,8 +1619,8 @@ static struct AsyncLineInputRange(Char) * // Get a line in a background thread and wait in * // main thread for 2 seconds for it to arrive. * auto range3 = byLineAsync("dlang.com"); - * if (range.wait(dur!"seconds"(2))) - * writeln(range.front); + * if (range3.wait(dur!"seconds"(2))) + * writeln(range3.front); * else * writeln("No line received after 2 seconds!"); * ---- @@ -1336,7 +1628,7 @@ static struct AsyncLineInputRange(Char) * Params: * url = The url to receive content from * postData = Data to HTTP Post - * keepTerminator = KeepTerminator.yes signals that the line terminator should be + * keepTerminator = $(D Yes.keepTerminator) signals that the line terminator should be * returned as part of the lines in the range. * terminator = The character that terminates a line * transmitBuffers = The number of lines buffered asynchronously @@ -1348,10 +1640,10 @@ static struct AsyncLineInputRange(Char) */ auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char, PostUnit) (const(char)[] url, const(PostUnit)[] postData, - KeepTerminator keepTerminator = KeepTerminator.no, + KeepTerminator keepTerminator = No.keepTerminator, Terminator terminator = '\n', size_t transmitBuffers = 10, Conn conn = Conn()) - if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) +if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) { static if (is(Conn : AutoProtocol)) { @@ -1369,7 +1661,7 @@ auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char, PostUnit) auto tid = spawn(&_spawnAsync!(Conn, Char, Terminator)); tid.send(thisTid); tid.send(terminator); - tid.send(keepTerminator == KeepTerminator.yes); + tid.send(keepTerminator == Yes.keepTerminator); _asyncDuplicateConnection(url, conn, postData, tid); @@ -1380,46 +1672,44 @@ auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char, PostUnit) /// ditto auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char) - (const(char)[] url, KeepTerminator keepTerminator = KeepTerminator.no, + (const(char)[] url, KeepTerminator keepTerminator = No.keepTerminator, Terminator terminator = '\n', size_t transmitBuffers = 10, Conn conn = Conn()) { static if (is(Conn : AutoProtocol)) { if (isFTPUrl(url)) - return byLineAsync(url, cast(void[])null, keepTerminator, + return byLineAsync(url, cast(void[]) null, keepTerminator, terminator, transmitBuffers, FTP()); else - return byLineAsync(url, cast(void[])null, keepTerminator, + return byLineAsync(url, cast(void[]) null, keepTerminator, terminator, transmitBuffers, HTTP()); } else { - return byLineAsync(url, cast(void[])null, keepTerminator, + return byLineAsync(url, cast(void[]) null, keepTerminator, terminator, transmitBuffers, conn); } } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = byLineAsync(testUrl2, "Hello world"); - auto line = res.front; - assert(line == "Hello world", - "byLineAsync!HTTP() returns unexpected content " ~ line); - res = byLineAsync(testUrl1); - line = res.front; - assert(line == "Hello world", - "byLineAsync!HTTP() returns unexpected content: " ~ line); -} + import std.algorithm.comparison : equal; + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + auto req = s.recvReq; + s.send(httpOK("Line1\nLine2\nLine3")); + }); + assert(byLineAsync(host).equal(["Line1", "Line2", "Line3"])); + } +} -// Workaround bug #2458 -// It should really be defined inside the byLineAsync method. -// Do not create instances of this struct since it will be -// moved when the bug has been fixed. +// @@@@BUG 15831@@@@ +// this should be inside byLineAsync // Range that reads one chunk at a time asynchronously. -static struct AsyncChunkInputRange +private static struct AsyncChunkInputRange { private ubyte[] chunk; mixin WorkerThreadProtocol!(ubyte, chunk); @@ -1435,7 +1725,7 @@ static struct AsyncChunkInputRange // Send buffers to other thread for it to use. Since no mechanism is in // place for moving ownership a cast to shared is done here and a cast // back to non-shared in the receiving end. - foreach (i ; 0..transmitBuffers) + foreach (i ; 0 .. transmitBuffers) { ubyte[] arr = new ubyte[](chunkSize); workerTid.send(cast(immutable(ubyte[]))arr); @@ -1482,8 +1772,8 @@ static struct AsyncChunkInputRange * // Get a line in a background thread and wait in * // main thread for 2 seconds for it to arrive. * auto range3 = byChunkAsync("dlang.com", 10); - * if (range.wait(dur!"seconds"(2))) - * writeln(range.front); + * if (range3.wait(dur!"seconds"(2))) + * writeln(range3.front); * else * writeln("No chunk received after 2 seconds!"); * ---- @@ -1503,7 +1793,7 @@ auto byChunkAsync(Conn = AutoProtocol, PostUnit) (const(char)[] url, const(PostUnit)[] postData, size_t chunkSize = 1024, size_t transmitBuffers = 10, Conn conn = Conn()) - if (isCurlConn!(Conn)) +if (isCurlConn!(Conn)) { static if (is(Conn : AutoProtocol)) { @@ -1532,35 +1822,36 @@ auto byChunkAsync(Conn = AutoProtocol) (const(char)[] url, size_t chunkSize = 1024, size_t transmitBuffers = 10, Conn conn = Conn()) - if (isCurlConn!(Conn)) +if (isCurlConn!(Conn)) { static if (is(Conn : AutoProtocol)) { if (isFTPUrl(url)) - return byChunkAsync(url, cast(void[])null, chunkSize, + return byChunkAsync(url, cast(void[]) null, chunkSize, transmitBuffers, FTP()); else - return byChunkAsync(url, cast(void[])null, chunkSize, + return byChunkAsync(url, cast(void[]) null, chunkSize, transmitBuffers, HTTP()); } else { - return byChunkAsync(url, cast(void[])null, chunkSize, + return byChunkAsync(url, cast(void[]) null, chunkSize, transmitBuffers, conn); } } -unittest +@system unittest { - if (!netAllowed()) return; - auto res = byChunkAsync(testUrl2, "Hello world"); - auto line = res.front; - assert(line == cast(ubyte[])"Hello world", - "byLineAsync!HTTP() returns unexpected content " ~ to!string(line)); - res = byChunkAsync(testUrl1); - line = res.front; - assert(line == cast(ubyte[])"Hello world\n", - "byLineAsync!HTTP() returns unexpected content: " ~ to!string(line)); + import std.algorithm.comparison : equal; + + foreach (host; [testServer.addr, "http://"~testServer.addr]) + { + testServer.handle((s) { + auto req = s.recvReq; + s.send(httpOK(cast(ubyte[])[0, 1, 2, 3, 4, 5])); + }); + assert(byChunkAsync(host, 2).equal([[0, 1], [2, 3], [4, 5]])); + } } @@ -1596,14 +1887,14 @@ private void _asyncDuplicateConnection(Conn, PostData) connDup.handle.set(CurlOption.copypostfields, cast(void*) postData.ptr); } - tid.send(cast(ulong)connDup.handle.handle); + tid.send(cast(ulong) connDup.handle.handle); tid.send(connDup.method); } else { enforce!CurlException(postData is null, "Cannot put ftp data using byLineAsync()"); - tid.send(cast(ulong)connDup.handle.handle); + tid.send(cast(ulong) connDup.handle.handle); tid.send(HTTP.Method.undefined); } connDup.p.curl.handle = null; // make sure handle is not freed @@ -1631,7 +1922,7 @@ private mixin template Protocol() /** The curl handle used by this connection. */ - @property ref Curl handle() + @property ref Curl handle() return { return p.curl; } @@ -1684,7 +1975,7 @@ private mixin template Protocol() // Network settings /** Proxy - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) */ @property void proxy(const(char)[] host) { @@ -1692,7 +1983,7 @@ private mixin template Protocol() } /** Proxy port - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) */ @property void proxyPort(ushort port) { @@ -1703,7 +1994,7 @@ private mixin template Protocol() alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) */ @property void proxyType(CurlProxy type) { @@ -1725,7 +2016,7 @@ private mixin template Protocol() * theprotocol.netInterface = [ 192, 168, 1, 32 ]; * ---- * - * See: $(XREF socket, InternetAddress) + * See: $(REF InternetAddress, std,socket) */ @property void netInterface(const(char)[] i) { @@ -1735,7 +2026,8 @@ private mixin template Protocol() /// ditto @property void netInterface(const(ubyte)[4] i) { - auto str = format("%d.%d.%d.%d", i[0], i[1], i[2], i[3]); + import std.format : format; + const str = format("%d.%d.%d.%d", i[0], i[1], i[2], i[3]); netInterface = str; } @@ -1752,7 +2044,7 @@ private mixin template Protocol() */ @property void localPort(ushort port) { - p.curl.set(CurlOption.localport, cast(long)port); + p.curl.set(CurlOption.localport, cast(long) port); } /** @@ -1764,11 +2056,11 @@ private mixin template Protocol() */ @property void localPortRange(ushort range) { - p.curl.set(CurlOption.localportrange, cast(long)range); + p.curl.set(CurlOption.localportrange, cast(long) range); } /** Set the tcp no-delay socket option on or off. - See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) + See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) */ @property void tcpNoDelay(bool on) { @@ -1776,7 +2068,7 @@ private mixin template Protocol() } /** Sets whether SSL peer certificates should be verified. - See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYPEER, verifypeer) + See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYPEER, verifypeer) */ @property void verifyPeer(bool on) { @@ -1784,7 +2076,7 @@ private mixin template Protocol() } /** Sets whether the host within an SSL certificate should be verified. - See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYHOST, verifypeer) + See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYHOST, verifypeer) */ @property void verifyHost(bool on) { @@ -1809,20 +2101,26 @@ private mixin template Protocol() void setAuthentication(const(char)[] username, const(char)[] password, const(char)[] domain = "") { + import std.format : format; if (!domain.empty) username = format("%s/%s", domain, username); p.curl.set(CurlOption.userpwd, format("%s:%s", username, password)); } - unittest + @system unittest { - if (!netAllowed()) return; - auto http = HTTP("http://www.protected.com"); - http.onReceiveHeader = - (in char[] key, - in char[] value) { /* writeln(key ~ ": " ~ value); */ }; + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq; + assert(req.hdrs.canFind("GET /")); + assert(req.hdrs.canFind("Basic dXNlcjpwYXNz")); + s.send(httpOK()); + }); + + auto http = HTTP(testServer.addr); http.onReceive = (ubyte[] data) { return data.length; }; - http.setAuthentication("myuser", "mypassword"); + http.setAuthentication("user", "pass"); http.perform(); } @@ -1835,6 +2133,9 @@ private mixin template Protocol() */ void setProxyAuthentication(const(char)[] username, const(char)[] password) { + import std.array : replace; + import std.format : format; + p.curl.set(CurlOption.proxyuserpwd, format("%s:%s", username.replace(":", "%3A"), @@ -1862,10 +2163,10 @@ private mixin template Protocol() * auto client = HTTP("dlang.org"); * client.onSend = delegate size_t(void[] data) * { - * auto m = cast(void[])msg; + * auto m = cast(void[]) msg; * size_t length = m.length > data.length ? data.length : m.length; * if (length == 0) return 0; - * data[0..length] = m[0..length]; + * data[0 .. length] = m[0 .. length]; * msg = msg[length..$]; * return length; * }; @@ -1949,14 +2250,14 @@ decodeString(Char = char)(const(ubyte)[] data, size_t maxChars = size_t.max) { Char[] res; - auto startLen = data.length; + immutable startLen = data.length; size_t charsDecoded = 0; while (data.length && charsDecoded < maxChars) { - dchar dc = scheme.safeDecode(data); + immutable dchar dc = scheme.safeDecode(data); if (dc == INVALID_SEQUENCE) { - return typeof(return)(size_t.max, cast(Char[])null); + return typeof(return)(size_t.max, cast(Char[]) null); } charsDecoded++; res ~= dc; @@ -1987,19 +2288,19 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc, EncodingScheme scheme, Terminator terminator) { - auto startLen = src.length; - size_t charsDecoded = 0; + import std.algorithm.searching : endsWith; + // if there is anything in the basesrc then try to decode that // first. if (basesrc.length != 0) { // Try to ensure 4 entries in the basesrc by copying from src. - auto blen = basesrc.length; - size_t len = (basesrc.length + src.length) >= 4 ? + immutable blen = basesrc.length; + immutable len = (basesrc.length + src.length) >= 4 ? 4 : basesrc.length + src.length; basesrc.length = len; - dchar dc = scheme.safeDecode(basesrc); + immutable dchar dc = scheme.safeDecode(basesrc); if (dc == INVALID_SEQUENCE) { enforce!CurlException(len != 4, "Invalid code sequence"); @@ -2012,7 +2313,7 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc, while (src.length) { - auto lsrc = src; + const lsrc = src; dchar dc = scheme.safeDecode(src); if (dc == INVALID_SEQUENCE) { @@ -2053,10 +2354,10 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc, * http.contentLength = msg.length; * http.onSend = (void[] data) * { - * auto m = cast(void[])msg; + * auto m = cast(void[]) msg; * size_t len = m.length > data.length ? data.length : m.length; * if (len == 0) return len; - * data[0..len] = m[0..len]; + * data[0 .. len] = m[0 .. len]; * msg = msg[len..$]; * return len; * }; @@ -2076,14 +2377,16 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc, * http.perform(); * --- * - * See_Also: $(WEB www.ietf.org/rfc/rfc2616.txt, RFC2616) + * See_Also: $(HTTP www.ietf.org/rfc/rfc2616.txt, RFC2616) * */ struct HTTP { mixin Protocol; - /// Authentication method equal to $(ECXREF curl, CurlAuth) + import std.datetime : SysTime; + + /// Authentication method equal to $(REF CurlAuth, etc,c,curl) alias AuthMethod = CurlAuth; static private uint defaultMaxRedirects = 10; @@ -2093,7 +2396,7 @@ struct HTTP ~this() { if (headersOut !is null) - curl_slist_free_all(headersOut); + Curl.curl.slist_free_all(headersOut); if (curl.handle !is null) // work around RefCounted/emplace bug curl.shutdown(); } @@ -2109,9 +2412,14 @@ struct HTTP /// The HTTP method to use. Method method = Method.undefined; - @property void onReceiveHeader(void delegate(in char[] key, + @system @property void onReceiveHeader(void delegate(in char[] key, in char[] value) callback) { + import std.algorithm.searching : startsWith; + import std.conv : to; + import std.regex : regex, match; + import std.uni : toLower; + // Wrap incoming callback in order to separate http status line from // http headers. On redirected requests there may be several such // status lines. The last one is the one recorded. @@ -2127,10 +2435,9 @@ struct HTTP } if (header.startsWith("HTTP/")) { - string[string] empty; - headersIn = empty; // clear + headersIn.clear(); - auto m = match(header, regex(r"^HTTP/(\d+)\.(\d+) (\d+) (.*)$")); + const m = match(header, regex(r"^HTTP/(\d+)\.(\d+) (\d+) (.*)$")); if (m.empty) { // Invalid status line @@ -2154,7 +2461,7 @@ struct HTTP if (fieldName == "content-type") { auto mct = match(cast(char[]) m.captures[2], - regex("charset=([^;]*)")); + regex("charset=([^;]*)", "i")); if (!mct.empty && mct.captures.length > 1) charset = mct.captures[1].idup; } @@ -2163,7 +2470,7 @@ struct HTTP callback(fieldName, m.captures[2]); headersIn[fieldName] = m.captures[2].idup; } - catch(UTFException e) + catch (UTFException e) { //munch it - a header should be all ASCII, any "wrong UTF" is broken header } @@ -2175,9 +2482,9 @@ struct HTTP private RefCounted!Impl p; - /** Time condition enumeration as an alias of $(ECXREF curl, CurlTimeCond) + /** Time condition enumeration as an alias of $(REF CurlTimeCond, etc,c,curl) - $(WEB www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) + $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) */ alias TimeCond = CurlTimeCond; @@ -2192,6 +2499,7 @@ struct HTTP return http; } + /// static HTTP opCall() { HTTP http; @@ -2199,6 +2507,7 @@ struct HTTP return http; } + /// HTTP dup() { HTTP copy; @@ -2208,7 +2517,7 @@ struct HTTP curl_slist* newlist = null; while (cur) { - newlist = curl_slist_append(newlist, cur.data); + newlist = Curl.curl.slist_append(newlist, cur.data); cur = cur.next; } copy.p.headersOut = newlist; @@ -2228,7 +2537,6 @@ struct HTTP setUserAgent(HTTP.defaultUserAgent); dataTimeout = _defaultDataTimeout; onReceiveHeader = null; - version (unittest) verbose = true; verifyPeer = true; verifyHost = true; } @@ -2243,45 +2551,61 @@ struct HTTP Params: throwOnError = whether to throw an exception or return a CurlCode on error */ - CurlCode perform(ThrowOnError throwOnError = ThrowOnError.yes) + CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) { p.status.reset(); + CurlOption opt; final switch (p.method) { case Method.head: p.curl.set(CurlOption.nobody, 1L); + opt = CurlOption.nobody; break; case Method.undefined: case Method.get: p.curl.set(CurlOption.httpget, 1L); + opt = CurlOption.httpget; break; case Method.post: p.curl.set(CurlOption.post, 1L); + opt = CurlOption.post; break; case Method.put: p.curl.set(CurlOption.upload, 1L); + opt = CurlOption.upload; break; case Method.del: p.curl.set(CurlOption.customrequest, "DELETE"); + opt = CurlOption.customrequest; break; case Method.options: p.curl.set(CurlOption.customrequest, "OPTIONS"); + opt = CurlOption.customrequest; break; case Method.trace: p.curl.set(CurlOption.customrequest, "TRACE"); + opt = CurlOption.customrequest; break; case Method.connect: p.curl.set(CurlOption.customrequest, "CONNECT"); + opt = CurlOption.customrequest; + break; + case Method.patch: + p.curl.set(CurlOption.customrequest, "PATCH"); + opt = CurlOption.customrequest; break; } + scope (exit) p.curl.clear(opt); return p.curl.perform(throwOnError); } /// The URL to specify the location of the resource. @property void url(const(char)[] url) { + import std.algorithm.searching : startsWith; + import std.uni : toLower; if (!startsWith(url.toLower(), "http://", "https://")) url = "http://" ~ url; p.curl.set(CurlOption.url, url); @@ -2333,12 +2657,12 @@ struct HTTP // Network settings /** Proxy - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) */ @property void proxy(const(char)[] host); /** Proxy port - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) */ @property void proxyPort(ushort port); @@ -2346,7 +2670,7 @@ struct HTTP alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) */ @property void proxyType(CurlProxy type); @@ -2362,7 +2686,7 @@ struct HTTP * theprotocol.netInterface = [ 192, 168, 1, 32 ]; * ---- * - * See: $(XREF socket, InternetAddress) + * See: $(REF InternetAddress, std,socket) */ @property void netInterface(const(char)[] i); @@ -2389,7 +2713,7 @@ struct HTTP @property void localPortRange(ushort range); /** Set the tcp no-delay socket option on or off. - See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) + See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) */ @property void tcpNoDelay(bool on); @@ -2440,10 +2764,10 @@ struct HTTP * auto client = HTTP("dlang.org"); * client.onSend = delegate size_t(void[] data) * { - * auto m = cast(void[])msg; + * auto m = cast(void[]) msg; * size_t length = m.length > data.length ? data.length : m.length; * if (length == 0) return 0; - * data[0..length] = m[0..length]; + * data[0 .. length] = m[0 .. length]; * msg = msg[length..$]; * return length; * }; @@ -2508,7 +2832,7 @@ struct HTTP void clearRequestHeaders() { if (p.headersOut !is null) - curl_slist_free_all(p.headersOut); + Curl.curl.slist_free_all(p.headersOut); p.headersOut = null; p.curl.clear(CurlOption.httpheader); } @@ -2523,16 +2847,19 @@ struct HTTP * import std.net.curl; * auto client = HTTP(); * client.addRequestHeader("X-Custom-ABC", "This is the custom value"); - * string content = get("dlang.org", client); + * auto content = get("dlang.org", client); * --- */ void addRequestHeader(const(char)[] name, const(char)[] value) { + import std.format : format; + import std.uni : icmp; + if (icmp(name, "User-Agent") == 0) return setUserAgent(value); string nv = format("%s: %s", name, value); - p.headersOut = curl_slist_append(p.headersOut, - nv.tempCString().buffPtr); + p.headersOut = Curl.curl.slist_append(p.headersOut, + nv.tempCString().buffPtr); p.curl.set(CurlOption.httpheader, p.headersOut); } @@ -2540,22 +2867,26 @@ struct HTTP * The default "User-Agent" value send with a request. * It has the form "Phobos-std.net.curl/$(I PHOBOS_VERSION) (libcurl/$(I CURL_VERSION))" */ - static immutable string defaultUserAgent; - - shared static this() + static string defaultUserAgent() @property { import std.compiler : version_major, version_minor; + import std.format : format, sformat; // http://curl.haxx.se/docs/versions.html enum fmt = "Phobos-std.net.curl/%d.%03d (libcurl/%d.%d.%d)"; enum maxLen = fmt.length - "%d%03d%d%d%d".length + 10 + 10 + 3 + 3 + 3; - __gshared char[maxLen] buf = void; + static char[maxLen] buf = void; + static string userAgent; - auto curlVer = curl_version_info(CURLVERSION_NOW).version_num; - defaultUserAgent = cast(immutable)sformat( - buf, fmt, version_major, version_minor, - curlVer >> 16 & 0xFF, curlVer >> 8 & 0xFF, curlVer & 0xFF); + if (!userAgent.length) + { + auto curlVer = Curl.curl.version_info(CURLVERSION_NOW).version_num; + userAgent = cast(immutable) sformat( + buf, fmt, version_major, version_minor, + curlVer >> 16 & 0xFF, curlVer >> 8 & 0xFF, curlVer & 0xFF); + } + return userAgent; } /** Set the value of the user agent request header field. @@ -2569,6 +2900,46 @@ struct HTTP p.curl.set(CurlOption.useragent, userAgent); } + /** + * Get various timings defined in $(REF CurlInfo, etc, c, curl). + * The value is usable only if the return value is equal to $(D etc.c.curl.CurlError.ok). + * + * Params: + * timing = one of the timings defined in $(REF CurlInfo, etc, c, curl). + * The values are: + * $(D etc.c.curl.CurlInfo.namelookup_time), + * $(D etc.c.curl.CurlInfo.connect_time), + * $(D etc.c.curl.CurlInfo.pretransfer_time), + * $(D etc.c.curl.CurlInfo.starttransfer_time), + * $(D etc.c.curl.CurlInfo.redirect_time), + * $(D etc.c.curl.CurlInfo.appconnect_time), + * $(D etc.c.curl.CurlInfo.total_time). + * val = the actual value of the inquired timing. + * + * Returns: + * The return code of the operation. The value stored in val + * should be used only if the return value is $(D etc.c.curl.CurlInfo.ok). + * + * Example: + * --- + * import std.net.curl; + * import etc.c.curl : CurlError, CurlInfo; + * + * auto client = HTTP("dlang.org"); + * client.perform(); + * + * double val; + * CurlCode code; + * + * code = http.getTiming(CurlInfo.namelookup_time, val); + * assert(code == CurlError.ok); + *--- + */ + CurlCode getTiming(CurlInfo timing, ref double val) + { + return p.curl.getTiming(timing, val); + } + /** The headers read from a successful response. * */ @@ -2637,7 +3008,7 @@ struct HTTP cond = $(D CurlTimeCond.{none,ifmodsince,ifunmodsince,lastmod}) timestamp = Timestamp for the condition - $(WEB www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) + $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) */ void setTimeCondition(HTTP.TimeCond cond, SysTime timestamp) { @@ -2707,25 +3078,33 @@ struct HTTP // cannot use callback when specifying data directly so it is disabled here. p.curl.clear(CurlOption.readfunction); addRequestHeader("Content-Type", contentType); - p.curl.set(CurlOption.postfields, cast(void*)data.ptr); + p.curl.set(CurlOption.postfields, cast(void*) data.ptr); p.curl.set(CurlOption.postfieldsize, data.length); if (method == Method.undefined) method = Method.post; } - unittest + @system unittest { - if (!netAllowed()) return; - ubyte[] data; - foreach (n; 0..256) - data ~= cast(ubyte)n; - auto http = HTTP(testUrl2); + import std.algorithm.searching : canFind; + + testServer.handle((s) { + auto req = s.recvReq!ubyte; + assert(req.hdrs.canFind("POST /path")); + assert(req.bdy.canFind(cast(ubyte[])[0, 1, 2, 3, 4])); + assert(req.bdy.canFind(cast(ubyte[])[253, 254, 255])); + s.send(httpOK(cast(ubyte[])[17, 27, 35, 41])); + }); + auto data = new ubyte[](256); + foreach (i, ref ub; data) + ub = cast(ubyte) i; + + auto http = HTTP(testServer.addr~"/path"); http.postData = data; - ubyte[] result; - http.onReceive = (ubyte[] data) { result ~= data; return data.length; }; + ubyte[] res; + http.onReceive = (data) { res ~= data; return data.length; }; http.perform(); - assert(data == result, - "HTTP POST with binary data returns unexpected content (" ~ text(result.length) ~ " bytes)"); + assert(res == cast(ubyte[])[17, 27, 35, 41]); } /** @@ -2765,23 +3144,28 @@ struct HTTP /** The content length in bytes when using request that has content e.g. POST/PUT and not using chunked transfer. Is set as the - "Content-Length" header. Set to size_t.max to reset to chunked transfer. + "Content-Length" header. Set to ulong.max to reset to chunked transfer. */ - @property void contentLength(size_t len) + @property void contentLength(ulong len) { + import std.conv : to; + CurlOption lenOpt; // Force post if necessary - if (p.method != Method.put && p.method != Method.post) + if (p.method != Method.put && p.method != Method.post && + p.method != Method.patch) p.method = Method.post; - if (p.method == Method.put) - lenOpt = CurlOption.infilesize_large; - else - // post + if (p.method == Method.post || p.method == Method.patch) lenOpt = CurlOption.postfieldsize_large; + else + lenOpt = CurlOption.infilesize_large; + + if (size_t.max != ulong.max && len == size_t.max) + len = ulong.max; // check size_t.max for backwards compat, turn into error - if (len == size_t.max) + if (len == ulong.max) { // HTTP 1.1 supports requests with no length header set. addRequestHeader("Transfer-Encoding", "chunked"); @@ -2789,7 +3173,7 @@ struct HTTP } else { - p.curl.set(lenOpt, len); + p.curl.set(lenOpt, to!curl_off_t(len)); } } @@ -2819,8 +3203,8 @@ struct HTTP } } - /** The standard HTTP methods : - * $(WEB www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1, _RFC2616 Section 5.1.1) + /** The standard HTTP methods : + * $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1, _RFC2616 Section 5.1.1) */ enum Method { @@ -2832,7 +3216,8 @@ struct HTTP del, /// options, /// trace, /// - connect /// + connect, /// + patch, /// } /** @@ -2858,8 +3243,9 @@ struct HTTP } /// - string toString() + string toString() const { + import std.format : format; return format("%s %s (%s.%s)", code, reason, majorVersion, minorVersion); } @@ -2867,10 +3253,49 @@ struct HTTP } // HTTP +@system unittest // charset/Charset/CHARSET/... +{ + import std.meta : AliasSeq; + + foreach (c; AliasSeq!("charset", "Charset", "CHARSET", "CharSet", "charSet", + "ChArSeT", "cHaRsEt")) + { + testServer.handle((s) { + s.send("HTTP/1.1 200 OK\r\n"~ + "Content-Length: 0\r\n"~ + "Content-Type: text/plain; " ~ c ~ "=foo\r\n" ~ + "\r\n"); + }); + + auto http = HTTP(testServer.addr); + http.perform(); + assert(http.p.charset == "foo"); + + // Bugzilla 16736 + double val; + CurlCode code; + + code = http.getTiming(CurlInfo.total_time, val); + assert(code == CurlError.ok); + code = http.getTiming(CurlInfo.namelookup_time, val); + assert(code == CurlError.ok); + code = http.getTiming(CurlInfo.connect_time, val); + assert(code == CurlError.ok); + code = http.getTiming(CurlInfo.pretransfer_time, val); + assert(code == CurlError.ok); + code = http.getTiming(CurlInfo.starttransfer_time, val); + assert(code == CurlError.ok); + code = http.getTiming(CurlInfo.redirect_time, val); + assert(code == CurlError.ok); + code = http.getTiming(CurlInfo.appconnect_time, val); + assert(code == CurlError.ok); + } +} + /** FTP client functionality. - See_Also: $(WEB tools.ietf.org/html/rfc959, RFC959) + See_Also: $(HTTP tools.ietf.org/html/rfc959, RFC959) */ struct FTP { @@ -2882,7 +3307,7 @@ struct FTP ~this() { if (commands !is null) - curl_slist_free_all(commands); + Curl.curl.slist_free_all(commands); if (curl.handle !is null) // work around RefCounted/emplace bug curl.shutdown(); } @@ -2904,6 +3329,7 @@ struct FTP return ftp; } + /// static FTP opCall() { FTP ftp; @@ -2911,6 +3337,7 @@ struct FTP return ftp; } + /// FTP dup() { FTP copy = FTP(); @@ -2921,7 +3348,7 @@ struct FTP curl_slist* newlist = null; while (cur) { - newlist = curl_slist_append(newlist, cur.data); + newlist = Curl.curl.slist_append(newlist, cur.data); cur = cur.next; } copy.p.commands = newlist; @@ -2935,7 +3362,6 @@ struct FTP p.curl.initialize(); p.encoding = "ISO-8859-1"; dataTimeout = _defaultDataTimeout; - version (unittest) verbose = true; } /** @@ -2948,7 +3374,7 @@ struct FTP Params: throwOnError = whether to throw an exception or return a CurlCode on error */ - CurlCode perform(ThrowOnError throwOnError = ThrowOnError.yes) + CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) { return p.curl.perform(throwOnError); } @@ -2956,6 +3382,9 @@ struct FTP /// The URL to specify the location of the resource. @property void url(const(char)[] url) { + import std.algorithm.searching : startsWith; + import std.uni : toLower; + if (!startsWith(url.toLower(), "ftp://", "ftps://")) url = "ftp://" ~ url; p.curl.set(CurlOption.url, url); @@ -3001,12 +3430,12 @@ struct FTP // Network settings /** Proxy - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) */ @property void proxy(const(char)[] host); /** Proxy port - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) */ @property void proxyPort(ushort port); @@ -3014,7 +3443,7 @@ struct FTP alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) */ @property void proxyType(CurlProxy type); @@ -3030,7 +3459,7 @@ struct FTP * theprotocol.netInterface = [ 192, 168, 1, 32 ]; * ---- * - * See: $(XREF socket, InternetAddress) + * See: $(REF InternetAddress, std,socket) */ @property void netInterface(const(char)[] i); @@ -3057,7 +3486,7 @@ struct FTP @property void localPortRange(ushort range); /** Set the tcp no-delay socket option on or off. - See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) + See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) */ @property void tcpNoDelay(bool on); @@ -3137,7 +3566,7 @@ struct FTP void clearCommands() { if (p.commands !is null) - curl_slist_free_all(p.commands); + Curl.curl.slist_free_all(p.commands); p.commands = null; p.curl.clear(CurlOption.postquote); } @@ -3158,8 +3587,8 @@ struct FTP */ void addCommand(const(char)[] command) { - p.commands = curl_slist_append(p.commands, - command.tempCString().buffPtr); + p.commands = Curl.curl.slist_append(p.commands, + command.tempCString().buffPtr); p.curl.set(CurlOption.postquote, p.commands); } @@ -3178,9 +3607,75 @@ struct FTP /** The content length in bytes of the ftp data. */ - @property void contentLength(size_t len) + @property void contentLength(ulong len) + { + import std.conv : to; + p.curl.set(CurlOption.infilesize_large, to!curl_off_t(len)); + } + + /** + * Get various timings defined in $(REF CurlInfo, etc, c, curl). + * The value is usable only if the return value is equal to $(D etc.c.curl.CurlError.ok). + * + * Params: + * timing = one of the timings defined in $(REF CurlInfo, etc, c, curl). + * The values are: + * $(D etc.c.curl.CurlInfo.namelookup_time), + * $(D etc.c.curl.CurlInfo.connect_time), + * $(D etc.c.curl.CurlInfo.pretransfer_time), + * $(D etc.c.curl.CurlInfo.starttransfer_time), + * $(D etc.c.curl.CurlInfo.redirect_time), + * $(D etc.c.curl.CurlInfo.appconnect_time), + * $(D etc.c.curl.CurlInfo.total_time). + * val = the actual value of the inquired timing. + * + * Returns: + * The return code of the operation. The value stored in val + * should be used only if the return value is $(D etc.c.curl.CurlInfo.ok). + * + * Example: + * --- + * import std.net.curl; + * import etc.c.curl : CurlError, CurlInfo; + * + * auto client = FTP(); + * client.addCommand("RNFR my_file.txt"); + * client.addCommand("RNTO my_renamed_file.txt"); + * upload("my_file.txt", "ftp.digitalmars.com", client); + * + * double val; + * CurlCode code; + * + * code = http.getTiming(CurlInfo.namelookup_time, val); + * assert(code == CurlError.ok); + *--- + */ + CurlCode getTiming(CurlInfo timing, ref double val) + { + return p.curl.getTiming(timing, val); + } + + @system unittest { - p.curl.set(CurlOption.infilesize_large, len); + auto client = FTP(); + + double val; + CurlCode code; + + code = client.getTiming(CurlInfo.total_time, val); + assert(code == CurlError.ok); + code = client.getTiming(CurlInfo.namelookup_time, val); + assert(code == CurlError.ok); + code = client.getTiming(CurlInfo.connect_time, val); + assert(code == CurlError.ok); + code = client.getTiming(CurlInfo.pretransfer_time, val); + assert(code == CurlError.ok); + code = client.getTiming(CurlInfo.starttransfer_time, val); + assert(code == CurlError.ok); + code = client.getTiming(CurlInfo.redirect_time, val); + assert(code == CurlError.ok); + code = client.getTiming(CurlInfo.appconnect_time, val); + assert(code == CurlError.ok); } } @@ -3200,7 +3695,7 @@ struct FTP * smtp.perform(); * --- * - * See_Also: $(WEB www.ietf.org/rfc/rfc2821.txt, RFC2821) + * See_Also: $(HTTP www.ietf.org/rfc/rfc2821.txt, RFC2821) */ struct SMTP { @@ -3217,6 +3712,8 @@ struct SMTP @property void message(string msg) { + import std.algorithm.comparison : min; + auto _message = msg; /** This delegate reads the message text and copies it. @@ -3224,9 +3721,8 @@ struct SMTP curl.onSend = delegate size_t(void[] data) { if (!msg.length) return 0; - auto m = cast(void[])msg; size_t to_copy = min(data.length, _message.length); - data[0..to_copy] = (cast(void[])_message)[0..to_copy]; + data[0 .. to_copy] = (cast(void[])_message)[0 .. to_copy]; _message = _message[to_copy..$]; return to_copy; }; @@ -3246,6 +3742,7 @@ struct SMTP return smtp; } + /// static SMTP opCall() { SMTP smtp; @@ -3264,7 +3761,7 @@ struct SMTP curl_slist* newlist = null; while (cur) { - newlist = curl_slist_append(newlist, cur.data); + newlist = Curl.curl.slist_append(newlist, cur.data); cur = cur.next; } copy.p.commands = newlist; @@ -3279,7 +3776,7 @@ struct SMTP Params: throwOnError = whether to throw an exception or return a CurlCode on error */ - CurlCode perform(ThrowOnError throwOnError = ThrowOnError.yes) + CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) { return p.curl.perform(throwOnError); } @@ -3287,6 +3784,9 @@ struct SMTP /// The URL to specify the location of the resource. @property void url(const(char)[] url) { + import std.algorithm.searching : startsWith; + import std.uni : toLower; + auto lowered = url.toLower(); if (lowered.startsWith("smtps://")) @@ -3350,12 +3850,12 @@ struct SMTP // Network settings /** Proxy - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy) */ @property void proxy(const(char)[] host); /** Proxy port - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port) */ @property void proxyPort(ushort port); @@ -3363,7 +3863,7 @@ struct SMTP alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type - * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) + * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) */ @property void proxyType(CurlProxy type); @@ -3379,7 +3879,7 @@ struct SMTP * theprotocol.netInterface = [ 192, 168, 1, 32 ]; * ---- * - * See: $(XREF socket, InternetAddress) + * See: $(REF InternetAddress, std,socket) */ @property void netInterface(const(char)[] i); @@ -3406,7 +3906,7 @@ struct SMTP @property void localPortRange(ushort range); /** Set the tcp no-delay socket option on or off. - See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) + See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay) */ @property void tcpNoDelay(bool on); @@ -3495,10 +3995,10 @@ struct SMTP { assert(!recipients.empty, "Recipient must not be empty"); curl_slist* recipients_list = null; - foreach(recipient; recipients) + foreach (recipient; recipients) { recipients_list = - curl_slist_append(recipients_list, + Curl.curl.slist_append(recipients_list, recipient.tempCString().buffPtr); } p.curl.set(CurlOption.mail_rcpt, recipients_list); @@ -3558,13 +4058,132 @@ class CurlTimeoutException : CurlException } } -/// Equal to $(ECXREF curl, CURLcode) +/// Equal to $(REF CURLcode, etc,c,curl) alias CurlCode = CURLcode; -import std.typecons : Flag; +import std.typecons : Flag, Yes, No; /// Flag to specify whether or not an exception is thrown on error. alias ThrowOnError = Flag!"throwOnError"; +private struct CurlAPI +{ + static struct API + { + extern(C): + import core.stdc.config : c_long; + CURLcode function(c_long flags) global_init; + void function() global_cleanup; + curl_version_info_data * function(CURLversion) version_info; + CURL* function() easy_init; + CURLcode function(CURL *curl, CURLoption option,...) easy_setopt; + CURLcode function(CURL *curl) easy_perform; + CURLcode function(CURL *curl, CURLINFO info,...) easy_getinfo; + CURL* function(CURL *curl) easy_duphandle; + char* function(CURLcode) easy_strerror; + CURLcode function(CURL *handle, int bitmask) easy_pause; + void function(CURL *curl) easy_cleanup; + curl_slist* function(curl_slist *, char *) slist_append; + void function(curl_slist *) slist_free_all; + } + __gshared API _api; + __gshared void* _handle; + + static ref API instance() @property + { + import std.concurrency : initOnce; + initOnce!_handle(loadAPI()); + return _api; + } + + static void* loadAPI() + { + version (Posix) + { + import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY; + alias loadSym = dlsym; + } + else version (Windows) + { + import core.sys.windows.windows : GetProcAddress, GetModuleHandleA, + LoadLibraryA; + alias loadSym = GetProcAddress; + } + else + static assert(0, "unimplemented"); + + void* handle; + version (Posix) + handle = dlopen(null, RTLD_LAZY); + else version (Windows) + handle = GetModuleHandleA(null); + assert(handle !is null); + + // try to load curl from the executable to allow static linking + if (loadSym(handle, "curl_global_init") is null) + { + import std.format : format; + version (Posix) + dlclose(handle); + + version (OSX) + static immutable names = ["libcurl.4.dylib"]; + else version (Posix) + { + static immutable names = ["libcurl.so", "libcurl.so.4", + "libcurl-gnutls.so.4", "libcurl-nss.so.4", "libcurl.so.3"]; + } + else version (Windows) + static immutable names = ["libcurl.dll", "curl.dll"]; + + foreach (name; names) + { + version (Posix) + handle = dlopen(name.ptr, RTLD_LAZY); + else version (Windows) + handle = LoadLibraryA(name.ptr); + if (handle !is null) break; + } + + enforce!CurlException(handle !is null, "Failed to load curl, tried %(%s, %).".format(names)); + } + + foreach (i, FP; typeof(API.tupleof)) + { + enum name = __traits(identifier, _api.tupleof[i]); + auto p = enforce!CurlException(loadSym(handle, "curl_"~name), + "Couldn't load curl_"~name~" from libcurl."); + _api.tupleof[i] = cast(FP) p; + } + + enforce!CurlException(!_api.global_init(CurlGlobal.all), + "Failed to initialize libcurl"); + + return handle; + } + + shared static ~this() + { + if (_handle is null) return; + + _api.global_cleanup(); + version (Posix) + { + import core.sys.posix.dlfcn : dlclose; + dlclose(_handle); + } + else version (Windows) + { + import core.sys.windows.windows : FreeLibrary; + FreeLibrary(_handle); + } + else + static assert(0, "unimplemented"); + + _api = API.init; + _handle = null; + } +} + /** Wrapper to provide a better interface to libcurl than using the plain C API. It is recommended to use the $(D HTTP)/$(D FTP) etc. structs instead unless @@ -3577,21 +4196,11 @@ alias ThrowOnError = Flag!"throwOnError"; */ struct Curl { - shared static this() - { - // initialize early to prevent thread races - enforce!CurlException(!curl_global_init(CurlGlobal.all), - "Couldn't initialize libcurl"); - } - - shared static ~this() - { - curl_global_cleanup(); - } - alias OutData = void[]; alias InData = ubyte[]; - bool stopped; + private bool _stopped; + + private static auto ref curl() @property { return CurlAPI.instance; } // A handle should not be used by two threads simultaneously private CURL* handle; @@ -3614,12 +4223,18 @@ struct Curl void initialize() { enforce!CurlException(!handle, "Curl instance already initialized"); - handle = curl_easy_init(); + handle = curl.easy_init(); enforce!CurlException(handle, "Curl instance couldn't be initialized"); - stopped = false; + _stopped = false; set(CurlOption.nosignal, 1); } + /// + @property bool stopped() const + { + return _stopped; + } + /** Duplicate this handle. @@ -3631,24 +4246,19 @@ struct Curl Curl dup() { Curl copy; - copy.handle = curl_easy_duphandle(handle); - copy.stopped = false; + copy.handle = curl.easy_duphandle(handle); + copy._stopped = false; with (CurlOption) { - auto tt = TypeTuple!(file, writefunction, writeheader, - headerfunction, infile, - readfunction, ioctldata, ioctlfunction, - seekdata, seekfunction, sockoptdata, - sockoptfunction, opensocketdata, - opensocketfunction, noprogress, - progressdata, progressfunction, - debugdata, debugfunction, - interleavedata, - interleavefunction, chunk_data, - chunk_bgn_function, chunk_end_function, - fnmatch_data, fnmatch_function, - cookiejar, postfields); - foreach(option; tt) + auto tt = AliasSeq!(file, writefunction, writeheader, + headerfunction, infile, readfunction, ioctldata, ioctlfunction, + seekdata, seekfunction, sockoptdata, sockoptfunction, + opensocketdata, opensocketfunction, progressdata, + progressfunction, debugdata, debugfunction, interleavedata, + interleavefunction, chunk_data, chunk_bgn_function, + chunk_end_function, fnmatch_data, fnmatch_function, cookiejar, postfields); + + foreach (option; tt) copy.clear(option); } @@ -3695,10 +4305,11 @@ struct Curl private string errorString(CurlCode code) { import core.stdc.string : strlen; + import std.format : format; - auto msgZ = curl_easy_strerror(code); + auto msgZ = curl.easy_strerror(code); // doing the following (instead of just using std.conv.to!string) avoids 1 allocation - return format("%s on handle %s", msgZ[0 .. core.stdc.string.strlen(msgZ)], handle); + return format("%s on handle %s", msgZ[0 .. strlen(msgZ)], handle); } private void throwOnStopped(string message = null) @@ -3715,8 +4326,8 @@ struct Curl void shutdown() { throwOnStopped(); - stopped = true; - curl_easy_cleanup(this.handle); + _stopped = true; + curl.easy_cleanup(this.handle); this.handle = null; } @@ -3726,7 +4337,7 @@ struct Curl void pause(bool sendingPaused, bool receivingPaused) { throwOnStopped(); - _check(curl_easy_pause(this.handle, + _check(curl.easy_pause(this.handle, (sendingPaused ? CurlPause.send_cont : CurlPause.send) | (receivingPaused ? CurlPause.recv_cont : CurlPause.recv))); } @@ -3734,64 +4345,62 @@ struct Curl /** Set a string curl option. Params: - option = A $(ECXREF curl, CurlOption) as found in the curl documentation + option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation value = The string */ void set(CurlOption option, const(char)[] value) { throwOnStopped(); - _check(curl_easy_setopt(this.handle, option, value.tempCString().buffPtr)); + _check(curl.easy_setopt(this.handle, option, value.tempCString().buffPtr)); } /** Set a long curl option. Params: - option = A $(ECXREF curl, CurlOption) as found in the curl documentation + option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation value = The long */ void set(CurlOption option, long value) { throwOnStopped(); - _check(curl_easy_setopt(this.handle, option, value)); + _check(curl.easy_setopt(this.handle, option, value)); } /** Set a void* curl option. Params: - option = A $(ECXREF curl, CurlOption) as found in the curl documentation + option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation value = The pointer */ void set(CurlOption option, void* value) { throwOnStopped(); - _check(curl_easy_setopt(this.handle, option, value)); + _check(curl.easy_setopt(this.handle, option, value)); } /** Clear a pointer option. Params: - option = A $(ECXREF curl, CurlOption) as found in the curl documentation + option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation */ void clear(CurlOption option) { throwOnStopped(); - _check(curl_easy_setopt(this.handle, option, null)); + _check(curl.easy_setopt(this.handle, option, null)); } /** Clear a pointer option. Does not raise an exception if the underlying libcurl does not support the option. Use sparingly. Params: - option = A $(ECXREF curl, CurlOption) as found in the curl documentation + option = A $(REF CurlOption, etc,c,curl) as found in the curl documentation */ void clearIfSupported(CurlOption option) { throwOnStopped(); - auto rval = curl_easy_setopt(this.handle, option, null); - if (rval != CurlError.unknown_telnet_option) - { + auto rval = curl.easy_setopt(this.handle, option, null); + if (rval != CurlError.unknown_option && rval != CurlError.not_built_in) _check(rval); - } } /** @@ -3801,20 +4410,26 @@ struct Curl Params: throwOnError = whether to throw an exception or return a CurlCode on error */ - CurlCode perform(ThrowOnError throwOnError = ThrowOnError.yes) + CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError) { throwOnStopped(); - CurlCode code = curl_easy_perform(this.handle); + CurlCode code = curl.easy_perform(this.handle); if (throwOnError) _check(code); return code; } - // Explicitly undocumented. It will be removed in November 2015. - deprecated("Pass ThrowOnError.yes or .no instead of a boolean.") - CurlCode perform(bool throwOnError) + /** + Get the various timings like name lookup time, total time, connect time etc. + The timed category is passed through the timing parameter while the timing + value is stored at val. The value is usable only if res is equal to + $(D etc.c.curl.CurlError.ok). + */ + CurlCode getTiming(CurlInfo timing, ref double val) { - return perform(cast(ThrowOnError)throwOnError); + CurlCode code; + code = curl.easy_getinfo(handle, timing, &val); + return code; } /** @@ -3909,10 +4524,10 @@ struct Curl * string msg = "Hello world"; * curl.onSend = (void[] data) * { - * auto m = cast(void[])msg; + * auto m = cast(void[]) msg; * size_t length = m.length > data.length ? data.length : m.length; * if (length == 0) return 0; - * data[0..length] = m[0..length]; + * data[0 .. length] = m[0 .. length]; * msg = msg[length..$]; * return length; * }; @@ -3936,11 +4551,11 @@ struct Curl * * Params: * callback = the callback that receives a seek offset and a seek position - * $(ECXREF curl, CurlSeekPos) + * $(REF CurlSeekPos, etc,c,curl) * * Returns: * The callback returns the success state of the seeking - * $(ECXREF curl, CurlSeek) + * $(REF CurlSeek, etc,c,curl) * * Example: * ---- @@ -3973,7 +4588,7 @@ struct Curl * * Params: * callback = the callback that receives the socket and socket type - * $(ECXREF curl, CurlSockType) + * $(REF CurlSockType, etc,c,curl) * * Returns: * Return 0 from the callback to signal success, return 1 to signal error @@ -4052,7 +4667,7 @@ struct Curl { auto b = cast(Curl*) ptr; if (b._onReceive != null) - return b._onReceive(cast(InData)(str[0..size*nmemb])); + return b._onReceive(cast(InData)(str[0 .. size*nmemb])); return size*nmemb; } @@ -4060,8 +4675,10 @@ struct Curl size_t _receiveHeaderCallback(const char* str, size_t size, size_t nmemb, void* ptr) { + import std.string : chomp; + auto b = cast(Curl*) ptr; - auto s = str[0..size*nmemb].chomp(); + auto s = str[0 .. size*nmemb].chomp(); if (b._onReceiveHeader != null) b._onReceiveHeader(s); @@ -4072,7 +4689,7 @@ struct Curl size_t _sendCallback(char *str, size_t size, size_t nmemb, void *ptr) { Curl* b = cast(Curl*) ptr; - auto a = cast(void[]) str[0..size*nmemb]; + auto a = cast(void[]) str[0 .. size*nmemb]; if (b._onSend == null) return 0; return b._onSend(a); @@ -4112,8 +4729,8 @@ struct Curl return 0; // return: 0 ok, 1 fail - return b._onProgress(cast(size_t)dltotal, cast(size_t)dlnow, - cast(size_t)ultotal, cast(size_t)ulnow); + return b._onProgress(cast(size_t) dltotal, cast(size_t) dlnow, + cast(size_t) ultotal, cast(size_t) ulnow); } } @@ -4139,7 +4756,7 @@ private struct Pool(Data) { Data data; Entry* next; - }; + } private Entry* root; private Entry* freeList; @@ -4173,7 +4790,7 @@ private struct Pool(Data) root = n; return d; } -}; +} // Shared function for reading incoming chunks of data and // sending the to a parent thread @@ -4197,12 +4814,12 @@ private static size_t _receiveAsyncChunks(ubyte[] data, ref ubyte[] outdata, // them. receive((immutable(ubyte)[] buf) { - buffer = cast(ubyte[])buf; + buffer = cast(ubyte[]) buf; outdata = buffer[]; }, (bool flag) { aborted = true; } ); - if (aborted) return cast(size_t)0; + if (aborted) return cast(size_t) 0; } if (outdata.empty) { @@ -4214,7 +4831,7 @@ private static size_t _receiveAsyncChunks(ubyte[] data, ref ubyte[] outdata, auto copyBytes = outdata.length < data.length ? outdata.length : data.length; - outdata[0..copyBytes] = data[0..copyBytes]; + outdata[0 .. copyBytes] = data[0 .. copyBytes]; outdata = outdata[copyBytes..$]; data = data[copyBytes..$]; @@ -4247,6 +4864,7 @@ private static size_t _receiveAsyncLines(Terminator, Unit) ref Pool!(Unit[]) freeBuffers, ref Unit[] buffer, Tid fromTid, ref bool aborted) { + import std.format : format; immutable datalen = data.length; @@ -4267,14 +4885,14 @@ private static size_t _receiveAsyncLines(Terminator, Unit) // reuse them. receive((immutable(Unit)[] buf) { - buffer = cast(Unit[])buf; + buffer = cast(Unit[]) buf; buffer.length = 0; buffer.assumeSafeAppend(); bufferValid = true; }, (bool flag) { aborted = true; } ); - if (aborted) return cast(size_t)0; + if (aborted) return cast(size_t) 0; } if (!bufferValid) { @@ -4326,7 +4944,7 @@ private static size_t _receiveAsyncLines(Terminator, Unit) catch (CurlException ex) { prioritySend(fromTid, cast(immutable(CurlException))ex); - return cast(size_t)0; + return cast(size_t) 0; } } return datalen; @@ -4360,8 +4978,8 @@ private static void _spawnAsync(Conn, Unit, Terminator = void)() static if ( !is(Terminator == void)) { // Only lines reading will receive a terminator - auto terminator = receiveOnly!Terminator(); - auto keepTerminator = receiveOnly!bool(); + const terminator = receiveOnly!Terminator(); + const keepTerminator = receiveOnly!bool(); // max number of bytes to carry over from an onReceive // callback. This is 4 because it is the max code units to @@ -4376,7 +4994,7 @@ private static void _spawnAsync(Conn, Unit, Terminator = void)() } // no move semantic available in std.concurrency ie. must use casting. - auto connDup = cast(CURL*)receiveOnly!ulong(); + auto connDup = cast(CURL*) receiveOnly!ulong(); auto client = Conn(); client.p.curl.handle = connDup; @@ -4415,7 +5033,7 @@ private static void _spawnAsync(Conn, Unit, Terminator = void)() CurlCode code; try { - code = client.perform(ThrowOnError.no); + code = client.perform(No.throwOnError); } catch (Exception ex) { @@ -4447,8 +5065,3 @@ private static void _spawnAsync(Conn, Unit, Terminator = void)() fromTid.send(thisTid, curlMessage(true)); // signal done } - -version (unittest) private auto netAllowed() -{ - return environment.get("PHOBOS_TEST_ALLOW_NET") != null; -} diff --git a/std/net/isemail.d b/std/net/isemail.d index 10c5ce76700..c2c80c11f77 100644 --- a/std/net/isemail.d +++ b/std/net/isemail.d @@ -1,7 +1,7 @@ /** * Validates an email address according to RFCs 5321, 5322 and others. * - * Authors: Dominic Sayers , Jacob Carlborg + * Authors: Dominic Sayers $(LT)dominic@sayers.cc$(GT), Jacob Carlborg * Copyright: Dominic Sayers, Jacob Carlborg 2008-. * Test schema documentation: Copyright © 2011, Daniel Marschall * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) @@ -26,21 +26,22 @@ module std.net.isemail; // FIXME import std.range.primitives; // : ElementType; +import std.regex; import std.traits; +import std.typecons : Flag, Yes, No; /** * Check that an email address conforms to RFCs 5321, 5322 and others. * - * As of Version 3.0, we are now distinguishing clearly between a Mailbox as defined - * by RFC 5321 and an addr-spec as defined by RFC 5322. Depending on the context, - * either can be regarded as a valid email address. The RFC 5321 Mailbox specification - * is more restrictive (comments, white space and obsolete forms are not allowed). + * Distinguishes between a Mailbox as defined by RFC 5321 and an addr-spec as + * defined by RFC 5322. Depending on the context, either can be regarded as a + * valid email address. * * Note: The DNS check is currently not implemented. * * Params: * email = The email address to check - * checkDNS = If CheckDns.yes then a DNS check for MX records will be made + * checkDNS = If $(D Yes.checkDns) then a DNS check for MX records will be made * errorLevel = Determines the boundary between valid and invalid addresses. * Status codes above this number will be returned as-is, * status codes below will be returned as EmailStatusCode.valid. @@ -54,31 +55,41 @@ import std.traits; * either considered valid or not. Email status code will either be * EmailStatusCode.valid or EmailStatusCode.error. * - * Returns: an EmailStatus, indicating the status of the email address. + * Returns: + * An $(LREF EmailStatus), indicating the status of the email address. */ -EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no, - EmailStatusCode errorLevel = EmailStatusCode.none) if (isSomeChar!(Char)) +EmailStatus isEmail(Char)(const(Char)[] email, CheckDns checkDNS = No.checkDns, +EmailStatusCode errorLevel = EmailStatusCode.none) +if (isSomeChar!(Char)) { - import std.algorithm : uniq, canFind; + import std.algorithm.iteration : uniq, filter, map; + import std.algorithm.searching : canFind, maxElement; import std.exception : enforce; import std.array : array, split; import std.conv : to; - import std.regex : match, regex; import std.string : indexOf, lastIndexOf; import std.uni : isNumber; alias tstring = const(Char)[]; + alias Token = TokenImpl!(Char); + + static ipRegex = ctRegex!(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}`~ + `(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`.to!(const(Char)[])); + static fourChars = ctRegex!(`^[0-9A-Fa-f]{0,4}$`.to!(const(Char)[])); enum defaultThreshold = 16; int threshold; bool diagnose; - if (errorLevel == EmailStatusCode.any || errorLevel == EmailStatusCode.none) + if (errorLevel == EmailStatusCode.any) { threshold = EmailStatusCode.valid; - diagnose = errorLevel == EmailStatusCode.any; + diagnose = true; } + else if (errorLevel == EmailStatusCode.none) + threshold = defaultThreshold; + else { diagnose = true; @@ -229,15 +240,15 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - throw new Exception("More text found where none is allowed, but unrecognised prior " ~ - "context: " ~ to!(string)(contextPrior)); + throw new Exception("More text found where none is allowed, but " + ~"unrecognised prior context: " ~ to!(string)(contextPrior)); } } else { contextPrior = context; - auto c = token.front; + immutable c = token.front; if (c < '!' || c > '~' || c == '\n' || Token.specials.canFind(token)) returnStatus ~= EmailStatusCode.errorExpectingText; @@ -254,9 +265,11 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no { case Token.openParenthesis: if (elementLength == 0) - returnStatus ~= elementCount == 0 ? EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt + { + returnStatus ~= elementCount == 0 ? + EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt : EmailStatusCode.deprecatedComment; - + } else { returnStatus ~= EmailStatusCode.comment; @@ -316,9 +329,11 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no } if (elementLength == 0) - returnStatus ~= elementCount == 0 ? EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt + { + returnStatus ~= elementCount == 0 ? + EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt : EmailStatusCode.deprecatedFoldingWhitespace; - + } else { returnStatus ~= EmailStatusCode.foldingWhitespace; @@ -345,13 +360,13 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - throw new Exception("More text found where none is allowed, but unrecognised prior " ~ - "context: " ~ to!(string)(contextPrior)); + throw new Exception("More text found where none is allowed, but " + ~"unrecognised prior context: " ~ to!(string)(contextPrior)); } } - auto c = token.front; + immutable c = token.front; hyphenFlag = false; if (c < '!' || c > '~' || Token.specials.canFind(token)) @@ -378,14 +393,12 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no switch (token) { case Token.closeBracket: - if (returnStatus.max() < EmailStatusCode.deprecated_) + if (returnStatus.maxElement() < EmailStatusCode.deprecated_) { auto maxGroups = 8; size_t index = -1; auto addressLiteral = parseData[EmailPart.componentLiteral]; - enum regexStr = `\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}`~ - `(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`; - auto matchesIp = array(addressLiteral.match(regex!tstring(regexStr)).captures); + auto matchesIp = addressLiteral.matchAll(ipRegex).map!(a => a.hit).array; if (!matchesIp.empty) { @@ -398,14 +411,14 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no if (index == 0) returnStatus ~= EmailStatusCode.rfc5321AddressLiteral; - else if (addressLiteral.compareFirstN(Token.ipV6Tag, 5, true)) + else if (addressLiteral.compareFirstN(Token.ipV6Tag, 5)) returnStatus ~= EmailStatusCode.rfc5322DomainLiteral; else { auto ipV6 = addressLiteral.substr(5); matchesIp = ipV6.split(Token.colon); - auto groupCount = matchesIp.length; + immutable groupCount = matchesIp.length; index = ipV6.indexOf(Token.doubleColon); if (index == -1) @@ -438,7 +451,9 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no else if (ipV6.substr(-1) == Token.colon && ipV6.substr(-2, -1) != Token.colon) returnStatus ~= EmailStatusCode.rfc5322IpV6ColonEnd; - else if (!matchesIp.grep(regex!(tstring)(`^[0-9A-Fa-f]{0,4}$`), true).empty) + else if (!matchesIp + .filter!(a => a.matchFirst(fourChars).empty) + .empty) returnStatus ~= EmailStatusCode.rfc5322IpV6BadChar; else @@ -478,7 +493,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - auto c = token.front; + immutable c = token.front; if (c > AsciiToken.delete_ || c == '\0' || token == Token.openBracket) { @@ -531,7 +546,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - auto c = token.front; + immutable c = token.front; if (c > AsciiToken.delete_ || c == '\0' || c == '\n') returnStatus ~= EmailStatusCode.errorExpectingQuotedText; @@ -546,7 +561,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; case EmailPart.contextQuotedPair: - auto c = token.front; + immutable c = token.front; if (c > AsciiToken.delete_) returnStatus ~= EmailStatusCode.errorExpectingQuotedPair; @@ -614,7 +629,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - auto c = token.front; + immutable c = token.front; if (c > AsciiToken.delete_ || c == '\0' || c == '\n') { @@ -678,11 +693,11 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no throw new Exception("Unkown context: " ~ to!(string)(context)); } - if (returnStatus.max() > EmailStatusCode.rfc5322) + if (returnStatus.maxElement() > EmailStatusCode.rfc5322) break; } - if (returnStatus.max() < EmailStatusCode.rfc5322) + if (returnStatus.maxElement() < EmailStatusCode.rfc5322) { if (context == EmailPart.contextQuotedString) returnStatus ~= EmailStatusCode.errorUnclosedQuotedString; @@ -721,12 +736,12 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no auto dnsChecked = false; - if (checkDNS == CheckDns.yes && returnStatus.max() < EmailStatusCode.dnsWarning) + if (checkDNS == Yes.checkDns && returnStatus.maxElement() < EmailStatusCode.dnsWarning) { assert(false, "DNS check is currently not implemented"); } - if (!dnsChecked && returnStatus.max() < EmailStatusCode.dnsWarning) + if (!dnsChecked && returnStatus.maxElement() < EmailStatusCode.dnsWarning) { if (elementCount == 0) returnStatus ~= EmailStatusCode.rfc5321TopLevelDomain; @@ -736,7 +751,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no } returnStatus = array(uniq(returnStatus)); - auto finalStatus = returnStatus.max(); + auto finalStatus = returnStatus.maxElement(); if (returnStatus.length != 1) returnStatus.popFront(); @@ -762,484 +777,509 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no return EmailStatus(valid, to!(string)(localPart), to!(string)(domainPart), finalStatus); } -unittest +@safe unittest { - assert(``.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - assert(`test`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - assert(`@`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart); - assert(`test@`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); + assert(`test.test@iana.org`.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); + assert(`test.test@iana.org`.isEmail(No.checkDns, EmailStatusCode.none).statusCode == EmailStatusCode.valid); - // assert(`test@io`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::8888]`.isEmail(No.checkDns, + EmailStatusCode.none).statusCode == EmailStatusCode.valid); + + assert(`test`.isEmail(No.checkDns, EmailStatusCode.none).statusCode == EmailStatusCode.error); + assert(`(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.none).statusCode == EmailStatusCode.error); + + assert(``.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); + assert(`test`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); + assert(`@`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart); + assert(`test@`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); + + // assert(`test@io`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid, // `io. currently has an MX-record (Feb 2011). Some DNS setups seem to find it, some don't.` // ` If you don't see the MX for io. then try setting your DNS server to 8.8.8.8 (the Google DNS server)`); - assert(`@io`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart, + assert(`@io`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart, `io. currently has an MX-record (Feb 2011)`); - assert(`@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart); - assert(`test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@nominet.org.uk`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@about.museum`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`a@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart); + assert(`test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`test@nominet.org.uk`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`test@about.museum`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`a@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - //assert(`test@e.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); + //assert(`test@e.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - //assert(`test@iana.a`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); + //assert(`test@iana.a`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - assert(`test.test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`.test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotStart); - assert(`test.@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotEnd); + assert(`test.test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`.test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotStart); + assert(`test.@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotEnd); - assert(`test..iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test .. iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorConsecutiveDots); - assert(`test_exa-mple.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); - assert("!#$%&`*+/=?^`{|}~@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`test_exa-mple.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoDomain); + assert("!#$%&`*+/=?^`{|}~@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test\@test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test\@test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`123@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@123.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`123@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`test@123.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@iana.123`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@iana.123`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321TopLevelDomainNumeric); - assert(`test@255.255.255.255`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@255.255.255.255`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321TopLevelDomainNumeric); - assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(CheckDns.no, + assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org`.isEmail(CheckDns.no, + assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong); - // assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(CheckDns.no, + // assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(No.checkDns, // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com`.isEmail(CheckDns.no, + assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LabelTooLong); - assert(`test@mason-dixon.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`test@mason-dixon.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - assert(`test@-iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@-iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDomainHyphenStart); - assert(`test@iana-.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@iana-.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDomainHyphenEnd); - assert(`test@g--a.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid); + assert(`test@g--a.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid); - //assert(`test@iana.co-uk`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + //assert(`test@iana.co-uk`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == //EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - assert(`test@.iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotStart); - assert(`test@iana.org.`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotEnd); - assert(`test@iana..com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@.iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotStart); + assert(`test@iana.org.`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDotEnd); + assert(`test@iana .. com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorConsecutiveDots); //assert(`a@a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z` // `.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z` - // `.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + // `.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == // EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented // assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz` // `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.` - // `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi`.isEmail(CheckDns.no, + // `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi`.isEmail(No.checkDns, // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented assert((`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz`~ `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`).isEmail(CheckDns.no, + `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`).isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong); assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~ `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`).isEmail(CheckDns.no, + `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`).isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong); assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~ `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`).isEmail(CheckDns.no, + `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`).isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainTooLong); - assert(`"test"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`""@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`"""@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`"\a"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`"\""@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); + assert(`""@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); + assert(`"""@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); + assert(`"\a"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); + assert(`"\""@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`"\"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedQuotedString); - assert(`"\\"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`test"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); + assert(`"\\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); + assert(`test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`"test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedQuotedString); - assert(`"test"test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorTextAfterQuotedString); - assert(`test"text"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test"text"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`"test""test"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test""test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`"test"."test"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test"."test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedLocalPart); - assert(`"test\ test"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test\ test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321QuotedString); - assert(`"test".test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test".test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedLocalPart); - assert("\"test\u0000\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"test\u0000\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingQuotedText); - assert("\"test\\\u0000\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"test\\\u0000\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedQuotedPair); - assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghj"@iana.org`.isEmail(CheckDns.no, + assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghj"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong, `Quotes are still part of the length restriction`); - assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg\h"@iana.org`.isEmail(CheckDns.no, + assert(`"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg\h"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong, `Quoted pair is still part of the length restriction`); - assert(`test@[255.255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@a[255.255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@a[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`test@[255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral); - assert(`test@[255.255.255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[255.255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral); - assert(`test@[255.255.255.256]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[255.255.255.256]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral); - assert(`test@[1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:8888:9999]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:888G]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:888G]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6BadChar); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::8888]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321IpV6Deprecated); - assert(`test@[IPv6:1111:2222:3333:4444:5555::8888]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6:1111:2222:3333:4444:5555::8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::7777:8888]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6MaxGroups); - assert(`test@[IPv6::3333:4444:5555:6666:7777:8888]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6::3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6ColonStart); - assert(`test@[IPv6:::3333:4444:5555:6666:7777:8888]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6:::3333:4444:5555:6666:7777:8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@[IPv6:1111::4444:5555::8888]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6:1111::4444:5555::8888]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6TooManyDoubleColons); - assert(`test@[IPv6:::]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6:::]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@[IPv6:1111:2222:3333:4444:5555:255.255.255.255]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:255.255.255.255]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666:7777:255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6GroupCount); - assert(`test@[IPv6:1111:2222:3333:4444::255.255.255.255]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral); - assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::255.255.255.255]`.isEmail(CheckDns.no, + assert(`test@[IPv6:1111:2222:3333:4444:5555:6666::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6MaxGroups); - assert(`test@[IPv6:1111:2222:3333:4444:::255.255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode + assert(`test@[IPv6:1111:2222:3333:4444:::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6TooManyDoubleColons); - assert(`test@[IPv6::255.255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6ColonStart); - assert(` test @iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(` test @iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - assert(`test@ iana .com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@ iana .com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - assert(`test . test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test . test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedFoldingWhitespace); - assert("\u000D\u000A test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace, `Folding whitespace`); - assert("\u000D\u000A \u000D\u000A test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\u000D\u000A \u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP`~ ` -- only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`); - assert(`(comment)test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.comment); - assert(`((comment)test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.comment); + assert(`((comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedComment); - assert(`(comment(comment))test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`(comment(comment))test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.comment); - assert(`test@(comment)iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@(comment)iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - assert(`test(comment)test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorTextAfterCommentFoldingWhitespace); - assert(`test@(comment)[255.255.255.255]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@(comment)[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - assert(`(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(CheckDns.no, + assert(`(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.comment); - assert(`test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(CheckDns.no, + assert(`test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); assert((`(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyz`~ `abcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.`~ - `abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`).isEmail(CheckDns.no, + `abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`).isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.comment); - assert("test@iana.org\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert(`test@xn--hxajbheg2az3al.xn--jxalpdlp`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@xn--hxajbheg2az3al.xn--jxalpdlp`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid, `A valid IDN from ICANN's `~ `IDN TLD evaluation gateway`); - assert(`xn--test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid, + assert(`xn--test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid, `RFC 3490: "unless the email standards are revised to invite the use of IDNA for local parts, a domain label`~ ` that holds the local part of an email address SHOULD NOT begin with the ACE prefix, and even if it does,`~ ` it is to be interpreted literally as a local part that happens to begin with the ACE prefix"`); - assert(`test@iana.org-`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@iana.org-`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorDomainHyphenEnd); - assert(`"test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedQuotedString); - assert(`(test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`(test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedComment); - assert(`test@(iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@(iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedComment); - assert(`test@[1.2.3.4`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[1.2.3.4`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedDomainLiteral); - assert(`"test\"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`"test\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedQuotedString); - assert(`(comment\)test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`(comment\)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedComment); - assert(`test@iana.org(comment\)`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@iana.org(comment\)`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedComment); - assert(`test@iana.org(comment\`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@iana.org(comment\`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorBackslashEnd); - assert(`test@[RFC-5322-domain-literal]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral); - assert(`test@[RFC-5322]-domain-literal]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322]-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorTextAfterDomainLiteral); - assert(`test@[RFC-5322-[domain-literal]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322-[domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingDomainText); - assert("test@[RFC-5322-\\\u0007-domain-literal]".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@[RFC-5322-\\\u0007-domain-literal]".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteralObsoleteText, `obs-dtext and obs-qp`); - assert("test@[RFC-5322-\\\u0009-domain-literal]".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@[RFC-5322-\\\u0009-domain-literal]".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteralObsoleteText); - assert(`test@[RFC-5322-\]-domain-literal]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322-\]-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteralObsoleteText); - assert(`test@[RFC-5322-domain-literal\]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322-domain-literal\]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorUnclosedDomainLiteral); - assert(`test@[RFC-5322-domain-literal\`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322-domain-literal\`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorBackslashEnd); - assert(`test@[RFC 5322 domain literal]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC 5322 domain literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral, `Spaces are FWS in a domain literal`); - assert(`test@[RFC-5322-domain-literal] (comment)`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[RFC-5322-domain-literal] (comment)`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainLiteral); - assert("\u007F@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert("test@\u007F.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert("\"\u007F\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedQuotedText); + assert("\u007F@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == + EmailStatusCode.errorExpectingText); + assert("test@\u007F.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == + EmailStatusCode.errorExpectingText); + assert("\"\u007F\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == + EmailStatusCode.deprecatedQuotedText); - assert("\"\\\u007F\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"\\\u007F\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedQuotedPair); - assert("(\u007F)test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("(\u007F)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentText); - assert("test@iana.org\u000D".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, + assert("test@iana.org\u000D".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, `No LF after the CR`); - assert("\u000Dtest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, + assert("\u000Dtest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, `No LF after the CR`); - assert("\"\u000Dtest\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf - ,`No LF after the CR`); + assert("\"\u000Dtest\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == + EmailStatusCode.errorCrNoLf, `No LF after the CR`); - assert("(\u000D)test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, + assert("(\u000D)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, `No LF after the CR`); - assert("(\u000D".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, + assert("(\u000D".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, `No LF after the CR`); - assert("test@iana.org(\u000D)".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, + assert("test@iana.org(\u000D)".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf, `No LF after the CR`); - assert("\u000Atest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert("\"\u000A\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"\u000A\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingQuotedText); - assert("\"\\\u000A\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"\\\u000A\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedQuotedPair); - assert("(\u000A)test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("(\u000A)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingCommentText); - assert("\u0007@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\u0007@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert("test@\u0007.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@\u0007.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); - assert("\"\u0007\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"\u0007\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedQuotedText); - assert("\"\\\u0007\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"\\\u0007\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedQuotedPair); - assert("(\u0007)test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("(\u0007)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentText); - assert("\u000D\u000Atest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`); - assert("\u000D\u000A \u000D\u000Atest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\u000D\u000A \u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not obs-FWS because there must be white space on each "fold"`); - assert(" \u000D\u000Atest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(" \u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`); - assert(" \u000D\u000A test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(" \u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace, `FWS`); - assert(" \u000D\u000A \u000D\u000Atest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(" \u000D\u000A \u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the second fold`); - assert(" \u000D\u000A\u000D\u000Atest@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(" \u000D\u000A\u000D\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after either fold`); - assert(" \u000D\u000A\u000D\u000A test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(" \u000D\u000A\u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`); - assert("test@iana.org\u000D\u000A ".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org\u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace, `FWS`); - assert("test@iana.org\u000D\u000A \u000D\u000A ".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org\u000D\u000A \u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP -- `~ `only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`); - assert("test@iana.org\u000D\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org\u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`); - assert("test@iana.org\u000D\u000A \u000D\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org\u000D\u000A \u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not obs-FWS because there must be white space on each "fold"`); - assert("test@iana.org \u000D\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org \u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`); - assert("test@iana.org \u000D\u000A ".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org \u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace, `FWS`); - assert("test@iana.org \u000D\u000A \u000D\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org \u000D\u000A \u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the second fold`); - assert("test@iana.org \u000D\u000A\u000D\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org \u000D\u000A\u000D\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after either fold`); - assert("test@iana.org \u000D\u000A\u000D\u000A ".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("test@iana.org \u000D\u000A\u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`); - assert(" test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace); - assert(`test@iana.org `.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace); + assert(" test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace); + assert(`test@iana.org `.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.foldingWhitespace); - assert(`test@[IPv6:1::2:]`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test@[IPv6:1::2:]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6ColonEnd); - assert("\"test\\\u00A9\"@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert("\"test\\\u00A9\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingQuotedPair); - assert(`test@iana/icann.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322Domain); + assert(`test@iana/icann.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322Domain); - assert(`test.(comment)test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + assert(`test.(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedComment); - assert(`test@org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321TopLevelDomain); + assert(`test@org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321TopLevelDomain); - // assert(`test@test.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == + // assert(`test@test.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == //EmailStatusCode.dnsWarningNoMXRecord, `test.com has an A-record but not an MX-record`); // DNS check is currently not implemented // - // assert(`test@nic.no`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord, + // assert(`test@nic.no`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord, // `nic.no currently has no MX-records or A-records (Feb 2011). If you are seeing an A-record for nic.io then` // ` try setting your DNS server to 8.8.8.8 (the Google DNS server) - your DNS server may be faking an A-record` // ` (OpenDNS does this, for instance).`); // DNS check is currently not implemented } -/// Enum for indicating if the isEmail function should perform a DNS check or not. -enum CheckDns +// https://issues.dlang.org/show_bug.cgi?id=17217 +@safe unittest { - /// Does not perform DNS checking - no, - - /// Performs DNS checking - yes + wstring a = `test.test@iana.org`w; + dstring b = `test.test@iana.org`d; + const(wchar)[] c = `test.test@iana.org`w; + const(dchar)[] d = `test.test@iana.org`d; + + assert(a.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); + assert(b.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); + assert(c.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); + assert(d.isEmail(No.checkDns).statusCode == EmailStatusCode.valid); } -/// This struct represents the status of an email address +/** + * Flag for indicating if the isEmail function should perform a DNS check or not. + * + * If set to $(D CheckDns.no), isEmail does not perform DNS checking. + * + * Otherwise if set to $(D CheckDns.yes), isEmail performs DNS checking. + */ +alias CheckDns = Flag!"checkDns"; + +/// Represents the status of an email address struct EmailStatus { private @@ -1250,7 +1290,7 @@ struct EmailStatus EmailStatusCode statusCode_; } - /// + /// Self aliases to a `bool` representing if the email is valid or not alias valid this; /* @@ -1260,7 +1300,7 @@ struct EmailStatus * domainPart = the domain part of the email address * statusCode = the status code */ - private this (bool valid, string localPart, string domainPart, EmailStatusCode statusCode) + private this (bool valid, string localPart, string domainPart, EmailStatusCode statusCode) @safe @nogc pure nothrow { this.valid_ = valid; this.localPart_ = localPart; @@ -1268,38 +1308,38 @@ struct EmailStatus this.statusCode_ = statusCode; } - /// Indicates if the email address is valid or not. - @property bool valid () const + /// Returns: If the email address is valid or not. + @property bool valid() const @safe @nogc pure nothrow { return valid_; } - /// The local part of the email address, that is, the part before the @ sign. - @property string localPart () const + /// Returns: The local part of the email address, that is, the part before the @ sign. + @property string localPart() const @safe @nogc pure nothrow { return localPart_; } - /// The domain part of the email address, that is, the part after the @ sign. - @property string domainPart () const + /// Returns: The domain part of the email address, that is, the part after the @ sign. + @property string domainPart() const @safe @nogc pure nothrow { return domainPart_; } - /// The email status code - @property EmailStatusCode statusCode () const + /// Returns: The email status code + @property EmailStatusCode statusCode() const @safe @nogc pure nothrow { return statusCode_; } - /// Returns a describing string of the status code - @property string status () const + /// Returns: A describing string of the status code + @property string status() const @safe @nogc pure nothrow { return statusCodeDescription(statusCode_); } - /// Returns a textual representation of the email status - string toString () const + /// Returns: A textual representation of the email status + string toString() const @safe pure { import std.format : format; return format("EmailStatus\n{\n\tvalid: %s\n\tlocalPart: %s\n\tdomainPart: %s\n\tstatusCode: %s\n}", valid, @@ -1307,8 +1347,13 @@ struct EmailStatus } } -/// Returns a describing string of the given status code -string statusCodeDescription (EmailStatusCode statusCode) +/** + * Params: + * statusCode = The $(LREF EmailStatusCode) to read + * Returns: + * A detailed string describing the given status code + */ +string statusCodeDescription(EmailStatusCode statusCode) @safe @nogc pure nothrow { final switch (statusCode) { @@ -1690,9 +1735,9 @@ enum EmailPart } // Miscellaneous string constants -struct Token +struct TokenImpl(Char) { - enum + enum : const(Char)[] { at = "@", backslash = `\`, @@ -1723,51 +1768,10 @@ enum AsciiToken delete_ = 127 } -/* - * Returns the maximum of the values in the given array. - * - * Examples: - * --- - * assert([1, 2, 3, 4].max == 4); - * assert([3, 5, 9, 2, 5].max == 9); - * assert([7, 13, 9, 12, 0].max == 13); - * --- - * - * Params: - * arr = the array containing the values to return the maximum of - * - * Returns: the maximum value - */ -T max (T) (T[] arr) -{ - import std.algorithm/* : max*/; - - auto max = arr.front; - - foreach (i ; 0 .. arr.length - 1) - max = std.algorithm.max(max, arr[i + 1]); - - return max; -} - -unittest -{ - assert([1, 2, 3, 4].max() == 4); - assert([3, 5, 9, 2, 5].max() == 9); - assert([7, 13, 9, 12, 0].max() == 13); -} - /* * Returns the portion of string specified by the $(D_PARAM start) and * $(D_PARAM length) parameters. * - * Examples: - * --- - * assert("abcdef".substr(-1) == "f"); - * assert("abcdef".substr(-2) == "ef"); - * assert("abcdef".substr(-3, 1) == "d"); - * --- - * * Params: * str = the input string. Must be one character or longer. * start = if $(D_PARAM start) is non-negative, the returned string will start at the @@ -1839,11 +1843,15 @@ T[] substr (T) (T[] str, ptrdiff_t start = 0, ptrdiff_t length = ptrdiff_t.min) return str[start .. end]; } -unittest +@safe unittest { assert("abcdef".substr(-1) == "f"); assert("abcdef".substr(-2) == "ef"); assert("abcdef".substr(-3, 1) == "d"); +} + +@safe unittest +{ assert("abcdef".substr(0, -1) == "abcde"); assert("abcdef".substr(2, -1) == "cde"); assert("abcdef".substr(4, -4) == []); @@ -1857,14 +1865,6 @@ unittest * characters, that will be used in the comparison, can be specified. Supports both * case-sensitive and case-insensitive comparison. * - * Examples: - * --- - * assert("abc".compareFirstN("abcdef", 3) == 0); - * assert("abc".compareFirstN("Abc", 3, true) == 0); - * assert("abc".compareFirstN("abcdef", 6) < 0); - * assert("abcdef".compareFirstN("abc", 6) > 0); - * --- - * * Params: * s1 = the first string to be compared * s2 = the second string to be compared @@ -1880,89 +1880,44 @@ unittest * $(TR $(TD $(D > 0)) $(TD $(D s1 > s2))) * ) */ -int compareFirstN (alias pred = "a < b", S1, S2) (S1 s1, S2 s2, size_t length, bool caseInsensitive = false) - if (is(Unqual!(ElementType!(S1)) == dchar) && is(Unqual!(ElementType!(S2)) == dchar)) +int compareFirstN(alias pred = "a < b", S1, S2) (S1 s1, S2 s2, size_t length) +if (is(Unqual!(ElementType!(S1)) == dchar) && is(Unqual!(ElementType!(S2)) == dchar)) { import std.uni : icmp; - import std.algorithm : cmp; auto s1End = length <= s1.length ? length : s1.length; auto s2End = length <= s2.length ? length : s2.length; auto slice1 = s1[0 .. s1End]; auto slice2 = s2[0 .. s2End]; - return caseInsensitive ? slice1.icmp(slice2) : slice1.cmp(slice2); + return slice1.icmp(slice2); } -unittest +@safe unittest { assert("abc".compareFirstN("abcdef", 3) == 0); - assert("abc".compareFirstN("Abc", 3, true) == 0); + assert("abc".compareFirstN("Abc", 3) == 0); assert("abc".compareFirstN("abcdef", 6) < 0); assert("abcdef".compareFirstN("abc", 6) > 0); } -/* - * Returns a range consisting of the elements of the $(D_PARAM input) range that - * matches the given $(D_PARAM pattern). - * - * Examples: - * --- - * assert(equal(["ab", "0a", "cd", "1b"].grep(regex(`\d\w`)), ["0a", "1b"])); - * assert(equal(["abc", "0123", "defg", "4567"].grep(regex(`(\w+)`), true), ["0123", "4567"])); - * --- - * - * Params: - * input = the input range - * pattern = the regular expression pattern to search for - * invert = if $(D_KEYWORD true), this function returns the elements of the - * input range that do $(B not) match the given $(D_PARAM pattern). - * - * Returns: a range containing the matched elements - */ -auto grep (Range, Regex) (Range input, Regex pattern, bool invert = false) -{ - import std.regex : match; - import std.algorithm : filter; - auto dg = invert ? (ElementType!(Range) e) { return e.match(pattern).empty; } : - (ElementType!(Range) e) { return !e.match(pattern).empty; }; - - return filter!(dg)(input); -} - -unittest -{ - import std.algorithm : equal; - import std.regex; - assert(equal(["ab", "0a", "cd", "1b"].grep(regex(`\d\w`)), ["0a", "1b"])); - assert(equal(["abc", "0123", "defg", "4567"].grep(regex(`4567`), true), ["abc", "0123", "defg"])); -} - /* * Pops the last element of the given range and returns the element. * - * Examples: - * --- - * auto array = [0, 1, 2, 3]; - * auto result = array.pop(); - * - * assert(array == [0, 1, 2]); - * assert(result == 3); - * --- - * * Params: * range = the range to pop the element from * * Returns: the popped element */ -ElementType!(A) pop (A) (ref A a) if (isDynamicArray!(A) && !isNarrowString!(A) && isMutable!(A) && !is(A == void[])) +ElementType!(A) pop (A) (ref A a) +if (isDynamicArray!(A) && !isNarrowString!(A) && isMutable!(A) && !is(A == void[])) { auto e = a.back; a.popBack(); return e; } -unittest +@safe unittest { auto array = [0, 1, 2, 3]; auto result = array.pop(); @@ -1975,12 +1930,6 @@ unittest * Returns the character at the given index as a string. The returned string will be a * slice of the original string. * - * Examples: - * --- - * assert("abc".get(1, 'b') == "b"); - * assert("löv".get(1, 'ö') == "ö"); - * --- - * * Params: * str = the string to get the character from * index = the index of the character to get @@ -1994,7 +1943,13 @@ const(T)[] get (T) (const(T)[] str, size_t index, dchar c) return str[index .. index + codeLength!(T)(c)]; } -unittest +@safe unittest +{ + assert("abc".get(1, 'b') == "b"); + assert("löv".get(1, 'ö') == "ö"); +} + +@safe unittest { assert("abc".get(1, 'b') == "b"); assert("löv".get(1, 'ö') == "ö"); diff --git a/std/numeric.d b/std/numeric.d index 1759549bc69..c6cea6fc608 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -6,13 +6,10 @@ header in Alexander Stepanov's $(LINK2 http://sgi.com/tech/stl, Standard Template Library), with a few additions. Macros: - -WIKI = Phobos/StdNumeric - Copyright: Copyright Andrei Alexandrescu 2008 - 2009. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.org, Andrei Alexandrescu), - Don Clugston, Robert Jacques +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu), + Don Clugston, Robert Jacques, Ilya Yaroshenko Source: $(PHOBOSSRC std/_numeric.d) */ /* @@ -24,7 +21,6 @@ Distributed under the Boost Software License, Version 1.0. module std.numeric; import std.complex; -import std.exception; import std.math; import std.range.primitives; import std.traits; @@ -44,20 +40,23 @@ public enum CustomFloatFlags * Store values in normalized form by default. The actual precision of the * significand is extended by 1 bit by assuming an implicit leading bit of 1 * instead of 0. i.e. $(D 1.nnnn) instead of $(D 0.nnnn). - * True for all $(LUCKY IEE754) types + * True for all $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEE754) types */ storeNormalized = 2, /** - * Stores the significand in $(LUCKY IEEE754 denormalized) form when the - * exponent is 0. Required to express the value 0. + * Stores the significand in $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Denormalized_numbers, + * IEEE754 denormalized) form when the exponent is 0. Required to express the value 0. */ allowDenorm = 4, - /// Allows the storage of $(LUCKY IEEE754 _infinity) values. + /** + * Allows the storage of $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Positive_and_negative_infinity, + * IEEE754 _infinity) values. + */ infinity = 8, - /// Allows the storage of $(LUCKY IEEE754 Not a Number) values. + /// Allows the storage of $(LINK2 https://en.wikipedia.org/wiki/NaN, IEEE754 Not a Number) values. nan = 16, /** @@ -70,37 +69,19 @@ public enum CustomFloatFlags /// If set, unsigned custom floats are assumed to be negative. negativeUnsigned = 64, - /**If set, 0 is the only allowed $(LUCKY IEEE754 denormalized) number. + /**If set, 0 is the only allowed $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Denormalized_numbers, + * IEEE754 denormalized) number. * Requires allowDenorm and storeNormalized. */ allowDenormZeroOnly = 128 | allowDenorm | storeNormalized, - /// Include _all of the $(LUCKY IEEE754) options. + /// Include _all of the $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEEE754) options. ieee = signed | storeNormalized | allowDenorm | infinity | nan , /// Include none of the above options. none = 0 } -// 64-bit version of core.bitop.bsr -private int bsr64(ulong value) -{ - import core.bitop : bsr; - - union Ulong - { - ulong raw; - struct - { - uint low; - uint high; - } - } - Ulong v; - v.raw = value; - return v.high==0 ? core.bitop.bsr(v.low) : core.bitop.bsr(v.high) + 32; -} - private template CustomFloatParams(uint bits) { enum CustomFloatFlags flags = CustomFloatFlags.ieee @@ -114,9 +95,9 @@ private template CustomFloatParams(uint bits) private template CustomFloatParams(uint precision, uint exponentWidth, CustomFloatFlags flags) { - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; alias CustomFloatParams = - TypeTuple!( + AliasSeq!( precision, exponentWidth, flags, @@ -132,21 +113,23 @@ private template CustomFloatParams(uint precision, uint exponentWidth, CustomFlo * result can be stored in a custom floating-point value via assignment. */ template CustomFloat(uint bits) - if (bits == 8 || bits == 16 || bits == 32 || bits == 64 || bits == 80) +if (bits == 8 || bits == 16 || bits == 32 || bits == 64 || bits == 80) { alias CustomFloat = CustomFloat!(CustomFloatParams!(bits)); } /// ditto template CustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags = CustomFloatFlags.ieee) - if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision + exponentWidth > 0) +if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision + exponentWidth > 0) { alias CustomFloat = CustomFloat!(CustomFloatParams!(precision, exponentWidth, flags)); } /// -unittest +@safe unittest { + import std.math : sin, cos; + // Define a 16-bit floating point values CustomFloat!16 x; // Using the number of bits CustomFloat!(10, 5) y; // Using the precision and exponent width @@ -159,7 +142,7 @@ unittest // Functions calls require conversion z = sin(+x) + cos(+y); // Use unary plus to concisely convert to a real z = sin(x.get!float) + cos(y.get!float); // Or use get!T - z = sin(cast(float)x) + cos(cast(float)y); // Or use cast(T) to explicitly convert + z = sin(cast(float) x) + cos(cast(float) y); // Or use cast(T) to explicitly convert // Define a 8-bit custom float for storing probabilities alias Probability = CustomFloat!(4, 4, CustomFloatFlags.ieee^CustomFloatFlags.probability^CustomFloatFlags.signed ); @@ -171,23 +154,23 @@ struct CustomFloat(uint precision, // fraction bits (23 for float) uint exponentWidth, // exponent bits (8 for float) Exponent width CustomFloatFlags flags, uint bias) - if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && - precision + exponentWidth > 0) +if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && + precision + exponentWidth > 0) { - import std.bitmanip; - import std.typetuple; + import std.bitmanip : bitfields; + import std.meta : staticIndexOf; private: // get the correct unsigned bitfield type to support > 32 bits template uType(uint bits) { - static if(bits <= size_t.sizeof*8) alias uType = size_t; + static if (bits <= size_t.sizeof*8) alias uType = size_t; else alias uType = ulong ; } // get the correct signed bitfield type to support > 32 bits template sType(uint bits) { - static if(bits <= ptrdiff_t.sizeof*8-1) alias sType = ptrdiff_t; + static if (bits <= ptrdiff_t.sizeof*8-1) alias sType = ptrdiff_t; else alias sType = long; } @@ -205,7 +188,7 @@ private: // If on Linux or Mac, where 80-bit reals are padded, ignore the // padding. - import std.algorithm : min; + import std.algorithm.comparison : min; CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get; // Convert F to the correct binary type. @@ -260,11 +243,12 @@ private: } if ((~flags&Flags.storeNormalized) || // Convert denormalized form to normalized form - ((flags&Flags.allowDenorm) && exp==0)) + ((flags&Flags.allowDenorm) && exp == 0)) { - if(sig > 0) + if (sig > 0) { - auto shift2 = precision - bsr64(sig); + import core.bitop : bsr; + auto shift2 = precision - bsr(sig); exp -= shift2-1; shift += shift2; } @@ -292,12 +276,12 @@ private: // convert back to normalized form static if (~flags & Flags.infinity) // No infinity support? - enforce(sig != 0, "Infinity floating point value assigned to a " - ~ typeof(this).stringof~" (no infinity support)."); + assert(sig != 0, "Infinity floating point value assigned to a " + ~ typeof(this).stringof ~ " (no infinity support)."); static if (~flags & Flags.nan) // No NaN support? - enforce(sig == 0,"NaN floating point value assigned to a " ~ - typeof(this).stringof~" (no nan support)."); + assert(sig == 0, "NaN floating point value assigned to a " ~ + typeof(this).stringof ~ " (no nan support)."); sig >>= shift; return; } @@ -322,7 +306,7 @@ private: exp = 0; } else - enforce((flags&Flags.storeNormalized) && exp == 0, + assert((flags&Flags.storeNormalized) && exp == 0, "Underflow occured assigning to a " ~ typeof(this).stringof ~ " (no denormal support)."); } @@ -370,12 +354,12 @@ private: sig = 0; exp = exponent_max; static if (~flags&(Flags.infinity)) - enforce(false, "Overflow occured assigning to a " ~ - typeof(this).stringof~" (no infinity support)."); + assert(0, "Overflow occured assigning to a " ~ + typeof(this).stringof ~ " (no infinity support)."); } else - enforce(exp == exponent_max, "Overflow occured assigning to a " - ~ typeof(this).stringof~" (no infinity support)."); + assert(exp == exponent_max, "Overflow occured assigning to a " + ~ typeof(this).stringof ~ " (no infinity support)."); } } @@ -424,7 +408,7 @@ public: static @property size_t dig() { auto shiftcnt = precision - ((flags&Flags.storeNormalized) != 0); - auto x = (shiftcnt == 64) ? 0 : 1uL << shiftcnt; + immutable x = (shiftcnt == 64) ? 0 : 1uL << shiftcnt; return cast(size_t) log10(x); } @@ -483,7 +467,7 @@ public: static if (flags & Flags.signed) value.sign = 0; value.exponent = 1; - static if(flags&Flags.storeNormalized) + static if (flags&Flags.storeNormalized) value.significand = 0; else value.significand = cast(T_sig) 1uL << (precision - 1); @@ -497,7 +481,7 @@ public: static @property CustomFloat im() { return CustomFloat(0.0f); } /// Initialize from any $(D real) compatible type. - this(F)(F input) if (__traits(compiles, cast(real)input )) + this(F)(F input) if (__traits(compiles, cast(real) input )) { this = input; } @@ -513,9 +497,10 @@ public: /// Assigns from any $(D real) compatible type. void opAssign(F)(F input) - if (__traits(compiles, cast(real)input)) + if (__traits(compiles, cast(real) input)) { - import std.conv: text; + import std.conv : text; + static if (staticIndexOf!(Unqual!F, float, double, real) >= 0) auto value = ToBinary!(Unqual!F)(input); else @@ -523,9 +508,9 @@ public: // Assign the sign bit static if (~flags & Flags.signed) - enforce( (!value.sign)^((flags&flags.negativeUnsigned)>0) , + assert((!value.sign) ^ ((flags&flags.negativeUnsigned) > 0), "Incorrectly signed floating point value assigned to a " ~ - typeof(this).stringof~" (no sign support)."); + typeof(this).stringof ~ " (no sign support)."); else sign = value.sign; @@ -548,7 +533,7 @@ public: @property F get(F)() if (staticIndexOf!(Unqual!F, float, double, real) >= 0) { - import std.conv: text; + import std.conv : text; ToBinary!F result; @@ -603,18 +588,18 @@ public: /// ditto int opCmp(T)(auto ref T b) - if (__traits(compiles, cast(real)b)) + if (__traits(compiles, cast(real) b)) { auto x = get!real; auto y = cast(real) b; - return (x>=y)-(x<=y); + return (x >= y)-(x <= y); } /// ditto void opOpAssign(string op, T)(auto ref T b) - if (__traits(compiles, mixin(`get!real`~op~`cast(real)b`))) + if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`))) { - return mixin(`this = this `~op~` cast(real)b`); + return mixin(`this = this `~op~` cast(real) b`); } /// ditto @@ -629,11 +614,11 @@ public: } } -unittest +@safe unittest { - import std.typetuple; + import std.meta; alias FPTypes = - TypeTuple!( + AliasSeq!( CustomFloat!(5, 10), CustomFloat!(5, 11, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), CustomFloat!(1, 15, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), @@ -665,8 +650,9 @@ unittest } } -unittest +@system unittest { + // @system due to to!string(CustomFloat) import std.conv; CustomFloat!(5, 10) y = CustomFloat!(5, 10)(0.125); assert(y.to!string == "0.125"); @@ -696,14 +682,19 @@ always be fastest, as the speed of floating-point calculations depends on very many factors. */ template FPTemporary(F) - if (isFloatingPoint!F) +if (isFloatingPoint!F) { - alias FPTemporary = real; + version(X86) + alias FPTemporary = real; + else + alias FPTemporary = Unqual!F; } /// -unittest +@safe unittest { + import std.math : approxEqual; + // Average numbers in an array double avg(in double[] a) { @@ -718,7 +709,7 @@ unittest } /** -Implements the $(WEB tinyurl.com/2zb9yr, secant method) for finding a +Implements the $(HTTP tinyurl.com/2zb9yr, secant method) for finding a root of the function $(D fun) starting from points $(D [xn_1, x_n]) (ideally close to the root). $(D Num) may be $(D float), $(D double), or $(D real). @@ -745,8 +736,10 @@ template secantMethod(alias fun) } /// -unittest +@safe unittest { + import std.math : approxEqual, cos; + float f(float x) { return cos(x) - x*x*x; @@ -755,8 +748,9 @@ unittest assert(approxEqual(x, 0.865474)); } -unittest +@system unittest { + // @system because of __gshared stderr scope(failure) stderr.writeln("Failure testing secantMethod"); float f(float x) { @@ -782,12 +776,12 @@ public: /** Find a real root of a real function f(x) via bracketing. * - * Given a function $(D f) and a range $(D [a..b]) such that $(D f(a)) - * and $(D f(b)) have opposite signs or at least one of them equals ±0, - * returns the value of $(D x) in - * the range which is closest to a root of $(D f(x)). If $(D f(x)) + * Given a function `f` and a range `[a .. b]` such that `f(a)` + * and `f(b)` have opposite signs or at least one of them equals ±0, + * returns the value of `x` in + * the range which is closest to a root of `f(x)`. If `f(x)` * has more than one root in the range, one will be chosen - * arbitrarily. If $(D f(x)) returns NaN, NaN will be returned; + * arbitrarily. If `f(x)` returns NaN, NaN will be returned; * otherwise, this algorithm is guaranteed to succeed. * * Uses an algorithm based on TOMS748, which uses inverse cubic @@ -795,29 +789,29 @@ public: * or secant interpolation. Compared to TOMS748, this implementation * improves worst-case performance by a factor of more than 100, and * typical performance by a factor of 2. For 80-bit reals, most - * problems require 8 to 15 calls to $(D f(x)) to achieve full machine + * problems require 8 to 15 calls to `f(x)` to achieve full machine * precision. The worst-case performance (pathological cases) is * approximately twice the number of bits. * * References: "On Enclosing Simple Roots of Nonlinear Equations", * G. Alefeld, F.A. Potra, Yixun Shi, Mathematics of Computation 61, - * pp733-744 (1993). Fortran code available from $(WEB + * pp733-744 (1993). Fortran code available from $(HTTP * www.netlib.org,www.netlib.org) as algorithm TOMS478. * */ T findRoot(T, DF, DT)(scope DF f, in T a, in T b, scope DT tolerance) //= (T a, T b) => false) - if( - isFloatingPoint!T && - is(typeof(tolerance(T.init, T.init)) : bool) && - is(typeof(f(T.init)) == R, R) && isFloatingPoint!R +if ( + isFloatingPoint!T && + is(typeof(tolerance(T.init, T.init)) : bool) && + is(typeof(f(T.init)) == R, R) && isFloatingPoint!R ) { immutable fa = f(a); - if(fa == 0) + if (fa == 0) return a; immutable fb = f(b); - if(fb == 0) + if (fb == 0) return b; immutable r = findRoot(f, a, b, fa, fb, tolerance); // Return the first value if it is smaller or NaN @@ -837,15 +831,15 @@ T findRoot(T, DF)(scope DF f, in T a, in T b) * * f = Function to be analyzed * - * ax = Left bound of initial range of $(D f) known to contain the + * ax = Left bound of initial range of `f` known to contain the * root. * - * bx = Right bound of initial range of $(D f) known to contain the + * bx = Right bound of initial range of `f` known to contain the * root. * * fax = Value of $(D f(ax)). * - * fbx = Value of $(D f(bx)). $(D fax) and $(D fbx) should have opposite signs. + * fbx = Value of $(D f(bx)). $(D fax) and $(D fbx) should have opposite signs. * ($(D f(ax)) and $(D f(bx)) are commonly known in advance.) * * @@ -858,17 +852,17 @@ T findRoot(T, DF)(scope DF f, in T a, in T b) * Returns: * * A tuple consisting of two ranges. The first two elements are the - * range (in $(D x)) of the root, while the second pair of elements + * range (in `x`) of the root, while the second pair of elements * are the corresponding function values at those points. If an exact * root was found, both of the first two elements will contain the * root, and the second pair of elements will be 0. */ Tuple!(T, T, R, R) findRoot(T, R, DF, DT)(scope DF f, in T ax, in T bx, in R fax, in R fbx, scope DT tolerance) // = (T a, T b) => false) - if( - isFloatingPoint!T && - is(typeof(tolerance(T.init, T.init)) : bool) && - is(typeof(f(T.init)) == R) && isFloatingPoint!R +if ( + isFloatingPoint!T && + is(typeof(tolerance(T.init, T.init)) : bool) && + is(typeof(f(T.init)) == R) && isFloatingPoint!R ) in { @@ -881,7 +875,7 @@ body // (www.netlib.org). The changes to improve the worst-cast performance are // entirely original. - T a, b, d; // [a..b] is our current bracket. d is the third best guess. + T a, b, d; // [a .. b] is our current bracket. d is the third best guess. R fa, fb, fd; // Values of f at a, b, d. bool done = false; // Has a root been found? @@ -934,7 +928,7 @@ body */ static T secant_interpolate(T a, T b, R fa, R fb) { - if (( ((a - b) == a) && b!=0) || (a!=0 && ((b - a) == b))) + if (( ((a - b) == a) && b != 0) || (a != 0 && ((b - a) == b))) { // Catastrophic cancellation if (a == 0) @@ -957,10 +951,10 @@ body return c; } - /* Uses 'numsteps' newton steps to approximate the zero in [a..b] of the + /* Uses 'numsteps' newton steps to approximate the zero in [a .. b] of the quadratic polynomial interpolating f(x) at a, b, and d. Returns: - The approximate zero in [a..b] of the quadratic polynomial. + The approximate zero in [a .. b] of the quadratic polynomial. */ T newtonQuadratic(int numsteps) { @@ -973,7 +967,7 @@ body T c = oppositeSigns(a2, fa) ? a : b; // start the safeguarded newton steps. - foreach (int i; 0..numsteps) + foreach (int i; 0 .. numsteps) { immutable T pc = a0 + (a1 + a2 * (c - b))*(c - a); immutable T pdc = a1 + a2*((2 * c) - (a + b)); @@ -1016,7 +1010,7 @@ whileloop: T a0 = a, b0 = b; // record the brackets // Do two higher-order (cubic or parabolic) interpolation steps. - foreach (int QQ; 0..2) + foreach (int QQ; 0 .. 2) { // Cubic inverse interpolation requires that // all four function values fa, fb, fd, and fe are distinct; @@ -1097,7 +1091,7 @@ whileloop: // DAC: If the secant predicts a value equal to an endpoint, it's // probably false. - if (c==a || c==b || c.isNaN() || fabs(c - u) > (b - a) / 2) + if (c == a || c == b || c.isNaN() || fabs(c - u) > (b - a) / 2) { if ((a-b) == a || (b-a) == b) { @@ -1105,9 +1099,9 @@ whileloop: c = 0; else { - if (a==0) + if (a == 0) c = ieeeMean(copysign(T(0), b), b); - else if (b==0) + else if (b == 0) c = ieeeMean(copysign(T(0), a), a); else c = ieeeMean(a, b); @@ -1130,7 +1124,7 @@ whileloop: // yet, or if we don't yet know what the exponent is, // perform a binary chop. - if ((a==0 || b==0 || + if ((a == 0 || b == 0 || (fabs(a) >= T(0.5) * fabs(b) && fabs(b) >= T(0.5) * fabs(a))) && (b - a) < T(0.25) * (b0 - a0)) { @@ -1144,7 +1138,7 @@ whileloop: if ((b - a) < T(0.25) * (b0 - a0)) baditer = 1; - foreach (int QQ; 0..baditer) + foreach (int QQ; 0 .. baditer) { e = d; fe = fd; @@ -1182,7 +1176,7 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b, return findRoot!(T, R delegate(T), bool delegate(T lo, T hi))(f, a, b, tolerance); } -nothrow unittest +@safe nothrow unittest { int numProblems = 0; int numCalls; @@ -1198,7 +1192,7 @@ nothrow unittest auto flo = f(result[0]); auto fhi = f(result[1]); - if (flo!=0) + if (flo != 0) { assert(oppositeSigns(flo, fhi)); } @@ -1314,7 +1308,7 @@ nothrow unittest numProblems=0; //testFindRoot(&alefeld0, PI_2, PI); - for (n=1; n<=10; ++n) + for (n=1; n <= 10; ++n) { //testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L); } @@ -1393,15 +1387,249 @@ nothrow unittest } //regression control -unittest +@system unittest +{ + // @system due to the case in the 2nd line + static assert(__traits(compiles, findRoot((float x)=>cast(real) x, float.init, float.init))); + static assert(__traits(compiles, findRoot!real((x)=>cast(double) x, real.init, real.init))); + static assert(__traits(compiles, findRoot((real x)=>cast(double) x, real.init, real.init))); +} + +/++ +Find a real minimum of a real function `f(x)` via bracketing. +Given a function `f` and a range `(ax .. bx)`, +returns the value of `x` in the range which is closest to a minimum of `f(x)`. +`f` is never evaluted at the endpoints of `ax` and `bx`. +If `f(x)` has more than one minimum in the range, one will be chosen arbitrarily. +If `f(x)` returns NaN or -Infinity, `(x, f(x), NaN)` will be returned; +otherwise, this algorithm is guaranteed to succeed. + +Params: + f = Function to be analyzed + ax = Left bound of initial range of f known to contain the minimum. + bx = Right bound of initial range of f known to contain the minimum. + relTolerance = Relative tolerance. + absTolerance = Absolute tolerance. + +Preconditions: + `ax` and `bx` shall be finite reals. $(BR) + $(D relTolerance) shall be normal positive real. $(BR) + $(D absTolerance) shall be normal positive real no less then $(D T.epsilon*2). + +Returns: + A tuple consisting of `x`, `y = f(x)` and `error = 3 * (absTolerance * fabs(x) + relTolerance)`. + + The method used is a combination of golden section search and +successive parabolic interpolation. Convergence is never much slower +than that for a Fibonacci search. + +References: + "Algorithms for Minimization without Derivatives", Richard Brent, Prentice-Hall, Inc. (1973) + +See_Also: $(LREF findRoot), $(REF isNormal, std,math) ++/ +Tuple!(T, "x", Unqual!(ReturnType!DF), "y", T, "error") +findLocalMin(T, DF)( + scope DF f, + in T ax, + in T bx, + in T relTolerance = sqrt(T.epsilon), + in T absTolerance = sqrt(T.epsilon), + ) +if (isFloatingPoint!T + && __traits(compiles, {T _ = DF.init(T.init);})) +in +{ + assert(isFinite(ax), "ax is not finite"); + assert(isFinite(bx), "bx is not finite"); + assert(isNormal(relTolerance), "relTolerance is not normal floating point number"); + assert(isNormal(absTolerance), "absTolerance is not normal floating point number"); + assert(relTolerance >= 0, "absTolerance is not positive"); + assert(absTolerance >= T.epsilon*2, "absTolerance is not greater then `2*T.epsilon`"); +} +out (result) +{ + assert(isFinite(result.x)); +} +body +{ + alias R = Unqual!(CommonType!(ReturnType!DF, T)); + // c is the squared inverse of the golden ratio + // (3 - sqrt(5))/2 + // Value obtained from Wolfram Alpha. + enum T c = 0x0.61c8864680b583ea0c633f9fa31237p+0L; + enum T cm1 = 0x0.9e3779b97f4a7c15f39cc0605cedc8p+0L; + R tolerance; + T a = ax > bx ? bx : ax; + T b = ax > bx ? ax : bx; + // sequence of declarations suitable for SIMD instructions + T v = a * cm1 + b * c; + assert(isFinite(v)); + R fv = f(v); + if (isNaN(fv) || fv == -T.infinity) + { + return typeof(return)(v, fv, T.init); + } + T w = v; + R fw = fv; + T x = v; + R fx = fv; + size_t i; + for (R d = 0, e = 0;;) + { + i++; + T m = (a + b) / 2; + // This fix is not part of the original algorithm + if (!isFinite(m)) // fix infinity loop. Issue can be reproduced in R. + { + m = a / 2 + b / 2; + if (!isFinite(m)) // fast-math compiler switch is enabled + { + //SIMD instructions can be used by compiler, do not reduce declarations + int a_exp = void; + int b_exp = void; + immutable an = frexp(a, a_exp); + immutable bn = frexp(b, b_exp); + immutable am = ldexp(an, a_exp-1); + immutable bm = ldexp(bn, b_exp-1); + m = am + bm; + if (!isFinite(m)) // wrong input: constraints are disabled in release mode + { + return typeof(return).init; + } + } + } + tolerance = absTolerance * fabs(x) + relTolerance; + immutable t2 = tolerance * 2; + // check stopping criterion + if (!(fabs(x - m) > t2 - (b - a) / 2)) + { + break; + } + R p = 0; + R q = 0; + R r = 0; + // fit parabola + if (fabs(e) > tolerance) + { + immutable xw = x - w; + immutable fxw = fx - fw; + immutable xv = x - v; + immutable fxv = fx - fv; + immutable xwfxv = xw * fxv; + immutable xvfxw = xv * fxw; + p = xv * xvfxw - xw * xwfxv; + q = (xvfxw - xwfxv) * 2; + if (q > 0) + p = -p; + else + q = -q; + r = e; + e = d; + } + T u; + // a parabolic-interpolation step + if (fabs(p) < fabs(q * r / 2) && p > q * (a - x) && p < q * (b - x)) + { + d = p / q; + u = x + d; + // f must not be evaluated too close to a or b + if (u - a < t2 || b - u < t2) + d = x < m ? tolerance : -tolerance; + } + // a golden-section step + else + { + e = (x < m ? b : a) - x; + d = c * e; + } + // f must not be evaluated too close to x + u = x + (fabs(d) >= tolerance ? d : d > 0 ? tolerance : -tolerance); + immutable fu = f(u); + if (isNaN(fu) || fu == -T.infinity) + { + return typeof(return)(u, fu, T.init); + } + // update a, b, v, w, and x + if (fu <= fx) + { + u < x ? b : a = x; + v = w; fv = fw; + w = x; fw = fx; + x = u; fx = fu; + } + else + { + u < x ? a : b = u; + if (fu <= fw || w == x) + { + v = w; fv = fw; + w = u; fw = fu; + } + else if (fu <= fv || v == x || v == w) + { // do not remove this braces + v = u; fv = fu; + } + } + } + return typeof(return)(x, fx, tolerance * 3); +} + +/// +@safe unittest { - static assert(__traits(compiles, findRoot((float x)=>cast(real)x, float.init, float.init))); - static assert(__traits(compiles, findRoot!real((x)=>cast(double)x, real.init, real.init))); - static assert(__traits(compiles, findRoot((real x)=>cast(double)x, real.init, real.init))); + import std.math : approxEqual; + + auto ret = findLocalMin((double x) => (x-4)^^2, -1e7, 1e7); + assert(ret.x.approxEqual(4.0)); + assert(ret.y.approxEqual(0.0)); +} + +@safe unittest +{ + import std.meta : AliasSeq; + foreach (T; AliasSeq!(double, float, real)) + { + { + auto ret = findLocalMin!T((T x) => (x-4)^^2, T.min_normal, 1e7); + assert(ret.x.approxEqual(T(4))); + assert(ret.y.approxEqual(T(0))); + } + { + auto ret = findLocalMin!T((T x) => fabs(x-1), -T.max/4, T.max/4, T.min_normal, 2*T.epsilon); + assert(approxEqual(ret.x, T(1))); + assert(approxEqual(ret.y, T(0))); + assert(ret.error <= 10 * T.epsilon); + } + { + auto ret = findLocalMin!T((T x) => T.init, 0, 1, T.min_normal, 2*T.epsilon); + assert(!ret.x.isNaN); + assert(ret.y.isNaN); + assert(ret.error.isNaN); + } + { + auto ret = findLocalMin!T((T x) => log(x), 0, 1, T.min_normal, 2*T.epsilon); + assert(ret.error < 3.00001 * ((2*T.epsilon)*fabs(ret.x)+ T.min_normal)); + assert(ret.x >= 0 && ret.x <= ret.error); + } + { + auto ret = findLocalMin!T((T x) => log(x), 0, T.max, T.min_normal, 2*T.epsilon); + assert(ret.y < -18); + assert(ret.error < 5e-08); + assert(ret.x >= 0 && ret.x <= ret.error); + } + { + auto ret = findLocalMin!T((T x) => -fabs(x), -1, 1, T.min_normal, 2*T.epsilon); + assert(ret.x.fabs.approxEqual(T(1))); + assert(ret.y.fabs.approxEqual(T(1))); + assert(ret.error.approxEqual(T(0))); + } + } } /** -Computes $(LUCKY Euclidean distance) between input ranges $(D a) and +Computes $(LINK2 https://en.wikipedia.org/wiki/Euclidean_distance, +Euclidean distance) between input ranges $(D a) and $(D b). The two ranges must have the same length. The three-parameter version stops computation as soon as the distance is greater than or equal to $(D limit) (this is useful to save computation if a small @@ -1409,47 +1637,47 @@ distance is sought). */ CommonType!(ElementType!(Range1), ElementType!(Range2)) euclideanDistance(Range1, Range2)(Range1 a, Range2 b) - if (isInputRange!(Range1) && isInputRange!(Range2)) +if (isInputRange!(Range1) && isInputRange!(Range2)) { enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) result = 0; for (; !a.empty; a.popFront(), b.popFront()) { - auto t = a.front - b.front; + immutable t = a.front - b.front; result += t * t; } - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); return sqrt(result); } /// Ditto CommonType!(ElementType!(Range1), ElementType!(Range2)) euclideanDistance(Range1, Range2, F)(Range1 a, Range2 b, F limit) - if (isInputRange!(Range1) && isInputRange!(Range2)) +if (isInputRange!(Range1) && isInputRange!(Range2)) { limit *= limit; enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) result = 0; for (; ; a.popFront(), b.popFront()) { if (a.empty) { - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); break; } - auto t = a.front - b.front; + immutable t = a.front - b.front; result += t * t; if (result >= limit) break; } return sqrt(result); } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(double, const double, immutable double)) + import std.meta : AliasSeq; + foreach (T; AliasSeq!(double, const double, immutable double)) { T[] a = [ 1.0, 2.0, ]; T[] b = [ 4.0, 6.0, ]; @@ -1461,24 +1689,25 @@ unittest } /** -Computes the $(LUCKY dot product) of input ranges $(D a) and $(D +Computes the $(LINK2 https://en.wikipedia.org/wiki/Dot_product, +dot product) of input ranges $(D a) and $(D b). The two ranges must have the same length. If both ranges define length, the check is done once; otherwise, it is done at each iteration. */ CommonType!(ElementType!(Range1), ElementType!(Range2)) dotProduct(Range1, Range2)(Range1 a, Range2 b) - if (isInputRange!(Range1) && isInputRange!(Range2) && - !(isArray!(Range1) && isArray!(Range2))) +if (isInputRange!(Range1) && isInputRange!(Range2) && + !(isArray!(Range1) && isArray!(Range2))) { enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) result = 0; for (; !a.empty; a.popFront(), b.popFront()) { result += a.front * b.front; } - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); return result; } @@ -1536,10 +1765,12 @@ dotProduct(F1, F2)(in F1[] avector, in F2[] bvector) return sum0; } -unittest +@system unittest { - import std.typetuple; - foreach(T; TypeTuple!(double, const double, immutable double)) + // @system due to dotProduct and assertCTFEable + import std.exception : assertCTFEable; + import std.meta : AliasSeq; + foreach (T; AliasSeq!(double, const double, immutable double)) { T[] a = [ 1.0, 2.0, ]; T[] b = [ 4.0, 6.0, ]; @@ -1556,17 +1787,18 @@ unittest } /** -Computes the $(LUCKY cosine similarity) of input ranges $(D a) and $(D +Computes the $(LINK2 https://en.wikipedia.org/wiki/Cosine_similarity, +cosine similarity) of input ranges $(D a) and $(D b). The two ranges must have the same length. If both ranges define length, the check is done once; otherwise, it is done at each iteration. If either range has all-zero elements, return 0. */ CommonType!(ElementType!(Range1), ElementType!(Range2)) cosineSimilarity(Range1, Range2)(Range1 a, Range2 b) - if (isInputRange!(Range1) && isInputRange!(Range2)) +if (isInputRange!(Range1) && isInputRange!(Range2)) { enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) norma = 0, normb = 0, dotprod = 0; for (; !a.empty; a.popFront(), b.popFront()) { @@ -1575,15 +1807,15 @@ cosineSimilarity(Range1, Range2)(Range1 a, Range2 b) normb += t2 * t2; dotprod += t1 * t2; } - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); if (norma == 0 || normb == 0) return 0; return dotprod / sqrt(norma * normb); } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(double, const double, immutable double)) + import std.meta : AliasSeq; + foreach (T; AliasSeq!(double, const double, immutable double)) { T[] a = [ 1.0, 2.0, ]; T[] b = [ 4.0, 3.0, ]; @@ -1604,7 +1836,7 @@ Returns: $(D true) if normalization completed normally, $(D false) if all elements in $(D range) were zero or if $(D range) is empty. */ bool normalize(R)(R range, ElementType!(R) sum = 1) - if (isForwardRange!(R)) +if (isForwardRange!(R)) { ElementType!(R) s = 0; // Step 1: Compute sum and length of the range @@ -1630,21 +1862,21 @@ bool normalize(R)(R range, ElementType!(R) sum = 1) { if (length) { - auto f = sum / range.length; + immutable f = sum / range.length; foreach (ref e; range) e = f; } return false; } // The path most traveled assert(s >= 0); - auto f = sum / s; + immutable f = sum / s; foreach (ref e; range) e *= f; return true; } /// -unittest +@safe unittest { double[] a = []; assert(!normalize(a)); @@ -1661,7 +1893,7 @@ Compute the sum of binary logarithms of the input range $(D r). The error of this method is much smaller than with a naive sum of log2. */ ElementType!Range sumOfLog2s(Range)(Range r) - if (isInputRange!Range && isFloatingPoint!(ElementType!Range)) +if (isInputRange!Range && isFloatingPoint!(ElementType!Range)) { long exp = 0; Unqual!(typeof(return)) x = 1; @@ -1682,8 +1914,10 @@ ElementType!Range sumOfLog2s(Range)(Range r) } /// -unittest +@safe unittest { + import std.math : isNaN; + assert(sumOfLog2s(new double[0]) == 0); assert(sumOfLog2s([0.0L]) == -real.infinity); assert(sumOfLog2s([-0.0L]) == -real.infinity); @@ -1697,20 +1931,22 @@ unittest } /** -Computes $(LUCKY _entropy) of input range $(D r) in bits. This +Computes $(LINK2 https://en.wikipedia.org/wiki/Entropy_(information_theory), +_entropy) of input range $(D r) in bits. This function assumes (without checking) that the values in $(D r) are all in $(D [0, 1]). For the entropy to be meaningful, often $(D r) should be normalized too (i.e., its values should sum to 1). The two-parameter version stops evaluating as soon as the intermediate result is greater than or equal to $(D max). */ -ElementType!Range entropy(Range)(Range r) if (isInputRange!Range) +ElementType!Range entropy(Range)(Range r) +if (isInputRange!Range) { Unqual!(typeof(return)) result = 0.0; - foreach (e; r) + for (;!r.empty; r.popFront) { - if (!e) continue; - result -= e * log2(e); + if (!r.front) continue; + result -= r.front * log2(r.front); } return result; } @@ -1721,19 +1957,19 @@ if (isInputRange!Range && !is(CommonType!(ElementType!Range, F) == void)) { Unqual!(typeof(return)) result = 0.0; - foreach (e; r) + for (;!r.empty; r.popFront) { - if (!e) continue; - result -= e * log2(e); + if (!r.front) continue; + result -= r.front * log2(r.front); if (result >= max) break; } return result; } -unittest +@safe unittest { - import std.typetuple; - foreach(T; TypeTuple!(double, const double, immutable double)) + import std.meta : AliasSeq; + foreach (T; AliasSeq!(double, const double, immutable double)) { T[] p = [ 0.0, 0, 0, 1 ]; assert(entropy(p) == 0); @@ -1744,7 +1980,8 @@ unittest } /** -Computes the $(LUCKY Kullback-Leibler divergence) between input ranges +Computes the $(LINK2 https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence, +Kullback-Leibler divergence) between input ranges $(D a) and $(D b), which is the sum $(D ai * log(ai / bi)). The base of logarithm is 2. The ranges are assumed to contain elements in $(D [0, 1]). Usually the ranges are normalized probability distributions, @@ -1757,10 +1994,10 @@ positive. */ CommonType!(ElementType!Range1, ElementType!Range2) kullbackLeiblerDivergence(Range1, Range2)(Range1 a, Range2 b) - if (isInputRange!(Range1) && isInputRange!(Range2)) +if (isInputRange!(Range1) && isInputRange!(Range2)) { enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) result = 0; for (; !a.empty; a.popFront(), b.popFront()) { @@ -1771,13 +2008,15 @@ kullbackLeiblerDivergence(Range1, Range2)(Range1 a, Range2 b) assert(t1 > 0 && t2 > 0); result += t1 * log2(t1 / t2); } - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); return result; } /// -unittest +@safe unittest { + import std.math : approxEqual; + double[] p = [ 0.0, 0, 0, 1 ]; assert(kullbackLeiblerDivergence(p, p) == 0); double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ]; @@ -1790,7 +2029,8 @@ unittest } /** -Computes the $(LUCKY Jensen-Shannon divergence) between $(D a) and $(D +Computes the $(LINK2 https://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence, +Jensen-Shannon divergence) between $(D a) and $(D b), which is the sum $(D (ai * log(2 * ai / (ai + bi)) + bi * log(2 * bi / (ai + bi))) / 2). The base of logarithm is 2. The ranges are assumed to contain elements in $(D [0, 1]). Usually the ranges are @@ -1802,11 +2042,11 @@ or equal to $(D limit). */ CommonType!(ElementType!Range1, ElementType!Range2) jensenShannonDivergence(Range1, Range2)(Range1 a, Range2 b) - if (isInputRange!Range1 && isInputRange!Range2 && - is(CommonType!(ElementType!Range1, ElementType!Range2))) +if (isInputRange!Range1 && isInputRange!Range2 && + is(CommonType!(ElementType!Range1, ElementType!Range2))) { enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) result = 0; for (; !a.empty; a.popFront(), b.popFront()) { @@ -1822,19 +2062,19 @@ jensenShannonDivergence(Range1, Range2)(Range1 a, Range2 b) result += t2 * log2(t2 / avg); } } - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); return result / 2; } /// Ditto CommonType!(ElementType!Range1, ElementType!Range2) jensenShannonDivergence(Range1, Range2, F)(Range1 a, Range2 b, F limit) - if (isInputRange!Range1 && isInputRange!Range2 && - is(typeof(CommonType!(ElementType!Range1, ElementType!Range2).init - >= F.init) : bool)) +if (isInputRange!Range1 && isInputRange!Range2 && + is(typeof(CommonType!(ElementType!Range1, ElementType!Range2).init + >= F.init) : bool)) { enum bool haveLen = hasLength!(Range1) && hasLength!(Range2); - static if (haveLen) enforce(a.length == b.length); + static if (haveLen) assert(a.length == b.length); Unqual!(typeof(return)) result = 0; limit *= 2; for (; !a.empty; a.popFront(), b.popFront()) @@ -1852,13 +2092,15 @@ jensenShannonDivergence(Range1, Range2, F)(Range1 a, Range2 b, F limit) } if (result >= limit) break; } - static if (!haveLen) enforce(b.empty); + static if (!haveLen) assert(b.empty); return result / 2; } /// -unittest +@safe unittest { + import std.math : approxEqual; + double[] p = [ 0.0, 0, 0, 1 ]; assert(jensenShannonDivergence(p, p) == 0); double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ]; @@ -1942,18 +2184,21 @@ t.length)) extra bytes of memory and $(BIGOH s.length * t.length) time to complete. */ F gapWeightedSimilarity(alias comp = "a == b", R1, R2, F)(R1 s, R2 t, F lambda) - if (isRandomAccessRange!(R1) && hasLength!(R1) && - isRandomAccessRange!(R2) && hasLength!(R2)) +if (isRandomAccessRange!(R1) && hasLength!(R1) && + isRandomAccessRange!(R2) && hasLength!(R2)) { import std.functional : binaryFun; - import std.algorithm : swap; - import core.stdc.stdlib; + import std.algorithm.mutation : swap; + import core.stdc.stdlib : malloc, free; + import core.exception : onOutOfMemoryError; if (s.length < t.length) return gapWeightedSimilarity(t, s, lambda); if (!t.length) return 0; - immutable tl1 = t.length + 1; - auto dpvi = enforce(cast(F*) malloc(F.sizeof * 2 * t.length)); + auto dpvi = cast(F*) malloc(F.sizeof * 2 * t.length); + if (!dpvi) + onOutOfMemoryError(); + auto dpvi1 = dpvi + t.length; scope(exit) free(dpvi < dpvi1 ? dpvi : dpvi1); dpvi[0 .. t.length] = 0; @@ -1987,7 +2232,7 @@ F gapWeightedSimilarity(alias comp = "a == b", R1, R2, F)(R1 s, R2 t, F lambda) return result; } -unittest +@system unittest { string[] s = ["Hello", "brave", "new", "world"]; string[] t = ["Hello", "new", "world"]; @@ -2020,8 +2265,8 @@ as $(D sSelfSim) and $(D tSelfSim), respectively. Select!(isFloatingPoint!(F), F, double) gapWeightedSimilarityNormalized(alias comp = "a == b", R1, R2, F) (R1 s, R2 t, F lambda, F sSelfSim = F.init, F tSelfSim = F.init) - if (isRandomAccessRange!(R1) && hasLength!(R1) && - isRandomAccessRange!(R2) && hasLength!(R2)) +if (isRandomAccessRange!(R1) && hasLength!(R1) && + isRandomAccessRange!(R2) && hasLength!(R2)) { static bool uncomputed(F n) { @@ -2042,8 +2287,10 @@ gapWeightedSimilarityNormalized(alias comp = "a == b", R1, R2, F) } /// -unittest +@system unittest { + import std.math : approxEqual, sqrt; + string[] s = ["Hello", "brave", "new", "world"]; string[] t = ["Hello", "new", "world"]; assert(gapWeightedSimilarity(s, s, 1) == 15); @@ -2061,15 +2308,15 @@ t.length). The time complexity is $(BIGOH s.length * t.length) time for computing each step. Continuing on the previous example: The implementation is based on the pseudocode in Fig. 4 of the paper -$(WEB jmlr.csail.mit.edu/papers/volume6/rousu05a/rousu05a.pdf, +$(HTTP jmlr.csail.mit.edu/papers/volume6/rousu05a/rousu05a.pdf, "Efï¬cient Computation of Gapped Substring Kernels on Large Alphabets") by Rousu et al., with additional algorithmic and systems-level optimizations. */ struct GapWeightedSimilarityIncremental(Range, F = double) - if (isRandomAccessRange!(Range) && hasLength!(Range)) +if (isRandomAccessRange!(Range) && hasLength!(Range)) { - import core.stdc.stdlib; + import core.stdc.stdlib : malloc, realloc, alloca, free; private: Range s, t; @@ -2086,7 +2333,9 @@ time and computes all matches of length 1. */ this(Range s, Range t, F lambda) { - enforce(lambda > 0); + import core.exception : onOutOfMemoryError; + + assert(lambda > 0); this.gram = 0; this.lambda = lambda; this.lambda2 = lambda * lambda; // for efficiency only @@ -2127,8 +2376,9 @@ time and computes all matches of length 1. this.s = s; this.t = t; - // Si = errnoEnforce(cast(F *) malloc(t.length * F.sizeof)); - kl = errnoEnforce(cast(F *) malloc(s.length * t.length * F.sizeof)); + kl = cast(F*) malloc(s.length * t.length * F.sizeof); + if (!kl) + onOutOfMemoryError(); kl[0 .. s.length * t.length] = 0; foreach (pos; 0 .. k0len) @@ -2154,7 +2404,7 @@ time and computes all matches of length 1. */ void popFront() { - import std.algorithm : swap; + import std.algorithm.mutation : swap; // This is a large source of optimization: if similarity at // the gram-1 level was 0, then we can safely assume @@ -2263,7 +2513,7 @@ GapWeightedSimilarityIncremental!(R, F) gapWeightedSimilarityIncremental(R, F) } /// -unittest +@system unittest { string[] s = ["Hello", "brave", "new", "world"]; string[] t = ["Hello", "new", "world"]; @@ -2277,9 +2527,9 @@ unittest assert(simIter.empty); // no more match } -unittest +@system unittest { - import std.conv: text; + import std.conv : text; string[] s = ["Hello", "brave", "new", "world"]; string[] t = ["Hello", "new", "world"]; auto simIter = gapWeightedSimilarityIncremental(s, t, 1.0); @@ -2319,7 +2569,7 @@ unittest assert(simIter.front == 0.5, text(simIter.front)); // one 2-gram match, 1 gap } -unittest +@system unittest { GapWeightedSimilarityIncremental!(string[]) sim = GapWeightedSimilarityIncremental!(string[])( @@ -2349,38 +2599,153 @@ unittest /** Computes the greatest common divisor of $(D a) and $(D b) by using -Euclid's algorithm. +an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm, Euclid's) +or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm. + +Params: + T = Any numerical type that supports the modulo operator `%`. If + bit-shifting `<<` and `>>` are also supported, Stein's algorithm will + be used; otherwise, Euclid's algorithm is used as _a fallback. +Returns: + The greatest common divisor of the given arguments. */ T gcd(T)(T a, T b) + if (isIntegral!T) { static if (is(T == const) || is(T == immutable)) { return gcd!(Unqual!T)(a, b); } - else + else version(DigitalMars) { static if (T.min < 0) { - enforce(a >= 0 && b >=0); + assert(a >= 0 && b >= 0); } while (b) { - auto t = b; + immutable t = b; b = a % b; a = t; } return a; } + else + { + if (a == 0) + return b; + if (b == 0) + return a; + + import core.bitop : bsf; + import std.algorithm.mutation : swap; + + immutable uint shift = bsf(a | b); + a >>= a.bsf; + + do + { + b >>= b.bsf; + if (a > b) + swap(a, b); + b -= a; + } while (b); + + return a << shift; + } } /// -unittest +@safe unittest { assert(gcd(2 * 5 * 7 * 7, 5 * 7 * 11) == 5 * 7); const int a = 5 * 13 * 23 * 23, b = 13 * 59; assert(gcd(a, b) == 13); } +// This overload is for non-builtin numerical types like BigInt or +// user-defined types. +/// ditto +T gcd(T)(T a, T b) + if (!isIntegral!T && + is(typeof(T.init % T.init)) && + is(typeof(T.init == 0 || T.init > 0))) +{ + import std.algorithm.mutation : swap; + + enum canUseBinaryGcd = is(typeof(() { + T t, u; + t <<= 1; + t >>= 1; + t -= u; + bool b = (t & 1) == 0; + swap(t, u); + })); + + assert(a >= 0 && b >= 0); + + static if (canUseBinaryGcd) + { + uint shift = 0; + while ((a & 1) == 0 && (b & 1) == 0) + { + a >>= 1; + b >>= 1; + shift++; + } + + do + { + assert((a & 1) != 0); + while ((b & 1) == 0) + b >>= 1; + if (a > b) + swap(a, b); + b -= a; + } while (b); + + return a << shift; + } + else + { + // The only thing we have is %; fallback to Euclidean algorithm. + while (b != 0) + { + auto t = b; + b = a % b; + a = t; + } + return a; + } +} + +// Issue 7102 +@system pure unittest +{ + import std.bigint : BigInt; + assert(gcd(BigInt("71_000_000_000_000_000_000"), + BigInt("31_000_000_000_000_000_000")) == + BigInt("1_000_000_000_000_000_000")); +} + +@safe pure nothrow unittest +{ + // A numerical type that only supports % and - (to force gcd implementation + // to use Euclidean algorithm). + struct CrippledInt + { + int impl; + CrippledInt opBinary(string op : "%")(CrippledInt i) + { + return CrippledInt(impl % i.impl); + } + int opEquals(CrippledInt i) { return impl == i.impl; } + int opEquals(int i) { return impl == i; } + int opCmp(int i) { return (impl < i) ? -1 : (impl > i) ? 1 : 0; } + } + assert(gcd(CrippledInt(2310), CrippledInt(1309)) == CrippledInt(77)); +} + // This is to make tweaking the speed/size vs. accuracy tradeoff easy, // though floats seem accurate enough for all practical purposes, since // they pass the "approxEqual(inverseFft(fft(arr)), arr)" test even for @@ -2396,11 +2761,11 @@ private alias lookup_t = float; * one-off FFT. * * References: - * $(WEB en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm) + * $(HTTP en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm) */ final class Fft { - import std.algorithm : map; + import std.algorithm.iteration : map; import core.bitop : bsf; import std.array : uninitializedArray; @@ -2409,8 +2774,8 @@ private: void enforceSize(R)(R range) const { - import std.conv: text; - enforce(range.length <= size, text( + import std.conv : text; + assert(range.length <= size, text( "FFT size mismatch. Expected ", size, ", got ", range.length)); } @@ -2418,14 +2783,14 @@ private: in { assert(range.length >= 4); - assert(isPowerOfTwo(range.length)); + assert(isPowerOf2(range.length)); } body { auto recurseRange = range; recurseRange.doubleSteps(); - if(buf.length > 4) + if (buf.length > 4) { fftImpl(recurseRange, buf[0..$ / 2]); recurseRange.popHalf(); @@ -2452,7 +2817,7 @@ private: in { assert(range.length >= 4); - assert(isPowerOfTwo(range.length)); + assert(isPowerOf2(range.length)); } body { @@ -2460,7 +2825,7 @@ private: // Converts odd indices of range to the imaginary components of // a range half the size. The even indices become the real components. - static if(isArray!R && isFloatingPoint!E) + static if (isArray!R && isFloatingPoint!E) { // Then the memory layout of complex numbers provides a dirt // cheap way to convert. This is a common case, so take advantage. @@ -2523,7 +2888,7 @@ private: typeof(this) opSlice(size_t lower, size_t upper) { - return typeof(this)(source[lower * 2..upper * 2]); + return typeof(this)(source[lower * 2 .. upper * 2]); } } @@ -2539,7 +2904,7 @@ private: evenFft[0].im = 0; // evenFft[0].re is already right b/c it's aliased with buf[0].re. - foreach (k; 1..halfN / 2 + 1) + foreach (k; 1 .. halfN / 2 + 1) { immutable bufk = buf[k]; immutable bufnk = buf[buf.length / 2 - k]; @@ -2560,7 +2925,7 @@ private: void butterfly(R)(R buf) const in { - assert(isPowerOfTwo(buf.length)); + assert(isPowerOf2(buf.length)); } body { @@ -2642,21 +3007,22 @@ private: * inefficient, but having all the lookups be next to each other in * memory at every level of iteration is a huge win performance-wise. */ - if(size == 0) + if (size == 0) { return; } - enforce(isPowerOfTwo(size), + assert(isPowerOf2(size), "Can only do FFTs on ranges with a size that is a power of two."); + auto table = new lookup_t[][bsf(size) + 1]; table[$ - 1] = memSpace[$ - size..$]; - memSpace = memSpace[0..size]; + memSpace = memSpace[0 .. size]; auto lastRow = table[$ - 1]; lastRow[0] = 0; // -sin(0) == 0. - foreach (ptrdiff_t i; 1..size) + foreach (ptrdiff_t i; 1 .. size) { // The hard coded cases are for improved accuracy and to prevent // annoying non-zeroness when stuff should be zero. @@ -2672,7 +3038,7 @@ private: } // Fill in all the other rows with strided versions. - foreach (i; 1..table.length - 1) + foreach (i; 1 .. table.length - 1) { immutable strideLength = size / (2 ^^ i); auto strided = Stride!(lookup_t[])(lastRow, strideLength); @@ -2747,9 +3113,9 @@ public: * property that can be both read and written and are floating point numbers. */ void fft(Ret, R)(R range, Ret buf) const - if(isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) + if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) { - enforce(buf.length == range.length); + assert(buf.length == range.length); enforceSize(range); if (range.length == 0) @@ -2828,7 +3194,7 @@ public: immutable lenNeg1 = 1.0 / buf.length; foreach (ref elem; buf) { - auto temp = elem.re * lenNeg1; + immutable temp = elem.re * lenNeg1; elem.re = elem.im * lenNeg1; elem.im = temp; } @@ -2840,13 +3206,13 @@ public: // scope. private enum string MakeLocalFft = q{ import core.stdc.stdlib; - import core.exception : OutOfMemoryError; + import core.exception : onOutOfMemoryError; + auto lookupBuf = (cast(lookup_t*) malloc(range.length * 2 * lookup_t.sizeof)) - [0..2 * range.length]; + [0 .. 2 * range.length]; if (!lookupBuf.ptr) - { - throw new OutOfMemoryError(__FILE__, __LINE__); - } + onOutOfMemoryError(); + scope(exit) free(cast(void*) lookupBuf.ptr); auto fftObj = scoped!Fft(lookupBuf); }; @@ -2886,7 +3252,7 @@ void inverseFft(Ret, R)(R range, Ret buf) return fftObj.inverseFft!(Ret, R)(range, buf); } -unittest +@system unittest { import std.algorithm; import std.range; @@ -3008,12 +3374,12 @@ struct Stride(R) { if (range.length >= _nSteps) { - range = range[_nSteps..range.length]; + range = range[_nSteps .. range.length]; _length--; } else { - range = range[0..0]; + range = range[0 .. 0]; _length = 0; } } @@ -3021,7 +3387,7 @@ struct Stride(R) // Pops half the range's stride. void popHalf() { - range = range[_nSteps / 2..range.length]; + range = range[_nSteps / 2 .. range.length]; } bool empty() const @property @@ -3075,19 +3441,14 @@ void slowFourier4(Ret, R)(R range, Ret buf) buf[3] = range[0] + range[1] * C(0, 1) - range[2] - range[3] * C(0, 1); } -bool isPowerOfTwo(size_t num) -{ - import core.bitop : bsf, bsr; - return bsr(num) == bsf(num); -} - -size_t roundDownToPowerOf2(size_t num) +N roundDownToPowerOf2(N)(N num) +if (isScalarType!N && !isFloatingPoint!N) { import core.bitop : bsr; - return num & (1 << bsr(num)); + return num & (cast(N) 1 << bsr(num)); } -unittest +@safe unittest { assert(roundDownToPowerOf2(7) == 4); assert(roundDownToPowerOf2(4) == 4); @@ -3099,7 +3460,7 @@ template isComplexLike(T) is(typeof(T.init.im)); } -unittest +@safe unittest { static assert(isComplexLike!(Complex!double)); static assert(!isComplexLike!(uint)); diff --git a/std/outbuffer.d b/std/outbuffer.d index 20b76c5e08a..f136c8f42b1 100644 --- a/std/outbuffer.d +++ b/std/outbuffer.d @@ -3,25 +3,16 @@ /** Serialize data to $(D ubyte) arrays. - * Macros: - * WIKI = Phobos/StdOutbuffer - * * Copyright: Copyright Digital Mars 2000 - 2015. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Source: $(PHOBOSSRC std/_outbuffer.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) */ module std.outbuffer; -private -{ - import core.memory; - import core.stdc.stdarg; - import core.stdc.stdio; - import core.stdc.stdlib; - import std.algorithm; - import std.string; -} +import core.stdc.stdarg; // : va_list; /********************************************* * OutBuffer provides a way to build up an array of bytes out @@ -30,7 +21,9 @@ private * OutBuffer's byte order is the format native to the computer. * To control the byte order (endianness), use a class derived * from OutBuffer. - * OutBuffer's internal buffer is allocated with the GC. + * OutBuffer's internal buffer is allocated with the GC. Pointers + * stored into the buffer are scanned by the GC, but you have to + * ensure proper alignment, e.g. by using alignSize((void*).sizeof). */ class OutBuffer @@ -40,21 +33,14 @@ class OutBuffer invariant() { - //printf("this = %p, offset = %x, data.length = %u\n", this, offset, data.length); assert(offset <= data.length); } pure nothrow @safe { - this() - { - //printf("in OutBuffer constructor\n"); - } - /********************************* * Convert to array of bytes. */ - ubyte[] toBytes() { return data[0 .. offset]; } /*********************************** @@ -64,8 +50,6 @@ class OutBuffer * speed optimization, a good guess at the maximum size of the resulting * buffer will improve performance by eliminating reallocations and copying. */ - - void reserve(size_t nbytes) @trusted in { @@ -77,18 +61,18 @@ class OutBuffer } body { - //c.stdio.printf("OutBuffer.reserve: length = %d, offset = %d, nbytes = %d\n", data.length, offset, nbytes); if (data.length < offset + nbytes) { - data.length = (offset + nbytes) * 2; - GC.clrAttr(data.ptr, GC.BlkAttr.NO_SCAN); + void[] vdata = data; + vdata.length = (offset + nbytes + 7) * 2; // allocates as void[] to not set BlkAttr.NO_SCAN + data = cast(ubyte[]) vdata; } } /********************************** * put enables OutBuffer to be used as an OutputRange. */ - alias write put; + alias put = write; /************************************* * Append data to the internal buffer. @@ -118,9 +102,9 @@ class OutBuffer offset += ubyte.sizeof; } - void write(byte b) { write(cast(ubyte)b); } /// ditto - void write(char c) { write(cast(ubyte)c); } /// ditto - void write(dchar c) { write(cast(uint)c); } /// ditto + void write(byte b) { write(cast(ubyte) b); } /// ditto + void write(char c) { write(cast(ubyte) c); } /// ditto + void write(dchar c) { write(cast(uint) c); } /// ditto void write(ushort w) @trusted /// ditto { @@ -129,7 +113,7 @@ class OutBuffer offset += ushort.sizeof; } - void write(short s) { write(cast(ushort)s); } /// ditto + void write(short s) { write(cast(ushort) s); } /// ditto void write(wchar c) @trusted /// ditto { @@ -145,7 +129,7 @@ class OutBuffer offset += uint.sizeof; } - void write(int i) { write(cast(uint)i); } /// ditto + void write(int i) { write(cast(uint) i); } /// ditto void write(ulong l) @trusted /// ditto { @@ -154,7 +138,7 @@ class OutBuffer offset += ulong.sizeof; } - void write(long l) { write(cast(ulong)l); } /// ditto + void write(long l) { write(cast(ulong) l); } /// ditto void write(float f) @trusted /// ditto { @@ -179,7 +163,7 @@ class OutBuffer void write(in char[] s) @trusted /// ditto { - write(cast(ubyte[])s); + write(cast(ubyte[]) s); } void write(OutBuffer buf) /// ditto @@ -225,7 +209,7 @@ class OutBuffer void align2() { if (offset & 1) - write(cast(byte)0); + write(cast(byte) 0); } /**************************************** @@ -257,7 +241,14 @@ class OutBuffer void vprintf(string format, va_list args) @trusted nothrow { - char[128] buffer; + import std.string : toStringz; + import core.stdc.stdio : vsnprintf; + import core.stdc.stdlib : alloca; + + version (unittest) + char[3] buffer = void; // trigger reallocation + else + char[128] buffer = void; int count; // Can't use `tempCString()` here as it will result in compilation error: @@ -267,43 +258,26 @@ class OutBuffer auto psize = buffer.length; for (;;) { - version(Windows) + va_list args2; + va_copy(args2, args); + count = vsnprintf(p, psize, f, args2); + va_end(args2); + if (count == -1) { - count = _vsnprintf(p,psize,f,args); - if (count != -1) - break; + if (psize > psize.max / 2) assert(0); // overflow check psize *= 2; - p = cast(char *) alloca(psize); // buffer too small, try again with larger size } - else version(Posix) + else if (count >= psize) { - count = vsnprintf(p,psize,f,args); - if (count == -1) - psize *= 2; - else if (count >= psize) - psize = count + 1; - else - break; - /+ - if (p != buffer) - c.stdlib.free(p); - p = (char *) c.stdlib.malloc(psize); // buffer too small, try again with larger size - +/ - p = cast(char *) alloca(psize); // buffer too small, try again with larger size + if (count == count.max) assert(0); // overflow check + psize = count + 1; } else - { - static assert(0); - } + break; + + p = cast(char *) alloca(psize); // buffer too small, try again with larger size } write(cast(ubyte[]) p[0 .. count]); - /+ - version (Posix) - { - if (p != buffer) - c.stdlib.free(p); - } - +/ } /***************************************** @@ -322,12 +296,12 @@ class OutBuffer * Formats and writes its arguments in text format to the OutBuffer. * * Params: - * fmt = format string as described in $(XREF format, formattedWrite) + * fmt = format string as described in $(REF formattedWrite, std,format) * args = arguments to be formatted * * See_Also: - * $(XREF stdio, writef); - * $(XREF format, formattedWrite); + * $(REF _writef, std,stdio); + * $(REF formattedWrite, std,format); */ void writef(Char, A...)(in Char[] fmt, A args) { @@ -336,7 +310,7 @@ class OutBuffer } /// - unittest + @safe unittest { OutBuffer b = new OutBuffer(); b.writef("a%sb", 16); @@ -348,12 +322,12 @@ class OutBuffer * followed by a newline. * * Params: - * fmt = format string as described in $(XREF format, formattedWrite) + * fmt = format string as described in $(REF formattedWrite, std,format) * args = arguments to be formatted * * See_Also: - * $(XREF stdio, writefln); - * $(XREF format, formattedWrite); + * $(REF _writefln, std,stdio); + * $(REF formattedWrite, std,format); */ void writefln(Char, A...)(in Char[] fmt, A args) { @@ -363,7 +337,7 @@ class OutBuffer } /// - unittest + @safe unittest { OutBuffer b = new OutBuffer(); b.writefln("a%sb", 16); @@ -394,25 +368,21 @@ class OutBuffer } } -unittest +@safe unittest { - //printf("Starting OutBuffer test\n"); + import std.string : cmp; OutBuffer buf = new OutBuffer(); - //printf("buf = %p\n", buf); - //printf("buf.offset = %x\n", buf.offset); assert(buf.offset == 0); buf.write("hello"[]); - buf.write(cast(byte)0x20); + buf.write(cast(byte) 0x20); buf.write("world"[]); - buf.printf(" %d", 6); - //auto s = buf.toString(); - //printf("buf = '%.*s'\n", s.length, s.ptr); - assert(cmp(buf.toString(), "hello world 6") == 0); + buf.printf(" %d", 62665); + assert(cmp(buf.toString(), "hello world 62665") == 0); } -unittest +@safe unittest { import std.range; static assert(isOutputRange!(OutBuffer, char)); diff --git a/std/parallelism.d b/std/parallelism.d index 267ea77ebcc..7a5bebaeae0 100644 --- a/std/parallelism.d +++ b/std/parallelism.d @@ -33,63 +33,58 @@ Warning: Unless marked as $(D @trusted) or $(D @safe), artifacts in this module allow implicit data sharing between threads and cannot guarantee that client code is free from low level data races. -Synopsis: +Source: $(PHOBOSSRC std/_parallelism.d) +Author: David Simcha +Copyright: Copyright (c) 2009-2011, David Simcha. +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) +*/ +module std.parallelism; ---- -import std.algorithm, std.parallelism, std.range; +/// +@system unittest +{ + import std.algorithm.iteration : map; + import std.range : iota; + import std.math : approxEqual; + import std.parallelism : taskPool; -void main() { // Parallel reduce can be combined with - // std.algorithm.map to interesting effect. + // std.algorithm.iteration.map to interesting effect. // The following example (thanks to Russel Winder) // calculates pi by quadrature using // std.algorithm.map and TaskPool.reduce. // getTerm is evaluated in parallel as needed by // TaskPool.reduce. // - // Timings on an Athlon 64 X2 dual core machine: + // Timings on an Intel i5-3450 quad core machine + // for n = 1_000_000_000: // - // TaskPool.reduce: 12.170 s - // std.algorithm.reduce: 24.065 s + // TaskPool.reduce: 1.067 s + // std.algorithm.reduce: 4.011 s - immutable n = 1_000_000_000; - immutable delta = 1.0 / n; + enum n = 1_000_000; + enum delta = 1.0 / n; - real getTerm(int i) + alias getTerm = (int i) { immutable x = ( i - 0.5 ) * delta; return delta / ( 1.0 + x * x ) ; - } + }; - immutable pi = 4.0 * taskPool.reduce!"a + b"( - std.algorithm.map!getTerm(iota(n)) - ); -} ---- + immutable pi = 4.0 * taskPool.reduce!"a + b"(n.iota.map!getTerm); -Source: $(PHOBOSSRC std/_parallelism.d) -Author: David Simcha -Copyright: Copyright (c) 2009-2011, David Simcha. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) -*/ -module std.parallelism; + assert(pi.approxEqual(3.1415926)); +} import core.atomic; -import core.cpuid; -import core.exception; import core.memory; import core.sync.condition; import core.thread; -import std.algorithm; -import std.conv; -import std.exception; import std.functional; -import std.math; -import std.range; +import std.meta; +import std.range.primitives; import std.traits; -import std.typecons; -import std.typetuple; version(OSX) { @@ -99,39 +94,20 @@ else version(FreeBSD) { version = useSysctlbyname; } +else version(NetBSD) +{ + version = useSysctlbyname; +} + version(Windows) { // BUGS: Only works on Windows 2000 and above. - - import core.sys.windows.windows; - - struct SYSTEM_INFO - { - union - { - DWORD dwOemId; - struct - { - WORD wProcessorArchitecture; - WORD wReserved; - } - } - DWORD dwPageSize; - LPVOID lpMinimumApplicationAddress; - LPVOID lpMaximumApplicationAddress; - LPVOID dwActiveProcessorMask; - DWORD dwNumberOfProcessors; - DWORD dwProcessorType; - DWORD dwAllocationGranularity; - WORD wProcessorLevel; - WORD wProcessorRevision; - } - - private extern(Windows) void GetSystemInfo(void*); - shared static this() { + import core.sys.windows.windows : SYSTEM_INFO, GetSystemInfo; + import std.algorithm.comparison : max; + SYSTEM_INFO si; GetSystemInfo(&si); totalCPUs = max(1, cast(uint) si.dwNumberOfProcessors); @@ -140,28 +116,17 @@ version(Windows) } else version(linux) { - import core.sys.posix.unistd; - shared static this() { + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } } else version(Solaris) { - import core.sys.posix.unistd; - - shared static this() - { - totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); - } -} -else version(Android) -{ - import core.sys.posix.unistd; - shared static this() { + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } } @@ -181,6 +146,10 @@ else version(useSysctlbyname) { auto nameStr = "hw.ncpu\0".ptr; } + else version(NetBSD) + { + auto nameStr = "hw.ncpu\0".ptr; + } uint ans; size_t len = uint.sizeof; @@ -194,6 +163,23 @@ else static assert(0, "Don't know how to get N CPUs on this OS."); } +immutable size_t cacheLineSize; +shared static this() +{ + import core.cpuid : datacache; + size_t lineSize = 0; + foreach (cachelevel; datacache) + { + if (cachelevel.lineSize > lineSize && cachelevel.lineSize < uint.max) + { + lineSize = cachelevel.lineSize; + } + } + + cacheLineSize = lineSize; +} + + /* Atomics code. These forward to core.atomic, but are written like this for two reasons: @@ -254,10 +240,10 @@ private template isSafeTask(F) (functionAttributes!F & (FunctionAttribute.safe | FunctionAttribute.trusted)) != 0 && (functionAttributes!F & FunctionAttribute.ref_) == 0 && (isFunctionPointer!F || !hasUnsharedAliasing!F) && - allSatisfy!(noUnsharedAliasing, ParameterTypeTuple!F); + allSatisfy!(noUnsharedAliasing, Parameters!F); } -unittest +@safe unittest { alias F1 = void function() @safe; alias F2 = void function(); @@ -283,11 +269,11 @@ unittest // since they can't read global state. private template isSafeReturn(T) { - static if(!hasUnsharedAliasing!(T.ReturnType)) + static if (!hasUnsharedAliasing!(T.ReturnType)) { enum isSafeReturn = true; } - else static if(T.isPure) + else static if (T.isPure) { enum isSafeReturn = true; } @@ -302,12 +288,6 @@ private template randAssignable(R) enum randAssignable = isRandomAccessRange!R && hasAssignableElements!R; } -// Work around syntactic ambiguity w.r.t. address of function return vals. -private T* addressOf(T)(ref T val) pure nothrow -{ - return &val; -} - private enum TaskStatus : ubyte { notStarted, @@ -324,7 +304,7 @@ private template AliasReturn(alias fun, T...) // and won't work w/ private. template reduceAdjoin(functions...) { - static if(functions.length == 1) + static if (functions.length == 1) { alias reduceAdjoin = binaryFun!(functions[0]); } @@ -334,7 +314,7 @@ template reduceAdjoin(functions...) { alias funs = staticMap!(binaryFun, functions); - foreach(i, Unused; typeof(lhs.expand)) + foreach (i, Unused; typeof(lhs.expand)) { lhs.expand[i] = funs[i](lhs.expand[i], rhs); } @@ -346,7 +326,7 @@ template reduceAdjoin(functions...) private template reduceFinish(functions...) { - static if(functions.length == 1) + static if (functions.length == 1) { alias reduceFinish = binaryFun!(functions[0]); } @@ -356,7 +336,7 @@ private template reduceFinish(functions...) { alias funs = staticMap!(binaryFun, functions); - foreach(i, Unused; typeof(lhs.expand)) + foreach (i, Unused; typeof(lhs.expand)) { lhs.expand[i] = funs[i](lhs.expand[i], rhs.expand[i]); } @@ -376,7 +356,7 @@ private template isRoundRobin(T) enum isRoundRobin = false; } -unittest +@safe unittest { static assert( isRoundRobin!(RoundRobinBuffer!(void delegate(char[]), bool delegate()))); static assert(!isRoundRobin!(uint)); @@ -397,9 +377,9 @@ private struct AbstractTask bool done() @property { - if(atomicReadUbyte(taskStatus) == TaskStatus.done) + if (atomicReadUbyte(taskStatus) == TaskStatus.done) { - if(exception) + if (exception) { throw exception; } @@ -429,7 +409,7 @@ meaning that any memory writes made in the thread that executed the $(D Task) are guaranteed to be visible in the calling thread after one of these functions returns. -The $(XREF parallelism, task) and $(XREF parallelism, scopedTask) functions can +The $(REF task, std,parallelism) and $(REF scopedTask, std,parallelism) functions can be used to create an instance of this struct. See $(D task) for usage examples. Function results are returned from $(D yieldForce), $(D spinForce) and @@ -456,12 +436,14 @@ struct Task(alias fun, Args...) private static void impl(void* myTask) { + import std.algorithm.internal : addressOf; + Task* myCastedTask = cast(typeof(this)*) myTask; - static if(is(ReturnType == void)) + static if (is(ReturnType == void)) { fun(myCastedTask._args); } - else static if(is(typeof(addressOf(fun(myCastedTask._args))))) + else static if (is(typeof(addressOf(fun(myCastedTask._args))))) { myCastedTask.returnVal = addressOf(fun(myCastedTask._args)); } @@ -480,7 +462,7 @@ struct Task(alias fun, Args...) The arguments the function was called with. Changes to $(D out) and $(D ref) arguments will be visible here. */ - static if(__traits(isSame, fun, run)) + static if (__traits(isSame, fun, run)) { alias args = _args[1..$]; } @@ -493,9 +475,9 @@ struct Task(alias fun, Args...) // The purpose of this code is to decide whether functions whose // return values have unshared aliasing can be executed via // TaskPool from @safe code. See isSafeReturn. - static if(__traits(isSame, fun, run)) + static if (__traits(isSame, fun, run)) { - static if(isFunctionPointer!(_args[0])) + static if (isFunctionPointer!(_args[0])) { private enum bool isPure = functionAttributes!(Args[0]) & FunctionAttribute.pure_; @@ -524,9 +506,9 @@ struct Task(alias fun, Args...) */ alias ReturnType = typeof(fun(_args)); - static if(!is(ReturnType == void)) + static if (!is(ReturnType == void)) { - static if(is(typeof(&fun(_args)))) + static if (is(typeof(&fun(_args)))) { // Ref return. ReturnType* returnVal; @@ -550,10 +532,11 @@ struct Task(alias fun, Args...) private void enforcePool() { + import std.exception : enforce; enforce(this.pool !is null, "Job not submitted yet."); } - static if(Args.length > 0) + static if (Args.length > 0) { private this(Args args) { @@ -562,11 +545,11 @@ struct Task(alias fun, Args...) } // Work around DMD bug 6588, allow immutable elements. - static if(allSatisfy!(isAssignable, Args)) + static if (allSatisfy!(isAssignable, Args)) { typeof(this) opAssign(typeof(this) rhs) { - foreach(i, Type; typeof(this.tupleof)) + foreach (i, Type; typeof(this.tupleof)) { this.tupleof[i] = rhs.tupleof[i]; } @@ -597,14 +580,14 @@ struct Task(alias fun, Args...) this.pool.tryDeleteExecute(basePtr); - while(atomicReadUbyte(this.taskStatus) != TaskStatus.done) {} + while (atomicReadUbyte(this.taskStatus) != TaskStatus.done) {} - if(exception) + if (exception) { throw exception; } - static if(!is(ReturnType == void)) + static if (!is(ReturnType == void)) { return fixRef(this.returnVal); } @@ -624,9 +607,9 @@ struct Task(alias fun, Args...) enforcePool(); this.pool.tryDeleteExecute(basePtr); - if(done) + if (done) { - static if(is(ReturnType == void)) + static if (is(ReturnType == void)) { return; } @@ -639,17 +622,17 @@ struct Task(alias fun, Args...) pool.waiterLock(); scope(exit) pool.waiterUnlock(); - while(atomicReadUbyte(this.taskStatus) != TaskStatus.done) + while (atomicReadUbyte(this.taskStatus) != TaskStatus.done) { pool.waitUntilCompletion(); } - if(exception) + if (exception) { throw exception; } - static if(!is(ReturnType == void)) + static if (!is(ReturnType == void)) { return fixRef(this.returnVal); } @@ -669,11 +652,11 @@ struct Task(alias fun, Args...) enforcePool(); this.pool.tryDeleteExecute(basePtr); - while(true) + while (true) { - if(done) // done() implicitly checks for exceptions. + if (done) // done() implicitly checks for exceptions. { - static if(is(ReturnType == void)) + static if (is(ReturnType == void)) { return; } @@ -695,7 +678,7 @@ struct Task(alias fun, Args...) } - if(job !is null) + if (job !is null) { version(verboseUnittest) @@ -705,9 +688,9 @@ struct Task(alias fun, Args...) pool.doJob(job); - if(done) + if (done) { - static if(is(ReturnType == void)) + static if (is(ReturnType == void)) { return; } @@ -746,7 +729,7 @@ struct Task(alias fun, Args...) newly created thread, then terminate the thread. This can be used for future/promise parallelism. An explicit priority may be given to the $(D Task). If one is provided, its value is forwarded to - $(D core.thread.Thread.priority). See $(XREF parallelism, task) for + $(D core.thread.Thread.priority). See $(REF task, std,parallelism) for usage example. */ void executeInNewThread() @trusted @@ -762,7 +745,7 @@ struct Task(alias fun, Args...) @safe ~this() { - if(isScoped && pool !is null && taskStatus != TaskStatus.done) + if (isScoped && pool !is null && taskStatus != TaskStatus.done) { yieldForce; } @@ -784,12 +767,12 @@ ReturnType!F run(F, Args...)(F fpOrDelegate, ref Args args) /** Creates a $(D Task) on the GC heap that calls an alias. This may be executed via $(D Task.executeInNewThread) or by submitting to a -$(XREF parallelism, TaskPool). A globally accessible instance of -$(D TaskPool) is provided by $(XREF parallelism, taskPool). +$(REF TaskPool, std,parallelism). A globally accessible instance of +$(D TaskPool) is provided by $(REF taskPool, std,parallelism). Returns: A pointer to the $(D Task). -Examples: +Example: --- // Read two files into memory at the same time. import std.file; @@ -822,7 +805,7 @@ void main() void parallelSort(T)(T[] data) { // Sort small subarrays serially. - if(data.length < 100) + if (data.length < 100) { std.algorithm.sort(data); return; @@ -856,7 +839,7 @@ auto task(alias fun, Args...)(Args args) Creates a $(D Task) on the GC heap that calls a function pointer, delegate, or class/struct with overloaded opCall. -Examples: +Example: --- // Read two files in at the same time again, // but this time use a function pointer instead @@ -884,7 +867,7 @@ Notes: This function takes a non-scope delegate, meaning it can be takes a scope delegate. */ auto task(F, Args...)(F delegateOrFp, Args args) -if(is(typeof(delegateOrFp(args))) && !isSafeTask!F) +if (is(typeof(delegateOrFp(args))) && !isSafeTask!F) { return new Task!(run, F, Args)(delegateOrFp, args); } @@ -896,7 +879,7 @@ identical to the non-@safe case, but safety introduces some restrictions: 1. $(D fun) must be @safe or @trusted. 2. $(D F) must not have any unshared aliasing as defined by - $(XREF traits, hasUnsharedAliasing). This means it + $(REF hasUnsharedAliasing, std,traits). This means it may not be an unshared delegate or a non-shared class or struct with overloaded $(D opCall). This also precludes accepting template alias parameters. @@ -911,7 +894,7 @@ identical to the non-@safe case, but safety introduces some restrictions: */ @trusted auto task(F, Args...)(F fun, Args args) -if(is(typeof(fun(args))) && isSafeTask!F) +if (is(typeof(fun(args))) && isSafeTask!F) { return new Task!(run, F, Args)(fun, args); } @@ -946,7 +929,7 @@ auto scopedTask(alias fun, Args...)(Args args) /// Ditto auto scopedTask(F, Args...)(scope F delegateOrFp, Args args) -if(is(typeof(delegateOrFp(args))) && !isSafeTask!F) +if (is(typeof(delegateOrFp(args))) && !isSafeTask!F) { auto ret = Task!(run, F, Args)(delegateOrFp, args); ret.isScoped = true; @@ -955,7 +938,7 @@ if(is(typeof(delegateOrFp(args))) && !isSafeTask!F) /// Ditto @trusted auto scopedTask(F, Args...)(F fun, Args args) -if(is(typeof(fun(args))) && isSafeTask!F) +if (is(typeof(fun(args))) && isSafeTask!F) { auto ret = Task!(run, F, Args)(fun, args); ret.isScoped = true; @@ -991,14 +974,12 @@ private final class ParallelismThread : Thread // Kill daemon threads. shared static ~this() { - auto allThreads = Thread.getAll(); - - foreach(thread; allThreads) + foreach (ref thread; Thread) { auto pthread = cast(ParallelismThread) thread; - if(pthread is null) continue; + if (pthread is null) continue; auto pool = pthread.pool; - if(!pool.isDaemon) continue; + if (!pool.isDaemon) continue; pool.stop(); pthread.join(); } @@ -1013,7 +994,7 @@ thread that executes the $(D Task) at the front of the queue when one is available and sleeps when the queue is empty. This class should usually be used via the global instantiation -available via the $(XREF parallelism, taskPool) property. +available via the $(REF taskPool, std,parallelism) property. Occasionally it is useful to explicitly instantiate a $(D TaskPool): 1. When you want $(D TaskPool) instances with multiple priorities, for example @@ -1070,7 +1051,7 @@ private: scope(exit) { - if(!isSingleTask) + if (!isSingleTask) { waiterLock(); scope(exit) waiterUnlock(); @@ -1082,7 +1063,7 @@ private: { job.job(); } - catch(Throwable e) + catch (Throwable e) { job.exception = e; } @@ -1122,12 +1103,12 @@ private: // finish() is called with the blocking variable set to true. void executeWorkLoop() { - while(atomicReadUbyte(status) != PoolState.stopNow) + while (atomicReadUbyte(status) != PoolState.stopNow) { AbstractTask* task = pop(); if (task is null) { - if(atomicReadUbyte(status) == PoolState.finishing) + if (atomicReadUbyte(status) == PoolState.finishing) { atomicSetUbyte(status, PoolState.stopNow); return; @@ -1146,7 +1127,7 @@ private: queueLock(); scope(exit) queueUnlock(); auto ret = popNoSync(); - while(ret is null && status == PoolState.running) + while (ret is null && status == PoolState.running) { wait(); ret = popNoSync(); @@ -1161,7 +1142,7 @@ private: * can try to delete this task from the pool after it's * alreadly been deleted/popped. */ - if(returned !is null) + if (returned !is null) { assert(returned.next is null); assert(returned.prev is null); @@ -1169,7 +1150,7 @@ private: } body { - if(isSingleTask) return null; + if (isSingleTask) return null; AbstractTask* returned = head; if (head !is null) @@ -1179,7 +1160,7 @@ private: returned.next = null; returned.taskStatus = TaskStatus.inProgress; } - if(head !is null) + if (head !is null) { head.prev = null; } @@ -1202,9 +1183,11 @@ private: } out { + import std.conv : text; + assert(tail.prev !is tail); assert(tail.next is null, text(tail.prev, '\t', tail.next)); - if(tail.prev !is null) + if (tail.prev !is null) { assert(tail.prev.next is tail, text(tail.prev, '\t', tail.next)); } @@ -1213,7 +1196,7 @@ private: { // Not using enforce() to save on function call overhead since this // is a performance critical function. - if(status != PoolState.running) + if (status != PoolState.running) { throw new Error( "Cannot submit a new task to a pool after calling " ~ @@ -1228,7 +1211,8 @@ private: tail = task; tail.prev = null; } - else { + else + { assert(tail); task.prev = tail; tail.next = task; @@ -1239,7 +1223,7 @@ private: void abstractPutGroupNoSync(AbstractTask* h, AbstractTask* t) { - if(status != PoolState.running) + if (status != PoolState.running) { throw new Error( "Cannot submit a new task to a pool after calling " ~ @@ -1247,7 +1231,7 @@ private: ); } - if(head is null) + if (head is null) { head = h; tail = t; @@ -1264,9 +1248,9 @@ private: void tryDeleteExecute(AbstractTask* toExecute) { - if(isSingleTask) return; + if (isSingleTask) return; - if( !deleteItem(toExecute) ) + if ( !deleteItem(toExecute) ) { return; } @@ -1275,7 +1259,7 @@ private: { toExecute.job(); } - catch(Exception e) + catch (Exception e) { toExecute.exception = e; } @@ -1292,22 +1276,22 @@ private: bool deleteItemNoSync(AbstractTask* item) { - if(item.taskStatus != TaskStatus.notStarted) + if (item.taskStatus != TaskStatus.notStarted) { return false; } item.taskStatus = TaskStatus.inProgress; - if(item is head) + if (item is head) { // Make sure head gets set properly. popNoSync(); return true; } - if(item is tail) + if (item is tail) { tail = tail.prev; - if(tail !is null) + if (tail !is null) { tail.next = null; } @@ -1315,12 +1299,12 @@ private: item.prev = null; return true; } - if(item.next !is null) + if (item.next !is null) { assert(item.next.prev is item); // Check queue consistency. item.next.prev = item.prev; } - if(item.prev !is null) + if (item.prev !is null) { assert(item.prev.next is item); // Check queue consistency. item.prev.next = item.next; @@ -1333,43 +1317,43 @@ private: void queueLock() { assert(queueMutex); - if(!isSingleTask) queueMutex.lock(); + if (!isSingleTask) queueMutex.lock(); } void queueUnlock() { assert(queueMutex); - if(!isSingleTask) queueMutex.unlock(); + if (!isSingleTask) queueMutex.unlock(); } void waiterLock() { - if(!isSingleTask) waiterMutex.lock(); + if (!isSingleTask) waiterMutex.lock(); } void waiterUnlock() { - if(!isSingleTask) waiterMutex.unlock(); + if (!isSingleTask) waiterMutex.unlock(); } void wait() { - if(!isSingleTask) workerCondition.wait(); + if (!isSingleTask) workerCondition.wait(); } void notify() { - if(!isSingleTask) workerCondition.notify(); + if (!isSingleTask) workerCondition.notify(); } void notifyAll() { - if(!isSingleTask) workerCondition.notifyAll(); + if (!isSingleTask) workerCondition.notifyAll(); } void waitUntilCompletion() { - if(isSingleTask) + if (isSingleTask) { singleTaskThread.join(); } @@ -1381,7 +1365,7 @@ private: void notifyWaiters() { - if(!isSingleTask) waiterCondition.notifyAll(); + if (!isSingleTask) waiterCondition.notifyAll(); } // Private constructor for creating dummy pools that only have one thread, @@ -1404,7 +1388,7 @@ private: // running thread with specified priority // See https://d.puremagic.com/issues/show_bug.cgi?id=8960 - /*if(priority != int.max) + /*if (priority != int.max) { singleTaskThread.priority = priority; }*/ @@ -1415,7 +1399,9 @@ public: // as public API. size_t defaultWorkUnitSize(size_t rangeLen) const @safe pure nothrow { - if(this.size == 0) + import std.algorithm.comparison : max; + + if (this.size == 0) { return rangeLen; } @@ -1460,7 +1446,7 @@ public: waiterCondition = new Condition(waiterMutex); pool = new ParallelismThread[nWorkers]; - foreach(ref poolThread; pool) + foreach (ref poolThread; pool) { poolThread = new ParallelismThread(&startWorkLoop); poolThread.pool = this; @@ -1483,7 +1469,7 @@ public: $(D workUnitSize) should be 1. An overload that chooses a default work unit size is also available. - Examples: + Example: --- // Find the logarithm of every number from 1 to // 10_000_000 in parallel. @@ -1494,7 +1480,7 @@ public: // returns by ref. // Iterate over logs using work units of size 100. - foreach(i, ref elem; taskPool.parallel(logs, 100)) + foreach (i, ref elem; taskPool.parallel(logs, 100)) { elem = log(i + 1.0); } @@ -1505,7 +1491,7 @@ public: // // Parallel foreach: 388 milliseconds // Regular foreach: 619 milliseconds - foreach(i, ref elem; taskPool.parallel(logs)) + foreach (i, ref elem; taskPool.parallel(logs)) { elem = log(i + 1.0); } @@ -1541,6 +1527,7 @@ public: */ ParallelForeach!R parallel(R)(R range, size_t workUnitSize) { + import std.exception : enforce; enforce(workUnitSize > 0, "workUnitSize must be > 0."); alias RetType = ParallelForeach!R; return RetType(this, range, workUnitSize); @@ -1550,7 +1537,7 @@ public: /// Ditto ParallelForeach!R parallel(R)(R range) { - static if(hasLength!R) + static if (hasLength!R) { // Default work unit size is such that we would use 4x as many // slots as are in this thread pool. @@ -1572,12 +1559,14 @@ public: Eager parallel map. The eagerness of this function means it has less overhead than the lazily evaluated $(D TaskPool.map) and should be preferred where the memory requirements of eagerness are acceptable. - $(D functions) are the functions to be evaluated, passed as template alias - parameters in a style similar to $(XREF algorithm, map). The first - argument must be a random access range. For performance reasons, amap - will assume the range elements have not yet been initialized. Elements will - be overwritten without calling a destructor nor doing an assignment. As such, - the range must not contain meaningful data: either un-initialized objects, or + $(D functions) are the functions to be evaluated, passed as template + alias parameters in a style similar to + $(REF map, std,algorithm,iteration). + The first argument must be a random access range. For performance + reasons, amap will assume the range elements have not yet been + initialized. Elements will be overwritten without calling a destructor + nor doing an assignment. As such, the range must not contain meaningful + data$(DDOC_COMMENT not a section): either un-initialized objects, or objects in their $(D .init) state. --- @@ -1650,19 +1639,24 @@ public: rethrown. The order of the exception chaining is non-deterministic. */ auto amap(Args...)(Args args) - if(isRandomAccessRange!(Args[0])) + if (isRandomAccessRange!(Args[0])) { + import std.conv : emplaceRef; + alias fun = adjoin!(staticMap!(unaryFun, functions)); alias range = args[0]; immutable len = range.length; - static if( + static if ( Args.length > 1 && randAssignable!(Args[$ - 1]) && is(MapType!(Args[0], functions) : ElementType!(Args[$ - 1])) ) { + import std.conv : text; + import std.exception : enforce; + alias buf = args[$ - 1]; alias args2 = args[0..$ - 1]; alias Args2 = Args[0..$ - 1]; @@ -1670,20 +1664,22 @@ public: text("Can't use a user supplied buffer that's the wrong ", "size. (Expected :", len, " Got: ", buf.length)); } - else static if(randAssignable!(Args[$ - 1]) && Args.length > 1) + else static if (randAssignable!(Args[$ - 1]) && Args.length > 1) { static assert(0, "Wrong buffer type."); } else { + import std.array : uninitializedArray; + auto buf = uninitializedArray!(MapType!(Args[0], functions)[])(len); alias args2 = args; alias Args2 = Args; } - if(!len) return buf; + if (!len) return buf; - static if(isIntegral!(Args2[$ - 1])) + static if (isIntegral!(Args2[$ - 1])) { static assert(args2.length == 2); auto workUnitSize = cast(size_t) args2[1]; @@ -1696,16 +1692,16 @@ public: alias R = typeof(range); - if(workUnitSize > len) + if (workUnitSize > len) { workUnitSize = len; } // Handle as a special case: - if(size == 0) + if (size == 0) { size_t index = 0; - foreach(elem; range) + foreach (elem; range) { emplaceRef(buf[index++], fun(elem)); } @@ -1718,17 +1714,19 @@ public: void doIt() { + import std.algorithm.comparison : min; + scope(failure) { // If an exception is thrown, all threads should bail. atomicStore(shouldContinue, false); } - while(atomicLoad(shouldContinue)) + while (atomicLoad(shouldContinue)) { immutable myUnitIndex = atomicOp!"+="(workUnitIndex, 1); immutable start = workUnitSize * myUnitIndex; - if(start >= len) + if (start >= len) { atomicStore(shouldContinue, false); break; @@ -1738,8 +1736,8 @@ public: static if (hasSlicing!R) { - auto subrange = range[start..end]; - foreach(i; start..end) + auto subrange = range[start .. end]; + foreach (i; start .. end) { emplaceRef(buf[i], fun(subrange.front)); subrange.popFront(); @@ -1747,7 +1745,7 @@ public: } else { - foreach(i; start..end) + foreach (i; start .. end) { emplaceRef(buf[i], fun(range[i])); } @@ -1805,7 +1803,7 @@ public: current call to $(D map) will be ignored and the size of the buffer will be the buffer size of $(D source). - Examples: + Example: --- // Pipeline reading a file, converting each line // to a number, taking the logarithms of the numbers, @@ -1818,7 +1816,7 @@ public: auto logs = taskPool.map!log10(nums); double sum = 0; - foreach(elem; logs) + foreach (elem; logs) { sum += elem; } @@ -1834,8 +1832,10 @@ public: */ auto map(S)(S source, size_t bufSize = 100, size_t workUnitSize = size_t.max) - if(isInputRange!S) + if (isInputRange!S) { + import std.exception : enforce; + enforce(workUnitSize == size_t.max || workUnitSize <= bufSize, "Work unit size must be smaller than buffer size."); alias fun = adjoin!(staticMap!(unaryFun, functions)); @@ -1860,17 +1860,19 @@ public: size_t bufPos; bool lastTaskWaited; - static if(isRandomAccessRange!S) + static if (isRandomAccessRange!S) { alias FromType = S; void popSource() { - static if(__traits(compiles, source[0..source.length])) + import std.algorithm.comparison : min; + + static if (__traits(compiles, source[0 .. source.length])) { source = source[min(buf1.length, source.length)..source.length]; } - else static if(__traits(compiles, source[0..$])) + else static if (__traits(compiles, source[0..$])) { source = source[min(buf1.length, source.length)..$]; } @@ -1881,11 +1883,11 @@ public: } } } - else static if(bufferTrick) + else static if (bufferTrick) { // Make sure we don't have the buffer recycling overload of // asyncBuf. - static if( + static if ( is(typeof(source.source)) && isRoundRobin!(typeof(source.source)) ) @@ -1902,6 +1904,8 @@ public: // No need to copy element by element. FromType dumpToFrom() { + import std.algorithm.mutation : swap; + assert(source.buf1.length <= from.length); from.length = source.buf1.length; swap(source.buf1, from); @@ -1910,7 +1914,7 @@ public: // being sent to map: from = from[source.bufPos..$]; - static if(is(typeof(source._length))) + static if (is(typeof(source._length))) { source._length -= (from.length - source.bufPos); } @@ -1933,17 +1937,17 @@ public: assert(from !is null); size_t i; - for(; !source.empty && i < from.length; source.popFront()) + for (; !source.empty && i < from.length; source.popFront()) { from[i++] = source.front; } - from = from[0..i]; + from = from[0 .. i]; return from; } } - static if(hasLength!S) + static if (hasLength!S) { size_t _length; @@ -1955,7 +1959,7 @@ public: this(S source, size_t bufSize, size_t workUnitSize, TaskPool pool) { - static if(bufferTrick) + static if (bufferTrick) { bufSize = source.buf1.length; } @@ -1963,7 +1967,7 @@ public: buf1.length = bufSize; buf2.length = bufSize; - static if(!isRandomAccessRange!S) + static if (!isRandomAccessRange!S) { from.length = bufSize; } @@ -1973,7 +1977,7 @@ public: this.source = source; this.pool = pool; - static if(hasLength!S) + static if (hasLength!S) { _length = source.length; } @@ -1986,8 +1990,11 @@ public: // case. E[] fillBuf(E[] buf) { - static if(isRandomAccessRange!S) + import std.algorithm.comparison : min; + + static if (isRandomAccessRange!S) { + import std.range : take; auto toMap = take(source, buf.length); scope(success) popSource(); } @@ -1996,13 +2003,13 @@ public: auto toMap = dumpToFrom(); } - buf = buf[0..min(buf.length, toMap.length)]; + buf = buf[0 .. min(buf.length, toMap.length)]; // Handle as a special case: - if(pool.size == 0) + if (pool.size == 0) { size_t index = 0; - foreach(elem; toMap) + foreach (elem; toMap) { buf[index++] = fun(elem); } @@ -2031,13 +2038,13 @@ public: void doBufSwap() { - if(lastTaskWaited) + if (lastTaskWaited) { // Then the source is empty. Signal it here. buf1 = null; buf2 = null; - static if(!isRandomAccessRange!S) + static if (!isRandomAccessRange!S) { from = null; } @@ -2049,7 +2056,7 @@ public: buf1 = nextBufTask.yieldForce; bufPos = 0; - if(source.empty) + if (source.empty) { lastTaskWaited = true; } @@ -2067,26 +2074,26 @@ public: void popFront() { - static if(hasLength!S) + static if (hasLength!S) { _length--; } bufPos++; - if(bufPos >= buf1.length) + if (bufPos >= buf1.length) { doBufSwap(); } } - static if(std.range.isInfinite!S) + static if (isInfinite!S) { enum bool empty = false; } else { - bool empty() @property + bool empty() const @property { // popFront() sets this when source is empty return buf1.length == 0; @@ -2107,7 +2114,7 @@ public: $(D asyncBuf) is useful, for example, when performing expensive operations on the elements of ranges that represent data on a disk or network. - Examples: + Example: --- import std.conv, std.stdio; @@ -2126,7 +2133,7 @@ public: double[][] matrix; auto asyncReader = taskPool.asyncBuf(duped); - foreach(line; asyncReader) + foreach (line; asyncReader) { auto ls = line.split("\t"); matrix ~= to!(double[])(ls); @@ -2140,7 +2147,7 @@ public: call to $(D popFront) or, if thrown during construction, simply allowed to propagate to the caller. */ - auto asyncBuf(S)(S source, size_t bufSize = 100) if(isInputRange!S) + auto asyncBuf(S)(S source, size_t bufSize = 100) if (isInputRange!S) { static final class AsyncBuf { @@ -2158,7 +2165,7 @@ public: size_t bufPos; bool lastTaskWaited; - static if(hasLength!S) + static if (hasLength!S) { size_t _length; @@ -2177,7 +2184,7 @@ public: this.source = source; this.pool = pool; - static if(hasLength!S) + static if (hasLength!S) { _length = source.length; } @@ -2191,12 +2198,12 @@ public: assert(buf !is null); size_t i; - for(; !source.empty && i < buf.length; source.popFront()) + for (; !source.empty && i < buf.length; source.popFront()) { buf[i++] = source.front; } - buf = buf[0..i]; + buf = buf[0 .. i]; return buf; } @@ -2217,7 +2224,7 @@ public: void doBufSwap() { - if(lastTaskWaited) + if (lastTaskWaited) { // Then source is empty. Signal it here. buf1 = null; @@ -2229,7 +2236,7 @@ public: buf1 = nextBufTask.yieldForce; bufPos = 0; - if(source.empty) + if (source.empty) { lastTaskWaited = true; } @@ -2247,19 +2254,19 @@ public: void popFront() { - static if(hasLength!S) + static if (hasLength!S) { _length--; } bufPos++; - if(bufPos >= buf1.length) + if (bufPos >= buf1.length) { doBufSwap(); } } - static if(std.range.isInfinite!S) + static if (isInfinite!S) { enum bool empty = false; } @@ -2303,7 +2310,7 @@ public: nBuffers = The number of buffers to cycle through when calling $(D next). - Examples: + Example: --- // Fetch lines of a file in a background // thread while processing previously fetched @@ -2321,7 +2328,7 @@ public: double[][] matrix; auto asyncReader = taskPool.asyncBuf(&next, &file.eof); - foreach(line; asyncReader) + foreach (line; asyncReader) { auto ls = line.split("\t"); matrix ~= to!(double[])(ls); @@ -2341,10 +2348,10 @@ public: and will result in a static assertion failure. */ auto asyncBuf(C1, C2)(C1 next, C2 empty, size_t initialBufSize = 0, size_t nBuffers = 100) - if(is(typeof(C2.init()) : bool) && - ParameterTypeTuple!C1.length == 1 && - ParameterTypeTuple!C2.length == 0 && - isArray!(ParameterTypeTuple!C1[0]) + if (is(typeof(C2.init()) : bool) && + Parameters!C1.length == 1 && + Parameters!C2.length == 0 && + isArray!(Parameters!C1[0]) ) { auto roundRobin = RoundRobinBuffer!(C1, C2)(next, empty, initialBufSize, nBuffers); return asyncBuf(roundRobin, nBuffers / 2); @@ -2354,15 +2361,16 @@ public: template reduce(functions...) { /** - Parallel reduce on a random access range. Except as otherwise noted, usage - is similar to $(XREF algorithm, _reduce). This function works by splitting - the range to be reduced into work units, which are slices to be reduced in - parallel. Once the results from all work units are computed, a final serial - reduction is performed on these results to compute the final answer. - Therefore, care must be taken to choose the seed value appropriately. - - Because the reduction is being performed in parallel, - $(D functions) must be associative. For notational simplicity, let # be an + Parallel reduce on a random access range. Except as otherwise noted, + usage is similar to $(REF _reduce, std,algorithm,iteration). This + function works by splitting the range to be reduced into work units, + which are slices to be reduced in parallel. Once the results from all + work units are computed, a final serial reduction is performed on these + results to compute the final answer. Therefore, care must be taken to + choose the seed value appropriately. + + Because the reduction is being performed in parallel, $(D functions) + must be associative. For notational simplicity, let # be an infix operator representing $(D functions). Then, (a # b) # c must equal a # (b # c). Floating point addition is not associative even though addition in exact arithmetic is. Summing floating @@ -2370,20 +2378,21 @@ public: serially. However, for many practical purposes floating point addition can be treated as associative. - Note that, since $(D functions) are assumed to be associative, additional - optimizations are made to the serial portion of the reduction algorithm. - These take advantage of the instruction level parallelism of modern CPUs, - in addition to the thread-level parallelism that the rest of this - module exploits. This can lead to better than linear speedups relative - to $(XREF algorithm, _reduce), especially for fine-grained benchmarks - like dot products. + Note that, since $(D functions) are assumed to be associative, + additional optimizations are made to the serial portion of the reduction + algorithm. These take advantage of the instruction level parallelism of + modern CPUs, in addition to the thread-level parallelism that the rest + of this module exploits. This can lead to better than linear speedups + relative to $(REF _reduce, std,algorithm,iteration), especially for + fine-grained benchmarks like dot products. An explicit seed may be provided as the first argument. If provided, it is used as the seed for all work units and for the final reduction of results from all work units. Therefore, if it is not the - identity value for the operation being performed, results may differ from - those generated by $(XREF algorithm, _reduce) or depending on how many work - units are used. The next argument must be the range to be reduced. + identity value for the operation being performed, results may differ + from those generated by $(REF _reduce, std,algorithm,iteration) or + depending on how many work units are used. The next argument must be + the range to be reduced. --- // Find the sum of squares of a range in parallel, using // an explicit seed. @@ -2437,10 +2446,14 @@ public: */ auto reduce(Args...)(Args args) { + import core.exception : OutOfMemoryError; + import std.conv : emplaceRef; + import std.exception : enforce; + alias fun = reduceAdjoin!functions; alias finishFun = reduceFinish!functions; - static if(isIntegral!(Args[$ - 1])) + static if (isIntegral!(Args[$ - 1])) { size_t workUnitSize = cast(size_t) args[$ - 1]; alias args2 = args[0..$ - 1]; @@ -2454,7 +2467,7 @@ public: auto makeStartValue(Type)(Type e) { - static if(functions.length == 1) + static if (functions.length == 1) { return e; } @@ -2470,14 +2483,14 @@ public: } } - static if(args2.length == 2) + static if (args2.length == 2) { static assert(isInputRange!(Args2[1])); alias range = args2[1]; alias seed = args2[0]; enum explicitSeed = true; - static if(!is(typeof(workUnitSize))) + static if (!is(typeof(workUnitSize))) { size_t workUnitSize = defaultWorkUnitSize(range.length); } @@ -2487,7 +2500,7 @@ public: static assert(args2.length == 1); alias range = args2[0]; - static if(!is(typeof(workUnitSize))) + static if (!is(typeof(workUnitSize))) { size_t workUnitSize = defaultWorkUnitSize(range.length); } @@ -2510,14 +2523,14 @@ public: // since we're assuming functions are associative anyhow. // This is so that loops can be unrolled automatically. - enum ilpTuple = TypeTuple!(0, 1, 2, 3, 4, 5); + enum ilpTuple = AliasSeq!(0, 1, 2, 3, 4, 5); enum nILP = ilpTuple.length; immutable subSize = (upperBound - lowerBound) / nILP; - if(subSize <= 1) + if (subSize <= 1) { // Handle as a special case. - static if(explicitSeed) + static if (explicitSeed) { E result = seed; } @@ -2527,7 +2540,7 @@ public: lowerBound++; } - foreach(i; lowerBound..upperBound) + foreach (i; lowerBound .. upperBound) { result = fun(result, range[i]); } @@ -2539,11 +2552,11 @@ public: E[nILP] results; size_t[nILP] offsets; - foreach(i; ilpTuple) + foreach (i; ilpTuple) { offsets[i] = lowerBound + subSize * i; - static if(explicitSeed) + static if (explicitSeed) { results[i] = seed; } @@ -2555,9 +2568,9 @@ public: } immutable nLoop = subSize - (!explicitSeed); - foreach(i; 0..nLoop) + foreach (i; 0 .. nLoop) { - foreach(j; ilpTuple) + foreach (j; ilpTuple) { results[j] = fun(results[j], range[offsets[j]]); offsets[j]++; @@ -2565,12 +2578,12 @@ public: } // Finish the remainder. - foreach(i; nILP * subSize + lowerBound..upperBound) + foreach (i; nILP * subSize + lowerBound .. upperBound) { results[$ - 1] = fun(results[$ - 1], range[i]); } - foreach(i; ilpTuple[1..$]) + foreach (i; ilpTuple[1..$]) { results[0] = finishFun(results[0], results[i]); } @@ -2579,12 +2592,12 @@ public: } immutable len = range.length; - if(len == 0) + if (len == 0) { return seed; } - if(this.size == 0) + if (this.size == 0) { return finishFun(seed, reduceOnRange(range, 0, len)); } @@ -2595,7 +2608,7 @@ public: // be applied on the results of these to get a final result, but // it can't be evaluated out of order. - if(workUnitSize > len) + if (workUnitSize > len) { workUnitSize = len; } @@ -2612,44 +2625,48 @@ public: byte[maxStack] buf = void; immutable size_t nBytesNeeded = nWorkUnits * RTask.sizeof; - import core.stdc.stdlib; - if(nBytesNeeded < maxStack) + import core.stdc.stdlib : malloc, free; + if (nBytesNeeded < maxStack) { - tasks = (cast(RTask*) buf.ptr)[0..nWorkUnits]; + tasks = (cast(RTask*) buf.ptr)[0 .. nWorkUnits]; } else { auto ptr = cast(RTask*) malloc(nBytesNeeded); - if(!ptr) + if (!ptr) { throw new OutOfMemoryError( "Out of memory in std.parallelism." ); } - tasks = ptr[0..nWorkUnits]; + tasks = ptr[0 .. nWorkUnits]; } scope(exit) { - if(nBytesNeeded > maxStack) + if (nBytesNeeded > maxStack) { free(tasks.ptr); } } - tasks[] = RTask.init; + foreach (ref t; tasks[]) + emplaceRef(t, RTask()); // Hack to take the address of a nested function w/o // making a closure. - static auto scopedAddress(D)(scope D del) + static auto scopedAddress(D)(scope D del) @system { - return del; + auto tmp = del; + return tmp; } size_t curPos = 0; void useTask(ref RTask task) { + import std.algorithm.comparison : min; + task.pool = this; task._args[0] = scopedAddress(&reduceOnRange); task._args[3] = min(len, curPos + workUnitSize); // upper bound. @@ -2659,18 +2676,18 @@ public: curPos += workUnitSize; } - foreach(ref task; tasks) + foreach (ref task; tasks) { useTask(task); } - foreach(i; 1..tasks.length - 1) + foreach (i; 1 .. tasks.length - 1) { tasks[i].next = tasks[i + 1].basePtr; tasks[i + 1].prev = tasks[i].basePtr; } - if(tasks.length > 1) + if (tasks.length > 1) { queueLock(); scope(exit) queueUnlock(); @@ -2681,20 +2698,20 @@ public: ); } - if(tasks.length > 0) + if (tasks.length > 0) { try { tasks[0].job(); } - catch(Throwable e) + catch (Throwable e) { tasks[0].exception = e; } tasks[0].taskStatus = TaskStatus.done; // Try to execute each of these in the current thread - foreach(ref task; tasks[1..$]) + foreach (ref task; tasks[1..$]) { tryDeleteExecute(task.basePtr); } @@ -2706,22 +2723,22 @@ public: Throwable firstException, lastException; - foreach(ref task; tasks) + foreach (ref task; tasks) { try { task.yieldForce; } - catch(Throwable e) + catch (Throwable e) { addToChain(e, firstException, lastException); continue; } - if(!firstException) result = finishFun(result, task.returnVal); + if (!firstException) result = finishFun(result, task.returnVal); } - if(firstException) throw firstException; + if (firstException) throw firstException; return result; } @@ -2734,7 +2751,7 @@ public: This function is useful for maintaining worker-local resources. - Examples: + Example: --- // Execute a loop that computes the greatest common // divisor of every number from 0 through 999 with @@ -2748,17 +2765,18 @@ public: { auto filesHandles = new File[taskPool.size + 1]; scope(exit) { - foreach(ref handle; fileHandles) { + foreach (ref handle; fileHandles) + { handle.close(); } } - foreach(i, ref handle; fileHandles) + foreach (i, ref handle; fileHandles) { handle = File("workerResults" ~ to!string(i) ~ ".txt"); } - foreach(num; parallel(iota(1_000))) + foreach (num; parallel(iota(1_000))) { auto outHandle = fileHandles[taskPool.workerIndex]; outHandle.writeln(num, '\t', gcd(num, 42)); @@ -2795,7 +2813,7 @@ public: 2. Recycling temporary buffers across iterations of a parallel foreach loop. - Examples: + Example: --- // Calculate pi as in our synopsis example, but // use an imperative instead of a functional style. @@ -2803,7 +2821,7 @@ public: immutable delta = 1.0L / n; auto sums = taskPool.workerLocalStorage(0.0L); - foreach(i; parallel(iota(n))) + foreach (i; parallel(iota(n))) { immutable x = ( i - 0.5L ) * delta; immutable toAdd = delta / ( 1.0 + x * x ); @@ -2812,7 +2830,7 @@ public: // Add up the results from each worker thread. real pi = 0; - foreach(threadResult; sums.toRange) + foreach (threadResult; sums.toRange) { pi += 4.0L * threadResult; } @@ -2824,31 +2842,17 @@ public: TaskPool pool; size_t size; - static immutable size_t cacheLineSize; size_t elemSize; bool* stillThreadLocal; - shared static this() - { - size_t lineSize = 0; - foreach(cachelevel; datacache) - { - if(cachelevel.lineSize > lineSize && cachelevel.lineSize < uint.max) - { - lineSize = cachelevel.lineSize; - } - } - - cacheLineSize = lineSize; - } - static size_t roundToLine(size_t num) pure nothrow { - if(num % cacheLineSize == 0) + if (num % cacheLineSize == 0) { return num; } - else { + else + { return ((num / cacheLineSize) + 1) * cacheLineSize; } } @@ -2879,16 +2883,17 @@ public: // Cache line align data ptr. data = cast(void*) roundToLine(cast(size_t) data); - foreach(i; 0..nElem) + foreach (i; 0 .. nElem) { this.opIndex(i) = T.init; } } - ref T opIndex(size_t index) + ref opIndex(this Qualified)(size_t index) { + import std.conv : text; assert(index < size, text(index, '\t', uint.max)); - return *(cast(T*) (data + elemSize * index)); + return *(cast(CopyTypeQualifiers!(Qualified, T)*) (data + elemSize * index)); } void opIndexAssign(T val, size_t index) @@ -2911,7 +2916,7 @@ public: failure will result when calling this method. This is not checked when assertions are disabled for performance reasons. */ - ref T get() @property + ref get(this Qualified)() @property { assert(*stillThreadLocal, "Cannot call get() on this instance of WorkerLocalStorage " ~ @@ -2946,7 +2951,7 @@ public: */ WorkerLocalStorageRange!T toRange() @property { - if(*stillThreadLocal) + if (*stillThreadLocal) { *stillThreadLocal = false; @@ -2991,19 +2996,19 @@ public: } public: - ref T front() @property + ref front(this Qualified)() @property { return this[0]; } - ref T back() @property + ref back(this Qualified)() @property { return this[_length - 1]; } void popFront() { - if(_length > 0) + if (_length > 0) { beginOffset++; _length--; @@ -3012,7 +3017,7 @@ public: void popBack() { - if(_length > 0) + if (_length > 0) { _length--; } @@ -3023,7 +3028,7 @@ public: return this; } - ref T opIndex(size_t index) + ref opIndex(this Qualified)(size_t index) { assert(index < _length); return workerLocalStorage[index + beginOffset]; @@ -3044,12 +3049,12 @@ public: return typeof(this)(newWl); } - bool empty() @property + bool empty() const @property { return length == 0; } - size_t length() @property + size_t length() const @property { return _length; } @@ -3065,7 +3070,7 @@ public: { WorkerLocalStorage!T ret; ret.initialize(this); - foreach(i; 0..size + 1) + foreach (i; 0 .. size + 1) { ret[i] = initialVal; } @@ -3123,7 +3128,7 @@ public: // Use this thread as a worker until everything is finished. executeWorkLoop(); - foreach(t; pool) + foreach (t; pool) { // Maybe there should be something here to prevent a thread // from calling join() on itself if this function is called @@ -3164,7 +3169,7 @@ public: Notes: @trusted overloads of this function are called for $(D Task)s if - $(XREF traits, hasUnsharedAliasing) is false for the $(D Task)'s + $(REF hasUnsharedAliasing, std,traits) is false for the $(D Task)'s return type or the function the $(D Task) executes is $(D pure). $(D Task) objects that meet all other requirements specified in the $(D @trusted) overloads of $(D task) and $(D scopedTask) may be created @@ -3179,7 +3184,7 @@ public: complete and no longer referenced by a $(D TaskPool). */ void put(alias fun, Args...)(ref Task!(fun, Args) task) - if(!isSafeReturn!(typeof(task))) + if (!isSafeReturn!(typeof(task))) { task.pool = this; abstractPut(task.basePtr); @@ -3187,22 +3192,24 @@ public: /// Ditto void put(alias fun, Args...)(Task!(fun, Args)* task) - if(!isSafeReturn!(typeof(*task))) + if (!isSafeReturn!(typeof(*task))) { + import std.exception : enforce; enforce(task !is null, "Cannot put a null Task on a TaskPool queue."); put(*task); } @trusted void put(alias fun, Args...)(ref Task!(fun, Args) task) - if(isSafeReturn!(typeof(task))) + if (isSafeReturn!(typeof(task))) { task.pool = this; abstractPut(task.basePtr); } @trusted void put(alias fun, Args...)(Task!(fun, Args)* task) - if(isSafeReturn!(typeof(*task))) + if (isSafeReturn!(typeof(*task))) { + import std.exception : enforce; enforce(task !is null, "Cannot put a null Task on a TaskPool queue."); put(*task); } @@ -3235,7 +3242,7 @@ public: { queueLock(); scope(exit) queueUnlock(); - foreach(thread; pool) + foreach (thread; pool) { thread.isDaemon = newVal; } @@ -3259,9 +3266,9 @@ public: /// Ditto void priority(int newPriority) @property @trusted { - if(size > 0) + if (size > 0) { - foreach(t; pool) + foreach (t; pool) { t.priority = newPriority; } @@ -3278,24 +3285,13 @@ terminating the main thread. */ @property TaskPool taskPool() @trusted { - static bool initialized; - __gshared static TaskPool pool; - - if(!initialized) - { - synchronized(typeid(TaskPool)) - { - if(!pool) - { - pool = new TaskPool(defaultPoolThreads); - pool.isDaemon = true; - } - } - - initialized = true; - } - - return pool; + import std.concurrency : initOnce; + __gshared TaskPool pool; + return initOnce!pool({ + auto p = new TaskPool(defaultPoolThreads); + p.isDaemon = true; + return p; + }()); } private shared uint _defaultPoolThreads; @@ -3333,7 +3329,8 @@ Example: // default TaskPool instance. auto logs = new double[1_000_000]; -foreach(i, ref elem; parallel(logs)) { +foreach (i, ref elem; parallel(logs)) +{ elem = log(i + 1.0); } --- @@ -3371,10 +3368,11 @@ private void submitAndExecute( scope void delegate() doIt ) { + import core.exception : OutOfMemoryError; immutable nThreads = pool.size + 1; alias PTask = typeof(scopedTask(doIt)); - import core.stdc.stdlib; + import core.stdc.stdlib : malloc, free; import core.stdc.string : memcpy; // The logical thing to do would be to just use alloca() here, but that @@ -3388,31 +3386,33 @@ private void submitAndExecute( enum nBuf = 64; byte[nBuf * PTask.sizeof] buf = void; PTask[] tasks; - if(nThreads <= nBuf) + if (nThreads <= nBuf) { - tasks = (cast(PTask*) buf.ptr)[0..nThreads]; + tasks = (cast(PTask*) buf.ptr)[0 .. nThreads]; } else { auto ptr = cast(PTask*) malloc(nThreads * PTask.sizeof); - if(!ptr) throw new OutOfMemoryError("Out of memory in std.parallelism."); - tasks = ptr[0..nThreads]; + if (!ptr) throw new OutOfMemoryError("Out of memory in std.parallelism."); + tasks = ptr[0 .. nThreads]; } scope(exit) { - if(nThreads > nBuf) + if (nThreads > nBuf) { free(tasks.ptr); } } - foreach(ref t; tasks) + foreach (ref t; tasks) { + import core.stdc.string : memcpy; + // This silly looking code is necessary to prevent d'tors from being // called on uninitialized objects. auto temp = scopedTask(doIt); - core.stdc.string.memcpy(&t, &temp, PTask.sizeof); + memcpy(&t, &temp, PTask.sizeof); // This has to be done to t after copying, not temp before copying. // Otherwise, temp's destructor will sit here and wait for the @@ -3420,13 +3420,13 @@ private void submitAndExecute( t.pool = pool; } - foreach(i; 1..tasks.length - 1) + foreach (i; 1 .. tasks.length - 1) { tasks[i].next = tasks[i + 1].basePtr; tasks[i + 1].prev = tasks[i].basePtr; } - if(tasks.length > 1) + if (tasks.length > 1) { pool.queueLock(); scope(exit) pool.queueUnlock(); @@ -3437,20 +3437,20 @@ private void submitAndExecute( ); } - if(tasks.length > 0) + if (tasks.length > 0) { try { tasks[0].job(); } - catch(Throwable e) + catch (Throwable e) { tasks[0].exception = e; } tasks[0].taskStatus = TaskStatus.done; // Try to execute each of these in the current thread - foreach(ref task; tasks[1..$]) + foreach (ref task; tasks[1..$]) { pool.tryDeleteExecute(task.basePtr); } @@ -3458,20 +3458,20 @@ private void submitAndExecute( Throwable firstException, lastException; - foreach(i, ref task; tasks) + foreach (i, ref task; tasks) { try { task.yieldForce; } - catch(Throwable e) + catch (Throwable e) { addToChain(e, firstException, lastException); continue; } } - if(firstException) throw firstException; + if (firstException) throw firstException; } void foreachErr() @@ -3488,11 +3488,11 @@ int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg) // The explicit ElementType!R in the foreach loops is necessary for // correct behavior when iterating over strings. - static if(hasLvalueElements!R) + static if (hasLvalueElements!R) { - foreach(ref ElementType!R elem; range) + foreach (ref ElementType!R elem; range) { - static if(ParameterTypeTuple!dg.length == 2) + static if (Parameters!dg.length == 2) { res = dg(index, elem); } @@ -3500,15 +3500,15 @@ int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg) { res = dg(elem); } - if(res) foreachErr(); + if (res) foreachErr(); index++; } } else { - foreach(ElementType!R elem; range) + foreach (ElementType!R elem; range) { - static if(ParameterTypeTuple!dg.length == 2) + static if (Parameters!dg.length == 2) { res = dg(index, elem); } @@ -3516,7 +3516,7 @@ int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg) { res = dg(elem); } - if(res) foreachErr(); + if (res) foreachErr(); index++; } } @@ -3526,33 +3526,35 @@ int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg) private enum string parallelApplyMixinRandomAccess = q{ // Handle empty thread pool as special case. - if(pool.size == 0) + if (pool.size == 0) { return doSizeZeroCase(this, dg); } // Whether iteration is with or without an index variable. - enum withIndex = ParameterTypeTuple!(typeof(dg)).length == 2; + enum withIndex = Parameters!(typeof(dg)).length == 2; shared size_t workUnitIndex = size_t.max; // Effectively -1: chunkIndex + 1 == 0 immutable len = range.length; - if(!len) return 0; + if (!len) return 0; shared bool shouldContinue = true; void doIt() { + import std.algorithm.comparison : min; + scope(failure) { // If an exception is thrown, all threads should bail. atomicStore(shouldContinue, false); } - while(atomicLoad(shouldContinue)) + while (atomicLoad(shouldContinue)) { immutable myUnitIndex = atomicOp!"+="(workUnitIndex, 1); immutable start = workUnitSize * myUnitIndex; - if(start >= len) + if (start >= len) { atomicStore(shouldContinue, false); break; @@ -3560,15 +3562,15 @@ private enum string parallelApplyMixinRandomAccess = q{ immutable end = min(len, start + workUnitSize); - foreach(i; start..end) + foreach (i; start .. end) { - static if(withIndex) + static if (withIndex) { - if(dg(i, range[i])) foreachErr(); + if (dg(i, range[i])) foreachErr(); } else { - if(dg(range[i])) foreachErr(); + if (dg(range[i])) foreachErr(); } } } @@ -3581,13 +3583,13 @@ private enum string parallelApplyMixinRandomAccess = q{ enum string parallelApplyMixinInputRange = q{ // Handle empty thread pool as special case. - if(pool.size == 0) + if (pool.size == 0) { return doSizeZeroCase(this, dg); } // Whether iteration is with or without an index variable. - enum withIndex = ParameterTypeTuple!(typeof(dg)).length == 2; + enum withIndex = Parameters!(typeof(dg)).length == 2; // This protects the range while copying it. auto rangeMutex = new Mutex(); @@ -3598,7 +3600,7 @@ enum string parallelApplyMixinInputRange = q{ // This is updated only while protected by rangeMutex; size_t nPopped = 0; - static if( + static if ( is(typeof(range.buf1)) && is(typeof(range.bufPos)) && is(typeof(range.doBufSwap())) @@ -3606,7 +3608,7 @@ enum string parallelApplyMixinInputRange = q{ { // Make sure we don't have the buffer recycling overload of // asyncBuf. - static if( + static if ( is(typeof(range.source)) && isRoundRobin!(typeof(range.source)) ) @@ -3630,7 +3632,7 @@ enum string parallelApplyMixinInputRange = q{ atomicStore(shouldContinue, false); } - static if(hasLvalueElements!R) + static if (hasLvalueElements!R) { alias Temp = ElementType!R*[]; Temp temp; @@ -3638,7 +3640,10 @@ enum string parallelApplyMixinInputRange = q{ // Returns: The previous value of nPopped. size_t makeTemp() { - if(temp is null) + import std.algorithm.internal : addressOf; + import std.array : uninitializedArray; + + if (temp is null) { temp = uninitializedArray!Temp(workUnitSize); } @@ -3647,12 +3652,12 @@ enum string parallelApplyMixinInputRange = q{ scope(exit) rangeMutex.unlock(); size_t i = 0; - for(; i < workUnitSize && !range.empty; range.popFront(), i++) + for (; i < workUnitSize && !range.empty; range.popFront(), i++) { temp[i] = addressOf(range.front); } - temp = temp[0..i]; + temp = temp[0 .. i]; auto ret = nPopped; nPopped += temp.length; return ret; @@ -3666,9 +3671,11 @@ enum string parallelApplyMixinInputRange = q{ Temp temp; // Returns: The previous value of nPopped. - static if(!bufferTrick) size_t makeTemp() + static if (!bufferTrick) size_t makeTemp() { - if(temp is null) + import std.array : uninitializedArray; + + if (temp is null) { temp = uninitializedArray!Temp(workUnitSize); } @@ -3677,19 +3684,20 @@ enum string parallelApplyMixinInputRange = q{ scope(exit) rangeMutex.unlock(); size_t i = 0; - for(; i < workUnitSize && !range.empty; range.popFront(), i++) + for (; i < workUnitSize && !range.empty; range.popFront(), i++) { temp[i] = range.front; } - temp = temp[0..i]; + temp = temp[0 .. i]; auto ret = nPopped; nPopped += temp.length; return ret; } - static if(bufferTrick) size_t makeTemp() + static if (bufferTrick) size_t makeTemp() { + import std.algorithm.mutation : swap; rangeMutex.lock(); scope(exit) rangeMutex.unlock(); @@ -3701,7 +3709,7 @@ enum string parallelApplyMixinInputRange = q{ // range before entering the parallel foreach loop. temp = temp[range.bufPos..$]; - static if(is(typeof(range._length))) + static if (is(typeof(range._length))) { range._length -= (temp.length - range.bufPos); } @@ -3713,39 +3721,39 @@ enum string parallelApplyMixinInputRange = q{ } } - while(atomicLoad(shouldContinue)) + while (atomicLoad(shouldContinue)) { auto overallIndex = makeTemp(); - if(temp.empty) + if (temp.empty) { atomicStore(shouldContinue, false); break; } - foreach(i; 0..temp.length) + foreach (i; 0 .. temp.length) { scope(success) overallIndex++; - static if(hasLvalueElements!R) + static if (hasLvalueElements!R) { - static if(withIndex) + static if (withIndex) { - if(dg(overallIndex, *temp[i])) foreachErr(); + if (dg(overallIndex, *temp[i])) foreachErr(); } else { - if(dg(*temp[i])) foreachErr(); + if (dg(*temp[i])) foreachErr(); } } else { - static if(withIndex) + static if (withIndex) { - if(dg(overallIndex, temp[i])) foreachErr(); + if (dg(overallIndex, temp[i])) foreachErr(); } else { - if(dg(temp[i])) foreachErr(); + if (dg(temp[i])) foreachErr(); } } } @@ -3760,9 +3768,9 @@ enum string parallelApplyMixinInputRange = q{ // Calls e.next until the end of the chain is found. private Throwable findLastException(Throwable e) pure nothrow { - if(e is null) return null; + if (e is null) return null; - while(e.next) + while (e.next) { e = e.next; } @@ -3777,7 +3785,7 @@ private void addToChain( ref Throwable lastException ) pure nothrow { - if(firstException) + if (firstException) { assert(lastException); lastException.next = e; @@ -3797,7 +3805,7 @@ private struct ParallelForeach(R) size_t workUnitSize; alias E = ElementType!R; - static if(hasLvalueElements!R) + static if (hasLvalueElements!R) { alias NoIndexDg = int delegate(ref E); alias IndexDg = int delegate(size_t, ref E); @@ -3810,7 +3818,7 @@ private struct ParallelForeach(R) int opApply(scope NoIndexDg dg) { - static if(randLen!R) + static if (randLen!R) { mixin(parallelApplyMixinRandomAccess); } @@ -3822,7 +3830,7 @@ private struct ParallelForeach(R) int opApply(scope IndexDg dg) { - static if(randLen!R) + static if (randLen!R) { mixin(parallelApplyMixinRandomAccess); } @@ -3844,7 +3852,7 @@ private struct RoundRobinBuffer(C1, C2) { // No need for constraints because they're already checked for in asyncBuf. - alias Array = ParameterTypeTuple!(C1.init)[0]; + alias Array = Parameters!(C1.init)[0]; alias T = typeof(Array.init[0]); T[][] bufs; @@ -3864,7 +3872,7 @@ private struct RoundRobinBuffer(C1, C2) this.emptyDel = emptyDel; bufs.length = nBuffers; - foreach(ref buf; bufs) + foreach (ref buf; bufs) { buf.length = initialBufSize; } @@ -3889,13 +3897,13 @@ private struct RoundRobinBuffer(C1, C2) } body { - if(!primed) prime(); + if (!primed) prime(); return bufs[index]; } void popFront() { - if(empty || emptyDel()) + if (empty || emptyDel()) { _empty = true; return; @@ -3921,8 +3929,17 @@ version(unittest) // These test basic functionality but don't stress test for threading bugs. // These are the tests that should be run every time Phobos is compiled. -unittest +@system unittest { + import std.algorithm.iteration : filter, map, reduce; + import std.algorithm.comparison : equal, min, max; + import std.array : split; + import std.conv : text; + import std.exception : assertThrown; + import std.math : approxEqual, sqrt, log; + import std.range : indexed, iota, join; + import std.typecons : Tuple, tuple; + poolInstance = new TaskPool(2); scope(exit) poolInstance.stop(); @@ -4008,7 +4025,7 @@ unittest auto nums = new uint[5]; auto nums2 = new uint[5]; - foreach(i, ref elem; poolInstance.parallel(arr)) + foreach (i, ref elem; poolInstance.parallel(arr)) { elem++; nums[i] = cast(uint) i + 2; @@ -4036,7 +4053,7 @@ unittest // Test parallel foreach with non-random access range. auto range = filter!"a != 666"([0, 1, 2, 3, 4]); - foreach(i, elem; poolInstance.parallel(range)) + foreach (i, elem; poolInstance.parallel(range)) { nums[i] = cast(uint) i; } @@ -4044,12 +4061,12 @@ unittest assert(nums == [0,1,2,3,4]); auto logs = new double[1_000_000]; - foreach(i, ref elem; poolInstance.parallel(logs)) + foreach (i, ref elem; poolInstance.parallel(logs)) { elem = log(i + 1.0); } - foreach(i, elem; logs) + foreach (i, elem; logs) { assert(approxEqual(elem, cast(double) log(i + 1))); } @@ -4067,12 +4084,12 @@ unittest // Test amap with a non-array buffer. auto toIndex = new int[5]; - auto indexed = std.range.indexed(toIndex, [3, 1, 4, 0, 2]); - poolInstance.amap!"a * 2"([1, 2, 3, 4, 5], indexed); - assert(equal(indexed, [2, 4, 6, 8, 10])); + auto ind = indexed(toIndex, [3, 1, 4, 0, 2]); + poolInstance.amap!"a * 2"([1, 2, 3, 4, 5], ind); + assert(equal(ind, [2, 4, 6, 8, 10])); assert(equal(toIndex, [8, 4, 10, 2, 6])); - poolInstance.amap!"a / 2"(indexed, indexed); - assert(equal(indexed, [1, 2, 3, 4, 5])); + poolInstance.amap!"a / 2"(ind, ind); + assert(equal(ind, [1, 2, 3, 4, 5])); assert(equal(toIndex, [4, 2, 5, 1, 3])); auto buf = new int[5]; @@ -4089,13 +4106,13 @@ unittest assert(poolInstance.reduce!("a + b", "a * b")(tuple(0, 1), [1,2,3,4]) == tuple(10, 24)); - immutable serialAns = std.algorithm.reduce!"a + b"(iota(1000)); + immutable serialAns = reduce!"a + b"(iota(1000)); assert(poolInstance.reduce!"a + b"(0, iota(1000)) == serialAns); assert(poolInstance.reduce!"a + b"(iota(1000)) == serialAns); // Test worker-local storage. auto wl = poolInstance.workerLocalStorage(0); - foreach(i; poolInstance.parallel(iota(1000), 1)) + foreach (i; poolInstance.parallel(iota(1000), 1)) { wl.get = wl.get + i; } @@ -4103,8 +4120,8 @@ unittest auto wlRange = wl.toRange; auto parallelSum = poolInstance.reduce!"a + b"(wlRange); assert(parallelSum == 499500); - assert(wlRange[0..1][0] == wlRange[0]); - assert(wlRange[1..2][0] == wlRange[1]); + assert(wlRange[0 .. 1][0] == wlRange[0]); + assert(wlRange[1 .. 2][0] == wlRange[1]); // Test finish() { @@ -4142,7 +4159,7 @@ unittest assert(taskPool.size == totalCPUs - 1); nums = new uint[1000]; - foreach(i; parallel(iota(1000))) + foreach (i; parallel(iota(1000))) { nums[i] = cast(uint) i; } @@ -4150,7 +4167,7 @@ unittest assert(equal( poolInstance.map!"a * a"(iota(30_000_001), 10_000), - std.algorithm.map!"a * a"(iota(30_000_001)) + map!"a * a"(iota(30_000_001)) )); // The filter is to kill random access and test the non-random access @@ -4159,7 +4176,7 @@ unittest poolInstance.map!"a * a"( filter!"a == a"(iota(30_000_001) ), 10_000, 1000), - std.algorithm.map!"a * a"(iota(30_000_001)) + map!"a * a"(iota(30_000_001)) )); assert( @@ -4167,7 +4184,7 @@ unittest poolInstance.map!"a * a"(iota(3_000_001), 10_000) ) == reduce!"a + b"(0UL, - std.algorithm.map!"a * a"(iota(3_000_001)) + map!"a * a"(iota(3_000_001)) ) ); @@ -4177,9 +4194,10 @@ unittest )); { + import std.conv : to; import std.file : deleteme; - string temp_file = std.file.deleteme ~ "-tempDelMe.txt"; + string temp_file = deleteme ~ "-tempDelMe.txt"; auto file = File(temp_file, "wb"); scope(exit) { @@ -4189,7 +4207,7 @@ unittest } auto written = [[1.0, 2, 3], [4.0, 5, 6], [7.0, 8, 9]]; - foreach(row; written) + foreach (row; written) { file.writeln(join(to!(string[])(row), "\t")); } @@ -4206,9 +4224,9 @@ unittest double[][] read; auto asyncReader = taskPool.asyncBuf(&next, &file.eof); - foreach(line; asyncReader) + foreach (line; asyncReader) { - if(line.length == 0) continue; + if (line.length == 0) continue; auto ls = line.split("\t"); read ~= to!(double[])(ls); } @@ -4227,9 +4245,9 @@ unittest lmchain.popFront(); int ii; - foreach( elem; (lmchain)) + foreach ( elem; (lmchain)) { - if(!approxEqual(elem, ii)) + if (!approxEqual(elem, ii)) { stderr.writeln(ii, '\t', elem); } @@ -4240,7 +4258,7 @@ unittest abuf = poolInstance.asyncBuf(iota(-1.0, 1_000_000), 100); abuf.popFront(); auto bufTrickTest = new size_t[abuf.length]; - foreach(i, elem; parallel(abuf)) + foreach (i, elem; parallel(abuf)) { bufTrickTest[i] = i; } @@ -4254,12 +4272,12 @@ unittest // Test that worker local storage from one pool receives an index of 0 // when the index is queried w.r.t. another pool. The only way to do this // is non-deterministically. - foreach(i; parallel(iota(1000), 1)) + foreach (i; parallel(iota(1000), 1)) { assert(poolInstance.workerIndex == 0); } - foreach(i; poolInstance.parallel(iota(1000), 1)) + foreach (i; poolInstance.parallel(iota(1000), 1)) { assert(taskPool.workerIndex == 0); } @@ -4267,7 +4285,7 @@ unittest // Test exception handling. static void parallelForeachThrow() { - foreach(elem; parallel(iota(10))) + foreach (elem; parallel(iota(10))) { throw new Exception(""); } @@ -4323,24 +4341,24 @@ unittest // tons of stuff and should not be run every time make unittest is run. version(parallelismStressTest) { - unittest + @safe unittest { size_t attempt; - for(; attempt < 10; attempt++) - foreach(poolSize; [0, 4]) + for (; attempt < 10; attempt++) + foreach (poolSize; [0, 4]) { poolInstance = new TaskPool(poolSize); uint[] numbers = new uint[1_000]; - foreach(i; poolInstance.parallel( iota(0, numbers.length)) ) + foreach (i; poolInstance.parallel( iota(0, numbers.length)) ) { numbers[i] = cast(uint) i; } // Make sure it works. - foreach(i; 0..numbers.length) + foreach (i; 0 .. numbers.length) { assert(numbers[i] == i); } @@ -4349,7 +4367,7 @@ version(parallelismStressTest) auto myNumbers = filter!"a % 7 > 0"( iota(0, 1000)); - foreach(num; poolInstance.parallel(myNumbers)) + foreach (num; poolInstance.parallel(myNumbers)) { assert(num % 7 > 0 && num < 1000); } @@ -4357,7 +4375,7 @@ version(parallelismStressTest) uint[] squares = poolInstance.amap!"a * a"(numbers, 100); assert(squares.length == numbers.length); - foreach(i, number; numbers) + foreach (i, number; numbers) { assert(squares[i] == number * number); } @@ -4367,7 +4385,7 @@ version(parallelismStressTest) poolInstance.put(sumFuture); ulong sumSquares = 0; - foreach(elem; numbers) + foreach (elem; numbers) { sumSquares += elem * elem; } @@ -4388,9 +4406,9 @@ version(parallelismStressTest) auto nestedOuter = "abcd"; auto nestedInner = iota(0, 10, 2); - foreach(i, letter; poolInstance.parallel(nestedOuter, 1)) + foreach (i, letter; poolInstance.parallel(nestedOuter, 1)) { - foreach(j, number; poolInstance.parallel(nestedInner, 1)) + foreach (j, number; poolInstance.parallel(nestedInner, 1)) { synchronized writeln(i, ": ", letter, " ", j, ": ", number); } @@ -4406,10 +4424,10 @@ version(parallelismStressTest) // These unittests are intended more for actual testing and not so much // as examples. - unittest + @safe unittest { - foreach(attempt; 0..10) - foreach(poolSize; [0, 4]) + foreach (attempt; 0 .. 10) + foreach (poolSize; [0, 4]) { poolInstance = new TaskPool(poolSize); @@ -4419,7 +4437,7 @@ version(parallelismStressTest) // Test worker-local storage. auto workerLocalStorage = poolInstance.workerLocalStorage!uint(1); - foreach(i; poolInstance.parallel(iota(0U, 1_000_000))) + foreach (i; poolInstance.parallel(iota(0U, 1_000_000))) { workerLocalStorage.get++; } @@ -4430,13 +4448,13 @@ version(parallelismStressTest) // non-deterministic and is more of a sanity check than something that // has an absolute pass/fail. shared(uint)[void*] nJobsByThread; - foreach(thread; poolInstance.pool) + foreach (thread; poolInstance.pool) { nJobsByThread[cast(void*) thread] = 0; } nJobsByThread[ cast(void*) Thread.getThis()] = 0; - foreach(i; poolInstance.parallel( iota(0, 1_000_000), 100 )) + foreach (i; poolInstance.parallel( iota(0, 1_000_000), 100 )) { atomicOp!"+="( nJobsByThread[ cast(void*) Thread.getThis() ], 1); } @@ -4444,16 +4462,16 @@ version(parallelismStressTest) stderr.writeln("\nCurrent thread is: ", cast(void*) Thread.getThis()); stderr.writeln("Workload distribution: "); - foreach(k, v; nJobsByThread) + foreach (k, v; nJobsByThread) { stderr.writeln(k, '\t', v); } // Test whether amap can be nested. real[][] matrix = new real[][](1000, 1000); - foreach(i; poolInstance.parallel( iota(0, matrix.length) )) + foreach (i; poolInstance.parallel( iota(0, matrix.length) )) { - foreach(j; poolInstance.parallel( iota(0, matrix[0].length) )) + foreach (j; poolInstance.parallel( iota(0, matrix[0].length) )) { matrix[i][j] = i * j; } @@ -4472,9 +4490,9 @@ version(parallelismStressTest) real[][] sqrtMatrix = poolInstance.amap!parallelSqrt(matrix); - foreach(i, row; sqrtMatrix) + foreach (i, row; sqrtMatrix) { - foreach(j, elem; row) + foreach (j, elem; row) { real shouldBe = sqrt( cast(real) i * j); assert(approxEqual(shouldBe, elem)); @@ -4512,24 +4530,24 @@ version(parallelismStressTest) poolInstance.put(nanTask); assert(nanTask.spinForce == false); - if(poolInstance.size > 0) + if (poolInstance.size > 0) { // Test work waiting. static void uselessFun() { - foreach(i; 0..1_000_000) {} + foreach (i; 0 .. 1_000_000) {} } auto uselessTasks = new typeof(task(&uselessFun))[1000]; - foreach(ref uselessTask; uselessTasks) + foreach (ref uselessTask; uselessTasks) { uselessTask = task(&uselessFun); } - foreach(ref uselessTask; uselessTasks) + foreach (ref uselessTask; uselessTasks) { poolInstance.put(uselessTask); } - foreach(ref uselessTask; uselessTasks) + foreach (ref uselessTask; uselessTasks) { uselessTask.workForce(); } @@ -4556,7 +4574,7 @@ version(parallelismStressTest) } auto refRange = RemoveRandom(nums); - foreach(ref elem; poolInstance.parallel(refRange)) + foreach (ref elem; poolInstance.parallel(refRange)) { elem++; } @@ -4572,19 +4590,37 @@ version(unittest) { struct __S_12733 { - invariant() { assert(checksum == 1234567890); } + invariant() { assert(checksum == 1_234_567_890); } this(ulong u){n = u;} void opAssign(__S_12733 s){this.n = s.n;} ulong n; - ulong checksum = 1234567890; + ulong checksum = 1_234_567_890; } static auto __genPair_12733(ulong n) { return __S_12733(n); } } -unittest +@system unittest { immutable ulong[] data = [ 2UL^^59-1, 2UL^^59-1, 2UL^^59-1, 112_272_537_195_293UL ]; auto result = taskPool.amap!__genPair_12733(data); } + +@safe unittest +{ + import std.range : iota; + + // this test was in std.range, but caused cycles. + assert(__traits(compiles, { foreach (i; iota(0, 100UL).parallel) {} })); +} + +@safe unittest +{ + import std.algorithm.iteration : each; + + long[] arr; + static assert(is(typeof({ + arr.parallel.each!"a++"; + }))); +} diff --git a/std/path.d b/std/path.d index 254d8f0001c..d2d19a18f8c 100644 --- a/std/path.d +++ b/std/path.d @@ -10,8 +10,8 @@ between a _path that points to a directory and a _path that points to a file, and it does not know whether or not the object pointed to by the _path actually exists in the file system. - To differentiate between these cases, use $(XREF file,isDir) and - $(XREF file,exists). + To differentiate between these cases, use $(REF isDir, std,file) and + $(REF exists, std,file). Note that on Windows, both the backslash ($(D `\`)) and the slash ($(D `/`)) are in principle valid directory separators. This module treats them @@ -32,24 +32,66 @@ returned, it is usually a slice of an input string. If a function allocates, this is explicitly mentioned in the documentation. - Upgrading: - $(WEB digitalmars.com/d/1.0/phobos/std_path.html#fnmatch) can - be replaced with $(D globMatch). +$(SCRIPT inhibitQuickIndex = 1;) +$(DIVC quickindex, +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Normalization) $(TD + $(LREF absolutePath) + $(LREF asAbsolutePath) + $(LREF asNormalizedPath) + $(LREF asRelativePath) + $(LREF buildNormalizedPath) + $(LREF buildPath) + $(LREF chainPath) + $(LREF expandTilde) +)) +$(TR $(TD Partitioning) $(TD + $(LREF baseName) + $(LREF dirName) + $(LREF dirSeparator) + $(LREF driveName) + $(LREF pathSeparator) + $(LREF pathSplitter) + $(LREF relativePath) + $(LREF rootName) + $(LREF stripDrive) +)) +$(TR $(TD Validation) $(TD + $(LREF isAbsolute) + $(LREF isDirSeparator) + $(LREF isRooted) + $(LREF isValidFilename) + $(LREF isValidPath) +)) +$(TR $(TD Extension) $(TD + $(LREF defaultExtension) + $(LREF extension) + $(LREF setExtension) + $(LREF stripExtension) + $(LREF withDefaultExtension) + $(LREF withExtension) +)) +$(TR $(TD Other) $(TD + $(LREF filenameCharCmp) + $(LREF filenameCmp) + $(LREF globMatch) + $(LREF CaseSensitive) +)) +)) Authors: Lars Tandle Kyllingstad, - $(WEB digitalmars.com, Walter Bright), + $(HTTP digitalmars.com, Walter Bright), Grzegorz Adam Hankiewicz, Thomas K$(UUML)hne, - $(WEB erdani.org, Andrei Alexandrescu) + $(HTTP erdani.org, Andrei Alexandrescu) Copyright: Copyright (c) 2000-2014, the authors. All rights reserved. License: - $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) + $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) Source: $(PHOBOSSRC std/_path.d) - Macros: - WIKI = Phobos/StdPath */ module std.path; @@ -58,13 +100,31 @@ module std.path; import std.file; //: getcwd; import std.range.primitives; import std.traits; +static import std.meta; + +version (unittest) +{ +private: + struct TestAliasedString + { + string get() @safe @nogc pure nothrow { return _s; } + alias get this; + @disable this(this); + string _s; + } + + bool testAliasedString(alias func, Args...)(string s, Args args) + { + return func(TestAliasedString(s), args) == func(s, args); + } +} /** String used to separate directory names in a path. Under POSIX this is a slash, under Windows a backslash. */ version(Posix) enum string dirSeparator = "/"; else version(Windows) enum string dirSeparator = "\\"; -else static assert (0, "unsupported platform"); +else static assert(0, "unsupported platform"); @@ -74,7 +134,7 @@ else static assert (0, "unsupported platform"); */ version(Posix) enum string pathSeparator = ":"; else version(Windows) enum string pathSeparator = ";"; -else static assert (0, "unsupported platform"); +else static assert(0, "unsupported platform"); @@ -117,9 +177,9 @@ version(Posix) private alias isSeparator = isDirSeparator; drive/directory separator in a string. Returns -1 if none is found. */ -private ptrdiff_t lastSeparator(R)(const R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) +private ptrdiff_t lastSeparator(R)(R path) +if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { auto i = (cast(ptrdiff_t) path.length) - 1; while (i >= 0 && !isSeparator(path[i])) --i; @@ -129,18 +189,18 @@ private ptrdiff_t lastSeparator(R)(const R path) version (Windows) { - private bool isUNC(R)(const R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) + private bool isUNC(R)(R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return path.length >= 3 && isDirSeparator(path[0]) && isDirSeparator(path[1]) && !isDirSeparator(path[2]); } - private ptrdiff_t uncRootLength(R)(const R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) - in { assert (isUNC(path)); } + private ptrdiff_t uncRootLength(R)(R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) + in { assert(isUNC(path)); } body { ptrdiff_t i = 3; @@ -158,16 +218,16 @@ version (Windows) return i; } - private bool hasDrive(R)(const R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) + private bool hasDrive(R)(R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return path.length >= 2 && isDriveSeparator(path[1]); } - private bool isDriveRoot(R)(const R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) + private bool isDriveRoot(R)(R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return path.length >= 3 && isDriveSeparator(path[1]) && isDirSeparator(path[2]); @@ -178,31 +238,83 @@ version (Windows) /* Helper functions that strip leading/trailing slashes and backslashes from a path. */ -private auto ltrimDirSeparators(R)(inout R path) - if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - isNarrowString!R) +private auto ltrimDirSeparators(R)(R path) +if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { - int i = 0; - while (i < path.length && isDirSeparator(path[i])) ++i; - return path[i .. path.length]; + static if (isRandomAccessRange!R && hasSlicing!R || isNarrowString!R) + { + int i = 0; + while (i < path.length && isDirSeparator(path[i])) + ++i; + return path[i .. path.length]; + } + else + { + while (!path.empty && isDirSeparator(path.front)) + path.popFront(); + return path; + } } -private auto rtrimDirSeparators(R)(inout R path) - if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - isNarrowString!R) +@system unittest { - auto i = (cast(ptrdiff_t) path.length) - 1; - while (i >= 0 && isDirSeparator(path[i])) --i; - return path[0 .. i+1]; + import std.array; + import std.utf : byDchar; + + assert(ltrimDirSeparators("//abc//").array == "abc//"); + assert(ltrimDirSeparators("//abc//"d).array == "abc//"d); + assert(ltrimDirSeparators("//abc//".byDchar).array == "abc//"d); } -private auto trimDirSeparators(R)(inout R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - isNarrowString!R) +private auto rtrimDirSeparators(R)(R path) +if (isBidirectionalRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) +{ + static if (isRandomAccessRange!R && hasSlicing!R && hasLength!R || isNarrowString!R) + { + auto i = (cast(ptrdiff_t) path.length) - 1; + while (i >= 0 && isDirSeparator(path[i])) + --i; + return path[0 .. i+1]; + } + else + { + while (!path.empty && isDirSeparator(path.back)) + path.popBack(); + return path; + } +} + +@system unittest +{ + import std.array; + import std.utf : byDchar; + + assert(rtrimDirSeparators("//abc//").array == "//abc"); + assert(rtrimDirSeparators("//abc//"d).array == "//abc"d); + + assert(rtrimDirSeparators(MockBiRange!char("//abc//")).array == "//abc"); +} + +private auto trimDirSeparators(R)(R path) +if (isBidirectionalRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return ltrimDirSeparators(rtrimDirSeparators(path)); } +@system unittest +{ + import std.array; + import std.utf : byDchar; + + assert(trimDirSeparators("//abc//").array == "abc"); + assert(trimDirSeparators("//abc//"d).array == "abc"d); + + assert(trimDirSeparators(MockBiRange!char("//abc//")).array == "abc"); +} + @@ -227,13 +339,19 @@ enum CaseSensitive : bool version (Windows) private enum osDefaultCaseSensitivity = false; else version (OSX) private enum osDefaultCaseSensitivity = false; else version (Posix) private enum osDefaultCaseSensitivity = true; -else static assert (0); +else static assert(0); -/** Returns the name of a file, without any leading directory - and with an optional suffix chopped off. +/** + Params: + cs = Whether or not suffix matching is case-sensitive. + path = A path name. It can be a string, or any random-access range of + characters. + suffix = An optional suffix to be removed from the file name. + Returns: The name of the file in the path name, without any leading + directory and with an optional suffix chopped off. If $(D suffix) is specified, it will be compared to $(D path) using $(D filenameCmp!cs), @@ -241,18 +359,18 @@ else static assert (0); the comparison is case sensitive or not. See the $(LREF filenameCmp) documentation for details. - Examples: + Example: --- - assert (baseName("dir/file.ext") == "file.ext"); - assert (baseName("dir/file.ext", ".ext") == "file"); - assert (baseName("dir/file.ext", ".xyz") == "file.ext"); - assert (baseName("dir/filename", "name") == "file"); - assert (baseName("dir/subdir/") == "subdir"); + assert(baseName("dir/file.ext") == "file.ext"); + assert(baseName("dir/file.ext", ".ext") == "file"); + assert(baseName("dir/file.ext", ".xyz") == "file.ext"); + assert(baseName("dir/filename", "name") == "file"); + assert(baseName("dir/subdir/") == "subdir"); version (Windows) { - assert (baseName(`d:file.ext`) == "file.ext"); - assert (baseName(`d:\dir\file.ext`) == "file.ext"); + assert(baseName(`d:file.ext`) == "file.ext"); + assert(baseName(`d:\dir\file.ext`) == "file.ext"); } --- @@ -264,7 +382,7 @@ else static assert (0); To obtain the filename without leading directories and without an extension, combine the functions like this: --- - assert (baseName(stripExtension("dir/file.ext")) == "file"); + assert(baseName(stripExtension("dir/file.ext")) == "file"); --- Standards: @@ -274,15 +392,15 @@ else static assert (0); (with suitable adaptations for Windows paths). */ auto baseName(R)(R path) - if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) +if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { auto p1 = stripDrive!(BaseOf!R)(path); if (p1.empty) { version (Windows) if (isUNC!(BaseOf!R)(path)) { - return path[0..1]; + return path[0 .. 1]; } static if (is(StringTypeOf!R)) return StringTypeOf!R.init[]; // which is null @@ -300,11 +418,11 @@ auto baseName(R)(R path) inout(C)[] baseName(CaseSensitive cs = CaseSensitive.osDefault, C, C1) (inout(C)[] path, in C1[] suffix) @safe pure //TODO: nothrow (because of filenameCmp()) - if (isSomeChar!C && isSomeChar!C1) +if (isSomeChar!C && isSomeChar!C1) { auto p = baseName(path); if (p.length > suffix.length - && filenameCmp!cs(p[$-suffix.length .. $], suffix) == 0) + && filenameCmp!cs(cast(const(C)[])p[$-suffix.length .. $], suffix) == 0) { return p[0 .. $-suffix.length]; } @@ -312,24 +430,24 @@ inout(C)[] baseName(CaseSensitive cs = CaseSensitive.osDefault, C, C1) } -unittest +@safe unittest { - assert (baseName("").empty); - assert (baseName("file.ext"w) == "file.ext"); - assert (baseName("file.ext"d, ".ext") == "file"); - assert (baseName("file", "file"w.dup) == "file"); - assert (baseName("dir/file.ext"d.dup) == "file.ext"); - assert (baseName("dir/file.ext", ".ext"d) == "file"); - assert (baseName("dir/file"w, "file"d) == "file"); - assert (baseName("dir///subdir////") == "subdir"); - assert (baseName("dir/subdir.ext/", ".ext") == "subdir"); - assert (baseName("dir/subdir/".dup, "subdir") == "subdir"); - assert (baseName("/"w.dup) == "/"); - assert (baseName("//"d.dup) == "/"); - assert (baseName("///") == "/"); + assert(baseName("").empty); + assert(baseName("file.ext"w) == "file.ext"); + assert(baseName("file.ext"d, ".ext") == "file"); + assert(baseName("file", "file"w.dup) == "file"); + assert(baseName("dir/file.ext"d.dup) == "file.ext"); + assert(baseName("dir/file.ext", ".ext"d) == "file"); + assert(baseName("dir/file"w, "file"d) == "file"); + assert(baseName("dir///subdir////") == "subdir"); + assert(baseName("dir/subdir.ext/", ".ext") == "subdir"); + assert(baseName("dir/subdir/".dup, "subdir") == "subdir"); + assert(baseName("/"w.dup) == "/"); + assert(baseName("//"d.dup) == "/"); + assert(baseName("///") == "/"); - assert (baseName!(CaseSensitive.yes)("file.ext", ".EXT") == "file.ext"); - assert (baseName!(CaseSensitive.no)("file.ext", ".EXT") == "file"); + assert(baseName!(CaseSensitive.yes)("file.ext", ".EXT") == "file.ext"); + assert(baseName!(CaseSensitive.no)("file.ext", ".EXT") == "file"); { auto r = MockRange!(immutable(char))(`dir/file.ext`); @@ -340,23 +458,23 @@ unittest version (Windows) { - assert (baseName(`dir\file.ext`) == `file.ext`); - assert (baseName(`dir\file.ext`, `.ext`) == `file`); - assert (baseName(`dir\file`, `file`) == `file`); - assert (baseName(`d:file.ext`) == `file.ext`); - assert (baseName(`d:file.ext`, `.ext`) == `file`); - assert (baseName(`d:file`, `file`) == `file`); - assert (baseName(`dir\\subdir\\\`) == `subdir`); - assert (baseName(`dir\subdir.ext\`, `.ext`) == `subdir`); - assert (baseName(`dir\subdir\`, `subdir`) == `subdir`); - assert (baseName(`\`) == `\`); - assert (baseName(`\\`) == `\`); - assert (baseName(`\\\`) == `\`); - assert (baseName(`d:\`) == `\`); - assert (baseName(`d:`).empty); - assert (baseName(`\\server\share\file`) == `file`); - assert (baseName(`\\server\share\`) == `\`); - assert (baseName(`\\server\share`) == `\`); + assert(baseName(`dir\file.ext`) == `file.ext`); + assert(baseName(`dir\file.ext`, `.ext`) == `file`); + assert(baseName(`dir\file`, `file`) == `file`); + assert(baseName(`d:file.ext`) == `file.ext`); + assert(baseName(`d:file.ext`, `.ext`) == `file`); + assert(baseName(`d:file`, `file`) == `file`); + assert(baseName(`dir\\subdir\\\`) == `subdir`); + assert(baseName(`dir\subdir.ext\`, `.ext`) == `subdir`); + assert(baseName(`dir\subdir\`, `subdir`) == `subdir`); + assert(baseName(`\`) == `\`); + assert(baseName(`\\`) == `\`); + assert(baseName(`\\\`) == `\`); + assert(baseName(`d:\`) == `\`); + assert(baseName(`d:`).empty); + assert(baseName(`\\server\share\file`) == `file`); + assert(baseName(`\\server\share\`) == `\`); + assert(baseName(`\\server\share`) == `\`); auto r = MockRange!(immutable(char))(`\\server\share`); auto s = r.baseName(); @@ -364,10 +482,10 @@ unittest assert(s[i] == c); } - assert (baseName(stripExtension("dir/file.ext")) == "file"); + assert(baseName(stripExtension("dir/file.ext")) == "file"); - static assert (baseName("dir/file.ext") == "file.ext"); - static assert (baseName("dir/file.ext", ".ext") == "file"); + static assert(baseName("dir/file.ext") == "file.ext"); + static assert(baseName("dir/file.ext", ".ext") == "file"); static struct DirEntry { string s; alias s this; } assert(baseName(DirEntry("dir/file.ext")) == "file.ext"); @@ -379,25 +497,11 @@ unittest /** Returns the directory part of a path. On Windows, this includes the drive letter if present. - This function performs a memory allocation if and only if $(D path) - does not have a directory (in which case a new string is needed to - hold the returned current-directory symbol, $(D ".")). - - Examples: - --- - assert (dirName("file") == "."); - assert (dirName("dir/file") == "dir"); - assert (dirName("/file") == "/"); - assert (dirName("dir/subdir/") == "dir"); + Params: + path = A path name. - version (Windows) - { - assert (dirName("d:file") == "d:"); - assert (dirName(`d:\dir\file`) == `d:\dir`); - assert (dirName(`d:\file`) == `d:\`); - assert (dirName(`dir\subdir\`) == `dir`); - } - --- + Returns: + A slice of $(D path) or ".". Standards: This function complies with @@ -405,78 +509,144 @@ unittest the POSIX requirements for the 'dirname' shell utility) (with suitable adaptations for Windows paths). */ -C[] dirName(C)(C[] path) - //TODO: @safe (BUG 6169) pure nothrow (because of to()) - if (isSomeChar!C) +auto dirName(R)(R path) +if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R) { - import std.conv : to; - if (path.empty) return to!(typeof(return))("."); + static auto result(bool dot, typeof(path[0 .. 1]) p) + { + static if (isSomeString!R) + return dot ? "." : p; + else + { + import std.range : choose, only; + return choose(dot, only(cast(ElementEncodingType!R)'.'), p); + } + } + + if (path.empty) + return result(true, path[0 .. 0]); auto p = rtrimDirSeparators(path); - if (p.empty) return path[0 .. 1]; + if (p.empty) + return result(false, path[0 .. 1]); version (Windows) { if (isUNC(p) && uncRootLength(p) == p.length) - return p; + return result(false, p); + if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2) - return path[0 .. 3]; + return result(false, path[0 .. 3]); } auto i = lastSeparator(p); - if (i == -1) return to!(typeof(return))("."); - if (i == 0) return p[0 .. 1]; + if (i == -1) + return result(true, p); + if (i == 0) + return result(false, p[0 .. 1]); version (Windows) { - // If the directory part is either d: or d:\, don't - // chop off the last symbol. + // If the directory part is either d: or d:\ + // do not chop off the last symbol. if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1])) - return p[0 .. i+1]; + return result(false, p[0 .. i+1]); } - // Remove any remaining trailing (back)slashes. - return rtrimDirSeparators(p[0 .. i]); + return result(false, rtrimDirSeparators(p[0 .. i])); } +/// +@safe unittest +{ + assert(dirName("") == "."); + assert(dirName("file"w) == "."); + assert(dirName("dir/"d) == "."); + assert(dirName("dir///") == "."); + assert(dirName("dir/file"w.dup) == "dir"); + assert(dirName("dir///file"d.dup) == "dir"); + assert(dirName("dir/subdir/") == "dir"); + assert(dirName("/dir/file"w) == "/dir"); + assert(dirName("/file"d) == "/"); + assert(dirName("/") == "/"); + assert(dirName("///") == "/"); + + version (Windows) + { + assert(dirName(`dir\`) == `.`); + assert(dirName(`dir\\\`) == `.`); + assert(dirName(`dir\file`) == `dir`); + assert(dirName(`dir\\\file`) == `dir`); + assert(dirName(`dir\subdir\`) == `dir`); + assert(dirName(`\dir\file`) == `\dir`); + assert(dirName(`\file`) == `\`); + assert(dirName(`\`) == `\`); + assert(dirName(`\\\`) == `\`); + assert(dirName(`d:`) == `d:`); + assert(dirName(`d:file`) == `d:`); + assert(dirName(`d:\`) == `d:\`); + assert(dirName(`d:\file`) == `d:\`); + assert(dirName(`d:\dir\file`) == `d:\dir`); + assert(dirName(`\\server\share\dir\file`) == `\\server\share\dir`); + assert(dirName(`\\server\share\file`) == `\\server\share`); + assert(dirName(`\\server\share\`) == `\\server\share`); + assert(dirName(`\\server\share`) == `\\server\share`); + } +} -unittest +auto dirName(R)(auto ref R path) +if (isConvertibleToString!R) +{ + return dirName!(StringTypeOf!R)(path); +} + +@safe unittest +{ + assert(testAliasedString!dirName("file")); +} + +@system unittest { - assert (dirName("") == "."); - assert (dirName("file"w) == "."); - assert (dirName("dir/"d) == "."); - assert (dirName("dir///") == "."); - assert (dirName("dir/file"w.dup) == "dir"); - assert (dirName("dir///file"d.dup) == "dir"); - assert (dirName("dir/subdir/") == "dir"); - assert (dirName("/dir/file"w) == "/dir"); - assert (dirName("/file"d) == "/"); - assert (dirName("/") == "/"); - assert (dirName("///") == "/"); + static assert(dirName("dir/file") == "dir"); + + import std.array; + import std.utf : byChar, byWchar, byDchar; + + assert(dirName("".byChar).array == "."); + assert(dirName("file"w.byWchar).array == "."w); + assert(dirName("dir/"d.byDchar).array == "."d); + assert(dirName("dir///".byChar).array == "."); + assert(dirName("dir/subdir/".byChar).array == "dir"); + assert(dirName("/dir/file"w.byWchar).array == "/dir"w); + assert(dirName("/file"d.byDchar).array == "/"d); + assert(dirName("/".byChar).array == "/"); + assert(dirName("///".byChar).array == "/"); version (Windows) { - assert (dirName(`dir\`) == `.`); - assert (dirName(`dir\\\`) == `.`); - assert (dirName(`dir\file`) == `dir`); - assert (dirName(`dir\\\file`) == `dir`); - assert (dirName(`dir\subdir\`) == `dir`); - assert (dirName(`\dir\file`) == `\dir`); - assert (dirName(`\file`) == `\`); - assert (dirName(`\`) == `\`); - assert (dirName(`\\\`) == `\`); - assert (dirName(`d:`) == `d:`); - assert (dirName(`d:file`) == `d:`); - assert (dirName(`d:\`) == `d:\`); - assert (dirName(`d:\file`) == `d:\`); - assert (dirName(`d:\dir\file`) == `d:\dir`); - assert (dirName(`\\server\share\dir\file`) == `\\server\share\dir`); - assert (dirName(`\\server\share\file`) == `\\server\share`); - assert (dirName(`\\server\share\`) == `\\server\share`); - assert (dirName(`\\server\share`) == `\\server\share`); + assert(dirName(`dir\`.byChar).array == `.`); + assert(dirName(`dir\\\`.byChar).array == `.`); + assert(dirName(`dir\file`.byChar).array == `dir`); + assert(dirName(`dir\\\file`.byChar).array == `dir`); + assert(dirName(`dir\subdir\`.byChar).array == `dir`); + assert(dirName(`\dir\file`.byChar).array == `\dir`); + assert(dirName(`\file`.byChar).array == `\`); + assert(dirName(`\`.byChar).array == `\`); + assert(dirName(`\\\`.byChar).array == `\`); + assert(dirName(`d:`.byChar).array == `d:`); + assert(dirName(`d:file`.byChar).array == `d:`); + assert(dirName(`d:\`.byChar).array == `d:\`); + assert(dirName(`d:\file`.byChar).array == `d:\`); + assert(dirName(`d:\dir\file`.byChar).array == `d:\dir`); + assert(dirName(`\\server\share\dir\file`.byChar).array == `\\server\share\dir`); + assert(dirName(`\\server\share\file`) == `\\server\share`); + assert(dirName(`\\server\share\`.byChar).array == `\\server\share`); + assert(dirName(`\\server\share`.byChar).array == `\\server\share`); } - static assert (dirName("dir/file") == "dir"); + //static assert(dirName("dir/file".byChar).array == "dir"); } @@ -485,22 +655,19 @@ unittest /** Returns the root directory of the specified path, or $(D null) if the path is not rooted. - Examples: - --- - assert (rootName("foo") is null); - assert (rootName("/foo") == "/"); + Params: + path = A path name. - version (Windows) - { - assert (rootName(`\foo`) == `\`); - assert (rootName(`c:\foo`) == `c:\`); - assert (rootName(`\\server\share\foo`) == `\\server\share`); - } - --- + Returns: + A slice of $(D path). */ -inout(C)[] rootName(C)(inout(C)[] path) @safe pure nothrow @nogc if (isSomeChar!C) +auto rootName(R)(R path) +if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R) { - if (path.empty) return null; + if (path.empty) + goto Lnull; version (Posix) { @@ -519,50 +686,81 @@ inout(C)[] rootName(C)(inout(C)[] path) @safe pure nothrow @nogc if (isSomeCha return path[0 .. 3]; } } - else static assert (0, "unsupported platform"); + else static assert(0, "unsupported platform"); - assert (!isRooted(path)); - return null; + assert(!isRooted(path)); +Lnull: + static if (is(StringTypeOf!R)) + return null; // legacy code may rely on null return rather than slice + else + return path[0 .. 0]; } - -unittest +/// +@safe unittest { - assert (rootName("") is null); - assert (rootName("foo") is null); - assert (rootName("/") == "/"); - assert (rootName("/foo/bar") == "/"); + assert(rootName("") is null); + assert(rootName("foo") is null); + assert(rootName("/") == "/"); + assert(rootName("/foo/bar") == "/"); version (Windows) { - assert (rootName("d:foo") is null); - assert (rootName(`d:\foo`) == `d:\`); - assert (rootName(`\\server\share\foo`) == `\\server\share`); - assert (rootName(`\\server\share`) == `\\server\share`); + assert(rootName("d:foo") is null); + assert(rootName(`d:\foo`) == `d:\`); + assert(rootName(`\\server\share\foo`) == `\\server\share`); + assert(rootName(`\\server\share`) == `\\server\share`); } } +@safe unittest +{ + assert(testAliasedString!rootName("/foo/bar")); +} +@safe unittest +{ + import std.array; + import std.utf : byChar; + assert(rootName("".byChar).array == ""); + assert(rootName("foo".byChar).array == ""); + assert(rootName("/".byChar).array == "/"); + assert(rootName("/foo/bar".byChar).array == "/"); -/** Returns the drive of a path, or $(D null) if the drive - is not specified. In the case of UNC paths, the network share - is returned. - - Always returns $(D null) on POSIX. - - Examples: - --- version (Windows) { - assert (driveName(`d:\file`) == "d:"); - assert (driveName(`\\server\share\file`) == `\\server\share`); - assert (driveName(`dir\file`).empty); + assert(rootName("d:foo".byChar).array == ""); + assert(rootName(`d:\foo`.byChar).array == `d:\`); + assert(rootName(`\\server\share\foo`.byChar).array == `\\server\share`); + assert(rootName(`\\server\share`.byChar).array == `\\server\share`); } - --- +} + +auto rootName(R)(R path) +if (isConvertibleToString!R) +{ + return rootName!(StringTypeOf!R)(path); +} + + +/** + Get the drive portion of a path. + + Params: + path = string or range of characters + + Returns: + A slice of $(D _path) that is the drive, or an empty range if the drive + is not specified. In the case of UNC paths, the network share + is returned. + + Always returns an empty range on POSIX. */ -inout(C)[] driveName(C)(inout(C)[] path) @safe pure nothrow @nogc - if (isSomeChar!C) +auto driveName(R)(R path) +if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R) { version (Windows) { @@ -571,45 +769,75 @@ inout(C)[] driveName(C)(inout(C)[] path) @safe pure nothrow @nogc else if (isUNC(path)) return path[0 .. uncRootLength(path)]; } - return null; + static if (isSomeString!R) + return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice + else + return path[0 .. 0]; } - -unittest +/// +@safe unittest { - version (Posix) assert (driveName("c:/foo").empty); + import std.range : empty; + version (Posix) assert(driveName("c:/foo").empty); version (Windows) { - assert (driveName(`dir\file`).empty); - assert (driveName(`d:file`) == "d:"); - assert (driveName(`d:\file`) == "d:"); - assert (driveName("d:") == "d:"); - assert (driveName(`\\server\share\file`) == `\\server\share`); - assert (driveName(`\\server\share\`) == `\\server\share`); - assert (driveName(`\\server\share`) == `\\server\share`); + assert(driveName(`dir\file`).empty); + assert(driveName(`d:file`) == "d:"); + assert(driveName(`d:\file`) == "d:"); + assert(driveName("d:") == "d:"); + assert(driveName(`\\server\share\file`) == `\\server\share`); + assert(driveName(`\\server\share\`) == `\\server\share`); + assert(driveName(`\\server\share`) == `\\server\share`); - static assert (driveName(`d:\file`) == "d:"); + static assert(driveName(`d:\file`) == "d:"); } } +auto driveName(R)(auto ref R path) +if (isConvertibleToString!R) +{ + return driveName!(StringTypeOf!R)(path); +} +@safe unittest +{ + assert(testAliasedString!driveName(`d:\file`)); +} +@safe unittest +{ + import std.array; + import std.utf : byChar; -/** Strips the drive from a Windows path. On POSIX, the path is returned - unaltered. - - Example: - --- + version (Posix) assert(driveName("c:/foo".byChar).empty); version (Windows) { - assert (stripDrive(`d:\dir\file`) == `\dir\file`); - assert (stripDrive(`\\server\share\dir\file`) == `\dir\file`); + assert(driveName(`dir\file`.byChar).empty); + assert(driveName(`d:file`.byChar).array == "d:"); + assert(driveName(`d:\file`.byChar).array == "d:"); + assert(driveName("d:".byChar).array == "d:"); + assert(driveName(`\\server\share\file`.byChar).array == `\\server\share`); + assert(driveName(`\\server\share\`.byChar).array == `\\server\share`); + assert(driveName(`\\server\share`.byChar).array == `\\server\share`); + + static assert(driveName(`d:\file`).array == "d:"); } - --- +} + + +/** Strips the drive from a Windows path. On POSIX, the path is returned + unaltered. + + Params: + path = A pathname + + Returns: A slice of path without the drive component. */ -auto stripDrive(R)(inout R path) - if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) +auto stripDrive(R)(R path) +if ((isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R) { version(Windows) { @@ -619,14 +847,34 @@ auto stripDrive(R)(inout R path) return path; } +/// +@safe unittest +{ + version (Windows) + { + assert(stripDrive(`d:\dir\file`) == `\dir\file`); + assert(stripDrive(`\\server\share\dir\file`) == `\dir\file`); + } +} -unittest +auto stripDrive(R)(auto ref R path) +if (isConvertibleToString!R) +{ + return stripDrive!(StringTypeOf!R)(path); +} + +@safe unittest +{ + assert(testAliasedString!stripDrive(`d:\dir\file`)); +} + +@safe unittest { version(Windows) { - assert (stripDrive(`d:\dir\file`) == `\dir\file`); - assert (stripDrive(`\\server\share\dir\file`) == `\dir\file`); - static assert (stripDrive(`d:\dir\file`) == `\dir\file`); + assert(stripDrive(`d:\dir\file`) == `\dir\file`); + assert(stripDrive(`\\server\share\dir\file`) == `\dir\file`); + static assert(stripDrive(`d:\dir\file`) == `\dir\file`); auto r = MockRange!(immutable(char))(`d:\dir\file`); auto s = r.stripDrive(); @@ -635,7 +883,7 @@ unittest } version(Posix) { - assert (stripDrive(`d:\dir\file`) == `d:\dir\file`); + assert(stripDrive(`d:\dir\file`) == `d:\dir\file`); auto r = MockRange!(immutable(char))(`d:\dir\file`); auto s = r.stripDrive(); @@ -645,42 +893,72 @@ unittest } - - /* Helper function that returns the position of the filename/extension - separator dot in path. If not found, returns -1. + separator dot in path. + + Params: + path = file spec as string or indexable range + Returns: + index of extension separator (the dot), or -1 if not found */ private ptrdiff_t extSeparatorPos(R)(const R path) - if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - isNarrowString!R) +if (isRandomAccessRange!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { - auto i = (cast(ptrdiff_t) path.length) - 1; - while (i >= 0 && !isSeparator(path[i])) + for (auto i = path.length; i-- > 0 && !isSeparator(path[i]); ) { - if (path[i] == '.' && i > 0 && !isSeparator(path[i-1])) return i; - --i; + if (path[i] == '.' && i > 0 && !isSeparator(path[i-1])) + return i; } return -1; } +@safe unittest +{ + assert(extSeparatorPos("file") == -1); + assert(extSeparatorPos("file.ext"w) == 4); + assert(extSeparatorPos("file.ext1.ext2"d) == 9); + assert(extSeparatorPos(".foo".dup) == -1); + assert(extSeparatorPos(".foo.ext"w.dup) == 4); +} -/** Returns the _extension part of a file name, including the dot. +@safe unittest +{ + assert(extSeparatorPos("dir/file"d.dup) == -1); + assert(extSeparatorPos("dir/file.ext") == 8); + assert(extSeparatorPos("dir/file.ext1.ext2"w) == 13); + assert(extSeparatorPos("dir/.foo"d) == -1); + assert(extSeparatorPos("dir/.foo.ext".dup) == 8); - If there is no _extension, $(D null) is returned. + version(Windows) + { + assert(extSeparatorPos("dir\\file") == -1); + assert(extSeparatorPos("dir\\file.ext") == 8); + assert(extSeparatorPos("dir\\file.ext1.ext2") == 13); + assert(extSeparatorPos("dir\\.foo") == -1); + assert(extSeparatorPos("dir\\.foo.ext") == 8); - Examples: - --- - assert (extension("file").empty); - assert (extension("file.ext") == ".ext"); - assert (extension("file.ext1.ext2") == ".ext2"); - assert (extension("file.") == "."); - assert (extension(".file").empty); - assert (extension(".file.ext") == ".ext"); - --- + assert(extSeparatorPos("d:file") == -1); + assert(extSeparatorPos("d:file.ext") == 6); + assert(extSeparatorPos("d:file.ext1.ext2") == 11); + assert(extSeparatorPos("d:.foo") == -1); + assert(extSeparatorPos("d:.foo.ext") == 6); + } + + static assert(extSeparatorPos("file") == -1); + static assert(extSeparatorPos("file.ext"w) == 4); +} + + +/** + Params: path = A path name. + Returns: The _extension part of a file name, including the dot. + + If there is no _extension, $(D null) is returned. */ auto extension(R)(R path) - if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) +if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { auto i = extSeparatorPos!(BaseOf!R)(path); if (i == -1) @@ -693,43 +971,23 @@ auto extension(R)(R path) else return path[i .. path.length]; } - -unittest +/// +@safe unittest { - assert (extension("file").empty); - assert (extension("file.") == "."); - assert (extension("file.ext"w) == ".ext"); - assert (extension("file.ext1.ext2"d) == ".ext2"); - assert (extension(".foo".dup).empty); - assert (extension(".foo.ext"w.dup) == ".ext"); - - assert (extension("dir/file"d.dup).empty); - assert (extension("dir/file.") == "."); - assert (extension("dir/file.ext") == ".ext"); - assert (extension("dir/file.ext1.ext2"w) == ".ext2"); - assert (extension("dir/.foo"d).empty); - assert (extension("dir/.foo.ext".dup) == ".ext"); + import std.range : empty; + assert(extension("file").empty); + assert(extension("file.") == "."); + assert(extension("file.ext"w) == ".ext"); + assert(extension("file.ext1.ext2"d) == ".ext2"); + assert(extension(".foo".dup).empty); + assert(extension(".foo.ext"w.dup) == ".ext"); - version(Windows) - { - assert (extension(`dir\file`).empty); - assert (extension(`dir\file.`) == "."); - assert (extension(`dir\file.ext`) == `.ext`); - assert (extension(`dir\file.ext1.ext2`) == `.ext2`); - assert (extension(`dir\.foo`).empty); - assert (extension(`dir\.foo.ext`) == `.ext`); - - assert (extension(`d:file`).empty); - assert (extension(`d:file.`) == "."); - assert (extension(`d:file.ext`) == `.ext`); - assert (extension(`d:file.ext1.ext2`) == `.ext2`); - assert (extension(`d:.foo`).empty); - assert (extension(`d:.foo.ext`) == `.ext`); - } - - static assert (extension("file").empty); - static assert (extension("file.ext") == ".ext"); + static assert(extension("file").empty); + static assert(extension("file.ext") == ".ext"); +} +@safe unittest +{ { auto r = MockRange!(immutable(char))(`file.ext1.ext2`); auto s = r.extension(); @@ -738,75 +996,68 @@ unittest } static struct DirEntry { string s; alias s this; } - assert (extension(DirEntry("file")).empty); + assert(extension(DirEntry("file")).empty); } +/** Remove extension from path. + Params: + path = string or range to be sliced -/** Returns slice of path[] with the extension stripped off. - - Examples: - --- - assert (stripExtension("file") == "file"); - assert (stripExtension("file.ext") == "file"); - assert (stripExtension("file.ext1.ext2") == "file.ext1"); - assert (stripExtension("file.") == "file"); - assert (stripExtension(".file") == ".file"); - assert (stripExtension(".file.ext") == ".file"); - assert (stripExtension("dir/file.ext") == "dir/file"); - --- + Returns: + slice of path with the extension (if any) stripped off */ -inout(C)[] stripExtension(C)(inout(C)[] path) @safe pure nothrow @nogc - if (isSomeChar!C) +auto stripExtension(R)(R path) +if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R) { auto i = extSeparatorPos(path); - if (i == -1) return path; - else return path[0 .. i]; + return (i == -1) ? path : path[0 .. i]; } +/// +@safe unittest +{ + assert(stripExtension("file") == "file"); + assert(stripExtension("file.ext") == "file"); + assert(stripExtension("file.ext1.ext2") == "file.ext1"); + assert(stripExtension("file.") == "file"); + assert(stripExtension(".file") == ".file"); + assert(stripExtension(".file.ext") == ".file"); + assert(stripExtension("dir/file.ext") == "dir/file"); +} -unittest +auto stripExtension(R)(auto ref R path) +if (isConvertibleToString!R) { - assert (stripExtension("file") == "file"); - assert (stripExtension("file.ext"w) == "file"); - assert (stripExtension("file.ext1.ext2"d) == "file.ext1"); - assert (stripExtension(".foo".dup) == ".foo"); - assert (stripExtension(".foo.ext"w.dup) == ".foo"); + return stripExtension!(StringTypeOf!R)(path); +} - assert (stripExtension("dir/file"d.dup) == "dir/file"); - assert (stripExtension("dir/file.ext") == "dir/file"); - assert (stripExtension("dir/file.ext1.ext2"w) == "dir/file.ext1"); - assert (stripExtension("dir/.foo"d) == "dir/.foo"); - assert (stripExtension("dir/.foo.ext".dup) == "dir/.foo"); +@safe unittest +{ + assert(testAliasedString!stripExtension("file")); +} - version(Windows) - { - assert (stripExtension("dir\\file") == "dir\\file"); - assert (stripExtension("dir\\file.ext") == "dir\\file"); - assert (stripExtension("dir\\file.ext1.ext2") == "dir\\file.ext1"); - assert (stripExtension("dir\\.foo") == "dir\\.foo"); - assert (stripExtension("dir\\.foo.ext") == "dir\\.foo"); +@safe unittest +{ + assert(stripExtension("file.ext"w) == "file"); + assert(stripExtension("file.ext1.ext2"d) == "file.ext1"); - assert (stripExtension("d:file") == "d:file"); - assert (stripExtension("d:file.ext") == "d:file"); - assert (stripExtension("d:file.ext1.ext2") == "d:file.ext1"); - assert (stripExtension("d:.foo") == "d:.foo"); - assert (stripExtension("d:.foo.ext") == "d:.foo"); - } + import std.array; + import std.utf : byChar, byWchar, byDchar; - static assert (stripExtension("file") == "file"); - static assert (stripExtension("file.ext"w) == "file"); + assert(stripExtension("file".byChar).array == "file"); + assert(stripExtension("file.ext"w.byWchar).array == "file"); + assert(stripExtension("file.ext1.ext2"d.byDchar).array == "file.ext1"); } +/** Sets or replaces an extension. - -/** Returns a string containing the _path given by $(D path), but where - the extension has been set to $(D ext). - - If the filename already has an extension, it is replaced. If not, the - extension is simply appended to the filename. Including a leading dot + If the filename already has an extension, it is replaced. If not, the + extension is simply appended to the filename. Including a leading dot in $(D ext) is optional. If the extension is empty, this function is equivalent to @@ -816,139 +1067,239 @@ unittest being the case when path is immutable and doesn't already have an extension). - Examples: - --- - assert (setExtension("file", "ext") == "file.ext"); - assert (setExtension("file", ".ext") == "file.ext"); - assert (setExtension("file.old", "") == "file"); - assert (setExtension("file.old", "new") == "file.new"); - assert (setExtension("file.old", ".new") == "file.new"); - --- + Params: + path = A path name + ext = The new extension + + Returns: A string containing the _path given by $(D path), but where + the extension has been set to $(D ext). + + See_Also: + $(LREF withExtension) which does not allocate and returns a lazy range. */ immutable(Unqual!C1)[] setExtension(C1, C2)(in C1[] path, in C2[] ext) - @trusted pure nothrow - if (isSomeChar!C1 && !is(C1 == immutable) && is(Unqual!C1 == Unqual!C2)) +if (isSomeChar!C1 && !is(C1 == immutable) && is(Unqual!C1 == Unqual!C2)) { - if (ext.length > 0 && ext[0] != '.') - return cast(typeof(return))(stripExtension(path)~'.'~ext); - else - return cast(typeof(return))(stripExtension(path)~ext); + try + { + import std.conv : to; + return withExtension(path, ext).to!(typeof(return)); + } + catch (Exception e) + { + assert(0); + } } ///ditto immutable(C1)[] setExtension(C1, C2)(immutable(C1)[] path, const(C2)[] ext) - @trusted pure nothrow - if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2)) +if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2)) { if (ext.length == 0) return stripExtension(path); - // Optimised for the case where path is immutable and has no extension - if (ext.length > 0 && ext[0] == '.') ext = ext[1 .. $]; - auto i = extSeparatorPos(path); - if (i == -1) - { - path ~= '.'; - path ~= ext; - return path; - } - else if (i == path.length - 1) + try { - path ~= ext; - return path; + import std.conv : to; + return withExtension(path, ext).to!(typeof(return)); } - else + catch (Exception e) { - return cast(typeof(return))(path[0 .. i+1] ~ ext); + assert(0); } } - -unittest +/// +@safe unittest { - assert (setExtension("file", "ext") == "file.ext"); - assert (setExtension("file"w, ".ext"w) == "file.ext"); - assert (setExtension("file."d, "ext"d) == "file.ext"); - assert (setExtension("file.", ".ext") == "file.ext"); - assert (setExtension("file.old"w, "new"w) == "file.new"); - assert (setExtension("file.old"d, ".new"d) == "file.new"); + assert(setExtension("file", "ext") == "file.ext"); + assert(setExtension("file"w, ".ext"w) == "file.ext"); + assert(setExtension("file."d, "ext"d) == "file.ext"); + assert(setExtension("file.", ".ext") == "file.ext"); + assert(setExtension("file.old"w, "new"w) == "file.new"); + assert(setExtension("file.old"d, ".new"d) == "file.new"); +} - assert (setExtension("file"w.dup, "ext"w) == "file.ext"); - assert (setExtension("file"w.dup, ".ext"w) == "file.ext"); - assert (setExtension("file."w, "ext"w.dup) == "file.ext"); - assert (setExtension("file."w, ".ext"w.dup) == "file.ext"); - assert (setExtension("file.old"d.dup, "new"d) == "file.new"); - assert (setExtension("file.old"d.dup, ".new"d) == "file.new"); +@safe unittest +{ + assert(setExtension("file"w.dup, "ext"w) == "file.ext"); + assert(setExtension("file"w.dup, ".ext"w) == "file.ext"); + assert(setExtension("file."w, "ext"w.dup) == "file.ext"); + assert(setExtension("file."w, ".ext"w.dup) == "file.ext"); + assert(setExtension("file.old"d.dup, "new"d) == "file.new"); + assert(setExtension("file.old"d.dup, ".new"d) == "file.new"); - static assert (setExtension("file", "ext") == "file.ext"); - static assert (setExtension("file.old", "new") == "file.new"); + static assert(setExtension("file", "ext") == "file.ext"); + static assert(setExtension("file.old", "new") == "file.new"); - static assert (setExtension("file"w.dup, "ext"w) == "file.ext"); - static assert (setExtension("file.old"d.dup, "new"d) == "file.new"); + static assert(setExtension("file"w.dup, "ext"w) == "file.ext"); + static assert(setExtension("file.old"d.dup, "new"d) == "file.new"); // Issue 10601 - assert (setExtension("file", "") == "file"); - assert (setExtension("file.ext", "") == "file"); + assert(setExtension("file", "") == "file"); + assert(setExtension("file.ext", "") == "file"); +} + +/************ + * Replace existing extension on filespec with new one. + * + * Params: + * path = string or random access range representing a filespec + * ext = the new extension + * Returns: + * Range with $(D path)'s extension (if any) replaced with $(D ext). + * The element encoding type of the returned range will be the same as $(D path)'s. + * See_Also: + * $(LREF setExtension) + */ +auto withExtension(R, C)(R path, C[] ext) +if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R && + isSomeChar!C) +{ + import std.range : only, chain; + import std.utf : byUTF; + + alias CR = Unqual!(ElementEncodingType!R); + auto dot = only(CR('.')); + if (ext.length == 0 || ext[0] == '.') + dot.popFront(); // so dot is an empty range, too + return chain(stripExtension(path).byUTF!CR, dot, ext.byUTF!CR); +} + +/// +@safe unittest +{ + import std.array; + assert(withExtension("file", "ext").array == "file.ext"); + assert(withExtension("file"w, ".ext"w).array == "file.ext"); + assert(withExtension("file.ext"w, ".").array == "file."); + + import std.utf : byChar, byWchar; + assert(withExtension("file".byChar, "ext").array == "file.ext"); + assert(withExtension("file"w.byWchar, ".ext"w).array == "file.ext"w); + assert(withExtension("file.ext"w.byWchar, ".").array == "file."w); +} + +auto withExtension(R, C)(auto ref R path, C[] ext) +if (isConvertibleToString!R) +{ + return withExtension!(StringTypeOf!R)(path, ext); } +@safe unittest +{ + assert(testAliasedString!withExtension("file", "ext")); +} +/** Params: + path = A path name. + ext = The default extension to use. -/** Returns the _path given by $(D path), with the extension given by - $(D ext) appended if the path doesn't already have one. + Returns: The _path given by $(D path), with the extension given by $(D ext) + appended if the path doesn't already have one. Including the dot in the extension is optional. This function always allocates a new string, except in the case when path is immutable and already has an extension. - - Examples: - --- - assert (defaultExtension("file", "ext") == "file.ext"); - assert (defaultExtension("file", ".ext") == "file.ext"); - assert (defaultExtension("file.", "ext") == "file."); - assert (defaultExtension("file.old", "new") == "file.old"); - assert (defaultExtension("file.old", ".new") == "file.old"); - --- */ immutable(Unqual!C1)[] defaultExtension(C1, C2)(in C1[] path, in C2[] ext) - @trusted pure // TODO: nothrow (because of to()) - if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2)) +if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2)) { import std.conv : to; - auto i = extSeparatorPos(path); - if (i == -1) - { - if (ext.length > 0 && ext[0] == '.') - return cast(typeof(return))(path~ext); - else - return cast(typeof(return))(path~'.'~ext); - } - else return to!(typeof(return))(path); + return withDefaultExtension(path, ext).to!(typeof(return)); } - -unittest +/// +@safe unittest { - assert (defaultExtension("file", "ext") == "file.ext"); - assert (defaultExtension("file", ".ext") == "file.ext"); - assert (defaultExtension("file.", "ext") == "file."); - assert (defaultExtension("file.old", "new") == "file.old"); - assert (defaultExtension("file.old", ".new") == "file.old"); + assert(defaultExtension("file", "ext") == "file.ext"); + assert(defaultExtension("file", ".ext") == "file.ext"); + assert(defaultExtension("file.", "ext") == "file."); + assert(defaultExtension("file.old", "new") == "file.old"); + assert(defaultExtension("file.old", ".new") == "file.old"); +} - assert (defaultExtension("file"w.dup, "ext"w) == "file.ext"); - assert (defaultExtension("file.old"d.dup, "new"d) == "file.old"); +@safe unittest +{ + assert(defaultExtension("file"w.dup, "ext"w) == "file.ext"); + assert(defaultExtension("file.old"d.dup, "new"d) == "file.old"); - static assert (defaultExtension("file", "ext") == "file.ext"); - static assert (defaultExtension("file.old", "new") == "file.old"); + static assert(defaultExtension("file", "ext") == "file.ext"); + static assert(defaultExtension("file.old", "new") == "file.old"); - static assert (defaultExtension("file"w.dup, "ext"w) == "file.ext"); - static assert (defaultExtension("file.old"d.dup, "new"d) == "file.old"); + static assert(defaultExtension("file"w.dup, "ext"w) == "file.ext"); + static assert(defaultExtension("file.old"d.dup, "new"d) == "file.old"); } -/** Combines one or more path segments. +/******************************** + * Set the extension of $(D path) to $(D ext) if $(D path) doesn't have one. + * + * Params: + * path = filespec as string or range + * ext = extension, may have leading '.' + * Returns: + * range with the result + */ +auto withDefaultExtension(R, C)(R path, C[] ext) +if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R && + isSomeChar!C) +{ + import std.range : only, chain; + import std.utf : byUTF; - This function takes a set of path segments, given as an input + alias CR = Unqual!(ElementEncodingType!R); + auto dot = only(CR('.')); + auto i = extSeparatorPos(path); + if (i == -1) + { + if (ext.length > 0 && ext[0] == '.') + ext = ext[1 .. $]; // remove any leading . from ext[] + } + else + { + // path already has an extension, so make these empty + ext = ext[0 .. 0]; + dot.popFront(); + } + return chain(path.byUTF!CR, dot, ext.byUTF!CR); +} + +/// +@safe unittest +{ + import std.array; + assert(withDefaultExtension("file", "ext").array == "file.ext"); + assert(withDefaultExtension("file"w, ".ext").array == "file.ext"w); + assert(withDefaultExtension("file.", "ext").array == "file."); + assert(withDefaultExtension("file", "").array == "file."); + + import std.utf : byChar, byWchar; + assert(withDefaultExtension("file".byChar, "ext").array == "file.ext"); + assert(withDefaultExtension("file"w.byWchar, ".ext").array == "file.ext"w); + assert(withDefaultExtension("file.".byChar, "ext"d).array == "file."); + assert(withDefaultExtension("file".byChar, "").array == "file."); +} + +auto withDefaultExtension(R, C)(auto ref R path, C[] ext) +if (isConvertibleToString!R) +{ + return withDefaultExtension!(StringTypeOf!R, C)(path, ext); +} + +@safe unittest +{ + assert(testAliasedString!withDefaultExtension("file", "ext")); +} + +/** Combines one or more path segments. + + This function takes a set of path segments, given as an input range of string elements or as a set of string arguments, and concatenates them with each other. Directory separators are inserted between segments if necessary. If any of the @@ -963,10 +1314,14 @@ unittest The variadic overload is guaranteed to only perform a single allocation, as is the range version if $(D paths) is a forward range. + + Params: + segments = An input range of segments to assemble the path from. + Returns: The assembled path. */ immutable(ElementEncodingType!(ElementType!Range))[] buildPath(Range)(Range segments) - if (isInputRange!Range && isSomeString!(ElementType!Range)) + if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range)) { if (segments.empty) return null; @@ -991,64 +1346,48 @@ immutable(ElementEncodingType!(ElementType!Range))[] if (buf.length < neededLength) buf.length = reserve(buf, neededLength + buf.length/2); } - if (pos > 0) + auto r = chainPath(buf[0 .. pos], segment); + size_t i; + foreach (c; r) { - if (isRooted(segment)) - { - version (Posix) - { - pos = 0; - } - else version (Windows) - { - if (isAbsolute(segment)) - pos = 0; - else - { - pos = rootName(buf[0 .. pos]).length; - if (pos > 0 && isDirSeparator(buf[pos-1])) --pos; - } - } - } - else if (!isDirSeparator(buf[pos-1])) - buf[pos++] = dirSeparator[0]; + buf[i] = c; + ++i; } - buf[pos .. pos + segment.length] = segment[]; - pos += segment.length; + pos = i; } static U trustedCast(U, V)(V v) @trusted pure nothrow { return cast(U) v; } return trustedCast!(typeof(return))(buf[0 .. pos]); } /// ditto -immutable(C)[] buildPath(C)(const(C[])[] paths...) +immutable(C)[] buildPath(C)(const(C)[][] paths...) @safe pure nothrow - if (isSomeChar!C) +if (isSomeChar!C) { return buildPath!(typeof(paths))(paths); } /// -unittest +@safe unittest { version (Posix) { - assert (buildPath("foo", "bar", "baz") == "foo/bar/baz"); - assert (buildPath("/foo/", "bar/baz") == "/foo/bar/baz"); - assert (buildPath("/foo", "/bar") == "/bar"); + assert(buildPath("foo", "bar", "baz") == "foo/bar/baz"); + assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz"); + assert(buildPath("/foo", "/bar") == "/bar"); } version (Windows) { - assert (buildPath("foo", "bar", "baz") == `foo\bar\baz`); - assert (buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); - assert (buildPath("foo", `d:\bar`) == `d:\bar`); - assert (buildPath("foo", `\bar`) == `\bar`); - assert (buildPath(`c:\foo`, `\bar`) == `c:\bar`); + assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`); + assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); + assert(buildPath("foo", `d:\bar`) == `d:\bar`); + assert(buildPath("foo", `\bar`) == `\bar`); + assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`); } } -unittest // non-documented +@system unittest // non-documented { import std.range; // ir() wraps an array in a plain (i.e. non-forward) input range, so that @@ -1056,92 +1395,218 @@ unittest // non-documented InputRange!(C[]) ir(C)(C[][] p...) { return inputRangeObject(p); } version (Posix) { - assert (buildPath("foo") == "foo"); - assert (buildPath("/foo/") == "/foo/"); - assert (buildPath("foo", "bar") == "foo/bar"); - assert (buildPath("foo", "bar", "baz") == "foo/bar/baz"); - assert (buildPath("foo/".dup, "bar") == "foo/bar"); - assert (buildPath("foo///", "bar".dup) == "foo///bar"); - assert (buildPath("/foo"w, "bar"w) == "/foo/bar"); - assert (buildPath("foo"w.dup, "/bar"w) == "/bar"); - assert (buildPath("foo"w, "bar/"w.dup) == "foo/bar/"); - assert (buildPath("/"d, "foo"d) == "/foo"); - assert (buildPath(""d.dup, "foo"d) == "foo"); - assert (buildPath("foo"d, ""d.dup) == "foo"); - assert (buildPath("foo", "bar".dup, "baz") == "foo/bar/baz"); - assert (buildPath("foo"w, "/bar"w, "baz"w.dup) == "/bar/baz"); - - static assert (buildPath("foo", "bar", "baz") == "foo/bar/baz"); - static assert (buildPath("foo", "/bar", "baz") == "/bar/baz"); + assert(buildPath("foo") == "foo"); + assert(buildPath("/foo/") == "/foo/"); + assert(buildPath("foo", "bar") == "foo/bar"); + assert(buildPath("foo", "bar", "baz") == "foo/bar/baz"); + assert(buildPath("foo/".dup, "bar") == "foo/bar"); + assert(buildPath("foo///", "bar".dup) == "foo///bar"); + assert(buildPath("/foo"w, "bar"w) == "/foo/bar"); + assert(buildPath("foo"w.dup, "/bar"w) == "/bar"); + assert(buildPath("foo"w, "bar/"w.dup) == "foo/bar/"); + assert(buildPath("/"d, "foo"d) == "/foo"); + assert(buildPath(""d.dup, "foo"d) == "foo"); + assert(buildPath("foo"d, ""d.dup) == "foo"); + assert(buildPath("foo", "bar".dup, "baz") == "foo/bar/baz"); + assert(buildPath("foo"w, "/bar"w, "baz"w.dup) == "/bar/baz"); + + static assert(buildPath("foo", "bar", "baz") == "foo/bar/baz"); + static assert(buildPath("foo", "/bar", "baz") == "/bar/baz"); // The following are mostly duplicates of the above, except that the // range version does not accept mixed constness. - assert (buildPath(ir("foo")) == "foo"); - assert (buildPath(ir("/foo/")) == "/foo/"); - assert (buildPath(ir("foo", "bar")) == "foo/bar"); - assert (buildPath(ir("foo", "bar", "baz")) == "foo/bar/baz"); - assert (buildPath(ir("foo/".dup, "bar".dup)) == "foo/bar"); - assert (buildPath(ir("foo///".dup, "bar".dup)) == "foo///bar"); - assert (buildPath(ir("/foo"w, "bar"w)) == "/foo/bar"); - assert (buildPath(ir("foo"w.dup, "/bar"w.dup)) == "/bar"); - assert (buildPath(ir("foo"w.dup, "bar/"w.dup)) == "foo/bar/"); - assert (buildPath(ir("/"d, "foo"d)) == "/foo"); - assert (buildPath(ir(""d.dup, "foo"d.dup)) == "foo"); - assert (buildPath(ir("foo"d, ""d)) == "foo"); - assert (buildPath(ir("foo", "bar", "baz")) == "foo/bar/baz"); - assert (buildPath(ir("foo"w.dup, "/bar"w.dup, "baz"w.dup)) == "/bar/baz"); + assert(buildPath(ir("foo")) == "foo"); + assert(buildPath(ir("/foo/")) == "/foo/"); + assert(buildPath(ir("foo", "bar")) == "foo/bar"); + assert(buildPath(ir("foo", "bar", "baz")) == "foo/bar/baz"); + assert(buildPath(ir("foo/".dup, "bar".dup)) == "foo/bar"); + assert(buildPath(ir("foo///".dup, "bar".dup)) == "foo///bar"); + assert(buildPath(ir("/foo"w, "bar"w)) == "/foo/bar"); + assert(buildPath(ir("foo"w.dup, "/bar"w.dup)) == "/bar"); + assert(buildPath(ir("foo"w.dup, "bar/"w.dup)) == "foo/bar/"); + assert(buildPath(ir("/"d, "foo"d)) == "/foo"); + assert(buildPath(ir(""d.dup, "foo"d.dup)) == "foo"); + assert(buildPath(ir("foo"d, ""d)) == "foo"); + assert(buildPath(ir("foo", "bar", "baz")) == "foo/bar/baz"); + assert(buildPath(ir("foo"w.dup, "/bar"w.dup, "baz"w.dup)) == "/bar/baz"); } version (Windows) { - assert (buildPath("foo") == "foo"); - assert (buildPath(`\foo/`) == `\foo/`); - assert (buildPath("foo", "bar", "baz") == `foo\bar\baz`); - assert (buildPath("foo", `\bar`) == `\bar`); - assert (buildPath(`c:\foo`, "bar") == `c:\foo\bar`); - assert (buildPath("foo"w, `d:\bar`w.dup) == `d:\bar`); - assert (buildPath(`c:\foo\bar`, `\baz`) == `c:\baz`); - assert (buildPath(`\\foo\bar\baz`d, `foo`d, `\bar`d) == `\\foo\bar\bar`d); + assert(buildPath("foo") == "foo"); + assert(buildPath(`\foo/`) == `\foo/`); + assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`); + assert(buildPath("foo", `\bar`) == `\bar`); + assert(buildPath(`c:\foo`, "bar") == `c:\foo\bar`); + assert(buildPath("foo"w, `d:\bar`w.dup) == `d:\bar`); + assert(buildPath(`c:\foo\bar`, `\baz`) == `c:\baz`); + assert(buildPath(`\\foo\bar\baz`d, `foo`d, `\bar`d) == `\\foo\bar\bar`d); - static assert (buildPath("foo", "bar", "baz") == `foo\bar\baz`); - static assert (buildPath("foo", `c:\bar`, "baz") == `c:\bar\baz`); + static assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`); + static assert(buildPath("foo", `c:\bar`, "baz") == `c:\bar\baz`); - assert (buildPath(ir("foo")) == "foo"); - assert (buildPath(ir(`\foo/`)) == `\foo/`); - assert (buildPath(ir("foo", "bar", "baz")) == `foo\bar\baz`); - assert (buildPath(ir("foo", `\bar`)) == `\bar`); - assert (buildPath(ir(`c:\foo`, "bar")) == `c:\foo\bar`); - assert (buildPath(ir("foo"w.dup, `d:\bar`w.dup)) == `d:\bar`); - assert (buildPath(ir(`c:\foo\bar`, `\baz`)) == `c:\baz`); - assert (buildPath(ir(`\\foo\bar\baz`d, `foo`d, `\bar`d)) == `\\foo\bar\bar`d); + assert(buildPath(ir("foo")) == "foo"); + assert(buildPath(ir(`\foo/`)) == `\foo/`); + assert(buildPath(ir("foo", "bar", "baz")) == `foo\bar\baz`); + assert(buildPath(ir("foo", `\bar`)) == `\bar`); + assert(buildPath(ir(`c:\foo`, "bar")) == `c:\foo\bar`); + assert(buildPath(ir("foo"w.dup, `d:\bar`w.dup)) == `d:\bar`); + assert(buildPath(ir(`c:\foo\bar`, `\baz`)) == `c:\baz`); + assert(buildPath(ir(`\\foo\bar\baz`d, `foo`d, `\bar`d)) == `\\foo\bar\bar`d); } // Test that allocation works as it should. auto manyShort = "aaa".repeat(1000).array(); auto manyShortCombined = join(manyShort, dirSeparator); - assert (buildPath(manyShort) == manyShortCombined); - assert (buildPath(ir(manyShort)) == manyShortCombined); + assert(buildPath(manyShort) == manyShortCombined); + assert(buildPath(ir(manyShort)) == manyShortCombined); auto fewLong = 'b'.repeat(500).array().repeat(10).array(); auto fewLongCombined = join(fewLong, dirSeparator); - assert (buildPath(fewLong) == fewLongCombined); - assert (buildPath(ir(fewLong)) == fewLongCombined); + assert(buildPath(fewLong) == fewLongCombined); + assert(buildPath(ir(fewLong)) == fewLongCombined); } -unittest +@safe unittest { // Test for issue 7397 string[] ary = ["a", "b"]; version (Posix) { - assert (buildPath(ary) == "a/b"); + assert(buildPath(ary) == "a/b"); } else version (Windows) { - assert (buildPath(ary) == `a\b`); + assert(buildPath(ary) == `a\b`); } } +/** + * Concatenate path segments together to form one path. + * + * Params: + * r1 = first segment + * r2 = second segment + * ranges = 0 or more segments + * Returns: + * Lazy range which is the concatenation of r1, r2 and ranges with path separators. + * The resulting element type is that of r1. + * See_Also: + * $(LREF buildPath) + */ +auto chainPath(R1, R2, Ranges...)(R1 r1, R2 r2, Ranges ranges) +if ((isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && isSomeChar!(ElementType!R1) || + isNarrowString!R1 && + !isConvertibleToString!R1) && + (isRandomAccessRange!R2 && hasSlicing!R2 && hasLength!R2 && isSomeChar!(ElementType!R2) || + isNarrowString!R2 && + !isConvertibleToString!R2) && + (Ranges.length == 0 || is(typeof(chainPath(r2, ranges)))) + ) +{ + static if (Ranges.length) + { + return chainPath(chainPath(r1, r2), ranges); + } + else + { + import std.range : only, chain; + import std.utf : byUTF; + + alias CR = Unqual!(ElementEncodingType!R1); + auto sep = only(CR(dirSeparator[0])); + bool usesep = false; + + auto pos = r1.length; + + if (pos) + { + if (isRooted(r2)) + { + version (Posix) + { + pos = 0; + } + else version (Windows) + { + if (isAbsolute(r2)) + pos = 0; + else + { + pos = rootName(r1).length; + if (pos > 0 && isDirSeparator(r1[pos - 1])) + --pos; + } + } + else + static assert(0); + } + else if (!isDirSeparator(r1[pos - 1])) + usesep = true; + } + if (!usesep) + sep.popFront(); + // Return r1 ~ '/' ~ r2 + return chain(r1[0 .. pos].byUTF!CR, sep, r2.byUTF!CR); + } +} + +/// +@safe unittest +{ + import std.array; + version (Posix) + { + assert(chainPath("foo", "bar", "baz").array == "foo/bar/baz"); + assert(chainPath("/foo/", "bar/baz").array == "/foo/bar/baz"); + assert(chainPath("/foo", "/bar").array == "/bar"); + } + + version (Windows) + { + assert(chainPath("foo", "bar", "baz").array == `foo\bar\baz`); + assert(chainPath(`c:\foo`, `bar\baz`).array == `c:\foo\bar\baz`); + assert(chainPath("foo", `d:\bar`).array == `d:\bar`); + assert(chainPath("foo", `\bar`).array == `\bar`); + assert(chainPath(`c:\foo`, `\bar`).array == `c:\bar`); + } + + import std.utf : byChar; + version (Posix) + { + assert(chainPath("foo", "bar", "baz").array == "foo/bar/baz"); + assert(chainPath("/foo/".byChar, "bar/baz").array == "/foo/bar/baz"); + assert(chainPath("/foo", "/bar".byChar).array == "/bar"); + } + + version (Windows) + { + assert(chainPath("foo", "bar", "baz").array == `foo\bar\baz`); + assert(chainPath(`c:\foo`.byChar, `bar\baz`).array == `c:\foo\bar\baz`); + assert(chainPath("foo", `d:\bar`).array == `d:\bar`); + assert(chainPath("foo", `\bar`.byChar).array == `\bar`); + assert(chainPath(`c:\foo`, `\bar`w).array == `c:\bar`); + } +} + +auto chainPath(Ranges...)(auto ref Ranges ranges) +if (Ranges.length >= 2 && + std.meta.anySatisfy!(isConvertibleToString, Ranges)) +{ + import std.meta : staticMap; + alias Types = staticMap!(convertToString, Ranges); + return chainPath!Types(ranges); +} + +@safe unittest +{ + assert(chainPath(TestAliasedString(null), TestAliasedString(null), TestAliasedString(null)).empty); + assert(chainPath(TestAliasedString(null), TestAliasedString(null), "").empty); + assert(chainPath(TestAliasedString(null), "", TestAliasedString(null)).empty); + static struct S { string s; } + static assert(!__traits(compiles, chainPath(TestAliasedString(null), S(""), TestAliasedString(null)))); +} + /** Performs the same task as $(LREF buildPath), while at the same time resolving current/parent directory symbols ($(D ".") and $(D "..")) and removing superfluous @@ -1154,602 +1619,665 @@ unittest Note that this function does not resolve symbolic links. This function always allocates memory to hold the resulting path. + Use $(LREF asNormalizedPath) to not allocate memory. - Examples: - --- - assert (buildNormalizedPath("foo", "..") == "."); + Params: + paths = An array of paths to assemble. + + Returns: The assembled path. +*/ +immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...) + @trusted pure nothrow +if (isSomeChar!C) +{ + import std.array : array; + + const(C)[] result; + foreach (path; paths) + { + if (result) + result = chainPath(result, path).array; + else + result = path; + } + result = asNormalizedPath(result).array; + return cast(typeof(return)) result; +} + +/// +@safe unittest +{ + assert(buildNormalizedPath("foo", "..") == "."); version (Posix) { - assert (buildNormalizedPath("/foo/./bar/..//baz/") == "/foo/baz"); - assert (buildNormalizedPath("../foo/.") == "../foo"); - assert (buildNormalizedPath("/foo", "bar/baz/") == "/foo/bar/baz"); - assert (buildNormalizedPath("/foo", "/bar/..", "baz") == "/baz"); - assert (buildNormalizedPath("foo/./bar", "../../", "../baz") == "../baz"); - assert (buildNormalizedPath("/foo/./bar", "../../baz") == "/baz"); + assert(buildNormalizedPath("/foo/./bar/..//baz/") == "/foo/baz"); + assert(buildNormalizedPath("../foo/.") == "../foo"); + assert(buildNormalizedPath("/foo", "bar/baz/") == "/foo/bar/baz"); + assert(buildNormalizedPath("/foo", "/bar/..", "baz") == "/baz"); + assert(buildNormalizedPath("foo/./bar", "../../", "../baz") == "../baz"); + assert(buildNormalizedPath("/foo/./bar", "../../baz") == "/baz"); } version (Windows) { - assert (buildNormalizedPath(`c:\foo\.\bar/..\\baz\`) == `c:\foo\baz`); - assert (buildNormalizedPath(`..\foo\.`) == `..\foo`); - assert (buildNormalizedPath(`c:\foo`, `bar\baz\`) == `c:\foo\bar\baz`); - assert (buildNormalizedPath(`c:\foo`, `bar/..`) == `c:\foo`); - assert (buildNormalizedPath(`\\server\share\foo`, `..\bar`) == `\\server\share\bar`); + assert(buildNormalizedPath(`c:\foo\.\bar/..\\baz\`) == `c:\foo\baz`); + assert(buildNormalizedPath(`..\foo\.`) == `..\foo`); + assert(buildNormalizedPath(`c:\foo`, `bar\baz\`) == `c:\foo\bar\baz`); + assert(buildNormalizedPath(`c:\foo`, `bar/..`) == `c:\foo`); + assert(buildNormalizedPath(`\\server\share\foo`, `..\bar`) == + `\\server\share\bar`); } - --- -*/ -immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...) - @trusted pure nothrow - if (isSomeChar!C) +} + +@safe unittest { - import core.stdc.stdlib; + assert(buildNormalizedPath(".", ".") == "."); + assert(buildNormalizedPath("foo", "..") == "."); + assert(buildNormalizedPath("", "") is null); + assert(buildNormalizedPath("", ".") == "."); + assert(buildNormalizedPath(".", "") == "."); + assert(buildNormalizedPath(null, "foo") == "foo"); + assert(buildNormalizedPath("", "foo") == "foo"); + assert(buildNormalizedPath("", "") == ""); + assert(buildNormalizedPath("", null) == ""); + assert(buildNormalizedPath(null, "") == ""); + assert(buildNormalizedPath!(char)(null, null) == ""); - //Remove empty fields - bool allEmpty = true; - foreach (ref const(C[]) path ; paths) + version (Posix) { - if (path !is null) - { - allEmpty = false; - break; - } + assert(buildNormalizedPath("/", "foo", "bar") == "/foo/bar"); + assert(buildNormalizedPath("foo", "bar", "baz") == "foo/bar/baz"); + assert(buildNormalizedPath("foo", "bar/baz") == "foo/bar/baz"); + assert(buildNormalizedPath("foo", "bar//baz///") == "foo/bar/baz"); + assert(buildNormalizedPath("/foo", "bar/baz") == "/foo/bar/baz"); + assert(buildNormalizedPath("/foo", "/bar/baz") == "/bar/baz"); + assert(buildNormalizedPath("/foo/..", "/bar/./baz") == "/bar/baz"); + assert(buildNormalizedPath("/foo/..", "bar/baz") == "/bar/baz"); + assert(buildNormalizedPath("/foo/../../", "bar/baz") == "/bar/baz"); + assert(buildNormalizedPath("/foo/bar", "../baz") == "/foo/baz"); + assert(buildNormalizedPath("/foo/bar", "../../baz") == "/baz"); + assert(buildNormalizedPath("/foo/bar", ".././/baz/..", "wee/") == "/foo/wee"); + assert(buildNormalizedPath("//foo/bar", "baz///wee") == "/foo/bar/baz/wee"); + static assert(buildNormalizedPath("/foo/..", "/bar/./baz") == "/bar/baz"); } - if (allEmpty) return null; + else version (Windows) + { + assert(buildNormalizedPath(`\`, `foo`, `bar`) == `\foo\bar`); + assert(buildNormalizedPath(`foo`, `bar`, `baz`) == `foo\bar\baz`); + assert(buildNormalizedPath(`foo`, `bar\baz`) == `foo\bar\baz`); + assert(buildNormalizedPath(`foo`, `bar\\baz\\\`) == `foo\bar\baz`); + assert(buildNormalizedPath(`\foo`, `bar\baz`) == `\foo\bar\baz`); + assert(buildNormalizedPath(`\foo`, `\bar\baz`) == `\bar\baz`); + assert(buildNormalizedPath(`\foo\..`, `\bar\.\baz`) == `\bar\baz`); + assert(buildNormalizedPath(`\foo\..`, `bar\baz`) == `\bar\baz`); + assert(buildNormalizedPath(`\foo\..\..\`, `bar\baz`) == `\bar\baz`); + assert(buildNormalizedPath(`\foo\bar`, `..\baz`) == `\foo\baz`); + assert(buildNormalizedPath(`\foo\bar`, `../../baz`) == `\baz`); + assert(buildNormalizedPath(`\foo\bar`, `..\.\/baz\..`, `wee\`) == `\foo\wee`); + + assert(buildNormalizedPath(`c:\`, `foo`, `bar`) == `c:\foo\bar`); + assert(buildNormalizedPath(`c:foo`, `bar`, `baz`) == `c:foo\bar\baz`); + assert(buildNormalizedPath(`c:foo`, `bar\baz`) == `c:foo\bar\baz`); + assert(buildNormalizedPath(`c:foo`, `bar\\baz\\\`) == `c:foo\bar\baz`); + assert(buildNormalizedPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); + assert(buildNormalizedPath(`c:\foo`, `\bar\baz`) == `c:\bar\baz`); + assert(buildNormalizedPath(`c:\foo\..`, `\bar\.\baz`) == `c:\bar\baz`); + assert(buildNormalizedPath(`c:\foo\..`, `bar\baz`) == `c:\bar\baz`); + assert(buildNormalizedPath(`c:\foo\..\..\`, `bar\baz`) == `c:\bar\baz`); + assert(buildNormalizedPath(`c:\foo\bar`, `..\baz`) == `c:\foo\baz`); + assert(buildNormalizedPath(`c:\foo\bar`, `..\..\baz`) == `c:\baz`); + assert(buildNormalizedPath(`c:\foo\bar`, `..\.\\baz\..`, `wee\`) == `c:\foo\wee`); + + assert(buildNormalizedPath(`\\server\share`, `foo`, `bar`) == `\\server\share\foo\bar`); + assert(buildNormalizedPath(`\\server\share\`, `foo`, `bar`) == `\\server\share\foo\bar`); + assert(buildNormalizedPath(`\\server\share\foo`, `bar\baz`) == `\\server\share\foo\bar\baz`); + assert(buildNormalizedPath(`\\server\share\foo`, `\bar\baz`) == `\\server\share\bar\baz`); + assert(buildNormalizedPath(`\\server\share\foo\..`, `\bar\.\baz`) == `\\server\share\bar\baz`); + assert(buildNormalizedPath(`\\server\share\foo\..`, `bar\baz`) == `\\server\share\bar\baz`); + assert(buildNormalizedPath(`\\server\share\foo\..\..\`, `bar\baz`) == `\\server\share\bar\baz`); + assert(buildNormalizedPath(`\\server\share\foo\bar`, `..\baz`) == `\\server\share\foo\baz`); + assert(buildNormalizedPath(`\\server\share\foo\bar`, `..\..\baz`) == `\\server\share\baz`); + assert(buildNormalizedPath(`\\server\share\foo\bar`, `..\.\\baz\..`, `wee\`) == `\\server\share\foo\wee`); + + static assert(buildNormalizedPath(`\foo\..\..\`, `bar\baz`) == `\bar\baz`); + } + else static assert(0); +} + +@safe unittest +{ + // Test for issue 7397 + string[] ary = ["a", "b"]; + version (Posix) + { + assert(buildNormalizedPath(ary) == "a/b"); + } + else version (Windows) + { + assert(buildNormalizedPath(ary) == `a\b`); + } +} + + +/** Normalize a path by resolving current/parent directory + symbols ($(D ".") and $(D "..")) and removing superfluous + directory separators. + It will return "." if the path leads to the starting directory. + On Windows, slashes are replaced with backslashes. + + Using asNormalizedPath on empty paths will always return an empty path. + + Does not resolve symbolic links. + + This function always allocates memory to hold the resulting path. + Use $(LREF buildNormalizedPath) to allocate memory and return a string. + + Params: + path = string or random access range representing the _path to normalize - auto paths2 = new const(C)[][](paths.length); - //(cast(const(C)[]*)alloca((const(C)[]).sizeof * paths.length))[0 .. paths.length]; + Returns: + normalized path as a forward range +*/ + +auto asNormalizedPath(R)(R path) +if (isSomeChar!(ElementEncodingType!R) && + (isRandomAccessRange!R && hasSlicing!R && hasLength!R || isNarrowString!R) && + !isConvertibleToString!R) +{ + alias C = Unqual!(ElementEncodingType!R); + alias S = typeof(path[0 .. 0]); - // Check whether the resulting path will be absolute or rooted, - // calculate its maximum length, and discard segments we won't use. - typeof(paths[0][0])[] rootElement; - int numPaths = 0; - bool seenAbsolute; - size_t segmentLengthSum = 0; - foreach (i; 0 .. paths.length) + static struct Result { - auto p = paths[i]; - if (p.empty) continue; - else if (isRooted(p)) + @property bool empty() { - immutable thisIsAbsolute = isAbsolute(p); - if (thisIsAbsolute || !seenAbsolute) - { - if (thisIsAbsolute) seenAbsolute = true; - rootElement = rootName(p); - paths2[0] = p[rootElement.length .. $]; - numPaths = 1; - segmentLengthSum = paths2[0].length; - } - else - { - paths2[0] = p; - numPaths = 1; - segmentLengthSum = p.length; - } - } - else - { - paths2[numPaths++] = p; - segmentLengthSum += p.length; + return c == c.init; } - } - if (rootElement.length + segmentLengthSum == 0) return null; - paths2 = paths2[0 .. numPaths]; - immutable rooted = !rootElement.empty; - assert (rooted || !seenAbsolute); // absolute => rooted - - // Allocate memory for the resulting path, including room for - // extra dir separators - auto fullPath = new C[rootElement.length + segmentLengthSum + paths2.length]; - // Copy the root element into fullPath, and let relPart be - // the remaining slice. - typeof(fullPath) relPart; - if (rooted) - { - // For Windows, we also need to perform normalization on - // the root element. - version (Posix) + @property C front() { - fullPath[0 .. rootElement.length] = rootElement[]; + return c; } - else version (Windows) + + void popFront() { - foreach (i, c; rootElement) + C lastc = c; + c = c.init; + if (!element.empty) + { + c = getElement0(); + return; + } + L1: + while (1) { - if (isDirSeparator(c)) + if (elements.empty) + { + element = element[0 .. 0]; + return; + } + element = elements.front; + elements.popFront(); + if (isDot(element) || (rooted && isDotDot(element))) + continue; + + if (rooted || !isDotDot(element)) { - static assert (dirSeparator.length == 1); - fullPath[i] = dirSeparator[0]; + int n = 1; + auto elements2 = elements.save; + while (!elements2.empty) + { + auto e = elements2.front; + elements2.popFront(); + if (isDot(e)) + continue; + if (isDotDot(e)) + { + --n; + if (n == 0) + { + elements = elements2; + element = element[0 .. 0]; + continue L1; + } + } + else + ++n; + } } - else fullPath[i] = c; + break; } + + static assert(dirSeparator.length == 1); + if (lastc == dirSeparator[0] || lastc == lastc.init) + c = getElement0(); + else + c = dirSeparator[0]; } - else static assert (0); - // If the root element doesn't end with a dir separator, - // we add one. - if (!isDirSeparator(rootElement[$-1])) + static if (isForwardRange!R) { - static assert (dirSeparator.length == 1); - fullPath[rootElement.length] = dirSeparator[0]; - relPart = fullPath[rootElement.length + 1 .. $]; + @property auto save() + { + auto result = this; + result.element = element.save; + result.elements = elements.save; + return result; + } } - else + + private: + this(R path) { - relPart = fullPath[rootElement.length .. $]; + element = rootName(path); + auto i = element.length; + while (i < path.length && isDirSeparator(path[i])) + ++i; + rooted = i > 0; + elements = pathSplitter(path[i .. $]); + popFront(); + if (c == c.init && path.length) + c = C('.'); } - } - else relPart = fullPath; - - // Now, we have ensured that all segments in path are relative to the - // root we found earlier. - bool hasParents = rooted; - ptrdiff_t i; - foreach (path; paths2) - { - path = trimDirSeparators(path); - enum Prev { nonSpecial, dirSep, dot, doubleDot } - Prev prev = Prev.dirSep; - foreach (j; 0 .. path.length+1) + C getElement0() { - // Fake a dir separator between path segments - immutable c = (j == path.length ? dirSeparator[0] : path[j]); - - if (isDirSeparator(c)) + static if (isNarrowString!S) // avoid autodecode { - final switch (prev) - { - case Prev.doubleDot: - if (hasParents) - { - while (i > 0 && !isDirSeparator(relPart[i-1])) --i; - if (i > 0) --i; // skip the dir separator - while (i > 0 && !isDirSeparator(relPart[i-1])) --i; - if (i == 0) hasParents = rooted; - } - else - { - relPart[i++] = '.'; - relPart[i++] = '.'; - static assert (dirSeparator.length == 1); - relPart[i++] = dirSeparator[0]; - } - break; - case Prev.dot: - while (i > 0 && !isDirSeparator(relPart[i-1])) --i; - break; - case Prev.nonSpecial: - static assert (dirSeparator.length == 1); - relPart[i++] = dirSeparator[0]; - hasParents = true; - break; - case Prev.dirSep: - break; - } - prev = Prev.dirSep; + C c = element[0]; + element = element[1 .. $]; } - else if (c == '.') + else { - final switch (prev) - { - case Prev.dirSep: - prev = Prev.dot; - break; - case Prev.dot: - prev = Prev.doubleDot; - break; - case Prev.doubleDot: - prev = Prev.nonSpecial; - relPart[i .. i+3] = "..."; - i += 3; - break; - case Prev.nonSpecial: - relPart[i] = '.'; - ++i; - break; - } + C c = element.front; + element.popFront(); } - else + version (Windows) { - final switch (prev) - { - case Prev.doubleDot: - relPart[i] = '.'; - ++i; - goto case; - case Prev.dot: - relPart[i] = '.'; - ++i; - break; - case Prev.dirSep: break; - case Prev.nonSpecial: break; - } - relPart[i] = c; - ++i; - prev = Prev.nonSpecial; + if (c == '/') // can appear in root element + c = '\\'; // use native Windows directory separator } + return c; } + + // See if elem is "." + static bool isDot(S elem) + { + return elem.length == 1 && elem[0] == '.'; + } + + // See if elem is ".." + static bool isDotDot(S elem) + { + return elem.length == 2 && elem[0] == '.' && elem[1] == '.'; + } + + bool rooted; // the path starts with a root directory + C c; + S element; + typeof(pathSplitter(path[0 .. 0])) elements; } - // Return path, including root element and excluding the - // final dir separator. - immutable len = (relPart.ptr - fullPath.ptr) + (i > 0 ? i - 1 : 0); - if (len == 0) + return Result(path); +} + +/// +@safe unittest +{ + import std.array; + assert(asNormalizedPath("foo/..").array == "."); + + version (Posix) { - fullPath.length = 1; - fullPath[0] = '.'; + assert(asNormalizedPath("/foo/./bar/..//baz/").array == "/foo/baz"); + assert(asNormalizedPath("../foo/.").array == "../foo"); + assert(asNormalizedPath("/foo/bar/baz/").array == "/foo/bar/baz"); + assert(asNormalizedPath("/foo/./bar/../../baz").array == "/baz"); } - else - fullPath = fullPath[0 .. len]; version (Windows) { - // On Windows, if the path is on the form `\\server\share`, - // with no further segments, normalization will have turned it - // into `\\server\share\`. If so, we need to remove the final - // backslash. - if (isUNC(fullPath) && uncRootLength(fullPath) == fullPath.length - 1) - fullPath = fullPath[0 .. $-1]; + assert(asNormalizedPath(`c:\foo\.\bar/..\\baz\`).array == `c:\foo\baz`); + assert(asNormalizedPath(`..\foo\.`).array == `..\foo`); + assert(asNormalizedPath(`c:\foo\bar\baz\`).array == `c:\foo\bar\baz`); + assert(asNormalizedPath(`c:\foo\bar/..`).array == `c:\foo`); + assert(asNormalizedPath(`\\server\share\foo\..\bar`).array == + `\\server\share\bar`); } - return cast(typeof(return)) fullPath; } -unittest +auto asNormalizedPath(R)(auto ref R path) +if (isConvertibleToString!R) +{ + return asNormalizedPath!(StringTypeOf!R)(path); +} + +@safe unittest +{ + assert(testAliasedString!asNormalizedPath(null)); +} + +@safe unittest { - assert (buildNormalizedPath("") is null); - assert (buildNormalizedPath("foo") == "foo"); - assert (buildNormalizedPath(".") == "."); - assert (buildNormalizedPath(".", ".") == "."); - assert (buildNormalizedPath("foo", "..") == "."); - assert (buildNormalizedPath("", "") is null); - assert (buildNormalizedPath("", ".") == "."); - assert (buildNormalizedPath(".", "") == "."); - assert (buildNormalizedPath(null, "foo") == "foo"); - assert (buildNormalizedPath("", "foo") == "foo"); - assert (buildNormalizedPath("", "") == ""); - assert (buildNormalizedPath("", null) == ""); - assert (buildNormalizedPath(null, "") == ""); - assert (buildNormalizedPath!(char)(null, null) == ""); + import std.array; + import std.utf : byChar; + + assert(asNormalizedPath("").array is null); + assert(asNormalizedPath("foo").array == "foo"); + assert(asNormalizedPath(".").array == "."); + assert(asNormalizedPath("./.").array == "."); + assert(asNormalizedPath("foo/..").array == "."); + + auto save = asNormalizedPath("fob").save; + save.popFront(); + assert(save.front == 'o'); version (Posix) { - assert (buildNormalizedPath("/", "foo", "bar") == "/foo/bar"); - assert (buildNormalizedPath("foo", "bar", "baz") == "foo/bar/baz"); - assert (buildNormalizedPath("foo", "bar/baz") == "foo/bar/baz"); - assert (buildNormalizedPath("foo", "bar//baz///") == "foo/bar/baz"); - assert (buildNormalizedPath("/foo", "bar/baz") == "/foo/bar/baz"); - assert (buildNormalizedPath("/foo", "/bar/baz") == "/bar/baz"); - assert (buildNormalizedPath("/foo/..", "/bar/./baz") == "/bar/baz"); - assert (buildNormalizedPath("/foo/..", "bar/baz") == "/bar/baz"); - assert (buildNormalizedPath("/foo/../../", "bar/baz") == "/bar/baz"); - assert (buildNormalizedPath("/foo/bar", "../baz") == "/foo/baz"); - assert (buildNormalizedPath("/foo/bar", "../../baz") == "/baz"); - assert (buildNormalizedPath("/foo/bar", ".././/baz/..", "wee/") == "/foo/wee"); - assert (buildNormalizedPath("//foo/bar", "baz///wee") == "/foo/bar/baz/wee"); - static assert (buildNormalizedPath("/foo/..", "/bar/./baz") == "/bar/baz"); - // Examples in docs: - assert (buildNormalizedPath("/foo", "bar/baz/") == "/foo/bar/baz"); - assert (buildNormalizedPath("/foo", "/bar/..", "baz") == "/baz"); - assert (buildNormalizedPath("foo/./bar", "../../", "../baz") == "../baz"); - assert (buildNormalizedPath("/foo/./bar", "../../baz") == "/baz"); - - assert (buildNormalizedPath("foo", "", "bar") == "foo/bar"); - assert (buildNormalizedPath("foo", null, "bar") == "foo/bar"); + assert(asNormalizedPath("/foo/bar").array == "/foo/bar"); + assert(asNormalizedPath("foo/bar/baz").array == "foo/bar/baz"); + assert(asNormalizedPath("foo/bar/baz").array == "foo/bar/baz"); + assert(asNormalizedPath("foo/bar//baz///").array == "foo/bar/baz"); + assert(asNormalizedPath("/foo/bar/baz").array == "/foo/bar/baz"); + assert(asNormalizedPath("/foo/../bar/baz").array == "/bar/baz"); + assert(asNormalizedPath("/foo/../..//bar/baz").array == "/bar/baz"); + assert(asNormalizedPath("/foo/bar/../baz").array == "/foo/baz"); + assert(asNormalizedPath("/foo/bar/../../baz").array == "/baz"); + assert(asNormalizedPath("/foo/bar/.././/baz/../wee/").array == "/foo/wee"); + assert(asNormalizedPath("//foo/bar/baz///wee").array == "/foo/bar/baz/wee"); + + assert(asNormalizedPath("foo//bar").array == "foo/bar"); + assert(asNormalizedPath("foo/bar").array == "foo/bar"); //Curent dir path - assert (buildNormalizedPath("./") == "."); - assert (buildNormalizedPath("././") == "."); - assert (buildNormalizedPath("./foo/..") == "."); - assert (buildNormalizedPath("foo/..") == "."); + assert(asNormalizedPath("./").array == "."); + assert(asNormalizedPath("././").array == "."); + assert(asNormalizedPath("./foo/..").array == "."); + assert(asNormalizedPath("foo/..").array == "."); } else version (Windows) { - assert (buildNormalizedPath(`\`, `foo`, `bar`) == `\foo\bar`); - assert (buildNormalizedPath(`foo`, `bar`, `baz`) == `foo\bar\baz`); - assert (buildNormalizedPath(`foo`, `bar\baz`) == `foo\bar\baz`); - assert (buildNormalizedPath(`foo`, `bar\\baz\\\`) == `foo\bar\baz`); - assert (buildNormalizedPath(`\foo`, `bar\baz`) == `\foo\bar\baz`); - assert (buildNormalizedPath(`\foo`, `\bar\baz`) == `\bar\baz`); - assert (buildNormalizedPath(`\foo\..`, `\bar\.\baz`) == `\bar\baz`); - assert (buildNormalizedPath(`\foo\..`, `bar\baz`) == `\bar\baz`); - assert (buildNormalizedPath(`\foo\..\..\`, `bar\baz`) == `\bar\baz`); - assert (buildNormalizedPath(`\foo\bar`, `..\baz`) == `\foo\baz`); - assert (buildNormalizedPath(`\foo\bar`, `../../baz`) == `\baz`); - assert (buildNormalizedPath(`\foo\bar`, `..\.\/baz\..`, `wee\`) == `\foo\wee`); - - assert (buildNormalizedPath(`c:\`, `foo`, `bar`) == `c:\foo\bar`); - assert (buildNormalizedPath(`c:foo`, `bar`, `baz`) == `c:foo\bar\baz`); - assert (buildNormalizedPath(`c:foo`, `bar\baz`) == `c:foo\bar\baz`); - assert (buildNormalizedPath(`c:foo`, `bar\\baz\\\`) == `c:foo\bar\baz`); - assert (buildNormalizedPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); - assert (buildNormalizedPath(`c:\foo`, `\bar\baz`) == `c:\bar\baz`); - assert (buildNormalizedPath(`c:\foo\..`, `\bar\.\baz`) == `c:\bar\baz`); - assert (buildNormalizedPath(`c:\foo\..`, `bar\baz`) == `c:\bar\baz`); - assert (buildNormalizedPath(`c:\foo\..\..\`, `bar\baz`) == `c:\bar\baz`); - assert (buildNormalizedPath(`c:\foo\bar`, `..\baz`) == `c:\foo\baz`); - assert (buildNormalizedPath(`c:\foo\bar`, `..\..\baz`) == `c:\baz`); - assert (buildNormalizedPath(`c:\foo\bar`, `..\.\\baz\..`, `wee\`) == `c:\foo\wee`); - - assert (buildNormalizedPath(`\\server\share`, `foo`, `bar`) == `\\server\share\foo\bar`); - assert (buildNormalizedPath(`\\server\share\`, `foo`, `bar`) == `\\server\share\foo\bar`); - assert (buildNormalizedPath(`\\server\share\foo`, `bar\baz`) == `\\server\share\foo\bar\baz`); - assert (buildNormalizedPath(`\\server\share\foo`, `\bar\baz`) == `\\server\share\bar\baz`); - assert (buildNormalizedPath(`\\server\share\foo\..`, `\bar\.\baz`) == `\\server\share\bar\baz`); - assert (buildNormalizedPath(`\\server\share\foo\..`, `bar\baz`) == `\\server\share\bar\baz`); - assert (buildNormalizedPath(`\\server\share\foo\..\..\`, `bar\baz`) == `\\server\share\bar\baz`); - assert (buildNormalizedPath(`\\server\share\foo\bar`, `..\baz`) == `\\server\share\foo\baz`); - assert (buildNormalizedPath(`\\server\share\foo\bar`, `..\..\baz`) == `\\server\share\baz`); - assert (buildNormalizedPath(`\\server\share\foo\bar`, `..\.\\baz\..`, `wee\`) == `\\server\share\foo\wee`); - - static assert (buildNormalizedPath(`\foo\..\..\`, `bar\baz`) == `\bar\baz`); - - // Examples in docs: - assert (buildNormalizedPath(`c:\foo`, `bar\baz\`) == `c:\foo\bar\baz`); - assert (buildNormalizedPath(`c:\foo`, `bar/..`) == `c:\foo`); - assert (buildNormalizedPath(`\\server\share\foo`, `..\bar`) == `\\server\share\bar`); - - assert (buildNormalizedPath("foo", "", "bar") == `foo\bar`); - assert (buildNormalizedPath("foo", null, "bar") == `foo\bar`); + assert(asNormalizedPath(`\foo\bar`).array == `\foo\bar`); + assert(asNormalizedPath(`foo\bar\baz`).array == `foo\bar\baz`); + assert(asNormalizedPath(`foo\bar\baz`).array == `foo\bar\baz`); + assert(asNormalizedPath(`foo\bar\\baz\\\`).array == `foo\bar\baz`); + assert(asNormalizedPath(`\foo\bar\baz`).array == `\foo\bar\baz`); + assert(asNormalizedPath(`\foo\..\\bar\.\baz`).array == `\bar\baz`); + assert(asNormalizedPath(`\foo\..\bar\baz`).array == `\bar\baz`); + assert(asNormalizedPath(`\foo\..\..\\bar\baz`).array == `\bar\baz`); + + assert(asNormalizedPath(`\foo\bar\..\baz`).array == `\foo\baz`); + assert(asNormalizedPath(`\foo\bar\../../baz`).array == `\baz`); + assert(asNormalizedPath(`\foo\bar\..\.\/baz\..\wee\`).array == `\foo\wee`); + + assert(asNormalizedPath(`c:\foo\bar`).array == `c:\foo\bar`); + assert(asNormalizedPath(`c:foo\bar\baz`).array == `c:foo\bar\baz`); + assert(asNormalizedPath(`c:foo\bar\baz`).array == `c:foo\bar\baz`); + assert(asNormalizedPath(`c:foo\bar\\baz\\\`).array == `c:foo\bar\baz`); + assert(asNormalizedPath(`c:\foo\bar\baz`).array == `c:\foo\bar\baz`); + + assert(asNormalizedPath(`c:\foo\..\\bar\.\baz`).array == `c:\bar\baz`); + assert(asNormalizedPath(`c:\foo\..\bar\baz`).array == `c:\bar\baz`); + assert(asNormalizedPath(`c:\foo\..\..\\bar\baz`).array == `c:\bar\baz`); + assert(asNormalizedPath(`c:\foo\bar\..\baz`).array == `c:\foo\baz`); + assert(asNormalizedPath(`c:\foo\bar\..\..\baz`).array == `c:\baz`); + assert(asNormalizedPath(`c:\foo\bar\..\.\\baz\..\wee\`).array == `c:\foo\wee`); + assert(asNormalizedPath(`\\server\share\foo\bar`).array == `\\server\share\foo\bar`); + assert(asNormalizedPath(`\\server\share\\foo\bar`).array == `\\server\share\foo\bar`); + assert(asNormalizedPath(`\\server\share\foo\bar\baz`).array == `\\server\share\foo\bar\baz`); + assert(asNormalizedPath(`\\server\share\foo\..\\bar\.\baz`).array == `\\server\share\bar\baz`); + assert(asNormalizedPath(`\\server\share\foo\..\bar\baz`).array == `\\server\share\bar\baz`); + assert(asNormalizedPath(`\\server\share\foo\..\..\\bar\baz`).array == `\\server\share\bar\baz`); + assert(asNormalizedPath(`\\server\share\foo\bar\..\baz`).array == `\\server\share\foo\baz`); + assert(asNormalizedPath(`\\server\share\foo\bar\..\..\baz`).array == `\\server\share\baz`); + assert(asNormalizedPath(`\\server\share\foo\bar\..\.\\baz\..\wee\`).array == `\\server\share\foo\wee`); + + static assert(asNormalizedPath(`\foo\..\..\\bar\baz`).array == `\bar\baz`); + + assert(asNormalizedPath("foo//bar").array == `foo\bar`); //Curent dir path - assert (buildNormalizedPath(`.\`) == "."); - assert (buildNormalizedPath(`.\.\`) == "."); - assert (buildNormalizedPath(`.\foo\..`) == "."); - assert (buildNormalizedPath(`foo\..`) == "."); + assert(asNormalizedPath(`.\`).array == "."); + assert(asNormalizedPath(`.\.\`).array == "."); + assert(asNormalizedPath(`.\foo\..`).array == "."); + assert(asNormalizedPath(`foo\..`).array == "."); } - else static assert (0); + else static assert(0); } -unittest +@safe unittest { + import std.array; + version (Posix) { // Trivial - assert (buildNormalizedPath("").empty); - assert (buildNormalizedPath("foo/bar") == "foo/bar"); + assert(asNormalizedPath("").empty); + assert(asNormalizedPath("foo/bar").array == "foo/bar"); // Correct handling of leading slashes - assert (buildNormalizedPath("/") == "/"); - assert (buildNormalizedPath("///") == "/"); - assert (buildNormalizedPath("////") == "/"); - assert (buildNormalizedPath("/foo/bar") == "/foo/bar"); - assert (buildNormalizedPath("//foo/bar") == "/foo/bar"); - assert (buildNormalizedPath("///foo/bar") == "/foo/bar"); - assert (buildNormalizedPath("////foo/bar") == "/foo/bar"); + assert(asNormalizedPath("/").array == "/"); + assert(asNormalizedPath("///").array == "/"); + assert(asNormalizedPath("////").array == "/"); + assert(asNormalizedPath("/foo/bar").array == "/foo/bar"); + assert(asNormalizedPath("//foo/bar").array == "/foo/bar"); + assert(asNormalizedPath("///foo/bar").array == "/foo/bar"); + assert(asNormalizedPath("////foo/bar").array == "/foo/bar"); // Correct handling of single-dot symbol (current directory) - assert (buildNormalizedPath("/./foo") == "/foo"); - assert (buildNormalizedPath("/foo/./bar") == "/foo/bar"); + assert(asNormalizedPath("/./foo").array == "/foo"); + assert(asNormalizedPath("/foo/./bar").array == "/foo/bar"); - assert (buildNormalizedPath("./foo") == "foo"); - assert (buildNormalizedPath("././foo") == "foo"); - assert (buildNormalizedPath("foo/././bar") == "foo/bar"); + assert(asNormalizedPath("./foo").array == "foo"); + assert(asNormalizedPath("././foo").array == "foo"); + assert(asNormalizedPath("foo/././bar").array == "foo/bar"); // Correct handling of double-dot symbol (previous directory) - assert (buildNormalizedPath("/foo/../bar") == "/bar"); - assert (buildNormalizedPath("/foo/../../bar") == "/bar"); - assert (buildNormalizedPath("/../foo") == "/foo"); - assert (buildNormalizedPath("/../../foo") == "/foo"); - assert (buildNormalizedPath("/foo/..") == "/"); - assert (buildNormalizedPath("/foo/../..") == "/"); - - assert (buildNormalizedPath("foo/../bar") == "bar"); - assert (buildNormalizedPath("foo/../../bar") == "../bar"); - assert (buildNormalizedPath("../foo") == "../foo"); - assert (buildNormalizedPath("../../foo") == "../../foo"); - assert (buildNormalizedPath("../foo/../bar") == "../bar"); - assert (buildNormalizedPath(".././../foo") == "../../foo"); - assert (buildNormalizedPath("foo/bar/..") == "foo"); - assert (buildNormalizedPath("/foo/../..") == "/"); + assert(asNormalizedPath("/foo/../bar").array == "/bar"); + assert(asNormalizedPath("/foo/../../bar").array == "/bar"); + assert(asNormalizedPath("/../foo").array == "/foo"); + assert(asNormalizedPath("/../../foo").array == "/foo"); + assert(asNormalizedPath("/foo/..").array == "/"); + assert(asNormalizedPath("/foo/../..").array == "/"); + + assert(asNormalizedPath("foo/../bar").array == "bar"); + assert(asNormalizedPath("foo/../../bar").array == "../bar"); + assert(asNormalizedPath("../foo").array == "../foo"); + assert(asNormalizedPath("../../foo").array == "../../foo"); + assert(asNormalizedPath("../foo/../bar").array == "../bar"); + assert(asNormalizedPath(".././../foo").array == "../../foo"); + assert(asNormalizedPath("foo/bar/..").array == "foo"); + assert(asNormalizedPath("/foo/../..").array == "/"); // The ultimate path - assert (buildNormalizedPath("/foo/../bar//./../...///baz//") == "/.../baz"); - static assert (buildNormalizedPath("/foo/../bar//./../...///baz//") == "/.../baz"); + assert(asNormalizedPath("/foo/../bar//./../...///baz//").array == "/.../baz"); + static assert(asNormalizedPath("/foo/../bar//./../...///baz//").array == "/.../baz"); } else version (Windows) { // Trivial - assert (buildNormalizedPath("").empty); - assert (buildNormalizedPath(`foo\bar`) == `foo\bar`); - assert (buildNormalizedPath("foo/bar") == `foo\bar`); + assert(asNormalizedPath("").empty); + assert(asNormalizedPath(`foo\bar`).array == `foo\bar`); + assert(asNormalizedPath("foo/bar").array == `foo\bar`); // Correct handling of absolute paths - assert (buildNormalizedPath("/") == `\`); - assert (buildNormalizedPath(`\`) == `\`); - assert (buildNormalizedPath(`\\\`) == `\`); - assert (buildNormalizedPath(`\\\\`) == `\`); - assert (buildNormalizedPath(`\foo\bar`) == `\foo\bar`); - assert (buildNormalizedPath(`\\foo`) == `\\foo`); - assert (buildNormalizedPath(`\\foo\\`) == `\\foo`); - assert (buildNormalizedPath(`\\foo/bar`) == `\\foo\bar`); - assert (buildNormalizedPath(`\\\foo\bar`) == `\foo\bar`); - assert (buildNormalizedPath(`\\\\foo\bar`) == `\foo\bar`); - assert (buildNormalizedPath(`c:\`) == `c:\`); - assert (buildNormalizedPath(`c:\foo\bar`) == `c:\foo\bar`); - assert (buildNormalizedPath(`c:\\foo\bar`) == `c:\foo\bar`); + assert(asNormalizedPath("/").array == `\`); + assert(asNormalizedPath(`\`).array == `\`); + assert(asNormalizedPath(`\\\`).array == `\`); + assert(asNormalizedPath(`\\\\`).array == `\`); + assert(asNormalizedPath(`\foo\bar`).array == `\foo\bar`); + assert(asNormalizedPath(`\\foo`).array == `\\foo`); + assert(asNormalizedPath(`\\foo\\`).array == `\\foo`); + assert(asNormalizedPath(`\\foo/bar`).array == `\\foo\bar`); + assert(asNormalizedPath(`\\\foo\bar`).array == `\foo\bar`); + assert(asNormalizedPath(`\\\\foo\bar`).array == `\foo\bar`); + assert(asNormalizedPath(`c:\`).array == `c:\`); + assert(asNormalizedPath(`c:\foo\bar`).array == `c:\foo\bar`); + assert(asNormalizedPath(`c:\\foo\bar`).array == `c:\foo\bar`); // Correct handling of single-dot symbol (current directory) - assert (buildNormalizedPath(`\./foo`) == `\foo`); - assert (buildNormalizedPath(`\foo/.\bar`) == `\foo\bar`); + assert(asNormalizedPath(`\./foo`).array == `\foo`); + assert(asNormalizedPath(`\foo/.\bar`).array == `\foo\bar`); - assert (buildNormalizedPath(`.\foo`) == `foo`); - assert (buildNormalizedPath(`./.\foo`) == `foo`); - assert (buildNormalizedPath(`foo\.\./bar`) == `foo\bar`); + assert(asNormalizedPath(`.\foo`).array == `foo`); + assert(asNormalizedPath(`./.\foo`).array == `foo`); + assert(asNormalizedPath(`foo\.\./bar`).array == `foo\bar`); // Correct handling of double-dot symbol (previous directory) - assert (buildNormalizedPath(`\foo\..\bar`) == `\bar`); - assert (buildNormalizedPath(`\foo\../..\bar`) == `\bar`); - assert (buildNormalizedPath(`\..\foo`) == `\foo`); - assert (buildNormalizedPath(`\..\..\foo`) == `\foo`); - assert (buildNormalizedPath(`\foo\..`) == `\`); - assert (buildNormalizedPath(`\foo\../..`) == `\`); - - assert (buildNormalizedPath(`foo\..\bar`) == `bar`); - assert (buildNormalizedPath(`foo\..\../bar`) == `..\bar`); - assert (buildNormalizedPath(`..\foo`) == `..\foo`); - assert (buildNormalizedPath(`..\..\foo`) == `..\..\foo`); - assert (buildNormalizedPath(`..\foo\..\bar`) == `..\bar`); - assert (buildNormalizedPath(`..\.\..\foo`) == `..\..\foo`); - assert (buildNormalizedPath(`foo\bar\..`) == `foo`); - assert (buildNormalizedPath(`\foo\..\..`) == `\`); - assert (buildNormalizedPath(`c:\foo\..\..`) == `c:\`); + assert(asNormalizedPath(`\foo\..\bar`).array == `\bar`); + assert(asNormalizedPath(`\foo\../..\bar`).array == `\bar`); + assert(asNormalizedPath(`\..\foo`).array == `\foo`); + assert(asNormalizedPath(`\..\..\foo`).array == `\foo`); + assert(asNormalizedPath(`\foo\..`).array == `\`); + assert(asNormalizedPath(`\foo\../..`).array == `\`); + + assert(asNormalizedPath(`foo\..\bar`).array == `bar`); + assert(asNormalizedPath(`foo\..\../bar`).array == `..\bar`); + + assert(asNormalizedPath(`..\foo`).array == `..\foo`); + assert(asNormalizedPath(`..\..\foo`).array == `..\..\foo`); + assert(asNormalizedPath(`..\foo\..\bar`).array == `..\bar`); + assert(asNormalizedPath(`..\.\..\foo`).array == `..\..\foo`); + assert(asNormalizedPath(`foo\bar\..`).array == `foo`); + assert(asNormalizedPath(`\foo\..\..`).array == `\`); + assert(asNormalizedPath(`c:\foo\..\..`).array == `c:\`); // Correct handling of non-root path with drive specifier - assert (buildNormalizedPath(`c:foo`) == `c:foo`); - assert (buildNormalizedPath(`c:..\foo\.\..\bar`) == `c:..\bar`); + assert(asNormalizedPath(`c:foo`).array == `c:foo`); + assert(asNormalizedPath(`c:..\foo\.\..\bar`).array == `c:..\bar`); // The ultimate path - assert (buildNormalizedPath(`c:\foo\..\bar\\.\..\...\\\baz\\`) == `c:\...\baz`); - static assert (buildNormalizedPath(`c:\foo\..\bar\\.\..\...\\\baz\\`) == `c:\...\baz`); - } - else static assert (false); -} - -unittest -{ - // Test for issue 7397 - string[] ary = ["a", "b"]; - version (Posix) - { - assert (buildNormalizedPath(ary) == "a/b"); - } - else version (Windows) - { - assert (buildNormalizedPath(ary) == `a\b`); + assert(asNormalizedPath(`c:\foo\..\bar\\.\..\...\\\baz\\`).array == `c:\...\baz`); + static assert(asNormalizedPath(`c:\foo\..\bar\\.\..\...\\\baz\\`).array == `c:\...\baz`); } + else static assert(false); } +/** Slice up a path into its elements. + Params: + path = string or slicable random access range - -/** Returns a bidirectional range that iterates over the elements of a path. - - Examples: - --- - assert (equal(pathSplitter("/"), ["/"])); - assert (equal(pathSplitter("/foo/bar"), ["/", "foo", "bar"])); - assert (equal(pathSplitter("//foo/bar"), ["//foo", "bar"])); - assert (equal(pathSplitter("foo/../bar//./"), ["foo", "..", "bar", "."])); - - version (Windows) - { - assert (equal(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); - assert (equal(pathSplitter("c:"), ["c:"])); - assert (equal(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); - assert (equal(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); - } - --- + Returns: + bidirectional range of slices of `path` */ -auto pathSplitter(C)(const(C)[] path) @safe pure nothrow - if (isSomeChar!C) +auto pathSplitter(R)(R path) +if ((isRandomAccessRange!R && hasSlicing!R || + isNarrowString!R) && + !isConvertibleToString!R) { static struct PathSplitter { - @safe pure nothrow @nogc: - @property bool empty() const { return _empty; } + @property bool empty() const { return pe == 0; } - @property const(C)[] front() const + @property R front() { - assert (!empty, "PathSplitter: called front() on empty range"); - return _front; + assert(!empty); + return _path[fs .. fe]; } void popFront() { - assert (!empty, "PathSplitter: called popFront() on empty range"); - if (_path.empty) + assert(!empty); + if (ps == pe) { - if (_front is _back) + if (fs == bs && fe == be) { - _empty = true; - _front = null; - _back = null; + pe = 0; } else { - _front = _back; + fs = bs; + fe = be; } } else { - ptrdiff_t i = 0; - while (i < _path.length && !isDirSeparator(_path[i])) ++i; - _front = _path[0 .. i]; - _path = ltrimDirSeparators(_path[i .. $]); + fs = ps; + fe = fs; + while (fe < pe && !isDirSeparator(_path[fe])) + ++fe; + ps = ltrim(fe, pe); } } - @property const(C)[] back() const + @property R back() { - assert (!empty, "PathSplitter: called back() on empty range"); - return _back; + assert(!empty); + return _path[bs .. be]; } void popBack() { - assert (!empty, "PathSplitter: called popBack() on empty range"); - if (_path.empty) + assert(!empty); + if (ps == pe) { - if (_front is _back) + if (fs == bs && fe == be) { - _empty = true; - _front = null; - _back = null; + pe = 0; } else { - _back = _front; + bs = fs; + be = fe; } } else { - auto i = (cast(ptrdiff_t) _path.length) - 1; - while (i >= 0 && !isDirSeparator(_path[i])) --i; - _back = _path[i + 1 .. $]; - _path = rtrimDirSeparators(_path[0 .. i+1]); + bs = pe; + be = bs; + while (bs > ps && !isDirSeparator(_path[bs - 1])) + --bs; + pe = rtrim(ps, bs); } } @property auto save() { return this; } private: - typeof(path) _path, _front, _back; - bool _empty; + R _path; + size_t ps, pe; + size_t fs, fe; + size_t bs, be; - this(typeof(path) p) + this(R p) { if (p.empty) { - _empty = true; + pe = 0; return; } _path = p; + ps = 0; + pe = _path.length; + // If path is rooted, first element is special version (Windows) { if (isUNC(_path)) { auto i = uncRootLength(_path); - _front = _path[0 .. i]; - _path = ltrimDirSeparators(_path[i .. $]); + fs = 0; + fe = i; + ps = ltrim(fe, pe); } else if (isDriveRoot(_path)) { - _front = _path[0 .. 3]; - _path = ltrimDirSeparators(_path[3 .. $]); + fs = 0; + fe = 3; + ps = ltrim(fe, pe); } else if (_path.length >= 1 && isDirSeparator(_path[0])) { - _front = _path[0 .. 1]; - _path = ltrimDirSeparators(_path[1 .. $]); + fs = 0; + fe = 1; + ps = ltrim(fe, pe); } else { - assert (!isRooted(_path)); + assert(!isRooted(_path)); popFront(); } } @@ -1757,29 +2285,84 @@ auto pathSplitter(C)(const(C)[] path) @safe pure nothrow { if (_path.length >= 1 && isDirSeparator(_path[0])) { - _front = _path[0 .. 1]; - _path = ltrimDirSeparators(_path[1 .. $]); + fs = 0; + fe = 1; + ps = ltrim(fe, pe); } else { popFront(); } } - else static assert (0); + else static assert(0); - if (_path.empty) _back = _front; + if (ps == pe) + { + bs = fs; + be = fe; + } else { - _path = rtrimDirSeparators(_path); + pe = rtrim(ps, pe); popBack(); } } + + size_t ltrim(size_t s, size_t e) + { + while (s < e && isDirSeparator(_path[s])) + ++s; + return s; + } + + size_t rtrim(size_t s, size_t e) + { + while (s < e && isDirSeparator(_path[e - 1])) + --e; + return e; + } } return PathSplitter(path); } -unittest +/// +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.conv : to; + + assert(equal(pathSplitter("/"), ["/"])); + assert(equal(pathSplitter("/foo/bar"), ["/", "foo", "bar"])); + assert(equal(pathSplitter("foo/../bar//./"), ["foo", "..", "bar", "."])); + + version (Posix) + { + assert(equal(pathSplitter("//foo/bar"), ["/", "foo", "bar"])); + } + + version (Windows) + { + assert(equal(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); + assert(equal(pathSplitter("c:"), ["c:"])); + assert(equal(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); + assert(equal(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); + } +} + +auto pathSplitter(R)(auto ref R path) +if (isConvertibleToString!R) +{ + return pathSplitter!(StringTypeOf!R)(path); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + assert(testAliasedString!pathSplitter("/")); +} + +@safe unittest { // equal2 verifies that the range is the same both ways, i.e. // through front/popFront and back/popBack. @@ -1787,59 +2370,60 @@ unittest import std.algorithm; bool equal2(R1, R2)(R1 r1, R2 r2) { - static assert (isBidirectionalRange!R1); + static assert(isBidirectionalRange!R1); return equal(r1, r2) && equal(retro(r1), retro(r2)); } - assert (pathSplitter("").empty); + assert(pathSplitter("").empty); // Root directories - assert (equal2(pathSplitter("/"), ["/"])); - assert (equal2(pathSplitter("//"), ["/"])); - assert (equal2(pathSplitter("///"w), ["/"w])); + assert(equal2(pathSplitter("/"), ["/"])); + assert(equal2(pathSplitter("//"), ["/"])); + assert(equal2(pathSplitter("///"w), ["/"w])); // Absolute paths - assert (equal2(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"])); + assert(equal2(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"])); // General - assert (equal2(pathSplitter("foo/bar"d.dup), ["foo"d, "bar"d])); - assert (equal2(pathSplitter("foo//bar"), ["foo", "bar"])); - assert (equal2(pathSplitter("foo/bar//"w), ["foo"w, "bar"w])); - assert (equal2(pathSplitter("foo/../bar//./"d), ["foo"d, ".."d, "bar"d, "."d])); + assert(equal2(pathSplitter("foo/bar"d.dup), ["foo"d, "bar"d])); + assert(equal2(pathSplitter("foo//bar"), ["foo", "bar"])); + assert(equal2(pathSplitter("foo/bar//"w), ["foo"w, "bar"w])); + assert(equal2(pathSplitter("foo/../bar//./"d), ["foo"d, ".."d, "bar"d, "."d])); // save() auto ps1 = pathSplitter("foo/bar/baz"); auto ps2 = ps1.save; ps1.popFront(); - assert (equal2(ps1, ["bar", "baz"])); - assert (equal2(ps2, ["foo", "bar", "baz"])); + assert(equal2(ps1, ["bar", "baz"])); + assert(equal2(ps2, ["foo", "bar", "baz"])); // Platform specific version (Posix) { - assert (equal2(pathSplitter("//foo/bar"w.dup), ["/"w, "foo"w, "bar"w])); + assert(equal2(pathSplitter("//foo/bar"w.dup), ["/"w, "foo"w, "bar"w])); } version (Windows) { - assert (equal2(pathSplitter(`\`), [`\`])); - assert (equal2(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); - assert (equal2(pathSplitter("c:"), ["c:"])); - assert (equal2(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); - assert (equal2(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); - assert (equal2(pathSplitter(`\\foo\bar`), [`\\foo\bar`])); - assert (equal2(pathSplitter(`\\foo\bar\\`), [`\\foo\bar`])); - assert (equal2(pathSplitter(`\\foo\bar\baz`), [`\\foo\bar`, "baz"])); + assert(equal2(pathSplitter(`\`), [`\`])); + assert(equal2(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); + assert(equal2(pathSplitter("c:"), ["c:"])); + assert(equal2(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); + assert(equal2(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); + assert(equal2(pathSplitter(`\\foo\bar`), [`\\foo\bar`])); + assert(equal2(pathSplitter(`\\foo\bar\\`), [`\\foo\bar`])); + assert(equal2(pathSplitter(`\\foo\bar\baz`), [`\\foo\bar`, "baz"])); } import std.exception; assertCTFEable!( { - assert (equal(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"])); + assert(equal(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"])); }); - // Bugzilla 11691 - // front should return a mutable array of const elements - static assert(is(typeof(pathSplitter!char(null).front) == const(char)[])); + static assert(is(typeof(pathSplitter!(const(char)[])(null).front) == const(char)[])); + + import std.utf : byDchar; + assert(equal2(pathSplitter("foo/bar"d.byDchar), ["foo"d, "bar"d])); } @@ -1847,15 +2431,18 @@ unittest /** Determines whether a path starts at a root directory. + Params: path = A path name. + Returns: Whether a path starts at a root directory. + On POSIX, this function returns true if and only if the path starts with a slash (/). --- version (Posix) { - assert (isRooted("/")); - assert (isRooted("/foo")); - assert (!isRooted("foo")); - assert (!isRooted("../foo")); + assert(isRooted("/")); + assert(isRooted("/foo")); + assert(!isRooted("foo")); + assert(!isRooted("../foo")); } --- @@ -1865,18 +2452,18 @@ unittest --- version (Windows) { - assert (isRooted(`\`)); - assert (isRooted(`\foo`)); - assert (isRooted(`d:\foo`)); - assert (isRooted(`\\foo\bar`)); - assert (!isRooted("foo")); - assert (!isRooted("d:foo")); + assert(isRooted(`\`)); + assert(isRooted(`\foo`)); + assert(isRooted(`d:\foo`)); + assert(isRooted(`\\foo\bar`)); + assert(!isRooted("foo")); + assert(!isRooted("d:foo")); } --- */ -bool isRooted(R)(inout R path) - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) +bool isRooted(R)(R path) +if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { if (path.length >= 1 && isDirSeparator(path[0])) return true; version (Posix) return false; @@ -1884,28 +2471,28 @@ bool isRooted(R)(inout R path) } -unittest +@safe unittest { - assert (isRooted("/")); - assert (isRooted("/foo")); - assert (!isRooted("foo")); - assert (!isRooted("../foo")); + assert(isRooted("/")); + assert(isRooted("/foo")); + assert(!isRooted("foo")); + assert(!isRooted("../foo")); version (Windows) { - assert (isRooted(`\`)); - assert (isRooted(`\foo`)); - assert (isRooted(`d:\foo`)); - assert (isRooted(`\\foo\bar`)); - assert (!isRooted("foo")); - assert (!isRooted("d:foo")); + assert(isRooted(`\`)); + assert(isRooted(`\foo`)); + assert(isRooted(`d:\foo`)); + assert(isRooted(`\\foo\bar`)); + assert(!isRooted("foo")); + assert(!isRooted("d:foo")); } - static assert (isRooted("/foo")); - static assert (!isRooted("foo")); + static assert(isRooted("/foo")); + static assert(!isRooted("foo")); static struct DirEntry { string s; alias s this; } - assert (!isRooted(DirEntry("foo"))); + assert(!isRooted(DirEntry("foo"))); } @@ -1913,16 +2500,20 @@ unittest /** Determines whether a path is absolute or not. - Examples: + Params: path = A path name. + + Returns: Whether a path is absolute or not. + + Example: On POSIX, an absolute path starts at the root directory. (In fact, $(D _isAbsolute) is just an alias for $(LREF isRooted).) --- version (Posix) { - assert (isAbsolute("/")); - assert (isAbsolute("/foo")); - assert (!isAbsolute("foo")); - assert (!isAbsolute("../foo")); + assert(isAbsolute("/")); + assert(isAbsolute("/foo")); + assert(!isAbsolute("foo")); + assert(!isAbsolute("../foo")); } --- @@ -1933,26 +2524,26 @@ unittest --- version (Windows) { - assert (isAbsolute(`d:\`)); - assert (isAbsolute(`d:\foo`)); - assert (isAbsolute(`\\foo\bar`)); - assert (!isAbsolute(`\`)); - assert (!isAbsolute(`\foo`)); - assert (!isAbsolute("d:foo")); + assert(isAbsolute(`d:\`)); + assert(isAbsolute(`d:\foo`)); + assert(isAbsolute(`\\foo\bar`)); + assert(!isAbsolute(`\`)); + assert(!isAbsolute(`\foo`)); + assert(!isAbsolute("d:foo")); } --- */ version (StdDdoc) { - bool isAbsolute(R)(const R path) @safe pure nothrow @nogc - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)); + bool isAbsolute(R)(R path) pure nothrow @safe + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)); } else version (Windows) { - bool isAbsolute(R)(const R path) @safe pure nothrow @nogc - if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) + bool isAbsolute(R)(R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { return isDriveRoot!(BaseOf!R)(path) || isUNC!(BaseOf!R)(path); } @@ -1963,29 +2554,29 @@ else version (Posix) } -unittest +@safe unittest { - assert (!isAbsolute("foo")); - assert (!isAbsolute("../foo"w)); - static assert (!isAbsolute("foo")); + assert(!isAbsolute("foo")); + assert(!isAbsolute("../foo"w)); + static assert(!isAbsolute("foo")); version (Posix) { - assert (isAbsolute("/"d)); - assert (isAbsolute("/foo".dup)); - static assert (isAbsolute("/foo")); + assert(isAbsolute("/"d)); + assert(isAbsolute("/foo".dup)); + static assert(isAbsolute("/foo")); } version (Windows) { - assert (isAbsolute("d:\\"w)); - assert (isAbsolute("d:\\foo"d)); - assert (isAbsolute("\\\\foo\\bar")); - assert (!isAbsolute("\\"w.dup)); - assert (!isAbsolute("\\foo"d.dup)); - assert (!isAbsolute("d:")); - assert (!isAbsolute("d:foo")); - static assert (isAbsolute(`d:\foo`)); + assert(isAbsolute("d:\\"w)); + assert(isAbsolute("d:\\foo"d)); + assert(isAbsolute("\\\\foo\\bar")); + assert(!isAbsolute("\\"w.dup)); + assert(!isAbsolute("\\foo"d.dup)); + assert(!isAbsolute("d:")); + assert(!isAbsolute("d:foo")); + static assert(isAbsolute(`d:\foo`)); } { @@ -2000,7 +2591,7 @@ unittest -/** Translates $(D path) into an absolute _path. +/** Transforms $(D path) into an absolute _path. The following algorithm is used: $(OL @@ -2013,64 +2604,123 @@ unittest The function allocates memory if and only if it gets to the third stage of this algorithm. - Examples: - --- - version (Posix) - { - assert (absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); - assert (absolutePath("../file", "/foo/bar") == "/foo/bar/../file"); - assert (absolutePath("/some/file", "/foo/bar") == "/some/file"); - } + Params: + path = the relative path to transform + base = the base directory of the relative path - version (Windows) - { - assert (absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); - assert (absolutePath(`..\file`, `c:\foo\bar`) == `c:\foo\bar\..\file`); - assert (absolutePath(`c:\some\file`, `c:\foo\bar`) == `c:\some\file`); - assert (absolutePath(`\file`, `c:\foo\bar`) == `c:\file`); - } - --- + Returns: + string of transformed path Throws: $(D Exception) if the specified _base directory is not absolute. + + See_Also: + $(LREF asAbsolutePath) which does not allocate */ string absolutePath(string path, lazy string base = getcwd()) @safe pure { + import std.array : array; if (path.empty) return null; if (isAbsolute(path)) return path; - immutable baseVar = base; + auto baseVar = base; if (!isAbsolute(baseVar)) throw new Exception("Base directory must be absolute"); - return buildPath(baseVar, path); + return chainPath(baseVar, path).array; } +/// +@safe unittest +{ + version (Posix) + { + assert(absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); + assert(absolutePath("../file", "/foo/bar") == "/foo/bar/../file"); + assert(absolutePath("/some/file", "/foo/bar") == "/some/file"); + } -unittest + version (Windows) + { + assert(absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); + assert(absolutePath(`..\file`, `c:\foo\bar`) == `c:\foo\bar\..\file`); + assert(absolutePath(`c:\some\file`, `c:\foo\bar`) == `c:\some\file`); + assert(absolutePath(`\`, `c:\`) == `c:\`); + assert(absolutePath(`\some\file`, `c:\foo\bar`) == `c:\some\file`); + } +} + +@safe unittest { version (Posix) { - assert (absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); - assert (absolutePath("../file", "/foo/bar") == "/foo/bar/../file"); - assert (absolutePath("/some/file", "/foo/bar") == "/some/file"); - static assert (absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); + static assert(absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); } version (Windows) { - assert (absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); - assert (absolutePath(`..\file`, `c:\foo\bar`) == `c:\foo\bar\..\file`); - assert (absolutePath(`c:\some\file`, `c:\foo\bar`) == `c:\some\file`); - assert (absolutePath(`\`, `c:\`) == `c:\`); - assert (absolutePath(`\some\file`, `c:\foo\bar`) == `c:\some\file`); - static assert (absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); + static assert(absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); } import std.exception; assertThrown(absolutePath("bar", "foo")); } +/** Transforms $(D path) into an absolute _path. + + The following algorithm is used: + $(OL + $(LI If $(D path) is empty, return $(D null).) + $(LI If $(D path) is already absolute, return it.) + $(LI Otherwise, append $(D path) to the current working directory, + which allocates memory.) + ) + + Params: + path = the relative path to transform + + Returns: + the transformed path as a lazy range + + See_Also: + $(LREF absolutePath) which returns an allocated string +*/ +auto asAbsolutePath(R)(R path) +if ((isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) && + !isConvertibleToString!R) +{ + import std.file : getcwd; + string base = null; + if (!path.empty && !isAbsolute(path)) + base = getcwd(); + return chainPath(base, path); +} + +/// +@system unittest +{ + import std.array; + assert(asAbsolutePath(cast(string) null).array == ""); + version (Posix) + { + assert(asAbsolutePath("/foo").array == "/foo"); + } + version (Windows) + { + assert(asAbsolutePath("c:/foo").array == "c:/foo"); + } + asAbsolutePath("foo"); +} +auto asAbsolutePath(R)(auto ref R path) +if (isConvertibleToString!R) +{ + return asAbsolutePath!(StringTypeOf!R)(path); +} +@system unittest +{ + assert(testAliasedString!asAbsolutePath(null)); +} /** Translates $(D path) into a relative _path. @@ -2096,156 +2746,251 @@ unittest the comparison is case sensitive or not. See the $(LREF filenameCmp) documentation for details. - The function allocates memory if and only if it reaches the third stage - of the above algorithm. + This function allocates memory. - Examples: - --- - assert (relativePath("foo") == "foo"); + Params: + cs = Whether matching path name components against the base path should + be case-sensitive or not. + path = A path name. + base = The base path to construct the relative path from. + + Returns: The relative path. + + See_Also: + $(LREF asRelativePath) which does not allocate memory + + Throws: + $(D Exception) if the specified _base directory is not absolute. +*/ +string relativePath(CaseSensitive cs = CaseSensitive.osDefault) + (string path, lazy string base = getcwd()) +{ + if (!isAbsolute(path)) + return path; + auto baseVar = base; + if (!isAbsolute(baseVar)) + throw new Exception("Base directory must be absolute"); + + import std.conv : to; + return asRelativePath!cs(path, baseVar).to!string; +} + +/// +@system unittest +{ + assert(relativePath("foo") == "foo"); version (Posix) { - assert (relativePath("foo", "/bar") == "foo"); - assert (relativePath("/foo/bar", "/foo/bar") == "."); - assert (relativePath("/foo/bar", "/foo/baz") == "../bar"); - assert (relativePath("/foo/bar/baz", "/foo/woo/wee") == "../../bar/baz"); - assert (relativePath("/foo/bar/baz", "/foo/bar") == "baz"); + assert(relativePath("foo", "/bar") == "foo"); + assert(relativePath("/foo/bar", "/foo/bar") == "."); + assert(relativePath("/foo/bar", "/foo/baz") == "../bar"); + assert(relativePath("/foo/bar/baz", "/foo/woo/wee") == "../../bar/baz"); + assert(relativePath("/foo/bar/baz", "/foo/bar") == "baz"); } version (Windows) { - assert (relativePath("foo", `c:\bar`) == "foo"); - assert (relativePath(`c:\foo\bar`, `c:\foo\bar`) == "."); - assert (relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`); - assert (relativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`); - assert (relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); - assert (relativePath(`c:\foo\bar`, `d:\foo`) == `c:\foo\bar`); + assert(relativePath("foo", `c:\bar`) == "foo"); + assert(relativePath(`c:\foo\bar`, `c:\foo\bar`) == "."); + assert(relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`); + assert(relativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`); + assert(relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); + assert(relativePath(`c:\foo\bar`, `d:\foo`) == `c:\foo\bar`); } - --- +} - Throws: - $(D Exception) if the specified _base directory is not absolute. +@system unittest +{ + import std.exception; + assert(relativePath("foo") == "foo"); + version (Posix) + { + relativePath("/foo"); + assert(relativePath("/foo/bar", "/foo/baz") == "../bar"); + assertThrown(relativePath("/foo", "bar")); + } + else version (Windows) + { + relativePath(`\foo`); + assert(relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); + assertThrown(relativePath(`c:\foo`, "bar")); + } + else static assert(0); +} + +/** Transforms `path` into a _path relative to `base`. + + The returned _path is relative to `base`, which is usually + the current working directory. + `base` must be an absolute _path, and it is always assumed + to refer to a directory. If `path` and `base` refer to + the same directory, the function returns `'.'`. + + The following algorithm is used: + $(OL + $(LI If `path` is a relative directory, return it unaltered.) + $(LI Find a common root between `path` and `base`. + If there is no common root, return `path` unaltered.) + $(LI Prepare a string with as many `../` or `..\` as + necessary to reach the common root from base path.) + $(LI Append the remaining segments of `path` to the string + and return.) + ) + + In the second step, path components are compared using `filenameCmp!cs`, + where `cs` is an optional template parameter determining whether + the comparison is case sensitive or not. See the + $(LREF filenameCmp) documentation for details. + + Params: + path = _path to transform + base = absolute path + cs = whether filespec comparisons are sensitive or not; defaults to + `CaseSensitive.osDefault` + + Returns: + a random access range of the transformed _path + + See_Also: + $(LREF relativePath) */ -string relativePath(CaseSensitive cs = CaseSensitive.osDefault) - (string path, lazy string base = getcwd()) - //TODO: @safe (object.reserve(T[]) should be @trusted) +auto asRelativePath(CaseSensitive cs = CaseSensitive.osDefault, R1, R2) + (R1 path, R2 base) +if ((isNarrowString!R1 || + (isRandomAccessRange!R1 && hasSlicing!R1 && isSomeChar!(ElementType!R1)) && + !isConvertibleToString!R1) && + (isNarrowString!R2 || + (isRandomAccessRange!R2 && hasSlicing!R2 && isSomeChar!(ElementType!R2)) && + !isConvertibleToString!R2)) { - if (!isAbsolute(path)) return path; - immutable baseVar = base; - if (!isAbsolute(baseVar)) throw new Exception("Base directory must be absolute"); + bool choosePath = !isAbsolute(path); // Find common root with current working directory - string result; - if (!__ctfe) result.reserve(baseVar.length + path.length); - auto basePS = pathSplitter(baseVar); + auto basePS = pathSplitter(base); auto pathPS = pathSplitter(path); - if (filenameCmp!cs(basePS.front, pathPS.front) != 0) return path; + choosePath |= filenameCmp!cs(basePS.front, pathPS.front) != 0; basePS.popFront(); pathPS.popFront(); - while (!basePS.empty && !pathPS.empty - && filenameCmp!cs(basePS.front, pathPS.front) == 0) - { - basePS.popFront(); - pathPS.popFront(); - } + import std.range.primitives : walkLength; + import std.range : repeat, chain, choose; + import std.algorithm.comparison : mismatch; + import std.algorithm.iteration : joiner; + import std.array : array; + import std.utf : byCodeUnit, byChar; + + // Remove matching prefix from basePS and pathPS + auto tup = mismatch!((a, b) => filenameCmp!cs(a, b) == 0)(basePS, pathPS); + basePS = tup[0]; + pathPS = tup[1]; + + string sep; + if (basePS.empty && pathPS.empty) + sep = "."; // if base == path, this is the return + else if (!basePS.empty && !pathPS.empty) + sep = dirSeparator; // Append as many "../" as necessary to reach common base from path - while (!basePS.empty) + auto r1 = ".." + .byChar + .repeat(basePS.walkLength()) + .joiner(dirSeparator.byChar); + + auto r2 = pathPS + .joiner(dirSeparator.byChar) + .byChar; + + // Return (r1 ~ sep ~ r2) + return choose(choosePath, path.byCodeUnit, chain(r1, sep.byChar, r2)); +} + +/// +@system unittest +{ + import std.array; + version (Posix) { - result ~= ".."; - result ~= dirSeparator; - basePS.popFront(); + assert(asRelativePath("foo", "/bar").array == "foo"); + assert(asRelativePath("/foo/bar", "/foo/bar").array == "."); + assert(asRelativePath("/foo/bar", "/foo/baz").array == "../bar"); + assert(asRelativePath("/foo/bar/baz", "/foo/woo/wee").array == "../../bar/baz"); + assert(asRelativePath("/foo/bar/baz", "/foo/bar").array == "baz"); } - - // Append the remainder of path - while (!pathPS.empty) + else version (Windows) { - result ~= pathPS.front; - result ~= dirSeparator; - pathPS.popFront(); + assert(asRelativePath("foo", `c:\bar`).array == "foo"); + assert(asRelativePath(`c:\foo\bar`, `c:\foo\bar`).array == "."); + assert(asRelativePath(`c:\foo\bar`, `c:\foo\baz`).array == `..\bar`); + assert(asRelativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`).array == `..\..\bar\baz`); + assert(asRelativePath(`c:/foo/bar/baz`, `c:\foo\woo\wee`).array == `..\..\bar\baz`); + assert(asRelativePath(`c:\foo\bar\baz`, `c:\foo\bar`).array == "baz"); + assert(asRelativePath(`c:\foo\bar`, `d:\foo`).array == `c:\foo\bar`); + assert(asRelativePath(`\\foo\bar`, `c:\foo`).array == `\\foo\bar`); } + else + static assert(0); +} - // base == path - if (result.empty) return "."; +auto asRelativePath(CaseSensitive cs = CaseSensitive.osDefault, R1, R2) + (auto ref R1 path, auto ref R2 base) +if (isConvertibleToString!R1 || isConvertibleToString!R2) +{ + import std.meta : staticMap; + alias Types = staticMap!(convertToString, R1, R2); + return asRelativePath!(cs, Types)(path, base); +} - // Strip off last path separator - return result[0 .. $-1]; +@system unittest +{ + import std.array; + version (Posix) + assert(asRelativePath(TestAliasedString("foo"), TestAliasedString("/bar")).array == "foo"); + else version (Windows) + assert(asRelativePath(TestAliasedString("foo"), TestAliasedString(`c:\bar`)).array == "foo"); + assert(asRelativePath(TestAliasedString("foo"), "bar").array == "foo"); + assert(asRelativePath("foo", TestAliasedString("bar")).array == "foo"); + assert(asRelativePath(TestAliasedString("foo"), TestAliasedString("bar")).array == "foo"); + import std.utf : byDchar; + assert(asRelativePath("foo"d.byDchar, TestAliasedString("bar")).array == "foo"); } -unittest +@system unittest { - import std.exception; - assert (relativePath("foo") == "foo"); + import std.array, std.utf : bCU=byCodeUnit; version (Posix) { - assert (relativePath("foo", "/bar") == "foo"); - assert (relativePath("/foo/bar", "/foo/bar") == "."); - assert (relativePath("/foo/bar", "/foo/baz") == "../bar"); - assert (relativePath("/foo/bar/baz", "/foo/woo/wee") == "../../bar/baz"); - assert (relativePath("/foo/bar/baz", "/foo/bar") == "baz"); - assertThrown(relativePath("/foo", "bar")); - - assertCTFEable!( - { - assert (relativePath("/foo/bar", "/foo/baz") == "../bar"); - }); + assert(asRelativePath("/foo/bar/baz".bCU, "/foo/bar".bCU).array == "baz"); + assert(asRelativePath("/foo/bar/baz"w.bCU, "/foo/bar"w.bCU).array == "baz"w); + assert(asRelativePath("/foo/bar/baz"d.bCU, "/foo/bar"d.bCU).array == "baz"d); } else version (Windows) { - assert (relativePath("foo", `c:\bar`) == "foo"); - assert (relativePath(`c:\foo\bar`, `c:\foo\bar`) == "."); - assert (relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`); - assert (relativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`); - assert (relativePath(`c:/foo/bar/baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`); - assert (relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); - assert (relativePath(`c:\foo\bar`, `d:\foo`) == `c:\foo\bar`); - assert (relativePath(`\\foo\bar`, `c:\foo`) == `\\foo\bar`); - assertThrown(relativePath(`c:\foo`, "bar")); - - assertCTFEable!( - { - assert (relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`); - }); + assert(asRelativePath(`\\foo\bar`.bCU, `c:\foo`.bCU).array == `\\foo\bar`); + assert(asRelativePath(`\\foo\bar`w.bCU, `c:\foo`w.bCU).array == `\\foo\bar`w); + assert(asRelativePath(`\\foo\bar`d.bCU, `c:\foo`d.bCU).array == `\\foo\bar`d); } - else static assert (0); } - - - -/** Compares filename characters and return $(D < 0) if $(D a < b), $(D 0) if - $(D a == b) and $(D > 0) if $(D a > b). +/** Compares filename characters. This function can perform a case-sensitive or a case-insensitive comparison. This is controlled through the $(D cs) template parameter - which, if not specified, is given by - $(LREF CaseSensitive)$(D .osDefault). + which, if not specified, is given by $(LREF CaseSensitive)$(D .osDefault). On Windows, the backslash and slash characters ($(D `\`) and $(D `/`)) are considered equal. - Examples: - --- - assert (filenameCharCmp('a', 'a') == 0); - assert (filenameCharCmp('a', 'b') < 0); - assert (filenameCharCmp('b', 'a') > 0); + Params: + cs = Case-sensitivity of the comparison. + a = A filename character. + b = A filename character. - version (linux) - { - // Same as calling filenameCharCmp!(CaseSensitive.yes)(a, b) - assert (filenameCharCmp('A', 'a') < 0); - assert (filenameCharCmp('a', 'A') > 0); - } - version (Windows) - { - // Same as calling filenameCharCmp!(CaseSensitive.no)(a, b) - assert (filenameCharCmp('a', 'A') == 0); - assert (filenameCharCmp('a', 'B') < 0); - assert (filenameCharCmp('A', 'b') < 0); - } - --- + Returns: + $(D < 0) if $(D a < b), + $(D 0) if $(D a == b), and + $(D > 0) if $(D a > b). */ int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b) @safe pure nothrow @@ -2253,117 +2998,176 @@ int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b if (isDirSeparator(a) && isDirSeparator(b)) return 0; static if (!cs) { - import std.uni; + import std.uni : toLower; a = toLower(a); b = toLower(b); } return cast(int)(a - b); } - -unittest +/// +@safe unittest { - assert (filenameCharCmp!(CaseSensitive.yes)('a', 'a') == 0); - assert (filenameCharCmp!(CaseSensitive.yes)('a', 'b') < 0); - assert (filenameCharCmp!(CaseSensitive.yes)('b', 'a') > 0); - assert (filenameCharCmp!(CaseSensitive.yes)('A', 'a') < 0); - assert (filenameCharCmp!(CaseSensitive.yes)('a', 'A') > 0); - - assert (filenameCharCmp!(CaseSensitive.no)('a', 'a') == 0); - assert (filenameCharCmp!(CaseSensitive.no)('a', 'b') < 0); - assert (filenameCharCmp!(CaseSensitive.no)('b', 'a') > 0); - assert (filenameCharCmp!(CaseSensitive.no)('A', 'a') == 0); - assert (filenameCharCmp!(CaseSensitive.no)('a', 'A') == 0); - assert (filenameCharCmp!(CaseSensitive.no)('a', 'B') < 0); - assert (filenameCharCmp!(CaseSensitive.no)('B', 'a') > 0); - assert (filenameCharCmp!(CaseSensitive.no)('A', 'b') < 0); - assert (filenameCharCmp!(CaseSensitive.no)('b', 'A') > 0); + assert(filenameCharCmp('a', 'a') == 0); + assert(filenameCharCmp('a', 'b') < 0); + assert(filenameCharCmp('b', 'a') > 0); - version (Posix) assert (filenameCharCmp('\\', '/') != 0); - version (Windows) assert (filenameCharCmp('\\', '/') == 0); + version (linux) + { + // Same as calling filenameCharCmp!(CaseSensitive.yes)(a, b) + assert(filenameCharCmp('A', 'a') < 0); + assert(filenameCharCmp('a', 'A') > 0); + } + version (Windows) + { + // Same as calling filenameCharCmp!(CaseSensitive.no)(a, b) + assert(filenameCharCmp('a', 'A') == 0); + assert(filenameCharCmp('a', 'B') < 0); + assert(filenameCharCmp('A', 'b') < 0); + } } +@safe unittest +{ + assert(filenameCharCmp!(CaseSensitive.yes)('A', 'a') < 0); + assert(filenameCharCmp!(CaseSensitive.yes)('a', 'A') > 0); + + assert(filenameCharCmp!(CaseSensitive.no)('a', 'a') == 0); + assert(filenameCharCmp!(CaseSensitive.no)('a', 'b') < 0); + assert(filenameCharCmp!(CaseSensitive.no)('b', 'a') > 0); + assert(filenameCharCmp!(CaseSensitive.no)('A', 'a') == 0); + assert(filenameCharCmp!(CaseSensitive.no)('a', 'A') == 0); + assert(filenameCharCmp!(CaseSensitive.no)('a', 'B') < 0); + assert(filenameCharCmp!(CaseSensitive.no)('B', 'a') > 0); + assert(filenameCharCmp!(CaseSensitive.no)('A', 'b') < 0); + assert(filenameCharCmp!(CaseSensitive.no)('b', 'A') > 0); + version (Posix) assert(filenameCharCmp('\\', '/') != 0); + version (Windows) assert(filenameCharCmp('\\', '/') == 0); +} /** Compares file names and returns - $(D < 0) if $(D filename1 < filename2), - $(D 0) if $(D filename1 == filename2) and - $(D > 0) if $(D filename1 > filename2). Individual characters are compared using $(D filenameCharCmp!cs), where $(D cs) is an optional template parameter determining whether - the comparison is case sensitive or not. See the - $(LREF filenameCharCmp) documentation for details. + the comparison is case sensitive or not. - Examples: - --- - assert (filenameCmp("abc", "abc") == 0); - assert (filenameCmp("abc", "abd") < 0); - assert (filenameCmp("abc", "abb") > 0); - assert (filenameCmp("abc", "abcd") < 0); - assert (filenameCmp("abcd", "abc") > 0); + Treatment of invalid UTF encodings is implementation defined. + + Params: + cs = case sensitivity + filename1 = range for first file name + filename2 = range for second file name + + Returns: + $(D < 0) if $(D filename1 < filename2), + $(D 0) if $(D filename1 == filename2) and + $(D > 0) if $(D filename1 > filename2). + + See_Also: + $(LREF filenameCharCmp) +*/ +int filenameCmp(CaseSensitive cs = CaseSensitive.osDefault, Range1, Range2) + (Range1 filename1, Range2 filename2) +if (isInputRange!Range1 && !isInfinite!Range1 && + isSomeChar!(ElementEncodingType!Range1) && + !isConvertibleToString!Range1 && + isInputRange!Range2 && !isInfinite!Range2 && + isSomeChar!(ElementEncodingType!Range2) && + !isConvertibleToString!Range2) +{ + alias C1 = Unqual!(ElementEncodingType!Range1); + alias C2 = Unqual!(ElementEncodingType!Range2); + + static if (!cs && (C1.sizeof < 4 || C2.sizeof < 4) || + C1.sizeof != C2.sizeof) + { + // Case insensitive - decode so case is checkable + // Different char sizes - decode to bring to common type + import std.utf : byDchar; + return filenameCmp!cs(filename1.byDchar, filename2.byDchar); + } + else static if (isSomeString!Range1 && C1.sizeof < 4 || + isSomeString!Range2 && C2.sizeof < 4) + { + // Avoid autodecoding + import std.utf : byCodeUnit; + return filenameCmp!cs(filename1.byCodeUnit, filename2.byCodeUnit); + } + else + { + for (;;) + { + if (filename1.empty) return -(cast(int) !filename2.empty); + if (filename2.empty) return 1; + const c = filenameCharCmp!cs(filename1.front, filename2.front); + if (c != 0) return c; + filename1.popFront(); + filename2.popFront(); + } + } +} + +/// +@safe unittest +{ + assert(filenameCmp("abc", "abc") == 0); + assert(filenameCmp("abc", "abd") < 0); + assert(filenameCmp("abc", "abb") > 0); + assert(filenameCmp("abc", "abcd") < 0); + assert(filenameCmp("abcd", "abc") > 0); version (linux) { // Same as calling filenameCmp!(CaseSensitive.yes)(filename1, filename2) - assert (filenameCmp("Abc", "abc") < 0); - assert (filenameCmp("abc", "Abc") > 0); + assert(filenameCmp("Abc", "abc") < 0); + assert(filenameCmp("abc", "Abc") > 0); } version (Windows) { // Same as calling filenameCmp!(CaseSensitive.no)(filename1, filename2) - assert (filenameCmp("Abc", "abc") == 0); - assert (filenameCmp("abc", "Abc") == 0); - assert (filenameCmp("Abc", "abD") < 0); - assert (filenameCmp("abc", "AbB") > 0); - } - --- -*/ -int filenameCmp(CaseSensitive cs = CaseSensitive.osDefault, C1, C2) - (const(C1)[] filename1, const(C2)[] filename2) - @safe pure //TODO: nothrow (because of std.array.front()) - if (isSomeChar!C1 && isSomeChar!C2) -{ - for (;;) - { - if (filename1.empty) return -(cast(int) !filename2.empty); - if (filename2.empty) return (cast(int) !filename1.empty); - auto c = filenameCharCmp!cs(filename1.front, filename2.front); - if (c != 0) return c; - filename1.popFront(); - filename2.popFront(); + assert(filenameCmp("Abc", "abc") == 0); + assert(filenameCmp("abc", "Abc") == 0); + assert(filenameCmp("Abc", "abD") < 0); + assert(filenameCmp("abc", "AbB") > 0); } - assert (0); } - -unittest +int filenameCmp(CaseSensitive cs = CaseSensitive.osDefault, Range1, Range2) + (auto ref Range1 filename1, auto ref Range2 filename2) +if (isConvertibleToString!Range1 || isConvertibleToString!Range2) { - assert (filenameCmp!(CaseSensitive.yes)("abc", "abc") == 0); - assert (filenameCmp!(CaseSensitive.yes)("abc", "abd") < 0); - assert (filenameCmp!(CaseSensitive.yes)("abc", "abb") > 0); - assert (filenameCmp!(CaseSensitive.yes)("abc", "abcd") < 0); - assert (filenameCmp!(CaseSensitive.yes)("abcd", "abc") > 0); - assert (filenameCmp!(CaseSensitive.yes)("Abc", "abc") < 0); - assert (filenameCmp!(CaseSensitive.yes)("abc", "Abc") > 0); - - assert (filenameCmp!(CaseSensitive.no)("abc", "abc") == 0); - assert (filenameCmp!(CaseSensitive.no)("abc", "abd") < 0); - assert (filenameCmp!(CaseSensitive.no)("abc", "abb") > 0); - assert (filenameCmp!(CaseSensitive.no)("abc", "abcd") < 0); - assert (filenameCmp!(CaseSensitive.no)("abcd", "abc") > 0); - assert (filenameCmp!(CaseSensitive.no)("Abc", "abc") == 0); - assert (filenameCmp!(CaseSensitive.no)("abc", "Abc") == 0); - assert (filenameCmp!(CaseSensitive.no)("Abc", "abD") < 0); - assert (filenameCmp!(CaseSensitive.no)("abc", "AbB") > 0); + import std.meta : staticMap; + alias Types = staticMap!(convertToString, Range1, Range2); + return filenameCmp!(cs, Types)(filename1, filename2); +} - version (Posix) assert (filenameCmp(`abc\def`, `abc/def`) != 0); - version (Windows) assert (filenameCmp(`abc\def`, `abc/def`) == 0); +@safe unittest +{ + assert(filenameCmp!(CaseSensitive.yes)(TestAliasedString("Abc"), "abc") < 0); + assert(filenameCmp!(CaseSensitive.yes)("Abc", TestAliasedString("abc")) < 0); + assert(filenameCmp!(CaseSensitive.yes)(TestAliasedString("Abc"), TestAliasedString("abc")) < 0); } +@safe unittest +{ + assert(filenameCmp!(CaseSensitive.yes)("Abc", "abc") < 0); + assert(filenameCmp!(CaseSensitive.yes)("abc", "Abc") > 0); + assert(filenameCmp!(CaseSensitive.no)("abc", "abc") == 0); + assert(filenameCmp!(CaseSensitive.no)("abc", "abd") < 0); + assert(filenameCmp!(CaseSensitive.no)("abc", "abb") > 0); + assert(filenameCmp!(CaseSensitive.no)("abc", "abcd") < 0); + assert(filenameCmp!(CaseSensitive.no)("abcd", "abc") > 0); + assert(filenameCmp!(CaseSensitive.no)("Abc", "abc") == 0); + assert(filenameCmp!(CaseSensitive.no)("abc", "Abc") == 0); + assert(filenameCmp!(CaseSensitive.no)("Abc", "abD") < 0); + assert(filenameCmp!(CaseSensitive.no)("abc", "AbB") > 0); + version (Posix) assert(filenameCmp(`abc\def`, `abc/def`) != 0); + version (Windows) assert(filenameCmp(`abc\def`, `abc/def`) == 0); +} /** Matches a pattern against a path. @@ -2394,161 +3198,210 @@ unittest separators and dots don't stop a meta-character from matching further portions of the path. + Params: + cs = Whether the matching should be case-sensitive + path = The path to be matched against + pattern = The glob pattern + Returns: $(D true) if pattern matches path, $(D false) otherwise. See_also: $(LINK2 http://en.wikipedia.org/wiki/Glob_%28programming%29,Wikipedia: _glob (programming)) - - Examples: - ----- - assert (globMatch("foo.bar", "*")); - assert (globMatch("foo.bar", "*.*")); - assert (globMatch(`foo/foo\bar`, "f*b*r")); - assert (globMatch("foo.bar", "f???bar")); - assert (globMatch("foo.bar", "[fg]???bar")); - assert (globMatch("foo.bar", "[!gh]*bar")); - assert (globMatch("bar.fooz", "bar.{foo,bif}z")); - assert (globMatch("bar.bifz", "bar.{foo,bif}z")); - - version (Windows) - { - // Same as calling globMatch!(CaseSensitive.no)(path, pattern) - assert (globMatch("foo", "Foo")); - assert (globMatch("Goo.bar", "[fg]???bar")); - } - version (linux) - { - // Same as calling globMatch!(CaseSensitive.yes)(path, pattern) - assert (!globMatch("foo", "Foo")); - assert (!globMatch("Goo.bar", "[fg]???bar")); - } - ----- */ -bool globMatch(CaseSensitive cs = CaseSensitive.osDefault, C) - (const(C)[] path, const(C)[] pattern) +bool globMatch(CaseSensitive cs = CaseSensitive.osDefault, C, Range) + (Range path, const(C)[] pattern) @safe pure nothrow - if (isSomeChar!C) +if (isForwardRange!Range && !isInfinite!Range && + isSomeChar!(ElementEncodingType!Range) && !isConvertibleToString!Range && + isSomeChar!C && is(Unqual!C == Unqual!(ElementEncodingType!Range))) in { // Verify that pattern[] is valid - import std.algorithm : balancedParens; + import std.algorithm.searching : balancedParens; assert(balancedParens(pattern, '[', ']', 0)); assert(balancedParens(pattern, '{', '}', 0)); } body { - size_t ni; // current character in path + alias RC = Unqual!(ElementEncodingType!Range); - foreach (ref pi; 0 .. pattern.length) + static if (RC.sizeof == 1 && isSomeString!Range) + { + import std.utf : byChar; + return globMatch!cs(path.byChar, pattern); + } + else static if (RC.sizeof == 2 && isSomeString!Range) + { + import std.utf : byWchar; + return globMatch!cs(path.byWchar, pattern); + } + else { - C pc = pattern[pi]; - switch (pc) + C[] pattmp; + foreach (ref pi; 0 .. pattern.length) { - case '*': - if (pi + 1 == pattern.length) - return true; - foreach (j; ni .. path.length) - { - if (globMatch!(cs, C)(path[j .. path.length], - pattern[pi + 1 .. pattern.length])) + const pc = pattern[pi]; + switch (pc) + { + case '*': + if (pi + 1 == pattern.length) return true; - } - return false; - - case '?': - if (ni == path.length) - return false; - ni++; - break; - - case '[': - if (ni == path.length) - return false; - auto nc = path[ni]; - ni++; - auto not = false; - pi++; - if (pattern[pi] == '!') - { - not = true; - pi++; - } - auto anymatch = false; - while (1) - { - pc = pattern[pi]; - if (pc == ']') - break; - if (!anymatch && (filenameCharCmp!cs(nc, pc) == 0)) - anymatch = true; - pi++; - } - if (anymatch == not) + for (; !path.empty; path.popFront()) + { + auto p = path.save; + if (globMatch!(cs, C)(p, + pattern[pi + 1 .. pattern.length])) + return true; + } return false; - break; - case '{': - // find end of {} section - auto piRemain = pi; - for (; piRemain < pattern.length - && pattern[piRemain] != '}'; piRemain++) - {} - - if (piRemain < pattern.length) piRemain++; - pi++; + case '?': + if (path.empty) + return false; + path.popFront(); + break; - while (pi < pattern.length) - { - auto pi0 = pi; - pc = pattern[pi]; - // find end of current alternative - for (; pi= FILENAME_MAX) return false; @@ -2652,7 +3517,7 @@ bool isValidFilename(R)(R filename) { if (c == 0 || c == '/') return false; } - else static assert (0); + else static assert(0); } version (Windows) { @@ -2664,7 +3529,27 @@ bool isValidFilename(R)(R filename) return true; } +/// +@safe pure @nogc nothrow +unittest +{ + import std.utf : byCodeUnit; + + assert(isValidFilename("hello.exe".byCodeUnit)); +} + +bool isValidFilename(Range)(auto ref Range filename) +if (isConvertibleToString!Range) +{ + return isValidFilename!(StringTypeOf!Range)(filename); +} + +@safe unittest +{ + assert(testAliasedString!isValidFilename("hello.exe")); +} +@safe pure unittest { import std.conv; @@ -2673,16 +3558,16 @@ unittest auto pfdep = [`foo\bar`, "*.txt"]; version (Windows) invalid ~= pfdep; else version (Posix) valid ~= pfdep; - else static assert (0); + else static assert(0); - import std.typetuple; - foreach (T; TypeTuple!(char[], const(char)[], string, wchar[], + import std.meta : AliasSeq; + foreach (T; AliasSeq!(char[], const(char)[], string, wchar[], const(wchar)[], wstring, dchar[], const(dchar)[], dstring)) { foreach (fn; valid) - assert (isValidFilename(to!T(fn))); + assert(isValidFilename(to!T(fn))); foreach (fn; invalid) - assert (!isValidFilename(to!T(fn))); + assert(!isValidFilename(to!T(fn))); } { @@ -2692,8 +3577,20 @@ unittest static struct DirEntry { string s; alias s this; } assert(isValidFilename(DirEntry("file.ext"))); -} + version (Windows) + { + immutable string cases = "<>:\"/\\|?*"; + foreach (i; 0 .. 31 + cases.length) + { + char[3] buf; + buf[0] = 'a'; + buf[1] = i <= 31 ? cast(char) i : cases[i - 32]; + buf[2] = 'b'; + assert(!isValidFilename(buf[])); + } + } +} @@ -2702,8 +3599,9 @@ unittest Generally, this function checks that $(D path) is not empty, and that each component of the path either satisfies $(LREF isValidFilename) or is equal to $(D ".") or $(D ".."). - It does $(I not) check whether the _path points to an existing file - or directory; use $(XREF file,exists) for this purpose. + + $(B It does $(I not) check whether the _path points to an existing file + or directory; use $(REF exists, std,file) for this purpose.) On Windows, some special rules apply: $(UL @@ -2720,16 +3618,27 @@ unittest this function returns $(D false); such paths are beyond the scope of this module.) ) + + Params: + path = string or Range of characters to check + + Returns: + true if $(D path) is a valid _path. */ -bool isValidPath(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) +bool isValidPath(Range)(Range path) +if ((isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && isSomeChar!(ElementEncodingType!Range) || + isNarrowString!Range) && + !isConvertibleToString!Range) { + alias C = Unqual!(ElementEncodingType!Range); + if (path.empty) return false; // Check whether component is "." or "..", or whether it satisfies // isValidFilename. - bool isValidComponent(in C[] component) @safe pure nothrow + bool isValidComponent(Range component) { - assert (component.length > 0); + assert(component.length > 0); if (component[0] == '.') { if (component.length == 1) return true; @@ -2741,7 +3650,7 @@ bool isValidPath(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) if (path.length == 1) return isDirSeparator(path[0]) || isValidComponent(path); - const(C)[] remainder; + Range remainder; version (Windows) { if (isDirSeparator(path[0]) && isDirSeparator(path[1])) @@ -2783,7 +3692,7 @@ bool isValidPath(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) } else if (isDriveSeparator(path[1])) { - import std.ascii; + import std.ascii : isAlpha; if (!isAlpha(path[0])) return false; remainder = path[2 .. $]; } @@ -2796,8 +3705,7 @@ bool isValidPath(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) { remainder = path; } - else static assert (0); - assert (remainder !is null); + else static assert(0); remainder = ltrimDirSeparators(remainder); // Check that each component satisfies isValidComponent. @@ -2805,7 +3713,7 @@ bool isValidPath(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) { size_t i = 0; while (i < remainder.length && !isDirSeparator(remainder[i])) ++i; - assert (i > 0); + assert(i > 0); if (!isValidComponent(remainder[0 .. i])) return false; remainder = ltrimDirSeparators(remainder[i .. $]); } @@ -2814,40 +3722,55 @@ bool isValidPath(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) return true; } - +/// +@safe pure @nogc nothrow unittest { - assert (isValidPath("/foo/bar")); - assert (!isValidPath("/foo\0/bar")); + assert(isValidPath("/foo/bar")); + assert(!isValidPath("/foo\0/bar")); + assert(isValidPath("/")); + assert(isValidPath("a")); version (Windows) { - assert (isValidPath(`c:\`)); - assert (isValidPath(`c:\foo`)); - assert (isValidPath(`c:\foo\.\bar\\\..\`)); - assert (!isValidPath(`!:\foo`)); - assert (!isValidPath(`c::\foo`)); - assert (!isValidPath(`c:\foo?`)); - assert (!isValidPath(`c:\foo.`)); + assert(isValidPath(`c:\`)); + assert(isValidPath(`c:\foo`)); + assert(isValidPath(`c:\foo\.\bar\\\..\`)); + assert(!isValidPath(`!:\foo`)); + assert(!isValidPath(`c::\foo`)); + assert(!isValidPath(`c:\foo?`)); + assert(!isValidPath(`c:\foo.`)); - assert (isValidPath(`\\server\share`)); - assert (isValidPath(`\\server\share\foo`)); - assert (isValidPath(`\\server\share\\foo`)); - assert (!isValidPath(`\\\server\share\foo`)); - assert (!isValidPath(`\\server\\share\foo`)); - assert (!isValidPath(`\\ser*er\share\foo`)); - assert (!isValidPath(`\\server\sha?e\foo`)); - assert (!isValidPath(`\\server\share\|oo`)); + assert(isValidPath(`\\server\share`)); + assert(isValidPath(`\\server\share\foo`)); + assert(isValidPath(`\\server\share\\foo`)); + assert(!isValidPath(`\\\server\share\foo`)); + assert(!isValidPath(`\\server\\share\foo`)); + assert(!isValidPath(`\\ser*er\share\foo`)); + assert(!isValidPath(`\\server\sha?e\foo`)); + assert(!isValidPath(`\\server\share\|oo`)); - assert (isValidPath(`\\?\<>:"?*|/\..\.`)); - assert (!isValidPath("\\\\?\\foo\0bar")); + assert(isValidPath(`\\?\<>:"?*|/\..\.`)); + assert(!isValidPath("\\\\?\\foo\0bar")); - assert (!isValidPath(`\\.\PhysicalDisk1`)); + assert(!isValidPath(`\\.\PhysicalDisk1`)); + assert(!isValidPath(`\\`)); } -} + import std.utf : byCodeUnit; + assert(isValidPath("/foo/bar".byCodeUnit)); +} +bool isValidPath(Range)(auto ref Range path) +if (isConvertibleToString!Range) +{ + return isValidPath!(StringTypeOf!Range)(path); +} +@safe unittest +{ + assert(testAliasedString!isValidPath("/foo/bar")); +} /** Performs tilde expansion in paths on POSIX systems. On Windows, this function does nothing. @@ -2875,12 +3798,15 @@ unittest This function performs several memory allocations. + Params: + inputPath = The path name to expand. + Returns: $(D inputPath) with the tilde expanded, or just $(D inputPath) if it could not be expanded. For Windows, $(D expandTilde) merely returns its argument $(D inputPath). - Examples: + Example: ----- void processFile(string path) { @@ -2890,14 +3816,12 @@ unittest } ----- */ -string expandTilde(string inputPath) +string expandTilde(string inputPath) nothrow { version(Posix) { - import core.stdc.string : strlen; - import core.stdc.stdlib : getenv, malloc, free; + import core.stdc.stdlib : malloc, free, realloc; import core.exception : onOutOfMemoryError; - import core.sys.posix.pwd : passwd, getpwnam_r; import core.stdc.errno : errno, ERANGE; /* Joins a path from a C string to the remainder of path. @@ -2906,37 +3830,43 @@ string expandTilde(string inputPath) is joined to path[char_pos .. length] if char_pos is smaller than length, otherwise path is not appended to c_path. */ - static string combineCPathWithDPath(char* c_path, string path, size_t char_pos) + static string combineCPathWithDPath(char* c_path, string path, size_t char_pos) nothrow { + import core.stdc.string : strlen; + assert(c_path != null); assert(path.length > 0); assert(char_pos >= 0); // Search end of C string - size_t end = core.stdc.string.strlen(c_path); + size_t end = strlen(c_path); // Remove trailing path separator, if any if (end && isDirSeparator(c_path[end - 1])) end--; - // Create our own copy, as lifetime of c_path is undocumented - string cp = c_path[0 .. end].idup; - - // Do we append something from path? + // (this is the only GC allocation done in expandTilde()) + string cp; if (char_pos < path.length) - cp ~= path[char_pos .. $]; + // Append something from path + cp = cast(string)(c_path[0 .. end] ~ path[char_pos .. $]); + else + // Create our own copy, as lifetime of c_path is undocumented + cp = c_path[0 .. end].idup; return cp; } // Replaces the tilde from path with the environment variable HOME. - static string expandFromEnvironment(string path) + static string expandFromEnvironment(string path) nothrow { + import core.stdc.stdlib : getenv; + assert(path.length >= 1); assert(path[0] == '~'); // Get HOME and use that to replace the tilde. - auto home = core.stdc.stdlib.getenv("HOME"); + auto home = getenv("HOME"); if (home == null) return path; @@ -2944,80 +3874,76 @@ string expandTilde(string inputPath) } // Replaces the tilde from path with the path from the user database. - static string expandFromDatabase(string path) + static string expandFromDatabase(string path) nothrow { - // Android doesn't really support this, as getpwnam_r + // bionic doesn't really support this, as getpwnam_r // isn't provided and getpwnam is basically just a stub - version(Android) + version(CRuntime_Bionic) { return path; } else { + import core.sys.posix.pwd : passwd, getpwnam_r; import std.string : indexOf; assert(path.length > 2 || (path.length == 2 && !isDirSeparator(path[1]))); assert(path[0] == '~'); // Extract username, searching for path separator. - string username; auto last_char = indexOf(path, dirSeparator[0]); + size_t username_len = (last_char == -1) ? path.length : last_char; + char* username = cast(char*) malloc(username_len * char.sizeof); + if (!username) + onOutOfMemoryError(); + scope(exit) free(username); + if (last_char == -1) { - username = path[1 .. $] ~ '\0'; - last_char = username.length + 1; + username[0 .. username_len - 1] = path[1 .. $]; + last_char = path.length + 1; } else { - username = path[1 .. last_char] ~ '\0'; + username[0 .. username_len - 1] = path[1 .. last_char]; } + username[username_len - 1] = 0; + assert(last_char > 1); // Reserve C memory for the getpwnam_r() function. - passwd result; int extra_memory_size = 5 * 1024; - void* extra_memory; + char* extra_memory; + scope(exit) free(extra_memory); + passwd result; while (1) { - extra_memory = core.stdc.stdlib.malloc(extra_memory_size); + extra_memory = cast(char*) realloc(extra_memory, extra_memory_size * char.sizeof); if (extra_memory == null) - goto Lerror; + onOutOfMemoryError(); // Obtain info from database. passwd *verify; errno = 0; - if (getpwnam_r(cast(char*) username.ptr, &result, cast(char*) extra_memory, extra_memory_size, + if (getpwnam_r(username, &result, extra_memory, extra_memory_size, &verify) == 0) { - // Failure if verify doesn't point at result. - if (verify != &result) - // username is not found, so return path[] - goto Lnotfound; + // Succeeded if verify points at result + if (verify == &result) + // username is found + path = combineCPathWithDPath(result.pw_dir, path, last_char); break; } if (errno != ERANGE) - goto Lerror; + onOutOfMemoryError(); // extra_memory isn't large enough - core.stdc.stdlib.free(extra_memory); extra_memory_size *= 2; } - - path = combineCPathWithDPath(result.pw_dir, path, last_char); - - Lnotfound: - core.stdc.stdlib.free(extra_memory); return path; - - Lerror: - // Errors are going to be caused by running out of memory - if (extra_memory) - core.stdc.stdlib.free(extra_memory); - onOutOfMemoryError(); - return null; } } @@ -3042,8 +3968,8 @@ string expandTilde(string inputPath) } -version(unittest) import std.process: environment; -unittest +version(unittest) import std.process : environment; +@system unittest { version (Posix) { @@ -3118,6 +4044,31 @@ version (unittest) static assert( isRandomAccessRange!(MockRange!(const(char))) ); } +version (unittest) +{ + /* Define a mock BidirectionalRange to use for unittesting. + */ + + struct MockBiRange(C) + { + this(const(C)[] array) { this.array = array; } + const + { + @property bool empty() { return array.length == 0; } + @property C front() { return array[0]; } + @property C back() { return array[$ - 1]; } + @property size_t opDollar() { return array.length; } + } + void popFront() { array = array[1 .. $]; } + void popBack() { array = array[0 .. $-1]; } + @property MockBiRange save() { return this; } + private: + const(C)[] array; + } + + static assert( isBidirectionalRange!(MockBiRange!(const(char))) ); +} + private template BaseOf(R) { static if (isRandomAccessRange!R && isSomeChar!(ElementType!R)) diff --git a/std/process.d b/std/process.d index 5c03ff8f58e..e0182cb40be 100644 --- a/std/process.d +++ b/std/process.d @@ -72,15 +72,14 @@ $(LI Authors: $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad), $(LINK2 https://github.com/schveiguy, Steven Schveighoffer), - $(WEB thecybershadow.net, Vladimir Panteleev) + $(HTTP thecybershadow.net, Vladimir Panteleev) Copyright: Copyright (c) 2013, the authors. All rights reserved. License: - $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Source: $(PHOBOSSRC std/_process.d) Macros: - WIKI=Phobos/StdProcess OBJECTREF=$(D $(LINK2 object.html#$0,$0)) LREF=$(D $(LINK2 #.$0,$0)) */ @@ -88,9 +87,6 @@ module std.process; version (Posix) { - import core.stdc.errno; - import core.stdc.string; - import core.sys.posix.stdio; import core.sys.posix.unistd; import core.sys.posix.sys.wait; } @@ -103,9 +99,6 @@ version (Windows) } import std.range.primitives; -import std.conv; -import std.exception; -import std.path; import std.stdio; import std.internal.processinit; import std.internal.cstring; @@ -151,7 +144,7 @@ version (Posix) extern(C) extern __gshared const char** environ; } - unittest + @system unittest { new Thread({assert(environ !is null);}).start(); } @@ -226,7 +219,7 @@ wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv)); Standard_streams: The optional arguments $(D stdin), $(D stdout) and $(D stderr) may -be used to assign arbitrary $(XREF stdio,File) objects as the standard +be used to assign arbitrary $(REF File, std,stdio) objects as the standard input, output and error streams, respectively, of the child process. The former must be opened for reading, while the latter two must be opened for writing. The default is for the child process to inherit the standard @@ -261,14 +254,14 @@ Params: args = An array which contains the program name as the zeroth element and any command-line arguments in the following elements. stdin = The standard input stream of the child process. - This can be any $(XREF stdio,File) that is opened for reading. + This can be any $(REF File, std,stdio) that is opened for reading. By default the child process inherits the parent's input stream. stdout = The standard output stream of the child process. - This can be any $(XREF stdio,File) that is opened for writing. + This can be any $(REF File, std,stdio) that is opened for writing. By default the child process inherits the parent's output stream. stderr = The standard error stream of the child process. - This can be any $(XREF stdio,File) that is opened for writing. + This can be any $(REF File, std,stdio) that is opened for writing. By default the child process inherits the parent's error stream. env = Additional environment variables for the child process. config = Flags that control process creation. See $(LREF Config) @@ -282,9 +275,9 @@ A $(LREF Pid) object that corresponds to the spawned process. Throws: $(LREF ProcessException) on failure to start the process.$(BR) -$(XREF stdio,StdioException) on failure to pass one of the streams +$(REF StdioException, std,stdio) on failure to pass one of the streams to the child process (Windows only).$(BR) -$(CXREF exception,RangeError) if $(D args) is empty. +$(REF RangeError, core,exception) if $(D args) is empty. */ Pid spawnProcess(in char[][] args, File stdin = std.stdio.stdin, @@ -356,9 +349,10 @@ private Pid spawnProcessImpl(in char[][] args, in char[] workDir) @trusted // TODO: Should be @safe { - import core.exception: RangeError; + import core.exception : RangeError; + import std.conv : text; import std.path : isDirSeparator; - import std.algorithm : any; + import std.algorithm.searching : any; import std.string : toStringz; if (args.empty) throw new RangeError(); @@ -392,7 +386,7 @@ private Pid spawnProcessImpl(in char[][] args, scope(exit) if (workDirFD >= 0) close(workDirFD); if (workDir.length) { - import core.sys.posix.fcntl; + import core.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR; workDirFD = open(workDir.tempCString(), O_RDONLY); if (workDirFD < 0) throw ProcessException.newFromErrno("Failed to open working directory"); @@ -400,7 +394,7 @@ private Pid spawnProcessImpl(in char[][] args, if (fstat(workDirFD, &s) < 0) throw ProcessException.newFromErrno("Failed to stat working directory"); if (!S_ISDIR(s.st_mode)) - throw new ProcessException("Not a directory: " ~ cast(string)workDir); + throw new ProcessException("Not a directory: " ~ cast(string) workDir); } int getFD(ref File f) { return core.stdc.stdio.fileno(f.getFP()); } @@ -412,11 +406,15 @@ private Pid spawnProcessImpl(in char[][] args, auto stdoutFD = getFD(stdout); auto stderrFD = getFD(stderr); - auto id = fork(); + auto id = core.sys.posix.unistd.fork(); if (id < 0) throw ProcessException.newFromErrno("Failed to spawn new process"); - if (id == 0) + + void forkChild() nothrow @nogc { + static import core.sys.posix.stdio; + pragma(inline, true); + // Child process // Set the working directory. @@ -450,17 +448,54 @@ private Pid spawnProcessImpl(in char[][] args, setCLOEXEC(STDERR_FILENO, false); if (!(config & Config.inheritFDs)) { - import core.sys.posix.sys.resource; + import core.sys.posix.poll : pollfd, poll, POLLNVAL; + import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; + import core.stdc.stdlib : malloc; + + // Get the maximum number of file descriptors that could be open. rlimit r; - getrlimit(RLIMIT_NOFILE, &r); - foreach (i; 3 .. cast(int) r.rlim_cur) close(i); + if (getrlimit(RLIMIT_NOFILE, &r) != 0) + { + core.sys.posix.stdio.perror("getrlimit"); + core.sys.posix.unistd._exit(1); + assert(0); + } + immutable maxDescriptors = cast(int) r.rlim_cur; + + // The above, less stdin, stdout, and stderr + immutable maxToClose = maxDescriptors - 3; + + // Call poll() to see which ones are actually open: + pollfd* pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); + foreach (i; 0 .. maxToClose) + { + pfds[i].fd = i + 3; + pfds[i].events = 0; + pfds[i].revents = 0; + } + if (poll(pfds, maxToClose, 0) >= 0) + { + foreach (i; 0 .. maxToClose) + { + // POLLNVAL will be set if the file descriptor is invalid. + if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); + } + } + else + { + // Fall back to closing everything. + foreach (i; 3 .. maxDescriptors) close(i); + } } + else + { // This is already done if we don't inherit descriptors. - // Close the old file descriptors, unless they are - // either of the standard streams. - if (stdinFD > STDERR_FILENO) close(stdinFD); - if (stdoutFD > STDERR_FILENO) close(stdoutFD); - if (stderrFD > STDERR_FILENO) close(stderrFD); + // Close the old file descriptors, unless they are + // either of the standard streams. + if (stdinFD > STDERR_FILENO) close(stdinFD); + if (stdoutFD > STDERR_FILENO) close(stdoutFD); + if (stderrFD > STDERR_FILENO) close(stderrFD); + } // Execute program. core.sys.posix.unistd.execve(argz[0], argz.ptr, envz); @@ -468,7 +503,12 @@ private Pid spawnProcessImpl(in char[][] args, // If execution fails, exit as quickly as possible. core.sys.posix.stdio.perror("spawnProcess(): Failed to execute program"); core.sys.posix.unistd._exit(1); - assert (0); + } + + if (id == 0) + { + forkChild(); + assert(0); } else { @@ -505,7 +545,7 @@ private Pid spawnProcessImpl(in char[] commandLine, in char[] workDir) @trusted { - import core.exception: RangeError; + import core.exception : RangeError; if (commandLine.empty) throw new RangeError("Command line is empty"); @@ -515,8 +555,6 @@ private Pid spawnProcessImpl(in char[] commandLine, // Startup info for CreateProcessW(). STARTUPINFO_W startinfo; startinfo.cb = startinfo.sizeof; - startinfo.dwFlags = STARTF_USESTDHANDLES; - static int getFD(ref File f) { return f.isOpen ? f.fileno : -1; } // Extract file descriptors and HANDLEs from the streams and make the @@ -525,8 +563,12 @@ private Pid spawnProcessImpl(in char[] commandLine, out int fileDescriptor, out HANDLE handle) { fileDescriptor = getFD(file); - if (fileDescriptor < 0) handle = GetStdHandle(stdHandle); - else handle = file.windowsHandle; + handle = null; + if (fileDescriptor >= 0) + handle = file.windowsHandle; + // Windows GUI applications have a fd but not a valid Windows HANDLE. + if (handle is null || handle == INVALID_HANDLE_VALUE) + handle = GetStdHandle(stdHandle); DWORD dwFlags; if (GetHandleInformation(handle, &dwFlags)) @@ -550,13 +592,19 @@ private Pid spawnProcessImpl(in char[] commandLine, prepareStream(stdout, STD_OUTPUT_HANDLE, "stdout", stdoutFD, startinfo.hStdOutput); prepareStream(stderr, STD_ERROR_HANDLE, "stderr", stderrFD, startinfo.hStdError ); + if ((startinfo.hStdInput != null && startinfo.hStdInput != INVALID_HANDLE_VALUE) + || (startinfo.hStdOutput != null && startinfo.hStdOutput != INVALID_HANDLE_VALUE) + || (startinfo.hStdError != null && startinfo.hStdError != INVALID_HANDLE_VALUE)) + startinfo.dwFlags = STARTF_USESTDHANDLES; + // Create process. PROCESS_INFORMATION pi; DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | ((config & Config.suppressConsole) ? CREATE_NO_WINDOW : 0); + auto pworkDir = workDir.tempCStringW(); // workaround until Bugzilla 14696 is fixed if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr, null, null, true, dwCreationFlags, - envz, workDir.length ? workDir.tempCStringW() : null, &startinfo, &pi)) + envz, workDir.length ? pworkDir : null, &startinfo, &pi)) throw ProcessException.newFromLastError("Failed to spawn new process"); // figure out if we should close any of the streams @@ -613,25 +661,25 @@ private const(char*)* createEnv(const string[string] childEnv, return envz.ptr; } -version (Posix) unittest +version (Posix) @system unittest { auto e1 = createEnv(null, false); - assert (e1 != null && *e1 == null); + assert(e1 != null && *e1 == null); auto e2 = createEnv(null, true); - assert (e2 != null); + assert(e2 != null); int i = 0; for (; environ[i] != null; ++i) { - assert (e2[i] != null); + assert(e2[i] != null); import core.stdc.string; - assert (strcmp(e2[i], environ[i]) == 0); + assert(strcmp(e2[i], environ[i]) == 0); } - assert (e2[i] == null); + assert(e2[i] == null); auto e3 = createEnv(["foo" : "bar", "hello" : "world"], false); - assert (e3 != null && e3[0] != null && e3[1] != null && e3[2] == null); - assert ((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0") + assert(e3 != null && e3[0] != null && e3[1] != null && e3[2] == null); + assert((e3[0][0 .. 8] == "foo=bar\0" && e3[1][0 .. 12] == "hello=world\0") || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0")); } @@ -677,12 +725,12 @@ private LPVOID createEnv(const string[string] childEnv, return envz.data.ptr; } -version (Windows) unittest +version (Windows) @system unittest { - assert (createEnv(null, true) == null); - assert ((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w); + assert(createEnv(null, true) == null); + assert((cast(wchar*) createEnv(null, false))[0 .. 2] == "\0\0"w); auto e1 = (cast(wchar*) createEnv(["foo":"bar", "ab":"c"], false))[0 .. 14]; - assert (e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w); + assert(e1 == "FOO=bar\0AB=c\0\0"w || e1 == "AB=c\0FOO=bar\0\0"w); } // Searches the PATH variable for the given executable file, @@ -691,7 +739,9 @@ version (Posix) private string searchPathFor(in char[] executable) @trusted //TODO: @safe nothrow { - import std.algorithm : splitter; + import std.conv : to; + import std.algorithm.iteration : splitter; + import std.path : buildPath; auto pathz = core.stdc.stdlib.getenv("PATH"); if (pathz == null) return null; @@ -713,22 +763,22 @@ private bool isExecutable(in char[] path) @trusted nothrow @nogc //TODO: @safe return (access(path.tempCString(), X_OK) == 0); } -version (Posix) unittest +version (Posix) @safe unittest { import std.algorithm; - auto unamePath = searchPathFor("uname"); - assert (!unamePath.empty); - assert (unamePath[0] == '/'); - assert (unamePath.endsWith("uname")); + auto lsPath = searchPathFor("ls"); + assert(!lsPath.empty); + assert(lsPath[0] == '/'); + assert(lsPath.endsWith("ls")); auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm"); - assert (unlikely is null, "Are you kidding me?"); + assert(unlikely is null, "Are you kidding me?"); } // Sets or unsets the FD_CLOEXEC flag on the given file descriptor. version (Posix) -private void setCLOEXEC(int fd, bool on) +private void setCLOEXEC(int fd, bool on) nothrow @nogc { - import core.sys.posix.fcntl; + import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD; auto flags = fcntl(fd, F_GETFD); if (flags >= 0) { @@ -736,10 +786,10 @@ private void setCLOEXEC(int fd, bool on) else flags &= ~(cast(typeof(flags)) FD_CLOEXEC); flags = fcntl(fd, F_SETFD, flags); } - assert (flags != -1 || .errno == EBADF); + assert(flags != -1 || .errno == EBADF); } -unittest // Command line arguments in spawnProcess(). +@system unittest // Command line arguments in spawnProcess(). { version (Windows) TestScript prog = "if not [%~1]==[foo] ( exit 1 ) @@ -749,14 +799,87 @@ unittest // Command line arguments in spawnProcess(). `if test "$1" != "foo"; then exit 1; fi if test "$2" != "bar"; then exit 2; fi exit 0`; - assert (wait(spawnProcess(prog.path)) == 1); - assert (wait(spawnProcess([prog.path])) == 1); - assert (wait(spawnProcess([prog.path, "foo"])) == 2); - assert (wait(spawnProcess([prog.path, "foo", "baz"])) == 2); - assert (wait(spawnProcess([prog.path, "foo", "bar"])) == 0); + assert(wait(spawnProcess(prog.path)) == 1); + assert(wait(spawnProcess([prog.path])) == 1); + assert(wait(spawnProcess([prog.path, "foo"])) == 2); + assert(wait(spawnProcess([prog.path, "foo", "baz"])) == 2); + assert(wait(spawnProcess([prog.path, "foo", "bar"])) == 0); +} + +// test that file descriptors are correctly closed / left open. +// ideally this would be done by the child process making libc +// calls, but we make do... +version (Posix) @system unittest +{ + import core.sys.posix.fcntl : open, O_RDONLY; + import core.sys.posix.unistd : close; + import std.path : buildPath; + import std.algorithm.searching : canFind, findSplitBefore; + import std.functional : reverseArgs; + import std.array : split; + import std.conv : to; + static import std.file; + + auto directory = uniqueTempPath(); + std.file.mkdir(directory); + scope(exit) std.file.rmdirRecurse(directory); + auto path = buildPath(directory, "tmp"); + std.file.write(path, null); + auto fd = open(path.tempCString, O_RDONLY); + scope(exit) close(fd); + + // command >&2 (or any other number) checks whethether that number + // file descriptor is open. + // Can't use this for arbitrary descriptors as many shells only support + // single digit fds. + TestScript testDefaults = `command >&0 && command >&1 && command >&2`; + assert(execute(testDefaults.path).status == 0); + assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0); + + // try /proc//fd/ on linux + version (linux) + { + TestScript proc = "ls /proc/$$/fd"; + auto procRes = execute(proc.path, null); + if (procRes.status == 0) + { + auto fdStr = fd.to!string; + assert(!procRes.output.split.canFind(fdStr)); + assert(execute(proc.path, null, Config.inheritFDs) + .output.split.canFind(fdStr)); + return; + } + } + + // try fuser (might sometimes need permissions) + TestScript fuser = "echo $$ && fuser -f " ~ path; + auto fuserRes = execute(fuser.path, null); + if (fuserRes.status == 0) + { + assert(!reverseArgs!canFind(fuserRes + .output.findSplitBefore("\n").expand)); + assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs) + .output.findSplitBefore("\n").expand)); + return; + } + + // last resort, try lsof (not available on all Posix) + TestScript lsof = "lsof -p$$"; + auto lsofRes = execute(lsof.path, null); + if (lsofRes.status == 0) + { + assert(!lsofRes.output.canFind(path)); + assert(execute(lsof.path, null, Config.inheritFDs).output.canFind(path)); + return; + } + + std.stdio.stderr.writeln(__FILE__, ':', __LINE__, + ": Warning: Couldn't find any way to check open files"); + // DON'T DO ANY MORE TESTS BELOW HERE IN THIS UNITTEST BLOCK, THE ABOVE + // TESTS RETURN ON SUCCESS } -unittest // Environment variables in spawnProcess(). +@system unittest // Environment variables in spawnProcess(). { // We really should use set /a on Windows, but Wine doesn't support it. version (Windows) TestScript envProg = @@ -781,29 +904,30 @@ unittest // Environment variables in spawnProcess(). environment.remove("std_process_unittest1"); // Just in case. environment.remove("std_process_unittest2"); - assert (wait(spawnProcess(envProg.path)) == 0); - assert (wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); + assert(wait(spawnProcess(envProg.path)) == 0); + assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); environment["std_process_unittest1"] = "1"; - assert (wait(spawnProcess(envProg.path)) == 1); - assert (wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); + assert(wait(spawnProcess(envProg.path)) == 1); + assert(wait(spawnProcess(envProg.path, null, Config.newEnv)) == 0); auto env = ["std_process_unittest2" : "2"]; - assert (wait(spawnProcess(envProg.path, env)) == 3); - assert (wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2); + assert(wait(spawnProcess(envProg.path, env)) == 3); + assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 2); env["std_process_unittest1"] = "4"; - assert (wait(spawnProcess(envProg.path, env)) == 6); - assert (wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); + assert(wait(spawnProcess(envProg.path, env)) == 6); + assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); environment.remove("std_process_unittest1"); - assert (wait(spawnProcess(envProg.path, env)) == 6); - assert (wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); + assert(wait(spawnProcess(envProg.path, env)) == 6); + assert(wait(spawnProcess(envProg.path, env, Config.newEnv)) == 6); } -unittest // Stream redirection in spawnProcess(). +@system unittest // Stream redirection in spawnProcess(). { import std.string; + import std.path : buildPath; version (Windows) TestScript prog = "set /p INPUT= echo %INPUT% output %~1 @@ -821,8 +945,8 @@ unittest // Stream redirection in spawnProcess(). pipei.readEnd, pipeo.writeEnd, pipee.writeEnd); pipei.writeEnd.writeln("input"); pipei.writeEnd.flush(); - assert (pipeo.readEnd.readln().chomp() == "input output foo"); - assert (pipee.readEnd.readln().chomp().stripRight() == "input error bar"); + assert(pipeo.readEnd.readln().chomp() == "input output foo"); + assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar"); wait(pid); // Files @@ -836,21 +960,23 @@ unittest // Stream redirection in spawnProcess(). auto filee = File(pathe, "w"); pid = spawnProcess([prog.path, "bar", "baz" ], filei, fileo, filee); wait(pid); - assert (readText(patho).chomp() == "INPUT output bar"); - assert (readText(pathe).chomp().stripRight() == "INPUT error baz"); + assert(readText(patho).chomp() == "INPUT output bar"); + assert(readText(pathe).chomp().stripRight() == "INPUT error baz"); remove(pathi); remove(patho); remove(pathe); } -unittest // Error handling in spawnProcess() +@system unittest // Error handling in spawnProcess() { + import std.exception : assertThrown; assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf")); assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf")); } -unittest // Specifying a working directory. +@system unittest // Specifying a working directory. { + import std.path; TestScript prog = "echo foo>bar"; auto directory = uniqueTempPath(); @@ -862,8 +988,9 @@ unittest // Specifying a working directory. assert(exists(buildPath(directory, "bar"))); } -unittest // Specifying a bad working directory. +@system unittest // Specifying a bad working directory. { + import std.exception : assertThrown; TestScript prog = "echo"; auto directory = uniqueTempPath(); @@ -874,7 +1001,7 @@ unittest // Specifying a bad working directory. assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); } -unittest // Specifying empty working directory. +@system unittest // Specifying empty working directory. { TestScript prog = ""; @@ -883,7 +1010,7 @@ unittest // Specifying empty working directory. spawnProcess([prog.path], null, Config.none, directory).wait(); } -unittest // Reopening the standard streams (issue 13258) +@system unittest // Reopening the standard streams (issue 13258) { import std.string; void fun() @@ -908,6 +1035,19 @@ unittest // Reopening the standard streams (issue 13258) assert(lines == ["foo", "bar"]); } +version (Windows) +@system unittest // MSVCRT workaround (issue 14422) +{ + auto fn = uniqueTempPath(); + std.file.write(fn, "AAAAAAAAAA"); + + auto f = File(fn, "a"); + spawnProcess(["cmd", "/c", "echo BBBBB"], std.stdio.stdin, f).wait(); + + auto data = readText(fn); + assert(data == "AAAAAAAAAABBBBB\r\n", data); +} + /** A variation on $(LREF spawnProcess) that runs the given _command through the current user's preferred _command interpreter (aka. shell). @@ -915,8 +1055,7 @@ the current user's preferred _command interpreter (aka. shell). The string $(D command) is passed verbatim to the shell, and is therefore subject to its rules about _command structure, argument/filename quoting and escaping of special characters. -The path to the shell executable is determined by the $(LREF userShell) -function. +The path to the shell executable defaults to $(LREF nativeShell). In all other respects this function works just like $(D spawnProcess). Please refer to the $(LREF spawnProcess) documentation for descriptions @@ -939,7 +1078,8 @@ Pid spawnShell(in char[] command, File stderr = std.stdio.stderr, const string[string] env = null, Config config = Config.none, - in char[] workDir = null) + in char[] workDir = null, + string shellPath = nativeShell) @trusted // TODO: Should be @safe { version (Windows) @@ -948,13 +1088,13 @@ Pid spawnShell(in char[] command, // It does not use CommandLineToArgvW. // Instead, it treats the first and last quote specially. // See CMD.EXE /? for details. - auto args = escapeShellFileName(userShell) + auto args = escapeShellFileName(shellPath) ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`; } else version (Posix) { const(char)[][3] args; - args[0] = userShell; + args[0] = shellPath; args[1] = shellSwitch; args[2] = command; } @@ -965,7 +1105,8 @@ Pid spawnShell(in char[] command, Pid spawnShell(in char[] command, const string[string] env, Config config = Config.none, - in char[] workDir = null) + in char[] workDir = null, + string shellPath = nativeShell) @trusted // TODO: Should be @safe { return spawnShell(command, @@ -974,10 +1115,11 @@ Pid spawnShell(in char[] command, std.stdio.stderr, env, config, - workDir); + workDir, + shellPath); } -unittest +@system unittest { version (Windows) auto cmd = "echo %FOO%"; @@ -988,17 +1130,17 @@ unittest scope(exit) if (exists(tmpFile)) remove(tmpFile); auto redir = "> \""~tmpFile~'"'; auto env = ["foo" : "bar"]; - assert (wait(spawnShell(cmd~redir, env)) == 0); + assert(wait(spawnShell(cmd~redir, env)) == 0); auto f = File(tmpFile, "a"); version(CRuntime_Microsoft) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before - assert (wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0); + assert(wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0); f.close(); auto output = std.file.readText(tmpFile); - assert (output == "bar\nbar\n" || output == "bar\r\nbar\r\n"); + assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n"); } version (Windows) -unittest +@system unittest { import std.string; TestScript prog = "echo %0 %*"; @@ -1142,7 +1284,7 @@ private: { if (_processID == terminated) return _exitCode; int exitCode; - while(true) + while (true) { int status; auto check = waitpid(_processID, &status, block ? 0 : WNOHANG); @@ -1157,7 +1299,7 @@ private: { // waitpid() was interrupted by a signal. We simply // restart it. - assert (errno == EINTR); + assert(errno == EINTR); continue; } } @@ -1189,7 +1331,7 @@ private: int performWait(bool block) @trusted { if (_processID == terminated) return _exitCode; - assert (_handle != INVALID_HANDLE_VALUE); + assert(_handle != INVALID_HANDLE_VALUE); if (block) { auto result = WaitForSingleObject(_handle, INFINITE); @@ -1207,7 +1349,7 @@ private: ~this() { - if(_handle != INVALID_HANDLE_VALUE) + if (_handle != INVALID_HANDLE_VALUE) { CloseHandle(_handle); _handle = INVALID_HANDLE_VALUE; @@ -1253,7 +1395,7 @@ its exit status. In general one should always _wait for child processes to terminate before exiting the parent process. Otherwise, they may become -"$(WEB en.wikipedia.org/wiki/Zombie_process,zombies)" – processes +"$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)" – processes that are defunct, yet still occupy a slot in the OS process table. If the process has already terminated, this function returns directly. @@ -1271,7 +1413,7 @@ Signal codes are defined in the $(D core.sys.posix.signal) module Throws: $(LREF ProcessException) on failure. -Examples: +Example: See the $(LREF spawnProcess) documentation. See_also: @@ -1284,21 +1426,21 @@ int wait(Pid pid) @safe } -unittest // Pid and wait() +@system unittest // Pid and wait() { version (Windows) TestScript prog = "exit %~1"; else version (Posix) TestScript prog = "exit $1"; - assert (wait(spawnProcess([prog.path, "0"])) == 0); - assert (wait(spawnProcess([prog.path, "123"])) == 123); + assert(wait(spawnProcess([prog.path, "0"])) == 0); + assert(wait(spawnProcess([prog.path, "123"])) == 123); auto pid = spawnProcess([prog.path, "10"]); - assert (pid.processID > 0); - version (Windows) assert (pid.osHandle != INVALID_HANDLE_VALUE); - else version (Posix) assert (pid.osHandle == pid.processID); - assert (wait(pid) == 10); - assert (wait(pid) == 10); // cached exit code - assert (pid.processID < 0); - version (Windows) assert (pid.osHandle == INVALID_HANDLE_VALUE); - else version (Posix) assert (pid.osHandle < 0); + assert(pid.processID > 0); + version (Windows) assert(pid.osHandle != INVALID_HANDLE_VALUE); + else version (Posix) assert(pid.osHandle == pid.processID); + assert(wait(pid) == 10); + assert(wait(pid) == 10); // cached exit code + assert(pid.processID < 0); + version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE); + else version (Posix) assert(pid.osHandle < 0); } @@ -1379,7 +1521,7 @@ used by Windows to signal that a process has in fact $(I not) terminated yet. --- auto pid = spawnProcess("some_app"); kill(pid, 10); -assert (wait(pid) == 10); +assert(wait(pid) == 10); --- POSIX_specific: @@ -1394,10 +1536,10 @@ $(D SIGTERM) signal will be sent. (This matches the behaviour of the $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html, $(D _kill)) shell command.) --- -import core.sys.posix.signal: SIGKILL; +import core.sys.posix.signal : SIGKILL; auto pid = spawnProcess("some_app"); kill(pid, SIGKILL); -assert (wait(pid) == -SIGKILL); // Negative return value on POSIX! +assert(wait(pid) == -SIGKILL); // Negative return value on POSIX! --- Throws: @@ -1410,7 +1552,7 @@ void kill(Pid pid) version (Windows) kill(pid, 1); else version (Posix) { - import core.sys.posix.signal: SIGTERM; + import core.sys.posix.signal : SIGTERM; kill(pid, SIGTERM); } } @@ -1430,15 +1572,16 @@ void kill(Pid pid, int codeOrSignal) } else version (Posix) { - import core.sys.posix.signal; + import core.sys.posix.signal : kill; if (kill(pid.osHandle, codeOrSignal) == -1) throw ProcessException.newFromErrno(); } } -unittest // tryWait() and kill() +@system unittest // tryWait() and kill() { import core.thread; + import std.exception : assertThrown; // The test script goes into an infinite loop. version (Windows) { @@ -1447,25 +1590,30 @@ unittest // tryWait() and kill() } else version (Posix) { - import core.sys.posix.signal: SIGTERM, SIGKILL; + import core.sys.posix.signal : SIGTERM, SIGKILL; TestScript prog = "while true; do sleep 1; done"; } auto pid = spawnProcess(prog.path); - Thread.sleep(dur!"seconds"(1)); + // Android appears to automatically kill sleeping processes very quickly, + // so shorten the wait before killing here. + version (Android) + Thread.sleep(dur!"msecs"(5)); + else + Thread.sleep(dur!"seconds"(1)); kill(pid); - version (Windows) assert (wait(pid) == 1); - else version (Posix) assert (wait(pid) == -SIGTERM); + version (Windows) assert(wait(pid) == 1); + else version (Posix) assert(wait(pid) == -SIGTERM); pid = spawnProcess(prog.path); Thread.sleep(dur!"seconds"(1)); auto s = tryWait(pid); - assert (!s.terminated && s.status == 0); + assert(!s.terminated && s.status == 0); assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed. version (Windows) kill(pid, 123); else version (Posix) kill(pid, SIGKILL); do { s = tryWait(pid); } while (!s.terminated); - version (Windows) assert (s.status == 123); - else version (Posix) assert (s.status == -SIGKILL); + version (Windows) assert(s.status == 123); + else version (Posix) assert(s.status == -SIGKILL); assertThrown!ProcessException(kill(pid)); } @@ -1477,7 +1625,8 @@ Data is written to one end of the _pipe and read from the other. --- auto p = pipe(); p.writeEnd.writeln("Hello World"); -assert (p.readEnd.readln().chomp() == "Hello World"); +p.writeEnd.flush(); +assert(p.readEnd.readln().chomp() == "Hello World"); --- Pipes can, for example, be used for interprocess communication by spawning a new process and passing one end of the _pipe to @@ -1502,11 +1651,12 @@ Returns: A $(LREF Pipe) object that corresponds to the created _pipe. Throws: -$(XREF stdio,StdioException) on failure. +$(REF StdioException, std,stdio) on failure. */ version (Posix) Pipe pipe() @trusted //TODO: @safe { + import core.sys.posix.stdio : fdopen; int[2] fds; if (core.sys.posix.unistd.pipe(fds) != 0) throw new StdioException("Unable to create pipe"); @@ -1569,7 +1719,7 @@ struct Pipe /** Closes both ends of the pipe. - Normally it is not necessary to do this manually, as $(XREF stdio,File) + Normally it is not necessary to do this manually, as $(REF File, std,stdio) objects are automatically closed when there are no more references to them. @@ -1578,7 +1728,7 @@ struct Pipe child process is platform dependent.) Throws: - $(XREF exception,ErrnoException) if an error occurs. + $(REF ErrnoException, std,exception) if an error occurs. */ void close() @safe { @@ -1590,16 +1740,16 @@ private: File _read, _write; } -unittest +@system unittest { import std.string; auto p = pipe(); p.writeEnd.writeln("Hello World"); p.writeEnd.flush(); - assert (p.readEnd.readln().chomp() == "Hello World"); + assert(p.readEnd.readln().chomp() == "Hello World"); p.close(); - assert (!p.readEnd.isOpen); - assert (!p.writeEnd.isOpen); + assert(!p.readEnd.isOpen); + assert(!p.writeEnd.isOpen); } @@ -1621,34 +1771,36 @@ parameters are forwarded straight to the underlying spawn functions, and we refer to their documentation for details. Params: -args = An array which contains the program name as the zeroth element - and any command-line arguments in the following elements. - (See $(LREF spawnProcess) for details.) -program = The program name, $(I without) command-line arguments. - (See $(LREF spawnProcess) for details.) -command = A shell command which is passed verbatim to the command - interpreter. (See $(LREF spawnShell) for details.) -redirect = Flags that determine which streams are redirected, and - how. See $(LREF Redirect) for an overview of available - flags. -env = Additional environment variables for the child process. - (See $(LREF spawnProcess) for details.) -config = Flags that control process creation. See $(LREF Config) - for an overview of available flags, and note that the - $(D retainStd...) flags have no effect in this function. -workDir = The working directory for the new process. - By default the child process inherits the parent's working - directory. +args = An array which contains the program name as the zeroth element + and any command-line arguments in the following elements. + (See $(LREF spawnProcess) for details.) +program = The program name, $(I without) command-line arguments. + (See $(LREF spawnProcess) for details.) +command = A shell command which is passed verbatim to the command + interpreter. (See $(LREF spawnShell) for details.) +redirect = Flags that determine which streams are redirected, and + how. See $(LREF Redirect) for an overview of available + flags. +env = Additional environment variables for the child process. + (See $(LREF spawnProcess) for details.) +config = Flags that control process creation. See $(LREF Config) + for an overview of available flags, and note that the + $(D retainStd...) flags have no effect in this function. +workDir = The working directory for the new process. + By default the child process inherits the parent's working + directory. +shellPath = The path to the shell to use to run the specified program. + By default this is $(LREF nativeShell). Returns: -A $(LREF ProcessPipes) object which contains $(XREF stdio,File) +A $(LREF ProcessPipes) object which contains $(REF File, std,stdio) handles that communicate with the redirected streams of the child process, along with a $(LREF Pid) object that corresponds to the spawned process. Throws: $(LREF ProcessException) on failure to start the process.$(BR) -$(XREF stdio,StdioException) on failure to redirect any of the streams.$(BR) +$(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR) Example: --- @@ -1713,19 +1865,26 @@ ProcessPipes pipeShell(in char[] command, Redirect redirect = Redirect.all, const string[string] env = null, Config config = Config.none, - in char[] workDir = null) + in char[] workDir = null, + string shellPath = nativeShell) @safe { - return pipeProcessImpl!spawnShell(command, redirect, env, config, workDir); + return pipeProcessImpl!spawnShell(command, + redirect, + env, + config, + workDir, + shellPath); } // Implementation of the pipeProcess() family of functions. -private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd) +private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...) (Cmd command, Redirect redirectFlags, const string[string] env = null, Config config = Config.none, - in char[] workDir = null) + in char[] workDir = null, + ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init) @trusted //TODO: @safe { File childStdin, childStdout, childStderr; @@ -1792,7 +1951,7 @@ private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd) config &= ~(Config.retainStdin | Config.retainStdout | Config.retainStderr); pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr, - env, config, workDir); + env, config, workDir, extraArgs); return pipes; } @@ -1828,7 +1987,7 @@ enum Redirect stdoutToStderr = 16, } -unittest +@system unittest { import std.string; version (Windows) TestScript prog = @@ -1854,39 +2013,40 @@ unittest auto pp = pipeProcess([prog.path, "bar", "baz"]); pp.stdin.writeln("foo"); pp.stdin.flush(); - assert (pp.stdout.readln().chomp() == "foo bar"); - assert (pp.stderr.readln().chomp().stripRight() == "foo baz"); + assert(pp.stdout.readln().chomp() == "foo bar"); + assert(pp.stderr.readln().chomp().stripRight() == "foo baz"); pp.stdin.writeln("1234567890"); pp.stdin.flush(); - assert (pp.stdout.readln().chomp() == "1234567890 bar"); - assert (pp.stderr.readln().chomp().stripRight() == "1234567890 baz"); + assert(pp.stdout.readln().chomp() == "1234567890 bar"); + assert(pp.stderr.readln().chomp().stripRight() == "1234567890 baz"); pp.stdin.writeln("stop"); pp.stdin.flush(); - assert (wait(pp.pid) == 2); + assert(wait(pp.pid) == 2); pp = pipeProcess([prog.path, "12345", "67890"], Redirect.stdin | Redirect.stdout | Redirect.stderrToStdout); pp.stdin.writeln("xyz"); pp.stdin.flush(); - assert (pp.stdout.readln().chomp() == "xyz 12345"); - assert (pp.stdout.readln().chomp().stripRight() == "xyz 67890"); + assert(pp.stdout.readln().chomp() == "xyz 12345"); + assert(pp.stdout.readln().chomp().stripRight() == "xyz 67890"); pp.stdin.writeln("stop"); pp.stdin.flush(); - assert (wait(pp.pid) == 1); + assert(wait(pp.pid) == 1); pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"), Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr); pp.stdin.writeln("ab"); pp.stdin.flush(); - assert (pp.stderr.readln().chomp() == "ab AAAAA"); - assert (pp.stderr.readln().chomp().stripRight() == "ab BBB"); + assert(pp.stderr.readln().chomp() == "ab AAAAA"); + assert(pp.stderr.readln().chomp().stripRight() == "ab BBB"); pp.stdin.writeln("stop"); pp.stdin.flush(); - assert (wait(pp.pid) == 1); + assert(wait(pp.pid) == 1); } -unittest +@system unittest { + import std.exception : assertThrown; TestScript prog = "exit 0"; assertThrown!StdioException(pipeProcess( prog.path, @@ -1905,7 +2065,7 @@ unittest } /** -Object which contains $(XREF stdio,File) handles that allow communication +Object which contains $(REF File, std,stdio) handles that allow communication with a child process through its standard streams. */ struct ProcessPipes @@ -1918,7 +2078,7 @@ struct ProcessPipes } /** - An $(XREF stdio,File) that allows writing to the child process' + An $(REF File, std,stdio) that allows writing to the child process' standard input stream. Throws: @@ -1934,7 +2094,7 @@ struct ProcessPipes } /** - An $(XREF stdio,File) that allows reading from the child process' + An $(REF File, std,stdio) that allows reading from the child process' standard output stream. Throws: @@ -1950,7 +2110,7 @@ struct ProcessPipes } /** - An $(XREF stdio,File) that allows reading from the child process' + An $(REF File, std,stdio) that allows reading from the child process' standard error stream. Throws: @@ -2013,6 +2173,9 @@ maxOutput = The maximum number of bytes of output that should be workDir = The working directory for the new process. By default the child process inherits the parent's working directory. +shellPath = The path to the shell to use to run the specified program. + By default this is $(LREF nativeShell). + Returns: An $(D std.typecons.Tuple!(int, "status", string, "output")). @@ -2024,7 +2187,7 @@ value is the signal number. (See $(LREF wait) for details.) Throws: $(LREF ProcessException) on failure to start the process.$(BR) -$(XREF stdio,StdioException) on failure to capture output. +$(REF StdioException, std,stdio) on failure to capture output. */ auto execute(in char[][] args, const string[string] env = null, @@ -2052,27 +2215,33 @@ auto executeShell(in char[] command, const string[string] env = null, Config config = Config.none, size_t maxOutput = size_t.max, - in char[] workDir = null) + in char[] workDir = null, + string shellPath = nativeShell) @trusted //TODO: @safe { - return executeImpl!pipeShell(command, env, config, maxOutput, workDir); + return executeImpl!pipeShell(command, + env, + config, + maxOutput, + workDir, + shellPath); } // Does the actual work for execute() and executeShell(). -private auto executeImpl(alias pipeFunc, Cmd)( +private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( Cmd commandLine, const string[string] env = null, Config config = Config.none, size_t maxOutput = size_t.max, - in char[] workDir = null) + in char[] workDir = null, + ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init) { - import std.string; import std.typecons : Tuple; import std.array : appender; - import std.algorithm : min; + import std.algorithm.comparison : min; auto p = pipeFunc(commandLine, Redirect.stdout | Redirect.stderrToStdout, - env, config, workDir); + env, config, workDir, extraArgs); auto a = appender!(ubyte[])(); enum size_t defaultChunkSize = 4096; @@ -2096,7 +2265,7 @@ private auto executeImpl(alias pipeFunc, Cmd)( return Tuple!(int, "status", string, "output")(wait(p.pid), cast(string) a.data); } -unittest +@system unittest { import std.string; // To avoid printing the newline characters, we use the echo|set trick on @@ -2105,33 +2274,37 @@ unittest "echo|set /p=%~1 echo|set /p=%~2 1>&2 exit 123"; + else version (Android) TestScript prog = + `echo -n $1 + echo -n $2 >&2 + exit 123`; else version (Posix) TestScript prog = `printf '%s' $1 printf '%s' $2 >&2 exit 123`; auto r = execute([prog.path, "foo", "bar"]); - assert (r.status == 123); - assert (r.output.stripRight() == "foobar"); + assert(r.status == 123); + assert(r.output.stripRight() == "foobar"); auto s = execute([prog.path, "Hello", "World"]); - assert (s.status == 123); - assert (s.output.stripRight() == "HelloWorld"); + assert(s.status == 123); + assert(s.output.stripRight() == "HelloWorld"); } -unittest +@safe unittest { import std.string; auto r1 = executeShell("echo foo"); - assert (r1.status == 0); - assert (r1.output.chomp() == "foo"); + assert(r1.status == 0); + assert(r1.output.chomp() == "foo"); auto r2 = executeShell("echo bar 1>&2"); - assert (r2.status == 0); - assert (r2.output.chomp().stripRight() == "bar"); + assert(r2.status == 0); + assert(r2.output.chomp().stripRight() == "bar"); auto r3 = executeShell("exit 123"); - assert (r3.status == 123); - assert (r3.output.empty); + assert(r3.status == 123); + assert(r3.output.empty); } -unittest +@safe unittest { import std.typecons : Tuple; void foo() //Just test the compilation @@ -2144,32 +2317,30 @@ unittest } } - /// An exception that signals a problem with starting or waiting for a process. class ProcessException : Exception { - // Standard constructor. - this(string msg, string file = __FILE__, size_t line = __LINE__) - { - super(msg, file, line); - } + import std.exception : basicExceptionCtors; + mixin basicExceptionCtors; // Creates a new ProcessException based on errno. static ProcessException newFromErrno(string customMsg = null, string file = __FILE__, size_t line = __LINE__) { - import core.stdc.errno; - import core.stdc.string; - version (linux) + import core.stdc.errno : errno; + import std.conv : to; + version (CRuntime_Glibc) { + import core.stdc.string : strerror_r; char[1024] buf; auto errnoMsg = to!string( core.stdc.string.strerror_r(errno, buf.ptr, buf.length)); } else { - auto errnoMsg = to!string(core.stdc.string.strerror(errno)); + import core.stdc.string : strerror; + auto errnoMsg = to!string(strerror(errno)); } auto msg = customMsg.empty ? errnoMsg : customMsg ~ " (" ~ errnoMsg ~ ')'; @@ -2191,30 +2362,49 @@ class ProcessException : Exception /** -Determines the path to the current user's default command interpreter. +Determines the path to the current user's preferred command interpreter. On Windows, this function returns the contents of the COMSPEC environment -variable, if it exists. Otherwise, it returns the string $(D "cmd.exe"). +variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell). On POSIX, $(D userShell) returns the contents of the SHELL environment -variable, if it exists and is non-empty. Otherwise, it returns -$(D "/bin/sh"). +variable, if it exists and is non-empty. Otherwise, it returns the result of +$(LREF nativeShell). */ @property string userShell() @safe { - version (Windows) return environment.get("COMSPEC", "cmd.exe"); + version (Windows) return environment.get("COMSPEC", nativeShell); + else version (Posix) return environment.get("SHELL", nativeShell); +} + +/** +The platform-specific native shell path. + +This function returns $(D "cmd.exe") on Windows, $(D "/bin/sh") on POSIX, and +$(D "/system/bin/sh") on Android. +*/ +@property string nativeShell() @safe @nogc pure nothrow +{ + version (Windows) return "cmd.exe"; else version (Android) return "/system/bin/sh"; else version (Posix) return "/bin/sh"; } - // A command-line switch that indicates to the shell that it should // interpret the following argument as a command to be executed. version (Posix) private immutable string shellSwitch = "-c"; version (Windows) private immutable string shellSwitch = "/C"; -/// Returns the process ID number of the current process. +/** + * Returns the process ID of the current process, + * which is guaranteed to be unique on the system. + * + * Example: + * --- + * writefln("Current process ID: %d", thisProcessID); + * --- + */ @property int thisProcessID() @trusted nothrow //TODO: @safe { version (Windows) return GetCurrentProcessId(); @@ -2222,6 +2412,51 @@ version (Windows) private immutable string shellSwitch = "/C"; } +/** + * Returns the process ID of the current thread, + * which is guaranteed to be unique within the current process. + * + * Returns: + * A $(REF ThreadID, core,thread) value for the calling thread. + * + * Example: + * --- + * writefln("Current thread ID: %s", thisThreadID); + * --- + */ +@property ThreadID thisThreadID() @trusted nothrow //TODO: @safe +{ + version (Windows) + return GetCurrentThreadId(); + else + version (Posix) + { + import core.sys.posix.pthread : pthread_self; + return pthread_self(); + } +} + + +@system unittest +{ + int pidA, pidB; + ThreadID tidA, tidB; + pidA = thisProcessID; + tidA = thisThreadID; + + import core.thread; + auto t = new Thread({ + pidB = thisProcessID; + tidB = thisThreadID; + }); + t.start(); + t.join(); + + assert(pidA == pidB); + assert(tidA != tidB); +} + + // Unittest support code: TestScript takes a string that contains a // shell script for the current platform, and writes it to a temporary // file. On Windows the file name gets a .cmd extension, while on @@ -2230,9 +2465,11 @@ version (Windows) private immutable string shellSwitch = "/C"; version (unittest) private struct TestScript { - this(string code) + this(string code) @system { - import std.ascii, std.file; + // @system due to chmod + import std.ascii : newline; + import std.file : write; version (Windows) { auto ext = ".cmd"; @@ -2241,21 +2478,20 @@ private struct TestScript else version (Posix) { auto ext = ""; - version(Android) auto firstLine = "#!" ~ userShell; - else auto firstLine = "#!/bin/sh"; + auto firstLine = "#!" ~ nativeShell; } path = uniqueTempPath()~ext; - std.file.write(path, firstLine~std.ascii.newline~code~std.ascii.newline); + write(path, firstLine ~ newline ~ code ~ newline); version (Posix) { - import core.sys.posix.sys.stat; + import core.sys.posix.sys.stat : chmod; chmod(path.tempCString(), octal!777); } } ~this() { - import std.file; + import std.file : remove, exists; if (!path.empty && exists(path)) { try { remove(path); } @@ -2270,9 +2506,11 @@ private struct TestScript } version (unittest) -private string uniqueTempPath() +private string uniqueTempPath() @safe { - import std.file, std.uuid; + import std.file : tempDir; + import std.path : buildPath; + import std.uuid : randomUUID; // Path should contain spaces to test escaping whitespace return buildPath(tempDir(), "std.process temporary file " ~ randomUUID().toString()); @@ -2349,7 +2587,7 @@ string escapeShellCommand(in char[][] args...) @safe pure } } -unittest +@safe unittest { // This is a simple unit test without any special requirements, // in addition to the unittest_burnin one below which requires @@ -2434,6 +2672,7 @@ private string escapeWindowsShellCommand(in char[] command) @safe pure private string escapeShellArguments(in char[][] args...) @trusted pure nothrow { + import std.exception : assumeUnique; char[] buf; @safe nothrow @@ -2446,7 +2685,7 @@ private string escapeShellArguments(in char[][] args...) auto p = buf.length; buf.length = buf.length + 1 + size; buf[p++] = ' '; - return buf[p..p+size]; + return buf[p .. p+size]; } } @@ -2476,7 +2715,7 @@ string escapeWindowsArgument(in char[] arg) @trusted pure nothrow // Rationale for leaving this function as public: // this algorithm of escaping paths is also used in other software, // e.g. DMD's response files. - + import std.exception : assumeUnique; auto buf = escapeWindowsArgumentImpl!charAllocator(arg); return assumeUnique(buf); } @@ -2490,7 +2729,7 @@ private char[] charAllocator(size_t size) @safe pure nothrow private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg) @safe nothrow - if (is(typeof(allocator(size_t.init)[0] = char.init))) +if (is(typeof(allocator(size_t.init)[0] = char.init))) { // References: // * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx @@ -2528,13 +2767,14 @@ private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg) } } + import std.ascii : isDigit; // Empty arguments need to be specified as "" if (!arg.length) needEscape = true; else // Arguments ending with digits need to be escaped, // to disambiguate with 1>file redirection syntax - if (std.ascii.isDigit(arg[$-1])) + if (isDigit(arg[$-1])) needEscape = true; if (!needEscape) @@ -2566,27 +2806,26 @@ private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg) version(Windows) version(unittest) { + import core.sys.windows.shellapi : CommandLineToArgvW; import core.sys.windows.windows; import core.stdc.stddef; + import core.stdc.wchar_ : wcslen; import std.array; - extern (Windows) wchar_t** CommandLineToArgvW(wchar_t*, int*); - extern (C) size_t wcslen(in wchar *); - string[] parseCommandLine(string line) { - import std.algorithm : map; + import std.algorithm.iteration : map; import std.array : array; LPWSTR lpCommandLine = (to!(wchar[])(line) ~ "\0"w).ptr; int numArgs; LPWSTR* args = CommandLineToArgvW(lpCommandLine, &numArgs); scope(exit) LocalFree(args); - return args[0..numArgs] - .map!(arg => to!string(arg[0..wcslen(arg)])) + return args[0 .. numArgs] + .map!(arg => to!string(arg[0 .. wcslen(arg)])) .array(); } - unittest + @system unittest { string[] testStrings = [ `Hello`, @@ -2608,7 +2847,7 @@ version(Windows) version(unittest) { auto q = escapeWindowsArgument(s); auto args = parseCommandLine("Dummy.exe " ~ q); - assert(args.length==2, s ~ " => " ~ q ~ " #" ~ text(args.length-1)); + assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1)); assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]); } } @@ -2616,13 +2855,14 @@ version(Windows) version(unittest) private string escapePosixArgument(in char[] arg) @trusted pure nothrow { + import std.exception : assumeUnique; auto buf = escapePosixArgumentImpl!charAllocator(arg); return assumeUnique(buf); } private char[] escapePosixArgumentImpl(alias allocator)(in char[] arg) @safe nothrow - if (is(typeof(allocator(size_t.init)[0] = char.init))) +if (is(typeof(allocator(size_t.init)[0] = char.init))) { // '\'' means: close quoted part of argument, append an escaped // single quote, and reopen quotes @@ -2641,7 +2881,7 @@ private char[] escapePosixArgumentImpl(alias allocator)(in char[] arg) foreach (char c; arg) if (c == '\'') { - buf[p..p+4] = `'\''`; + buf[p .. p+4] = `'\''`; p += 4; } else @@ -2683,7 +2923,7 @@ string escapeShellFileName(in char[] fileName) @trusted pure nothrow //version = unittest_burnin; version(unittest_burnin) -unittest +@system unittest { // There are no readily-available commands on all platforms suitable // for properly testing command escaping. The behavior of CMD's "echo" @@ -2716,23 +2956,25 @@ unittest e = escapeShellCommand(helper ~ s) ~ ">" ~ escapeShellFileName(fn); { - scope(failure) writefln("executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]); + scope(failure) writefln( + "executeShell() with redirect failed.\nExpected:\t%s\nFilename:\t%s\nEncoded:\t%s", s, [fn], [e]); auto result = executeShell(e); assert(result.status == 0, "std_process_unittest_helper failed"); assert(!result.output.length, "No output expected, got:\n" ~ result.output); g = readText(fn).split("\0")[1..$]; } remove(fn); - assert(s == g, format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); + assert(s == g, + format("executeShell() with redirect test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e])); } while (true) { string[] args; - foreach (n; 0..uniform(1, 4)) + foreach (n; 0 .. uniform(1, 4)) { string arg; - foreach (l; 0..uniform(0, 10)) + foreach (l; 0 .. uniform(0, 10)) { dchar c; while (true) @@ -2760,7 +3002,7 @@ unittest // generate filename string fn; - foreach (l; 0..uniform(1, 10)) + foreach (l; 0 .. uniform(1, 10)) { dchar c; while (true) @@ -2812,7 +3054,7 @@ static: Throws: $(OBJECTREF Exception) if the environment variable does not exist, - or $(XREF utf,UTFException) if the variable contains invalid UTF-16 + or $(REF UTFException, std,utf) if the variable contains invalid UTF-16 characters (Windows only). See_also: @@ -2820,6 +3062,7 @@ static: */ string opIndex(in char[] name) @safe { + import std.exception : enforce; string value; enforce(getImpl(name, value), "Environment variable not found: "~name); return value; @@ -2847,7 +3090,7 @@ static: --- Throws: - $(XREF utf,UTFException) if the variable contains invalid UTF-16 + $(REF UTFException, std,utf) if the variable contains invalid UTF-16 characters (Windows only). */ string get(in char[] name, string defaultValue = null) @safe @@ -2860,6 +3103,7 @@ static: /** Assigns the given $(D value) to the environment variable with the given $(D name). + If $(D value) is null the variable is removed from environment. If the variable does not exist, it will be created. If it already exists, it will be overwritten. @@ -2870,11 +3114,22 @@ static: Throws: $(OBJECTREF Exception) if the environment variable could not be added (e.g. if the name is invalid). + + Note: + On some platforms, modifying environment variables may not be allowed in + multi-threaded programs. See e.g. + $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc). */ inout(char)[] opIndexAssign(inout char[] value, in char[] name) @trusted { version (Posix) { + import std.exception : enforce, errnoEnforce; + if (value is null) + { + remove(name); + return value; + } if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1) { return value; @@ -2889,6 +3144,7 @@ static: } else version (Windows) { + import std.exception : enforce; enforce( SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()), sysErrorString(GetLastError()) @@ -2903,6 +3159,11 @@ static: If the variable isn't in the environment, this function returns successfully without doing anything. + + Note: + On some platforms, modifying environment variables may not be allowed in + multi-threaded programs. See e.g. + $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc). */ void remove(in char[] name) @trusted nothrow @nogc // TODO: @safe { @@ -2911,6 +3172,48 @@ static: else static assert(0); } + /** + Identify whether a variable is defined in the environment. + + Because it doesn't return the value, this function is cheaper than `get`. + However, if you do need the value as well, you should just check the + return of `get` for `null` instead of using this function first. + + Example: + ------------- + // good usage + if ("MY_ENV_FLAG" in environment) + doSomething(); + + // bad usage + if ("MY_ENV_VAR" in environment) + doSomething(environment["MY_ENV_VAR"]); + + // do this instead + if (auto var = environment.get("MY_ENV_VAR")) + doSomething(var); + ------------- + */ + bool opBinaryRight(string op : "in")(in char[] name) @trusted + { + version (Posix) + return core.sys.posix.stdlib.getenv(name.tempCString()) !is null; + else version (Windows) + { + SetLastError(NO_ERROR); + if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0) + return true; + immutable err = GetLastError(); + if (err == ERROR_ENVVAR_NOT_FOUND) + return false; + // some other windows error. Might actually be NO_ERROR, because + // GetEnvironmentVariable doesn't specify whether it sets on all + // failures + throw new WindowsException(err); + } + else static assert(0); + } + /** Copies all environment variables into an associative array. @@ -2925,14 +3228,17 @@ static: */ string[string] toAA() @trusted { + import std.conv : to; string[string] aa; version (Posix) { for (int i=0; environ[i] != null; ++i) { + import std.string : indexOf; + immutable varDef = to!string(environ[i]); - immutable eq = std.string.indexOf(varDef, '='); - assert (eq >= 0); + immutable eq = indexOf(varDef, '='); + assert(eq >= 0); immutable name = varDef[0 .. eq]; immutable value = varDef[eq+1 .. $]; @@ -2947,6 +3253,7 @@ static: } else version (Windows) { + import std.exception : enforce; import std.uni : toUpper; auto envBlock = GetEnvironmentStringsW(); enforce(envBlock, "Failed to retrieve environment variables."); @@ -2972,7 +3279,8 @@ static: // and it is just as much of a security issue there. Moreso, // in fact, due to the case insensensitivity of variable names, // which is not handled correctly by all programs. - if (name !in aa) aa[name] = toUTF8(envBlock[start .. i]); + auto val = toUTF8(envBlock[start .. i]); + if (name !in aa) aa[name] = val is null ? "" : val; } } else static assert(0); @@ -2980,32 +3288,68 @@ static: } private: - // Returns the length of an environment variable (in number of - // wchars, including the null terminator), or 0 if it doesn't exist. - version (Windows) - int varLength(LPCWSTR namez) @trusted nothrow - { - return GetEnvironmentVariableW(namez, null, 0); - } - // Retrieves the environment variable, returns false on failure. bool getImpl(in char[] name, out string value) @trusted { version (Windows) { + // first we ask windows how long the environment variable is, + // then we try to read it in to a buffer of that length. Lots + // of error conditions because the windows API is nasty. + + import std.conv : to; const namezTmp = name.tempCStringW(); - immutable len = varLength(namezTmp); - if (len == 0) return false; + WCHAR[] buf; + + // clear error because GetEnvironmentVariable only says it sets it + // if the environment variable is missing, not on other errors. + SetLastError(NO_ERROR); + // len includes terminating null + immutable len = GetEnvironmentVariableW(namezTmp, null, 0); + if (len == 0) + { + immutable err = GetLastError(); + if (err == ERROR_ENVVAR_NOT_FOUND) + return false; + // some other windows error. Might actually be NO_ERROR, because + // GetEnvironmentVariable doesn't specify whether it sets on all + // failures + throw new WindowsException(err); + } if (len == 1) { value = ""; return true; } + buf.length = len; - auto buf = new WCHAR[len]; - GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length)); - value = toUTF8(buf[0 .. $-1]); - return true; + while (true) + { + // lenRead is either the number of bytes read w/o null - if buf was long enough - or + // the number of bytes necessary *including* null if buf wasn't long enough + immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length)); + if (lenRead == 0) + { + immutable err = GetLastError(); + if (err == NO_ERROR) // sucessfully read a 0-length variable + { + value = ""; + return true; + } + if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist + return false; + // some other windows error + throw new WindowsException(err); + } + assert(lenRead != buf.length, "impossible according to msft docs"); + if (lenRead < buf.length) // the buffer was long enough + { + value = toUTF8(buf[0 .. lenRead]); + return true; + } + // resize and go around again, because the environment variable grew + buf.length = lenRead; + } } else version (Posix) { @@ -3015,7 +3359,16 @@ private: // Cache the last call's result. static string lastResult; - if (v != lastResult) lastResult = v.idup; + if (v.empty) + { + // Return non-null array for blank result to distinguish from + // not-present result. + lastResult = ""; + } + else if (v != lastResult) + { + lastResult = v.idup; + } value = lastResult; return true; } @@ -3023,46 +3376,57 @@ private: } } -unittest +@safe unittest { + import std.exception : assertThrown; // New variable environment["std_process"] = "foo"; - assert (environment["std_process"] == "foo"); + assert(environment["std_process"] == "foo"); + assert("std_process" in environment); - // Set variable again - environment["std_process"] = "bar"; - assert (environment["std_process"] == "bar"); + // Set variable again (also tests length 1 case) + environment["std_process"] = "b"; + assert(environment["std_process"] == "b"); + assert("std_process" in environment); // Remove variable environment.remove("std_process"); + assert("std_process" !in environment); // Remove again, should succeed environment.remove("std_process"); + assert("std_process" !in environment); // Throw on not found. assertThrown(environment["std_process"]); // get() without default value - assert (environment.get("std_process") == null); + assert(environment.get("std_process") is null); // get() with default value - assert (environment.get("std_process", "baz") == "baz"); + assert(environment.get("std_process", "baz") == "baz"); + + // get() on an empty (but present) value + environment["std_process"] = ""; + auto res = environment.get("std_process"); + assert(res !is null); + assert(res == ""); + assert("std_process" in environment); + + // Important to do the following round-trip after the previous test + // because it tests toAA with an empty var // Convert to associative array auto aa = environment.toAA(); - assert (aa.length > 0); + assert(aa.length > 0); foreach (n, v; aa) { // Wine has some bugs related to environment variables: // - Wine allows the existence of an env. variable with the name // "\0", but GetEnvironmentVariable refuses to retrieve it. // As of 2.067 we filter these out anyway (see comment in toAA). - // - If an env. variable has zero length, i.e. is "\0", - // GetEnvironmentVariable should return 1. Instead it returns - // 0, indicating the variable doesn't exist. - version (Windows) if (v.length == 0) continue; - assert (v == environment[n]); + assert(v == environment[n]); } // ... and back again. @@ -3071,7 +3435,13 @@ unittest // Complete the roundtrip auto aa2 = environment.toAA(); - assert(aa == aa2); + import std.conv : text; + assert(aa == aa2, text(aa, " != ", aa2)); + assert("std_process" in environment); + + // Setting null must have the same effect as remove + environment["std_process"] = null; + assert("std_process" !in environment); } @@ -3084,15 +3454,11 @@ unittest /* -Macros: - -WIKI=Phobos/StdProcess - Copyright: Copyright Digital Mars 2007 - 2009. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB digitalmars.com, Walter Bright), - $(WEB erdani.org, Andrei Alexandrescu), - $(WEB thecybershadow.net, Vladimir Panteleev) +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP digitalmars.com, Walter Bright), + $(HTTP erdani.org, Andrei Alexandrescu), + $(HTTP thecybershadow.net, Vladimir Panteleev) Source: $(PHOBOSSRC std/_process.d) */ /* @@ -3122,50 +3488,10 @@ version (unittest) } - -/** - Execute $(D command) in a _command shell. - - $(RED Deprecated. Please use $(LREF spawnShell) or $(LREF executeShell) - instead. This function will be removed in August 2015.) - - Returns: If $(D command) is null, returns nonzero if the _command - interpreter is found, and zero otherwise. If $(D command) is not - null, returns -1 on error, or the exit status of command (which may - in turn signal an error in command's execution). - - Note: On Unix systems, the homonym C function (which is accessible - to D programs as $(LINK2 core_stdc_stdlib.html, core.stdc.stdlib._system)) - returns a code in the same format as $(LUCKY waitpid, waitpid), - meaning that C programs must use the $(D WEXITSTATUS) macro to - extract the actual exit code from the $(D system) call. D's $(D - system) automatically extracts the exit status. - -*/ -deprecated("Please use wait(spawnShell(command)) or executeShell(command) instead") -int system(string command) -{ - if (!command.ptr) return core.stdc.stdlib.system(null); - immutable status = core.stdc.stdlib.system(command.tempCString()); - if (status == -1) return status; - version (Posix) - { - if (exited(status)) - return exitstatus(status); - - // Abnormal termination, return -1. - return -1; - } - else version (Windows) - return status; - else - static assert(0, "system not implemented for this OS."); -} - private void toAStringz(in string[] a, const(char)**az) { import std.string : toStringz; - foreach(string s; a) + foreach (string s; a) { *az++ = toStringz(s); } @@ -3179,7 +3505,8 @@ private void toAStringz(in string[] a, const(char)**az) //{ // int spawnvp(int mode, string pathname, string[] argv) // { -// char** argv_ = cast(char**)alloca((char*).sizeof * (1 + argv.length)); +// char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); +// scope(exit) core.stdc.stdlib.free(argv_); // // toAStringz(argv, argv_); // @@ -3189,96 +3516,11 @@ private void toAStringz(in string[] a, const(char)**az) // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo -enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY }; +enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY } version(Windows) extern(C) int spawnvp(int, in char *, in char **); alias P_WAIT = _P_WAIT; alias P_NOWAIT = _P_NOWAIT; -deprecated("Please use spawnProcess instead") -int spawnvp(int mode, string pathname, string[] argv) -{ - auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length)); - - toAStringz(argv, argv_); - - version (Posix) - { - return _spawnvp(mode, pathname.tempCString(), argv_); - } - else version (Windows) - { - return spawnvp(mode, pathname.tempCString(), argv_); - } - else - static assert(0, "spawnvp not implemented for this OS."); -} - -version (Posix) -{ -private import core.sys.posix.unistd; -private import core.sys.posix.sys.wait; - -deprecated("Please use spawnProcess instead") -int _spawnvp(int mode, in char *pathname, in char **argv) -{ - int retval = 0; - pid_t pid = fork(); - - if(!pid) - { // child - core.sys.posix.unistd.execvp(pathname, argv); - goto Lerror; - } - else if(pid > 0) - { // parent - if(mode == _P_NOWAIT) - { - retval = pid; // caller waits - } - else - { - while(1) - { - int status; - pid_t wpid = waitpid(pid, &status, 0); - if(exited(status)) - { - retval = exitstatus(status); - break; - } - else if(signaled(status)) - { - retval = -termsig(status); - break; - } - else if(stopped(status)) // ptrace support - continue; - else - goto Lerror; - } - } - - return retval; - } - -Lerror: - retval = errno; - char[80] buf = void; - throw new Exception( - "Cannot spawn " ~ to!string(pathname) ~ "; " - ~ to!string(strerror_r(retval, buf.ptr, buf.length)) - ~ " [errno " ~ to!string(retval) ~ "]"); -} // _spawnvp -private -{ - alias stopped = WIFSTOPPED; - alias signaled = WIFSIGNALED; - alias termsig = WTERMSIG; - alias exited = WIFEXITED; - alias exitstatus = WEXITSTATUS; -} // private -} // version (Posix) - /* ========================================================== */ version (StdDdoc) @@ -3287,11 +3529,7 @@ version (StdDdoc) Replaces the current process by executing a command, $(D pathname), with the arguments in $(D argv). - $(RED Deprecated on Windows. From August 2015, these functions will - only be available on POSIX platforms. The reason is that they never - did what the documentation claimed they did, nor is it technically - possible to implement such behaviour on Windows. See below for more - information.) + $(BLUE This functions is Posix-Only.) Typically, the first element of $(D argv) is the command being executed, i.e. $(D argv[0] == pathname). The 'p' @@ -3319,7 +3557,7 @@ version (StdDdoc) } else version (Windows) { - import core.stdc.stdlib: _exit; + import core.stdc.stdlib : _exit; _exit(wait(spawnProcess(commandLine))); } --- @@ -3343,7 +3581,7 @@ version (StdDdoc) else version (Windows) { spawnProcess(commandLine); - import core.stdc.stdlib: _exit; + import core.stdc.stdlib : _exit; _exit(0); } --- @@ -3356,38 +3594,24 @@ version (StdDdoc) /// ditto int execvpe(in string pathname, in string[] argv, in string[] envp); } -else +else version(Posix) { - private enum execvForwarderDefs = q{ - int execv(in string pathname, in string[] argv) - { - return execv_(pathname, argv); - } - int execve(in string pathname, in string[] argv, in string[] envp) - { - return execve_(pathname, argv, envp); - } - int execvp(in string pathname, in string[] argv) - { - return execvp_(pathname, argv); - } - int execvpe(in string pathname, in string[] argv, in string[] envp) - { - return execvpe_(pathname, argv, envp); - } - }; - version (Posix) + int execv(in string pathname, in string[] argv) { - mixin (execvForwarderDefs); + return execv_(pathname, argv); } - else version (Windows) + int execve(in string pathname, in string[] argv, in string[] envp) + { + return execve_(pathname, argv, envp); + } + int execvp(in string pathname, in string[] argv) + { + return execvp_(pathname, argv); + } + int execvpe(in string pathname, in string[] argv, in string[] envp) { - private enum execvDeprecationMsg = - "Please consult the API documentation for more information: " - ~"http://dlang.org/phobos/std_process.html#.execv"; - mixin (`deprecated ("`~execvDeprecationMsg~`") {` ~ execvForwarderDefs ~ `}`); + return execvpe_(pathname, argv, envp); } - else static assert (false, "Unsupported platform"); } // Move these C declarations to druntime if we decide to keep the D wrappers @@ -3401,7 +3625,8 @@ extern(C) private int execv_(in string pathname, in string[] argv) { - auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length)); + auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); + scope(exit) core.stdc.stdlib.free(argv_); toAStringz(argv, argv_); @@ -3410,8 +3635,10 @@ private int execv_(in string pathname, in string[] argv) private int execve_(in string pathname, in string[] argv, in string[] envp) { - auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length)); - auto envp_ = cast(const(char)**)alloca((char*).sizeof * (1 + envp.length)); + auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); + scope(exit) core.stdc.stdlib.free(argv_); + auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length)); + scope(exit) core.stdc.stdlib.free(envp_); toAStringz(argv, argv_); toAStringz(envp, envp_); @@ -3421,7 +3648,8 @@ private int execve_(in string pathname, in string[] argv, in string[] envp) private int execvp_(in string pathname, in string[] argv) { - auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length)); + auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); + scope(exit) core.stdc.stdlib.free(argv_); toAStringz(argv, argv_); @@ -3433,8 +3661,9 @@ private int execvpe_(in string pathname, in string[] argv, in string[] envp) version(Posix) { import std.array : split; + import std.conv : to; // Is pathname rooted? - if(pathname[0] == '/') + if (pathname[0] == '/') { // Yes, so just call execve() return execve(pathname, argv, envp); @@ -3450,13 +3679,13 @@ version(Posix) // execution, so there's no need to check the execve() result through // the loop. - foreach(string pathDir; envPaths) + foreach (string pathDir; envPaths) { string composite = cast(string) (pathDir ~ "/" ~ pathname); iRet = execve(composite, argv, envp); } - if(0 != iRet) + if (0 != iRet) { iRet = execve(pathname, argv, envp); } @@ -3466,8 +3695,10 @@ version(Posix) } else version(Windows) { - auto argv_ = cast(const(char)**)alloca((char*).sizeof * (1 + argv.length)); - auto envp_ = cast(const(char)**)alloca((char*).sizeof * (1 + envp.length)); + auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length)); + scope(exit) core.stdc.stdlib.free(argv_); + auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length)); + scope(exit) core.stdc.stdlib.free(envp_); toAStringz(argv, argv_); toAStringz(envp, envp_); @@ -3480,168 +3711,12 @@ else } // version } -/** - * Returns the process ID of the calling process, which is guaranteed to be - * unique on the system. This call is always successful. - * - * $(RED Deprecated. Please use $(LREF thisProcessID) instead. - * This function will be removed in August 2015.) - * - * Example: - * --- - * writefln("Current process id: %s", getpid()); - * --- - */ -deprecated("Please use thisProcessID instead") -alias getpid = core.thread.getpid; - -/** - Runs $(D_PARAM cmd) in a shell and returns its standard output. If - the process could not be started or exits with an error code, - throws ErrnoException. - - $(RED Deprecated. Please use $(LREF executeShell) instead. - This function will be removed in August 2015.) - - Example: - - ---- - auto tempFilename = chomp(shell("mcookie")); - auto f = enforce(fopen(tempFilename), "w"); - scope(exit) - { - fclose(f) == 0 || assert(false); - system(escapeShellCommand("rm", tempFilename)); - } - ... use f ... - ---- -*/ -deprecated("Please use executeShell instead") -string shell(string cmd) -{ - version(Windows) - { - import std.array : appender; - // Generate a random filename - auto a = appender!string(); - foreach (ref e; 0 .. 8) - { - formattedWrite(a, "%x", rndGen.front); - rndGen.popFront(); - } - auto filename = a.data; - scope(exit) if (exists(filename)) remove(filename); - // We can't use escapeShellCommands here because we don't know - // if cmd is escaped (wrapped in quotes) or not, without relying - // on shady heuristics. The current code shouldn't cause much - // trouble unless filename contained spaces (it won't). - errnoEnforce(system(cmd ~ "> " ~ filename) == 0); - return readText(filename); - } - else version(Posix) - { - File f; - f.popen(cmd, "r"); - char[] line; - string result; - while (f.readln(line)) - { - result ~= line; - } - f.close(); - return result; - } - else - static assert(0, "shell not implemented for this OS."); -} - -deprecated unittest -{ - auto x = shell("echo wyda"); - // @@@ This fails on wine - //assert(x == "wyda" ~ newline, text(x.length)); - - import std.exception; // Issue 9444 - version(windows) - string cmd = "98c10ec7e253a11cdff45f807b984a81 2>NUL"; - else - string cmd = "98c10ec7e253a11cdff45f807b984a81 2>/dev/null"; - assertThrown!ErrnoException(shell(cmd)); -} - -/** -Gets the value of environment variable $(D name) as a string. Calls -$(LINK2 core_stdc_stdlib.html#_getenv, core.stdc.stdlib._getenv) -internally. - -$(RED Deprecated. Please use $(LREF environment.opIndex) or - $(LREF environment.get) instead. This function will be - removed in August 2015.) -*/ - -deprecated("Please use environment.opIndex or environment.get instead") -string getenv(in char[] name) nothrow -{ - // Cache the last call's result - static string lastResult; - auto p = core.stdc.stdlib.getenv(name.tempCString()); - if (!p) return null; - auto value = p[0 .. strlen(p)]; - if (value == lastResult) return lastResult; - return lastResult = value.idup; -} - -/** -Sets the value of environment variable $(D name) to $(D value). If the -value was written, or the variable was already present and $(D -overwrite) is false, returns normally. Otherwise, it throws an -exception. Calls $(LINK2 core_sys_posix_stdlib.html#_setenv, -core.sys.posix.stdlib._setenv) internally. - -$(RED Deprecated. Please use $(LREF environment.opIndexAssign) instead. - This function will be removed in August 2015.) -*/ -version(StdDdoc) deprecated void setenv(in char[] name, in char[] value, bool overwrite); -else version(Posix) - deprecated("Please use environment.opIndexAssign instead.") - void setenv(in char[] name, in char[] value, bool overwrite) -{ - errnoEnforce( - core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), overwrite) == 0); -} - -/** -Removes variable $(D name) from the environment. Calls $(LINK2 -core_sys_posix_stdlib.html#_unsetenv, core.sys.posix.stdlib._unsetenv) internally. - -$(RED Deprecated. Please use $(LREF environment.remove) instead. - This function will be removed in August 2015.) -*/ -version(StdDdoc) deprecated void unsetenv(in char[] name); -else version(Posix) - deprecated("Please use environment.remove instead") - void unsetenv(in char[] name) -{ - errnoEnforce(core.sys.posix.stdlib.unsetenv(name.tempCString()) == 0); -} - -version (Posix) deprecated unittest -{ - setenv("wyda", "geeba", true); - assert(getenv("wyda") == "geeba"); - // Get again to make sure caching works - assert(getenv("wyda") == "geeba"); - unsetenv("wyda"); - assert(getenv("wyda") is null); -} - - version(StdDdoc) { /**************************************** * Start up the browser and set it to viewing the page at url. */ - void browse(string url); + void browse(const(char)[] url); } else version (Windows) @@ -3650,7 +3725,7 @@ version (Windows) pragma(lib,"shell32.lib"); - void browse(string url) + void browse(const(char)[] url) { ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL); } @@ -3661,33 +3736,34 @@ else version (OSX) import core.stdc.string; import core.sys.posix.unistd; - void browse(string url) + void browse(const(char)[] url) nothrow @nogc { const(char)*[5] args; + auto curl = url.tempCString(); const(char)* browser = core.stdc.stdlib.getenv("BROWSER"); if (browser) { browser = strdup(browser); args[0] = browser; - args[1] = url.tempCString(); + args[1] = curl; args[2] = null; } else { args[0] = "open".ptr; - args[1] = url.tempCString(); + args[1] = curl; args[2] = null; } - auto childpid = fork(); + auto childpid = core.sys.posix.unistd.fork(); if (childpid == 0) { - core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr); + core.sys.posix.unistd.execvp(args[0], cast(char**) args.ptr); perror(args[0]); // failed to execute return; } if (browser) - free(cast(void*)browser); + free(cast(void*) browser); } } else version (Posix) @@ -3696,7 +3772,7 @@ else version (Posix) import core.stdc.string; import core.sys.posix.unistd; - void browse(string url) + void browse(const(char)[] url) nothrow @nogc { const(char)*[3] args; @@ -3712,16 +3788,17 @@ else version (Posix) args[1] = url.tempCString(); args[2] = null; - auto childpid = fork(); + auto childpid = core.sys.posix.unistd.fork(); if (childpid == 0) { - core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr); + core.sys.posix.unistd.execvp(args[0], cast(char**) args.ptr); perror(args[0]); // failed to execute return; } if (browser) - free(cast(void*)browser); + free(cast(void*) browser); } } else static assert(0, "os not supported"); + diff --git a/std/random.d b/std/random.d index dddf8c58ffa..5148ac86f5f 100644 --- a/std/random.d +++ b/std/random.d @@ -3,14 +3,22 @@ /** Facilities for random number generation. +$(RED Disclaimer:) The _random number generators and API provided in this +module are not designed to be cryptographically secure, and are therefore +unsuitable for cryptographic or security-related purposes such as generating +authentication tokens or network sequence numbers. For such needs, please use a +reputable cryptographic library instead. + The new-style generator objects hold their own state so they are immune of threading issues. The generators feature a number of well-known and well-documented methods of generating random numbers. An overall fast and reliable means to generate random numbers is the $(D_PARAM Mt19937) generator, which derives its name from -"$(LUCKY Mersenne Twister) with a period of 2 to the power of -19937". In memory-constrained situations, $(LUCKY linear congruential) -generators such as $(D MinstdRand0) and $(D MinstdRand) might be +"$(LINK2 https://en.wikipedia.org/wiki/Mersenne_Twister, Mersenne Twister) +with a period of 2 to the power of +19937". In memory-constrained situations, +$(LINK2 https://en.wikipedia.org/wiki/Linear_congruential_generator, +linear congruential generators) such as $(D MinstdRand0) and $(D MinstdRand) might be useful. The standard library provides an alias $(D_PARAM Random) for whichever generator it considers the most fit for the target environment. @@ -20,10 +28,14 @@ Example: ---- // Generate a uniformly-distributed integer in the range [0, 14] auto i = uniform(0, 15); + // Generate a uniformly-distributed real in the range [0, 100) // using a specific random generator Random gen; auto r = uniform(0.0L, 100.0L, gen); + +// Generate a 32-bit random number +auto l = uniform!uint(); ---- In addition to random number generators, this module features @@ -31,24 +43,18 @@ distributions, which skew a generator's output statistical distribution in various ways. So far the uniform distribution for integers and real numbers have been implemented. -Upgrading: - $(WEB digitalmars.com/d/1.0/phobos/std_random.html#rand Phobos D1 $(D rand())) can - be replaced with $(D uniform!uint()). - Source: $(PHOBOSSRC std/_random.d) Macros: -WIKI = Phobos/StdRandom - - Copyright: Copyright Andrei Alexandrescu 2008 - 2009, Joseph Rushton Wakeling 2012. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.org, Andrei Alexandrescu) +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu) Masahiro Nakagawa (Xorshift random generator) - $(WEB braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling) + $(HTTP braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling) + Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-_random, mir-_random)) Credits: The entire random number library architecture is derived from the - excellent $(WEB open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X) + excellent $(HTTP open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X) random number facility proposed by Jens Maurer and contributed to by researchers at the Fermi laboratory (excluding Xorshift). */ @@ -66,9 +72,9 @@ import std.traits; version(unittest) { - static import std.typetuple; - package alias PseudoRngTypes = std.typetuple.TypeTuple!(MinstdRand0, MinstdRand, Mt19937, Xorshift32, Xorshift64, - Xorshift96, Xorshift128, Xorshift160, Xorshift192); + static import std.meta; + package alias PseudoRngTypes = std.meta.AliasSeq!(MinstdRand0, MinstdRand, Mt19937, Xorshift32, Xorshift64, + Xorshift96, Xorshift128, Xorshift160, Xorshift192); } // Segments of the code in this file Copyright (c) 1997 by Rick Booth @@ -251,7 +257,7 @@ template isSeedable(Rng) Linear Congruential generator. */ struct LinearCongruentialEngine(UIntType, UIntType a, UIntType c, UIntType m) - if(isUnsigned!UIntType) +if (isUnsigned!UIntType) { ///Mark this as a Rng enum bool isUniformRandom = true; @@ -275,10 +281,10 @@ The parameters of this distribution. The random number is $(D_PARAM x static assert(m == 0 || a < m); static assert(m == 0 || c < m); static assert(m == 0 || - (cast(ulong)a * (m-1) + c) % m == (c < a ? c - a + m : c - a)); + (cast(ulong) a * (m-1) + c) % m == (c < a ? c - a + m : c - a)); // Check for maximum range - private static ulong gcd(ulong a, ulong b) @safe pure nothrow + private static ulong gcd(ulong a, ulong b) @safe pure nothrow @nogc { while (b) { @@ -289,7 +295,7 @@ The parameters of this distribution. The random number is $(D_PARAM x return a; } - private static ulong primeFactorsOnly(ulong n) @safe pure nothrow + private static ulong primeFactorsOnly(ulong n) @safe pure nothrow @nogc { ulong result = 1; ulong iter = 2; @@ -317,7 +323,7 @@ The parameters of this distribution. The random number is $(D_PARAM x } private static bool properLinearCongruentialParameters(ulong m, - ulong a, ulong c) @safe pure nothrow + ulong a, ulong c) @safe pure nothrow @nogc { if (m == 0) { @@ -374,7 +380,7 @@ $(D x0). /** Advances the random sequence. */ - void popFront() @safe pure nothrow + void popFront() @safe pure nothrow @nogc { static if (m) { @@ -410,13 +416,13 @@ $(D x0). /** Returns the current number in the random sequence. */ - @property UIntType front() const @safe pure nothrow + @property UIntType front() const @safe pure nothrow @nogc { return _x; } /// - @property typeof(this) save() @safe pure nothrow + @property typeof(this) save() @safe pure nothrow @nogc { return this; } @@ -429,7 +435,7 @@ Always $(D false) (random generators are infinite ranges). /** Compares against $(D_PARAM rhs) for equality. */ - bool opEquals(ref const LinearCongruentialEngine rhs) const @safe pure nothrow + bool opEquals(ref const LinearCongruentialEngine rhs) const @safe pure nothrow @nogc { return _x == rhs._x; } @@ -440,28 +446,28 @@ Always $(D false) (random generators are infinite ranges). /** Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen parameters. $(D MinstdRand0) implements Park and Miller's "minimal -standard" $(WEB +standard" $(HTTP wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator, generator) that uses 16807 for the multiplier. $(D MinstdRand) implements a variant that has slightly better spectral behavior by using the multiplier 48271. Both generators are rather simplistic. - -Example: - ----- -// seed with a constant -auto rnd0 = MinstdRand0(1); -auto n = rnd0.front; // same for each run -// Seed with an unpredictable value -rnd0.seed(unpredictableSeed); -n = rnd0.front; // different across runs ----- */ -alias MinstdRand0 = LinearCongruentialEngine!(uint, 16807, 0, 2147483647); +alias MinstdRand0 = LinearCongruentialEngine!(uint, 16_807, 0, 2_147_483_647); /// ditto -alias MinstdRand = LinearCongruentialEngine!(uint, 48271, 0, 2147483647); +alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647); + +/// +@safe unittest +{ + // seed with a constant + auto rnd0 = MinstdRand0(1); + auto n = rnd0.front; // same for each run + // Seed with an unpredictable value + rnd0.seed(unpredictableSeed); + n = rnd0.front; // different across runs +} -unittest +@safe unittest { import std.range; static assert(isForwardRange!MinstdRand); @@ -515,7 +521,7 @@ unittest assert(rnd.front == 399268537); // Check .save works - foreach (Type; std.typetuple.TypeTuple!(MinstdRand0, MinstdRand)) + foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand)) { auto rnd1 = Type(unpredictableSeed); auto rnd2 = rnd1.save; @@ -527,19 +533,20 @@ unittest } /** -The $(LUCKY Mersenne Twister) generator. +The $(LINK2 https://en.wikipedia.org/wiki/Mersenne_Twister, Mersenne Twister) generator. */ struct MersenneTwisterEngine(UIntType, size_t w, size_t n, size_t m, size_t r, - UIntType a, size_t u, size_t s, + UIntType a, size_t u, UIntType d, size_t s, UIntType b, size_t t, - UIntType c, size_t l) - if(isUnsigned!UIntType) + UIntType c, size_t l, UIntType f) +if (isUnsigned!UIntType) { static assert(0 < w && w <= UIntType.sizeof * 8); static assert(1 <= m && m <= n); static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l); static assert(r <= w && u <= w && s <= w && t <= w && l <= w); static assert(0 <= a && 0 <= b && 0 <= c); + static assert(n <= sizediff_t.max); ///Mark this as a Rng enum bool isUniformRandom = true; @@ -552,59 +559,141 @@ Parameters for the generator. enum size_t shiftSize = m; /// ditto enum size_t maskBits = r; /// ditto enum UIntType xorMask = a; /// ditto - enum UIntType temperingU = u; /// ditto + enum size_t temperingU = u; /// ditto + enum UIntType temperingD = d; /// ditto enum size_t temperingS = s; /// ditto enum UIntType temperingB = b; /// ditto enum size_t temperingT = t; /// ditto enum UIntType temperingC = c; /// ditto enum size_t temperingL = l; /// ditto + enum UIntType initializationMultiplier = f; /// ditto /// Smallest generated value (0). enum UIntType min = 0; /// Largest generated value. enum UIntType max = UIntType.max >> (UIntType.sizeof * 8u - w); - static assert(a <= max && b <= max && c <= max); + // note, `max` also serves as a bitmask for the lowest `w` bits + static assert(a <= max && b <= max && c <= max && f <= max); + /// The default seed value. enum UIntType defaultSeed = 5489u; + // Bitmasks used in the 'twist' part of the algorithm + private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1, + upperMask = (~lowerMask) & this.max; + + /* + Collection of all state variables + used by the generator + */ + private struct State + { + /* + State array of the generator. This + is iterated through backwards (from + last element to first), providing a + few extra compiler optimizations by + comparison to the forward iteration + used in most implementations. + */ + UIntType[n] data; + + /* + Cached copy of most recently updated + element of `data` state array, ready + to be tempered to generate next + `front` value + */ + UIntType z; + + /* + Most recently generated random variate + */ + UIntType front; + + /* + Index of the entry in the `data` + state array that will be twisted + in the next `popFront()` call + */ + size_t index; + } + + /* + State variables used by the generator; + initialized to values equivalent to + explicitly seeding the generator with + `defaultSeed` + */ + private State state = defaultState(); + /* NOTE: the above is a workaround to ensure + backwards compatibility with the original + implementation, which permitted implicit + construction. With `@disable this();` + it would not be necessary. */ + /** Constructs a MersenneTwisterEngine object. */ - this(UIntType value) @safe pure nothrow + this(UIntType value) @safe pure nothrow @nogc { seed(value); } + /** + Generates the default initial state for a Mersenne + Twister; equivalent to the internal state obtained + by calling `seed(defaultSeed)` + */ + private static State defaultState() @safe pure nothrow @nogc + { + if (!__ctfe) assert(false); + State mtState; + seedImpl(defaultSeed, mtState); + return mtState; + } + /** Seeds a MersenneTwisterEngine object. Note: - This seed function gives 2^32 starting points. To allow the RNG to be started in any one of its - internal states use the seed overload taking an InputRange. + This seed function gives 2^w starting points (the lowest w bits of + the value provided will be used). To allow the RNG to be started + in any one of its internal states use the seed overload taking an + InputRange. */ - void seed()(UIntType value = defaultSeed) @safe pure nothrow + void seed()(UIntType value = defaultSeed) @safe pure nothrow @nogc { - static if (w == UIntType.sizeof * 8) - { - mt[0] = value; - } - else + this.seedImpl(value, this.state); + } + + /** + Implementation of the seeding mechanism, which + can be used with an arbitrary `State` instance + */ + private static void seedImpl(UIntType value, ref State mtState) + { + mtState.data[$ - 1] = value; + static if (this.max != UIntType.max) { - static assert(max + 1 > 0); - mt[0] = value % (max + 1); + mtState.data[$ - 1] &= this.max; } - for (mti = 1; mti < n; ++mti) + + foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1]) { - mt[mti] = - cast(UIntType) - (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> (w - 2))) + mti); - /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ - /* In the previous versions, MSBs of the seed affect */ - /* only MSBs of the array mt[]. */ - /* 2002/01/09 modified by Makoto Matsumoto */ - //mt[mti] &= ResultType.max; - /* for >32 bit machines */ + e = f * (mtState.data[i + 1] ^ (mtState.data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1)); + static if (this.max != UIntType.max) + { + e &= this.max; + } } - popFront(); + + mtState.index = n - 1; + + /* double popFront() to guarantee both `mtState.z` + and `mtState.front` are derived from the newly + set values in `mtState.data` */ + MersenneTwisterEngine.popFrontImpl(mtState); + MersenneTwisterEngine.popFrontImpl(mtState); } /** @@ -613,94 +702,119 @@ Parameters for the generator. Throws: $(D Exception) if the InputRange didn't provide enough elements to seed the generator. The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct. - - Examples: - ---------------- - Mt19937 gen; - gen.seed(map!((a) => unpredictableSeed)(repeat(0))); - ---------------- */ - void seed(T)(T range) if(isInputRange!T && is(Unqual!(ElementType!T) == UIntType)) + void seed(T)(T range) if (isInputRange!T && is(Unqual!(ElementType!T) == UIntType)) + { + this.seedImpl(range, this.state); + } + + /** + Implementation of the range-based seeding mechanism, + which can be used with an arbitrary `State` instance + */ + private static void seedImpl(T)(T range, ref State mtState) + if (isInputRange!T && is(Unqual!(ElementType!T) == UIntType)) { size_t j; - for(j = 0; j < n && !range.empty; ++j, range.popFront()) + for (j = 0; j < n && !range.empty; ++j, range.popFront()) { - mt[j] = range.front; + sizediff_t idx = n - j - 1; + mtState.data[idx] = range.front; } - mti = n; - if(range.empty && j < n) + mtState.index = n - 1; + + if (range.empty && j < n) { - import std.format : format; - throw new Exception(format("MersenneTwisterEngine.seed: Input range didn't provide enough"~ - " elements: Need %s elemnets.", n)); + import core.internal.string : UnsignedStringBuf, unsignedToTempString; + + UnsignedStringBuf buf = void; + string s = "MersenneTwisterEngine.seed: Input range didn't provide enough elements: Need "; + s ~= unsignedToTempString(n, buf, 10) ~ " elements."; + throw new Exception(s); } - popFront(); + /* double popFront() to guarantee both `mtState.z` + and `mtState.front` are derived from the newly + set values in `mtState.data` */ + MersenneTwisterEngine.popFrontImpl(mtState); + MersenneTwisterEngine.popFrontImpl(mtState); } /** Advances the generator. */ - void popFront() @safe pure nothrow - { - if (mti == size_t.max) seed(); - enum UIntType - upperMask = ~((cast(UIntType) 1u << - (UIntType.sizeof * 8 - (w - r))) - 1), - lowerMask = (cast(UIntType) 1u << r) - 1; - static immutable UIntType[2] mag01 = [0x0UL, a]; - - ulong y = void; - - if (mti >= n) + void popFront() @safe pure nothrow @nogc + { + this.popFrontImpl(this.state); + } + + /* + Internal implementation of `popFront()`, which + can be used with an arbitrary `State` instance + */ + private static void popFrontImpl(ref State mtState) + { + /* This function blends two nominally independent + processes: (i) calculation of the next random + variate `mtState.front` from the cached previous + `data` entry `z`, and (ii) updating the value + of `data[index]` and `mtState.z` and advancing + the `index` value to the next in sequence. + + By interweaving the steps involved in these + procedures, rather than performing each of + them separately in sequence, the variables + are kept 'hot' in CPU registers, allowing + for significantly faster performance. */ + sizediff_t index = mtState.index; + sizediff_t next = index - 1; + if (next < 0) + next = n - 1; + auto z = mtState.z; + sizediff_t conj = index - m; + if (conj < 0) + conj = index - m + n; + + static if (d == UIntType.max) + { + z ^= (z >> u); + } + else { - /* generate N words at one time */ - - int kk = 0; - const limit1 = n - m; - for (; kk < limit1; ++kk) - { - y = (mt[kk] & upperMask)|(mt[kk + 1] & lowerMask); - mt[kk] = cast(UIntType) (mt[kk + m] ^ (y >> 1) - ^ mag01[cast(UIntType) y & 0x1U]); - } - const limit2 = n - 1; - for (; kk < limit2; ++kk) - { - y = (mt[kk] & upperMask)|(mt[kk + 1] & lowerMask); - mt[kk] = cast(UIntType) (mt[kk + (m -n)] ^ (y >> 1) - ^ mag01[cast(UIntType) y & 0x1U]); - } - y = (mt[n -1] & upperMask)|(mt[0] & lowerMask); - mt[n - 1] = cast(UIntType) (mt[m - 1] ^ (y >> 1) - ^ mag01[cast(UIntType) y & 0x1U]); - - mti = 0; + z ^= (z >> u) & d; } - y = mt[mti++]; - - /* Tempering */ - y ^= (y >> temperingU); - y ^= (y << temperingS) & temperingB; - y ^= (y << temperingT) & temperingC; - y ^= (y >> temperingL); + auto q = mtState.data[index] & upperMask; + auto p = mtState.data[next] & lowerMask; + z ^= (z << s) & b; + auto y = q | p; + auto x = y >> 1; + z ^= (z << t) & c; + if (y & 1) + x ^= a; + auto e = mtState.data[conj] ^ x; + z ^= (z >> l); + mtState.z = mtState.data[index] = e; + mtState.index = next; - _y = cast(UIntType) y; + /* technically we should take the lowest `w` + bits here, but if the tempering bitmasks + `b` and `c` are set correctly, this should + be unnecessary */ + mtState.front = z; } /** Returns the current random value. */ - @property UIntType front() @safe pure nothrow + @property UIntType front() @safe const pure nothrow @nogc { - if (mti == size_t.max) seed(); - return _y; + return this.state.front; } /// - @property typeof(this) save() @safe pure nothrow + @property typeof(this) save() @safe pure nothrow @nogc { return this; } @@ -709,37 +823,33 @@ Parameters for the generator. Always $(D false). */ enum bool empty = false; - - private UIntType[n] mt; - private size_t mti = size_t.max; /* means mt is not initialized */ - UIntType _y = UIntType.max; } /** A $(D MersenneTwisterEngine) instantiated with the parameters of the -original engine $(WEB math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html, +original engine $(HTTP math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html, MT19937), generating uniformly-distributed 32-bit numbers with a period of 2 to the power of 19937. Recommended for random number generation unless memory is severely restricted, in which case a $(D LinearCongruentialEngine) would be the generator of choice. - -Example: - ----- -// seed with a constant -Mt19937 gen; -auto n = gen.front; // same for each run -// Seed with an unpredictable value -gen.seed(unpredictableSeed); -n = gen.front; // different across runs ----- */ alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31, - 0x9908b0df, 11, 7, + 0x9908b0df, 11, 0xffffffff, 7, 0x9d2c5680, 15, - 0xefc60000, 18); + 0xefc60000, 18, 1_812_433_253); + +/// +@safe unittest +{ + // seed with a constant + Mt19937 gen; + auto n = gen.front; // same for each run + // Seed with an unpredictable value + gen.seed(unpredictableSeed); + n = gen.front; // different across runs +} -nothrow unittest +@safe nothrow unittest { import std.algorithm; import std.range; @@ -749,11 +859,63 @@ nothrow unittest static assert(isSeedable!(Mt19937, uint)); static assert(isSeedable!(Mt19937, typeof(map!((a) => unpredictableSeed)(repeat(0))))); Mt19937 gen; + assert(gen.front == 3499211612); popFrontN(gen, 9999); assert(gen.front == 4123659995); + try { gen.seed(iota(624u)); } catch (Exception) { assert(false); } + assert(gen.front == 3708921088u); + popFrontN(gen, 9999); + assert(gen.front == 165737292u); +} + +/** +A $(D MersenneTwisterEngine) instantiated with the parameters of the +original engine $(HTTP en.wikipedia.org/wiki/Mersenne_Twister, +MT19937-64), generating uniformly-distributed 64-bit numbers with a +period of 2 to the power of 19937. +*/ +alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31, + 0xb5026f5aa96619e9, 29, 0x5555555555555555, 17, + 0x71d67fffeda60000, 37, + 0xfff7eee000000000, 43, 6_364_136_223_846_793_005); + +/// +@safe unittest +{ + // Seed with a constant + auto gen = Mt19937_64(12345); + auto n = gen.front; // same for each run + // Seed with an unpredictable value + gen.seed(unpredictableSeed); + n = gen.front; // different across runs } -unittest +@safe nothrow unittest +{ + import std.algorithm; + import std.range; + static assert(isUniformRNG!Mt19937_64); + static assert(isUniformRNG!(Mt19937_64, ulong)); + static assert(isSeedable!Mt19937_64); + static assert(isSeedable!(Mt19937_64, ulong)); + // FIXME: this test demonstrates viably that Mt19937_64 + // is seedable with an infinite range of `ulong` values + // but it's a poor example of how to actually seed the + // generator, since it can't cover the full range of + // possible seed values. Ideally we need a 64-bit + // unpredictable seed to complement the 32-bit one! + static assert(isSeedable!(Mt19937_64, typeof(map!((a) => (cast(ulong) unpredictableSeed))(repeat(0))))); + Mt19937_64 gen; + assert(gen.front == 14514284786278117030uL); + popFrontN(gen, 9999); + assert(gen.front == 9981545732273789042uL); + try { gen.seed(iota(312uL)); } catch (Exception) { assert(false); } + assert(gen.front == 14660652410669508483uL); + popFrontN(gen, 9999); + assert(gen.front == 15956361063660440239uL); +} + +@safe unittest { import std.exception; import std.range; @@ -784,11 +946,11 @@ unittest assert(a != b); } -unittest +@safe unittest { import std.range; // Check .save works - foreach(Type; std.typetuple.TypeTuple!(Mt19937)) + foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64)) { auto gen1 = Type(unpredictableSeed); auto gen2 = gen1.save; @@ -802,19 +964,31 @@ unittest @safe pure nothrow unittest //11690 { alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31, - 0x9908b0df, 11, 7, + 0x9908b0df, 11, 0xffffffff, 7, 0x9d2c5680, 15, - 0xefc60000, 18); + 0xefc60000, 18, 1812433253); + + ulong[] expectedFirstValue = [3499211612uL, 3499211612uL, + 171143175841277uL, 1145028863177033374uL]; - foreach (R; std.typetuple.TypeTuple!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64))) + ulong[] expected10kValue = [4123659995uL, 4123659995uL, + 51991688252792uL, 3031481165133029945uL]; + + foreach (i, R; std.meta.AliasSeq!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64))) + { auto a = R(); + a.seed(a.defaultSeed); // checks that some alternative paths in `seed` are utilized + assert(a.front == expectedFirstValue[i]); + a.popFrontN(9999); + assert(a.front == expected10kValue[i]); + } } /** * Xorshift generator using 32bit algorithm. * - * Implemented according to $(WEB www.jstatsoft.org/v08/i14/paper, Xorshift RNGs). + * Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs). * * $(BOOKTABLE $(TEXTWITHCOMMAS Supporting bits are below, $(D bits) means second parameter of XorshiftEngine.), * $(TR $(TH bits) $(TH period)) @@ -827,7 +1001,7 @@ unittest * ) */ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType c) - if(isUnsigned!UIntType) +if (isUnsigned!UIntType) { static assert(bits == 32 || bits == 64 || bits == 96 || bits == 128 || bits == 160 || bits == 192, "Xorshift supports only 32, 64, 96, 128, 160 and 192 bit versions. " @@ -848,18 +1022,18 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType enum size = bits / 32; static if (bits == 32) - UIntType[size] seeds_ = [2463534242]; + UIntType[size] seeds_ = [2_463_534_242]; else static if (bits == 64) - UIntType[size] seeds_ = [123456789, 362436069]; + UIntType[size] seeds_ = [123_456_789, 362_436_069]; else static if (bits == 96) - UIntType[size] seeds_ = [123456789, 362436069, 521288629]; + UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629]; else static if (bits == 128) - UIntType[size] seeds_ = [123456789, 362436069, 521288629, 88675123]; + UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123]; else static if (bits == 160) - UIntType[size] seeds_ = [123456789, 362436069, 521288629, 88675123, 5783321]; + UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321]; else static if (bits == 192) { - UIntType[size] seeds_ = [123456789, 362436069, 521288629, 88675123, 5783321, 6615241]; + UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241]; UIntType value_; } else @@ -873,8 +1047,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Constructs a $(D XorshiftEngine) generator seeded with $(D_PARAM x0). */ - @safe - nothrow this(UIntType x0) pure + this(UIntType x0) @safe pure nothrow @nogc { seed(x0); } @@ -883,12 +1056,11 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * (Re)seeds the generator. */ - @safe - nothrow void seed(UIntType x0) pure + void seed(UIntType x0) @safe pure nothrow @nogc { // Initialization routine from MersenneTwisterEngine. foreach (i, e; seeds_) - seeds_[i] = x0 = cast(UIntType)(1812433253U * (x0 ^ (x0 >> 30)) + i + 1); + seeds_[i] = x0 = cast(UIntType)(1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1); // All seeds must not be 0. sanitizeSeeds(seeds_); @@ -900,8 +1072,8 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Returns the current number in the random sequence. */ - @property @safe - nothrow UIntType front() const pure + @property + UIntType front() const @safe pure nothrow @nogc { static if (bits == 192) return value_; @@ -913,8 +1085,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Advances the random sequence. */ - @safe - nothrow void popFront() pure + void popFront() @safe pure nothrow @nogc { UIntType temp; @@ -962,7 +1133,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType seeds_[2] = seeds_[3]; seeds_[3] = seeds_[4]; seeds_[4] = seeds_[4] ^ (seeds_[4] << c) ^ temp ^ (temp << b); - value_ = seeds_[4] + (seeds_[5] += 362437); + value_ = seeds_[4] + (seeds_[5] += 362_437); } else { @@ -975,8 +1146,8 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Captures a range state. */ - @property @safe - nothrow typeof(this) save() pure + @property + typeof(this) save() @safe pure nothrow @nogc { return this; } @@ -985,16 +1156,14 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Compares against $(D_PARAM rhs) for equality. */ - @safe - nothrow bool opEquals(ref const XorshiftEngine rhs) const pure + bool opEquals(ref const XorshiftEngine rhs) const @safe pure nothrow @nogc { return seeds_ == rhs.seeds_; } private: - @safe - static nothrow void sanitizeSeeds(ref UIntType[size] seeds) pure + static void sanitizeSeeds(ref UIntType[size] seeds) @safe pure nothrow @nogc { for (uint i; i < seeds.length; i++) { @@ -1021,17 +1190,6 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Define $(D XorshiftEngine) generators with well-chosen parameters. See each bits examples of "Xorshift RNGs". * $(D Xorshift) is a Xorshift128's alias because 128bits implementation is mostly used. - * - * Example: - * ----- - * // Seed with a constant - * auto rnd = Xorshift(1); - * auto num = rnd.front; // same for each run - * - * // Seed with an unpredictable value - * rnd.seed(unpredictableSeed()); - * num = rnd.front; // different across runs - * ----- */ alias Xorshift32 = XorshiftEngine!(uint, 32, 13, 17, 15) ; alias Xorshift64 = XorshiftEngine!(uint, 64, 10, 13, 10); /// ditto @@ -1041,8 +1199,19 @@ alias Xorshift160 = XorshiftEngine!(uint, 160, 2, 1, 4); /// ditto alias Xorshift192 = XorshiftEngine!(uint, 192, 2, 1, 4); /// ditto alias Xorshift = Xorshift128; /// ditto +/// +@safe unittest +{ + // Seed with a constant + auto rnd = Xorshift(1); + auto num = rnd.front; // same for each run + + // Seed with an unpredictable value + rnd.seed(unpredictableSeed); + num = rnd.front; // different across rnd +} -unittest +@safe unittest { import std.range; static assert(isForwardRange!Xorshift); @@ -1053,15 +1222,21 @@ unittest // Result from reference implementation. auto checking = [ - [2463534242UL, 901999875, 3371835698, 2675058524, 1053936272, 3811264849, 472493137, 3856898176, 2131710969, 2312157505], - [362436069UL, 2113136921, 19051112, 3010520417, 951284840, 1213972223, 3173832558, 2611145638, 2515869689, 2245824891], - [521288629UL, 1950277231, 185954712, 1582725458, 3580567609, 2303633688, 2394948066, 4108622809, 1116800180, 3357585673], - [88675123UL, 3701687786, 458299110, 2500872618, 3633119408, 516391518, 2377269574, 2599949379, 717229868, 137866584], - [5783321UL, 393427209, 1947109840, 565829276, 1006220149, 971147905, 1436324242, 2800460115, 1484058076, 3823330032], - [0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219, 2464530826, 1604040631, 3653403911] + [2463534242UL, 901999875, 3371835698, 2675058524, 1053936272, 3811264849, + 472493137, 3856898176, 2131710969, 2312157505], + [362436069UL, 2113136921, 19051112, 3010520417, 951284840, 1213972223, + 3173832558, 2611145638, 2515869689, 2245824891], + [521288629UL, 1950277231, 185954712, 1582725458, 3580567609, 2303633688, + 2394948066, 4108622809, 1116800180, 3357585673], + [88675123UL, 3701687786, 458299110, 2500872618, 3633119408, 516391518, + 2377269574, 2599949379, 717229868, 137866584], + [5783321UL, 393427209, 1947109840, 565829276, 1006220149, 971147905, + 1436324242, 2800460115, 1484058076, 3823330032], + [0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219, + 2464530826, 1604040631, 3653403911] ]; - alias XorshiftTypes = std.typetuple.TypeTuple!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192); + alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192); foreach (I, Type; XorshiftTypes) { @@ -1091,24 +1266,13 @@ unittest * std.random. This can be used to confirm that a given function or * object is compatible with all the pseudo-random number generators * available. It is enabled only in unittest mode. - * - * Example: - * - * ---- - * foreach(Rng; PseudoRngTypes) - * { - * static assert(isUniformRng!Rng); - * auto rng = Rng(unpredictableSeed); - * foo(rng); - * } - * ---- */ - -unittest +@safe unittest { - foreach(Rng; PseudoRngTypes) + foreach (Rng; PseudoRngTypes) { static assert(isUniformRNG!Rng); + auto rng = Rng(unpredictableSeed); } } @@ -1120,36 +1284,28 @@ random number sequences every run. Returns: A single unsigned integer seed value, different on each successive call - -Example: - ----- -auto rnd = Random(unpredictableSeed); -auto n = rnd.front; -... ----- */ - @property uint unpredictableSeed() @trusted { - import core.thread : Thread, getpid, TickDuration; + import core.thread : Thread, getpid, MonoTime; static bool seeded; static MinstdRand0 rand; if (!seeded) { uint threadID = cast(uint) cast(void*) Thread.getThis(); - rand.seed((getpid() + threadID) ^ cast(uint) TickDuration.currSystemTick.length); + rand.seed((getpid() + threadID) ^ cast(uint) MonoTime.currTime.ticks); seeded = true; } rand.popFront(); - return cast(uint) (TickDuration.currSystemTick.length ^ rand.front); + return cast(uint) (MonoTime.currTime.ticks ^ rand.front); } +/// @safe unittest { - // not much to test here - auto a = unpredictableSeed; - static assert(is(typeof(a) == uint)); + auto rnd = Random(unpredictableSeed); + auto n = rnd.front; + static assert(is(typeof(n) == uint)); } /** @@ -1162,7 +1318,7 @@ method being used. alias Random = Mt19937; -unittest +@safe unittest { static assert(isUniformRNG!Random); static assert(isUniformRNG!(Random, uint)); @@ -1180,14 +1336,14 @@ A singleton instance of the default random number generator */ @property ref Random rndGen() @safe { - import std.algorithm : map; + import std.algorithm.iteration : map; import std.range : repeat; static Random result; static bool initialized; if (!initialized) { - static if(isSeedable!(Random, typeof(map!((a) => unpredictableSeed)(repeat(0))))) + static if (isSeedable!(Random, typeof(map!((a) => unpredictableSeed)(repeat(0))))) result.seed(map!((a) => unpredictableSeed)(repeat(0))); else result = Random(unpredictableSeed); @@ -1214,23 +1370,24 @@ Returns: A single random variate drawn from the _uniform distribution between $(D a) and $(D b), whose type is the common type of these parameters - -Example: - ----- -auto gen = Random(unpredictableSeed); -// Generate an integer in [0, 1023] -auto a = uniform(0, 1024, gen); -// Generate a float in [0, 1) -auto a = uniform(0.0f, 1.0f, gen); ----- */ auto uniform(string boundaries = "[)", T1, T2) -(T1 a, T2 b) if (!is(CommonType!(T1, T2) == void)) +(T1 a, T2 b) +if (!is(CommonType!(T1, T2) == void)) { return uniform!(boundaries, T1, T2, Random)(a, b, rndGen); } +/// +@safe unittest +{ + auto gen = Random(unpredictableSeed); + // Generate an integer in [0, 1023] + auto a = uniform(0, 1024, gen); + // Generate a float in [0, 1) + auto b = uniform(0.0f, 1.0f, gen); +} + @safe unittest { MinstdRand0 gen; @@ -1251,7 +1408,7 @@ auto uniform(string boundaries = "[)", T1, T2) assert('a' <= x && x < 'z'); } - foreach(i; 0 .. 20) + foreach (i; 0 .. 20) { immutable ubyte a = 0; immutable ubyte b = 15; @@ -1436,7 +1593,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && auto c = uniform(0.0, 1.0); assert(0 <= c && c < 1); - foreach (T; std.typetuple.TypeTuple!(char, wchar, dchar, byte, ubyte, short, ushort, + foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real)) { T lo = 0, hi = 100; @@ -1490,7 +1647,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && auto reproRng = Xorshift(239842); - foreach (T; std.typetuple.TypeTuple!(char, wchar, dchar, byte, ubyte, short, + foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong)) { T lo = T.min + 10, hi = T.max - 10; @@ -1593,7 +1750,7 @@ if (!is(T == enum) && (isIntegral!T || isSomeChar!T) && isUniformRNG!UniformRand else { static assert(T.sizeof == 8 && r.sizeof == 4); - T r1 = urng.front | (cast(T)r << 32); + T r1 = urng.front | (cast(T) r << 32); urng.popFront(); return r1; } @@ -1609,7 +1766,7 @@ if (!is(T == enum) && (isIntegral!T || isSomeChar!T)) @safe unittest { - foreach(T; std.typetuple.TypeTuple!(char, wchar, dchar, byte, ubyte, short, ushort, + foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong)) { T init = uniform!T(); @@ -1666,7 +1823,7 @@ if (is(E == enum)) enum Fruit { Apple = 12, Mango = 29, Pear = 72 } foreach (_; 0 .. 100) { - foreach(f; [uniform!Fruit(), rndGen.uniform!Fruit()]) + foreach (f; [uniform!Fruit(), rndGen.uniform!Fruit()]) { assert(f == Fruit.Apple || f == Fruit.Mango || f == Fruit.Pear); } @@ -1684,8 +1841,8 @@ if (is(E == enum)) * for some applications. * * Params: - * urng = (optional) random number generator to use; - * if not specified, defaults to $(D rndGen) + * rng = (optional) random number generator to use; + * if not specified, defaults to $(D rndGen) * * Returns: * Floating-point random variate of type $(D T) drawn from the _uniform @@ -1693,14 +1850,14 @@ if (is(E == enum)) * */ T uniform01(T = double)() - if (isFloatingPoint!T) +if (isFloatingPoint!T) { return uniform01!T(rndGen); } /// ditto T uniform01(T = double, UniformRNG)(ref UniformRNG rng) - if (isFloatingPoint!T && isUniformRNG!UniformRNG) +if (isFloatingPoint!T && isUniformRNG!UniformRNG) out (result) { assert(0 <= result); @@ -1756,11 +1913,11 @@ body @safe unittest { - import std.typetuple; + import std.meta; foreach (UniformRNG; PseudoRngTypes) { - foreach (T; std.typetuple.TypeTuple!(float, double, real)) + foreach (T; std.meta.AliasSeq!(float, double, real)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 UniformRNG rng = UniformRNG(unpredictableSeed); @@ -1795,7 +1952,7 @@ array of size $(D n) of positive numbers of type $(D F) that sum to $(D 1). If $(D useThis) is provided, it is used as storage. */ F[] uniformDistribution(F = double)(size_t n, F[] useThis = null) - if(isFloatingPoint!F) +if (isFloatingPoint!F) { import std.numeric : normalize; useThis.length = n; @@ -1820,6 +1977,89 @@ F[] uniformDistribution(F = double)(size_t n, F[] useThis = null) assert(approxEqual(reduce!"a + b"(a), 1)); } +/** +Returns a random, uniformly chosen, element `e` from the supplied +$(D Range range). If no random number generator is passed, the default +`rndGen` is used. + +Params: + range = a random access range that has the `length` property defined + urng = (optional) random number generator to use; + if not specified, defaults to `rndGen` + +Returns: + A single random element drawn from the `range`. If it can, it will + return a `ref` to the $(D range element), otherwise it will return + a copy. + */ +auto ref choice(Range, RandomGen = Random)(auto ref Range range, + ref RandomGen urng = rndGen) +if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) +{ + assert(range.length > 0, + __PRETTY_FUNCTION__ ~ ": invalid Range supplied. Range cannot be empty"); + + return range[uniform(size_t(0), $, urng)]; +} + +/// +@safe unittest +{ + import std.algorithm.searching : canFind; + + auto array = [1, 2, 3, 4, 5]; + auto elem = choice(array); + + assert(canFind(array, elem), + "Choice did not return a valid element from the given Range"); + + auto urng = Random(unpredictableSeed); + elem = choice(array, urng); + + assert(canFind(array, elem), + "Choice did not return a valid element from the given Range"); +} + +@safe unittest +{ + import std.algorithm.searching : canFind; + + class MyTestClass + { + int x; + + this(int x) + { + this.x = x; + } + } + + MyTestClass[] testClass; + foreach (i; 0 .. 5) + { + testClass ~= new MyTestClass(i); + } + + auto elem = choice(testClass); + + assert(canFind!((ref MyTestClass a, ref MyTestClass b) => a.x == b.x)(testClass, elem), + "Choice did not return a valid element from the given Range"); +} + +@system unittest +{ + import std.algorithm.searching : canFind; + import std.algorithm.iteration : map; + + auto array = [1, 2, 3, 4, 5]; + auto elemAddr = &choice(array); + + assert(array.map!((ref e) => &e).canFind(elemAddr), + "Choice did not return a ref to an element from the given Range"); + assert(array.canFind(*(cast(int *)(elemAddr))), + "Choice did not return a valid element from the given Range"); +} + /** Shuffles elements of $(D r) using $(D gen) as a shuffler. $(D r) must be a random-access range with length. If no RNG is specified, $(D rndGen) @@ -1832,22 +2072,22 @@ Params: */ void randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen) - if(isRandomAccessRange!Range && isUniformRNG!RandomGen) +if (isRandomAccessRange!Range && isUniformRNG!RandomGen) { return partialShuffle!(Range, RandomGen)(r, r.length, gen); } /// ditto void randomShuffle(Range)(Range r) - if(isRandomAccessRange!Range) +if (isRandomAccessRange!Range) { return randomShuffle(r, rndGen); } -unittest +@safe unittest { - import std.algorithm; - foreach(RandomGen; PseudoRngTypes) + import std.algorithm.sorting : sort; + foreach (RandomGen; PseudoRngTypes) { // Also tests partialShuffle indirectly. auto a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; @@ -1863,9 +2103,9 @@ unittest } /** -Partially shuffles the elements of $(D r) such that upon returning $(D r[0..n]) -is a random subset of $(D r) and is randomly ordered. $(D r[n..r.length]) -will contain the elements not in $(D r[0..n]). These will be in an undefined +Partially shuffles the elements of $(D r) such that upon returning $(D r[0 .. n]) +is a random subset of $(D r) and is randomly ordered. $(D r[n .. r.length]) +will contain the elements not in $(D r[0 .. n]). These will be in an undefined order, but will not be random in the sense that their order after $(D partialShuffle) returns will not be independent of their order before $(D partialShuffle) was called. @@ -1881,40 +2121,61 @@ Params: specified, defaults to $(D rndGen) */ void partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen) - if(isRandomAccessRange!Range && isUniformRNG!RandomGen) +if (isRandomAccessRange!Range && isUniformRNG!RandomGen) { import std.exception : enforce; - import std.algorithm : swapAt; + import std.algorithm.mutation : swapAt; enforce(n <= r.length, "n must be <= r.length for partialShuffle."); foreach (i; 0 .. n) { - swapAt(r, i, uniform(i, n, gen)); + r.swapAt(i, uniform(i, r.length, gen)); } } /// ditto void partialShuffle(Range)(Range r, in size_t n) - if(isRandomAccessRange!Range) +if (isRandomAccessRange!Range) { return partialShuffle(r, n, rndGen); } -unittest +@safe unittest { import std.algorithm; - foreach(RandomGen; PseudoRngTypes) + foreach (RandomGen; PseudoRngTypes) { - auto a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + auto a = [0, 1, 1, 2, 3]; auto b = a.dup; - auto gen = RandomGen(unpredictableSeed); - partialShuffle(a, 5, gen); - assert(a[5 .. $] == b[5 .. $]); - sort(a[0 .. 5]); - assert(a[0 .. 5] == b[0 .. 5]); - partialShuffle(a, 6); - assert(a[6 .. $] == b[6 .. $]); - sort(a[0 .. 6]); - assert(a[0 .. 6] == b[0 .. 6]); + + // Pick a fixed seed so that the outcome of the statistical + // test below is deterministic. + auto gen = RandomGen(12345); + + // NUM times, pick LEN elements from the array at random. + immutable int LEN = 2; + immutable int NUM = 750; + int[][] chk; + foreach (step; 0 .. NUM) + { + partialShuffle(a, LEN, gen); + chk ~= a[0 .. LEN].dup; + } + + // Check that each possible a[0 .. LEN] was produced at least once. + // For a perfectly random RandomGen, the probability that each + // particular combination failed to appear would be at most + // 0.95 ^^ NUM which is approximately 1,962e-17. + // As long as hardware failure (e.g. bit flip) probability + // is higher, we are fine with this unittest. + sort(chk); + assert(equal(uniq(chk), [ [0,1], [0,2], [0,3], + [1,0], [1,1], [1,2], [1,3], + [2,0], [2,1], [2,3], + [3,0], [3,1], [3,2], ])); + + // Check that all the elements are still there. + sort(a); + assert(equal(a, b)); } } @@ -1935,15 +2196,6 @@ Returns: [0, ... $(D proportions.length) - 1], with the probability of getting an individual index value $(D i) being proportional to $(D proportions[i]). - -Example: - ----- -auto x = dice(0.5, 0.5); // x is 0 or 1 in equal proportions -auto y = dice(50, 50); // y is 0 or 1 in equal proportions -auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time, - // and 2 10% of the time ----- */ size_t dice(Rng, Num)(ref Rng rnd, Num[] proportions...) if (isNumeric!Num && isForwardRange!Rng) @@ -1972,17 +2224,26 @@ if (isNumeric!Num) return diceImpl(rndGen, proportions); } -private size_t diceImpl(Rng, Range)(ref Rng rng, Range proportions) - if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng) +/// +@safe unittest +{ + auto x = dice(0.5, 0.5); // x is 0 or 1 in equal proportions + auto y = dice(50, 50); // y is 0 or 1 in equal proportions + auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time, + // and 2 10% of the time +} + +private size_t diceImpl(Rng, Range)(ref Rng rng, scope Range proportions) +if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng) in { - import std.algorithm : all; + import std.algorithm.searching : all; assert(proportions.save.all!"a >= 0"); } body { import std.exception : enforce; - import std.algorithm : reduce; + import std.algorithm.iteration : reduce; double sum = reduce!"a + b"(0.0, proportions.save); enforce(sum > 0, "Proportions in a dice cannot sum to zero"); immutable point = uniform(0.0, sum, rng); @@ -2000,7 +2261,7 @@ body assert(false); } -unittest +@safe unittest { auto rnd = Random(unpredictableSeed); auto i = dice(rnd, 0.0, 100.0); @@ -2062,17 +2323,15 @@ foreach (e; randomCover(a, rndGen)) // ... so this second random cover writeln(e); // the previous one. } ---- - -These issues will be resolved in a second-generation std.random that -re-implements random number generators as reference types. */ struct RandomCover(Range, UniformRNG = void) - if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) +if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) { private Range _input; private bool[] _chosen; private size_t _current; private size_t _alreadyChosen = 0; + private bool _isEmpty = false; static if (is(UniformRNG == void)) { @@ -2080,9 +2339,13 @@ struct RandomCover(Range, UniformRNG = void) { _input = input; _chosen.length = _input.length; - if (_chosen.length == 0) + if (_input.empty) { - _alreadyChosen = 1; + _isEmpty = true; + } + else + { + _current = uniform(0, _chosen.length); } } } @@ -2095,9 +2358,13 @@ struct RandomCover(Range, UniformRNG = void) _input = input; _rng = rng; _chosen.length = _input.length; - if (_chosen.length == 0) + if (_input.empty) { - _alreadyChosen = 1; + _isEmpty = true; + } + else + { + _current = uniform(0, _chosen.length, rng); } } @@ -2111,39 +2378,32 @@ struct RandomCover(Range, UniformRNG = void) { @property size_t length() { - if (_alreadyChosen == 0) - { - return _input.length; - } - else - { - return (1 + _input.length) - _alreadyChosen; - } + return _input.length - _alreadyChosen; } } @property auto ref front() { - if (_alreadyChosen == 0) - { - popFront(); - } + assert(!_isEmpty); return _input[_current]; } void popFront() { - if (_alreadyChosen >= _input.length) + assert(!_isEmpty); + + size_t k = _input.length - _alreadyChosen - 1; + if (k == 0) { - // No more elements - ++_alreadyChosen; // means we're done + _isEmpty = true; + ++_alreadyChosen; return; } - size_t k = _input.length - _alreadyChosen; + size_t i; foreach (e; _input) { - if (_chosen[i]) { ++i; continue; } + if (_chosen[i] || i == _current) { ++i; continue; } // Roll a dice with k faces static if (is(UniformRNG == void)) { @@ -2156,7 +2416,7 @@ struct RandomCover(Range, UniformRNG = void) assert(k > 1 || chooseMe); if (chooseMe) { - _chosen[i] = true; + _chosen[_current] = true; _current = i; ++_alreadyChosen; return; @@ -2177,29 +2437,30 @@ struct RandomCover(Range, UniformRNG = void) } } - @property bool empty() { return _alreadyChosen > _input.length; } + @property bool empty() { return _isEmpty; } } /// Ditto auto randomCover(Range, UniformRNG)(Range r, auto ref UniformRNG rng) - if (isRandomAccessRange!Range && isUniformRNG!UniformRNG) +if (isRandomAccessRange!Range && isUniformRNG!UniformRNG) { return RandomCover!(Range, UniformRNG)(r, rng); } /// Ditto auto randomCover(Range)(Range r) - if (isRandomAccessRange!Range) +if (isRandomAccessRange!Range) { return RandomCover!(Range, void)(r); } -unittest +@safe unittest { import std.algorithm; import std.conv; int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]; - foreach (UniformRNG; std.typetuple.TypeTuple!(void, PseudoRngTypes)) + int[] c; + foreach (UniformRNG; std.meta.AliasSeq!(void, PseudoRngTypes)) { static if (is(UniformRNG == void)) { @@ -2215,6 +2476,8 @@ unittest // check for constructor passed a value-type RNG auto rc2 = RandomCover!(int[], UniformRNG)(a, UniformRNG(unpredictableSeed)); static assert(isForwardRange!(typeof(rc2))); + auto rcEmpty = randomCover(c, rng); + assert(rcEmpty.length == 0); } int[] b = new int[9]; @@ -2229,13 +2492,24 @@ unittest } } -unittest +@safe unittest { // Bugzilla 12589 int[] r = []; auto rc = randomCover(r); assert(rc.length == 0); assert(rc.empty); + + // Bugzilla 16724 + import std.range : iota; + auto range = iota(10); + auto randy = range.randomCover; + + for (int i=1; i <= range.length; i++) + { + randy.popFront; + assert(randy.length == range.length - i); + } } // RandomSample @@ -2269,7 +2543,7 @@ Returns: and $(D rng) are forward ranges, an input range otherwise. $(D RandomSample) implements Jeffrey Scott Vitter's Algorithm D -(see Vitter $(WEB dx.doi.org/10.1145/358105.893, 1984), $(WEB +(see Vitter $(HTTP dx.doi.org/10.1145/358105.893, 1984), $(HTTP dx.doi.org/10.1145/23002.23003, 1987)), which selects a sample of size $(D n) in O(n) steps and requiring O(n) random variates, regardless of the size of the data being sampled. The exception @@ -2318,12 +2592,9 @@ foreach (e; randomSample(a, 5, rndGen)) // ... so this second random writeln(e); // values as the previous one. } ---- - -These issues will be resolved in a second-generation std.random that -re-implements random number generators as reference types. */ struct RandomSample(Range, UniformRNG = void) - if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) +if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) { private size_t _available, _toSelect; private enum ushort _alphaInverse = 13; // Vitter's recommended value. @@ -2429,6 +2700,7 @@ struct RandomSample(Range, UniformRNG = void) return _toSelect == 0; } +/// Ditto @property auto ref front() { assert(!empty); @@ -2526,7 +2798,7 @@ to remaining data values is sufficiently large. size_t s; double v, quot, top; - if (_toSelect==1) + if (_toSelect == 1) { static if (is(UniformRNG == void)) { @@ -2610,7 +2882,7 @@ Variable names are chosen to match those in Vitter's paper. while (true) { // Step D2: set values of x and u. - while(1) + while (1) { x = _available * (1-_Vprime); s = cast(size_t) trunc(x); @@ -2710,34 +2982,35 @@ Variable names are chosen to match those in Vitter's paper. /// Ditto auto randomSample(Range)(Range r, size_t n, size_t total) - if (isInputRange!Range) +if (isInputRange!Range) { return RandomSample!(Range, void)(r, n, total); } /// Ditto auto randomSample(Range)(Range r, size_t n) - if (isInputRange!Range && hasLength!Range) +if (isInputRange!Range && hasLength!Range) { return RandomSample!(Range, void)(r, n, r.length); } /// Ditto auto randomSample(Range, UniformRNG)(Range r, size_t n, size_t total, auto ref UniformRNG rng) - if (isInputRange!Range && isUniformRNG!UniformRNG) +if (isInputRange!Range && isUniformRNG!UniformRNG) { return RandomSample!(Range, UniformRNG)(r, n, total, rng); } /// Ditto auto randomSample(Range, UniformRNG)(Range r, size_t n, auto ref UniformRNG rng) - if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG) +if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG) { return RandomSample!(Range, UniformRNG)(r, n, r.length, rng); } -unittest +@system unittest { + // @system because it takes the address of a local import std.exception; import std.range; import std.conv : text; @@ -2756,7 +3029,7 @@ unittest foreach (UniformRNG; PseudoRngTypes) { - auto rng = UniformRNG(unpredictableSeed); + auto rng = UniformRNG(1234); /* First test the most general case: randomSample of input range, with and * without a specified random number generator. */ @@ -2819,10 +3092,18 @@ unittest /* Check that randomSample will throw an error if we claim more * items are available than there actually are, or if we try to * sample more items than are available. */ - assert(collectExceptionMsg(randomSample(a, 5, 15)) == "RandomSample: specified 15 items as available when input contains only 10"); - assert(collectExceptionMsg(randomSample(a, 15)) == "RandomSample: cannot sample 15 items when only 10 are available"); - assert(collectExceptionMsg(randomSample(a, 9, 8)) == "RandomSample: cannot sample 9 items when only 8 are available"); - assert(collectExceptionMsg(randomSample(TestInputRange(), 12, 11)) == "RandomSample: cannot sample 12 items when only 11 are available"); + assert(collectExceptionMsg( + randomSample(a, 5, 15) + ) == "RandomSample: specified 15 items as available when input contains only 10"); + assert(collectExceptionMsg( + randomSample(a, 15) + ) == "RandomSample: cannot sample 15 items when only 10 are available"); + assert(collectExceptionMsg( + randomSample(a, 9, 8) + ) == "RandomSample: cannot sample 9 items when only 8 are available"); + assert(collectExceptionMsg( + randomSample(TestInputRange(), 12, 11) + ) == "RandomSample: cannot sample 12 items when only 11 are available"); /* Check that sampling algorithm never accidentally overruns the end of * the input range. If input is an InputRange without .length, this @@ -2950,16 +3231,16 @@ unittest * This is a rough-and-ready check that the statistical properties * are in the ballpark -- not a proper validation of statistical * quality! This incidentally also checks for reference-type - * initialization bugs, as the foreach() loop will operate on a + * initialization bugs, as the foreach () loop will operate on a * copy of the popFronted (and hence initialized) sample. */ { size_t count0, count1, count99; - foreach(_; 0 .. 100_000) + foreach (_; 0 .. 100_000) { - auto sample = randomSample(iota(100), 5); + auto sample = randomSample(iota(100), 5, &rng); sample.popFront(); - foreach(s; sample) + foreach (s; sample) { if (s == 0) { diff --git a/std/range/interfaces.d b/std/range/interfaces.d index 9fa91d9f948..e519a667e90 100644 --- a/std/range/interfaces.d +++ b/std/range/interfaces.d @@ -1,83 +1,86 @@ /** -This module is a submodule of $(LINK2 std_range.html, std.range). +This module is a submodule of $(MREF std, range). -The main $(D std.range) module provides template-based tools for working with +The main $(MREF std, range) module provides template-based tools for working with ranges, but sometimes an object-based interface for ranges is needed, such as when runtime polymorphism is required. For this purpose, this submodule provides a number of object and $(D interface) definitions that can be used to -wrap around _range objects created by the $(D std.range) templates. +wrap around _range objects created by the $(MREF std, range) templates. +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , - $(TR $(TD $(D $(LREF InputRange))) + $(TR $(TD $(LREF InputRange)) $(TD Wrapper for input ranges. )) - $(TR $(TD $(D $(LREF InputAssignable))) + $(TR $(TD $(LREF InputAssignable)) $(TD Wrapper for input ranges with assignable elements. )) - $(TR $(TD $(D $(LREF ForwardRange))) + $(TR $(TD $(LREF ForwardRange)) $(TD Wrapper for forward ranges. )) - $(TR $(TD $(D $(LREF ForwardAssignable))) + $(TR $(TD $(LREF ForwardAssignable)) $(TD Wrapper for forward ranges with assignable elements. )) - $(TR $(TD $(D $(LREF BidirectionalRange))) + $(TR $(TD $(LREF BidirectionalRange)) $(TD Wrapper for bidirectional ranges. )) - $(TR $(TD $(D $(LREF BidirectionalAssignable))) + $(TR $(TD $(LREF BidirectionalAssignable)) $(TD Wrapper for bidirectional ranges with assignable elements. )) - $(TR $(TD $(D $(LREF RandomAccessFinite))) + $(TR $(TD $(LREF RandomAccessFinite)) $(TD Wrapper for finite random-access ranges. )) - $(TR $(TD $(D $(LREF RandomAccessAssignable))) + $(TR $(TD $(LREF RandomAccessAssignable)) $(TD Wrapper for finite random-access ranges with assignable elements. )) - $(TR $(TD $(D $(LREF RandomAccessInfinite))) + $(TR $(TD $(LREF RandomAccessInfinite)) $(TD Wrapper for infinite random-access ranges. )) - $(TR $(TD $(D $(LREF OutputRange))) + $(TR $(TD $(LREF OutputRange)) $(TD Wrapper for output ranges. )) - $(TR $(TD $(D $(LREF OutputRangeObject))) + $(TR $(TD $(LREF OutputRangeObject)) $(TD Class that implements the $(D OutputRange) interface and wraps the $(D put) methods in virtual functions. + $(TR $(TD $(LREF outputRangeObject)) + Convenience function for creating an $(D OutputRangeObject) with a base + range of type R that accepts types E. )) - $(TR $(TD $(D $(LREF InputRangeObject))) + $(TR $(TD $(LREF InputRangeObject)) $(TD Class that implements the $(D InputRange) interface and wraps the input _range methods in virtual functions. )) - $(TR $(TD $(D $(LREF RefRange))) - $(TD Wrapper around a forward _range that gives it reference semantics. + $(TR $(TD $(LREF InputRangeObject)) + $(TD Convenience function for creating an $(D InputRangeObject) + of the proper type. + )) + $(TR $(TD $(LREF MostDerivedInputRange)) + $(TD Returns the interface type that best matches the range.) )) ) Source: $(PHOBOSSRC std/range/_interfaces.d) -Macros: - -WIKI = Phobos/StdRange - -Copyright: Copyright by authors 2008-. +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(WEB erdani.com, Andrei Alexandrescu), David Simcha, +Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and Jonathan M Davis. Credit for some of the ideas in building this module goes -to $(WEB fantascienza.net/leonardo/so/, Leonardo Maffi). +to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range.interfaces; +import std.meta; import std.range.primitives; import std.traits; -import std.typetuple; /**These interfaces are intended to provide virtual function-based wrappers * around input ranges with element type E. This is useful where a well-defined * binary interface is required, such as when a DLL function or virtual function - * needs to accept a generic range as a parameter. Note that - * $(LREF isInputRange) and friends check for conformance to structural - * interfaces, not for implementation of these $(D interface) types. + * needs to accept a generic range as a parameter. Note that + * $(REF_ALTTEXT isInputRange, isInputRange, std, range, primitives) + * and friends check for conformance to structural interfaces + * not for implementation of these $(D interface) types. * * Limitations: * @@ -115,17 +118,17 @@ interface InputRange(E) { /**$(D foreach) iteration uses opApply, since one delegate call per loop * iteration is faster than three virtual function calls. */ - int opApply(int delegate(E)); + int opApply(scope int delegate(E)); /// Ditto - int opApply(int delegate(size_t, E)); + int opApply(scope int delegate(size_t, E)); } /// -unittest +@safe unittest { - import std.algorithm : map; + import std.algorithm.iteration : map; import std.range : iota; void useRange(InputRange!int range) { @@ -182,7 +185,8 @@ interface RandomAccessFinite(E) : BidirectionalRange!(E) { // Can't support slicing until issues with requiring slicing for all // finite random access ranges are fully resolved. - version(none) { + version(none) + { /// RandomAccessFinite!E opSlice(size_t, size_t); } @@ -204,6 +208,13 @@ interface RandomAccessInfinite(E) : ForwardRange!E { interface InputAssignable(E) : InputRange!E { /// @property void front(E newVal); + + alias front = InputRange!E.front; // overload base interface method +} + +@safe unittest +{ + static assert(isInputRange!(InputAssignable!int)); } /**Adds assignable elements to ForwardRange.*/ @@ -268,6 +279,7 @@ class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) { // DMD won't let me put them in. private R _range; + /// this(R range) { this._range = range; } @@ -277,33 +289,56 @@ class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) { /**Returns the interface type that best matches $(D R).*/ -template MostDerivedInputRange(R) if (isInputRange!(Unqual!R)) { +template MostDerivedInputRange(R) +if (isInputRange!(Unqual!R)) +{ private alias E = ElementType!R; - static if (isRandomAccessRange!R) { - static if (isInfinite!R) { + static if (isRandomAccessRange!R) + { + static if (isInfinite!R) + { alias MostDerivedInputRange = RandomAccessInfinite!E; - } else static if (hasAssignableElements!R) { + } + else static if (hasAssignableElements!R) + { alias MostDerivedInputRange = RandomFiniteAssignable!E; - } else { + } + else + { alias MostDerivedInputRange = RandomAccessFinite!E; } - } else static if (isBidirectionalRange!R) { - static if (hasAssignableElements!R) { + } + else static if (isBidirectionalRange!R) + { + static if (hasAssignableElements!R) + { alias MostDerivedInputRange = BidirectionalAssignable!E; - } else { + } + else + { alias MostDerivedInputRange = BidirectionalRange!E; } - } else static if (isForwardRange!R) { - static if (hasAssignableElements!R) { + } + else static if (isForwardRange!R) + { + static if (hasAssignableElements!R) + { alias MostDerivedInputRange = ForwardAssignable!E; - } else { + } + else + { alias MostDerivedInputRange = ForwardRange!E; } - } else { - static if (hasAssignableElements!R) { + } + else + { + static if (hasAssignableElements!R) + { alias MostDerivedInputRange = InputAssignable!E; - } else { + } + else + { alias MostDerivedInputRange = InputRange!E; } } @@ -313,12 +348,19 @@ template MostDerivedInputRange(R) if (isInputRange!(Unqual!R)) { * all relevant range primitives in virtual functions. If $(D R) is already * derived from the $(D InputRange) interface, aliases itself away. */ -template InputRangeObject(R) if (isInputRange!(Unqual!R)) { - static if (is(R : InputRange!(ElementType!R))) { +template InputRangeObject(R) +if (isInputRange!(Unqual!R)) +{ + static if (is(R : InputRange!(ElementType!R))) + { alias InputRangeObject = R; - } else static if (!is(Unqual!R == R)) { + } + else static if (!is(Unqual!R == R)) + { alias InputRangeObject = InputRangeObject!(Unqual!R); - } else { + } + else + { /// class InputRangeObject : MostDerivedInputRange!(R) { @@ -332,56 +374,63 @@ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { @property E front() { return _range.front; } E moveFront() { - return .moveFront(_range); + return _range.moveFront(); } void popFront() { _range.popFront(); } @property bool empty() { return _range.empty; } - static if (isForwardRange!R) { + static if (isForwardRange!R) + { @property typeof(this) save() { return new typeof(this)(_range.save); } } - static if (hasAssignableElements!R) { + static if (hasAssignableElements!R) + { @property void front(E newVal) { _range.front = newVal; } } - static if (isBidirectionalRange!R) { + static if (isBidirectionalRange!R) + { @property E back() { return _range.back; } E moveBack() { - return .moveBack(_range); + return _range.moveBack(); } void popBack() { return _range.popBack(); } - static if (hasAssignableElements!R) { + static if (hasAssignableElements!R) + { @property void back(E newVal) { _range.back = newVal; } } } - static if (isRandomAccessRange!R) { + static if (isRandomAccessRange!R) + { E opIndex(size_t index) { return _range[index]; } E moveAt(size_t index) { - return .moveAt(_range, index); + return _range.moveAt(index); } - static if (hasAssignableElements!R) { + static if (hasAssignableElements!R) + { void opIndexAssign(E val, size_t index) { _range[index] = val; } } - static if (!isInfinite!R) { + static if (!isInfinite!R) + { @property size_t length() { return _range.length; } @@ -391,9 +440,10 @@ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { // Can't support slicing until all the issues with // requiring slicing support for finite random access // ranges are resolved. - version(none) { + version(none) + { typeof(this) opSlice(size_t lower, size_t upper) { - return new typeof(this)(_range[lower..upper]); + return new typeof(this)(_range[lower .. upper]); } } } @@ -401,10 +451,11 @@ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { // Optimization: One delegate call is faster than three virtual // function calls. Use opApply for foreach syntax. - int opApply(int delegate(E) dg) { + int opApply(scope int delegate(E) dg) { int res; - for(auto r = _range; !r.empty; r.popFront()) { + for (auto r = _range; !r.empty; r.popFront()) + { res = dg(r.front); if (res) break; } @@ -412,11 +463,12 @@ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { return res; } - int opApply(int delegate(size_t, E) dg) { + int opApply(scope int delegate(size_t, E) dg) { int res; size_t i = 0; - for(auto r = _range; !r.empty; r.popFront()) { + for (auto r = _range; !r.empty; r.popFront()) + { res = dg(i, r.front); if (res) break; i++; @@ -431,10 +483,15 @@ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { /**Convenience function for creating an $(D InputRangeObject) of the proper type. * See $(LREF InputRange) for an example. */ -InputRangeObject!R inputRangeObject(R)(R range) if (isInputRange!R) { - static if (is(R : InputRange!(ElementType!R))) { +InputRangeObject!R inputRangeObject(R)(R range) +if (isInputRange!R) +{ + static if (is(R : InputRange!(ElementType!R))) + { return range; - } else { + } + else + { return new InputRangeObject!R(range); } } @@ -451,7 +508,7 @@ template outputRangeObject(E...) { } /// -unittest +@safe unittest { import std.array; auto app = appender!(uint[])(); @@ -460,10 +517,10 @@ unittest static assert(is(typeof(appWrapped) : OutputRange!(uint))); } -unittest +@system unittest { import std.internal.test.dummyrange; - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.array; static void testEquality(R)(iInputRange r1, R r2) { @@ -481,12 +538,13 @@ unittest assert(arr.moveBack() == 4); assert(arr.moveAt(1) == 2); - foreach(elem; arrWrapped) {} - foreach(i, elem; arrWrapped) {} + foreach (elem; arrWrapped) {} + foreach (i, elem; arrWrapped) {} assert(inputRangeObject(arrWrapped) is arrWrapped); - foreach(DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { auto d = DummyType.init; static assert(propagatesRangeType!(DummyType, typeof(inputRangeObject(d)))); diff --git a/std/range/package.d b/std/range/package.d index 3465024a6d5..83a93a2bd19 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -3,169 +3,227 @@ /** This module defines the notion of a range. Ranges generalize the concept of arrays, lists, or anything that involves sequential access. This abstraction -enables the same set of algorithms (see $(LINK2 std_algorithm.html, -std.algorithm)) to be used with a vast variety of different concrete types. For -example, a linear search algorithm such as $(LINK2 std_algorithm.html#find, -std.algorithm.find) works not just for arrays, but for linked-lists, input -files, incoming network data, etc. - -For more detailed information about the conceptual aspect of ranges and the -motivation behind them, see Andrei Alexandrescu's article -$(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357&rll=1, -$(I On Iteration)). +enables the same set of algorithms (see $(MREF std, algorithm)) to be used +with a vast variety of different concrete types. For example, +a linear search algorithm such as $(REF find, std, algorithm, searching) +works not just for arrays, but for linked-lists, input files, +incoming network data, etc. + +Guides: + +There are many articles available that can bolster understanding ranges: + +$(UL + $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on _ranges) + for the basics of working with and creating range-based code.) + $(LI Jonathan M. Davis $(LINK2 http://dconf.org/2015/talks/davis.html, $(I Introduction to Ranges)) + talk at DConf 2015 a vivid introduction from its core constructs to practical advice.) + $(LI The DLang Tour's $(LINK2 http://tour.dlang.org/tour/en/basics/ranges, chapter on ranges) + for an interactive introduction.) + $(LI H. S. Teoh's $(LINK2 http://wiki.dlang.org/Component_programming_with_ranges, tutorial on + component programming with ranges) for a real-world showcase of the influence + of _range-based programming on complex algorithms.) + $(LI Andrei Alexandrescu's article + $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1, + $(I On Iteration)) for conceptual aspect of ranges and the motivation + ) +) Submodules: This module has two submodules: -$(LIST -$(DIV , -The $(LINK2 std_range_primitives.html, $(D std._range.primitives)) submodule +The $(MREF std, _range, primitives) submodule provides basic _range functionality. It defines several templates for testing whether a given object is a _range, what kind of _range it is, and provides some common _range operations. -), -$(DIV , -The $(LINK2 std_range_interfaces.html, $(D std._range.interfaces)) submodule + +The $(MREF std, _range, interfaces) submodule provides object-based interfaces for working with ranges via runtime polymorphism. -)) The remainder of this module provides a rich set of _range creation and composition templates that let you construct new ranges out of existing ranges: + +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , - $(TR $(TD $(D $(LREF chain))) + $(TR $(TD $(LREF chain)) $(TD Concatenates several ranges into a single _range. )) - $(TR $(TD $(D $(LREF chunks))) + $(TR $(TD $(LREF choose)) + $(TD Chooses one of two ranges at runtime based on a boolean condition. + )) + $(TR $(TD $(LREF chooseAmong)) + $(TD Chooses one of several ranges at runtime based on an index. + )) + $(TR $(TD $(LREF chunks)) $(TD Creates a _range that returns fixed-size chunks of the original _range. )) - $(TR $(TD $(D $(LREF cycle))) + $(TR $(TD $(LREF cycle)) $(TD Creates an infinite _range that repeats the given forward _range indefinitely. Good for implementing circular buffers. )) - $(TR $(TD $(D $(LREF drop))) + $(TR $(TD $(LREF drop)) $(TD Creates the _range that results from discarding the first $(I n) elements from the given _range. )) - $(TR $(TD $(D $(LREF dropExactly))) + $(TR $(TD $(LREF dropBack)) + $(TD Creates the _range that results from discarding the last $(I n) + elements from the given _range. + )) + $(TR $(TD $(LREF dropExactly)) $(TD Creates the _range that results from discarding exactly $(I n) of the first elements from the given _range. )) - $(TR $(TD $(D $(LREF dropOne))) + $(TR $(TD $(LREF dropBackExactly)) + $(TD Creates the _range that results from discarding exactly $(I n) + of the last elements from the given _range. + )) + $(TR $(TD $(LREF dropOne)) + $(TD Creates the _range that results from discarding + the first element from the given _range. + )) + $(TR $(TD $(D $(LREF dropBackOne))) $(TD Creates the _range that results from discarding - the first elements from the given _range. + the last element from the given _range. )) - $(TR $(TD $(D $(LREF enumerate))) + $(TR $(TD $(LREF enumerate)) $(TD Iterates a _range with an attached index variable. )) - $(TR $(TD $(D $(LREF frontTransversal))) + $(TR $(TD $(LREF evenChunks)) + $(TD Creates a _range that returns a number of chunks of + approximately equal length from the original _range. + )) + $(TR $(TD $(LREF frontTransversal)) $(TD Creates a _range that iterates over the first elements of the given ranges. )) - $(TR $(TD $(D $(LREF indexed))) + $(TR $(TD $(LREF generate)) + $(TD Creates a _range by successive calls to a given function. This + allows to create ranges as a single delegate. + )) + $(TR $(TD $(LREF indexed)) $(TD Creates a _range that offers a view of a given _range as though its elements were reordered according to a given _range of indices. )) - $(TR $(TD $(D $(LREF iota))) + $(TR $(TD $(LREF iota)) $(TD Creates a _range consisting of numbers between a starting point and ending point, spaced apart by a given interval. )) - $(TR $(TD $(D $(LREF lockstep))) + $(TR $(TD $(LREF lockstep)) $(TD Iterates $(I n) _ranges in lockstep, for use in a $(D foreach) loop. Similar to $(D zip), except that $(D lockstep) is designed especially for $(D foreach) loops. )) - $(TR $(TD $(D $(LREF NullSink))) + $(TR $(TD $(LREF NullSink)) $(TD An output _range that discards the data it receives. )) - $(TR $(TD $(D $(LREF only))) + $(TR $(TD $(LREF only)) $(TD Creates a _range that iterates over the given arguments. )) - $(TR $(TD $(D $(LREF radial))) + $(TR $(TD $(LREF padLeft)) + $(TD Pads a _range to a specified length by adding a given element to + the front of the _range. Is lazy if the _range has a known length. + )) + $(TR $(TD $(LREF padRight)) + $(TD Lazily pads a _range to a specified length by adding a given element to + the back of the _range. + )) + $(TR $(TD $(LREF radial)) $(TD Given a random-access _range and a starting point, creates a _range that alternately returns the next left and next right element to the starting point. )) - $(TR $(TD $(D $(LREF recurrence))) + $(TR $(TD $(LREF recurrence)) $(TD Creates a forward _range whose values are defined by a mathematical recurrence relation. )) - $(TR $(TD $(D $(LREF repeat))) + $(TR $(TD $(LREF refRange)) + $(TD Pass a _range by reference. Both the original _range and the RefRange + will always have the exact same elements. + Any operation done on one will affect the other. + )) + $(TR $(TD $(LREF repeat)) $(TD Creates a _range that consists of a single element repeated $(I n) times, or an infinite _range repeating that element indefinitely. )) - $(TR $(TD $(D $(LREF retro))) + $(TR $(TD $(LREF retro)) $(TD Iterates a bidirectional _range backwards. )) - $(TR $(TD $(D $(LREF roundRobin))) + $(TR $(TD $(LREF roundRobin)) $(TD Given $(I n) ranges, creates a new _range that return the $(I n) first elements of each _range, in turn, then the second element of each _range, and so on, in a round-robin fashion. )) - $(TR $(TD $(D $(LREF sequence))) + $(TR $(TD $(LREF sequence)) $(TD Similar to $(D recurrence), except that a random-access _range is created. )) - $(TR $(TD $(D $(LREF stride))) + $(TR $(TD $(D $(LREF slide))) + $(TD Creates a _range that returns a fixed-size sliding window + over the original _range. Unlike chunks, + it advances a configurable number of items at a time, + not one chunk at a time). + )) + $(TR $(TD $(LREF stride)) $(TD Iterates a _range with stride $(I n). )) - $(TR $(TD $(D $(LREF take))) + $(TR $(TD $(LREF tail)) + $(TD Return a _range advanced to within $(D n) elements of the end of + the given _range. + )) + $(TR $(TD $(LREF take)) $(TD Creates a sub-_range consisting of only up to the first $(I n) elements of the given _range. )) - $(TR $(TD $(D $(LREF takeExactly))) + $(TR $(TD $(LREF takeExactly)) $(TD Like $(D take), but assumes the given _range actually has $(I n) elements, and therefore also defines the $(D length) property. )) - $(TR $(TD $(D $(LREF takeNone))) + $(TR $(TD $(LREF takeNone)) $(TD Creates a random-access _range consisting of zero elements of the given _range. )) - $(TR $(TD $(D $(LREF takeOne))) + $(TR $(TD $(LREF takeOne)) $(TD Creates a random-access _range consisting of exactly the first element of the given _range. )) - $(TR $(TD $(D $(LREF tee))) + $(TR $(TD $(LREF tee)) $(TD Creates a _range that wraps a given _range, forwarding along its elements while also calling a provided function with each element. )) - $(TR $(TD $(D $(LREF transposed))) + $(TR $(TD $(LREF transposed)) $(TD Transposes a _range of ranges. )) - $(TR $(TD $(D $(LREF transversal))) + $(TR $(TD $(LREF transversal)) $(TD Creates a _range that iterates over the $(I n)'th elements of the given random-access ranges. )) - $(TR $(TD $(D $(LREF zip))) + $(TR $(TD $(LREF zip)) $(TD Given $(I n) _ranges, creates a _range that successively returns a tuple of all the first elements, a tuple of all the second elements, etc. )) ) +Sortedness: + Ranges whose elements are sorted afford better efficiency with certain -operations. For this, the $(D $(LREF assumeSorted)) function can be used to -construct a $(D $(LREF SortedRange)) from a pre-sorted _range. The $(LINK2 -std_algorithm.html#sort, $(D std.algorithm.sort)) function also conveniently -returns a $(D SortedRange). $(D SortedRange) objects provide some additional +operations. For this, the $(LREF assumeSorted) function can be used to +construct a $(LREF SortedRange) from a pre-sorted _range. The $(REF +sort, std, algorithm, sorting) function also conveniently +returns a $(LREF SortedRange). $(LREF SortedRange) objects provide some additional _range operations that take advantage of the fact that the _range is sorted. Source: $(PHOBOSSRC std/_range/_package.d) -Macros: +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -WIKI = Phobos/StdRange - -Copyright: Copyright by authors 2008-. - -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(WEB erdani.com, Andrei Alexandrescu), David Simcha, -and Jonathan M Davis. Credit for some of the ideas in building this module goes -to $(WEB fantascienza.net/leonardo/so/, Leonardo Maffi). +Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, Jonathan M Davis, +and Jack Stouffer. Credit for some of the ideas in building this module goes +to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range; @@ -174,14 +232,25 @@ public import std.range.interfaces; public import std.array; public import std.typecons : Flag, Yes, No; -import std.traits; -import std.typetuple; +import std.meta; // allSatisfy, staticMap +import std.traits; // CommonType, isCallable, isFloatingPoint, isIntegral, + // isPointer, isSomeFunction, isStaticArray, Unqual /** Iterates a bidirectional range backwards. The original range can be accessed by using the $(D source) property. Applying retro twice to the same range yields the original range. + +Params: + r = the bidirectional range to iterate backwards + +Returns: + A bidirectional range with length if `r` also provides a length. Or, + if `r` is a random access range, then the return value will be random + access as well. +See_Also: + $(REF reverse, std,algorithm,mutation) for mutating the source range directly. */ auto retro(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) @@ -202,9 +271,7 @@ if (isBidirectionalRange!(Unqual!Range)) static if (hasLength!R) { - private alias IndexType = CommonType!(size_t, typeof(source.length)); - - IndexType retroIndex(IndexType n) + size_t retroIndex(size_t n) { return source.length - n - 1; } @@ -223,30 +290,30 @@ if (isBidirectionalRange!(Unqual!Range)) @property auto ref back() { return source.front; } void popBack() { source.popFront(); } - static if(is(typeof(.moveBack(source)))) + static if (is(typeof(source.moveBack()))) { ElementType!R moveFront() { - return .moveBack(source); + return source.moveBack(); } } - static if(is(typeof(.moveFront(source)))) + static if (is(typeof(source.moveFront()))) { ElementType!R moveBack() { - return .moveFront(source); + return source.moveFront(); } } static if (hasAssignableElements!R) { - @property auto front(ElementType!R val) + @property void front(ElementType!R val) { source.back = val; } - @property auto back(ElementType!R val) + @property void back(ElementType!R val) { source.front = val; } @@ -254,26 +321,26 @@ if (isBidirectionalRange!(Unqual!Range)) static if (isRandomAccessRange!(R) && hasLength!(R)) { - auto ref opIndex(IndexType n) { return source[retroIndex(n)]; } + auto ref opIndex(size_t n) { return source[retroIndex(n)]; } static if (hasAssignableElements!R) { - void opIndexAssign(ElementType!R val, IndexType n) + void opIndexAssign(ElementType!R val, size_t n) { source[retroIndex(n)] = val; } } - static if (is(typeof(.moveAt(source, 0)))) + static if (is(typeof(source.moveAt(0)))) { - ElementType!R moveAt(IndexType index) + ElementType!R moveAt(size_t index) { - return .moveAt(source, retroIndex(index)); + return source.moveAt(retroIndex(index)); } } static if (hasSlicing!R) - typeof(this) opSlice(IndexType a, IndexType b) + typeof(this) opSlice(size_t a, size_t b) { return typeof(this)(source[source.length - b .. source.length - a]); } @@ -296,18 +363,19 @@ if (isBidirectionalRange!(Unqual!Range)) /// -@safe unittest +pure @safe nothrow @nogc unittest { - import std.algorithm : equal; - int[] a = [ 1, 2, 3, 4, 5 ]; - assert(equal(retro(a), [ 5, 4, 3, 2, 1 ][])); - assert(retro(a).source is a); - assert(retro(retro(a)) is a); + import std.algorithm.comparison : equal; + int[5] a = [ 1, 2, 3, 4, 5 ]; + int[5] b = [ 5, 4, 3, 2, 1 ]; + assert(equal(retro(a[]), b[])); + assert(retro(a[]).source is a[]); + assert(retro(retro(a[])) is a[]); } -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; static assert(isBidirectionalRange!(typeof(retro("hello")))); int[] a; static assert(is(typeof(a) == typeof(retro(retro(a))))); @@ -331,14 +399,19 @@ if (isBidirectionalRange!(Unqual!Range)) auto r = retro(foo); } -@safe unittest +pure @safe nothrow unittest { - import std.internal.test.dummyrange; + import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType, + ReturnBy; - foreach(DummyType; AllDummyRanges) { - static if (!isBidirectionalRange!DummyType) { + foreach (DummyType; AllDummyRanges) + { + static if (!isBidirectionalRange!DummyType) + { static assert(!__traits(compiles, Retro!DummyType)); - } else { + } + else + { DummyType dummyRange; dummyRange.reinit(); @@ -349,11 +422,13 @@ if (isBidirectionalRange!(Unqual!Range)) assert(myRetro.moveFront() == 10); assert(myRetro.moveBack() == 1); - static if (isRandomAccessRange!DummyType && hasLength!DummyType) { + static if (isRandomAccessRange!DummyType && hasLength!DummyType) + { assert(myRetro[0] == myRetro.front); assert(myRetro.moveAt(2) == 8); - static if (DummyType.r == ReturnBy.Reference) { + static if (DummyType.r == ReturnBy.Reference) + { { myRetro[9]++; scope(exit) myRetro[9]--; @@ -385,16 +460,17 @@ if (isBidirectionalRange!(Unqual!Range)) } } -@safe unittest +pure @safe nothrow @nogc unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto LL = iota(1L, 4L); auto r = retro(LL); - assert(equal(r, [3L, 2L, 1L])); + long[3] excepted = [3, 2, 1]; + assert(equal(r, excepted[])); } // Issue 12662 -@safe @nogc unittest +pure @safe nothrow @nogc unittest { int[3] src = [1,2,3]; int[] data = src[]; @@ -409,6 +485,15 @@ random-access range, moves by indexing into the range; otherwise, moves by successive calls to $(D popFront). Applying stride twice to the same range results in a stride with a step that is the product of the two applications. It is an error for $(D n) to be 0. + +Params: + r = the input range to stride over + n = the number of elements to skip over + +Returns: + At minimum, an input range. The resulting range will adopt the + range primitives of the underlying range as long as + $(REF, hasLength, std,range,primitives) is `true`. */ auto stride(Range)(Range r, size_t n) if (isInputRange!(Unqual!Range)) @@ -418,7 +503,7 @@ in } body { - import std.algorithm : min; + import std.algorithm.comparison : min; static if (is(typeof(stride(r.source, n)) == Range)) { @@ -454,7 +539,7 @@ body slack = 0; } if (!slack) return; - static if (isRandomAccessRange!R && hasSlicing!R) + static if (isRandomAccessRange!R && hasLength!R && hasSlicing!R) { source = source[0 .. source.length - slack]; } @@ -496,13 +581,13 @@ body { ElementType!R moveFront() { - return .moveFront(source); + return source.moveFront(); } } static if (hasAssignableElements!R) { - @property auto front(ElementType!R val) + @property void front(ElementType!R val) { source.front = val; } @@ -510,28 +595,7 @@ body void popFront() { - static if (isRandomAccessRange!R && hasLength!R && hasSlicing!R) - { - source = source[min(_n, source.length) .. source.length]; - } - else - { - static if (hasLength!R) - { - foreach (i; 0 .. min(source.length, _n)) - { - source.popFront(); - } - } - else - { - foreach (i; 0 .. _n) - { - source.popFront(); - if (source.empty) break; - } - } - } + source.popFrontN(_n); } static if (isBidirectionalRange!R && hasLength!R) @@ -552,13 +616,13 @@ body ElementType!R moveBack() { eliminateSlackElements(); - return .moveBack(source); + return source.moveBack(); } } static if (hasAssignableElements!R) { - @property auto back(ElementType!R val) + @property void back(ElementType!R val) { eliminateSlackElements(); source.back = val; @@ -576,11 +640,11 @@ body /** Forwards to $(D moveAt(source, n)). */ - static if (is(typeof(.moveAt(source, 0)))) + static if (is(typeof(source.moveAt(0)))) { ElementType!R moveAt(size_t n) { - return .moveAt(source, _n * n); + return source.moveAt(_n * n); } } @@ -603,7 +667,7 @@ body assert(translatedLower <= translatedUpper); - return typeof(this)(source[translatedLower..translatedUpper], _n); + return typeof(this)(source[translatedLower .. translatedUpper], _n); } static if (hasLength!R) @@ -621,34 +685,45 @@ body } /// -unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][])); assert(stride(stride(a, 2), 3) == stride(a, 6)); } -nothrow @nogc unittest +pure @safe nothrow @nogc unittest { int[4] testArr = [1,2,3,4]; //just checking it compiles auto s = testArr[].stride(2); } -debug unittest +debug pure nothrow @system unittest {//check the contract int[4] testArr = [1,2,3,4]; - import std.exception : assertThrown; + bool passed = false; + scope (success) assert(passed); import core.exception : AssertError; - assertThrown!AssertError(testArr[].stride(0)); + //std.exception.assertThrown won't do because it can't infer nothrow + // @@@BUG@@@ 12647 + try + { + auto unused = testArr[].stride(0); + } + catch (AssertError unused) + { + passed = true; + } } -@safe unittest +pure @safe nothrow unittest { - import std.internal.test.dummyrange; - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType, + ReturnBy; static assert(isRandomAccessRange!(typeof(stride([1, 2, 3], 2)))); void test(size_t n, int[] input, int[] witness) @@ -665,22 +740,22 @@ debug unittest // Test slicing. auto s1 = stride(arr, 1); - assert(equal(s1[1..4], [2, 3, 4])); - assert(s1[1..4].length == 3); - assert(equal(s1[1..5], [2, 3, 4, 5])); - assert(s1[1..5].length == 4); - assert(s1[0..0].empty); - assert(s1[3..3].empty); + assert(equal(s1[1 .. 4], [2, 3, 4])); + assert(s1[1 .. 4].length == 3); + assert(equal(s1[1 .. 5], [2, 3, 4, 5])); + assert(s1[1 .. 5].length == 4); + assert(s1[0 .. 0].empty); + assert(s1[3 .. 3].empty); // assert(s1[$ .. $].empty); assert(s1[s1.opDollar .. s1.opDollar].empty); auto s2 = stride(arr, 2); - assert(equal(s2[0..2], [1,3])); - assert(s2[0..2].length == 2); - assert(equal(s2[1..5], [3, 5, 7, 9])); - assert(s2[1..5].length == 4); - assert(s2[0..0].empty); - assert(s2[3..3].empty); + assert(equal(s2[0 .. 2], [1,3])); + assert(s2[0 .. 2].length == 2); + assert(equal(s2[1 .. 5], [3, 5, 7, 9])); + assert(s2[1 .. 5].length == 4); + assert(s2[0 .. 0].empty); + assert(s2[3 .. 3].empty); // assert(s2[$ .. $].empty); assert(s2[s2.opDollar .. s2.opDollar].empty); @@ -696,7 +771,8 @@ debug unittest // Check for infiniteness propagation. static assert(isInfinite!(typeof(stride(repeat(1), 3)))); - foreach(DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { DummyType dummyRange; dummyRange.reinit(); @@ -704,23 +780,27 @@ debug unittest // Should fail if no length and bidirectional b/c there's no way // to know how much slack we have. - static if (hasLength!DummyType || !isBidirectionalRange!DummyType) { + static if (hasLength!DummyType || !isBidirectionalRange!DummyType) + { static assert(propagatesRangeType!(typeof(myStride), DummyType)); } assert(myStride.front == 1); assert(myStride.moveFront() == 1); assert(equal(myStride, [1, 5, 9])); - static if (hasLength!DummyType) { + static if (hasLength!DummyType) + { assert(myStride.length == 3); } - static if (isBidirectionalRange!DummyType && hasLength!DummyType) { + static if (isBidirectionalRange!DummyType && hasLength!DummyType) + { assert(myStride.back == 9); assert(myStride.moveBack() == 9); } - static if (isRandomAccessRange!DummyType && hasLength!DummyType) { + static if (isRandomAccessRange!DummyType && hasLength!DummyType) + { assert(myStride[0] == 1); assert(myStride[1] == 5); assert(myStride.moveAt(1) == 5); @@ -729,7 +809,8 @@ debug unittest static assert(hasSlicing!(typeof(myStride))); } - static if (DummyType.r == ReturnBy.Reference) { + static if (DummyType.r == ReturnBy.Reference) + { // Make sure reference is propagated. { @@ -743,7 +824,8 @@ debug unittest assert(dummyRange.front == 4); } - static if (isBidirectionalRange!DummyType && hasLength!DummyType) { + static if (isBidirectionalRange!DummyType && hasLength!DummyType) + { { myStride.back++; scope(exit) myStride.back--; @@ -755,7 +837,8 @@ debug unittest assert(myStride.back == 111); } - static if (isRandomAccessRange!DummyType) { + static if (isRandomAccessRange!DummyType) + { { myStride[1]++; scope(exit) myStride[1]--; @@ -772,9 +855,9 @@ debug unittest } } -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto LL = iota(1L, 10L); auto s = stride(LL, 3); @@ -792,6 +875,16 @@ length), $(D Chain) offers them as well. If only one range is offered to $(D Chain) or $(D chain), the $(D Chain) type exits the picture by aliasing itself directly to that range's type. + +Params: + rs = the input ranges to chain together + +Returns: + An input range at minimum. If all of the ranges in `rs` provide + a range primitive, the returned range will also provide that range + primitive. + +See_Also: $(LREF only) to chain values to a range */ auto chain(Ranges...)(Ranges rs) if (Ranges.length > 0 && @@ -816,10 +909,10 @@ if (Ranges.length > 0 && enum bool allSameType = allSatisfy!(sameET, R); -// This doesn't work yet + // This doesn't work yet static if (allSameType) { - alias ref RvalueElementType ElementType; + alias ElementType = ref RvalueElementType; } else { @@ -840,9 +933,9 @@ if (Ranges.length > 0 && } } -// This is the entire state + // This is the entire state R source; -// TODO: use a vtable (or more) instead of linear iteration + // TODO: use a vtable (or more) instead of linear iteration public: this(R input) @@ -853,11 +946,11 @@ if (Ranges.length > 0 && } } - import std.typetuple : anySatisfy; + import std.meta : anySatisfy; static if (anySatisfy!(isInfinite, R)) { -// Propagate infiniteness. + // Propagate infiniteness. enum bool empty = false; } else @@ -908,8 +1001,7 @@ if (Ranges.length > 0 && // @@@BUG@@@ //@property void front(T)(T v) if (is(T : RvalueElementType)) - // Return type must be auto due to Bug 4706. - @property auto front(RvalueElementType v) + @property void front(RvalueElementType v) { foreach (i, Unused; R) { @@ -928,7 +1020,7 @@ if (Ranges.length > 0 && foreach (i, Unused; R) { if (source[i].empty) continue; - return .moveFront(source[i]); + return source[i].moveFront(); } assert(false); } @@ -963,7 +1055,7 @@ if (Ranges.length > 0 && foreach_reverse (i, Unused; R) { if (source[i].empty) continue; - return .moveBack(source[i]); + return source[i].moveBack(); } assert(false); } @@ -971,9 +1063,7 @@ if (Ranges.length > 0 && static if (allSameType && allSatisfy!(hasAssignableElements, R)) { - // Return type must be auto due to extremely strange bug in DMD's - // function overloading. - @property auto back(RvalueElementType v) + @property void back(RvalueElementType v) { foreach_reverse (i, Unused; R) { @@ -1029,12 +1119,12 @@ if (Ranges.length > 0 && { static if (isInfinite!(Range)) { - return .moveAt(source[i], index); + return source[i].moveAt(index); } else { immutable length = source[i].length; - if (index < length) return .moveAt(source[i], index); + if (index < length) return source[i].moveAt(index); index -= length; } } @@ -1112,9 +1202,9 @@ if (Ranges.length > 0 && } /// -unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] arr1 = [ 1, 2, 3, 4 ]; int[] arr2 = [ 5, 6 ]; @@ -1125,10 +1215,33 @@ unittest assert(equal(s, [1, 2, 3, 4, 5, 6, 7][])); } -@safe unittest +/** + * Range primitives are carried over to the returned range if + * all of the ranges provide them + */ +pure @safe nothrow unittest { - import std.internal.test.dummyrange; - import std.algorithm : equal; + import std.algorithm.sorting : sort; + import std.algorithm.comparison : equal; + + int[] arr1 = [5, 2, 8]; + int[] arr2 = [3, 7, 9]; + int[] arr3 = [1, 4, 6]; + + // in-place sorting across all of the arrays + auto s = arr1.chain(arr2, arr3).sort; + + assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); + assert(arr1.equal([1, 2, 3])); + assert(arr2.equal([4, 5, 6])); + assert(arr3.equal([7, 8, 9])); +} + +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges, dummyLength, + propagatesRangeType; { int[] arr1 = [ 1, 2, 3, 4 ]; @@ -1182,9 +1295,11 @@ unittest // Check that chain at least instantiates and compiles with every possible // pair of DummyRange types, in either order. - foreach(DummyType1; AllDummyRanges) { + foreach (DummyType1; AllDummyRanges) + { DummyType1 dummy1; - foreach(DummyType2; AllDummyRanges) { + foreach (DummyType2; AllDummyRanges) + { DummyType2 dummy2; auto myChain = chain(dummy1, dummy2); @@ -1193,7 +1308,8 @@ unittest ); assert(myChain.front == 1); - foreach(i; 0..dummyLength) { + foreach (i; 0 .. dummyLength) + { myChain.popFront(); } assert(myChain.front == 1); @@ -1220,7 +1336,7 @@ unittest } } -@safe unittest +pure @safe nothrow @nogc unittest { class Foo{} immutable(Foo)[] a; @@ -1229,136 +1345,492 @@ unittest } /** -$(D roundRobin(r1, r2, r3)) yields $(D r1.front), then $(D r2.front), -then $(D r3.front), after which it pops off one element from each and -continues again from $(D r1). For example, if two ranges are involved, -it alternately yields elements off the two ranges. $(D roundRobin) -stops after it has consumed all ranges (skipping over the ones that -finish early). +Choose one of two ranges at runtime depending on a Boolean condition. + +The ranges may be different, but they must have compatible element types (i.e. +$(D CommonType) must exist for the two element types). The result is a range +that offers the weakest capabilities of the two (e.g. $(D ForwardRange) if $(D +R1) is a random-access range and $(D R2) is a forward range). + +Params: + condition = which range to choose: $(D r1) if $(D true), $(D r2) otherwise + r1 = the "true" range + r2 = the "false" range + +Returns: + A range type dependent on $(D R1) and $(D R2). + +Bugs: + $(BUGZILLA 14660) */ -auto roundRobin(Rs...)(Rs rs) -if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) +auto choose(R1, R2)(bool condition, R1 r1, R2 r2) +if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && + !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) { - struct Result + static struct Result { - import std.conv : to; + import std.algorithm.comparison : max; + import std.algorithm.internal : addressOf; + import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor; - public Rs source; - private size_t _current = size_t.max; + private union + { + void[max(R1.sizeof, R2.sizeof)] buffer = void; + void* forAlignmentOnly = void; + } + private bool condition; + private @property ref R1 r1() + { + assert(condition); + return *cast(R1*) buffer.ptr; + } + private @property ref R2 r2() + { + assert(!condition); + return *cast(R2*) buffer.ptr; + } - @property bool empty() + this(bool condition, R1 r1, R2 r2) { - foreach (i, Unused; Rs) + this.condition = condition; + import std.conv : emplace; + if (condition) emplace(addressOf(this.r1), r1); + else emplace(addressOf(this.r2), r2); + } + + // Carefully defined postblit to postblit the appropriate range + static if (hasElaborateCopyConstructor!R1 + || hasElaborateCopyConstructor!R2) + this(this) + { + if (condition) { - if (!source[i].empty) return false; + static if (hasElaborateCopyConstructor!R1) r1.__postblit(); + } + else + { + static if (hasElaborateCopyConstructor!R2) r2.__postblit(); } - return true; } - @property auto ref front() + static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2) + ~this() { - final switch (_current) + if (condition) destroy(r1); + else destroy(r2); + } + + static if (isInfinite!R1 && isInfinite!R2) + // Propagate infiniteness. + enum bool empty = false; + else + @property bool empty() { - foreach (i, R; Rs) - { - case i: - assert(!source[i].empty); - return source[i].front; - } + return condition ? r1.empty : r2.empty; } - assert(0); + + @property auto ref front() + { + return condition ? r1.front : r2.front; } void popFront() { - final switch (_current) + return condition ? r1.popFront : r2.popFront; + } + + static if (isForwardRange!R1 && isForwardRange!R2) + @property auto save() { - foreach (i, R; Rs) - { - case i: - source[i].popFront(); - break; - } + auto result = this; + if (condition) r1 = r1.save; + else r2 = r2.save; + return result; } - auto next = _current == (Rs.length - 1) ? 0 : (_current + 1); - final switch (next) + @property void front(T)(T v) + if (is(typeof({ r1.front = v; r2.front = v; }))) + { + if (condition) r1.front = v; else r2.front = v; + } + + static if (hasMobileElements!R1 && hasMobileElements!R2) + auto moveFront() { - foreach (i, R; Rs) - { - case i: - if (!source[i].empty) - { - _current = i; - return; - } - if (i == _current) - { - _current = _current.max; - return; - } - goto case (i + 1) % Rs.length; - } + return condition ? r1.moveFront : r2.moveFront; } - } - static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) - @property auto save() + static if (isBidirectionalRange!R1 && isBidirectionalRange!R2) + { + @property auto ref back() { - Result result = this; - foreach (i, Unused; Rs) + return condition ? r1.back : r2.back; + } + + void popBack() + { + return condition ? r1.popBack : r2.popBack; + } + + static if (hasMobileElements!R1 && hasMobileElements!R2) + auto moveBack() { - result.source[i] = result.source[i].save; + return condition ? r1.moveBack : r2.moveBack; } - return result; + + @property void back(T)(T v) + if (is(typeof({ r1.back = v; r2.back = v; }))) + { + if (condition) r1.back = v; else r2.back = v; } + } - static if (allSatisfy!(hasLength, Rs)) + static if (hasLength!R1 && hasLength!R2) { @property size_t length() { - size_t result; - foreach (i, R; Rs) + return condition ? r1.length : r2.length; + } + alias opDollar = length; + } + + static if (isRandomAccessRange!R1 && isRandomAccessRange!R2) + { + auto ref opIndex(size_t index) + { + return condition ? r1[index] : r2[index]; + } + + static if (hasMobileElements!R1 && hasMobileElements!R2) + auto moveAt(size_t index) { - result += source[i].length; + return condition ? r1.moveAt(index) : r2.moveAt(index); } - return result; - } - alias opDollar = length; + void opIndexAssign(T)(T v, size_t index) + if (is(typeof({ r1[1] = v; r2[1] = v; }))) + { + if (condition) r1[index] = v; else r2[index] = v; + } } - } - return Result(rs, 0); + // BUG: this should work for infinite ranges, too + static if (hasSlicing!R1 && hasSlicing!R2 && + !isInfinite!R2 && !isInfinite!R2) + auto opSlice(size_t begin, size_t end) + { + auto result = this; + if (condition) result.r1 = result.r1[begin .. end]; + else result.r2 = result.r2[begin .. end]; + return result; + } + } + return Result(condition, r1, r2); } /// -@safe unittest +@system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter, map; - int[] a = [ 1, 2, 3 ]; - int[] b = [ 10, 20, 30, 40 ]; - auto r = roundRobin(a, b); - assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ])); + auto data1 = [ 1, 2, 3, 4 ].filter!(a => a != 3); + auto data2 = [ 5, 6, 7, 8 ].map!(a => a + 1); + + // choose() is primarily useful when you need to select one of two ranges + // with different types at runtime. + static assert(!is(typeof(data1) == typeof(data2))); + + auto chooseRange(bool pickFirst) + { + // The returned range is a common wrapper type that can be used for + // returning or storing either range without running into a type error. + return choose(pickFirst, data1, data2); + + // Simply returning the chosen range without using choose() does not + // work, because map() and filter() return different types. + //return pickFirst ? data1 : data2; // does not compile + } + + auto result = chooseRange(true); + assert(result.equal([ 1, 2, 4 ])); + + result = chooseRange(false); + assert(result.equal([ 6, 7, 8, 9 ])); } /** -Iterates a random-access range starting from a given point and -progressively extending left and right from that point. If no initial -point is given, iteration starts from the middle of the -range. Iteration spans the entire range. - */ -auto radial(Range, I)(Range r, I startingIndex) -if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && isIntegral!I) -{ - if (!r.empty) ++startingIndex; +Choose one of multiple ranges at runtime. + +The ranges may be different, but they must have compatible element types. The +result is a range that offers the weakest capabilities of all $(D Ranges). + +Params: + index = which range to choose, must be less than the number of ranges + rs = two or more ranges + +Returns: + The indexed range. If rs consists of only one range, the return type is an + alias of that range's type. + */ +auto chooseAmong(Ranges...)(size_t index, Ranges rs) +if (Ranges.length > 2 + && is(typeof(choose(true, rs[0], rs[1]))) + && is(typeof(chooseAmong(0, rs[1 .. $])))) +{ + return choose(index == 0, rs[0], chooseAmong(index - 1, rs[1 .. $])); +} + +/// ditto +auto chooseAmong(Ranges...)(size_t index, Ranges rs) +if (Ranges.length == 2 && is(typeof(choose(true, rs[0], rs[1])))) +{ + return choose(index == 0, rs[0], rs[1]); +} + +/// +@system unittest +{ + import std.algorithm.comparison : equal; + + int[] arr1 = [ 1, 2, 3, 4 ]; + int[] arr2 = [ 5, 6 ]; + int[] arr3 = [ 7 ]; + + { + auto s = chooseAmong(0, arr1, arr2, arr3); + auto t = s.save; + assert(s.length == 4); + assert(s[2] == 3); + s.popFront(); + assert(equal(t, [1, 2, 3, 4][])); + } + { + auto s = chooseAmong(1, arr1, arr2, arr3); + assert(s.length == 2); + s.front = 8; + assert(equal(s, [8, 6][])); + } + { + auto s = chooseAmong(1, arr1, arr2, arr3); + assert(s.length == 2); + s[1] = 9; + assert(equal(s, [8, 9][])); + } + { + auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3]; + assert(s.length == 2); + assert(equal(s, [2, 3][])); + } + { + auto s = chooseAmong(0, arr1, arr2, arr3); + assert(s.length == 4); + assert(s.back == 4); + s.popBack(); + s.back = 5; + assert(equal(s, [1, 2, 5][])); + s.back = 3; + assert(equal(s, [1, 2, 3][])); + } + { + uint[] foo = [1,2,3,4,5]; + uint[] bar = [6,7,8,9,10]; + auto c = chooseAmong(1,foo, bar); + assert(c[3] == 9); + c[3] = 42; + assert(c[3] == 42); + assert(c.moveFront() == 6); + assert(c.moveBack() == 10); + assert(c.moveAt(4) == 10); + } + { + import std.range : cycle; + auto s = chooseAmong(1, cycle(arr2), cycle(arr3)); + assert(isInfinite!(typeof(s))); + assert(!s.empty); + assert(s[100] == 7); + } +} + +@system unittest +{ + int[] a = [1, 2, 3]; + long[] b = [4, 5, 6]; + auto c = chooseAmong(0, a, b); + c[0] = 42; + assert(c[0] == 42); +} + + +/** +$(D roundRobin(r1, r2, r3)) yields $(D r1.front), then $(D r2.front), +then $(D r3.front), after which it pops off one element from each and +continues again from $(D r1). For example, if two ranges are involved, +it alternately yields elements off the two ranges. $(D roundRobin) +stops after it has consumed all ranges (skipping over the ones that +finish early). + */ +auto roundRobin(Rs...)(Rs rs) +if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) +{ + struct Result + { + import std.conv : to; + + public Rs source; + private size_t _current = size_t.max; + + @property bool empty() + { + foreach (i, Unused; Rs) + { + if (!source[i].empty) return false; + } + return true; + } + + @property auto ref front() + { + final switch (_current) + { + foreach (i, R; Rs) + { + case i: + assert( + !source[i].empty, + "Attempting to fetch the front of an empty roundRobin" + ); + return source[i].front; + } + } + assert(0); + } + + void popFront() + { + final switch (_current) + { + foreach (i, R; Rs) + { + case i: + source[i].popFront(); + break; + } + } + + auto next = _current == (Rs.length - 1) ? 0 : (_current + 1); + final switch (next) + { + foreach (i, R; Rs) + { + case i: + if (!source[i].empty) + { + _current = i; + return; + } + if (i == _current) + { + _current = _current.max; + return; + } + goto case (i + 1) % Rs.length; + } + } + } + + static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) + @property auto save() + { + Result result = this; + foreach (i, Unused; Rs) + { + result.source[i] = result.source[i].save; + } + return result; + } + + static if (allSatisfy!(hasLength, Rs)) + { + @property size_t length() + { + size_t result; + foreach (i, R; Rs) + { + result += source[i].length; + } + return result; + } + + alias opDollar = length; + } + } + + return Result(rs, 0); +} + +/// +@safe unittest +{ + import std.algorithm.comparison : equal; + + int[] a = [ 1, 2, 3 ]; + int[] b = [ 10, 20, 30, 40 ]; + auto r = roundRobin(a, b); + assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ])); +} + +/** + * roundRobin can be used to create "interleave" functionality which inserts + * an element between each element in a range. + */ +@safe unittest +{ + import std.algorithm.comparison : equal; + + auto interleave(R, E)(R range, E element) + if ((isInputRange!R && hasLength!R) || isForwardRange!R) + { + static if (hasLength!R) + immutable len = range.length; + else + immutable len = range.save.walkLength; + + return roundRobin( + range, + element.repeat(len - 1) + ); + } + + assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3])); +} + +/** +Iterates a random-access range starting from a given point and +progressively extending left and right from that point. If no initial +point is given, iteration starts from the middle of the +range. Iteration spans the entire range. + +When `startingIndex` is 0 the range will be fully iterated in order +and in reverse order when `r.length` is given. + +Params: + r = a random access range with length and slicing + startingIndex = the index to begin iteration from + +Returns: + A forward range with length + */ +auto radial(Range, I)(Range r, I startingIndex) +if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I) +{ + if (startingIndex != r.length) ++startingIndex; return roundRobin(retro(r[0 .. startingIndex]), r[startingIndex .. r.length]); } /// Ditto auto radial(R)(R r) -if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R)) +if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R)) { return .radial(r, (r.length - !r.empty) / 2); } @@ -1366,19 +1838,28 @@ if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R)) /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] a = [ 1, 2, 3, 4, 5 ]; assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); a = [ 1, 2, 3, 4 ]; assert(equal(radial(a), [ 2, 3, 1, 4 ])); + + // If the left end is reached first, the remaining elements on the right + // are concatenated in order: + a = [ 0, 1, 2, 3, 4, 5 ]; + assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ])); + + // If the right end is reached first, the remaining elements on the left + // are concatenated in reverse order: + assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ])); } @safe unittest { + import std.algorithm.comparison : equal; import std.conv : text; import std.exception : enforce; - import std.algorithm : equal; - import std.internal.test.dummyrange; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; void test(int[] input, int[] witness) { @@ -1394,11 +1875,13 @@ if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R)) test([ 1, 2, 3, 4, 5, 6 ], [ 3, 4, 2, 5, 1, 6 ]); int[] a = [ 1, 2, 3, 4, 5 ]; - assert(equal(radial(a, 1), [ 2, 3, 1, 4, 5 ][])); + assert(equal(radial(a, 1), [ 2, 3, 1, 4, 5 ])); + assert(equal(radial(a, 0), [ 1, 2, 3, 4, 5 ])); // only right subrange + assert(equal(radial(a, a.length), [ 5, 4, 3, 2, 1 ])); // only left subrange static assert(isForwardRange!(typeof(radial(a, 1)))); auto r = radial([1,2,3,4,5]); - for(auto rr = r.save; !rr.empty; rr.popFront()) + for (auto rr = r.save; !rr.empty; rr.popFront()) { assert(rr.front == moveFront(rr)); } @@ -1415,7 +1898,7 @@ if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R)) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto LL = iota(1L, 6L); auto r = radial(LL); @@ -1423,10 +1906,31 @@ if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R)) } /** -Lazily takes only up to $(D n) elements of a range. This is -particularly useful when using with infinite ranges. If the range -offers random access and $(D length), $(D Take) offers them as well. +Lazily takes only up to `n` elements of a range. This is +particularly useful when using with infinite ranges. + +Unlike $(LREF takeExactly), `take` does not require that there +are `n` or more elements in `input`. As a consequence, length +information is not applied to the result unless `input` also has +length information. + +Params: + input = an input range to iterate over up to `n` times + n = the number of elements to take + +Returns: + At minimum, an input range. If the range offers random access + and `length`, `take` offers them as well. */ +Take!R take(R)(R input, size_t n) +if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) && + !is(R T == Take!T)) +{ + import std.algorithm.comparison : min; + return input[0 .. min(n, input.length)]; +} + +/// ditto struct Take(Range) if (isInputRange!(Unqual!Range) && //take _cannot_ test hasSlicing on infinite ranges, because hasSlicing uses @@ -1435,18 +1939,20 @@ if (isInputRange!(Unqual!Range) && { private alias R = Unqual!Range; - // User accessible in read and write + /// User accessible in read and write public R source; private size_t _maxAvailable; alias Source = R; + /// Range primitives @property bool empty() { return _maxAvailable == 0 || source.empty; } + /// ditto @property auto ref front() { assert(!empty, @@ -1455,6 +1961,7 @@ if (isInputRange!(Unqual!Range) && return source.front; } + /// ditto void popFront() { assert(!empty, @@ -1465,13 +1972,15 @@ if (isInputRange!(Unqual!Range) && } static if (isForwardRange!R) + /// ditto @property Take save() { return Take(source.save, _maxAvailable); } static if (hasAssignableElements!R) - @property auto front(ElementType!R v) + /// ditto + @property void front(ElementType!R v) { assert(!empty, "Attempting to assign to the front of an empty " @@ -1482,40 +1991,45 @@ if (isInputRange!(Unqual!Range) && static if (hasMobileElements!R) { + /// ditto auto moveFront() { assert(!empty, "Attempting to move the front of an empty " ~ Take.stringof); - return .moveFront(source); + return source.moveFront(); } } static if (isInfinite!R) { + /// ditto @property size_t length() const { return _maxAvailable; } + /// ditto alias opDollar = length; //Note: Due to Take/hasSlicing circular dependency, //This needs to be a restrained template. + /// ditto auto opSlice()(size_t i, size_t j) if (hasSlicing!R) { assert(i <= j, "Invalid slice bounds"); - assert(j - i <= length, "Attempting to slice past the end of a " + assert(j <= length, "Attempting to slice past the end of a " ~ Take.stringof); - return source[i .. j - i]; + return source[i .. j]; } } else static if (hasLength!R) { + /// ditto @property size_t length() { - import std.algorithm : min; + import std.algorithm.comparison : min; return min(_maxAvailable, source.length); } @@ -1524,6 +2038,7 @@ if (isInputRange!(Unqual!Range) && static if (isRandomAccessRange!R) { + /// ditto void popBack() { assert(!empty, @@ -1532,6 +2047,7 @@ if (isInputRange!(Unqual!Range) && --_maxAvailable; } + /// ditto @property auto ref back() { assert(!empty, @@ -1540,6 +2056,7 @@ if (isInputRange!(Unqual!Range) && return source[this.length - 1]; } + /// ditto auto ref opIndex(size_t index) { assert(index < length, @@ -1550,7 +2067,8 @@ if (isInputRange!(Unqual!Range) && static if (hasAssignableElements!R) { - @property auto back(ElementType!R v) + /// ditto + @property void back(ElementType!R v) { // This has to return auto instead of void because of Bug 4706. assert(!empty, @@ -1559,6 +2077,7 @@ if (isInputRange!(Unqual!Range) && source[this.length - 1] = v; } + /// ditto void opIndexAssign(ElementType!R v, size_t index) { assert(index < length, @@ -1570,33 +2089,41 @@ if (isInputRange!(Unqual!Range) && static if (hasMobileElements!R) { + /// ditto auto moveBack() { assert(!empty, "Attempting to move the back of an empty " ~ Take.stringof); - return .moveAt(source, this.length - 1); + return source.moveAt(this.length - 1); } + /// ditto auto moveAt(size_t index) { assert(index < length, "Attempting to index out of the bounds of a " ~ Take.stringof); - return .moveAt(source, index); + return source.moveAt(index); } } } - // Nonstandard + /** + Access to maximal length of the range. + Note: the actual length of the range depends on the underlying range. + If it has fewer elements, it will stop before maxLength is reached. + */ @property size_t maxLength() const { return _maxAvailable; } } -// This template simply aliases itself to R and is useful for consistency in -// generic code. +/** +This template simply aliases itself to R and is useful for consistency in +generic code. +*/ template Take(R) if (isInputRange!(Unqual!R) && ((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T))) @@ -1604,22 +2131,10 @@ if (isInputRange!(Unqual!R) && alias Take = R; } -// take for finite ranges with slicing -/// ditto -Take!R take(R)(R input, size_t n) -if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) && - !is(R T == Take!T)) -{ - // @@@BUG@@@ - //return input[0 .. min(n, $)]; - import std.algorithm : min; - return input[0 .. min(n, input.length)]; -} - /// -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; auto s = take(arr1, 5); @@ -1628,25 +2143,40 @@ if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) & assert(equal(s, [ 1, 2, 3, 4, 5 ][])); } -// take(take(r, n1), n2) +/** + * If the range runs out before `n` elements, `take` simply returns the entire + * range (unlike $(LREF takeExactly), which will cause an assertion failure if + * the range ends prematurely): + */ +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + + int[] arr2 = [ 1, 2, 3 ]; + auto t = take(arr2, 5); + assert(t.length == 3); + assert(equal(t, [ 1, 2, 3 ])); +} + +/// ditto Take!R take(R)(R input, size_t n) if (is(R T == Take!T)) { - import std.algorithm : min; + import std.algorithm.comparison : min; return R(input.source, min(n, input._maxAvailable)); } -// Regular take for input ranges +/// ditto Take!(R) take(R)(R input, size_t n) if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) && !is(R T == Take!T))) { return Take!R(input, n); } -@safe unittest +pure @safe nothrow unittest { - import std.internal.test.dummyrange; - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges; int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; auto s = take(arr1, 5); @@ -1660,33 +2190,38 @@ if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) static assert(is(typeof(s) == int[])); // Test using narrow strings. + import std.exception : assumeWontThrow; + auto myStr = "This is a string."; auto takeMyStr = take(myStr, 7); - assert(equal(takeMyStr, "This is")); - + assert(assumeWontThrow(equal(takeMyStr, "This is"))); // Test fix for bug 5052. auto takeMyStrAgain = take(takeMyStr, 4); - assert(equal(takeMyStrAgain, "This")); - static assert (is (typeof(takeMyStrAgain) == typeof(takeMyStr))); + assert(assumeWontThrow(equal(takeMyStrAgain, "This"))); + static assert(is (typeof(takeMyStrAgain) == typeof(takeMyStr))); takeMyStrAgain = take(takeMyStr, 10); - assert(equal(takeMyStrAgain, "This is")); + assert(assumeWontThrow(equal(takeMyStrAgain, "This is"))); - foreach(DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { DummyType dummy; auto t = take(dummy, 5); alias T = typeof(t); - static if (isRandomAccessRange!DummyType) { + static if (isRandomAccessRange!DummyType) + { static assert(isRandomAccessRange!T); assert(t[4] == 5); assert(moveAt(t, 1) == t[1]); assert(t.back == moveBack(t)); - } else static if (isForwardRange!DummyType) { + } + else static if (isForwardRange!DummyType) + { static assert(isForwardRange!T); } - for(auto tt = t; !tt.empty; tt.popFront()) + for (auto tt = t; !tt.empty; tt.popFront()) { assert(tt.front == moveFront(tt)); } @@ -1704,7 +2239,17 @@ if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) static assert(is(Take!(typeof(myRepeat)))); } -@safe unittest +pure @safe nothrow @nogc unittest +{ + //check for correct slicing of Take on an infinite range + import std.algorithm.comparison : equal; + foreach (start; 0 .. 4) + foreach (stop; start .. 4) + assert(iota(4).cycle.take(4)[start .. stop] + .equal(iota(start, stop))); +} + +pure @safe nothrow @nogc unittest { // Check that one can declare variables of all Take types, // and that they match the return type of the corresponding @@ -1721,7 +2266,7 @@ if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) t3 = take(t2, 1); } -@safe unittest +pure @safe nothrow @nogc unittest { alias R1 = typeof(repeat(1)); alias R2 = typeof(cycle([1])); @@ -1731,16 +2276,16 @@ if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R) static assert(isBidirectionalRange!TR2); } -@safe unittest //12731 +pure @safe nothrow @nogc unittest //12731 { auto a = repeat(1); auto s = a[1 .. 5]; s = s[1 .. 3]; } -@safe unittest //13151 +pure @safe nothrow @nogc unittest //13151 { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto r = take(repeat(1, 4), 3); assert(r.take(2).equal(repeat(1, 2))); @@ -1755,6 +2300,9 @@ even when $(D range) itself does not define $(D length). The result of $(D takeExactly) is identical to that of $(LREF take) in cases where the original range defines $(D length) or is infinite. + +Unlike $(LREF take), however, it is illegal to pass a range with less than +$(D n) elements to $(D takeExactly); this will cause an assertion failure. */ auto takeExactly(R)(R range, size_t n) if (isInputRange!R) @@ -1814,7 +2362,7 @@ if (isInputRange!R) assert(!empty, "Attempting to move the front of an empty " ~ typeof(this).stringof); - return .moveFront(_input); + return _input.moveFront(); } } @@ -1835,9 +2383,9 @@ if (isInputRange!R) } /// -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = [ 1, 2, 3, 4, 5 ]; @@ -1849,9 +2397,10 @@ if (isInputRange!R) assert(b.back == 3); } -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal, filter; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; auto a = [ 1, 2, 3, 4, 5 ]; auto b = takeExactly(a, 3); @@ -1867,10 +2416,10 @@ if (isInputRange!R) assert(equal(takeExactly(e, 3), [1, 2, 3])); } -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; - import std.internal.test.dummyrange; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges; auto a = [ 1, 2, 3, 4, 5 ]; //Test that take and takeExactly are the same for ranges which define length @@ -1899,7 +2448,7 @@ if (isInputRange!R) //define length. static assert(!is(typeof(take(filter!"true"(a), 3)) == typeof(takeExactly(filter!"true"(a), 3)))); - foreach(DummyType; AllDummyRanges) + foreach (DummyType; AllDummyRanges) { { DummyType dummy; @@ -1909,7 +2458,7 @@ if (isInputRange!R) assert(takeExactly(t, 4) == takeExactly(dummy, 4)); } - static if(hasMobileElements!DummyType) + static if (hasMobileElements!DummyType) { { auto t = takeExactly(DummyType.init, 4); @@ -1918,7 +2467,7 @@ if (isInputRange!R) } } - static if(hasAssignableElements!DummyType) + static if (hasAssignableElements!DummyType) { { auto t = takeExactly(DummyType.init, 4); @@ -1929,10 +2478,10 @@ if (isInputRange!R) } } -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; - import std.internal.test.dummyrange; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; alias DummyType = DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward); auto te = takeExactly(DummyType(), 5); @@ -1951,10 +2500,13 @@ certain interfaces it is important to know statically that the range may only have at most one element. The type returned by $(D takeOne) is a random-access range with length -regardless of $(D R)'s capabilities (another feature that distinguishes -$(D takeOne) from $(D take)). +regardless of $(D R)'s capabilities, as long as it is a forward range. +(another feature that distinguishes $(D takeOne) from $(D take)). If +(D R) is an input range but not a forward range, return type is an input +range with all random-access capabilities except save. */ -auto takeOne(R)(R source) if (isInputRange!R) +auto takeOne(R)(R source) +if (isInputRange!R) { static if (hasSlicing!R) { @@ -1967,20 +2519,42 @@ auto takeOne(R)(R source) if (isInputRange!R) private R _source; private bool _empty = true; @property bool empty() const { return _empty; } - @property auto ref front() { assert(!empty); return _source.front; } - void popFront() { assert(!empty); _empty = true; } - void popBack() { assert(!empty); _empty = true; } + @property auto ref front() + { + assert(!empty, "Attempting to fetch the front of an empty takeOne"); + return _source.front; + } + void popFront() + { + assert(!empty, "Attempting to popFront an empty takeOne"); + _source.popFront(); + _empty = true; + } + void popBack() + { + assert(!empty, "Attempting to popBack an empty takeOne"); + _source.popFront(); + _empty = true; + } static if (isForwardRange!(Unqual!R)) { @property auto save() { return Result(_source.save, empty); } } - @property auto ref back() { assert(!empty); return _source.front; } + @property auto ref back() + { + assert(!empty, "Attempting to fetch the back of an empty takeOne"); + return _source.front; + } @property size_t length() const { return !empty; } alias opDollar = length; - auto ref opIndex(size_t n) { assert(n < length); return _source.front; } + auto ref opIndex(size_t n) + { + assert(n < length, "Attempting to index a takeOne out of bounds"); + return _source.front; + } auto opSlice(size_t m, size_t n) { - assert(m <= n && n < length); + assert(m <= n && n < length, "Attempting to index a takeOne out of bounds"); return n > m ? this : Result(_source, false); } // Non-standard property @@ -1992,7 +2566,7 @@ auto takeOne(R)(R source) if (isInputRange!R) } /// -@safe unittest +pure @safe nothrow unittest { auto s = takeOne([42, 43, 44]); static assert(isRandomAccessRange!(typeof(s))); @@ -2008,7 +2582,7 @@ auto takeOne(R)(R source) if (isInputRange!R) assert(s.empty); } -unittest +pure @safe nothrow @nogc unittest { struct NonForwardRange { @@ -2023,26 +2597,45 @@ unittest assert(s.front == 42); } +//guards against issue 16999 +pure @safe unittest +{ + auto myIota = new class + { + int front = 0; + @safe void popFront(){front++;} + enum empty = false; + }; + auto iotaPart = myIota.takeOne; + int sum; + foreach (var; chain(iotaPart, iotaPart, iotaPart)) + { + sum += var; + } + assert(sum == 3); + assert(iotaPart.front == 3); +} + /++ Returns an empty range which is statically known to be empty and is guaranteed to have $(D length) and be random access regardless of $(D R)'s capabilities. +/ auto takeNone(R)() - if(isInputRange!R) +if (isInputRange!R) { return typeof(takeOne(R.init)).init; } /// -@safe unittest +pure @safe nothrow @nogc unittest { auto range = takeNone!(int[])(); assert(range.length == 0); assert(range.empty); } -@safe unittest +pure @safe nothrow @nogc unittest { enum ctfe = takeNone!(int[])(); static assert(ctfe.length == 0); @@ -2056,20 +2649,21 @@ auto takeNone(R)() $(D takeExactly(range, 0)). +/ auto takeNone(R)(R range) - if(isInputRange!R) +if (isInputRange!R) { + import std.traits : isDynamicArray; //Makes it so that calls to takeNone which don't use UFCS still work with a //member version if it's defined. - static if(is(typeof(R.takeNone))) + static if (is(typeof(R.takeNone))) auto retval = range.takeNone(); //@@@BUG@@@ 8339 - else static if(isDynamicArray!R)/+ || + else static if (isDynamicArray!R)/+ || (is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/ { auto retval = R.init; } //An infinite range sliced at [0 .. 0] would likely still not be empty... - else static if(hasSlicing!R && !isInfinite!R) + else static if (hasSlicing!R && !isInfinite!R) auto retval = range[0 .. 0]; else auto retval = takeExactly(range, 0); @@ -2080,9 +2674,9 @@ auto takeNone(R)(R range) } /// -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : filter; + import std.algorithm.iteration : filter; assert(takeNone([42, 27, 19]).empty); assert(takeNone("dlang.org").empty); assert(takeNone(filter!"true"([42, 27, 19])).empty); @@ -2090,12 +2684,14 @@ auto takeNone(R)(R range) @safe unittest { - import std.algorithm : filter; + import std.algorithm.iteration : filter; + import std.meta : AliasSeq; struct Dummy { mixin template genInput() { + @safe: @property bool empty() { return _arr.empty; } @property auto front() { return _arr.front; } void popFront() { _arr.popFront(); } @@ -2148,6 +2744,7 @@ auto takeNone(R)(R range) static class SliceClass { + @safe: this(int[] arr) { _arr = arr; } mixin genInput; @property auto save() { return new typeof(this)(_arr); } @@ -2158,6 +2755,7 @@ auto takeNone(R)(R range) static class TakeNoneClass { + @safe: this(int[] arr) { _arr = arr; } mixin genInput; auto takeNone() { return new typeof(this)(null); } @@ -2166,22 +2764,22 @@ auto takeNone(R)(R range) import std.format : format; - foreach(range; TypeTuple!([1, 2, 3, 4, 5], - "hello world", - "hello world"w, - "hello world"d, - SliceStruct([1, 2, 3]), - //@@@BUG@@@ 8339 forces this to be takeExactly - //`InitStruct([1, 2, 3]), - TakeNoneStruct([1, 2, 3]))) + foreach (range; AliasSeq!([1, 2, 3, 4, 5], + "hello world", + "hello world"w, + "hello world"d, + SliceStruct([1, 2, 3]), + //@@@BUG@@@ 8339 forces this to be takeExactly + //`InitStruct([1, 2, 3]), + TakeNoneStruct([1, 2, 3]))) { static assert(takeNone(range).empty, typeof(range).stringof); assert(takeNone(range).empty); static assert(is(typeof(range) == typeof(takeNone(range))), typeof(range).stringof); } - foreach(range; TypeTuple!(NormalStruct([1, 2, 3]), - InitStruct([1, 2, 3]))) + foreach (range; AliasSeq!(NormalStruct([1, 2, 3]), + InitStruct([1, 2, 3]))) { static assert(takeNone(range).empty, typeof(range).stringof); assert(takeNone(range).empty); @@ -2207,29 +2805,153 @@ auto takeNone(R)(R range) //static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof); } +/++ + + Return a _range advanced to within $(D _n) elements of the end of + + $(D _range). + + + + Intended as the _range equivalent of the Unix + + $(HTTP en.wikipedia.org/wiki/Tail_%28Unix%29, _tail) utility. When the length + + of $(D _range) is less than or equal to $(D _n), $(D _range) is returned + + as-is. + + + + Completes in $(BIGOH 1) steps for ranges that support slicing and have + + length. Completes in $(BIGOH _range.length) time for all other ranges. + + + + Params: + + range = _range to get _tail of + + n = maximum number of elements to include in _tail + + + + Returns: + + Returns the _tail of $(D _range) augmented with length information + +/ +auto tail(Range)(Range range, size_t n) +if (isInputRange!Range && !isInfinite!Range && + (hasLength!Range || isForwardRange!Range)) +{ + static if (hasLength!Range) + { + immutable length = range.length; + if (n >= length) + return range.takeExactly(length); + else + return range.drop(length - n).takeExactly(n); + } + else + { + Range scout = range.save; + foreach (immutable i; 0 .. n) + { + if (scout.empty) + return range.takeExactly(i); + scout.popFront(); + } + + auto tail = range.save; + while (!scout.empty) + { + assert(!tail.empty); + scout.popFront(); + tail.popFront(); + } + + return tail.takeExactly(n); + } +} + +/// +pure @safe nothrow unittest +{ + // tail -c n + assert([1, 2, 3].tail(1) == [3]); + assert([1, 2, 3].tail(2) == [2, 3]); + assert([1, 2, 3].tail(3) == [1, 2, 3]); + assert([1, 2, 3].tail(4) == [1, 2, 3]); + assert([1, 2, 3].tail(0).length == 0); + + // tail --lines=n + import std.algorithm.comparison : equal; + import std.algorithm.iteration : joiner; + import std.string : lineSplitter; + import std.exception : assumeWontThrow; + assert("one\ntwo\nthree" + .lineSplitter + .tail(2) + .joiner("\n") + .equal("two\nthree") + .assumeWontThrow); +} + +// @nogc prevented by @@@BUG@@@ 15408 +pure nothrow @safe /+@nogc+/ unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges, DummyRange, Length, + RangeType, ReturnBy; + + static immutable cheatsheet = [6, 7, 8, 9, 10]; + + foreach (R; AllDummyRanges) + { + static if (isInputRange!R && !isInfinite!R && + (hasLength!R || isForwardRange!R)) + { + assert(R.init.tail(5).equal(cheatsheet)); + static assert(R.init.tail(5).equal(cheatsheet)); + + assert(R.init.tail(0).length == 0); + assert(R.init.tail(10).equal(R.init)); + assert(R.init.tail(11).equal(R.init)); + } + } + + // Infinite ranges are not supported + static assert(!__traits(compiles, repeat(0).tail(0))); + + // Neither are non-forward ranges without length + static assert(!__traits(compiles, DummyRange!(ReturnBy.Value, Length.No, + RangeType.Input).init.tail(5))); +} + +pure @safe nothrow @nogc unittest +{ + static immutable input = [1, 2, 3]; + static immutable expectedOutput = [2, 3]; + assert(input.tail(2) == expectedOutput); +} + /++ Convenience function which calls - $(D range.$(LREF popFrontN)(n)) and returns $(D range). $(D drop) - makes it easier to pop elements from a range + `range.$(REF popFrontN, std, range, primitives)(n) and returns `range`. + `drop` makes it easier to pop elements from a range and then pass it to another function within a single expression, - whereas $(D popFrontN) would require multiple statements. + whereas `popFrontN` would require multiple statements. + + `dropBack` provides the same functionality but instead calls + `range.$(REF popBackN, std, range, primitives)(n) + + Note: `drop` and `dropBack` will only pop $(I up to) + `n` elements but will stop if the range is empty first. + In other languages this is sometimes called `skip`. - $(D dropBack) provides the same functionality but instead calls - $(D range.popBackN(n)). + Params: + range = the input range to drop from + n = the number of elements to drop - Note: $(D drop) and $(D dropBack) will only pop $(I up to) - $(D n) elements but will stop if the range is empty first. + Returns: + `range` with up to `n` elements dropped + See_Also: + $(REF popFront, std, range, primitives), $(REF popBackN, std, range, primitives) +/ R drop(R)(R range, size_t n) - if(isInputRange!R) +if (isInputRange!R) { range.popFrontN(n); return range; } /// ditto R dropBack(R)(R range, size_t n) - if(isBidirectionalRange!R) +if (isBidirectionalRange!R) { range.popBackN(n); return range; @@ -2238,7 +2960,7 @@ R dropBack(R)(R range, size_t n) /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); assert("hello world".drop(6) == "world"); @@ -2248,7 +2970,7 @@ R dropBack(R)(R range, size_t n) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); assert("hello world".dropBack(6) == "hello"); @@ -2258,8 +2980,8 @@ R dropBack(R)(R range, size_t n) @safe unittest { - import std.algorithm : equal; - import std.container.dlist; + import std.algorithm.comparison : equal; + import std.container.dlist : DList; //Remove all but the first two elements auto a = DList!int(0, 1, 9, 9, 9, 9); @@ -2269,7 +2991,8 @@ R dropBack(R)(R range, size_t n) @safe unittest { - import std.algorithm : equal, filter; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; assert(drop("", 5).empty); assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3])); @@ -2277,8 +3000,8 @@ R dropBack(R)(R range, size_t n) @safe unittest { - import std.algorithm : equal; - import std.container.dlist; + import std.algorithm.comparison : equal; + import std.container.dlist : DList; //insert before the last two elements auto a = DList!int(0, 1, 2, 5, 6); @@ -2298,16 +3021,27 @@ R dropBack(R)(R range, size_t n) on an empty range, which is undefined behavior. So, only use $(D popFrontExactly) when it is guaranteed that $(D range) holds at least $(D n) elements. + + Params: + range = the input range to drop from + n = the number of elements to drop + + Returns: + `range` with `n` elements dropped + + See_Also: + $(REF popFrontExcatly, std, range, primitives), + $(REF popBackExcatly, std, range, primitives) +/ R dropExactly(R)(R range, size_t n) - if(isInputRange!R) +if (isInputRange!R) { popFrontExactly(range, n); return range; } /// ditto R dropBackExactly(R)(R range, size_t n) - if(isBidirectionalRange!R) +if (isBidirectionalRange!R) { popBackExactly(range, n); return range; @@ -2316,7 +3050,8 @@ R dropBackExactly(R)(R range, size_t n) /// @safe unittest { - import std.algorithm : equal, filterBidirectional; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filterBidirectional; auto a = [1, 2, 3]; assert(a.dropExactly(2) == [3]); @@ -2342,25 +3077,25 @@ R dropBackExactly(R)(R range, size_t n) $(D range.popBack()). +/ R dropOne(R)(R range) - if (isInputRange!R) +if (isInputRange!R) { range.popFront(); return range; } /// ditto R dropBackOne(R)(R range) - if (isBidirectionalRange!R) +if (isBidirectionalRange!R) { range.popBack(); return range; } /// -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal, filterBidirectional; - - import std.container.dlist; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filterBidirectional; + import std.container.dlist : DList; auto dl = DList!int(9, 1, 2, 3, 9); assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); @@ -2370,8 +3105,9 @@ R dropBackOne(R)(R range) assert(a.dropBackOne() == [1, 2]); string s = "日本語"; - assert(s.dropOne() == "本語"); - assert(s.dropBackOne() == "日本"); + import std.exception : assumeWontThrow; + assert(assumeWontThrow(s.dropOne() == "本語")); + assert(assumeWontThrow(s.dropBackOne() == "日本")); auto bd = filterBidirectional!"true"([1, 2, 3]); assert(bd.dropOne().equal([2, 3])); @@ -2379,9 +3115,13 @@ R dropBackOne(R)(R range) } /** -Repeats one value forever. +Create a range which repeats one value forever. -Models an infinite bidirectional and random access range, with slicing. +Params: + value = the value to repeat + +Returns: + An infinite random access range with slicing. */ struct Repeat(T) { @@ -2389,7 +3129,7 @@ private: //Store a non-qualified T when possible: This is to make Repeat assignable static if ((is(T == class) || is(T == interface)) && (is(T == const) || is(T == immutable))) { - import std.typecons; + import std.typecons : Rebindable; alias UT = Rebindable!T; } else static if (is(T : Unqual!T) && is(Unqual!T : T)) @@ -2399,25 +3139,46 @@ private: UT _value; public: + /// Range primitives @property inout(T) front() inout { return _value; } + + /// ditto @property inout(T) back() inout { return _value; } + + /// ditto enum bool empty = false; + + /// ditto void popFront() {} + + /// ditto void popBack() {} + + /// ditto @property auto save() inout { return this; } + + /// ditto inout(T) opIndex(size_t) inout { return _value; } + + /// ditto auto opSlice(size_t i, size_t j) in { - import core.exception : RangeError; - if (i > j) throw new RangeError(); + assert( + i <= j, + "Attempting to slice a Repeat with a larger first argument than the second." + ); } body { return this.takeExactly(j - i); } private static struct DollarToken {} + + /// ditto enum opDollar = DollarToken.init; + + /// ditto auto opSlice(size_t, DollarToken) inout { return this; } } @@ -2425,16 +3186,16 @@ public: Repeat!T repeat(T)(T value) { return Repeat!T(value); } /// -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; assert(equal(5.repeat().take(4), [ 5, 5, 5, 5 ])); } -@safe unittest +pure @safe nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto r = repeat(5); alias R = typeof(r); @@ -2461,14 +3222,14 @@ Take!(Repeat!T) repeat(T)(T value, size_t n) } /// -@safe unittest +pure @safe nothrow @nogc unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; assert(equal(5.repeat(4), 5.repeat().take(4))); } -@safe unittest //12007 +pure @safe nothrow unittest //12007 { static class C{} Repeat!(immutable int) ri; @@ -2476,7 +3237,7 @@ Take!(Repeat!T) repeat(T)(T value, size_t n) Repeat!(immutable C) rc; rc = rc.save; - import std.algorithm; + import std.algorithm.setops : cartesianProduct; immutable int[] A = [1,2,3]; immutable int[] B = [4,5,6]; @@ -2484,68 +3245,43 @@ Take!(Repeat!T) repeat(T)(T value, size_t n) } /** -Given callable ($(XREF traits, isCallable)) $(D fun), create as a range -whose front is defined by successive calls to $(D fun()). +Given callable ($(REF isCallable, std,traits)) `fun`, create as a range +whose front is defined by successive calls to `fun()`. This is especially useful to call function with global side effects (random functions), or to create ranges expressed as a single delegate, rather than -an entire $(D front)/$(D popFront)/$(D empty) structure. -$(D fun) maybe be passed either a template alias parameter (existing -function, delegate, struct type defining static $(D opCall)... ) or -a run-time value argument (delegate, function object... ). -The result range models an InputRange ($(XREF range, isInputRange)). -The resulting range will call $(D fun()) on every call to $(D front), -and only when $(D front) is called, regardless of how the range is -iterated. -It is advised to compose generate with either $(XREF algorithm,cache) -or $(XREF array,array), or to use it in a foreach loop. -A by-value foreach loop means that the loop value is not $(D ref). - -Params: - Fun = is the $(D isCallable) that gets called on every call to front. - -Returns: an $(D inputRange) that returns a new value generated by $(D Fun) on - any call to $(D front). +an entire `front`/`popFront`/`empty` structure. +`fun` maybe be passed either a template alias parameter (existing +function, delegate, struct type defining `static opCall`) or +a run-time value argument (delegate, function object). +The result range models an InputRange +($(REF isInputRange, std,range,primitives)). +The resulting range will call `fun()` on construction, and every call to +`popFront`, and the cached value will be returned when `front` is called. + +Returns: an `inputRange` where each element represents another call to fun. */ auto generate(Fun)(Fun fun) - if (isCallable!fun) +if (isCallable!fun) { - return Generator!(Fun)(fun); + auto gen = Generator!(Fun)(fun); + gen.popFront(); // prime the first element + return gen; } -/// +/// ditto auto generate(alias fun)() - if (isCallable!fun) -{ - return Generator!(fun)(); -} - -//private struct Generator(bool onPopFront, bool runtime, Fun...) -private struct Generator(Fun...) +if (isCallable!fun) { - static assert(Fun.length == 1); - static assert(isInputRange!Generator); - -private: - static if (is(Fun[0])) - Fun[0] fun; - else - alias fun = Fun[0]; - -public: - enum empty = false; - - auto ref front() @property - { - return fun(); - } - - void popFront() { } + auto gen = Generator!(fun)(); + gen.popFront(); // prime the first element + return gen; } /// @safe pure unittest { - import std.algorithm : equal, map; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; int i = 1; auto powersOfTwo = generate!(() => i *= 2)().take(10); @@ -2555,7 +3291,7 @@ public: /// @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; //Returns a run-time delegate auto infiniteIota(T)(T low, T high) @@ -2567,9 +3303,67 @@ public: assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1])); } +/// +@safe unittest +{ + import std.format : format; + import std.random : uniform; + + auto r = generate!(() => uniform(0, 6)).take(10); + format("%(%s %)", r); +} + +private struct Generator(Fun...) +{ + static assert(Fun.length == 1); + static assert(isInputRange!Generator); + +private: + static if (is(Fun[0])) + Fun[0] fun; + else + alias fun = Fun[0]; + + enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false; + static if (returnByRef_) + ReturnType!fun *elem_; + else + ReturnType!fun elem_; +public: + /// Range primitives + enum empty = false; + + static if (returnByRef_) + { + /// ditto + ref front() @property + { + return *elem_; + } + /// ditto + void popFront() + { + elem_ = &fun(); + } + } + else + { + /// ditto + auto front() @property + { + return elem_; + } + /// ditto + void popFront() + { + elem_ = fun(); + } + } +} + @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; struct StaticOpCall { @@ -2581,7 +3375,7 @@ public: @safe pure unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; struct OpCall { @@ -2592,11 +3386,43 @@ public: assert(equal(generate(op).take(10), repeat(5).take(10))); } +// verify ref mechanism works +@system unittest +{ + int[10] arr; + int idx; + + ref int fun() { + auto x = idx++; + idx %= arr.length; + return arr[x]; + } + int y = 1; + foreach (ref x; generate!(fun).take(20)) + { + x += y++; + } + import std.algorithm.comparison : equal; + assert(equal(arr[], iota(12, 32, 2))); +} + +// assure front isn't the mechanism to make generate go to the next element. +@safe unittest +{ + int i; + auto g = generate!(() => ++i); + auto f = g.front; + assert(f == g.front); + g = g.drop(5); // reassign because generate caches + assert(g.front == f + 5); +} + /** Repeats the given forward range ad infinitum. If the original range is infinite (fact that would make $(D Cycle) the identity application), $(D Cycle) detects that and aliases itself to the range type -itself. If the original range has random access, $(D Cycle) offers +itself. That works for non-forward ranges too. +If the original range has random access, $(D Cycle) offers random access and also offers a constructor taking an initial position $(D index). $(D Cycle) works with static arrays in addition to ranges, mostly for performance reasons. @@ -2606,19 +3432,21 @@ Note: The input range must not be empty. Tip: This is a great way to implement simple circular buffers. */ struct Cycle(R) - if (isForwardRange!R && !isInfinite!R) +if (isForwardRange!R && !isInfinite!R) { static if (isRandomAccessRange!R && hasLength!R) { private R _original; private size_t _index; + /// Range primitives this(R input, size_t index = 0) { _original = input; _index = index % _original.length; } + /// ditto @property auto ref front() { return _original[_index]; @@ -2626,6 +3454,7 @@ struct Cycle(R) static if (is(typeof((cast(const R)_original)[_index]))) { + /// ditto @property auto ref front() const { return _original[_index]; @@ -2634,14 +3463,17 @@ struct Cycle(R) static if (hasAssignableElements!R) { - @property auto front(ElementType!R val) + /// ditto + @property void front(ElementType!R val) { _original[_index] = val; } } + /// ditto enum bool empty = false; + /// ditto void popFront() { ++_index; @@ -2649,6 +3481,7 @@ struct Cycle(R) _index = 0; } + /// ditto auto ref opIndex(size_t n) { return _original[(n + _index) % _original.length]; @@ -2657,6 +3490,7 @@ struct Cycle(R) static if (is(typeof((cast(const R)_original)[_index])) && is(typeof((cast(const R)_original).length))) { + /// ditto auto ref opIndex(size_t n) const { return _original[(n + _index) % _original.length]; @@ -2665,12 +3499,14 @@ struct Cycle(R) static if (hasAssignableElements!R) { - auto opIndexAssign(ElementType!R val, size_t n) + /// ditto + void opIndexAssign(ElementType!R val, size_t n) { _original[(n + _index) % _original.length] = val; } } + /// ditto @property Cycle save() { //No need to call _original.save, because Cycle never actually modifies _original @@ -2678,22 +3514,28 @@ struct Cycle(R) } private static struct DollarToken {} + + /// ditto enum opDollar = DollarToken.init; - auto opSlice(size_t i, size_t j) - in - { - import core.exception : RangeError; - if (i > j) throw new RangeError(); - } - body + static if (hasSlicing!R) { - return this[i .. $].takeExactly(j - i); - } + /// ditto + auto opSlice(size_t i, size_t j) + in + { + assert(i <= j); + } + body + { + return this[i .. $].takeExactly(j - i); + } - auto opSlice(size_t i, DollarToken) - { - return typeof(this)(_original, _index + i); + /// ditto + auto opSlice(size_t i, DollarToken) + { + return typeof(this)(_original, _index + i); + } } } else @@ -2701,12 +3543,14 @@ struct Cycle(R) private R _original; private R _current; + /// ditto this(R input) { _original = input; _current = input.save; } + /// ditto @property auto ref front() { return _current.front; @@ -2714,6 +3558,7 @@ struct Cycle(R) static if (is(typeof((cast(const R)_current).front))) { + /// ditto @property auto ref front() const { return _current.front; @@ -2722,14 +3567,17 @@ struct Cycle(R) static if (hasAssignableElements!R) { + /// ditto @property auto front(ElementType!R val) { return _current.front = val; } } + /// ditto enum bool empty = false; + /// ditto void popFront() { _current.popFront(); @@ -2737,6 +3585,7 @@ struct Cycle(R) _current = _original.save; } + /// ditto @property Cycle save() { //No need to call _original.save, because Cycle never actually modifies _original @@ -2748,26 +3597,31 @@ struct Cycle(R) } } +/// ditto template Cycle(R) - if (isInfinite!R) +if (isInfinite!R) { alias Cycle = R; } +/// struct Cycle(R) - if (isStaticArray!R) +if (isStaticArray!R) { private alias ElementType = typeof(R.init[0]); private ElementType* _ptr; private size_t _index; nothrow: + + /// Range primitives this(ref R input, size_t index = 0) @system { _ptr = input.ptr; _index = index % R.length; } + /// ditto @property ref inout(ElementType) front() inout @safe { static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted @@ -2777,8 +3631,10 @@ nothrow: return trustedPtrIdx(_ptr, _index); } + /// ditto enum bool empty = false; + /// ditto void popFront() @safe { ++_index; @@ -2786,6 +3642,7 @@ nothrow: _index = 0; } + /// ditto ref inout(ElementType) opIndex(size_t n) inout @safe { static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted @@ -2795,75 +3652,85 @@ nothrow: return trustedPtrIdx(_ptr, n + _index); } + /// ditto @property inout(Cycle) save() inout @safe { return this; } private static struct DollarToken {} + /// ditto enum opDollar = DollarToken.init; + /// ditto auto opSlice(size_t i, size_t j) @safe in { - import core.exception : RangeError; - if (i > j) throw new RangeError(); + assert( + i <= j, + "Attempting to slice a Repeat with a larger first argument than the second." + ); } body { return this[i .. $].takeExactly(j - i); } + /// ditto inout(typeof(this)) opSlice(size_t i, DollarToken) inout @safe { static auto trustedCtor(typeof(_ptr) p, size_t idx) @trusted { - return cast(inout)Cycle(*cast(R*)(p), idx); + return cast(inout) Cycle(*cast(R*)(p), idx); } return trustedCtor(_ptr, _index + i); } } /// Ditto -Cycle!R cycle(R)(R input) - if (isForwardRange!R && !isInfinite!R) +auto cycle(R)(R input) +if (isInputRange!R) { - assert(!input.empty); - return Cycle!R(input); + static assert(isForwardRange!R || isInfinite!R, + "Cycle requires a forward range argument unless it's statically known" + ~ " to be infinite"); + assert(!input.empty, "Attempting to pass an empty input to cycle"); + static if (isInfinite!R) return input; + else return Cycle!R(input); } /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.range : cycle, take; - assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][])); + // Here we create an infinitive cyclic sequence from [1, 2] + // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then + // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1]) + // and compare them with the expected values for equality. + assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ])); } /// Ditto Cycle!R cycle(R)(R input, size_t index = 0) - if (isRandomAccessRange!R && !isInfinite!R) +if (isRandomAccessRange!R && !isInfinite!R) { - assert(!input.empty); + assert(!input.empty, "Attempting to pass an empty input to cycle"); return Cycle!R(input, index); } -Cycle!R cycle(R)(R input) - if (isInfinite!R) -{ - return input; -} - +/// Ditto Cycle!R cycle(R)(ref R input, size_t index = 0) @system - if (isStaticArray!R) +if (isStaticArray!R) { return Cycle!R(input, index); } @safe unittest { - import std.internal.test.dummyrange; - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges; static assert(isForwardRange!(Cycle!(uint[]))); @@ -2876,7 +3743,7 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system immutable int[] immarr = [1, 2, 3]; auto cycleimm = cycle(immarr); - foreach(DummyType; AllDummyRanges) + foreach (DummyType; AllDummyRanges) { static if (isForwardRange!DummyType) { @@ -2899,9 +3766,6 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system static if (isRandomAccessRange!DummyType) { - import core.exception : RangeError; - import std.exception : assertThrown; - { cy[10] = 66; scope(exit) cy[10] = 1; @@ -2912,7 +3776,7 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system } } - static if(hasSlicing!DummyType) + static if (hasSlicing!DummyType) { auto slice = cy[5 .. 15]; assert(equal(slice, [6, 7, 8, 9, 10, 1, 2, 3, 4, 5])); @@ -2928,7 +3792,7 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system @system unittest // For static arrays. { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[3] a = [ 1, 2, 3 ]; static assert(isStaticArray!(typeof(a))); @@ -2947,6 +3811,13 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system @safe unittest // For infinite ranges { struct InfRange + { + void popFront() { } + @property int front() { return 0; } + enum empty = false; + auto save() { return this; } + } + struct NonForwardInfRange { void popFront() { } @property int front() { return 0; } @@ -2954,13 +3825,16 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system } InfRange i; + NonForwardInfRange j; auto c = cycle(i); - assert (c == i); + assert(c == i); + //make sure it can alias out even non-forward infinite ranges + static assert(is(typeof(j.cycle) == typeof(j))); } @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[5] arr = [0, 1, 2, 3, 4]; auto cleD = cycle(arr[]); //Dynamic @@ -2980,7 +3854,7 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[5] arr = [0, 1, 2, 3, 4]; auto cleS = cycle(arr); //Static @@ -3000,7 +3874,7 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system @system unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[1] arr = [0]; auto cleS = cycle(arr); @@ -3009,9 +3883,10 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) @system assert(cleS.front == 0); } -unittest //10845 +@system unittest //10845 { - import std.algorithm : equal, filter; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; auto a = inputRangeObject(iota(3).filter!"true"); assert(equal(cycle(a).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0])); @@ -3023,11 +3898,11 @@ unittest //10845 } // Issue 13390 -unittest +@system unittest { - import std.exception; import core.exception : AssertError; - assertThrown!AssertError(cycle([0, 1, 2][0..0])); + import std.exception : assertThrown; + assertThrown!AssertError(cycle([0, 1, 2][0 .. 0])); } private alias lengthType(R) = typeof(R.init.length.init); @@ -3036,23 +3911,35 @@ private alias lengthType(R) = typeof(R.init.length.init); Iterate several ranges in lockstep. The element type is a proxy tuple that allows accessing the current element in the $(D n)th range by using $(D e[n]). - $(D Zip) offers the lowest range facilities of all components, e.g. it - offers random access iff all ranges offer random access, and also - offers mutation and swapping if all ranges offer it. Due to this, $(D - Zip) is extremely powerful because it allows manipulating several - ranges in lockstep. For example, the following code sorts two arrays - in parallel: + + `zip` is similar to $(LREF lockstep), but `lockstep` doesn't + bundle its elements and uses the `opApply` protocol. + `lockstep` allows reference access to the elements in + `foreach` iterations. + + Params: + sp = controls what `zip` will do if the _ranges are different lengths + ranges = the ranges to zip together + Returns: + At minimum, an input range. `Zip` offers the lowest range facilities + of all components, e.g. it offers random access iff all ranges offer + random access, and also offers mutation and swapping if all ranges offer + it. Due to this, `Zip` is extremely powerful because it allows manipulating + several ranges in lockstep. + Throws: + An `Exception` if all of the _ranges are not the same length and + `sp` is set to `StoppingPolicy.requireSameLength`. */ struct Zip(Ranges...) - if (Ranges.length && allSatisfy!(isInputRange, Ranges)) +if (Ranges.length && allSatisfy!(isInputRange, Ranges)) { import std.format : format; //for generic mixins import std.typecons : Tuple; alias R = Ranges; - R ranges; + private R ranges; alias ElementType = Tuple!(staticMap!(.ElementType, R)); - StoppingPolicy stoppingPolicy = StoppingPolicy.shortest; + private StoppingPolicy stoppingPolicy = StoppingPolicy.shortest; /** Builds an object. Usually this is invoked indirectly by using the @@ -3077,9 +3964,11 @@ struct Zip(Ranges...) } else { + /// @property bool empty() { import std.exception : enforce; + import std.meta : anySatisfy; final switch (stoppingPolicy) { @@ -3090,11 +3979,18 @@ struct Zip(Ranges...) } return false; case StoppingPolicy.longest: - foreach (i, Unused; R) + static if (anySatisfy!(isInfinite, R)) + { + return false; + } + else { - if (!ranges[i].empty) return false; + foreach (i, Unused; R) + { + if (!ranges[i].empty) return false; + } + return true; } - return true; case StoppingPolicy.requireSameLength: foreach (i, Unused; R[1 .. $]) { @@ -3110,10 +4006,11 @@ struct Zip(Ranges...) static if (allSatisfy!(isForwardRange, R)) { + /// @property Zip save() { //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy) - return mixin (q{Zip(%(ranges[%s]%|, %), stoppingPolicy)}.format(iota(0, R.length))); + return mixin (q{Zip(%(ranges[%s].save%|, %), stoppingPolicy)}.format(iota(0, R.length))); } } @@ -3160,7 +4057,7 @@ struct Zip(Ranges...) { ElementType moveFront() { - @property tryMoveFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : .moveFront(ranges[i]);} + @property tryMoveFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveFront();} //ElementType(tryMoveFront!0, tryMoveFront!1, ...) return mixin(q{ElementType(%(tryMoveFront!%s, %))}.format(iota(0, R.length))); } @@ -3189,7 +4086,7 @@ struct Zip(Ranges...) { //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness - @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : .moveFront(ranges[i]);} + @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveFront();} //ElementType(tryMoveBack!0, tryMoveBack!1, ...) return mixin(q{ElementType(%(tryMoveBack!%s, %))}.format(iota(0, R.length))); } @@ -3300,7 +4197,7 @@ struct Zip(Ranges...) return ranges[0].length; //[min|max](ranges[0].length, ranges[1].length, ...) - import std.algorithm : min, max; + import std.algorithm.comparison : min, max; if (stoppingPolicy == StoppingPolicy.shortest) return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); else @@ -3370,8 +4267,8 @@ struct Zip(Ranges...) //TODO: Fixme! This may create an out of bounds access //for StoppingPolicy.longest - //ElementType(.moveAt(ranges[0], n), .moveAt(ranges[1], n), ..., ) - return mixin (q{ElementType(%(.moveAt(ranges[%s], n)%|, %))}.format(iota(0, R.length))); + //ElementType(ranges[0].moveAt(n), ranges[1].moveAt(n), ..., ) + return mixin (q{ElementType(%(ranges[%s].moveAt(n)%|, %))}.format(iota(0, R.length))); } } } @@ -3379,40 +4276,65 @@ struct Zip(Ranges...) /// Ditto auto zip(Ranges...)(Ranges ranges) - if (Ranges.length && allSatisfy!(isInputRange, Ranges)) +if (Ranges.length && allSatisfy!(isInputRange, Ranges)) { return Zip!Ranges(ranges); } /// -pure unittest +pure @safe unittest { - import std.algorithm : sort; - int[] a = [ 1, 2, 3 ]; - string[] b = [ "a", "b", "c" ]; - sort!((c, d) => c[0] > d[0])(zip(a, b)); - assert(a == [ 3, 2, 1 ]); - assert(b == [ "c", "b", "a" ]); + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + + // pairwise sum + auto arr = [0, 1, 2]; + assert(zip(arr, arr.dropOne).map!"a[0] + a[1]".equal([1, 3])); } /// -unittest +pure @safe unittest { - int[] a = [ 1, 2, 3 ]; - string[] b = [ "a", "b", "c" ]; + import std.conv : to; - size_t idx = 0; - foreach (e; zip(a, b)) - { - assert(e[0] == a[idx]); - assert(e[1] == b[idx]); - ++idx; - } + int[] a = [ 1, 2, 3 ]; + string[] b = [ "a", "b", "c" ]; + string[] result; + + foreach (tup; zip(a, b)) + { + result ~= tup[0].to!string ~ tup[1]; + } + + assert(result == [ "1a", "2b", "3c" ]); + + size_t idx = 0; + // unpacking tuple elements with foreach + foreach (e1, e2; zip(a, b)) + { + assert(e1 == a[idx]); + assert(e2 == b[idx]); + ++idx; + } +} + +/// $(D zip) is powerful - the following code sorts two arrays in parallel: +pure @safe unittest +{ + import std.algorithm.sorting : sort; + + int[] a = [ 1, 2, 3 ]; + string[] b = [ "a", "c", "b" ]; + zip(a, b).sort!((t1, t2) => t1[0] > t2[0]); + + assert(a == [ 3, 2, 1 ]); + // b is sorted according to a's sorting + assert(b == [ "b", "c", "a" ]); } /// Ditto auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) - if (Ranges.length && allSatisfy!(isInputRange, Ranges)) +if (Ranges.length && allSatisfy!(isInputRange, Ranges)) { return Zip!Ranges(ranges, sp); } @@ -3431,10 +4353,12 @@ enum StoppingPolicy requireSameLength, } -unittest +@system unittest { - import std.internal.test.dummyrange; - import std.algorithm : swap, sort, filter, equal, map; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter, map; + import std.algorithm.mutation : swap; + import std.algorithm.sorting : sort; import std.exception : assertThrown, assertNotThrown; import std.typecons : tuple; @@ -3481,7 +4405,8 @@ unittest alias FOO = Zip!(immutable(int)[], immutable(float)[]); - foreach(t; stuff.expand) { + foreach (t; stuff.expand) + { auto arr1 = t[0]; auto arr2 = t[1]; auto zShortest = zip(arr1, arr2); @@ -3490,7 +4415,7 @@ unittest try { auto zSame = zip(StoppingPolicy.requireSameLength, arr1, arr2); - foreach(elem; zSame) {} + foreach (elem; zSame) {} assert(0); } catch (Throwable) { /* It's supposed to throw.*/ } @@ -3509,8 +4434,8 @@ unittest } // BUG 8900 - static assert(__traits(compiles, zip([1, 2], repeat('a')))); - static assert(__traits(compiles, zip(repeat('a'), [1, 2]))); + assert(zip([1, 2], repeat('a')).array == [tuple(1, 'a'), tuple(2, 'a')]); + assert(zip(repeat('a'), [1, 2]).array == [tuple('a', 1), tuple('a', 2)]); // Doesn't work yet. Issues w/ emplace. // static assert(is(Zip!(immutable int[], immutable float[]))); @@ -3522,15 +4447,18 @@ unittest // make -fwin32.mak unittest makes the compiler completely run out of RAM. // You need to test just this module. /+ - foreach(DummyType1; AllDummyRanges) { + foreach (DummyType1; AllDummyRanges) + { DummyType1 d1; - foreach(DummyType2; AllDummyRanges) { + foreach (DummyType2; AllDummyRanges) + { DummyType2 d2; auto r = zip(d1, d2); assert(equal(map!"a[0]"(r), [1,2,3,4,5,6,7,8,9,10])); assert(equal(map!"a[1]"(r), [1,2,3,4,5,6,7,8,9,10])); - static if (isForwardRange!DummyType1 && isForwardRange!DummyType2) { + static if (isForwardRange!DummyType1 && isForwardRange!DummyType2) + { static assert(isForwardRange!(typeof(r))); } @@ -3547,9 +4475,9 @@ unittest +/ } -pure unittest +pure @safe unittest { - import std.algorithm : sort; + import std.algorithm.sorting : sort; auto a = [5,4,3,2,1]; auto b = [3,1,2,5,6]; @@ -3563,8 +4491,8 @@ pure unittest @safe pure unittest { + import std.algorithm.comparison : equal; import std.typecons : tuple; - import std.algorithm : equal; auto LL = iota(1L, 1000L); auto z = zip(LL, [4]); @@ -3582,8 +4510,8 @@ pure unittest import std.exception : assertThrown; static struct S { @disable this(); } - static assert(__traits(compiles, zip((S[5]).init[]))); - auto z = zip(StoppingPolicy.longest, cast(S[]) null, new int[1]); + assert(zip((S[5]).init[]).length == 5); + assert(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).length == 1); assertThrown(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).front); } @@ -3602,11 +4530,24 @@ pure unittest auto zz = z.save; } +pure @system unittest +{ + import std.typecons : tuple; + + auto r1 = [0,1,2]; + auto r2 = [1,2,3]; + auto z1 = zip(refRange(&r1), refRange(&r2)); + auto z2 = z1.save; + z1.popFront(); + assert(z1.front == tuple(1,2)); + assert(z2.front == tuple(0,1)); +} + /* Generate lockstep's opApply function as a mixin string. If withIndex is true prepend a size_t index to the delegate. */ -private string lockstepMixin(Ranges...)(bool withIndex) +private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) { import std.format : format; @@ -3614,24 +4555,52 @@ private string lockstepMixin(Ranges...)(bool withIndex) string[] emptyChecks; string[] dgArgs; string[] popFronts; + string indexDef; + string indexInc; if (withIndex) { params ~= "size_t"; dgArgs ~= "index"; + if (reverse) + { + indexDef = q{ + size_t index = ranges[0].length-1; + enforce(_stoppingPolicy == StoppingPolicy.requireSameLength, "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); + + foreach (range; ranges[1..$]) + enforce(range.length == ranges[0].length); + }; + indexInc = "--index;"; + } + else + { + indexDef = "size_t index = 0;"; + indexInc = "++index;"; + } } foreach (idx, Range; Ranges) { params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); emptyChecks ~= format("!ranges[%s].empty", idx); - dgArgs ~= format("ranges[%s].front", idx); - popFronts ~= format("ranges[%s].popFront();", idx); + if (reverse) + { + dgArgs ~= format("ranges[%s].back", idx); + popFronts ~= format("ranges[%s].popBack();", idx); + } + else + { + dgArgs ~= format("ranges[%s].front", idx); + popFronts ~= format("ranges[%s].popFront();", idx); + } } + string name = reverse ? "opApplyReverse" : "opApply"; + return format( q{ - int opApply(scope int delegate(%s) dg) + int %s(scope int delegate(%s) dg) { import std.exception : enforce; @@ -3649,19 +4618,20 @@ private string lockstepMixin(Ranges...)(bool withIndex) if (_stoppingPolicy == StoppingPolicy.requireSameLength) { - foreach(range; ranges) + foreach (range; ranges) enforce(range.empty); } return res; } - }, params.join(", "), withIndex ? "size_t index = 0;" : "", + }, name, params.join(", "), indexDef, emptyChecks.join(" && "), dgArgs.join(", "), popFronts.join("\n "), - withIndex ? "index++;" : ""); + indexInc); } /** - Iterate multiple ranges in lockstep using a $(D foreach) loop. If only a single + Iterate multiple ranges in lockstep using a $(D foreach) loop. In contrast to + $(LREF zip) it allows reference access to its elements. If only a single range is passed in, the $(D Lockstep) aliases itself away. If the ranges are of different lengths and $(D s) == $(D StoppingPolicy.shortest) stop after the shortest range is empty. If the ranges are of different @@ -3669,23 +4639,24 @@ private string lockstepMixin(Ranges...)(bool withIndex) exception. $(D s) may not be $(D StoppingPolicy.longest), and passing this will throw an exception. + Iterating over $(D Lockstep) in reverse and with an index is only possible + when $(D s) == $(D StoppingPolicy.requireSameLength), in order to preserve + indexes. If an attempt is made at iterating in reverse when $(D s) == + $(D StoppingPolicy.shortest), an exception will be thrown. + By default $(D StoppingPolicy) is set to $(D StoppingPolicy.shortest). - BUGS: If a range does not offer lvalue access, but $(D ref) is used in the - $(D foreach) loop, it will be silently accepted but any modifications - to the variable will not be propagated to the underlying range. + See_Also: $(LREF zip) - // Lockstep also supports iterating with an index variable: - Example: - ------- - foreach(index, a, b; lockstep(arr1, arr2)) { - writefln("Index %s: a = %s, b = %s", index, a, b); - } - ------- + `lockstep` is similar to $(LREF zip), but `zip` bundles its + elements and returns a range. + `lockstep` also supports reference access. + Use `zip` if you want to pass the result to a range function. */ struct Lockstep(Ranges...) - if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) +if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) { + /// this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest) { import std.exception : enforce; @@ -3696,8 +4667,25 @@ struct Lockstep(Ranges...) _stoppingPolicy = sp; } - mixin(lockstepMixin!Ranges(false)); - mixin(lockstepMixin!Ranges(true)); + mixin(lockstepMixin!Ranges(false, false)); + mixin(lockstepMixin!Ranges(true, false)); + static if (allSatisfy!(isBidirectionalRange, Ranges)) + { + mixin(lockstepMixin!Ranges(false, true)); + static if (allSatisfy!(hasLength, Ranges)) + { + mixin(lockstepMixin!Ranges(true, true)); + } + else + { + mixin(lockstepReverseFailMixin!Ranges(true)); + } + } + else + { + mixin(lockstepReverseFailMixin!Ranges(false)); + mixin(lockstepReverseFailMixin!Ranges(true)); + } private: alias R = Ranges; @@ -3705,6 +4693,41 @@ private: StoppingPolicy _stoppingPolicy; } +string lockstepReverseFailMixin(Ranges...)(bool withIndex) +{ + import std.format : format; + string[] params; + string message; + + if (withIndex) + { + message = "Indexed reverse iteration with lockstep is only supported" + ~"if all ranges are bidirectional and have a length.\n"; + } + else + { + message = "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n"; + } + + if (withIndex) + { + params ~= "size_t"; + } + + foreach (idx, Range; Ranges) + { + params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); + } + + return format( + q{ + int opApplyReverse()(scope int delegate(%s) dg) + { + static assert(false, "%s"); + } + }, params.join(", "), message); +} + // For generic programming, make sure Lockstep!(Range) is well defined for a // single range. template Lockstep(Range) @@ -3714,13 +4737,13 @@ template Lockstep(Range) /// Ditto Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges) - if (allSatisfy!(isInputRange, Ranges)) +if (allSatisfy!(isInputRange, Ranges)) { return Lockstep!(Ranges)(ranges); } /// Ditto Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) - if (allSatisfy!(isInputRange, Ranges)) +if (allSatisfy!(isInputRange, Ranges)) { static if (Ranges.length > 1) return Lockstep!Ranges(ranges, s); @@ -3729,23 +4752,54 @@ Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) } /// -unittest +@system unittest { - auto arr1 = [1,2,3,4,5]; + auto arr1 = [1,2,3,4,5,100]; auto arr2 = [6,7,8,9,10]; - foreach(ref a, ref b; lockstep(arr1, arr2)) + foreach (ref a, b; lockstep(arr1, arr2)) { a += b; } - assert(arr1 == [7,9,11,13,15]); + assert(arr1 == [7,9,11,13,15,100]); + + /// Lockstep also supports iterating with an index variable: + foreach (index, a, b; lockstep(arr1, arr2)) + { + assert(arr1[index] == a); + assert(arr2[index] == b); + } } -unittest +@system unittest // Bugzilla 15860: foreach_reverse on lockstep +{ + auto arr1 = [0, 1, 2, 3]; + auto arr2 = [4, 5, 6, 7]; + + size_t n = arr1.length -1; + foreach_reverse (index, a, b; lockstep(arr1, arr2, StoppingPolicy.requireSameLength)) + { + assert(n == index); + assert(index == a); + assert(arr1[index] == a); + assert(arr2[index] == b); + n--; + } + + auto arr3 = [4, 5]; + n = 1; + foreach_reverse (a, b; lockstep(arr1, arr3)) + { + assert(a == arr1[$-n] && b == arr3[$-n]); + n++; + } +} + +@system unittest { + import std.algorithm.iteration : filter; import std.conv : to; - import std.algorithm : filter; // The filters are to make these the lowest common forward denominator ranges, // i.e. w/o ref return, random access, length, etc. @@ -3754,12 +4808,13 @@ unittest auto l = lockstep(foo, bar); // Should work twice. These are forward ranges with implicit save. - foreach(i; 0..2) + foreach (i; 0 .. 2) { uint[] res1; float[] res2; - foreach(a, ref b; l) { + foreach (a, ref b; l) + { res1 ~= a; res2 ~= b; } @@ -3773,7 +4828,7 @@ unittest auto arr1 = [1,2,3,4,5]; auto arr2 = [6,7,8,9,10]; - foreach(ref a, ref b; lockstep(arr1, arr2)) + foreach (ref a, ref b; lockstep(arr1, arr2)) { a += b; } @@ -3783,14 +4838,14 @@ unittest // Make sure StoppingPolicy.requireSameLength doesn't throw. auto ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); - foreach(a, b; ls) {} + foreach (a, b; ls) {} // Make sure StoppingPolicy.requireSameLength throws. arr2.popBack(); ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); try { - foreach(a, b; ls) {} + foreach (a, b; ls) {} assert(0); } catch (Exception) {} @@ -3802,7 +4857,7 @@ unittest uint[] res1; float[] res2; size_t[] indices; - foreach(i, a, b; lockstep(foo, bar)) + foreach (i, a, b; lockstep(foo, bar)) { indices ~= i; res1 ~= a; @@ -3822,12 +4877,40 @@ unittest const(int[])[] bar2 = [[4, 5, 6]]; auto c = chain(foo2, bar2); - foreach(f, b; lockstep(c, c)) {} + foreach (f, b; lockstep(c, c)) {} // Regression 10468 foreach (x, y; lockstep(iota(0, 10), iota(0, 10))) { } } +@system unittest +{ + struct RvalueRange + { + int[] impl; + @property bool empty() { return impl.empty; } + @property int front() { return impl[0]; } // N.B. non-ref + void popFront() { impl.popFront(); } + } + auto data1 = [ 1, 2, 3, 4 ]; + auto data2 = [ 5, 6, 7, 8 ]; + auto r1 = RvalueRange(data1); + auto r2 = data2; + foreach (a, ref b; lockstep(r1, r2)) + { + a++; + b++; + } + assert(data1 == [ 1, 2, 3, 4 ]); // changes to a do not propagate to data + assert(data2 == [ 6, 7, 8, 9 ]); // but changes to b do. + + // Since r1 is by-value only, the compiler should reject attempts to + // foreach over it with ref. + static assert(!__traits(compiles, { + foreach (ref a, ref b; lockstep(r1, r2)) { a++; } + })); +} + /** Creates a mathematical sequence given the initial values and a recurrence function that computes the next value from the existing @@ -3862,7 +4945,7 @@ managing the recurrence's state and shifting it appropriately. */ struct Recurrence(alias fun, StateType, size_t stateSize) { - private import std.functional : binaryFun; + import std.functional : binaryFun; StateType[stateSize] _state; size_t _n; @@ -3899,7 +4982,7 @@ struct Recurrence(alias fun, StateType, size_t stateSize) /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; // The Fibonacci numbers, using function in string form: // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n] @@ -3935,7 +5018,7 @@ recurrence(alias fun, State...)(State initial) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); static assert(isForwardRange!(typeof(fib))); @@ -3956,7 +5039,7 @@ recurrence(alias fun, State...)(State initial) /** $(D Sequence) is similar to $(D Recurrence) except that iteration is - presented in the so-called $(WEB en.wikipedia.org/wiki/Closed_form, + presented in the so-called $(HTTP en.wikipedia.org/wiki/Closed_form, closed form). This means that the $(D n)th element in the series is computable directly from the initial values and $(D n) itself. This implies that the interface offered by $(D Sequence) is a random-access @@ -4000,7 +5083,10 @@ public: auto opSlice(size_t lower, size_t upper) in { - assert(upper >= lower); + assert( + upper >= lower, + "Attempting to slice a Sequence with a larger first argument than the second." + ); } body { @@ -4090,7 +5176,8 @@ auto sequence(alias fun, State...)(State args) auto odds = Sequence!("a[0] + n * a[1]", Tuple!(int, int))( tuple(1, 2)); - for(int currentOdd = 1; currentOdd <= 21; currentOdd += 2) { + for (int currentOdd = 1; currentOdd <= 21; currentOdd += 2) + { assert(odds.front == odds[0]); assert(odds[0] == currentOdd); odds.popFront(); @@ -4099,7 +5186,7 @@ auto sequence(alias fun, State...)(State args) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto odds = sequence!("a[0] + n * a[1]")(1, 2); static assert(hasSlicing!(typeof(odds))); @@ -4122,14 +5209,15 @@ auto sequence(alias fun, State...)(State args) } // Issue 5036 -unittest +@safe unittest { auto s = sequence!((a, n) => new int)(0); assert(s.front != s.front); // no caching } +// iota /** - Construct a range of values that span the given starting and stopping + Creates a range of values that span the given starting and stopping values. Params: @@ -4159,7 +5247,7 @@ unittest // The following groups all produce the same output of: // 0 1 2 3 4 - foreach (i; 0..5) + foreach (i; 0 .. 5) writef("%s ", i); writeln(); @@ -4170,7 +5258,8 @@ unittest writefln("%(%s %|%)", iota(0, 5)); - import std.algorithm : map, copy; + import std.algorithm.iteration : map; + import std.algorithm.mutation : copy; import std.format; iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter()); writeln(); @@ -4180,56 +5269,67 @@ unittest auto iota(B, E, S)(B begin, E end, S step) if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) && isIntegral!S) -in -{ - assert(!(step == 0 && begin != end)); -} -body { import std.conv : unsigned; alias Value = CommonType!(Unqual!B, Unqual!E); alias StepType = Unqual!S; - alias IndexType = typeof(unsigned((end - begin) / step)); + + assert(step != 0 || begin == end); static struct Result { - private Value current, pastLast; - private StepType step; + private Value current, last; + private StepType step; // by convention, 0 if range is empty this(Value current, Value pastLast, StepType step) { - if ((current < pastLast && step >= 0) || - (current > pastLast && step <= 0)) + if (current < pastLast && step > 0) { - this.step = step; - this.current = current; - if (step > 0) - { - this.pastLast = pastLast - 1; - this.pastLast -= (this.pastLast - current) % step; - } - else - { - this.pastLast = pastLast + 1; - this.pastLast += (current - this.pastLast) % -step; - } - this.pastLast += step; + // Iterating upward + assert(unsigned((pastLast - current) / step) <= size_t.max); + // Cast below can't fail because current < pastLast + this.last = cast(Value) (pastLast - 1); + this.last -= unsigned(this.last - current) % step; + } + else if (current > pastLast && step < 0) + { + // Iterating downward + assert(unsigned((current - pastLast) / -step) <= size_t.max); + // Cast below can't fail because current > pastLast + this.last = cast(Value) (pastLast + 1); + this.last += unsigned(current - this.last) % -step; } else { // Initialize an empty range - this.current = this.pastLast = current; - this.step = 1; + this.step = 0; + return; } + this.step = step; + this.current = current; } - @property bool empty() const { return current == pastLast; } + @property bool empty() const { return step == 0; } @property inout(Value) front() inout { assert(!empty); return current; } - void popFront() { assert(!empty); current += step; } + void popFront() + { + assert(!empty); + if (current == last) step = 0; + else current += step; + } - @property inout(Value) back() inout { assert(!empty); return pastLast - step; } - void popBack() { assert(!empty); pastLast -= step; } + @property inout(Value) back() inout + { + assert(!empty); + return last; + } + void popBack() + { + assert(!empty); + if (current == last) step = 0; + else last -= step; + } @property auto save() { return this; } @@ -4246,20 +5346,18 @@ body { assert(upper >= lower && upper <= this.length); - return cast(inout Result)Result(cast(Value)(current + lower * step), - cast(Value)(pastLast - (length - upper) * step), - step); + return cast(inout Result) Result( + cast(Value)(current + lower * step), + cast(Value)(current + upper * step), + step); } - @property IndexType length() const + @property size_t length() const { if (step > 0) - { - return unsigned((pastLast - current) / step); - } - else - { - return unsigned((current - pastLast) / -step); - } + return 1 + cast(size_t) (unsigned(last - current) / step); + if (step < 0) + return 1 + cast(size_t) (unsigned(current - last) / -step); + return 0; } alias opDollar = length; @@ -4272,7 +5370,7 @@ body auto iota(B, E)(B begin, E end) if (isFloatingPoint!(CommonType!(B, E))) { - return iota(begin, end, 1.0); + return iota(begin, end, CommonType!(B, E)(1)); } /// Ditto @@ -4282,7 +5380,6 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) import std.conv : unsigned; alias Value = CommonType!(Unqual!B, Unqual!E); - alias IndexType = typeof(unsigned(end - begin)); static struct Result { @@ -4292,6 +5389,8 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) { if (current < pastLast) { + assert(unsigned(pastLast - current) <= size_t.max); + this.current = current; this.pastLast = pastLast; } @@ -4311,7 +5410,7 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) @property auto save() { return this; } - inout(Value) opIndex(ulong n) inout + inout(Value) opIndex(size_t n) inout { assert(n < this.length); @@ -4324,12 +5423,12 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) { assert(upper >= lower && upper <= this.length); - return cast(inout Result)Result(cast(Value)(current + lower), + return cast(inout Result) Result(cast(Value)(current + lower), cast(Value)(pastLast - (length - upper))); } - @property IndexType length() const + @property size_t length() const { - return unsigned(pastLast - current); + return cast(size_t)(pastLast - current); } alias opDollar = length; @@ -4340,8 +5439,9 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) /// Ditto auto iota(E)(E end) +if (is(typeof(iota(E(0), end)))) { - E begin = 0; + E begin = E(0); return iota(begin, end); } @@ -4419,7 +5519,7 @@ body Result ret = this; ret.index += lower; ret.count = upper - lower + ret.index; - return cast(inout Result)ret; + return cast(inout Result) ret; } @property size_t length() const { @@ -4435,7 +5535,7 @@ body /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.math : approxEqual; auto r = iota(0, 10, 1); @@ -4447,25 +5547,31 @@ body assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4])); } -nothrow @nogc unittest +nothrow @nogc @safe unittest { auto t0 = iota(0, 10); auto t1 = iota(0, 10, 2); auto t2 = iota(1, 1, 0); //float overloads use std.conv.to so can't be @nogc or nothrow + alias ssize_t = Signed!size_t; + assert(iota(ssize_t.max, 0, -1).length == ssize_t.max); + assert(iota(ssize_t.max, ssize_t.min, -1).length == size_t.max); + assert(iota(ssize_t.max, ssize_t.min, -2).length == 1 + size_t.max / 2); + assert(iota(ssize_t.min, ssize_t.max, 2).length == 1 + size_t.max / 2); + assert(iota(ssize_t.max, ssize_t.min, -3).length == size_t.max / 3); } -debug unittest +debug @system unittest {//check the contracts - import std.exception : assertThrown; import core.exception : AssertError; + import std.exception : assertThrown; assertThrown!AssertError(iota(1,2,0)); assertThrown!AssertError(iota(0f,1f,0f)); assertThrown!AssertError(iota(1f,0f,0.1f)); assertThrown!AssertError(iota(0f,1f,-0.1f)); } -unittest +@system unittest { int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; auto r1 = iota(a.ptr, a.ptr + a.length, 1); @@ -4475,15 +5581,29 @@ unittest @safe unittest { + assert(iota(1UL, 0UL).length == 0); + assert(iota(1UL, 0UL, 1).length == 0); + assert(iota(0, 1, 1).length == 1); + assert(iota(1, 0, -1).length == 1); + assert(iota(0, 1, -1).length == 0); + assert(iota(ulong.max, 0).length == 0); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.searching : count; import std.math : approxEqual, nextUp, nextDown; - import std.algorithm : count, equal; + import std.meta : AliasSeq; + + static assert(is(ElementType!(typeof(iota(0f))) == float)); static assert(hasLength!(typeof(iota(0, 2)))); auto r = iota(0, 10, 1); assert(r[$ - 1] == 9); assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); - auto rSlice = r[2..8]; + auto rSlice = r[2 .. 8]; assert(equal(rSlice, [2, 3, 4, 5, 6, 7])); rSlice.popFront(); @@ -4494,7 +5614,7 @@ unittest assert(rSlice[rSlice.length - 1] == rSlice.back); assert(rSlice.back == 6); - rSlice = r[0..4]; + rSlice = r[0 .. 4]; assert(equal(rSlice, [0, 1, 2, 3])); auto rr = iota(10); @@ -4502,23 +5622,23 @@ unittest r = iota(0, -10, -1); assert(equal(r, [0, -1, -2, -3, -4, -5, -6, -7, -8, -9][])); - rSlice = r[3..9]; + rSlice = r[3 .. 9]; assert(equal(rSlice, [-3, -4, -5, -6, -7, -8])); r = iota(0, -6, -3); assert(equal(r, [0, -3][])); - rSlice = r[1..2]; + rSlice = r[1 .. 2]; assert(equal(rSlice, [-3])); r = iota(0, -7, -3); assert(equal(r, [0, -3, -6][])); - rSlice = r[1..3]; + rSlice = r[1 .. 3]; assert(equal(rSlice, [-3, -6])); r = iota(0, 11, 3); assert(equal(r, [0, 3, 6, 9][])); assert(r[2] == 6); - rSlice = r[1..3]; + rSlice = r[1 .. 3]; assert(equal(rSlice, [3, 6])); auto rf = iota(0.0, 0.5, 0.1); @@ -4528,7 +5648,7 @@ unittest rf.popFront(); assert(rf.length == 4); - auto rfSlice = rf[1..4]; + auto rfSlice = rf[1 .. 4]; assert(rfSlice.length == 3); assert(approxEqual(rfSlice, [0.2, 0.3, 0.4])); @@ -4538,7 +5658,7 @@ unittest rf.popFront(); assert(rf.length == 3); - rfSlice = rf[1..3]; + rfSlice = rf[1 .. 3]; assert(rfSlice.length == 2); assert(approxEqual(rfSlice, [0.3, 0.4])); assert(approxEqual(rfSlice[0], 0.3)); @@ -4554,7 +5674,7 @@ unittest // going down rf = iota(0.0, -0.5, -0.1); assert(approxEqual(rf, [0.0, -0.1, -0.2, -0.3, -0.4][])); - rfSlice = rf[2..5]; + rfSlice = rf[2 .. 5]; assert(approxEqual(rfSlice, [-0.2, -0.3, -0.4])); rf = iota(0.0, nextDown(-0.5), -0.1); @@ -4584,28 +5704,29 @@ unittest assert(iota(uint.max, 0u, -1).length == uint.max); // Issue 8920 - foreach (Type; TypeTuple!(byte, ubyte, short, ushort, + foreach (Type; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) { Type val; - foreach (i; iota(cast(Type)0, cast(Type)10)) { val++; } + foreach (i; iota(cast(Type) 0, cast(Type) 10)) { val++; } assert(val == 10); } } @safe unittest { - import std.algorithm : copy; + import std.algorithm.mutation : copy; auto idx = new size_t[100]; copy(iota(0, idx.length), idx); } @safe unittest { - foreach(range; TypeTuple!(iota(2, 27, 4), - iota(3, 9), - iota(2.7, 12.3, .1), - iota(3.2, 9.7))) + import std.meta : AliasSeq; + foreach (range; AliasSeq!(iota(2, 27, 4), + iota(3, 9), + iota(2.7, 12.3, .1), + iota(3.2, 9.7))) { const cRange = range; const e = cRange.empty; @@ -4618,7 +5739,7 @@ unittest } } -unittest +@system unittest { //The ptr stuff can't be done at compile time, so we unfortunately end //up with some code duplication here. @@ -4647,16 +5768,39 @@ unittest } } +@nogc nothrow pure @safe +unittest +{ + { + ushort start = 0, end = 10, step = 2; + foreach (i; iota(start, end, step)) + static assert(is(typeof(i) == ushort)); + } + { + ubyte start = 0, end = 255, step = 128; + uint x; + foreach (i; iota(start, end, step)) + { + static assert(is(typeof(i) == ubyte)); + ++x; + } + assert(x == 2); + } +} + /* Generic overload that handles arbitrary types that support arithmetic * operations. + * + * User-defined types such as $(REF BigInt, std,bigint) are also supported, as long + * as they can be incremented with $(D ++) and compared with $(D <) or $(D ==). */ /// ditto auto iota(B, E)(B begin, E end) - if (!isIntegral!(CommonType!(B, E)) && - !isFloatingPoint!(CommonType!(B, E)) && - !isPointer!(CommonType!(B, E)) && - is(typeof((ref B b) { ++b; })) && - (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) ) +if (!isIntegral!(CommonType!(B, E)) && + !isFloatingPoint!(CommonType!(B, E)) && + !isPointer!(CommonType!(B, E)) && + is(typeof((ref B b) { ++b; })) && + (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) ) { static struct Result { @@ -4682,27 +5826,7 @@ auto iota(B, E)(B begin, E end) return Result(begin, end); } -/** -User-defined types such as $(XREF bigint, BigInt) are also supported, as long -as they can be incremented with $(D ++) and compared with $(D <) or $(D ==). -*/ -// Issue 6447 -unittest -{ - import std.algorithm.comparison : equal; - import std.bigint; - - auto s = BigInt(1_000_000_000_000); - auto e = BigInt(1_000_000_000_003); - auto r = iota(s, e); - assert(r.equal([ - BigInt(1_000_000_000_000), - BigInt(1_000_000_000_001), - BigInt(1_000_000_000_002) - ])); -} - -unittest +@safe unittest { import std.algorithm.comparison : equal; @@ -4820,6 +5944,12 @@ struct FrontTransversal(Ror, { @property bool empty() { + static if (opt != TransverseOptions.assumeJagged) + { + if (!_input.empty) + return _input.front.empty; + } + return _input.empty; } } @@ -4827,7 +5957,7 @@ struct FrontTransversal(Ror, /// Ditto @property auto ref front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty FrontTransversal"); return _input.front.front; } @@ -4836,13 +5966,13 @@ struct FrontTransversal(Ror, { ElementType moveFront() { - return .moveFront(_input.front); + return _input.front.moveFront(); } } static if (hasAssignableElements!RangeType) { - @property auto front(ElementType val) + @property void front(ElementType val) { _input.front.front = val; } @@ -4851,7 +5981,7 @@ struct FrontTransversal(Ror, /// Ditto void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront an empty FrontTransversal"); _input.popFront(); prime(); } @@ -4877,13 +6007,13 @@ struct FrontTransversal(Ror, */ @property auto ref back() { - assert(!empty); + assert(!empty, "Attempting to fetch the back of an empty FrontTransversal"); return _input.back.front; } /// Ditto void popBack() { - assert(!empty); + assert(!empty, "Attempting to popBack an empty FrontTransversal"); _input.popBack(); prime(); } @@ -4893,13 +6023,13 @@ struct FrontTransversal(Ror, { ElementType moveBack() { - return .moveFront(_input.back); + return _input.back.moveFront(); } } static if (hasAssignableElements!RangeType) { - @property auto back(ElementType val) + @property void back(ElementType val) { _input.back.front = val; } @@ -4926,7 +6056,7 @@ struct FrontTransversal(Ror, { ElementType moveAt(size_t n) { - return .moveFront(_input[n]); + return _input[n].moveFront(); } } /// Ditto @@ -4937,6 +6067,16 @@ struct FrontTransversal(Ror, _input[n].front = val; } } + /// Ditto + static if (hasLength!RangeOfRanges) + { + @property size_t length() + { + return _input.length; + } + + alias opDollar = length; + } /** Slicing if offered if $(D RangeOfRanges) supports slicing and all the @@ -4946,7 +6086,7 @@ struct FrontTransversal(Ror, { typeof(this) opSlice(size_t lower, size_t upper) { - return typeof(this)(_input[lower..upper]); + return typeof(this)(_input[lower .. upper]); } } } @@ -4969,7 +6109,7 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal( /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[][] x = new int[][2]; x[0] = [1, 2]; x[1] = [3, 4]; @@ -4979,30 +6119,33 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal( @safe unittest { - import std.internal.test.dummyrange; - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges, DummyRange, ReturnBy; static assert(is(FrontTransversal!(immutable int[][]))); - foreach(DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { auto dummies = [DummyType.init, DummyType.init, DummyType.init, DummyType.init]; - foreach(i, ref elem; dummies) { + foreach (i, ref elem; dummies) + { // Just violate the DummyRange abstraction to get what I want. elem.arr = elem.arr[i..$ - (3 - i)]; } auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(dummies); - static if (isForwardRange!DummyType) { + static if (isForwardRange!DummyType) + { static assert(isForwardRange!(typeof(ft))); } assert(equal(ft, [1, 2, 3, 4])); // Test slicing. - assert(equal(ft[0..2], [1, 2])); - assert(equal(ft[1..3], [2, 3])); + assert(equal(ft[0 .. 2], [1, 2])); + assert(equal(ft[1 .. 3], [2, 3])); assert(ft.front == ft.moveFront()); assert(ft.back == ft.moveBack()); @@ -5012,7 +6155,8 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal( // Test infiniteness propagation. static assert(isInfinite!(typeof(frontTransversal(repeat("foo"))))); - static if (DummyType.r == ReturnBy.Reference) { + static if (DummyType.r == ReturnBy.Reference) + { { ft.front++; scope(exit) ft.front--; @@ -5040,10 +6184,41 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal( } } +// Issue 16363 +@safe unittest +{ + import std.algorithm.comparison : equal; + + int[][] darr = [[0, 1], [4, 5]]; + auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(darr); + + assert(equal(ft, [0, 4])); + static assert(isRandomAccessRange!(typeof(ft))); +} + +// Bugzilla 16442 +@safe unittest +{ + int[][] arr = [[], []]; + + auto ft1 = frontTransversal!(TransverseOptions.assumeNotJagged)(arr); + assert(ft1.empty); + + auto ft2 = frontTransversal!(TransverseOptions.enforceNotJagged)(arr); + assert(ft2.empty); +} + /** - Given a range of ranges, iterate transversally through the the $(D - n)th element of each of the enclosed ranges. All elements of the - enclosing range must offer random access. + Given a range of ranges, iterate transversally through the + `n`th element of each of the enclosed ranges. + + Params: + opt = Controls the assumptions the function makes about the lengths + of the ranges + rr = An input range of random access ranges + Returns: + At minimum, an input range. Range primitives such as bidirectionality + and random access are given if the element type of `rr` provides them. */ struct Transversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged) @@ -5109,7 +6284,7 @@ struct Transversal(Ror, /// Ditto @property auto ref front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty Transversal"); return _input.front[_n]; } @@ -5118,14 +6293,14 @@ struct Transversal(Ror, { E moveFront() { - return .moveAt(_input.front, _n); + return _input.front.moveAt(_n); } } /// Ditto static if (hasAssignableElements!InnerRange) { - @property auto front(E val) + @property void front(E val) { _input.front[_n] = val; } @@ -5135,7 +6310,7 @@ struct Transversal(Ror, /// Ditto void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront an empty Transversal"); _input.popFront(); prime(); } @@ -5159,13 +6334,14 @@ struct Transversal(Ror, */ @property auto ref back() { + assert(!empty, "Attempting to fetch the back of an empty Transversal"); return _input.back[_n]; } /// Ditto void popBack() { - assert(!empty); + assert(!empty, "Attempting to popBack an empty Transversal"); _input.popBack(); prime(); } @@ -5175,14 +6351,14 @@ struct Transversal(Ror, { E moveBack() { - return .moveAt(_input.back, _n); + return _input.back.moveAt(_n); } } /// Ditto static if (hasAssignableElements!InnerRange) { - @property auto back(E val) + @property void back(E val) { _input.back[_n] = val; } @@ -5210,7 +6386,7 @@ struct Transversal(Ror, { E moveAt(size_t n) { - return .moveAt(_input[n], _n); + return _input[n].moveAt(_n); } } @@ -5224,7 +6400,7 @@ struct Transversal(Ror, } /// Ditto - static if(hasLength!RangeOfRanges) + static if (hasLength!RangeOfRanges) { @property size_t length() { @@ -5242,7 +6418,7 @@ struct Transversal(Ror, { typeof(this) opSlice(size_t lower, size_t upper) { - return typeof(this)(_input[lower..upper], _n); + return typeof(this)(_input[lower .. upper], _n); } } } @@ -5265,7 +6441,7 @@ Transversal!(RangeOfRanges, opt) transversal /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[][] x = new int[][2]; x[0] = [1, 2]; x[1] = [3, 4]; @@ -5275,7 +6451,7 @@ Transversal!(RangeOfRanges, opt) transversal @safe unittest { - import std.internal.test.dummyrange; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; int[][] x = new int[][2]; x[0] = [ 1, 2 ]; @@ -5317,7 +6493,8 @@ Transversal!(RangeOfRanges, opt) transversal // Test w/o ref return. alias D = DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random); auto drs = [D.init, D.init]; - foreach(num; 0..10) { + foreach (num; 0 .. 10) + { auto t = transversal!(TransverseOptions.enforceNotJagged)(drs, num); assert(t[0] == t[1]); assert(t[1] == num + 1); @@ -5327,15 +6504,15 @@ Transversal!(RangeOfRanges, opt) transversal // Test slicing. auto mat = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]; - auto mat1 = transversal!(TransverseOptions.assumeNotJagged)(mat, 1)[1..3]; + auto mat1 = transversal!(TransverseOptions.assumeNotJagged)(mat, 1)[1 .. 3]; assert(mat1[0] == 6); assert(mat1[1] == 10); } struct Transposed(RangeOfRanges) - if (isForwardRange!RangeOfRanges && - isInputRange!(ElementType!RangeOfRanges) && - hasAssignableElements!RangeOfRanges) +if (isForwardRange!RangeOfRanges && + isInputRange!(ElementType!RangeOfRanges) && + hasAssignableElements!RangeOfRanges) { //alias ElementType = typeof(map!"a.front"(RangeOfRanges.init)); @@ -5346,7 +6523,7 @@ struct Transposed(RangeOfRanges) @property auto front() { - import std.algorithm : filter, map; + import std.algorithm.iteration : filter, map; return _input.save .filter!(a => !a.empty) .map!(a => a.front); @@ -5403,9 +6580,9 @@ private: } // Issue 9507 -unittest +@safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto r = [[1,2], [3], [4,5], [], [6]]; assert(r.transposed.equal!equal([ @@ -5419,17 +6596,17 @@ Given a range of ranges, returns a range of ranges where the $(I i)'th subrange contains the $(I i)'th elements of the original subranges. */ Transposed!RangeOfRanges transposed(RangeOfRanges)(RangeOfRanges rr) - if (isForwardRange!RangeOfRanges && - isInputRange!(ElementType!RangeOfRanges) && - hasAssignableElements!RangeOfRanges) +if (isForwardRange!RangeOfRanges && + isInputRange!(ElementType!RangeOfRanges) && + hasAssignableElements!RangeOfRanges) { return Transposed!RangeOfRanges(rr); } -/// Example +/// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[][] ror = [ [1, 2, 3], [4, 5, 6] @@ -5461,7 +6638,7 @@ Transposed!RangeOfRanges transposed(RangeOfRanges)(RangeOfRanges rr) // Issue 8764 @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; ulong[1] t0 = [ 123 ]; assert(!hasAssignableElements!(typeof(t0[].chunks(1)))); @@ -5483,8 +6660,8 @@ bidirectional or random-access if $(D Indices) is bidirectional or random-access, respectively. */ struct Indexed(Source, Indices) - if(isRandomAccessRange!Source && isInputRange!Indices && - is(typeof(Source.init[ElementType!(Indices).init]))) +if (isRandomAccessRange!Source && isInputRange!Indices && + is(typeof(Source.init[ElementType!(Indices).init]))) { this(Source source, Indices indices) { @@ -5495,18 +6672,18 @@ struct Indexed(Source, Indices) /// Range primitives @property auto ref front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty Indexed"); return _source[_indices.front]; } /// Ditto void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront an empty Indexed"); _indices.popFront(); } - static if(isInfinite!Indices) + static if (isInfinite!Indices) { enum bool empty = false; } @@ -5519,7 +6696,7 @@ struct Indexed(Source, Indices) } } - static if(isForwardRange!Indices) + static if (isForwardRange!Indices) { /// Ditto @property typeof(this) save() @@ -5530,7 +6707,7 @@ struct Indexed(Source, Indices) } /// Ditto - static if(hasAssignableElements!Source) + static if (hasAssignableElements!Source) { @property auto ref front(ElementType!Source newVal) { @@ -5540,34 +6717,34 @@ struct Indexed(Source, Indices) } - static if(hasMobileElements!Source) + static if (hasMobileElements!Source) { /// Ditto auto moveFront() { assert(!empty); - return .moveAt(_source, _indices.front); + return _source.moveAt(_indices.front); } } - static if(isBidirectionalRange!Indices) + static if (isBidirectionalRange!Indices) { /// Ditto @property auto ref back() { - assert(!empty); + assert(!empty, "Attempting to fetch the back of an empty Indexed"); return _source[_indices.back]; } /// Ditto void popBack() { - assert(!empty); + assert(!empty, "Attempting to popBack an empty Indexed"); _indices.popBack(); } /// Ditto - static if(hasAssignableElements!Source) + static if (hasAssignableElements!Source) { @property auto ref back(ElementType!Source newVal) { @@ -5577,18 +6754,18 @@ struct Indexed(Source, Indices) } - static if(hasMobileElements!Source) + static if (hasMobileElements!Source) { /// Ditto auto moveBack() { assert(!empty); - return .moveAt(_source, _indices.back); + return _source.moveAt(_indices.back); } } } - static if(hasLength!Indices) + static if (hasLength!Indices) { /// Ditto @property size_t length() @@ -5599,7 +6776,7 @@ struct Indexed(Source, Indices) alias opDollar = length; } - static if(isRandomAccessRange!Indices) + static if (isRandomAccessRange!Indices) { /// Ditto auto ref opIndex(size_t index) @@ -5607,14 +6784,17 @@ struct Indexed(Source, Indices) return _source[_indices[index]]; } - /// Ditto - typeof(this) opSlice(size_t a, size_t b) + static if (hasSlicing!Indices) { - return typeof(this)(_source, _indices[a..b]); + /// Ditto + typeof(this) opSlice(size_t a, size_t b) + { + return typeof(this)(_source, _indices[a .. b]); + } } - static if(hasAssignableElements!Source) + static if (hasAssignableElements!Source) { /// Ditto auto opIndexAssign(ElementType!Source newVal, size_t index) @@ -5624,12 +6804,12 @@ struct Indexed(Source, Indices) } - static if(hasMobileElements!Source) + static if (hasMobileElements!Source) { /// Ditto auto moveAt(size_t index) { - return .moveAt(_source, _indices[index]); + return _source.moveAt(_indices[index]); } } } @@ -5653,23 +6833,24 @@ struct Indexed(Source, Indices) return _indices; } - static if(isRandomAccessRange!Indices) + static if (isRandomAccessRange!Indices) { /** Returns the physical index into the source range corresponding to a given logical index. This is useful, for example, when indexing an $(D Indexed) without adding another layer of indirection. - - Examples: - --- - auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); - assert(ind.physicalIndex(0) == 1); - --- */ size_t physicalIndex(size_t logicalIndex) { return _indices[logicalIndex]; } + + /// + @safe unittest + { + auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); + assert(ind.physicalIndex(0) == 1); + } } private: @@ -5687,7 +6868,7 @@ Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indice /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto source = [1, 2, 3, 4, 5]; auto indices = [4, 3, 1, 2, 0, 4]; auto ind = indexed(source, indices); @@ -5715,9 +6896,10 @@ Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indice @safe unittest { - import std.internal.test.dummyrange; + import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, + propagatesRangeType, RangeType; - foreach(DummyType; AllDummyRanges) + foreach (DummyType; AllDummyRanges) { auto d = DummyType.init; auto r = indexed([1, 2, 3, 4, 5], d); @@ -5728,14 +6910,24 @@ Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indice /** This range iterates over fixed-sized chunks of size $(D chunkSize) of a -$(D source) range. $(D Source) must be a forward range. +$(D source) range. $(D Source) must be a forward range. $(D chunkSize) must be +greater than zero. If $(D !isInfinite!Source) and $(D source.walkLength) is not evenly divisible by $(D chunkSize), the back element of this range will contain fewer than $(D chunkSize) elements. + +Params: + r = Range from which the chunks will be selected + chunkSize = Chunk size + +See_Also: $(LREF slide) + +Returns: Forward range of all chunks with propagated bidirectionality, + conditional random access and slicing. */ struct Chunks(Source) - if (isForwardRange!Source) +if (isForwardRange!Source) { /// Standard constructor this(Source source, size_t chunkSize) @@ -5748,14 +6940,14 @@ struct Chunks(Source) /// Forward range primitives. Always present. @property auto front() { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty Chunks"); return _source.save.take(_chunkSize); } /// Ditto void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront and empty Chunks"); _source.popFrontN(_chunkSize); } @@ -5809,7 +7001,7 @@ struct Chunks(Source) return _source[start .. end]; else { - import std.algorithm : min; + import std.algorithm.comparison : min; immutable len = _source.length; assert(start < len, "chunks index out of bounds"); return _source[start .. min(end, len)]; @@ -5820,7 +7012,7 @@ struct Chunks(Source) static if (hasLength!Source) typeof(this) opSlice(size_t lower, size_t upper) { - import std.algorithm : min; + import std.algorithm.comparison : min; assert(lower <= upper && upper <= length, "chunks slicing index out of bounds"); immutable len = _source.length; return chunks(_source[min(lower * _chunkSize, len) .. min(upper * _chunkSize, len)], _chunkSize); @@ -5885,7 +7077,7 @@ struct Chunks(Source) } typeof(this) opSlice(size_t lower, DollarToken) { - import std.algorithm : min; + import std.algorithm.comparison : min; assert(lower <= length, "chunks slicing index out of bounds"); static if (hasSliceToEnd) return chunks(_source[min(lower * _chunkSize, _source.length) .. $], _chunkSize); @@ -5942,7 +7134,7 @@ if (isForwardRange!Source) /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; auto chunks = chunks(source, 4); assert(chunks[0] == [1, 2, 3, 4]); @@ -5954,77 +7146,1246 @@ if (isForwardRange!Source) assert(equal(retro(array(chunks)), array(retro(chunks)))); } -@safe unittest +@safe unittest +{ + auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + auto chunks = chunks(source, 4); + auto chunks2 = chunks.save; + chunks.popFront(); + assert(chunks[0] == [5, 6, 7, 8]); + assert(chunks[1] == [9, 10]); + chunks2.popBack(); + assert(chunks2[1] == [5, 6, 7, 8]); + assert(chunks2.length == 2); + + static assert(isRandomAccessRange!(typeof(chunks))); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + + //Extra toying with slicing and indexing. + auto chunks1 = [0, 0, 1, 1, 2, 2, 3, 3, 4].chunks(2); + auto chunks2 = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].chunks(2); + + assert(chunks1.length == 5); + assert(chunks2.length == 5); + assert(chunks1[4] == [4]); + assert(chunks2[4] == [4, 4]); + assert(chunks1.back == [4]); + assert(chunks2.back == [4, 4]); + + assert(chunks1[0 .. 1].equal([[0, 0]])); + assert(chunks1[0 .. 2].equal([[0, 0], [1, 1]])); + assert(chunks1[4 .. 5].equal([[4]])); + assert(chunks2[4 .. 5].equal([[4, 4]])); + + assert(chunks1[0 .. 0].equal((int[][]).init)); + assert(chunks1[5 .. 5].equal((int[][]).init)); + assert(chunks2[5 .. 5].equal((int[][]).init)); + + //Fun with opDollar + assert(chunks1[$ .. $].equal((int[][]).init)); //Quick + assert(chunks2[$ .. $].equal((int[][]).init)); //Quick + assert(chunks1[$ - 1 .. $].equal([[4]])); //Semiquick + assert(chunks2[$ - 1 .. $].equal([[4, 4]])); //Semiquick + assert(chunks1[$ .. 5].equal((int[][]).init)); //Semiquick + assert(chunks2[$ .. 5].equal((int[][]).init)); //Semiquick + + assert(chunks1[$ / 2 .. $ - 1].equal([[2, 2], [3, 3]])); //Slow +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; + + //ForwardRange + auto r = filter!"true"([1, 2, 3, 4, 5]).chunks(2); + assert(equal!"equal(a, b)"(r, [[1, 2], [3, 4], [5]])); + + //InfiniteRange w/o RA + auto fibsByPairs = recurrence!"a[n-1] + a[n-2]"(1, 1).chunks(2); + assert(equal!`equal(a, b)`(fibsByPairs.take(2), [[ 1, 1], [ 2, 3]])); + + //InfiniteRange w/ RA and slicing + auto odds = sequence!("a[0] + n * a[1]")(1, 2); + auto oddsByPairs = odds.chunks(2); + assert(equal!`equal(a, b)`(oddsByPairs.take(2), [[ 1, 3], [ 5, 7]])); + + //Requires phobos#991 for Sequence to have slice to end + static assert(hasSlicing!(typeof(odds))); + assert(equal!`equal(a, b)`(oddsByPairs[3 .. 5], [[13, 15], [17, 19]])); + assert(equal!`equal(a, b)`(oddsByPairs[3 .. $].take(2), [[13, 15], [17, 19]])); +} + + + +/** +This range splits a $(D source) range into $(D chunkCount) chunks of +approximately equal length. $(D Source) must be a forward range with +known length. + +Unlike $(LREF chunks), $(D evenChunks) takes a chunk count (not size). +The returned range will contain zero or more $(D source.length / +chunkCount + 1) elements followed by $(D source.length / chunkCount) +elements. If $(D source.length < chunkCount), some chunks will be empty. + +$(D chunkCount) must not be zero, unless $(D source) is also empty. +*/ +struct EvenChunks(Source) +if (isForwardRange!Source && hasLength!Source) +{ + /// Standard constructor + this(Source source, size_t chunkCount) + { + assert(chunkCount != 0 || source.empty, "Cannot create EvenChunks with a zero chunkCount"); + _source = source; + _chunkCount = chunkCount; + } + + /// Forward range primitives. Always present. + @property auto front() + { + assert(!empty, "Attempting to fetch the front of an empty evenChunks"); + return _source.save.take(_chunkPos(1)); + } + + /// Ditto + void popFront() + { + assert(!empty, "Attempting to popFront an empty evenChunks"); + _source.popFrontN(_chunkPos(1)); + _chunkCount--; + } + + /// Ditto + @property bool empty() + { + return _source.empty; + } + + /// Ditto + @property typeof(this) save() + { + return typeof(this)(_source.save, _chunkCount); + } + + /// Length + @property size_t length() const + { + return _chunkCount; + } + //Note: No point in defining opDollar here without slicing. + //opDollar is defined below in the hasSlicing!Source section + + static if (hasSlicing!Source) + { + /** + Indexing, slicing and bidirectional operations and range primitives. + Provided only if $(D hasSlicing!Source) is $(D true). + */ + auto opIndex(size_t index) + { + assert(index < _chunkCount, "evenChunks index out of bounds"); + return _source[_chunkPos(index) .. _chunkPos(index+1)]; + } + + /// Ditto + typeof(this) opSlice(size_t lower, size_t upper) + { + assert(lower <= upper && upper <= length, "evenChunks slicing index out of bounds"); + return evenChunks(_source[_chunkPos(lower) .. _chunkPos(upper)], upper - lower); + } + + /// Ditto + @property auto back() + { + assert(!empty, "back called on empty evenChunks"); + return _source[_chunkPos(_chunkCount - 1) .. _source.length]; + } + + /// Ditto + void popBack() + { + assert(!empty, "popBack() called on empty evenChunks"); + _source = _source[0 .. _chunkPos(_chunkCount - 1)]; + _chunkCount--; + } + } + +private: + Source _source; + size_t _chunkCount; + + size_t _chunkPos(size_t i) + { + /* + _chunkCount = 5, _source.length = 13: + + chunk0 + | chunk3 + | | + v v + +-+-+-+-+-+ ^ + |0|3|.| | | | + +-+-+-+-+-+ | div + |1|4|.| | | | + +-+-+-+-+-+ v + |2|5|.| + +-+-+-+ + + <-----> + mod + + <---------> + _chunkCount + + One column is one chunk. + popFront and popBack pop the left-most + and right-most column, respectively. + */ + + auto div = _source.length / _chunkCount; + auto mod = _source.length % _chunkCount; + auto pos = i <= mod + ? i * (div+1) + : mod * (div+1) + (i-mod) * div + ; + //auto len = i < mod + // ? div+1 + // : div + //; + return pos; + } +} + +/// Ditto +EvenChunks!Source evenChunks(Source)(Source source, size_t chunkCount) +if (isForwardRange!Source && hasLength!Source) +{ + return typeof(return)(source, chunkCount); +} + +/// +@safe unittest +{ + import std.algorithm.comparison : equal; + auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + auto chunks = evenChunks(source, 3); + assert(chunks[0] == [1, 2, 3, 4]); + assert(chunks[1] == [5, 6, 7]); + assert(chunks[2] == [8, 9, 10]); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + + auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + auto chunks = evenChunks(source, 3); + assert(chunks.back == chunks[2]); + assert(chunks.front == chunks[0]); + assert(chunks.length == 3); + assert(equal(retro(array(chunks)), array(retro(chunks)))); + + auto chunks2 = chunks.save; + chunks.popFront(); + assert(chunks[0] == [5, 6, 7]); + assert(chunks[1] == [8, 9, 10]); + chunks2.popBack(); + assert(chunks2[1] == [5, 6, 7]); + assert(chunks2.length == 2); + + static assert(isRandomAccessRange!(typeof(chunks))); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + + int[] source = []; + auto chunks = source.evenChunks(0); + assert(chunks.length == 0); + chunks = source.evenChunks(3); + assert(equal(chunks, [[], [], []])); + chunks = [1, 2, 3].evenChunks(5); + assert(equal(chunks, [[1], [2], [3], [], []])); +} + +/** +A fixed-sized sliding window iteration +of size `windowSize` over a `source` range by a custom `stepSize`. + +The `Source` range must be at least an `ForwardRange` and +the `windowSize` must be greater than zero. + +For `windowSize = 1` it splits the range into single element groups (aka `unflatten`) +For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. + +Params: + f = If `Yes.withFewerElements` slide with fewer + elements than `windowSize`. This can only happen if the initial range + contains less elements than `windowSize`. In this case + if `No.withFewerElements` an empty range will be returned. + r = Range from which the slide will be selected + windowSize = Sliding window size + stepSize = Steps between the windows (by default 1) + +Returns: Range of all sliding windows with propagated bi-directionality, + forwarding, conditional random access, and slicing. + +See_Also: $(LREF chunks) +*/ +auto slide(Flag!"withFewerElements" f = Yes.withFewerElements, + Source)(Source source, size_t windowSize, size_t stepSize = 1) + if (isForwardRange!Source) +{ + return Slides!(f, Source)(source, windowSize, stepSize); +} + +private struct Slides(Flag!"withFewerElements" withFewerElements = Yes.withFewerElements, Source) + if (isForwardRange!Source) +{ +private: + Source _source; + size_t _windowSize; + size_t _stepSize; + + static if (hasLength!Source) + { + enum needsEndTracker = false; + } + else + { + // if there's no information about the length, track needs to be kept manually + Source _nextSource; + enum needsEndTracker = true; + } + + bool _empty; + + static if (hasSlicing!Source) + { + enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); + } + +public: + /// Standard constructor + this(Source source, size_t windowSize, size_t stepSize) + { + assert(windowSize > 0, "windowSize must be greater than zero"); + assert(stepSize > 0, "stepSize must be greater than zero"); + _source = source; + _windowSize = windowSize; + _stepSize = stepSize; + + static if (needsEndTracker) + { + // _nextSource is used to "look into the future" and check for the end + _nextSource = source.save; + _nextSource.popFrontN(windowSize); + } + + static if (!withFewerElements) + { + // empty source range is needed, s.t. length, slicing etc. works properly + static if (needsEndTracker) + { + if (_nextSource.empty) + _source = _nextSource; + } + else + { + if (_source.length < windowSize) + { + static if (hasSlicing!Source) + { + // if possible use the faster opDollar overload + static if (hasSliceToEnd) + _source = _source[$ .. $]; + else + _source = _source[_source.length .. _source.length]; + } + else + { + _source.popFrontN(_source.length); + } + } + } + } + + _empty = _source.empty; + } + + /// Forward range primitives. Always present. + @property auto front() + { + assert(!empty, "Attempting to access front on an empty slide"); + static if (hasSlicing!Source && hasLength!Source) + { + import std.algorithm.comparison : min; + return _source[0 .. min(_windowSize, _source.length)]; + } + else + { + return _source.save.take(_windowSize); + } + } + + /// Ditto + void popFront() + { + assert(!empty, "Attempting to call popFront() on an empty slide"); + _source.popFrontN(_stepSize); + + // if the range has less elements than its window size, + // we have seen the last full window (i.e. its empty) + static if (needsEndTracker) + { + if (_nextSource.empty) + _empty = true; + else + _nextSource.popFrontN(_stepSize); + } + else + { + if (_source.length < _windowSize) + _empty = true; + } + } + + static if (!isInfinite!Source) + { + /// Ditto + @property bool empty() const + { + return _empty; + } + } + else + { + // undocumented + enum empty = false; + } + + /// Ditto + @property typeof(this) save() + { + return typeof(this)(_source.save, _windowSize, _stepSize); + } + + static if (hasLength!Source) + { + /// Length. Only if $(D hasLength!Source) is $(D true) + @property size_t length() + { + if (_source.length < _windowSize) + { + static if (withFewerElements) + return 1; + else + return 0; + } + else + { + return (_source.length - _windowSize + _stepSize) / _stepSize; + } + } + } + + static if (hasSlicing!Source) + { + /** + Indexing and slicing operations. Provided only if + `hasSlicing!Source` is `true`. + */ + auto opIndex(size_t index) + { + immutable start = index * _stepSize; + + static if (isInfinite!Source) + { + immutable end = start + _windowSize; + } + else + { + import std.algorithm.comparison : min; + + immutable len = _source.length; + assert(start < len, "slide index out of bounds"); + immutable end = min(start + _windowSize, len); + } + + return _source[start .. end]; + } + + static if (!isInfinite!Source) + { + /// ditto + typeof(this) opSlice(size_t lower, size_t upper) + { + import std.algorithm.comparison : min; + assert(lower <= upper && upper <= length, "slide slicing index out of bounds"); + + lower *= _stepSize; + upper *= _stepSize; + + immutable len = _source.length; + + /* + * Notice that we only need to move for windowSize - 1 to the right: + * source = [0, 1, 2, 3] (length: 4) + * - source.slide(2) -> s = [[0, 1], [1, 2], [2, 3]] + * right pos for s[0..3]: 3 (upper) + 2 (windowSize) - 1 = 4 + * + * - source.slide(3) -> s = [[0, 1, 2], [1, 2, 3]] + * right pos for s[0..2]: 2 (upper) + 3 (windowSize) - 1 = 4 + * + * source = [0, 1, 2, 3, 4] (length: 5) + * - source.slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] + * right pos for s[0..2]: 2 (upper) + 4 (windowSize) - 1 = 5 + */ + return typeof(this) + (_source[min(lower, len) .. min(upper + _windowSize - 1, len)], + _windowSize, _stepSize); + } + } + else static if (hasSliceToEnd) + { + //For slicing an infinite chunk, we need to slice the source to the infinite end. + auto opSlice(size_t lower, size_t upper) + { + assert(lower <= upper, "slide slicing index out of bounds"); + return typeof(this)(_source[lower * _stepSize .. $], + _windowSize, _stepSize).takeExactly(upper - lower); + } + } + + static if (isInfinite!Source) + { + static if (hasSliceToEnd) + { + private static struct DollarToken{} + DollarToken opDollar() + { + return DollarToken(); + } + //Slice to dollar + typeof(this) opSlice(size_t lower, DollarToken) + { + return typeof(this)(_source[lower * _stepSize .. $], _windowSize, _stepSize); + } + } + } + else + { + //Dollar token carries a static type, with no extra information. + //It can lazily transform into _source.length on algorithmic + //operations such as : slide[$/2, $-1]; + private static struct DollarToken + { + private size_t _length; + alias _length this; + } + + DollarToken opDollar() + { + return DollarToken(this.length); + } + + // Optimized slice overloads optimized for using dollar. + typeof(this) opSlice(DollarToken, DollarToken) + { + static if (hasSliceToEnd) + { + return typeof(this)(_source[$ .. $], _windowSize, _stepSize); + } + else + { + immutable len = _source.length; + return typeof(this)(_source[len .. len], _windowSize, _stepSize); + } + } + + // Optimized slice overloads optimized for using dollar. + typeof(this) opSlice(size_t lower, DollarToken) + { + import std.algorithm.comparison : min; + assert(lower <= length, "slide slicing index out of bounds"); + lower *= _stepSize; + static if (hasSliceToEnd) + { + return typeof(this)(_source[min(lower, _source.length) .. $], _windowSize, _stepSize); + } + else + { + immutable len = _source.length; + return typeof(this)(_source[min(lower, len) .. len], _windowSize, _stepSize); + } + } + + // Optimized slice overloads optimized for using dollar. + typeof(this) opSlice(DollarToken, size_t upper) + { + assert(upper == length, "slide slicing index out of bounds"); + return this[$ .. $]; + } + } + + // Bidirectional range primitives + static if (!isInfinite!Source) + { + /** + Bidirectional range primitives. Provided only if both + `hasSlicing!Source` and `!isInfinite!Source` are `true`. + */ + @property auto back() + { + import std.algorithm.comparison : max; + + assert(!empty, "Attempting to access front on an empty slide"); + + immutable len = _source.length; + /* + * Note: + * - `end` in the following is the exclusive end as used in opSlice + * - For the trivial case with `stepSize = 1` `end` is at `len`: + * + * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3] (end = 4) + * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) + * + * - For the non-trivial cases, we need to calculate the gap + * between `len` and `end` - this is the number of missing elements + * from the input range: + * + * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || 6 + * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || 6 + * iota(7).slide(1, 5) = [[0], [5]] || 6 + * + * As it can be seen `gap` can be at most `stepSize - 1` + * More generally the elements of the sliding window with + * `w = windowSize` and `s = stepSize` are: + * + * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w] + * + * We can thus calculate the gap between the `end` and `len` as: + * + * gap = len - (n * s + w) = len - w - (n * s) + * + * As we aren't interested in exact value of `n`, but the best + * minimal `gap` value, we can use modulo to "cut" `len - w` optimally: + * + * gap = len - w - (s - s ... - s) = (len - w) % s + * + * So for example: + * + * iota(7).slide(2, 3) = [[0, 1], [3, 4]] + * gap: (7 - 2) % 3 = 5 % 3 = 2 + * end: 7 - 2 = 5 + * + * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] + * gap: (7 - 4) % 2 = 3 % 2 = 1 + * end: 7 - 1 = 6 + */ + size_t gap = (len - _windowSize) % _stepSize; + + // check for underflow + immutable start = (len > _windowSize + gap) ? len - _windowSize - gap : 0; + + return _source[start .. len - gap]; + } + + /// Ditto + void popBack() + { + assert(!empty, "Attempting to call popBack() on an empty slide"); + + immutable end = _source.length > _stepSize ? _source.length - _stepSize : 0; + _source = _source[0 .. end]; + + if (_source.length < _windowSize) + _empty = true; + } + } + } +} + +/// +@safe pure nothrow unittest +{ + import std.array : array; + import std.algorithm.comparison : equal; + + assert([0, 1, 2, 3].slide(2).equal!equal( + [[0, 1], [1, 2], [2, 3]] + )); + assert(5.iota.slide(3).equal!equal( + [[0, 1, 2], [1, 2, 3], [2, 3, 4]] + )); + + assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); + assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + + // set a custom stepsize (default 1) + assert(6.iota.slide(1, 2).equal!equal( + [[0], [2], [4]] + )); + + assert(6.iota.slide(2, 4).equal!equal( + [[0, 1], [4, 5]] + )); + + // allow slide with less elements than the window size + assert(3.iota.slide!(No.withFewerElements)(4).empty); + assert(3.iota.slide!(Yes.withFewerElements)(4).equal!equal( + [[0, 1, 2]] + )); +} + +/// count k-mers +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : each; + + int[dstring] d; + "AGAGA"d.slide(2).each!(a => d[a]++); + assert(d == ["AG"d: 2, "GA"d: 2]); +} + +// @nogc +@safe pure nothrow @nogc unittest +{ + import std.algorithm.comparison : equal; + + static immutable res1 = [[0], [1], [2], [3]]; + assert(4.iota.slide(1).equal!equal(res1)); + + static immutable res2 = [[0, 1], [1, 2], [2, 3]]; + assert(4.iota.slide(2).equal!equal(res2)); +} + +// different window sizes +@safe pure nothrow unittest +{ + import std.array : array; + import std.algorithm.comparison : equal; + + assert([0, 1, 2, 3].slide(1).array == [[0], [1], [2], [3]]); + assert([0, 1, 2, 3].slide(2).array == [[0, 1], [1, 2], [2, 3]]); + assert([0, 1, 2, 3].slide(3).array == [[0, 1, 2], [1, 2, 3]]); + assert([0, 1, 2, 3].slide(4).array == [[0, 1, 2, 3]]); + assert([0, 1, 2, 3].slide(5).array == [[0, 1, 2, 3]]); + + + assert(iota(2).slide(2).front.equal([0, 1])); + assert(iota(3).slide(2).equal!equal([[0, 1],[1, 2]])); + assert(iota(3).slide(3).equal!equal([[0, 1, 2]])); + assert(iota(3).slide(4).equal!equal([[0, 1, 2]])); + assert(iota(1, 4).slide(1).equal!equal([[1], [2], [3]])); + assert(iota(1, 4).slide(3).equal!equal([[1, 2, 3]])); +} + +unittest +{ + import std.algorithm.comparison : equal; + + assert(6.iota.slide(1, 1).equal!equal( + [[0], [1], [2], [3], [4], [5]] + )); + assert(6.iota.slide(1, 2).equal!equal( + [[0], [2], [4]] + )); + assert(6.iota.slide(1, 3).equal!equal( + [[0], [3]] + )); + assert(6.iota.slide(1, 4).equal!equal( + [[0], [4]] + )); + assert(6.iota.slide(1, 5).equal!equal( + [[0], [5]] + )); + assert(6.iota.slide(2, 1).equal!equal( + [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]] + )); + assert(6.iota.slide(2, 2).equal!equal( + [[0, 1], [2, 3], [4, 5]] + )); + assert(6.iota.slide(2, 3).equal!equal( + [[0, 1], [3, 4]] + )); + assert(6.iota.slide(2, 4).equal!equal( + [[0, 1], [4, 5]] + )); + assert(6.iota.slide(2, 5).equal!equal( + [[0, 1]] + )); + assert(6.iota.slide(3, 1).equal!equal( + [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]] + )); + assert(6.iota.slide(3, 2).equal!equal( + [[0, 1, 2], [2, 3, 4]] + )); + assert(6.iota.slide(3, 3).equal!equal( + [[0, 1, 2], [3, 4, 5]] + )); + assert(6.iota.slide(3, 4).equal!equal( + [[0, 1, 2]] + )); + assert(6.iota.slide(4, 1).equal!equal( + [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] + )); + assert(6.iota.slide(4, 2).equal!equal( + [[0, 1, 2, 3], [2, 3, 4, 5]] + )); + assert(6.iota.slide(4, 3).equal!equal( + [[0, 1, 2, 3]] + )); + assert(6.iota.slide(5, 1).equal!equal( + [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]] + )); + assert(6.iota.slide(5, 2).equal!equal( + [[0, 1, 2, 3, 4]] + )); + assert(6.iota.slide(5, 3).equal!equal( + [[0, 1, 2, 3, 4]] + )); +} + +// emptyness, copyability, strings +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : each, map; + + // check with empty input + int[] d; + assert(d.slide(2).empty); + assert(d.slide(2, 2).empty); + + // is copyable? + auto e = iota(5).slide(2); + e.popFront; + assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); + assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); + assert(e.map!"a.array".array == [[1, 2], [2, 3], [3, 4]]); + + // test with strings + int[dstring] f; + "AGAGA"d.slide(3).each!(a => f[a]++); + assert(f == ["AGA"d: 2, "GAG"d: 1]); + + int[dstring] g; + "ABCDEFG"d.slide(3, 3).each!(a => g[a]++); + assert(g == ["ABC"d:1, "DEF"d:1]); +} + +// test slicing, length +@safe pure nothrow unittest { - auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - auto chunks = chunks(source, 4); - auto chunks2 = chunks.save; - chunks.popFront(); - assert(chunks[0] == [5, 6, 7, 8]); - assert(chunks[1] == [9, 10]); - chunks2.popBack(); - assert(chunks2[1] == [5, 6, 7, 8]); - assert(chunks2.length == 2); + import std.array : array; + import std.algorithm.comparison : equal; - static assert(isRandomAccessRange!(typeof(chunks))); + // test index + assert(iota(3).slide(4)[0].equal([0, 1, 2])); + assert(iota(5).slide(4)[1].equal([1, 2, 3, 4])); + assert(iota(3).slide(4, 2)[0].equal([0, 1, 2])); + assert(iota(5).slide(4, 2)[1].equal([2, 3, 4])); + assert(iota(3).slide(4, 3)[0].equal([0, 1, 2])); + assert(iota(5).slide(4, 3)[1].equal([3, 4,])); + + // test slicing + assert(iota(3).slide(4)[0 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slide(2)[1 .. $].equal!equal([[1, 2]])); + assert(iota(1, 5).slide(2)[0 .. 1].equal!equal([[1, 2]])); + assert(iota(1, 5).slide(2)[0 .. 2].equal!equal([[1, 2], [2, 3]])); + assert(iota(1, 5).slide(3)[0 .. 1].equal!equal([[1, 2, 3]])); + assert(iota(1, 5).slide(3)[0 .. 2].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(iota(1, 6).slide(3)[2 .. 3].equal!equal([[3, 4, 5]])); + assert(iota(1, 5).slide(4)[0 .. 1].equal!equal([[1, 2, 3, 4]])); + + // length + assert(iota(3).slide(1).length == 3); + assert(iota(3).slide(1, 2).length == 2); + assert(iota(3).slide(1, 3).length == 1); + assert(iota(3).slide(1, 4).length == 1); + assert(iota(3).slide(2).length == 2); + assert(iota(3).slide(2, 2).length == 1); + assert(iota(3).slide(2, 3).length == 1); + assert(iota(3).slide(3).length == 1); + assert(iota(3).slide(3, 2).length == 1); + + // opDollar + assert(iota(3).slide(4)[$/2 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slide(4)[$ .. $].empty); + assert(iota(3).slide(4)[$ .. 1].empty); + + assert(iota(5).slide(3, 1)[$/2 .. $].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(iota(5).slide(3, 2)[$/2 .. $].equal!equal([[2, 3, 4]])); + assert(iota(5).slide(3, 3)[$/2 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slide(4, 3)[$ .. $].empty); + assert(iota(3).slide(4, 3)[$ .. 1].empty); +} + +// test No.withFewerElements +@safe pure nothrow unittest +{ + assert(iota(3).slide(4).length == 1); + assert(iota(3).slide(4, 4).length == 1); + + assert(iota(3).slide!(No.withFewerElements)(4).empty); + assert(iota(3, 3).slide!(No.withFewerElements)(4).empty); + assert(iota(3).slide!(No.withFewerElements)(4).length == 0); + assert(iota(3).slide!(No.withFewerElements)(4, 4).length == 0); + + assert(iota(3).slide!(No.withFewerElements)(400).empty); + assert(iota(3).slide!(No.withFewerElements)(400).length == 0); + assert(iota(3).slide!(No.withFewerElements)(400, 10).length == 0); + + assert(iota(3).slide!(No.withFewerElements)(4)[0 .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[$ .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[$ .. 0].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[$/2 .. $].empty); + + // with different step sizes + assert(iota(3).slide!(No.withFewerElements)(4, 5)[0 .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 6)[$ .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 7)[$ .. 0].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 8)[$/2 .. $].empty); +} + +// test with infinite ranges +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + // InfiniteRange without RandomAccess + auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); + assert(fibs.slide(2).take(2).equal!equal([[1, 1], [1, 2]])); + assert(fibs.slide(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); + + // InfiniteRange with RandomAccess and slicing + auto odds = sequence!("a[0] + n * a[1]")(1, 2); + auto oddsByPairs = odds.slide(2); + assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]])); + assert(oddsByPairs[1].equal([3, 5])); + assert(oddsByPairs[4].equal([9, 11])); + + static assert(hasSlicing!(typeof(odds))); + assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]])); + assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]])); + + auto oddsWithGaps = odds.slide(2, 4); + assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]])); + assert(oddsWithGaps[2].equal([17, 19])); + assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]])); + assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]])); } -@safe unittest +// test reverse +@safe pure nothrow unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; - //Extra toying with slicing and indexing. - auto chunks1 = [0, 0, 1, 1, 2, 2, 3, 3, 4].chunks(2); - auto chunks2 = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].chunks(2); + auto e = iota(3).slide(2); + assert(e.retro.equal!equal([[1, 2], [0, 1]])); + assert(e.retro.array.equal(e.array.retro)); - assert (chunks1.length == 5); - assert (chunks2.length == 5); - assert (chunks1[4] == [4]); - assert (chunks2[4] == [4, 4]); - assert (chunks1.back == [4]); - assert (chunks2.back == [4, 4]); + auto e2 = iota(5).slide(3); + assert(e2.retro.equal!equal([[2, 3, 4], [1, 2, 3], [0, 1, 2]])); + assert(e2.retro.array.equal(e2.array.retro)); - assert (chunks1[0 .. 1].equal([[0, 0]])); - assert (chunks1[0 .. 2].equal([[0, 0], [1, 1]])); - assert (chunks1[4 .. 5].equal([[4]])); - assert (chunks2[4 .. 5].equal([[4, 4]])); + auto e3 = iota(3).slide(4); + assert(e3.retro.equal!equal([[0, 1, 2]])); + assert(e3.retro.array.equal(e3.array.retro)); +} - assert (chunks1[0 .. 0].equal((int[][]).init)); - assert (chunks1[5 .. 5].equal((int[][]).init)); - assert (chunks2[5 .. 5].equal((int[][]).init)); +// test reverse with different steps +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; - //Fun with opDollar - assert (chunks1[$ .. $].equal((int[][]).init)); //Quick - assert (chunks2[$ .. $].equal((int[][]).init)); //Quick - assert (chunks1[$ - 1 .. $].equal([[4]])); //Semiquick - assert (chunks2[$ - 1 .. $].equal([[4, 4]])); //Semiquick - assert (chunks1[$ .. 5].equal((int[][]).init)); //Semiquick - assert (chunks2[$ .. 5].equal((int[][]).init)); //Semiquick + assert(iota(7).slide(2, 1).retro.equal!equal( + [[5, 6], [4, 5], [3, 4], [2, 3], [1, 2], [0, 1]] + )); + assert(iota(7).slide(2, 2).retro.equal!equal( + [[4, 5], [2, 3], [0, 1]] + )); + assert(iota(7).slide(2, 3).retro.equal!equal( + [[3, 4], [0, 1]] + )); + assert(iota(7).slide(2, 4).retro.equal!equal( + [[4, 5], [0, 1]] + )); + assert(iota(7).slide(2, 5).retro.equal!equal( + [[5, 6], [0, 1]] + )); + assert(iota(7).slide(3, 1).retro.equal!equal( + [[4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] + )); + assert(iota(7).slide(3, 2).retro.equal!equal( + [[4, 5, 6], [2, 3, 4], [0, 1, 2]] + )); + assert(iota(7).slide(4, 1).retro.equal!equal( + [[3, 4, 5, 6], [2, 3, 4, 5], [1, 2, 3, 4], [0, 1, 2, 3]] + )); + assert(iota(7).slide(4, 2).retro.equal!equal( + [[2, 3, 4, 5], [0, 1, 2, 3]] + )); + assert(iota(7).slide(4, 3).retro.equal!equal( + [[3, 4, 5, 6], [0, 1, 2, 3]] + )); + assert(iota(7).slide(4, 4).retro.equal!equal( + [[0, 1, 2, 3]] + )); + assert(iota(7).slide(5, 1).retro.equal!equal( + [[2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [0, 1, 2, 3, 4]] + )); + assert(iota(7).slide(5, 2).retro.equal!equal( + [[2, 3, 4, 5, 6], [0, 1, 2, 3, 4]] + )); + assert(iota(7).slide(5, 3).retro.equal!equal( + [[0, 1, 2, 3, 4]] + )); + assert(iota(7).slide(5, 4).retro.equal!equal( + [[0, 1, 2, 3, 4]] + )); +} + +// step size +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; - assert (chunks1[$ / 2 .. $ - 1].equal([[2, 2], [3, 3]])); //Slow + assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); + assert(iota(8).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); + assert(iota(9).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); + assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + assert(iota(13).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); } -unittest +// test with dummy ranges +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy, AllDummyRanges; + import std.meta : AliasSeq; + + alias AllForwardDummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional), + //DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), + //DummyRange!(ReturnBy.Value, Length.No, RangeType.Input), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional) + ); + + foreach (Range; AliasSeq!AllForwardDummyRanges) + { + Range r; + assert(r.slide(1).equal!equal( + [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] + )); + assert(r.slide(2).equal!equal( + [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] + )); + assert(r.slide(3).equal!equal( + [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], + [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] + )); + assert(r.slide(6).equal!equal( + [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], + [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]] + )); + assert(r.slide(15).equal!equal( + [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] + )); + + assert(r.slide!(No.withFewerElements)(15).empty); + } + + alias BackwardsDummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), + ); + + foreach (Range; AliasSeq!BackwardsDummyRanges) + { + Range r; + assert(r.slide(1).retro.equal!equal( + [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]] + )); + assert(r.slide(2).retro.equal!equal( + [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]] + )); + assert(r.slide(5).retro.equal!equal( + [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8], + [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]] + )); + assert(r.slide(15).retro.equal!equal( + [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] + )); + + // different step sizes + assert(r.slide(2, 4)[2].equal([9, 10])); + assert(r.slide(2, 1).equal!equal( + [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] + )); + assert(r.slide(2, 2).equal!equal( + [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] + )); + assert(r.slide(2, 3).equal!equal( + [[1, 2], [4, 5], [7, 8]] + )); + assert(r.slide(2, 4).equal!equal( + [[1, 2], [5, 6], [9, 10]] + )); + + // front = back + foreach (windowSize; 1..10) + foreach (stepSize; 1..10) + { + auto slider = r.slide(windowSize, stepSize); + assert(slider.retro.retro.equal!equal(slider)); + } + } + + assert(iota(1, 12).slide(2, 4)[0..3].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[0..$].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[$/2..$].equal!equal([[5, 6], [9, 10]])); + + // reverse + assert(iota(1, 12).slide(2, 4).retro.equal!equal([[9, 10], [5, 6], [1, 2]])); +} + +// test different sliceable ranges +@safe pure nothrow unittest { - import std.algorithm : equal, filter; + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; + import std.meta : AliasSeq; - //ForwardRange - auto r = filter!"true"([1, 2, 3, 4, 5]).chunks(2); - assert(equal!"equal(a, b)"(r, [[1, 2], [3, 4], [5]])); + struct SliceableRange(Range, Flag!"withOpDollar" withOpDollar = No.withOpDollar, + Flag!"withInfiniteness" withInfiniteness = No.withInfiniteness) + { + Range arr = 10.iota.array; // similar to DummyRange + @property auto save() { return typeof(this)(arr); } + @property auto front() { return arr[0]; } + void popFront() { arr.popFront(); } + auto opSlice(size_t i, size_t j) + { + // subslices can't be infinite + return SliceableRange!(Range, withOpDollar, No.withInfiniteness)(arr[i .. j]); + } - //InfiniteRange w/o RA - auto fibsByPairs = recurrence!"a[n-1] + a[n-2]"(1, 1).chunks(2); - assert(equal!`equal(a, b)`(fibsByPairs.take(2), [[ 1, 1], [ 2, 3]])); + static if (withInfiniteness) + { + enum empty = false; + } + else + { + @property bool empty() { return arr.empty; } + @property auto length() { return arr.length; } + } - //InfiniteRange w/ RA and slicing - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - auto oddsByPairs = odds.chunks(2); - assert(equal!`equal(a, b)`(oddsByPairs.take(2), [[ 1, 3], [ 5, 7]])); + static if (withOpDollar) + { + static if (withInfiniteness) + { + struct Dollar {} + Dollar opDollar() const { return Dollar.init; } - //Requires phobos#991 for Sequence to have slice to end - static assert(hasSlicing!(typeof(odds))); - assert(equal!`equal(a, b)`(oddsByPairs[3 .. 5], [[13, 15], [17, 19]])); - assert(equal!`equal(a, b)`(oddsByPairs[3 .. $].take(2), [[13, 15], [17, 19]])); + //Slice to dollar + typeof(this) opSlice(size_t lower, Dollar) + { + return typeof(this)(arr[lower .. $]); + } + + } + else + { + alias opDollar = length; + } + } + } + + alias T = int[]; + + alias SliceableDummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T), + SliceableRange!(T, No.withOpDollar, No.withInfiniteness), + SliceableRange!(T, Yes.withOpDollar, No.withInfiniteness), + SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness), + ); + + foreach (Range; AliasSeq!SliceableDummyRanges) + { + Range r; + r.arr = 10.iota.array; // for clarity + + static assert (isForwardRange!Range); + enum hasSliceToEnd = hasSlicing!Range && is(typeof(Range.init[0 .. $]) == Range); + + assert(r.slide(2)[0].equal([0, 1])); + assert(r.slide(2)[1].equal([1, 2])); + + // saveable + auto s = r.slide(2); + assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); + s.save.popFront; + assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); + + assert(r.slide(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); + } + + alias SliceableDummyRangesWithoutInfinity = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T), + SliceableRange!(T, No.withOpDollar, No.withInfiniteness), + SliceableRange!(T, Yes.withOpDollar, No.withInfiniteness), + ); + + foreach (Range; AliasSeq!SliceableDummyRangesWithoutInfinity) + { + static assert (hasSlicing!Range); + static assert (hasLength!Range); + + Range r; + r.arr = 10.iota.array; // for clarity + + assert(r.slide!(No.withFewerElements)(6).equal!equal( + [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], + [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] + )); + assert(r.slide!(No.withFewerElements)(16).empty); + + assert(r.slide(4)[0 .. $].equal(r.slide(4))); + assert(r.slide(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); + assert(r.slide(2)[$ .. $].empty); + + assert(r.slide(3).retro.equal!equal( + [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] + )); + } + + // separate checks for infinity + auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]); + assert(infIndex.slide(2)[0].equal([0, 1])); + assert(infIndex.slide(2)[1].equal([1, 2])); + + auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)(); + assert(infDollar.slide(2)[1 .. $].front.equal([1, 2])); + assert(infDollar.slide(4)[0 .. $].front.equal([0, 1, 2, 3])); + assert(infDollar.slide(4)[2 .. $].front.equal([2, 3, 4, 5])); } private struct OnlyResult(T, size_t arity) @@ -6042,25 +8403,25 @@ private struct OnlyResult(T, size_t arity) T front() @property { - assert(!empty); + assert(!empty, "Attempting to fetch the front of an empty Only range"); return data[frontIndex]; } void popFront() { - assert(!empty); + assert(!empty, "Attempting to popFront an empty Only range"); ++frontIndex; } T back() @property { - assert(!empty); + assert(!empty, "Attempting to fetch the back of an empty Only range"); return data[backIndex - 1]; } void popBack() { - assert(!empty); + assert(!empty, "Attempting to popBack an empty Only range"); --backIndex; } @@ -6069,7 +8430,7 @@ private struct OnlyResult(T, size_t arity) return this; } - size_t length() @property + size_t length() const @property { return backIndex - frontIndex; } @@ -6078,15 +8439,9 @@ private struct OnlyResult(T, size_t arity) T opIndex(size_t idx) { - // data[i + idx] will not throw a RangeError // when i + idx points to elements popped // with popBack - version(assert) - { - import core.exception : RangeError; - if (idx >= length) - throw new RangeError; - } + assert(idx < length, "Attempting to fetch an out of bounds index from an Only range"); return data[frontIndex + idx]; } @@ -6100,13 +8455,14 @@ private struct OnlyResult(T, size_t arity) OnlyResult result = this; result.frontIndex += from; result.backIndex = this.frontIndex + to; - - version(assert) - { - import core.exception : RangeError; - if (to < from || to > length) - throw new RangeError; - } + assert( + from <= to, + "Attempting to slice an Only range with a larger first argument than the second." + ); + assert( + to <= length, + "Attempting to slice using an out of bounds index on an Only range" + ); return result; } @@ -6116,7 +8472,8 @@ private struct OnlyResult(T, size_t arity) // @@@BUG@@@ 10643 version(none) { - static if(hasElaborateAssign!T) + import std.traits : hasElaborateAssign; + static if (hasElaborateAssign!T) private T[arity] data; else private T[arity] data = void; @@ -6128,13 +8485,29 @@ private struct OnlyResult(T, size_t arity) // Specialize for single-element results private struct OnlyResult(T, size_t arity : 1) { - @property T front() { assert(!_empty); return _value; } - @property T back() { assert(!_empty); return _value; } + @property T front() + { + assert(!empty, "Attempting to fetch the front of an empty Only range"); + return _value; + } + @property T back() + { + assert(!empty, "Attempting to fetch the back of an empty Only range"); + return _value; + } @property bool empty() const { return _empty; } @property size_t length() const { return !_empty; } @property auto save() { return this; } - void popFront() { assert(!_empty); _empty = true; } - void popBack() { assert(!_empty); _empty = true; } + void popFront() + { + assert(!_empty, "Attempting to popFront an empty Only range"); + _empty = true; + } + void popBack() + { + assert(!_empty, "Attempting to popBack an empty Only range"); + _empty = true; + } alias opDollar = length; private this()(auto ref T value) @@ -6145,12 +8518,7 @@ private struct OnlyResult(T, size_t arity : 1) T opIndex(size_t i) { - version (assert) - { - import core.exception : RangeError; - if (_empty || i != 0) - throw new RangeError; - } + assert(!_empty && i == 0, "Attempting to fetch an out of bounds index from an Only range"); return _value; } @@ -6161,12 +8529,14 @@ private struct OnlyResult(T, size_t arity : 1) OnlyResult opSlice(size_t from, size_t to) { - version (assert) - { - import core.exception : RangeError; - if (from > to || to > length) - throw new RangeError; - } + assert( + from <= to, + "Attempting to slice an Only range with a larger first argument than the second." + ); + assert( + to <= length, + "Attempting to slice using an out of bounds index on an Only range" + ); OnlyResult copy = this; copy._empty = _empty || from == to; return copy; @@ -6182,7 +8552,7 @@ private struct OnlyResult(T, size_t arity : 0) private static struct EmptyElementType {} bool empty() @property { return true; } - size_t length() @property { return 0; } + size_t length() const @property { return 0; } alias opDollar = length; EmptyElementType front() @property { assert(false); } void popFront() { assert(false); } @@ -6192,11 +8562,6 @@ private struct OnlyResult(T, size_t arity : 0) EmptyElementType opIndex(size_t i) { - version(assert) - { - import core.exception : RangeError; - throw new RangeError; - } assert(false); } @@ -6204,12 +8569,7 @@ private struct OnlyResult(T, size_t arity : 0) OnlyResult opSlice(size_t from, size_t to) { - version(assert) - { - import core.exception : RangeError; - if (from != 0 || to != 0) - throw new RangeError; - } + assert(from == 0 && to == 0); return this; } } @@ -6225,9 +8585,17 @@ having to perform dynamic memory allocation. As copying the range means copying all elements, it can be safely returned from functions. For the same reason, copying the returned range may be expensive for a large number of arguments. + +Params: + values = the values to assemble together + +Returns: + A `RandomAccessRange` of the assembled values. + +See_Also: $(LREF chain) to chain ranges */ auto only(Values...)(auto ref Values values) - if(!is(CommonType!Values == void) || Values.length == 0) +if (!is(CommonType!Values == void) || Values.length == 0) { return OnlyResult!(CommonType!Values, Values.length)(values); } @@ -6235,8 +8603,10 @@ auto only(Values...)(auto ref Values values) /// @safe unittest { - import std.algorithm; - import std.uni; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter, joiner, map; + import std.algorithm.searching : findSplitBefore; + import std.uni : isUpper; assert(equal(only('♡'), "♡")); assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); @@ -6244,10 +8614,14 @@ auto only(Values...)(auto ref Values values) assert(only("one", "two", "three").joiner(" ").equal("one two three")); string title = "The D Programming Language"; - assert(filter!isUpper(title).map!only().join(".") == "T.D.P.L"); + assert(title + .filter!isUpper // take the upper case letters + .map!only // make each letter its own range + .joiner(".") // join the ranges together lazily + .equal("T.D.P.L")); } -unittest +@safe unittest { // Verify that the same common type and same arity // results in the same template instantiation @@ -6261,7 +8635,7 @@ unittest // Tests the zero-element result @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto emptyRange = only(); @@ -6283,7 +8657,7 @@ unittest // Tests the single-element result @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.typecons : tuple; foreach (x; tuple(1, '1', 1.0, "1", [1])) { @@ -6294,9 +8668,9 @@ unittest assert(!a.empty); assert(a.length == 1); assert(equal(a, a[])); - assert(equal(a, a[0..1])); - assert(equal(a[0..0], e)); - assert(equal(a[1..1], e)); + assert(equal(a, a[0 .. 1])); + assert(equal(a[0 .. 0], e)); + assert(equal(a[1 .. 1], e)); assert(a[0] == x); auto b = a.save; @@ -6323,28 +8697,30 @@ unittest assert(imm.init.empty); // Issue 13441 assert(imm.length == 1); assert(equal(imm, imm[])); - assert(equal(imm, imm[0..1])); - assert(equal(imm[0..0], imme)); - assert(equal(imm[1..1], imme)); + assert(equal(imm, imm[0 .. 1])); + assert(equal(imm[0 .. 0], imme)); + assert(equal(imm[1 .. 1], imme)); assert(imm[0] == 1); } // Tests multiple-element results @safe unittest { - import std.algorithm : equal, joiner; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : joiner; + import std.meta : AliasSeq; static assert(!__traits(compiles, only(1, "1"))); auto nums = only!(byte, uint, long)(1, 2, 3); static assert(is(ElementType!(typeof(nums)) == long)); assert(nums.length == 3); - foreach(i; 0 .. 3) + foreach (i; 0 .. 3) assert(nums[i] == i + 1); auto saved = nums.save; - foreach(i; 1 .. 4) + foreach (i; 1 .. 4) { assert(nums.front == nums[0]); assert(nums.front == i); @@ -6364,12 +8740,12 @@ unittest assert(saved[0 .. 0].empty); assert(saved[3 .. 3].empty); - alias data = TypeTuple!("one", "two", "three", "four"); + alias data = AliasSeq!("one", "two", "three", "four"); static joined = ["one two", "one two three", "one two three four"]; string[] joinedRange = joined; - foreach(argCount; TypeTuple!(2, 3, 4)) + foreach (argCount; AliasSeq!(2, 3, 4)) { auto values = only(data[0 .. argCount]); alias Values = typeof(values); @@ -6416,30 +8792,37 @@ unittest static struct Test { int* a; } immutable(Test) test; - cast(void)only(test, test); // Works with mutable indirection + cast(void) only(test, test); // Works with mutable indirection } /** -Iterate over $(D range) with an attached index variable. +Iterate over `range` with an attached index variable. -Each element is a $(XREF typecons, Tuple) containing the index +Each element is a $(REF Tuple, std,typecons) containing the index and the element, in that order, where the index member is named $(D index) -and the element member is named $(D value). - -The index starts at $(D start) and is incremented by one on every iteration. +and the element member is named `value`. -Bidirectionality is propagated only if $(D range) has length. +The index starts at `start` and is incremented by one on every iteration. Overflow: -If $(D range) has length, then it is an error to pass a value for $(D start) -so that $(D start + range.length) is bigger than $(D Enumerator.max), thus it is -ensured that overflow cannot happen. + If `range` has length, then it is an error to pass a value for `start` + so that `start + range.length` is bigger than `Enumerator.max`, thus + it is ensured that overflow cannot happen. -If $(D range) does not have length, and $(D popFront) is called when -$(D front.index == Enumerator.max), the index will overflow and -continue from $(D Enumerator.min). + If `range` does not have length, and `popFront` is called when + `front.index == Enumerator.max`, the index will overflow and + continue from `Enumerator.min`. -Examples: +Params: + range = the input range to attach indexes to + start = the number to start the index counter from + +Returns: + At minimum, an input range. All other range primitives are given in the + resulting range if `range` has them. The exceptions are the bidirectional + primitives, which are propagated only if `range` has length. + +Example: Useful for using $(D foreach) with an index loop variable: ---- import std.stdio : stdin, stdout; @@ -6450,7 +8833,7 @@ Useful for using $(D foreach) with an index loop variable: ---- */ auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0) - if (isIntegral!Enumerator && isInputRange!Range) +if (isIntegral!Enumerator && isInputRange!Range) in { static if (hasLength!Range) @@ -6458,32 +8841,31 @@ in // TODO: core.checkedint supports mixed signedness yet? import core.checkedint : adds, addu; import std.conv : ConvException, to; - import core.exception : RangeError; + import std.traits : isSigned, Largest, Signed; alias LengthType = typeof(range.length); bool overflow; - static if(isSigned!Enumerator && isSigned!LengthType) + static if (isSigned!Enumerator && isSigned!LengthType) auto result = adds(start, range.length, overflow); - else static if(isSigned!Enumerator) + else static if (isSigned!Enumerator) { Largest!(Enumerator, Signed!LengthType) signedLength; try signedLength = to!(typeof(signedLength))(range.length); - catch(ConvException) + catch (ConvException) overflow = true; - catch(Exception) + catch (Exception) assert(false); auto result = adds(start, signedLength, overflow); } else { - static if(isSigned!LengthType) + static if (isSigned!LengthType) assert(range.length >= 0); auto result = addu(start, range.length, overflow); } - if (overflow || result > Enumerator.max) - throw new RangeError("overflow in `start + range.length`"); + assert(!overflow && result <= Enumerator.max); } } body @@ -6501,7 +8883,7 @@ body public: ElemType front() @property { - assert(!range.empty); + assert(!range.empty, "Attempting to fetch the front of an empty enumerate"); return typeof(return)(index, range.front); } @@ -6517,7 +8899,7 @@ body void popFront() { - assert(!range.empty); + assert(!range.empty, "Attempting to popFront an empty enumerate"); range.popFront(); ++index; // When !hasLength!Range, overflow is expected } @@ -6543,13 +8925,13 @@ body { ElemType back() @property { - assert(!range.empty); + assert(!range.empty, "Attempting to fetch the back of an empty enumerate"); return typeof(return)(cast(Enumerator)(index + range.length - 1), range.back); } void popBack() { - assert(!range.empty); + assert(!range.empty, "Attempting to popBack an empty enumerate"); range.popBack(); } } @@ -6607,8 +8989,8 @@ pure @safe nothrow unittest pure @safe nothrow unittest { - import std.internal.test.dummyrange; - + import std.internal.test.dummyrange : AllDummyRanges; + import std.meta : AliasSeq; import std.typecons : tuple; static struct HasSlicing @@ -6623,7 +9005,7 @@ pure @safe nothrow unittest } } - foreach (DummyType; TypeTuple!(AllDummyRanges, HasSlicing)) + foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing)) { alias R = typeof(enumerate(DummyType.init)); static assert(isInputRange!R); @@ -6675,7 +9057,7 @@ pure @safe nothrow unittest assert(enumerated[0 .. $].front == tuple(0, "zero")); assert(enumerated[$ - 1 .. $].front == tuple(3, "three")); - foreach(i; 0 .. 10) + foreach (i; 0 .. 10) { auto shifted = values[0 .. 2].enumerate(i); assert(shifted.front == tuple(i, "zero")); @@ -6689,7 +9071,7 @@ pure @safe nothrow unittest assert(shifted.empty); } - foreach(T; TypeTuple!(ubyte, byte, uint, int)) + foreach (T; AliasSeq!(ubyte, byte, uint, int)) { auto inf = 42.repeat().enumerate(T.max); alias Inf = typeof(inf); @@ -6714,17 +9096,18 @@ pure @safe nothrow unittest pure @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.meta : AliasSeq; static immutable int[] values = [0, 1, 2, 3, 4]; - foreach(T; TypeTuple!(ubyte, ushort, uint, ulong)) + foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) { auto enumerated = values.enumerate!T(); static assert(is(typeof(enumerated.front.index) == T)); assert(enumerated.equal(values[].zip(values))); - foreach(T i; 0 .. 5) + foreach (T i; 0 .. 5) { - auto subset = values[cast(size_t)i .. $]; + auto subset = values[cast(size_t) i .. $]; auto offsetEnumerated = subset.enumerate(i); static assert(is(typeof(enumerated.front.index) == T)); assert(offsetEnumerated.equal(subset.zip(subset))); @@ -6735,10 +9118,11 @@ pure @safe unittest version(none) // @@@BUG@@@ 10939 { // Re-enable (or remove) if 10939 is resolved. - /+pure+/ unittest // Impure because of std.conv.to + /+pure+/ @safe unittest // Impure because of std.conv.to { import core.exception : RangeError; import std.exception : assertNotThrown, assertThrown; + import std.meta : AliasSeq; static immutable values = [42]; @@ -6757,7 +9141,7 @@ version(none) // @@@BUG@@@ 10939 } SignedLengthRange svalues; - foreach(Enumerator; TypeTuple!(ubyte, byte, ushort, short, uint, int, ulong, long)) + foreach (Enumerator; AliasSeq!(ubyte, byte, ushort, short, uint, int, ulong, long)) { assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max)); assertNotThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length)); @@ -6768,7 +9152,7 @@ version(none) // @@@BUG@@@ 10939 assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length + 1)); } - foreach(Enumerator; TypeTuple!(byte, short, int)) + foreach (Enumerator; AliasSeq!(byte, short, int)) { assertThrown!RangeError(repeat(0, uint.max).enumerate!Enumerator()); } @@ -6824,7 +9208,8 @@ enum SearchPolicy trot, /** - Performs a $(LUCKY galloping search algorithm), i.e. searches + Performs a $(LINK2 https://en.wikipedia.org/wiki/Exponential_search, + galloping search algorithm), i.e. searches with a step that doubles every time, (1, 2, 4, 8, ...) leading to an exponential search schedule (indexes tried are 0, 1, 3, 7, 15, 31, 63,...) Once the search overshoots its target, the @@ -6861,16 +9246,16 @@ enum SearchPolicy Represents a sorted range. In addition to the regular range primitives, supports additional operations that take advantage of the ordering, such as merge and binary search. To obtain a $(D -SortedRange) from an unsorted range $(D r), use $(XREF algorithm, -sort) which sorts $(D r) in place and returns the corresponding $(D -SortedRange). To construct a $(D SortedRange) from a range $(D r) that -is known to be already sorted, use $(LREF assumeSorted) described +SortedRange) from an unsorted range $(D r), use +$(REF sort, std,algorithm,sorting) which sorts $(D r) in place and returns the +corresponding $(D SortedRange). To construct a $(D SortedRange) from a range +$(D r) that is known to be already sorted, use $(LREF assumeSorted) described below. */ struct SortedRange(Range, alias pred = "a < b") if (isInputRange!Range) { - private import std.functional : binaryFun; + import std.functional : binaryFun; private alias predFun = binaryFun!pred; private bool geq(L, R)(L lhs, R rhs) @@ -6899,32 +9284,23 @@ if (isInputRange!Range) // Assertion only. private void dbgVerifySorted() { - if(!__ctfe) + if (!__ctfe) debug { - import core.bitop : bsr; - import std.conv : text; - import std.random : MinstdRand, uniform; - - static if (isRandomAccessRange!Range) + static if (isRandomAccessRange!Range && hasLength!Range) { - import std.algorithm : isSorted; + import core.bitop : bsr; + import std.algorithm.sorting : isSorted; + // Check the sortedness of the input if (this._input.length < 2) return; + immutable size_t msb = bsr(this._input.length) + 1; assert(msb > 0 && msb <= this._input.length); immutable step = this._input.length / msb; - static MinstdRand gen; - immutable start = uniform(0, step, gen); auto st = stride(this._input, step); - static if (is(typeof(text(st)))) - { - assert(isSorted!pred(st), text(st)); - } - else - { - assert(isSorted!pred(st)); - } + + assert(isSorted!pred(st), "Range is not sorted"); } } } @@ -6983,7 +9359,10 @@ if (isInputRange!Range) static if (hasSlicing!Range) auto opSlice(size_t a, size_t b) { - assert(a <= b); + assert( + a <= b, + "Attempting to slice a SortedRange with a larger first argument than the second." + ); typeof(this) result = this; result._input = _input[a .. b];// skip checking return result; @@ -7004,7 +9383,7 @@ if (isInputRange!Range) */ auto release() { - import std.algorithm : move; + import std.algorithm.mutation : move; return move(_input); } @@ -7012,7 +9391,7 @@ if (isInputRange!Range) // of the range and then 1 for the rest, returns the index at // which the first 1 appears. Used internally by the search routines. private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range) + if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range && hasLength!Range) { size_t first = 0, count = _input.length; while (count > 0) @@ -7104,14 +9483,7 @@ if (isInputRange!Range) the range with elements strictly smaller than $(D value)). The search schedule and its complexity are documented in $(LREF SearchPolicy). See also STL's - $(WEB sgi.com/tech/stl/lower_bound.html, lower_bound). - - Example: - ---- - auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); - auto p = a.lowerBound(4); - assert(equal(p, [ 0, 1, 2, 3 ])); - ---- + $(HTTP sgi.com/tech/stl/lower_bound.html, lower_bound). */ auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V) @@ -7120,6 +9492,15 @@ if (isInputRange!Range) return this[0 .. getTransitionIndex!(sp, geq)(value)]; } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); + auto p = a.lowerBound(4); + assert(equal(p, [ 0, 1, 2, 3 ])); + } + // upperBound /** This function searches with policy $(D sp) to find the largest right @@ -7133,14 +9514,7 @@ is the only policy allowed (and it must be specified explicitly lest it exposes user code to unexpected inefficiencies). For random-access searches, all policies are allowed, and $(D SearchPolicy.binarySearch) is the default. -See_Also: STL's $(WEB sgi.com/tech/stl/lower_bound.html,upper_bound). - -Example: ----- -auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); -auto p = a.upperBound(3); -assert(equal(p, [4, 4, 5, 6])); ----- +See_Also: STL's $(HTTP sgi.com/tech/stl/lower_bound.html,upper_bound). */ auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V)) @@ -7162,6 +9536,16 @@ assert(equal(p, [4, 4, 5, 6])); } } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); + auto p = a.upperBound(3); + assert(equal(p, [4, 4, 5, 6])); + } + + // equalRange /** Returns the subrange containing all elements $(D e) for which both $(D @@ -7174,14 +9558,7 @@ assert(equal(p, [4, 4, 5, 6])); policies are justified by the fact that the two boundaries are likely to be near the first found value (i.e., equal ranges are relatively small). Completes the entire search in $(BIGOH log(n)) time. See also - STL's $(WEB sgi.com/tech/stl/equal_range.html, equal_range). - - Example: - ---- - auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; - auto r = equalRange(a, 3); - assert(equal(r, [ 3, 3, 3 ])); - ---- + STL's $(HTTP sgi.com/tech/stl/equal_range.html, equal_range). */ auto equalRange(V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V) @@ -7222,6 +9599,15 @@ assert(equal(p, [4, 4, 5, 6])); return this.init; } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; + auto r = a.assumeSorted.equalRange(3); + assert(equal(r, [ 3, 3, 3 ])); + } + // trisect /** Returns a tuple $(D r) such that $(D r[0]) is the same as the result @@ -7230,19 +9616,10 @@ equalRange(value)), and $(D r[2]) is the same as the result of $(D upperBound(value)). The call is faster than computing all three separately. Uses a search schedule similar to $(D equalRange). Completes the entire search in $(BIGOH log(n)) time. - -Example: ----- -auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; -auto r = assumeSorted(a).trisect(3); -assert(equal(r[0], [ 1, 2 ])); -assert(equal(r[1], [ 3, 3, 3 ])); -assert(equal(r[2], [ 4, 4, 5, 6 ])); ----- */ auto trisect(V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V) - && isRandomAccessRange!Range) + && isRandomAccessRange!Range && hasLength!Range) { import std.typecons : tuple; size_t first = 0, count = _input.length; @@ -7282,39 +9659,32 @@ assert(equal(r[2], [ 4, 4, 5, 6 ])); return tuple(this[0 .. first], this.init, this[first .. length]); } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; + auto r = assumeSorted(a).trisect(3); + assert(equal(r[0], [ 1, 2 ])); + assert(equal(r[1], [ 3, 3, 3 ])); + assert(equal(r[2], [ 4, 4, 5, 6 ])); + } + // contains /** Returns $(D true) if and only if $(D value) can be found in $(D range), which is assumed to be sorted. Performs $(BIGOH log(r.length)) -evaluations of $(D pred). See also STL's $(WEB +evaluations of $(D pred). See also STL's $(HTTP sgi.com/tech/stl/binary_search.html, binary_search). */ bool contains(V)(V value) if (isRandomAccessRange!Range) { - size_t first = 0, count = _input.length; - while (count > 0) - { - immutable step = count / 2, it = first + step; - if (predFun(_input[it], value)) - { - // Less than value, bump left bound up - first = it + 1; - count -= step + 1; - } - else if (predFun(value, _input[it])) - { - // Greater than value, chop count - count = step; - } - else - { - // Found!!! - return true; - } - } - return false; + if (empty) return false; + immutable i = getTransitionIndex!(SearchPolicy.binarySearch, geq)(value); + if (i >= length) return false; + return !predFun(value, _input[i]); } // groupBy @@ -7330,9 +9700,9 @@ sorting relation. } /// -unittest +@safe unittest { - import std.algorithm : sort; + import std.algorithm.sorting : sort; auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.contains(3)); @@ -7350,11 +9720,11 @@ $(D SortedRange) is currently restricted to random-access ranges. No copy of the original range is ever made. If the underlying range is changed concurrently with its corresponding $(D SortedRange) in ways -that break its sortedness, $(D SortedRange) will work erratically. +that break its sorted-ness, $(D SortedRange) will work erratically. */ @safe unittest { - import std.algorithm : swap; + import std.algorithm.mutation : swap; auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.contains(42)); @@ -7364,7 +9734,7 @@ that break its sortedness, $(D SortedRange) will work erratically. @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ]; auto r = assumeSorted(a).trisect(30); @@ -7380,7 +9750,7 @@ that break its sortedness, $(D SortedRange) will work erratically. @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; auto a = [ "A", "AG", "B", "E", "F" ]; auto r = assumeSorted!"cmp(a,b) < 0"(a).trisect("B"w); assert(equal(r[0], [ "A", "AG" ])); @@ -7394,7 +9764,7 @@ that break its sortedness, $(D SortedRange) will work erratically. @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; static void test(SearchPolicy pol)() { auto a = [ 1, 2, 3, 42, 52, 64 ]; @@ -7442,7 +9812,7 @@ that break its sortedness, $(D SortedRange) will work erratically. @safe unittest { - import std.algorithm : swap; + import std.algorithm.mutation : swap; auto a = [ 1, 2, 3, 42, 52, 64 ]; auto r = assumeSorted(a); assert(r.contains(42)); @@ -7456,7 +9826,7 @@ that break its sortedness, $(D SortedRange) will work erratically. auto s = assumeSorted(arr); } -unittest +@system unittest { import std.algorithm.comparison : equal; int[] arr = [100, 101, 102, 200, 201, 300]; @@ -7465,9 +9835,13 @@ unittest } // Test on an input range -unittest +@system unittest { - import std.stdio, std.file, std.path, std.conv, std.uuid; + import std.conv : text; + import std.file : exists, remove, tempDir; + import std.path : buildPath; + import std.stdio : File; + import std.uuid : randomUUID; auto name = buildPath(tempDir(), "test.std.range.line-" ~ text(__LINE__) ~ "." ~ randomUUID().toString()); auto f = File(name, "w"); @@ -7486,14 +9860,14 @@ unittest Assumes $(D r) is sorted by predicate $(D pred) and returns the corresponding $(D SortedRange!(pred, R)) having $(D r) as support. To keep the checking costs low, the cost is $(BIGOH 1) in release mode -(no checks for sortedness are performed). In debug mode, a few random -elements of $(D r) are checked for sortedness. The size of the sample +(no checks for sorted-ness are performed). In debug mode, a few random +elements of $(D r) are checked for sorted-ness. The size of the sample is proportional $(BIGOH log(r.length)). That way, checking has no effect on the complexity of subsequent operations specific to sorted ranges (such as binary search). The probability of an arbitrary unsorted range failing the test is very high (however, an -almost-sorted range is likely to pass it). To check for sortedness at -cost $(BIGOH n), use $(XREF algorithm,isSorted). +almost-sorted range is likely to pass it). To check for sorted-ness at +cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). */ auto assumeSorted(alias pred = "a < b", R)(R r) if (isInputRange!(Unqual!R)) @@ -7503,7 +9877,7 @@ if (isInputRange!(Unqual!R)) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; static assert(isRandomAccessRange!(SortedRange!(int[]))); int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; auto p = assumeSorted(a).lowerBound(4); @@ -7518,7 +9892,7 @@ if (isInputRange!(Unqual!R)) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; auto p = assumeSorted(a).upperBound(3); assert(equal(p, [4, 4, 5, 6 ])); @@ -7528,8 +9902,8 @@ if (isInputRange!(Unqual!R)) @safe unittest { + import std.algorithm.comparison : equal; import std.conv : text; - import std.algorithm : equal; int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; auto p = assumeSorted(a).equalRange(3); @@ -7567,7 +9941,7 @@ if (isInputRange!(Unqual!R)) r = assumeSorted(a); } -unittest +@system unittest { bool ok = true; try @@ -7581,6 +9955,12 @@ unittest assert(ok); } +// issue 15003 +@nogc @safe unittest +{ + static immutable a = [1, 2, 3, 4]; + auto r = a.assumeSorted; +} /++ Wrapper which effectively makes it possible to pass a range by reference. @@ -7590,13 +9970,21 @@ unittest if it were passed to it, the original range is $(I not) copied but is consumed as if it were a reference type. - Note that $(D save) works as normal and operates on a new range, so if - $(D save) is ever called on the RefRange, then no operations on the saved - range will affect the original. + Note: + `save` works as normal and operates on a new _range, so if + `save` is ever called on the `RefRange`, then no operations on the + saved _range will affect the original. + + Params: + range = the range to construct the `RefRange` from + Returns: + A `RefRange`. If the given _range is a class type + (and thus is already a reference type), then the original + range is returned rather than a `RefRange`. +/ struct RefRange(R) - if(isForwardRange!R) +if (isInputRange!R) { public: @@ -7618,7 +10006,7 @@ public: +/ auto opAssign(RefRange rhs) { - if(_range && rhs._range) + if (_range && rhs._range) *_range = *rhs._range; else _range = rhs._range; @@ -7627,7 +10015,7 @@ public: } /++ +/ - auto opAssign(typeof(null) rhs) + void opAssign(typeof(null) rhs) { _range = null; } @@ -7658,12 +10046,12 @@ public: return (*_range).front; } - static if(is(typeof((*(cast(const R*)_range)).front))) @property ElementType!R front() const + static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const { return (*_range).front; } - static if(is(typeof((*_range).front = (*_range).front))) @property auto front(ElementType!R value) + static if (is(typeof((*_range).front = (*_range).front))) @property auto front(ElementType!R value) { return (*_range).front = value; } @@ -7675,7 +10063,7 @@ public: @property bool empty(); /// @property bool empty() const; ///Ditto } - else static if(isInfinite!R) + else static if (isInfinite!R) enum empty = false; else { @@ -7684,7 +10072,7 @@ public: return (*_range).empty; } - static if(is(typeof((*cast(const R*)_range).empty))) @property bool empty() const + static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const { return (*_range).empty; } @@ -7700,7 +10088,9 @@ public: version(StdDdoc) { - /++ +/ + /++ + Only defined if $(D isForwardRange!R) is $(D true). + +/ @property auto save() {assert(0);} /++ Ditto +/ @property auto save() const {assert(0);} @@ -7709,28 +10099,34 @@ public: /++ Ditto +/ auto opSlice() const {assert(0);} } - else + else static if (isForwardRange!R) { - static if(isSafe!((R* r) => (*r).save)) + import std.traits : isSafe; + private alias S = typeof((*_range).save); + + static if (is(typeof((*cast(const R*)_range).save))) + private alias CS = typeof((*cast(const R*)_range).save); + + static if (isSafe!((R* r) => (*r).save)) { - @property auto save() @trusted + @property RefRange!S save() @trusted { mixin(_genSave()); } - static if(is(typeof((*cast(const R*)_range).save))) @property auto save() @trusted const + static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const { mixin(_genSave()); } } else { - @property auto save() + @property RefRange!S save() { mixin(_genSave()); } - static if(is(typeof((*cast(const R*)_range).save))) @property auto save() const + static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const { mixin(_genSave()); } @@ -7748,12 +10144,12 @@ public: private static string _genSave() @safe pure nothrow { - return `import std.conv;` ~ + return `import std.conv : emplace;` ~ `alias S = typeof((*_range).save);` ~ `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ `auto mem = new void[S.sizeof];` ~ `emplace!S(mem, cast(S)(*_range).save);` ~ - `return RefRange!S(cast(S*)mem.ptr);`; + `return RefRange!S(cast(S*) mem.ptr);`; } static assert(isForwardRange!RefRange); @@ -7771,19 +10167,19 @@ public: /++ Ditto +/ @property auto back(ElementType!R value) {assert(0);} } - else static if(isBidirectionalRange!R) + else static if (isBidirectionalRange!R) { @property auto back() { return (*_range).back; } - static if(is(typeof((*(cast(const R*)_range)).back))) @property ElementType!R back() const + static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const { return (*_range).back; } - static if(is(typeof((*_range).back = (*_range).back))) @property auto back(ElementType!R value) + static if (is(typeof((*_range).back = (*_range).back))) @property auto back(ElementType!R value) { return (*_range).back = value; } @@ -7791,7 +10187,7 @@ public: /++ Ditto +/ - static if(isBidirectionalRange!R) void popBack() + static if (isBidirectionalRange!R) void popBack() { return (*_range).popBack(); } @@ -7807,16 +10203,16 @@ public: /++ Ditto +/ auto ref opIndex(IndexType)(IndexType index) const {assert(0);} } - else static if(isRandomAccessRange!R) + else static if (isRandomAccessRange!R) { auto ref opIndex(IndexType)(IndexType index) - if(is(typeof((*_range)[index]))) + if (is(typeof((*_range)[index]))) { return (*_range)[index]; } auto ref opIndex(IndexType)(IndexType index) const - if(is(typeof((*cast(const R*)_range)[index]))) + if (is(typeof((*cast(const R*)_range)[index]))) { return (*_range)[index]; } @@ -7827,7 +10223,7 @@ public: Only defined if $(D hasMobileElements!R) and $(D isForwardRange!R) are $(D true). +/ - static if(hasMobileElements!R && isForwardRange!R) auto moveFront() + static if (hasMobileElements!R && isForwardRange!R) auto moveFront() { return (*_range).moveFront(); } @@ -7837,7 +10233,7 @@ public: Only defined if $(D hasMobileElements!R) and $(D isBidirectionalRange!R) are $(D true). +/ - static if(hasMobileElements!R && isBidirectionalRange!R) auto moveBack() + static if (hasMobileElements!R && isBidirectionalRange!R) auto moveBack() { return (*_range).moveBack(); } @@ -7847,8 +10243,7 @@ public: Only defined if $(D hasMobileElements!R) and $(D isRandomAccessRange!R) are $(D true). +/ - static if(hasMobileElements!R && isRandomAccessRange!R) auto moveAt(IndexType)(IndexType index) - if(is(typeof((*_range).moveAt(index)))) + static if (hasMobileElements!R && isRandomAccessRange!R) auto moveAt(size_t index) { return (*_range).moveAt(index); } @@ -7863,18 +10258,23 @@ public: /++ Ditto +/ @property auto length() const {assert(0);} + + /++ Ditto +/ + alias opDollar = length; } - else static if(hasLength!R) + else static if (hasLength!R) { @property auto length() { return (*_range).length; } - static if(is(typeof((*cast(const R*)_range).length))) @property auto length() const + static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const { return (*_range).length; } + + alias opDollar = length; } @@ -7890,30 +10290,36 @@ public: auto opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) const {assert(0);} } - else static if(hasSlicing!R) + else static if (hasSlicing!R) { - auto opSlice(IndexType1, IndexType2) + private alias T = typeof((*_range)[1 .. 2]); + static if (is(typeof((*cast(const R*)_range)[1 .. 2]))) + { + private alias CT = typeof((*cast(const R*)_range)[1 .. 2]); + } + + RefRange!T opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) - if(is(typeof((*_range)[begin .. end]))) + if (is(typeof((*_range)[begin .. end]))) { mixin(_genOpSlice()); } - auto opSlice(IndexType1, IndexType2) + RefRange!CT opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) const - if(is(typeof((*cast(const R*)_range)[begin .. end]))) + if (is(typeof((*cast(const R*)_range)[begin .. end]))) { mixin(_genOpSlice()); } private static string _genOpSlice() @safe pure nothrow { - return `import std.conv;` ~ + return `import std.conv : emplace;` ~ `alias S = typeof((*_range)[begin .. end]);` ~ `static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~ `auto mem = new void[S.sizeof];` ~ `emplace!S(mem, cast(S)(*_range)[begin .. end]);` ~ - `return RefRange!S(cast(S*)mem.ptr);`; + `return RefRange!S(cast(S*) mem.ptr);`; } } @@ -7924,9 +10330,9 @@ private: } /// Basic Example -unittest +@system unittest { - import std.algorithm; + import std.algorithm.searching : find; ubyte[] buffer = [1, 9, 45, 12, 22]; auto found1 = find(buffer, 45); assert(found1 == [45, 12, 22]); @@ -7951,7 +10357,7 @@ unittest } /// opAssign Example. -unittest +@system unittest { ubyte[] buffer1 = [1, 2, 3, 4, 5]; ubyte[] buffer2 = [6, 7, 8, 9, 10]; @@ -7996,9 +10402,9 @@ unittest assert(buffer2 == [11, 12, 13, 14, 15]); } -unittest +@system unittest { - import std.algorithm; + import std.algorithm.iteration : filter; { ubyte[] buffer = [1, 2, 3, 4, 5]; auto wrapper = refRange(&buffer); @@ -8017,6 +10423,7 @@ unittest wrapper.moveAt(0); auto l = wrapper.length; auto sl = wrapper[0 .. 1]; + assert(wrapper[0 .. $].length == buffer[0 .. $].length); } { @@ -8070,10 +10477,19 @@ unittest auto b = wrapper.back; wrapper.popBack(); } + + { + // Issue 16534 - opDollar should be defined if the + // wrapped range defines length. + auto range = 10.iota.takeExactly(5); + auto wrapper = refRange(&range); + assert(wrapper.length == 5); + assert(wrapper[0 .. $ - 1].length == 4); + } } //Test assignment. -unittest +@system unittest { ubyte[] buffer1 = [1, 2, 3, 4, 5]; ubyte[] buffer2 = [6, 7, 8, 9, 10]; @@ -8100,9 +10516,12 @@ unittest assert(buffer2 == [6, 7, 8, 9, 10]); } -unittest +@system unittest { - import std.algorithm; + import std.algorithm.comparison : equal; + import std.algorithm.mutation : bringToFront; + import std.algorithm.searching : commonPrefix, find, until; + import std.algorithm.sorting : sort; //Test that ranges are properly consumed. { @@ -8217,7 +10636,7 @@ unittest } } -unittest +@system unittest { struct S { @@ -8227,107 +10646,584 @@ unittest @property auto save() @safe pure nothrow { return this; } } - S s; - auto wrapper = refRange(&s); - static assert(isInfinite!(typeof(wrapper))); + S s; + auto wrapper = refRange(&s); + static assert(isInfinite!(typeof(wrapper))); +} + +@system unittest +{ + class C + { + @property int front() @safe const pure nothrow { return 0; } + @property bool empty() @safe const pure nothrow { return false; } + void popFront() @safe pure nothrow { } + @property auto save() @safe pure nothrow { return this; } + } + static assert(isForwardRange!C); + + auto c = new C; + auto cWrapper = refRange(&c); + static assert(is(typeof(cWrapper) == C)); + assert(cWrapper is c); +} + +@system unittest // issue 14373 +{ + static struct R + { + @property int front() {return 0;} + void popFront() {empty = true;} + bool empty = false; + } + R r; + refRange(&r).popFront(); + assert(r.empty); +} + +@system unittest // issue 14575 +{ + struct R + { + Object front; + alias back = front; + bool empty = false; + void popFront() {empty = true;} + alias popBack = popFront; + @property R save() {return this;} + } + static assert(isBidirectionalRange!R); + R r; + auto rr = refRange(&r); + + struct R2 + { + @property Object front() {return null;} + @property const(Object) front() const {return null;} + alias back = front; + bool empty = false; + void popFront() {empty = true;} + alias popBack = popFront; + @property R2 save() {return this;} + } + static assert(isBidirectionalRange!R2); + R2 r2; + auto rr2 = refRange(&r2); +} + +/// ditto +auto refRange(R)(R* range) +if (isInputRange!R && !is(R == class)) +{ + return RefRange!R(range); +} + +/// ditto +auto refRange(R)(R* range) +if (isInputRange!R && is(R == class)) +{ + return *range; +} + +/*****************************************************************************/ + +@safe unittest // bug 9060 +{ + import std.algorithm.iteration : map, joiner, group; + import std.algorithm.searching : until; + // fix for std.algorithm + auto r = map!(x => 0)([1]); + chain(r, r); + zip(r, r); + roundRobin(r, r); + + struct NRAR { + typeof(r) input; + @property empty() { return input.empty; } + @property front() { return input.front; } + void popFront() { input.popFront(); } + @property save() { return NRAR(input.save); } + } + auto n1 = NRAR(r); + cycle(n1); // non random access range version + + assumeSorted(r); + + // fix for std.range + joiner([r], [9]); + + struct NRAR2 { + NRAR input; + @property empty() { return true; } + @property front() { return input; } + void popFront() { } + @property save() { return NRAR2(input.save); } + } + auto n2 = NRAR2(n1); + joiner(n2); + + group(r); + + until(r, 7); + static void foo(R)(R r) { until!(x => x > 7)(r); } + foo(r); +} + +private struct Bitwise(R) +if (isInputRange!R && isIntegral!(ElementType!R)) +{ +private: + alias ElemType = ElementType!R; + alias UnsignedElemType = Unsigned!ElemType; + + R parent; + enum bitsNum = ElemType.sizeof * 8; + size_t maskPos = 1; + + static if (isBidirectionalRange!R) + { + size_t backMaskPos = bitsNum; + } + +public: + this()(auto ref R range) + { + parent = range; + } + + static if (isInfinite!R) + { + enum empty = false; + } + else + { + /** + * Check if the range is empty + * + * Returns: a boolean true or false + */ + bool empty() + { + static if (hasLength!R) + { + return length == 0; + } + else static if (isBidirectionalRange!R) + { + if (parent.empty) + { + return true; + } + else + { + /* + If we have consumed the last element of the range both from + the front and the back, then the masks positions will overlap + */ + return parent.save.dropOne.empty && (maskPos > backMaskPos); + } + } + else + { + /* + If we consumed the last element of the range, but not all the + bits in the last element + */ + return parent.empty; + } + } + } + + bool front() + { + assert(!empty); + return (parent.front & mask(maskPos)) != 0; + } + + void popFront() + { + assert(!empty); + ++maskPos; + if (maskPos > bitsNum) + { + parent.popFront; + maskPos = 1; + } + } + + static if (hasLength!R) + { + size_t length() + { + auto len = parent.length * bitsNum - (maskPos - 1); + static if (isBidirectionalRange!R) + { + len -= bitsNum - backMaskPos; + } + return len; + } + + alias opDollar = length; + } + + static if (isForwardRange!R) + { + typeof(this) save() + { + auto result = this; + result.parent = parent.save; + return result; + } + } + + static if (isBidirectionalRange!R) + { + bool back() + { + assert(!empty); + return (parent.back & mask(backMaskPos)) != 0; + } + + void popBack() + { + assert(!empty); + --backMaskPos; + if (backMaskPos == 0) + { + parent.popBack; + backMaskPos = bitsNum; + } + } + } + + static if (isRandomAccessRange!R) + { + /** + Return the `n`th bit within the range + */ + bool opIndex(size_t n) + in + { + /* + If it does not have the length property, it means that R is + an infinite range + */ + static if (hasLength!R) + { + assert(n < length, "Index out of bounds"); + } + } + body + { + immutable size_t remainingBits = bitsNum - maskPos + 1; + // If n >= maskPos, then the bit sign will be 1, otherwise 0 + immutable sizediff_t sign = (remainingBits - n - 1) >> (sizediff_t.sizeof * 8 - 1); + /* + By truncating n with remainingBits bits we have skipped the + remaining bits in parent[0], so we need to add 1 to elemIndex. + + Because bitsNum is a power of 2, n / bitsNum == n >> bitsNum.bsf + */ + import core.bitop : bsf; + immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); + + /* + Since the indexing is from LSB to MSB, we need to index at the + remainder of (n - remainingBits). + + Because bitsNum is a power of 2, n % bitsNum == n & (bitsNum - 1) + */ + immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) + + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); + + return (parent[elemIndex] & mask(elemMaskPos)) != 0; + } + + static if (hasAssignableElements!R) + { + /** + Assigns `flag` to the `n`th bit within the range + */ + void opIndexAssign(bool flag, size_t n) + in + { + static if (hasLength!R) + { + assert(n < length, "Index out of bounds"); + } + } + body + { + import core.bitop : bsf; + + immutable size_t remainingBits = bitsNum - maskPos + 1; + immutable sizediff_t sign = (remainingBits - n - 1) >> (sizediff_t.sizeof * 8 - 1); + immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); + immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) + + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); + + auto elem = parent[elemIndex]; + auto elemMask = mask(elemMaskPos); + parent[elemIndex] = cast(UnsignedElemType)(flag * (elem | elemMask) + + (flag ^ 1) * (elem & ~elemMask)); + } + } + + Bitwise!R opSlice() + { + return this.save; + } + + Bitwise!R opSlice(size_t start, size_t end) + in + { + assert(start < end, "Invalid bounds: end <= start"); + } + body + { + import core.bitop : bsf; + + size_t remainingBits = bitsNum - maskPos + 1; + sizediff_t sign = (remainingBits - start - 1) >> (sizediff_t.sizeof * 8 - 1); + immutable size_t startElemIndex = sign * (((start - remainingBits) >> bitsNum.bsf) + 1); + immutable size_t startElemMaskPos = (sign ^ 1) * (maskPos + start) + + sign * (1 + ((start - remainingBits) & (bitsNum - 1))); + + immutable size_t sliceLen = end - start - 1; + remainingBits = bitsNum - startElemMaskPos + 1; + sign = (remainingBits - sliceLen - 1) >> (sizediff_t.sizeof * 8 - 1); + immutable size_t endElemIndex = startElemIndex + + sign * (((sliceLen - remainingBits) >> bitsNum.bsf) + 1); + immutable size_t endElemMaskPos = (sign ^ 1) * (startElemMaskPos + sliceLen) + + sign * (1 + ((sliceLen - remainingBits) & (bitsNum - 1))); + + typeof(return) result; + // Get the slice to be returned from the parent + result.parent = (parent[startElemIndex .. endElemIndex + 1]).save; + result.maskPos = startElemMaskPos; + static if (isBidirectionalRange!R) + { + result.backMaskPos = endElemMaskPos; + } + return result; + } + } + +private: + auto mask(size_t maskPos) + { + return (1UL << (maskPos - 1UL)); + } +} + +/** +Bitwise adapter over an integral type range. Consumes the range elements bit by +bit, from the least significant bit to the most significant bit. + +Params: + R = an integral input range to iterate over + range = range to consume bit by by + +Returns: + A `Bitwise` input range with propagated forward, bidirectional + and random access capabilities +*/ +auto bitwise(R)(auto ref R range) +if (isInputRange!R && isIntegral!(ElementType!R)) +{ + return Bitwise!R(range); +} + +/// +@safe pure unittest +{ + import std.algorithm.comparison : equal; + import std.format : format; + + // 00000011 00001001 + ubyte[] arr = [3, 9]; + auto r = arr.bitwise; + + // iterate through it as with any other range + assert(format("%(%d%)", r) == "1100000010010000"); + assert(format("%(%d%)", r.retro).equal("1100000010010000".retro)); + + auto r2 = r[5 .. $]; + // set a bit + r[2] = 1; + assert(arr[0] == 7); + assert(r[5] == r2[0]); +} + +/// You can use bitwise to implement an uniform bool generator +@safe unittest +{ + import std.random : rndGen; + import std.algorithm.comparison : equal; + + auto rb = rndGen.bitwise; + static assert(isInfinite!(typeof(rb))); + + auto rb2 = rndGen.bitwise; + // Don't forget that structs are passed by value + assert(rb.take(10).equal(rb2.take(10))); +} + +// Test nogc inference +@safe @nogc unittest +{ + static ubyte[] arr = [3, 9]; + auto bw = arr.bitwise; + auto bw2 = bw[]; + auto bw3 = bw[8 .. $]; + bw3[2] = true; + + assert(arr[1] == 13); + assert(bw[$ - 6]); + assert(bw[$ - 6] == bw2[$ - 6]); + assert(bw[$ - 6] == bw3[$ - 6]); } -unittest +// Test all range types over all integral types +@safe pure nothrow unittest { - class C + import std.internal.test.dummyrange; + + alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, + long, ulong); + foreach (IntegralType; IntegralTypes) { - @property int front() @safe const pure nothrow { return 0; } - @property bool empty() @safe const pure nothrow { return false; } - void popFront() @safe pure nothrow { } - @property auto save() @safe pure nothrow { return this; } - } - static assert(isForwardRange!C); + foreach (T; AllDummyRangesType!(IntegralType[])) + { + T a; + auto bw = Bitwise!T(a); - auto c = new C; - auto cWrapper = refRange(&c); - static assert(is(typeof(cWrapper) == C)); - assert(cWrapper is c); + static if (isForwardRange!T) + { + auto bwFwdSave = bw.save; + } - struct S - { - @property int front() @safe const pure nothrow { return 0; } - @property bool empty() @safe const pure nothrow { return false; } - void popFront() @safe pure nothrow { } + static if (isBidirectionalRange!T) + { + auto bwBack = bw.save; + auto bwBackSave = bw.save; + } - int i = 27; - } - static assert(isInputRange!S); - static assert(!isForwardRange!S); + static if (hasLength!T) + { + auto bwLength = bw.length; + assert(bw.length == (IntegralType.sizeof * 8 * a.length)); + static if (isForwardRange!T) + { + assert(bw.length == bwFwdSave.length); + } + } - auto s = S(42); - auto sWrapper = refRange(&s); - static assert(is(typeof(sWrapper) == S)); - assert(sWrapper == s); -} + // Make sure front and back are not the mechanisms that modify the range + long numCalls = 42; + bool initialFrontValue; -/++ - Helper function for constructing a $(LREF RefRange). + if (!bw.empty) + { + initialFrontValue = bw.front; + } - If the given range is not a forward range or it is a class type (and thus is - already a reference type), then the original range is returned rather than - a $(LREF RefRange). - +/ -auto refRange(R)(R* range) - if(isForwardRange!R && !is(R == class)) -{ - return RefRange!R(range); + while (!bw.empty && (--numCalls)) + { + bw.front; + assert(bw.front == initialFrontValue); + } + + /* + Check that empty works properly and that popFront does not get called + more times than it should + */ + numCalls = 0; + while (!bw.empty) + { + ++numCalls; + + static if (hasLength!T) + { + assert(bw.length == bwLength); + --bwLength; + } + + static if (isForwardRange!T) + { + assert(bw.front == bwFwdSave.front); + bwFwdSave.popFront(); + } + + static if (isBidirectionalRange!T) + { + assert(bwBack.front == bwBackSave.front); + bwBack.popBack(); + bwBackSave.popBack(); + } + bw.popFront(); + } + + auto rangeLen = numCalls / (IntegralType.sizeof * 8); + assert(numCalls == (IntegralType.sizeof * 8 * rangeLen)); + assert(bw.empty); + static if (isForwardRange!T) + { + assert(bwFwdSave.empty); + } + + static if (isBidirectionalRange!T) + { + assert(bwBack.empty); + } + } + } } -auto refRange(R)(R* range) - if((!isForwardRange!R && isInputRange!R) || - is(R == class)) +// Test opIndex and opSlice +@system unittest { - return *range; -} + alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, + long, ulong); + foreach (IntegralType; IntegralTypes) + { + size_t bitsNum = IntegralType.sizeof * 8; -/*****************************************************************************/ + auto first = cast(IntegralType)(1); -@safe unittest // bug 9060 -{ - import std.algorithm : map, joiner, group, until; - // fix for std.algorithm - auto r = map!(x => 0)([1]); - chain(r, r); - zip(r, r); - roundRobin(r, r); + // 2 ^ (bitsNum - 1) + auto second = cast(IntegralType)(cast(IntegralType)(1) << (bitsNum - 2)); - struct NRAR { - typeof(r) input; - @property empty() { return input.empty; } - @property front() { return input.front; } - void popFront() { input.popFront(); } - @property save() { return NRAR(input.save); } - } - auto n1 = NRAR(r); - cycle(n1); // non random access range version + IntegralType[] a = [first, second]; + auto bw = Bitwise!(IntegralType[])(a); - assumeSorted(r); + // Check against lsb of a[0] + assert(bw[0] == true); + // Check against msb - 1 of a[1] + assert(bw[2 * bitsNum - 2] == true); - // fix for std.range - joiner([r], [9]); + bw.popFront(); + assert(bw[2 * bitsNum - 3] == true); - struct NRAR2 { - NRAR input; - @property empty() { return true; } - @property front() { return input; } - void popFront() { } - @property save() { return NRAR2(input.save); } - } - auto n2 = NRAR2(n1); - joiner(n2); + import core.exception : Error; + import std.exception : assertThrown; - group(r); + // Check out of bounds error + assertThrown!Error(bw[2 * bitsNum - 1]); - until(r, 7); - static void foo(R)(R r) { until!(x => x > 7)(r); } - foo(r); -} + bw[2] = true; + assert(bw[2] == true); + bw.popFront(); + assert(bw[1] == true); + auto bw2 = bw[0 .. $ - 5]; + auto bw3 = bw2[]; + assert(bw2.length == (bw.length - 5)); + assert(bw2.length == bw3.length); + bw2.popFront(); + assert(bw2.length != bw3.length); + } +} /********************************* * An OutputRange that discards the data it receives. @@ -8337,30 +11233,50 @@ struct NullSink void put(E)(E){} } +/// @safe unittest { - import std.algorithm : map, copy; - [4, 5, 6].map!(x => x * 2).copy(NullSink()); + import std.algorithm.iteration : map; + import std.algorithm.mutation : copy; + [4, 5, 6].map!(x => x * 2).copy(NullSink()); // data is discarded } /++ - Implements a "tee" style pipe, wrapping an input range so that elements - of the range can be passed to a provided function or $(LREF OutputRange) - as they are iterated over. This is useful for printing out intermediate - values in a long chain of range code, performing some operation with - side-effects on each call to $(D front) or $(D popFront), or diverting - the elements of a range into an auxiliary $(LREF OutputRange). + + Implements a "tee" style pipe, wrapping an input range so that elements of the + range can be passed to a provided function or $(LREF OutputRange) as they are + iterated over. This is useful for printing out intermediate values in a long + chain of range code, performing some operation with side-effects on each call + to $(D front) or $(D popFront), or diverting the elements of a range into an + auxiliary $(LREF OutputRange). It is important to note that as the resultant range is evaluated lazily, in the case of the version of $(D tee) that takes a function, the function will not actually be executed until the range is "walked" using functions - that evaluate ranges, such as $(XREF array,array) or - $(XREF algorithm,reduce). - - See_Also: $(XREF argorithm,each) + that evaluate ranges, such as $(REF array, std,array) or + $(REF fold, std,algorithm,iteration). + + Params: + pipeOnPop = If `Yes.pipeOnPop`, simply iterating the range without ever + calling `front` is enough to have `tee` mirror elements to `outputRange` (or, + respectively, `fun`). If `No.pipeOnPop`, only elements for which `front` does + get called will be also sent to `outputRange`/`fun`. + inputRange = The input range being passed through. + outputRange = This range will receive elements of `inputRange` progressively + as iteration proceeds. + fun = This function will be called with elements of `inputRange` + progressively as iteration proceeds. + + Returns: + An input range that offers the elements of `inputRange`. Regardless of + whether `inputRange` is a more powerful range (forward, bidirectional etc), + the result is always an input range. Reading this causes `inputRange` to be + iterated and returns its elements in turn. In addition, the same elements + will be passed to `outputRange` or `fun` as well. + + See_Also: $(REF each, std,algorithm,iteration) +/ - auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange) if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) { @@ -8375,7 +11291,7 @@ if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) static if (hasLength!R1) { - @property length() + @property auto length() { return _input.length; } @@ -8392,7 +11308,7 @@ if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) void popFront() { - assert(!_input.empty); + assert(!_input.empty, "Attempting to popFront an empty tee"); static if (pipeOnPop) { put(_output, _input.front); @@ -8406,6 +11322,7 @@ if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) @property auto ref front() { + assert(!_input.empty, "Attempting to fetch the front of an empty tee"); static if (!pipeOnPop) { if (!_frontAccessed) @@ -8421,12 +11338,11 @@ if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) return Result(inputRange, outputRange); } -/++ - Overload for taking a function or template lambda as an $(LREF OutputRange) -+/ +/// Ditto auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange) if (is(typeof(fun) == void) || isSomeFunction!fun) { + import std.traits : isDelegate, isFunctionPointer; /* Distinguish between function literals and template lambdas when using either as an $(LREF OutputRange). Since a template @@ -8449,17 +11365,20 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) } } -// +/// @safe unittest { - import std.algorithm : equal, filter, map; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter, map; - // Pass-through + // Sum values while copying int[] values = [1, 4, 9, 16, 25]; - - auto newValues = values.tee!(a => a + 1).array; + int sum = 0; + auto newValues = values.tee!(a => sum += a).array; assert(equal(newValues, values)); + assert(sum == 1 + 4 + 9 + 16 + 25); + // Count values that pass the first filter int count = 0; auto newValues4 = values.filter!(a => a < 10) .tee!(a => count++) @@ -8467,7 +11386,7 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) .filter!(a => a < 10); //Fine, equal also evaluates any lazy ranges passed to it. - //count is not 3 until equal evaluates newValues3 + //count is not 3 until equal evaluates newValues4 assert(equal(newValues4, [2, 5])); assert(count == 3); } @@ -8475,7 +11394,8 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) // @safe unittest { - import std.algorithm : equal, filter, map; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter, map; int[] values = [1, 4, 9, 16, 25]; @@ -8512,13 +11432,15 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) // @safe unittest { - import std.algorithm : filter, equal, map; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter, map; char[] txt = "Line one, Line 2".dup; bool isVowel(dchar c) { - return std.string.indexOf("AaEeIiOoUu", c) != -1; + import std.string : indexOf; + return "AaEeIiOoUu".indexOf(c) != -1; } int vowelCount = 0; @@ -8571,7 +11493,8 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.meta : AliasSeq; //Test diverting elements to an OutputRange string txt = "abcdefghijklmnopqrstuvwxyz"; @@ -8594,14 +11517,14 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) auto result3 = txt.tee(asink3).array; assert(equal(txt, result3) && equal(result3, asink3)); - foreach (CharType; TypeTuple!(char, wchar, dchar)) + foreach (CharType; AliasSeq!(char, wchar, dchar)) { auto appSink = appender!(CharType[])(); auto appResult = txt.tee(appSink).array; assert(equal(txt, appResult) && equal(appResult, appSink.data)); } - foreach (StringType; TypeTuple!(string, wstring, dstring)) + foreach (StringType; AliasSeq!(string, wstring, dstring)) { auto appSink = appender!StringType(); auto appResult = txt.tee(appSink).array; @@ -8617,3 +11540,306 @@ if (is(typeof(fun) == void) || isSomeFunction!fun) auto r = [1, 2, 3, 4].tee!func1.tee!func2; } + +/** +Extends the length of the input range `r` by padding out the start of the +range with the element `e`. The element `e` must be of a common type with +the element type of the range `r` as defined by $(REF CommonType, std, traits). +If `n` is less than the length of of `r`, then `r` is returned unmodified. + +If `r` is a string with Unicode characters in it, `padLeft` follows D's rules +about length for strings, which is not the number of characters, or +graphemes, but instead the number of encoding units. If you want to treat each +grapheme as only one encoding unit long, then call +$(REF byGrapheme, std, uni) before calling this function. + +If `r` has a length, then this is $(BIGOH 1). Otherwise, it's $(BIGOH r.length). + +Params: + r = an input range with a length, or a forward range + e = element to pad the range with + n = the length to pad to + +Returns: + A range containing the elements of the original range with the extra padding + +See Also: + $(REF leftJustifier, std, string) +*/ +auto padLeft(R, E)(R r, E e, size_t n) +if ( + ((isInputRange!R && hasLength!R) || isForwardRange!R) && + !is(CommonType!(ElementType!R, E) == void) +) +{ + static if (hasLength!R) + auto dataLength = r.length; + else + auto dataLength = r.save.walkLength(n); + + return e.repeat(n > dataLength ? n - dataLength : 0).chain(r); +} + +/// +@safe pure unittest +{ + import std.algorithm.comparison : equal; + + assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4])); + assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4])); + + assert("abc".padLeft('_', 6).equal("___abc")); +} + +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; + import std.meta : AliasSeq; + + alias DummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward) + ); + + foreach (Range; DummyRanges) + { + Range r; + assert(r + .padLeft(0, 12) + .equal([0, 0, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) + ); + } +} + +// Test nogc inference +@safe @nogc pure unittest +{ + import std.algorithm.comparison : equal; + + static immutable r1 = [1, 2, 3, 4]; + static immutable r2 = [0, 0, 1, 2, 3, 4]; + assert(r1.padLeft(0, 6).equal(r2)); +} + +/** +Extend the length of the input range `r` by padding out the end of the range +with the element `e`. The element `e` must be of a common type with the +element type of the range `r` as defined by $(REF CommonType, std, traits). +If `n` is less than the length of of `r`, then the contents of `r` are +returned. + +The range primitives that the resulting range provides depends whether or not `r` +provides them. Except the functions `back` and `popBack`, which also require +the range to have a length as well as `back` and `popBack` + +Params: + r = an input range with a length + e = element to pad the range with + n = the length to pad to + +Returns: + A range containing the elements of the original range with the extra padding + +See Also: + $(REF rightJustifier, std, string) +*/ +auto padRight(R, E)(R r, E e, size_t n) +if ( + isInputRange!R && + !isInfinite!R && + !is(CommonType!(ElementType!R, E) == void)) +{ + static struct Result + { + private: + R data; + E element; + size_t counter; + static if (isBidirectionalRange!R && hasLength!R) size_t backPosition; + size_t maxSize; + + public: + bool empty() @property + { + return data.empty && counter >= maxSize; + } + + auto front() @property + { + assert(!empty, "Attempting to fetch the front of an empty padRight"); + return data.empty ? element : data.front; + } + + void popFront() + { + assert(!empty, "Attempting to popFront an empty padRight"); + ++counter; + + if (!data.empty) + { + data.popFront; + } + } + + static if (hasLength!R) + { + size_t length() @property + { + import std.algorithm.comparison : max; + return max(data.length, maxSize); + } + } + + static if (isForwardRange!R) + { + auto save() @property + { + typeof(this) result = this; + data = data.save; + return result; + } + } + + static if (isBidirectionalRange!R && hasLength!R) + { + auto back() @property + { + assert(!empty, "Attempting to fetch the back of an empty padRight"); + return backPosition > data.length ? element : data.back; + } + + void popBack() + { + assert(!empty, "Attempting to popBack an empty padRight"); + if (backPosition > data.length) + { + --backPosition; + --maxSize; + } + else + { + data.popBack; + } + } + } + + static if (isRandomAccessRange!R && hasLength!R) + { + E opIndex(size_t index) + { + assert(index <= this.length, "Index out of bounds"); + return (index > data.length && index <= maxSize) ? element : + data[index]; + } + } + + static if (hasSlicing!R && hasLength!R) + { + auto opSlice(size_t a, size_t b) + { + assert( + a <= b, + "Attempting to slice a padRight with a larger first argument than the second." + ); + assert( + b <= length, + "Attempting to slice using an out of bounds index on a padRight" + ); + return Result((b <= data.length) ? data[a .. b] : data[a .. data.length], + element, b - a); + } + + alias opDollar = length; + } + + this(R r, E e, size_t max) + { + data = r; + element = e; + maxSize = max; + static if (isBidirectionalRange!R && hasLength!R) + backPosition = max; + } + + @disable this(); + } + + return Result(r, e, n); +} + +/// +@safe pure unittest +{ + import std.algorithm.comparison : equal; + + assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0])); + assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4])); + + assert("abc".padRight('_', 6).equal("abc___")); +} + +pure @safe unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : AllDummyRanges, ReferenceInputRange; + import std.meta : AliasSeq; + + auto string_input_range = new ReferenceInputRange!dchar(['a', 'b', 'c']); + dchar padding = '_'; + assert(string_input_range.padRight(padding, 6).equal("abc___")); + + foreach (RangeType; AllDummyRanges) + { + RangeType r1; + assert(r1 + .padRight(0, 12) + .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) + ); + + // test if Result properly uses random access ranges + static if (isRandomAccessRange!RangeType) + { + RangeType r3; + assert(r3.padRight(0, 12)[0] == 1); + assert(r3.padRight(0, 12)[2] == 3); + assert(r3.padRight(0, 12)[11] == 0); + } + + // test if Result properly uses slicing and opDollar + static if (hasSlicing!RangeType) + { + RangeType r4; + assert(r4 + .padRight(0, 12)[0 .. 3] + .equal([1, 2, 3]) + ); + assert(r4 + .padRight(0, 12)[2 .. $] + .equal([3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) + ); + assert(r4 + .padRight(0, 12)[0 .. $] + .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) + ); + } + } +} + +// Test nogc inference +@safe @nogc pure unittest +{ + import std.algorithm.comparison : equal; + + static immutable r1 = [1, 2, 3, 4]; + static immutable r2 = [1, 2, 3, 4, 0, 0]; + assert(r1.padRight(0, 6).equal(r2)); +} diff --git a/std/range/primitives.d b/std/range/primitives.d index 38d9d6e3760..aeedf605601 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1,32 +1,33 @@ /** -This module is a submodule of $(LINK2 std_range.html, std.range). +This module is a submodule of $(MREF std, range). It provides basic range functionality by defining several templates for testing whether a given object is a _range, and what kind of _range it is: +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , - $(TR $(TD $(D $(LREF isInputRange))) + $(TR $(TD $(LREF isInputRange)) $(TD Tests if something is an $(I input _range), defined to be something from which one can sequentially read data using the primitives $(D front), $(D popFront), and $(D empty). )) - $(TR $(TD $(D $(LREF isOutputRange))) + $(TR $(TD $(LREF isOutputRange)) $(TD Tests if something is an $(I output _range), defined to be something to which one can sequentially write data using the - $(D $(LREF put)) primitive. + $(LREF put) primitive. )) - $(TR $(TD $(D $(LREF isForwardRange))) + $(TR $(TD $(LREF isForwardRange)) $(TD Tests if something is a $(I forward _range), defined to be an input _range with the additional capability that one can save one's current position with the $(D save) primitive, thus allowing one to iterate over the same _range multiple times. )) - $(TR $(TD $(D $(LREF isBidirectionalRange))) + $(TR $(TD $(LREF isBidirectionalRange)) $(TD Tests if something is a $(I bidirectional _range), that is, a forward _range that allows reverse traversal using the primitives $(D back) and $(D popBack). )) - $(TR $(TD $(D $(LREF isRandomAccessRange))) + $(TR $(TD $(LREF isRandomAccessRange)) $(TD Tests if something is a $(I random access _range), which is a bidirectional _range that also supports the array subscripting operation via the primitive $(D opIndex). @@ -36,82 +37,79 @@ $(BOOKTABLE , It also provides number of templates that test for various _range capabilities: $(BOOKTABLE , - $(TR $(TD $(D $(LREF hasMobileElements))) + $(TR $(TD $(LREF hasMobileElements)) $(TD Tests if a given _range's elements can be moved around using the primitives $(D moveFront), $(D moveBack), or $(D moveAt). )) - $(TR $(TD $(D $(LREF ElementType))) + $(TR $(TD $(LREF ElementType)) $(TD Returns the element type of a given _range. )) - $(TR $(TD $(D $(LREF ElementEncodingType))) + $(TR $(TD $(LREF ElementEncodingType)) $(TD Returns the encoding element type of a given _range. )) - $(TR $(TD $(D $(LREF hasSwappableElements))) + $(TR $(TD $(LREF hasSwappableElements)) $(TD Tests if a _range is a forward _range with swappable elements. )) - $(TR $(TD $(D $(LREF hasAssignableElements))) + $(TR $(TD $(LREF hasAssignableElements)) $(TD Tests if a _range is a forward _range with mutable elements. )) - $(TR $(TD $(D $(LREF hasLvalueElements))) + $(TR $(TD $(LREF hasLvalueElements)) $(TD Tests if a _range is a forward _range with elements that can be passed by reference and have their address taken. )) - $(TR $(TD $(D $(LREF hasLength))) + $(TR $(TD $(LREF hasLength)) $(TD Tests if a given _range has the $(D length) attribute. )) - $(TR $(TD $(D $(LREF isInfinite))) + $(TR $(TD $(LREF isInfinite)) $(TD Tests if a given _range is an $(I infinite _range). )) - $(TR $(TD $(D $(LREF hasSlicing))) + $(TR $(TD $(LREF hasSlicing)) $(TD Tests if a given _range supports the array slicing operation $(D - R[x..y]). + R[x .. y]). )) ) Finally, it includes some convenience functions for manipulating ranges: $(BOOKTABLE , - $(TR $(TD $(D $(LREF popFrontN))) + $(TR $(TD $(LREF popFrontN)) $(TD Advances a given _range by up to $(I n) elements. )) - $(TR $(TD $(D $(LREF popBackN))) + $(TR $(TD $(LREF popBackN)) $(TD Advances a given bidirectional _range from the right by up to $(I n) elements. )) - $(TR $(TD $(D $(LREF popFrontExactly))) + $(TR $(TD $(LREF popFrontExactly)) $(TD Advances a given _range by up exactly $(I n) elements. )) - $(TR $(TD $(D $(LREF popBackExactly))) + $(TR $(TD $(LREF popBackExactly)) $(TD Advances a given bidirectional _range from the right by exactly $(I n) elements. )) - $(TR $(TD $(D $(LREF moveFront))) + $(TR $(TD $(LREF moveFront)) $(TD Removes the front element of a _range. )) - $(TR $(TD $(D $(LREF moveBack))) + $(TR $(TD $(LREF moveBack)) $(TD Removes the back element of a bidirectional _range. )) - $(TR $(TD $(D $(LREF moveAt))) + $(TR $(TD $(LREF moveAt)) $(TD Removes the $(I i)'th element of a random-access _range. )) - $(TR $(TD $(D $(LREF walkLength))) + $(TR $(TD $(LREF walkLength)) $(TD Computes the length of any _range in O(n) time. )) + $(TR $(TD $(LREF put)) + $(TD Outputs element $(D e) to a _range. + )) ) -Source: $(PHOBOSSRC std/range/_constraints.d) - -Macros: - -WIKI = Phobos/StdRange +Source: $(PHOBOSSRC std/range/_primitives.d) -Copyright: Copyright by authors 2008-. +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(WEB erdani.com, Andrei Alexandrescu), David Simcha, +Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and Jonathan M Davis. Credit for some of the ideas in building this module goes -to $(WEB fantascienza.net/leonardo/so/, Leonardo Maffi). +to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range.primitives; @@ -129,16 +127,33 @@ r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range of non-void type ---- -The semantics of an input range (not checkable during compilation) are -assumed to be the following ($(D r) is an object of type $(D R)): +The following are rules of input ranges are assumed to hold true in all +Phobos code. These rules are not checkable at compile-time, so not conforming +to these rules when writing ranges or range based code will result in +undefined behavior. + +$(UL + $(LI `r.empty` returns `false` if and only if there is more data + available in the range.) + $(LI `r.empty` evaluated multiple times, without calling + `r.popFront`, or otherwise mutating the range object or the + underlying data, yields the same result for every evaluation.) + $(LI `r.front` returns the current element in the range. + It may return by value or by reference.) + $(LI `r.front` can be legally evaluated if and only if evaluating + `r.empty` has, or would have, equaled `false`.) + $(LI `r.front` evaluated multiple times, without calling + `r.popFront`, or otherwise mutating the range object or the + underlying data, yields the same result for every evaluation.) + $(LI `r.popFront` advances to the next element in the range.) + $(LI `r.popFront` can be called if and only if evaluating `r.empty` + has, or would have, equaled `false`.) +) -$(UL $(LI $(D r.empty) returns $(D false) iff there is more data -available in the range.) $(LI $(D r.front) returns the current -element in the range. It may return by value or by reference. Calling -$(D r.front) is allowed only if calling $(D r.empty) has, or would -have, returned $(D false).) $(LI $(D r.popFront) advances to the next -element in the range. Calling $(D r.popFront) is allowed only if -calling $(D r.empty) has, or would have, returned $(D false).)) +Also, note that Phobos code assumes that the primitives `r.front` and +`r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms of +running time. $(BIGOH) statements in the documentation of range functions +are made with this assumption. Params: R = type to be tested @@ -153,7 +168,7 @@ template isInputRange(R) { R r = R.init; // can define a range object if (r.empty) {} // can test for empty - r.popFront(); // can invoke popFront() + r.popFront; // can invoke popFront() auto h = r.front; // can get the front of the range })); } @@ -188,7 +203,7 @@ guarantees that no more than a single element will be placed. +/ private void doPut(R, E)(ref R r, auto ref E e) { - static if(is(PointerTarget!R == struct)) + static if (is(PointerTarget!R == struct)) enum usingPut = hasMember!(PointerTarget!R, "put"); else enum usingPut = hasMember!(R, "put"); @@ -196,13 +211,13 @@ private void doPut(R, E)(ref R r, auto ref E e) static if (usingPut) { static assert(is(typeof(r.put(e))), - "Cannot nativaly put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); + "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.put(e); } else static if (isInputRange!R) { static assert(is(typeof(r.front = e)), - "Cannot nativaly put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); + "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); r.front = e; r.popFront(); } @@ -212,33 +227,33 @@ private void doPut(R, E)(ref R r, auto ref E e) } else { - static assert (false, - "Cannot nativaly put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); + static assert(false, + "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } @safe unittest { - static assert (!isNativeOutputRange!(int, int)); - static assert ( isNativeOutputRange!(int[], int)); - static assert (!isNativeOutputRange!(int[][], int)); + static assert(!isNativeOutputRange!(int, int)); + static assert( isNativeOutputRange!(int[], int)); + static assert(!isNativeOutputRange!(int[][], int)); - static assert (!isNativeOutputRange!(int, int[])); - static assert (!isNativeOutputRange!(int[], int[])); - static assert ( isNativeOutputRange!(int[][], int[])); + static assert(!isNativeOutputRange!(int, int[])); + static assert(!isNativeOutputRange!(int[], int[])); + static assert( isNativeOutputRange!(int[][], int[])); - static assert (!isNativeOutputRange!(int, int[][])); - static assert (!isNativeOutputRange!(int[], int[][])); - static assert (!isNativeOutputRange!(int[][], int[][])); + static assert(!isNativeOutputRange!(int, int[][])); + static assert(!isNativeOutputRange!(int[], int[][])); + static assert(!isNativeOutputRange!(int[][], int[][])); - static assert (!isNativeOutputRange!(int[4], int)); - static assert ( isNativeOutputRange!(int[4][], int)); //Scary! - static assert ( isNativeOutputRange!(int[4][], int[4])); + static assert(!isNativeOutputRange!(int[4], int)); + static assert( isNativeOutputRange!(int[4][], int)); //Scary! + static assert( isNativeOutputRange!(int[4][], int[4])); - static assert (!isNativeOutputRange!( char[], char)); - static assert (!isNativeOutputRange!( char[], dchar)); - static assert ( isNativeOutputRange!(dchar[], char)); - static assert ( isNativeOutputRange!(dchar[], dchar)); + static assert(!isNativeOutputRange!( char[], char)); + static assert(!isNativeOutputRange!( char[], dchar)); + static assert( isNativeOutputRange!(dchar[], char)); + static assert( isNativeOutputRange!(dchar[], dchar)); } @@ -304,7 +319,7 @@ void put(R, E)(ref R r, E e) doPut(r, arr[]); } else - doPut(r, (ref e) @trusted { return (&e)[0..1]; }(e)); + doPut(r, (ref e) @trusted { return (&e)[0 .. 1]; }(e)); } //special case for char to string. else static if (isSomeChar!E && is(typeof(putChar(r, e)))) @@ -318,10 +333,11 @@ void put(R, E)(ref R r, E e) //Special optimization: If E is a narrow string, and r accepts characters no-wider than the string's //Then simply feed the characters 1 by 1. static if (isNarrowString!E && ( - (is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) && !is(typeof(doPut(r, wchar.max)))) || + (is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) && + !is(typeof(doPut(r, wchar.max)))) || (is(E : const wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) ) { - foreach(c; e) + foreach (c; e) doPut(r, c); } else @@ -332,7 +348,7 @@ void put(R, E)(ref R r, E e) } else { - static assert (false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); + static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } @@ -386,11 +402,11 @@ if (isSomeChar!E) } else { - static assert (false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); + static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ "."); } } -pure unittest +pure @safe unittest { auto f = delegate (const(char)[]) {}; putChar(f, cast(dchar)'a'); @@ -404,7 +420,7 @@ pure unittest putChar(r, 'a'); } -unittest +@safe unittest { struct A {} static assert(!isInputRange!(A)); @@ -416,7 +432,7 @@ unittest put(b, 5); } -unittest +@safe unittest { int[] a = [1, 2, 3], b = [10, 20]; auto c = a; @@ -425,7 +441,7 @@ unittest assert(a == [3]); } -unittest +@safe unittest { int[] a = new int[10]; int b; @@ -433,28 +449,30 @@ unittest put(a, b); } -unittest +@safe unittest { void myprint(in char[] s) { } auto r = &myprint; put(r, 'a'); } -unittest +@safe unittest { int[] a = new int[10]; static assert(!__traits(compiles, put(a, 1.0L))); - static assert( __traits(compiles, put(a, 1))); + put(a, 1); + assert(a.length == 9); /* * a[0] = 65; // OK * a[0] = 'A'; // OK * a[0] = "ABC"[0]; // OK * put(a, "ABC"); // OK */ - static assert( __traits(compiles, put(a, "ABC"))); + put(a, "ABC"); + assert(a.length == 6); } -unittest +@safe unittest { char[] a = new char[10]; static assert(!__traits(compiles, put(a, 1.0L))); @@ -464,17 +482,19 @@ unittest static assert(!__traits(compiles, put(a, "ABC"))); } -unittest +@safe unittest { - int[][] a; - int[] b; + int[][] a = new int[][10]; + int[] b = new int[10]; int c; - static assert( __traits(compiles, put(b, c))); - static assert( __traits(compiles, put(a, b))); + put(b, c); + assert(b.length == 9); + put(a, b); + assert(a.length == 9); static assert(!__traits(compiles, put(a, c))); } -unittest +@safe unittest { int[][] a = new int[][](3); int[] b = [1]; @@ -489,7 +509,7 @@ unittest assert(a == [[2], [2], [2]]); } -unittest +@safe unittest { // Test fix for bug 7476. struct LockingTextWriter @@ -508,16 +528,16 @@ unittest put(w, r); } -unittest +@system unittest { import std.conv : to; + import std.meta : AliasSeq; import std.typecons : tuple; - import std.typetuple; static struct PutC(C) { string result; - void put(const(C) c) { result ~= to!string((&c)[0..1]); } + void put(const(C) c) { result ~= to!string((&c)[0 .. 1]); } } static struct PutS(C) { @@ -529,7 +549,7 @@ unittest string result; void put(const(C)[][] ss) { - foreach(s; ss) + foreach (s; ss) result ~= to!string(s); } } @@ -538,7 +558,7 @@ unittest putChar(p, cast(dchar)'a'); //Source Char - foreach (SC; TypeTuple!(char, wchar, dchar)) + foreach (SC; AliasSeq!(char, wchar, dchar)) { SC ch = 'I'; dchar dh = '♥'; @@ -546,10 +566,10 @@ unittest immutable(SC)[][] ss = ["日本語", "ãŒ", "好ã", "ã§ã™ã‹", "?"]; //Target Char - foreach (TC; TypeTuple!(char, wchar, dchar)) + foreach (TC; AliasSeq!(char, wchar, dchar)) { //Testing PutC and PutS - foreach (Type; TypeTuple!(PutC!TC, PutS!TC)) + foreach (Type; AliasSeq!(PutC!TC, PutS!TC)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 Type type; auto sink = new Type(); @@ -571,13 +591,13 @@ unittest } } -unittest +@safe unittest { static struct CharRange { char c; enum empty = false; - void popFront(){}; + void popFront(){} ref char front() return @property { return c; @@ -588,7 +608,7 @@ unittest put(c, "hello"d); } -unittest +@system unittest { // issue 9823 const(char)[] r; @@ -597,7 +617,7 @@ unittest assert(r == "ABC"); } -unittest +@safe unittest { // issue 10571 import std.format; @@ -606,10 +626,10 @@ unittest assert(buf == "hello"); } -unittest +@safe unittest { import std.format; - import std.typetuple; + import std.meta : AliasSeq; struct PutC(C) { void put(C){} @@ -642,7 +662,7 @@ unittest } void foo() { - foreach(C; TypeTuple!(char, wchar, dchar)) + foreach (C; AliasSeq!(char, wchar, dchar)) { formattedWrite((C c){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); formattedWrite((const(C)[]){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d); @@ -671,7 +691,7 @@ are: 2: if $(D E) is a non $(empty) $(D InputRange), then placing $(D e) is guaranteed to not overflow the range. +/ -package template isNativeOutputRange(R, E) +package(std) template isNativeOutputRange(R, E) { enum bool isNativeOutputRange = is(typeof( (inout int = 0) @@ -682,7 +702,6 @@ package template isNativeOutputRange(R, E) })); } -/// @safe unittest { int[] r = new int[](4); @@ -759,7 +778,8 @@ The following code should compile for any forward range. ---- static assert(isInputRange!R); R r1; -static assert (is(typeof(r1.save) == R)); +auto s1 = r1.save; +static assert(is(typeof(s1) == R)); ---- Saving a range is not duplicating it; in the example above, $(D r1) @@ -777,7 +797,11 @@ template isForwardRange(R) (inout int = 0) { R r1 = R.init; - static assert (is(typeof(r1.save) == R)); + // NOTE: we cannot check typeof(r1.save) directly + // because typeof may not check the right type there, and + // because we want to ensure the range can be copied. + auto s1 = r1.save; + static assert(is(typeof(s1) == R)); })); } @@ -789,6 +813,20 @@ template isForwardRange(R) static assert( isForwardRange!(inout(int)[])); } +@safe unittest +{ + // BUG 14544 + struct R14544 + { + int front() { return 0;} + void popFront() {} + bool empty() { return false; } + R14544 save() {return this;} + } + + static assert( isForwardRange!R14544 ); +} + /** Returns $(D true) if $(D R) is a bidirectional range. A bidirectional range is a forward range that also offers the primitives $(D back) and @@ -809,7 +847,7 @@ template isBidirectionalRange(R) (inout int = 0) { R r = R.init; - r.popBack(); + r.popBack; auto t = r.back; auto w = r.front; static assert(is(typeof(t) == typeof(w))); @@ -817,7 +855,7 @@ template isBidirectionalRange(R) } /// -unittest +@safe unittest { alias R = int[]; R r = [0,1]; @@ -882,23 +920,26 @@ template isRandomAccessRange(R) isForwardRange!R && isInfinite!R); R r = R.init; auto e = r[1]; - static assert(is(typeof(e) == typeof(r.front))); + auto f = r.front; + static assert(is(typeof(e) == typeof(f))); static assert(!isNarrowString!R); static assert(hasLength!R || isInfinite!R); - static if(is(typeof(r[$]))) + static if (is(typeof(r[$]))) { - static assert(is(typeof(r.front) == typeof(r[$]))); + static assert(is(typeof(f) == typeof(r[$]))); - static if(!isInfinite!R) - static assert(is(typeof(r.front) == typeof(r[$ - 1]))); + static if (!isInfinite!R) + static assert(is(typeof(f) == typeof(r[$ - 1]))); } })); } /// -unittest +@safe unittest { + import std.traits : isNarrowString; + alias R = int[]; // range is finite and bidirectional or infinite and forward. @@ -907,19 +948,20 @@ unittest R r = [0,1]; auto e = r[1]; // can index - static assert(is(typeof(e) == typeof(r.front))); // same type for indexed and front + auto f = r.front; + static assert(is(typeof(e) == typeof(f))); // same type for indexed and front static assert(!isNarrowString!R); // narrow strings cannot be indexed as ranges static assert(hasLength!R || isInfinite!R); // must have length or be infinite // $ must work as it does with arrays if opIndex works with $ - static if(is(typeof(r[$]))) + static if (is(typeof(r[$]))) { - static assert(is(typeof(r.front) == typeof(r[$]))); + static assert(is(typeof(f) == typeof(r[$]))); // $ - 1 doesn't make sense with infinite ranges but needs to work // with finite ones. - static if(!isInfinite!R) - static assert(is(typeof(r.front) == typeof(r[$ - 1]))); + static if (!isInfinite!R) + static assert(is(typeof(f) == typeof(r[$ - 1]))); } } @@ -953,10 +995,24 @@ unittest alias opDollar = length; //int opSlice(uint, uint); } + struct E + { + bool empty(); + E save(); + int front(); + void popFront(); + int back(); + void popBack(); + ref int opIndex(uint); + size_t length(); + alias opDollar = length; + //int opSlice(uint, uint); + } static assert(!isRandomAccessRange!(A)); static assert(!isRandomAccessRange!(B)); static assert(!isRandomAccessRange!(C)); static assert( isRandomAccessRange!(D)); + static assert( isRandomAccessRange!(E)); static assert( isRandomAccessRange!(int[])); static assert( isRandomAccessRange!(inout(int)[])); } @@ -1027,7 +1083,7 @@ template hasMobileElements(R) /// @safe unittest { - import std.algorithm : map; + import std.algorithm.iteration : map; import std.range : iota, repeat; static struct HasPostblit @@ -1209,12 +1265,12 @@ R r; static assert(isInputRange!R); swap(r.front, r.front); static if (isBidirectionalRange!R) swap(r.back, r.front); -static if (isRandomAccessRange!R) swap(r[], r.front); +static if (isRandomAccessRange!R) swap(r[0], r.front); ---- */ template hasSwappableElements(R) { - import std.algorithm : swap; + import std.algorithm.mutation : swap; enum bool hasSwappableElements = isInputRange!R && is(typeof( (inout int = 0) { @@ -1354,7 +1410,7 @@ template hasLength(R) (inout int = 0) { R r = R.init; - static assert(is(typeof(r.length) : ulong)); + ulong l = r.length; })); } @@ -1369,7 +1425,7 @@ template hasLength(R) struct B { size_t length() { return 0; } } struct C { @property size_t length() { return 0; } } static assert( hasLength!(A)); - static assert(!hasLength!(B)); + static assert( hasLength!(B)); static assert( hasLength!(C)); } @@ -1423,7 +1479,7 @@ The following code must compile for $(D hasSlicing) to be $(D true): ---- R r = void; -static if(isInfinite!R) +static if (isInfinite!R) typeof(take(r, 1)) s = r[1 .. 2]; else { @@ -1433,13 +1489,13 @@ else s = r[1 .. 2]; -static if(is(typeof(r[0 .. $]))) +static if (is(typeof(r[0 .. $]))) { static assert(is(typeof(r[0 .. $]) == R)); R t = r[0 .. $]; t = r[0 .. $]; - static if(!isInfinite!R) + static if (!isInfinite!R) { static assert(is(typeof(r[0 .. $ - 1]) == R)); R u = r[0 .. $ - 1]; @@ -1458,7 +1514,7 @@ template hasSlicing(R) { R r = R.init; - static if(isInfinite!R) + static if (isInfinite!R) { typeof(r[1 .. 1]) s = r[1 .. 2]; } @@ -1470,13 +1526,13 @@ template hasSlicing(R) s = r[1 .. 2]; - static if(is(typeof(r[0 .. $]))) + static if (is(typeof(r[0 .. $]))) { static assert(is(typeof(r[0 .. $]) == R)); R t = r[0 .. $]; t = r[0 .. $]; - static if(!isInfinite!R) + static if (!isInfinite!R) { static assert(is(typeof(r[0 .. $ - 1]) == R)); R u = r[0 .. $ - 1]; @@ -1555,7 +1611,7 @@ Infinite ranges are compatible, provided the parameter $(D upTo) is specified, in which case the implementation simply returns upTo. */ auto walkLength(Range)(Range range) - if (isInputRange!Range && !isInfinite!Range) +if (isInputRange!Range && !isInfinite!Range) { static if (hasLength!Range) return range.length; @@ -1569,7 +1625,7 @@ auto walkLength(Range)(Range range) } /// ditto auto walkLength(Range)(Range range, const size_t upTo) - if (isInputRange!Range) +if (isInputRange!Range) { static if (hasLength!Range) return range.length; @@ -1586,7 +1642,7 @@ auto walkLength(Range)(Range range, const size_t upTo) @safe unittest { - import std.algorithm : filter; + import std.algorithm.iteration : filter; import std.range : recurrence, take; //hasLength Range @@ -1624,9 +1680,11 @@ auto walkLength(Range)(Range range, const size_t upTo) $(D popBackN) will behave the same but instead removes elements from the back of the (bidirectional) range instead of the front. + + See_Also: $(REF drop, std, range), $(REF dropBack, std, range) */ size_t popFrontN(Range)(ref Range r, size_t n) - if (isInputRange!Range) +if (isInputRange!Range) { static if (hasLength!Range) { @@ -1662,7 +1720,7 @@ size_t popFrontN(Range)(ref Range r, size_t n) /// ditto size_t popBackN(Range)(ref Range r, size_t n) - if (isBidirectionalRange!Range) +if (isBidirectionalRange!Range) { static if (hasLength!Range) { @@ -1709,7 +1767,7 @@ size_t popBackN(Range)(ref Range r, size_t n) /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : iota; auto LL = iota(1L, 7L); auto r = popFrontN(LL, 2); @@ -1730,7 +1788,7 @@ size_t popBackN(Range)(ref Range r, size_t n) /// @safe unittest { - import std.algorithm : equal; + import std.algorithm.comparison : equal; import std.range : iota; auto LL = iota(1L, 7L); auto r = popBackN(LL, 2); @@ -1755,9 +1813,11 @@ size_t popBackN(Range)(ref Range r, size_t n) $(D popBackExactly) will behave the same but instead removes elements from the back of the (bidirectional) range instead of the front. + + See_Also: $(REF dropExcatly, std, range), $(REF dropBackExactly, std, range) */ void popFrontExactly(Range)(ref Range r, size_t n) - if (isInputRange!Range) +if (isInputRange!Range) { static if (hasLength!Range) assert(n <= r.length, "range is smaller than amount of items to pop"); @@ -1773,7 +1833,7 @@ void popFrontExactly(Range)(ref Range r, size_t n) /// ditto void popBackExactly(Range)(ref Range r, size_t n) - if (isBidirectionalRange!Range) +if (isBidirectionalRange!Range) { static if (hasLength!Range) assert(n <= r.length, "range is smaller than amount of items to pop"); @@ -1790,7 +1850,8 @@ void popBackExactly(Range)(ref Range r, size_t n) /// @safe unittest { - import std.algorithm : filterBidirectional, equal; + import std.algorithm.iteration : filterBidirectional; + import std.algorithm.comparison : equal; auto a = [1, 2, 3]; a.popFrontExactly(1); @@ -1818,14 +1879,21 @@ void popBackExactly(Range)(ref Range r, size_t n) */ ElementType!R moveFront(R)(R r) { - static if (is(typeof(&r.moveFront))) { + static if (is(typeof(&r.moveFront))) + { return r.moveFront(); - } else static if (!hasElaborateCopyConstructor!(ElementType!R)) { + } + else static if (!hasElaborateCopyConstructor!(ElementType!R)) + { return r.front; - } else static if (is(typeof(&(r.front())) == ElementType!R*)) { - import std.algorithm : move; + } + else static if (is(typeof(&(r.front())) == ElementType!R*)) + { + import std.algorithm.mutation : move; return move(r.front); - } else { + } + else + { static assert(0, "Cannot move front of a range with a postblit and an rvalue front."); } @@ -1836,16 +1904,18 @@ ElementType!R moveFront(R)(R r) { auto a = [ 1, 2, 3 ]; assert(moveFront(a) == 1); + assert(a.length == 3); // define a perfunctory input range struct InputRange { - @property bool empty() { return false; } - @property int front() { return 42; } + enum bool empty = false; + enum int front = 7; void popFront() {} int moveFront() { return 43; } } InputRange r; + // calls r.moveFront assert(moveFront(r) == 43); } @@ -1867,14 +1937,21 @@ ElementType!R moveFront(R)(R r) */ ElementType!R moveBack(R)(R r) { - static if (is(typeof(&r.moveBack))) { + static if (is(typeof(&r.moveBack))) + { return r.moveBack(); - } else static if (!hasElaborateCopyConstructor!(ElementType!R)) { + } + else static if (!hasElaborateCopyConstructor!(ElementType!R)) + { return r.back; - } else static if (is(typeof(&(r.back())) == ElementType!R*)) { - import std.algorithm : move; + } + else static if (is(typeof(&(r.back())) == ElementType!R*)) + { + import std.algorithm.mutation : move; return move(r.back); - } else { + } + else + { static assert(0, "Cannot move back of a range with a postblit and an rvalue back."); } @@ -1901,19 +1978,26 @@ ElementType!R moveBack(R)(R r) /** Moves element at index $(D i) of $(D r) out and returns it. Leaves $(D - r.front) in a destroyable state that does not allocate any resources + r[i]) in a destroyable state that does not allocate any resources (usually equal to its $(D .init) value). */ -ElementType!R moveAt(R, I)(R r, I i) if (isIntegral!I) +ElementType!R moveAt(R)(R r, size_t i) { - static if (is(typeof(&r.moveAt))) { + static if (is(typeof(&r.moveAt))) + { return r.moveAt(i); - } else static if (!hasElaborateCopyConstructor!(ElementType!(R))) { + } + else static if (!hasElaborateCopyConstructor!(ElementType!(R))) + { return r[i]; - } else static if (is(typeof(&r[i]) == ElementType!R*)) { - import std.algorithm : move; + } + else static if (is(typeof(&r[i]) == ElementType!R*)) + { + import std.algorithm.mutation : move; return move(r[i]); - } else { + } + else + { static assert(0, "Cannot move element of a range with a postblit and rvalue elements."); } @@ -1923,7 +2007,7 @@ ElementType!R moveAt(R, I)(R r, I i) if (isIntegral!I) @safe unittest { auto a = [1,2,3,4]; - foreach(idx, it; a) + foreach (idx, it; a) { assert(it == moveAt(a, idx)); } @@ -1933,15 +2017,18 @@ ElementType!R moveAt(R, I)(R r, I i) if (isIntegral!I) { import std.internal.test.dummyrange; - foreach(DummyType; AllDummyRanges) { + foreach (DummyType; AllDummyRanges) + { auto d = DummyType.init; assert(moveFront(d) == 1); - static if (isBidirectionalRange!DummyType) { + static if (isBidirectionalRange!DummyType) + { assert(moveBack(d) == 10); } - static if (isRandomAccessRange!DummyType) { + static if (isRandomAccessRange!DummyType) + { assert(moveAt(d, 2) == 3); } } @@ -1953,8 +2040,7 @@ arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.empty) is equivalent to $(D empty(array)). */ - -@property bool empty(T)(in T[] a) @safe pure nothrow +@property bool empty(T)(in T[] a) @safe pure nothrow @nogc { return !a.length; } @@ -1974,8 +2060,7 @@ the first argument using the dot notation, $(D array.save) is equivalent to $(D save(array)). The function does not duplicate the content of the array, it simply returns its argument. */ - -@property T[] save(T)(T[] a) @safe pure nothrow +@property T[] save(T)(T[] a) @safe pure nothrow @nogc { return a; } @@ -1987,6 +2072,7 @@ content of the array, it simply returns its argument. auto b = a.save; assert(b is a); } + /** Implements the range interface primitive $(D popFront) for built-in arrays. Due to the fact that nonmember functions can be called with @@ -1995,8 +2081,7 @@ equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings), $(D popFront) automatically advances to the next $(GLOSSARY code point). */ - -void popFront(T)(ref T[] a) @safe pure nothrow +void popFront(T)(ref T[] a) @safe pure nothrow @nogc if (!isNarrowString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); @@ -2018,52 +2103,55 @@ version(unittest) static assert(!is(typeof({ void[] a; popFront(a); }))); } -// Specialization for narrow strings. The necessity of +/// ditto void popFront(C)(ref C[] str) @trusted pure nothrow if (isNarrowString!(C[])) { + import std.algorithm.comparison : min; + assert(str.length, "Attempting to popFront() past the end of an array of " ~ C.stringof); - static if(is(Unqual!C == char)) + static if (is(Unqual!C == char)) { + __gshared static immutable ubyte[] charWidthTab = [ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 + ]; + immutable c = str[0]; - if(c < 0x80) + if (c < 192) { - //ptr is used to avoid unnnecessary bounds checking. str = str.ptr[1 .. str.length]; } else { - import core.bitop : bsr; - auto msbs = 7 - bsr(~c); - if((msbs < 2) | (msbs > 6)) - { - //Invalid UTF-8 - msbs = 1; - } - str = str[msbs .. $]; + str = str.ptr[min(str.length, charWidthTab.ptr[c - 192]) .. str.length]; } + } - else static if(is(Unqual!C == wchar)) + else static if (is(Unqual!C == wchar)) { immutable u = str[0]; - str = str[1 + (u >= 0xD800 && u <= 0xDBFF) .. $]; + immutable seqLen = 1 + (u >= 0xD800 && u <= 0xDBFF); + str = str.ptr[min(seqLen, str.length) .. str.length]; } else static assert(0, "Bad template constraint."); } @safe pure unittest { - import std.typetuple; + import std.meta : AliasSeq; - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { S s = "\xC2\xA9hello"; s.popFront(); assert(s == "hello"); S str = "hello\U00010143\u0100\U00010143"; - foreach(dchar c; ['h', 'e', 'l', 'l', 'o', '\U00010143', '\u0100', '\U00010143']) + foreach (dchar c; ['h', 'e', 'l', 'l', 'o', '\U00010143', '\u0100', '\U00010143']) { assert(str.front == c); str.popFront(); @@ -2076,7 +2164,7 @@ if (isNarrowString!(C[])) C[] _eatString(C)(C[] str) { - while(!str.empty) + while (!str.empty) str.popFront(); return str; @@ -2087,6 +2175,26 @@ if (isNarrowString!(C[])) static assert(checkCTFEW.empty); } +@safe unittest // issue 16090 +{ + string s = "\u00E4"; + assert(s.length == 2); + s = s[0 .. 1]; + assert(s.length == 1); + s.popFront; + assert(s.empty); +} + +@safe unittest +{ + wstring s = "\U00010000"; + assert(s.length == 2); + s = s[0 .. 1]; + assert(s.length == 1); + s.popFront; + assert(s.empty); +} + /** Implements the range interface primitive $(D popBack) for built-in arrays. Due to the fact that nonmember functions can be called with @@ -2094,8 +2202,7 @@ the first argument using the dot notation, $(D array.popBack) is equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D popFront) automatically eliminates the last $(GLOSSARY code point). */ - -void popBack(T)(ref T[] a) @safe pure nothrow +void popBack(T)(ref T[] a) @safe pure nothrow @nogc if (!isNarrowString!(T[]) && !is(T[] == void[])) { assert(a.length); @@ -2117,19 +2224,20 @@ version(unittest) static assert(!is(typeof({ void[] a; popBack(a); }))); } -// Specialization for arrays of char +/// ditto void popBack(T)(ref T[] a) @safe pure if (isNarrowString!(T[])) { + import std.utf : strideBack; assert(a.length, "Attempting to popBack() past the front of an array of " ~ T.stringof); - a = a[0 .. $ - std.utf.strideBack(a, $)]; + a = a[0 .. $ - strideBack(a, $)]; } @safe pure unittest { - import std.typetuple; + import std.meta : AliasSeq; - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { S s = "hello\xE2\x89\xA0"; s.popBack(); @@ -2141,7 +2249,7 @@ if (isNarrowString!(T[])) assert(s3 == ""); S str = "\U00010143\u0100\U00010143hello"; - foreach(dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143']) + foreach (dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143']) { assert(str.back == ch); str.popBack(); @@ -2158,10 +2266,10 @@ Implements the range interface primitive $(D front) for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.front) is equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D -front) automatically returns the first $(GLOSSARY code point) as a $(D +front) automatically returns the first $(GLOSSARY code point) as _a $(D dchar). */ -@property ref T front(T)(T[] a) @safe pure nothrow +@property ref T front(T)(T[] a) @safe pure nothrow @nogc if (!isNarrowString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); @@ -2189,7 +2297,9 @@ if (!isNarrowString!(T[]) && !is(T[] == void[])) assert(c.front == 1); } -@property dchar front(T)(T[] a) @safe pure if (isNarrowString!(T[])) +/// ditto +@property dchar front(T)(T[] a) @safe pure +if (isNarrowString!(T[])) { import std.utf : decode; assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); @@ -2202,10 +2312,11 @@ Implements the range interface primitive $(D back) for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.back) is equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D -back) automatically returns the last $(GLOSSARY code point) as a $(D +back) automatically returns the last $(GLOSSARY code point) as _a $(D dchar). */ -@property ref T back(T)(T[] a) @safe pure nothrow if (!isNarrowString!(T[])) +@property ref T back(T)(T[] a) @safe pure nothrow @nogc +if (!isNarrowString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); return a[$ - 1]; @@ -2229,11 +2340,13 @@ dchar). assert(c.back == 3); } +/// ditto // Specialization for strings -@property dchar back(T)(T[] a) @safe pure if (isNarrowString!(T[])) +@property dchar back(T)(T[] a) @safe pure +if (isNarrowString!(T[])) { - import std.utf : decode; + import std.utf : decode, strideBack; assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof); - size_t i = a.length - std.utf.strideBack(a, a.length); + size_t i = a.length - strideBack(a, a.length); return decode(a, i); } diff --git a/std/regex/internal/backtracking.d b/std/regex/internal/backtracking.d index 9e2cd656e73..c13e371e2ee 100644 --- a/std/regex/internal/backtracking.d +++ b/std/regex/internal/backtracking.d @@ -7,7 +7,7 @@ module std.regex.internal.backtracking; package(std.regex): import std.regex.internal.ir; -import std.range, std.typecons, std.traits, core.stdc.stdlib; +import std.range.primitives, std.typecons, std.traits, core.stdc.stdlib; /+ BacktrackingMatcher implements backtracking scheme of matching @@ -16,7 +16,7 @@ import std.range, std.typecons, std.traits, core.stdc.stdlib; template BacktrackingMatcher(bool CTregex) { @trusted struct BacktrackingMatcher(Char, Stream = Input!Char) - if(is(Char : dchar)) + if (is(Char : dchar)) { alias DataIndex = Stream.DataIndex; struct State @@ -26,12 +26,12 @@ template BacktrackingMatcher(bool CTregex) } static assert(State.sizeof % size_t.sizeof == 0); enum stateSize = State.sizeof / size_t.sizeof; - enum initialStack = 1<<11; // items in a block of segmented stack - alias const(Char)[] String; + enum initialStack = 1 << 11; // items in a block of segmented stack + alias String = const(Char)[]; alias RegEx = Regex!Char; alias MatchFn = bool function (ref BacktrackingMatcher!(Char, Stream)); RegEx re; //regex program - static if(CTregex) + static if (CTregex) MatchFn nativeFn; //native code for that program //Stream state Stream s; @@ -41,14 +41,36 @@ template BacktrackingMatcher(bool CTregex) //backtracking machine state uint pc, counter; DataIndex lastState = 0; //top of state stack - DataIndex[] trackers; - static if(!CTregex) + static if (!CTregex) uint infiniteNesting; size_t[] memory; + Trace[] merge; + static struct Trace + { + ulong mask; + size_t offset; + + bool mark(size_t idx) + { + immutable d = idx - offset; + if (d < 64) // including overflow + { + immutable p = mask & (1UL << d); + mask |= 1UL << d; + return p != 0; + } + else + { + offset = idx; + mask = 1; + return false; + } + } + } //local slice of matches, global for backref Group!DataIndex[] matches, backrefed; - static if(__traits(hasMember,Stream, "search")) + static if (__traits(hasMember,Stream, "search")) { enum kicked = true; } @@ -57,13 +79,14 @@ template BacktrackingMatcher(bool CTregex) static size_t initialMemory(const ref RegEx re) { - return (re.ngroup+1)*DataIndex.sizeof //trackers - + stackSize(re)*size_t.sizeof; + return stackSize(re)*size_t.sizeof + re.hotspotTableSize*Trace.sizeof; } static size_t stackSize(const ref RegEx re) { - return initialStack*(stateSize + re.ngroup*(Group!DataIndex).sizeof/size_t.sizeof)+1; + size_t itemSize = stateSize + + re.ngroup * (Group!DataIndex).sizeof / size_t.sizeof; + return initialStack * itemSize + 2; } @property bool atStart(){ return index == 0; } @@ -72,15 +95,15 @@ template BacktrackingMatcher(bool CTregex) void next() { - if(!s.nextChar(front, index)) + if (!s.nextChar(front, index)) index = s.lastIndex; } void search() { - static if(kicked) + static if (kicked) { - if(!s.search(re.kickstart, front, index)) + if (!s.search(re.kickstart, front, index)) { index = s.lastIndex; } @@ -94,15 +117,40 @@ template BacktrackingMatcher(bool CTregex) { auto chunk = mallocArray!(size_t)(stackSize(re)); chunk[0] = cast(size_t)(memory.ptr); - memory = chunk[1..$]; + chunk[1] = lastState; + memory = chunk[2..$]; + lastState = 0; + } + + bool prevStack() + { + // pointer to previous block + size_t* prev = cast(size_t*) memory.ptr[-2]; + if (!prev) + { + // The last segment is freed in RegexMatch + return false; + } + else + { + import core.stdc.stdlib : free; + // memory used in previous block + size_t size = memory.ptr[-1]; + free(memory.ptr-2); + memory = prev[0 .. size]; + lastState = size; + return true; + } } void initExternalMemory(void[] memBlock) { - trackers = arrayInChunk!(DataIndex)(re.ngroup+1, memBlock); - memory = cast(size_t[])memBlock; - memory[0] = 0; //hidden pointer - memory = memory[1..$]; + merge = arrayInChunk!(Trace)(re.hotspotTableSize, memBlock); + merge[] = Trace.init; + memory = cast(size_t[]) memBlock; + memory[0] = 0; // hidden pointer + memory[1] = 0; // used size + memory = memory[2..$]; } void initialize(ref RegEx program, Stream stream, void[] memBlock) @@ -152,60 +200,61 @@ template BacktrackingMatcher(bool CTregex) } // - bool matchFinalize() + int matchFinalize() { - size_t start = index; - if(matchImpl()) + immutable start = index; + immutable val = matchImpl(); + if (val) {//stream is updated here matches[0].begin = start; matches[0].end = index; - if(!(re.flags & RegexOption.global) || atEnd) + if (!(re.flags & RegexOption.global) || atEnd) exhausted = true; - if(start == index)//empty match advances input + if (start == index)//empty match advances input next(); - return true; + return val; } else - return false; + return 0; } //lookup next match, fill matches with indices into input - bool match(Group!DataIndex[] matches) + int match(Group!DataIndex[] matches) { debug(std_regex_matcher) { writeln("------------------------------------------"); } - if(exhausted) //all matches collected + if (exhausted) //all matches collected return false; this.matches = matches; - if(re.flags & RegexInfo.oneShot) + if (re.flags & RegexInfo.oneShot) { exhausted = true; - DataIndex start = index; - auto m = matchImpl(); - if(m) + const DataIndex start = index; + immutable m = matchImpl(); + if (m) { matches[0].begin = start; matches[0].end = index; } return m; } - static if(kicked) + static if (kicked) { - if(!re.kickstart.empty) + if (!re.kickstart.empty) { - for(;;) + for (;;) { - - if(matchFinalize()) - return true; + immutable val = matchFinalize(); + if (val) + return val; else { - if(atEnd) + if (atEnd) break; search(); - if(atEnd) + if (atEnd) { exhausted = true; return matchFinalize(); @@ -213,20 +262,21 @@ template BacktrackingMatcher(bool CTregex) } } exhausted = true; - return false; //early return + return 0; //early return } } //no search available - skip a char at a time - for(;;) + for (;;) { - if(matchFinalize()) - return true; + immutable val = matchFinalize(); + if (val) + return val; else { - if(atEnd) + if (atEnd) break; next(); - if(atEnd) + if (atEnd) { exhausted = true; return matchFinalize(); @@ -234,16 +284,16 @@ template BacktrackingMatcher(bool CTregex) } } exhausted = true; - return false; + return 0; } /+ match subexpression against input, results are stored in matches +/ - bool matchImpl() + int matchImpl() { - static if(CTregex && is(typeof(nativeFn(this)))) + static if (CTregex && is(typeof(nativeFn(this)))) { debug(std_regex_ctr) writeln("using C-T matcher"); return nativeFn(this); @@ -253,55 +303,54 @@ template BacktrackingMatcher(bool CTregex) pc = 0; counter = 0; lastState = 0; - infiniteNesting = -1;//intentional + matches[] = Group!DataIndex.init; auto start = s._index; debug(std_regex_matcher) - writeln("Try match starting at ", s[index..s.lastIndex]); - for(;;) + writeln("Try match starting at ", s[index .. s.lastIndex]); + for (;;) { debug(std_regex_matcher) writefln("PC: %s\tCNT: %s\t%s \tfront: %s src: %s", pc, counter, disassemble(re.ir, pc, re.dict), front, s._index); - switch(re.ir[pc].code) + switch (re.ir[pc].code) { case IR.OrChar://assumes IRL!(OrChar) == 1 - if(atEnd) + if (atEnd) goto L_backtrack; uint len = re.ir[pc].sequence; uint end = pc + len; - if(re.ir[pc].data != front && re.ir[pc+1].data != front) + if (re.ir[pc].data != front && re.ir[pc+1].data != front) { - for(pc = pc+2; pc < end; pc++) - if(re.ir[pc].data == front) + for (pc = pc+2; pc < end; pc++) + if (re.ir[pc].data == front) break; - if(pc == end) + if (pc == end) goto L_backtrack; } pc = end; next(); break; case IR.Char: - if(atEnd || front != re.ir[pc].data) + if (atEnd || front != re.ir[pc].data) goto L_backtrack; pc += IRL!(IR.Char); next(); break; case IR.Any: - if(atEnd || (!(re.flags & RegexOption.singleline) - && (front == '\r' || front == '\n'))) + if (atEnd) goto L_backtrack; pc += IRL!(IR.Any); next(); break; case IR.CodepointSet: - if(atEnd || !re.charsets[re.ir[pc].data].scanFor(front)) + if (atEnd || !re.charsets[re.ir[pc].data].scanFor(front)) goto L_backtrack; next(); pc += IRL!(IR.CodepointSet); break; case IR.Trie: - if(atEnd || !re.tries[re.ir[pc].data][front]) + if (atEnd || !re.matchers[re.ir[pc].data][front]) goto L_backtrack; next(); pc += IRL!(IR.Trie); @@ -310,22 +359,22 @@ template BacktrackingMatcher(bool CTregex) dchar back; DataIndex bi; //at start & end of input - if(atStart && wordTrie[front]) + if (atStart && wordMatcher[front]) { pc += IRL!(IR.Wordboundary); break; } - else if(atEnd && s.loopBack(index).nextChar(back, bi) - && wordTrie[back]) + else if (atEnd && s.loopBack(index).nextChar(back, bi) + && wordMatcher[back]) { pc += IRL!(IR.Wordboundary); break; } - else if(s.loopBack(index).nextChar(back, bi)) + else if (s.loopBack(index).nextChar(back, bi)) { - bool af = wordTrie[front]; - bool ab = wordTrie[back]; - if(af ^ ab) + immutable af = wordMatcher[front]; + immutable ab = wordMatcher[back]; + if (af ^ ab) { pc += IRL!(IR.Wordboundary); break; @@ -336,27 +385,32 @@ template BacktrackingMatcher(bool CTregex) dchar back; DataIndex bi; //at start & end of input - if(atStart && wordTrie[front]) + if (atStart && wordMatcher[front]) goto L_backtrack; - else if(atEnd && s.loopBack(index).nextChar(back, bi) - && wordTrie[back]) + else if (atEnd && s.loopBack(index).nextChar(back, bi) + && wordMatcher[back]) goto L_backtrack; - else if(s.loopBack(index).nextChar(back, bi)) + else if (s.loopBack(index).nextChar(back, bi)) { - bool af = wordTrie[front]; - bool ab = wordTrie[back]; - if(af ^ ab) + immutable af = wordMatcher[front]; + immutable ab = wordMatcher[back]; + if (af ^ ab) goto L_backtrack; } pc += IRL!(IR.Wordboundary); break; + case IR.Bof: + if (atStart) + pc += IRL!(IR.Bol); + else + goto L_backtrack; + break; case IR.Bol: dchar back; DataIndex bi; - if(atStart) + if (atStart) pc += IRL!(IR.Bol); - else if((re.flags & RegexOption.multiline) - && s.loopBack(index).nextChar(back,bi) + else if (s.loopBack(index).nextChar(back,bi) && endOfLine(back, front == '\n')) { pc += IRL!(IR.Bol); @@ -364,13 +418,18 @@ template BacktrackingMatcher(bool CTregex) else goto L_backtrack; break; + case IR.Eof: + if (atEnd) + pc += IRL!(IR.Eol); + else + goto L_backtrack; + break; case IR.Eol: dchar back; DataIndex bi; - debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index..s.lastIndex]); + debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index .. s.lastIndex]); //no matching inside \r\n - if(atEnd || ((re.flags & RegexOption.multiline) - && endOfLine(front, s.loopBack(index).nextChar(back,bi) + if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi) && back == '\r'))) { pc += IRL!(IR.Eol); @@ -379,49 +438,52 @@ template BacktrackingMatcher(bool CTregex) goto L_backtrack; break; case IR.InfiniteStart, IR.InfiniteQStart: - trackers[infiniteNesting+1] = index; pc += re.ir[pc].data + IRL!(IR.InfiniteStart); - //now pc is at end IR.Infininite(Q)End + //now pc is at end IR.Infinite(Q)End uint len = re.ir[pc].data; - int test; - if(re.ir[pc].code == IR.InfiniteEnd) + if (re.ir[pc].code == IR.InfiniteEnd) { - test = quickTestFwd(pc+IRL!(IR.InfiniteEnd), front, re); - if(test >= 0) - pushState(pc+IRL!(IR.InfiniteEnd), counter); - infiniteNesting++; + pushState(pc+IRL!(IR.InfiniteEnd), counter); pc -= len; } else { - test = quickTestFwd(pc - len, front, re); - if(test >= 0) - { - infiniteNesting++; - pushState(pc - len, counter); - infiniteNesting--; - } + pushState(pc - len, counter); pc += IRL!(IR.InfiniteEnd); } break; + case IR.InfiniteBloomStart: + pc += re.ir[pc].data + IRL!(IR.InfiniteBloomStart); + //now pc is at end IR.InfiniteBloomEnd + immutable len = re.ir[pc].data; + immutable filterIdx = re.ir[pc+2].raw; + if (re.filters[filterIdx][front]) + pushState(pc+IRL!(IR.InfiniteBloomEnd), counter); + pc -= len; + break; case IR.RepeatStart, IR.RepeatQStart: pc += re.ir[pc].data + IRL!(IR.RepeatStart); break; case IR.RepeatEnd: case IR.RepeatQEnd: + if (merge[re.ir[pc + 1].raw+counter].mark(index)) + { + // merged! + goto L_backtrack; + } //len, step, min, max - uint len = re.ir[pc].data; - uint step = re.ir[pc+2].raw; - uint min = re.ir[pc+3].raw; - uint max = re.ir[pc+4].raw; - if(counter < min) + immutable len = re.ir[pc].data; + immutable step = re.ir[pc+2].raw; + immutable min = re.ir[pc+3].raw; + immutable max = re.ir[pc+4].raw; + if (counter < min) { counter += step; pc -= len; } - else if(counter < max) + else if (counter < max) { - if(re.ir[pc].code == IR.RepeatEnd) + if (re.ir[pc].code == IR.RepeatEnd) { pushState(pc + IRL!(IR.RepeatEnd), counter%step); counter += step; @@ -442,48 +504,55 @@ template BacktrackingMatcher(bool CTregex) break; case IR.InfiniteEnd: case IR.InfiniteQEnd: - uint len = re.ir[pc].data; debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting); - assert(infiniteNesting < trackers.length); - - if(trackers[infiniteNesting] == index) - {//source not consumed - pc += IRL!(IR.InfiniteEnd); - infiniteNesting--; - break; + if (merge[re.ir[pc + 1].raw+counter].mark(index)) + { + // merged! + goto L_backtrack; } - else - trackers[infiniteNesting] = index; - int test; - if(re.ir[pc].code == IR.InfiniteEnd) + immutable len = re.ir[pc].data; + if (re.ir[pc].code == IR.InfiniteEnd) { - test = quickTestFwd(pc+IRL!(IR.InfiniteEnd), front, re); - if(test >= 0) - { - infiniteNesting--; - pushState(pc + IRL!(IR.InfiniteEnd), counter); - infiniteNesting++; - } + pushState(pc + IRL!(IR.InfiniteEnd), counter); pc -= len; } else { - test = quickTestFwd(pc-len, front, re); - if(test >= 0) - pushState(pc-len, counter); + pushState(pc-len, counter); pc += IRL!(IR.InfiniteEnd); + } + break; + case IR.InfiniteBloomEnd: + debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting); + if (merge[re.ir[pc + 1].raw+counter].mark(index)) + { + // merged! + goto L_backtrack; + } + immutable len = re.ir[pc].data; + immutable filterIdx = re.ir[pc+2].raw; + if (re.filters[filterIdx][front]) + { infiniteNesting--; + pushState(pc + IRL!(IR.InfiniteBloomEnd), counter); + infiniteNesting++; } + pc -= len; break; case IR.OrEnd: + if (merge[re.ir[pc + 1].raw+counter].mark(index)) + { + // merged! + goto L_backtrack; + } pc += IRL!(IR.OrEnd); break; case IR.OrStart: pc += IRL!(IR.OrStart); goto case; case IR.Option: - uint len = re.ir[pc].data; - if(re.ir[pc+len].code == IR.GotoEndOr)//not a last one + immutable len = re.ir[pc].data; + if (re.ir[pc+len].code == IR.GotoEndOr)//not a last one { pushState(pc + len + IRL!(IR.Option), counter); //remember 2nd branch } @@ -493,25 +562,25 @@ template BacktrackingMatcher(bool CTregex) pc = pc + re.ir[pc].data + IRL!(IR.GotoEndOr); break; case IR.GroupStart: - uint n = re.ir[pc].data; + immutable n = re.ir[pc].data; matches[n].begin = index; debug(std_regex_matcher) writefln("IR group #%u starts at %u", n, index); pc += IRL!(IR.GroupStart); break; case IR.GroupEnd: - uint n = re.ir[pc].data; + immutable n = re.ir[pc].data; matches[n].end = index; debug(std_regex_matcher) writefln("IR group #%u ends at %u", n, index); pc += IRL!(IR.GroupEnd); break; case IR.LookaheadStart: case IR.NeglookaheadStart: - uint len = re.ir[pc].data; + immutable len = re.ir[pc].data; auto save = index; - uint ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw; - auto mem = malloc(initialMemory(re))[0..initialMemory(re)]; + immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw; + auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)]; scope(exit) free(mem.ptr); - static if(Stream.isLoopback) + static if (Stream.isLoopback) { auto matcher = bwdMatcher(this, mem); } @@ -521,11 +590,13 @@ template BacktrackingMatcher(bool CTregex) } matcher.matches = matches[ms .. me]; matcher.backrefed = backrefed.empty ? matches : backrefed; - matcher.re.ir = re.ir[pc+IRL!(IR.LookaheadStart) .. pc+IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd)]; - bool match = matcher.matchImpl() ^ (re.ir[pc].code == IR.NeglookaheadStart); + matcher.re.ir = re.ir[ + pc+IRL!(IR.LookaheadStart) .. pc+IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd) + ]; + immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookaheadStart); s.reset(save); next(); - if(!match) + if (!match) goto L_backtrack; else { @@ -534,11 +605,11 @@ template BacktrackingMatcher(bool CTregex) break; case IR.LookbehindStart: case IR.NeglookbehindStart: - uint len = re.ir[pc].data; - uint ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw; - auto mem = malloc(initialMemory(re))[0..initialMemory(re)]; + immutable len = re.ir[pc].data; + immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw; + auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)]; scope(exit) free(mem.ptr); - static if(Stream.isLoopback) + static if (Stream.isLoopback) { alias Matcher = BacktrackingMatcher!(Char, Stream); auto matcher = Matcher(re, s, mem, front, index); @@ -549,10 +620,12 @@ template BacktrackingMatcher(bool CTregex) auto matcher = Matcher(re, s.loopBack(index), mem); } matcher.matches = matches[ms .. me]; - matcher.re.ir = re.ir[pc + IRL!(IR.LookbehindStart) .. pc + IRL!(IR.LookbehindStart) + len + IRL!(IR.LookbehindEnd)]; + matcher.re.ir = re.ir[ + pc + IRL!(IR.LookbehindStart) .. pc + IRL!(IR.LookbehindStart) + len + IRL!(IR.LookbehindEnd) + ]; matcher.backrefed = backrefed.empty ? matches : backrefed; - bool match = matcher.matchImpl() ^ (re.ir[pc].code == IR.NeglookbehindStart); - if(!match) + immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookbehindStart); + if (!match) goto L_backtrack; else { @@ -560,16 +633,16 @@ template BacktrackingMatcher(bool CTregex) } break; case IR.Backref: - uint n = re.ir[pc].data; + immutable n = re.ir[pc].data; auto referenced = re.ir[pc].localRef ? s[matches[n].begin .. matches[n].end] : s[backrefed[n].begin .. backrefed[n].end]; - while(!atEnd && !referenced.empty && front == referenced.front) + while (!atEnd && !referenced.empty && front == referenced.front) { next(); referenced.popFront(); } - if(referenced.empty) + if (referenced.empty) pc++; else goto L_backtrack; @@ -582,15 +655,17 @@ template BacktrackingMatcher(bool CTregex) case IR.LookbehindEnd: case IR.NeglookbehindEnd: case IR.End: - return true; + // cleanup stale stack blocks if any + while (prevStack()) {} + return re.ir[pc].data; default: debug printBytecode(re.ir[0..$]); assert(0); L_backtrack: - if(!popState()) + if (!popState()) { s.reset(start); - return false; + return 0; } } } @@ -603,22 +678,8 @@ template BacktrackingMatcher(bool CTregex) return memory.length - lastState; } - bool prevStack() - { - import core.stdc.stdlib; - size_t* prev = memory.ptr-1; - prev = cast(size_t*)*prev;//take out hidden pointer - if(!prev) - return false; - free(memory.ptr);//last segment is freed in RegexMatch - immutable size = initialStack*(stateSize + 2*re.ngroup); - memory = prev[0..size]; - lastState = size; - return true; - } - void stackPush(T)(T val) - if(!isDynamicArray!T) + if (!isDynamicArray!T) { *cast(T*)&memory[lastState] = val; enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; @@ -629,14 +690,14 @@ template BacktrackingMatcher(bool CTregex) void stackPush(T)(T[] val) { static assert(T.sizeof % size_t.sizeof == 0); - (cast(T*)&memory[lastState])[0..val.length] + (cast(T*)&memory[lastState])[0 .. val.length] = val[0..$]; lastState += val.length*(T.sizeof/size_t.sizeof); debug(std_regex_matcher) writeln("push array SP= ", lastState); } void stackPop(T)(ref T val) - if(!isDynamicArray!T) + if (!isDynamicArray!T) { enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; lastState -= delta; @@ -651,47 +712,36 @@ template BacktrackingMatcher(bool CTregex) void stackPop(T)(ref T[] val) { lastState -= val.length*(T.sizeof/size_t.sizeof); - val[0..$] = (cast(T*)&memory[lastState])[0..val.length]; + val[0..$] = (cast(T*)&memory[lastState])[0 .. val.length]; debug(std_regex_matcher) writeln("pop array SP= ", lastState); } - static if(!CTregex) + static if (!CTregex) { //helper function, saves engine state void pushState(uint pc, uint counter) { - if(stateSize + trackers.length + matches.length > stackAvail) + if (stateSize + 2 * matches.length > stackAvail) { newStack(); - lastState = 0; } *cast(State*)&memory[lastState] = State(index, pc, counter, infiniteNesting); lastState += stateSize; - memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[])matches)[]; + memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[]) matches)[]; lastState += 2*matches.length; - if(trackers.length) - { - memory[lastState .. lastState + trackers.length] = trackers[]; - lastState += trackers.length; - } debug(std_regex_matcher) writefln("Saved(pc=%s) front: %s src: %s", - pc, front, s[index..s.lastIndex]); + pc, front, s[index .. s.lastIndex]); } //helper function, restores engine state bool popState() { - if(!lastState) - return prevStack(); - if (trackers.length) - { - lastState -= trackers.length; - trackers[] = memory[lastState .. lastState + trackers.length]; - } + if (!lastState && !prevStack()) + return false; lastState -= 2*matches.length; - auto pm = cast(size_t[])matches; + auto pm = cast(size_t[]) matches; pm[] = memory[lastState .. lastState + 2 * matches.length]; lastState -= stateSize; State* state = cast(State*)&memory[lastState]; @@ -702,14 +752,14 @@ template BacktrackingMatcher(bool CTregex) debug(std_regex_matcher) { writefln("Restored matches", front, s[index .. s.lastIndex]); - foreach(i, m; matches) + foreach (i, m; matches) writefln("Sub(%d) : %s..%s", i, m.begin, m.end); } s.reset(index); next(); debug(std_regex_matcher) writefln("Backtracked (pc=%s) front: %s src: %s", - pc, front, s[index..s.lastIndex]); + pc, front, s[index .. s.lastIndex]); return true; } } @@ -719,15 +769,15 @@ template BacktrackingMatcher(bool CTregex) //very shitty string formatter, $$ replaced with next argument converted to string @trusted string ctSub( U...)(string format, U args) { - import std.conv; + import std.conv : to; bool seenDollar; - foreach(i, ch; format) + foreach (i, ch; format) { - if(ch == '$') + if (ch == '$') { - if(seenDollar) + if (seenDollar) { - static if(args.length > 0) + static if (args.length > 0) { return format[0 .. i - 1] ~ to!string(args[0]) ~ ctSub(format[i + 1 .. $], args[1 .. $]); @@ -749,18 +799,17 @@ alias Sequence(int B, int E) = staticIota!(B, E); struct CtContext { - import std.conv; + import std.conv : to, text; //dirty flags - bool counter, infNesting; - // to make a unique advancement counter per nesting level of loops - int curInfLoop, nInfLoops; + bool counter; //to mark the portion of matches to save int match, total_matches; int reserved; + CodepointSet[] charsets; //state of codegenerator - struct CtState + static struct CtState { string code; int addr; @@ -771,6 +820,7 @@ struct CtContext match = 1; reserved = 1; //first match is skipped total_matches = re.ngroup; + charsets = re.charsets; } CtContext lookaround(uint s, uint e) @@ -791,13 +841,7 @@ struct CtContext stackPop(counter);" : " counter = 0;"; - if(infNesting) - { - text ~= ctSub(` - stackPop(trackers[0..$$]); - `, curInfLoop + 1); - } - if(match < total_matches) + if (match < total_matches) { text ~= ctSub(" stackPop(matches[$$..$$]);", reserved, match); @@ -814,23 +858,16 @@ struct CtContext string saveCode(uint pc, string count_expr="counter") { string text = ctSub(" - if(stackAvail < $$*(Group!(DataIndex)).sizeof/size_t.sizeof + trackers.length + $$) + if (stackAvail < $$*(Group!(DataIndex)).sizeof/size_t.sizeof + $$) { newStack(); - lastState = 0; - }", match - reserved, cast(int)counter + 2); - if(match < total_matches) + }", match - reserved, cast(int) counter + 2); + if (match < total_matches) text ~= ctSub(" stackPush(matches[$$..$$]);", reserved, match); else text ~= ctSub(" stackPush(matches[$$..$]);", reserved); - if(infNesting) - { - text ~= ctSub(` - stackPush(trackers[0..$$]); - `, curInfLoop + 1); - } text ~= counter ? ctSub(" stackPush($$);", count_expr) : ""; text ~= ctSub(" @@ -843,7 +880,7 @@ struct CtContext { CtState result; result.addr = addr; - while(!ir.empty) + while (!ir.empty) { auto n = ctGenGroup(ir, result.addr); result.code ~= n.code; @@ -855,39 +892,33 @@ struct CtContext // CtState ctGenGroup(ref Bytecode[] ir, int addr) { - import std.algorithm : max; + import std.algorithm.comparison : max; auto bailOut = "goto L_backtrack;"; auto nextInstr = ctSub("goto case $$;", addr+1); CtState r; assert(!ir.empty); - switch(ir[0].code) + switch (ir[0].code) { - case IR.InfiniteStart, IR.InfiniteQStart, IR.RepeatStart, IR.RepeatQStart: - bool infLoop = - ir[0].code == IR.InfiniteStart || ir[0].code == IR.InfiniteQStart; - infNesting = infNesting || infLoop; - if(infLoop) - { - curInfLoop++; - nInfLoops = max(nInfLoops, curInfLoop+1); - } + case IR.InfiniteStart, IR.InfiniteBloomStart,IR.InfiniteQStart, IR.RepeatStart, IR.RepeatQStart: + immutable infLoop = + ir[0].code == IR.InfiniteStart || ir[0].code == IR.InfiniteQStart || + ir[0].code == IR.InfiniteBloomStart; + counter = counter || ir[0].code == IR.RepeatStart || ir[0].code == IR.RepeatQStart; - uint len = ir[0].data; + immutable len = ir[0].data; auto nir = ir[ir[0].length .. ir[0].length+len]; r = ctGenBlock(nir, addr+1); - if(infLoop) - curInfLoop--; //start/end codegen //r.addr is at last test+ jump of loop, addr+1 is body of loop nir = ir[ir[0].length + len .. $]; - r.code = ctGenFixupCode(ir[0..ir[0].length], addr, r.addr) ~ r.code; + r.code = ctGenFixupCode(ir[0 .. ir[0].length], addr, r.addr) ~ r.code; r.code ~= ctGenFixupCode(nir, r.addr, addr+1); r.addr += 2; //account end instruction + restore state ir = nir; break; case IR.OrStart: - uint len = ir[0].data; + immutable len = ir[0].data; auto nir = ir[ir[0].length .. ir[0].length+len]; r = ctGenAlternation(nir, addr); ir = ir[ir[0].length + len .. $]; @@ -898,20 +929,20 @@ struct CtContext case IR.NeglookaheadStart: case IR.LookbehindStart: case IR.NeglookbehindStart: - uint len = ir[0].data; - bool behind = ir[0].code == IR.LookbehindStart || ir[0].code == IR.NeglookbehindStart; - bool negative = ir[0].code == IR.NeglookaheadStart || ir[0].code == IR.NeglookbehindStart; + immutable len = ir[0].data; + immutable behind = ir[0].code == IR.LookbehindStart || ir[0].code == IR.NeglookbehindStart; + immutable negative = ir[0].code == IR.NeglookaheadStart || ir[0].code == IR.NeglookbehindStart; string fwdType = "typeof(fwdMatcher(matcher, []))"; string bwdType = "typeof(bwdMatcher(matcher, []))"; string fwdCreate = "fwdMatcher(matcher, mem)"; string bwdCreate = "bwdMatcher(matcher, mem)"; - uint start = IRL!(IR.LookbehindStart); - uint end = IRL!(IR.LookbehindStart)+len+IRL!(IR.LookaheadEnd); + immutable start = IRL!(IR.LookbehindStart); + immutable end = IRL!(IR.LookbehindStart)+len+IRL!(IR.LookaheadEnd); CtContext context = lookaround(ir[1].raw, ir[2].raw); //split off new context auto slice = ir[start .. end]; r.code ~= ctSub(` case $$: //fake lookaround "atom" - static if(typeof(matcher.s).isLoopback) + static if (typeof(matcher.s).isLoopback) alias Lookaround = $$; else alias Lookaround = $$; @@ -922,19 +953,19 @@ struct CtContext //(neg)lookaround piece ends } auto save = index; - auto mem = malloc(initialMemory(re))[0..initialMemory(re)]; + auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)]; scope(exit) free(mem.ptr); - static if(typeof(matcher.s).isLoopback) + static if (typeof(matcher.s).isLoopback) auto lookaround = $$; else auto lookaround = $$; lookaround.matches = matches[$$..$$]; lookaround.backrefed = backrefed.empty ? matches : backrefed; lookaround.nativeFn = &matcher_$$; //hookup closure's binary code - bool match = $$; + int match = $$; s.reset(save); next(); - if(match) + if (match) $$ else $$`, addr, @@ -966,11 +997,11 @@ struct CtContext CtState[] pieces; CtState r; enum optL = IRL!(IR.Option); - for(;;) + for (;;) { assert(ir[0].code == IR.Option); auto len = ir[0].data; - if(optL+len < ir.length && ir[optL+len].code == IR.Option)//not a last option + if (optL+len < ir.length && ir[optL+len].code == IR.Option)//not a last option { auto nir = ir[optL .. optL+len-IRL!(IR.GotoEndOr)]; r = ctGenBlock(nir, addr+2);//space for Option + restore state @@ -988,7 +1019,7 @@ struct CtContext } } r = pieces[0]; - for(uint i = 1; i < pieces.length; i++) + for (uint i = 1; i < pieces.length; i++) { r.code ~= ctSub(` case $$: @@ -1012,22 +1043,42 @@ struct CtContext r = ctSub(` case $$: debug(std_regex_matcher) writeln("#$$");`, addr, addr); - switch(ir[0].code) + switch (ir[0].code) { - case IR.InfiniteStart, IR.InfiniteQStart: + case IR.InfiniteStart, IR.InfiniteQStart, IR.InfiniteBloomStart: r ~= ctSub( ` - trackers[$$] = DataIndex.max; - goto case $$;`, curInfLoop, fixup); + goto case $$;`, fixup); ir = ir[ir[0].length..$]; break; case IR.InfiniteEnd: testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1); r ~= ctSub( ` - if(trackers[$$] == index) - {//source not consumed - goto case $$; + if (merge[$$+counter].mark(index)) + { + // merged! + goto L_backtrack; + } + + $$ + { + $$ + } + goto case $$; + case $$: //restore state and go out of loop + $$ + goto case;`, ir[1].raw, testCode, saveCode(addr+1), fixup, + addr+1, restoreCode()); + ir = ir[ir[0].length..$]; + break; + case IR.InfiniteBloomEnd: + //TODO: check bloom filter and skip on failure + testCode = ctQuickTest(ir[IRL!(IR.InfiniteBloomEnd) .. $],addr + 1); + r ~= ctSub( ` + if (merge[$$+counter].mark(index)) + { + // merged! + goto L_backtrack; } - trackers[$$] = index; $$ { @@ -1036,20 +1087,19 @@ struct CtContext goto case $$; case $$: //restore state and go out of loop $$ - goto case;`, curInfLoop, addr+2, - curInfLoop, testCode, saveCode(addr+1), - fixup, addr+1, restoreCode()); + goto case;`, ir[1].raw, testCode, saveCode(addr+1), fixup, + addr+1, restoreCode()); ir = ir[ir[0].length..$]; break; case IR.InfiniteQEnd: testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1); auto altCode = testCode.length ? ctSub("else goto case $$;", fixup) : ""; r ~= ctSub( ` - if(trackers[$$] == index) - {//source not consumed - goto case $$; + if (merge[$$+counter].mark(index)) + { + // merged! + goto L_backtrack; } - trackers[$$] = index; $$ { @@ -1059,9 +1109,9 @@ struct CtContext $$ case $$://restore state and go inside loop $$ - goto case $$;`, curInfLoop, addr+2, - curInfLoop, testCode, saveCode(addr+1), - addr+2, altCode, addr+1, restoreCode(), fixup); + goto case $$;`, ir[1].raw, + testCode, saveCode(addr+1), addr+2, altCode, + addr+1, restoreCode(), fixup); ir = ir[ir[0].length..$]; break; case IR.RepeatStart, IR.RepeatQStart: @@ -1071,22 +1121,27 @@ struct CtContext break; case IR.RepeatEnd, IR.RepeatQEnd: //len, step, min, max - uint len = ir[0].data; - uint step = ir[2].raw; - uint min = ir[3].raw; - uint max = ir[4].raw; + immutable len = ir[0].data; + immutable step = ir[2].raw; + immutable min = ir[3].raw; + immutable max = ir[4].raw; r ~= ctSub(` - if(counter < $$) + if (merge[$$+counter].mark(index)) + { + // merged! + goto L_backtrack; + } + if (counter < $$) { debug(std_regex_matcher) writeln("RepeatEnd min case pc=", $$); counter += $$; goto case $$; - }`, min, addr, step, fixup); - if(ir[0].code == IR.RepeatEnd) + }`, ir[1].raw, min, addr, step, fixup); + if (ir[0].code == IR.RepeatEnd) { string counter_expr = ctSub("counter % $$", step); r ~= ctSub(` - else if(counter < $$) + else if (counter < $$) { $$ counter += $$; @@ -1097,7 +1152,7 @@ struct CtContext { string counter_expr = ctSub("counter % $$", step); r ~= ctSub(` - else if(counter < $$) + else if (counter < $$) { $$ counter = counter % $$; @@ -1138,13 +1193,13 @@ struct CtContext string ctQuickTest(Bytecode[] ir, int id) { uint pc = 0; - while(pc < ir.length && ir[pc].isAtom) + while (pc < ir.length && ir[pc].isAtom) { - if(ir[pc].code == IR.GroupStart || ir[pc].code == IR.GroupEnd) + if (ir[pc].code == IR.GroupStart || ir[pc].code == IR.GroupEnd) { pc++; } - else if(ir[pc].code == IR.Backref) + else if (ir[pc].code == IR.Backref) break; else { @@ -1154,7 +1209,7 @@ struct CtContext { $$ //$$ } - if(test_$$() >= 0)`, id, code.ptr ? code : "return 0;", + if (test_$$() >= 0)`, id, code.ptr ? code : "return 0;", ir[pc].mnemonic, id); } } @@ -1176,7 +1231,7 @@ struct CtContext { string code; string bailOut, nextInstr; - if(addr < 0) + if (addr < 0) { bailOut = "return -1;"; nextInstr = "return 0;"; @@ -1189,17 +1244,17 @@ struct CtContext case $$: debug(std_regex_matcher) writeln("#$$"); `, addr, addr); } - switch(ir[0].code) + switch (ir[0].code) { case IR.OrChar://assumes IRL!(OrChar) == 1 code ~= ctSub(` - if(atEnd) + if (atEnd) $$`, bailOut); - uint len = ir[0].sequence; - for(uint i = 0; i < len; i++) + immutable len = ir[0].sequence; + for (uint i = 0; i < len; i++) { code ~= ctSub( ` - if(front == $$) + if (front == $$) { $$ $$ @@ -1210,29 +1265,43 @@ struct CtContext break; case IR.Char: code ~= ctSub( ` - if(atEnd || front != $$) + if (atEnd || front != $$) $$ $$ $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr); break; case IR.Any: code ~= ctSub( ` - if(atEnd || (!(re.flags & RegexOption.singleline) + if (atEnd || (!(re.flags & RegexOption.singleline) && (front == '\r' || front == '\n'))) $$ $$ $$`, bailOut, addr >= 0 ? "next();" :"",nextInstr); break; case IR.CodepointSet: - code ~= ctSub( ` - if(atEnd || !re.charsets[$$].scanFor(front)) + if (charsets.length) + { + string name = `func_`~to!string(addr+1); + string funcCode = charsets[ir[0].data].toSourceCode(name); + code ~= ctSub( ` + static $$ + if (atEnd || !$$(front)) + $$ + $$ + $$`, funcCode, name, bailOut, addr >= 0 ? "next();" :"", nextInstr); + } + else + code ~= ctSub( ` + if (atEnd || !re.charsets[$$].scanFor(front)) $$ $$ $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr); break; case IR.Trie: + if (charsets.length && charsets[ir[0].data].byInterval.length <= 8) + goto case IR.CodepointSet; code ~= ctSub( ` - if(atEnd || !re.tries[$$][front]) + if (atEnd || !re.matchers[$$][front]) $$ $$ $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr); @@ -1241,20 +1310,20 @@ struct CtContext code ~= ctSub( ` dchar back; DataIndex bi; - if(atStart && wordTrie[front]) + if (atStart && wordMatcher[front]) { $$ } - else if(atEnd && s.loopBack(index).nextChar(back, bi) - && wordTrie[back]) + else if (atEnd && s.loopBack(index).nextChar(back, bi) + && wordMatcher[back]) { $$ } - else if(s.loopBack(index).nextChar(back, bi)) + else if (s.loopBack(index).nextChar(back, bi)) { - bool af = wordTrie[front]; - bool ab = wordTrie[back]; - if(af ^ ab) + bool af = wordMatcher[front]; + bool ab = wordMatcher[back]; + if (af ^ ab) { $$ } @@ -1266,16 +1335,16 @@ struct CtContext dchar back; DataIndex bi; //at start & end of input - if(atStart && wordTrie[front]) + if (atStart && wordMatcher[front]) $$ - else if(atEnd && s.loopBack(index).nextChar(back, bi) - && wordTrie[back]) + else if (atEnd && s.loopBack(index).nextChar(back, bi) + && wordMatcher[back]) $$ - else if(s.loopBack(index).nextChar(back, bi)) + else if (s.loopBack(index).nextChar(back, bi)) { - bool af = wordTrie[front]; - bool ab = wordTrie[back]; - if(af ^ ab) + bool af = wordMatcher[front]; + bool ab = wordMatcher[back]; + if (af ^ ab) $$ } $$`, bailOut, bailOut, bailOut, nextInstr); @@ -1285,8 +1354,7 @@ struct CtContext code ~= ctSub(` dchar back; DataIndex bi; - if(atStart || ((re.flags & RegexOption.multiline) - && s.loopBack(index).nextChar(back,bi) + if (atStart || (s.loopBack(index).nextChar(back,bi) && endOfLine(back, front == '\n'))) { debug(std_regex_matcher) writeln("BOL matched"); @@ -1296,14 +1364,23 @@ struct CtContext $$`, nextInstr, bailOut); break; + case IR.Bof: + code ~= ctSub(` + if (atStart) + { + debug(std_regex_matcher) writeln("BOF matched"); + $$ + } + else + $$`, nextInstr, bailOut); + break; case IR.Eol: code ~= ctSub(` dchar back; DataIndex bi; - debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index..s.lastIndex]); + debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index .. s.lastIndex]); //no matching inside \r\n - if(atEnd || ((re.flags & RegexOption.multiline) - && endOfLine(front, s.loopBack(index).nextChar(back,bi) + if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi) && back == '\r'))) { debug(std_regex_matcher) writeln("EOL matched"); @@ -1311,7 +1388,16 @@ struct CtContext } else $$`, nextInstr, bailOut); - + break; + case IR.Eof: + code ~= ctSub(` + if (atEnd) + { + debug(std_regex_matcher) writeln("BOF matched"); + $$ + } + else + $$`, nextInstr, bailOut); break; case IR.GroupStart: code ~= ctSub(` @@ -1333,12 +1419,12 @@ struct CtContext ir[0].data, ir[0].data); code ~= ctSub( ` $$ - while(!atEnd && !referenced.empty && front == referenced.front) + while (!atEnd && !referenced.empty && front == referenced.front) { next(); referenced.popFront(); } - if(referenced.empty) + if (referenced.empty) $$ else $$`, mStr, nextInstr, bailOut); @@ -1363,12 +1449,13 @@ struct CtContext pc = 0; counter = 0; lastState = 0; + matches[] = Group!DataIndex.init; auto start = s._index;`; r ~= ` goto StartLoop; - debug(std_regex_matcher) writeln("Try CT matching starting at ",s[index..s.lastIndex]); + debug(std_regex_matcher) writeln("Try CT matching starting at ",s[index .. s.lastIndex]); L_backtrack: - if(lastState || prevStack()) + if (lastState || prevStack()) { stackPop(pc); stackPop(index); @@ -1381,7 +1468,7 @@ struct CtContext return false; } StartLoop: - switch(pc) + switch (pc) { `; r ~= bdy.code; @@ -1391,6 +1478,8 @@ struct CtContext default: assert(0); } + // cleanup stale stack blocks + while (prevStack()) {} return true; } `; diff --git a/std/regex/internal/generator.d b/std/regex/internal/generator.d index 541acc3c268..8bfda4a1092 100644 --- a/std/regex/internal/generator.d +++ b/std/regex/internal/generator.d @@ -12,8 +12,11 @@ module std.regex.internal.generator; */ @trusted private struct SampleGenerator(Char) { - import std.regex.internal.ir; - import std.array, std.format, std.utf, std.random; + import std.regex.internal.ir : Regex, IR, IRL; + import std.array : appender, Appender; + import std.format : formattedWrite; + import std.utf : isValidDchar, byChar; + import std.random : Xorshift; Regex!Char re; Appender!(char[]) app; uint limit, seed; @@ -39,23 +42,23 @@ module std.regex.internal.generator; void compose() { uint pc = 0, counter = 0, dataLenOld = uint.max; - for(;;) + for (;;) { - switch(re.ir[pc].code) + switch (re.ir[pc].code) { case IR.Char: - formattedWrite(app,"%s", cast(dchar)re.ir[pc].data); + formattedWrite(app,"%s", cast(dchar) re.ir[pc].data); pc += IRL!(IR.Char); break; case IR.OrChar: uint len = re.ir[pc].sequence; - formattedWrite(app, "%s", cast(dchar)re.ir[pc + rand(len)].data); + formattedWrite(app, "%s", cast(dchar) re.ir[pc + rand(len)].data); pc += len; break; case IR.CodepointSet: case IR.Trie: auto set = re.charsets[re.ir[pc].data]; - auto x = rand(cast(uint)set.byInterval.length); + auto x = rand(cast(uint) set.byInterval.length); auto y = rand(set.byInterval[x].b - set.byInterval[x].a); formattedWrite(app, "%s", cast(dchar)(set.byInterval[x].a+y)); pc += IRL!(IR.CodepointSet); @@ -65,8 +68,8 @@ module std.regex.internal.generator; do { x = rand(0x11_000); - }while(x == '\r' || x == '\n' || !isValidDchar(x)); - formattedWrite(app, "%s", cast(dchar)x); + }while (x == '\r' || x == '\n' || !isValidDchar(x)); + formattedWrite(app, "%s", cast(dchar) x); pc += IRL!(IR.Any); break; case IR.GotoEndOr: @@ -83,14 +86,14 @@ module std.regex.internal.generator; uint next = pc + re.ir[pc].data + IRL!(IR.Option); uint nOpt = 0; //queue next Option - while(re.ir[next].code == IR.Option) + while (re.ir[next].code == IR.Option) { nOpt++; next += re.ir[next].data + IRL!(IR.Option); } nOpt++; nOpt = rand(nOpt); - for(;nOpt; nOpt--) + for (;nOpt; nOpt--) { pc += re.ir[pc].data + IRL!(IR.Option); } @@ -105,16 +108,16 @@ module std.regex.internal.generator; uint len = re.ir[pc].data; uint step = re.ir[pc+2].raw; uint min = re.ir[pc+3].raw; - if(counter < min) + if (counter < min) { counter += step; pc -= len; break; } uint max = re.ir[pc+4].raw; - if(counter < max) + if (counter < max) { - if(app.data.length < limit && rand(3) > 0) + if (app.data.length < limit && rand(3) > 0) { pc -= len; counter += step; @@ -131,22 +134,21 @@ module std.regex.internal.generator; pc += IRL!(IR.RepeatEnd); } break; - case IR.InfiniteStart, IR.InfiniteQStart: + case IR.InfiniteStart, IR.InfiniteBloomStart, IR.InfiniteQStart: pc += re.ir[pc].data + IRL!(IR.InfiniteStart); goto case IR.InfiniteEnd; //both Q and non-Q - case IR.InfiniteEnd: - case IR.InfiniteQEnd: + case IR.InfiniteEnd, IR.InfiniteBloomEnd, IR.InfiniteQEnd: uint len = re.ir[pc].data; - if(app.data.length == dataLenOld) + if (app.data.length == dataLenOld) { pc += IRL!(IR.InfiniteEnd); break; } - dataLenOld = cast(uint)app.data.length; - if(app.data.length < limit && rand(3) > 0) + dataLenOld = cast(uint) app.data.length; + if (app.data.length < limit && rand(3) > 0) pc = pc - len; else - pc = pc + IRL!(IR.InfiniteEnd); + pc = pc + re.ir[pc].length; break; case IR.GroupStart, IR.GroupEnd: pc += IRL!(IR.GroupStart); @@ -164,7 +166,7 @@ module std.regex.internal.generator; return app.data; } - @property empty(){ return false; } + enum empty = false; void popFront() { @@ -173,13 +175,13 @@ module std.regex.internal.generator; } } -unittest +@system unittest { import std.range, std.regex; auto re = regex(`P[a-z]{3,}q`); auto gen = SampleGenerator!char(re, 20, 3141592); static assert(isInputRange!(typeof(gen))); //@@@BUG@@@ somehow gen.take(1_000) doesn't work - foreach(v; take(gen, 1_000)) + foreach (v; take(gen, 1_000)) assert(v.match(re)); } diff --git a/std/regex/internal/ir.d b/std/regex/internal/ir.d index d66b234abb3..755eca176c7 100644 --- a/std/regex/internal/ir.d +++ b/std/regex/internal/ir.d @@ -9,49 +9,52 @@ module std.regex.internal.ir; package(std.regex): -import std.exception, std.uni, std.typetuple, std.traits, std.range; +import std.exception, std.uni, std.meta, std.traits, std.range.primitives; +debug(std_regex_parser) import std.stdio; // just a common trait, may be moved elsewhere alias BasicElementOf(Range) = Unqual!(ElementEncodingType!Range); +enum privateUseStart = '\U000F0000', privateUseEnd ='\U000FFFFD'; + // heuristic value determines maximum CodepointSet length suitable for linear search enum maxCharsetUsed = 6; // another variable to tweak behavior of caching generated Tries for character classes -enum maxCachedTries = 8; +enum maxCachedMatchers = 8; -alias CodepointSetTrie!(13, 8) Trie; -alias codepointSetTrie!(13, 8) makeTrie; +alias Trie = CodepointSetTrie!(13, 8); +alias makeTrie = codepointSetTrie!(13, 8); -Trie[CodepointSet] trieCache; +CharMatcher[CodepointSet] matcherCache; //accessor with caching -@trusted Trie getTrie(CodepointSet set) +@trusted CharMatcher getMatcher(CodepointSet set) {// @@@BUG@@@ 6357 almost all properties of AA are not @safe - if(__ctfe || maxCachedTries == 0) - return makeTrie(set); + if (__ctfe || maxCachedMatchers == 0) + return CharMatcher(set); else { - auto p = set in trieCache; - if(p) + auto p = set in matcherCache; + if (p) return *p; - if(trieCache.length == maxCachedTries) + if (matcherCache.length == maxCachedMatchers) { - // flush entries in trieCache - trieCache = null; + // flush enmatchers in trieCache + matcherCache = null; } - return (trieCache[set] = makeTrie(set)); + return (matcherCache[set] = CharMatcher(set)); } } @trusted auto memoizeExpr(string expr)() { - if(__ctfe) + if (__ctfe) return mixin(expr); alias T = typeof(mixin(expr)); static T slot; static bool initialized; - if(!initialized) + if (!initialized) { slot = mixin(expr); initialized = true; @@ -66,14 +69,18 @@ Trie[CodepointSet] trieCache; | unicode.Me | unicode.Nd | unicode.Pc")(); } -@property Trie wordTrie() +@property CharMatcher wordMatcher() { - return memoizeExpr!("makeTrie(wordCharacter)")(); + return memoizeExpr!("CharMatcher(wordCharacter)")(); } // some special Unicode white space characters private enum NEL = '\u0085', LS = '\u2028', PS = '\u2029'; +// Characters that need escaping in string posed as regular expressions +alias Escapables = AliasSeq!('[', ']', '\\', '^', '$', '.', '|', '?', ',', '-', + ';', ':', '#', '&', '%', '/', '<', '>', '`', '*', '+', '(', ')', '{', '}', '~'); + //Regular expression engine/parser options: // global - search all nonoverlapping matches in input // casefold - case insensitive matching, do casefolding on match in unicode mode @@ -88,7 +95,7 @@ enum RegexOption: uint { singleline = 0x20 } //do not reorder this list -alias RegexOptionNames = TypeTuple!('g', 'i', 'x', 'U', 'm', 's'); +alias RegexOptionNames = AliasSeq!('g', 'i', 'x', 'U', 'm', 's'); static assert( RegexOption.max < 0x80); // flags that allow guide execution of engine enum RegexInfo : uint { oneShot = 0x80 } @@ -116,41 +123,46 @@ enum IR:uint { //OrChar holds in upper two bits of data total number of OrChars in this _sequence_ //the drawback of this representation is that it is difficult // to detect a jump in the middle of it - OrChar = 0b1_00100_00, - Nop = 0b1_00101_00, //no operation (padding) - End = 0b1_00110_00, //end of program - Bol = 0b1_00111_00, //beginning of a string ^ - Eol = 0b1_01000_00, //end of a string $ - Wordboundary = 0b1_01001_00, //boundary of a word - Notwordboundary = 0b1_01010_00, //not a word boundary - Backref = 0b1_01011_00, //backreference to a group (that has to be pinned, i.e. locally unique) (group index) - GroupStart = 0b1_01100_00, //start of a group (x) (groupIndex+groupPinning(1bit)) - GroupEnd = 0b1_01101_00, //end of a group (x) (groupIndex+groupPinning(1bit)) - Option = 0b1_01110_00, //start of an option within an alternation x | y (length) - GotoEndOr = 0b1_01111_00, //end of an option (length of the rest) + OrChar = 0b1_00100_00, + Nop = 0b1_00101_00, //no operation (padding) + End = 0b1_00110_00, //end of program + Bol = 0b1_00111_00, //beginning of a line ^ + Eol = 0b1_01000_00, //end of a line $ + Wordboundary = 0b1_01001_00, //boundary of a word + Notwordboundary = 0b1_01010_00, //not a word boundary + Backref = 0b1_01011_00, //backreference to a group (that has to be pinned, i.e. locally unique) (group index) + GroupStart = 0b1_01100_00, //start of a group (x) (groupIndex+groupPinning(1bit)) + GroupEnd = 0b1_01101_00, //end of a group (x) (groupIndex+groupPinning(1bit)) + Option = 0b1_01110_00, //start of an option within an alternation x | y (length) + GotoEndOr = 0b1_01111_00, //end of an option (length of the rest) + Bof = 0b1_10000_00, //begining of "file" (string) ^ + Eof = 0b1_10001_00, //end of "file" (string) $ //... any additional atoms here - OrStart = 0b1_00000_01, //start of alternation group (length) - OrEnd = 0b1_00000_10, //end of the or group (length,mergeIndex) + OrStart = 0b1_00000_01, //start of alternation group (length) + OrEnd = 0b1_00000_10, //end of the or group (length,mergeIndex) //with this instruction order //bit mask 0b1_00001_00 could be used to test/set greediness - InfiniteStart = 0b1_00001_01, //start of an infinite repetition x* (length) - InfiniteEnd = 0b1_00001_10, //end of infinite repetition x* (length,mergeIndex) - InfiniteQStart = 0b1_00010_01, //start of a non eager infinite repetition x*? (length) - InfiniteQEnd = 0b1_00010_10, //end of non eager infinite repetition x*? (length,mergeIndex) - RepeatStart = 0b1_00011_01, //start of a {n,m} repetition (length) - RepeatEnd = 0b1_00011_10, //end of x{n,m} repetition (length,step,minRep,maxRep) - RepeatQStart = 0b1_00100_01, //start of a non eager x{n,m}? repetition (length) - RepeatQEnd = 0b1_00100_10, //end of non eager x{n,m}? repetition (length,step,minRep,maxRep) + InfiniteStart = 0b1_00001_01, //start of an infinite repetition x* (length) + InfiniteEnd = 0b1_00001_10, //end of infinite repetition x* (length,mergeIndex) + InfiniteQStart = 0b1_00010_01, //start of a non eager infinite repetition x*? (length) + InfiniteQEnd = 0b1_00010_10, //end of non eager infinite repetition x*? (length,mergeIndex) + InfiniteBloomStart = 0b1_00011_01, //start of an filtered infinite repetition x* (length) + InfiniteBloomEnd = 0b1_00011_10, //end of filtered infinite repetition x* (length,mergeIndex) + RepeatStart = 0b1_00100_01, //start of a {n,m} repetition (length) + RepeatEnd = 0b1_00100_10, //end of x{n,m} repetition (length,step,minRep,maxRep) + RepeatQStart = 0b1_00101_01, //start of a non eager x{n,m}? repetition (length) + RepeatQEnd = 0b1_00101_10, //end of non eager x{n,m}? repetition (length,step,minRep,maxRep) + // - LookaheadStart = 0b1_00101_01, //begin of the lookahead group (length) - LookaheadEnd = 0b1_00101_10, //end of a lookahead group (length) - NeglookaheadStart = 0b1_00110_01, //start of a negative lookahead (length) - NeglookaheadEnd = 0b1_00110_10, //end of a negative lookahead (length) - LookbehindStart = 0b1_00111_01, //start of a lookbehind (length) - LookbehindEnd = 0b1_00111_10, //end of a lookbehind (length) - NeglookbehindStart= 0b1_01000_01, //start of a negative lookbehind (length) - NeglookbehindEnd = 0b1_01000_10, //end of negative lookbehind (length) + LookaheadStart = 0b1_00110_01, //begin of the lookahead group (length) + LookaheadEnd = 0b1_00110_10, //end of a lookahead group (length) + NeglookaheadStart = 0b1_00111_01, //start of a negative lookahead (length) + NeglookaheadEnd = 0b1_00111_10, //end of a negative lookahead (length) + LookbehindStart = 0b1_01000_01, //start of a lookbehind (length) + LookbehindEnd = 0b1_01000_10, //end of a lookbehind (length) + NeglookbehindStart = 0b1_01001_01, //start of a negative lookbehind (length) + NeglookbehindEnd = 0b1_01001_10, //end of negative lookbehind (length) } //a shorthand for IR length - full length of specific opcode evaluated at compile time @@ -158,17 +170,20 @@ template IRL(IR code) { enum uint IRL = lengthOfIR(code); } -static assert (IRL!(IR.LookaheadStart) == 3); +static assert(IRL!(IR.LookaheadStart) == 3); //how many parameters follow the IR, should be optimized fixing some IR bits int immediateParamsIR(IR i){ - switch (i){ + switch (i) + { case IR.OrEnd,IR.InfiniteEnd,IR.InfiniteQEnd: - return 1; + return 1; // merge table index + case IR.InfiniteBloomEnd: + return 2; // bloom filter index + merge table index case IR.RepeatEnd, IR.RepeatQEnd: return 4; case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - return 2; + return 2; // start-end of captures used default: return 0; } @@ -223,18 +238,18 @@ struct Bytecode uint raw; //natural constraints enum maxSequence = 2+4; - enum maxData = 1<<22; - enum maxRaw = 1<<31; + enum maxData = 1 << 22; + enum maxRaw = 1 << 31; this(IR code, uint data) { - assert(data < (1<<22) && code < 256); - raw = code<<24 | data; + assert(data < (1 << 22) && code < 256); + raw = code << 24 | data; } this(IR code, uint data, uint seq) { - assert(data < (1<<22) && code < 256 ); + assert(data < (1 << 22) && code < 256 ); assert(seq >= 2 && seq < maxSequence); raw = code << 24 | (seq - 2)<<22 | data; } @@ -251,13 +266,18 @@ struct Bytecode //0-arg template due to @@@BUG@@@ 10985 @property uint data()() const { return raw & 0x003f_ffff; } + @property void data()(uint val) + { + raw = (raw & ~0x003f_ffff) | (val & 0x003f_ffff); + } + //ditto //0-arg template due to @@@BUG@@@ 10985 @property uint sequence()() const { return 2 + (raw >> 22 & 0x3); } //ditto //0-arg template due to @@@BUG@@@ 10985 - @property IR code()() const { return cast(IR)(raw>>24); } + @property IR code()() const { return cast(IR)(raw >> 24); } //ditto @property bool hotspot() const { return hasMerge(code); } @@ -305,7 +325,7 @@ struct Bytecode //human readable name of instruction @trusted @property string mnemonic()() const {//@@@BUG@@@ to is @system - import std.conv; + import std.conv : to; return to!string(code); } @@ -352,7 +372,8 @@ struct Group(DataIndex) DataIndex begin, end; @trusted string toString()() const { - import std.format; + import std.array : appender; + import std.format : formattedWrite; auto a = appender!string(); formattedWrite(a, "%s..%s", begin, end); return a.data; @@ -362,18 +383,20 @@ struct Group(DataIndex) //debugging tool, prints out instruction along with opcodes @trusted string disassemble(in Bytecode[] irb, uint pc, in NamedGroup[] dict=[]) { - import std.array, std.format; + import std.array : appender; + import std.format : formattedWrite; auto output = appender!string(); formattedWrite(output,"%s", irb[pc].mnemonic); - switch(irb[pc].code) + switch (irb[pc].code) { case IR.Char: - formattedWrite(output, " %s (0x%x)",cast(dchar)irb[pc].data, irb[pc].data); + formattedWrite(output, " %s (0x%x)",cast(dchar) irb[pc].data, irb[pc].data); break; case IR.OrChar: - formattedWrite(output, " %s (0x%x) seq=%d", cast(dchar)irb[pc].data, irb[pc].data, irb[pc].sequence); + formattedWrite(output, " %s (0x%x) seq=%d", cast(dchar) irb[pc].data, irb[pc].data, irb[pc].sequence); break; - case IR.RepeatStart, IR.InfiniteStart, IR.Option, IR.GotoEndOr, IR.OrStart: + case IR.RepeatStart, IR.InfiniteStart, IR.InfiniteBloomStart, + IR.Option, IR.GotoEndOr, IR.OrStart: //forward-jump instructions uint len = irb[pc].data; formattedWrite(output, " pc=>%u", pc+len+IRL!(IR.RepeatStart)); @@ -383,7 +406,7 @@ struct Group(DataIndex) formattedWrite(output, " pc=>%u min=%u max=%u step=%u", pc - len, irb[pc + 3].raw, irb[pc + 4].raw, irb[pc + 2].raw); break; - case IR.InfiniteEnd, IR.InfiniteQEnd, IR.OrEnd: //ditto + case IR.InfiniteEnd, IR.InfiniteQEnd, IR.InfiniteBloomEnd, IR.OrEnd: //ditto uint len = irb[pc].data; formattedWrite(output, " pc=>%u", pc-len); break; @@ -394,8 +417,8 @@ struct Group(DataIndex) case IR.GroupStart, IR.GroupEnd: uint n = irb[pc].data; string name; - foreach(v;dict) - if(v.group == n) + foreach (v;dict) + if (v.group == n) { name = "'"~v.name~"'"; break; @@ -411,12 +434,12 @@ struct Group(DataIndex) case IR.Backref: case IR.CodepointSet: case IR.Trie: uint n = irb[pc].data; formattedWrite(output, " %u", n); - if(irb[pc].code == IR.Backref) + if (irb[pc].code == IR.Backref) formattedWrite(output, " %s", irb[pc].localRef ? "local" : "global"); break; default://all data-free instructions } - if(irb[pc].hotspot) + if (irb[pc].hotspot) formattedWrite(output, " Hotspot %u", irb[pc+1].raw); return output.data; } @@ -424,8 +447,8 @@ struct Group(DataIndex) //disassemble the whole chunk @trusted void printBytecode()(in Bytecode[] slice, in NamedGroup[] dict=[]) { - import std.stdio; - for(uint pc=0; pc user group number - uint ngroup; //number of internal groups - uint maxCounterDepth; //max depth of nested {n,m} repetitions - uint hotspotTableSize; //number of entries in merge table - uint threadCount; - uint flags; //global regex flags - public const(Trie)[] tries; // - uint[] backrefed; //bit array of backreferenced submatches + import std.regex.internal.kickstart : Kickstart; //TODO: get rid of this dependency + NamedGroup[] dict; // maps name -> user group number + uint ngroup; // number of internal groups + uint maxCounterDepth; // max depth of nested {n,m} repetitions + uint hotspotTableSize; // number of entries in merge table + uint threadCount; // upper bound on number of Thompson VM threads + uint flags; // global regex flags + public const(CharMatcher)[] matchers; // tables that represent character sets + public const(BitTable)[] filters; // bloom filters for conditional loops + uint[] backrefed; // bit array of backreferenced submatches Kickstart!Char kickstart; //bit access helper uint isBackref(uint n) { - if(n/32 >= backrefed.length) + if (n/32 >= backrefed.length) return 0; return backrefed[n / 32] & (1 << (n & 31)); } @@ -512,17 +536,16 @@ package(std.regex): //check if searching is not needed void checkIfOneShot() { - if(flags & RegexOption.multiline) - return; L_CheckLoop: - for(uint i = 0; i < ir.length; i += ir[i].length) + for (uint i = 0; i < ir.length; i += ir[i].length) { - switch(ir[i].code) + switch (ir[i].code) { - case IR.Bol: + case IR.Bof: flags |= RegexInfo.oneShot; break L_CheckLoop; - case IR.GroupStart, IR.GroupEnd, IR.Eol, IR.Wordboundary, IR.Notwordboundary: + case IR.GroupStart, IR.GroupEnd, IR.Bol, IR.Eol, IR.Eof, + IR.Wordboundary, IR.Notwordboundary: break; default: break L_CheckLoop; @@ -533,7 +556,7 @@ package(std.regex): //print out disassembly a program's IR @trusted debug(std_regex_parser) void print() const {//@@@BUG@@@ write is system - for(uint i = 0; i < ir.length; i += ir[i].length) + for (uint i = 0; i < ir.length; i += ir[i].length) { writefln("%d\t%s ", i, disassemble(ir, i, dict)); } @@ -547,7 +570,7 @@ package(std.regex): /*public*/ struct StaticRegex(Char) { package(std.regex): - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; alias Matcher = BacktrackingMatcher!(true); alias MatchFn = bool function(ref Matcher!Char) @trusted; MatchFn nativeFn; @@ -569,11 +592,11 @@ package(std.regex): //Simple UTF-string abstraction compatible with stream interface struct Input(Char) - if(is(Char :dchar)) +if (is(Char :dchar)) { - import std.utf; + import std.utf : decode; alias DataIndex = size_t; - enum { isLoopback = false }; + enum bool isLoopback = false; alias String = const(Char)[]; String _origin; size_t _index; @@ -586,13 +609,15 @@ struct Input(Char) } //codepoint at current stream position - bool nextChar(ref dchar res, ref size_t pos) + pragma(inline, true) bool nextChar(ref dchar res, ref size_t pos) { pos = _index; - if(_index == _origin.length) - return false; - res = std.utf.decode(_origin, _index); - return true; + // DMD's inliner hates multiple return functions + // but can live with single statement if/else bodies + bool n = !(_index == _origin.length); + if (n) + res = decode(_origin, _index); + return n; } @property bool atEnd(){ return _index == _origin.length; @@ -610,57 +635,71 @@ struct Input(Char) //support for backtracker engine, might not be present void reset(size_t index){ _index = index; } - String opSlice(size_t start, size_t end){ return _origin[start..end]; } + String opSlice(size_t start, size_t end){ return _origin[start .. end]; } + + auto loopBack(size_t index){ return BackLooper!Input(this, index); } +} - struct BackLooper +struct BackLooperImpl(Input) +{ + import std.utf : strideBack; + alias DataIndex = size_t; + alias String = Input.String; + enum bool isLoopback = true; + String _origin; + size_t _index; + this(Input input, size_t index) { - alias DataIndex = size_t; - enum { isLoopback = true }; - String _origin; - size_t _index; - this(Input input, size_t index) - { - _origin = input._origin; - _index = index; - } - @trusted bool nextChar(ref dchar res,ref size_t pos) - { - pos = _index; - if(_index == 0) - return false; + _origin = input._origin; + _index = index; + } + @trusted bool nextChar(ref dchar res,ref size_t pos) + { + pos = _index; + if (_index == 0) + return false; - res = _origin[0.._index].back; - _index -= std.utf.strideBack(_origin, _index); + res = _origin[0.._index].back; + _index -= strideBack(_origin, _index); - return true; - } - @property atEnd(){ return _index == 0 || _index == std.utf.strideBack(_origin, _index); } - auto loopBack(size_t index){ return Input(_origin, index); } + return true; + } + @property atEnd(){ return _index == 0 || _index == strideBack(_origin, _index); } + auto loopBack(size_t index){ return Input(_origin, index); } - //support for backtracker engine, might not be present - //void reset(size_t index){ _index = index ? index-std.utf.strideBack(_origin, index) : 0; } - void reset(size_t index){ _index = index; } + //support for backtracker engine, might not be present + //void reset(size_t index){ _index = index ? index-std.utf.strideBack(_origin, index) : 0; } + void reset(size_t index){ _index = index; } - String opSlice(size_t start, size_t end){ return _origin[end..start]; } - //index of at End position - @property size_t lastIndex(){ return 0; } - } - auto loopBack(size_t index){ return BackLooper(this, index); } + String opSlice(size_t start, size_t end){ return _origin[end .. start]; } + //index of at End position + @property size_t lastIndex(){ return 0; } } +template BackLooper(E) +{ + static if (is(E : BackLooperImpl!U, U)) + { + alias BackLooper = U; + } + else + { + alias BackLooper = BackLooperImpl!E; + } +} //both helpers below are internal, on its own are quite "explosive" //unsafe, no initialization of elements @system T[] mallocArray(T)(size_t len) { - import core.stdc.stdlib; - return (cast(T*)malloc(len * T.sizeof))[0 .. len]; + import core.stdc.stdlib : malloc; + return (cast(T*) malloc(len * T.sizeof))[0 .. len]; } //very unsafe, no initialization @system T[] arrayInChunk(T)(size_t len, ref void[] chunk) { - auto ret = (cast(T*)chunk.ptr)[0..len]; + auto ret = (cast(T*) chunk.ptr)[0 .. len]; chunk = chunk[len * T.sizeof .. $]; return ret; } @@ -668,8 +707,10 @@ struct Input(Char) // @trusted uint lookupNamedGroup(String)(NamedGroup[] dict, String name) {//equal is @system? - import std.conv; - import std.algorithm : map, equal; + import std.range : assumeSorted; + import std.conv : text; + import std.algorithm.iteration : map; + import std.algorithm.comparison : equal; auto fnd = assumeSorted!"cmp(a,b) < 0"(map!"a.name"(dict)).lowerBound(name).length; enforce(fnd < dict.length && equal(dict[fnd].name, name), @@ -693,57 +734,55 @@ bool startOfLine()(dchar back, bool seenNl) || back == NEL || back == LS || back == PS; } -//Test if bytecode starting at pc in program 're' can match given codepoint -//Returns: 0 - can't tell, -1 if doesn't match -int quickTestFwd(RegEx)(uint pc, dchar front, const ref RegEx re) +///Exception object thrown in case of errors during regex compilation. +public class RegexException : Exception { - static assert(IRL!(IR.OrChar) == 1);//used in code processing IR.OrChar - for(;;) - switch(re.ir[pc].code) + mixin basicExceptionCtors; +} + +// simple 128-entry bit-table used with a hash function +struct BitTable { + uint[4] filter; + + this(CodepointSet set){ + foreach (iv; set.byInterval) { - case IR.OrChar: - uint len = re.ir[pc].sequence; - uint end = pc + len; - if(re.ir[pc].data != front && re.ir[pc+1].data != front) - { - for(pc = pc+2; pc < end; pc++) - if(re.ir[pc].data == front) - break; - if(pc == end) - return -1; - } - return 0; - case IR.Char: - if(front == re.ir[pc].data) - return 0; - else - return -1; - case IR.Any: - return 0; - case IR.CodepointSet: - if(re.charsets[re.ir[pc].data].scanFor(front)) - return 0; - else - return -1; - case IR.GroupStart, IR.GroupEnd: - pc += IRL!(IR.GroupStart); - break; - case IR.Trie: - if(re.tries[re.ir[pc].data][front]) - return 0; - else - return -1; - default: - return 0; + foreach (v; iv.a .. iv.b) + add(v); } + } + + void add()(dchar ch){ + immutable i = index(ch); + filter[i >> 5] |= 1<<(i & 31); + } + // non-zero -> might be present, 0 -> absent + bool opIndex()(dchar ch) const{ + immutable i = index(ch); + return (filter[i >> 5]>>(i & 31)) & 1; + } + + static uint index()(dchar ch){ + return ((ch >> 7) ^ ch) & 0x7F; + } } -///Exception object thrown in case of errors during regex compilation. -public class RegexException : Exception -{ - /// - @trusted this(string msg, string file = __FILE__, size_t line = __LINE__) - {//@@@BUG@@@ Exception constructor is not @safe - super(msg, file, line); +struct CharMatcher { + BitTable ascii; // fast path for ASCII + Trie trie; // slow path for Unicode + + this(CodepointSet set) + { + auto asciiSet = set & unicode.ASCII; + ascii = BitTable(asciiSet); + trie = makeTrie(set); + } + + bool opIndex()(dchar ch) const + { + if (ch < 0x80) + return ascii[ch]; + else + return trie[ch]; } } diff --git a/std/regex/internal/kickstart.d b/std/regex/internal/kickstart.d index ad6a8f41ead..162ef7cc88d 100644 --- a/std/regex/internal/kickstart.d +++ b/std/regex/internal/kickstart.d @@ -7,16 +7,16 @@ module std.regex.internal.kickstart; package(std.regex): import std.regex.internal.ir; -import std.algorithm, std.range, std.utf; +import std.range.primitives, std.utf; //utility for shiftOr, returns a minimum number of bytes to test in a Char uint effectiveSize(Char)() { - static if(is(Char == char)) + static if (is(Char == char)) return 1; - else static if(is(Char == wchar)) + else static if (is(Char == wchar)) return 2; - else static if(is(Char == dchar)) + else static if (is(Char == dchar)) return 3; else static assert(0); @@ -63,7 +63,7 @@ private: void set(alias setBits = setInvMask)(dchar ch) { - static if(charSize == 3) + static if (charSize == 3) { uint val = ch, tmask = mask; setBits(val&0xFF, tmask); @@ -81,11 +81,11 @@ private: Char[dchar.sizeof/Char.sizeof] buf; uint tmask = mask; size_t total = encode(buf, ch); - for(size_t i = 0; i < total; i++, tmask<<=1) + for (size_t i = 0; i < total; i++, tmask<<=1) { - static if(charSize == 1) + static if (charSize == 1) setBits(buf[i], tmask); - else static if(charSize == 2) + else static if (charSize == 2) { setBits(buf[i]&0xFF, tmask); tmask <<= 1; @@ -115,31 +115,45 @@ private: { auto t = worklist[$-1]; worklist.length -= 1; - if(!__ctfe) - cast(void)worklist.assumeSafeAppend(); + if (!__ctfe) + cast(void) worklist.assumeSafeAppend(); return t; } static uint charLen(uint ch) { assert(ch <= 0x10FFFF); - return codeLength!Char(cast(dchar)ch)*charSize; + return codeLength!Char(cast(dchar) ch)*charSize; } public: @trusted this(ref Regex!Char re, uint[] memory) { - import std.conv; + static import std.algorithm.comparison; + import std.algorithm.searching : countUntil; + import std.conv : text; + import std.range : assumeSorted; assert(memory.length == 256); fChar = uint.max; + // FNV-1a flavored hash (uses 32bits at a time) + ulong hash(uint[] tab) + { + ulong h = 0xcbf29ce484222325; + foreach (v; tab) + { + h ^= v; + h *= 0x100000001b3; + } + return h; + } L_FindChar: - for(size_t i = 0;;) + for (size_t i = 0;;) { - switch(re.ir[i].code) + switch (re.ir[i].code) { case IR.Char: fChar = re.ir[i].data; - static if(charSize != 3) + static if (charSize != 3) { Char[dchar.sizeof/Char.sizeof] buf; encode(buf, fChar); @@ -150,7 +164,7 @@ public: case IR.GroupStart, IR.GroupEnd: i += IRL!(IR.GroupStart); break; - case IR.Bol, IR.Wordboundary, IR.Notwordboundary: + case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary: i += IRL!(IR.Bol); break; default: @@ -159,20 +173,23 @@ public: } table = memory; table[] = uint.max; + alias MergeTab = bool[ulong]; + // use reasonably complex hash to identify equivalent tables + auto merge = new MergeTab[re.hotspotTableSize]; ShiftThread[] trs; ShiftThread t = ShiftThread(0, 0, table); //locate first fixed char if any n_length = 32; - for(;;) + for (;;) { L_Eval_Thread: - for(;;) + for (;;) { - switch(re.ir[t.pc].code) + switch (re.ir[t.pc].code) { case IR.Char: uint s = charLen(re.ir[t.pc].data); - if(t.idx+s > n_length) + if (t.idx+s > n_length) goto L_StopThread; t.add(re.ir[t.pc].data); t.advance(s); @@ -183,26 +200,26 @@ public: uint end = t.pc + len; uint[Bytecode.maxSequence] s; uint numS; - for(uint i = 0; i < len; i++) + for (uint i = 0; i < len; i++) { auto x = charLen(re.ir[t.pc+i].data); - if(countUntil(s[0..numS], x) < 0) + if (countUntil(s[0 .. numS], x) < 0) s[numS++] = x; } - for(uint i = t.pc; i < end; i++) + for (uint i = t.pc; i < end; i++) { t.add(re.ir[i].data); } - for(uint i = 0; i < numS; i++) + for (uint i = 0; i < numS; i++) { auto tx = fork(t, t.pc + len, t.counter); - if(tx.idx + s[i] <= n_length) + if (tx.idx + s[i] <= n_length) { tx.advance(s[i]); trs ~= tx; } } - if(!trs.empty) + if (!trs.empty) t = fetch(trs); else goto L_StopThread; @@ -212,7 +229,7 @@ public: auto set = re.charsets[re.ir[t.pc].data]; uint[4] s; uint numS; - static if(charSize == 3) + static if (charSize == 3) { s[0] = charSize; numS = 1; @@ -220,45 +237,45 @@ public: else { - static if(charSize == 1) + static if (charSize == 1) static immutable codeBounds = [0x0, 0x7F, 0x80, 0x7FF, 0x800, 0xFFFF, 0x10000, 0x10FFFF]; else //== 2 static immutable codeBounds = [0x0, 0xFFFF, 0x10000, 0x10FFFF]; uint[] arr = new uint[set.byInterval.length * 2]; size_t ofs = 0; - foreach(ival; set.byInterval) + foreach (ival; set.byInterval) { arr[ofs++] = ival.a; arr[ofs++] = ival.b; } auto srange = assumeSorted!"a <= b"(arr); - for(uint i = 0; i < codeBounds.length/2; i++) + for (uint i = 0; i < codeBounds.length/2; i++) { auto start = srange.lowerBound(codeBounds[2*i]).length; auto end = srange.lowerBound(codeBounds[2*i+1]).length; - if(end > start || (end == start && (end & 1))) + if (end > start || (end == start && (end & 1))) s[numS++] = (i+1)*charSize; } } - if(numS == 0 || t.idx + s[numS-1] > n_length) + if (numS == 0 || t.idx + s[numS-1] > n_length) goto L_StopThread; auto chars = set.length; - if(chars > charsetThreshold) + if (chars > charsetThreshold) goto L_StopThread; - foreach(ch; set.byCodepoint) + foreach (ch; set.byCodepoint) { //avoid surrogate pairs - if(0xD800 <= ch && ch <= 0xDFFF) + if (0xD800 <= ch && ch <= 0xDFFF) continue; t.add(ch); } - for(uint i = 0; i < numS; i++) + for (uint i = 0; i < numS; i++) { auto tx = fork(t, t.pc + IRL!(IR.CodepointSet), t.counter); tx.advance(s[i]); trs ~= tx; } - if(!trs.empty) + if (!trs.empty) t = fetch(trs); else goto L_StopThread; @@ -271,6 +288,11 @@ public: assert(re.ir[t.pc].code == IR.OrEnd); goto case; case IR.OrEnd: + auto slot = re.ir[t.pc+1].raw+t.counter; + auto val = hash(t.tab); + if (val in merge[slot]) + goto L_StopThread; // merge equivalent + merge[slot][val] = true; t.pc += IRL!(IR.OrEnd); break; case IR.OrStart: @@ -279,7 +301,7 @@ public: case IR.Option: uint next = t.pc + re.ir[t.pc].data + IRL!(IR.Option); //queue next Option - if(re.ir[next].code == IR.Option) + if (re.ir[next].code == IR.Option) { trs ~= fork(t, next, t.counter); } @@ -290,17 +312,22 @@ public: goto case IR.RepeatEnd; case IR.RepeatEnd: case IR.RepeatQEnd: + auto slot = re.ir[t.pc+1].raw+t.counter; + auto val = hash(t.tab); + if (val in merge[slot]) + goto L_StopThread; // merge equivalent + merge[slot][val] = true; uint len = re.ir[t.pc].data; uint step = re.ir[t.pc+2].raw; uint min = re.ir[t.pc+3].raw; - if(t.counter < min) + if (t.counter < min) { t.counter += step; t.pc -= len; break; } uint max = re.ir[t.pc+4].raw; - if(t.counter < max) + if (t.counter < max) { trs ~= fork(t, t.pc - len, t.counter + step); t.counter = t.counter%step; @@ -317,9 +344,14 @@ public: goto case IR.InfiniteEnd; //both Q and non-Q case IR.InfiniteEnd: case IR.InfiniteQEnd: + auto slot = re.ir[t.pc+1].raw+t.counter; + auto val = hash(t.tab); + if (val in merge[slot]) + goto L_StopThread; // merge equivalent + merge[slot][val] = true; uint len = re.ir[t.pc].data; uint pc1, pc2; //branches to take in priority order - if(++t.hops == 32) + if (++t.hops == 32) goto L_StopThread; pc1 = t.pc + IRL!(IR.InfiniteEnd); pc2 = t.pc - len; @@ -329,7 +361,7 @@ public: case IR.GroupStart, IR.GroupEnd: t.pc += IRL!(IR.GroupStart); break; - case IR.Bol, IR.Wordboundary, IR.Notwordboundary: + case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary: t.pc += IRL!(IR.Bol); break; case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: @@ -339,11 +371,11 @@ public: L_StopThread: assert(re.ir[t.pc].code >= 0x80, text(re.ir[t.pc].code)); debug (fred_search) writeln("ShiftOr stumbled on ",re.ir[t.pc].mnemonic); - n_length = min(t.idx, n_length); + n_length = std.algorithm.comparison.min(t.idx, n_length); break L_Eval_Thread; } } - if(trs.empty) + if (trs.empty) break; t = fetch(trs); } @@ -363,63 +395,64 @@ public: // (that given the haystack in question is valid UTF string) @trusted size_t search(const(Char)[] haystack, size_t idx) {//@BUG: apparently assumes little endian machines - import std.conv, core.stdc.string; + import std.conv : text; + import core.stdc.string : memchr; assert(!empty); auto p = cast(const(ubyte)*)(haystack.ptr+idx); uint state = uint.max; uint limit = 1u<<(n_length - 1u); debug(std_regex_search) writefln("Limit: %32b",limit); - if(fChar != uint.max) + if (fChar != uint.max) { const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length); - const orginalAlign = cast(size_t)p & (Char.sizeof-1); - while(p != end) + const orginalAlign = cast(size_t) p & (Char.sizeof-1); + while (p != end) { - if(!~state) + if (!~state) {//speed up seeking first matching place - for(;;) + for (;;) { assert(p <= end, text(p," vs ", end)); - p = cast(ubyte*)memchr(p, fChar, end - p); - if(!p) + p = cast(ubyte*) memchr(p, fChar, end - p); + if (!p) return haystack.length; - if((cast(size_t)p & (Char.sizeof-1)) == orginalAlign) + if ((cast(size_t) p & (Char.sizeof-1)) == orginalAlign) break; - if(++p == end) + if (++p == end) return haystack.length; } state = ~1u; - assert((cast(size_t)p & (Char.sizeof-1)) == orginalAlign); - static if(charSize == 3) + assert((cast(size_t) p & (Char.sizeof-1)) == orginalAlign); + static if (charSize == 3) { - state = (state<<1) | table[p[1]]; - state = (state<<1) | table[p[2]]; + state = (state << 1) | table[p[1]]; + state = (state << 1) | table[p[2]]; p += 4; } else p++; //first char is tested, see if that's all - if(!(state & limit)) - return (p-cast(ubyte*)haystack.ptr)/Char.sizeof + if (!(state & limit)) + return (p-cast(ubyte*) haystack.ptr)/Char.sizeof -length; } else {//have some bits/states for possible matches, //use the usual shift-or cycle - static if(charSize == 3) + static if (charSize == 3) { - state = (state<<1) | table[p[0]]; - state = (state<<1) | table[p[1]]; - state = (state<<1) | table[p[2]]; + state = (state << 1) | table[p[0]]; + state = (state << 1) | table[p[1]]; + state = (state << 1) | table[p[2]]; p += 4; } else { - state = (state<<1) | table[p[0]]; + state = (state << 1) | table[p[0]]; p++; } - if(!(state & limit)) - return (p-cast(ubyte*)haystack.ptr)/Char.sizeof + if (!(state & limit)) + return (p-cast(ubyte*) haystack.ptr)/Char.sizeof -length; } debug(std_regex_search) writefln("State: %32b", state); @@ -428,17 +461,17 @@ public: else { //normal path, partially unrolled for char/wchar - static if(charSize == 3) + static if (charSize == 3) { const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length); - while(p != end) + while (p != end) { - state = (state<<1) | table[p[0]]; - state = (state<<1) | table[p[1]]; - state = (state<<1) | table[p[2]]; + state = (state << 1) | table[p[0]]; + state = (state << 1) | table[p[1]]; + state = (state << 1) | table[p[2]]; p += 4; - if(!(state & limit))//division rounds down for dchar - return (p-cast(ubyte*)haystack.ptr)/Char.sizeof + if (!(state & limit))//division rounds down for dchar + return (p-cast(ubyte*) haystack.ptr)/Char.sizeof -length; } } @@ -446,20 +479,20 @@ public: { auto len = cast(ubyte*)(haystack.ptr + haystack.length) - p; size_t i = 0; - if(len & 1) + if (len & 1) { - state = (state<<1) | table[p[i++]]; - if(!(state & limit)) + state = (state << 1) | table[p[i++]]; + if (!(state & limit)) return idx+i/Char.sizeof-length; } - while(i < len) + while (i < len) { - state = (state<<1) | table[p[i++]]; - if(!(state & limit)) + state = (state << 1) | table[p[i++]]; + if (!(state & limit)) return idx+i/Char.sizeof -length; - state = (state<<1) | table[p[i++]]; - if(!(state & limit)) + state = (state << 1) | table[p[i++]]; + if (!(state & limit)) return idx+i/Char.sizeof -length; debug(std_regex_search) writefln("State: %32b", state); @@ -471,20 +504,20 @@ public: @system debug static void dump(uint[] table) {//@@@BUG@@@ writef(ln) is @system - import std.stdio; - for(size_t i = 0; i < table.length; i += 4) + import std.stdio : writefln; + for (size_t i = 0; i < table.length; i += 4) { writefln("%32b %32b %32b %32b",table[i], table[i+1], table[i+2], table[i+3]); } } } -unittest +@system unittest { import std.conv, std.regex; @trusted void test_fixed(alias Kick)() { - foreach(i, v; TypeTuple!(char, wchar, dchar)) + foreach (i, v; AliasSeq!(char, wchar, dchar)) { alias Char = v; alias String = immutable(v)[]; @@ -510,7 +543,7 @@ unittest } @trusted void test_flex(alias Kick)() { - foreach(i, v;TypeTuple!(char, wchar, dchar)) + foreach (i, v; AliasSeq!(char, wchar, dchar)) { alias Char = v; alias String = immutable(v)[]; @@ -533,7 +566,7 @@ unittest assert(kick.length == 0); auto rN = regex(to!String(`a(b+|c+)x`)); kick = Kick!Char(rN, new uint[256]); - assert(kick.length == 3); + assert(kick.length == 3, to!string(kick.length)); assert(kick.search("ababx",0) == 2); assert(kick.search("abaacba",0) == 3);//expected inexact diff --git a/std/regex/internal/parser.d b/std/regex/internal/parser.d index d99985969b3..58e10225fbf 100644 --- a/std/regex/internal/parser.d +++ b/std/regex/internal/parser.d @@ -5,27 +5,29 @@ module std.regex.internal.parser; import std.regex.internal.ir; -import std.algorithm, std.range, std.uni, std.typetuple, +import std.range.primitives, std.uni, std.meta, std.traits, std.typecons, std.exception; +static import std.ascii; // package relevant info from parser into a regex object -auto makeRegex(S)(Parser!S p) +auto makeRegex(S, CG)(Parser!(S, CG) p) { Regex!(BasicElementOf!S) re; + auto g = p.g; with(re) { - ir = p.ir; - dict = p.dict; - ngroup = p.groupStack.top; - maxCounterDepth = p.counterDepth; + ir = g.ir; + dict = g.dict; + ngroup = g.ngroup; + maxCounterDepth = g.counterDepth; flags = p.re_flags; - charsets = p.charsets; - tries = p.tries; - backrefed = p.backrefed; - re.lightPostprocess(); + charsets = g.charsets; + matchers = g.matchers; + backrefed = g.backrefed; + re.postprocess(); debug(std_regex_parser) { - print(); + __ctfe || print(); } //@@@BUG@@@ (not reduced) //somehow just using validate _collides_ with std.utf.validate (!) @@ -36,13 +38,14 @@ auto makeRegex(S)(Parser!S p) // helper for unittest auto makeRegex(S)(S arg) - if(isSomeString!S) +if (isSomeString!S) { - return makeRegex(Parser!S(arg, "")); + return makeRegex(Parser!(S, CodeGen)(arg, "")); } -unittest +@system unittest { + import std.algorithm.comparison : equal; auto re = makeRegex(`(?P\w+) = (?P\d+)`); auto nc = re.namedCaptures; static assert(isRandomAccessRange!(typeof(nc))); @@ -77,49 +80,49 @@ unittest @trusted void reverseBytecode()(Bytecode[] code) { Bytecode[] rev = new Bytecode[code.length]; - uint revPc = cast(uint)rev.length; + uint revPc = cast(uint) rev.length; Stack!(Tuple!(uint, uint, uint)) stack; uint start = 0; - uint end = cast(uint)code.length; - for(;;) + uint end = cast(uint) code.length; + for (;;) { - for(uint pc = start; pc < end; ) + for (uint pc = start; pc < end; ) { - uint len = code[pc].length; - if(code[pc].code == IR.GotoEndOr) + immutable len = code[pc].length; + if (code[pc].code == IR.GotoEndOr) break; //pick next alternation branch - if(code[pc].isAtom) + if (code[pc].isAtom) { rev[revPc - len .. revPc] = code[pc .. pc + len]; revPc -= len; pc += len; } - else if(code[pc].isStart || code[pc].isEnd) + else if (code[pc].isStart || code[pc].isEnd) { //skip over other embedded lookbehinds they are reversed - if(code[pc].code == IR.LookbehindStart + if (code[pc].code == IR.LookbehindStart || code[pc].code == IR.NeglookbehindStart) { - uint blockLen = len + code[pc].data + immutable blockLen = len + code[pc].data + code[pc].pairedLength; rev[revPc - blockLen .. revPc] = code[pc .. pc + blockLen]; pc += blockLen; revPc -= blockLen; continue; } - uint second = code[pc].indexOfPair(pc); - uint secLen = code[second].length; + immutable second = code[pc].indexOfPair(pc); + immutable secLen = code[second].length; rev[revPc - secLen .. revPc] = code[second .. second + secLen]; revPc -= secLen; - if(code[pc].code == IR.OrStart) + if (code[pc].code == IR.OrStart) { //we pass len bytes forward, but secLen in reverse - uint revStart = revPc - (second + len - secLen - pc); + immutable revStart = revPc - (second + len - secLen - pc); uint r = revStart; uint i = pc + IRL!(IR.OrStart); - while(code[i].code == IR.Option) + while (code[i].code == IR.Option) { - if(code[i - 1].code != IR.OrStart) + if (code[i - 1].code != IR.OrStart) { assert(code[i - 1].code == IR.GotoEndOr); rev[r - 1] = code[i - 1]; @@ -128,7 +131,7 @@ unittest auto newStart = i + IRL!(IR.Option); auto newEnd = newStart + code[i].data; auto newRpc = r + code[i].data + IRL!(IR.Option); - if(code[newEnd].code != IR.OrEnd) + if (code[newEnd].code != IR.OrEnd) { newRpc--; } @@ -144,7 +147,7 @@ unittest pc += len; } } - if(stack.empty) + if (stack.empty) break; start = stack.top[0]; end = stack.top[1]; @@ -154,10 +157,6 @@ unittest code[] = rev[]; } - -alias Escapables = TypeTuple!('[', ']', '\\', '^', '$', '.', '|', '?', ',', '-', - ';', ':', '#', '&', '%', '/', '<', '>', '`', '*', '+', '(', ')', '{', '}', '~'); - //test if a given string starts with hex number of maxDigit that's a valid codepoint //returns it's value and skips these maxDigit chars on success, throws on failure dchar parseUniHex(Char)(ref Char[] str, size_t maxDigit) @@ -165,14 +164,14 @@ dchar parseUniHex(Char)(ref Char[] str, size_t maxDigit) //std.conv.parse is both @system and bogus enforce(str.length >= maxDigit,"incomplete escape sequence"); uint val; - for(int k = 0; k < maxDigit; k++) + for (int k = 0; k < maxDigit; k++) { - auto current = str[k];//accepts ascii only, so it's OK to index directly - if('0' <= current && current <= '9') + immutable current = str[k];//accepts ascii only, so it's OK to index directly + if ('0' <= current && current <= '9') val = val * 16 + current - '0'; - else if('a' <= current && current <= 'f') + else if ('a' <= current && current <= 'f') val = val * 16 + current -'a' + 10; - else if('A' <= current && current <= 'F') + else if ('A' <= current && current <= 'F') val = val * 16 + current - 'A' + 10; else throw new Exception("invalid escape sequence"); @@ -184,55 +183,26 @@ dchar parseUniHex(Char)(ref Char[] str, size_t maxDigit) @system unittest //BUG canFind is system { + import std.algorithm.searching : canFind; string[] non_hex = [ "000j", "000z", "FffG", "0Z"]; string[] hex = [ "01", "ff", "00af", "10FFFF" ]; int[] value = [ 1, 0xFF, 0xAF, 0x10FFFF ]; - foreach(v; non_hex) + foreach (v; non_hex) assert(collectException(parseUniHex(v, v.length)).msg .canFind("invalid escape sequence")); - foreach(i, v; hex) + foreach (i, v; hex) assert(parseUniHex(v, v.length) == value[i]); string over = "0011FFFF"; assert(collectException(parseUniHex(over, over.length)).msg .canFind("invalid codepoint")); } -//heuristic value determines maximum CodepointSet length suitable for linear search -enum maxCharsetUsed = 6; - -enum maxCachedTries = 8; - -alias CodepointSetTrie!(13, 8) Trie; -alias codepointSetTrie!(13, 8) makeTrie; - -Trie[CodepointSet] trieCache; - -//accessor with caching -@trusted Trie getTrie(CodepointSet set) -{// @@@BUG@@@ 6357 almost all properties of AA are not @safe - if(__ctfe || maxCachedTries == 0) - return makeTrie(set); - else - { - auto p = set in trieCache; - if(p) - return *p; - if(trieCache.length == maxCachedTries) - { - // flush entries in trieCache - trieCache = null; - } - return (trieCache[set] = makeTrie(set)); - } -} - - auto caseEnclose(CodepointSet set) { auto cased = set & unicode.LC; foreach (dchar ch; cased.byCodepoint) { - foreach(c; simpleCaseFoldings(ch)) + foreach (c; simpleCaseFoldings(ch)) set |= c; } return set; @@ -245,9 +215,9 @@ auto caseEnclose(CodepointSet set) { CodepointSet s = unicode(name); //FIXME: caseEnclose for new uni as Set | CaseEnclose(SET && LC) - if(casefold) + if (casefold) s = caseEnclose(s); - if(negated) + if (negated) s = s.inverted; return s; } @@ -267,8 +237,8 @@ auto caseEnclose(CodepointSet set) assert(!empty); auto val = data[$ - 1]; data = data[0 .. $ - 1]; - if(!__ctfe) - cast(void)data.assumeSafeAppend(); + if (!__ctfe) + cast(void) data.assumeSafeAppend(); return val; } @@ -279,76 +249,404 @@ auto caseEnclose(CodepointSet set) } } -//safety limits +struct CodeGen +{ + Bytecode[] ir; // resulting bytecode + Stack!(uint) fixupStack; // stack of opened start instructions + NamedGroup[] dict; // maps name -> user group number + Stack!(uint) groupStack; // stack of current number of group + uint nesting = 0; // group nesting level and repetitions step + uint lookaroundNest = 0; // nesting of lookaround + uint counterDepth = 0; // current depth of nested counted repetitions + CodepointSet[] charsets; // sets for char classes + const(CharMatcher)[] matchers; // matchers for char classes + uint[] backrefed; // bitarray for groups refered by backref + uint ngroup; // final number of groups (of all patterns) + + void start(uint length) + { + if (!__ctfe) + ir.reserve((length*5+2)/4); + fixupStack.push(0); + groupStack.push(1);//0 - whole match + } + + //mark referenced groups for latter processing + void markBackref(uint n) + { + if (n/32 >= backrefed.length) + backrefed.length = n/32 + 1; + backrefed[n / 32] |= 1 << (n & 31); + } + + bool isOpenGroup(uint n) + { + import std.algorithm.searching : canFind; + // walk the fixup stack and see if there are groups labeled 'n' + // fixup '0' is reserved for alternations + return fixupStack.data[1..$]. + canFind!(fix => ir[fix].code == IR.GroupStart && ir[fix].data == n)(); + } + + void put(Bytecode code) + { + enforce(ir.length < maxCompiledLength, + "maximum compiled pattern length is exceeded"); + ir ~= code; + } + + void putRaw(uint number) + { + enforce(ir.length < maxCompiledLength, + "maximum compiled pattern length is exceeded"); + ir ~= Bytecode.fromRaw(number); + } + + //try to generate optimal IR code for this CodepointSet + @trusted void charsetToIr(CodepointSet set) + {//@@@BUG@@@ writeln is @system + uint chars = cast(uint) set.length; + if (chars < Bytecode.maxSequence) + { + switch (chars) + { + case 1: + put(Bytecode(IR.Char, set.byCodepoint.front)); + break; + case 0: + throw new RegexException("empty CodepointSet not allowed"); + default: + foreach (ch; set.byCodepoint) + put(Bytecode(IR.OrChar, ch, chars)); + } + } + else + { + import std.algorithm.searching : countUntil; + const ivals = set.byInterval; + immutable n = charsets.countUntil(set); + if (n >= 0) + { + if (ivals.length*2 > maxCharsetUsed) + put(Bytecode(IR.Trie, cast(uint) n)); + else + put(Bytecode(IR.CodepointSet, cast(uint) n)); + return; + } + if (ivals.length*2 > maxCharsetUsed) + { + auto t = getMatcher(set); + put(Bytecode(IR.Trie, cast(uint) matchers.length)); + matchers ~= t; + debug(std_regex_allocation) writeln("Trie generated"); + } + else + { + put(Bytecode(IR.CodepointSet, cast(uint) charsets.length)); + matchers ~= CharMatcher.init; + } + charsets ~= set; + assert(charsets.length == matchers.length); + } + } + + void genLogicGroup() + { + nesting++; + pushFixup(length); + put(Bytecode(IR.Nop, 0)); + } + + void genGroup() + { + nesting++; + pushFixup(length); + immutable nglob = groupStack.top++; + enforce(groupStack.top <= maxGroupNumber, "limit on number of submatches is exceeded"); + put(Bytecode(IR.GroupStart, nglob)); + } + + void genNamedGroup(string name) + { + import std.array : insertInPlace; + import std.range : assumeSorted; + nesting++; + pushFixup(length); + immutable nglob = groupStack.top++; + enforce(groupStack.top <= maxGroupNumber, "limit on submatches is exceeded"); + auto t = NamedGroup(name, nglob); + auto d = assumeSorted!"a.name < b.name"(dict); + immutable ind = d.lowerBound(t).length; + insertInPlace(dict, ind, t); + put(Bytecode(IR.GroupStart, nglob)); + } + + //generate code for start of lookaround: (?= (?! (?<= (? fix && ir[fix].code == IR.Option) + { + ir[fix] = Bytecode(ir[fix].code, cast(uint) ir.length - fix); + put(Bytecode(IR.GotoEndOr, 0)); + fixupStack.top = cast(uint) ir.length; //replace latest fixup for Option + put(Bytecode(IR.Option, 0)); + return; + } + uint len, orStart; + //start a new option + if (fixupStack.length == 1) + {//only root entry, effectively no fixup + len = cast(uint) ir.length + IRL!(IR.GotoEndOr); + orStart = 0; + } + else + {//IR.lookahead, etc. fixups that have length > 1, thus check ir[x].length + len = cast(uint) ir.length - fix - (ir[fix].length - 1); + orStart = fix + ir[fix].length; + } + insertInPlace(ir, orStart, Bytecode(IR.OrStart, 0), Bytecode(IR.Option, len)); + assert(ir[orStart].code == IR.OrStart); + put(Bytecode(IR.GotoEndOr, 0)); + fixupStack.push(orStart); //fixup for StartOR + fixupStack.push(cast(uint) ir.length); //for second Option + put(Bytecode(IR.Option, 0)); + } + + // finalizes IR.Option, fix points to the first option of sequence + void finishAlternation(uint fix) + { + enforce(ir[fix].code == IR.Option, "no matching ')'"); + ir[fix] = Bytecode(ir[fix].code, cast(uint) ir.length - fix - IRL!(IR.OrStart)); + fix = fixupStack.pop(); + enforce(ir[fix].code == IR.OrStart, "no matching ')'"); + ir[fix] = Bytecode(IR.OrStart, cast(uint) ir.length - fix - IRL!(IR.OrStart)); + put(Bytecode(IR.OrEnd, cast(uint) ir.length - fix - IRL!(IR.OrStart))); + uint pc = fix + IRL!(IR.OrStart); + while (ir[pc].code == IR.Option) + { + pc = pc + ir[pc].data; + if (ir[pc].code != IR.GotoEndOr) + break; + ir[pc] = Bytecode(IR.GotoEndOr, cast(uint)(ir.length - pc - IRL!(IR.OrEnd))); + pc += IRL!(IR.GotoEndOr); + } + put(Bytecode.fromRaw(0)); + } + + // returns: (flag - repetition possible?, fixup of the start of this "group") + Tuple!(bool, uint) onClose() + { + nesting--; + uint fix = popFixup(); + switch (ir[fix].code) + { + case IR.GroupStart: + put(Bytecode(IR.GroupEnd, ir[fix].data)); + return tuple(true, fix); + case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: + assert(lookaroundNest); + fixLookaround(fix); + return tuple(false, 0u); + case IR.Option: //| xxx ) + //two fixups: last option + full OR + finishAlternation(fix); + fix = topFixup; + switch (ir[fix].code) + { + case IR.GroupStart: + popFixup(); + put(Bytecode(IR.GroupEnd, ir[fix].data)); + return tuple(true, fix); + case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: + assert(lookaroundNest); + fix = popFixup(); + fixLookaround(fix); + return tuple(false, 0u); + default://(?:xxx) + popFixup(); + return tuple(true, fix); + } + default://(?:xxx) + return tuple(true, fix); + } + } + + uint popFixup(){ return fixupStack.pop(); } + + void pushFixup(uint val){ return fixupStack.push(val); } + + @property uint topFixup(){ return fixupStack.top; } + + @property size_t fixupLength(){ return fixupStack.data.length; } + + @property uint length(){ return cast(uint) ir.length; } +} + +// safety limits enum maxGroupNumber = 2^^19; enum maxLookaroundDepth = 16; // *Bytecode.sizeof, i.e. 1Mb of bytecode alone enum maxCompiledLength = 2^^18; -//amounts to up to 4 Mb of auxilary table for matching +// amounts to up to 4 Mb of auxilary table for matching enum maxCumulativeRepetitionLength = 2^^20; +// marker to indicate infinite repetition +enum infinite = ~0u; -struct Parser(R) - if (isForwardRange!R && is(ElementType!R : dchar)) +struct Parser(R, Generator) +if (isForwardRange!R && is(ElementType!R : dchar)) { - enum infinite = ~0u; dchar _current; bool empty; R pat, origin; //keep full pattern for pretty printing error messages - Bytecode[] ir; //resulting bytecode uint re_flags = 0; //global flags e.g. multiline + internal ones - Stack!(uint) fixupStack; //stack of opened start instructions - NamedGroup[] dict; //maps name -> user group number - //current num of group, group nesting level and repetitions step - Stack!(uint) groupStack; - uint nesting = 0; - uint lookaroundNest = 0; - uint counterDepth = 0; //current depth of nested counted repetitions - CodepointSet[] charsets; // - const(Trie)[] tries; // - uint[] backrefed; //bitarray for groups + Generator g; @trusted this(S)(R pattern, S flags) - if(isSomeString!S) + if (isSomeString!S) { pat = origin = pattern; //reserve slightly more then avg as sampled from unittests - if(!__ctfe) - ir.reserve((pat.length*5+2)/4); parseFlags(flags); _current = ' ';//a safe default for freeform parsing next(); + g.start(cast(uint) pat.length); try { parseRegex(); } - catch(Exception e) + catch (Exception e) { error(e.msg);//also adds pattern location } - put(Bytecode(IR.End, 0)); - } - - //mark referenced groups for latter processing - void markBackref(uint n) - { - if(n/32 >= backrefed.length) - backrefed.length = n/32 + 1; - backrefed[n / 32] |= 1 << (n & 31); - } - - bool isOpenGroup(uint n) - { - // walk the fixup stack and see if there are groups labeled 'n' - // fixup '0' is reserved for alternations - return fixupStack.data[1..$]. - canFind!(fix => ir[fix].code == IR.GroupStart && ir[fix].data == n)(); + g.endPattern(1); } @property dchar current(){ return _current; } bool _next() { - if(pat.empty) + if (pat.empty) { empty = true; return false; @@ -360,14 +658,14 @@ struct Parser(R) void skipSpace() { - while(isWhite(current) && _next()){ } + while (isWhite(current) && _next()){ } } bool next() { - if(re_flags & RegexOption.freeform) + if (re_flags & RegexOption.freeform) { - bool r = _next(); + immutable r = _next(); skipSpace(); return r; } @@ -375,30 +673,16 @@ struct Parser(R) return _next(); } - void put(Bytecode code) - { - enforce(ir.length < maxCompiledLength, - "maximum compiled pattern length is exceeded"); - ir ~= code; - } - - void putRaw(uint number) - { - enforce(ir.length < maxCompiledLength, - "maximum compiled pattern length is exceeded"); - ir ~= Bytecode.fromRaw(number); - } - //parsing number with basic overflow check uint parseDecimal() { uint r = 0; - while(std.ascii.isDigit(current)) + while (std.ascii.isDigit(current)) { - if(r >= (uint.max/10)) + if (r >= (uint.max/10)) error("Overflow in decimal number"); r = 10*r + cast(uint)(current-'0'); - if(!next()) + if (!next()) break; } return r; @@ -416,17 +700,17 @@ struct Parser(R) // @trusted void parseFlags(S)(S flags) {//@@@BUG@@@ text is @system - import std.conv; - foreach(ch; flags)//flags are ASCII anyway + import std.conv : text; + foreach (ch; flags)//flags are ASCII anyway { L_FlagSwitch: - switch(ch) + switch (ch) { - foreach(i, op; __traits(allMembers, RegexOption)) + foreach (i, op; __traits(allMembers, RegexOption)) { case RegexOptionNames[i]: - if(re_flags & mixin("RegexOption."~op)) + if (re_flags & mixin("RegexOption."~op)) throw new RegexException(text("redundant flag specified: ",ch)); re_flags |= mixin("RegexOption."~op); break L_FlagSwitch; @@ -440,201 +724,158 @@ struct Parser(R) //parse and store IR for regex pattern @trusted void parseRegex() { - fixupStack.push(0); - groupStack.push(1);//0 - whole match - auto maxCounterDepth = counterDepth; uint fix;//fixup pointer - while(!empty) + while (!empty) { debug(std_regex_parser) - writeln("*LR*\nSource: ", pat, "\nStack: ",fixupStack.stack.data); - switch(current) + __ctfe || writeln("*LR*\nSource: ", pat, "\nStack: ",fixupStack.data); + switch (current) { case '(': next(); - nesting++; - uint nglob; - fixupStack.push(cast(uint)ir.length); - if(current == '?') + if (current == '?') { next(); - switch(current) + switch (current) { + case '#': + for (;;) + { + if (!next()) + error("Unexpected end of pattern"); + if (current == ')') + { + next(); + break; + } + } + break; case ':': - put(Bytecode(IR.Nop, 0)); + g.genLogicGroup(); next(); break; case '=': - genLookaround(IR.LookaheadStart); + g.genLookaround(IR.LookaheadStart); next(); break; case '!': - genLookaround(IR.NeglookaheadStart); + g.genLookaround(IR.NeglookaheadStart); next(); break; case 'P': next(); - if(current != '<') + if (current != '<') error("Expected '<' in named group"); string name; - if(!next() || !(isAlpha(current) || current == '_')) + if (!next() || !(isAlpha(current) || current == '_')) error("Expected alpha starting a named group"); name ~= current; - while(next() && (isAlpha(current) || + while (next() && (isAlpha(current) || current == '_' || std.ascii.isDigit(current))) { name ~= current; } - if(current != '>') + if (current != '>') error("Expected '>' closing named group"); next(); - nglob = groupStack.top++; - enforce(groupStack.top <= maxGroupNumber, "limit on submatches is exceeded"); - auto t = NamedGroup(name, nglob); - auto d = assumeSorted!"a.name < b.name"(dict); - auto ind = d.lowerBound(t).length; - insertInPlace(dict, ind, t); - put(Bytecode(IR.GroupStart, nglob)); + g.genNamedGroup(name); break; case '<': next(); - if(current == '=') - genLookaround(IR.LookbehindStart); - else if(current == '!') - genLookaround(IR.NeglookbehindStart); + if (current == '=') + g.genLookaround(IR.LookbehindStart); + else if (current == '!') + g.genLookaround(IR.NeglookbehindStart); else error("'!' or '=' expected after '<'"); next(); break; default: - error(" ':', '=', '<', 'P' or '!' expected after '(?' "); + uint enableFlags, disableFlags; + bool enable = true; + do + { + switch (current) + { + case 's': + if (enable) + enableFlags |= RegexOption.singleline; + else + disableFlags |= RegexOption.singleline; + break; + case 'x': + if (enable) + enableFlags |= RegexOption.freeform; + else + disableFlags |= RegexOption.freeform; + break; + case 'i': + if (enable) + enableFlags |= RegexOption.casefold; + else + disableFlags |= RegexOption.casefold; + break; + case 'm': + if (enable) + enableFlags |= RegexOption.multiline; + else + disableFlags |= RegexOption.multiline; + break; + case '-': + if (!enable) + error(" unexpected second '-' in flags"); + enable = false; + break; + default: + error(" 's', 'x', 'i', 'm' or '-' expected after '(?' "); + } + next(); + }while (current != ')'); + next(); + re_flags |= enableFlags; + re_flags &= ~disableFlags; } } else { - nglob = groupStack.top++; - enforce(groupStack.top <= maxGroupNumber, "limit on number of submatches is exceeded"); - put(Bytecode(IR.GroupStart, nglob)); + g.genGroup(); } break; case ')': - enforce(nesting, "Unmatched ')'"); - nesting--; + enforce(g.nesting, "Unmatched ')'"); next(); - fix = fixupStack.pop(); - switch(ir[fix].code) - { - case IR.GroupStart: - put(Bytecode(IR.GroupEnd,ir[fix].data)); - parseQuantifier(fix); - break; - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - assert(lookaroundNest); - fixLookaround(fix); - lookaroundNest--; - break; - case IR.Option: //| xxx ) - //two fixups: last option + full OR - finishAlternation(fix); - fix = fixupStack.top; - switch(ir[fix].code) - { - case IR.GroupStart: - fixupStack.pop(); - put(Bytecode(IR.GroupEnd,ir[fix].data)); - parseQuantifier(fix); - break; - case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart: - assert(lookaroundNest); - lookaroundNest--; - fix = fixupStack.pop(); - fixLookaround(fix); - break; - default://(?:xxx) - fixupStack.pop(); - parseQuantifier(fix); - } - break; - default://(?:xxx) - parseQuantifier(fix); - } + auto pair = g.onClose(); + if (pair[0]) + parseQuantifier(pair[1]); break; case '|': next(); - fix = fixupStack.top; - if(ir.length > fix && ir[fix].code == IR.Option) - { - ir[fix] = Bytecode(ir[fix].code, cast(uint)ir.length - fix); - put(Bytecode(IR.GotoEndOr, 0)); - fixupStack.top = cast(uint)ir.length; //replace latest fixup for Option - put(Bytecode(IR.Option, 0)); - break; - } - uint len, orStart; - //start a new option - if(fixupStack.length == 1) - {//only root entry, effectively no fixup - len = cast(uint)ir.length + IRL!(IR.GotoEndOr); - orStart = 0; - } - else - {//IR.lookahead, etc. fixups that have length > 1, thus check ir[x].length - len = cast(uint)ir.length - fix - (ir[fix].length - 1); - orStart = fix + ir[fix].length; - } - insertInPlace(ir, orStart, Bytecode(IR.OrStart, 0), Bytecode(IR.Option, len)); - assert(ir[orStart].code == IR.OrStart); - put(Bytecode(IR.GotoEndOr, 0)); - fixupStack.push(orStart); //fixup for StartOR - fixupStack.push(cast(uint)ir.length); //for second Option - put(Bytecode(IR.Option, 0)); + g.fixAlternation(); break; default://no groups or whatever - uint start = cast(uint)ir.length; + immutable start = g.length; parseAtom(); parseQuantifier(start); } } - if(fixupStack.length != 1) + if (g.fixupLength != 1) { - fix = fixupStack.pop(); - enforce(ir[fix].code == IR.Option, "no matching ')'"); - finishAlternation(fix); - enforce(fixupStack.length == 1, "no matching ')'"); + fix = g.popFixup(); + g.finishAlternation(fix); + enforce(g.fixupLength == 1, "no matching ')'"); } } - //helper function, finalizes IR.Option, fix points to the first option of sequence - void finishAlternation(uint fix) - { - enforce(ir[fix].code == IR.Option, "no matching ')'"); - ir[fix] = Bytecode(ir[fix].code, cast(uint)ir.length - fix - IRL!(IR.OrStart)); - fix = fixupStack.pop(); - enforce(ir[fix].code == IR.OrStart, "no matching ')'"); - ir[fix] = Bytecode(IR.OrStart, cast(uint)ir.length - fix - IRL!(IR.OrStart)); - put(Bytecode(IR.OrEnd, cast(uint)ir.length - fix - IRL!(IR.OrStart))); - uint pc = fix + IRL!(IR.OrStart); - while(ir[pc].code == IR.Option) - { - pc = pc + ir[pc].data; - if(ir[pc].code != IR.GotoEndOr) - break; - ir[pc] = Bytecode(IR.GotoEndOr, cast(uint)(ir.length - pc - IRL!(IR.OrEnd))); - pc += IRL!(IR.GotoEndOr); - } - put(Bytecode.fromRaw(0)); - } //parse and store IR for atom-quantifier pair @trusted void parseQuantifier(uint offset) {//copy is @system - uint replace = ir[offset].code == IR.Nop; - if(empty && !replace) - return; + if (empty) + return g.fixRepetition(offset); uint min, max; - switch(current) + switch (current) { case '*': min = 0; @@ -652,114 +893,58 @@ struct Parser(R) enforce(next(), "Unexpected end of regex pattern"); enforce(std.ascii.isDigit(current), "First number required in repetition"); min = parseDecimal(); - if(current == '}') + if (current == '}') max = min; - else if(current == ',') + else if (current == ',') { next(); - if(std.ascii.isDigit(current)) + if (std.ascii.isDigit(current)) max = parseDecimal(); - else if(current == '}') + else if (current == '}') max = infinite; else error("Unexpected symbol in regex pattern"); skipSpace(); - if(current != '}') + if (current != '}') error("Unmatched '{' in regex pattern"); } else error("Unexpected symbol in regex pattern"); - if(min > max) + if (min > max) error("Illegal {n,m} quantifier"); break; default: - if(replace) - { - copy(ir[offset + 1 .. $], ir[offset .. $ - 1]); - ir.length -= 1; - } + g.fixRepetition(offset); return; } - uint len = cast(uint)ir.length - offset - replace; bool greedy = true; //check only if we managed to get new symbol - if(next() && current == '?') + if (next() && current == '?') { greedy = false; next(); } - if(max != infinite) - { - if(min != 1 || max != 1) - { - Bytecode op = Bytecode(greedy ? IR.RepeatStart : IR.RepeatQStart, len); - if(replace) - ir[offset] = op; - else - insertInPlace(ir, offset, op); - put(Bytecode(greedy ? IR.RepeatEnd : IR.RepeatQEnd, len)); - put(Bytecode.init); //hotspot - putRaw(1); - putRaw(min); - putRaw(max); - counterDepth = std.algorithm.max(counterDepth, nesting+1); - } - } - else if(min) //&& max is infinite - { - if(min != 1) - { - Bytecode op = Bytecode(greedy ? IR.RepeatStart : IR.RepeatQStart, len); - if(replace) - ir[offset] = op; - else - insertInPlace(ir, offset, op); - offset += 1;//so it still points to the repeated block - put(Bytecode(greedy ? IR.RepeatEnd : IR.RepeatQEnd, len)); - put(Bytecode.init); //hotspot - putRaw(1); - putRaw(min); - putRaw(min); - counterDepth = std.algorithm.max(counterDepth, nesting+1); - } - else if(replace) - { - copy(ir[offset+1 .. $], ir[offset .. $-1]); - ir.length -= 1; - } - put(Bytecode(greedy ? IR.InfiniteStart : IR.InfiniteQStart, len)); - enforce(ir.length + len < maxCompiledLength, "maximum compiled pattern length is exceeded"); - ir ~= ir[offset .. offset+len]; - //IR.InfinteX is always a hotspot - put(Bytecode(greedy ? IR.InfiniteEnd : IR.InfiniteQEnd, len)); - put(Bytecode.init); //merge index - } - else//vanila {0,inf} - { - Bytecode op = Bytecode(greedy ? IR.InfiniteStart : IR.InfiniteQStart, len); - if(replace) - ir[offset] = op; - else - insertInPlace(ir, offset, op); - //IR.InfinteX is always a hotspot - put(Bytecode(greedy ? IR.InfiniteEnd : IR.InfiniteQEnd, len)); - put(Bytecode.init); //merge index - - } + g.fixRepetition(offset, min, max, greedy); } //parse and store IR for atom void parseAtom() { - if(empty) + if (empty) return; - switch(current) + switch (current) { case '*', '?', '+', '|', '{', '}': error("'*', '+', '?', '{', '}' not allowed in atom"); break; case '.': - put(Bytecode(IR.Any, 0)); + if (re_flags & RegexOption.singleline) + g.put(Bytecode(IR.Any, 0)); + else + { + CodepointSet set; + g.charsetToIr(set.add('\n','\n'+1).add('\r', '\r'+1).inverted); + } next(); break; case '[': @@ -770,60 +955,38 @@ struct Parser(R) parseEscape(); break; case '^': - put(Bytecode(IR.Bol, 0)); + if (re_flags & RegexOption.multiline) + g.put(Bytecode(IR.Bol, 0)); + else + g.put(Bytecode(IR.Bof, 0)); next(); break; case '$': - put(Bytecode(IR.Eol, 0)); + if (re_flags & RegexOption.multiline) + g.put(Bytecode(IR.Eol, 0)); + else + g.put(Bytecode(IR.Eof, 0)); next(); break; default: //FIXME: getCommonCasing in new std uni - if(re_flags & RegexOption.casefold) + if (re_flags & RegexOption.casefold) { auto range = simpleCaseFoldings(current); assert(range.length <= 5); - if(range.length == 1) - put(Bytecode(IR.Char, range.front)); + if (range.length == 1) + g.put(Bytecode(IR.Char, range.front)); else - foreach(v; range) - put(Bytecode(IR.OrChar, v, cast(uint)range.length)); + foreach (v; range) + g.put(Bytecode(IR.OrChar, v, cast(uint) range.length)); } else - put(Bytecode(IR.Char, current)); + g.put(Bytecode(IR.Char, current)); next(); } } - //generate code for start of lookaround: (?= (?! (?<= (?= 0) - { - if(ivals.length*2 > maxCharsetUsed) - put(Bytecode(IR.Trie, cast(uint)n)); - else - put(Bytecode(IR.CodepointSet, cast(uint)n)); - return; - } - if(ivals.length*2 > maxCharsetUsed) - { - auto t = getTrie(set); - put(Bytecode(IR.Trie, cast(uint)tries.length)); - tries ~= t; - debug(std_regex_allocation) writeln("Trie generated"); - } - else - { - put(Bytecode(IR.CodepointSet, cast(uint)charsets.length)); - tries ~= Trie.init; - } - charsets ~= set; - assert(charsets.length == tries.length); - } + g.charsetToIr(vstack.top); } //parse and generate IR for escape stand alone escape sequence @trusted void parseEscape() {//accesses array of appender - - switch(current) + import std.algorithm.iteration : sum; + switch (current) { - case 'f': next(); put(Bytecode(IR.Char, '\f')); break; - case 'n': next(); put(Bytecode(IR.Char, '\n')); break; - case 'r': next(); put(Bytecode(IR.Char, '\r')); break; - case 't': next(); put(Bytecode(IR.Char, '\t')); break; - case 'v': next(); put(Bytecode(IR.Char, '\v')); break; + case 'f': next(); g.put(Bytecode(IR.Char, '\f')); break; + case 'n': next(); g.put(Bytecode(IR.Char, '\n')); break; + case 'r': next(); g.put(Bytecode(IR.Char, '\r')); break; + case 't': next(); g.put(Bytecode(IR.Char, '\t')); break; + case 'v': next(); g.put(Bytecode(IR.Char, '\v')); break; case 'd': next(); - charsetToIr(unicode.Nd); + g.charsetToIr(unicode.Nd); break; case 'D': next(); - charsetToIr(unicode.Nd.inverted); + g.charsetToIr(unicode.Nd.inverted); break; - case 'b': next(); put(Bytecode(IR.Wordboundary, 0)); break; - case 'B': next(); put(Bytecode(IR.Notwordboundary, 0)); break; + case 'b': next(); g.put(Bytecode(IR.Wordboundary, 0)); break; + case 'B': next(); g.put(Bytecode(IR.Notwordboundary, 0)); break; case 's': next(); - charsetToIr(unicode.White_Space); + g.charsetToIr(unicode.White_Space); break; case 'S': next(); - charsetToIr(unicode.White_Space.inverted); + g.charsetToIr(unicode.White_Space.inverted); break; case 'w': next(); - charsetToIr(wordCharacter); + g.charsetToIr(wordCharacter); break; case 'W': next(); - charsetToIr(wordCharacter.inverted); + g.charsetToIr(wordCharacter.inverted); break; case 'p': case 'P': auto CodepointSet = parseUnicodePropertySpec(current == 'P'); - charsetToIr(CodepointSet); + g.charsetToIr(CodepointSet); break; case 'x': - uint code = parseUniHex(pat, 2); + immutable code = parseUniHex(pat, 2); next(); - put(Bytecode(IR.Char,code)); + g.put(Bytecode(IR.Char,code)); break; case 'u': case 'U': - uint code = parseUniHex(pat, current == 'u' ? 4 : 8); + immutable code = parseUniHex(pat, current == 'u' ? 4 : 8); next(); - put(Bytecode(IR.Char, code)); + g.put(Bytecode(IR.Char, code)); break; case 'c': //control codes Bytecode code = Bytecode(IR.Char, parseControlCode()); next(); - put(code); + g.put(code); break; case '0': next(); - put(Bytecode(IR.Char, 0));//NUL character + g.put(Bytecode(IR.Char, 0));//NUL character break; case '1': .. case '9': - uint nref = cast(uint)current - '0'; - uint maxBackref = sum(groupStack.data); + uint nref = cast(uint) current - '0'; + immutable maxBackref = sum(g.groupStack.data); enforce(nref < maxBackref, "Backref to unseen group"); //perl's disambiguation rule i.e. //get next digit only if there is such group number - while(nref < maxBackref && next() && std.ascii.isDigit(current)) + while (nref < maxBackref && next() && std.ascii.isDigit(current)) { nref = nref * 10 + current - '0'; } - if(nref >= maxBackref) + if (nref >= maxBackref) nref /= 10; - enforce(!isOpenGroup(nref), "Backref to open group"); - uint localLimit = maxBackref - groupStack.top; - if(nref >= localLimit) + enforce(!g.isOpenGroup(nref), "Backref to open group"); + uint localLimit = maxBackref - g.groupStack.top; + if (nref >= localLimit) { - put(Bytecode(IR.Backref, nref-localLimit)); - ir[$-1].setLocalRef(); + g.put(Bytecode(IR.Backref, nref-localLimit)); + g.ir[$-1].setLocalRef(); } else - put(Bytecode(IR.Backref, nref)); - markBackref(nref); + g.put(Bytecode(IR.Backref, nref)); + g.markBackref(nref); break; default: + if (current >= privateUseStart && current <= privateUseEnd) + { + g.endPattern(current - privateUseStart + 1); + break; + } auto op = Bytecode(IR.Char, current); next(); - put(op); + g.put(op); } } @@ -1391,21 +1501,21 @@ struct Parser(R) enum MAX_PROPERTY = 128; char[MAX_PROPERTY] result; uint k = 0; - enforce(next()); - if(current == '{') + enforce(next(), "eof parsing unicode property spec"); + if (current == '{') { - while(k < MAX_PROPERTY && next() && current !='}' && current !=':') - if(current != '-' && current != ' ' && current != '_') - result[k++] = cast(char)std.ascii.toLower(current); + while (k < MAX_PROPERTY && next() && current !='}' && current !=':') + if (current != '-' && current != ' ' && current != '_') + result[k++] = cast(char) std.ascii.toLower(current); enforce(k != MAX_PROPERTY, "invalid property name"); enforce(current == '}', "} expected "); } else {//single char properties e.g.: \pL, \pN ... enforce(current < 0x80, "invalid property name"); - result[k++] = cast(char)current; + result[k++] = cast(char) current; } - auto s = getUnicodeSet(result[0..k], negated, + auto s = getUnicodeSet(result[0 .. k], negated, cast(bool)(re_flags & RegexOption.casefold)); enforce(!s.empty, "unrecognized unicode property spec"); next(); @@ -1415,9 +1525,9 @@ struct Parser(R) // @trusted void error(string msg) { - import std.format; + import std.array : appender; + import std.format : formattedWrite; auto app = appender!string(); - ir = null; formattedWrite(app, "%s\nPattern with error: `%s` <--HERE-- `%s`", msg, origin[0..$-pat.length], pat); throw new RegexException(app.data); @@ -1432,10 +1542,9 @@ struct Parser(R) } /+ - lightweight post process step, - only essentials + Postproces the IR, then optimize. +/ -@trusted void lightPostprocess(Char)(ref Regex!Char zis) +@trusted void postprocess(Char)(ref Regex!Char zis) {//@@@BUG@@@ write is @system with(zis) { @@ -1452,21 +1561,21 @@ struct Parser(R) auto counterRange = FixedStack!uint(new uint[maxCounterDepth+1], -1); counterRange.push(1); ulong cumRange = 0; - for(uint i = 0; i < ir.length; i += ir[i].length) + for (uint i = 0; i < ir.length; i += ir[i].length) { - if(ir[i].hotspot) + if (ir[i].hotspot) { assert(i + 1 < ir.length, "unexpected end of IR while looking for hotspot"); ir[i+1] = Bytecode.fromRaw(hotspotTableSize); hotspotTableSize += counterRange.top; } - switch(ir[i].code) + switch (ir[i].code) { case IR.RepeatStart, IR.RepeatQStart: uint repEnd = cast(uint)(i + ir[i].data + IRL!(IR.RepeatStart)); assert(ir[repEnd].code == ir[i].paired.code); - uint max = ir[repEnd + 4].raw; + immutable max = ir[repEnd + 4].raw; ir[repEnd+2].raw = counterRange.top; ir[repEnd+3].raw *= counterRange.top; ir[repEnd+4].raw *= counterRange.top; @@ -1474,7 +1583,7 @@ struct Parser(R) cumRange += cntRange; enforce(cumRange < maxCumulativeRepetitionLength, "repetition length limit is exceeded"); - counterRange.push(cast(uint)cntRange + counterRange.top); + counterRange.push(cast(uint) cntRange + counterRange.top); threadCount += counterRange.top; break; case IR.RepeatEnd, IR.RepeatQEnd: @@ -1482,12 +1591,12 @@ struct Parser(R) counterRange.pop(); break; case IR.GroupStart: - if(isBackref(ir[i].data)) + if (isBackref(ir[i].data)) ir[i].setBackrefence(); threadCount += counterRange.top; break; case IR.GroupEnd: - if(isBackref(ir[i].data)) + if (isBackref(ir[i].data)) ir[i].setBackrefence(); threadCount += counterRange.top; break; @@ -1496,29 +1605,121 @@ struct Parser(R) } } checkIfOneShot(); - if(!(flags & RegexInfo.oneShot)) + if (!(flags & RegexInfo.oneShot)) kickstart = Kickstart!Char(zis, new uint[](256)); debug(std_regex_allocation) writefln("IR processed, max threads: %d", threadCount); + optimize(zis); + } +} + +void fixupBytecode()(Bytecode[] ir) +{ + Stack!uint fixups; + + with(IR) for (uint i=0; itext"); } -unittest +@safe unittest { + import std.algorithm.comparison : equal; auto cr8 = ctRegex!("^(a)(b)?(c*)"); auto m8 = bmatch("abcc",cr8); assert(m8); @@ -499,8 +514,9 @@ unittest assert(equal(bmatch("xxqababqyy",cr9).captures, ["qababq", "b"])); } -unittest +@safe unittest { + import std.algorithm.comparison : equal; auto rtr = regex("a|b|c"); enum ctr = regex("a|b|c"); assert(equal(rtr.ir,ctr.ir)); @@ -511,8 +527,10 @@ unittest assert(equal(testCT.ir,testRT.ir)); } -unittest +@safe unittest { + import std.algorithm.iteration : map; + import std.algorithm.comparison : equal; enum cx = ctRegex!"(A|B|C)"; auto mx = match("B",cx); assert(mx); @@ -540,15 +558,17 @@ unittest assert(equal(map!"a.hit"(m9), ["First", "", "Second"])); } -unittest +@safe unittest { + import std.algorithm.iteration : map; + import std.algorithm.comparison : equal; //global matching void test_body(alias matchFn)() { string s = "a quick brown fox jumps over a lazy dog"; auto r1 = regex("\\b[a-z]+\\b","g"); string[] test; - foreach(m; matchFn(s, r1)) + foreach (m; matchFn(s, r1)) test ~= m.hit; assert(equal(test, [ "a", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"])); auto free_reg = regex(` @@ -581,8 +601,10 @@ unittest } //tests for accumulated std.regex issues and other regressions -unittest +@safe unittest { + import std.algorithm.iteration : map; + import std.algorithm.comparison : equal; void test_body(alias matchFn)() { //issue 5857 @@ -604,7 +626,8 @@ unittest //issue 4574 //empty successful match still advances the input string[] pres, posts, hits; - foreach(m; matchFn("abcabc", regex("","g"))) { + foreach (m; matchFn("abcabc", regex("","g"))) + { pres ~= m.pre; posts ~= m.post; assert(m.hit.empty); @@ -643,7 +666,7 @@ unittest assert(collectException( regex(r"^(import|file|binary|config)\s+([^\(]+)\(?([^\)]*)\)?\s*$") ) is null); - foreach(ch; [Escapables]) + foreach (ch; [Escapables]) { assert(match(to!string(ch),regex(`[\`~ch~`]`))); assert(!match(to!string(ch),regex(`[^\`~ch~`]`))); @@ -660,13 +683,13 @@ unittest } // tests for replace -unittest +@safe unittest { void test(alias matchFn)() { import std.uni : toUpper; - foreach(i, v; TypeTuple!(string, wstring, dstring)) + foreach (i, v; AliasSeq!(string, wstring, dstring)) { auto baz(Cap)(Cap m) if (is(Cap == Captures!(Cap.String))) @@ -680,8 +703,9 @@ unittest == to!String("ack capacity")); assert(std.regex.replace!(matchFn)(to!String("noon"), regex(to!String("^n")), to!String("[$&]")) == to!String("[n]oon")); - assert(std.regex.replace!(matchFn)(to!String("test1 test2"), regex(to!String(`\w+`),"g"), to!String("$`:$'")) - == to!String(": test2 test1 :")); + assert(std.regex.replace!(matchFn)( + to!String("test1 test2"), regex(to!String(`\w+`),"g"), to!String("$`:$'") + ) == to!String(": test2 test1 :")); auto s = std.regex.replace!(baz!(Captures!(String)))(to!String("Strap a rocket engine on a chicken."), regex(to!String("[ar]"), "g")); assert(s == "StRAp A Rocket engine on A chicken."); @@ -693,8 +717,9 @@ unittest } // tests for splitter -unittest +@safe unittest { + import std.algorithm.comparison : equal; auto s1 = ", abc, de, fg, hi, "; auto sp1 = splitter(s1, regex(", *")); auto w1 = ["", "abc", "de", "fg", "hi", ""]; @@ -705,26 +730,28 @@ unittest auto w2 = ["", "abc", "de", "fg", "hi"]; uint cnt; - foreach(e; sp2) { + foreach (e; sp2) + { assert(w2[cnt++] == e); } assert(equal(sp2, w2)); } -unittest +@safe unittest { char[] s1 = ", abc, de, fg, hi, ".dup; auto sp2 = splitter(s1, regex(", *")); } -unittest +@safe unittest { + import std.algorithm.comparison : equal; auto s1 = ", abc, de, fg, hi, "; auto w1 = ["", "abc", "de", "fg", "hi", ""]; assert(equal(split(s1, regex(", *")), w1[])); } -unittest +@safe unittest { // bugzilla 7141 string pattern = `[a\--b]`; assert(match("-", pattern)); @@ -732,24 +759,35 @@ unittest string pattern2 = `[&-z]`; assert(match("b", pattern2)); } -unittest +@safe unittest {//bugzilla 7111 assert(match("", regex("^"))); } -unittest +@safe unittest {//bugzilla 7300 assert(!match("a"d, "aa"d)); } -unittest +// bugzilla 7551 +@safe unittest +{ + auto r = regex("[]abc]*"); + assert("]ab".matchFirst(r).hit == "]ab"); + assertThrown(regex("[]")); + auto r2 = regex("[]abc--ab]*"); + assert("]ac".matchFirst(r2).hit == "]"); +} + +@safe unittest {//bugzilla 7674 assert("1234".replace(regex("^"), "$$") == "$1234"); assert("hello?".replace(regex(r"\?", "g"), r"\?") == r"hello\?"); assert("hello?".replace(regex(r"\?", "g"), r"\\?") != r"hello\?"); } -unittest +@safe unittest {// bugzilla 7679 - foreach(S; TypeTuple!(string, wstring, dstring)) + import std.algorithm.comparison : equal; + foreach (S; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 enum re = ctRegex!(to!S(r"\.")); auto str = to!S("a.b"); @@ -757,7 +795,7 @@ unittest assert(split(str, re) == [to!S("a"), to!S("b")]); }(); } -unittest +@safe unittest {//bugzilla 8203 string data = " NAME = XPAW01_STA:STATION @@ -767,20 +805,20 @@ unittest auto r = regex( r"^NAME = (?P[a-zA-Z0-9_]+):*(?P[a-zA-Z0-9_]*)","gm"); auto uniCapturesNew = match(uniFileOld, r); - for(int i = 0; i < 20; i++) + for (int i = 0; i < 20; i++) foreach (matchNew; uniCapturesNew) {} //a second issue with same symptoms auto r2 = regex(`([а-ÑÐ-Я\-_]+\s*)+(?<=[\s\.,\^])`); match("Ð°Ð»Ð»ÐµÑ Ð¢ÐµÐ°Ñ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ", r2); } -unittest +@safe unittest {// bugzilla 8637 purity of enforce auto m = match("hello world", regex("world")); enforce(m); } // bugzilla 8725 -unittest +@safe unittest { static italic = regex( r"\* (?!\s+) @@ -793,7 +831,7 @@ unittest } // bugzilla 8349 -unittest +@safe unittest { enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)"; enum peakRegex = ctRegex!(peakRegexStr); @@ -802,8 +840,9 @@ unittest } // bugzilla 9211 -unittest +@safe unittest { + import std.algorithm.comparison : equal; auto rx_1 = regex(r"^(\w)*(\d)"); auto m = match("1234", rx_1); assert(equal(m.front, ["1234", "3", "4"])); @@ -813,7 +852,7 @@ unittest } // bugzilla 9280 -unittest +@safe unittest { string tomatch = "a!b@c"; static r = regex(r"^(?P.*?)!(?P.*?)@(?P.*?)$"); @@ -826,7 +865,7 @@ unittest // bugzilla 9579 -unittest +@safe unittest { char[] input = ['a', 'b', 'c']; string format = "($1)"; @@ -837,14 +876,14 @@ unittest } // bugzilla 9634 -unittest +@safe unittest { auto re = ctRegex!"(?:a+)"; assert(match("aaaa", re).hit == "aaaa"); } //bugzilla 10798 -unittest +@safe unittest { auto cr = ctRegex!("[abcd--c]*"); auto m = "abc".match(cr); @@ -853,7 +892,7 @@ unittest } // bugzilla 10913 -unittest +@system unittest { @system static string foo(const(char)[] s) { @@ -872,7 +911,7 @@ unittest } // bugzilla 11262 -unittest +@safe unittest { enum reg = ctRegex!(r",", "g"); auto str = "This,List"; @@ -881,14 +920,15 @@ unittest } // bugzilla 11775 -unittest +@safe unittest { assert(collectException(regex("a{1,0}"))); } // bugzilla 11839 -unittest +@safe unittest { + import std.algorithm.comparison : equal; assert(regex(`(?P\w+)`).namedCaptures.equal(["var1"])); assert(collectException(regex(`(?P<1>\w+)`))); assert(regex(`(?P\w+)`).namedCaptures.equal(["v1"])); @@ -897,7 +937,7 @@ unittest } // bugzilla 12076 -unittest +@safe unittest { auto RE = ctRegex!(r"(?abc)`); assert(collectException("abc".matchFirst(r)["b"])); } // bugzilla 12691 -unittest +@safe unittest { assert(bmatch("e@", "^([a-z]|)*$").empty); assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty); } //bugzilla 12713 -unittest +@safe unittest { assertThrown(regex("[[a-z]([a-z]|(([[a-z])))")); } //bugzilla 12747 -unittest +@safe unittest { assertThrown(regex(`^x(\1)`)); assertThrown(regex(`^(x(\1))`)); assertThrown(regex(`^((x)(?=\1))`)); } + +// bugzilla 14504 +@safe unittest +{ + auto p = ctRegex!("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?" ~ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +// bugzilla 14529 +@safe unittest +{ + auto ctPat2 = regex(r"^[CDF]$", "i"); + foreach (v; ["C", "c", "D", "d", "F", "f"]) + assert(matchAll(v, ctPat2).front.hit == v); +} + +// bugzilla 14615 +@safe unittest +{ + import std.stdio : writeln; + import std.regex : replaceFirst, replaceFirstInto, regex; + import std.array : appender; + + auto example = "Hello, world!"; + auto pattern = regex("^Hello, (bug)"); // won't find this one + auto result = replaceFirst(example, pattern, "$1 Sponge Bob"); + assert(result == "Hello, world!"); // Ok. + + auto sink = appender!string; + replaceFirstInto(sink, example, pattern, "$1 Sponge Bob"); + assert(sink.data == "Hello, world!"); + replaceAllInto(sink, example, pattern, "$1 Sponge Bob"); + assert(sink.data == "Hello, world!Hello, world!"); +} + +// bugzilla 15573 +@safe unittest +{ + auto rx = regex("[c d]", "x"); + assert("a b".matchFirst(rx)); +} + +// bugzilla 15864 +@safe unittest +{ + regex(`((.+)`; + static titleRegex = ctRegex!titlePattern; + string input = "" ~ "<".repeat(100_000).join; + assert(input.matchFirst(titleRegex).empty); +} + +// bugzilla 17212 +@safe unittest +{ + auto r = regex(" [a] ", "x"); + assert("a".matchFirst(r)); +} + +// bugzilla 17157 +@safe unittest +{ + import std.algorithm.comparison : equal; + auto ctr = ctRegex!"(a)|(b)|(c)|(d)"; + auto r = regex("(a)|(b)|(c)|(d)", "g"); + auto s = "--a--b--c--d--"; + auto outcomes = [ + ["a", "a", "", "", ""], + ["b", "", "b", "", ""], + ["c", "", "", "c", ""], + ["d", "", "", "", "d"] + ]; + assert(equal!equal(s.matchAll(ctr), outcomes)); + assert(equal!equal(s.bmatch(r), outcomes)); +} diff --git a/std/regex/internal/thompson.d b/std/regex/internal/thompson.d index 1faf438a415..103726edb62 100644 --- a/std/regex/internal/thompson.d +++ b/std/regex/internal/thompson.d @@ -11,7 +11,7 @@ module std.regex.internal.thompson; package(std.regex): import std.regex.internal.ir; -import std.range; +import std.range.primitives; //State of VM thread struct Thread(DataIndex) @@ -30,7 +30,7 @@ struct ThreadList(DataIndex) //add new thread to the start of list void insertFront(Thread!DataIndex* t) { - if(tip) + if (tip) { t.next = tip; tip = t; @@ -44,7 +44,7 @@ struct ThreadList(DataIndex) //add new thread to the end of list void insertBack(Thread!DataIndex* t) { - if(toe) + if (toe) { toe.next = t; toe = t; @@ -57,7 +57,7 @@ struct ThreadList(DataIndex) Thread!DataIndex* fetch() { auto t = tip; - if(tip == toe) + if (tip == toe) tip = toe = null; else tip = tip.next; @@ -86,14 +86,642 @@ struct ThreadList(DataIndex) } } +template ThompsonOps(E, S, bool withInput:true) +{ +@trusted: + static bool op(IR code:IR.End)(E* e, S* state) + { + with(e) with(state) + { + finish(t, matches, re.ir[t.pc].data); + //fix endpoint of the whole match + matches[0].end = index; + recycle(t); + //cut off low priority threads + recycle(clist); + recycle(worklist); + debug(std_regex_matcher) writeln("Finished thread ", matches); + return false; // no more state to eval + } + } + + static bool op(IR code:IR.Wordboundary)(E* e, S* state) + { + with(e) with(state) + { + dchar back; + DataIndex bi; + //at start & end of input + if (atStart && wordMatcher[front]) + { + t.pc += IRL!(IR.Wordboundary); + return true; + } + else if (atEnd && s.loopBack(index).nextChar(back, bi) + && wordMatcher[back]) + { + t.pc += IRL!(IR.Wordboundary); + return true; + } + else if (s.loopBack(index).nextChar(back, bi)) + { + bool af = wordMatcher[front]; + bool ab = wordMatcher[back]; + if (af ^ ab) + { + t.pc += IRL!(IR.Wordboundary); + return true; + } + } + return popState(e); + } + } + + static bool op(IR code:IR.Notwordboundary)(E* e, S* state) + { + with(e) with(state) + { + dchar back; + DataIndex bi; + //at start & end of input + if (atStart && wordMatcher[front]) + { + return popState(e); + } + else if (atEnd && s.loopBack(index).nextChar(back, bi) + && wordMatcher[back]) + { + return popState(e); + } + else if (s.loopBack(index).nextChar(back, bi)) + { + bool af = wordMatcher[front]; + bool ab = wordMatcher[back] != 0; + if (af ^ ab) + { + return popState(e); + } + } + t.pc += IRL!(IR.Notwordboundary); + } + return true; + } + + static bool op(IR code:IR.Bof)(E* e, S* state) + { + with(e) with(state) + { + if (atStart) + { + t.pc += IRL!(IR.Bof); + return true; + } + else + { + return popState(e); + } + } + } + + static bool op(IR code:IR.Bol)(E* e, S* state) + { + with(e) with(state) + { + dchar back; + DataIndex bi; + if (atStart + ||(s.loopBack(index).nextChar(back,bi) + && startOfLine(back, front == '\n'))) + { + t.pc += IRL!(IR.Bol); + return true; + } + else + { + return popState(e); + } + } + } + + static bool op(IR code:IR.Eof)(E* e, S* state) + { + with(e) with(state) + { + if (atEnd) + { + t.pc += IRL!(IR.Eol); + return true; + } + else + { + return popState(e); + } + } + } + + static bool op(IR code:IR.Eol)(E* e, S* state) + { + with(e) with(state) + { + dchar back; + DataIndex bi; + //no matching inside \r\n + if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back, bi) + && back == '\r'))) + { + t.pc += IRL!(IR.Eol); + return true; + } + else + { + return popState(e); + } + + } + } + + static bool op(IR code:IR.InfiniteStart)(E* e, S* state) + { + with(e) with(state) + t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteStart); + return op!(IR.InfiniteEnd)(e,state); + } + + static bool op(IR code:IR.InfiniteBloomStart)(E* e, S* state) + { + with(e) with(state) + t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteBloomStart); + return op!(IR.InfiniteBloomEnd)(e,state); + } + + static bool op(IR code:IR.InfiniteQStart)(E* e, S* state) + { + with(e) with(state) + t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteQStart); + return op!(IR.InfiniteQEnd)(e,state); + } + + static bool op(IR code:IR.RepeatStart)(E* e, S* state) + { + with(e) with(state) + t.pc += re.ir[t.pc].data + IRL!(IR.RepeatStart); + return op!(IR.RepeatEnd)(e,state); + } + + static bool op(IR code:IR.RepeatQStart)(E* e, S* state) + { + with(e) with(state) + t.pc += re.ir[t.pc].data + IRL!(IR.RepeatQStart); + return op!(IR.RepeatQEnd)(e,state); + } + + static bool op(IR code)(E* e, S* state) + if (code == IR.RepeatEnd || code == IR.RepeatQEnd) + { + with(e) with(state) + { + //len, step, min, max + uint len = re.ir[t.pc].data; + uint step = re.ir[t.pc+2].raw; + uint min = re.ir[t.pc+3].raw; + if (t.counter < min) + { + t.counter += step; + t.pc -= len; + return true; + } + if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) + { + debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", + t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); + merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; + } + else + { + debug(std_regex_matcher) + writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", + t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); + return popState(e); + } + uint max = re.ir[t.pc+4].raw; + if (t.counter < max) + { + if (re.ir[t.pc].code == IR.RepeatEnd) + { + //queue out-of-loop thread + worklist.insertFront(fork(t, t.pc + IRL!(IR.RepeatEnd), t.counter % step)); + t.counter += step; + t.pc -= len; + } + else + { + //queue into-loop thread + worklist.insertFront(fork(t, t.pc - len, t.counter + step)); + t.counter %= step; + t.pc += IRL!(IR.RepeatEnd); + } + } + else + { + t.counter %= step; + t.pc += IRL!(IR.RepeatEnd); + } + return true; + } + } + + static bool op(IR code)(E* e, S* state) + if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd) + { + with(e) with(state) + { + if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) + { + debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", + t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); + merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; + } + else + { + debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", + t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); + return popState(e); + } + uint len = re.ir[t.pc].data; + uint pc1, pc2; //branches to take in priority order + if (re.ir[t.pc].code == IR.InfiniteEnd) + { + pc1 = t.pc - len; + pc2 = t.pc + IRL!(IR.InfiniteEnd); + } + else + { + pc1 = t.pc + IRL!(IR.InfiniteEnd); + pc2 = t.pc - len; + } + worklist.insertFront(fork(t, pc2, t.counter)); + t.pc = pc1; + return true; + } + } + + static bool op(IR code)(E* e, S* state) + if (code == IR.InfiniteBloomEnd) + { + with(e) with(state) + { + if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) + { + debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", + t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); + merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; + } + else + { + debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", + t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); + return popState(e); + } + uint len = re.ir[t.pc].data; + uint pc1, pc2; //branches to take in priority order + pc1 = t.pc - len; + pc2 = t.pc + IRL!(IR.InfiniteBloomEnd); + uint filterIndex = re.ir[t.pc + 2].raw; + if (re.filters[filterIndex][front]) + worklist.insertFront(fork(t, pc2, t.counter)); + t.pc = pc1; + return true; + } + } + + static bool op(IR code:IR.OrEnd)(E* e, S* state) + { + with(e) with(state) + { + if (merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) + { + debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", + t.pc, s[index .. s.lastIndex], genCounter, merge[re.ir[t.pc + 1].raw + t.counter] ); + merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; + t.pc += IRL!(IR.OrEnd); + } + else + { + debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", + t.pc, s[index .. s.lastIndex], genCounter, merge[re.ir[t.pc + 1].raw + t.counter] ); + return popState(e); + } + return true; + } + } + + static bool op(IR code:IR.OrStart)(E* e, S* state) + { + with(e) with(state) + { + t.pc += IRL!(IR.OrStart); + return op!(IR.Option)(e,state); + } + } + + static bool op(IR code:IR.Option)(E* e, S* state) + { + with(e) with(state) + { + uint next = t.pc + re.ir[t.pc].data + IRL!(IR.Option); + //queue next Option + if (re.ir[next].code == IR.Option) + { + worklist.insertFront(fork(t, next, t.counter)); + } + t.pc += IRL!(IR.Option); + return true; + } + } + + static bool op(IR code:IR.GotoEndOr)(E* e, S* state) + { + with(e) with(state) + { + t.pc = t.pc + re.ir[t.pc].data + IRL!(IR.GotoEndOr); + return op!(IR.OrEnd)(e, state); + } + } + + static bool op(IR code:IR.GroupStart)(E* e, S* state) + { + with(e) with(state) + { + uint n = re.ir[t.pc].data; + t.matches.ptr[n].begin = index; + t.pc += IRL!(IR.GroupStart); + return true; + } + } + static bool op(IR code:IR.GroupEnd)(E* e, S* state) + { + with(e) with(state) + { + uint n = re.ir[t.pc].data; + t.matches.ptr[n].end = index; + t.pc += IRL!(IR.GroupEnd); + return true; + } + } + + static bool op(IR code:IR.Backref)(E* e, S* state) + { + with(e) with(state) + { + uint n = re.ir[t.pc].data; + Group!DataIndex* source = re.ir[t.pc].localRef ? t.matches.ptr : backrefed.ptr; + assert(source); + if (source[n].begin == source[n].end)//zero-width Backref! + { + t.pc += IRL!(IR.Backref); + return true; + } + else + { + size_t idx = source[n].begin + t.uopCounter; + size_t end = source[n].end; + if (s[idx .. end].front == front) + { + import std.utf : stride; + + t.uopCounter += stride(s[idx .. end], 0); + if (t.uopCounter + source[n].begin == source[n].end) + {//last codepoint + t.pc += IRL!(IR.Backref); + t.uopCounter = 0; + } + nlist.insertBack(t); + } + else + recycle(t); + t = worklist.fetch(); + return t != null; + } + } + } + + + static bool op(IR code)(E* e, S* state) + if (code == IR.LookbehindStart || code == IR.NeglookbehindStart) + { + with(e) with(state) + { + uint len = re.ir[t.pc].data; + uint ms = re.ir[t.pc + 1].raw, me = re.ir[t.pc + 2].raw; + uint end = t.pc + len + IRL!(IR.LookbehindEnd) + IRL!(IR.LookbehindStart); + bool positive = re.ir[t.pc].code == IR.LookbehindStart; + static if (Stream.isLoopback) + auto matcher = fwdMatcher(t.pc, end, subCounters.get(t.pc, 0)); + else + auto matcher = bwdMatcher(t.pc, end, subCounters.get(t.pc, 0)); + matcher.re.ngroup = me - ms; + matcher.backrefed = backrefed.empty ? t.matches : backrefed; + //backMatch + auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookbehindStart)); + freelist = matcher.freelist; + subCounters[t.pc] = matcher.genCounter; + if ((mRes != 0 ) ^ positive) + { + return popState(e); + } + t.pc = end; + return true; + } + } + + static bool op(IR code)(E* e, S* state) + if (code == IR.LookaheadStart || code == IR.NeglookaheadStart) + { + with(e) with(state) + { + auto save = index; + uint len = re.ir[t.pc].data; + uint ms = re.ir[t.pc+1].raw, me = re.ir[t.pc+2].raw; + uint end = t.pc+len+IRL!(IR.LookaheadEnd)+IRL!(IR.LookaheadStart); + bool positive = re.ir[t.pc].code == IR.LookaheadStart; + static if (Stream.isLoopback) + auto matcher = bwdMatcher(t.pc, end, subCounters.get(t.pc, 0)); + else + auto matcher = fwdMatcher(t.pc, end, subCounters.get(t.pc, 0)); + matcher.re.ngroup = me - ms; + matcher.backrefed = backrefed.empty ? t.matches : backrefed; + auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookaheadStart)); + freelist = matcher.freelist; + subCounters[t.pc] = matcher.genCounter; + s.reset(index); + next(); + if ((mRes != 0) ^ positive) + { + return popState(e); + } + t.pc = end; + return true; + } + } + + static bool op(IR code)(E* e, S* state) + if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd || + code == IR.LookbehindEnd || code == IR.NeglookbehindEnd) + { + with(e) with(state) + { + finish(t, matches.ptr[0 .. re.ngroup], re.ir[t.pc].data); + recycle(t); + //cut off low priority threads + recycle(clist); + recycle(worklist); + return false; // no more state + } + } + + static bool op(IR code:IR.Nop)(E* e, S* state) + { + with(state) t.pc += IRL!(IR.Nop); + return true; + } + + static bool op(IR code:IR.OrChar)(E* e, S* state) + { + with(e) with(state) + { + uint len = re.ir[t.pc].sequence; + uint end = t.pc + len; + static assert(IRL!(IR.OrChar) == 1); + for (; t.pc < end; t.pc++) + if (re.ir[t.pc].data == front) + break; + if (t.pc != end) + { + t.pc = end; + nlist.insertBack(t); + } + else + recycle(t); + t = worklist.fetch(); + return t != null; + } + } + + static bool op(IR code:IR.Char)(E* e, S* state) + { + with(e) with(state) + { + if (front == re.ir[t.pc].data) + { + t.pc += IRL!(IR.Char); + nlist.insertBack(t); + } + else + recycle(t); + t = worklist.fetch(); + return t != null; + } + } + + static bool op(IR code:IR.Any)(E* e, S* state) + { + with(e) with(state) + { + t.pc += IRL!(IR.Any); + nlist.insertBack(t); + t = worklist.fetch(); + return t != null; + } + } + + static bool op(IR code:IR.CodepointSet)(E* e, S* state) + { + with(e) with(state) + { + if (re.charsets[re.ir[t.pc].data].scanFor(front)) + { + t.pc += IRL!(IR.CodepointSet); + nlist.insertBack(t); + } + else + { + recycle(t); + } + t = worklist.fetch(); + return t != null; + } + } + + static bool op(IR code:IR.Trie)(E* e, S* state) + { + with(e) with(state) + { + if (re.matchers[re.ir[t.pc].data][front]) + { + t.pc += IRL!(IR.Trie); + nlist.insertBack(t); + } + else + { + recycle(t); + } + t = worklist.fetch(); + return t != null; + } + } + +} + +template ThompsonOps(E,S, bool withInput:false) +{ +@trusted: + // can't match these without input + static bool op(IR code)(E* e, S* state) + if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet + || code == IR.Trie || code == IR.Char || code == IR.Any) + { + return state.popState(e); + } + + // special case of zero-width backref + static bool op(IR code:IR.Backref)(E* e, S* state) + { + with(e) with(state) + { + uint n = re.ir[t.pc].data; + Group!DataIndex* source = re.ir[t.pc].localRef ? t.matches.ptr : backrefed.ptr; + assert(source); + if (source[n].begin == source[n].end)//zero-width Backref! + { + t.pc += IRL!(IR.Backref); + return true; + } + else + return popState(e); + } + } + + // forward all control flow to normal versions + static bool op(IR code)(E* e, S* state) + if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet + && code != IR.Trie && code != IR.Char && code != IR.Any && code != IR.Backref) + { + return ThompsonOps!(E,S,true).op!code(e,state); + } +} + /+ Thomspon matcher does all matching in lockstep, never looking at the same char twice +/ -@trusted struct ThompsonMatcher(Char, Stream = Input!Char) - if(is(Char : dchar)) +@trusted struct ThompsonMatcher(Char, StreamType = Input!Char) +if (is(Char : dchar)) { alias DataIndex = Stream.DataIndex; + alias Stream = StreamType; + alias OpFunc = bool function(ThompsonMatcher*, State*); + alias BackMatcher = ThompsonMatcher!(Char, BackLooper!(Stream)); + alias OpBackFunc = bool function(BackMatcher*, BackMatcher.State*); Thread!DataIndex* freelist; ThreadList!DataIndex clist, nlist; DataIndex[] merge; @@ -104,10 +732,33 @@ struct ThreadList(DataIndex) DataIndex index; DataIndex genCounter; //merge trace counter, goes up on every dchar size_t[size_t] subCounters; //a table of gen counter per sub-engine: PC -> counter + OpFunc[] opCacheTrue; // pointers to Op!(IR.xyz) for each bytecode + OpFunc[] opCacheFalse; // ditto + OpBackFunc[] opCacheBackTrue; // ditto + OpBackFunc[] opCacheBackFalse; // ditto size_t threadSize; - bool matched; + int matched; bool exhausted; - static if(__traits(hasMember,Stream, "search")) + + static struct State + { + Thread!DataIndex* t; + ThreadList!DataIndex worklist; + Group!DataIndex[] matches; + + bool popState(E)(E* e) + { + with(e) + { + recycle(t); + t = worklist.fetch(); + return t != null; + } + } + + } + + static if (__traits(hasMember,Stream, "search")) { enum kicked = true; } @@ -123,7 +774,8 @@ struct ThreadList(DataIndex) static size_t initialMemory(const ref Regex!Char re) { - return getThreadSize(re)*re.threadCount + re.hotspotTableSize*size_t.sizeof; + return getThreadSize(re)*re.threadCount + re.hotspotTableSize*size_t.sizeof + +4*OpFunc.sizeof*re.ir.length; } //true if it's start of input @@ -134,7 +786,7 @@ struct ThreadList(DataIndex) bool next() { - if(!s.nextChar(front, index)) + if (!s.nextChar(front, index)) { index = s.lastIndex; return false; @@ -142,12 +794,12 @@ struct ThreadList(DataIndex) return true; } - static if(kicked) + static if (kicked) { bool search() { - if(!s.search(re.kickstart, front, index)) + if (!s.search(re.kickstart, front, index)) { index = s.lastIndex; return false; @@ -160,11 +812,35 @@ struct ThreadList(DataIndex) { threadSize = getThreadSize(re); prepareFreeList(re.threadCount, memory); - if(re.hotspotTableSize) + if (re.hotspotTableSize) { merge = arrayInChunk!(DataIndex)(re.hotspotTableSize, memory); merge[] = 0; } + opCacheTrue = arrayInChunk!(OpFunc)(re.ir.length, memory); + opCacheFalse = arrayInChunk!(OpFunc)(re.ir.length, memory); + opCacheBackTrue = arrayInChunk!(OpBackFunc)(re.ir.length, memory); + opCacheBackFalse = arrayInChunk!(OpBackFunc)(re.ir.length, memory); + + for (uint pc = 0; pc<re.ir.length; pc += re.ir[pc].length) + { + L_dispatch: + switch (re.ir[pc].code) + { + foreach (e; __traits(allMembers, IR)) + { + mixin(`case IR.`~e~`: + opCacheTrue[pc] = &Ops!(true).op!(IR.`~e~`); + opCacheBackTrue[pc] = &BackOps!(true).op!(IR.`~e~`); + opCacheFalse[pc] = &Ops!(false).op!(IR.`~e~`); + opCacheBackFalse[pc] = &BackOps!(false).op!(IR.`~e~`); + break L_dispatch; + `); + } + default: + assert(0, "Unrecognized instruction "~re.ir[pc].mnemonic); + } + } } this()(Regex!Char program, Stream stream, void[] memory) @@ -175,29 +851,49 @@ struct ThreadList(DataIndex) genCounter = 0; } - this(S)(ref ThompsonMatcher!(Char,S) matcher, Bytecode[] piece, Stream stream) + this(ref ThompsonMatcher matcher, size_t lo, size_t hi, Stream stream) { s = stream; re = matcher.re; - re.ir = piece; + re.ir = re.ir[lo .. hi]; threadSize = matcher.threadSize; merge = matcher.merge; freelist = matcher.freelist; + opCacheTrue = matcher.opCacheTrue[lo .. hi]; + opCacheBackTrue = matcher.opCacheBackTrue[lo .. hi]; + opCacheFalse = matcher.opCacheFalse[lo .. hi]; + opCacheBackFalse = matcher.opCacheBackFalse[lo .. hi]; front = matcher.front; index = matcher.index; } - auto fwdMatcher()(Bytecode[] piece, size_t counter) + this(ref BackMatcher matcher, size_t lo, size_t hi, Stream stream) { - auto m = ThompsonMatcher!(Char, Stream)(this, piece, s); + s = stream; + re = matcher.re; + re.ir = re.ir[lo .. hi]; + threadSize = matcher.threadSize; + merge = matcher.merge; + freelist = matcher.freelist; + opCacheTrue = matcher.opCacheBackTrue[lo .. hi]; + opCacheBackTrue = matcher.opCacheTrue[lo .. hi]; + opCacheFalse = matcher.opCacheBackFalse[lo .. hi]; + opCacheBackFalse = matcher.opCacheFalse[lo .. hi]; + front = matcher.front; + index = matcher.index; + } + + auto fwdMatcher()(size_t lo, size_t hi, size_t counter) + { + auto m = ThompsonMatcher!(Char, Stream)(this, lo, hi, s); m.genCounter = counter; return m; } - auto bwdMatcher()(Bytecode[] piece, size_t counter) + auto bwdMatcher()(size_t lo, size_t hi, size_t counter) { alias BackLooper = typeof(s.loopBack(index)); - auto m = ThompsonMatcher!(Char, BackLooper)(this, piece, s.loopBack(index)); + auto m = ThompsonMatcher!(Char, BackLooper)(this, lo, hi, s.loopBack(index)); m.genCounter = counter; m.next(); return m; @@ -211,55 +907,51 @@ struct ThreadList(DataIndex) return tmp; } - enum MatchResult{ - NoMatch, - PartialMatch, - Match, - } - - bool match(Group!DataIndex[] matches) + int match(Group!DataIndex[] matches) { debug(std_regex_matcher) writeln("------------------------------------------"); - if(exhausted) + if (exhausted) { return false; } - if(re.flags & RegexInfo.oneShot) + if (re.flags & RegexInfo.oneShot) { next(); exhausted = true; - return matchOneShot(matches)==MatchResult.Match; + return matchOneShot(matches); } - static if(kicked) - if(!re.kickstart.empty) + static if (kicked) + if (!re.kickstart.empty) return matchImpl!(true)(matches); return matchImpl!(false)(matches); } //match the input and fill matches - bool matchImpl(bool withSearch)(Group!DataIndex[] matches) + int matchImpl(bool withSearch)(Group!DataIndex[] matches) { - if(!matched && clist.empty) + if (!matched && clist.empty) { - static if(withSearch) + static if (withSearch) search(); else next(); } else//char in question is fetched in prev call to match { - matched = false; + matched = 0; } + State state; + state.matches = matches; - if(!atEnd)//if no char - for(;;) + if (!atEnd)//if no char + for (;;) { genCounter++; debug(std_regex_matcher) { - writefln("Threaded matching threads at %s", s[index..s.lastIndex]); - foreach(t; clist[]) + writefln("Threaded matching threads at %s", s[index .. s.lastIndex]); + foreach (t; clist[]) { assert(t); writef("pc=%s ",t.pc); @@ -267,33 +959,37 @@ struct ThreadList(DataIndex) writeln(); } } - for(Thread!DataIndex* t = clist.fetch(); t; t = clist.fetch()) + for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) { - eval!true(t, matches); + eval!true(&state); } - if(!matched)//if we already have match no need to push the engine - eval!true(createStart(index), matches);//new thread staring at this position - else if(nlist.empty) + //if we already have match no need to push the engine + if (!matched) + { + state.t = createStart(index); + eval!true(&state);//new thread staring at this position + } + else if (nlist.empty) { debug(std_regex_matcher) writeln("Stopped matching before consuming full input"); break;//not a partial match for sure } clist = nlist; nlist = (ThreadList!DataIndex).init; - if(clist.tip is null) + if (clist.tip is null) { - static if(withSearch) + static if (withSearch) { - if(!search()) + if (!search()) break; } else { - if(!next()) + if (!next()) break; } } - else if(!next()) + else if (!next()) { if (!atEnd) return false; exhausted = true; @@ -304,13 +1000,16 @@ struct ThreadList(DataIndex) genCounter++; //increment also on each end debug(std_regex_matcher) writefln("Threaded matching threads at end"); //try out all zero-width posibilities - for(Thread!DataIndex* t = clist.fetch(); t; t = clist.fetch()) + for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) + { + eval!false(&state); + } + if (!matched) { - eval!false(t, matches); + state.t = createStart(index); + eval!false(&state);//new thread starting at end of input } - if(!matched) - eval!false(createStart(index), matches);//new thread starting at end of input - if(matched) + if (matched) {//in case NFA found match along the way //and last possible longer alternative ultimately failed s.reset(matches[0].end);//reset to last successful match @@ -318,7 +1017,7 @@ struct ThreadList(DataIndex) //--- here the exact state of stream was restored --- exhausted = atEnd || !(re.flags & RegexOption.global); //+ empty match advances the input - if(!exhausted && matches[0].begin == matches[0].end) + if (!exhausted && matches[0].begin == matches[0].end) next(); } return matched; @@ -327,497 +1026,39 @@ struct ThreadList(DataIndex) /+ handle succesful threads +/ - void finish(const(Thread!DataIndex)* t, Group!DataIndex[] matches) + void finish(const(Thread!DataIndex)* t, Group!DataIndex[] matches, int code) { - matches.ptr[0..re.ngroup] = t.matches.ptr[0..re.ngroup]; + matches.ptr[0 .. re.ngroup] = t.matches.ptr[0 .. re.ngroup]; debug(std_regex_matcher) { writef("FOUND pc=%s prog_len=%s", t.pc, re.ir.length); - if(!matches.empty) + if (!matches.empty) writefln(": %s..%s", matches[0].begin, matches[0].end); - foreach(v; matches) + foreach (v; matches) writefln("%d .. %d", v.begin, v.end); } - matched = true; + matched = code; } + alias Ops(bool withInput) = ThompsonOps!(ThompsonMatcher, State, withInput); + alias BackOps(bool withInput) = ThompsonOps!(BackMatcher, BackMatcher.State, withInput); + /+ match thread against codepoint, cutting trough all 0-width instructions and taking care of control flow, then add it to nlist +/ - void eval(bool withInput)(Thread!DataIndex* t, Group!DataIndex[] matches) + void eval(bool withInput)(State* state) { - ThreadList!DataIndex worklist; debug(std_regex_matcher) writeln("---- Evaluating thread"); - for(;;) - { - debug(std_regex_matcher) - { - writef("\tpc=%s [", t.pc); - foreach(x; worklist[]) - writef(" %s ", x.pc); - writeln("]"); - } - switch(re.ir[t.pc].code) - { - case IR.End: - finish(t, matches); - matches[0].end = index; //fix endpoint of the whole match - recycle(t); - //cut off low priority threads - recycle(clist); - recycle(worklist); - debug(std_regex_matcher) writeln("Finished thread ", matches); - return; - case IR.Wordboundary: - dchar back; - DataIndex bi; - //at start & end of input - if(atStart && wordTrie[front]) - { - t.pc += IRL!(IR.Wordboundary); - break; - } - else if(atEnd && s.loopBack(index).nextChar(back, bi) - && wordTrie[back]) - { - t.pc += IRL!(IR.Wordboundary); - break; - } - else if(s.loopBack(index).nextChar(back, bi)) - { - bool af = wordTrie[front]; - bool ab = wordTrie[back]; - if(af ^ ab) - { - t.pc += IRL!(IR.Wordboundary); - break; - } - } - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - case IR.Notwordboundary: - dchar back; - DataIndex bi; - //at start & end of input - if(atStart && wordTrie[front]) - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - else if(atEnd && s.loopBack(index).nextChar(back, bi) - && wordTrie[back]) - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - else if(s.loopBack(index).nextChar(back, bi)) - { - bool af = wordTrie[front]; - bool ab = wordTrie[back] != 0; - if(af ^ ab) - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - } - t.pc += IRL!(IR.Wordboundary); - break; - case IR.Bol: - dchar back; - DataIndex bi; - if(atStart - ||( (re.flags & RegexOption.multiline) - && s.loopBack(index).nextChar(back,bi) - && startOfLine(back, front == '\n'))) - { - t.pc += IRL!(IR.Bol); - } - else - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - } - break; - case IR.Eol: - debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index..s.lastIndex]); - dchar back; - DataIndex bi; - //no matching inside \r\n - if(atEnd || ((re.flags & RegexOption.multiline) - && endOfLine(front, s.loopBack(index).nextChar(back, bi) - && back == '\r'))) - { - t.pc += IRL!(IR.Eol); - } - else - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - } - break; - case IR.InfiniteStart, IR.InfiniteQStart: - t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteStart); - goto case IR.InfiniteEnd; //both Q and non-Q - case IR.RepeatStart, IR.RepeatQStart: - t.pc += re.ir[t.pc].data + IRL!(IR.RepeatStart); - goto case IR.RepeatEnd; //both Q and non-Q - case IR.RepeatEnd: - case IR.RepeatQEnd: - //len, step, min, max - uint len = re.ir[t.pc].data; - uint step = re.ir[t.pc+2].raw; - uint min = re.ir[t.pc+3].raw; - if(t.counter < min) - { - t.counter += step; - t.pc -= len; - break; - } - if(merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - } - else - { - debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - uint max = re.ir[t.pc+4].raw; - if(t.counter < max) - { - if(re.ir[t.pc].code == IR.RepeatEnd) - { - //queue out-of-loop thread - worklist.insertFront(fork(t, t.pc + IRL!(IR.RepeatEnd), t.counter % step)); - t.counter += step; - t.pc -= len; - } - else - { - //queue into-loop thread - worklist.insertFront(fork(t, t.pc - len, t.counter + step)); - t.counter %= step; - t.pc += IRL!(IR.RepeatEnd); - } - } - else - { - t.counter %= step; - t.pc += IRL!(IR.RepeatEnd); - } - break; - case IR.InfiniteEnd: - case IR.InfiniteQEnd: - if(merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - } - else - { - debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, index, genCounter, merge[re.ir[t.pc + 1].raw+t.counter] ); - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - uint len = re.ir[t.pc].data; - uint pc1, pc2; //branches to take in priority order - if(re.ir[t.pc].code == IR.InfiniteEnd) - { - pc1 = t.pc - len; - pc2 = t.pc + IRL!(IR.InfiniteEnd); - } - else - { - pc1 = t.pc + IRL!(IR.InfiniteEnd); - pc2 = t.pc - len; - } - static if(withInput) - { - int test = quickTestFwd(pc1, front, re); - if(test >= 0) - { - worklist.insertFront(fork(t, pc2, t.counter)); - t.pc = pc1; - } - else - t.pc = pc2; - } - else - { - worklist.insertFront(fork(t, pc2, t.counter)); - t.pc = pc1; - } - break; - case IR.OrEnd: - if(merge[re.ir[t.pc + 1].raw+t.counter] < genCounter) - { - debug(std_regex_matcher) writefln("A thread(pc=%s) passed there : %s ; GenCounter=%s mergetab=%s", - t.pc, s[index .. s.lastIndex], genCounter, merge[re.ir[t.pc + 1].raw + t.counter] ); - merge[re.ir[t.pc + 1].raw+t.counter] = genCounter; - t.pc += IRL!(IR.OrEnd); - } - else - { - debug(std_regex_matcher) writefln("A thread(pc=%s) got merged there : %s ; GenCounter=%s mergetab=%s", - t.pc, s[index .. s.lastIndex], genCounter, merge[re.ir[t.pc + 1].raw + t.counter] ); - recycle(t); - t = worklist.fetch(); - if(!t) - return; - } - break; - case IR.OrStart: - t.pc += IRL!(IR.OrStart); - goto case; - case IR.Option: - uint next = t.pc + re.ir[t.pc].data + IRL!(IR.Option); - //queue next Option - if(re.ir[next].code == IR.Option) - { - worklist.insertFront(fork(t, next, t.counter)); - } - t.pc += IRL!(IR.Option); - break; - case IR.GotoEndOr: - t.pc = t.pc + re.ir[t.pc].data + IRL!(IR.GotoEndOr); - goto case IR.OrEnd; - case IR.GroupStart: - uint n = re.ir[t.pc].data; - t.matches.ptr[n].begin = index; - t.pc += IRL!(IR.GroupStart); - break; - case IR.GroupEnd: - uint n = re.ir[t.pc].data; - t.matches.ptr[n].end = index; - t.pc += IRL!(IR.GroupEnd); - break; - case IR.Backref: - uint n = re.ir[t.pc].data; - Group!DataIndex* source = re.ir[t.pc].localRef ? t.matches.ptr : backrefed.ptr; - assert(source); - if(source[n].begin == source[n].end)//zero-width Backref! - { - t.pc += IRL!(IR.Backref); - } - else static if(withInput) - { - size_t idx = source[n].begin + t.uopCounter; - size_t end = source[n].end; - if(s[idx..end].front == front) - { - t.uopCounter += std.utf.stride(s[idx..end], 0); - if(t.uopCounter + source[n].begin == source[n].end) - {//last codepoint - t.pc += IRL!(IR.Backref); - t.uopCounter = 0; - } - nlist.insertBack(t); - } - else - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - else - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - break; - case IR.LookbehindStart: - case IR.NeglookbehindStart: - uint len = re.ir[t.pc].data; - uint ms = re.ir[t.pc + 1].raw, me = re.ir[t.pc + 2].raw; - uint end = t.pc + len + IRL!(IR.LookbehindEnd) + IRL!(IR.LookbehindStart); - bool positive = re.ir[t.pc].code == IR.LookbehindStart; - static if(Stream.isLoopback) - auto matcher = fwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); - else - auto matcher = bwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); - matcher.re.ngroup = me - ms; - matcher.backrefed = backrefed.empty ? t.matches : backrefed; - //backMatch - auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookbehindStart)); - freelist = matcher.freelist; - subCounters[t.pc] = matcher.genCounter; - if((mRes == MatchResult.Match) ^ positive) - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - else - t.pc = end; - break; - case IR.LookaheadStart: - case IR.NeglookaheadStart: - auto save = index; - uint len = re.ir[t.pc].data; - uint ms = re.ir[t.pc+1].raw, me = re.ir[t.pc+2].raw; - uint end = t.pc+len+IRL!(IR.LookaheadEnd)+IRL!(IR.LookaheadStart); - bool positive = re.ir[t.pc].code == IR.LookaheadStart; - static if(Stream.isLoopback) - auto matcher = bwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); - else - auto matcher = fwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); - matcher.re.ngroup = me - ms; - matcher.backrefed = backrefed.empty ? t.matches : backrefed; - auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookaheadStart)); - freelist = matcher.freelist; - subCounters[t.pc] = matcher.genCounter; - s.reset(index); - next(); - if((mRes == MatchResult.Match) ^ positive) - { - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - } - else - t.pc = end; - break; - case IR.LookaheadEnd: - case IR.NeglookaheadEnd: - case IR.LookbehindEnd: - case IR.NeglookbehindEnd: - finish(t, matches.ptr[0 .. re.ngroup]); - recycle(t); - //cut off low priority threads - recycle(clist); - recycle(worklist); - return; - case IR.Nop: - t.pc += IRL!(IR.Nop); - break; - - static if(withInput) - { - case IR.OrChar: - uint len = re.ir[t.pc].sequence; - uint end = t.pc + len; - static assert(IRL!(IR.OrChar) == 1); - for(; t.pc < end; t.pc++) - if(re.ir[t.pc].data == front) - break; - if(t.pc != end) - { - t.pc = end; - nlist.insertBack(t); - } - else - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - case IR.Char: - if(front == re.ir[t.pc].data) - { - t.pc += IRL!(IR.Char); - nlist.insertBack(t); - } - else - recycle(t); - t = worklist.fetch(); - if(!t) - return; - break; - case IR.Any: - t.pc += IRL!(IR.Any); - if(!(re.flags & RegexOption.singleline) - && (front == '\r' || front == '\n')) - recycle(t); - else - nlist.insertBack(t); - t = worklist.fetch(); - if(!t) - return; - break; - case IR.CodepointSet: - if(re.charsets[re.ir[t.pc].data].scanFor(front)) - { - t.pc += IRL!(IR.CodepointSet); - nlist.insertBack(t); - } - else - { - recycle(t); - } - t = worklist.fetch(); - if(!t) - return; - break; - case IR.Trie: - if(re.tries[re.ir[t.pc].data][front]) - { - t.pc += IRL!(IR.Trie); - nlist.insertBack(t); - } - else - { - recycle(t); - } - t = worklist.fetch(); - if(!t) - return; - break; - default: - assert(0, "Unrecognized instruction " ~ re.ir[t.pc].mnemonic); - } - else - { - default: - recycle(t); - t = worklist.fetch(); - if(!t) - return; - } - } - } - + static if (withInput) + while (opCacheTrue.ptr[state.t.pc](&this, state)){} + else + while (opCacheFalse.ptr[state.t.pc](&this, state)){} } enum uint RestartPc = uint.max; //match the input, evaluating IR without searching - MatchResult matchOneShot(Group!DataIndex[] matches, uint startPc = 0) + int matchOneShot(Group!DataIndex[] matches, uint startPc = 0) { debug(std_regex_matcher) { @@ -826,59 +1067,60 @@ struct ThreadList(DataIndex) alias evalFn = eval; assert(clist == (ThreadList!DataIndex).init || startPc == RestartPc); // incorrect after a partial match assert(nlist == (ThreadList!DataIndex).init || startPc == RestartPc); - if(!atEnd)//if no char + State state; + state.matches = matches; + if (!atEnd)//if no char { debug(std_regex_matcher) { - writefln("-- Threaded matching threads at %s", s[index..s.lastIndex]); + writefln("-- Threaded matching threads at %s", s[index .. s.lastIndex]); } - if(startPc!=RestartPc) + if (startPc != RestartPc) { - auto startT = createStart(index, startPc); + state.t = createStart(index, startPc); genCounter++; - evalFn!true(startT, matches); + evalFn!true(&state); } - for(;;) + for (;;) { debug(std_regex_matcher) writeln("\n-- Started iteration of main cycle"); genCounter++; debug(std_regex_matcher) { - foreach(t; clist[]) + foreach (t; clist[]) { assert(t); } } - for(Thread!DataIndex* t = clist.fetch(); t; t = clist.fetch()) + for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) { - evalFn!true(t, matches); + evalFn!true(&state); } - if(nlist.empty) + if (nlist.empty) { debug(std_regex_matcher) writeln("Stopped matching before consuming full input"); break;//not a partial match for sure } clist = nlist; nlist = (ThreadList!DataIndex).init; - if(!next()) - { - if (!atEnd) return MatchResult.PartialMatch; + if (!next()) break; - } debug(std_regex_matcher) writeln("-- Ended iteration of main cycle\n"); } } genCounter++; //increment also on each end debug(std_regex_matcher) writefln("-- Matching threads at end"); //try out all zero-width posibilities - for(Thread!DataIndex* t = clist.fetch(); t; t = clist.fetch()) + for (state.t = clist.fetch(); state.t; state.t = clist.fetch()) { - evalFn!false(t, matches); + evalFn!false(&state); } - if(!matched) - evalFn!false(createStart(index, startPc), matches); - - return (matched?MatchResult.Match:MatchResult.NoMatch); + if (!matched) + { + state.t = createStart(index, startPc); + evalFn!false(&state); + } + return matched; } //get a dirty recycled Thread @@ -897,7 +1139,7 @@ struct ThreadList(DataIndex) memory = memory[threadSize * size .. $]; freelist = cast(Thread!DataIndex*)&mem[0]; size_t i; - for(i = threadSize; i < threadSize*size; i += threadSize) + for (i = threadSize; i < threadSize*size; i += threadSize) (cast(Thread!DataIndex*)&mem[i-threadSize]).next = cast(Thread!DataIndex*)&mem[i]; (cast(Thread!DataIndex*)&mem[i-threadSize]).next = null; } @@ -912,21 +1154,20 @@ struct ThreadList(DataIndex) //dispose list of threads void recycle(ref ThreadList!DataIndex list) { - auto t = list.tip; - while(t) + if (list.tip) { - auto next = t.next; - recycle(t); - t = next; + // just put this head-tail list in front of freelist + list.toe.next = freelist; + freelist = list.tip; + list = list.init; } - list = list.init; } //creates a copy of master thread with given pc Thread!DataIndex* fork(Thread!DataIndex* master, uint pc, uint counter) { auto t = allocate(); - t.matches.ptr[0..re.ngroup] = master.matches.ptr[0..re.ngroup]; + t.matches.ptr[0 .. re.ngroup] = master.matches.ptr[0 .. re.ngroup]; t.pc = pc; t.counter = counter; t.uopCounter = 0; @@ -937,7 +1178,7 @@ struct ThreadList(DataIndex) Thread!DataIndex* createStart(DataIndex index, uint pc = 0) { auto t = allocate(); - t.matches.ptr[0..re.ngroup] = (Group!DataIndex).init; + t.matches.ptr[0 .. re.ngroup] = (Group!DataIndex).init; t.matches[0].begin = index; t.pc = pc; t.counter = 0; diff --git a/std/regex/package.d b/std/regex/package.d index f421919cda2..f21698b9643 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -1,10 +1,46 @@ /++ - $(LUCKY Regular expressions) are a commonly used method of pattern matching + $(LINK2 https://en.wikipedia.org/wiki/Regular_expression, Regular expressions) + are a commonly used method of pattern matching on strings, with $(I regex) being a catchy word for a pattern in this domain specific language. Typical problems usually solved by regular expressions - include validation of user input and the ubiquitous find & replace + include validation of user input and the ubiquitous find $(AMP) replace in text processing utilities. +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Matching) $(TD + $(LREF bmatch) + $(LREF match) + $(LREF matchAll) + $(LREF matchFirst) +)) +$(TR $(TD Building) $(TD + $(LREF ctRegex) + $(LREF escaper) + $(LREF _regex) +)) +$(TR $(TD Replace) $(TD + $(LREF replace) + $(LREF replaceAll) + $(LREF replaceAllInto) + $(LREF replaceFirst) + $(LREF replaceFirstInto) +)) +$(TR $(TD Split) $(TD + $(LREF split) + $(LREF splitter) +)) +$(TR $(TD Objects) $(TD + $(LREF Captures) + $(LREF Regex) + $(LREF RegexException) + $(LREF RegexMatch) + $(LREF Splitter) + $(LREF StaticRegex) +)) +) + $(SECTION Synopsis) --- import std.regex; @@ -13,11 +49,11 @@ { // Print out all possible dd/mm/yy(yy) dates found in user input. auto r = regex(r"\b[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9](?:[0-9][0-9])?\b"); - foreach(line; stdin.byLine) + foreach (line; stdin.byLine) { // matchAll() returns a range that can be iterated // to get all subsequent matches. - foreach(c; matchAll(line, r)) + foreach (c; matchAll(line, r)) writeln(c.hit); } } @@ -31,14 +67,23 @@ assert(!c2.empty); // Be sure to check if there is a match before examining contents! assert(c2[1] == "bar"); // Captures is a range of submatches: 0 = full match. + ... + // multi-pattern regex + auto multi = regex([`\d+,\d+`,`(a-z]+):(\d+)`]); + auto m = "abc:43 12,34".matchAll(multi); + assert(m.front.whichPattern == 2); + assert(m.front[1] == "abc"); + assert(m.front[2] == "43"); + m.popFront(); + assert(m.front.whichPattern == 1); + assert(m.front[1] == "12"); ... - // The result of the $(D matchAll) is directly testable with if/assert/while. + // The result of the $(D matchAll/matchFirst) is directly testable with if/assert/while. // e.g. test if a string consists of letters: assert(matchFirst("Letter", `^\p{L}+$`)); - - --- + $(SECTION Syntax and general information) The general usage guideline is to keep regex complexity on the side of simplicity, as its capabilities reside in purely character-level manipulation. @@ -48,11 +93,11 @@ The basic syntax shouldn't surprise experienced users of regular expressions. For an introduction to $(D std.regex) see a - $(WEB dlang.org/regular-expression.html, short tour) of the module API + $(HTTP dlang.org/regular-expression.html, short tour) of the module API and its abilities. There are other web resources on regular expressions to help newcomers, - and a good $(WEB www.regular-expressions.info, reference with tutorial) + and a good $(HTTP www.regular-expressions.info, reference with tutorial) can easily be found. This library uses a remarkably common ECMAScript syntax flavor @@ -117,21 +162,22 @@ $(REG_ROW +?, Matches previous character/subexpression 1 or more times. Lazy version - stops as early as possible.) $(REG_ROW {n}, Matches previous character/subexpression exactly n times. ) - $(REG_ROW {n,}, Matches previous character/subexpression n times or more. + $(REG_ROW {n$(COMMA)}, Matches previous character/subexpression n times or more. Greedy version - tries as many times as possible. ) - $(REG_ROW {n,}?, Matches previous character/subexpression n times or more. + $(REG_ROW {n$(COMMA)}?, Matches previous character/subexpression n times or more. Lazy version - stops as early as possible.) - $(REG_ROW {n,m}, Matches previous character/subexpression n to m times. + $(REG_ROW {n$(COMMA)m}, Matches previous character/subexpression n to m times. Greedy version - tries as many times as possible, but no more than m times. ) - $(REG_ROW {n,m}?, Matches previous character/subexpression n to m times. + $(REG_ROW {n$(COMMA)m}?, Matches previous character/subexpression n to m times. Lazy version - stops as early as possible, but no less then n times.) - $(REG_TITLE Other, Subexpressions & alternations ) + $(REG_TITLE Other, Subexpressions $(AMP) alternations ) $(REG_ROW (regex), Matches subexpression regex, saving matched portion of text for later retrieval. ) + $(REG_ROW (?#comment), An inline comment that is ignored while matching.) $(REG_ROW (?:regex), Matches subexpression regex, $(U not) saving matched portion of text. Useful to speed up matching. ) $(REG_ROW A|B, Matches subexpression A, or failing that, matches B. ) - $(REG_ROW (?P<name>regex), Matches named subexpression + $(REG_ROW (?P$(LT)name$(GT)regex), Matches named subexpression regex labeling it with name 'name'. When referring to a matched portion of text, names work like aliases in addition to direct numbers. @@ -164,8 +210,9 @@ $(REG_TITLE Pattern element, Semantics ) $(REG_ROW Any atom, Has the same meaning as outside of a character class.) $(REG_ROW a-z, Includes characters a, b, c, ..., z. ) - $(REG_ROW [a||b], [a--b], [a~~b], [a&&b], Where a, b are arbitrary classes, - means union, set difference, symmetric set difference, and intersection respectively. + $(REG_ROW [a||b]$(COMMA) [a--b]$(COMMA) [a~~b]$(COMMA) [a$(AMP)$(AMP)b], + Where a, b are arbitrary classes, means union, set difference, + symmetric set difference, and intersection respectively. $(I Any sequence of character class elements implicitly forms a union.) ) ) @@ -184,7 +231,7 @@ $(SECTION Unicode support) This library provides full Level 1 support* according to - $(WEB unicode.org/reports/tr18/, UTS 18). Specifically: + $(HTTP unicode.org/reports/tr18/, UTS 18). Specifically: $(UL $(LI 1.1 Hex notation via any of \uxxxx, \U00YYYYYY, \xZZ.) $(LI 1.2 Unicode properties.) @@ -209,13 +256,13 @@ The format string can reference parts of match using the following notation. $(REG_TABLE $(REG_TITLE Format specifier, Replaced by ) - $(REG_ROW $&, the whole match. ) + $(REG_ROW $$(AMP), the whole match. ) $(REG_ROW $(DOLLAR)$(BACKTICK), part of input $(I preceding) the match. ) $(REG_ROW $', part of input $(I following) the match. ) $(REG_ROW $$, '$' character. ) - $(REG_ROW \c , where c is any character, the character c itself. ) + $(REG_ROW \c $(COMMA) where c is any character, the character c itself. ) $(REG_ROW \\, '\' character. ) - $(REG_ROW $1 .. $99, submatch number 1 to 99 respectively. ) + $(REG_ROW $(DOLLAR)1 .. $(DOLLAR)99, submatch number 1 to 99 respectively. ) ) $(SECTION Slicing and zero memory allocations orientation) @@ -230,14 +277,14 @@ Copyright: Copyright Dmitry Olshansky, 2011- - License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). + License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Dmitry Olshansky, API and utility constructs are modeled after the original $(D std.regex) by Walter Bright and Andrei Alexandrescu. - Source: $(PHOBOSSRC std/_regex.d) + Source: $(PHOBOSSRC std/_regex/_package.d) Macros: REG_ROW = $(TR $(TD $(I $1 )) $(TD $+) ) @@ -251,7 +298,8 @@ module std.regex; import std.regex.internal.ir; import std.regex.internal.thompson; //TODO: get rid of this dependency -import std.exception, std.traits, std.range; +import std.traits, std.range.primitives; +import std.typecons; // : Flag, Yes, No; /++ $(D Regex) object holds regular expression pattern in compiled form. @@ -260,7 +308,7 @@ import std.exception, std.traits, std.range; This is an intended form for caching and storage of frequently used regular expressions. - Examples: + Example: Test if this object doesn't contain any compiled pattern. --- @@ -302,26 +350,67 @@ public alias StaticRegex(Char) = std.regex.internal.ir.StaticRegex!(Char); the same character width as $(D pattern). Params: - pattern = Regular expression + pattern(s) = Regular expression(s) to match flags = The _attributes (g, i, m and x accepted) Throws: $(D RegexException) if there were any errors during compilation. +/ -@trusted public auto regex(S)(S pattern, const(char)[] flags="") - if(isSomeString!(S)) +@trusted public auto regex(S)(S[] patterns, const(char)[] flags="") +if (isSomeString!(S)) { - import std.functional; + import std.array : appender; + import std.functional : memoize; enum cacheSize = 8; //TODO: invent nice interface to control regex caching - if(__ctfe) - return regexImpl(pattern, flags); - return memoize!(regexImpl!S, cacheSize)(pattern, flags); + S pat; + if (patterns.length > 1) + { + auto app = appender!S(); + foreach (i, p; patterns) + { + if (i != 0) + app.put("|"); + app.put("(?:"); + app.put(patterns[i]); + app.put("\\"); + app.put(cast(dchar)(privateUseStart+i)); // special end marker + app.put(")"); + } + pat = app.data; + } + else + pat = patterns[0]; + + if (__ctfe) + return regexImpl(pat, flags); + return memoize!(regexImpl!S, cacheSize)(pat, flags); +} + +///ditto +@trusted public auto regex(S)(S pattern, const(char)[] flags="") +if (isSomeString!(S)) +{ + return regex([pattern], flags); +} + +/// +@system unittest +{ + // multi-pattern regex example + auto multi = regex([`([a-z]+):(\d+)`, `(\d+),\d+`]); // multi regex + auto m = "abc:43 12,34".matchAll(multi); + assert(m.front.whichPattern == 1); + assert(m.front[1] == "abc"); + assert(m.front[2] == "43"); + m.popFront(); + assert(m.front.whichPattern == 2); + assert(m.front[1] == "12"); } public auto regexImpl(S)(S pattern, const(char)[] flags="") - if(isSomeString!(S)) +if (isSomeString!(S)) { - import std.regex.internal.parser; - auto parser = Parser!(Unqual!(typeof(pattern)))(pattern, flags); + import std.regex.internal.parser : Parser, CodeGen; + auto parser = Parser!(Unqual!(typeof(pattern)), CodeGen)(pattern, flags); auto r = parser.program; return r; } @@ -365,86 +454,123 @@ enum isRegexFor(RegEx, R) = is(RegEx == Regex!(BasicElementOf!R)) First element of range is the whole match. +/ @trusted public struct Captures(R, DIndex = size_t) - if(isSomeString!R) +if (isSomeString!R) {//@trusted because of union inside alias DataIndex = DIndex; alias String = R; private: - import std.conv; + import std.conv : text; R _input; - bool _empty; + int _nMatch; enum smallString = 3; + enum SMALL_MASK = 0x8000_0000, REF_MASK= 0x1FFF_FFFF; union { Group!DataIndex[] big_matches; Group!DataIndex[smallString] small_matches; } uint _f, _b; - uint _ngroup; + uint _refcount; // ref count or SMALL MASK + num groups NamedGroup[] _names; - this()(R input, uint ngroups, NamedGroup[] named) + this()(R input, uint n, NamedGroup[] named) { _input = input; - _ngroup = ngroups; _names = named; - newMatches(); - _b = _ngroup; + newMatches(n); + _b = n; _f = 0; } this(alias Engine)(ref RegexMatch!(R,Engine) rmatch) { _input = rmatch._input; - _ngroup = rmatch._engine.re.ngroup; _names = rmatch._engine.re.dict; - newMatches(); - _b = _ngroup; + immutable n = rmatch._engine.re.ngroup; + newMatches(n); + _b = n; _f = 0; } - @property Group!DataIndex[] matches() + @property inout(Group!DataIndex[]) matches() inout { - return _ngroup > smallString ? big_matches : small_matches[0 .. _ngroup]; + return (_refcount & SMALL_MASK) ? small_matches[0 .. _refcount & 0xFF] : big_matches; } - void newMatches() + void newMatches(uint n) { - if(_ngroup > smallString) - big_matches = new Group!DataIndex[_ngroup]; + import core.stdc.stdlib : calloc; + import std.exception : enforce; + if (n > smallString) + { + auto p = cast(Group!DataIndex*) enforce( + calloc(Group!DataIndex.sizeof,n), + "Failed to allocate Captures struct" + ); + big_matches = p[0 .. n]; + _refcount = 1; + } + else + { + _refcount = SMALL_MASK | n; + } + } + + bool unique() + { + return (_refcount & SMALL_MASK) || _refcount == 1; } public: + this(this) + { + if (!(_refcount & SMALL_MASK)) + { + _refcount++; + } + } + ~this() + { + import core.stdc.stdlib : free; + if (!(_refcount & SMALL_MASK)) + { + if (--_refcount == 0) + { + free(big_matches.ptr); + big_matches = null; + } + } + } ///Slice of input prior to the match. @property R pre() { - return _empty ? _input[] : _input[0 .. matches[0].begin]; + return _nMatch == 0 ? _input[] : _input[0 .. matches[0].begin]; } ///Slice of input immediately after the match. @property R post() { - return _empty ? _input[] : _input[matches[0].end .. $]; + return _nMatch == 0 ? _input[] : _input[matches[0].end .. $]; } ///Slice of matched portion of input. @property R hit() { - assert(!_empty); + assert(_nMatch); return _input[matches[0].begin .. matches[0].end]; } ///Range interface. @property R front() { - assert(!empty); + assert(_nMatch); return _input[matches[_f].begin .. matches[_f].end]; } ///ditto @property R back() { - assert(!empty); + assert(_nMatch); return _input[matches[_b - 1].begin .. matches[_b - 1].end]; } @@ -463,10 +589,10 @@ public: } ///ditto - @property bool empty() const { return _empty || _f >= _b; } + @property bool empty() const { return _nMatch == 0 || _f >= _b; } ///ditto - R opIndex()(size_t i) /*const*/ //@@@BUG@@@ + inout(R) opIndex()(size_t i) inout { assert(_f + i < _b,text("requested submatch number ", i," is out of range")); assert(matches[_f + i].begin <= matches[_f + i].end, @@ -485,7 +611,21 @@ public: --- +/ - @safe bool opCast(T:bool)() const nothrow { return !empty; } + @safe bool opCast(T:bool)() const nothrow { return _nMatch != 0; } + + /++ + Number of pattern matched counting, where 1 - the first pattern. + Returns 0 on no match. + +/ + + @safe @property int whichPattern() const nothrow { return _nMatch; } + + /// + @system unittest + { + import std.regex; + assert(matchFirst("abc", "[0-9]+", "[a-z]+").whichPattern == 2); + } /++ Lookup named submatch. @@ -504,22 +644,24 @@ public: ---- +/ R opIndex(String)(String i) /*const*/ //@@@BUG@@@ - if(isSomeString!String) + if (isSomeString!String) { size_t index = lookupNamedGroup(_names, i); return _input[matches[index].begin .. matches[index].end]; } ///Number of matches in this object. - @property size_t length() const { return _empty ? 0 : _b - _f; } + @property size_t length() const { return _nMatch == 0 ? 0 : _b - _f; } ///A hook for compatibility with original std.regex. @property ref captures(){ return this; } } /// -unittest +@system unittest { + import std.range.primitives : popFrontN; + auto c = matchFirst("@abc#", regex(`(\w)(\w)(\w)`)); assert(c.pre == "@"); // Part of input preceding match assert(c.post == "#"); // Immediately after match @@ -547,10 +689,10 @@ unittest and is automatically deduced in a call to $(D match)/$(D bmatch). +/ @trusted public struct RegexMatch(R, alias Engine = ThompsonMatcher) - if(isSomeString!R) +if (isSomeString!R) { private: - import core.stdc.stdlib; + import core.stdc.stdlib : malloc, free; alias Char = BasicElementOf!R; alias EngineType = Engine!Char; EngineType _engine; @@ -560,16 +702,17 @@ private: this(RegEx)(R input, RegEx prog) { + import std.exception : enforce; _input = input; immutable size = EngineType.initialMemory(prog)+size_t.sizeof; - _memory = (enforce(malloc(size))[0..size]); + _memory = (enforce(malloc(size), "malloc failed")[0 .. size]); scope(failure) free(_memory.ptr); *cast(size_t*)_memory.ptr = 1; _engine = EngineType(prog, Input!Char(input), _memory[size_t.sizeof..$]); - static if(is(RegEx == StaticRegex!(BasicElementOf!R))) + static if (is(RegEx == StaticRegex!(BasicElementOf!R))) _engine.nativeFn = prog.nativeFn; _captures = Captures!(R,EngineType.DataIndex)(this); - _captures._empty = !_engine.match(_captures.matches); + _captures._nMatch = _engine.match(_captures.matches); debug(std_regex_allocation) writefln("RefCount (ctor): %x %d", _memory.ptr, counter); } @@ -577,7 +720,7 @@ private: public: this(this) { - if(_memory.ptr) + if (_memory.ptr) { ++counter; debug(std_regex_allocation) writefln("RefCount (postblit): %x %d", @@ -587,7 +730,7 @@ public: ~this() { - if(_memory.ptr && --*cast(size_t*)_memory.ptr == 0) + if (_memory.ptr && --*cast(size_t*)_memory.ptr == 0) { debug(std_regex_allocation) writefln("RefCount (dtor): %x %d", _memory.ptr, *cast(size_t*)_memory.ptr); @@ -633,48 +776,53 @@ public: ///ditto void popFront() { - - if(counter != 1) + import std.exception : enforce; + if (counter != 1) {//do cow magic first counter--;//we abandon this reference immutable size = EngineType.initialMemory(_engine.re)+size_t.sizeof; - _memory = (enforce(malloc(size))[0..size]); - _engine = _engine.dupTo(_memory[size_t.sizeof..size]); + _memory = (enforce(malloc(size), "malloc failed")[0 .. size]); + _engine = _engine.dupTo(_memory[size_t.sizeof .. size]); counter = 1;//points to new chunk } - //previous _captures can have escaped references from Capture object - _captures.newMatches(); - _captures._empty = !_engine.match(_captures.matches); + + if (!_captures.unique) + { + // has external references - allocate new space + _captures.newMatches(_engine.re.ngroup); + } + _captures._nMatch = _engine.match(_captures.matches); } ///ditto auto save(){ return this; } ///Test if this match object is empty. - @property bool empty(){ return _captures._empty; } + @property bool empty() const { return _captures._nMatch == 0; } ///Same as !(x.empty), provided for its convenience in conditional statements. T opCast(T:bool)(){ return !empty; } /// Same as .front, provided for compatibility with original std.regex. - @property auto captures(){ return _captures; } + @property auto captures() inout { return _captures; } } private @trusted auto matchOnce(alias Engine, RegEx, R)(R input, RegEx re) { - import core.stdc.stdlib; + import core.stdc.stdlib : malloc, free; + import std.exception : enforce; alias Char = BasicElementOf!R; alias EngineType = Engine!Char; size_t size = EngineType.initialMemory(re); - void[] memory = enforce(malloc(size))[0..size]; + void[] memory = enforce(malloc(size), "malloc failed")[0 .. size]; scope(exit) free(memory.ptr); auto captures = Captures!(R, EngineType.DataIndex)(input, re.ngroup, re.dict); auto engine = EngineType(re, Input!Char(input), memory); - static if(is(RegEx == StaticRegex!(BasicElementOf!R))) + static if (is(RegEx == StaticRegex!(BasicElementOf!R))) engine.nativeFn = re.nativeFn; - captures._empty = !engine.match(captures.matches); + captures._nMatch = engine.match(captures.matches); return captures; } @@ -684,7 +832,7 @@ private auto matchMany(alias Engine, RegEx, R)(R input, RegEx re) return RegexMatch!(R, Engine)(input, re); } -unittest +@system unittest { //sanity checks for new API auto re = regex("abc"); @@ -699,12 +847,17 @@ private enum isReplaceFunctor(alias fun, R) = // the lowest level - just stuff replacements into the sink private @trusted void replaceCapturesInto(alias output, Sink, R, T) (ref Sink sink, R input, T captures) - if(isOutputRange!(Sink, dchar) && isSomeString!R) +if (isOutputRange!(Sink, dchar) && isSomeString!R) { + if (captures.empty) + { + sink.put(input); + return; + } sink.put(captures.pre); // a hack to get around bogus errors, should be simply output(captures, sink) // "is a nested function and cannot be accessed from" - static if(isReplaceFunctor!(output, R)) + static if (isReplaceFunctor!(output, R)) sink.put(output(captures)); //"mutator" type of function else output(captures, sink); //"output" type of function @@ -714,14 +867,14 @@ private @trusted void replaceCapturesInto(alias output, Sink, R, T) // ditto for a range of captures private void replaceMatchesInto(alias output, Sink, R, T) (ref Sink sink, R input, T matches) - if(isOutputRange!(Sink, dchar) && isSomeString!R) +if (isOutputRange!(Sink, dchar) && isSomeString!R) { size_t offset = 0; - foreach(cap; matches) + foreach (cap; matches) { sink.put(cap.pre[offset .. $]); // same hack, see replaceCapturesInto - static if(isReplaceFunctor!(output, R)) + static if (isReplaceFunctor!(output, R)) sink.put(output(cap)); //"mutator" type of function else output(cap, sink); //"output" type of function @@ -732,10 +885,11 @@ private void replaceMatchesInto(alias output, Sink, R, T) // a general skeleton of replaceFirst private R replaceFirstWith(alias output, R, RegEx)(R input, RegEx re) - if(isSomeString!R && isRegexFor!(RegEx, R)) +if (isSomeString!R && isRegexFor!(RegEx, R)) { + import std.array : appender; auto data = matchFirst(input, re); - if(data.empty) + if (data.empty) return input; auto app = appender!(R)(); replaceCapturesInto!output(app, input, data); @@ -746,10 +900,11 @@ private R replaceFirstWith(alias output, R, RegEx)(R input, RegEx re) // the method parameter allows old API to ride on the back of the new one private R replaceAllWith(alias output, alias method=matchAll, R, RegEx)(R input, RegEx re) - if(isSomeString!R && isRegexFor!(RegEx, R)) +if (isSomeString!R && isRegexFor!(RegEx, R)) { + import std.array : appender; auto matches = method(input, re); //inout(C)[] fails - if(matches.empty) + if (matches.empty) return input; auto app = appender!(R)(); replaceMatchesInto!output(app, input, matches); @@ -774,24 +929,24 @@ private R replaceAllWith(alias output, +/ public auto match(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) { - import std.regex.internal.thompson; + import std.regex.internal.thompson : ThompsonMatcher; return RegexMatch!(Unqual!(typeof(input)),ThompsonMatcher)(input, re); } ///ditto public auto match(R, String)(R input, String re) - if(isSomeString!R && isSomeString!String) +if (isSomeString!R && isSomeString!String) { - import std.regex.internal.thompson; + import std.regex.internal.thompson : ThompsonMatcher; return RegexMatch!(Unqual!(typeof(input)),ThompsonMatcher)(input, regex(re)); } public auto match(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) { - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; return RegexMatch!(Unqual!(typeof(input)),BacktrackingMatcher!true)(input, re); } @@ -802,7 +957,7 @@ public auto match(R, RegEx)(R input, RegEx re) $(D re) parameter can be one of three types: $(UL - $(LI Plain string, in which case it's compiled to bytecode before matching. ) + $(LI Plain string(s), in which case it's compiled to bytecode before matching. ) $(LI Regex!char (wchar/dchar) that contains a pattern in the form of compiled bytecode. ) $(LI StaticRegex!char (wchar/dchar) that contains a pattern in the form of @@ -814,24 +969,32 @@ public auto match(R, RegEx)(R input, RegEx re) if there was a match, otherwise an empty $(LREF Captures) object. +/ public auto matchFirst(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) { - import std.regex.internal.thompson; + import std.regex.internal.thompson : ThompsonMatcher; return matchOnce!ThompsonMatcher(input, re); } ///ditto public auto matchFirst(R, String)(R input, String re) - if(isSomeString!R && isSomeString!String) +if (isSomeString!R && isSomeString!String) +{ + import std.regex.internal.thompson : ThompsonMatcher; + return matchOnce!ThompsonMatcher(input, regex(re)); +} + +///ditto +public auto matchFirst(R, String)(R input, String[] re...) +if (isSomeString!R && isSomeString!String) { - import std.regex.internal.thompson; + import std.regex.internal.thompson : ThompsonMatcher; return matchOnce!ThompsonMatcher(input, regex(re)); } public auto matchFirst(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) { - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; return matchOnce!(BacktrackingMatcher!true)(input, re); } @@ -845,7 +1008,7 @@ public auto matchFirst(R, RegEx)(R input, RegEx re) $(D re) parameter can be one of three types: $(UL - $(LI Plain string, in which case it's compiled to bytecode before matching. ) + $(LI Plain string(s), in which case it's compiled to bytecode before matching. ) $(LI Regex!char (wchar/dchar) that contains a pattern in the form of compiled bytecode. ) $(LI StaticRegex!char (wchar/dchar) that contains a pattern in the form of @@ -857,24 +1020,32 @@ public auto matchFirst(R, RegEx)(R input, RegEx re) after the first match was found or an empty one if not present. +/ public auto matchAll(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) { - import std.regex.internal.thompson; + import std.regex.internal.thompson : ThompsonMatcher; return matchMany!ThompsonMatcher(input, re); } ///ditto public auto matchAll(R, String)(R input, String re) - if(isSomeString!R && isSomeString!String) +if (isSomeString!R && isSomeString!String) { - import std.regex.internal.thompson; + import std.regex.internal.thompson : ThompsonMatcher; + return matchMany!ThompsonMatcher(input, regex(re)); +} + +///ditto +public auto matchAll(R, String)(R input, String[] re...) +if (isSomeString!R && isSomeString!String) +{ + import std.regex.internal.thompson : ThompsonMatcher; return matchMany!ThompsonMatcher(input, regex(re)); } public auto matchAll(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) { - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; return matchMany!(BacktrackingMatcher!true)(input, re); } @@ -882,9 +1053,10 @@ public auto matchAll(R, RegEx)(R input, RegEx re) @system unittest { import std.conv : to; - import std.algorithm : map, equal; + import std.algorithm.iteration : map; + import std.algorithm.comparison : equal; - foreach(String; TypeTuple!(string, wstring, const(dchar)[])) + foreach (String; AliasSeq!(string, wstring, const(dchar)[])) { auto str1 = "blah-bleh".to!String(); auto pat1 = "bl[ae]h".to!String(); @@ -895,7 +1067,7 @@ public auto matchAll(R, RegEx)(R input, RegEx re) ([["blah".to!String()], ["bleh".to!String()]])); auto str2 = "1/03/12 - 3/03/12".to!String(); - auto pat2 = regex(r"(\d+)/(\d+)/(\d+)".to!String()); + auto pat2 = regex([r"(\d+)/(\d+)/(\d+)".to!String(), "abc".to!String]); auto mf2 = matchFirst(str2, pat2); assert(mf2.equal(["1/03/12", "1", "03", "12"].map!(to!String)())); auto mAll2 = matchAll(str2, pat2); @@ -921,7 +1093,8 @@ public auto matchAll(R, RegEx)(R input, RegEx re) /++ Start matching of $(D input) to regex pattern $(D re), - using traditional $(LUCKY backtracking) matching scheme. + using traditional $(LINK2 https://en.wikipedia.org/wiki/Backtracking, + backtracking) matching scheme. The use of this function is $(RED discouraged) - use either of $(LREF matchAll) or $(LREF matchFirst). @@ -937,45 +1110,48 @@ public auto matchAll(R, RegEx)(R input, RegEx re) +/ public auto bmatch(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R))) { - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; return RegexMatch!(Unqual!(typeof(input)), BacktrackingMatcher!false)(input, re); } ///ditto public auto bmatch(R, String)(R input, String re) - if(isSomeString!R && isSomeString!String) +if (isSomeString!R && isSomeString!String) { - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; return RegexMatch!(Unqual!(typeof(input)), BacktrackingMatcher!false)(input, regex(re)); } public auto bmatch(R, RegEx)(R input, RegEx re) - if(isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) +if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) { - import std.regex.internal.backtracking; + import std.regex.internal.backtracking : BacktrackingMatcher; return RegexMatch!(Unqual!(typeof(input)),BacktrackingMatcher!true)(input, re); } // produces replacement string from format using captures for substitution package void replaceFmt(R, Capt, OutR) (R format, Capt captures, OutR sink, bool ignoreBadSubs = false) - if(isOutputRange!(OutR, ElementEncodingType!R[]) && - isOutputRange!(OutR, ElementEncodingType!(Capt.String)[])) +if (isOutputRange!(OutR, ElementEncodingType!R[]) && + isOutputRange!(OutR, ElementEncodingType!(Capt.String)[])) { - import std.algorithm, std.conv; + import std.algorithm.searching : find; + import std.conv : text, parse; + import std.ascii : isDigit, isAlpha; + import std.exception : enforce; enum State { Normal, Dollar } auto state = State.Normal; size_t offset; L_Replace_Loop: - while(!format.empty) - final switch(state) + while (!format.empty) + final switch (state) { case State.Normal: - for(offset = 0; offset < format.length; offset++)//no decoding + for (offset = 0; offset < format.length; offset++)//no decoding { - if(format[offset] == '$') + if (format[offset] == '$') { state = State.Dollar; sink.put(format[0 .. offset]); @@ -987,38 +1163,38 @@ L_Replace_Loop: format = format[offset .. $]; break; case State.Dollar: - if(std.ascii.isDigit(format[0])) + if (isDigit(format[0])) { uint digit = parse!uint(format); enforce(ignoreBadSubs || digit < captures.length, text("invalid submatch number ", digit)); - if(digit < captures.length) + if (digit < captures.length) sink.put(captures[digit]); } - else if(format[0] == '{') + else if (format[0] == '{') { - auto x = find!(a => !std.ascii.isAlpha(a))(format[1..$]); + auto x = find!(a => !isAlpha(a))(format[1..$]); enforce(!x.empty && x[0] == '}', "no matching '}' in replacement format"); auto name = format[1 .. $ - x.length]; format = x[1..$]; enforce(!name.empty, "invalid name in ${...} replacement format"); sink.put(captures[name]); } - else if(format[0] == '&') + else if (format[0] == '&') { sink.put(captures[0]); format = format[1 .. $]; } - else if(format[0] == '`') + else if (format[0] == '`') { sink.put(captures.pre); format = format[1 .. $]; } - else if(format[0] == '\'') + else if (format[0] == '\'') { sink.put(captures.post); format = format[1 .. $]; } - else if(format[0] == '$') + else if (format[0] == '$') { sink.put(format[0 .. 1]); format = format[1 .. $]; @@ -1044,18 +1220,19 @@ L_Replace_Loop: Returns: A string of the same type with the first match (if any) replaced. If no match is found returns the input string itself. - - Example: - --- - assert(replaceFirst("noon", regex("n"), "[$&]") == "[n]oon"); - --- +/ public R replaceFirst(R, C, RegEx)(R input, RegEx re, const(C)[] format) - if(isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R)) +if (isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R)) { return replaceFirstWith!((m, sink) => replaceFmt(format, m, sink))(input, re); } +/// +@system unittest +{ + assert(replaceFirst("noon", regex("n"), "[$&]") == "[n]oon"); +} + /++ This is a general replacement tool that construct a new string by replacing matches of pattern $(D re) in the $(D input). Unlike the other overload @@ -1070,19 +1247,21 @@ public R replaceFirst(R, C, RegEx)(R input, RegEx re, const(C)[] format) A new string of the same type as $(D input) with all matches replaced by return values of $(D fun). If no matches found returns the $(D input) itself. ++/ +public R replaceFirst(alias fun, R, RegEx)(R input, RegEx re) +if (isSomeString!R && isRegexFor!(RegEx, R)) +{ + return replaceFirstWith!((m, sink) => sink.put(fun(m)))(input, re); +} - Example: - --- +/// +@system unittest +{ + import std.conv : to; string list = "#21 out of 46"; string newList = replaceFirst!(cap => to!string(to!int(cap.hit)+1)) (list, regex(`[0-9]+`)); assert(newList == "#22 out of 46"); - --- -+/ -public R replaceFirst(alias fun, R, RegEx)(R input, RegEx re) - if(isSomeString!R && isRegexFor!(RegEx, R)) -{ - return replaceFirstWith!((m, sink) => sink.put(fun(m)))(input, re); } /++ @@ -1093,23 +1272,11 @@ public R replaceFirst(alias fun, R, RegEx)(R input, RegEx re) Like in $(LREF replaceFirst) family of functions there is an overload for the substitution guided by the $(D format) string and the one with the user defined callback. - - Example: - --- - import std.array; - string m1 = "first message\n"; - string m2 = "second message\n"; - auto result = appender!string(); - replaceFirstInto(result, m1, regex(`([a-z]+) message`), "$1"); - //equivalent of the above with user-defined callback - replaceFirstInto!(cap=>cap[1])(result, m2, regex(`([a-z]+) message`)); - assert(result.data == "first\nsecond\n"); - --- +/ public @trusted void replaceFirstInto(Sink, R, C, RegEx) (ref Sink sink, R input, RegEx re, const(C)[] format) - if(isOutputRange!(Sink, dchar) && isSomeString!R - && is(C : dchar) && isRegexFor!(RegEx, R)) +if (isOutputRange!(Sink, dchar) && isSomeString!R + && is(C : dchar) && isRegexFor!(RegEx, R)) { replaceCapturesInto!((m, sink) => replaceFmt(format, m, sink)) (sink, input, matchFirst(input, re)); @@ -1118,11 +1285,24 @@ public @trusted void replaceFirstInto(Sink, R, C, RegEx) ///ditto public @trusted void replaceFirstInto(alias fun, Sink, R, RegEx) (Sink sink, R input, RegEx re) - if(isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) +if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) { replaceCapturesInto!fun(sink, input, matchFirst(input, re)); } +/// +@system unittest +{ + import std.array; + string m1 = "first message\n"; + string m2 = "second message\n"; + auto result = appender!string(); + replaceFirstInto(result, m1, regex(`([a-z]+) message`), "$1"); + //equivalent of the above with user-defined callback + replaceFirstInto!(cap=>cap[1])(result, m2, regex(`([a-z]+) message`)); + assert(result.data == "first\nsecond\n"); +} + //examples for replaceFirst @system unittest { @@ -1158,20 +1338,21 @@ public @trusted void replaceFirstInto(alias fun, Sink, R, RegEx) A string of the same type as $(D input) with the all of the matches (if any) replaced. If no match is found returns the input string itself. - - Example: - --- - // Comify a number - auto com = regex(r"(?<=\d)(?=(\d\d\d)+\b)","g"); - assert(replaceAll("12000 + 42100 = 54100", com, ",") == "12,000 + 42,100 = 54,100"); - --- +/ public @trusted R replaceAll(R, C, RegEx)(R input, RegEx re, const(C)[] format) - if(isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R)) +if (isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R)) { return replaceAllWith!((m, sink) => replaceFmt(format, m, sink))(input, re); } +/// +@system unittest +{ + // insert comma as thousands delimiter + auto re = regex(r"(?<=\d)(?=(\d\d\d)+\b)","g"); + assert(replaceAll("12000 + 42100 = 54100", re, ",") == "12,000 + 42,100 = 54,100"); +} + /++ This is a general replacement tool that construct a new string by replacing matches of pattern $(D re) in the $(D input). Unlike the other overload @@ -1191,23 +1372,25 @@ public @trusted R replaceAll(R, C, RegEx)(R input, RegEx re, const(C)[] format) input = string to search re = compiled regular expression fun = delegate to use ++/ +public @trusted R replaceAll(alias fun, R, RegEx)(R input, RegEx re) +if (isSomeString!R && isRegexFor!(RegEx, R)) +{ + return replaceAllWith!((m, sink) => sink.put(fun(m)))(input, re); +} - Example: - Capitalize the letters 'a' and 'r': - --- +/// +@system unittest +{ string baz(Captures!(string) m) { - return std.string.toUpper(m.hit); + import std.string : toUpper; + return toUpper(m.hit); } + // Capitalize the letters 'a' and 'r': auto s = replaceAll!(baz)("Strap a rocket engine on a chicken.", regex("[ar]")); assert(s == "StRAp A Rocket engine on A chicken."); - --- -+/ -public @trusted R replaceAll(alias fun, R, RegEx)(R input, RegEx re) - if(isSomeString!R && isRegexFor!(RegEx, R)) -{ - return replaceAllWith!((m, sink) => sink.put(fun(m)))(input, re); } /++ @@ -1217,24 +1400,11 @@ public @trusted R replaceAll(alias fun, R, RegEx)(R input, RegEx re) As with $(LREF replaceAll) there are 2 overloads - one with a format string, the other one with a user defined functor. - - Example: - --- - //swap all 3 letter words and bring it back - string text = "How are you doing?"; - auto sink = appender!(char[])(); - replaceAllInto!(cap => retro(cap[0]))(sink, text, regex(`\b\w{3}\b`)); - auto swapped = sink.data.dup; // make a copy explicitly - assert(swapped == "woH era uoy doing?"); - sink.clear(); - replaceAllInto!(cap => retro(cap[0]))(sink, swapped, regex(`\b\w{3}\b`)); - assert(sink.data == text); - --- +/ public @trusted void replaceAllInto(Sink, R, C, RegEx) (Sink sink, R input, RegEx re, const(C)[] format) - if(isOutputRange!(Sink, dchar) && isSomeString!R - && is(C : dchar) && isRegexFor!(RegEx, R)) +if (isOutputRange!(Sink, dchar) && isSomeString!R + && is(C : dchar) && isRegexFor!(RegEx, R)) { replaceMatchesInto!((m, sink) => replaceFmt(format, m, sink)) (sink, input, matchAll(input, re)); @@ -1243,31 +1413,35 @@ public @trusted void replaceAllInto(Sink, R, C, RegEx) ///ditto public @trusted void replaceAllInto(alias fun, Sink, R, RegEx) (Sink sink, R input, RegEx re) - if(isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) +if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) { replaceMatchesInto!fun(sink, input, matchAll(input, re)); } -// a bit of examples +/// @system unittest { - //swap all 3 letter words and bring it back - string text = "How are you doing?"; - auto sink = appender!(char[])(); - replaceAllInto!(cap => retro(cap[0]))(sink, text, regex(`\b\w{3}\b`)); - auto swapped = sink.data.dup; // make a copy explicitly - assert(swapped == "woH era uoy doing?"); - sink.clear(); - replaceAllInto!(cap => retro(cap[0]))(sink, swapped, regex(`\b\w{3}\b`)); - assert(sink.data == text); + // insert comma as thousands delimiter in fifty randomly produced big numbers + import std.array, std.random, std.conv, std.range; + static re = regex(`(?<=\d)(?=(\d\d\d)+\b)`, "g"); + auto sink = appender!(char [])(); + enum ulong min = 10UL ^^ 10, max = 10UL ^^ 19; + foreach (i; 0 .. 50) + { + sink.clear(); + replaceAllInto(sink, text(uniform(min, max)), re, ","); + foreach (pos; iota(sink.data.length - 4, 0, -4)) + assert(sink.data[pos] == ','); + } } // exercise all of the replace APIs @system unittest { + import std.array : appender; import std.conv; // try and check first/all simple substitution - foreach(S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) { S s1 = "curt trial".to!S(); S s2 = "round dome".to!S(); @@ -1311,31 +1485,30 @@ public @trusted void replaceAllInto(alias fun, Sink, R, RegEx) or $(LREF replaceFirst) explicitly. +/ public R replace(alias scheme = match, R, C, RegEx)(R input, RegEx re, const(C)[] format) - if(isSomeString!R && isRegexFor!(RegEx, R)) +if (isSomeString!R && isRegexFor!(RegEx, R)) { return replaceAllWith!((m, sink) => replaceFmt(format, m, sink), match)(input, re); } ///ditto public R replace(alias fun, R, RegEx)(R input, RegEx re) - if(isSomeString!R && isRegexFor!(RegEx, R)) +if (isSomeString!R && isRegexFor!(RegEx, R)) { return replaceAllWith!(fun, match)(input, re); } -/++ -Range that splits a string using a regular expression as a -separator. - -Example: ----- -auto s1 = ", abc, de, fg, hi, "; -assert(equal(splitter(s1, regex(", *")), - ["", "abc", "de", "fg", "hi", ""])); ----- -+/ -public struct Splitter(Range, alias RegEx = Regex) - if(isSomeString!Range && isRegexFor!(RegEx, Range)) +/** +Splits a string `r` using a regular expression `pat` as a separator. + +Params: + keepSeparators = flag to specify if the matches should be in the resulting range + r = the string to split + pat = the pattern to split on +Returns: + A lazy range of strings +*/ +public struct Splitter(Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, alias RegEx = Regex) +if (isSomeString!Range && isRegexFor!(RegEx, Range)) { private: Range _input; @@ -1343,6 +1516,8 @@ private: alias Rx = typeof(match(Range.init,RegEx.init)); Rx _match; + static if (keepSeparators) bool onMatch = false; + @trusted this(Range input, RegEx separator) {//@@@BUG@@@ generated opAssign of RegexMatch is not @trusted _input = input; @@ -1355,6 +1530,10 @@ private: else { _match = Rx(_input, separator); + + static if (keepSeparators) + if (_match.pre.empty) + popFront(); } } @@ -1367,17 +1546,31 @@ public: ///Forward range primitives. @property Range front() { - import std.algorithm : min; + import std.algorithm.comparison : min; assert(!empty && _offset <= _match.pre.length && _match.pre.length <= _input.length); - return _input[_offset .. min($, _match.pre.length)]; + + static if (keepSeparators) + { + if (!onMatch) + return _input[_offset .. min($, _match.pre.length)]; + else + return _match.hit(); + } + else + { + return _input[_offset .. min($, _match.pre.length)]; + } } ///ditto @property bool empty() { - return _offset > _input.length; + static if (keepSeparators) + return _offset >= _input.length; + else + return _offset > _input.length; } ///ditto @@ -1391,9 +1584,27 @@ public: } else { - //skip past the separator - _offset = _match.pre.length + _match.hit.length; - _match.popFront(); + static if (keepSeparators) + { + if (!onMatch) + { + //skip past the separator + _offset = _match.pre.length; + } + else + { + _offset += _match.hit.length; + _match.popFront(); + } + + onMatch = !onMatch; + } + else + { + //skip past the separator + _offset = _match.pre.length + _match.hit.length; + _match.popFront(); + } } } @@ -1404,25 +1615,112 @@ public: } } -/** - A helper function, creates a $(D Splitter) on range $(D r) separated by regex $(D pat). - Captured subexpressions have no effect on the resulting range. -*/ -public Splitter!(Range, RegEx) splitter(Range, RegEx)(Range r, RegEx pat) - if(is(BasicElementOf!Range : dchar) && isRegexFor!(RegEx, Range)) +/// ditto +public Splitter!(keepSeparators, Range, RegEx) splitter( + Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, RegEx)(Range r, RegEx pat) +if ( + is(BasicElementOf!Range : dchar) && isRegexFor!(RegEx, Range)) { - return Splitter!(Range, RegEx)(r, pat); + return Splitter!(keepSeparators, Range, RegEx)(r, pat); +} + +/// +@system unittest +{ + import std.algorithm.comparison : equal; + auto s1 = ", abc, de, fg, hi, "; + assert(equal(splitter(s1, regex(", *")), + ["", "abc", "de", "fg", "hi", ""])); +} + +/// Split on a pattern, but keep the matches in the resulting range +@system unittest +{ + import std.algorithm.comparison : equal; + import std.typecons : Yes; + + auto pattern = regex(`([\.,])`); + + assert("2003.04.05" + .splitter!(Yes.keepSeparators)(pattern) + .equal(["2003", ".", "04", ".", "05"])); + + assert(",1,2,3" + .splitter!(Yes.keepSeparators)(pattern) + .equal([",", "1", ",", "2", ",", "3"])); } ///An eager version of $(D splitter) that creates an array with splitted slices of $(D input). public @trusted String[] split(String, RegEx)(String input, RegEx rx) - if(isSomeString!String && isRegexFor!(RegEx, String)) +if (isSomeString!String && isRegexFor!(RegEx, String)) { + import std.array : appender; auto a = appender!(String[])(); - foreach(e; splitter(input, rx)) + foreach (e; splitter(input, rx)) a.put(e); return a.data; } ///Exception object thrown in case of errors during regex compilation. public alias RegexException = std.regex.internal.ir.RegexException; + +/++ + A range that lazily produces a string output escaped + to be used inside of a regular expression. ++/ +auto escaper(Range)(Range r) +{ + import std.algorithm.searching : find; + static immutable escapables = [Escapables]; + static struct Escaper // template to deduce attributes + { + Range r; + bool escaped; + + @property ElementType!Range front(){ + if (escaped) + return '\\'; + else + return r.front; + } + + @property bool empty(){ return r.empty; } + + void popFront(){ + if (escaped) escaped = false; + else + { + r.popFront(); + if (!r.empty && !escapables.find(r.front).empty) + escaped = true; + } + } + + @property auto save(){ return Escaper(r.save, escaped); } + } + + bool escaped = !r.empty && !escapables.find(r.front).empty; + return Escaper(r, escaped); +} + +/// +@system unittest +{ + import std.regex; + import std.algorithm.comparison; + string s = `This is {unfriendly} to *regex*`; + assert(s.escaper.equal(`This is \{unfriendly\} to \*regex\*`)); +} + +@system unittest +{ + import std.conv; + import std.algorithm.comparison; + foreach (S; AliasSeq!(string, wstring, dstring)) + { + auto s = "^".to!S; + assert(s.escaper.equal(`\^`)); + auto s2 = ""; + assert(s2.escaper.equal("")); + } +} diff --git a/std/signals.d b/std/signals.d index c683f733e9e..646908ac72b 100644 --- a/std/signals.d +++ b/std/signals.d @@ -46,13 +46,14 @@ * Not safe for multiple threads operating on the same signals * or slots. * Macros: - * WIKI = Phobos/StdSignals * SIGNALS=signals * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Source: $(PHOBOSSRC std/_signals.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) */ /* Copyright Digital Mars 2000 - 2009. * Distributed under the Boost Software License, Version 1.0. @@ -80,63 +81,6 @@ extern (C) void rt_detachDisposeEvent( Object obj, DisposeEvt evt ); * Mixin to create a signal within a class object. * * Different signals can be added to a class by naming the mixins. - * - * Example: ---- -import std.signals; -import std.stdio; - -class Observer -{ // our slot - void watch(string msg, int i) - { - writefln("Observed msg '%s' and value %s", msg, i); - } -} - -class Foo -{ - int value() { return _value; } - - int value(int v) - { - if (v != _value) - { _value = v; - // call all the connected slots with the two parameters - emit("setting new value", v); - } - return v; - } - - // Mix in all the code we need to make Foo into a signal - mixin Signal!(string, int); - - private : - int _value; -} - -void main() -{ - Foo a = new Foo; - Observer o = new Observer; - - a.value = 3; // should not call o.watch() - a.connect(&o.watch); // o.watch is the slot - a.value = 4; // should call o.watch() - a.disconnect(&o.watch); // o.watch is no longer a slot - a.value = 5; // so should not call o.watch() - a.connect(&o.watch); // connect again - a.value = 6; // should call o.watch() - destroy(o); // destroying o should automatically disconnect it - a.value = 7; // should not call o.watch() -} ---- - * which should print: - * <pre> - * Observed msg 'setting new value' and value 4 - * Observed msg 'setting new value' and value 6 - * </pre> - * */ mixin template Signal(T1...) @@ -155,13 +99,36 @@ mixin template Signal(T1...) /*** * Call each of the connected slots, passing the argument(s) i to them. + * Nested call will be ignored. */ final void emit( T1 i ) { + if (status >= ST.inemitting || !slots.length) + return; // should not nest + + status = ST.inemitting; + scope (exit) + status = ST.idle; + foreach (slot; slots[0 .. slots_idx]) { if (slot) slot(i); } + + assert(status >= ST.inemitting); + if (status == ST.inemitting_disconnected) + { + for (size_t j = 0; j < slots_idx;) + { + if (slots[j] is null) + { + slots_idx--; + slots[j] = slots[slots_idx]; + } + else + j++; + } + } } /*** @@ -182,15 +149,20 @@ mixin template Signal(T1...) auto p = core.stdc.stdlib.calloc(slot_t.sizeof, len); if (!p) core.exception.onOutOfMemoryError(); - slots = (cast(slot_t*)p)[0 .. len]; + slots = (cast(slot_t*) p)[0 .. len]; } else { - len = len * 2 + 4; - auto p = core.stdc.stdlib.realloc(slots.ptr, slot_t.sizeof * len); + import core.checkedint : addu, mulu; + bool overflow; + len = addu(mulu(len, 2, overflow), 4, overflow); // len = len * 2 + 4 + const nbytes = mulu(len, slot_t.sizeof, overflow); + if (overflow) assert(0); + + auto p = core.stdc.stdlib.realloc(slots.ptr, nbytes); if (!p) core.exception.onOutOfMemoryError(); - slots = (cast(slot_t*)p)[0 .. len]; + slots = (cast(slot_t*) p)[0 .. len]; slots[slots_idx + 1 .. $] = null; } } @@ -207,20 +179,47 @@ mixin template Signal(T1...) final void disconnect(slot_t slot) { debug (signal) writefln("Signal.disconnect(slot)"); - for (size_t i = 0; i < slots_idx; ) + size_t disconnectedSlots = 0; + size_t instancePreviousSlots = 0; + if (status >= ST.inemitting) { - if (slots[i] == slot) - { slots_idx--; - slots[i] = slots[slots_idx]; - slots[slots_idx] = null; // not strictly necessary - - Object o = _d_toObject(slot.ptr); - rt_detachDisposeEvent(o, &unhook); + foreach (i, sloti; slots[0 .. slots_idx]) + { + if (sloti.ptr == slot.ptr && + ++instancePreviousSlots && + sloti == slot) + { + disconnectedSlots++; + slots[i] = null; + status = ST.inemitting_disconnected; + } + } + } + else + { + for (size_t i = 0; i < slots_idx; ) + { + if (slots[i].ptr == slot.ptr && + ++instancePreviousSlots && + slots[i] == slot) + { + slots_idx--; + disconnectedSlots++; + slots[i] = slots[slots_idx]; + slots[slots_idx] = null; // not strictly necessary + } + else + i++; } - else - i++; } - } + + // detach object from dispose event if all its slots have been removed + if (instancePreviousSlots == disconnectedSlots) + { + Object o = _d_toObject(slot.ptr); + rt_detachDisposeEvent(o, &unhook); + } + } /* ** * Special function called when o is destroyed. @@ -228,8 +227,9 @@ mixin template Signal(T1...) * of slots to be called by emit(). */ final void unhook(Object o) - { - debug (signal) writefln("Signal.unhook(o = %s)", cast(void*)o); + in { assert( status == ST.idle ); } + body { + debug (signal) writefln("Signal.unhook(o = %s)", cast(void*) o); for (size_t i = 0; i < slots_idx; ) { if (_d_toObject(slots[i].ptr) is o) @@ -269,13 +269,100 @@ mixin template Signal(T1...) private: slot_t[] slots; // the slots to call from emit() size_t slots_idx; // used length of slots[] + + enum ST { idle, inemitting, inemitting_disconnected } + ST status; +} + +/// +@system unittest +{ + import std.signals; + + int observedMessageCounter = 0; + + class Observer + { // our slot + void watch(string msg, int value) + { + switch (observedMessageCounter++) + { + case 0: + assert(msg == "setting new value"); + assert(value == 4); + break; + case 1: + assert(msg == "setting new value"); + assert(value == 6); + break; + default: + assert(0, "Unknown observation"); + } + } + } + + class Observer2 + { // our slot + void watch(string msg, int value) + { + } + } + + class Foo + { + int value() { return _value; } + + int value(int v) + { + if (v != _value) + { _value = v; + // call all the connected slots with the two parameters + emit("setting new value", v); + } + return v; + } + + // Mix in all the code we need to make Foo into a signal + mixin Signal!(string, int); + + private : + int _value; + } + + Foo a = new Foo; + Observer o = new Observer; + auto o2 = new Observer2; + auto o3 = new Observer2; + auto o4 = new Observer2; + auto o5 = new Observer2; + + a.value = 3; // should not call o.watch() + a.connect(&o.watch); // o.watch is the slot + a.connect(&o2.watch); + a.connect(&o3.watch); + a.connect(&o4.watch); + a.connect(&o5.watch); + a.value = 4; // should call o.watch() + a.disconnect(&o.watch); // o.watch is no longer a slot + a.disconnect(&o3.watch); + a.disconnect(&o5.watch); + a.disconnect(&o4.watch); + a.disconnect(&o2.watch); + a.value = 5; // so should not call o.watch() + a.connect(&o2.watch); + a.connect(&o.watch); // connect again + a.value = 6; // should call o.watch() + destroy(o); // destroying o should automatically disconnect it + a.value = 7; // should not call o.watch() + + assert(observedMessageCounter == 2); } // A function whose sole purpose is to get this module linked in // so the unittest will run. void linkin() { } -unittest +@system unittest { class Observer { @@ -345,7 +432,8 @@ unittest a.value = 7; } -unittest { +@system unittest +{ class Observer { int i; @@ -523,8 +611,36 @@ unittest { a.value6 = 46; } +// Triggers bug from issue 15341 +@system unittest +{ + class Observer + { + void watch() { } + void watch2() { } + } + + class Bar + { + mixin Signal!(); + } + + auto a = new Bar; + auto o = new Observer; + + //Connect both observer methods for the same instance + a.connect(&o.watch); + a.connect(&o.watch2); // not connecting watch2() or disconnecting it manually fixes the issue + + //Disconnect a single method of the two + a.disconnect(&o.watch); // NOT disconnecting watch() fixes the issue + + destroy(o); // destroying o should automatically call unhook and disconnect the slot for watch2 + a.emit(); // should not raise segfault since &o.watch2 is no longer connected +} + version(none) // Disabled because of dmd @@@BUG5028@@@ -unittest +@system unittest { class A { @@ -536,3 +652,57 @@ unittest mixin Signal!(string, int) s2; } } + +// Triggers bug from issue 16249 +@system unittest +{ + class myLINE + { + mixin Signal!( myLINE, int ); + + void value( int v ) + { + if ( v >= 0 ) emit( this, v ); + else emit( new myLINE, v ); + } + } + + class Dot + { + int value; + + myLINE line_; + void line( myLINE line_x ) + { + if ( line_ is line_x ) return; + + if ( line_ !is null ) + { + line_.disconnect( &watch ); + } + line_ = line_x; + line_.connect( &watch ); + } + + void watch( myLINE line_x, int value_x ) + { + line = line_x; + value = value_x; + } + } + + auto dot1 = new Dot; + auto dot2 = new Dot; + auto line = new myLINE; + dot1.line = line; + dot2.line = line; + + line.value = 11; + assert( dot1.value == 11 ); + assert( dot2.value == 11 ); + + line.value = -22; + assert( dot1.value == -22 ); + assert( dot2.value == -22 ); +} + diff --git a/std/socket.d b/std/socket.d index f4db5357906..d5b4693700b 100644 --- a/std/socket.d +++ b/std/socket.d @@ -36,12 +36,10 @@ /** * Socket primitives. * Example: See $(SAMPLESRC listener.d) and $(SAMPLESRC htmlget.d) - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Christopher E. Miller, $(WEB klickverbot.at, David Nadlinger), - * $(WEB thecybershadow.net, Vladimir Panteleev) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Christopher E. Miller, $(HTTP klickverbot.at, David Nadlinger), + * $(HTTP thecybershadow.net, Vladimir Panteleev) * Source: $(PHOBOSSRC std/_socket.d) - * Macros: - * WIKI=Phobos/StdSocket */ module std.socket; @@ -50,8 +48,7 @@ import core.stdc.stdint, core.stdc.string, std.string, core.stdc.stdlib, std.con import core.stdc.config; import core.time : dur, Duration; -import std.algorithm : max; -import std.exception : assumeUnique, enforce, collectException; +import std.exception; import std.internal.cstring; @@ -63,7 +60,8 @@ version(Windows) pragma (lib, "ws2_32.lib"); pragma (lib, "wsock32.lib"); - private import core.sys.windows.windows, core.sys.windows.winsock2, std.windows.syserror; + public import core.sys.windows.winsock2; + private import core.sys.windows.windows, std.windows.syserror; private alias _ctimeval = core.sys.windows.winsock2.timeval; private alias _clinger = core.sys.windows.winsock2.linger; @@ -145,31 +143,23 @@ version(unittest) /// Base exception thrown by $(D std.socket). class SocketException: Exception { - /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) pure nothrow - { - super(msg, file, line, next); - } - - /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) pure nothrow - { - super(msg, next, file, line); - } + mixin basicExceptionCtors; } -// Needs to be public so that SocketOSException can be thrown outside of -// std.socket (since it uses it as a default argument), but it probably doesn't -// need to actually show up in the docs, since there's not really any public -// need for it outside of being a default argument. +/* + * Needs to be public so that SocketOSException can be thrown outside of + * std.socket (since it uses it as a default argument), but it probably doesn't + * need to actually show up in the docs, since there's not really any public + * need for it outside of being a default argument. + */ string formatSocketError(int err) @trusted { version(Posix) { char[80] buf; const(char)* cs; - version (linux) + version (CRuntime_Glibc) { cs = strerror_r(err, buf.ptr, buf.length); } @@ -189,6 +179,14 @@ string formatSocketError(int err) @trusted else return "Socket error " ~ to!string(err); } + else version (NetBSD) + { + auto errs = strerror_r(err, buf.ptr, buf.length); + if (errs == 0) + cs = buf.ptr; + else + return "Socket error " ~ to!string(err); + } else version (Solaris) { auto errs = strerror_r(err, buf.ptr, buf.length); @@ -197,7 +195,7 @@ string formatSocketError(int err) @trusted else return "Socket error " ~ to!string(err); } - else version (Android) + else version (CRuntime_Bionic) { auto errs = strerror_r(err, buf.ptr, buf.length); if (errs == 0) @@ -210,9 +208,9 @@ string formatSocketError(int err) @trusted auto len = strlen(cs); - if(cs[len - 1] == '\n') + if (cs[len - 1] == '\n') len--; - if(cs[len - 1] == '\r') + if (cs[len - 1] == '\r') len--; return cs[0 .. len].idup; } @@ -231,8 +229,10 @@ string formatSocketError(int err) @trusted return formatSocketError(_lasterr()); } -/// Socket exceptions representing network errors reported by the operating -/// system. +/** + * Socket exceptions representing network errors reported by the operating + * system. + */ class SocketOSException: SocketException { int errorCode; /// Platform-specific error code. @@ -279,39 +279,24 @@ class SocketOSException: SocketException /// Socket exceptions representing invalid parameters specified by user code. class SocketParameterException: SocketException { - /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) pure nothrow - { - super(msg, file, line, next); - } - - /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) pure nothrow - { - super(msg, next, file, line); - } + mixin basicExceptionCtors; } -/// Socket exceptions representing attempts to use network capabilities not -/// available on the current system. +/** + * Socket exceptions representing attempts to use network capabilities not + * available on the current system. + */ class SocketFeatureException: SocketException { - /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) pure nothrow - { - super(msg, file, line, next); - } - - /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) pure nothrow - { - super(msg, next, file, line); - } + mixin basicExceptionCtors; } -/// Return $(D true) if the last socket operation failed because the socket -/// was in non-blocking mode and the operation would have blocked. +/** + * Returns: + * $(D true) if the last socket operation failed because the socket + * was in non-blocking mode and the operation would have blocked. + */ bool wouldHaveBlocked() nothrow @nogc { version(Windows) @@ -340,7 +325,7 @@ shared static this() @system // The version is just a request. int val; val = WSAStartup(0x2020, &wd); - if(val) // Request Winsock 2.2 for IPv6. + if (val) // Request Winsock 2.2 for IPv6. throw new SocketOSException("Unable to initialize socket library", val); // These functions may not be present on older Windows versions. @@ -403,37 +388,18 @@ enum SocketType: int /** * Protocol */ -version(Android) +enum ProtocolType: int { - // no GGP on Android - enum ProtocolType: int - { - IP = IPPROTO_IP, /// Internet Protocol version 4 - ICMP = IPPROTO_ICMP, /// Internet Control Message Protocol - IGMP = IPPROTO_IGMP, /// Internet Group Management Protocol - TCP = IPPROTO_TCP, /// Transmission Control Protocol - PUP = IPPROTO_PUP, /// PARC Universal Packet Protocol - UDP = IPPROTO_UDP, /// User Datagram Protocol - IDP = IPPROTO_IDP, /// Xerox NS protocol - RAW = IPPROTO_RAW, /// Raw IP packets - IPV6 = IPPROTO_IPV6, /// Internet Protocol version 6 - } -} -else -{ - enum ProtocolType: int - { - IP = IPPROTO_IP, /// Internet Protocol version 4 - ICMP = IPPROTO_ICMP, /// Internet Control Message Protocol - IGMP = IPPROTO_IGMP, /// Internet Group Management Protocol - GGP = IPPROTO_GGP, /// Gateway to Gateway Protocol - TCP = IPPROTO_TCP, /// Transmission Control Protocol - PUP = IPPROTO_PUP, /// PARC Universal Packet Protocol - UDP = IPPROTO_UDP, /// User Datagram Protocol - IDP = IPPROTO_IDP, /// Xerox NS protocol - RAW = IPPROTO_RAW, /// Raw IP packets - IPV6 = IPPROTO_IPV6, /// Internet Protocol version 6 - } + IP = IPPROTO_IP, /// Internet Protocol version 4 + ICMP = IPPROTO_ICMP, /// Internet Control Message Protocol + IGMP = IPPROTO_IGMP, /// Internet Group Management Protocol + GGP = IPPROTO_GGP, /// Gateway to Gateway Protocol + TCP = IPPROTO_TCP, /// Transmission Control Protocol + PUP = IPPROTO_PUP, /// PARC Universal Packet Protocol + UDP = IPPROTO_UDP, /// User Datagram Protocol + IDP = IPPROTO_IDP, /// Xerox NS protocol + RAW = IPPROTO_RAW, /// Raw IP packets + IPV6 = IPPROTO_IPV6, /// Internet Protocol version 6 } @@ -447,7 +413,7 @@ else * if (proto.getProtocolByType(ProtocolType.TCP)) * { * writefln(" Name: %s", proto.name); - * foreach(string s; proto.aliases) + * foreach (string s; proto.aliases) * writefln(" Alias: %s", s); * } * else @@ -464,20 +430,20 @@ class Protocol void populate(protoent* proto) @system pure nothrow { - type = cast(ProtocolType)proto.p_proto; + type = cast(ProtocolType) proto.p_proto; name = to!string(proto.p_name); int i; - for(i = 0;; i++) + for (i = 0;; i++) { - if(!proto.p_aliases[i]) + if (!proto.p_aliases[i]) break; } - if(i) + if (i) { aliases = new string[i]; - for(i = 0; i != aliases.length; i++) + for (i = 0; i != aliases.length; i++) { aliases[i] = to!string(proto.p_aliases[i]); @@ -494,7 +460,7 @@ class Protocol { protoent* proto; proto = getprotobyname(name.tempCString()); - if(!proto) + if (!proto) return false; populate(proto); return true; @@ -507,7 +473,7 @@ class Protocol { protoent* proto; proto = getprotobynumber(type); - if(!proto) + if (!proto) return false; populate(proto); return true; @@ -515,15 +481,17 @@ class Protocol } -unittest +// Skip this test on Android because getprotobyname/number are +// unimplemented in bionic. +version(CRuntime_Bionic) {} else +@safe unittest { - // getprotobyname,number are unimplemented on Android softUnittest({ Protocol proto = new Protocol; assert(proto.getProtocolByType(ProtocolType.TCP)); //writeln("About protocol TCP:"); //writefln("\tName: %s", proto.name); - // foreach(string s; proto.aliases) + // foreach (string s; proto.aliases) // { // writefln("\tAlias: %s", s); // } @@ -564,20 +532,20 @@ class Service void populate(servent* serv) @system pure nothrow { name = to!string(serv.s_name); - port = ntohs(cast(ushort)serv.s_port); + port = ntohs(cast(ushort) serv.s_port); protocolName = to!string(serv.s_proto); int i; - for(i = 0;; i++) + for (i = 0;; i++) { - if(!serv.s_aliases[i]) + if (!serv.s_aliases[i]) break; } - if(i) + if (i) { aliases = new string[i]; - for(i = 0; i != aliases.length; i++) + for (i = 0; i != aliases.length; i++) { aliases[i] = to!string(serv.s_aliases[i]); @@ -597,7 +565,7 @@ class Service { servent* serv; serv = getservbyname(name.tempCString(), protocolName.tempCString()); - if(!serv) + if (!serv) return false; populate(serv); return true; @@ -609,7 +577,7 @@ class Service { servent* serv; serv = getservbyport(port, protocolName.tempCString()); - if(!serv) + if (!serv) return false; populate(serv); return true; @@ -617,17 +585,17 @@ class Service } -unittest +@safe unittest { softUnittest({ Service serv = new Service; - if(serv.getServiceByName("epmap", "tcp")) + if (serv.getServiceByName("epmap", "tcp")) { // writefln("About service epmap:"); // writefln("\tService: %s", serv.name); // writefln("\tPort: %d", serv.port); // writefln("\tProtocol: %s", serv.protocolName); - // foreach(string s; serv.aliases) + // foreach (string s; serv.aliases) // { // writefln("\tAlias: %s", s); // } @@ -644,65 +612,44 @@ unittest } -/** - * Class for exceptions thrown from an $(D InternetHost). - */ -class HostException: SocketOSException +private mixin template socketOSExceptionCtors() { /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null, int err = _lasterr()) + this(string msg, string file = __FILE__, size_t line = __LINE__, + Throwable next = null, int err = _lasterr()) { super(msg, file, line, next, err); } /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__, int err = _lasterr()) + this(string msg, Throwable next, string file = __FILE__, + size_t line = __LINE__, int err = _lasterr()) { super(msg, next, file, line, err); } /// - this(string msg, int err, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + this(string msg, int err, string file = __FILE__, size_t line = __LINE__, + Throwable next = null) { super(msg, next, file, line, err); } } + +/** + * Class for exceptions thrown from an `InternetHost`. + */ +class HostException: SocketOSException +{ + mixin socketOSExceptionCtors; +} + /** - * $(D InternetHost) is a class for resolving IPv4 addresses. + * `InternetHost` is a class for resolving IPv4 addresses. * - * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods + * Consider using `getAddress`, `parseAddress` and `Address` methods * instead of using this class directly. - * - * Example: - * --- - * auto ih = new InternetHost; - * - * // Forward lookup - * writeln("About www.digitalmars.com:"); - * if (ih.getHostByName("www.digitalmars.com")) - * { - * writefln(" Name: %s", ih.name); - * auto ip = InternetAddress.addrToString(ih.addrList[0]); - * writefln(" IP address: %s", ip); - * foreach (string s; ih.aliases) - * writefln(" Alias: %s", s); - * writeln("---"); - * - * // Reverse lookup - * writefln("About IP %s:", ip); - * if (ih.getHostByAddr(ih.addrList[0])) - * { - * writefln(" Name: %s", ih.name); - * foreach (string s; ih.aliases) - * writefln(" Alias: %s", s); - * } - * else - * writeln(" Reverse lookup failed"); - * } - * else - * writeln(" Can't resolve www.digitalmars.com"); - * --- */ class InternetHost { @@ -714,7 +661,7 @@ class InternetHost void validHostent(in hostent* he) { - if(he.h_addrtype != cast(int)AddressFamily.INET || he.h_length != 4) + if (he.h_addrtype != cast(int) AddressFamily.INET || he.h_length != 4) throw new HostException("Address family mismatch"); } @@ -726,17 +673,17 @@ class InternetHost name = to!string(he.h_name); - for(i = 0;; i++) + for (i = 0;; i++) { p = he.h_aliases[i]; - if(!p) + if (!p) break; } - if(i) + if (i) { aliases = new string[i]; - for(i = 0; i != aliases.length; i++) + for (i = 0; i != aliases.length; i++) { aliases[i] = to!string(he.h_aliases[i]); @@ -747,19 +694,19 @@ class InternetHost aliases = null; } - for(i = 0;; i++) + for (i = 0;; i++) { p = he.h_addr_list[i]; - if(!p) + if (!p) break; } - if(i) + if (i) { addrList = new uint[i]; - for(i = 0; i != addrList.length; i++) + for (i = 0; i != addrList.length; i++) { - addrList[i] = ntohl(*(cast(uint*)he.h_addr_list[i])); + addrList[i] = ntohl(*(cast(uint*) he.h_addr_list[i])); } } else @@ -836,7 +783,7 @@ class InternetHost { return getHost!q{ auto x = htonl(param); - auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET); + auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET); }(addr); } @@ -851,13 +798,13 @@ class InternetHost auto x = inet_addr(param.tempCString()); enforce(x != INADDR_NONE, new SocketParameterException("Invalid IPv4 address")); - auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET); + auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET); }(addr); } } - -unittest +/// +@safe unittest { InternetHost ih = new InternetHost; @@ -866,29 +813,21 @@ unittest ih.getHostByAddr("127.0.0.1"); assert(ih.addrList[0] == 0x7F_00_00_01); - softUnittest({ - if (!ih.getHostByName("www.digitalmars.com")) - return; // don't fail if not connected to internet - //writefln("addrList.length = %d", ih.addrList.length); - assert(ih.addrList.length); - InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY); - assert(ih.name == "www.digitalmars.com" || ih.name == "digitalmars.com", - ih.name); - // writefln("IP address = %s", ia.toAddrString()); - // writefln("name = %s", ih.name); - // foreach(int i, string s; ih.aliases) - // { - // writefln("aliases[%d] = %s", i, s); - // } - // writefln("---"); + if (!ih.getHostByName("www.digitalmars.com")) + return; // don't fail if not connected to internet - //assert(ih.getHostByAddr(ih.addrList[0])); - // writefln("name = %s", ih.name); - // foreach(int i, string s; ih.aliases) - // { - // writefln("aliases[%d] = %s", i, s); - // } - }); + assert(ih.addrList.length); + InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY); + assert(ih.name == "www.digitalmars.com" || ih.name == "digitalmars.com", + ih.name); + + assert(ih.getHostByAddr(ih.addrList[0])); + string getHostNameFromInt = ih.name.dup; + + assert(ih.getHostByAddr(ia.toAddrString())); + string getHostNameFromStr = ih.name.dup; + + assert(getHostNameFromInt == getHostNameFromStr); } @@ -902,8 +841,10 @@ struct AddressInfo string canonicalName; /// Canonical name, when $(D AddressInfoFlags.CANONNAME) is used. } -// A subset of flags supported on all platforms with getaddrinfo. -/// Specifies option flags for $(D getAddressInfo). +/** + * A subset of flags supported on all platforms with getaddrinfo. + * Specifies option flags for $(D getAddressInfo). + */ enum AddressInfoFlags: int { /// The resulting addresses will be used in a call to $(D Socket.bind). @@ -912,14 +853,18 @@ enum AddressInfoFlags: int /// The canonical name is returned in $(D canonicalName) member in the first $(D AddressInfo). CANONNAME = AI_CANONNAME, - /// The $(D node) parameter passed to $(D getAddressInfo) must be a numeric string. - /// This will suppress any potentially lengthy network host address lookups. + /** + * The $(D node) parameter passed to $(D getAddressInfo) must be a numeric string. + * This will suppress any potentially lengthy network host address lookups. + */ NUMERICHOST = AI_NUMERICHOST, } -/// On POSIX, getaddrinfo uses its own error codes, and thus has its own -/// formatting function. +/** + * On POSIX, getaddrinfo uses its own error codes, and thus has its own + * formatting function. + */ private string formatGaiError(int err) @trusted { version(Windows) @@ -985,7 +930,7 @@ private string formatGaiError(int err) @trusted * AddressFamily.INET6); * --- */ -AddressInfo[] getAddressInfo(T...)(in char[] node, T options) @trusted +AddressInfo[] getAddressInfo(T...)(in char[] node, T options) { const(char)[] service = null; addrinfo hints; @@ -1011,7 +956,23 @@ AddressInfo[] getAddressInfo(T...)(in char[] node, T options) @trusted static assert(0, "Unknown getAddressInfo option type: " ~ typeof(option).stringof); } - return getAddressInfoImpl(node, service, &hints); + return () @trusted { return getAddressInfoImpl(node, service, &hints); }(); +} + +@system unittest +{ + struct Oops + { + const(char[]) breakSafety() + { + *cast(int*) 0xcafebabe = 0xdeadbeef; + return null; + } + alias breakSafety this; + } + assert(!__traits(compiles, () { + getAddressInfo("", Oops.init); + }), "getAddressInfo breaks @safe"); } private AddressInfo[] getAddressInfoImpl(in char[] node, in char[] service, addrinfo* hints) @system @@ -1049,7 +1010,7 @@ private AddressInfo[] getAddressInfoImpl(in char[] node, in char[] service, addr } -unittest +@safe unittest { softUnittest({ if (getaddrinfoPointer) @@ -1080,6 +1041,13 @@ unittest assert(results.length && results[0].family == AddressFamily.INET6); } }); + + if (getaddrinfoPointer) + { + auto results = getAddressInfo(null, "1234", AddressInfoFlags.PASSIVE, + SocketType.STREAM, ProtocolType.TCP, AddressFamily.INET); + assert(results.length == 1 && results[0].address.toString() == "0.0.0.0:1234"); + } } @@ -1157,7 +1125,7 @@ Address[] getAddress(in char[] hostname, ushort port) } -unittest +@safe unittest { softUnittest({ auto addresses = getAddress("63.105.9.61"); @@ -1237,7 +1205,7 @@ Address parseAddress(in char[] hostaddr, ushort port) } -unittest +@safe unittest { softUnittest({ auto address = parseAddress("63.105.9.61"); @@ -1264,23 +1232,7 @@ unittest */ class AddressException: SocketOSException { - /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null, int err = _lasterr()) - { - super(msg, file, line, next, err); - } - - /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__, int err = _lasterr()) - { - super(msg, next, file, line, err); - } - - /// - this(string msg, int err, string file = __FILE__, size_t line = __LINE__, Throwable next = null) - { - super(msg, next, file, line, err); - } + mixin socketOSExceptionCtors; } @@ -1324,7 +1276,7 @@ abstract class Address } // Common code for toAddrString and toHostNameString - private final string toHostString(bool numeric) @trusted const + private string toHostString(bool numeric) @trusted const { // getnameinfo() is the recommended way to perform a reverse (name) // lookup on both Posix and Windows. However, it is only available @@ -1341,16 +1293,16 @@ abstract class Address auto buf = new char[NI_MAXHOST]; auto ret = getnameinfoPointer( name, nameLen, - buf.ptr, cast(uint)buf.length, + buf.ptr, cast(uint) buf.length, null, 0, numeric ? NI_NUMERICHOST : NI_NAMEREQD); if (!numeric) { - if (ret==EAI_NONAME) + if (ret == EAI_NONAME) return null; version(Windows) - if (ret==WSANO_DATA) + if (ret == WSANO_DATA) return null; } @@ -1364,7 +1316,7 @@ abstract class Address } // Common code for toPortString and toServiceNameString - private final string toServiceString(bool numeric) @trusted const + private string toServiceString(bool numeric) @trusted const { // See toHostNameString() for details about getnameinfo(). if (getnameinfoPointer) @@ -1373,7 +1325,7 @@ abstract class Address enforce(getnameinfoPointer( name, nameLen, null, 0, - buf.ptr, cast(uint)buf.length, + buf.ptr, cast(uint) buf.length, numeric ? NI_NUMERICSERV : NI_NAMEREQD ) == 0, new AddressException("Could not get " ~ (numeric ? "port number" : "service name"))); @@ -1502,7 +1454,7 @@ public: /// Constructs an $(D Address) with a copy of the specified $(D sockaddr). this(const(sockaddr)* sa, socklen_t len) @system pure nothrow { - this.sa = cast(sockaddr*) (cast(ubyte*)sa)[0..len].dup.ptr; + this.sa = cast(sockaddr*) (cast(ubyte*) sa)[0 .. len].dup.ptr; this.len = len; } @@ -1587,10 +1539,10 @@ public: this(in char[] addr, ushort port) { uint uiaddr = parse(addr); - if(ADDR_NONE == uiaddr) + if (ADDR_NONE == uiaddr) { InternetHost ih = new InternetHost; - if(!ih.getHostByName(addr)) + if (!ih.getHostByName(addr)) //throw new AddressException("Invalid internet address"); throw new AddressException( text("Unable to resolve host '", addr, "'")); @@ -1622,6 +1574,17 @@ public: sin.sin_port = htons(port); } + /** + * Construct a new $(D InternetAddress). + * Params: + * addr = A sockaddr_in as obtained from lower-level API calls such as getifaddrs. + */ + this(sockaddr_in addr) pure nothrow @nogc + { + assert(addr.sin_family == AddressFamily.INET); + sin = addr; + } + /// Human readable string representing the IPv4 address in dotted-decimal form. override string toAddrString() @trusted const { @@ -1667,19 +1630,24 @@ public: * Compares with another InternetAddress of same type for equality * Returns: true if the InternetAddresses share the same address and * port number. - * Examples: - * -------------- - * InternetAddress addr1,addr2; - * if (addr1 == addr2) { } - * -------------- */ override bool opEquals(Object o) const { - auto other = cast(InternetAddress)o; + auto other = cast(InternetAddress) o; return other && this.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr && this.sin.sin_port == other.sin.sin_port; } + /// + @system unittest + { + auto addr1 = new InternetAddress("127.0.0.1", 80); + auto addr2 = new InternetAddress("127.0.0.2", 80); + + assert(addr1 == addr1); + assert(addr1 != addr2); + } + /** * Parse an IPv4 address string in the dotted-decimal form $(I a.b.c.d) * and return the number. @@ -1704,13 +1672,25 @@ public: } -unittest +@safe unittest { softUnittest({ const InternetAddress ia = new InternetAddress("63.105.9.61", 80); assert(ia.toString() == "63.105.9.61:80"); }); + softUnittest({ + // test construction from a sockaddr_in + sockaddr_in sin; + + sin.sin_addr.s_addr = htonl(0x7F_00_00_01); // 127.0.0.1 + sin.sin_family = AddressFamily.INET; + sin.sin_port = htons(80); + + const InternetAddress ia = new InternetAddress(sin); + assert(ia.toString() == "127.0.0.1:80"); + }); + softUnittest({ // test reverse lookup auto ih = new InternetHost; @@ -1830,7 +1810,7 @@ public: { auto results = getAddressInfo(addr, service, AddressFamily.INET6); assert(results.length && results[0].family == AddressFamily.INET6); - sin6 = *cast(sockaddr_in6*)results[0].address.name; + sin6 = *cast(sockaddr_in6*) results[0].address.name; } /** @@ -1870,7 +1850,18 @@ public: sin6.sin6_port = htons(port); } - /** + /** + * Construct a new $(D Internet6Address). + * Params: + * addr = A sockaddr_in6 as obtained from lower-level API calls such as getifaddrs. + */ + this(sockaddr_in6 addr) pure nothrow @nogc + { + assert(addr.sin6_family == AddressFamily.INET6); + sin6 = addr; + } + + /** * Parse an IPv6 host address string as described in RFC 2373, and return the * address. * Throws: $(D SocketException) on error. @@ -1882,37 +1873,72 @@ public: // instead. auto results = getAddressInfo(addr, AddressInfoFlags.NUMERICHOST); if (results.length && results[0].family == AddressFamily.INET6) - return (cast(sockaddr_in6*)results[0].address.name).sin6_addr.s6_addr; + return (cast(sockaddr_in6*) results[0].address.name).sin6_addr.s6_addr; throw new AddressException("Not an IPv6 address", 0); } } -unittest +@safe unittest { softUnittest({ const Internet6Address ia = new Internet6Address("::1", 80); assert(ia.toString() == "[::1]:80"); }); + + softUnittest({ + // test construction from a sockaddr_in6 + sockaddr_in6 sin; + + sin.sin6_addr.s6_addr = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; // [::1] + sin.sin6_family = AddressFamily.INET6; + sin.sin6_port = htons(80); + + const Internet6Address ia = new Internet6Address(sin); + assert(ia.toString() == "[::1]:80"); + }); } version(StdDdoc) { + static if (!is(sockaddr_un)) + { + // This exists only to allow the constructor taking + // a sockaddr_un to be compilable for documentation + // on platforms that don't supply a sockaddr_un. + struct sockaddr_un + { + } + } + /** * $(D UnixAddress) encapsulates an address for a Unix domain socket * ($(D AF_UNIX)). Available only on supported systems. */ class UnixAddress: Address { + private this() pure nothrow @nogc {} + /// Construct a new $(D UnixAddress) from the specified path. - this(in char[] path); + this(in char[] path) { } + + /** + * Construct a new $(D UnixAddress). + * Params: + * addr = A sockaddr_un as obtained from lower-level API calls. + */ + this(sockaddr_un addr) pure nothrow @nogc { } /// Get the underlying _path. - @property string path() const; + @property string path() const { return null; } /// ditto - override string toString() const; + override string toString() const { return null; } + + override @property sockaddr* name() { return null; } + override @property const(sockaddr)* name() const { return null; } + override @property socklen_t nameLen() const { return 0; } } } else @@ -1921,45 +1947,53 @@ static if (is(sockaddr_un)) class UnixAddress: Address { protected: - sockaddr_un* sun; - socklen_t len; - + struct + { + align (1): + sockaddr_un sun; + char unused = '\0'; // placeholder for a terminating '\0' + } this() pure nothrow @nogc { + sun.sun_family = AddressFamily.UNIX; + sun.sun_path = '?'; } - public: override @property sockaddr* name() { - return cast(sockaddr*)sun; + return cast(sockaddr*)&sun; } override @property const(sockaddr)* name() const { - return cast(const(sockaddr)*)sun; + return cast(const(sockaddr)*)&sun; } - - override @property socklen_t nameLen() const + override @property socklen_t nameLen() @trusted const { - return len; + return cast(socklen_t) (sockaddr_un.init.sun_path.offsetof + + strlen(cast(const(char*)) sun.sun_path.ptr) + 1); } - - this(in char[] path) @trusted pure nothrow + this(in char[] path) @trusted pure { - len = cast(socklen_t)(sockaddr_un.init.sun_path.offsetof + path.length + 1); - sun = cast(sockaddr_un*) (new ubyte[len]).ptr; - sun.sun_family = AF_UNIX; - sun.sun_path.ptr[0..path.length] = (cast(byte[]) path)[]; + enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long")); + sun.sun_family = AddressFamily.UNIX; + sun.sun_path.ptr[0 .. path.length] = (cast(byte[]) path)[]; sun.sun_path.ptr[path.length] = 0; } - @property string path() const pure + this(sockaddr_un addr) pure nothrow @nogc + { + assert(addr.sun_family == AddressFamily.UNIX); + sun = addr; + } + + @property string path() @trusted const pure { - return to!string(sun.sun_path.ptr); + return to!string(cast(const(char)*)sun.sun_path.ptr); } override string toString() const pure @@ -1968,15 +2002,15 @@ static if (is(sockaddr_un)) } } - unittest + @safe unittest { import core.stdc.stdio : remove; - import std.file: deleteme; + import std.file : deleteme; immutable ubyte[] data = [1, 2, 3, 4]; Socket[2] pair; - auto name = std.file.deleteme ~ "-unix-socket"; + auto name = deleteme ~ "-unix-socket"; auto address = new UnixAddress(name); auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM); @@ -1984,6 +2018,7 @@ static if (is(sockaddr_un)) listener.bind(address); scope(exit) () @trusted { remove(name.tempCString()); } (); + assert(listener.localAddress.toString == name); listener.listen(1); @@ -2010,23 +2045,7 @@ static if (is(sockaddr_un)) */ class SocketAcceptException: SocketOSException { - /// - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null, int err = _lasterr()) - { - super(msg, file, line, next, err); - } - - /// - this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__, int err = _lasterr()) - { - super(msg, next, file, line, err); - } - - /// - this(string msg, int err, string file = __FILE__, size_t line = __LINE__, Throwable next = null) - { - super(msg, next, file, line, err); - } + mixin socketOSExceptionCtors; } /// How a socket is shutdown: @@ -2117,25 +2136,25 @@ private: fd_set_type[] set; - final void resize(size_t size) pure nothrow + void resize(size_t size) pure nothrow { set.length = FD_SET_OFFSET + size; } - final ref inout(fd_set_count_type) count() @trusted @property inout pure nothrow @nogc + ref inout(fd_set_count_type) count() @trusted @property inout pure nothrow @nogc { assert(set.length); return *cast(inout(fd_set_count_type)*)set.ptr; } - final size_t capacity() @property const pure nothrow @nogc + size_t capacity() @property const pure nothrow @nogc { return set.length - FD_SET_OFFSET; } - final inout(socket_t)[] fds() @trusted inout @property pure nothrow @nogc + inout(socket_t)[] fds() @trusted inout @property pure nothrow @nogc { - return cast(inout(socket_t)[])set[FD_SET_OFFSET..FD_SET_OFFSET+count]; + return cast(inout(socket_t)[])set[FD_SET_OFFSET .. FD_SET_OFFSET+count]; } } else @@ -2145,7 +2164,7 @@ private: // type (declared in core.sys.posix.sys.select) is a structure // containing a single field, a static array. - static assert(fd_set.tupleof.length==1); + static assert(fd_set.tupleof.length == 1); // This is the type used in the fd_set array. // Using the type of the correct size is important for big-endian @@ -2159,7 +2178,7 @@ private: static fd_set_type mask(uint n) pure nothrow @nogc { - return (cast(fd_set_type)1) << (n % FD_NFDBITS); + return (cast(fd_set_type) 1) << (n % FD_NFDBITS); } // Array size to fit that many sockets @@ -2171,21 +2190,21 @@ private: fd_set_type[] set; - final void resize(size_t size) pure nothrow + void resize(size_t size) pure nothrow { set.length = lengthFor(size); } // Make sure we can fit that many sockets - final void setMinCapacity(size_t size) pure nothrow + void setMinCapacity(size_t size) pure nothrow { auto length = lengthFor(size); if (set.length < length) set.length = length; } - final size_t capacity() @property const pure nothrow @nogc + size_t capacity() @property const pure nothrow @nogc { return set.length * FD_NFDBITS; } @@ -2229,7 +2248,8 @@ public: set.length *= 2; set.length = set.capacity; } - fds[count++] = s; + ++count; + fds[$-1] = s; } else { @@ -2248,8 +2268,10 @@ public: } } - /// Add a $(D Socket) to the collection. - /// The socket must not already be in the collection. + /** + * Add a $(D Socket) to the collection. + * The socket must not already be in the collection. + */ void add(Socket s) pure nothrow { add(s.sock); @@ -2259,7 +2281,7 @@ public: { version (Windows) { - import std.algorithm : countUntil; + import std.algorithm.searching : countUntil; auto fds = fds; auto p = fds.countUntil(s); if (p >= 0) @@ -2276,8 +2298,10 @@ public: } - /// Remove this $(D Socket) from the collection. - /// Does nothing if the socket is not in the collection already. + /** + * Remove this $(D Socket) from the collection. + * Does nothing if the socket is not in the collection already. + */ void remove(Socket s) pure nothrow { remove(s.sock); @@ -2287,7 +2311,7 @@ public: { version (Windows) { - import std.algorithm; + import std.algorithm.searching : canFind; return fds.canFind(s) ? 1 : 0; } else @@ -2307,20 +2331,25 @@ public: } - /// Return the current capacity of this $(D SocketSet). The exact - /// meaning of the return value varies from platform to platform. - /// Note that since D 2.065, this value does not indicate a - /// restriction, and $(D SocketSet) will grow its capacity as - /// needed automatically. + /** + * Returns: + * The current capacity of this $(D SocketSet). The exact + * meaning of the return value varies from platform to platform. + * + * Note: + * Since D 2.065, this value does not indicate a + * restriction, and $(D SocketSet) will grow its capacity as + * needed automatically. + */ @property uint max() const pure nothrow @nogc { - return cast(uint)capacity; + return cast(uint) capacity; } fd_set* toFd_set() @trusted pure nothrow @nogc { - return cast(fd_set*)set.ptr; + return cast(fd_set*) set.ptr; } @@ -2337,10 +2366,10 @@ public: } } -unittest +@safe unittest { auto fds = cast(socket_t[]) - [cast(socket_t)1, 2, 0, 1024, 17, 42, 1234, 77, 77+32, 77+64]; + [cast(socket_t) 1, 2, 0, 1024, 17, 42, 1234, 77, 77+32, 77+64]; auto set = new SocketSet(); foreach (fd; fds) assert(!set.isSet(fd)); foreach (fd; fds) set.add(fd); @@ -2349,7 +2378,7 @@ unittest // Make sure SocketSet reimplements fd_set correctly auto fdset = set.toFd_set(); foreach (fd; fds[0]..cast(socket_t)(fds[$-1]+1)) - assert(cast(bool)set.isSet(fd) == cast(bool)(() @trusted => FD_ISSET(fd, fdset))()); + assert(cast(bool) set.isSet(fd) == cast(bool)(() @trusted => FD_ISSET(fd, fdset))()); foreach (fd; fds) { @@ -2359,7 +2388,7 @@ unittest } } -unittest +@safe unittest { softUnittest({ enum PAIRS = 768; @@ -2434,51 +2463,31 @@ unittest }); } -unittest // Issue 14012, 14013 +@safe unittest // Issue 14012, 14013 { auto set = new SocketSet(1); assert(set.max >= 0); enum LIMIT = 4096; - foreach (n; 0..LIMIT) - set.add(cast(socket_t)n); + foreach (n; 0 .. LIMIT) + set.add(cast(socket_t) n); assert(set.max >= LIMIT); } /// The level at which a socket option is defined: -version(Android) +enum SocketOptionLevel: int { - // no GGP on Android - enum SocketOptionLevel: int - { - SOCKET = SOL_SOCKET, /// Socket level - IP = ProtocolType.IP, /// Internet Protocol version 4 level - ICMP = ProtocolType.ICMP, /// Internet Control Message Protocol level - IGMP = ProtocolType.IGMP, /// Internet Group Management Protocol level - TCP = ProtocolType.TCP, /// Transmission Control Protocol level - PUP = ProtocolType.PUP, /// PARC Universal Packet Protocol level - UDP = ProtocolType.UDP, /// User Datagram Protocol level - IDP = ProtocolType.IDP, /// Xerox NS protocol level - RAW = ProtocolType.RAW, /// Raw IP packet level - IPV6 = ProtocolType.IPV6, /// Internet Protocol version 6 level - } -} -else -{ - enum SocketOptionLevel: int - { - SOCKET = SOL_SOCKET, /// Socket level - IP = ProtocolType.IP, /// Internet Protocol version 4 level - ICMP = ProtocolType.ICMP, /// Internet Control Message Protocol level - IGMP = ProtocolType.IGMP, /// Internet Group Management Protocol level - GGP = ProtocolType.GGP, /// Gateway to Gateway Protocol level - TCP = ProtocolType.TCP, /// Transmission Control Protocol level - PUP = ProtocolType.PUP, /// PARC Universal Packet Protocol level - UDP = ProtocolType.UDP, /// User Datagram Protocol level - IDP = ProtocolType.IDP, /// Xerox NS protocol level - RAW = ProtocolType.RAW, /// Raw IP packet level - IPV6 = ProtocolType.IPV6, /// Internet Protocol version 6 level - } + SOCKET = SOL_SOCKET, /// Socket level + IP = ProtocolType.IP, /// Internet Protocol version 4 level + ICMP = ProtocolType.ICMP, /// Internet Control Message Protocol level + IGMP = ProtocolType.IGMP, /// Internet Group Management Protocol level + GGP = ProtocolType.GGP, /// Gateway to Gateway Protocol level + TCP = ProtocolType.TCP, /// Transmission Control Protocol level + PUP = ProtocolType.PUP, /// PARC Universal Packet Protocol level + UDP = ProtocolType.UDP, /// User Datagram Protocol level + IDP = ProtocolType.IDP, /// Xerox NS protocol level + RAW = ProtocolType.RAW, /// Raw IP packet level + IPV6 = ProtocolType.IPV6, /// Internet Protocol version 6 level } /// _Linger information for use with SocketOption.LINGER. @@ -2555,11 +2564,12 @@ private: // behavior. enum WINSOCK_TIMEOUT_SKEW = 500; - unittest + @safe unittest { version(SlowTests) softUnittest({ import std.datetime; + import std.typecons; enum msecs = 1000; auto pair = socketPair(); @@ -2567,7 +2577,7 @@ private: sock.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"msecs"(msecs)); - auto sw = StopWatch(AutoStart.yes); + auto sw = StopWatch(Yes.autoStart); ubyte[1] buf; sock.receive(buf); sw.stop(); @@ -2589,7 +2599,7 @@ private: // has it (e.g. on OS X). static if (is(typeof(SO_NOSIGPIPE))) { - setOption(SocketOptionLevel.SOCKET, cast(SocketOption)SO_NOSIGPIPE, true); + setOption(SocketOptionLevel.SOCKET, cast(SocketOption) SO_NOSIGPIPE, true); } } @@ -2611,18 +2621,18 @@ public: { _family = af; auto handle = cast(socket_t) socket(af, type, protocol); - if(handle == socket_t.init) + if (handle == socket_t.init) throw new SocketOSException("Unable to create socket"); setSock(handle); } - - // A single protocol exists to support this socket type within the - // protocol family, so the ProtocolType is assumed. /// ditto this(AddressFamily af, SocketType type) { - this(af, type, cast(ProtocolType)0); // Pseudo protocol number. + /* A single protocol exists to support this socket type within the + * protocol family, so the ProtocolType is assumed. + */ + this(af, type, cast(ProtocolType) 0); // Pseudo protocol number. } @@ -2631,9 +2641,9 @@ public: { protoent* proto; proto = getprotobyname(protocolName.tempCString()); - if(!proto) + if (!proto) throw new SocketOSException("Unable to find the protocol"); - this(af, type, cast(ProtocolType)proto.p_proto); + this(af, type, cast(ProtocolType) proto.p_proto); } @@ -2692,20 +2702,20 @@ public: version(Windows) { uint num = !byes; - if(_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num)) + if (_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num)) goto err; _blocking = byes; } else version(Posix) { int x = fcntl(sock, F_GETFL, 0); - if(-1 == x) + if (-1 == x) goto err; - if(byes) + if (byes) x &= ~O_NONBLOCK; else x |= O_NONBLOCK; - if(-1 == fcntl(sock, F_SETFL, x)) + if (-1 == fcntl(sock, F_SETFL, x)) goto err; } return; // Success. @@ -2732,7 +2742,7 @@ public: /// Associate a local address with this socket. void bind(Address addr) @trusted { - if(_SOCKET_ERROR == .bind(sock, addr.name, addr.nameLen)) + if (_SOCKET_ERROR == .bind(sock, addr.name, addr.nameLen)) throw new SocketOSException("Unable to bind socket"); } @@ -2743,21 +2753,21 @@ public: */ void connect(Address to) @trusted { - if(_SOCKET_ERROR == .connect(sock, to.name, to.nameLen)) + if (_SOCKET_ERROR == .connect(sock, to.name, to.nameLen)) { int err; err = _lasterr(); - if(!blocking) + if (!blocking) { version(Windows) { - if(WSAEWOULDBLOCK == err) + if (WSAEWOULDBLOCK == err) return; } else version(Posix) { - if(EINPROGRESS == err) + if (EINPROGRESS == err) return; } else @@ -2776,7 +2786,7 @@ public: */ void listen(int backlog) @trusted { - if(_SOCKET_ERROR == .listen(sock, backlog)) + if (_SOCKET_ERROR == .listen(sock, backlog)) throw new SocketOSException("Unable to listen on socket"); } @@ -2786,9 +2796,10 @@ public: * instance of your class. The returned $(D Socket)'s handle must not be * set; $(D Socket) has a protected constructor $(D this()) to use in this * situation. + * + * Override to use a derived class. + * The returned socket's handle must not be set. */ - // Override to use a derived class. - // The returned socket's handle must not be set. protected Socket accepting() pure nothrow { return new Socket; @@ -2802,7 +2813,7 @@ public: Socket accept() @trusted { auto newsock = cast(socket_t).accept(sock, null, null); - if(socket_t.init == newsock) + if (socket_t.init == newsock) throw new SocketAcceptException("Unable to accept socket connection"); Socket newSocket; @@ -2816,7 +2827,7 @@ public: newSocket._blocking = _blocking; //inherits blocking mode newSocket._family = _family; //same family } - catch(Throwable o) + catch (Throwable o) { _close(newsock); throw o; @@ -2828,7 +2839,7 @@ public: /// Disables sends and/or receives. void shutdown(SocketShutdown how) @trusted nothrow @nogc { - .shutdown(sock, cast(int)how); + .shutdown(sock, cast(int) how); } @@ -2850,9 +2861,9 @@ public: * Calling $(D shutdown) before $(D close) is recommended for * connection-oriented sockets. The $(D Socket) object is no longer * usable after $(D close). + * Calling shutdown() before this is recommended + * for connection-oriented sockets. */ - //calling shutdown() before this is recommended - //for connection-oriented sockets void close() @trusted nothrow @nogc { _close(sock); @@ -2860,12 +2871,13 @@ public: } - /// Returns the local machine's host name. - // Idea from mango. + /** + * Returns: the local machine's host name + */ static @property string hostName() @trusted // getter { char[256] result; // Host names are limited to 255 chars. - if(_SOCKET_ERROR == .gethostname(result.ptr, result.length)) + if (_SOCKET_ERROR == .gethostname(result.ptr, result.length)) throw new SocketOSException("Unable to obtain host name"); return to!string(result.ptr); } @@ -2875,9 +2887,9 @@ public: { Address addr = createAddress(); socklen_t nameLen = addr.nameLen; - if(_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen)) + if (_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen)) throw new SocketOSException("Unable to obtain remote socket address"); - if(nameLen > addr.nameLen) + if (nameLen > addr.nameLen) throw new SocketParameterException("Not enough socket address storage"); assert(addr.addressFamily == _family); return addr; @@ -2888,9 +2900,9 @@ public: { Address addr = createAddress(); socklen_t nameLen = addr.nameLen; - if(_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen)) + if (_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen)) throw new SocketOSException("Unable to obtain local socket address"); - if(nameLen > addr.nameLen) + if (nameLen > addr.nameLen) throw new SocketParameterException("Not enough socket address storage"); assert(addr.addressFamily == _family); return addr; @@ -2903,13 +2915,21 @@ public: */ enum int ERROR = _SOCKET_ERROR; + private static int capToInt(size_t size) nothrow @nogc + { + // Windows uses int instead of size_t for length arguments. + // Luckily, the send/recv functions make no guarantee that + // all the data is sent, so we use that to send at most + // int.max bytes. + return size > size_t(int.max) ? int.max : cast(int) size; + } + /** * Send data on the connection. If the socket is blocking and there is no * buffer space left, $(D send) waits. * Returns: The number of bytes actually sent, or $(D Socket.ERROR) on * failure. */ - //returns number of bytes actually sent, or -1 on error ptrdiff_t send(const(void)[] buf, SocketFlags flags) @trusted { static if (is(typeof(MSG_NOSIGNAL))) @@ -2917,9 +2937,9 @@ public: flags = cast(SocketFlags)(flags | MSG_NOSIGNAL); } version( Windows ) - auto sent = .send(sock, buf.ptr, to!int(buf.length), cast(int)flags); + auto sent = .send(sock, buf.ptr, capToInt(buf.length), cast(int) flags); else - auto sent = .send(sock, buf.ptr, buf.length, cast(int)flags); + auto sent = .send(sock, buf.ptr, buf.length, cast(int) flags); return sent; } @@ -2944,11 +2964,11 @@ public: } version( Windows ) return .sendto( - sock, buf.ptr, std.conv.to!int(buf.length), - cast(int)flags, to.name, to.nameLen + sock, buf.ptr, capToInt(buf.length), + cast(int) flags, to.name, to.nameLen ); else - return .sendto(sock, buf.ptr, buf.length, cast(int)flags, to.name, to.nameLen); + return .sendto(sock, buf.ptr, buf.length, cast(int) flags, to.name, to.nameLen); } /// ditto @@ -2967,9 +2987,9 @@ public: flags = cast(SocketFlags)(flags | MSG_NOSIGNAL); } version(Windows) - return .sendto(sock, buf.ptr, to!int(buf.length), cast(int)flags, null, 0); + return .sendto(sock, buf.ptr, capToInt(buf.length), cast(int) flags, null, 0); else - return .sendto(sock, buf.ptr, buf.length, cast(int)flags, null, 0); + return .sendto(sock, buf.ptr, buf.length, cast(int) flags, null, 0); } @@ -2987,17 +3007,18 @@ public: * Returns: The number of bytes actually received, $(D 0) if the remote side * has closed the connection, or $(D Socket.ERROR) on failure. */ - //returns number of bytes actually received, 0 on connection closure, or -1 on error ptrdiff_t receive(void[] buf, SocketFlags flags) @trusted { version(Windows) // Does not use size_t { return buf.length - ? .recv(sock, buf.ptr, to!int(buf.length), cast(int)flags) + ? .recv(sock, buf.ptr, capToInt(buf.length), cast(int) flags) : 0; - } else { + } + else + { return buf.length - ? .recv(sock, buf.ptr, buf.length, cast(int)flags) + ? .recv(sock, buf.ptr, buf.length, cast(int) flags) : 0; } } @@ -3017,21 +3038,23 @@ public: */ ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) @trusted { - if(!buf.length) //return 0 and don't think the connection closed + if (!buf.length) //return 0 and don't think the connection closed return 0; if (from is null || from.addressFamily != _family) from = createAddress(); socklen_t nameLen = from.nameLen; version(Windows) { - auto read = .recvfrom(sock, buf.ptr, to!int(buf.length), cast(int)flags, from.name, &nameLen); + auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, from.name, &nameLen); assert(from.addressFamily == _family); - // if(!read) //connection closed + // if (!read) //connection closed return read; - } else { - auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, from.name, &nameLen); + } + else + { + auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, from.name, &nameLen); assert(from.addressFamily == _family); - // if(!read) //connection closed + // if (!read) //connection closed return read; } } @@ -3048,16 +3071,18 @@ public: /// ditto ptrdiff_t receiveFrom(void[] buf, SocketFlags flags) @trusted { - if(!buf.length) //return 0 and don't think the connection closed + if (!buf.length) //return 0 and don't think the connection closed return 0; version(Windows) { - auto read = .recvfrom(sock, buf.ptr, to!int(buf.length), cast(int)flags, null, null); - // if(!read) //connection closed + auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, null, null); + // if (!read) //connection closed return read; - } else { - auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, null, null); - // if(!read) //connection closed + } + else + { + auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, null, null); + // if (!read) //connection closed return read; } } @@ -3071,13 +3096,15 @@ public: } - /// Get a socket option. - /// Returns: The number of bytes written to $(D result). - //returns the length, in bytes, of the actual result - very different from getsockopt() + /** + * Get a socket option. + * Returns: The number of bytes written to $(D result). + * The length, in bytes, of the actual result - very different from getsockopt() + */ int getOption(SocketOptionLevel level, SocketOption option, void[] result) @trusted { socklen_t len = cast(socklen_t) result.length; - if(_SOCKET_ERROR == .getsockopt(sock, cast(int)level, cast(int)option, result.ptr, &len)) + if (_SOCKET_ERROR == .getsockopt(sock, cast(int) level, cast(int) option, result.ptr, &len)) throw new SocketOSException("Unable to get socket option"); return len; } @@ -3093,7 +3120,7 @@ public: /// Get the linger option. int getOption(SocketOptionLevel level, SocketOption option, out Linger result) @trusted { - //return getOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]); + //return getOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]); return getOption(level, option, (&result.clinger)[0 .. 1]); } @@ -3115,7 +3142,7 @@ public: else version (Posix) { TimeVal tv; - getOption(level, option, (&tv.ctimeval)[0..1]); + getOption(level, option, (&tv.ctimeval)[0 .. 1]); result = dur!"seconds"(tv.seconds) + dur!"usecs"(tv.microseconds); } else static assert(false); @@ -3124,8 +3151,8 @@ public: /// Set a socket option. void setOption(SocketOptionLevel level, SocketOption option, void[] value) @trusted { - if(_SOCKET_ERROR == .setsockopt(sock, cast(int)level, - cast(int)option, value.ptr, cast(uint) value.length)) + if (_SOCKET_ERROR == .setsockopt(sock, cast(int) level, + cast(int) option, value.ptr, cast(uint) value.length)) throw new SocketOSException("Unable to set socket option"); } @@ -3140,7 +3167,7 @@ public: /// Set the linger option. void setOption(SocketOptionLevel level, SocketOption option, Linger value) @trusted { - //setOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]); + //setOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]); setOption(level, option, (&value.clinger)[0 .. 1]); } @@ -3170,6 +3197,7 @@ public: * Example: * --- * import std.datetime; + * import std.typecons; * auto pair = socketPair(); * scope(exit) foreach (s; pair) s.close(); * @@ -3178,7 +3206,7 @@ public: * pair[0].setOption(SocketOptionLevel.SOCKET, * SocketOption.RCVTIMEO, dur!"seconds"(1)); * - * auto sw = StopWatch(AutoStart.yes); + * auto sw = StopWatch(Yes.autoStart); * ubyte[1] buffer; * pair[0].receive(buffer); * writefln("Waited %s ms until the socket timed out.", @@ -3195,6 +3223,8 @@ public: version (Windows) { + import std.algorithm.comparison : max; + auto msecs = to!int(value.total!"msecs"); if (msecs != 0 && option == SocketOption.RCVTIMEO) msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW); @@ -3209,8 +3239,10 @@ public: else static assert(false); } - /// Get a text description of this socket's error status, and clear the - /// socket's error status. + /** + * Get a text description of this socket's error status, and clear the + * socket's error status. + */ string getErrorText() { int32_t error; @@ -3249,8 +3281,8 @@ public: else static if (is(typeof(TCP_KEEPIDLE)) && is(typeof(TCP_KEEPINTVL))) { - setOption(SocketOptionLevel.TCP, cast(SocketOption)TCP_KEEPIDLE, time); - setOption(SocketOptionLevel.TCP, cast(SocketOption)TCP_KEEPINTVL, interval); + setOption(SocketOptionLevel.TCP, cast(SocketOption) TCP_KEEPIDLE, time); + setOption(SocketOptionLevel.TCP, cast(SocketOption) TCP_KEEPINTVL, interval); setOption(SocketOptionLevel.SOCKET, SocketOption.KEEPALIVE, true); } else @@ -3270,18 +3302,21 @@ public: * connection is established and it's able to send. For a listening socket, * a read status change means there is an incoming connection request and * it's able to accept. + * + * `SocketSet`'s updated to include only those sockets which an event occured. + * For a `connect()`ing socket, writeability means connected. + * For a `listen()`ing socket, readability means listening + * `Winsock`; possibly internally limited to 64 sockets per set. + * + * Returns: + * the number of events, 0 on timeout, or -1 on interruption */ - //SocketSet's updated to include only those sockets which an event occured - //returns the number of events, 0 on timeout, or -1 on interruption - //for a connect()ing socket, writeability means connected - //for a listen()ing socket, readability means listening - //Winsock: possibly internally limited to 64 sockets per set static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, Duration timeout) @trusted { auto vals = timeout.split!("seconds", "usecs")(); TimeVal tv; - tv.seconds = cast(tv.tv_sec_t )vals.seconds; - tv.microseconds = cast(tv.tv_usec_t)vals.usecs; + tv.seconds = cast(tv.tv_sec_t ) vals.seconds; + tv.microseconds = cast(tv.tv_usec_t) vals.usecs; return select(checkRead, checkWrite, checkError, &tv); } @@ -3297,12 +3332,12 @@ public: in { //make sure none of the SocketSet's are the same object - if(checkRead) + if (checkRead) { assert(checkRead !is checkWrite); assert(checkRead !is checkError); } - if(checkWrite) + if (checkWrite) { assert(checkWrite !is checkError); } @@ -3321,7 +3356,7 @@ public: } else { - if(checkRead) + if (checkRead) { fr = checkRead.toFd_set(); n = checkRead.selectn(); @@ -3331,12 +3366,12 @@ public: fr = null; } - if(checkWrite) + if (checkWrite) { fw = checkWrite.toFd_set(); int _n; _n = checkWrite.selectn(); - if(_n > n) + if (_n > n) n = _n; } else @@ -3344,12 +3379,12 @@ public: fw = null; } - if(checkError) + if (checkError) { fe = checkError.toFd_set(); int _n; _n = checkError.selectn(); - if(_n > n) + if (_n > n) n = _n; } else @@ -3368,12 +3403,12 @@ public: version(Windows) { - if(_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR) + if (_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR) return -1; } else version(Posix) { - if(_SOCKET_ERROR == result && errno == EINTR) + if (_SOCKET_ERROR == result && errno == EINTR) return -1; } else @@ -3381,30 +3416,29 @@ public: static assert(0); } - if(_SOCKET_ERROR == result) + if (_SOCKET_ERROR == result) throw new SocketOSException("Socket select error"); return result; } - // Explicitly undocumented. It will be removed in December 2014. - deprecated("Please use the overload of select which takes a Duration instead.") - static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, long microseconds) @trusted - { - TimeVal tv; - tv.seconds = to!(tv.tv_sec_t )(microseconds / 1_000_000); - tv.microseconds = to!(tv.tv_usec_t)(microseconds % 1_000_000); - return select(checkRead, checkWrite, checkError, &tv); - } - - /// Returns a new Address object for the current address family. - /// Can be overridden to support other addresses. + /** + * Can be overridden to support other addresses. + * Returns: a new `Address` object for the current address family. + */ protected Address createAddress() pure nothrow { Address result; - switch(_family) + switch (_family) { + static if (is(sockaddr_un)) + { + case AddressFamily.UNIX: + result = new UnixAddress; + break; + } + case AddressFamily.INET: result = new InternetAddress; break; @@ -3465,25 +3499,60 @@ class UdpSocket: Socket } } +// Issue 16514 +@safe unittest +{ + class TestSocket : Socket + { + override + { + const pure nothrow @nogc @property @safe socket_t handle() { assert(0); } + const nothrow @nogc @property @trusted bool blocking() { assert(0); } + @property @trusted void blocking(bool byes) { assert(0); } + @property @safe AddressFamily addressFamily() { assert(0); } + const @property @trusted bool isAlive() { assert(0); } + @trusted void bind(Address addr) { assert(0); } + @trusted void connect(Address to) { assert(0); } + @trusted void listen(int backlog) { assert(0); } + protected pure nothrow @safe Socket accepting() { assert(0); } + @trusted Socket accept() { assert(0); } + nothrow @nogc @trusted void shutdown(SocketShutdown how) { assert(0); } + nothrow @nogc @trusted void close() { assert(0); } + @property @trusted Address remoteAddress() { assert(0); } + @property @trusted Address localAddress() { assert(0); } + @trusted ptrdiff_t send(const(void)[] buf, SocketFlags flags) { assert(0); } + @safe ptrdiff_t send(const(void)[] buf) { assert(0); } + @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) { assert(0); } + @safe ptrdiff_t sendTo(const(void)[] buf, Address to) { assert(0); } + @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags) { assert(0); } + @safe ptrdiff_t sendTo(const(void)[] buf) { assert(0); } + @trusted ptrdiff_t receive(void[] buf, SocketFlags flags) { assert(0); } + @safe ptrdiff_t receive(void[] buf) { assert(0); } + @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) { assert(0); } + @safe ptrdiff_t receiveFrom(void[] buf, ref Address from) { assert(0); } + @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags) { assert(0); } + @safe ptrdiff_t receiveFrom(void[] buf) { assert(0); } + @trusted int getOption(SocketOptionLevel level, SocketOption option, void[] result) { assert(0); } + @trusted int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) { assert(0); } + @trusted int getOption(SocketOptionLevel level, SocketOption option, out Linger result) { assert(0); } + @trusted void getOption(SocketOptionLevel level, SocketOption option, out Duration result) { assert(0); } + @trusted void setOption(SocketOptionLevel level, SocketOption option, void[] value) { assert(0); } + @trusted void setOption(SocketOptionLevel level, SocketOption option, int32_t value) { assert(0); } + @trusted void setOption(SocketOptionLevel level, SocketOption option, Linger value) { assert(0); } + @trusted void setOption(SocketOptionLevel level, SocketOption option, Duration value) { assert(0); } + @safe string getErrorText() { assert(0); } + @trusted void setKeepAlive(int time, int interval) { assert(0); } + protected pure nothrow @safe Address createAddress() { assert(0); } + } + } +} + /** * Creates a pair of connected sockets. * * The two sockets are indistinguishable. * * Throws: $(D SocketException) if creation of the sockets fails. - * - * Example: - * --- - * immutable ubyte[] data = [1, 2, 3, 4]; - * auto pair = socketPair(); - * scope(exit) foreach (s; pair) s.close(); - * - * pair[0].send(data); - * - * auto buf = new ubyte[data.length]; - * pair[1].receive(buf); - * assert(buf == data); - * --- */ Socket[2] socketPair() @trusted { @@ -3496,7 +3565,7 @@ Socket[2] socketPair() @trusted Socket toSocket(size_t id) { auto s = new Socket; - s.setSock(cast(socket_t)socks[id]); + s.setSock(cast(socket_t) socks[id]); s._family = AddressFamily.UNIX; return s; } @@ -3525,7 +3594,8 @@ Socket[2] socketPair() @trusted static assert(false); } -unittest +/// +@safe unittest { immutable ubyte[] data = [1, 2, 3, 4]; auto pair = socketPair(); diff --git a/std/socketstream.d b/std/socketstream.d deleted file mode 100644 index e89358eb6a4..00000000000 --- a/std/socketstream.d +++ /dev/null @@ -1,149 +0,0 @@ -// Written in the D programming language - -/* - Copyright (C) 2004 Christopher E. Miller - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -/************** - * $(RED Warning: This module is considered out-dated and not up to Phobos' - * current standards. It will remain until we have a suitable replacement, - * but be aware that it will not remain long term.) - * - * $(D SocketStream) is a stream for a blocking, - * connected $(D Socket). - * - * Example: - * See $(SAMPLESRC htmlget.d) - * Authors: Christopher E. Miller - * References: - * $(LINK2 std_stream.html, std.stream) - * Source: $(PHOBOSSRC std/_socketstream.d) - * Macros: WIKI=Phobos/StdSocketstream - */ - -module std.socketstream; - -private import std.stream; -private import std.socket; - -/************** - * $(D SocketStream) is a stream for a blocking, - * connected $(D Socket). - */ -class SocketStream: Stream -{ - private: - Socket sock; - - public: - - /** - * Constructs a SocketStream with the specified Socket and FileMode flags. - */ - this(Socket sock, FileMode mode) - { - if(mode & FileMode.In) - readable = true; - if(mode & FileMode.Out) - writeable = true; - - this.sock = sock; - } - - /** - * Uses mode $(D FileMode.In | FileMode.Out). - */ - this(Socket sock) - { - writeable = readable = true; - this.sock = sock; - } - - /** - * Property to get the $(D Socket) that is being streamed. - */ - Socket socket() - { - return sock; - } - - /** - * Attempts to read the entire block, waiting if necessary. - */ - override size_t readBlock(void* _buffer, size_t size) - { - ubyte* buffer = cast(ubyte*)_buffer; - assertReadable(); - - if (size == 0) - return size; - - auto len = sock.receive(buffer[0 .. size]); - readEOF = cast(bool)(len == 0); - if (len == sock.ERROR) - len = 0; - return len; - } - - /** - * Attempts to write the entire block, waiting if necessary. - */ - override size_t writeBlock(const void* _buffer, size_t size) - { - ubyte* buffer = cast(ubyte*)_buffer; - assertWriteable(); - - if (size == 0) - return size; - - auto len = sock.send(buffer[0 .. size]); - readEOF = cast(bool)(len == 0); - if (len == sock.ERROR) - len = 0; - return len; - } - - /** - * Socket streams do not support seeking. This disabled method throws - * a $(D SeekException). - */ - @disable override ulong seek(long offset, SeekPos whence) - { - throw new SeekException("Cannot seek a socket."); - } - - /** - * Does not return the entire stream because that would - * require the remote connection to be closed. - */ - override string toString() - { - return sock.toString(); - } - - /** - * Close the $(D Socket). - */ - override void close() - { - sock.close(); - super.close(); - } -} - diff --git a/std/stdint.d b/std/stdint.d index 2d09658d352..b4a5ff918a5 100644 --- a/std/stdint.d +++ b/std/stdint.d @@ -9,115 +9,116 @@ vs efficiency, in a manner compatible with the <tt>stdint.h</tt> definitions in C. - The exact aliases are types of exactly the specified number of bits. - The at least aliases are at least the specified number of bits + In the table below, the $(B exact alias)es are types of exactly the + specified number of bits. + The $(B at least alias)es are at least the specified number of bits large, and can be larger. - The fast aliases are the fastest integral type supported by the + The $(B fast alias)es are the fastest integral type supported by the processor that is at least as wide as the specified number of bits. The aliases are: - <table border=1 cellspacing=0 cellpadding=5> - <th>Exact Alias - <th>Description - <th>At Least Alias - <th>Description - <th>Fast Alias - <th>Description - <tr> - <td>int8_t - <td>exactly 8 bits signed - <td>int_least8_t - <td>at least 8 bits signed - <td>int_fast8_t - <td>fast 8 bits signed - <tr> - <td>uint8_t - <td>exactly 8 bits unsigned - <td>uint_least8_t - <td>at least 8 bits unsigned - <td>uint_fast8_t - <td>fast 8 bits unsigned + $(ATABLE $(TR + $(TH Exact Alias) + $(TH Description) + $(TH At Least Alias) + $(TH Description) + $(TH Fast Alias) + $(TH Description) + )$(TR + $(TD int8_t) + $(TD exactly 8 bits signed) + $(TD int_least8_t) + $(TD at least 8 bits signed) + $(TD int_fast8_t) + $(TD fast 8 bits signed) + )$(TR + $(TD uint8_t) + $(TD exactly 8 bits unsigned) + $(TD uint_least8_t) + $(TD at least 8 bits unsigned) + $(TD uint_fast8_t) + $(TD fast 8 bits unsigned) - <tr> - <td>int16_t - <td>exactly 16 bits signed - <td>int_least16_t - <td>at least 16 bits signed - <td>int_fast16_t - <td>fast 16 bits signed - <tr> - <td>uint16_t - <td>exactly 16 bits unsigned - <td>uint_least16_t - <td>at least 16 bits unsigned - <td>uint_fast16_t - <td>fast 16 bits unsigned + )$(TR + $(TD int16_t) + $(TD exactly 16 bits signed) + $(TD int_least16_t) + $(TD at least 16 bits signed) + $(TD int_fast16_t) + $(TD fast 16 bits signed) + )$(TR + $(TD uint16_t) + $(TD exactly 16 bits unsigned) + $(TD uint_least16_t) + $(TD at least 16 bits unsigned) + $(TD uint_fast16_t) + $(TD fast 16 bits unsigned) - <tr> - <td>int32_t - <td>exactly 32 bits signed - <td>int_least32_t - <td>at least 32 bits signed - <td>int_fast32_t - <td>fast 32 bits signed - <tr> - <td>uint32_t - <td>exactly 32 bits unsigned - <td>uint_least32_t - <td>at least 32 bits unsigned - <td>uint_fast32_t - <td>fast 32 bits unsigned + )$(TR + $(TD int32_t) + $(TD exactly 32 bits signed) + $(TD int_least32_t) + $(TD at least 32 bits signed) + $(TD int_fast32_t) + $(TD fast 32 bits signed) + )$(TR + $(TD uint32_t) + $(TD exactly 32 bits unsigned) + $(TD uint_least32_t) + $(TD at least 32 bits unsigned) + $(TD uint_fast32_t) + $(TD fast 32 bits unsigned) - <tr> - <td>int64_t - <td>exactly 64 bits signed - <td>int_least64_t - <td>at least 64 bits signed - <td>int_fast64_t - <td>fast 64 bits signed - <tr> - <td>uint64_t - <td>exactly 64 bits unsigned - <td>uint_least64_t - <td>at least 64 bits unsigned - <td>uint_fast64_t - <td>fast 64 bits unsigned - </table> + )$(TR + $(TD int64_t) + $(TD exactly 64 bits signed) + $(TD int_least64_t) + $(TD at least 64 bits signed) + $(TD int_fast64_t) + $(TD fast 64 bits signed) + )$(TR + $(TD uint64_t) + $(TD exactly 64 bits unsigned) + $(TD uint_least64_t) + $(TD at least 64 bits unsigned) + $(TD uint_fast64_t) + $(TD fast 64 bits unsigned) + )) The ptr aliases are integral types guaranteed to be large enough to hold a pointer without losing bits: - <table border=1 cellspacing=0 cellpadding=5> - <th>Alias - <th>Description - <tr> - <td>intptr_t - <td>signed integral type large enough to hold a pointer - <tr> - <td>uintptr_t - <td>unsigned integral type large enough to hold a pointer - </table> + $(ATABLE $(TR + $(TH Alias) + $(TH Description) + )$(TR + $(TD intptr_t) + $(TD signed integral type large enough to hold a pointer) + )$(TR + $(TD uintptr_t) + $(TD unsigned integral type large enough to hold a pointer) + )) The max aliases are the largest integral types: - <table border=1 cellspacing=0 cellpadding=5> - <th>Alias - <th>Description - <tr> - <td>intmax_t - <td>the largest signed integral type - <tr> - <td>uintmax_t - <td>the largest unsigned integral type - </table> + $(ATABLE $(TR + $(TH Alias) + $(TH Description) + )$(TR + $(TD intmax_t) + $(TD the largest signed integral type) + )$(TR + $(TD uintmax_t) + $(TD the largest unsigned integral type) + )) * Macros: - * WIKI=Phobos/StdStdint + * ATABLE=<table border="1" cellspacing="0" cellpadding="5">$0</table> * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Source: $(PHOBOSSRC std/_stdint.d) */ /* Copyright Digital Mars 2000 - 2009. diff --git a/std/stdio.d b/std/stdio.d index e5aaafc2886..9da7656a4f9 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -5,24 +5,23 @@ Standard I/O functions that extend $(B core.stdc.stdio). $(B core.stdc.stdio) is $(D_PARAM public)ally imported when importing $(B std.stdio). Source: $(PHOBOSSRC std/_stdio.d) -Macros: -WIKI=Phobos/StdStdio - Copyright: Copyright Digital Mars 2007-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB digitalmars.com, Walter Bright), - $(WEB erdani.org, Andrei Alexandrescu), +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP digitalmars.com, Walter Bright), + $(HTTP erdani.org, Andrei Alexandrescu), Alex Rønne Petersen */ module std.stdio; public import core.stdc.stdio; -import std.typecons;// Flag +import core.stdc.stddef; // wchar_t +import std.algorithm.mutation; // copy +import std.meta; // allSatisfy +import std.range.primitives; // ElementEncodingType, empty, front, + // isBidirectionalRange, isInputRange, put import std.stdiobase; -import core.stdc.stddef;// wchar_t -import std.range.primitives;// empty, front, isBidirectionalRange -import std.traits;// Unqual, isSomeChar, isSomeString - +import std.traits; // isSomeChar, isSomeString, Unqual, isPointer +import std.typecons; // Flag /++ If flag $(D KeepTerminator) is set to $(D KeepTerminator.yes), then the delimiter @@ -40,7 +39,7 @@ else version (CRuntime_DigitalMars) version = DIGITAL_MARS_STDIO; } -version (linux) +version (CRuntime_Glibc) { // Specific to the way Gnu C does stdio version = GCC_IO; @@ -59,18 +58,36 @@ version (FreeBSD) version = HAS_GETDELIM; } +version (NetBSD) +{ + version = GENERIC_IO; + version = HAS_GETDELIM; +} + version (Solaris) { version = GENERIC_IO; version = NO_GETDELIM; } -version (Android) +version (CRuntime_Bionic) { version = GENERIC_IO; version = NO_GETDELIM; } +// Character type used for operating system filesystem APIs +version (Windows) +{ + private alias FSChar = wchar; +} +else version (Posix) +{ + private alias FSChar = char; +} +else + static assert(0); + version(Windows) { // core.stdc.stdio.fopen expects file names to be @@ -78,6 +95,7 @@ version(Windows) /+ Waiting for druntime pull 299 +/ extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode); + extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp); import core.sys.windows.windows : HANDLE; } @@ -133,6 +151,8 @@ else version (MICROSOFT_STDIO) int _setmode(int, int); int _fileno(FILE*); FILE* _fdopen(int, const (char)*); + int _fseeki64(FILE*, long, int); + long _ftelli64(FILE*); } alias FPUTC = _fputc_nolock; alias FPUTWC = _fputwc_nolock; @@ -142,15 +162,13 @@ else version (MICROSOFT_STDIO) alias FLOCK = _lock_file; alias FUNLOCK = _unlock_file; + alias setmode = _setmode; + alias fileno = _fileno; + enum { _O_RDONLY = 0x0000, - _O_WRONLY = 0x0001, - _O_RDWR = 0x0002, _O_APPEND = 0x0004, - _O_CREAT = 0x0100, - _O_TRUNC = 0x0200, - _O_EXCL = 0x0400, _O_TEXT = 0x4000, _O_BINARY = 0x8000, } @@ -268,7 +286,7 @@ public: import std.format : formattedRead; import std.string : chomp; - enforce(file.isOpen); + enforce(file.isOpen, "ByRecord: File must be open"); file.readln(line); if (!line.length) { @@ -304,18 +322,10 @@ manner, such that as soon as the last $(D File) variable bound to a given $(D FILE*) goes out of scope, the underlying $(D FILE*) is automatically closed. -Bugs: -$(D File) expects file names to be encoded in $(B CP_ACP) on $(I Windows) -instead of UTF-8 ($(BUGZILLA 7648)) thus must not be used in $(I Windows) -or cross-platform applications other than with an immediate ASCII string as -a file name to prevent accidental changes to result in incorrect behavior. -One can use $(XREF file, read)/$(XREF file, write)/$(XREF stream, _File) -instead. - Example: ---- // test.d -void main(string args[]) +void main(string[] args) { auto f = File("test.txt", "w"); // open for writing f.write("Hello"); @@ -331,12 +341,12 @@ void main(string args[]) // underlying $(D FILE*) is closed. } ---- -<pre class=console> +$(CONSOLE % rdmd test.d Jimmy % cat test.txt Hello, Jimmy! % __ -</pre> +) */ struct File { @@ -369,10 +379,7 @@ struct File } /** -Constructor taking the name of the file to open and the open mode -(with the same semantics as in the C standard library $(WEB -cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) -function). +Constructor taking the name of the file to open and the open mode. Copying one $(D File) object to another results in the two $(D File) objects referring to the same underlying file. @@ -380,6 +387,13 @@ objects referring to the same underlying file. The destructor automatically closes the file as soon as no $(D File) object refers to it anymore. +Params: + name = range or string representing the file _name + stdioOpenmode = range or string represting the open mode + (with the same semantics as in the C standard library + $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) + function) + Throws: $(D ErrnoException) if the file could not be opened. */ this(string name, in char[] stdioOpenmode = "rb") @safe @@ -391,6 +405,47 @@ Throws: $(D ErrnoException) if the file could not be opened. text("Cannot open file `", name, "' in mode `", stdioOpenmode, "'")), name); + + // MSVCRT workaround (issue 14422) + version (MICROSOFT_STDIO) + { + bool append, update; + foreach (c; stdioOpenmode) + if (c == 'a') + append = true; + else + if (c == '+') + update = true; + if (append && !update) + seek(size); + } + } + + /// ditto + this(R1, R2)(R1 name) + if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1)) + { + import std.conv : to; + this(name.to!string, "rb"); + } + + /// ditto + this(R1, R2)(R1 name, R2 mode) + if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) && + isInputRange!R2 && isSomeChar!(ElementEncodingType!R2)) + { + import std.conv : to; + this(name.to!string, mode.to!string); + } + + @safe unittest + { + static import std.file; + import std.utf : byChar; + auto deleteme = testFilename(); + auto f = File(deleteme.byChar, "w".byChar); + f.close(); + std.file.remove(deleteme); } ~this() @safe @@ -412,7 +467,7 @@ file. */ void opAssign(File rhs) @safe { - import std.algorithm : swap; + import std.algorithm.mutation : swap; swap(this, rhs); } @@ -420,7 +475,7 @@ file. /** First calls $(D detach) (throwing on failure), and then attempts to _open file $(D name) with mode $(D stdioOpenmode). The mode has the -same semantics as in the C standard library $(WEB +same semantics as in the C standard library $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function. Throws: $(D ErrnoException) in case of error. @@ -431,9 +486,89 @@ Throws: $(D ErrnoException) in case of error. this = File(name, stdioOpenmode); } +/** +Reuses the `File` object to either open a different file, or change +the file mode. If `name` is `null`, the mode of the currently open +file is changed; otherwise, a new file is opened, reusing the C +`FILE*`. The function has the same semantics as in the C standard +library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen) +function. + +Note: Calling `reopen` with a `null` `name` is not implemented +in all C runtimes. + +Throws: $(D ErrnoException) in case of error. + */ + void reopen(string name, in char[] stdioOpenmode = "rb") @trusted + { + import std.internal.cstring : tempCString; + import std.exception : enforce, errnoEnforce; + import std.conv : text; + + enforce(isOpen, "Attempting to reopen() an unopened file"); + + auto namez = (name == null ? _name : name).tempCString!FSChar(); + auto modez = stdioOpenmode.tempCString!FSChar(); + + FILE* fd = _p.handle; + version (Windows) + fd = _wfreopen(namez, modez, fd); + else + fd = freopen(namez, modez, fd); + + errnoEnforce(fd, name + ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'") + : text("Cannot reopen file in mode `", stdioOpenmode, "'")); + + if (name !is null) + _name = name; + } + + @system unittest // Test changing filename + { + static import std.file; + import std.exception : assertThrown, assertNotThrown; + + auto deleteme = testFilename(); + std.file.write(deleteme, "foo"); + scope(exit) std.file.remove(deleteme); + auto f = File(deleteme); + assert(f.readln() == "foo"); + + auto deleteme2 = testFilename(); + std.file.write(deleteme2, "bar"); + scope(exit) std.file.remove(deleteme2); + f.reopen(deleteme2); + assert(f.name == deleteme2); + assert(f.readln() == "bar"); + f.close(); + } + + version (CRuntime_DigitalMars) {} else // Not implemented + version (CRuntime_Microsoft) {} else // Not implemented + @system unittest // Test changing mode + { + static import std.file; + import std.exception : assertThrown, assertNotThrown; + + auto deleteme = testFilename(); + std.file.write(deleteme, "foo"); + scope(exit) std.file.remove(deleteme); + auto f = File(deleteme, "r+"); + assert(f.readln() == "foo"); + f.reopen(null, "w"); + f.write("bar"); + f.seek(0); + f.reopen(null, "a"); + f.write("baz"); + assert(f.name == deleteme); + f.close(); + assert(std.file.readText(deleteme) == "barbaz"); + } + /** First calls $(D detach) (throwing on failure), and then runs a command -by calling the C standard library function $(WEB +by calling the C standard library function $(HTTP opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen). Throws: $(D ErrnoException) in case of error. @@ -462,9 +597,10 @@ Throws: $(D ErrnoException) in case of error. package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted { - import std.internal.cstring : tempCString; import std.exception : errnoEnforce; + import std.internal.cstring : tempCString; + auto modez = stdioOpenmode.tempCString(); detach(); version (DIGITAL_MARS_STDIO) @@ -474,10 +610,10 @@ Throws: $(D ErrnoException) in case of error. // new fdopen'd file to retain the given file descriptor's // position. import core.stdc.stdio : fopen; - auto fp = fopen("NUL", stdioOpenmode.tempCString()); + auto fp = fopen("NUL", modez); errnoEnforce(fp, "Cannot open placeholder NUL stream"); FLOCK(fp); - auto iob = cast(_iobuf*)fp; + auto iob = cast(_iobuf*) fp; .close(iob._file); iob._file = fd; iob._flag &= ~_IOTRAN; @@ -486,11 +622,11 @@ Throws: $(D ErrnoException) in case of error. else { version (Windows) // MSVCRT - auto fp = _fdopen(fd, stdioOpenmode.tempCString()); + auto fp = _fdopen(fd, modez); else version (Posix) { import core.sys.posix.stdio : fdopen; - auto fp = fdopen(fd, stdioOpenmode.tempCString()); + auto fp = fdopen(fd, modez); } errnoEnforce(fp); } @@ -516,6 +652,7 @@ Throws: $(D ErrnoException) in case of error. { import std.exception : errnoEnforce; import std.format : format; + import core.stdc.stdint : intptr_t; // Create file descriptors from the handles version (DIGITAL_MARS_STDIO) @@ -536,7 +673,7 @@ Throws: $(D ErrnoException) in case of error. default: break; } - auto fd = _open_osfhandle(cast(intptr_t)handle, mode); + auto fd = _open_osfhandle(cast(intptr_t) handle, mode); } errnoEnforce(fd >= 0, "Cannot open Windows HANDLE"); @@ -551,7 +688,7 @@ Throws: $(D ErrnoException) in case of error. } /** -Returns $(D true) if the file is at end (see $(WEB +Returns $(D true) if the file is at end (see $(HTTP cplusplus.com/reference/clibrary/cstdio/feof.html, feof)). Throws: $(D Exception) if the file is not opened. @@ -573,8 +710,8 @@ it has no name.*/ } /** -If the file is not opened, returns $(D false). Otherwise, returns -$(WEB cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for +If the file is not opened, returns $(D true). Otherwise, returns +$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for the file handle. */ @property bool error() const @trusted pure nothrow @@ -629,7 +766,7 @@ Throws: $(D ErrnoException) on failure if closing the file. /** If the file was unopened, succeeds vacuously. Otherwise closes the -file (by calling $(WEB +file (by calling $(HTTP cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)), throwing on error. Even if an exception is thrown, afterwards the $(D File) object is empty. This is different from $(D detach) in that it @@ -647,7 +784,7 @@ Throws: $(D ErrnoException) on error. scope(exit) { assert(_p.refs); - if(!--_p.refs) + if (!--_p.refs) free(_p); _p = null; // start a new life } @@ -656,8 +793,8 @@ Throws: $(D ErrnoException) on error. scope(exit) _p.handle = null; // nullify the handle anyway version (Posix) { - import std.format : format; import core.sys.posix.stdio : pclose; + import std.format : format; if (_p.isPopened) { @@ -668,14 +805,13 @@ Throws: $(D ErrnoException) on error. return; } } - //fprintf(core.stdc.stdio.stderr, ("Closing file `"~name~"`.\n\0").ptr); errnoEnforce(.fclose(_p.handle) == 0, "Could not close file `"~_name~"'"); } /** If the file is not opened, succeeds vacuously. Otherwise, returns -$(WEB cplusplus.com/reference/clibrary/cstdio/_clearerr.html, +$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html, _clearerr) for the file handle. */ void clearerr() @safe pure nothrow @@ -685,7 +821,9 @@ _clearerr) for the file handle. } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush) +Flushes the C $(D FILE) buffers. + +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush) for the file handle. Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) fails. @@ -702,7 +840,7 @@ Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) f { // Issue 12349 static import std.file; - import std.exception: assertThrown; + import std.exception : assertThrown; auto deleteme = testFilename(); auto f = File(deleteme, "w"); @@ -713,7 +851,38 @@ Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) f } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the +Forces any data buffered by the OS to be written to disk. +Call $(LREF flush) before calling this function to flush the C $(D FILE) buffers first. + +This function calls +$(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx, +$(D FlushFileBuffers)) on Windows and +$(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html, +$(D fsync)) on POSIX for the file handle. + +Throws: $(D Exception) if the file is not opened or if the OS call fails. + */ + void sync() @trusted + { + import std.exception : enforce; + + enforce(isOpen, "Attempting to sync() an unopened file"); + + version (Windows) + { + import core.sys.windows.windows : FlushFileBuffers; + wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed"); + } + else + { + import core.sys.posix.unistd : fsync; + import std.exception : errnoEnforce; + errnoEnforce(fsync(fileno) == 0, "fsync failed"); + } + } + +/** +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the file handle. The number of items to read and the size of each item is inferred from the size and type of the input array, respectively. @@ -728,7 +897,7 @@ $(D rawRead) always reads in binary mode on Windows. */ T[] rawRead(T)(T[] buffer) { - import std.exception : Exception, errnoEnforce; + import std.exception : errnoEnforce; if (!buffer.length) throw new Exception("rawRead must take a non-empty buffer"); @@ -739,7 +908,7 @@ $(D rawRead) always reads in binary mode on Windows. scope(exit) ._setmode(fd, mode); version(DIGITAL_MARS_STDIO) { - import core.atomic; + import core.atomic : atomicOp; // @@@BUG@@@ 4243 immutable info = __fhnd_info[fd]; @@ -747,9 +916,8 @@ $(D rawRead) always reads in binary mode on Windows. scope(exit) __fhnd_info[fd] = info; } } - immutable freadResult = - fread(buffer.ptr, T.sizeof, buffer.length, _p.handle); - assert (freadResult <= buffer.length); // fread return guarantee + immutable freadResult = trustedFread(_p.handle, buffer); + assert(freadResult <= buffer.length); // fread return guarantee if (freadResult != buffer.length) // error or eof { errnoEnforce(!error); @@ -758,25 +926,23 @@ $(D rawRead) always reads in binary mode on Windows. return buffer; } - unittest + /// + @system unittest { static import std.file; - auto deleteme = testFilename(); - std.file.write(deleteme, "\r\n\n\r\n"); - scope(exit) std.file.remove(deleteme); - auto f = File(deleteme, "r"); + auto testFile = testFilename(); + std.file.write(testFile, "\r\n\n\r\n"); + scope(exit) std.file.remove(testFile); + + auto f = File(testFile, "r"); auto buf = f.rawRead(new char[5]); f.close(); assert(buf == "\r\n\n\r\n"); - /+ - buf = stdin.rawRead(new char[5]); - assert(buf == "\r\n\n\r\n"); - +/ } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file handle. The number of items to write and the size of each item is inferred from the size and type of the input array, respectively. An error is thrown if the buffer could not be written in its entirety. @@ -798,7 +964,7 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri scope(exit) ._setmode(fd, mode); version(DIGITAL_MARS_STDIO) { - import core.atomic; + import core.atomic : atomicOp; // @@@BUG@@@ 4243 immutable info = __fhnd_info[fd]; @@ -807,8 +973,7 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri } scope(exit) flush(); // before restoring translation mode } - auto result = - .fwrite(buffer.ptr, T.sizeof, buffer.length, _p.handle); + auto result = trustedFwrite(_p.handle, buffer); if (result == result.max) result = 0; errnoEnforce(result == buffer.length, text("Wrote ", result, " instead of ", buffer.length, @@ -816,20 +981,22 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri _name, "'")); } - unittest + /// + @system unittest { static import std.file; - auto deleteme = testFilename(); - auto f = File(deleteme, "w"); - scope(exit) std.file.remove(deleteme); + auto testFile = testFilename(); + auto f = File(testFile, "w"); + scope(exit) std.file.remove(testFile); + f.rawWrite("\r\n\n\r\n"); f.close(); - assert(std.file.read(deleteme) == "\r\n\n\r\n"); + assert(std.file.read(testFile) == "\r\n\n\r\n"); } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek) +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek) for the file handle. Throws: $(D Exception) if the file is not opened. @@ -837,27 +1004,36 @@ Throws: $(D Exception) if the file is not opened. */ void seek(long offset, int origin = SEEK_SET) @trusted { - import std.exception : enforce, errnoEnforce; import std.conv : to, text; + import std.exception : enforce, errnoEnforce; enforce(isOpen, "Attempting to seek() in an unopened file"); version (Windows) { - errnoEnforce(fseek(_p.handle, to!int(offset), origin) == 0, - "Could not seek in file `"~_name~"'"); + version (CRuntime_Microsoft) + { + alias fseekFun = _fseeki64; + alias off_t = long; + } + else + { + alias fseekFun = fseek; + alias off_t = int; + } } else version (Posix) { import core.sys.posix.stdio : fseeko, off_t; - //static assert(off_t.sizeof == 8); - errnoEnforce(fseeko(_p.handle, cast(off_t) offset, origin) == 0, - "Could not seek in file `"~_name~"'"); + alias fseekFun = fseeko; } + errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0, + "Could not seek in file `"~_name~"'"); } - unittest + @system unittest { static import std.file; + import std.conv : text; auto deleteme = testFilename(); auto f = File(deleteme, "w+"); @@ -865,30 +1041,25 @@ Throws: $(D Exception) if the file is not opened. f.rawWrite("abcdefghijklmnopqrstuvwxyz"); f.seek(7); assert(f.readln() == "hijklmnopqrstuvwxyz"); - version (Windows) - { - // No test for large files yet - } - else - { - import std.conv : text; - version (Android) - auto bigOffset = int.max - 100; - else - auto bigOffset = cast(ulong) int.max + 100; - f.seek(bigOffset); - assert(f.tell == bigOffset, text(f.tell)); - // Uncomment the tests below only if you want to wait for - // a long time - // f.rawWrite("abcdefghijklmnopqrstuvwxyz"); - // f.seek(-3, SEEK_END); - // assert(f.readln() == "xyz"); - } + version (CRuntime_DigitalMars) + auto bigOffset = int.max - 100; + else + version (CRuntime_Bionic) + auto bigOffset = int.max - 100; + else + auto bigOffset = cast(ulong) int.max + 100; + f.seek(bigOffset); + assert(f.tell == bigOffset, text(f.tell)); + // Uncomment the tests below only if you want to wait for + // a long time + // f.rawWrite("abcdefghijklmnopqrstuvwxyz"); + // f.seek(-3, SEEK_END); + // assert(f.readln() == "xyz"); } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the managed file handle. Throws: $(D Exception) if the file is not opened. @@ -901,7 +1072,10 @@ Throws: $(D Exception) if the file is not opened. enforce(isOpen, "Attempting to tell() in an unopened file"); version (Windows) { - immutable result = ftell(cast(FILE*) _p.handle); + version (CRuntime_Microsoft) + immutable result = _ftelli64(cast(FILE*) _p.handle); + else + immutable result = ftell(cast(FILE*) _p.handle); } else version (Posix) { @@ -913,22 +1087,24 @@ Throws: $(D Exception) if the file is not opened. return result; } - unittest + /// + @system unittest { static import std.file; import std.conv : text; - auto deleteme = testFilename(); - std.file.write(deleteme, "abcdefghijklmnopqrstuvwqxyz"); - scope(exit) { std.file.remove(deleteme); } - auto f = File(deleteme); + auto testFile = testFilename(); + std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz"); + scope(exit) { std.file.remove(testFile); } + + auto f = File(testFile); auto a = new ubyte[4]; f.rawRead(a); assert(f.tell == 4, text(f.tell)); } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind) +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind) for the file handle. Throws: $(D Exception) if the file is not opened. @@ -942,7 +1118,7 @@ Throws: $(D Exception) if the file is not opened. } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for the file handle. Throws: $(D Exception) if the file is not opened. @@ -958,7 +1134,7 @@ Throws: $(D Exception) if the file is not opened. } /** -Calls $(WEB cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, +Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for the file handle. Throws: $(D Exception) if the file is not opened. @@ -977,7 +1153,7 @@ Throws: $(D Exception) if the file is not opened. version(Windows) { - import core.sys.windows.windows; + import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL; private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, Flags flags) @@ -997,7 +1173,8 @@ Throws: $(D Exception) if the file is not opened. private static T wenforce(T)(T cond, string str) { - import std.windows.syserror; + import std.windows.syserror : sysErrorString; + import core.sys.windows.windows : GetLastError; if (cond) return cond; throw new Exception(str ~ ": " ~ sysErrorString(GetLastError())); @@ -1008,9 +1185,9 @@ Throws: $(D Exception) if the file is not opened. private int lockImpl(int operation, short l_type, ulong start, ulong length) { - import std.conv : to; import core.sys.posix.fcntl : fcntl, flock, off_t; import core.sys.posix.unistd : getpid; + import std.conv : to; flock fl = void; fl.l_type = l_type; @@ -1039,11 +1216,12 @@ $(UL void lock(LockType lockType = LockType.readWrite, ulong start = 0, ulong length = 0) { - import std.exception : enforce, errnoEnforce; + import std.exception : enforce; enforce(isOpen, "Attempting to call lock() on an unopened file"); version (Posix) { + import std.exception : errnoEnforce; import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK; immutable short type = lockType == LockType.readWrite ? F_WRLCK : F_RDLCK; @@ -1053,6 +1231,7 @@ $(UL else version(Windows) { + import core.sys.windows.windows : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK; immutable type = lockType == LockType.readWrite ? LOCKFILE_EXCLUSIVE_LOCK : 0; wenforce(lockImpl!LockFileEx(start, length, type), @@ -1071,11 +1250,12 @@ specified file segment was already locked. bool tryLock(LockType lockType = LockType.readWrite, ulong start = 0, ulong length = 0) { - import std.exception : enforce, errnoEnforce; + import std.exception : enforce; enforce(isOpen, "Attempting to call tryLock() on an unopened file"); version (Posix) { + import std.exception : errnoEnforce; import core.stdc.errno : EACCES, EAGAIN, errno; import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK; immutable short type = lockType == LockType.readWrite @@ -1089,6 +1269,8 @@ specified file segment was already locked. else version(Windows) { + import core.sys.windows.windows : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK, + ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, LOCKFILE_FAIL_IMMEDIATELY; immutable type = lockType == LockType.readWrite ? LOCKFILE_EXCLUSIVE_LOCK : 0; immutable res = lockImpl!LockFileEx(start, length, @@ -1108,11 +1290,12 @@ Removes the lock over the specified file segment. */ void unlock(ulong start = 0, ulong length = 0) { - import std.exception : enforce, errnoEnforce; + import std.exception : enforce; enforce(isOpen, "Attempting to call unlock() on an unopened file"); version (Posix) { + import std.exception : errnoEnforce; import core.sys.posix.fcntl : F_SETLK, F_UNLCK; errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1, "Could not remove lock for file `"~_name~"'"); @@ -1120,6 +1303,7 @@ Removes the lock over the specified file segment. else version(Windows) { + import core.sys.windows.windows : UnlockFileEx; wenforce(lockImpl!UnlockFileEx(start, length), "Could not remove lock for file `"~_name~"'"); } @@ -1128,7 +1312,7 @@ Removes the lock over the specified file segment. } version(Windows) - unittest + @system unittest { static import std.file; auto deleteme = testFilename(); @@ -1147,7 +1331,7 @@ Removes the lock over the specified file segment. } version(Posix) - unittest + @system unittest { static import std.file; auto deleteme = testFilename(); @@ -1158,8 +1342,8 @@ Removes the lock over the specified file segment. static void runForked(void delegate() code) { import core.stdc.stdlib : exit; - import core.sys.posix.unistd; - import core.sys.posix.sys.wait; + import core.sys.posix.sys.wait : wait; + import core.sys.posix.unistd : fork; int child, status; if ((child = fork()) == 0) { @@ -1221,11 +1405,10 @@ Throws: $(D Exception) if the file is not opened. { import std.format : formattedWrite; - std.format.formattedWrite(w, "%s", arg); + formattedWrite(w, "%s", arg); } else static if (isSomeString!A) { - import std.range.primitives : put; put(w, arg); } else static if (isIntegral!A) @@ -1240,7 +1423,6 @@ Throws: $(D Exception) if the file is not opened. } else static if (isSomeChar!A) { - import std.range.primitives : put; put(w, arg); } else @@ -1248,7 +1430,7 @@ Throws: $(D Exception) if the file is not opened. import std.format : formattedWrite; // Most general case - std.format.formattedWrite(w, "%s", arg); + formattedWrite(w, "%s", arg); } } } @@ -1266,31 +1448,53 @@ Throws: $(D Exception) if the file is not opened. /** Writes its arguments in text format to the file, according to the -format in the first argument. +format string fmt. + +Params: +fmt = The $(LINK2 std_format.html#format-string, format string). +When passed as a compile-time argument, the string will be statically checked +against the argument types passed. +args = Items to write. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) on an error writing to the file. */ + void writef(alias fmt, A...)(A args) + if (isSomeString!(typeof(fmt))) + { + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return this.writef(fmt, args); + } + + /// ditto void writef(Char, A...)(in Char[] fmt, A args) { import std.format : formattedWrite; - std.format.formattedWrite(lockingTextWriter(), fmt, args); + formattedWrite(lockingTextWriter(), fmt, args); } -/** -Writes its arguments in text format to the file, according to the -format in the first argument, followed by a newline. + /// Equivalent to `file.writef(fmt, args, '\n')`. + void writefln(alias fmt, A...)(A args) + if (isSomeString!(typeof(fmt))) + { + import std.format : checkFormatException; -Throws: $(D Exception) if the file is not opened. - $(D ErrnoException) on an error writing to the file. -*/ + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return this.writefln(fmt, args); + } + + /// ditto void writefln(Char, A...)(in Char[] fmt, A args) { import std.format : formattedWrite; auto w = lockingTextWriter(); - std.format.formattedWrite(w, fmt, args); + formattedWrite(w, fmt, args); w.put('\n'); } @@ -1332,19 +1536,19 @@ void main() { Unqual!(ElementEncodingType!S)[] buf; readln(buf, terminator); - return cast(S)buf; + return cast(S) buf; } - unittest + @system unittest { static import std.file; - import std.algorithm : equal; - import std.typetuple : TypeTuple; + import std.algorithm.comparison : equal; + import std.meta : AliasSeq; auto deleteme = testFilename(); std.file.write(deleteme, "hello\nworld\n"); scope(exit) std.file.remove(deleteme); - foreach (String; TypeTuple!(string, char[], wstring, wchar[], dstring, dchar[])) + foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[])) { auto witness = [ "hello\n", "world\n" ]; auto f = File(deleteme); @@ -1359,7 +1563,7 @@ void main() } } - unittest + @system unittest { static import std.file; import std.typecons : Tuple; @@ -1389,7 +1593,7 @@ Params: buf = Buffer used to store the resulting line data. buf is resized as necessary. terminator = Line terminator (by default, $(D '\n')). Use -$(XREF ascii, newline) for portability (unless the file was opened in +$(REF newline, std,ascii) for portability (unless the file was opened in text mode). Returns: @@ -1425,6 +1629,35 @@ This method can be more efficient than the one in the previous example because $(D stdin.readln(buf)) reuses (if possible) memory allocated for $(D buf), whereas $(D line = stdin.readln()) makes a new memory allocation for every line. + +For even better performance you can help $(D readln) by passing in a +large buffer to avoid memory reallocations. This can be done by reusing the +largest buffer returned by $(D readln): + +Example: +--- +// Read lines from $(D stdin) and count words + +void main() +{ + char[] buf; + size_t words = 0; + + while (!stdin.eof) + { + char[] line = buf; + stdin.readln(line); + if (line.length > buf.length) + buf = line; + + words += line.split.length; + } + + writeln(words); +} +--- +This is actually what $(LREF byLine) does internally, so its usage +is recommended if you want to process a complete file. */ size_t readln(C)(ref C[] buf, dchar terminator = '\n') if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum)) @@ -1457,22 +1690,69 @@ for every line. } } + @system unittest + { + // @system due to readln + static import std.file; + auto deleteme = testFilename(); + std.file.write(deleteme, "123\n456789"); + scope(exit) std.file.remove(deleteme); + + auto file = File(deleteme); + char[] buffer = new char[10]; + char[] line = buffer; + file.readln(line); + auto beyond = line.length; + buffer[beyond] = 'a'; + file.readln(line); // should not write buffer beyond line + assert(buffer[beyond] == 'a'); + } + + @system unittest // bugzilla 15293 + { + // @system due to readln + static import std.file; + auto deleteme = testFilename(); + std.file.write(deleteme, "a\n\naa"); + scope(exit) std.file.remove(deleteme); + + auto file = File(deleteme); + char[] buffer; + char[] line; + + file.readln(buffer, '\n'); + + line = buffer; + file.readln(line, '\n'); + + line = buffer; + file.readln(line, '\n'); + + assert(line[0 .. 1].capacity == 0); + } + /** ditto */ size_t readln(C, R)(ref C[] buf, R terminator) if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && isBidirectionalRange!R && is(typeof(terminator.front == dchar.init))) { - import std.algorithm : endsWith, swap; + import std.algorithm.mutation : swap; + import std.algorithm.searching : endsWith; import std.range.primitives : back; auto last = terminator.back; C[] buf2; swap(buf, buf2); - for (;;) { - if (!readln(buf2, last) || endsWith(buf2, terminator)) { - if (buf.empty) { + for (;;) + { + if (!readln(buf2, last) || endsWith(buf2, terminator)) + { + if (buf.empty) + { buf = buf2; - } else { + } + else + { buf ~= buf2; } break; @@ -1482,7 +1762,7 @@ for every line. return buf.length; } - unittest + @system unittest { static import std.file; import std.typecons : Tuple; @@ -1501,16 +1781,52 @@ for every line. assert(i < witness.length); assert(buf == witness[i++]); } - assert(buf.length==0); + assert(buf.length == 0); } } /** - * Read data from the file according to the specified - * $(LINK2 std_format.html#format-string, format specifier) using - * $(XREF format,formattedRead). + * Reads formatted _data from the file using $(REF formattedRead, std,_format). + * Params: + * format = The $(LINK2 std_format.html#_format-string, _format string). + * When passed as a compile-time argument, the string will be statically checked + * against the argument types passed. + * data = Items to be read. + * Example: +---- +// test.d +void main() +{ + import std.stdio; + auto f = File("input"); + foreach (_; 0 .. 3) + { + int a; + f.readf!" %d"(a); + writeln(++a); + } +} +---- +$(CONSOLE +% echo "1 2 3" > input +% rdmd test.d +2 +3 +4 +) */ - uint readf(Data...)(in char[] format, Data data) + uint readf(alias format, Data...)(auto ref Data data) + if (isSomeString!(typeof(format))) + { + import std.format : checkFormatException; + + alias e = checkFormatException!(format, Data); + static assert(!e, e.msg); + return this.readf(format, data); + } + + /// ditto + uint readf(Data...)(in char[] format, auto ref Data data) { import std.format : formattedRead; @@ -1519,10 +1835,32 @@ for every line. return formattedRead(input, format, data); } - unittest + /// + @system unittest { static import std.file; + auto deleteme = testFilename(); + std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); + scope(exit) std.file.remove(deleteme); + string s; + auto f = File(deleteme); + f.readf!"%s\n"(s); + assert(s == "hello", "["~s~"]"); + f.readf("%s\n", s); + assert(s == "world", "["~s~"]"); + + bool b1, b2; + f.readf("%s\n%s\n", b1, b2); + assert(b1 == true && b2 == false); + } + + // backwards compatibility with pointers + @system unittest + { + // @system due to readf + static import std.file; + auto deleteme = testFilename(); std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); scope(exit) std.file.remove(deleteme); @@ -1539,12 +1877,31 @@ for every line. assert(b1 == true && b2 == false); } + // backwards compatibility (mixed) + @system unittest + { + // @system due to readf + static import std.file; + + auto deleteme = testFilename(); + std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); + scope(exit) std.file.remove(deleteme); + string s1, s2; + auto f = File(deleteme); + f.readf("%s\n%s\n", s1, &s2); + assert(s1 == "hello"); + assert(s2 == "world"); + + // Issue 11698 + bool b1, b2; + f.readf("%s\n%s\n", &b1, b2); + assert(b1 == true && b2 == false); + } + /** Returns a temporary file by calling - $(WEB cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). - - $(RED Warning: The $(LREF File) returned has an empty $(LREF name)). - */ + $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). + Note that the created file has no $(LREF name).*/ static File tmpfile() @safe { import std.exception : errnoEnforce; @@ -1554,185 +1911,6 @@ for every line. null); } -/** -Creates a file with a randomly generated name and returns an open File to it. - -There is nothing special about the file created other than the fact that its -name is randomly generated, and it defaults to being in the temp directory on -the system. It will only be deleted if explicitly deleted or if it's in the -temp directory, and the temp directory is cleared out (as happens on system -startup or shutdown on some systems). - -The file is created with R/W permissions and opened with $(D "w+b"). On POSIX -systems, the permissions are restricted to the current user, though the -effective permissions are modified by the process' umask in the usual way. - -Params: - prefix = Prefix for the generated file name. - suffix = Suffix for the generated file name (which also provides a way to - give the file an extension). - dir = Directory of the temporary file. Defaults to the result of - $(XREF file, tempDir). - -Throws: - $(D Exception) if the file could not be created. - -See_Also: - $(LREF tmpfile) - */ - static File scratchFile(const(char)[] prefix, const(char)[] suffix, const(char)[] dir) - { - import std.exception : ErrnoException; - import std.file : isSymlink; - import std.path : absolutePath, baseName, buildPath, dirName; - - static string genTempName(const(char)[] prefix, const(char)[] suffix, const(char)[] dir) - { - import std.ascii : digits, letters; - import std.random : rndGen; - import std.range : chain; - import std.string : representation; - auto name = (char[]).init ~ prefix ~ "012345678901234" ~ suffix; - - // Replace the 012345678901234 with random characters - rndGen.popFront(); - auto chars = chain(letters.representation, digits.representation); - foreach (ref c; name[prefix.length .. $ - suffix.length]) - { - c = chars[rndGen.front % chars.length]; - rndGen.popFront(); - } - - return buildPath(dir, name); - } - - // Once should be enough given the randomness, but we'll give it three - // times to be safe. - enum limit = 3; - for (int i = 0; true; ++i) - { - auto filename = genTempName(prefix, suffix, dir); - - // So that we can test the case where the file already exists. - version (unittest) - { - if (i == 0) - { - import std.file; - std.file.write(filename, "nothing"); - } - } - - // O_EXCL does not support symbolic links. - if (dirName(filename).isSymlink) - filename = buildPath(absolutePath(dirName(filename), baseName(filename))); - - import std.internal.cstring : tempCString; - version (Posix) - { - import core.sys.posix.sys.stat, core.sys.posix.fcntl; - auto fd = open(tempCString(filename), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - } - else version (Windows) - { - version (DIGITAL_MARS_STDIO) - { - enum openFlags = O_RDWR | O_CREAT | O_EXCL; - enum permissions = S_IREAD | S_IWRITE; - } - else version (MICROSOFT_STDIO) - { - enum openFlags = _O_RDWR | _O_CREAT | _O_EXCL; - enum permissions = _S_IREAD | _S_IWRITE; - } - auto fd = _wsopen(tempCString!wchar(filename), openFlags, _SH_DENYNO, permissions); - } - else static assert("Unsupported OS"); - - if (fd == -1) - { - import std.string: format; - version (unittest) - { - import std.file; - if (i == 0 && filename.exists) - { - std.file.remove(filename); - import core.stdc.errno : errno, EEXIST; - assert(errno == EEXIST, format("Error code: %s", errno)); - } - } - if (i == limit - 1) - { - auto msg = format("Failed to create temporary file after %s attempts. Last file name tried [%s].", - limit, filename); - throw new ErrnoException(msg); - } - continue; - } - - File retval; - retval.fdopen(fd, "w+b", filename); - return retval; - } - - assert(0); - } - - /// Ditto - static File scratchFile(string prefix = null, string suffix = null) - { - import std.file : tempDir; - return File.scratchFile(prefix, suffix, std.file.tempDir()); - } - - unittest - { - import std.algorithm, std.conv, std.file, std.path, std.process; - - { - string name; - scope(exit) if(name.exists) std.file.remove(name); - - { - auto file = File.scratchFile(); - name = file.name; - assert(!name.empty); - assert(buildNormalizedPath(name.dirName) == buildNormalizedPath(tempDir())); - assert(!name.baseName.empty); - assert(name.exists); - assert(name.isFile); - file.write("hello world"); - } - - assert(name.exists); - assert(name.isFile); - assert(readText(name) == "hello world"); - } - - auto dir = buildPath(tempDir(), "test_sub_dir_" ~ to!string(thisProcessID)); - mkdir(dir); - scope(exit) if(dir.exists) rmdirRecurse(dir); - string name; - { - auto file = File.scratchFile("foobar", ".baz", dir); - name = file.name; - assert(!name.empty); - assert(name.dirName == dir); - assert(buildNormalizedPath(name.dirName) == buildNormalizedPath(dir)); - assert(!name.baseName.empty); - assert(name.baseName.startsWith("foobar")); - assert(name.extension == ".baz"); - assert(name.length > "foobar".length + ".baz".length); - assert(name.exists); - assert(name.isFile); - file.write("D for the win!"); - } - assert(name.exists); - assert(name.isFile); - assert(readText(name) == "D for the win!"); - } - /** Unsafe function that wraps an existing $(D FILE*). The resulting $(D File) never takes the initiative in closing the file. @@ -1757,7 +1935,7 @@ Returns the $(D FILE*) corresponding to this object. return _p.handle; } - unittest + @system unittest { static import core.stdc.stdio; assert(stdout.getFP() == core.stdc.stdio.stdout); @@ -1766,7 +1944,7 @@ Returns the $(D FILE*) corresponding to this object. /** Returns the file number corresponding to this object. */ - /*version(Posix) */@property int fileno() const @trusted + @property int fileno() const @trusted { import std.exception : enforce; @@ -1799,7 +1977,7 @@ Allows to directly use range operations on lines of a file. struct ByLine(Char, Terminator) { private: - import std.typecons; + import std.typecons : RefCounted, RefCountedAutoInitialize; /* Ref-counting stops the source range's Impl * from getting out of sync after the range is copied, e.g. @@ -1814,7 +1992,7 @@ Allows to directly use range operations on lines of a file. enum defTerm = cast(Terminator)"\n"; public: - this(File f, KeepTerminator kt = KeepTerminator.no, + this(File f, KeepTerminator kt = No.keepTerminator, Terminator terminator = defTerm) { impl = PImpl(f, kt, terminator); @@ -1867,7 +2045,7 @@ Allows to directly use range operations on lines of a file. void popFront() { - import std.algorithm : endsWith; + import std.algorithm.searching : endsWith; assert(file.isOpen); line = buffer; file.readln(line, terminator); @@ -1880,8 +2058,8 @@ Allows to directly use range operations on lines of a file. file.detach(); line = null; } - else if (keepTerminator == KeepTerminator.no - && std.algorithm.endsWith(line, terminator)) + else if (keepTerminator == No.keepTerminator + && endsWith(line, terminator)) { static if (isScalarType!Terminator) enum tlen = 1; @@ -1893,7 +2071,7 @@ Allows to directly use range operations on lines of a file. } else static assert(false); - line = line.ptr[0 .. line.length - tlen]; + line = line[0 .. line.length - tlen]; } } } @@ -1915,10 +2093,10 @@ instead. Params: Char = Character type for each line, defaulting to $(D char). -keepTerminator = Use $(D KeepTerminator.yes) to include the +keepTerminator = Use $(D Yes.keepTerminator) to include the terminator at the end of each line. terminator = Line separator ($(D '\n') by default). Use -$(XREF ascii, newline) for portability (unless the file was opened in +$(REF newline, std,ascii) for portability (unless the file was opened in text mode). Example: @@ -1960,7 +2138,7 @@ $(D front) after the corresponding $(D popFront) call is made (because the contents may well have changed). */ auto byLine(Terminator = char, Char = char) - (KeepTerminator keepTerminator = KeepTerminator.no, + (KeepTerminator keepTerminator = No.keepTerminator, Terminator terminator = '\n') if (isScalarType!Terminator) { @@ -1975,14 +2153,15 @@ the contents may well have changed). return ByLine!(Char, Terminator)(this, keepTerminator, terminator); } - unittest + @system unittest { + static import std.file; auto deleteme = testFilename(); std.file.write(deleteme, "hi"); scope(success) std.file.remove(deleteme); - import std.typetuple; - foreach (T; TypeTuple!(char, wchar, dchar)) + import std.meta : AliasSeq; + foreach (T; AliasSeq!(char, wchar, dchar)) { auto blc = File(deleteme).byLine!(T, T); assert(blc.front == "hi"); @@ -1994,7 +2173,7 @@ the contents may well have changed). private struct ByLineCopy(Char, Terminator) { private: - import std.typecons; + import std.typecons : RefCounted, RefCountedAutoInitialize; /* Ref-counting stops the source range's ByLineCopyImpl * from getting out of sync after the range is copied, e.g. @@ -2073,10 +2252,10 @@ primitives may throw $(D StdioException) on I/O error. Params: Char = Character type for each line, defaulting to $(D immutable char). -keepTerminator = Use $(D KeepTerminator.yes) to include the +keepTerminator = Use $(D Yes.keepTerminator) to include the terminator at the end of each line. terminator = Line separator ($(D '\n') by default). Use -$(XREF ascii, newline) for portability (unless the file was opened in +$(REF newline, std,ascii) for portability (unless the file was opened in text mode). Example: @@ -2094,10 +2273,10 @@ void main() } ---- See_Also: -$(XREF file,readText) +$(REF readText, std,file) */ auto byLineCopy(Terminator = char, Char = immutable char) - (KeepTerminator keepTerminator = KeepTerminator.no, + (KeepTerminator keepTerminator = No.keepTerminator, Terminator terminator = '\n') if (isScalarType!Terminator) { @@ -2112,7 +2291,7 @@ $(XREF file,readText) return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator); } - unittest + @safe unittest { static assert(is(typeof(File("").byLine.front) == char[])); static assert(is(typeof(File("").byLineCopy.front) == string)); @@ -2120,13 +2299,11 @@ $(XREF file,readText) is(typeof(File("").byLineCopy!(char, char).front) == char[])); } - unittest + @system unittest { static import std.file; - import std.algorithm : equal; - import std.range; + import std.algorithm.comparison : equal; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); auto deleteme = testFilename(); std.file.write(deleteme, ""); @@ -2144,8 +2321,9 @@ $(XREF file,readText) void test(Terminator)(string txt, in string[] witness, KeepTerminator kt, Terminator term, bool popFirstLine = false) { + import std.algorithm.sorting : sort; + import std.array : array; import std.conv : text; - import std.algorithm : sort; import std.range.primitives : walkLength; uint i; @@ -2177,7 +2355,7 @@ $(XREF file,readText) assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort()); } - KeepTerminator kt = KeepTerminator.no; + KeepTerminator kt = No.keepTerminator; test("", null, kt, '\n'); test("\n", [ "" ], kt, '\n'); test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n'); @@ -2188,7 +2366,7 @@ $(XREF file,readText) kt, "\r\n"); test("sue\r", ["sue"], kt, '\r'); - kt = KeepTerminator.yes; + kt = Yes.keepTerminator; test("", null, kt, '\n'); test("\n", [ "\n" ], kt, '\n'); test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n'); @@ -2200,10 +2378,10 @@ $(XREF file,readText) test("sue\r", ["sue\r"], kt, '\r'); } - unittest + @system unittest { - import std.algorithm : equal; - import std.range; + import std.algorithm.comparison : equal; + import std.range : drop, take; version(Win64) { @@ -2214,7 +2392,7 @@ $(XREF file,readText) auto file = File(deleteme, "w+"); scope(success) std.file.remove(deleteme); } - else version(Android) + else version(CRuntime_Bionic) { static import std.file; @@ -2251,8 +2429,9 @@ $(XREF file,readText) assert(!file.isOpen); } - unittest + @system unittest { + static import std.file; auto deleteme = testFilename(); std.file.write(deleteme, "hi"); scope(success) std.file.remove(deleteme); @@ -2263,6 +2442,23 @@ $(XREF file,readText) assert(blc.front is blc.front); } + /** + Creates an input range set up to parse one line at a time from the file + into a tuple. + + Range primitives may throw $(D StdioException) on I/O error. + + Params: + format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format) + + Returns: + The input range set up to parse one line at a time into a record tuple. + + See_Also: + + It is similar to $(LREF byLine) and uses + $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood. + */ template byRecord(Fields...) { ByRecord!(Fields) byRecord(string format) @@ -2271,24 +2467,27 @@ $(XREF file,readText) } } - unittest + /// + @system unittest { - // static import std.file; - // - // auto deleteme = testFilename(); - // rndGen.popFront(); - // scope(failure) printf("Failed test at line %d\n", __LINE__); - // std.file.write(deleteme, "1 2\n4 1\n5 100"); - // scope(exit) std.file.remove(deleteme); - // File f = File(deleteme); - // scope(exit) f.close(); - // auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ]; - // uint i; - // foreach (e; f.byRecord!(int, int)("%s %s")) - // { - // //.writeln(e); - // assert(e == t[i++]); - // } + static import std.file; + import std.typecons : tuple; + + // prepare test file + auto testFile = testFilename(); + scope(failure) printf("Failed test at line %d\n", __LINE__); + std.file.write(testFile, "1 2\n4 1\n5 100"); + scope(exit) std.file.remove(testFile); + + File f = File(testFile); + scope(exit) f.close(); + + auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)]; + uint i; + foreach (e; f.byRecord!(int, int)("%s %s")) + { + assert(e == expected[i++]); + } } // Note: This was documented until 2013/08 @@ -2316,7 +2515,7 @@ $(XREF file,readText) this(File file, ubyte[] buffer) { - import std.exception; + import std.exception : enforce; enforce(buffer.length, "size must be larger than 0"); file_ = file; chunk_ = buffer; @@ -2399,7 +2598,7 @@ In the example above, $(D buffer.length) is 4096 for all iterations, except for the last one, in which case $(D buffer.length) may be less than 4096 (but always greater than zero). -With the mentioned limitations, $(D byChunks) works with any algorithm +With the mentioned limitations, $(D byChunk) works with any algorithm compatible with input ranges. Example: @@ -2412,8 +2611,8 @@ void main() } --- -$(XREF algorithm, joiner) can be used to join chunks together into a single -range lazily. +$(REF joiner, std,algorithm,iteration) can be used to join chunks together into +a single range lazily. Example: --- import std.algorithm, std.stdio; @@ -2443,7 +2642,7 @@ $(D StdioException). return ByChunk(this, buffer); } - unittest + @system unittest { static import std.file; @@ -2463,12 +2662,12 @@ $(D StdioException). uint i; foreach (chunk; f.byChunk(4)) - assert(chunk == cast(ubyte[])witness[i++]); + assert(chunk == cast(ubyte[]) witness[i++]); assert(i == witness.length); } - unittest + @system unittest { static import std.file; @@ -2488,7 +2687,7 @@ $(D StdioException). uint i; foreach (chunk; f.byChunk(new ubyte[4])) - assert(chunk == cast(ubyte[])witness[i++]); + assert(chunk == cast(ubyte[]) witness[i++]); assert(i == witness.length); } @@ -2501,42 +2700,39 @@ $(D Range) that locks the file and allows fast writing to it. { private: import std.range.primitives : ElementType, isInfinite, isInputRange; - FILE* fps_; // the shared file handle - _iobuf* handle_; // the unshared version of fps + // the shared file handle + FILE* fps_; + + // the unshared version of fps + @property _iobuf* handle_() @trusted { return cast(_iobuf*) fps_; } + + // the file's orientation (byte- or wide-oriented) int orientation_; public: - deprecated("accessing fps/handle/orientation directly can break LockingTextWriter integrity") - { - alias fps = fps_; - alias handle = handle_; - alias orientation = orientation_; - } this(ref File f) @trusted { import core.stdc.wchar_ : fwide; import std.exception : enforce; - enforce(f._p && f._p.handle); + enforce(f._p && f._p.handle, "Attempting to write to closed File"); fps_ = f._p.handle; orientation_ = fwide(fps_, 0); FLOCK(fps_); - handle_ = cast(_iobuf*)fps_; } ~this() @trusted { - if(fps_) + if (fps_) { FUNLOCK(fps_); fps_ = null; - handle_ = null; } } this(this) @trusted { - if(fps_) + if (fps_) { FLOCK(fps_); } @@ -2544,7 +2740,8 @@ $(D Range) that locks the file and allows fast writing to it. /// Range primitive implementations. void put(A)(A writeme) - if (is(ElementType!A : const(dchar)) && + if ((isSomeChar!(Unqual!(ElementType!A)) || + is(ElementType!A : const(ubyte))) && isInputRange!A && !isInfinite!A) { @@ -2552,41 +2749,35 @@ $(D Range) that locks the file and allows fast writing to it. alias C = ElementEncodingType!A; static assert(!is(C == void)); - static if (isSomeString!A && C.sizeof == 1) + static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[])) { if (orientation_ <= 0) { //file.write(writeme); causes infinite recursion!!! //file.rawWrite(writeme); - static auto trustedFwrite(in void* ptr, size_t size, size_t nmemb, FILE* stream) @trusted - { - return .fwrite(ptr, size, nmemb, stream); - } - auto result = - trustedFwrite(writeme.ptr, C.sizeof, writeme.length, fps_); + auto result = trustedFwrite(fps_, writeme); if (result != writeme.length) errnoEnforce(0); return; } } - // put each character in turn - foreach (dchar c; writeme) + // put each element in turn. + alias Elem = Unqual!(ElementType!A); + foreach (Elem c; writeme) { put(c); } } - // @@@BUG@@@ 2340 - //void front(C)(C c) if (is(C : dchar)) { /// ditto - void put(C)(C c) @safe if (is(C : const(dchar))) + void put(C)(C c) @safe if (isSomeChar!C || is(C : const(ubyte))) { - import std.traits : ParameterTypeTuple; + import std.traits : Parameters; static auto trustedFPUTC(int ch, _iobuf* h) @trusted { return FPUTC(ch, h); } - static auto trustedFPUTWC(ParameterTypeTuple!FPUTWC[0] ch, _iobuf* h) @trusted + static auto trustedFPUTWC(Parameters!FPUTWC[0] ch, _iobuf* h) @trusted { return FPUTWC(ch, h); } @@ -2599,7 +2790,7 @@ $(D Range) that locks the file and allows fast writing to it. } else static if (c.sizeof == 2) { - import std.utf : toUTF8; + import std.utf : encode, UseReplacementDchar; if (orientation_ <= 0) { @@ -2610,9 +2801,9 @@ $(D Range) that locks the file and allows fast writing to it. else { char[4] buf; - auto b = std.utf.toUTF8(buf, c); - foreach (i ; 0 .. b.length) - trustedFPUTC(b[i], handle_); + immutable size = encode!(UseReplacementDchar.yes)(buf, c); + foreach (i ; 0 .. size) + trustedFPUTC(buf[i], handle_); } } else @@ -2622,7 +2813,7 @@ $(D Range) that locks the file and allows fast writing to it. } else // 32-bit characters { - import std.utf : toUTF8; + import std.utf : encode; if (orientation_ <= 0) { @@ -2633,9 +2824,9 @@ $(D Range) that locks the file and allows fast writing to it. else { char[4] buf = void; - auto b = std.utf.toUTF8(buf, c); - foreach (i ; 0 .. b.length) - trustedFPUTC(b[i], handle_); + immutable len = encode(buf, c); + foreach (i ; 0 .. len) + trustedFPUTC(buf[i], handle_); } } else @@ -2681,6 +2872,213 @@ See $(LREF byChunk) for an example. return LockingTextWriter(this); } + // An output range which optionally locks the file and puts it into + // binary mode (similar to rawWrite). Because it needs to restore + // the file mode on destruction, it is RefCounted on Windows. + struct BinaryWriterImpl(bool locking) + { + import std.traits : hasIndirections; + private: + FILE* fps; + string name; + + version (Windows) + { + int fd, oldMode; + version (DIGITAL_MARS_STDIO) + ubyte oldInfo; + } + + package: + this(ref File f) + { + import std.exception : enforce; + + enforce(f._p && f._p.handle); + name = f._name; + fps = f._p.handle; + static if (locking) + FLOCK(fps); + + version (Windows) + { + .fflush(fps); // before changing translation mode + fd = ._fileno(fps); + oldMode = ._setmode(fd, _O_BINARY); + version (DIGITAL_MARS_STDIO) + { + import core.atomic : atomicOp; + + // @@@BUG@@@ 4243 + oldInfo = __fhnd_info[fd]; + atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); + } + } + } + + public: + ~this() + { + if (!fps) + return; + + version (Windows) + { + .fflush(fps); // before restoring translation mode + version (DIGITAL_MARS_STDIO) + { + // @@@BUG@@@ 4243 + __fhnd_info[fd] = oldInfo; + } + ._setmode(fd, oldMode); + } + + FUNLOCK(fps); + fps = null; + } + + void rawWrite(T)(in T[] buffer) + { + import std.conv : text; + import std.exception : errnoEnforce; + + auto result = trustedFwrite(fps, buffer); + if (result == result.max) result = 0; + errnoEnforce(result == buffer.length, + text("Wrote ", result, " instead of ", buffer.length, + " objects of type ", T.stringof, " to file `", + name, "'")); + } + + version (Windows) + { + @disable this(this); + } + else + { + this(this) + { + if (fps) + { + FLOCK(fps); + } + } + } + + void put(T)(auto ref in T value) + if (!hasIndirections!T && + !isInputRange!T) + { + rawWrite((&value)[0 .. 1]); + } + + void put(T)(in T[] array) + if (!hasIndirections!T && + !isInputRange!T) + { + rawWrite(array); + } + } + +/** Returns an output range that locks the file and allows fast writing to it. + +Example: +Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set) +in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output. +--- +import std.algorithm, std.range, std.stdio; + +void main() +{ + enum size = 500; + writef("P5\n%d %d %d\n", size, size, ubyte.max); + + iota(-1, 3, 2.0/size).map!(y => + iota(-1.5, 0.5, 2.0/size).map!(x => + cast(ubyte)(1+ + recurrence!((a, n) => x + y*1i + a[n-1]^^2)(0+0i) + .take(ubyte.max) + .countUntil!(z => z.re^^2 + z.im^^2 > 4)) + ) + ) + .copy(stdout.lockingBinaryWriter); +} +--- +*/ + auto lockingBinaryWriter() + { + alias LockingBinaryWriterImpl = BinaryWriterImpl!true; + + version (Windows) + { + import std.typecons : RefCounted; + alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl; + } + else + alias LockingBinaryWriter = LockingBinaryWriterImpl; + + return LockingBinaryWriter(this); + } + + @system unittest + { + static import std.file; + import std.algorithm.mutation : reverse; + import std.exception : collectException; + import std.range : only, retro; + import std.string : format; + + auto deleteme = testFilename(); + scope(exit) collectException(std.file.remove(deleteme)); + auto output = File(deleteme, "wb"); + auto writer = output.lockingBinaryWriter(); + auto input = File(deleteme, "rb"); + + T[] readExact(T)(T[] buf) + { + auto result = input.rawRead(buf); + assert(result.length == buf.length, + "Read %d out of %d bytes" + .format(result.length, buf.length)); + return result; + } + + // test raw values + ubyte byteIn = 42; + byteIn.only.copy(writer); output.flush(); + ubyte byteOut = readExact(new ubyte[1])[0]; + assert(byteIn == byteOut); + + // test arrays + ubyte[] bytesIn = [1, 2, 3, 4, 5]; + bytesIn.copy(writer); output.flush(); + ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]); + scope(failure) .writeln(bytesOut); + assert(bytesIn == bytesOut); + + // test ranges of values + bytesIn.retro.copy(writer); output.flush(); + bytesOut = readExact(bytesOut); + bytesOut.reverse(); + assert(bytesIn == bytesOut); + + // test string + "foobar".copy(writer); output.flush(); + char[] charsOut = readExact(new char[6]); + assert(charsOut == "foobar"); + + // test ranges of arrays + only("foo", "bar").copy(writer); output.flush(); + charsOut = readExact(charsOut); + assert(charsOut == "foobar"); + + // test that we are writing arrays as is, + // without UTF-8 transcoding + "foo"d.copy(writer); output.flush(); + dchar[] dcharsOut = readExact(new dchar[3]); + assert(dcharsOut == "foo"); + } + /// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs. @property ulong size() @safe { @@ -2694,7 +3092,7 @@ See $(LREF byChunk) for an example. } } -unittest +@system unittest { @system struct SystemToString { @@ -2723,7 +3121,7 @@ unittest @system void systemTests() { //system code can write to files/stdout with anything! - if(false) + if (false) { auto f = File(); @@ -2778,7 +3176,7 @@ unittest auto f = File(); //safe code can write to files only with @safe and @trusted code... - if(false) + if (false) { f.write("just a string"); f.write("string with arg: ", 47); @@ -2832,7 +3230,7 @@ unittest safeTests(); } -unittest +@safe unittest { static import std.file; import std.exception : collectException; @@ -2845,10 +3243,12 @@ unittest assert(f.tell == 0); } -unittest +@system unittest { + // @system due to readln static import std.file; - import std.range; + import std.range : chain, only, repeat; + import std.range.primitives : isOutputRange; auto deleteme = testFilename(); scope(exit) std.file.remove(deleteme); @@ -2863,38 +3263,51 @@ unittest writer.put('æ—¥'); writer.put(chain(only('本'), only('語'))); writer.put(repeat('#', 12)); // BUG 11945 + writer.put(cast(immutable(ubyte)[])"日本語"); // Bug 17229 } - assert(File(deleteme).readln() == "日本語日本語日本語日本語############"); + assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語"); +} + +@safe unittest +{ + import std.exception : collectException; + auto e = collectException({ File f; f.writeln("Hello!"); }()); + assert(e && e.msg == "Attempting to write to closed File"); } /// Used to specify the lock type for $(D File.lock) and $(D File.tryLock). enum LockType { - /// Specifies a _read (shared) lock. A _read lock denies all processes - /// write access to the specified region of the file, including the - /// process that first locks the region. All processes can _read the - /// locked region. Multiple simultaneous _read locks are allowed, as - /// long as there are no exclusive locks. + /** + * Specifies a _read (shared) lock. A _read lock denies all processes + * write access to the specified region of the file, including the + * process that first locks the region. All processes can _read the + * locked region. Multiple simultaneous _read locks are allowed, as + * long as there are no exclusive locks. + */ read, - /// Specifies a read/write (exclusive) lock. A read/write lock denies all - /// other processes both read and write access to the locked file region. - /// If a segment has an exclusive lock, it may not have any shared locks - /// or other exclusive locks. + + /** + * Specifies a read/write (exclusive) lock. A read/write lock denies all + * other processes both read and write access to the locked file region. + * If a segment has an exclusive lock, it may not have any shared locks + * or other exclusive locks. + */ readWrite } struct LockingTextReader { private File _f; - private dchar _front; + private char _front; + private bool _hasChar; this(File f) { import std.exception : enforce; - enforce(f.isOpen); + enforce(f.isOpen, "LockingTextReader: File must be open"); _f = f; FLOCK(_f._p.handle); - readFront(); } this(this) @@ -2904,108 +3317,66 @@ struct LockingTextReader ~this() { + if (_hasChar) + ungetc(_front, cast(FILE*)_f._p.handle); + // File locking has its own reference count if (_f.isOpen) FUNLOCK(_f._p.handle); } void opAssign(LockingTextReader r) { - import std.algorithm : swap; + import std.algorithm.mutation : swap; swap(this, r); } @property bool empty() { - return !_f.isOpen || _f.eof; - } - - @property dchar front() - { - version(assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - return _front; - } - - /* Read a utf8 sequence from the file, removing the chars from the stream. - Returns an empty result when at EOF. */ - private char[] takeFront(return ref char[4] buf) - { - import std.utf : stride, UTFException; + if (!_hasChar) { + if (!_f.isOpen || _f.eof) + return true; immutable int c = FGETC(cast(_iobuf*) _f._p.handle); if (c == EOF) - return buf[0 .. 0]; - buf[0] = cast(char) c; - } - immutable seqLen = stride(buf[]); - foreach(ref u; buf[1 .. seqLen]) - { - immutable int c = FGETC(cast(_iobuf*) _f._p.handle); - if (c == EOF) // incomplete sequence - throw new UTFException("Invalid UTF-8 sequence"); - u = cast(char) c; + { + .destroy(_f); + return true; + } + _front = cast(char) c; + _hasChar = true; } - return buf[0 .. seqLen]; + return false; } - /* Read a utf8 sequence from the file into _front, putting the chars back so - that they can be read again. - Destroys/closes the file when at EOF. */ - private void readFront() + @property char front() { - import std.exception : enforce; - import std.utf : decodeFront; - - char[4] buf; - auto chars = takeFront(buf); - - if (chars.empty) - { - .destroy(_f); - assert(empty); - return; - } - - auto s = chars; - _front = decodeFront(s); - - // Put everything back. - foreach(immutable i; 0 .. chars.length) + if (!_hasChar) { - immutable c = chars[$ - 1 - i]; - enforce(ungetc(c, cast(FILE*) _f._p.handle) == c); + version(assert) + { + import core.exception : RangeError; + if (empty) + throw new RangeError(); + } + else + { + empty; + } } + return _front; } void popFront() { - version(assert) - { - import core.exception : RangeError; - if (empty) - throw new RangeError(); - } - - // Pop the current front. - char[4] buf; - takeFront(buf); - - // Read the next front, leaving the chars on the stream. - readFront(); + if (!_hasChar) + empty; + _hasChar = false; } - - // void unget(dchar c) - // { - // ungetc(c, cast(FILE*) _f._p.handle); - // } } -unittest +@system unittest { + // @system due to readf static import std.file; import std.range.primitives : isInputRange; @@ -3021,13 +3392,13 @@ unittest assert(x == 2); f.readf("%d ", &x); assert(x == 3); - //pragma(msg, "--- todo: readf ---"); } -unittest // bugzilla 13686 +@system unittest // bugzilla 13686 { static import std.file; - import std.algorithm : equal; + import std.algorithm.comparison : equal; + import std.utf : byDchar; auto deleteme = testFilename(); std.file.write(deleteme, "ТеÑÑ‚"); @@ -3037,11 +3408,11 @@ unittest // bugzilla 13686 File(deleteme).readf("%s", &s); assert(s == "ТеÑÑ‚"); - auto ltr = LockingTextReader(File(deleteme)); - assert(equal(ltr, "ТеÑÑ‚")); + auto ltr = LockingTextReader(File(deleteme)).byDchar; + assert(equal(ltr, "ТеÑÑ‚".byDchar)); } -unittest // bugzilla 12320 +@system unittest // bugzilla 12320 { static import std.file; auto deleteme = testFilename(); @@ -3055,8 +3426,32 @@ unittest // bugzilla 12320 assert(ltr.empty); } +@system unittest // bugzilla 14861 +{ + // @system due to readf + static import std.file; + auto deleteme = testFilename(); + File fw = File(deleteme, "w"); + for (int i; i != 5000; i++) + fw.writeln(i, ";", "Иванов;Пётр;Петрович"); + fw.close(); + scope(exit) std.file.remove(deleteme); + // Test read + File fr = File(deleteme, "r"); + scope (exit) fr.close(); + int nom; string fam, nam, ot; + // Error format read + while (!fr.eof) + fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot); +} + /** - * Indicates whether $(D T) is a file handle of some kind. + * Indicates whether $(D T) is a file handle, i.e. the type + * is implicitly convertable to $(LREF File) or a pointer to a + * $(REF FILE, core,stdc,stdio). + * + * Returns: + * `true` if `T` is a file handle, `false` otherwise. */ template isFileHandle(T) { @@ -3064,42 +3459,59 @@ template isFileHandle(T) is(T : File); } -unittest +/// +@safe unittest { static assert(isFileHandle!(FILE*)); static assert(isFileHandle!(File)); } -/** - * $(RED Deprecated. Please use $(D isFileHandle) instead. This alias will be - * removed in June 2015.) - */ -deprecated("Please use isFileHandle instead.") -alias isStreamingDevice = isFileHandle; - /** * Property used by writeln/etc. so it can infer @safe since stdout is __gshared */ -private @property File trustedStdout() @trusted { return stdout; } +private @property File trustedStdout() @trusted +{ + return stdout; +} /*********************************** -For each argument $(D arg) in $(D args), format the argument (as per -$(LINK2 std_conv.html, to!(string)(arg))) and write the resulting +For each argument $(D arg) in $(D args), format the argument (using +$(REF to, std,conv)) and write the resulting string to $(D args[0]). A call without any arguments will fail to compile. +Params: + args = the items to write to `stdout` + Throws: In case of an I/O error, throws an $(D StdioException). + +Example: + Reads `stdin` and writes it to `stdout` with an argument + counter. +--- +import std.stdio; + +void main() +{ + string line; + + for (size_t count = 0; (line = readln) !is null; count++) + { + write("Input ", count, ": ", line, "\n"); + } +} +--- */ -void write(T...)(T args) if (!is(T[0] : File)) +void write(T...)(T args) +if (!is(T[0] : File)) { trustedStdout.write(args); } -unittest +@system unittest { static import std.file; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); void[] buf; if (false) write(buf); @@ -3110,21 +3522,34 @@ unittest f.close(); scope(exit) { std.file.remove(deleteme); } assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!"); - // // test write on stdout - //auto saveStdout = stdout; - //scope(exit) stdout = saveStdout; - //stdout.open(file, "w"); - Object obj; - //write("Hello, ", "world number ", 42, "! ", obj); - //stdout.close(); - // auto result = cast(char[]) std.file.read(file); - // assert(result == "Hello, world number 42! null", result); } /*********************************** - * Equivalent to $(D write(args, '\n')). Calling $(D writeln) without + * Equivalent to `write(args, '\n')`. Calling `writeln` without * arguments is valid and just prints a newline to the standard * output. + * + * Params: + * args = the items to write to `stdout` + * + * Throws: + * In case of an I/O error, throws an $(LREF StdioException). + * Example: + * Reads $(D stdin) and writes it to $(D stdout) with a argument + * counter. +--- +import std.stdio; + +void main() +{ + string line; + + for (size_t count = 0; (line = readln) !is null; count++) + { + writeln("Input ", count, ": ", line); + } +} +--- */ void writeln(T...)(T args) { @@ -3133,7 +3558,7 @@ void writeln(T...)(T args) { import std.exception : enforce; - enforce(fputc('\n', .trustedStdout._p.handle) == '\n'); + enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed"); } else static if (T.length == 1 && is(typeof(args[0]) : const(char)[]) && @@ -3141,7 +3566,7 @@ void writeln(T...)(T args) !is(Unqual!(typeof(args[0])) == typeof(null)) && !isAggregateType!(typeof(args[0]))) { - import std.exception : enforce; + import std.traits : isStaticArray; // Specialization for strings - a very frequent case auto w = .trustedStdout.lockingTextWriter(); @@ -3182,11 +3607,10 @@ void writeln(T...)(T args) } } -unittest +@system unittest { static import std.file; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); // test writeln @@ -3229,7 +3653,7 @@ unittest "Hello!\nHello!\nHello!\nembedded\0null\n"); } -unittest +@system unittest { static import std.file; @@ -3263,7 +3687,7 @@ unittest "A\nB\nA\nB\nA\nB\nA\nB\n"); } -unittest +@system unittest { static auto useInit(T)(T ltw) { @@ -3280,11 +3704,10 @@ unittest Writes formatted data to standard output (without a trailing newline). Params: -args = The first argument $(D args[0]) should be the format string, specifying -how to format the rest of the arguments. For a full description of the syntax -of the format string and how it controls the formatting of the rest of the -arguments, please refer to the documentation for $(XREF format, -formattedWrite). +fmt = The $(LINK2 std_format.html#format-string, format string). +When passed as a compile-time argument, the string will be statically checked +against the argument types passed. +args = Items to write. Note: In older versions of Phobos, it used to be possible to write: @@ -3300,55 +3723,74 @@ stderr.writef("%s", "message"); ------ */ +void writef(alias fmt, A...)(A args) +if (isSomeString!(typeof(fmt))) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return .writef(fmt, args); +} -void writef(T...)(T args) +/// ditto +void writef(Char, A...)(in Char[] fmt, A args) { - trustedStdout.writef(args); + trustedStdout.writef(fmt, args); } -unittest +@system unittest { static import std.file; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); // test writef auto deleteme = testFilename(); auto f = File(deleteme, "w"); scope(exit) { std.file.remove(deleteme); } - f.writef("Hello, %s world number %s!", "nice", 42); + f.writef!"Hello, %s world number %s!"("nice", 42); f.close(); assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!"); // test write on stdout auto saveStdout = stdout; scope(exit) stdout = saveStdout; stdout.open(deleteme, "w"); - writef("Hello, %s world number %s!", "nice", 42); + writef!"Hello, %s world number %s!"("nice", 42); stdout.close(); assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!"); } /*********************************** - * Equivalent to $(D writef(args, '\n')). + * Equivalent to $(D writef(fmt, args, '\n')). */ -void writefln(T...)(T args) +void writefln(alias fmt, A...)(A args) +if (isSomeString!(typeof(fmt))) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return .writefln(fmt, args); +} + +/// ditto +void writefln(Char, A...)(in Char[] fmt, A args) { - trustedStdout.writefln(args); + trustedStdout.writefln(fmt, args); } -unittest +@system unittest { static import std.file; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); - // test writefln + // test File.writefln auto deleteme = testFilename(); auto f = File(deleteme, "w"); scope(exit) { std.file.remove(deleteme); } - f.writefln("Hello, %s world number %s!", "nice", 42); + f.writefln!"Hello, %s world number %s!"("nice", 42); f.close(); version (Windows) assert(cast(char[]) std.file.read(deleteme) == @@ -3357,37 +3799,66 @@ unittest assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!\n", cast(char[]) std.file.read(deleteme)); - // test write on stdout - // auto saveStdout = stdout; - // scope(exit) stdout = saveStdout; - // stdout.open(file, "w"); - // assert(stdout.isOpen); - // writefln("Hello, %s world number %s!", "nice", 42); - // foreach (F ; TypeTuple!(ifloat, idouble, ireal)) - // { - // F a = 5i; - // F b = a % 2; - // writeln(b); - // } - // stdout.close(); - // auto read = cast(char[]) std.file.read(file); - // version (Windows) - // assert(read == "Hello, nice world number 42!\r\n1\r\n1\r\n1\r\n", read); - // else - // assert(read == "Hello, nice world number 42!\n1\n1\n1\n", "["~read~"]"); + + // test writefln + auto saveStdout = stdout; + scope(exit) stdout = saveStdout; + stdout.open(deleteme, "w"); + writefln!"Hello, %s world number %s!"("nice", 42); + stdout.close(); + version (Windows) + assert(cast(char[]) std.file.read(deleteme) == + "Hello, nice world number 42!\r\n"); + else + assert(cast(char[]) std.file.read(deleteme) == + "Hello, nice world number 42!\n"); } /** - * Read data from $(D stdin) according to the specified - * $(LINK2 std_format.html#format-string, format specifier) using - * $(XREF format,formattedRead). + * Reads formatted data from $(D stdin) using $(REF formattedRead, std,_format). + * Params: + * format = The $(LINK2 std_format.html#_format-string, _format string). + * When passed as a compile-time argument, the string will be statically checked + * against the argument types passed. + * args = Items to be read. + * Example: +---- +// test.d +void main() +{ + import std.stdio; + foreach (_; 0 .. 3) + { + int a; + readf!" %d"(a); + writeln(++a); + } +} +---- +$(CONSOLE +% echo "1 2 3" | rdmd test.d +2 +3 +4 +) */ -uint readf(A...)(in char[] format, A args) +uint readf(alias format, A...)(auto ref A args) +if (isSomeString!(typeof(format))) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(format, A); + static assert(!e, e.msg); + return .readf(format, args); +} + +/// ditto +uint readf(A...)(in char[] format, auto ref A args) { return stdin.readf(format, args); } -unittest +@system unittest { float f; if (false) uint x = readf("%s", &f); @@ -3395,7 +3866,10 @@ unittest char a; wchar b; dchar c; - if (false) readf("%s %s %s", &a,&b,&c); + if (false) readf("%s %s %s", a, b, c); + // backwards compatibility with pointers + if (false) readf("%s %s %s", a, &b, c); + if (false) readf("%s %s %s", &a, &b, &c); } /********************************** @@ -3444,7 +3918,7 @@ if (isSomeString!S) * $(D size_t) 0 for end of file, otherwise number of characters read * Params: * buf = Buffer used to store the resulting line data. buf is resized as necessary. - * terminator = Line terminator (by default, $(D '\n')). Use $(XREF ascii, newline) + * terminator = Line terminator (by default, $(D '\n')). Use $(REF newline, std,ascii) * for portability (unless the file was opened in text mode). * Throws: * $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error. @@ -3475,9 +3949,9 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && return stdin.readln(buf, terminator); } -unittest +@safe unittest { - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; //we can't actually test readln, so at the very least, //we test compilability @@ -3485,12 +3959,12 @@ unittest { readln(); readln('\t'); - foreach (String; TypeTuple!(string, char[], wstring, wchar[], dstring, dchar[])) + foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[])) { readln!String(); readln!String('\t'); } - foreach (String; TypeTuple!(char[], wchar[], dchar[])) + foreach (String; AliasSeq!(char[], wchar[], dchar[])) { String buf; readln(buf); @@ -3505,32 +3979,40 @@ unittest * (to $(D _wfopen) on Windows) * with appropriately-constructed C-style strings. */ -private FILE* fopen(in char[] name, in char[] mode = "r") @trusted nothrow @nogc +private FILE* fopen(R1, R2)(R1 name, R2 mode = "r") +if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) && + (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2)) { import std.internal.cstring : tempCString; - version(Windows) - { - import std.internal.cstring : tempCStringW; - return _wfopen(name.tempCStringW(), mode.tempCStringW()); - } - else version(Posix) - { - /* - * The new opengroup large file support API is transparently - * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0 - * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and - * the normal functions work fine. If not, then large file support - * probably isn't available. Do not use the old transitional API - * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0) - */ - import core.sys.posix.stdio : fopen; - return fopen(name.tempCString(), mode.tempCString()); - } - else + auto namez = name.tempCString!FSChar(); + auto modez = mode.tempCString!FSChar(); + + static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc { - return .fopen(name.tempCString(), mode.tempCString()); + version(Windows) + { + return _wfopen(namez, modez); + } + else version(Posix) + { + /* + * The new opengroup large file support API is transparently + * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0 + * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and + * the normal functions work fine. If not, then large file support + * probably isn't available. Do not use the old transitional API + * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0) + */ + import core.sys.posix.stdio : fopen; + return fopen(namez, modez); + } + else + { + return .fopen(namez, modez); + } } + return fopenImpl(namez, modez); } version (Posix) @@ -3539,23 +4021,38 @@ version (Posix) * Convenience function that forwards to $(D core.sys.posix.stdio.popen) * with appropriately-constructed C-style strings. */ - FILE* popen(in char[] name, in char[] mode = "r") @trusted nothrow @nogc + FILE* popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc + if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) && + (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2)) { - import core.sys.posix.stdio : popen; import std.internal.cstring : tempCString; - return popen(name.tempCString(), mode.tempCString()); + auto namez = name.tempCString!FSChar(); + auto modez = mode.tempCString!FSChar(); + + static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc + { + import core.sys.posix.stdio : popen; + return popen(namez, modez); + } + return popenImpl(namez, modez); } } /* * Convenience function that forwards to $(D core.stdc.stdio.fwrite) - * and throws an exception upon error */ -private void binaryWrite(T)(FILE* f, T obj) +private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted +{ + return fwrite(obj.ptr, T.sizeof, obj.length, f); +} + +/* + * Convenience function that forwards to $(D core.stdc.stdio.fread) + */ +private auto trustedFread(T)(FILE* f, T[] obj) @trusted { - immutable result = fwrite(obj.ptr, obj[0].sizeof, obj.length, f); - if (result != obj.length) StdioException(); + return fread(obj.ptr, T.sizeof, obj.length, f); } /** @@ -3610,7 +4107,6 @@ struct lines { private File f; private dchar terminator = '\n'; - // private string fileName; // Curretly, no use /** Constructor. @@ -3624,26 +4120,10 @@ struct lines this.terminator = terminator; } - // Keep these commented lines for later, when Walter fixes the - // exception model. - -// static lines opCall(string fName, dchar terminator = '\n') -// { -// auto f = enforce(fopen(fName), -// new StdioException("Cannot open file `"~fName~"' for reading")); -// auto result = lines(f, terminator); -// result.fileName = fName; -// return result; -// } - int opApply(D)(scope D dg) { -// scope(exit) { -// if (fileName.length && fclose(f)) -// StdioException("Could not close file `"~fileName~"'"); -// } - import std.traits : ParameterTypeTuple; - alias Parms = ParameterTypeTuple!(dg); + import std.traits : Parameters; + alias Parms = Parameters!(dg); static if (isSomeString!(Parms[$ - 1])) { enum bool duplicate = is(Parms[$ - 1] == string) @@ -3686,11 +4166,11 @@ struct lines // no UTF checking int opApplyRaw(D)(scope D dg) { - import std.exception : assumeUnique; import std.conv : to; - import std.traits : ParameterTypeTuple; + import std.exception : assumeUnique; + import std.traits : Parameters; - alias Parms = ParameterTypeTuple!(dg); + alias Parms = Parameters!(dg); enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]); int result = 1; int c = void; @@ -3699,7 +4179,7 @@ struct lines ubyte[] buffer; static if (Parms.length == 2) Parms[0] line = 0; - while ((c = FGETC(cast(_iobuf*)f._p.handle)) != -1) + while ((c = FGETC(cast(_iobuf*) f._p.handle)) != -1) { buffer ~= to!(ubyte)(c); if (c == terminator) @@ -3731,21 +4211,21 @@ struct lines } } -unittest +@system unittest { static import std.file; - import std.typetuple : TypeTuple; + import std.meta : AliasSeq; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); auto deleteme = testFilename(); scope(exit) { std.file.remove(deleteme); } alias TestedWith = - TypeTuple!(string, wstring, dstring, - char[], wchar[], dchar[]); - foreach (T; TestedWith) { + AliasSeq!(string, wstring, dstring, + char[], wchar[], dchar[]); + foreach (T; TestedWith) + { // test looping with an empty file std.file.write(deleteme, ""); auto f = File(deleteme, "r"); @@ -3785,10 +4265,9 @@ unittest } // test with ubyte[] inputs - //@@@BUG 2612@@@ - //alias TestedWith2 = TypeTuple!(immutable(ubyte)[], ubyte[]); - alias TestedWith2 = TypeTuple!(immutable(ubyte)[], ubyte[]); - foreach (T; TestedWith2) { + alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]); + foreach (T; TestedWith2) + { // test looping with an empty file std.file.write(deleteme, ""); auto f = File(deleteme, "r"); @@ -3829,7 +4308,7 @@ unittest } - foreach (T; TypeTuple!(ubyte[])) + foreach (T; AliasSeq!(ubyte[])) { // test looping with a file with three lines, last without a newline // using a counter too this time @@ -3891,15 +4370,6 @@ private struct ChunksImpl this.size = size; } -// static chunks opCall(string fName, size_t size) -// { -// auto f = enforce(fopen(fName), -// new StdioException("Cannot open file `"~fName~"' for reading")); -// auto result = chunks(f, size); -// result.fileName = fName; -// return result; -// } - int opApply(D)(scope D dg) { import core.stdc.stdlib : alloca; @@ -3912,7 +4382,7 @@ private struct ChunksImpl size_t r = void; int result = 1; uint tally = 0; - while ((r = fread(buffer.ptr, buffer[0].sizeof, size, f._p.handle)) > 0) + while ((r = trustedFread(f._p.handle, buffer)) > 0) { assert(r <= size); if (r != size) @@ -3921,9 +4391,12 @@ private struct ChunksImpl if (!f.eof) throw new StdioException(null); buffer.length = r; } - static if (is(typeof(dg(tally, buffer)))) { + static if (is(typeof(dg(tally, buffer)))) + { if ((result = dg(tally, buffer)) != 0) break; - } else { + } + else + { if ((result = dg(buffer)) != 0) break; } ++tally; @@ -3932,11 +4405,10 @@ private struct ChunksImpl } } -unittest +@system unittest { static import std.file; - //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); auto deleteme = testFilename(); @@ -3966,6 +4438,32 @@ unittest f.close(); } + +/** +Writes an array or range to a file. +Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)). +Similar to $(REF write, std,file), strings are written as-is, +rather than encoded according to the $(D File)'s $(HTTP +en.cppreference.com/w/c/io#Narrow_and_wide_orientation, +orientation). +*/ +void toFile(T)(T data, string fileName) +if (is(typeof(copy(data, stdout.lockingBinaryWriter)))) +{ + copy(data, File(fileName, "wb").lockingBinaryWriter); +} + +@system unittest +{ + static import std.file; + + auto deleteme = testFilename(); + scope(exit) { std.file.remove(deleteme); } + + "Test".toFile(deleteme); + assert(std.file.readText(deleteme) == "Test"); +} + /********************* * Thrown if I/O errors happen. */ @@ -3978,7 +4476,7 @@ class StdioException : Exception /** Initialize with a message and an error code. */ - this(string message, uint e = core.stdc.errno.errno) + this(string message, uint e = core.stdc.errno.errno) @trusted { import std.conv : to; @@ -3988,13 +4486,13 @@ Initialize with a message and an error code. import core.stdc.string : strerror_r; char[256] buf = void; - version (linux) + version (CRuntime_Glibc) { - auto s = core.stdc.string.strerror_r(errno, buf.ptr, buf.length); + auto s = strerror_r(errno, buf.ptr, buf.length); } else { - core.stdc.string.strerror_r(errno, buf.ptr, buf.length); + strerror_r(errno, buf.ptr, buf.length); auto s = buf.ptr; } } @@ -4002,13 +4500,13 @@ Initialize with a message and an error code. { import core.stdc.string : strerror; - auto s = core.stdc.string.strerror(errno); + auto s = strerror(errno); } auto sysmsg = to!string(s); // If e is 0, we don't use the system error message. (The message // is "Success", which is rather pointless for an exception.) super(e == 0 ? message - : (message.ptr ? message ~ " (" ~ sysmsg ~ ")" : sysmsg)); + : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg)); } /** Convenience functions that throw an $(D StdioException). */ @@ -4045,17 +4543,24 @@ extern(C) void std_stdio_static_this() __gshared { /** The standard input stream. + Bugs: + Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), + it is thread un-safe to reassign `stdin` to a different `File` instance + than the default. */ File stdin; /// - unittest + @safe unittest { // Read stdin, sort lines, write to stdout - import std.stdio, std.array, std.algorithm : sort, copy; + import std.array : array; + import std.algorithm.sorting : sort; + import std.algorithm.mutation : copy; + import std.typecons : Yes; void main() { stdin // read from stdin - .byLineCopy(KeepTerminator.yes) // copying each line + .byLineCopy(Yes.keepTerminator) // copying each line .array() // convert to array of lines .sort() // sort the lines .copy( // copy output of .sort to an OutputRange @@ -4063,11 +4568,25 @@ __gshared } } - File stdout; /// The standard output stream. - File stderr; /// The standard error stream. + /** + The standard output stream. + Bugs: + Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), + it is thread un-safe to reassign `stdout` to a different `File` instance + than the default. + */ + File stdout; + /** + The standard error stream. + Bugs: + Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), + it is thread un-safe to reassign `stderr` to a different `File` instance + than the default. + */ + File stderr; } -unittest +@system unittest { static import std.file; import std.typecons : tuple; @@ -4091,33 +4610,107 @@ unittest } } +// roll our own appender, but with "safe" arrays +private struct ReadlnAppender +{ + char[] buf; + size_t pos; + bool safeAppend = false; + + void initialize(char[] b) + { + buf = b; + pos = 0; + } + @property char[] data() @trusted + { + if (safeAppend) + assumeSafeAppend(buf.ptr[0 .. pos]); + return buf.ptr[0 .. pos]; + } + + bool reserveWithoutAllocating(size_t n) + { + if (buf.length >= pos + n) // buf is already large enough + return true; + + immutable curCap = buf.capacity; + if (curCap >= pos + n) + { + buf.length = curCap; + /* Any extra capacity we end up not using can safely be claimed + by someone else. */ + safeAppend = true; + return true; + } + + return false; + } + void reserve(size_t n) @trusted + { + import core.stdc.string : memcpy; + if (!reserveWithoutAllocating(n)) + { + size_t ncap = buf.length * 2 + 128 + n; + char[] nbuf = new char[ncap]; + memcpy(nbuf.ptr, buf.ptr, pos); + buf = nbuf; + // Allocated a new buffer. No one else knows about it. + safeAppend = true; + } + } + void putchar(char c) @trusted + { + reserve(1); + buf.ptr[pos++] = c; + } + void putdchar(dchar dc) @trusted + { + import std.utf : encode, UseReplacementDchar; + + char[4] ubuf; + immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc); + reserve(size); + foreach (c; ubuf) + buf.ptr[pos++] = c; + } + void putonly(char[] b) @trusted + { + import core.stdc.string : memcpy; + assert(pos == 0); // assume this is the only put call + if (reserveWithoutAllocating(b.length)) + memcpy(buf.ptr + pos, b.ptr, b.length); + else + buf = b.dup; + pos = b.length; + } +} + // Private implementation of readln version (DIGITAL_MARS_STDIO) private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/) { - import core.memory; - import core.stdc.string : memcpy; - import std.array : appender, uninitializedArray; - FLOCK(fps); scope(exit) FUNLOCK(fps); /* Since fps is now locked, we can create an "unshared" version * of fp. */ - auto fp = cast(_iobuf*)fps; + auto fp = cast(_iobuf*) fps; + + ReadlnAppender app; + app.initialize(buf); if (__fhnd_info[fp._file] & FHND_WCHAR) { /* Stream is in wide characters. * Read them and convert to chars. */ static assert(wchar_t.sizeof == 2); - auto app = appender(buf); - app.clear(); for (int c = void; (c = FGETWC(fp)) != -1; ) { if ((c & ~0x7F) == 0) - { app.put(cast(char) c); + { + app.putchar(cast(char) c); if (c == terminator) break; } @@ -4133,35 +4726,26 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie } c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); } - //std.utf.encode(buf, c); - app.put(cast(dchar)c); + app.putdchar(cast(dchar) c); } } if (ferror(fps)) StdioException(); - buf = app.data; - return buf.length; } - auto sz = GC.sizeOf(buf.ptr); - //auto sz = buf.length; - buf = buf.ptr[0 .. sz]; - if (fp._flag & _IONBF) + else if (fp._flag & _IONBF) { /* Use this for unbuffered I/O, when running * across buffer boundaries, or for any but the common * cases. */ L1: - auto app = appender(buf); - app.clear(); - if(app.capacity == 0) - app.reserve(128); // get at least 128 bytes available - int c; - while((c = FGETC(fp)) != -1) { - app.put(cast(char) c); - if(c == terminator) { + while ((c = FGETC(fp)) != -1) + { + app.putchar(cast(char) c); + if (c == terminator) + { buf = app.data; return buf.length; } @@ -4170,8 +4754,6 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie if (ferror(fps)) StdioException(); - buf = app.data; - return buf.length; } else { @@ -4184,7 +4766,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie char c; while (1) { - if (i == u) // if end of buffer + if (i == u) // if end of buffer goto L1; // give up c = p[i]; i++; @@ -4202,14 +4784,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie goto L1; } } - if (i > sz) - { - buf = uninitializedArray!(char[])(i); - } - if (i - 1) - memcpy(buf.ptr, p, i - 1); - buf[i - 1] = cast(char)terminator; - buf = buf[0 .. i]; + app.putonly(p[0 .. i]); + app.buf[i - 1] = cast(char) terminator; if (terminator == '\n' && c == '\r') i++; } @@ -4217,53 +4793,43 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie { while (1) { - if (i == u) // if end of buffer + if (i == u) // if end of buffer goto L1; // give up auto c = p[i]; i++; if (c == terminator) break; } - if (i > sz) - { - buf = uninitializedArray!(char[])(i); - } - memcpy(buf.ptr, p, i); - buf = buf[0 .. i]; + app.putonly(p[0 .. i]); } fp._cnt -= i; fp._ptr += i; - return i; } + + buf = app.data; + return buf.length; } version (MICROSOFT_STDIO) private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/) { - import core.memory; - import std.array : appender, uninitializedArray; - FLOCK(fps); scope(exit) FUNLOCK(fps); /* Since fps is now locked, we can create an "unshared" version * of fp. */ - auto fp = cast(_iobuf*)fps; + auto fp = cast(_iobuf*) fps; - auto sz = GC.sizeOf(buf.ptr); - //auto sz = buf.length; - buf = buf.ptr[0 .. sz]; - - auto app = appender(buf); - app.clear(); - if(app.capacity == 0) - app.reserve(128); // get at least 128 bytes available + ReadlnAppender app; + app.initialize(buf); int c; - while((c = FGETC(fp)) != -1) { - app.put(cast(char) c); - if(c == terminator) { + while ((c = FGETC(fp)) != -1) + { + app.putchar(cast(char) c); + if (c == terminator) + { buf = app.data; return buf.length; } @@ -4279,10 +4845,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie version (HAS_GETDELIM) private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) { - import core.memory; import core.stdc.stdlib : free; import core.stdc.wchar_ : fwide; - import std.utf : encode; if (orientation == File.Orientation.wide) { @@ -4291,7 +4855,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie */ FLOCK(fps); scope(exit) FUNLOCK(fps); - auto fp = cast(_iobuf*)fps; + auto fp = cast(_iobuf*) fps; version (Windows) { buf.length = 0; @@ -4314,7 +4878,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie } c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); } - std.utf.encode(buf, c); + import std.utf : encode; + encode(buf, c); } } if (ferror(fp)) @@ -4326,10 +4891,12 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie buf.length = 0; for (int c; (c = FGETWC(fp)) != -1; ) { + import std.utf : encode; + if ((c & ~0x7F) == 0) - buf ~= cast(char)c; + buf ~= cast(char) c; else - std.utf.encode(buf, cast(dchar)c); + encode(buf, cast(dchar) c); if (c == terminator) break; } @@ -4364,9 +4931,10 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie buf.length = 0; // end of file return 0; } - if (s <= buf.length || s <= GC.sizeOf(buf.ptr)) + + if (s <= buf.length) { - buf = buf.ptr[0 .. s]; + buf = buf[0 .. s]; buf[] = lineptr[0 .. s]; } else @@ -4380,11 +4948,10 @@ version (NO_GETDELIM) private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) { import core.stdc.wchar_ : fwide; - import std.utf : encode; FLOCK(fps); scope(exit) FUNLOCK(fps); - auto fp = cast(_iobuf*)fps; + auto fp = cast(_iobuf*) fps; if (orientation == File.Orientation.wide) { /* Stream is in wide characters. @@ -4412,7 +4979,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie } c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); } - std.utf.encode(buf, c); + import std.utf : encode; + encode(buf, c); } } if (ferror(fp)) @@ -4421,13 +4989,14 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie } else version (Posix) { + import std.utf : encode; buf.length = 0; for (int c; (c = FGETWC(fp)) != -1; ) { if ((c & ~0x7F) == 0) - buf ~= cast(char)c; + buf ~= cast(char) c; else - std.utf.encode(buf, cast(dchar)c); + encode(buf, cast(dchar) c); if (c == terminator) break; } @@ -4451,7 +5020,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie buf.length = bufPos; goto endGame; } - buf.ptr[bufPos++] = cast(char) c; + buf[bufPos++] = cast(char) c; if (c == terminator) { // No need to test for errors in file @@ -4462,7 +5031,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie // Then, append to it for (int c; (c = FGETC(fp)) != -1; ) { - buf ~= cast(char)c; + buf ~= cast(char) c; if (c == terminator) { // No need to test for errors in file @@ -4476,6 +5045,37 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie return buf.length; } +@system unittest +{ + static import std.file; + auto deleteme = testFilename(); + scope(exit) std.file.remove(deleteme); + + std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n"); + File f = File(deleteme, "rb"); + + char[] ln = new char[2]; + char* lnptr = ln.ptr; + f.readln(ln); + + assert(ln == "abcd\n"); + char[] t = ln[0 .. 2]; + t ~= 't'; + assert(t == "abt"); + assert(ln == "abcd\n"); // bug 13856: ln stomped to "abtd" + + // it can also stomp the array length + ln = new char[4]; + lnptr = ln.ptr; + f.readln(ln); + assert(ln == "0123456789abcde\n"); + + char[100] buf; + ln = buf[]; + f.readln(ln); + assert(ln == "1234\n"); + assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough +} /** Experimental network access via the File interface @@ -4532,16 +5132,12 @@ version(linux) } } -version(unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe pure +version(unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe { import std.conv : text; + import std.file : deleteme; import std.path : baseName; - // Non-ASCII characters can't be used because of snn.lib @@@BUG8643@@@ - version(DIGITAL_MARS_STDIO) - return text("deleteme-.", baseName(file), ".", line); - else - - // filename intentionally contains non-ASCII (Russian) characters - return text("deleteme-детка.", baseName(file), ".", line); + // filename intentionally contains non-ASCII (Russian) characters for test Issue 7648 + return text(deleteme, "-детка.", baseName(file), ".", line); } diff --git a/std/stdiobase.d b/std/stdiobase.d index f12e409a531..4570a4c9c11 100644 --- a/std/stdiobase.d +++ b/std/stdiobase.d @@ -5,8 +5,8 @@ * std.stdio, to eliminate cyclic construction errors. * * Copyright: Copyright Andrei Alexandrescu 2008 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB erdani.org, Andrei Alexandrescu) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP erdani.org, Andrei Alexandrescu) * Source: $(PHOBOSSRC std/_stdiobase.d) */ /* Copyright Andrei Alexandrescu 2008 - 2009. diff --git a/std/stream.d b/std/stream.d deleted file mode 100644 index c52c27d564a..00000000000 --- a/std/stream.d +++ /dev/null @@ -1,3072 +0,0 @@ -// Written in the D programming language - -/** - * $(RED Warning: This module is considered out-dated and not up to Phobos' - * current standards. It will remain until we have a suitable replacement, - * but be aware that it will not remain long term.) - * - * Source: $(PHOBOSSRC std/_stream.d) - * Macros: - * WIKI = Phobos/StdStream - */ - -/* - * Copyright (c) 2001-2005 - * Pavel "EvilOne" Minayev - * with buffering and endian support added by Ben Hinkle - * with buffered readLine performance improvements by Dave Fladebo - * with opApply inspired by (and mostly copied from) Regan Heath - * with bug fixes and MemoryStream/SliceStream enhancements by Derick Eddington - * - * Permission to use, copy, modify, distribute and sell this software - * and its documentation for any purpose is hereby granted without fee, - * provided that the above copyright notice appear in all copies and - * that both that copyright notice and this permission notice appear - * in supporting documentation. Author makes no representations about - * the suitability of this software for any purpose. It is provided - * "as is" without express or implied warranty. - */ - -module std.stream; - - -import std.internal.cstring; - -/* Class structure: - * InputStream interface for reading - * OutputStream interface for writing - * Stream abstract base of stream implementations - * File an OS file stream - * FilterStream a base-class for wrappers around another stream - * BufferedStream a buffered stream wrapping another stream - * BufferedFile a buffered File - * EndianStream a wrapper stream for swapping byte order and BOMs - * SliceStream a portion of another stream - * MemoryStream a stream entirely stored in main memory - * TArrayStream a stream wrapping an array-like buffer - */ - -/// A base class for stream exceptions. -class StreamException: Exception { - /// Construct a StreamException with given error message. - this(string msg) { super(msg); } -} - -/// Thrown when unable to read data from Stream. -class ReadException: StreamException { - /// Construct a ReadException with given error message. - this(string msg) { super(msg); } -} - -/// Thrown when unable to write data to Stream. -class WriteException: StreamException { - /// Construct a WriteException with given error message. - this(string msg) { super(msg); } -} - -/// Thrown when unable to move Stream pointer. -class SeekException: StreamException { - /// Construct a SeekException with given error message. - this(string msg) { super(msg); } -} - -// seek whence... -enum SeekPos { - Set, - Current, - End -} - -private { - import std.conv; - import std.algorithm; - import std.ascii; - import std.format; - import std.system; // for Endian enumeration - import std.utf; - import core.bitop; // for bswap - import core.vararg; - import std.file; -} - -/// InputStream is the interface for readable streams. - -interface InputStream { - - /*** - * Read exactly size bytes into the buffer. - * - * Throws a ReadException if it is not correct. - */ - void readExact(void* buffer, size_t size); - - /*** - * Read a block of data big enough to fill the given array buffer. - * - * Returns: the actual number of bytes read. Unfilled bytes are not modified. - */ - size_t read(ubyte[] buffer); - - /*** - * Read a basic type or counted string. - * - * Throw a ReadException if it could not be read. - * Outside of byte, ubyte, and char, the format is - * implementation-specific and should not be used except as opposite actions - * to write. - */ - void read(out byte x); - void read(out ubyte x); /// ditto - void read(out short x); /// ditto - void read(out ushort x); /// ditto - void read(out int x); /// ditto - void read(out uint x); /// ditto - void read(out long x); /// ditto - void read(out ulong x); /// ditto - void read(out float x); /// ditto - void read(out double x); /// ditto - void read(out real x); /// ditto - void read(out ifloat x); /// ditto - void read(out idouble x); /// ditto - void read(out ireal x); /// ditto - void read(out cfloat x); /// ditto - void read(out cdouble x); /// ditto - void read(out creal x); /// ditto - void read(out char x); /// ditto - void read(out wchar x); /// ditto - void read(out dchar x); /// ditto - - // reads a string, written earlier by write() - void read(out char[] s); /// ditto - - // reads a Unicode string, written earlier by write() - void read(out wchar[] s); /// ditto - - /*** - * Read a line that is terminated with some combination of carriage return and - * line feed or end-of-file. - * - * The terminators are not included. The wchar version - * is identical. The optional buffer parameter is filled (reallocating - * it if necessary) and a slice of the result is returned. - */ - char[] readLine(); - char[] readLine(char[] result); /// ditto - wchar[] readLineW(); /// ditto - wchar[] readLineW(wchar[] result); /// ditto - - /*** - * Overload foreach statements to read the stream line by line and call the - * supplied delegate with each line or with each line with line number. - * - * The string passed in line may be reused between calls to the delegate. - * Line numbering starts at 1. - * Breaking out of the foreach will leave the stream - * position at the beginning of the next line to be read. - * For example, to echo a file line-by-line with line numbers run: - * ------------------------------------ - * Stream file = new BufferedFile("sample.txt"); - * foreach(ulong n, char[] line; file) - * { - * writefln("line %d: %s", n, line); - * } - * file.close(); - * ------------------------------------ - */ - - // iterate through the stream line-by-line - int opApply(scope int delegate(ref char[] line) dg); - int opApply(scope int delegate(ref ulong n, ref char[] line) dg); /// ditto - int opApply(scope int delegate(ref wchar[] line) dg); /// ditto - int opApply(scope int delegate(ref ulong n, ref wchar[] line) dg); /// ditto - - /// Read a string of the given length, - /// throwing ReadException if there was a problem. - char[] readString(size_t length); - - /*** - * Read a string of the given length, throwing ReadException if there was a - * problem. - * - * The file format is implementation-specific and should not be used - * except as opposite actions to <b>write</b>. - */ - - wchar[] readStringW(size_t length); - - - /*** - * Read and return the next character in the stream. - * - * This is the only method that will handle ungetc properly. - * getcw's format is implementation-specific. - * If EOF is reached then getc returns char.init and getcw returns wchar.init. - */ - - char getc(); - wchar getcw(); /// ditto - - /*** - * Push a character back onto the stream. - * - * They will be returned in first-in last-out order from getc/getcw. - * Only has effect on further calls to getc() and getcw(). - */ - char ungetc(char c); - wchar ungetcw(wchar c); /// ditto - - /*** - * Scan a string from the input using a similar form to C's scanf - * and <a href="std_format.html">std.format</a>. - * - * An argument of type string is interpreted as a format string. - * All other arguments must be pointer types. - * If a format string is not present a default will be supplied computed from - * the base type of the pointer type. An argument of type string* is filled - * (possibly with appending characters) and a slice of the result is assigned - * back into the argument. For example the following readf statements - * are equivalent: - * -------------------------- - * int x; - * double y; - * string s; - * file.readf(&x, " hello ", &y, &s); - * file.readf("%d hello %f %s", &x, &y, &s); - * file.readf("%d hello %f", &x, &y, "%s", &s); - * -------------------------- - */ - int vreadf(TypeInfo[] arguments, va_list args); - int readf(...); /// ditto - - /// Retrieve the number of bytes available for immediate reading. - @property size_t available(); - - /*** - * Return whether the current file position is the same as the end of the - * file. - * - * This does not require actually reading past the end, as with stdio. For - * non-seekable streams this might only return true after attempting to read - * past the end. - */ - - @property bool eof(); - - @property bool isOpen(); /// Return true if the stream is currently open. -} - -/// Interface for writable streams. -interface OutputStream { - - /*** - * Write exactly size bytes from buffer, or throw a WriteException if that - * could not be done. - */ - void writeExact(const void* buffer, size_t size); - - /*** - * Write as much of the buffer as possible, - * returning the number of bytes written. - */ - size_t write(const(ubyte)[] buffer); - - /*** - * Write a basic type. - * - * Outside of byte, ubyte, and char, the format is implementation-specific - * and should only be used in conjunction with read. - * Throw WriteException on error. - */ - void write(byte x); - void write(ubyte x); /// ditto - void write(short x); /// ditto - void write(ushort x); /// ditto - void write(int x); /// ditto - void write(uint x); /// ditto - void write(long x); /// ditto - void write(ulong x); /// ditto - void write(float x); /// ditto - void write(double x); /// ditto - void write(real x); /// ditto - void write(ifloat x); /// ditto - void write(idouble x); /// ditto - void write(ireal x); /// ditto - void write(cfloat x); /// ditto - void write(cdouble x); /// ditto - void write(creal x); /// ditto - void write(char x); /// ditto - void write(wchar x); /// ditto - void write(dchar x); /// ditto - - /*** - * Writes a string, together with its length. - * - * The format is implementation-specific - * and should only be used in conjunction with read. - * Throw WriteException on error. - */ - void write(const(char)[] s); - void write(const(wchar)[] s); /// ditto - - /*** - * Write a line of text, - * appending the line with an operating-system-specific line ending. - * - * Throws WriteException on error. - */ - void writeLine(const(char)[] s); - - /*** - * Write a line of text, - * appending the line with an operating-system-specific line ending. - * - * The format is implementation-specific. - * Throws WriteException on error. - */ - void writeLineW(const(wchar)[] s); - - /*** - * Write a string of text. - * - * Throws WriteException if it could not be fully written. - */ - void writeString(const(char)[] s); - - /*** - * Write a string of text. - * - * The format is implementation-specific. - * Throws WriteException if it could not be fully written. - */ - void writeStringW(const(wchar)[] s); - - /*** - * Print a formatted string into the stream using printf-style syntax, - * returning the number of bytes written. - */ - size_t vprintf(const(char)[] format, va_list args); - size_t printf(const(char)[] format, ...); /// ditto - - /*** - * Print a formatted string into the stream using writef-style syntax. - * References: <a href="std_format.html">std.format</a>. - * Returns: self to chain with other stream commands like flush. - */ - OutputStream writef(...); - OutputStream writefln(...); /// ditto - OutputStream writefx(TypeInfo[] arguments, va_list argptr, int newline = false); /// ditto - - void flush(); /// Flush pending output if appropriate. - void close(); /// Close the stream, flushing output if appropriate. - @property bool isOpen(); /// Return true if the stream is currently open. -} - - -/*** - * Stream is the base abstract class from which the other stream classes derive. - * - * Stream's byte order is the format native to the computer. - * - * Reading: - * These methods require that the readable flag be set. - * Problems with reading result in a ReadException being thrown. - * Stream implements the InputStream interface in addition to the - * readBlock method. - * - * Writing: - * These methods require that the writeable flag be set. Problems with writing - * result in a WriteException being thrown. Stream implements the OutputStream - * interface in addition to the following methods: - * writeBlock - * copyFrom - * copyFrom - * - * Seeking: - * These methods require that the seekable flag be set. - * Problems with seeking result in a SeekException being thrown. - * seek, seekSet, seekCur, seekEnd, position, size, toString, toHash - */ - -// not really abstract, but its instances will do nothing useful -class Stream : InputStream, OutputStream { - private import std.string, std.digest.crc, core.stdc.stdlib, core.stdc.stdio; - - // stream abilities - bool readable = false; /// Indicates whether this stream can be read from. - bool writeable = false; /// Indicates whether this stream can be written to. - bool seekable = false; /// Indicates whether this stream can be seeked within. - protected bool isopen = true; /// Indicates whether this stream is open. - - protected bool readEOF = false; /** Indicates whether this stream is at eof - * after the last read attempt. - */ - - protected bool prevCr = false; /** For a non-seekable stream indicates that - * the last readLine or readLineW ended on a - * '\r' character. - */ - - this() {} - - /*** - * Read up to size bytes into the buffer and return the number of bytes - * actually read. A return value of 0 indicates end-of-file. - */ - abstract size_t readBlock(void* buffer, size_t size); - - // reads block of data of specified size, - // throws ReadException on error - void readExact(void* buffer, size_t size) { - for(;;) { - if (!size) return; - size_t readsize = readBlock(buffer, size); // return 0 on eof - if (readsize == 0) break; - buffer += readsize; - size -= readsize; - } - if (size != 0) - throw new ReadException("not enough data in stream"); - } - - // reads block of data big enough to fill the given - // array, returns actual number of bytes read - size_t read(ubyte[] buffer) { - return readBlock(buffer.ptr, buffer.length); - } - - // read a single value of desired type, - // throw ReadException on error - void read(out byte x) { readExact(&x, x.sizeof); } - void read(out ubyte x) { readExact(&x, x.sizeof); } - void read(out short x) { readExact(&x, x.sizeof); } - void read(out ushort x) { readExact(&x, x.sizeof); } - void read(out int x) { readExact(&x, x.sizeof); } - void read(out uint x) { readExact(&x, x.sizeof); } - void read(out long x) { readExact(&x, x.sizeof); } - void read(out ulong x) { readExact(&x, x.sizeof); } - void read(out float x) { readExact(&x, x.sizeof); } - void read(out double x) { readExact(&x, x.sizeof); } - void read(out real x) { readExact(&x, x.sizeof); } - void read(out ifloat x) { readExact(&x, x.sizeof); } - void read(out idouble x) { readExact(&x, x.sizeof); } - void read(out ireal x) { readExact(&x, x.sizeof); } - void read(out cfloat x) { readExact(&x, x.sizeof); } - void read(out cdouble x) { readExact(&x, x.sizeof); } - void read(out creal x) { readExact(&x, x.sizeof); } - void read(out char x) { readExact(&x, x.sizeof); } - void read(out wchar x) { readExact(&x, x.sizeof); } - void read(out dchar x) { readExact(&x, x.sizeof); } - - // reads a string, written earlier by write() - void read(out char[] s) { - size_t len; - read(len); - s = readString(len); - } - - // reads a Unicode string, written earlier by write() - void read(out wchar[] s) { - size_t len; - read(len); - s = readStringW(len); - } - - // reads a line, terminated by either CR, LF, CR/LF, or EOF - char[] readLine() { - return readLine(null); - } - - // reads a line, terminated by either CR, LF, CR/LF, or EOF - // reusing the memory in buffer if result will fit and otherwise - // allocates a new string - char[] readLine(char[] result) { - size_t strlen = 0; - char ch = getc(); - while (readable) { - switch (ch) { - case '\r': - if (seekable) { - ch = getc(); - if (ch != '\n') - ungetc(ch); - } else { - prevCr = true; - } - goto case; - case '\n': - case char.init: - result.length = strlen; - return result; - - default: - if (strlen < result.length) { - result[strlen] = ch; - } else { - result ~= ch; - } - strlen++; - } - ch = getc(); - } - result.length = strlen; - return result; - } - - // reads a Unicode line, terminated by either CR, LF, CR/LF, - // or EOF; pretty much the same as the above, working with - // wchars rather than chars - wchar[] readLineW() { - return readLineW(null); - } - - // reads a Unicode line, terminated by either CR, LF, CR/LF, - // or EOF; - // fills supplied buffer if line fits and otherwise allocates a new string. - wchar[] readLineW(wchar[] result) { - size_t strlen = 0; - wchar c = getcw(); - while (readable) { - switch (c) { - case '\r': - if (seekable) { - c = getcw(); - if (c != '\n') - ungetcw(c); - } else { - prevCr = true; - } - goto case; - case '\n': - case wchar.init: - result.length = strlen; - return result; - - default: - if (strlen < result.length) { - result[strlen] = c; - } else { - result ~= c; - } - strlen++; - } - c = getcw(); - } - result.length = strlen; - return result; - } - - // iterate through the stream line-by-line - due to Regan Heath - int opApply(scope int delegate(ref char[] line) dg) { - int res = 0; - char[128] buf; - while (!eof) { - char[] line = readLine(buf); - res = dg(line); - if (res) break; - } - return res; - } - - // iterate through the stream line-by-line with line count and string - int opApply(scope int delegate(ref ulong n, ref char[] line) dg) { - int res = 0; - ulong n = 1; - char[128] buf; - while (!eof) { - auto line = readLine(buf); - res = dg(n,line); - if (res) break; - n++; - } - return res; - } - - // iterate through the stream line-by-line with wchar[] - int opApply(scope int delegate(ref wchar[] line) dg) { - int res = 0; - wchar[128] buf; - while (!eof) { - auto line = readLineW(buf); - res = dg(line); - if (res) break; - } - return res; - } - - // iterate through the stream line-by-line with line count and wchar[] - int opApply(scope int delegate(ref ulong n, ref wchar[] line) dg) { - int res = 0; - ulong n = 1; - wchar[128] buf; - while (!eof) { - auto line = readLineW(buf); - res = dg(n,line); - if (res) break; - n++; - } - return res; - } - - // reads a string of given length, throws - // ReadException on error - char[] readString(size_t length) { - char[] result = new char[length]; - readExact(result.ptr, length); - return result; - } - - // reads a Unicode string of given length, throws - // ReadException on error - wchar[] readStringW(size_t length) { - auto result = new wchar[length]; - readExact(result.ptr, result.length * wchar.sizeof); - return result; - } - - // unget buffer - private wchar[] unget; - final bool ungetAvailable() { return unget.length > 1; } - - // reads and returns next character from the stream, - // handles characters pushed back by ungetc() - // returns char.init on eof. - char getc() { - char c; - if (prevCr) { - prevCr = false; - c = getc(); - if (c != '\n') - return c; - } - if (unget.length > 1) { - c = cast(char)unget[unget.length - 1]; - unget.length = unget.length - 1; - } else { - readBlock(&c,1); - } - return c; - } - - // reads and returns next Unicode character from the - // stream, handles characters pushed back by ungetc() - // returns wchar.init on eof. - wchar getcw() { - wchar c; - if (prevCr) { - prevCr = false; - c = getcw(); - if (c != '\n') - return c; - } - if (unget.length > 1) { - c = unget[unget.length - 1]; - unget.length = unget.length - 1; - } else { - void* buf = &c; - size_t n = readBlock(buf,2); - if (n == 1 && readBlock(buf+1,1) == 0) - throw new ReadException("not enough data in stream"); - } - return c; - } - - // pushes back character c into the stream; only has - // effect on further calls to getc() and getcw() - char ungetc(char c) { - if (c == c.init) return c; - // first byte is a dummy so that we never set length to 0 - if (unget.length == 0) - unget.length = 1; - unget ~= c; - return c; - } - - // pushes back Unicode character c into the stream; only - // has effect on further calls to getc() and getcw() - wchar ungetcw(wchar c) { - if (c == c.init) return c; - // first byte is a dummy so that we never set length to 0 - if (unget.length == 0) - unget.length = 1; - unget ~= c; - return c; - } - - int vreadf(TypeInfo[] arguments, va_list args) { - string fmt; - int j = 0; - int count = 0, i = 0; - char c; - bool firstCharacter = true; - while ((j < arguments.length || i < fmt.length) && !eof) { - if(firstCharacter) { - c = getc(); - firstCharacter = false; - } - if (fmt.length == 0 || i == fmt.length) { - i = 0; - if (arguments[j] is typeid(string) || arguments[j] is typeid(char[]) - || arguments[j] is typeid(const(char)[])) { - fmt = va_arg!(string)(args); - j++; - continue; - } else if (arguments[j] is typeid(int*) || - arguments[j] is typeid(byte*) || - arguments[j] is typeid(short*) || - arguments[j] is typeid(long*)) { - fmt = "%d"; - } else if (arguments[j] is typeid(uint*) || - arguments[j] is typeid(ubyte*) || - arguments[j] is typeid(ushort*) || - arguments[j] is typeid(ulong*)) { - fmt = "%d"; - } else if (arguments[j] is typeid(float*) || - arguments[j] is typeid(double*) || - arguments[j] is typeid(real*)) { - fmt = "%f"; - } else if (arguments[j] is typeid(char[]*) || - arguments[j] is typeid(wchar[]*) || - arguments[j] is typeid(dchar[]*)) { - fmt = "%s"; - } else if (arguments[j] is typeid(char*)) { - fmt = "%c"; - } - } - if (fmt[i] == '%') { // a field - i++; - bool suppress = false; - if (fmt[i] == '*') { // suppress assignment - suppress = true; - i++; - } - // read field width - int width = 0; - while (isDigit(fmt[i])) { - width = width * 10 + (fmt[i] - '0'); - i++; - } - if (width == 0) - width = -1; - // skip any modifier if present - if (fmt[i] == 'h' || fmt[i] == 'l' || fmt[i] == 'L') - i++; - // check the typechar and act accordingly - switch (fmt[i]) { - case 'd': // decimal/hexadecimal/octal integer - case 'D': - case 'u': - case 'U': - case 'o': - case 'O': - case 'x': - case 'X': - case 'i': - case 'I': - { - while (isWhite(c)) { - c = getc(); - count++; - } - bool neg = false; - if (c == '-') { - neg = true; - c = getc(); - count++; - } else if (c == '+') { - c = getc(); - count++; - } - char ifmt = cast(char)(fmt[i] | 0x20); - if (ifmt == 'i') { // undetermined base - if (c == '0') { // octal or hex - c = getc(); - count++; - if (c == 'x' || c == 'X') { // hex - ifmt = 'x'; - c = getc(); - count++; - } else { // octal - ifmt = 'o'; - } - } - else // decimal - ifmt = 'd'; - } - long n = 0; - switch (ifmt) - { - case 'd': // decimal - case 'u': { - while (isDigit(c) && width) { - n = n * 10 + (c - '0'); - width--; - c = getc(); - count++; - } - } break; - - case 'o': { // octal - while (isOctalDigit(c) && width) { - n = n * 8 + (c - '0'); - width--; - c = getc(); - count++; - } - } break; - - case 'x': { // hexadecimal - while (isHexDigit(c) && width) { - n *= 0x10; - if (isDigit(c)) - n += c - '0'; - else - n += 0xA + (c | 0x20) - 'a'; - width--; - c = getc(); - count++; - } - } break; - - default: - assert(0); - } - if (neg) - n = -n; - if (arguments[j] is typeid(int*)) { - int* p = va_arg!(int*)(args); - *p = cast(int)n; - } else if (arguments[j] is typeid(short*)) { - short* p = va_arg!(short*)(args); - *p = cast(short)n; - } else if (arguments[j] is typeid(byte*)) { - byte* p = va_arg!(byte*)(args); - *p = cast(byte)n; - } else if (arguments[j] is typeid(long*)) { - long* p = va_arg!(long*)(args); - *p = n; - } else if (arguments[j] is typeid(uint*)) { - uint* p = va_arg!(uint*)(args); - *p = cast(uint)n; - } else if (arguments[j] is typeid(ushort*)) { - ushort* p = va_arg!(ushort*)(args); - *p = cast(ushort)n; - } else if (arguments[j] is typeid(ubyte*)) { - ubyte* p = va_arg!(ubyte*)(args); - *p = cast(ubyte)n; - } else if (arguments[j] is typeid(ulong*)) { - ulong* p = va_arg!(ulong*)(args); - *p = cast(ulong)n; - } - j++; - i++; - } break; - - case 'f': // float - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - { - while (isWhite(c)) { - c = getc(); - count++; - } - bool neg = false; - if (c == '-') { - neg = true; - c = getc(); - count++; - } else if (c == '+') { - c = getc(); - count++; - } - real r = 0; - while (isDigit(c) && width) { - r = r * 10 + (c - '0'); - width--; - c = getc(); - count++; - } - if (width && c == '.') { - width--; - c = getc(); - count++; - double frac = 1; - while (isDigit(c) && width) { - r = r * 10 + (c - '0'); - frac *= 10; - width--; - c = getc(); - count++; - } - r /= frac; - } - if (width && (c == 'e' || c == 'E')) { - width--; - c = getc(); - count++; - if (width) { - bool expneg = false; - if (c == '-') { - expneg = true; - width--; - c = getc(); - count++; - } else if (c == '+') { - width--; - c = getc(); - count++; - } - real exp = 0; - while (isDigit(c) && width) { - exp = exp * 10 + (c - '0'); - width--; - c = getc(); - count++; - } - if (expneg) { - while (exp--) - r /= 10; - } else { - while (exp--) - r *= 10; - } - } - } - if(width && (c == 'n' || c == 'N')) { - width--; - c = getc(); - count++; - if(width && (c == 'a' || c == 'A')) { - width--; - c = getc(); - count++; - if(width && (c == 'n' || c == 'N')) { - width--; - c = getc(); - count++; - r = real.nan; - } - } - } - if(width && (c == 'i' || c == 'I')) { - width--; - c = getc(); - count++; - if(width && (c == 'n' || c == 'N')) { - width--; - c = getc(); - count++; - if(width && (c == 'f' || c == 'F')) { - width--; - c = getc(); - count++; - r = real.infinity; - } - } - } - if (neg) - r = -r; - if (arguments[j] is typeid(float*)) { - float* p = va_arg!(float*)(args); - *p = r; - } else if (arguments[j] is typeid(double*)) { - double* p = va_arg!(double*)(args); - *p = r; - } else if (arguments[j] is typeid(real*)) { - real* p = va_arg!(real*)(args); - *p = r; - } - j++; - i++; - } break; - - case 's': { // string - while (isWhite(c)) { - c = getc(); - count++; - } - char[] s; - char[]* p; - size_t strlen; - if (arguments[j] is typeid(char[]*)) { - p = va_arg!(char[]*)(args); - s = *p; - } - while (!isWhite(c) && c != char.init) { - if (strlen < s.length) { - s[strlen] = c; - } else { - s ~= c; - } - strlen++; - c = getc(); - count++; - } - s = s[0 .. strlen]; - if (arguments[j] is typeid(char[]*)) { - *p = s; - } else if (arguments[j] is typeid(char*)) { - s ~= 0; - auto q = va_arg!(char*)(args); - q[0 .. s.length] = s[]; - } else if (arguments[j] is typeid(wchar[]*)) { - auto q = va_arg!(const(wchar)[]*)(args); - *q = toUTF16(s); - } else if (arguments[j] is typeid(dchar[]*)) { - auto q = va_arg!(const(dchar)[]*)(args); - *q = toUTF32(s); - } - j++; - i++; - } break; - - case 'c': { // character(s) - char* s = va_arg!(char*)(args); - if (width < 0) - width = 1; - else - while (isWhite(c)) { - c = getc(); - count++; - } - while (width-- && !eof) { - *(s++) = c; - c = getc(); - count++; - } - j++; - i++; - } break; - - case 'n': { // number of chars read so far - int* p = va_arg!(int*)(args); - *p = count; - j++; - i++; - } break; - - default: // read character as is - goto nws; - } - } else if (isWhite(fmt[i])) { // skip whitespace - while (isWhite(c)) - c = getc(); - i++; - } else { // read character as is - nws: - if (fmt[i] != c) - break; - c = getc(); - i++; - } - } - ungetc(c); - return count; - } - - int readf(...) { - return vreadf(_arguments, _argptr); - } - - // returns estimated number of bytes available for immediate reading - @property size_t available() { return 0; } - - /*** - * Write up to size bytes from buffer in the stream, returning the actual - * number of bytes that were written. - */ - abstract size_t writeBlock(const void* buffer, size_t size); - - // writes block of data of specified size, - // throws WriteException on error - void writeExact(const void* buffer, size_t size) { - const(void)* p = buffer; - for(;;) { - if (!size) return; - size_t writesize = writeBlock(p, size); - if (writesize == 0) break; - p += writesize; - size -= writesize; - } - if (size != 0) - throw new WriteException("unable to write to stream"); - } - - // writes the given array of bytes, returns - // actual number of bytes written - size_t write(const(ubyte)[] buffer) { - return writeBlock(buffer.ptr, buffer.length); - } - - // write a single value of desired type, - // throw WriteException on error - void write(byte x) { writeExact(&x, x.sizeof); } - void write(ubyte x) { writeExact(&x, x.sizeof); } - void write(short x) { writeExact(&x, x.sizeof); } - void write(ushort x) { writeExact(&x, x.sizeof); } - void write(int x) { writeExact(&x, x.sizeof); } - void write(uint x) { writeExact(&x, x.sizeof); } - void write(long x) { writeExact(&x, x.sizeof); } - void write(ulong x) { writeExact(&x, x.sizeof); } - void write(float x) { writeExact(&x, x.sizeof); } - void write(double x) { writeExact(&x, x.sizeof); } - void write(real x) { writeExact(&x, x.sizeof); } - void write(ifloat x) { writeExact(&x, x.sizeof); } - void write(idouble x) { writeExact(&x, x.sizeof); } - void write(ireal x) { writeExact(&x, x.sizeof); } - void write(cfloat x) { writeExact(&x, x.sizeof); } - void write(cdouble x) { writeExact(&x, x.sizeof); } - void write(creal x) { writeExact(&x, x.sizeof); } - void write(char x) { writeExact(&x, x.sizeof); } - void write(wchar x) { writeExact(&x, x.sizeof); } - void write(dchar x) { writeExact(&x, x.sizeof); } - - // writes a string, together with its length - void write(const(char)[] s) { - write(s.length); - writeString(s); - } - - // writes a Unicode string, together with its length - void write(const(wchar)[] s) { - write(s.length); - writeStringW(s); - } - - // writes a line, throws WriteException on error - void writeLine(const(char)[] s) { - writeString(s); - version (Windows) - writeString("\r\n"); - else version (Mac) - writeString("\r"); - else - writeString("\n"); - } - - // writes a Unicode line, throws WriteException on error - void writeLineW(const(wchar)[] s) { - writeStringW(s); - version (Windows) - writeStringW("\r\n"); - else version (Mac) - writeStringW("\r"); - else - writeStringW("\n"); - } - - // writes a string, throws WriteException on error - void writeString(const(char)[] s) { - writeExact(s.ptr, s.length); - } - - // writes a Unicode string, throws WriteException on error - void writeStringW(const(wchar)[] s) { - writeExact(s.ptr, s.length * wchar.sizeof); - } - - // writes data to stream using vprintf() syntax, - // returns number of bytes written - size_t vprintf(const(char)[] format, va_list args) { - // shamelessly stolen from OutBuffer, - // by Walter's permission - char[1024] buffer; - char* p = buffer.ptr; - // Can't use `tempCString()` here as it will result in compilation error: - // "cannot mix core.std.stdlib.alloca() and exception handling". - auto f = toStringz(format); - size_t psize = buffer.length; - size_t count; - while (true) { - version (Windows) { - count = _vsnprintf(p, psize, f, args); - if (count != -1) - break; - psize *= 2; - p = cast(char*) alloca(psize); - } else version (Posix) { - count = vsnprintf(p, psize, f, args); - if (count == -1) - psize *= 2; - else if (count >= psize) - psize = count + 1; - else - break; - p = cast(char*) alloca(psize); - } else - throw new Exception("unsupported platform"); - } - writeString(p[0 .. count]); - return count; - } - - // writes data to stream using printf() syntax, - // returns number of bytes written - size_t printf(const(char)[] format, ...) { - va_list ap; - va_start(ap, format); - auto result = vprintf(format, ap); - va_end(ap); - return result; - } - - private void doFormatCallback(dchar c) { - char[4] buf; - auto b = std.utf.toUTF8(buf, c); - writeString(b); - } - - // writes data to stream using writef() syntax, - OutputStream writef(...) { - return writefx(_arguments,_argptr,0); - } - - // writes data with trailing newline - OutputStream writefln(...) { - return writefx(_arguments,_argptr,1); - } - - // writes data with optional trailing newline - OutputStream writefx(TypeInfo[] arguments, va_list argptr, int newline=false) { - doFormat(&doFormatCallback,arguments,argptr); - if (newline) - writeLine(""); - return this; - } - - /*** - * Copies all data from s into this stream. - * This may throw ReadException or WriteException on failure. - * This restores the file position of s so that it is unchanged. - */ - void copyFrom(Stream s) { - if (seekable) { - ulong pos = s.position; - s.position = 0; - copyFrom(s, s.size); - s.position = pos; - } else { - ubyte[128] buf; - while (!s.eof) { - size_t m = s.readBlock(buf.ptr, buf.length); - writeExact(buf.ptr, m); - } - } - } - - /*** - * Copy a specified number of bytes from the given stream into this one. - * This may throw ReadException or WriteException on failure. - * Unlike the previous form, this doesn't restore the file position of s. - */ - void copyFrom(Stream s, ulong count) { - ubyte[128] buf; - while (count > 0) { - size_t n = cast(size_t)(count<buf.length ? count : buf.length); - s.readExact(buf.ptr, n); - writeExact(buf.ptr, n); - count -= n; - } - } - - /*** - * Change the current position of the stream. whence is either SeekPos.Set, in - which case the offset is an absolute index from the beginning of the stream, - SeekPos.Current, in which case the offset is a delta from the current - position, or SeekPos.End, in which case the offset is a delta from the end of - the stream (negative or zero offsets only make sense in that case). This - returns the new file position. - */ - abstract ulong seek(long offset, SeekPos whence); - - /*** - * Aliases for their normal seek counterparts. - */ - ulong seekSet(long offset) { return seek (offset, SeekPos.Set); } - ulong seekCur(long offset) { return seek (offset, SeekPos.Current); } /// ditto - ulong seekEnd(long offset) { return seek (offset, SeekPos.End); } /// ditto - - /*** - * Sets file position. Equivalent to calling seek(pos, SeekPos.Set). - */ - @property void position(ulong pos) { seek(cast(long)pos, SeekPos.Set); } - - /*** - * Returns current file position. Equivalent to seek(0, SeekPos.Current). - */ - @property ulong position() { return seek(0, SeekPos.Current); } - - /*** - * Retrieve the size of the stream in bytes. - * The stream must be seekable or a SeekException is thrown. - */ - @property ulong size() { - assertSeekable(); - ulong pos = position, result = seek(0, SeekPos.End); - position = pos; - return result; - } - - // returns true if end of stream is reached, false otherwise - @property bool eof() { - // for unseekable streams we only know the end when we read it - if (readEOF && !ungetAvailable()) - return true; - else if (seekable) - return position == size; - else - return false; - } - - // returns true if the stream is open - @property bool isOpen() { return isopen; } - - // flush the buffer if writeable - void flush() { - if (unget.length > 1) - unget.length = 1; // keep at least 1 so that data ptr stays - } - - // close the stream somehow; the default just flushes the buffer - void close() { - if (isopen) - flush(); - readEOF = prevCr = isopen = readable = writeable = seekable = false; - } - - /*** - * Read the entire stream and return it as a string. - * If the stream is not seekable the contents from the current position to eof - * is read and returned. - */ - override string toString() { - if (!readable) - return super.toString(); - try - { - size_t pos; - size_t rdlen; - size_t blockSize; - char[] result; - if (seekable) { - ulong orig_pos = position; - scope(exit) position = orig_pos; - position = 0; - blockSize = cast(size_t)size; - result = new char[blockSize]; - while (blockSize > 0) { - rdlen = readBlock(&result[pos], blockSize); - pos += rdlen; - blockSize -= rdlen; - } - } else { - blockSize = 4096; - result = new char[blockSize]; - while ((rdlen = readBlock(&result[pos], blockSize)) > 0) { - pos += rdlen; - blockSize += rdlen; - result.length = result.length + blockSize; - } - } - return cast(string) result[0 .. pos]; - } - catch (Throwable) - { - return super.toString(); - } - } - - /*** - * Get a hash of the stream by reading each byte and using it in a CRC-32 - * checksum. - */ - override size_t toHash() @trusted { - if (!readable || !seekable) - return super.toHash(); - try - { - ulong pos = position; - scope(exit) position = pos; - CRC32 crc; - crc.start(); - position = 0; - ulong len = size; - for (ulong i = 0; i < len; i++) - { - ubyte c; - read(c); - crc.put(c); - } - - union resUnion - { - size_t hash; - ubyte[4] crcVal; - } - resUnion res; - res.crcVal = crc.finish(); - return res.hash; - } - catch (Throwable) - { - return super.toHash(); - } - } - - // helper for checking that the stream is readable - final protected void assertReadable() { - if (!readable) - throw new ReadException("Stream is not readable"); - } - // helper for checking that the stream is writeable - final protected void assertWriteable() { - if (!writeable) - throw new WriteException("Stream is not writeable"); - } - // helper for checking that the stream is seekable - final protected void assertSeekable() { - if (!seekable) - throw new SeekException("Stream is not seekable"); - } - - unittest { // unit test for Issue 3363 - import std.stdio; - immutable fileName = std.file.deleteme ~ "-issue3363.txt"; - auto w = File(fileName, "w"); - scope (exit) remove(fileName.ptr); - w.write("one two three"); - w.close(); - auto r = File(fileName, "r"); - const(char)[] constChar; - string str; - char[] chars; - r.readf("%s %s %s", &constChar, &str, &chars); - assert (constChar == "one", constChar); - assert (str == "two", str); - assert (chars == "three", chars); - } - - unittest { //unit tests for Issue 1668 - void tryFloatRoundtrip(float x, string fmt = "", string pad = "") { - auto s = new MemoryStream(); - s.writef(fmt, x, pad); - s.position = 0; - - float f; - assert(s.readf(&f)); - assert(x == f || (x != x && f != f)); //either equal or both NaN - } - - tryFloatRoundtrip(1.0); - tryFloatRoundtrip(1.0, "%f"); - tryFloatRoundtrip(1.0, "", " "); - tryFloatRoundtrip(1.0, "%f", " "); - - tryFloatRoundtrip(3.14); - tryFloatRoundtrip(3.14, "%f"); - tryFloatRoundtrip(3.14, "", " "); - tryFloatRoundtrip(3.14, "%f", " "); - - float nan = float.nan; - tryFloatRoundtrip(nan); - tryFloatRoundtrip(nan, "%f"); - tryFloatRoundtrip(nan, "", " "); - tryFloatRoundtrip(nan, "%f", " "); - - float inf = 1.0/0.0; - tryFloatRoundtrip(inf); - tryFloatRoundtrip(inf, "%f"); - tryFloatRoundtrip(inf, "", " "); - tryFloatRoundtrip(inf, "%f", " "); - - tryFloatRoundtrip(-inf); - tryFloatRoundtrip(-inf,"%f"); - tryFloatRoundtrip(-inf, "", " "); - tryFloatRoundtrip(-inf, "%f", " "); - } -} - -/*** - * A base class for streams that wrap a source stream with additional - * functionality. - * - * The method implementations forward read/write/seek calls to the - * source stream. A FilterStream can change the position of the source stream - * arbitrarily and may not keep the source stream state in sync with the - * FilterStream, even upon flushing and closing the FilterStream. It is - * recommended to not make any assumptions about the state of the source position - * and read/write state after a FilterStream has acted upon it. Specifc subclasses - * of FilterStream should document how they modify the source stream and if any - * invariants hold true between the source and filter. - */ -class FilterStream : Stream { - private Stream s; // source stream - - /// Property indicating when this stream closes to close the source stream as - /// well. - /// Defaults to true. - bool nestClose = true; - - /// Construct a FilterStream for the given source. - this(Stream source) { - s = source; - resetSource(); - } - - // source getter/setter - - /*** - * Get the current source stream. - */ - final Stream source(){return s;} - - /*** - * Set the current source stream. - * - * Setting the source stream closes this stream before attaching the new - * source. Attaching an open stream reopens this stream and resets the stream - * state. - */ - void source(Stream s) { - close(); - this.s = s; - resetSource(); - } - - /*** - * Indicates the source stream changed state and that this stream should reset - * any readable, writeable, seekable, isopen and buffering flags. - */ - void resetSource() { - if (s !is null) { - readable = s.readable; - writeable = s.writeable; - seekable = s.seekable; - isopen = s.isOpen; - } else { - readable = writeable = seekable = false; - isopen = false; - } - readEOF = prevCr = false; - } - - // read from source - override size_t readBlock(void* buffer, size_t size) { - size_t res = s.readBlock(buffer,size); - readEOF = res == 0; - return res; - } - - // write to source - override size_t writeBlock(const void* buffer, size_t size) { - return s.writeBlock(buffer,size); - } - - // close stream - override void close() { - if (isopen) { - super.close(); - if (nestClose) - s.close(); - } - } - - // seek on source - override ulong seek(long offset, SeekPos whence) { - readEOF = false; - return s.seek(offset,whence); - } - - override @property size_t available() { return s.available; } - override void flush() { super.flush(); s.flush(); } -} - -/*** - * This subclass is for buffering a source stream. - * - * A buffered stream must be - * closed explicitly to ensure the final buffer content is written to the source - * stream. The source stream position is changed according to the block size so - * reading or writing to the BufferedStream may not change the source stream - * position by the same amount. - */ -class BufferedStream : FilterStream { - ubyte[] buffer; // buffer, if any - size_t bufferCurPos; // current position in buffer - size_t bufferLen; // amount of data in buffer - bool bufferDirty = false; - size_t bufferSourcePos; // position in buffer of source stream position - ulong streamPos; // absolute position in source stream - - /* Example of relationship between fields: - * - * s ...01234567890123456789012EOF - * buffer |-- --| - * bufferCurPos | - * bufferLen |-- --| - * bufferSourcePos | - * - */ - - invariant() { - assert(bufferSourcePos <= bufferLen); - assert(bufferCurPos <= bufferLen); - assert(bufferLen <= buffer.length); - } - - enum size_t DefaultBufferSize = 8192; - - /*** - * Create a buffered stream for the stream source with the buffer size - * bufferSize. - */ - this(Stream source, size_t bufferSize = DefaultBufferSize) { - super(source); - if (bufferSize) - buffer = new ubyte[bufferSize]; - } - - override protected void resetSource() { - super.resetSource(); - streamPos = 0; - bufferLen = bufferSourcePos = bufferCurPos = 0; - bufferDirty = false; - } - - // reads block of data of specified size using any buffered data - // returns actual number of bytes read - override size_t readBlock(void* result, size_t len) { - if (len == 0) return 0; - - assertReadable(); - - ubyte* outbuf = cast(ubyte*)result; - size_t readsize = 0; - - if (bufferCurPos + len < bufferLen) { - // buffer has all the data so copy it - outbuf[0 .. len] = buffer[bufferCurPos .. bufferCurPos+len]; - bufferCurPos += len; - readsize = len; - goto ExitRead; - } - - readsize = bufferLen - bufferCurPos; - if (readsize > 0) { - // buffer has some data so copy what is left - outbuf[0 .. readsize] = buffer[bufferCurPos .. bufferLen]; - outbuf += readsize; - bufferCurPos += readsize; - len -= readsize; - } - - flush(); - - if (len >= buffer.length) { - // buffer can't hold the data so fill output buffer directly - size_t siz = super.readBlock(outbuf, len); - readsize += siz; - streamPos += siz; - } else { - // read a new block into buffer - bufferLen = super.readBlock(buffer.ptr, buffer.length); - if (bufferLen < len) len = bufferLen; - outbuf[0 .. len] = buffer[0 .. len]; - bufferSourcePos = bufferLen; - streamPos += bufferLen; - bufferCurPos = len; - readsize += len; - } - - ExitRead: - return readsize; - } - - // write block of data of specified size - // returns actual number of bytes written - override size_t writeBlock(const void* result, size_t len) { - assertWriteable(); - - ubyte* buf = cast(ubyte*)result; - size_t writesize = 0; - - if (bufferLen == 0) { - // buffer is empty so fill it if possible - if ((len < buffer.length) && (readable)) { - // read in data if the buffer is currently empty - bufferLen = s.readBlock(buffer.ptr, buffer.length); - bufferSourcePos = bufferLen; - streamPos += bufferLen; - - } else if (len >= buffer.length) { - // buffer can't hold the data so write it directly and exit - writesize = s.writeBlock(buf,len); - streamPos += writesize; - goto ExitWrite; - } - } - - if (bufferCurPos + len <= buffer.length) { - // buffer has space for all the data so copy it and exit - buffer[bufferCurPos .. bufferCurPos+len] = buf[0 .. len]; - bufferCurPos += len; - bufferLen = bufferCurPos > bufferLen ? bufferCurPos : bufferLen; - writesize = len; - bufferDirty = true; - goto ExitWrite; - } - - writesize = buffer.length - bufferCurPos; - if (writesize > 0) { - // buffer can take some data - buffer[bufferCurPos .. buffer.length] = buf[0 .. writesize]; - bufferCurPos = bufferLen = buffer.length; - buf += writesize; - len -= writesize; - bufferDirty = true; - } - - assert(bufferCurPos == buffer.length); - assert(bufferLen == buffer.length); - - flush(); - - writesize += writeBlock(buf,len); - - ExitWrite: - return writesize; - } - - override ulong seek(long offset, SeekPos whence) { - assertSeekable(); - - if ((whence != SeekPos.Current) || - (offset + bufferCurPos < 0) || - (offset + bufferCurPos >= bufferLen)) { - flush(); - streamPos = s.seek(offset,whence); - } else { - bufferCurPos += offset; - } - readEOF = false; - return streamPos-bufferSourcePos+bufferCurPos; - } - - // Buffered readLine - Dave Fladebo - // reads a line, terminated by either CR, LF, CR/LF, or EOF - // reusing the memory in buffer if result will fit, otherwise - // will reallocate (using concatenation) - template TreadLine(T) { - T[] readLine(T[] inBuffer) - { - size_t lineSize = 0; - bool haveCR = false; - T c = '\0'; - size_t idx = 0; - ubyte* pc = cast(ubyte*)&c; - - L0: - for(;;) { - size_t start = bufferCurPos; - L1: - foreach(ubyte b; buffer[start .. bufferLen]) { - bufferCurPos++; - pc[idx] = b; - if(idx < T.sizeof - 1) { - idx++; - continue L1; - } else { - idx = 0; - } - if(c == '\n' || haveCR) { - if(haveCR && c != '\n') bufferCurPos--; - break L0; - } else { - if(c == '\r') { - haveCR = true; - } else { - if(lineSize < inBuffer.length) { - inBuffer[lineSize] = c; - } else { - inBuffer ~= c; - } - lineSize++; - } - } - } - flush(); - size_t res = super.readBlock(buffer.ptr, buffer.length); - if(!res) break L0; // EOF - bufferSourcePos = bufferLen = res; - streamPos += res; - } - return inBuffer[0 .. lineSize]; - } - } // template TreadLine(T) - - override char[] readLine(char[] inBuffer) { - if (ungetAvailable()) - return super.readLine(inBuffer); - else - return TreadLine!(char).readLine(inBuffer); - } - alias readLine = Stream.readLine; - - override wchar[] readLineW(wchar[] inBuffer) { - if (ungetAvailable()) - return super.readLineW(inBuffer); - else - return TreadLine!(wchar).readLine(inBuffer); - } - alias readLineW = Stream.readLineW; - - override void flush() - out { - assert(bufferCurPos == 0); - assert(bufferSourcePos == 0); - assert(bufferLen == 0); - } - body { - if (writeable && bufferDirty) { - if (bufferSourcePos != 0 && seekable) { - // move actual file pointer to front of buffer - streamPos = s.seek(-bufferSourcePos, SeekPos.Current); - } - // write buffer out - bufferSourcePos = s.writeBlock(buffer.ptr, bufferLen); - if (bufferSourcePos != bufferLen) { - throw new WriteException("Unable to write to stream"); - } - } - super.flush(); - long diff = cast(long)bufferCurPos-bufferSourcePos; - if (diff != 0 && seekable) { - // move actual file pointer to current position - streamPos = s.seek(diff, SeekPos.Current); - } - // reset buffer data to be empty - bufferSourcePos = bufferCurPos = bufferLen = 0; - bufferDirty = false; - } - - // returns true if end of stream is reached, false otherwise - override @property bool eof() { - if ((buffer.length == 0) || !readable) { - return super.eof; - } - // some simple tests to avoid flushing - if (ungetAvailable() || bufferCurPos != bufferLen) - return false; - if (bufferLen == buffer.length) - flush(); - size_t res = super.readBlock(&buffer[bufferLen],buffer.length-bufferLen); - bufferSourcePos += res; - bufferLen += res; - streamPos += res; - return readEOF; - } - - // returns size of stream - override @property ulong size() { - if (bufferDirty) flush(); - return s.size; - } - - // returns estimated number of bytes available for immediate reading - override @property size_t available() { - return bufferLen - bufferCurPos; - } -} - -/// An exception for File errors. -class StreamFileException: StreamException { - /// Construct a StreamFileException with given error message. - this(string msg) { super(msg); } -} - -/// An exception for errors during File.open. -class OpenException: StreamFileException { - /// Construct an OpenFileException with given error message. - this(string msg) { super(msg); } -} - -/// Specifies the $(LREF File) access mode used when opening the file. -enum FileMode { - In = 1, /// Opens the file for reading. - Out = 2, /// Opens the file for writing. - OutNew = 6, /// Opens the file for writing, creates a new file if it doesn't exist. - Append = 10 /// Opens the file for writing, appending new data to the end of the file. -} - -version (Windows) { - private import core.sys.windows.windows; - extern (Windows) { - void FlushFileBuffers(HANDLE hFile); - DWORD GetFileType(HANDLE hFile); - } -} -version (Posix) { - private import core.sys.posix.fcntl; - private import core.sys.posix.unistd; - alias HANDLE = int; -} - -/// This subclass is for unbuffered file system streams. -class File: Stream { - - version (Windows) { - private HANDLE hFile; - } - version (Posix) { - private HANDLE hFile = -1; - } - - this() { - super(); - version (Windows) { - hFile = null; - } - version (Posix) { - hFile = -1; - } - isopen = false; - } - - // opens existing handle; use with care! - this(HANDLE hFile, FileMode mode) { - super(); - this.hFile = hFile; - readable = cast(bool)(mode & FileMode.In); - writeable = cast(bool)(mode & FileMode.Out); - version(Windows) { - seekable = GetFileType(hFile) == 1; // FILE_TYPE_DISK - } else { - auto result = lseek(hFile, 0, 0); - seekable = (result != ~0); - } - } - - /*** - * Create the stream with no open file, an open file in read mode, or an open - * file with explicit file mode. - * mode, if given, is a combination of FileMode.In - * (indicating a file that can be read) and FileMode.Out (indicating a file - * that can be written). - * Opening a file for reading that doesn't exist will error. - * Opening a file for writing that doesn't exist will create the file. - * The FileMode.OutNew mode will open the file for writing and reset the - * length to zero. - * The FileMode.Append mode will open the file for writing and move the - * file position to the end of the file. - */ - this(string filename, FileMode mode = FileMode.In) - { - this(); - open(filename, mode); - } - - - /*** - * Open a file for the stream, in an identical manner to the constructors. - * If an error occurs an OpenException is thrown. - */ - void open(string filename, FileMode mode = FileMode.In) { - close(); - int access, share, createMode; - parseMode(mode, access, share, createMode); - seekable = true; - readable = cast(bool)(mode & FileMode.In); - writeable = cast(bool)(mode & FileMode.Out); - version (Windows) { - hFile = CreateFileW(filename.tempCStringW(), access, share, - null, createMode, 0, null); - isopen = hFile != INVALID_HANDLE_VALUE; - } - version (Posix) { - hFile = core.sys.posix.fcntl.open(filename.tempCString(), access | createMode, share); - isopen = hFile != -1; - } - if (!isopen) - throw new OpenException(cast(string) ("Cannot open or create file '" - ~ filename ~ "'")); - else if ((mode & FileMode.Append) == FileMode.Append) - seekEnd(0); - } - - private void parseMode(int mode, - out int access, - out int share, - out int createMode) { - version (Windows) { - share |= FILE_SHARE_READ | FILE_SHARE_WRITE; - if (mode & FileMode.In) { - access |= GENERIC_READ; - createMode = OPEN_EXISTING; - } - if (mode & FileMode.Out) { - access |= GENERIC_WRITE; - createMode = OPEN_ALWAYS; // will create if not present - } - if ((mode & FileMode.OutNew) == FileMode.OutNew) { - createMode = CREATE_ALWAYS; // resets file - } - } - version (Posix) { - share = octal!666; - if (mode & FileMode.In) { - access = O_RDONLY; - } - if (mode & FileMode.Out) { - createMode = O_CREAT; // will create if not present - access = O_WRONLY; - } - if (access == (O_WRONLY | O_RDONLY)) { - access = O_RDWR; - } - if ((mode & FileMode.OutNew) == FileMode.OutNew) { - access |= O_TRUNC; // resets file - } - } - } - - /// Create a file for writing. - void create(string filename) { - create(filename, FileMode.OutNew); - } - - /// ditto - void create(string filename, FileMode mode) { - close(); - open(filename, mode | FileMode.OutNew); - } - - /// Close the current file if it is open; otherwise it does nothing. - override void close() { - if (isopen) { - super.close(); - if (hFile) { - version (Windows) { - CloseHandle(hFile); - hFile = null; - } else version (Posix) { - core.sys.posix.unistd.close(hFile); - hFile = -1; - } - } - } - } - - // destructor, closes file if still opened - ~this() { close(); } - - version (Windows) { - // returns size of stream - override @property ulong size() { - assertSeekable(); - uint sizehi; - uint sizelow = GetFileSize(hFile,&sizehi); - return (cast(ulong)sizehi << 32) + sizelow; - } - } - - override size_t readBlock(void* buffer, size_t size) { - assertReadable(); - version (Windows) { - auto dwSize = to!DWORD(size); - ReadFile(hFile, buffer, dwSize, &dwSize, null); - size = dwSize; - } else version (Posix) { - size = core.sys.posix.unistd.read(hFile, buffer, size); - if (size == -1) - size = 0; - } - readEOF = (size == 0); - return size; - } - - override size_t writeBlock(const void* buffer, size_t size) { - assertWriteable(); - version (Windows) { - auto dwSize = to!DWORD(size); - WriteFile(hFile, buffer, dwSize, &dwSize, null); - size = dwSize; - } else version (Posix) { - size = core.sys.posix.unistd.write(hFile, buffer, size); - if (size == -1) - size = 0; - } - return size; - } - - override ulong seek(long offset, SeekPos rel) { - assertSeekable(); - version (Windows) { - int hi = cast(int)(offset>>32); - uint low = SetFilePointer(hFile, cast(int)offset, &hi, rel); - if ((low == INVALID_SET_FILE_POINTER) && (GetLastError() != 0)) - throw new SeekException("unable to move file pointer"); - ulong result = (cast(ulong)hi << 32) + low; - } else version (Posix) { - auto result = lseek(hFile, cast(off_t)offset, rel); - if (result == cast(typeof(result))-1) - throw new SeekException("unable to move file pointer"); - } - readEOF = false; - return cast(ulong)result; - } - - /*** - * For a seekable file returns the difference of the size and position and - * otherwise returns 0. - */ - - override @property size_t available() { - if (seekable) { - ulong lavail = size - position; - if (lavail > size_t.max) lavail = size_t.max; - return cast(size_t)lavail; - } - return 0; - } - - // OS-specific property, just in case somebody wants - // to mess with underlying API - HANDLE handle() { return hFile; } - - // run a few tests - unittest { - import std.internal.cstring : tempCString; - - File file = new File; - int i = 666; - auto stream_file = std.file.deleteme ~ "-stream.$$$"; - file.create(stream_file); - // should be ok to write - assert(file.writeable); - file.writeLine("Testing stream.d:"); - file.writeString("Hello, world!"); - file.write(i); - // string#1 + string#2 + int should give exacly that - version (Windows) - assert(file.position == 19 + 13 + 4); - version (Posix) - assert(file.position == 18 + 13 + 4); - // we must be at the end of file - assert(file.eof); - file.close(); - // no operations are allowed when file is closed - assert(!file.readable && !file.writeable && !file.seekable); - file.open(stream_file); - // should be ok to read - assert(file.readable); - assert(file.available == file.size); - char[] line = file.readLine(); - char[] exp = "Testing stream.d:".dup; - assert(line[0] == 'T'); - assert(line.length == exp.length); - assert(!std.algorithm.cmp(line, "Testing stream.d:")); - // jump over "Hello, " - file.seek(7, SeekPos.Current); - version (Windows) - assert(file.position == 19 + 7); - version (Posix) - assert(file.position == 18 + 7); - assert(!std.algorithm.cmp(file.readString(6), "world!")); - i = 0; file.read(i); - assert(i == 666); - // string#1 + string#2 + int should give exacly that - version (Windows) - assert(file.position == 19 + 13 + 4); - version (Posix) - assert(file.position == 18 + 13 + 4); - // we must be at the end of file - assert(file.eof); - file.close(); - file.open(stream_file,FileMode.OutNew | FileMode.In); - file.writeLine("Testing stream.d:"); - file.writeLine("Another line"); - file.writeLine(""); - file.writeLine("That was blank"); - file.position = 0; - char[][] lines; - foreach(char[] line; file) { - lines ~= line.dup; - } - assert( lines.length == 4 ); - assert( lines[0] == "Testing stream.d:"); - assert( lines[1] == "Another line"); - assert( lines[2] == ""); - assert( lines[3] == "That was blank"); - file.position = 0; - lines = new char[][4]; - foreach(ulong n, char[] line; file) { - lines[cast(size_t)(n-1)] = line.dup; - } - assert( lines[0] == "Testing stream.d:"); - assert( lines[1] == "Another line"); - assert( lines[2] == ""); - assert( lines[3] == "That was blank"); - file.close(); - remove(stream_file.tempCString()); - } -} - -/*** - * This subclass is for buffered file system streams. - * - * It is a convenience class for wrapping a File in a BufferedStream. - * A buffered stream must be closed explicitly to ensure the final buffer - * content is written to the file. - */ -class BufferedFile: BufferedStream { - - /// opens file for reading - this() { super(new File()); } - - /// opens file in requested mode and buffer size - this(string filename, FileMode mode = FileMode.In, - size_t bufferSize = DefaultBufferSize) { - super(new File(filename,mode),bufferSize); - } - - /// opens file for reading with requested buffer size - this(File file, size_t bufferSize = DefaultBufferSize) { - super(file,bufferSize); - } - - /// opens existing handle; use with care! - this(HANDLE hFile, FileMode mode, size_t buffersize = DefaultBufferSize) { - super(new File(hFile,mode),buffersize); - } - - /// opens file in requested mode - void open(string filename, FileMode mode = FileMode.In) { - File sf = cast(File)s; - sf.open(filename,mode); - resetSource(); - } - - /// creates file in requested mode - void create(string filename, FileMode mode = FileMode.OutNew) { - File sf = cast(File)s; - sf.create(filename,mode); - resetSource(); - } - - // run a few tests same as File - unittest { - import std.internal.cstring : tempCString; - - BufferedFile file = new BufferedFile; - int i = 666; - auto stream_file = std.file.deleteme ~ "-stream.$$$"; - file.create(stream_file); - // should be ok to write - assert(file.writeable); - file.writeLine("Testing stream.d:"); - file.writeString("Hello, world!"); - file.write(i); - // string#1 + string#2 + int should give exacly that - version (Windows) - assert(file.position == 19 + 13 + 4); - version (Posix) - assert(file.position == 18 + 13 + 4); - // we must be at the end of file - assert(file.eof); - long oldsize = cast(long)file.size; - file.close(); - // no operations are allowed when file is closed - assert(!file.readable && !file.writeable && !file.seekable); - file.open(stream_file); - // should be ok to read - assert(file.readable); - // test getc/ungetc and size - char c1 = file.getc(); - file.ungetc(c1); - assert( file.size == oldsize ); - assert(!std.algorithm.cmp(file.readLine(), "Testing stream.d:")); - // jump over "Hello, " - file.seek(7, SeekPos.Current); - version (Windows) - assert(file.position == 19 + 7); - version (Posix) - assert(file.position == 18 + 7); - assert(!std.algorithm.cmp(file.readString(6), "world!")); - i = 0; file.read(i); - assert(i == 666); - // string#1 + string#2 + int should give exacly that - version (Windows) - assert(file.position == 19 + 13 + 4); - version (Posix) - assert(file.position == 18 + 13 + 4); - // we must be at the end of file - assert(file.eof); - file.close(); - remove(stream_file.tempCString()); - } - -} - -/// UTF byte-order-mark signatures -enum BOM { - UTF8, /// UTF-8 - UTF16LE, /// UTF-16 Little Endian - UTF16BE, /// UTF-16 Big Endian - UTF32LE, /// UTF-32 Little Endian - UTF32BE, /// UTF-32 Big Endian -} - -private enum int NBOMS = 5; -immutable Endian[NBOMS] BOMEndian = -[ std.system.endian, - Endian.littleEndian, Endian.bigEndian, - Endian.littleEndian, Endian.bigEndian - ]; - -immutable ubyte[][NBOMS] ByteOrderMarks = -[ [0xEF, 0xBB, 0xBF], - [0xFF, 0xFE], - [0xFE, 0xFF], - [0xFF, 0xFE, 0x00, 0x00], - [0x00, 0x00, 0xFE, 0xFF] - ]; - - -/*** - * This subclass wraps a stream with big-endian or little-endian byte order - * swapping. - * - * UTF Byte-Order-Mark (BOM) signatures can be read and deduced or - * written. - * Note that an EndianStream should not be used as the source of another - * FilterStream since a FilterStream call the source with byte-oriented - * read/write requests and the EndianStream will not perform any byte swapping. - * The EndianStream reads and writes binary data (non-getc functions) in a - * one-to-one - * manner with the source stream so the source stream's position and state will be - * kept in sync with the EndianStream if only non-getc functions are called. - */ -class EndianStream : FilterStream { - - Endian endian; /// Endianness property of the source stream. - - /*** - * Create the endian stream for the source stream source with endianness end. - * The default endianness is the native byte order. - * The Endian type is defined - * in the std.system module. - */ - this(Stream source, Endian end = std.system.endian) { - super(source); - endian = end; - } - - /*** - * Return -1 if no BOM and otherwise read the BOM and return it. - * - * If there is no BOM or if bytes beyond the BOM are read then the bytes read - * are pushed back onto the ungetc buffer or ungetcw buffer. - * Pass ungetCharSize == 2 to use - * ungetcw instead of ungetc when no BOM is present. - */ - int readBOM(int ungetCharSize = 1) { - ubyte[4] BOM_buffer; - int n = 0; // the number of read bytes - int result = -1; // the last match or -1 - for (int i=0; i < NBOMS; ++i) { - int j; - immutable ubyte[] bom = ByteOrderMarks[i]; - for (j=0; j < bom.length; ++j) { - if (n <= j) { // have to read more - if (eof) - break; - readExact(&BOM_buffer[n++],1); - } - if (BOM_buffer[j] != bom[j]) - break; - } - if (j == bom.length) // found a match - result = i; - } - ptrdiff_t m = 0; - if (result != -1) { - endian = BOMEndian[result]; // set stream endianness - m = ByteOrderMarks[result].length; - } - if ((ungetCharSize == 1 && result == -1) || (result == BOM.UTF8)) { - while (n-- > m) - ungetc(BOM_buffer[n]); - } else { // should eventually support unget for dchar as well - if (n & 1) // make sure we have an even number of bytes - readExact(&BOM_buffer[n++],1); - while (n > m) { - n -= 2; - wchar cw = *(cast(wchar*)&BOM_buffer[n]); - fixBO(&cw,2); - ungetcw(cw); - } - } - return result; - } - - /*** - * Correct the byte order of buffer to match native endianness. - * size must be even. - */ - final void fixBO(const(void)* buffer, size_t size) { - if (endian != std.system.endian) { - ubyte* startb = cast(ubyte*)buffer; - uint* start = cast(uint*)buffer; - switch (size) { - case 0: break; - case 2: { - ubyte x = *startb; - *startb = *(startb+1); - *(startb+1) = x; - break; - } - case 4: { - *start = bswap(*start); - break; - } - default: { - uint* end = cast(uint*)(buffer + size - uint.sizeof); - while (start < end) { - uint x = bswap(*start); - *start = bswap(*end); - *end = x; - ++start; - --end; - } - startb = cast(ubyte*)start; - ubyte* endb = cast(ubyte*)end; - auto len = uint.sizeof - (startb - endb); - if (len > 0) - fixBO(startb,len); - } - } - } - } - - /*** - * Correct the byte order of the given buffer in blocks of the given size and - * repeated the given number of times. - * size must be even. - */ - final void fixBlockBO(void* buffer, uint size, size_t repeat) { - while (repeat--) { - fixBO(buffer,size); - buffer += size; - } - } - - override void read(out byte x) { readExact(&x, x.sizeof); } - override void read(out ubyte x) { readExact(&x, x.sizeof); } - override void read(out short x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out ushort x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out int x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out uint x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out long x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out ulong x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out float x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out double x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out real x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out ifloat x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out idouble x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out ireal x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out cfloat x) { readExact(&x, x.sizeof); fixBlockBO(&x,float.sizeof,2); } - override void read(out cdouble x) { readExact(&x, x.sizeof); fixBlockBO(&x,double.sizeof,2); } - override void read(out creal x) { readExact(&x, x.sizeof); fixBlockBO(&x,real.sizeof,2); } - override void read(out char x) { readExact(&x, x.sizeof); } - override void read(out wchar x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - override void read(out dchar x) { readExact(&x, x.sizeof); fixBO(&x,x.sizeof); } - - override wchar getcw() { - wchar c; - if (prevCr) { - prevCr = false; - c = getcw(); - if (c != '\n') - return c; - } - if (unget.length > 1) { - c = unget[unget.length - 1]; - unget.length = unget.length - 1; - } else { - void* buf = &c; - size_t n = readBlock(buf,2); - if (n == 1 && readBlock(buf+1,1) == 0) - throw new ReadException("not enough data in stream"); - fixBO(&c,c.sizeof); - } - return c; - } - - override wchar[] readStringW(size_t length) { - wchar[] result = new wchar[length]; - readExact(result.ptr, length * wchar.sizeof); - fixBlockBO(result.ptr, wchar.sizeof, length); - return result; - } - - /// Write the specified BOM b to the source stream. - void writeBOM(BOM b) { - immutable ubyte[] bom = ByteOrderMarks[b]; - writeBlock(bom.ptr, bom.length); - } - - override void write(byte x) { writeExact(&x, x.sizeof); } - override void write(ubyte x) { writeExact(&x, x.sizeof); } - override void write(short x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(ushort x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(int x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(uint x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(long x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(ulong x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(float x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(double x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(real x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(ifloat x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(idouble x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(ireal x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(cfloat x) { fixBlockBO(&x,float.sizeof,2); writeExact(&x, x.sizeof); } - override void write(cdouble x) { fixBlockBO(&x,double.sizeof,2); writeExact(&x, x.sizeof); } - override void write(creal x) { fixBlockBO(&x,real.sizeof,2); writeExact(&x, x.sizeof); } - override void write(char x) { writeExact(&x, x.sizeof); } - override void write(wchar x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - override void write(dchar x) { fixBO(&x,x.sizeof); writeExact(&x, x.sizeof); } - - override void writeStringW(const(wchar)[] str) { - foreach(wchar cw;str) { - fixBO(&cw,2); - s.writeExact(&cw, 2); - } - } - - override @property bool eof() { return s.eof && !ungetAvailable(); } - override @property ulong size() { return s.size; } - - unittest { - MemoryStream m; - m = new MemoryStream (); - EndianStream em = new EndianStream(m,Endian.bigEndian); - uint x = 0x11223344; - em.write(x); - assert( m.data[0] == 0x11 ); - assert( m.data[1] == 0x22 ); - assert( m.data[2] == 0x33 ); - assert( m.data[3] == 0x44 ); - em.position = 0; - ushort x2 = 0x5566; - em.write(x2); - assert( m.data[0] == 0x55 ); - assert( m.data[1] == 0x66 ); - em.position = 0; - static ubyte[12] x3 = [1,2,3,4,5,6,7,8,9,10,11,12]; - em.fixBO(x3.ptr,12); - if (std.system.endian == Endian.littleEndian) { - assert( x3[0] == 12 ); - assert( x3[1] == 11 ); - assert( x3[2] == 10 ); - assert( x3[4] == 8 ); - assert( x3[5] == 7 ); - assert( x3[6] == 6 ); - assert( x3[8] == 4 ); - assert( x3[9] == 3 ); - assert( x3[10] == 2 ); - assert( x3[11] == 1 ); - } - em.endian = Endian.littleEndian; - em.write(x); - assert( m.data[0] == 0x44 ); - assert( m.data[1] == 0x33 ); - assert( m.data[2] == 0x22 ); - assert( m.data[3] == 0x11 ); - em.position = 0; - em.write(x2); - assert( m.data[0] == 0x66 ); - assert( m.data[1] == 0x55 ); - em.position = 0; - em.fixBO(x3.ptr,12); - if (std.system.endian == Endian.bigEndian) { - assert( x3[0] == 12 ); - assert( x3[1] == 11 ); - assert( x3[2] == 10 ); - assert( x3[4] == 8 ); - assert( x3[5] == 7 ); - assert( x3[6] == 6 ); - assert( x3[8] == 4 ); - assert( x3[9] == 3 ); - assert( x3[10] == 2 ); - assert( x3[11] == 1 ); - } - em.writeBOM(BOM.UTF8); - assert( m.position == 3 ); - assert( m.data[0] == 0xEF ); - assert( m.data[1] == 0xBB ); - assert( m.data[2] == 0xBF ); - em.writeString ("Hello, world"); - em.position = 0; - assert( m.position == 0 ); - assert( em.readBOM() == BOM.UTF8 ); - assert( m.position == 3 ); - assert( em.getc() == 'H' ); - em.position = 0; - em.writeBOM(BOM.UTF16BE); - assert( m.data[0] == 0xFE ); - assert( m.data[1] == 0xFF ); - em.position = 0; - em.writeBOM(BOM.UTF16LE); - assert( m.data[0] == 0xFF ); - assert( m.data[1] == 0xFE ); - em.position = 0; - em.writeString ("Hello, world"); - em.position = 0; - assert( em.readBOM() == -1 ); - assert( em.getc() == 'H' ); - assert( em.getc() == 'e' ); - assert( em.getc() == 'l' ); - assert( em.getc() == 'l' ); - em.position = 0; - } -} - -/*** - * Parameterized subclass that wraps an array-like buffer with a stream - * interface. - * - * The type Buffer must support the length property, opIndex and opSlice. - * Compile in release mode when directly instantiating a TArrayStream to avoid - * link errors. - */ -class TArrayStream(Buffer): Stream { - Buffer buf; // current data - ulong len; // current data length - ulong cur; // current file position - - /// Create the stream for the the buffer buf. Non-copying. - this(Buffer buf) { - super (); - this.buf = buf; - this.len = buf.length; - readable = writeable = seekable = true; - } - - // ensure subclasses don't violate this - invariant() { - assert(len <= buf.length); - assert(cur <= len); - } - - override size_t readBlock(void* buffer, size_t size) { - assertReadable(); - ubyte* cbuf = cast(ubyte*) buffer; - if (len - cur < size) - size = cast(size_t)(len - cur); - ubyte[] ubuf = cast(ubyte[])buf[cast(size_t)cur .. cast(size_t)(cur + size)]; - cbuf[0 .. size] = ubuf[]; - cur += size; - return size; - } - - override size_t writeBlock(const void* buffer, size_t size) { - assertWriteable(); - ubyte* cbuf = cast(ubyte*) buffer; - ulong blen = buf.length; - if (cur + size > blen) - size = cast(size_t)(blen - cur); - ubyte[] ubuf = cast(ubyte[])buf[cast(size_t)cur .. cast(size_t)(cur + size)]; - ubuf[] = cbuf[0 .. size]; - cur += size; - if (cur > len) - len = cur; - return size; - } - - override ulong seek(long offset, SeekPos rel) { - assertSeekable(); - long scur; // signed to saturate to 0 properly - - switch (rel) { - case SeekPos.Set: scur = offset; break; - case SeekPos.Current: scur = cast(long)(cur + offset); break; - case SeekPos.End: scur = cast(long)(len + offset); break; - default: - assert(0); - } - - if (scur < 0) - cur = 0; - else if (scur > len) - cur = len; - else - cur = cast(ulong)scur; - - return cur; - } - - override @property size_t available () { return cast(size_t)(len - cur); } - - /// Get the current memory data in total. - @property ubyte[] data() { - if (len > size_t.max) - throw new StreamException("Stream too big"); - const(void)[] res = buf[0 .. cast(size_t)len]; - return cast(ubyte[])res; - } - - override string toString() { - // assume data is UTF8 - return to!(string)(cast(char[])data); - } -} - -/* Test the TArrayStream */ -unittest { - char[100] buf; - TArrayStream!(char[]) m; - - m = new TArrayStream!(char[]) (buf); - assert (m.isOpen); - m.writeString ("Hello, world"); - assert (m.position == 12); - assert (m.available == 88); - assert (m.seekSet (0) == 0); - assert (m.available == 100); - assert (m.seekCur (4) == 4); - assert (m.available == 96); - assert (m.seekEnd (-8) == 92); - assert (m.available == 8); - assert (m.size == 100); - assert (m.seekSet (4) == 4); - assert (m.readString (4) == "o, w"); - m.writeString ("ie"); - assert (buf[0..12] == "Hello, wield"); - assert (m.position == 10); - assert (m.available == 90); - assert (m.size == 100); - m.seekSet (0); - assert (m.printf ("Answer is %d", 42) == 12); - assert (buf[0..12] == "Answer is 42"); -} - -/// This subclass reads and constructs an array of bytes in memory. -class MemoryStream: TArrayStream!(ubyte[]) { - - /// Create the output buffer and setup for reading, writing, and seeking. - // clear to an empty buffer. - this() { this(cast(ubyte[]) null); } - - /*** - * Create the output buffer and setup for reading, writing, and seeking. - * Load it with specific input data. - */ - this(ubyte[] buf) { super (buf); } - this(byte[] buf) { this(cast(ubyte[]) buf); } /// ditto - this(char[] buf) { this(cast(ubyte[]) buf); } /// ditto - - /// Ensure the stream can write count extra bytes from cursor position without an allocation. - void reserve(size_t count) { - if (cur + count > buf.length) - buf.length = cast(uint)((cur + count) * 2); - } - - override size_t writeBlock(const void* buffer, size_t size) { - reserve(size); - return super.writeBlock(buffer,size); - } - - unittest { - MemoryStream m; - - m = new MemoryStream (); - assert (m.isOpen); - m.writeString ("Hello, world"); - assert (m.position == 12); - assert (m.seekSet (0) == 0); - assert (m.available == 12); - assert (m.seekCur (4) == 4); - assert (m.available == 8); - assert (m.seekEnd (-8) == 4); - assert (m.available == 8); - assert (m.size == 12); - assert (m.readString (4) == "o, w"); - m.writeString ("ie"); - assert (cast(char[]) m.data == "Hello, wield"); - m.seekEnd (0); - m.writeString ("Foo"); - assert (m.position == 15); - assert (m.available == 0); - m.writeString ("Foo foo foo foo foo foo foo"); - assert (m.position == 42); - m.position = 0; - assert (m.available == 42); - m.writef("%d %d %s",100,345,"hello"); - auto str = m.toString(); - assert (str[0..13] == "100 345 hello", str[0 .. 13]); - assert (m.available == 29); - assert (m.position == 13); - - MemoryStream m2; - m.position = 3; - m2 = new MemoryStream (); - m2.writeString("before"); - m2.copyFrom(m,10); - str = m2.toString(); - assert (str[0..16] == "before 345 hello"); - m2.position = 3; - m2.copyFrom(m); - auto str2 = m.toString(); - str = m2.toString(); - assert (str == ("bef" ~ str2)); - } -} - -import std.mmfile; - -/*** - * This subclass wraps a memory-mapped file with the stream API. - * See std.mmfile module. - */ -class MmFileStream : TArrayStream!(MmFile) { - - /// Create stream wrapper for file. - this(MmFile file) { - super (file); - MmFile.Mode mode = file.mode(); - writeable = mode > MmFile.Mode.read; - } - - override void flush() { - if (isopen) { - super.flush(); - buf.flush(); - } - } - - override void close() { - if (isopen) { - super.close(); - delete buf; - buf = null; - } - } -} - -unittest { - auto test_file = std.file.deleteme ~ "-testing.txt"; - MmFile mf = new MmFile(test_file,MmFile.Mode.readWriteNew,100,null); - MmFileStream m; - m = new MmFileStream (mf); - m.writeString ("Hello, world"); - assert (m.position == 12); - assert (m.seekSet (0) == 0); - assert (m.seekCur (4) == 4); - assert (m.seekEnd (-8) == 92); - assert (m.size == 100); - assert (m.seekSet (4)); - assert (m.readString (4) == "o, w"); - m.writeString ("ie"); - ubyte[] dd = m.data; - assert ((cast(char[]) dd)[0 .. 12] == "Hello, wield"); - m.position = 12; - m.writeString ("Foo"); - assert (m.position == 15); - m.writeString ("Foo foo foo foo foo foo foo"); - assert (m.position == 42); - m.close(); - mf = new MmFile(test_file); - m = new MmFileStream (mf); - assert (!m.writeable); - char[] str = m.readString(12); - assert (str == "Hello, wield"); - m.close(); - std.file.remove(test_file); -} - - -/*** - * This subclass slices off a portion of another stream, making seeking relative - * to the boundaries of the slice. - * - * It could be used to section a large file into a - * set of smaller files, such as with tar archives. Reading and writing a - * SliceStream does not modify the position of the source stream if it is - * seekable. - */ -class SliceStream : FilterStream { - private { - ulong pos; // our position relative to low - ulong low; // low stream offset. - ulong high; // high stream offset. - bool bounded; // upper-bounded by high. - } - - /*** - * Indicate both the source stream to use for reading from and the low part of - * the slice. - * - * The high part of the slice is dependent upon the end of the source - * stream, so that if you write beyond the end it resizes the stream normally. - */ - this (Stream s, ulong low) - in { - assert (low <= s.size); - } - body { - super(s); - this.low = low; - this.high = 0; - this.bounded = false; - } - - /*** - * Indicate the high index as well. - * - * Attempting to read or write past the high - * index results in the end being clipped off. - */ - this (Stream s, ulong low, ulong high) - in { - assert (low <= high); - assert (high <= s.size); - } - body { - super(s); - this.low = low; - this.high = high; - this.bounded = true; - } - - invariant() { - if (bounded) - assert (pos <= high - low); - else - // size() does not appear to be const, though it should be - assert (pos <= (cast()s).size - low); - } - - override size_t readBlock (void *buffer, size_t size) { - assertReadable(); - if (bounded && size > high - low - pos) - size = cast(size_t)(high - low - pos); - ulong bp = s.position; - if (seekable) - s.position = low + pos; - size_t ret = super.readBlock(buffer, size); - if (seekable) { - pos = s.position - low; - s.position = bp; - } - return ret; - } - - override size_t writeBlock (const void *buffer, size_t size) { - assertWriteable(); - if (bounded && size > high - low - pos) - size = cast(size_t)(high - low - pos); - ulong bp = s.position; - if (seekable) - s.position = low + pos; - size_t ret = s.writeBlock(buffer, size); - if (seekable) { - pos = s.position - low; - s.position = bp; - } - return ret; - } - - override ulong seek(long offset, SeekPos rel) { - assertSeekable(); - long spos; - - switch (rel) { - case SeekPos.Set: - spos = offset; - break; - case SeekPos.Current: - spos = cast(long)(pos + offset); - break; - case SeekPos.End: - if (bounded) - spos = cast(long)(high - low + offset); - else - spos = cast(long)(s.size - low + offset); - break; - default: - assert(0); - } - - if (spos < 0) - pos = 0; - else if (bounded && spos > high - low) - pos = high - low; - else if (!bounded && spos > s.size - low) - pos = s.size - low; - else - pos = cast(ulong)spos; - - readEOF = false; - return pos; - } - - override @property size_t available() { - size_t res = s.available; - ulong bp = s.position; - if (bp <= pos+low && pos+low <= bp+res) { - if (!bounded || bp+res <= high) - return cast(size_t)(bp + res - pos - low); - else if (high <= bp+res) - return cast(size_t)(high - pos - low); - } - return 0; - } - - unittest { - MemoryStream m; - SliceStream s; - - m = new MemoryStream ((cast(char[])"Hello, world").dup); - s = new SliceStream (m, 4, 8); - assert (s.size == 4); - assert (m.position == 0); - assert (s.position == 0); - assert (m.available == 12); - assert (s.available == 4); - - assert (s.writeBlock (cast(char *) "Vroom", 5) == 4); - assert (m.position == 0); - assert (s.position == 4); - assert (m.available == 12); - assert (s.available == 0); - assert (s.seekEnd (-2) == 2); - assert (s.available == 2); - assert (s.seekEnd (2) == 4); - assert (s.available == 0); - assert (m.position == 0); - assert (m.available == 12); - - m.seekEnd(0); - m.writeString("\nBlaho"); - assert (m.position == 18); - assert (m.available == 0); - assert (s.position == 4); - assert (s.available == 0); - - s = new SliceStream (m, 4); - assert (s.size == 14); - assert (s.toString () == "Vrooorld\nBlaho"); - s.seekEnd (0); - assert (s.available == 0); - - s.writeString (", etcetera."); - assert (s.position == 25); - assert (s.seekSet (0) == 0); - assert (s.size == 25); - assert (m.position == 18); - assert (m.size == 29); - assert (m.toString() == "HellVrooorld\nBlaho, etcetera."); - } -} diff --git a/std/string.d b/std/string.d index cb111274c50..fa59969fed7 100644 --- a/std/string.d +++ b/std/string.d @@ -40,8 +40,10 @@ $(TR $(TDNW Pruning and Filling) $(MYREF chomp) $(MYREF chompPrefix) $(MYREF chop) + $(MYREF detabber) $(MYREF detab) $(MYREF entab) + $(MYREF entabber) $(MYREF leftJustify) $(MYREF outdent) $(MYREF rightJustify) @@ -55,6 +57,7 @@ $(TR $(TDNW Substitution) $(TD $(MYREF abbrev) $(MYREF soundex) + $(MYREF soundexer) $(MYREF succ) $(MYREF tr) $(MYREF translate) @@ -84,62 +87,61 @@ $(TR $(TH Module) $(TH Functions) ) $(LEADINGROW Publicly imported functions) $(TR $(TD std.algorithm) $(TD - $(SHORTXREF algorithm, cmp) - $(SHORTXREF algorithm, count) - $(SHORTXREF algorithm, endsWith) - $(SHORTXREF algorithm, startsWith) + $(REF_SHORT cmp, std,algorithm,comparison) + $(REF_SHORT count, std,algorithm,searching) + $(REF_SHORT endsWith, std,algorithm,searching) + $(REF_SHORT startsWith, std,algorithm,searching) )) $(TR $(TD std.array) $(TD - $(SHORTXREF array, join) - $(SHORTXREF array, replace) - $(SHORTXREF array, replaceInPlace) - $(SHORTXREF array, split) + $(REF_SHORT join, std,array) + $(REF_SHORT replace, std,array) + $(REF_SHORT replaceInPlace, std,array) + $(REF_SHORT split, std,array) + $(REF_SHORT empty, std,array) )) $(TR $(TD std.format) $(TD - $(SHORTXREF format, format) - $(SHORTXREF format, sformat) + $(REF_SHORT format, std,format) + $(REF_SHORT sformat, std,format) )) $(TR $(TD std.uni) $(TD - $(SHORTXREF uni, icmp) - $(SHORTXREF uni, toLower) - $(SHORTXREF uni, toLowerInPlace) - $(SHORTXREF uni, toUpper) - $(SHORTXREF uni, toUpperInPlace) + $(REF_SHORT icmp, std,uni) + $(REF_SHORT toLower, std,uni) + $(REF_SHORT toLowerInPlace, std,uni) + $(REF_SHORT toUpper, std,uni) + $(REF_SHORT toUpperInPlace, std,uni) )) ) There is a rich set of functions for _string handling defined in other modules. -Functions related to Unicode and ASCII are found in $(LINK2 std_uni.html, std.uni) -and $(LINK2 std_ascii.html, std.ascii), respectively. Other functions that have a -wider generality than just strings can be found in $(LINK2 std_algorithm.html, -std.algorithm) and $(LINK2 std_range.html, std.range). +Functions related to Unicode and ASCII are found in $(MREF std, uni) +and $(MREF std, ascii), respectively. Other functions that have a +wider generality than just strings can be found in $(MREF std, algorithm) +and $(MREF std, range). See_Also: $(LIST - $(LINK2 std_algorithm.html, std.algorithm) and - $(LINK2 std_range.html, std.range) + $(MREF std, algorithm) and + $(MREF std, range) for generic range algorithms , - $(LINK2 std_ascii.html, std.ascii) + $(MREF std, ascii) for functions that work with ASCII strings , - $(LINK2 std_uni.html, std.uni) + $(MREF std, uni) for functions that work with unicode strings ) -Macros: WIKI = Phobos/StdString - SHORTXREF=$(XREF2 $1, $2, $(TT $2)) - Copyright: Copyright Digital Mars 2007-. -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB digitalmars.com, Walter Bright), - $(WEB erdani.org, Andrei Alexandrescu), - and Jonathan M Davis +Authors: $(HTTP digitalmars.com, Walter Bright), + $(HTTP erdani.org, Andrei Alexandrescu), + Jonathan M Davis, + and David L. 'SpottedTiger' Davis Source: $(PHOBOSSRC std/_string.d) @@ -155,17 +157,50 @@ void trustedPrintf(in char* str) @trusted nothrow @nogc printf("%s", str); } +version (unittest) +{ +private: + struct TestAliasedString + { + string get() @safe @nogc pure nothrow { return _s; } + alias get this; + @disable this(this); + string _s; + } + + bool testAliasedString(alias func, Args...)(string s, Args args) + { + import std.algorithm.comparison : equal; + auto a = func(TestAliasedString(s), args); + auto b = func(s, args); + static if (is(typeof(equal(a, b)))) + { + // For ranges, compare contents instead of object identity. + return equal(a, b); + } + else + { + return a == b; + } + } +} + public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace; public import std.format : format, sformat; -import std.typecons : Flag; +import std.typecons : Flag, Yes, No; -import std.range.primitives; -import std.traits; -import std.typetuple; +import std.meta; // AliasSeq, staticIndexOf +import std.range.primitives; // back, ElementEncodingType, ElementType, front, + // hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite, + // isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put, + // save; +import std.traits; // isConvertibleToString, isNarrowString, isSomeChar, + // isSomeString, StringTypeOf, Unqual //public imports for backward compatibility -public import std.algorithm : startsWith, endsWith, cmp, count; -public import std.array : join, replace, replaceInPlace, split; +public import std.algorithm.comparison : cmp; +public import std.algorithm.searching : startsWith, endsWith, count; +public import std.array : join, replace, replaceInPlace, split, empty; /* ************* Exceptions *************** */ @@ -174,32 +209,25 @@ public import std.array : join, replace, replaceInPlace, split; +/ class StringException : Exception { - /++ - Params: - msg = The message for the exception. - file = The file where the exception occurred. - line = The line number where the exception occurred. - next = The previous exception in the chain of exceptions, if any. - +/ - this(string msg, - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) @safe pure nothrow - { - super(msg, file, line, next); - } + import std.exception : basicExceptionCtors; + + /// + mixin basicExceptionCtors; } /++ - Returns a D-style array of $(D char) given a zero-terminated C-style string. - The returned array will retain the same type qualifiers as the input. + Params: + cString = A null-terminated c-style string. + + Returns: A D-style array of $(D char) referencing the same string. The + returned array will retain the same type qualifiers as the input. $(RED Important Note:) The returned array is a slice of the original buffer. The original data is not changed and not copied. +/ -inout(char)[] fromStringz(inout(char)* cString) @system pure { +inout(char)[] fromStringz(inout(char)* cString) @nogc @system pure nothrow { import core.stdc.string : strlen; return cString ? cString[0 .. strlen(cString)] : null; } @@ -212,23 +240,21 @@ inout(char)[] fromStringz(inout(char)* cString) @system pure { } /++ - Returns a C-style zero-terminated string equivalent to $(D s). $(D s) - must not contain embedded $(D '\0')'s as any C function will treat the first - $(D '\0') that it sees as the end of the string. If $(D s.empty) is + Params: + s = A D-style string. + + Returns: A C-style null-terminated string equivalent to $(D s). $(D s) + must not contain embedded $(D '\0')'s as any C function will treat the + first $(D '\0') that it sees as the end of the string. If $(D s.empty) is $(D true), then a string containing only $(D '\0') is returned. $(RED Important Note:) When passing a $(D char*) to a C function, and the C - function keeps it around for any reason, make sure that you keep a reference - to it in your D code. Otherwise, it may go away during a garbage collection - cycle and cause a nasty bug when the C code tries to use it. + function keeps it around for any reason, make sure that you keep a + reference to it in your D code. Otherwise, it may become invalid during a + garbage collection cycle and cause a nasty bug when the C code tries to use + it. +/ immutable(char)* toStringz(const(char)[] s) @trusted pure nothrow -in -{ - // The assert below contradicts the unittests! - //assert(memchr(s.ptr, 0, s.length) == null, - //text(s.length, ": `", s, "'")); -} out (result) { import core.stdc.string : strlen, memcmp; @@ -237,7 +263,7 @@ out (result) auto slen = s.length; while (slen > 0 && s[slen-1] == 0) --slen; assert(strlen(result) == slen); - assert(memcmp(result, s.ptr, slen) == 0); + assert(result[0 .. slen] == s[0 .. slen]); } } body @@ -260,10 +286,10 @@ body // Need to make a copy auto copy = new char[s.length + 1]; - copy[0..s.length] = s[]; + copy[0 .. s.length] = s[]; copy[s.length] = 0; - return assumeUnique(copy).ptr; + return &assumeUnique(copy)[0]; } /++ Ditto +/ @@ -282,24 +308,20 @@ immutable(char)* toStringz(in string s) @trusted pure nothrow // unreadable. Otherwise, it's definitely pointing to valid // memory. if ((cast(size_t) p & 3) && *p == 0) - return s.ptr; + return &s[0]; return toStringz(cast(const char[]) s); } -pure nothrow unittest +/// +pure nothrow @system unittest { import core.stdc.string : strlen; import std.conv : to; - debug(string) trustedPrintf("string.toStringz.unittest\n"); - - // TODO: CTFEable toStringz is really necessary? - //assertCTFEable!( - //{ auto p = toStringz("foo"); assert(strlen(p) == 3); const(char)[] foo = "abbzxyzzy"; - p = toStringz(foo[3..5]); + p = toStringz(foo[3 .. 5]); assert(strlen(p) == 2); string test = ""; @@ -317,7 +339,6 @@ pure nothrow unittest const string test2 = ""; p = toStringz(test2); assert(*p == 0); - //}); } @@ -327,93 +348,259 @@ pure nothrow unittest alias CaseSensitive = Flag!"caseSensitive"; /++ - Returns the index of the first occurrence of $(D c) in $(D s). If $(D c) - is not found, then $(D -1) is returned. + Searches for character in range. - $(D cs) indicates whether the comparisons are case sensitive. + Params: + s = string or InputRange of characters to search in correct UTF format + c = character to search for + startIdx = starting index to a well-formed code point + cs = $(D Yes.caseSensitive) or $(D No.caseSensitive) + + Returns: + the index of the first occurrence of $(D c) in $(D s) with + respect to the start index $(D startIdx). If $(D c) + is not found, then $(D -1) is returned. + If $(D c) is found the value of the returned index is at least + $(D startIdx). + If the parameters are not valid UTF, the result will still + be in the range [-1 .. s.length], but will not be reliable otherwise. + + Throws: + If the sequence starting at $(D startIdx) does not represent a well + formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + + See_Also: $(REF countUntil, std,algorithm,searching) +/ -ptrdiff_t indexOf(Char)(in Char[] s, in dchar c, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char) +ptrdiff_t indexOf(Range)(Range s, in dchar c, + in CaseSensitive cs = Yes.caseSensitive) +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) { - import std.ascii : toLower, isASCII; - import std.uni : toLower; - if (cs == CaseSensitive.yes) + static import std.ascii; + static import std.uni; + import std.utf : byDchar, byCodeUnit, UTFException, codeLength; + alias Char = Unqual!(ElementEncodingType!Range); + + if (cs == Yes.caseSensitive) { - static if (Char.sizeof == 1) + static if (Char.sizeof == 1 && isSomeString!Range) { - import core.stdc.string : memchr; if (std.ascii.isASCII(c) && !__ctfe) { // Plain old ASCII - auto trustedmemchr() @trusted { return cast(Char*)memchr(s.ptr, c, s.length); } - auto p = trustedmemchr(); - if (p) - return p - s.ptr; - else - return -1; + static ptrdiff_t trustedmemchr(Range s, char c) @trusted + { + import core.stdc.string : memchr; + const p = cast(const(Char)*)memchr(s.ptr, c, s.length); + return p ? p - s.ptr : -1; + } + + return trustedmemchr(s, cast(char) c); } } - // c is a universal character - foreach (ptrdiff_t i, dchar c2; s) + static if (Char.sizeof == 1) + { + if (c <= 0x7F) + { + ptrdiff_t i; + foreach (const c2; s) + { + if (c == c2) + return i; + ++i; + } + } + else + { + ptrdiff_t i; + foreach (const c2; s.byDchar()) + { + if (c == c2) + return i; + i += codeLength!Char(c2); + } + } + } + else static if (Char.sizeof == 2) + { + if (c <= 0xFFFF) + { + ptrdiff_t i; + foreach (const c2; s) + { + if (c == c2) + return i; + ++i; + } + } + else if (c <= 0x10FFFF) + { + // Encode UTF-16 surrogate pair + const wchar c1 = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); + const wchar c2 = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); + ptrdiff_t i; + for (auto r = s.byCodeUnit(); !r.empty; r.popFront()) + { + if (c1 == r.front) + { + r.popFront(); + if (r.empty) // invalid UTF - missing second of pair + break; + if (c2 == r.front) + return i; + ++i; + } + ++i; + } + } + } + else static if (Char.sizeof == 4) { - if (c == c2) - return i; + ptrdiff_t i; + foreach (const c2; s) + { + if (c == c2) + return i; + ++i; + } } + else + static assert(0); + return -1; } else { if (std.ascii.isASCII(c)) { // Plain old ASCII - auto c1 = cast(char) std.ascii.toLower(c); + immutable c1 = cast(char) std.ascii.toLower(c); - foreach (ptrdiff_t i, c2; s) + ptrdiff_t i; + foreach (const c2; s.byCodeUnit()) { - auto c3 = std.ascii.toLower(c2); - if (c1 == c3) + if (c1 == std.ascii.toLower(c2)) return i; + ++i; } } else { // c is a universal character - auto c1 = std.uni.toLower(c); + immutable c1 = std.uni.toLower(c); - foreach (ptrdiff_t i, dchar c2; s) + ptrdiff_t i; + foreach (const c2; s.byDchar()) { - auto c3 = std.uni.toLower(c2); - if (c1 == c3) + if (c1 == std.uni.toLower(c2)) return i; + i += codeLength!Char(c2); + } + } + } + return -1; +} + +/// Ditto +ptrdiff_t indexOf(Range)(Range s, in dchar c, in size_t startIdx, + in CaseSensitive cs = Yes.caseSensitive) +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) +{ + static if (isSomeString!(typeof(s)) || + (hasSlicing!(typeof(s)) && hasLength!(typeof(s)))) + { + if (startIdx < s.length) + { + ptrdiff_t foundIdx = indexOf(s[startIdx .. $], c, cs); + if (foundIdx != -1) + { + return foundIdx + cast(ptrdiff_t) startIdx; } } } + else + { + foreach (i; 0 .. startIdx) + { + if (s.empty) + return -1; + s.popFront(); + } + ptrdiff_t foundIdx = indexOf(s, c, cs); + if (foundIdx != -1) + { + return foundIdx + cast(ptrdiff_t) startIdx; + } + } return -1; } +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(indexOf(s, 'W') == 6); + assert(indexOf(s, 'Z') == -1); + assert(indexOf(s, 'w', No.caseSensitive) == 6); +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(indexOf(s, 'W', 4) == 6); + assert(indexOf(s, 'Z', 100) == -1); + assert(indexOf(s, 'w', 3, No.caseSensitive) == 6); +} + +ptrdiff_t indexOf(Range)(auto ref Range s, in dchar c, + in CaseSensitive cs = Yes.caseSensitive) +if (isConvertibleToString!Range) +{ + return indexOf!(StringTypeOf!Range)(s, c, cs); +} + +ptrdiff_t indexOf(Range)(auto ref Range s, in dchar c, in size_t startIdx, + in CaseSensitive cs = Yes.caseSensitive) +if (isConvertibleToString!Range) +{ + return indexOf!(StringTypeOf!Range)(s, c, startIdx, cs); +} + +@safe pure unittest +{ + assert(testAliasedString!indexOf("std/string.d", '/')); +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; + import std.traits : EnumMembers; + import std.utf : byChar, byWchar, byDchar; debug(string) trustedPrintf("string.indexOf.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - assert(indexOf(cast(S)null, cast(dchar)'a') == -1); + assert(indexOf(cast(S) null, cast(dchar)'a') == -1); assert(indexOf(to!S("def"), cast(dchar)'a') == -1); assert(indexOf(to!S("abba"), cast(dchar)'a') == 0); assert(indexOf(to!S("def"), cast(dchar)'f') == 2); - assert(indexOf(to!S("def"), cast(dchar)'a', CaseSensitive.no) == -1); - assert(indexOf(to!S("def"), cast(dchar)'a', CaseSensitive.no) == -1); - assert(indexOf(to!S("Abba"), cast(dchar)'a', CaseSensitive.no) == 0); - assert(indexOf(to!S("def"), cast(dchar)'F', CaseSensitive.no) == 2); - assert(indexOf(to!S("ödef"), 'ö', CaseSensitive.no) == 0); + assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1); + assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1); + assert(indexOf(to!S("Abba"), cast(dchar)'a', No.caseSensitive) == 0); + assert(indexOf(to!S("def"), cast(dchar)'F', No.caseSensitive) == 2); + assert(indexOf(to!S("ödef"), 'ö', No.caseSensitive) == 0); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - assert(indexOf("def", cast(char)'f', CaseSensitive.no) == 2); - assert(indexOf(sPlts, cast(char)'P', CaseSensitive.no) == 23); - assert(indexOf(sPlts, cast(char)'R', CaseSensitive.no) == 2); + assert(indexOf("def", cast(char)'f', No.caseSensitive) == 2); + assert(indexOf(sPlts, cast(char)'P', No.caseSensitive) == 23); + assert(indexOf(sPlts, cast(char)'R', No.caseSensitive) == 2); } foreach (cs; EnumMembers!CaseSensitive) @@ -421,66 +608,66 @@ ptrdiff_t indexOf(Char)(in Char[] s, in dchar c, assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', cs) == 9); assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', cs) == 7); assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', cs) == 6); + + assert(indexOf("hello\U00010143\u0100\U00010143".byChar, '\u0100', cs) == 9); + assert(indexOf("hello\U00010143\u0100\U00010143".byWchar, '\u0100', cs) == 7); + assert(indexOf("hello\U00010143\u0100\U00010143".byDchar, '\u0100', cs) == 6); + + assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, 'l', cs) == 2); + assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, '\u0100', cs) == 7); + assert(indexOf("hello\U0000EFFF\u0100\U00010143".byChar, '\u0100', cs) == 8); + + assert(indexOf("hello\U00010100".byWchar, '\U00010100', cs) == 5); + assert(indexOf("hello\U00010100".byWchar, '\U00010101', cs) == -1); } + + char[10] fixedSizeArray = "0123456789"; + assert(indexOf(fixedSizeArray, '2') == 2); }); } -/++ - Returns the index of the first occurrence of $(D c) in $(D s) with respect - to the start index $(D startIdx). If $(D c) is not found, then $(D -1) is - returned. If $(D c) is found the value of the returned index is at least - $(D startIdx). $(D startIdx) represents a codeunit index in $(D s). If the - sequence starting at $(D startIdx) does not represent a well formed codepoint, - then a $(XREF utf,UTFException) may be thrown. - - $(D cs) indicates whether the comparisons are case sensitive. - +/ -ptrdiff_t indexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char) +@safe pure unittest { - if (startIdx < s.length) - { - ptrdiff_t foundIdx = indexOf(s[startIdx .. $], c, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t)startIdx; - } - } - return -1; + assert(testAliasedString!indexOf("std/string.d", '/', 3)); } @safe pure unittest { import std.conv : to; + import std.traits : EnumMembers; + import std.utf : byCodeUnit, byChar, byWchar; debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n"); - foreach (S; TypeTuple!(string, wstring, dstring)) + assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2); + assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2); + assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1); + + foreach (S; AliasSeq!(string, wstring, dstring)) { - assert(indexOf(cast(S)null, cast(dchar)'a', 1) == -1); + assert(indexOf(cast(S) null, cast(dchar)'a', 1) == -1); assert(indexOf(to!S("def"), cast(dchar)'a', 1) == -1); assert(indexOf(to!S("abba"), cast(dchar)'a', 1) == 3); assert(indexOf(to!S("def"), cast(dchar)'f', 1) == 2); assert((to!S("def")).indexOf(cast(dchar)'a', 1, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOf(to!S("def"), cast(dchar)'a', 1, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOf(to!S("def"), cast(dchar)'a', 12, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOf(to!S("AbbA"), cast(dchar)'a', 2, - CaseSensitive.no) == 3); - assert(indexOf(to!S("def"), cast(dchar)'F', 2, CaseSensitive.no) == 2); + No.caseSensitive) == 3); + assert(indexOf(to!S("def"), cast(dchar)'F', 2, No.caseSensitive) == 2); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - assert(indexOf("def", cast(char)'f', cast(uint)2, - CaseSensitive.no) == 2); - assert(indexOf(sPlts, cast(char)'P', 12, CaseSensitive.no) == 23); - assert(indexOf(sPlts, cast(char)'R', cast(ulong)1, - CaseSensitive.no) == 2); + assert(indexOf("def", cast(char)'f', cast(uint) 2, + No.caseSensitive) == 2); + assert(indexOf(sPlts, cast(char)'P', 12, No.caseSensitive) == 23); + assert(indexOf(sPlts, cast(char)'R', cast(ulong) 1, + No.caseSensitive) == 2); } - foreach(cs; EnumMembers!CaseSensitive) + foreach (cs; EnumMembers!CaseSensitive) { assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs) == 9); @@ -492,72 +679,199 @@ ptrdiff_t indexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx, } /++ - Returns the index of the first occurrence of $(D sub) in $(D s). If $(D sub) - is not found, then $(D -1) is returned. + Searches for substring in $(D s). - $(D cs) indicates whether the comparisons are case sensitive. + Params: + s = string or ForwardRange of characters to search in correct UTF format + sub = substring to search for + startIdx = the index into s to start searching from + cs = $(D Yes.caseSensitive) or $(D No.caseSensitive) + + Returns: + the index of the first occurrence of $(D sub) in $(D s) with + respect to the start index $(D startIdx). If $(D sub) is not found, + then $(D -1) is returned. + If the arguments are not valid UTF, the result will still + be in the range [-1 .. s.length], but will not be reliable otherwise. + If $(D sub) is found the value of the returned index is at least + $(D startIdx). + + Throws: + If the sequence starting at $(D startIdx) does not represent a well + formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + + Bugs: + Does not work with case insensitive strings where the mapping of + tolower and toupper is not 1:1. +/ -ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in CaseSensitive cs = CaseSensitive.yes) @trusted - if (isSomeChar!Char1 && isSomeChar!Char2) +ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub, + in CaseSensitive cs = Yes.caseSensitive) +if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && + isSomeChar!Char) { - import std.uni : toLower; - import std.algorithm : find; - const(Char1)[] balance; - if (cs == CaseSensitive.yes) + alias Char1 = Unqual!(ElementEncodingType!Range); + + static if (isSomeString!Range) { - balance = std.algorithm.find(s, sub); + import std.algorithm.searching : find; + + const(Char1)[] balance; + if (cs == Yes.caseSensitive) + { + balance = find(s, sub); + } + else + { + balance = find! + ((a, b) => toLower(a) == toLower(b)) + (s, sub); + } + return () @trusted { return balance.empty ? -1 : balance.ptr - s.ptr; } (); } else { - balance = std.algorithm.find! - ((a, b) => std.uni.toLower(a) == std.uni.toLower(b)) - (s, sub); + if (s.empty) + return -1; + if (sub.empty) + return 0; // degenerate case + + import std.utf : byDchar, codeLength; + auto subr = sub.byDchar; // decode sub[] by dchar's + dchar sub0 = subr.front; // cache first character of sub[] + subr.popFront(); + + // Special case for single character search + if (subr.empty) + return indexOf(s, sub0, cs); + + if (cs == No.caseSensitive) + sub0 = toLower(sub0); + + /* Classic double nested loop search algorithm + */ + ptrdiff_t index = 0; // count code unit index into s + for (auto sbydchar = s.byDchar(); !sbydchar.empty; sbydchar.popFront()) + { + dchar c2 = sbydchar.front; + if (cs == No.caseSensitive) + c2 = toLower(c2); + if (c2 == sub0) + { + auto s2 = sbydchar.save; // why s must be a forward range + foreach (c; subr.save) + { + s2.popFront(); + if (s2.empty) + return -1; + if (cs == Yes.caseSensitive ? c != s2.front + : toLower(c) != toLower(s2.front) + ) + goto Lnext; + } + return index; + } + Lnext: + index += codeLength!Char1(c2); + } + return -1; + } +} + +/// Ditto +ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, + in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive) +@safe +if (isSomeChar!Char1 && isSomeChar!Char2) +{ + if (startIdx < s.length) + { + ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs); + if (foundIdx != -1) + { + return foundIdx + cast(ptrdiff_t) startIdx; + } } - return balance.empty ? -1 : balance.ptr - s.ptr; + return -1; +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(indexOf(s, "Wo", 4) == 6); + assert(indexOf(s, "Zo", 100) == -1); + assert(indexOf(s, "wo", 3, No.caseSensitive) == 6); +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(indexOf(s, "Wo") == 6); + assert(indexOf(s, "Zo") == -1); + assert(indexOf(s, "wO", No.caseSensitive) == 6); +} + +ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub, + in CaseSensitive cs = Yes.caseSensitive) +if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && + isSomeChar!Char) && + is(StringTypeOf!Range)) +{ + return indexOf!(StringTypeOf!Range)(s, sub, cs); +} + +@safe pure unittest +{ + assert(testAliasedString!indexOf("std/string.d", "string")); } @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.indexOf.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(indexOf(cast(S)null, to!T("a")) == -1); + assert(indexOf(cast(S) null, to!T("a")) == -1); assert(indexOf(to!S("def"), to!T("a")) == -1); assert(indexOf(to!S("abba"), to!T("a")) == 0); assert(indexOf(to!S("def"), to!T("f")) == 2); assert(indexOf(to!S("dfefffg"), to!T("fff")) == 3); assert(indexOf(to!S("dfeffgfff"), to!T("fff")) == 6); - assert(indexOf(to!S("dfeffgfff"), to!T("a"), CaseSensitive.no) == -1); - assert(indexOf(to!S("def"), to!T("a"), CaseSensitive.no) == -1); - assert(indexOf(to!S("abba"), to!T("a"), CaseSensitive.no) == 0); - assert(indexOf(to!S("def"), to!T("f"), CaseSensitive.no) == 2); - assert(indexOf(to!S("dfefffg"), to!T("fff"), CaseSensitive.no) == 3); - assert(indexOf(to!S("dfeffgfff"), to!T("fff"), CaseSensitive.no) == 6); + assert(indexOf(to!S("dfeffgfff"), to!T("a"), No.caseSensitive) == -1); + assert(indexOf(to!S("def"), to!T("a"), No.caseSensitive) == -1); + assert(indexOf(to!S("abba"), to!T("a"), No.caseSensitive) == 0); + assert(indexOf(to!S("def"), to!T("f"), No.caseSensitive) == 2); + assert(indexOf(to!S("dfefffg"), to!T("fff"), No.caseSensitive) == 3); + assert(indexOf(to!S("dfeffgfff"), to!T("fff"), No.caseSensitive) == 6); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; S sMars = "Who\'s \'My Favorite Maritian?\'"; - assert(indexOf(sMars, to!T("MY fAVe"), CaseSensitive.no) == -1); - assert(indexOf(sMars, to!T("mY fAVOriTe"), CaseSensitive.no) == 7); - assert(indexOf(sPlts, to!T("mArS:"), CaseSensitive.no) == 0); - assert(indexOf(sPlts, to!T("rOcK"), CaseSensitive.no) == 17); - assert(indexOf(sPlts, to!T("Un."), CaseSensitive.no) == 41); - assert(indexOf(sPlts, to!T(sPlts), CaseSensitive.no) == 0); + assert(indexOf(sMars, to!T("MY fAVe"), No.caseSensitive) == -1); + assert(indexOf(sMars, to!T("mY fAVOriTe"), No.caseSensitive) == 7); + assert(indexOf(sPlts, to!T("mArS:"), No.caseSensitive) == 0); + assert(indexOf(sPlts, to!T("rOcK"), No.caseSensitive) == 17); + assert(indexOf(sPlts, to!T("Un."), No.caseSensitive) == 41); + assert(indexOf(sPlts, to!T(sPlts), No.caseSensitive) == 0); - assert(indexOf("\u0100", to!T("\u0100"), CaseSensitive.no) == 0); + assert(indexOf("\u0100", to!T("\u0100"), No.caseSensitive) == 0); // Thanks to Carlos Santander B. and zwang assert(indexOf("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y", - to!T("page-break-before"), CaseSensitive.no) == -1); + to!T("page-break-before"), No.caseSensitive) == -1); }(); foreach (cs; EnumMembers!CaseSensitive) @@ -570,79 +884,73 @@ ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, }); } -/++ - Returns the index of the first occurrence of $(D sub) in $(D s) with - respect to the start index $(D startIdx). If $(D sub) is not found, then - $(D -1) is returned. If $(D sub) is found the value of the returned index - is at least $(D startIdx). $(D startIdx) represents a codeunit index in - $(D s). If the sequence starting at $(D startIdx) does not represent a well - formed codepoint, then a $(XREF utf,UTFException) may be thrown. - - $(D cs) indicates whether the comparisons are case sensitive. - +/ -ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in size_t startIdx, in CaseSensitive cs = CaseSensitive.yes) - @safe if (isSomeChar!Char1 && isSomeChar!Char2) +@safe pure @nogc nothrow +unittest { - if (startIdx < s.length) + import std.traits : EnumMembers; + import std.utf : byWchar; + + foreach (cs; EnumMembers!CaseSensitive) { - ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t)startIdx; - } + assert(indexOf("".byWchar, "", cs) == -1); + assert(indexOf("hello".byWchar, "", cs) == 0); + assert(indexOf("hello".byWchar, "l", cs) == 2); + assert(indexOf("heLLo".byWchar, "LL", cs) == 2); + assert(indexOf("hello".byWchar, "lox", cs) == -1); + assert(indexOf("hello".byWchar, "betty", cs) == -1); + assert(indexOf("hello\U00010143\u0100*\U00010143".byWchar, "\u0100*", cs) == 7); } - return -1; } @safe pure unittest { import std.conv : to; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n"); - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach(T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(indexOf(cast(S)null, to!T("a"), 1337) == -1); + assert(indexOf(cast(S) null, to!T("a"), 1337) == -1); assert(indexOf(to!S("def"), to!T("a"), 0) == -1); assert(indexOf(to!S("abba"), to!T("a"), 2) == 3); assert(indexOf(to!S("def"), to!T("f"), 1) == 2); assert(indexOf(to!S("dfefffg"), to!T("fff"), 1) == 3); assert(indexOf(to!S("dfeffgfff"), to!T("fff"), 5) == 6); - assert(indexOf(to!S("dfeffgfff"), to!T("a"), 1, CaseSensitive.no) == -1); - assert(indexOf(to!S("def"), to!T("a"), 2, CaseSensitive.no) == -1); - assert(indexOf(to!S("abba"), to!T("a"), 3, CaseSensitive.no) == 3); - assert(indexOf(to!S("def"), to!T("f"), 1, CaseSensitive.no) == 2); - assert(indexOf(to!S("dfefffg"), to!T("fff"), 2, CaseSensitive.no) == 3); - assert(indexOf(to!S("dfeffgfff"), to!T("fff"), 4, CaseSensitive.no) == 6); - assert(indexOf(to!S("dfeffgffföä"), to!T("öä"), 9, CaseSensitive.no) == 9, - to!string(indexOf(to!S("dfeffgffföä"), to!T("öä"), 9, CaseSensitive.no)) + assert(indexOf(to!S("dfeffgfff"), to!T("a"), 1, No.caseSensitive) == -1); + assert(indexOf(to!S("def"), to!T("a"), 2, No.caseSensitive) == -1); + assert(indexOf(to!S("abba"), to!T("a"), 3, No.caseSensitive) == 3); + assert(indexOf(to!S("def"), to!T("f"), 1, No.caseSensitive) == 2); + assert(indexOf(to!S("dfefffg"), to!T("fff"), 2, No.caseSensitive) == 3); + assert(indexOf(to!S("dfeffgfff"), to!T("fff"), 4, No.caseSensitive) == 6); + assert(indexOf(to!S("dfeffgffföä"), to!T("öä"), 9, No.caseSensitive) == 9, + to!string(indexOf(to!S("dfeffgffföä"), to!T("öä"), 9, No.caseSensitive)) ~ " " ~ S.stringof ~ " " ~ T.stringof); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; S sMars = "Who\'s \'My Favorite Maritian?\'"; assert(indexOf(sMars, to!T("MY fAVe"), 10, - CaseSensitive.no) == -1); - assert(indexOf(sMars, to!T("mY fAVOriTe"), 4, CaseSensitive.no) == 7); - assert(indexOf(sPlts, to!T("mArS:"), 0, CaseSensitive.no) == 0); - assert(indexOf(sPlts, to!T("rOcK"), 12, CaseSensitive.no) == 17); - assert(indexOf(sPlts, to!T("Un."), 32, CaseSensitive.no) == 41); - assert(indexOf(sPlts, to!T(sPlts), 0, CaseSensitive.no) == 0); + No.caseSensitive) == -1); + assert(indexOf(sMars, to!T("mY fAVOriTe"), 4, No.caseSensitive) == 7); + assert(indexOf(sPlts, to!T("mArS:"), 0, No.caseSensitive) == 0); + assert(indexOf(sPlts, to!T("rOcK"), 12, No.caseSensitive) == 17); + assert(indexOf(sPlts, to!T("Un."), 32, No.caseSensitive) == 41); + assert(indexOf(sPlts, to!T(sPlts), 0, No.caseSensitive) == 0); - assert(indexOf("\u0100", to!T("\u0100"), 0, CaseSensitive.no) == 0); + assert(indexOf("\u0100", to!T("\u0100"), 0, No.caseSensitive) == 0); // Thanks to Carlos Santander B. and zwang assert(indexOf("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y", - to!T("page-break-before"), 10, CaseSensitive.no) == -1); + to!T("page-break-before"), 10, No.caseSensitive) == -1); // In order for indexOf with and without index to be consistent assert(indexOf(to!S(""), to!T("")) == indexOf(to!S(""), to!T(""), 0)); }(); - foreach(cs; EnumMembers!CaseSensitive) + foreach (cs; EnumMembers!CaseSensitive) { assert(indexOf("hello\U00010143\u0100\U00010143", to!S("\u0100"), 3, cs) == 9); @@ -655,18 +963,31 @@ ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, } /++ - Returns the index of the last occurrence of $(D c) in $(D s). If $(D c) - is not found, then $(D -1) is returned. + Params: + s = string to search + c = character to search for + startIdx = the index into s to start searching from + cs = $(D Yes.caseSensitive) or $(D No.caseSensitive) + + Returns: + The index of the last occurrence of $(D c) in $(D s). If $(D c) is not + found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in + the following way $(D s[0 .. startIdx]). $(D startIdx) represents a + codeunit index in $(D s). + + Throws: + If the sequence ending at $(D startIdx) does not represent a well + formed codepoint, then a $(REF UTFException, std,utf) may be thrown. $(D cs) indicates whether the comparisons are case sensitive. +/ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char) + in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char) { - import std.ascii : isASCII, toLower; + static import std.ascii, std.uni; import std.utf : canSearchInCodeUnits; - if (cs == CaseSensitive.yes) + if (cs == Yes.caseSensitive) { if (canSearchInCodeUnits!Char(c)) { @@ -722,15 +1043,51 @@ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, return -1; } +/// Ditto +ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx, + in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char) +{ + if (startIdx <= s.length) + { + return lastIndexOf(s[0u .. startIdx], c, cs); + } + + return -1; +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(lastIndexOf(s, 'l') == 9); + assert(lastIndexOf(s, 'Z') == -1); + assert(lastIndexOf(s, 'L', No.caseSensitive) == 9); +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(lastIndexOf(s, 'l', 4) == 3); + assert(lastIndexOf(s, 'Z', 1337) == -1); + assert(lastIndexOf(s, 'L', 7, No.caseSensitive) == 3); +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.lastIndexOf.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { assert(lastIndexOf(cast(S) null, 'a') == -1); assert(lastIndexOf(to!S("def"), 'a') == -1); @@ -738,19 +1095,19 @@ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, assert(lastIndexOf(to!S("def"), 'f') == 2); assert(lastIndexOf(to!S("ödef"), 'ö') == 0); - assert(lastIndexOf(cast(S) null, 'a', CaseSensitive.no) == -1); - assert(lastIndexOf(to!S("def"), 'a', CaseSensitive.no) == -1); - assert(lastIndexOf(to!S("AbbA"), 'a', CaseSensitive.no) == 3); - assert(lastIndexOf(to!S("def"), 'F', CaseSensitive.no) == 2); - assert(lastIndexOf(to!S("ödef"), 'ö', CaseSensitive.no) == 0); + assert(lastIndexOf(cast(S) null, 'a', No.caseSensitive) == -1); + assert(lastIndexOf(to!S("def"), 'a', No.caseSensitive) == -1); + assert(lastIndexOf(to!S("AbbA"), 'a', No.caseSensitive) == 3); + assert(lastIndexOf(to!S("def"), 'F', No.caseSensitive) == 2); + assert(lastIndexOf(to!S("ödef"), 'ö', No.caseSensitive) == 0); assert(lastIndexOf(to!S("i\u0100def"), to!dchar("\u0100"), - CaseSensitive.no) == 1); + No.caseSensitive) == 1); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - assert(lastIndexOf(to!S("def"), 'f', CaseSensitive.no) == 2); - assert(lastIndexOf(sPlts, 'M', CaseSensitive.no) == 34); - assert(lastIndexOf(sPlts, 'S', CaseSensitive.no) == 40); + assert(lastIndexOf(to!S("def"), 'f', No.caseSensitive) == 2); + assert(lastIndexOf(sPlts, 'M', No.caseSensitive) == 34); + assert(lastIndexOf(sPlts, 'S', No.caseSensitive) == 40); } foreach (cs; EnumMembers!CaseSensitive) @@ -762,55 +1119,34 @@ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, }); } -/++ - Returns the index of the last occurrence of $(D c) in $(D s). If $(D c) is - not found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in - the following way $(D s[0 .. startIdx]). $(D startIdx) represents a - codeunit index in $(D s). If the sequence ending at $(D startIdx) does not - represent a well formed codepoint, then a $(XREF utf,UTFException) may be - thrown. - - $(D cs) indicates whether the comparisons are case sensitive. - +/ -ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char) -{ - if (startIdx <= s.length) - { - return lastIndexOf(s[0u .. startIdx], c, cs); - } - - return -1; -} - @safe pure unittest { import std.conv : to; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.lastIndexOf.unittest\n"); - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { assert(lastIndexOf(cast(S) null, 'a') == -1); assert(lastIndexOf(to!S("def"), 'a') == -1); assert(lastIndexOf(to!S("abba"), 'a', 3) == 0); assert(lastIndexOf(to!S("deff"), 'f', 3) == 2); - assert(lastIndexOf(cast(S) null, 'a', CaseSensitive.no) == -1); - assert(lastIndexOf(to!S("def"), 'a', CaseSensitive.no) == -1); - assert(lastIndexOf(to!S("AbbAa"), 'a', to!ushort(4), CaseSensitive.no) == 3, - to!string(lastIndexOf(to!S("AbbAa"), 'a', 4, CaseSensitive.no))); - assert(lastIndexOf(to!S("def"), 'F', 3, CaseSensitive.no) == 2); + assert(lastIndexOf(cast(S) null, 'a', No.caseSensitive) == -1); + assert(lastIndexOf(to!S("def"), 'a', No.caseSensitive) == -1); + assert(lastIndexOf(to!S("AbbAa"), 'a', to!ushort(4), No.caseSensitive) == 3, + to!string(lastIndexOf(to!S("AbbAa"), 'a', 4, No.caseSensitive))); + assert(lastIndexOf(to!S("def"), 'F', 3, No.caseSensitive) == 2); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; - assert(lastIndexOf(to!S("def"), 'f', 4, CaseSensitive.no) == -1); - assert(lastIndexOf(sPlts, 'M', sPlts.length -2, CaseSensitive.no) == 34); - assert(lastIndexOf(sPlts, 'S', sPlts.length -2, CaseSensitive.no) == 40); + assert(lastIndexOf(to!S("def"), 'f', 4, No.caseSensitive) == -1); + assert(lastIndexOf(sPlts, 'M', sPlts.length -2, No.caseSensitive) == 34); + assert(lastIndexOf(sPlts, 'S', sPlts.length -2, No.caseSensitive) == 40); } - foreach(cs; EnumMembers!CaseSensitive) + foreach (cs; EnumMembers!CaseSensitive) { assert(lastIndexOf("\U00010143\u0100\U00010143hello", '\u0100', cs) == 4); assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, '\u0100', cs) == 2); @@ -819,25 +1155,40 @@ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in size_t startIdx, } /++ - Returns the index of the last occurrence of $(D sub) in $(D s). If $(D sub) - is not found, then $(D -1) is returned. + Params: + s = string to search + sub = substring to search for + startIdx = the index into s to start searching from + cs = $(D Yes.caseSensitive) or $(D No.caseSensitive) + + Returns: + the index of the last occurrence of $(D sub) in $(D s). If $(D sub) is + not found, then $(D -1) is returned. The $(D startIdx) slices $(D s) + in the following way $(D s[0 .. startIdx]). $(D startIdx) represents a + codeunit index in $(D s). + + Throws: + If the sequence ending at $(D startIdx) does not represent a well + formed codepoint, then a $(REF UTFException, std,utf) may be thrown. $(D cs) indicates whether the comparisons are case sensitive. +/ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char1 && isSomeChar!Char2) + in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char1 && isSomeChar!Char2) { - import std.utf : strideBack; + import std.algorithm.searching : endsWith; import std.conv : to; - import std.algorithm : endsWith; + import std.range.primitives : walkLength; + static import std.uni; + import std.utf : strideBack; if (sub.empty) - return s.length; + return -1; if (walkLength(sub) == 1) return lastIndexOf(s, sub.front, cs); - if (cs == CaseSensitive.yes) + if (cs == Yes.caseSensitive) { static if (is(Unqual!Char1 == Unqual!Char2)) { @@ -876,7 +1227,7 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, for (size_t i = s.length; !s.empty;) { if (s.endsWith(sub)) - return cast(ptrdiff_t)i - to!(const(Char1)[])(sub).length; + return cast(ptrdiff_t) i - to!(const(Char1)[])(sub).length; i -= strideBack(s, i); s = s[0 .. i]; @@ -890,7 +1241,7 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, if (endsWith!((a, b) => std.uni.toLower(a) == std.uni.toLower(b)) (s, sub)) { - return cast(ptrdiff_t)i - to!(const(Char1)[])(sub).length; + return cast(ptrdiff_t) i - to!(const(Char1)[])(sub).length; } i -= strideBack(s, i); @@ -901,22 +1252,75 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, return -1; } -@safe pure unittest +/// Ditto +ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, + in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char1 && isSomeChar!Char2) { - import std.conv : to; + if (startIdx <= s.length) + { + return lastIndexOf(s[0u .. startIdx], sub, cs); + } + + return -1; +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(lastIndexOf(s, "ll") == 2); + assert(lastIndexOf(s, "Zo") == -1); + assert(lastIndexOf(s, "lL", No.caseSensitive) == 2); +} + +/// +@safe pure unittest +{ + import std.typecons : No; + + string s = "Hello World"; + assert(lastIndexOf(s, "ll", 4) == 2); + assert(lastIndexOf(s, "Zo", 128) == -1); + assert(lastIndexOf(s, "lL", 3, No.caseSensitive) == -1); +} + +@safe pure unittest +{ + import std.conv : to; + + foreach (S; AliasSeq!(string, wstring, dstring)) + { + auto r = to!S("").lastIndexOf("hello"); + assert(r == -1, to!string(r)); + + r = to!S("hello").lastIndexOf(""); + assert(r == -1, to!string(r)); + + r = to!S("").lastIndexOf(""); + assert(r == -1, to!string(r)); + } +} + +@safe pure unittest +{ + import std.conv : to; + import std.exception : assertCTFEable; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.lastIndexOf.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 enum typeStr = S.stringof ~ " " ~ T.stringof; - assert(lastIndexOf(cast(S)null, to!T("a")) == -1, typeStr); + assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("c")) == 6, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd")) == 6, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("ef")) == 8, typeStr); @@ -924,30 +1328,30 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, assert(lastIndexOf(to!S("abcdefCdef"), to!T("cd")) == 2, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("x")) == -1, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("xy")) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("")) == 10, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("")) == -1, typeStr); assert(lastIndexOf(to!S("öabcdefcdef"), to!T("ö")) == 0, typeStr); - assert(lastIndexOf(cast(S)null, to!T("a"), CaseSensitive.no) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), CaseSensitive.no) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), CaseSensitive.no) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("x"), CaseSensitive.no) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("xy"), CaseSensitive.no) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), CaseSensitive.no) == 10, typeStr); - assert(lastIndexOf(to!S("öabcdefcdef"), to!T("ö"), CaseSensitive.no) == 0, typeStr); + assert(lastIndexOf(cast(S) null, to!T("a"), No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), No.caseSensitive) == 6, typeStr); + assert(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), No.caseSensitive) == 6, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("x"), No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("xy"), No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("öabcdefcdef"), to!T("ö"), No.caseSensitive) == 0, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), CaseSensitive.no) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), CaseSensitive.no) == 6, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), CaseSensitive.no) == 7, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), No.caseSensitive) == 6, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), No.caseSensitive) == 6, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), No.caseSensitive) == 7, typeStr); - assert(lastIndexOf(to!S("ödfeffgfff"), to!T("ö"), CaseSensitive.yes) == 0); + assert(lastIndexOf(to!S("ödfeffgfff"), to!T("ö"), Yes.caseSensitive) == 0); S sPlts = "Mars: the fourth Rock (Planet) from the Sun."; S sMars = "Who\'s \'My Favorite Maritian?\'"; - assert(lastIndexOf(sMars, to!T("RiTE maR"), CaseSensitive.no) == 14, typeStr); - assert(lastIndexOf(sPlts, to!T("FOuRTh"), CaseSensitive.no) == 10, typeStr); - assert(lastIndexOf(sMars, to!T("whO\'s \'MY"), CaseSensitive.no) == 0, typeStr); - assert(lastIndexOf(sMars, to!T(sMars), CaseSensitive.no) == 0, typeStr); + assert(lastIndexOf(sMars, to!T("RiTE maR"), No.caseSensitive) == 14, typeStr); + assert(lastIndexOf(sPlts, to!T("FOuRTh"), No.caseSensitive) == 10, typeStr); + assert(lastIndexOf(sMars, to!T("whO\'s \'MY"), No.caseSensitive) == 0, typeStr); + assert(lastIndexOf(sMars, to!T(sMars), No.caseSensitive) == 0, typeStr); }(); foreach (cs; EnumMembers!CaseSensitive) @@ -965,9 +1369,9 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, @safe pure unittest // issue13529 { import std.conv : to; - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) { enum typeStr = S.stringof ~ " " ~ T.stringof; auto idx = lastIndexOf(to!T("Hällö Wörldö ö"),to!S("ö ö")); @@ -979,41 +1383,20 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, } } -/++ - Returns the index of the last occurrence of $(D sub) in $(D s). If $(D sub) - is not found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in - the following way $(D s[0 .. startIdx]). $(D startIdx) represents a - codeunit index in $(D s). If the sequence ending at $(D startIdx) does not - represent a well formed codepoint, then a $(XREF utf,UTFException) may be - thrown. - - $(D cs) indicates whether the comparisons are case sensitive. - +/ -ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, - in size_t startIdx, in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char1 && isSomeChar!Char2) -{ - if (startIdx <= s.length) - { - return lastIndexOf(s[0u .. startIdx], sub, cs); - } - - return -1; -} - @safe pure unittest { import std.conv : to; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.lastIndexOf.unittest\n"); - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach(T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 enum typeStr = S.stringof ~ " " ~ T.stringof; - assert(lastIndexOf(cast(S)null, to!T("a")) == -1, typeStr); + assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), 5) == 2, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 3) == -1, typeStr); assert(lastIndexOf(to!S("abcdefcdef"), to!T("ef"), 6) == 4, typeStr ~ @@ -1022,25 +1405,25 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, assert(lastIndexOf(to!S("abcdefCdef"), to!T("cd"), 3) == -1, typeStr); assert(lastIndexOf(to!S("abcdefcdefx"), to!T("x"), 1) == -1, typeStr); assert(lastIndexOf(to!S("abcdefcdefxy"), to!T("xy"), 6) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), 8) == 8, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), 8) == -1, typeStr); assert(lastIndexOf(to!S("öafö"), to!T("ö"), 3) == 0, typeStr ~ to!string(lastIndexOf(to!S("öafö"), to!T("ö"), 3))); //BUG 10472 - assert(lastIndexOf(cast(S)null, to!T("a"), 1, CaseSensitive.no) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), 5, CaseSensitive.no) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), 4, CaseSensitive.no) == 2, typeStr ~ - " " ~ to!string(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), 3, CaseSensitive.no))); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("x"),3 , CaseSensitive.no) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdefXY"), to!T("xy"), 4, CaseSensitive.no) == -1, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), 7, CaseSensitive.no) == 7, typeStr); - - assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), 4, CaseSensitive.no) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 4, CaseSensitive.no) == 2, typeStr); - assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), 6, CaseSensitive.no) == 3, typeStr); + assert(lastIndexOf(cast(S) null, to!T("a"), 1, No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("abcdefCdef"), to!T("c"), 5, No.caseSensitive) == 2, typeStr); + assert(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), 4, No.caseSensitive) == 2, typeStr ~ + " " ~ to!string(lastIndexOf(to!S("abcdefCdef"), to!T("cD"), 3, No.caseSensitive))); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("x"),3 , No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("abcdefcdefXY"), to!T("xy"), 4, No.caseSensitive) == -1, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T(""), 7, No.caseSensitive) == -1, typeStr); + + assert(lastIndexOf(to!S("abcdefcdef"), to!T("c"), 4, No.caseSensitive) == 2, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 4, No.caseSensitive) == 2, typeStr); + assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), 6, No.caseSensitive) == 3, typeStr); assert(lastIndexOf(to!S(""), to!T(""), 0) == lastIndexOf(to!S(""), to!T("")), typeStr); }(); - foreach(cs; EnumMembers!CaseSensitive) + foreach (cs; EnumMembers!CaseSensitive) { enum csString = to!string(cs); @@ -1053,17 +1436,16 @@ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( const(Char)[] haystack, const(Char2)[] needles, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) + in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char && isSomeChar!Char2) { - import std.algorithm : canFind; - if (cs == CaseSensitive.yes) + import std.algorithm.searching : canFind, findAmong; + if (cs == Yes.caseSensitive) { static if (forward) { static if (any) { - import std.algorithm : findAmong; size_t n = haystack.findAmong(needles).length; return n ? haystack.length - n : -1; } @@ -1082,9 +1464,8 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( { static if (any) { - import std.utf : strideBack; - import std.algorithm : findAmong; import std.range : retro; + import std.utf : strideBack; size_t n = haystack.retro.findAmong(needles).source.length; if (n) { @@ -1095,7 +1476,7 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( { foreach_reverse (idx, dchar hay; haystack) { - if(!canFind(needles, hay)) + if (!canFind(needles, hay)) { return idx; } @@ -1105,20 +1486,21 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( } else { + import std.range.primitives : walkLength; if (needles.length <= 16 && needles.walkLength(17)) { size_t si = 0; dchar[16] scratch = void; foreach ( dchar c; needles) { - scratch[si++] = std.uni.toLower(c); + scratch[si++] = toLower(c); } static if (forward) { foreach (i, dchar c; haystack) { - if (canFind(scratch[0 .. si], std.uni.toLower(c)) == any) + if (canFind(scratch[0 .. si], toLower(c)) == any) { return i; } @@ -1128,7 +1510,7 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( { foreach_reverse (i, dchar c; haystack) { - if (canFind(scratch[0 .. si], std.uni.toLower(c)) == any) + if (canFind(scratch[0 .. si], toLower(c)) == any) { return i; } @@ -1139,14 +1521,14 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( { static bool f(dchar a, dchar b) { - return std.uni.toLower(a) == b; + return toLower(a) == b; } static if (forward) { foreach (i, dchar c; haystack) { - if (canFind!f(needles, std.uni.toLower(c)) == any) + if (canFind!f(needles, toLower(c)) == any) { return i; } @@ -1156,7 +1538,7 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( { foreach_reverse (i, dchar c; haystack) { - if (canFind!f(needles, std.uni.toLower(c)) == any) + if (canFind!f(needles, toLower(c)) == any) { return i; } @@ -1169,24 +1551,49 @@ private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)( } /** - Returns the index of the first occurence of any of the elements in $(D + Returns the index of the first occurrence of any of the elements in $(D needles) in $(D haystack). If no element of $(D needles) is found, - then $(D -1) is returned. + then $(D -1) is returned. The $(D startIdx) slices $(D haystack) in the + following way $(D haystack[startIdx .. $]). $(D startIdx) represents a + codeunit index in $(D haystack). If the sequence ending at $(D startIdx) + does not represent a well formed codepoint, then a $(REF UTFException, std,utf) + may be thrown. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. + haystack = String to search for needles in. + needles = Strings to search for in haystack. + startIdx = slices haystack like this $(D haystack[startIdx .. $]). If + the startIdx is greater equal the length of haystack the functions + returns $(D -1). cs = Indicates whether the comparisons are case sensitive. */ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) + in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char && isSomeChar!Char2) { return indexOfAnyNeitherImpl!(true, true)(haystack, needles, cs); } +/// Ditto +ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, + in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char && isSomeChar!Char2) +{ + if (startIdx < haystack.length) + { + ptrdiff_t foundIdx = indexOfAny(haystack[startIdx .. $], needles, cs); + if (foundIdx != -1) + { + return foundIdx + cast(ptrdiff_t) startIdx; + } + } + + return -1; +} + /// -@safe pure unittest { +@safe pure unittest +{ import std.conv : to; ptrdiff_t i = "helloWorld".indexOfAny("Wr"); @@ -1195,20 +1602,49 @@ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, assert(i == 4, to!string(i)); } +/// +@safe pure unittest +{ + import std.conv : to; + + ptrdiff_t i = "helloWorld".indexOfAny("Wr", 4); + assert(i == 5); + + i = "Foo öällo world".indexOfAny("lh", 3); + assert(i == 8, to!string(i)); +} + +@safe pure unittest +{ + import std.conv : to; + + foreach (S; AliasSeq!(string, wstring, dstring)) + { + auto r = to!S("").indexOfAny("hello"); + assert(r == -1, to!string(r)); + + r = to!S("hello").indexOfAny(""); + assert(r == -1, to!string(r)); + + r = to!S("").indexOfAny(""); + assert(r == -1, to!string(r)); + } +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.indexOfAny.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(indexOfAny(cast(S)null, to!T("a")) == -1); + assert(indexOfAny(cast(S) null, to!T("a")) == -1); assert(indexOfAny(to!S("def"), to!T("rsa")) == -1); assert(indexOfAny(to!S("abba"), to!T("a")) == 0); assert(indexOfAny(to!S("def"), to!T("f")) == 2); @@ -1216,82 +1652,38 @@ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, assert(indexOfAny(to!S("dfeffgfff"), to!T("feg")) == 1); assert(indexOfAny(to!S("zfeffgfff"), to!T("ACDC"), - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfAny(to!S("def"), to!T("MI6"), - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfAny(to!S("abba"), to!T("DEA"), - CaseSensitive.no) == 0); - assert(indexOfAny(to!S("def"), to!T("FBI"), CaseSensitive.no) == 2); - assert(indexOfAny(to!S("dfefffg"), to!T("NSA"), CaseSensitive.no) + No.caseSensitive) == 0); + assert(indexOfAny(to!S("def"), to!T("FBI"), No.caseSensitive) == 2); + assert(indexOfAny(to!S("dfefffg"), to!T("NSA"), No.caseSensitive) == -1); assert(indexOfAny(to!S("dfeffgfff"), to!T("BND"), - CaseSensitive.no) == 0); + No.caseSensitive) == 0); assert(indexOfAny(to!S("dfeffgfff"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), - CaseSensitive.no) == 0); + No.caseSensitive) == 0); - assert(indexOfAny("\u0100", to!T("\u0100"), CaseSensitive.no) == 0); + assert(indexOfAny("\u0100", to!T("\u0100"), No.caseSensitive) == 0); }(); } } ); } -/** - Returns the index of the first occurence of any of the elements in $(D - needles) in $(D haystack). If no element of $(D needles) is found, - then $(D -1) is returned. The $(D startIdx) slices $(D s) in the following - way $(D haystack[startIdx .. $]). $(D startIdx) represents a codeunit - index in $(D haystack). If the sequence ending at $(D startIdx) does not - represent a well formed codepoint, then a $(XREF utf,UTFException) may be - thrown. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater equal the length of haystack the functions - returns $(D -1). - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, - in size_t startIdx, in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) -{ - if (startIdx < haystack.length) - { - ptrdiff_t foundIdx = indexOfAny(haystack[startIdx .. $], needles, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t)startIdx; - } - } - - return -1; -} - -/// -@safe pure unittest -{ - import std.conv : to; - - ptrdiff_t i = "helloWorld".indexOfAny("Wr", 4); - assert(i == 5); - - i = "Foo öällo world".indexOfAny("lh", 3); - assert(i == 8, to!string(i)); -} - @safe pure unittest { import std.conv : to; + import std.traits : EnumMembers; debug(string) trustedPrintf("string.indexOfAny(startIdx).unittest\n"); - foreach(S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach(T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(indexOfAny(cast(S)null, to!T("a"), 1337) == -1); + assert(indexOfAny(cast(S) null, to!T("a"), 1337) == -1); assert(indexOfAny(to!S("def"), to!T("AaF"), 0) == -1); assert(indexOfAny(to!S("abba"), to!T("NSa"), 2) == 3); assert(indexOfAny(to!S("def"), to!T("fbi"), 1) == 2); @@ -1299,25 +1691,25 @@ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, assert(indexOfAny(to!S("dfeffgfff"), to!T("fsb"), 5) == 6); assert(indexOfAny(to!S("dfeffgfff"), to!T("NDS"), 1, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfAny(to!S("def"), to!T("DRS"), 2, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfAny(to!S("abba"), to!T("SI"), 3, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfAny(to!S("deO"), to!T("ASIO"), 1, - CaseSensitive.no) == 2); + No.caseSensitive) == 2); assert(indexOfAny(to!S("dfefffg"), to!T("fbh"), 2, - CaseSensitive.no) == 3); + No.caseSensitive) == 3); assert(indexOfAny(to!S("dfeffgfff"), to!T("fEe"), 4, - CaseSensitive.no) == 4); + No.caseSensitive) == 4); assert(indexOfAny(to!S("dfeffgffföä"), to!T("föä"), 9, - CaseSensitive.no) == 9); + No.caseSensitive) == 9); assert(indexOfAny("\u0100", to!T("\u0100"), 0, - CaseSensitive.no) == 0); + No.caseSensitive) == 0); }(); - foreach(cs; EnumMembers!CaseSensitive) + foreach (cs; EnumMembers!CaseSensitive) { assert(indexOfAny("hello\U00010143\u0100\U00010143", to!S("e\u0100"), 3, cs) == 9); @@ -1330,22 +1722,44 @@ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, } /** - Returns the index of the last occurence of any of the elements in $(D + Returns the index of the last occurrence of any of the elements in $(D needles) in $(D haystack). If no element of $(D needles) is found, - then $(D -1) is returned. + then $(D -1) is returned. The $(D stopIdx) slices $(D haystack) in the + following way $(D s[0 .. stopIdx]). $(D stopIdx) represents a codeunit + index in $(D haystack). If the sequence ending at $(D startIdx) does not + represent a well formed codepoint, then a $(REF UTFException, std,utf) may be + thrown. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. + haystack = String to search for needles in. + needles = Strings to search for in haystack. + stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If + the stopIdx is greater equal the length of haystack the functions + returns $(D -1). cs = Indicates whether the comparisons are case sensitive. */ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) + const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) + @safe pure +if (isSomeChar!Char && isSomeChar!Char2) { return indexOfAnyNeitherImpl!(false, true)(haystack, needles, cs); } +/// Ditto +ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, + const(Char2)[] needles, in size_t stopIdx, + in CaseSensitive cs = Yes.caseSensitive) @safe pure +if (isSomeChar!Char && isSomeChar!Char2) +{ + if (stopIdx <= haystack.length) + { + return lastIndexOfAny(haystack[0u .. stopIdx], needles, cs); + } + + return -1; +} + /// @safe pure unittest { @@ -1356,20 +1770,49 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, assert(i == 8); } +/// +@safe pure unittest +{ + import std.conv : to; + + ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo", 4); + assert(i == 3); + + i = "Foo öäöllo world".lastIndexOfAny("öF", 3); + assert(i == 0); +} + +@safe pure unittest +{ + import std.conv : to; + + foreach (S; AliasSeq!(string, wstring, dstring)) + { + auto r = to!S("").lastIndexOfAny("hello"); + assert(r == -1, to!string(r)); + + r = to!S("hello").lastIndexOfAny(""); + assert(r == -1, to!string(r)); + + r = to!S("").lastIndexOfAny(""); + assert(r == -1, to!string(r)); + } +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.lastIndexOfAny.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(lastIndexOfAny(cast(S)null, to!T("a")) == -1); + assert(lastIndexOfAny(cast(S) null, to!T("a")) == -1); assert(lastIndexOfAny(to!S("def"), to!T("rsa")) == -1); assert(lastIndexOfAny(to!S("abba"), to!T("a")) == 3); assert(lastIndexOfAny(to!S("def"), to!T("f")) == 2); @@ -1385,15 +1828,15 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); assert(lastIndexOfAny(to!S("zfeffgfff"), to!T("ACDC"), - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(lastIndexOfAny(to!S("def"), to!T("MI6"), - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(lastIndexOfAny(to!S("abba"), to!T("DEA"), - CaseSensitive.no) == 3); + No.caseSensitive) == 3); assert(lastIndexOfAny(to!S("def"), to!T("FBI"), - CaseSensitive.no) == 2); + No.caseSensitive) == 2); assert(lastIndexOfAny(to!S("dfefffg"), to!T("NSA"), - CaseSensitive.no) == -1); + No.caseSensitive) == -1); oeIdx = 2; if (is(S == wstring) || is(S == dstring)) @@ -1401,73 +1844,32 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, oeIdx = 1; } assert(lastIndexOfAny(to!S("ödfeffgfff"), to!T("BND"), - CaseSensitive.no) == oeIdx); + No.caseSensitive) == oeIdx); assert(lastIndexOfAny("\u0100", to!T("\u0100"), - CaseSensitive.no) == 0); + No.caseSensitive) == 0); }(); } } ); } -/** - Returns the index of the last occurence of any of the elements in $(D - needles) in $(D haystack). If no element of $(D needles) is found, - then $(D -1) is returned. The $(D stopIdx) slices $(D s) in the following - way $(D s[0 .. stopIdx]). $(D stopIdx) represents a codeunit index in - $(D s). If the sequence ending at $(D startIdx) does not represent a well - formed codepoint, then a $(XREF utf,UTFException) may be thrown. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If - the stopIdx is greater equal the length of haystack the functions - returns $(D -1). - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in size_t stopIdx, - in CaseSensitive cs = CaseSensitive.yes) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) -{ - if (stopIdx <= haystack.length) - { - return lastIndexOfAny(haystack[0u .. stopIdx], needles, cs); - } - - return -1; -} - -/// -@safe pure unittest -{ - import std.conv : to; - - ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo", 4); - assert(i == 3); - - i = "Foo öäöllo world".lastIndexOfAny("öF", 3); - assert(i == 0); -} - @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.lastIndexOfAny(index).unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 enum typeStr = S.stringof ~ " " ~ T.stringof; - assert(lastIndexOfAny(cast(S)null, to!T("a"), 1337) == -1, + assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337) == -1, typeStr); assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("c"), 7) == 6, typeStr); @@ -1484,22 +1886,22 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, assert(lastIndexOfAny(to!S("öabcdefcdef"), to!T("ö"), 2) == 0, typeStr); - assert(lastIndexOfAny(cast(S)null, to!T("a"), 1337, - CaseSensitive.no) == -1, typeStr); + assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337, + No.caseSensitive) == -1, typeStr); assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("C"), 7, - CaseSensitive.no) == 6, typeStr); + No.caseSensitive) == 6, typeStr); assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("cd"), 5, - CaseSensitive.no) == 3, typeStr); + No.caseSensitive) == 3, typeStr); assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("EF"), 6, - CaseSensitive.no) == 5, typeStr); + No.caseSensitive) == 5, typeStr); assert(lastIndexOfAny(to!S("ABCDEFcDEF"), to!T("C"), 8, - CaseSensitive.no) == 6, typeStr); + No.caseSensitive) == 6, typeStr); assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("x"), 7, - CaseSensitive.no) == -1, typeStr); + No.caseSensitive) == -1, typeStr); assert(lastIndexOfAny(to!S("abCdefcdef"), to!T("XY"), 4, - CaseSensitive.no) == -1, typeStr); + No.caseSensitive) == -1, typeStr); assert(lastIndexOfAny(to!S("ÖABCDEFCDEF"), to!T("ö"), 2, - CaseSensitive.no) == 0, typeStr); + No.caseSensitive) == 0, typeStr); }(); } } @@ -1507,23 +1909,53 @@ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, } /** - Returns the index of the first occurence of any character not an elements + Returns the index of the first occurrence of any character not an elements in $(D needles) in $(D haystack). If all element of $(D haystack) are element of $(D needles) $(D -1) is returned. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. + haystack = String to search for needles in. + needles = Strings to search for in haystack. + startIdx = slices haystack like this $(D haystack[startIdx .. $]). If + the startIdx is greater equal the length of haystack the functions + returns $(D -1). cs = Indicates whether the comparisons are case sensitive. */ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in CaseSensitive cs = CaseSensitive.yes) + const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) +if (isSomeChar!Char && isSomeChar!Char2) { return indexOfAnyNeitherImpl!(true, false)(haystack, needles, cs); } +/// Ditto +ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, + const(Char2)[] needles, in size_t startIdx, + in CaseSensitive cs = Yes.caseSensitive) + @safe pure +if (isSomeChar!Char && isSomeChar!Char2) +{ + if (startIdx < haystack.length) + { + ptrdiff_t foundIdx = indexOfAnyNeitherImpl!(true, false)( + haystack[startIdx .. $], needles, cs); + if (foundIdx != -1) + { + return foundIdx + cast(ptrdiff_t) startIdx; + } + } + return -1; +} + +/// +@safe pure unittest +{ + assert(indexOfNeither("abba", "a", 2) == 2); + assert(indexOfNeither("def", "de", 1) == 2); + assert(indexOfNeither("dfefffg", "dfe", 4) == 6); +} + /// @safe pure unittest { @@ -1536,41 +1968,58 @@ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, { import std.conv : to; + foreach (S; AliasSeq!(string, wstring, dstring)) + { + auto r = to!S("").indexOfNeither("hello"); + assert(r == -1, to!string(r)); + + r = to!S("hello").indexOfNeither(""); + assert(r == 0, to!string(r)); + + r = to!S("").indexOfNeither(""); + assert(r == -1, to!string(r)); + } +} + +@safe pure unittest +{ + import std.conv : to; + import std.exception : assertCTFEable; + debug(string) trustedPrintf("string.indexOf.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(indexOfNeither(cast(S)null, to!T("a")) == -1); + assert(indexOfNeither(cast(S) null, to!T("a")) == -1); assert(indexOfNeither("abba", "a") == 1); assert(indexOfNeither(to!S("dfeffgfff"), to!T("a"), - CaseSensitive.no) == 0); + No.caseSensitive) == 0); assert(indexOfNeither(to!S("def"), to!T("D"), - CaseSensitive.no) == 1); + No.caseSensitive) == 1); assert(indexOfNeither(to!S("ABca"), to!T("a"), - CaseSensitive.no) == 1); + No.caseSensitive) == 1); assert(indexOfNeither(to!S("def"), to!T("f"), - CaseSensitive.no) == 0); + No.caseSensitive) == 0); assert(indexOfNeither(to!S("DfEfffg"), to!T("dFe"), - CaseSensitive.no) == 6); + No.caseSensitive) == 6); if (is(S == string)) { assert(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - CaseSensitive.no) == 8, + No.caseSensitive) == 8, to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - CaseSensitive.no))); + No.caseSensitive))); } else { assert(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - CaseSensitive.no) == 7, + No.caseSensitive) == 7, to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"), - CaseSensitive.no))); + No.caseSensitive))); } }(); } @@ -1578,83 +2027,44 @@ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, ); } -/** - Returns the index of the first occurence of any character not an elements - in $(D needles) in $(D haystack). If all element of $(D haystack) are - element of $(D needles) $(D -1) is returned. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater equal the length of haystack the functions - returns $(D -1). - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in size_t startIdx, - in CaseSensitive cs = CaseSensitive.yes) - @safe pure - if (isSomeChar!Char && isSomeChar!Char2) +@safe pure unittest { - if (startIdx < haystack.length) - { - ptrdiff_t foundIdx = indexOfAnyNeitherImpl!(true, false)( - haystack[startIdx .. $], needles, cs); - if (foundIdx != -1) - { - return foundIdx + cast(ptrdiff_t)startIdx; - } - } - return -1; -} - -/// -@safe pure unittest -{ - assert(indexOfNeither("abba", "a", 2) == 2); - assert(indexOfNeither("def", "de", 1) == 2); - assert(indexOfNeither("dfefffg", "dfe", 4) == 6); -} - -@safe pure unittest -{ - import std.conv : to; + import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.indexOfNeither(index).unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(indexOfNeither(cast(S)null, to!T("a"), 1) == -1); + assert(indexOfNeither(cast(S) null, to!T("a"), 1) == -1); assert(indexOfNeither(to!S("def"), to!T("a"), 1) == 1, to!string(indexOfNeither(to!S("def"), to!T("a"), 1))); assert(indexOfNeither(to!S("dfeffgfff"), to!T("a"), 4, - CaseSensitive.no) == 4); + No.caseSensitive) == 4); assert(indexOfNeither(to!S("def"), to!T("D"), 2, - CaseSensitive.no) == 2); + No.caseSensitive) == 2); assert(indexOfNeither(to!S("ABca"), to!T("a"), 3, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfNeither(to!S("def"), to!T("tzf"), 2, - CaseSensitive.no) == -1); + No.caseSensitive) == -1); assert(indexOfNeither(to!S("DfEfffg"), to!T("dFe"), 5, - CaseSensitive.no) == 6); + No.caseSensitive) == 6); if (is(S == string)) { assert(indexOfNeither(to!S("öDfEfffg"), to!T("äDi"), 2, - CaseSensitive.no) == 3, to!string(indexOfNeither( - to!S("öDfEfffg"), to!T("äDi"), 2, CaseSensitive.no))); + No.caseSensitive) == 3, to!string(indexOfNeither( + to!S("öDfEfffg"), to!T("äDi"), 2, No.caseSensitive))); } else { assert(indexOfNeither(to!S("öDfEfffg"), to!T("äDi"), 2, - CaseSensitive.no) == 2, to!string(indexOfNeither( - to!S("öDfEfffg"), to!T("äDi"), 2, CaseSensitive.no))); + No.caseSensitive) == 2, to!string(indexOfNeither( + to!S("öDfEfffg"), to!T("äDi"), 2, No.caseSensitive))); } }(); } @@ -1668,18 +2078,36 @@ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, $(D haystack) are element of $(D needles) $(D -1) is returned. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. + haystack = String to search for needles in. + needles = Strings to search for in haystack. + stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If + the stopIdx is greater equal the length of haystack the functions + returns $(D -1). cs = Indicates whether the comparisons are case sensitive. */ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in CaseSensitive cs = CaseSensitive.yes) + const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @safe pure - if (isSomeChar!Char && isSomeChar!Char2) +if (isSomeChar!Char && isSomeChar!Char2) { return indexOfAnyNeitherImpl!(false, false)(haystack, needles, cs); } +/// Ditto +ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, + const(Char2)[] needles, in size_t stopIdx, + in CaseSensitive cs = Yes.caseSensitive) + @safe pure +if (isSomeChar!Char && isSomeChar!Char2) +{ + if (stopIdx < haystack.length) + { + return indexOfAnyNeitherImpl!(false, false)(haystack[0 .. stopIdx], + needles, cs); + } + return -1; +} + /// @safe pure unittest { @@ -1687,20 +2115,44 @@ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, assert(lastIndexOfNeither("def", "f") == 1); } +/// +@safe pure unittest +{ + assert(lastIndexOfNeither("def", "rsa", 3) == -1); + assert(lastIndexOfNeither("abba", "a", 2) == 1); +} + +@safe pure unittest +{ + import std.conv : to; + + foreach (S; AliasSeq!(string, wstring, dstring)) + { + auto r = to!S("").lastIndexOfNeither("hello"); + assert(r == -1, to!string(r)); + + r = to!S("hello").lastIndexOfNeither(""); + assert(r == 4, to!string(r)); + + r = to!S("").lastIndexOfNeither(""); + assert(r == -1, to!string(r)); + } +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.lastIndexOfNeither.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(lastIndexOfNeither(cast(S)null, to!T("a")) == -1); + assert(lastIndexOfNeither(cast(S) null, to!T("a")) == -1); assert(lastIndexOfNeither(to!S("def"), to!T("rsa")) == 2); assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2); @@ -1714,74 +2166,40 @@ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); assert(lastIndexOfNeither(to!S("zfeffgfsb"), to!T("FSB"), - CaseSensitive.no) == 5); + No.caseSensitive) == 5); assert(lastIndexOfNeither(to!S("def"), to!T("MI6"), - CaseSensitive.no) == 2, to!string(lastIndexOfNeither(to!S("def"), - to!T("MI6"), CaseSensitive.no))); + No.caseSensitive) == 2, to!string(lastIndexOfNeither(to!S("def"), + to!T("MI6"), No.caseSensitive))); assert(lastIndexOfNeither(to!S("abbadeafsb"), to!T("fSb"), - CaseSensitive.no) == 6, to!string(lastIndexOfNeither( - to!S("abbadeafsb"), to!T("fSb"), CaseSensitive.no))); + No.caseSensitive) == 6, to!string(lastIndexOfNeither( + to!S("abbadeafsb"), to!T("fSb"), No.caseSensitive))); assert(lastIndexOfNeither(to!S("defbi"), to!T("FBI"), - CaseSensitive.no) == 1); + No.caseSensitive) == 1); assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), - CaseSensitive.no) == 6); + No.caseSensitive) == 6); assert(lastIndexOfNeither(to!S("dfeffgfffö"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), - CaseSensitive.no) == 8, to!string(lastIndexOfNeither(to!S("dfeffgfffö"), - to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), CaseSensitive.no))); + No.caseSensitive) == 8, to!string(lastIndexOfNeither(to!S("dfeffgfffö"), + to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), No.caseSensitive))); }(); } } ); } -/** - Returns the last index of the first occurence of any character that is not - an elements in $(D needles) in $(D haystack). If all element of - $(D haystack) are element of $(D needles) $(D -1) is returned. - - Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If - the stopIdx is greater equal the length of haystack the functions - returns $(D -1). - cs = Indicates whether the comparisons are case sensitive. -*/ -ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, - const(Char2)[] needles, in size_t stopIdx, - in CaseSensitive cs = CaseSensitive.yes) - @safe pure - if (isSomeChar!Char && isSomeChar!Char2) -{ - if (stopIdx < haystack.length) - { - return indexOfAnyNeitherImpl!(false, false)(haystack[0 .. stopIdx], - needles, cs); - } - return -1; -} - -/// -@safe pure unittest -{ - assert(lastIndexOfNeither("def", "rsa", 3) == -1); - assert(lastIndexOfNeither("abba", "a", 2) == 1); -} - @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.lastIndexOfNeither(index).unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { - foreach (T; TypeTuple!(string, wstring, dstring)) + foreach (T; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(lastIndexOfNeither(cast(S)null, to!T("a"), 1337) == -1); + assert(lastIndexOfNeither(cast(S) null, to!T("a"), 1337) == -1); assert(lastIndexOfNeither(to!S("def"), to!T("f")) == 1); assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2); @@ -1796,35 +2214,41 @@ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); assert(lastIndexOfNeither(to!S("zfeffgfsb"), to!T("FSB"), 6, - CaseSensitive.no) == 5); + No.caseSensitive) == 5); assert(lastIndexOfNeither(to!S("def"), to!T("MI6"), 2, - CaseSensitive.no) == 1, to!string(lastIndexOfNeither(to!S("def"), - to!T("MI6"), 2, CaseSensitive.no))); + No.caseSensitive) == 1, to!string(lastIndexOfNeither(to!S("def"), + to!T("MI6"), 2, No.caseSensitive))); assert(lastIndexOfNeither(to!S("abbadeafsb"), to!T("fSb"), 6, - CaseSensitive.no) == 5, to!string(lastIndexOfNeither( - to!S("abbadeafsb"), to!T("fSb"), 6, CaseSensitive.no))); + No.caseSensitive) == 5, to!string(lastIndexOfNeither( + to!S("abbadeafsb"), to!T("fSb"), 6, No.caseSensitive))); assert(lastIndexOfNeither(to!S("defbi"), to!T("FBI"), 3, - CaseSensitive.no) == 1); + No.caseSensitive) == 1); assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), 2, - CaseSensitive.no) == 1, to!string(lastIndexOfNeither( - to!S("dfefffg"), to!T("NSA"), 2, CaseSensitive.no))); + No.caseSensitive) == 1, to!string(lastIndexOfNeither( + to!S("dfefffg"), to!T("NSA"), 2, No.caseSensitive))); }(); } } ); } - /** - * Returns the representation of a string, which has the same type + * Returns the _representation of a string, which has the same type * as the string except the character type is replaced by $(D ubyte), * $(D ushort), or $(D uint) depending on the character width. + * + * Params: + * s = The string to return the _representation of. + * + * Returns: + * The _representation of the passed string. */ auto representation(Char)(Char[] s) @safe pure nothrow @nogc - if (isSomeChar!Char) +if (isSomeChar!Char) { - alias ToRepType(T) = TypeTuple!(ubyte, ushort, uint)[T.sizeof / 2]; - return cast(ModifyTypePreservingSTC!(ToRepType, Char)[])s; + import std.traits : ModifyTypePreservingTQ; + alias ToRepType(T) = AliasSeq!(ubyte, ushort, uint)[T.sizeof / 2]; + return cast(ModifyTypePreservingTQ!(ToRepType, Char)[])s; } /// @@ -1836,10 +2260,11 @@ auto representation(Char)(Char[] s) @safe pure nothrow @nogc assert(representation(s) == [0x68, 0x65, 0x6c, 0x6c, 0x6f]); } -@trusted pure unittest +@system pure unittest { - import std.exception; - import std.typecons; + import std.exception : assertCTFEable; + import std.traits : Fields; + import std.typecons : Tuple; assertCTFEable!( { @@ -1849,12 +2274,12 @@ auto representation(Char)(Char[] s) @safe pure nothrow @nogc assert(representation(str) is cast(T[]) str); } - foreach (Type; TypeTuple!(Tuple!(char , ubyte ), - Tuple!(wchar, ushort), - Tuple!(dchar, uint ))) + foreach (Type; AliasSeq!(Tuple!(char , ubyte ), + Tuple!(wchar, ushort), + Tuple!(dchar, uint ))) { - alias Char = FieldTypeTuple!Type[0]; - alias Int = FieldTypeTuple!Type[1]; + alias Char = Fields!Type[0]; + alias Int = Fields!Type[1]; enum immutable(Char)[] hello = "hello"; test!( immutable Char, immutable Int)(hello); @@ -1867,57 +2292,56 @@ auto representation(Char)(Char[] s) @safe pure nothrow @nogc } -/++ - Capitalize the first character of $(D s) and convert the rest of $(D s) - to lowercase. - +/ -S capitalize(S)(S s) @trusted pure - if (isSomeString!S) +/** + * Capitalize the first character of $(D s) and convert the rest of $(D s) to + * lowercase. + * + * Params: + * input = The string to _capitalize. + * + * Returns: + * The capitalized string. + * + * See_Also: + * $(REF asCapitalized, std,uni) for a lazy range version that doesn't allocate memory + */ +S capitalize(S)(S input) @trusted pure +if (isSomeString!S) { - import std.utf : encode; - - Unqual!(typeof(s[0]))[] retval; - bool changed = false; + import std.array : array; + import std.utf : byUTF; + import std.uni : asCapitalized; - foreach (i, dchar c; s) - { - dchar c2; + return input.asCapitalized.byUTF!(ElementEncodingType!(S)).array; +} - if (i == 0) - { - c2 = std.uni.toUpper(c); - if (c != c2) - changed = true; - } - else - { - c2 = std.uni.toLower(c); - if (c != c2) - { - if (!changed) - { - changed = true; - retval = s[0 .. i].dup; - } - } - } +/// +pure @safe unittest +{ + assert(capitalize("hello") == "Hello"); + assert(capitalize("World") == "World"); +} - if (changed) - std.utf.encode(retval, c2); - } +auto capitalize(S)(auto ref S s) +if (!isSomeString!S && is(StringTypeOf!S)) +{ + return capitalize!(StringTypeOf!S)(s); +} - return changed ? cast(S)retval : s; +@safe pure unittest +{ + assert(testAliasedString!capitalize("hello")); } -@trusted pure unittest +@safe pure unittest { + import std.algorithm.comparison : cmp; import std.conv : to; - import std.algorithm : cmp; + import std.exception : assertCTFEable; - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) + foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[])) { S s1 = to!S("FoL"); S s2; @@ -1928,7 +2352,6 @@ S capitalize(S)(S s) @trusted pure s2 = capitalize(s1[0 .. 2]); assert(cmp(s2, "Fo") == 0); - assert(s2.ptr == s1.ptr); s1 = to!S("fOl"); s2 = capitalize(s1); @@ -1936,7 +2359,7 @@ S capitalize(S)(S s) @trusted pure assert(s2 !is s1); s1 = to!S("\u0131 \u0130"); s2 = capitalize(s1); - assert(cmp(s2, "\u0049 \u0069") == 0); + assert(cmp(s2, "\u0049 i\u0307") == 0); assert(s2 !is s1); s1 = to!S("\u017F \u0049"); @@ -1949,8 +2372,8 @@ S capitalize(S)(S s) @trusted pure /++ Split $(D s) into an array of lines according to the unicode standard using - $(D '\r'), $(D '\n'), $(D "\r\n"), $(XREF uni, lineSep), - $(XREF uni, paraSep), $(D U+0085) (NEL), $(D '\v') and $(D '\f') + $(D '\r'), $(D '\n'), $(D "\r\n"), $(REF lineSep, std,uni), + $(REF paraSep, std,uni), $(D U+0085) (NEL), $(D '\v') and $(D '\f') as delimiters. If $(D keepTerm) is set to $(D KeepTerminator.yes), then the delimiter is included in the strings returned. @@ -1960,25 +2383,27 @@ S capitalize(S)(S s) @trusted pure Allocates memory; use $(LREF lineSplitter) for an alternative that does not. - Adheres to $(WEB http://www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0). + Adheres to $(HTTP www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0). Params: - s = a string of $(D chars), $(D wchars), or $(D dchars) + s = a string of $(D chars), $(D wchars), or $(D dchars), or any custom + type that casts to a $(D string) type keepTerm = whether delimiter is included or not in the results Returns: array of strings, each element is a line that is a slice of $(D s) See_Also: $(LREF lineSplitter) - $(XREF algorithm, splitter) - $(XREF regex, splitter) + $(REF splitter, std,algorithm) + $(REF splitter, std,regex) +/ alias KeepTerminator = Flag!"keepTerminator"; + /// ditto -S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pure - if (isSomeString!S) +S[] splitLines(S)(S s, in KeepTerminator keepTerm = No.keepTerminator) @safe pure +if (isSomeString!S) { - import std.uni : lineSep, paraSep; import std.array : appender; + import std.uni : lineSep, paraSep; size_t iStart = 0; auto retval = appender!(S[])(); @@ -1988,14 +2413,14 @@ S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pur switch (s[i]) { case '\v', '\f', '\n': - retval.put(s[iStart .. i + (keepTerm == KeepTerminator.yes)]); + retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator)]); iStart = i + 1; break; case '\r': if (i + 1 < s.length && s[i + 1] == '\n') { - retval.put(s[iStart .. i + (keepTerm == KeepTerminator.yes) * 2]); + retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 2]); iStart = i + 2; ++i; } @@ -2017,7 +2442,7 @@ S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pur (s[i + 2] == 0xA8 || s[i + 2] == 0xA9) ) { - retval.put(s[iStart .. i + (keepTerm == KeepTerminator.yes) * 3]); + retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 3]); iStart = i + 3; i += 2; } @@ -2028,9 +2453,9 @@ S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pur * NEL is C2 85 */ case 0xC2: - if(i + 1 < s.length && s[i + 1] == 0x85) + if (i + 1 < s.length && s[i + 1] == 0x85) { - retval.put(s[iStart .. i + (keepTerm == KeepTerminator.yes) * 2]); + retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 2]); iStart = i + 2; i += 1; } @@ -2057,16 +2482,40 @@ S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pur return retval.data; } +/// +@safe pure nothrow unittest +{ + string s = "Hello\nmy\rname\nis"; + assert(splitLines(s) == ["Hello", "my", "name", "is"]); +} + +@safe pure nothrow unittest +{ + string s = "a\xC2\x86b"; + assert(splitLines(s) == [s]); +} + +auto splitLines(S)(auto ref S s, in KeepTerminator keepTerm = No.keepTerminator) +if (!isSomeString!S && is(StringTypeOf!S)) +{ + return splitLines!(StringTypeOf!S)(s, keepTerm); +} + +@safe pure nothrow unittest +{ + assert(testAliasedString!splitLines("hello\nworld")); +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.splitLines.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { auto s = to!S( "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\n" ~ @@ -2091,10 +2540,10 @@ S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pur ubyte[] u = ['a', 0xFF, 0x12, 'b']; // invalid UTF - auto ulines = splitLines(cast(char[])u); + auto ulines = splitLines(cast(char[]) u); assert(cast(ubyte[])(ulines[0]) == u); - lines = splitLines(s, KeepTerminator.yes); + lines = splitLines(s, Yes.keepTerminator); assert(lines.length == 14); assert(lines[0] == "\r"); assert(lines[1] == "peter\n"); @@ -2116,194 +2565,215 @@ S[] splitLines(S)(S s, in KeepTerminator keepTerm = KeepTerminator.no) @safe pur assert(lines.length == 14); assert(lines[9] == "mon\u2030day"); - lines = splitLines(s, KeepTerminator.yes); + lines = splitLines(s, Yes.keepTerminator); assert(lines.length == 14); assert(lines[13] == "cookies"); } }); } -/*********************************** - * Split an array or slicable range of characters into a range of lines - using $(D '\r'), $(D '\n'), $(D '\v'), $(D '\f'), $(D "\r\n"), - $(XREF uni, lineSep), $(XREF uni, paraSep) and $(D '\u0085') (NEL) - as delimiters. If $(D keepTerm) is set to $(D KeepTerminator.yes), then the - delimiter is included in the slices returned. - - Does not throw on invalid UTF; such is simply passed unchanged - to the output. - - Adheres to $(WEB http://www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0). - - Does not allocate memory. - - Params: - r = array of $(D chars), $(D wchars), or $(D dchars) or a slicable range - keepTerm = whether delimiter is included or not in the results - Returns: - range of slices of the input range $(D r) - - See_Also: - $(LREF splitLines) - $(XREF algorithm, splitter) - $(XREF regex, splitter) - */ -auto lineSplitter(KeepTerminator keepTerm = KeepTerminator.no, Range)(Range r) -if ((hasSlicing!Range && hasLength!Range) || - isSomeString!Range) +private struct LineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range) { - import std.uni : lineSep, paraSep; import std.conv : unsigned; + import std.uni : lineSep, paraSep; +private: + Range _input; - static struct Result - { - private: - Range _input; - alias IndexType = typeof(unsigned(_input.length)); - enum IndexType _unComputed = IndexType.max; - IndexType iStart = _unComputed; - IndexType iEnd = 0; - IndexType iNext = 0; + alias IndexType = typeof(unsigned(_input.length)); + enum IndexType _unComputed = IndexType.max; + IndexType iStart = _unComputed; + IndexType iEnd = 0; + IndexType iNext = 0; - public: - this(Range input) - { - _input = input; - } +public: + this(Range input) + { + _input = input; + } - static if (isInfinite!Range) - { - enum bool empty = false; - } - else + static if (isInfinite!Range) + { + enum bool empty = false; + } + else + { + @property bool empty() { - @property bool empty() - { - return iStart == _unComputed && iNext == _input.length; - } + return iStart == _unComputed && iNext == _input.length; } + } - @property Range front() + @property typeof(_input) front() + { + if (iStart == _unComputed) { - if (iStart == _unComputed) + iStart = iNext; + Loop: + for (IndexType i = iNext; ; ++i) { - iStart = iNext; - Loop: - for (IndexType i = iNext; ; ++i) + if (i == _input.length) + { + iEnd = i; + iNext = i; + break Loop; + } + switch (_input[i]) { - if (i == _input.length) + case '\v', '\f', '\n': + iEnd = i + (keepTerm == Yes.keepTerminator); + iNext = i + 1; + break Loop; + + case '\r': + if (i + 1 < _input.length && _input[i + 1] == '\n') { - iEnd = i; - iNext = i; + iEnd = i + (keepTerm == Yes.keepTerminator) * 2; + iNext = i + 2; break Loop; } - switch (_input[i]) + else { - case '\v', '\f', '\n': - iEnd = i + (keepTerm == KeepTerminator.yes); - iNext = i + 1; - break Loop; - - case '\r': - if (i + 1 < _input.length && _input[i + 1] == '\n') - { - iEnd = i + (keepTerm == KeepTerminator.yes) * 2; - iNext = i + 2; - break Loop; - } - else - { - goto case '\n'; - } + goto case '\n'; + } - static if (_input[i].sizeof == 1) + static if (_input[i].sizeof == 1) + { + /* Manually decode: + * lineSep is E2 80 A8 + * paraSep is E2 80 A9 + */ + case 0xE2: + if (i + 2 < _input.length && + _input[i + 1] == 0x80 && + (_input[i + 2] == 0xA8 || _input[i + 2] == 0xA9) + ) { - /* Manually decode: - * lineSep is E2 80 A8 - * paraSep is E2 80 A9 - */ - case 0xE2: - if (i + 2 < _input.length && - _input[i + 1] == 0x80 && - (_input[i + 2] == 0xA8 || _input[i + 2] == 0xA9) - ) - { - iEnd = i + (keepTerm == KeepTerminator.yes) * 3; - iNext = i + 3; - break Loop; - } - else - goto default; - /* Manually decode: - * NEL is C2 85 - */ - case 0xC2: - if(i + 1 < _input.length && _input[i + 1] == 0x85) - { - iEnd = i + (keepTerm == KeepTerminator.yes) * 2; - iNext = i + 2; - break Loop; - } - else - goto default; + iEnd = i + (keepTerm == Yes.keepTerminator) * 3; + iNext = i + 3; + break Loop; } else + goto default; + /* Manually decode: + * NEL is C2 85 + */ + case 0xC2: + if (i + 1 < _input.length && _input[i + 1] == 0x85) { - case '\u0085': - case lineSep: - case paraSep: - goto case '\n'; + iEnd = i + (keepTerm == Yes.keepTerminator) * 2; + iNext = i + 2; + break Loop; } - - default: - break; + else + goto default; } + else + { + case '\u0085': + case lineSep: + case paraSep: + goto case '\n'; + } + + default: + break; } } - return _input[iStart .. iEnd]; } + return _input[iStart .. iEnd]; + } - void popFront() + void popFront() + { + if (iStart == _unComputed) { - if (iStart == _unComputed) - { - assert(!empty); - front(); - } - iStart = _unComputed; + assert(!empty); + front; } + iStart = _unComputed; + } - static if (isForwardRange!Range) + static if (isForwardRange!Range) + { + @property typeof(this) save() { - @property typeof(this) save() - { - auto ret = this; - ret._input = _input.save; - return ret; - } + auto ret = this; + ret._input = _input.save; + return ret; } } +} - return Result(r); +/*********************************** + * Split an array or slicable range of characters into a range of lines + using $(D '\r'), $(D '\n'), $(D '\v'), $(D '\f'), $(D "\r\n"), + $(REF lineSep, std,uni), $(REF paraSep, std,uni) and $(D '\u0085') (NEL) + as delimiters. If $(D keepTerm) is set to $(D Yes.keepTerminator), then the + delimiter is included in the slices returned. + + Does not throw on invalid UTF; such is simply passed unchanged + to the output. + + Adheres to $(HTTP www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0). + + Does not allocate memory. + + Params: + r = array of $(D chars), $(D wchars), or $(D dchars) or a slicable range + keepTerm = whether delimiter is included or not in the results + Returns: + range of slices of the input range $(D r) + + See_Also: + $(LREF splitLines) + $(REF splitter, std,algorithm) + $(REF splitter, std,regex) + */ +auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(Range r) +if ((hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) || + isSomeString!Range) && + !isConvertibleToString!Range) +{ + return LineSplitter!(keepTerm, Range)(r); } +/// @safe pure unittest { - import std.conv : to; import std.array : array; - debug(string) trustedPrintf("string.lineSplitter.unittest\n"); + string s = "Hello\nmy\rname\nis"; - import std.exception; - assertCTFEable!( - { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) - { - auto s = to!S( - "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\n" ~ - "sunday\nmon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085" - ); - auto lines = lineSplitter(s).array; + /* notice the call to $(D array) to turn the lazy range created by + lineSplitter comparable to the $(D string[]) created by splitLines. + */ + assert(lineSplitter(s).array == splitLines(s)); +} + +auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(auto ref Range r) +if (isConvertibleToString!Range) +{ + return LineSplitter!(keepTerm, StringTypeOf!Range)(r); +} + +@safe pure unittest +{ + import std.array : array; + import std.conv : to; + import std.exception : assertCTFEable; + + debug(string) trustedPrintf("string.lineSplitter.unittest\n"); + + assertCTFEable!( + { + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) + { + auto s = to!S( + "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\n" ~ + "sunday\nmon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085" + ); + + auto lines = lineSplitter(s).array; assert(lines.length == 14); assert(lines[0] == ""); assert(lines[1] == "peter"); @@ -2322,10 +2792,10 @@ if ((hasSlicing!Range && hasLength!Range) || ubyte[] u = ['a', 0xFF, 0x12, 'b']; // invalid UTF - auto ulines = lineSplitter(cast(char[])u).array; + auto ulines = lineSplitter(cast(char[]) u).array; assert(cast(ubyte[])(ulines[0]) == u); - lines = lineSplitter!(KeepTerminator.yes)(s).array; + lines = lineSplitter!(Yes.keepTerminator)(s).array; assert(lines.length == 14); assert(lines[0] == "\r"); assert(lines[1] == "peter\n"); @@ -2347,7 +2817,7 @@ if ((hasSlicing!Range && hasLength!Range) || assert(lines.length == 14); assert(lines[9] == "mon\u2030day"); - lines = lineSplitter!(KeepTerminator.yes)(s).array; + lines = lineSplitter!(Yes.keepTerminator)(s).array; assert(lines.length == 14); assert(lines[13] == "cookies"); } @@ -2368,24 +2838,66 @@ if ((hasSlicing!Range && hasLength!Range) || assert(i == witness.length); } +@nogc @safe pure unittest +{ + import std.algorithm.comparison : equal; + auto s = "std/string.d"; + auto as = TestAliasedString(s); + assert(equal(s.lineSplitter(), as.lineSplitter())); +} + +@safe pure unittest +{ + auto s = "line1\nline2"; + auto spl0 = s.lineSplitter!(Yes.keepTerminator); + auto spl1 = spl0.save; + spl0.popFront; + assert(spl1.front ~ spl0.front == s); + string r = "a\xC2\x86b"; + assert(r.lineSplitter.front == r); +} + /++ - Strips leading whitespace (as defined by $(XREF uni, isWhite)). + Strips leading whitespace (as defined by $(REF isWhite, std,uni)). + + Params: + input = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + of characters + + Returns: $(D input) stripped of leading whitespace. - Returns: $(D str) stripped of leading whitespace. + Postconditions: $(D input) and the returned value + will share the same tail (see $(REF sameTail, std,array)). - Postconditions: $(D str) and the returned value - will share the same tail (see $(XREF array, sameTail)). + See_Also: + Generic stripping on ranges: $(REF _stripLeft, std, algorithm, mutation) +/ -C[] stripLeft(C)(C[] str) @safe pure @nogc - if (isSomeChar!C) +auto stripLeft(Range)(Range input) +if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isInfinite!Range && !isConvertibleToString!Range) { - foreach (i, dchar c; str) + static import std.ascii; + static import std.uni; + import std.utf : decodeFront; + + while (!input.empty) { - if (!std.uni.isWhite(c)) - return str[i .. $]; + auto c = input.front; + if (std.ascii.isASCII(c)) + { + if (!std.ascii.isWhite(c)) + break; + input.popFront(); + } + else + { + auto save = input.save; + auto dc = decodeFront(input); + if (!std.uni.isWhite(dc)) + return save; + } } - - return str[$ .. $]; //Empty string with correct type. + return input; } /// @@ -2402,32 +2914,135 @@ C[] stripLeft(C)(C[] str) @safe pure @nogc "hello world" ~ [lineSep]); assert(stripLeft([paraSep] ~ "hello world" ~ paraSep) == "hello world" ~ [paraSep]); + + import std.array : array; + import std.utf : byChar; + assert(stripLeft(" hello world "w.byChar).array == + "hello world "); +} + +auto stripLeft(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + return stripLeft!(StringTypeOf!Range)(str); } +@safe pure unittest +{ + assert(testAliasedString!stripLeft(" hello")); +} /++ - Strips trailing whitespace (as defined by $(XREF uni, isWhite)). + Strips trailing whitespace (as defined by $(REF isWhite, std,uni)). + + Params: + str = string or random access range of characters - Returns: $(D str) stripped of trailing whitespace. + Returns: + slice of $(D str) stripped of trailing whitespace. - Postconditions: $(D str) and the returned value - will share the same head (see $(XREF array, sameHead)). + See_Also: + Generic stripping on ranges: $(REF _stripRight, std, algorithm, mutation) +/ -C[] stripRight(C)(C[] str) @safe pure @nogc - if (isSomeChar!C) +auto stripRight(Range)(Range str) +if (isSomeString!Range || + isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && + !isConvertibleToString!Range && + isSomeChar!(ElementEncodingType!Range)) { - import std.utf : codeLength; - foreach_reverse (i, dchar c; str) + import std.uni : isWhite; + alias C = Unqual!(ElementEncodingType!(typeof(str))); + + static if (isSomeString!(typeof(str))) { - if (!std.uni.isWhite(c)) - return str[0 .. i + codeLength!C(c)]; + import std.utf : codeLength; + + foreach_reverse (i, dchar c; str) + { + if (!isWhite(c)) + return str[0 .. i + codeLength!C(c)]; + } + + return str[0 .. 0]; } + else + { + size_t i = str.length; + while (i--) + { + static if (C.sizeof == 4) + { + if (isWhite(str[i])) + continue; + break; + } + else static if (C.sizeof == 2) + { + auto c2 = str[i]; + if (c2 < 0xD800 || c2 >= 0xE000) + { + if (isWhite(c2)) + continue; + } + else if (c2 >= 0xDC00) + { + if (i) + { + immutable c1 = str[i - 1]; + if (c1 >= 0xD800 && c1 < 0xDC00) + { + immutable dchar c = ((c1 - 0xD7C0) << 10) + (c2 - 0xDC00); + if (isWhite(c)) + { + --i; + continue; + } + } + } + } + break; + } + else static if (C.sizeof == 1) + { + import std.utf : byDchar; + + char cx = str[i]; + if (cx <= 0x7F) + { + if (isWhite(cx)) + continue; + break; + } + else + { + size_t stride = 0; + + while (1) + { + ++stride; + if (!i || (cx & 0xC0) == 0xC0 || stride == 4) + break; + cx = str[i - 1]; + if (!(cx & 0x80)) + break; + --i; + } + + if (!str[i .. i + stride].byDchar.front.isWhite) + return str[0 .. i + stride]; + } + } + else + static assert(0); + } - return str[0 .. 0]; + return str[0 .. i + 1]; + } } /// -@safe pure unittest +@safe pure +unittest { import std.uni : lineSep, paraSep; assert(stripRight(" hello world ") == @@ -2442,15 +3057,60 @@ C[] stripRight(C)(C[] str) @safe pure @nogc [paraSep] ~ "hello world"); } +auto stripRight(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + return stripRight!(StringTypeOf!Range)(str); +} + +@safe pure unittest +{ + assert(testAliasedString!stripRight("hello ")); +} + +@safe pure unittest +{ + import std.array : array; + import std.uni : lineSep, paraSep; + import std.utf : byChar, byDchar, byUTF, byWchar, invalidUTFstrings; + assert(stripRight(" hello world ".byChar).array == " hello world"); + assert(stripRight("\n\t\v\rhello world\n\t\v\r"w.byWchar).array == "\n\t\v\rhello world"w); + assert(stripRight("hello world"d.byDchar).array == "hello world"d); + assert(stripRight("\u2028hello world\u2020\u2028".byChar).array == "\u2028hello world\u2020"); + assert(stripRight("hello world\U00010001"w.byWchar).array == "hello world\U00010001"w); + + foreach (C; AliasSeq!(char, wchar, dchar)) + { + foreach (s; invalidUTFstrings!C()) + { + cast(void) stripRight(s.byUTF!C).array; + } + } + + cast(void) stripRight("a\x80".byUTF!char).array; + wstring ws = ['a', cast(wchar) 0xDC00]; + cast(void) stripRight(ws.byUTF!wchar).array; +} + /++ Strips both leading and trailing whitespace (as defined by - $(XREF uni, isWhite)). + $(REF isWhite, std,uni)). + + Params: + str = string or random access range of characters + + Returns: + slice of $(D str) stripped of leading and trailing whitespace. - Returns: $(D str) stripped of trailing whitespace. + See_Also: + Generic stripping on ranges: $(REF _strip, std, algorithm, mutation) +/ -C[] strip(C)(C[] str) @safe pure - if (isSomeChar!C) +auto strip(Range)(Range str) +if (isSomeString!Range || + isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && + !isConvertibleToString!Range && + isSomeChar!(ElementEncodingType!Range)) { return stripRight(stripLeft(str)); } @@ -2471,19 +3131,30 @@ C[] strip(C)(C[] str) @safe pure "hello world"); } +auto strip(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + return strip!(StringTypeOf!Range)(str); +} + +@safe pure unittest +{ + assert(testAliasedString!strip(" hello world ")); +} + @safe pure unittest { + import std.algorithm.comparison : equal; import std.conv : to; - import std.algorithm : equal; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.strip.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!( char[], const char[], string, - wchar[], const wchar[], wstring, - dchar[], const dchar[], dstring)) + foreach (S; AliasSeq!( char[], const char[], string, + wchar[], const wchar[], wstring, + dchar[], const dchar[], dstring)) { assert(equal(stripLeft(to!S(" foo\t ")), "foo\t ")); assert(equal(stripLeft(to!S("\u2008 foo\t \u2007")), "foo\t \u2007")); @@ -2510,8 +3181,8 @@ C[] strip(C)(C[] str) @safe pure @safe pure unittest { - import std.exception; - import std.range; + import std.array : sameHead, sameTail; + import std.exception : assertCTFEable; assertCTFEable!( { wstring s = " "; @@ -2527,7 +3198,7 @@ C[] strip(C)(C[] str) @safe pure $(D delimiter), then it is returned unchanged. If no $(D delimiter) is given, then one trailing $(D '\r'), $(D '\n'), - $(D "\r\n"), $(D '\f'), $(D '\v'), $(XREF uni, lineSep), $(XREF uni, paraSep), or $(XREF uni, nelSep) + $(D "\r\n"), $(D '\f'), $(D '\v'), $(REF lineSep, std,uni), $(REF paraSep, std,uni), or $(REF nelSep, std,uni) is removed from the end of $(D str). If $(D str) does not end with any of those characters, then it is returned unchanged. @@ -2539,8 +3210,9 @@ C[] strip(C)(C[] str) @safe pure slice of str +/ Range chomp(Range)(Range str) - if (isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) || - isSomeString!Range) +if ((isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) || + isNarrowString!Range) && + !isConvertibleToString!Range) { import std.uni : lineSep, paraSep, nelSep; if (str.empty) @@ -2594,25 +3266,26 @@ Range chomp(Range)(Range str) /// Ditto Range chomp(Range, C2)(Range str, const(C2)[] delimiter) - if ((isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) || - isSomeString!Range) && - isSomeChar!C2) +if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) || + isNarrowString!Range) && + !isConvertibleToString!Range && + isSomeChar!C2) { if (delimiter.empty) return chomp(str); alias C1 = ElementEncodingType!Range; - static if (is(Unqual!C1 == Unqual!C2)) + static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4))) { - import std.algorithm : endsWith; + import std.algorithm.searching : endsWith; if (str.endsWith(delimiter)) return str[0 .. $ - delimiter.length]; return str; } else { - auto orig = str; + auto orig = str.save; static if (isSomeString!Range) alias C = dchar; // because strings auto-decode @@ -2632,10 +3305,11 @@ Range chomp(Range, C2)(Range str, const(C2)[] delimiter) } /// -@safe pure unittest +@safe pure +unittest { - import std.utf : decode; import std.uni : lineSep, paraSep, nelSep; + import std.utf : decode; assert(chomp(" hello world \n\r") == " hello world \n"); assert(chomp(" hello world \r\n") == " hello world "); assert(chomp(" hello world \f") == " hello world "); @@ -2656,20 +3330,38 @@ Range chomp(Range, C2)(Range str, const(C2)[] delimiter) assert(chomp("hello\xFE", "\r") == "hello\xFE"); } -unittest +StringTypeOf!Range chomp(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + return chomp!(StringTypeOf!Range)(str); +} + +StringTypeOf!Range chomp(Range, C2)(auto ref Range str, const(C2)[] delimiter) +if (isConvertibleToString!Range) +{ + return chomp!(StringTypeOf!Range, C2)(str, delimiter); +} + +@safe pure unittest +{ + assert(testAliasedString!chomp(" hello world \n\r")); + assert(testAliasedString!chomp(" hello world", "orld")); +} + +@safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.chomp.unittest\n"); string s; - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { // @@@ BUG IN COMPILER, MUST INSERT CAST - assert(chomp(cast(S)null) is null); + assert(chomp(cast(S) null) is null); assert(chomp(to!S("hello")) == "hello"); assert(chomp(to!S("hello\n")) == "hello"); assert(chomp(to!S("hello\r")) == "hello"); @@ -2686,11 +3378,11 @@ unittest assert(chomp(to!S("hello\u2029\u2129")) == "hello\u2029\u2129"); assert(chomp(to!S("hello\u2029\u0185")) == "hello\u2029\u0185"); - foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 // @@@ BUG IN COMPILER, MUST INSERT CAST - assert(chomp(cast(S)null, cast(T)null) is null); - assert(chomp(to!S("hello\n"), cast(T)null) == "hello"); + assert(chomp(cast(S) null, cast(T) null) is null); + assert(chomp(to!S("hello\n"), cast(T) null) == "hello"); assert(chomp(to!S("hello"), to!T("o")) == "hell"); assert(chomp(to!S("hello"), to!T("p")) == "hello"); // @@@ BUG IN COMPILER, MUST INSERT CAST @@ -2703,45 +3395,66 @@ unittest }); // Ranges + import std.array : array; import std.utf : byChar, byWchar, byDchar; - import std.array; assert(chomp("hello world\r\n" .byChar ).array == "hello world"); assert(chomp("hello world\r\n"w.byWchar).array == "hello world"w); assert(chomp("hello world\r\n"d.byDchar).array == "hello world"d); assert(chomp("hello world"d.byDchar, "ld").array == "hello wor"d); + + assert(chomp("hello\u2020" .byChar , "\u2020").array == "hello"); + assert(chomp("hello\u2020"d.byDchar, "\u2020"d).array == "hello"d); } /++ If $(D str) starts with $(D delimiter), then the part of $(D str) following - $(D delimiter) is returned. If it $(D str) does $(I not) start with + $(D delimiter) is returned. If $(D str) does $(I not) start with + $(D delimiter), then it is returned unchanged. + + Params: + str = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + of characters + delimiter = string of characters to be sliced off front of str[] + + Returns: + slice of str +/ -C1[] chompPrefix(C1, C2)(C1[] str, C2[] delimiter) @safe pure - if (isSomeChar!C1 && isSomeChar!C2) +Range chompPrefix(Range, C2)(Range str, const(C2)[] delimiter) +if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) || + isNarrowString!Range) && + !isConvertibleToString!Range && + isSomeChar!C2) { - static if (is(Unqual!C1 == Unqual!C2)) + alias C1 = ElementEncodingType!Range; + + static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4))) { - import std.algorithm : startsWith; + import std.algorithm.searching : startsWith; if (str.startsWith(delimiter)) return str[delimiter.length .. $]; return str; } else { - import std.utf : decode; + auto orig = str.save; - auto orig = str; - size_t index = 0; + static if (isSomeString!Range) + alias C = dchar; // because strings auto-decode + else + alias C = C1; // and ranges do not - foreach (dchar c; delimiter) + foreach (C c; delimiter) { - if (index >= str.length || decode(str, index) != c) + if (str.empty || str.front != c) return orig; + + str.popFront(); } - return str[index .. $]; + return str; } } @@ -2754,16 +3467,23 @@ C1[] chompPrefix(C1, C2)(C1[] str, C2[] delimiter) @safe pure assert(chompPrefix("", "hello") == ""); } -/* @safe */ pure unittest +StringTypeOf!Range chompPrefix(Range, C2)(auto ref Range str, const(C2)[] delimiter) +if (isConvertibleToString!Range) +{ + return chompPrefix!(StringTypeOf!Range, C2)(str, delimiter); +} + +@safe pure +unittest { + import std.algorithm.comparison : equal; import std.conv : to; - import std.algorithm : equal; - import std.exception; + import std.exception : assertCTFEable; assertCTFEable!( { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { - foreach (T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 assert(equal(chompPrefix(to!S("abcdefgh"), to!T("abcde")), "fgh")); assert(equal(chompPrefix(to!S("abcde"), to!T("abcdefgh")), "abcde")); @@ -2773,26 +3493,93 @@ C1[] chompPrefix(C1, C2)(C1[] str, C2[] delimiter) @safe pure }(); } }); + + // Ranges + import std.array : array; + import std.utf : byChar, byWchar, byDchar; + assert(chompPrefix("hello world" .byChar , "hello"d).array == " world"); + assert(chompPrefix("hello world"w.byWchar, "hello" ).array == " world"w); + assert(chompPrefix("hello world"d.byDchar, "hello"w).array == " world"d); + assert(chompPrefix("hello world"c.byDchar, "hello"w).array == " world"d); + + assert(chompPrefix("hello world"d.byDchar, "lx").array == "hello world"d); + assert(chompPrefix("hello world"d.byDchar, "hello world xx").array == "hello world"d); + + assert(chompPrefix("\u2020world" .byChar , "\u2020").array == "world"); + assert(chompPrefix("\u2020world"d.byDchar, "\u2020"d).array == "world"d); } +@safe pure unittest +{ + assert(testAliasedString!chompPrefix("hello world", "hello")); +} /++ Returns $(D str) without its last character, if there is one. If $(D str) ends with $(D "\r\n"), then both are removed. If $(D str) is empty, then then it is returned unchanged. + + Params: + str = string (must be valid UTF) + Returns: + slice of str +/ -S chop(S)(S str) @safe pure - if (isSomeString!S) + +Range chop(Range)(Range str) +if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) || + isNarrowString!Range) && + !isConvertibleToString!Range) { if (str.empty) return str; - if (str.length >= 2 && str[$ - 1] == '\n' && str[$ - 2] == '\r') - return str[0 .. $ - 2]; - - str.popBack(); - - return str; + static if (isSomeString!Range) + { + if (str.length >= 2 && str[$ - 1] == '\n' && str[$ - 2] == '\r') + return str[0 .. $ - 2]; + str.popBack(); + return str; + } + else + { + alias C = Unqual!(ElementEncodingType!Range); + C c = str.back; + str.popBack(); + if (c == '\n') + { + if (!str.empty && str.back == '\r') + str.popBack(); + return str; + } + // Pop back a dchar, not just a code unit + static if (C.sizeof == 1) + { + int cnt = 1; + while ((c & 0xC0) == 0x80) + { + if (str.empty) + break; + c = str.back; + str.popBack(); + if (++cnt > 4) + break; + } + } + else static if (C.sizeof == 2) + { + if (c >= 0xD800 && c <= 0xDBFF) + { + if (!str.empty) + str.popBack(); + } + } + else static if (C.sizeof == 4) + { + } + else + static assert(0); + return str; + } } /// @@ -2807,17 +3594,62 @@ S chop(S)(S str) @safe pure assert(chop("") == ""); } -unittest +StringTypeOf!Range chop(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + return chop!(StringTypeOf!Range)(str); +} + +@safe pure unittest +{ + assert(testAliasedString!chop("hello world")); +} + +@safe pure unittest +{ + import std.array : array; + import std.utf : byChar, byWchar, byDchar, byCodeUnit, invalidUTFstrings; + + assert(chop("hello world".byChar).array == "hello worl"); + assert(chop("hello world\n"w.byWchar).array == "hello world"w); + assert(chop("hello world\r"d.byDchar).array == "hello world"d); + assert(chop("hello world\n\r".byChar).array == "hello world\n"); + assert(chop("hello world\r\n"w.byWchar).array == "hello world"w); + assert(chop("Walter Bright"d.byDchar).array == "Walter Brigh"d); + assert(chop("".byChar).array == ""); + + assert(chop(`ミツãƒãƒã¨ç§‘学者` .byCodeUnit).array == "ミツãƒãƒã¨ç§‘å­¦"); + assert(chop(`ミツãƒãƒã¨ç§‘学者`w.byCodeUnit).array == "ミツãƒãƒã¨ç§‘å­¦"w); + assert(chop(`ミツãƒãƒã¨ç§‘学者`d.byCodeUnit).array == "ミツãƒãƒã¨ç§‘å­¦"d); + + auto ca = invalidUTFstrings!char(); + foreach (s; ca) + { + foreach (c; chop(s.byCodeUnit)) + { + } + } + + auto wa = invalidUTFstrings!wchar(); + foreach (s; wa) + { + foreach (c; chop(s.byCodeUnit)) + { + } + } +} + +@safe pure unittest { + import std.algorithm.comparison : equal; import std.conv : to; - import std.algorithm : equal; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.chop.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { assert(chop(cast(S) null) is null); assert(equal(chop(to!S("hello")), "hell")); @@ -2835,129 +3667,376 @@ unittest Left justify $(D s) in a field $(D width) characters wide. $(D fillChar) is the character that will be used to fill up the space in the field that $(D s) doesn't fill. - +/ -S leftJustify(S)(S s, size_t width, dchar fillChar = ' ') @trusted pure - if (isSomeString!S) -{ - import std.utf : canSearchInCodeUnits; - import std.conv : to; - alias C = ElementEncodingType!S; - - if (canSearchInCodeUnits!C(fillChar)) - { - immutable len = s.walkLength(); - if (len >= width) - return s; + Params: + s = string + width = minimum field width + fillChar = used to pad end up to $(D width) characters - auto retval = new Unqual!(C)[width - len + s.length]; - retval[0 .. s.length] = s[]; - retval[s.length .. $] = cast(C)fillChar; - return cast(S)retval; - } - else - { - auto dstr = to!dstring(s); - if (dstr.length >= width) - return s; + Returns: + GC allocated string - auto retval = new dchar[](width); - retval[0 .. dstr.length] = dstr[]; - retval[dstr.length .. $] = fillChar; - return to!S(retval); - } + See_Also: + $(LREF leftJustifier), which does not allocate + +/ +S leftJustify(S)(S s, size_t width, dchar fillChar = ' ') +if (isSomeString!S) +{ + import std.array : array; + return leftJustifier(s, width, fillChar).array; } +/// +@safe pure unittest +{ + assert(leftJustify("hello", 7, 'X') == "helloXX"); + assert(leftJustify("hello", 2, 'X') == "hello"); + assert(leftJustify("hello", 9, 'X') == "helloXXXX"); +} /++ - Right justify $(D s) in a field $(D width) characters wide. $(D fillChar) + Left justify $(D s) in a field $(D width) characters wide. $(D fillChar) is the character that will be used to fill up the space in the field that $(D s) doesn't fill. - +/ -S rightJustify(S)(S s, size_t width, dchar fillChar = ' ') @trusted pure - if (isSomeString!S) -{ - import std.utf : canSearchInCodeUnits; - import std.conv : to; - alias C = ElementEncodingType!S; + Params: + r = string or range of characters + width = minimum field width + fillChar = used to pad end up to $(D width) characters - if (canSearchInCodeUnits!C(fillChar)) - { - immutable len = s.walkLength(); - if (len >= width) - return s; + Returns: + a lazy range of the left justified result + + See_Also: + $(LREF rightJustifier) + +/ + +auto leftJustifier(Range)(Range r, size_t width, dchar fillChar = ' ') +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) +{ + alias C = Unqual!(ElementEncodingType!Range); - auto retval = new Unqual!C[width - len + s.length]; - retval[0 .. $ - s.length] = cast(C)fillChar; - retval[$ - s.length .. $] = s[]; - return cast(S)retval; + static if (C.sizeof == 1) + { + import std.utf : byDchar, byChar; + return leftJustifier(r.byDchar, width, fillChar).byChar; } - else + else static if (C.sizeof == 2) + { + import std.utf : byDchar, byWchar; + return leftJustifier(r.byDchar, width, fillChar).byWchar; + } + else static if (C.sizeof == 4) { - auto dstr = to!dstring(s); - if (dstr.length >= width) - return s; + static struct Result + { + private: + Range _input; + size_t _width; + dchar _fillChar; + size_t len; + + public: + + @property bool empty() + { + return len >= _width && _input.empty; + } + + @property C front() + { + return _input.empty ? _fillChar : _input.front; + } + + void popFront() + { + ++len; + if (!_input.empty) + _input.popFront(); + } + + static if (isForwardRange!Range) + { + @property typeof(this) save() return scope + { + auto ret = this; + ret._input = _input.save; + return ret; + } + } + } - auto retval = new dchar[](width); - retval[0 .. $ - dstr.length] = fillChar; - retval[$ - dstr.length .. $] = dstr[]; - return to!S(retval); + return Result(r, width, fillChar); } + else + static assert(0); +} + +/// +@safe pure @nogc nothrow +unittest +{ + import std.algorithm.comparison : equal; + import std.utf : byChar; + assert(leftJustifier("hello", 2).equal("hello".byChar)); + assert(leftJustifier("hello", 7).equal("hello ".byChar)); + assert(leftJustifier("hello", 7, 'x').equal("helloxx".byChar)); +} + +auto leftJustifier(Range)(auto ref Range r, size_t width, dchar fillChar = ' ') +if (isConvertibleToString!Range) +{ + return leftJustifier!(StringTypeOf!Range)(r, width, fillChar); +} + +@safe pure unittest +{ + auto r = "hello".leftJustifier(8); + r.popFront(); + auto save = r.save; + r.popFront(); + assert(r.front == 'l'); + assert(save.front == 'e'); } +@safe pure unittest +{ + assert(testAliasedString!leftJustifier("hello", 2)); +} /++ - Center $(D s) in a field $(D width) characters wide. $(D fillChar) + Right justify $(D s) in a field $(D width) characters wide. $(D fillChar) is the character that will be used to fill up the space in the field that $(D s) doesn't fill. + + Params: + s = string + width = minimum field width + fillChar = used to pad end up to $(D width) characters + + Returns: + GC allocated string + + See_Also: + $(LREF rightJustifier), which does not allocate +/ -S center(S)(S s, size_t width, dchar fillChar = ' ') @trusted pure - if (isSomeString!S) +S rightJustify(S)(S s, size_t width, dchar fillChar = ' ') +if (isSomeString!S) { - import std.utf : canSearchInCodeUnits; - import std.conv : to; + import std.array : array; + return rightJustifier(s, width, fillChar).array; +} + +/// +@safe pure unittest +{ + assert(rightJustify("hello", 7, 'X') == "XXhello"); + assert(rightJustify("hello", 2, 'X') == "hello"); + assert(rightJustify("hello", 9, 'X') == "XXXXhello"); +} - alias C = ElementEncodingType!S; +/++ + Right justify $(D s) in a field $(D width) characters wide. $(D fillChar) + is the character that will be used to fill up the space in the field that + $(D s) doesn't fill. - if (canSearchInCodeUnits!C(fillChar)) - { - immutable len = s.walkLength(); - if (len >= width) - return s; + Params: + r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + of characters + width = minimum field width + fillChar = used to pad end up to $(D width) characters + + Returns: + a lazy range of the right justified result + + See_Also: + $(LREF leftJustifier) + +/ + +auto rightJustifier(Range)(Range r, size_t width, dchar fillChar = ' ') +if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) +{ + alias C = Unqual!(ElementEncodingType!Range); - auto retval = new Unqual!C[width - len + s.length]; - immutable left = (retval.length - s.length) / 2; - retval[0 .. left] = cast(C)fillChar; - retval[left .. left + s.length] = s[]; - retval[left + s.length .. $] = cast(C)fillChar; - return to!S(retval); + static if (C.sizeof == 1) + { + import std.utf : byDchar, byChar; + return rightJustifier(r.byDchar, width, fillChar).byChar; } - else + else static if (C.sizeof == 2) { - auto dstr = to!dstring(s); - if (dstr.length >= width) - return s; + import std.utf : byDchar, byWchar; + return rightJustifier(r.byDchar, width, fillChar).byWchar; + } + else static if (C.sizeof == 4) + { + static struct Result + { + private: + Range _input; + size_t _width; + alias nfill = _width; // number of fill characters to prepend + dchar _fillChar; + bool inited; + + // Lazy initialization so constructor is trivial and cannot fail + void initialize() + { + // Replace _width with nfill + // (use alias instead of union because CTFE cannot deal with unions) + assert(_width); + static if (hasLength!Range) + { + immutable len = _input.length; + nfill = (_width > len) ? _width - len : 0; + } + else + { + // Lookahead to see now many fill characters are needed + import std.range : take; + import std.range.primitives : walkLength; + nfill = _width - walkLength(_input.save.take(_width), _width); + } + inited = true; + } + + public: + this(Range input, size_t width, dchar fillChar) pure nothrow + { + _input = input; + _fillChar = fillChar; + _width = width; + } - auto retval = new dchar[](width); - immutable left = (retval.length - dstr.length) / 2; - retval[0 .. left] = fillChar; - retval[left .. left + dstr.length] = dstr[]; - retval[left + dstr.length .. $] = fillChar; - return to!S(retval); + @property bool empty() + { + return !nfill && _input.empty; + } + + @property C front() + { + if (!nfill) + return _input.front; // fast path + if (!inited) + initialize(); + return nfill ? _fillChar : _input.front; + } + + void popFront() + { + if (!nfill) + _input.popFront(); // fast path + else + { + if (!inited) + initialize(); + if (nfill) + --nfill; + else + _input.popFront(); + } + } + + @property typeof(this) save() + { + auto ret = this; + ret._input = _input.save; + return ret; + } + } + + return Result(r, width, fillChar); } + else + static assert(0); +} + +/// +@safe pure @nogc nothrow +unittest +{ + import std.algorithm.comparison : equal; + import std.utf : byChar; + assert(rightJustifier("hello", 2).equal("hello".byChar)); + assert(rightJustifier("hello", 7).equal(" hello".byChar)); + assert(rightJustifier("hello", 7, 'x').equal("xxhello".byChar)); +} + +auto rightJustifier(Range)(auto ref Range r, size_t width, dchar fillChar = ' ') +if (isConvertibleToString!Range) +{ + return rightJustifier!(StringTypeOf!Range)(r, width, fillChar); +} + +@safe pure unittest +{ + assert(testAliasedString!rightJustifier("hello", 2)); } -@trusted pure unittest +@safe pure unittest +{ + auto r = "hello"d.rightJustifier(6); + r.popFront(); + auto save = r.save; + r.popFront(); + assert(r.front == 'e'); + assert(save.front == 'h'); + + auto t = "hello".rightJustifier(7); + t.popFront(); + assert(t.front == ' '); + t.popFront(); + assert(t.front == 'h'); + + auto u = "hello"d.rightJustifier(5); + u.popFront(); + u.popFront(); + u.popFront(); +} + +/++ + Center $(D s) in a field $(D width) characters wide. $(D fillChar) + is the character that will be used to fill up the space in the field that + $(D s) doesn't fill. + + Params: + s = The string to center + width = Width of the field to center `s` in + fillChar = The character to use for filling excess space in the field + + Returns: + The resulting _center-justified string. The returned string is + GC-allocated. To avoid GC allocation, use $(LREF centerJustifier) + instead. + +/ +S center(S)(S s, size_t width, dchar fillChar = ' ') +if (isSomeString!S) +{ + import std.array : array; + return centerJustifier(s, width, fillChar).array; +} + +/// +@safe pure unittest +{ + assert(center("hello", 7, 'X') == "XhelloX"); + assert(center("hello", 2, 'X') == "hello"); + assert(center("hello", 9, 'X') == "XXhelloXX"); +} + +@safe pure +unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.justify.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { S s = to!S("hello"); @@ -2984,86 +4063,325 @@ S center(S)(S s, size_t width, dchar fillChar = ' ') @trusted pure }); } +/++ + Center justify $(D r) in a field $(D width) characters wide. $(D fillChar) + is the character that will be used to fill up the space in the field that + $(D r) doesn't fill. + + Params: + r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + of characters + width = minimum field width + fillChar = used to pad end up to $(D width) characters + + Returns: + a lazy range of the center justified result + + See_Also: + $(LREF leftJustifier) + $(LREF rightJustifier) + +/ + +auto centerJustifier(Range)(Range r, size_t width, dchar fillChar = ' ') +if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) +{ + alias C = Unqual!(ElementEncodingType!Range); + + static if (C.sizeof == 1) + { + import std.utf : byDchar, byChar; + return centerJustifier(r.byDchar, width, fillChar).byChar; + } + else static if (C.sizeof == 2) + { + import std.utf : byDchar, byWchar; + return centerJustifier(r.byDchar, width, fillChar).byWchar; + } + else static if (C.sizeof == 4) + { + import std.range : chain, repeat; + import std.range.primitives : walkLength; + + auto len = walkLength(r.save, width); + if (len > width) + len = width; + const nleft = (width - len) / 2; + const nright = width - len - nleft; + return chain(repeat(fillChar, nleft), r, repeat(fillChar, nright)); + } + else + static assert(0); +} + +/// +@safe pure @nogc nothrow +unittest +{ + import std.algorithm.comparison : equal; + import std.utf : byChar; + assert(centerJustifier("hello", 2).equal("hello".byChar)); + assert(centerJustifier("hello", 8).equal(" hello ".byChar)); + assert(centerJustifier("hello", 7, 'x').equal("xhellox".byChar)); +} + +auto centerJustifier(Range)(auto ref Range r, size_t width, dchar fillChar = ' ') +if (isConvertibleToString!Range) +{ + return centerJustifier!(StringTypeOf!Range)(r, width, fillChar); +} + +@safe pure unittest +{ + assert(testAliasedString!centerJustifier("hello", 8)); +} + +@system unittest +{ + static auto byFwdRange(dstring s) + { + static struct FRange + { + dstring str; + this(dstring s) { str = s; } + @property bool empty() { return str.length == 0; } + @property dchar front() { return str[0]; } + void popFront() { str = str[1 .. $]; } + @property FRange save() { return this; } + } + return FRange(s); + } + + auto r = centerJustifier(byFwdRange("hello"d), 6); + r.popFront(); + auto save = r.save; + r.popFront(); + assert(r.front == 'l'); + assert(save.front == 'e'); + + auto t = "hello".centerJustifier(7); + t.popFront(); + assert(t.front == 'h'); + t.popFront(); + assert(t.front == 'e'); + + auto u = byFwdRange("hello"d).centerJustifier(6); + u.popFront(); + u.popFront(); + u.popFront(); + u.popFront(); + u.popFront(); + u.popFront(); +} + /++ Replace each tab character in $(D s) with the number of spaces necessary - to align the following character at the next tab stop where $(D tabSize) - is the distance between tab stops. + to align the following character at the next tab stop. + + Params: + s = string + tabSize = distance between tab stops + + Returns: + GC allocated string with tabs replaced with spaces +/ -S detab(S)(S s, size_t tabSize = 8) @trusted pure - if (isSomeString!S) +auto detab(Range)(auto ref Range s, size_t tabSize = 8) pure +if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) + || __traits(compiles, StringTypeOf!Range)) { - import std.utf : encode; - import std.uni : lineSep, paraSep; + import std.array : array; + return detabber(s, tabSize).array; +} + +/// +@system pure unittest +{ + assert(detab(" \n\tx", 9) == " \n x"); +} + +@safe pure unittest +{ + static struct TestStruct + { + string s; + alias s this; + } + + static struct TestStruct2 + { + string s; + alias s this; + @disable this(this); + } + + string s = " \n\tx"; + string cmp = " \n x"; + auto t = TestStruct(s); + assert(detab(t, 9) == cmp); + assert(detab(TestStruct(s), 9) == cmp); + assert(detab(TestStruct(s), 9) == detab(TestStruct(s), 9)); + assert(detab(TestStruct2(s), 9) == detab(TestStruct2(s), 9)); + assert(detab(TestStruct2(s), 9) == cmp); +} + +/++ + Replace each tab character in $(D r) with the number of spaces + necessary to align the following character at the next tab stop. + + Params: + r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + tabSize = distance between tab stops + + Returns: + lazy forward range with tabs replaced with spaces + +/ +auto detabber(Range)(Range r, size_t tabSize = 8) +if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) +{ + import std.uni : lineSep, paraSep, nelSep; + import std.utf : codeUnitLimit, decodeFront; assert(tabSize > 0); - alias C = Unqual!(typeof(s[0])); - bool changes = false; - C[] result; - int column; - size_t nspaces; - foreach (size_t i, dchar c; s) + alias C = Unqual!(ElementEncodingType!(Range)); + + static struct Result { - switch (c) + private: + Range _input; + size_t _tabSize; + size_t nspaces; + int column; + size_t index; + + public: + + this(Range input, size_t tabSize) + { + _input = input; + _tabSize = tabSize; + } + + static if (isInfinite!(Range)) { - case '\t': - nspaces = tabSize - (column % tabSize); - if (!changes) + enum bool empty = false; + } + else + { + @property bool empty() { - changes = true; - result = null; - result.length = s.length + nspaces - 1; - result.length = i + nspaces; - result[0 .. i] = s[0 .. i]; - result[i .. i + nspaces] = ' '; + return _input.empty && nspaces == 0; } + } + + @property C front() + { + if (nspaces) + return ' '; + static if (isSomeString!(Range)) + C c = _input[0]; else + C c = _input.front; + if (index) + return c; + dchar dc; + if (c < codeUnitLimit!(immutable(C)[])) { - ptrdiff_t j = result.length; - result.length = j + nspaces; - result[j .. j + nspaces] = ' '; + dc = c; + index = 1; } - column += nspaces; - break; + else + { + auto r = _input.save; + dc = decodeFront(r, index); // lookahead to decode + } + switch (dc) + { + case '\r': + case '\n': + case paraSep: + case lineSep: + case nelSep: + column = 0; + break; - case '\r': - case '\n': - case paraSep: - case lineSep: - column = 0; - goto L1; + case '\t': + nspaces = _tabSize - (column % _tabSize); + column += nspaces; + c = ' '; + break; - default: - column++; - L1: - if (changes) + default: + ++column; + break; + } + return c; + } + + void popFront() + { + if (!index) + front; + if (nspaces) + --nspaces; + if (!nspaces) { - std.utf.encode(result, c); + static if (isSomeString!(Range)) + _input = _input[1 .. $]; + else + _input.popFront(); + --index; } - break; + } + + @property typeof(this) save() + { + auto ret = this; + ret._input = _input.save; + return ret; } } - return changes ? cast(S) result : s; + return Result(r, tabSize); +} + +/// +@system pure unittest +{ + import std.array : array; + + assert(detabber(" \n\tx", 9).array == " \n x"); +} + +auto detabber(Range)(auto ref Range r, size_t tabSize = 8) +if (isConvertibleToString!Range) +{ + return detabber!(StringTypeOf!Range)(r, tabSize); +} + +@safe pure unittest +{ + assert(testAliasedString!detabber( " ab\t asdf ", 8)); } -@trusted pure unittest +@system pure unittest { + import std.algorithm.comparison : cmp; import std.conv : to; - import std.algorithm : cmp; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.detab.unittest\n"); - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { S s = to!S("This \tis\t a fofof\tof list"); assert(cmp(detab(s), "This is a fofof of list") == 0); - assert(detab(cast(S)null) is null); + assert(detab(cast(S) null) is null); assert(detab("").empty); assert(detab("a") == "a"); assert(detab("\t") == " "); @@ -3071,10 +4389,31 @@ S detab(S)(S s, size_t tabSize = 8) @trusted pure assert(detab("\t", 9) == " "); assert(detab( " ab\t asdf ") == " ab asdf "); assert(detab( " \U00010000b\tasdf ") == " \U00010000b asdf "); + assert(detab("\r\t", 9) == "\r "); + assert(detab("\n\t", 9) == "\n "); + assert(detab("\u0085\t", 9) == "\u0085 "); + assert(detab("\u2028\t", 9) == "\u2028 "); + assert(detab(" \u2029\t", 9) == " \u2029 "); } }); } +/// +@system pure unittest +{ + import std.array : array; + import std.utf : byChar, byWchar; + + assert(detabber(" \u2029\t".byChar, 9).array == " \u2029 "); + auto r = "hel\tx".byWchar.detabber(); + assert(r.front == 'h'); + auto s = r.save; + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + assert(s.front == 'h'); +} + /++ Replaces spaces in $(D s) with the optimal number of tabs. All spaces and tabs at the end of a line are removed. @@ -3082,114 +4421,298 @@ S detab(S)(S s, size_t tabSize = 8) @trusted pure Params: s = String to convert. tabSize = Tab columns are $(D tabSize) spaces apart. + + Returns: + GC allocated string with spaces replaced with tabs; + use $(LREF entabber) to not allocate. + + See_Also: + $(LREF entabber) +/ -S entab(S)(S s, size_t tabSize = 8) @trusted pure - if (isSomeString!S) +auto entab(Range)(Range s, size_t tabSize = 8) +if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) { - import std.utf : encode; - import std.uni : lineSep, paraSep; - import std.exception : assumeUnique; + import std.array : array; + return entabber(s, tabSize).array; +} - bool changes = false; - alias C = Unqual!(typeof(s[0])); - C[] result; +/// +@safe pure unittest +{ + assert(entab(" x \n") == "\tx\n"); +} - int nspaces = 0; - int nwhite = 0; - size_t column = 0; // column number +auto entab(Range)(auto ref Range s, size_t tabSize = 8) +if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) && + is(StringTypeOf!Range)) +{ + return entab!(StringTypeOf!Range)(s, tabSize); +} - foreach (size_t i, dchar c; s) +@safe pure unittest +{ + assert(testAliasedString!entab(" x \n")); +} + +/++ + Replaces spaces in range $(D r) with the optimal number of tabs. + All spaces and tabs at the end of a line are removed. + + Params: + r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + tabSize = distance between tab stops + + Returns: + lazy forward range with spaces replaced with tabs + + See_Also: + $(LREF entab) + +/ +auto entabber(Range)(Range r, size_t tabSize = 8) +if (isForwardRange!Range && !isConvertibleToString!Range) +{ + import std.uni : lineSep, paraSep, nelSep; + import std.utf : codeUnitLimit, decodeFront; + + assert(tabSize > 0); + alias C = Unqual!(ElementEncodingType!Range); + + static struct Result { + private: + Range _input; + size_t _tabSize; + size_t nspaces; + size_t ntabs; + int column; + size_t index; - void change() + @property C getFront() { - changes = true; - result = null; - result.length = s.length; - result.length = i; - result[0 .. i] = s[0 .. i]; + static if (isSomeString!Range) + return _input[0]; // avoid autodecode + else + return _input.front; } - switch (c) + public: + + this(Range input, size_t tabSize) { - case '\t': - nwhite++; - if (nspaces) + _input = input; + _tabSize = tabSize; + } + + @property bool empty() + { + if (ntabs || nspaces) + return false; + + /* Since trailing spaces are removed, + * look ahead for anything that is not a trailing space + */ + static if (isSomeString!Range) { - if (!changes) - change(); - - ptrdiff_t j = result.length - nspaces; - auto ntabs = (((column - nspaces) % tabSize) + nspaces) / tabSize; - result.length = j + ntabs; - result[j .. j + ntabs] = '\t'; - nwhite += ntabs - nspaces; - nspaces = 0; + foreach (c; _input) + { + if (c != ' ' && c != '\t') + return false; + } + return true; } - column = (column + tabSize) / tabSize * tabSize; - break; - - case '\r': - case '\n': - case paraSep: - case lineSep: - // Truncate any trailing spaces or tabs - if (nwhite) + else { - if (!changes) - change(); - result = result[0 .. result.length - nwhite]; + if (_input.empty) + return true; + immutable c = _input.front; + if (c != ' ' && c != '\t') + return false; + auto t = _input.save; + t.popFront(); + foreach (c2; t) + { + if (c2 != ' ' && c2 != '\t') + return false; + } + return true; } - break; + } - default: - if (nspaces >= 2 && (column % tabSize) == 0) + @property C front() + { + //writefln(" front(): ntabs = %s nspaces = %s index = %s front = '%s'", ntabs, nspaces, index, getFront); + if (ntabs) + return '\t'; + if (nspaces) + return ' '; + C c = getFront; + if (index) + return c; + dchar dc; + if (c < codeUnitLimit!(immutable(C)[])) { - if (!changes) - change(); - - auto j = result.length - nspaces; - auto ntabs = (nspaces + tabSize - 1) / tabSize; - result.length = j + ntabs; - result[j .. j + ntabs] = '\t'; - nwhite += ntabs - nspaces; - nspaces = 0; - } - if (c == ' ') - { nwhite++; - nspaces++; + index = 1; + dc = c; + if (c == ' ' || c == '\t') + { + // Consume input until a non-blank is encountered + immutable startcol = column; + C cx; + static if (isSomeString!Range) + { + while (1) + { + assert(_input.length); + cx = _input[0]; + if (cx == ' ') + ++column; + else if (cx == '\t') + column += _tabSize - (column % _tabSize); + else + break; + _input = _input[1 .. $]; + } + } + else + { + while (1) + { + assert(!_input.empty); + cx = _input.front; + if (cx == ' ') + ++column; + else if (cx == '\t') + column += _tabSize - (column % _tabSize); + else + break; + _input.popFront(); + } + } + // Compute ntabs+nspaces to get from startcol to column + immutable n = column - startcol; + if (n == 1) + { + nspaces = 1; + } + else + { + ntabs = column / _tabSize - startcol / _tabSize; + if (ntabs == 0) + nspaces = column - startcol; + else + nspaces = column % _tabSize; + } + //writefln("\tstartcol = %s, column = %s, _tabSize = %s", startcol, column, _tabSize); + //writefln("\tntabs = %s, nspaces = %s", ntabs, nspaces); + if (cx < codeUnitLimit!(immutable(C)[])) + { + dc = cx; + index = 1; + } + else + { + auto r = _input.save; + dc = decodeFront(r, index); // lookahead to decode + } + switch (dc) + { + case '\r': + case '\n': + case paraSep: + case lineSep: + case nelSep: + column = 0; + // Spaces followed by newline are ignored + ntabs = 0; + nspaces = 0; + return cx; + + default: + ++column; + break; + } + return ntabs ? '\t' : ' '; + } } else - { nwhite = 0; - nspaces = 0; + { + auto r = _input.save; + dc = decodeFront(r, index); // lookahead to decode } - column++; - break; + //writefln("dc = x%x", dc); + switch (dc) + { + case '\r': + case '\n': + case paraSep: + case lineSep: + case nelSep: + column = 0; + break; + + default: + ++column; + break; + } + return c; } - if (changes) + + void popFront() { - std.utf.encode(result, c); + //writefln("popFront(): ntabs = %s nspaces = %s index = %s front = '%s'", ntabs, nspaces, index, getFront); + if (!index) + front; + if (ntabs) + --ntabs; + else if (nspaces) + --nspaces; + else if (!ntabs && !nspaces) + { + static if (isSomeString!Range) + _input = _input[1 .. $]; + else + _input.popFront(); + --index; + } } - } - // Truncate any trailing spaces or tabs - if (nwhite) - { - if (changes) - result = result[0 .. result.length - nwhite]; - else - s = s[0 .. s.length - nwhite]; + @property typeof(this) save() + { + auto ret = this; + ret._input = _input.save; + return ret; + } } - return changes ? assumeUnique(result) : s; + + return Result(r, tabSize); } +/// @safe pure unittest +{ + import std.array : array; + assert(entabber(" x \n").array == "\tx\n"); +} + +auto entabber(Range)(auto ref Range r, size_t tabSize = 8) +if (isConvertibleToString!Range) +{ + return entabber!(StringTypeOf!Range)(r, tabSize); +} + +@safe pure unittest +{ + assert(testAliasedString!entabber(" ab asdf ", 8)); +} + +@safe pure +unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.entab.unittest\n"); - import std.exception; assertCTFEable!( { assert(entab(cast(string) null) is null); @@ -3218,13 +4741,35 @@ S entab(S)(S s, size_t tabSize = 8) @trusted pure assert(entab("a\r\n") == "a\r\n"); assert(entab("a\u2028") == "a\u2028"); assert(entab("a\u2029") == "a\u2029"); + assert(entab("a\u0085") == "a\u0085"); assert(entab("a ") == "a"); assert(entab("a\t") == "a"); assert(entab("\uFF28\uFF45\uFF4C\uFF4C567 \t\uFF4F \t") == "\uFF28\uFF45\uFF4C\uFF4C567\t\t\uFF4F"); + assert(entab(" \naa") == "\naa"); + assert(entab(" \r aa") == "\r aa"); + assert(entab(" \u2028 aa") == "\u2028 aa"); + assert(entab(" \u2029 aa") == "\u2029 aa"); + assert(entab(" \u0085 aa") == "\u0085 aa"); }); } +@safe pure +unittest +{ + import std.array : array; + import std.utf : byChar; + assert(entabber(" \u0085 aa".byChar).array == "\u0085 aa"); + assert(entabber(" \u2028\t aa \t".byChar).array == "\u2028\t aa"); + + auto r = entabber("1234", 4); + r.popFront(); + auto rsave = r.save; + r.popFront(); + assert(r.front == '3'); + assert(rsave.front == '2'); +} + /++ Replaces the characters in $(D str) which are keys in $(D transTable) with @@ -3236,7 +4781,7 @@ S entab(S)(S s, size_t tabSize = 8) @trusted pure See_Also: $(LREF tr) - $(XREF array, replace) + $(REF replace, std,array) Params: str = The original string. @@ -3247,7 +4792,7 @@ S entab(S)(S s, size_t tabSize = 8) @trusted pure C1[] translate(C1, C2 = immutable char)(C1[] str, in dchar[dchar] transTable, const(C2)[] toRemove = null) @safe pure - if (isSomeChar!C1 && isSomeChar!C2) +if (isSomeChar!C1 && isSomeChar!C2) { import std.array : appender; auto buffer = appender!(C1[])(); @@ -3278,16 +4823,16 @@ C1[] translate(C1, C2 = immutable char)(C1[] str, assert(translate("hello world", transTable2) == "h5llorange worangerld"); } -@trusted pure unittest +@system pure unittest { import std.conv : to; + import std.exception : assertCTFEable; - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) + foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[], + wchar[], const(wchar)[], immutable(wchar)[], + dchar[], const(dchar)[], immutable(dchar)[])) { assert(translate(to!S("hello world"), cast(dchar[dchar])['h' : 'q', 'l' : '5']) == to!S("qe55o wor5d")); @@ -3297,13 +4842,13 @@ C1[] translate(C1, C2 = immutable char)(C1[] str, to!S("qe55o \U00010143 wor5d")); assert(translate(to!S("hello \U00010143 world"), cast(dchar[dchar])['o' : '0', '\U00010143' : 'o']) == to!S("hell0 o w0rld")); - assert(translate(to!S("hello world"), cast(dchar[dchar])null) == to!S("hello world")); + assert(translate(to!S("hello world"), cast(dchar[dchar]) null) == to!S("hello world")); - foreach (T; TypeTuple!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) + foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[], + wchar[], const(wchar)[], immutable(wchar)[], + dchar[], const(dchar)[], immutable(dchar)[])) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - foreach(R; TypeTuple!(dchar[dchar], const dchar[dchar], + foreach (R; AliasSeq!(dchar[dchar], const dchar[dchar], immutable dchar[dchar])) { R tt = ['h' : 'q', 'l' : '5']; @@ -3327,7 +4872,7 @@ C1[] translate(C1, C2 = immutable char)(C1[] str, C1[] translate(C1, S, C2 = immutable char)(C1[] str, in S[dchar] transTable, const(C2)[] toRemove = null) @safe pure - if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2) +if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2) { import std.array : appender; auto buffer = appender!(C1[])(); @@ -3335,16 +4880,16 @@ C1[] translate(C1, S, C2 = immutable char)(C1[] str, return buffer.data; } -@trusted pure unittest +@system pure unittest { import std.conv : to; + import std.exception : assertCTFEable; - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) + foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[], + wchar[], const(wchar)[], immutable(wchar)[], + dchar[], const(dchar)[], immutable(dchar)[])) { assert(translate(to!S("hello world"), ['h' : "yellow", 'l' : "42"]) == to!S("yellowe4242o wor42d")); @@ -3358,14 +4903,14 @@ C1[] translate(C1, S, C2 = immutable char)(C1[] str, to!S("ello \U00010143 world")); assert(translate(to!S("hello \U00010143 world"), ['\U00010143' : ""]) == to!S("hello world")); - assert(translate(to!S("hello world"), cast(string[dchar])null) == to!S("hello world")); + assert(translate(to!S("hello world"), cast(string[dchar]) null) == to!S("hello world")); - foreach (T; TypeTuple!( char[], const( char)[], immutable( char)[], - wchar[], const(wchar)[], immutable(wchar)[], - dchar[], const(dchar)[], immutable(dchar)[])) + foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[], + wchar[], const(wchar)[], immutable(wchar)[], + dchar[], const(dchar)[], immutable(dchar)[])) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - foreach(R; TypeTuple!(string[dchar], const string[dchar], + foreach (R; AliasSeq!(string[dchar], const string[dchar], immutable string[dchar])) { R tt = ['h' : "yellow", 'l' : "42"]; @@ -3403,7 +4948,7 @@ void translate(C1, C2 = immutable char, Buffer)(C1[] str, in dchar[dchar] transTable, const(C2)[] toRemove, Buffer buffer) - if (isSomeChar!C1 && isSomeChar!C2 && isOutputRange!(Buffer, C1)) +if (isSomeChar!C1 && isSomeChar!C2 && isOutputRange!(Buffer, C1)) { translateImpl(str, transTable, toRemove, buffer); } @@ -3450,7 +4995,7 @@ void translate(C1, S, C2 = immutable char, Buffer)(C1[] str, in S[dchar] transTable, const(C2)[] toRemove, Buffer buffer) - if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2 && isOutputRange!(Buffer, S)) +if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2 && isOutputRange!(Buffer, S)) { translateImpl(str, transTable, toRemove, buffer); } @@ -3485,9 +5030,9 @@ private void translateImpl(C1, T, C2, Buffer)(C1[] str, cases where Unicode processing is not necessary. Unlike the other overloads of $(LREF _translate), this one does not take - an AA. Rather, it takes a $(D string) generated by $(LREF makeTrans). + an AA. Rather, it takes a $(D string) generated by $(LREF makeTransTable). - The array generated by $(D makeTrans) is $(D 256) elements long such that + The array generated by $(D makeTransTable) is $(D 256) elements long such that the index is equal to the ASCII character being replaced and the value is equal to the character that it's being replaced with. Note that translate does not decode any of the characters, so you can actually pass it Extended @@ -3504,16 +5049,16 @@ private void translateImpl(C1, T, C2, Buffer)(C1[] str, See_Also: $(LREF tr) - $(XREF array, replace) + $(REF replace, std,array) Params: str = The original string. transTable = The string indicating which characters to replace and what - to replace them with. It is generated by $(LREF makeTrans). + to replace them with. It is generated by $(LREF makeTransTable). toRemove = The characters to remove from the string. +/ C[] translate(C = immutable char)(in char[] str, in char[] transTable, in char[] toRemove = null) @trusted pure nothrow - if (is(Unqual!C == char)) +if (is(Unqual!C == char)) in { assert(transTable.length == 256); @@ -3533,82 +5078,107 @@ body } auto buffer = new char[count]; - translateImplAscii(str, transTable, remTable, buffer, toRemove); + + size_t i = 0; + foreach (char c; str) + { + if (!remTable[c]) + buffer[i++] = transTable[c]; + } + return cast(C[])(buffer); } -/++ Ditto +/ +/** + * Do same thing as $(LREF makeTransTable) but allocate the translation table + * on the GC heap. + * + * Use $(LREF makeTransTable) instead. + */ string makeTrans(in char[] from, in char[] to) @trusted pure nothrow +{ + return makeTransTable(from, to)[].idup; +} + +/// +@safe pure nothrow unittest +{ + auto transTable1 = makeTrans("eo5", "57q"); + assert(translate("hello world", transTable1) == "h5ll7 w7rld"); + + assert(translate("hello world", transTable1, "low") == "h5 rd"); +} + +/******* + * Construct 256 character translation table, where characters in from[] are replaced + * by corresponding characters in to[]. + * + * Params: + * from = array of chars, less than or equal to 256 in length + * to = corresponding array of chars to translate to + * Returns: + * translation array + */ + +char[256] makeTransTable(in char[] from, in char[] to) @safe pure nothrow @nogc in { import std.ascii : isASCII; assert(from.length == to.length); assert(from.length <= 256); foreach (char c; from) - assert(std.ascii.isASCII(c)); + assert(isASCII(c)); foreach (char c; to) - assert(std.ascii.isASCII(c)); + assert(isASCII(c)); } body { - import std.exception : assumeUnique; - - char[] transTable = new char[256]; - - foreach (i; 0 .. transTable.length) - transTable[i] = cast(char)i; - foreach (i; 0 .. from.length) - transTable[from[i]] = to[i]; - - return assumeUnique(transTable); -} - -/// -@safe pure nothrow unittest -{ - auto transTable1 = makeTrans("eo5", "57q"); - assert(translate("hello world", transTable1) == "h5ll7 w7rld"); + char[256] result = void; - assert(translate("hello world", transTable1, "low") == "h5 rd"); + foreach (i; 0 .. result.length) + result[i] = cast(char) i; + foreach (i, c; from) + result[c] = to[i]; + return result; } @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; - import std.exception; assertCTFEable!( { - foreach (C; TypeTuple!(char, const char, immutable char)) + foreach (C; AliasSeq!(char, const char, immutable char)) { - assert(translate!C("hello world", makeTrans("hl", "q5")) == to!(C[])("qe55o wor5d")); + assert(translate!C("hello world", makeTransTable("hl", "q5")) == to!(C[])("qe55o wor5d")); auto s = to!(C[])("hello world"); - auto transTable = makeTrans("hl", "q5"); + auto transTable = makeTransTable("hl", "q5"); static assert(is(typeof(s) == typeof(translate!C(s, transTable)))); } - foreach (S; TypeTuple!(char[], const(char)[], immutable(char)[])) + foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[])) { - assert(translate(to!S("hello world"), makeTrans("hl", "q5")) == to!S("qe55o wor5d")); - assert(translate(to!S("hello \U00010143 world"), makeTrans("hl", "q5")) == + assert(translate(to!S("hello world"), makeTransTable("hl", "q5")) == to!S("qe55o wor5d")); + assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5")) == to!S("qe55o \U00010143 wor5d")); - assert(translate(to!S("hello world"), makeTrans("ol", "1o")) == to!S("heoo1 w1rod")); - assert(translate(to!S("hello world"), makeTrans("", "")) == to!S("hello world")); - assert(translate(to!S("hello world"), makeTrans("12345", "67890")) == to!S("hello world")); - assert(translate(to!S("hello \U00010143 world"), makeTrans("12345", "67890")) == + assert(translate(to!S("hello world"), makeTransTable("ol", "1o")) == to!S("heoo1 w1rod")); + assert(translate(to!S("hello world"), makeTransTable("", "")) == to!S("hello world")); + assert(translate(to!S("hello world"), makeTransTable("12345", "67890")) == to!S("hello world")); + assert(translate(to!S("hello \U00010143 world"), makeTransTable("12345", "67890")) == to!S("hello \U00010143 world")); - foreach (T; TypeTuple!(char[], const(char)[], immutable(char)[])) + foreach (T; AliasSeq!(char[], const(char)[], immutable(char)[])) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - assert(translate(to!S("hello world"), makeTrans("hl", "q5"), to!T("r")) == + assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("r")) == to!S("qe55o wo5d")); - assert(translate(to!S("hello \U00010143 world"), makeTrans("hl", "q5"), to!T("r")) == + assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5"), to!T("r")) == to!S("qe55o \U00010143 wo5d")); - assert(translate(to!S("hello world"), makeTrans("hl", "q5"), to!T("helo")) == + assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("helo")) == to!S(" wrd")); - assert(translate(to!S("hello world"), makeTrans("hl", "q5"), to!T("q5")) == + assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("q5")) == to!S("qe55o wor5d")); }(); } @@ -3621,13 +5191,13 @@ body Params: str = The original string. transTable = The string indicating which characters to replace and what - to replace them with. It is generated by $(LREF makeTrans). + to replace them with. It is generated by $(LREF makeTransTable). toRemove = The characters to remove from the string. buffer = An output range to write the contents to. +/ void translate(C = immutable char, Buffer)(in char[] str, in char[] transTable, in char[] toRemove, Buffer buffer) @trusted pure - if (is(Unqual!C == char) && isOutputRange!(Buffer, char)) +if (is(Unqual!C == char) && isOutputRange!(Buffer, char)) in { assert(transTable.length == 256); @@ -3639,7 +5209,11 @@ body foreach (char c; toRemove) remTable[c] = true; - translateImplAscii(str, transTable, remTable, buffer, toRemove); + foreach (char c; str) + { + if (!remTable[c]) + put(buffer, transTable[c]); + } } /// @@ -3647,7 +5221,7 @@ body { import std.array : appender; auto buffer = appender!(char[])(); - auto transTable1 = makeTrans("eo5", "57q"); + auto transTable1 = makeTransTable("eo5", "57q"); translate("hello world", transTable1, null, buffer); assert(buffer.data == "h5ll7 w7rld"); @@ -3656,49 +5230,32 @@ body assert(buffer.data == "h5 rd"); } -private void translateImplAscii(C = immutable char, Buffer)(in char[] str, - in char[] transTable, ref bool[256] remTable, Buffer buffer, - in char[] toRemove = null) @trusted pure -{ - static if (isOutputRange!(Buffer, char)) - { - foreach (char c; str) - { - if (!remTable[c]) - put(buffer, transTable[c]); - } - } - else - { - size_t i = 0; - foreach (char c; str) - { - if (!remTable[c]) - buffer[i++] = transTable[c]; - } - } -} - +//@@@DEPRECATED_2018-05@@@ /*********************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * See if character c is in the pattern. * Patterns: * - * A <i>pattern</i> is an array of characters much like a <i>character - * class</i> in regular expressions. A sequence of characters + * A $(I pattern) is an array of characters much like a $(I character + * class) in regular expressions. A sequence of characters * can be given, such as "abcde". The '-' can represent a range * of characters, as "a-e" represents the same pattern as "abcde". * "a-fA-F0-9" represents all the hex characters. * If the first character of a pattern is '^', then the pattern * is negated, i.e. "^0-9" means any character except a digit. - * The functions inPattern, <b>countchars</b>, <b>removeschars</b>, - * and <b>squeeze</b> - * use patterns. + * The functions inPattern, $(B countchars), $(B removeschars), + * and $(B squeeze) use patterns. * * Note: In the future, the pattern syntax may be improved * to be more like regular expression character classes. */ - -bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc if (isSomeString!S) +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc +if (isSomeString!S) { bool result = false; int range = 0; @@ -3734,10 +5291,10 @@ bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc if (isSomeString!S) @safe pure @nogc unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("std.string.inPattern.unittest\n"); - import std.exception; assertCTFEable!( { assert(inPattern('x', "x") == 1); @@ -3762,12 +5319,18 @@ bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc if (isSomeString!S) }); } - +//@@@DEPRECATED_2018-05@@@ /*********************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * See if character c is in the intersection of the patterns. */ - -bool inPattern(S)(dchar c, S[] patterns) @safe pure @nogc if (isSomeString!S) +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +bool inPattern(S)(dchar c, S[] patterns) @safe pure @nogc +if (isSomeString!S) { foreach (string pattern; patterns) { @@ -3779,12 +5342,18 @@ bool inPattern(S)(dchar c, S[] patterns) @safe pure @nogc if (isSomeString!S) return true; } - +//@@@DEPRECATED_2018-05@@@ /******************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * Count characters in s that match pattern. */ - -size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc if (isSomeString!S && isSomeString!S1) +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc +if (isSomeString!S && isSomeString!S1) { size_t count; foreach (dchar c; s) @@ -3797,10 +5366,10 @@ size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc if (isSomeString!S @safe pure @nogc unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("std.string.count.unittest\n"); - import std.exception; assertCTFEable!( { assert(countchars("abc", "a-c") == 3); @@ -3808,12 +5377,18 @@ size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc if (isSomeString!S }); } - +//@@@DEPRECATED_2018-05@@@ /******************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * Return string that is s with all characters removed that match pattern. */ - -S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S) +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") +S removechars(S)(S s, in S pattern) @safe pure +if (isSomeString!S) { import std.utf : encode; @@ -3833,7 +5408,7 @@ S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S) } if (changed) { - std.utf.encode(r, c); + encode(r, c); } } if (changed) @@ -3845,10 +5420,10 @@ S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S) @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("std.string.removechars.unittest\n"); - import std.exception; assertCTFEable!( { assert(removechars("abc", "a-c").length == 0); @@ -3858,16 +5433,26 @@ S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S) }); } +@safe pure unittest +{ + assert(removechars("abc", "x") == "abc"); +} +//@@@DEPRECATED_2018-05@@@ /*************************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * Return string where sequences of a character in s[] from pattern[] * are replaced with a single instance of that character. * If pattern is null, it defaults to all characters. */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") S squeeze(S)(S s, in S pattern = null) { - import std.utf : encode; + import std.utf : encode, stride; Unqual!(typeof(s[0]))[] r; dchar lastc; @@ -3888,10 +5473,10 @@ S squeeze(S)(S s, in S pattern = null) { if (r is null) r = s[0 .. lasti].dup; - std.utf.encode(r, c); + encode(r, c); } else - lasti = i + std.utf.stride(s, i); + lasti = i + stride(s, i); lastc = c; } else @@ -3901,20 +5486,20 @@ S squeeze(S)(S s, in S pattern = null) { if (r is null) r = s[0 .. lasti].dup; - std.utf.encode(r, c); + encode(r, c); } } } return changed ? ((r is null) ? s[0 .. lasti] : cast(S) r) : s; } -@trusted pure unittest +@system pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("std.string.squeeze.unittest\n"); - import std.exception; assertCTFEable!( { string s; @@ -3930,10 +5515,16 @@ S squeeze(S)(S s, in S pattern = null) }); } +//@@@DEPRECATED_2018-05@@@ /*************************************************************** + $(RED This function is deprecated and will be removed May 2018.) + Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + instead. If you still need this function, it will be available in + $(LINK2 https://github.com/dlang/undeaD, undeaD). + Finds the position $(D_PARAM pos) of the first character in $(D_PARAM s) that does not match $(D_PARAM pattern) (in the terminology used by - $(LINK2 std_string.html,inPattern)). Updates $(D_PARAM s = + $(REF inPattern, std,string)). Updates $(D_PARAM s = s[pos..$]). Returns the slice from the beginning of the original (before update) string up to, and excluding, $(D_PARAM pos). @@ -3941,7 +5532,7 @@ The $(D_PARAM munch) function is mostly convenient for skipping certain category of characters (e.g. whitespace) when parsing strings. (In such cases, the return value is not used.) */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") S1 munch(S1, S2)(ref S1 s, S2 pattern) @safe pure @nogc { size_t j = s.length; @@ -3986,11 +5577,12 @@ S1 munch(S1, S2)(ref S1 s, S2 pattern) @safe pure @nogc * repeated with the one to its immediate left. */ -S succ(S)(S s) @safe pure if (isSomeString!S) +S succ(S)(S s) @safe pure +if (isSomeString!S) { import std.ascii : isAlphaNum; - if (s.length && std.ascii.isAlphaNum(s[$ - 1])) + if (s.length && isAlphaNum(s[$ - 1])) { auto r = s.dup; size_t i = r.length - 1; @@ -4011,7 +5603,7 @@ S succ(S)(S s) @safe pure if (isSomeString!S) c -= 'Z' - 'A'; carry = c; Lcarry: - r[i] = cast(char)c; + r[i] = cast(char) c; if (i == 0) { auto t = new typeof(r[0])[r.length + 1]; @@ -4023,7 +5615,7 @@ S succ(S)(S s) @safe pure if (isSomeString!S) break; default: - if (std.ascii.isAlphaNum(c)) + if (isAlphaNum(c)) r[i]++; return r; } @@ -4044,10 +5636,10 @@ S succ(S)(S s) @safe pure if (isSomeString!S) @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("std.string.succ.unittest\n"); - import std.exception; assertCTFEable!( { assert(succ(string.init) is null); @@ -4065,7 +5657,7 @@ S succ(S)(S s) @safe pure if (isSomeString!S) the corresponding characters in $(D to) and returns the resulting string. $(D tr) is based on - $(WEB pubs.opengroup.org/onlinepubs/9699919799/utilities/_tr.html, Posix's tr), + $(HTTP pubs.opengroup.org/onlinepubs/9699919799/utilities/_tr.html, Posix's tr), though it doesn't do everything that the Posix utility does. Params: @@ -4102,9 +5694,9 @@ S succ(S)(S s) @safe pure if (isSomeString!S) C1[] tr(C1, C2, C3, C4 = immutable char) (C1[] str, const(C2)[] from, const(C3)[] to, const(C4)[] modifiers = null) { + import std.array : appender; import std.conv : conv_to = to; import std.utf : decode; - import std.array : appender; bool mod_c; bool mod_d; @@ -4137,10 +5729,10 @@ C1[] tr(C1, C2, C3, C4 = immutable char) for (size_t i = 0; i < from.length; ) { - dchar f = std.utf.decode(from, i); + immutable f = decode(from, i); if (f == '-' && lastf != dchar.init && i < from.length) { - dchar nextf = std.utf.decode(from, i); + immutable nextf = decode(from, i); if (lastf <= c && c <= nextf) { n += c - lastf - 1; @@ -4170,10 +5762,11 @@ C1[] tr(C1, C2, C3, C4 = immutable char) // Find the nth character in to[] dchar nextt; for (size_t i = 0; i < to.length; ) - { dchar t = std.utf.decode(to, i); + { + immutable t = decode(to, i); if (t == '-' && lastt != dchar.init && i < to.length) { - nextt = std.utf.decode(to, i); + nextt = decode(to, i); n -= nextt - lastt; if (n < 0) { @@ -4213,23 +5806,23 @@ C1[] tr(C1, C2, C3, C4 = immutable char) return result.data; } -unittest +@safe pure unittest { + import std.algorithm.comparison : equal; import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("std.string.tr.unittest\n"); - import std.algorithm : equal; // Complete list of test types; too slow to test'em all - // alias TestTypes = TypeTuple!( + // alias TestTypes = AliasSeq!( // char[], const( char)[], immutable( char)[], // wchar[], const(wchar)[], immutable(wchar)[], // dchar[], const(dchar)[], immutable(dchar)[]); // Reduced list of test types - alias TestTypes = TypeTuple!(char[], const(wchar)[], immutable(dchar)[]); + alias TestTypes = AliasSeq!(char[], const(wchar)[], immutable(dchar)[]); - import std.exception; assertCTFEable!( { foreach (S; TestTypes) @@ -4258,76 +5851,88 @@ unittest }); } - -/* ************************************************ - * Version : v0.3 - * Author : David L. 'SpottedTiger' Davis - * Date Created : 31.May.05 Compiled and Tested with dmd v0.125 - * Date Modified : 01.Jun.05 Modified the function to handle the - * : imaginary and complex float-point - * : datatypes. - * : - * Licence : Public Domain / Contributed to Digital Mars - */ +@system pure unittest +{ + import std.exception : assertThrown; + import core.exception : AssertError; + assertThrown!AssertError(tr("abcdef", "cd", "CD", "X")); +} /** - * [in] string s can be formatted in the following ways: - * - * Integer Whole Number: - * (for byte, ubyte, short, ushort, int, uint, long, and ulong) - * ['+'|'-']digit(s)[U|L|UL] - * - * examples: 123, 123UL, 123L, +123U, -123L - * - * Floating-Point Number: - * (for float, double, real, ifloat, idouble, and ireal) - * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] - * or [nan|nani|inf|-inf] - * - * examples: +123., -123.01, 123.3e-10f, 123.3e-10fi, 123.3e-10L - * - * (for cfloat, cdouble, and creal) - * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][+] - * [digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] - * or [nan|nani|nan+nani|inf|-inf] - * - * examples: nan, -123e-1+456.9e-10Li, +123e+10+456i, 123+456 - * - * [in] bool bAllowSep - * False by default, but when set to true it will accept the - * separator characters $(D ',') and $(D '__') within the string, but these + * Takes a string $(D s) and determines if it represents a number. This function + * also takes an optional parameter, $(D bAllowSep), which will accept the + * separator characters $(D ',') and $(D '__') within the string. But these * characters should be stripped from the string before using any - * of the conversion functions like toInt(), toFloat(), and etc + * of the conversion functions like $(D to!int()), $(D to!float()), and etc * else an error will occur. * * Also please note, that no spaces are allowed within the string * anywhere whether it's a leading, trailing, or embedded space(s), * thus they too must be stripped from the string before using this * function, or any of the conversion functions. + * + * Params: + * s = the string or random access range to check + * bAllowSep = accept separator characters or not + * + * Returns: + * $(D bool) */ +bool isNumeric(S)(S s, bool bAllowSep = false) +if (isSomeString!S || + (isRandomAccessRange!S && + hasSlicing!S && + isSomeChar!(ElementType!S) && + !isInfinite!S)) +{ + import std.algorithm.comparison : among; + import std.ascii : isASCII; -bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure -{ - import std.algorithm : among; + // ASCII only case insensitive comparison with two ranges + static bool asciiCmp(S1)(S1 a, string b) + { + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.ascii : toLower; + import std.utf : byChar; + return a.map!toLower.equal(b.byChar.map!toLower); + } + + // auto-decoding special case, we're only comparing characters + // in the ASCII range so there's no reason to decode + static if (isSomeString!S) + { + import std.utf : byCodeUnit; + auto codeUnits = s.byCodeUnit; + } + else + { + alias codeUnits = s; + } - immutable iLen = s.length; - if (iLen == 0) + if (codeUnits.empty) return false; // Check for NaN (Not a Number) and for Infinity - if (s.among!((a, b) => icmp(a, b) == 0) + if (codeUnits.among!((a, b) => asciiCmp(a.save, b)) ("nan", "nani", "nan+nani", "inf", "-inf")) return true; - immutable j = s[0].among!('-', '+')() != 0; + immutable frontResult = codeUnits.front; + if (frontResult == '-' || frontResult == '+') + codeUnits.popFront; + + immutable iLen = codeUnits.length; bool bDecimalPoint, bExponent, bComplex, sawDigits; - for (size_t i = j; i < iLen; i++) + for (size_t i = 0; i < iLen; i++) { - immutable c = s[i]; + immutable c = codeUnits[i]; + + if (!c.isASCII) + return false; - // Digits are good, continue checking - // with the popFront character... ;) + // Digits are good, skip to the next character if (c >= '0' && c <= '9') { sawDigits = true; @@ -4348,21 +5953,21 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure } // Allow only one exponent per number - if (c.among!('e', 'E')()) + if (c == 'e' || c == 'E') { // A 2nd exponent found, return not a number if (bExponent || i + 1 >= iLen) return false; // Look forward for the sign, and if // missing then this is not a number. - if (!s[i + 1].among!('-', '+')()) + if (codeUnits[i + 1] != '-' && codeUnits[i + 1] != '+') return false; bExponent = true; i++; continue; } // Allow only one decimal point per number to be used - if (c == '.' ) + if (c == '.') { // A 2nd decimal point found, return not a number if (bDecimalPoint) @@ -4377,19 +5982,19 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure if (!sawDigits) return false; // Integer Whole Number - if (icmp(s[i..iLen], "ul") == 0 && + if (asciiCmp(codeUnits[i .. iLen], "ul") && (!bDecimalPoint && !bExponent && !bComplex)) return true; // Floating-Point Number - if (s[i..iLen].among!((a, b) => icmp(a, b) == 0)("fi", "li") && + if (codeUnits[i .. iLen].among!((a, b) => asciiCmp(a, b))("fi", "li") && (bDecimalPoint || bExponent || bComplex)) return true; - if (icmp(s[i..iLen], "ul") == 0 && + if (asciiCmp(codeUnits[i .. iLen], "ul") && (bDecimalPoint || bExponent || bComplex)) return false; // Could be a Integer or a Float, thus // all these suffixes are valid for both - return s[i..iLen].among!((a, b) => icmp(a, b) == 0) + return codeUnits[i .. iLen].among!((a, b) => asciiCmp(a, b)) ("ul", "fi", "li") != 0; } if (i == iLen - 1) @@ -4415,7 +6020,51 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure return sawDigits; } -@safe pure unittest +/** + * Integer Whole Number: (byte, ubyte, short, ushort, int, uint, long, and ulong) + * ['+'|'-']digit(s)[U|L|UL] + */ +@safe @nogc pure nothrow unittest +{ + assert(isNumeric("123")); + assert(isNumeric("123UL")); + assert(isNumeric("123L")); + assert(isNumeric("+123U")); + assert(isNumeric("-123L")); +} + +/** + * Floating-Point Number: (float, double, real, ifloat, idouble, and ireal) + * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] + * or [nan|nani|inf|-inf] + */ +@safe @nogc pure nothrow unittest +{ + assert(isNumeric("+123")); + assert(isNumeric("-123.01")); + assert(isNumeric("123.3e-10f")); + assert(isNumeric("123.3e-10fi")); + assert(isNumeric("123.3e-10L")); + + assert(isNumeric("nan")); + assert(isNumeric("nani")); + assert(isNumeric("-inf")); +} + +/** + * Floating-Point Number: (cfloat, cdouble, and creal) + * ['+'|'-']digit(s)[.][digit(s)][[e-|e+]digit(s)][+] + * [digit(s)[.][digit(s)][[e-|e+]digit(s)][i|f|L|Li|fi]] + * or [nan|nani|nan+nani|inf|-inf] + */ +@safe @nogc pure nothrow unittest +{ + assert(isNumeric("-123e-1+456.9e-10Li")); + assert(isNumeric("+123e+10+456i")); + assert(isNumeric("123+456")); +} + +@safe @nogc pure nothrow unittest { assert(!isNumeric("F")); assert(!isNumeric("L")); @@ -4431,15 +6080,64 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure assert(!isNumeric("e+")); assert(!isNumeric(".f")); assert(!isNumeric("e+f")); + assert(!isNumeric("++1")); + assert(!isNumeric("")); + assert(!isNumeric("1E+1E+1")); + assert(!isNumeric("1E1")); + assert(!isNumeric("\x81")); +} + +// Test string types +@safe unittest +{ + import std.conv : to; + + foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[])) + { + assert("123".to!T.isNumeric()); + assert("123UL".to!T.isNumeric()); + assert("123fi".to!T.isNumeric()); + assert("123li".to!T.isNumeric()); + assert(!"--123L".to!T.isNumeric()); + } +} + +// test ranges +@system pure unittest +{ + import std.range : refRange; + import std.utf : byCodeUnit; + + assert("123".byCodeUnit.isNumeric()); + assert("123UL".byCodeUnit.isNumeric()); + assert("123fi".byCodeUnit.isNumeric()); + assert("123li".byCodeUnit.isNumeric()); + assert(!"--123L".byCodeUnit.isNumeric()); + + dstring z = "0"; + assert(isNumeric(refRange(&z))); + + dstring nani = "nani"; + assert(isNumeric(refRange(&nani))); +} + +/// isNumeric works with CTFE +@safe pure unittest +{ + enum a = isNumeric("123.00E-5+1234.45E-12Li"); + enum b = isNumeric("12345xxxx890"); + + static assert( a); + static assert(!b); } -@trusted unittest +@system unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("isNumeric(in string, bool = false).unittest\n"); - import std.exception; assertCTFEable!( { // Test the isNumeric(in string) function @@ -4477,16 +6175,15 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure } string s = "$250.99-"; - assert(isNumeric(s[1..s.length - 2]) == true); + assert(isNumeric(s[1 .. s.length - 2]) == true); assert(isNumeric(s) == false); - assert(isNumeric(s[0..s.length - 1]) == false); + assert(isNumeric(s[0 .. s.length - 1]) == false); }); assert(!isNumeric("-")); assert(!isNumeric("+")); } - /***************************** * Soundex algorithm. * @@ -4497,51 +6194,37 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure * of names. * * Params: - * string = String to convert to Soundex representation. - * buffer = Optional 4 char array to put the resulting Soundex - * characters into. If null, the return value - * buffer will be allocated on the heap. + * str = String or InputRange to convert to Soundex representation. + * * Returns: * The four character array with the Soundex result in it. - * Returns null if there is no Soundex representation for the string. + * The array has zero's in it if there is no Soundex representation for the string. * * See_Also: * $(LINK2 http://en.wikipedia.org/wiki/Soundex, Wikipedia), * $(LUCKY The Soundex Indexing System) + * $(LREF soundex) * * Bugs: * Only works well with English names. * There are other arguably better Soundex algorithms, * but this one is the standard one. */ - -char[] soundex(const(char)[] string, char[] buffer = null) - @safe pure nothrow -in -{ - assert(!buffer.ptr || buffer.length >= 4); -} -out (result) -{ - if (result.ptr) - { - assert(result.length == 4); - assert(result[0] >= 'A' && result[0] <= 'Z'); - foreach (char c; result[1 .. 4]) - assert(c >= '0' && c <= '6'); - } -} -body +char[4] soundexer(Range)(Range str) +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) { + alias C = Unqual!(ElementEncodingType!Range); + static immutable dex = // ABCDEFGHIJKLMNOPQRSTUVWXYZ - "01230120022455012623010202"; - - int b = 0; - char lastc; - foreach (char cs; string) - { auto c = cs; // necessary because cs is final + "01230120022455012623010202"; + char[4] result = void; + size_t b = 0; + C lastc; + foreach (C c; str) + { if (c >= 'a' && c <= 'z') c -= 'a' - 'A'; else if (c >= 'A' && c <= 'Z') @@ -4554,9 +6237,7 @@ body } if (b == 0) { - if (!buffer.ptr) - buffer = new char[4]; - buffer[0] = c; + result[0] = cast(char) c; b++; lastc = dex[c - 'A']; } @@ -4569,25 +6250,74 @@ body c = dex[c - 'A']; if (c != '0' && c != lastc) { - buffer[b] = c; + result[b] = cast(char) c; b++; lastc = c; } + if (b == 4) + goto Lret; } - if (b == 4) - goto Lret; } if (b == 0) - buffer = null; + result[] = 0; else - buffer[b .. 4] = '0'; + result[b .. 4] = '0'; Lret: + return result; +} + +char[4] soundexer(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + return soundexer!(StringTypeOf!Range)(str); +} + +/***************************** + * Like $(LREF soundexer), but with different parameters + * and return value. + * + * Params: + * str = String to convert to Soundex representation. + * buffer = Optional 4 char array to put the resulting Soundex + * characters into. If null, the return value + * buffer will be allocated on the heap. + * Returns: + * The four character array with the Soundex result in it. + * Returns null if there is no Soundex representation for the string. + * See_Also: + * $(LREF soundexer) + */ +char[] soundex(const(char)[] str, char[] buffer = null) + @safe pure nothrow +in +{ + assert(buffer is null || buffer.length >= 4); +} +out (result) +{ + if (result !is null) + { + assert(result.length == 4); + assert(result[0] >= 'A' && result[0] <= 'Z'); + foreach (char c; result[1 .. 4]) + assert(c >= '0' && c <= '6'); + } +} +body +{ + char[4] result = soundexer(str); + if (result[0] == 0) + return null; + if (buffer is null) + buffer = new char[4]; + buffer[] = result[]; return buffer; } + @safe pure nothrow unittest { - import std.exception; + import std.exception : assertCTFEable; assertCTFEable!( { char[4] buffer; @@ -4630,9 +6360,19 @@ body assert(soundex("johnsons") == "J525"); assert(soundex("Hardin") == "H635"); assert(soundex("Martinez") == "M635"); + + import std.utf : byChar, byDchar, byWchar; + assert(soundexer("Martinez".byChar ) == "M635"); + assert(soundexer("Martinez".byWchar) == "M635"); + assert(soundexer("Martinez".byDchar) == "M635"); }); } +@safe pure unittest +{ + assert(testAliasedString!soundexer("Martinez")); +} + /*************************************************** * Construct an associative array consisting of all @@ -4640,37 +6380,13 @@ body * * This is useful in cases where the user is expected to type * in one of a known set of strings, and the program will helpfully - * autocomplete the string once sufficient characters have been + * auto-complete the string once sufficient characters have been * entered that uniquely identify it. - * Example: - * --- - * import std.stdio; - * import std.string; - * - * void main() - * { - * static string[] list = [ "food", "foxy" ]; - * - * auto abbrevs = std.string.abbrev(list); - * - * foreach (key, value; abbrevs) - * { - * writefln("%s => %s", key, value); - * } - * } - * --- - * produces the output: - * <pre> - * fox => foxy - * food => food - * foxy => foxy - * foo => food - * </pre> */ string[string] abbrev(string[] values) @safe pure { - import std.algorithm : sort; + import std.algorithm.sorting : sort; string[string] result; @@ -4697,7 +6413,9 @@ string[string] abbrev(string[] values) @safe pure break; } - for (size_t j = 0; j < value.length; j += std.utf.stride(value, j)) + import std.utf : stride; + + for (size_t j = 0; j < value.length; j += stride(value, j)) { string v = value[0 .. j]; @@ -4715,14 +6433,26 @@ string[string] abbrev(string[] values) @safe pure return result; } -@trusted pure unittest +/// +@safe unittest +{ + import std.string; + + static string[] list = [ "food", "foxy" ]; + auto abbrevs = abbrev(list); + assert(abbrevs == ["fox": "foxy", "food": "food", + "foxy": "foxy", "foo": "food"]); +} + + +@system pure unittest { + import std.algorithm.sorting : sort; import std.conv : to; - import std.algorithm : sort; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.abbrev.unittest\n"); - import std.exception; assertCTFEable!( { string[] values; @@ -4769,13 +6499,14 @@ string[string] abbrev(string[] values) @safe pure */ size_t column(Range)(Range str, in size_t tabsize = 8) - if (isSomeString!Range || - isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range))) +if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) || + isNarrowString!Range) && + !isConvertibleToString!Range) { static if (is(Unqual!(ElementEncodingType!Range) == char)) { // decoding needed for chars - import std.utf: byDchar; + import std.utf : byDchar; return str.byDchar.column(tabsize); } @@ -4812,7 +6543,7 @@ size_t column(Range)(Range str, in size_t tabsize = 8) } /// -unittest +@safe pure unittest { import std.utf : byChar, byWchar, byDchar; @@ -4847,13 +6578,24 @@ unittest assert(column("abc\u00861") == 5); } +size_t column(Range)(auto ref Range str, in size_t tabsize = 8) +if (isConvertibleToString!Range) +{ + return column!(StringTypeOf!Range)(str, tabsize); +} + +@safe pure unittest +{ + assert(testAliasedString!column("abc\u00861")); +} + @safe @nogc unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.column.unittest\n"); - import std.exception; assertCTFEable!( { assert(column(string.init) == 0); @@ -4883,8 +6625,10 @@ unittest */ S wrap(S)(S s, in size_t columns = 80, S firstindent = null, - S indent = null, in size_t tabsize = 8) @safe pure if (isSomeString!S) +S indent = null, in size_t tabsize = 8) +if (isSomeString!S) { + import std.uni : isWhite; typeof(s.dup) result; bool inword; bool first = true; @@ -4898,7 +6642,7 @@ S wrap(S)(S s, in size_t columns = 80, S firstindent = null, auto col = column(firstindent, tabsize); foreach (size_t i, dchar c; s) { - if (std.uni.isWhite(c)) + if (isWhite(c)) { if (inword) { @@ -4948,13 +6692,25 @@ S wrap(S)(S s, in size_t columns = 80, S firstindent = null, return result; } +/// +@safe pure unittest +{ + assert(wrap("a short string", 7) == "a short\nstring\n"); + + // wrap will not break inside of a word, but at the next space + assert(wrap("a short string", 4) == "a\nshort\nstring\n"); + + assert(wrap("a short string", 7, "\t") == "\ta\nshort\nstring\n"); + assert(wrap("a short string", 7, "\t", " ") == "\ta\n short\n string\n"); +} + @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.wrap.unittest\n"); - import std.exception; assertCTFEable!( { assert(wrap(string.init) == "\n"); @@ -4987,9 +6743,10 @@ S wrap(S)(S s, in size_t columns = 80, S firstindent = null, * StringException if indentation is done with different sequences * of whitespace characters. */ -S outdent(S)(S str) @safe pure if(isSomeString!S) +S outdent(S)(S str) @safe pure +if (isSomeString!S) { - return str.splitLines(KeepTerminator.yes).outdent().join(); + return str.splitLines(Yes.keepTerminator).outdent().join(); } /// @@ -5029,9 +6786,10 @@ void main() { * StringException if indentation is done with different sequences * of whitespace characters. */ -S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) +S[] outdent(S)(S[] lines) @safe pure +if (isSomeString!S) { - import std.algorithm : startsWith; + import std.algorithm.searching : startsWith; if (lines.empty) { @@ -5046,7 +6804,7 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) S shortestIndent; foreach (ref line; lines) { - auto stripped = line.stripLeft(); + const stripped = line.stripLeft(); if (stripped.empty) { @@ -5054,7 +6812,7 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) } else { - auto indent = leadingWhiteOf(line); + const indent = leadingWhiteOf(line); // Comparing number of code units instead of code points is OK here // because this function throws upon inconsistent indentation. @@ -5069,7 +6827,7 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) foreach (ref line; lines) { - auto stripped = line.stripLeft(); + const stripped = line.stripLeft(); if (stripped.empty) { @@ -5091,6 +6849,7 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) @safe pure unittest { import std.conv : to; + import std.exception : assertCTFEable; debug(string) trustedPrintf("string.outdent.unittest\n"); @@ -5118,11 +6877,10 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) "; } - import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { enum S blank = ""; assert(blank.outdent() == blank); @@ -5183,12 +6941,22 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) }); } +@safe pure unittest +{ + import std.exception : assertThrown; + auto bad = " a\n\tb\n c"; + assertThrown!StringException(bad.outdent); +} + /** Assume the given array of integers $(D arr) is a well-formed UTF string and return it typed as a UTF string. $(D ubyte) becomes $(D char), $(D ushort) becomes $(D wchar) and $(D uint) becomes $(D dchar). Type qualifiers are preserved. +When compiled with debug mode, this function performs an extra check to make +sure the return value is a valid Unicode string. + Params: arr = array of bytes, ubytes, shorts, ushorts, ints, or uints @@ -5198,11 +6966,12 @@ Returns: See_Also: $(LREF representation) */ auto assumeUTF(T)(T[] arr) pure - if(staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1) +if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1) { + import std.traits : ModifyTypePreservingTQ; import std.utf : validate; - alias ToUTFType(U) = TypeTuple!(char, wchar, dchar)[U.sizeof / 2]; - auto asUTF = cast(ModifyTypePreservingSTC!(ToUTFType, T)[])arr; + alias ToUTFType(U) = AliasSeq!(char, wchar, dchar)[U.sizeof / 2]; + auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[])arr; debug validate(asUTF); return asUTF; } @@ -5217,29 +6986,29 @@ auto assumeUTF(T)(T[] arr) pure assert(a == c); } -pure unittest +pure @system unittest { - import std.algorithm : equal; - foreach(T; TypeTuple!(char[], wchar[], dchar[])) + import std.algorithm.comparison : equal; + foreach (T; AliasSeq!(char[], wchar[], dchar[])) { immutable T jti = "Hello World"; T jt = jti.dup; - static if(is(T == char[])) + static if (is(T == char[])) { - auto gt = cast(ubyte[])jt; + auto gt = cast(ubyte[]) jt; auto gtc = cast(const(ubyte)[])jt; auto gti = cast(immutable(ubyte)[])jt; } - else static if(is(T == wchar[])) + else static if (is(T == wchar[])) { - auto gt = cast(ushort[])jt; + auto gt = cast(ushort[]) jt; auto gtc = cast(const(ushort)[])jt; auto gti = cast(immutable(ushort)[])jt; } - else static if(is(T == dchar[])) + else static if (is(T == dchar[])) { - auto gt = cast(uint[])jt; + auto gt = cast(uint[]) jt; auto gtc = cast(const(uint)[])jt; auto gti = cast(immutable(uint)[])jt; } diff --git a/std/syserror.d b/std/syserror.d deleted file mode 100644 index d55429c3fc7..00000000000 --- a/std/syserror.d +++ /dev/null @@ -1,49 +0,0 @@ -// Written in the D programming language -// Placed in public domain. -// Written by Walter Bright - -/** - Convert Win32 error code to string - - Source: $(PHOBOSSRC std/_syserror.d) -*/ - -module std.syserror; - -// Deprecated - instead use std.windows.syserror.sysErrorString() - -deprecated("Please use std.windows.syserror.sysErrorString instead") -class SysError -{ - private import core.stdc.stdio; - private import core.stdc.string; - private import std.string; - - static string msg(uint errcode) - { - string result; - - switch (errcode) - { - case 2: result = "file not found"; break; - case 3: result = "path not found"; break; - case 4: result = "too many open files"; break; - case 5: result = "access denied"; break; - case 6: result = "invalid handle"; break; - case 8: result = "not enough memory"; break; - case 14: result = "out of memory"; break; - case 15: result = "invalid drive"; break; - case 21: result = "not ready"; break; - case 32: result = "sharing violation"; break; - case 87: result = "invalid parameter"; break; - - default: - auto r = new char[uint.sizeof * 3 + 1]; - auto len = sprintf(r.ptr, "%u", errcode); - result = cast(string) r[0 .. len]; - break; - } - - return result; - } -} diff --git a/std/system.d b/std/system.d index d4b47fe87ab..1af5013f2b0 100644 --- a/std/system.d +++ b/std/system.d @@ -3,12 +3,9 @@ /** * Information about the target operating system, environment, and CPU. * - * Macros: - * WIKI = Phobos/StdSystem - * * Copyright: Copyright Digital Mars 2000 - 2011 - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) and Jonathan M Davis + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) and Jonathan M Davis * Source: $(PHOBOSSRC std/_system.d) */ module std.system; @@ -25,7 +22,7 @@ immutable $(D version(linux)), etc. See_Also: - <a href="../version.html#PredefinedVersions">Predefined Versions</a> + $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) +/ enum OS { @@ -34,6 +31,7 @@ immutable linux, /// All Linux Systems osx, /// Mac OS X freeBSD, /// FreeBSD + netBSD, /// NetBSD solaris, /// Solaris android, /// Android otherPosix /// Other Posix Systems @@ -42,10 +40,11 @@ immutable /// The OS that the program was compiled for. version(Win32) OS os = OS.win32; else version(Win64) OS os = OS.win64; + else version(Android) OS os = OS.android; else version(linux) OS os = OS.linux; else version(OSX) OS os = OS.osx; else version(FreeBSD) OS os = OS.freeBSD; - else version(Android) OS os = OS.android; + else version(NetBSD) OS os = OS.netBSD; else version(Posix) OS os = OS.otherPosix; else static assert(0, "Unknown OS."); @@ -60,7 +59,7 @@ immutable $(D version(LittleEndian)). See_Also: - <a href="../version.html#PredefinedVersions">Predefined Versions</a> + $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions) +/ enum Endian { diff --git a/std/traits.d b/std/traits.d index 7390a187fb5..ec9a9c356e7 100644 --- a/std/traits.d +++ b/std/traits.d @@ -14,16 +14,19 @@ * $(LREF packageName) * )) * $(TR $(TD Function _traits) $(TD + * $(LREF isFunction) * $(LREF arity) * $(LREF functionAttributes) + * $(LREF hasFunctionAttributes) * $(LREF functionLinkage) * $(LREF FunctionTypeOf) * $(LREF isSafe) * $(LREF isUnsafe) - * $(LREF ParameterDefaultValueTuple) + * $(LREF isFinal) + * $(LREF ParameterDefaults) * $(LREF ParameterIdentifierTuple) * $(LREF ParameterStorageClassTuple) - * $(LREF ParameterTypeTuple) + * $(LREF Parameters) * $(LREF ReturnType) * $(LREF SetFunctionAttributes) * $(LREF variadicFunctionStyle) @@ -34,16 +37,18 @@ * $(LREF classInstanceAlignment) * $(LREF EnumMembers) * $(LREF FieldNameTuple) - * $(LREF FieldTypeTuple) + * $(LREF Fields) * $(LREF hasAliasing) * $(LREF hasElaborateAssign) * $(LREF hasElaborateCopyConstructor) * $(LREF hasElaborateDestructor) * $(LREF hasIndirections) * $(LREF hasMember) + * $(LREF hasStaticMember) * $(LREF hasNested) * $(LREF hasUnsharedAliasing) * $(LREF InterfacesTuple) + * $(LREF isInnerClass) * $(LREF isNested) * $(LREF MemberFunctionsTuple) * $(LREF RepresentationTypeTuple) @@ -54,11 +59,13 @@ * $(TR $(TD Type Conversion) $(TD * $(LREF CommonType) * $(LREF ImplicitConversionTargets) + * $(LREF CopyTypeQualifiers) + * $(LREF CopyConstness) * $(LREF isAssignable) * $(LREF isCovariantWith) * $(LREF isImplicitlyConvertible) * )) - * <!--$(TR $(TD SomethingTypeOf) $(TD + * $(TR $(TD SomethingTypeOf) $(TD * $(LREF BooleanTypeOf) * $(LREF IntegralTypeOf) * $(LREF FloatingPointTypeOf) @@ -72,18 +79,22 @@ * $(LREF StringTypeOf) * $(LREF AssocArrayTypeOf) * $(LREF BuiltinTypeOf) - * ))--> + * )) * $(TR $(TD Categories of types) $(TD + * $(LREF isType) * $(LREF isAggregateType) * $(LREF isArray) * $(LREF isAssociativeArray) + * $(LREF isAutodecodableString) * $(LREF isBasicType) * $(LREF isBoolean) * $(LREF isBuiltinType) + * $(LREF isCopyable) * $(LREF isDynamicArray) * $(LREF isFloatingPoint) * $(LREF isIntegral) * $(LREF isNarrowString) + * $(LREF isConvertibleToString) * $(LREF isNumeric) * $(LREF isPointer) * $(LREF isScalarType) @@ -98,7 +109,7 @@ * $(LREF isAbstractFunction) * $(LREF isCallable) * $(LREF isDelegate) - * $(LREF isExpressionTuple) + * $(LREF isExpressions) * $(LREF isFinalClass) * $(LREF isFinalFunction) * $(LREF isFunctionPointer) @@ -119,26 +130,29 @@ * $(LREF Unqual) * $(LREF Unsigned) * $(LREF ValueType) + * $(LREF Promoted) * )) * $(TR $(TD Misc) $(TD * $(LREF mangledName) * $(LREF Select) * $(LREF select) * )) + * $(TR $(TD User-Defined Attributes) $(TD + * $(LREF hasUDA) + * $(LREF getUDAs) + * $(LREF getSymbolsByUDA) + * )) * ) * ) * - * Macros: - * WIKI = Phobos/StdTraits - * * Copyright: Copyright Digital Mars 2005 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright), - * Tomasz Stachowiak ($(D isExpressionTuple)), - * $(WEB erdani.org, Andrei Alexandrescu), + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright), + * Tomasz Stachowiak ($(D isExpressions)), + * $(HTTP erdani.org, Andrei Alexandrescu), * Shin Fujishiro, - * $(WEB octarineparrot.com, Robert Clipsham), - * $(WEB klickverbot.at, David Nadlinger), + * $(HTTP octarineparrot.com, Robert Clipsham), + * $(HTTP klickverbot.at, David Nadlinger), * Kenji Hara, * Shoichi Kato * Source: $(PHOBOSSRC std/_traits.d) @@ -150,7 +164,7 @@ */ module std.traits; -import std.typetuple; +import std.typetuple; // TypeTuple /////////////////////////////////////////////////////////////////////////////// // Functions @@ -206,7 +220,7 @@ private /* Demangles mstr as FuncAttrs. */ Demangle!uint demangleFunctionAttributes(string mstr) { - enum LOOKUP_ATTRIBUTE = + immutable LOOKUP_ATTRIBUTE = [ 'a': FunctionAttribute.pure_, 'b': FunctionAttribute.nothrow_, @@ -215,7 +229,8 @@ private 'e': FunctionAttribute.trusted, 'f': FunctionAttribute.safe, 'i': FunctionAttribute.nogc, - 'j': FunctionAttribute.return_ + 'j': FunctionAttribute.return_, + 'l': FunctionAttribute.scope_ ]; uint atts = 0; @@ -234,59 +249,80 @@ private return Demangle!uint(atts, mstr); } - alias IntegralTypeList = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong); - alias SignedIntTypeList = TypeTuple!(byte, short, int, long); - alias UnsignedIntTypeList = TypeTuple!(ubyte, ushort, uint, ulong); + static if (is(ucent)) + { + alias CentTypeList = TypeTuple!(cent, ucent); + alias SignedCentTypeList = TypeTuple!(cent); + alias UnsignedCentTypeList = TypeTuple!(ucent); + } + else + { + alias CentTypeList = TypeTuple!(); + alias SignedCentTypeList = TypeTuple!(); + alias UnsignedCentTypeList = TypeTuple!(); + } + + alias IntegralTypeList = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList); + alias SignedIntTypeList = TypeTuple!(byte, short, int, long, SignedCentTypeList); + alias UnsignedIntTypeList = TypeTuple!(ubyte, ushort, uint, ulong, UnsignedCentTypeList); alias FloatingPointTypeList = TypeTuple!(float, double, real); alias ImaginaryTypeList = TypeTuple!(ifloat, idouble, ireal); alias ComplexTypeList = TypeTuple!(cfloat, cdouble, creal); alias NumericTypeList = TypeTuple!(IntegralTypeList, FloatingPointTypeList); alias CharTypeList = TypeTuple!(char, wchar, dchar); } + package { - // Add specific qualifier to the given type T + /// Add specific qualifier to the given type T. template MutableOf(T) { alias MutableOf = T ; } - template InoutOf(T) { alias InoutOf = inout(T) ; } - template ConstOf(T) { alias ConstOf = const(T) ; } - template SharedOf(T) { alias SharedOf = shared(T) ; } - template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); } - template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); } - template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; } +} - unittest - { - static assert(is( MutableOf!int == int)); - static assert(is( InoutOf!int == inout int)); - static assert(is( ConstOf!int == const int)); - static assert(is( SharedOf!int == shared int)); - static assert(is(SharedInoutOf!int == shared inout int)); - static assert(is(SharedConstOf!int == shared const int)); - static assert(is( ImmutableOf!int == immutable int)); - } +/// Add specific qualifier to the given type T. +template InoutOf(T) { alias InoutOf = inout(T) ; } +/// ditto. +template ConstOf(T) { alias ConstOf = const(T) ; } +/// ditto. +template SharedOf(T) { alias SharedOf = shared(T) ; } +/// ditto. +template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); } +/// ditto. +template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); } +/// ditto. +template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; } - // Get qualifier template from the given type T - template QualifierOf(T) - { - static if (is(T == shared(const U), U)) alias QualifierOf = SharedConstOf; - else static if (is(T == const U , U)) alias QualifierOf = ConstOf; - else static if (is(T == shared(inout U), U)) alias QualifierOf = SharedInoutOf; - else static if (is(T == inout U , U)) alias QualifierOf = InoutOf; - else static if (is(T == immutable U , U)) alias QualifierOf = ImmutableOf; - else static if (is(T == shared U , U)) alias QualifierOf = SharedOf; - else alias QualifierOf = MutableOf; - } +@safe unittest +{ + static assert(is( MutableOf!int == int)); + static assert(is( InoutOf!int == inout int)); + static assert(is( ConstOf!int == const int)); + static assert(is( SharedOf!int == shared int)); + static assert(is(SharedInoutOf!int == shared inout int)); + static assert(is(SharedConstOf!int == shared const int)); + static assert(is( ImmutableOf!int == immutable int)); +} - unittest - { - alias Qual1 = QualifierOf!( int); static assert(is(Qual1!long == long)); - alias Qual2 = QualifierOf!( inout int); static assert(is(Qual2!long == inout long)); - alias Qual3 = QualifierOf!( const int); static assert(is(Qual3!long == const long)); - alias Qual4 = QualifierOf!(shared int); static assert(is(Qual4!long == shared long)); - alias Qual5 = QualifierOf!(shared inout int); static assert(is(Qual5!long == shared inout long)); - alias Qual6 = QualifierOf!(shared const int); static assert(is(Qual6!long == shared const long)); - alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long)); - } +// Get qualifier template from the given type T +template QualifierOf(T) +{ + static if (is(T == shared(const U), U)) alias QualifierOf = SharedConstOf; + else static if (is(T == const U , U)) alias QualifierOf = ConstOf; + else static if (is(T == shared(inout U), U)) alias QualifierOf = SharedInoutOf; + else static if (is(T == inout U , U)) alias QualifierOf = InoutOf; + else static if (is(T == immutable U , U)) alias QualifierOf = ImmutableOf; + else static if (is(T == shared U , U)) alias QualifierOf = SharedOf; + else alias QualifierOf = MutableOf; +} + +@safe unittest +{ + alias Qual1 = QualifierOf!( int); static assert(is(Qual1!long == long)); + alias Qual2 = QualifierOf!( inout int); static assert(is(Qual2!long == inout long)); + alias Qual3 = QualifierOf!( const int); static assert(is(Qual3!long == const long)); + alias Qual4 = QualifierOf!(shared int); static assert(is(Qual4!long == shared long)); + alias Qual5 = QualifierOf!(shared inout int); static assert(is(Qual5!long == shared inout long)); + alias Qual6 = QualifierOf!(shared const int); static assert(is(Qual6!long == shared const long)); + alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long)); } version(unittest) @@ -308,7 +344,7 @@ private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__trait */ template packageName(alias T) { - import std.algorithm : startsWith; + import std.algorithm.searching : startsWith; static if (__traits(compiles, parentOf!T)) enum parent = packageName!(parentOf!T); @@ -324,13 +360,13 @@ template packageName(alias T) } /// -unittest +@safe unittest { import std.traits; static assert(packageName!packageName == "std"); } -unittest +@safe unittest { import std.array; @@ -362,7 +398,7 @@ version (none) version(unittest) //Please uncomment me when changing packageName */ template moduleName(alias T) { - import std.algorithm : startsWith; + import std.algorithm.searching : startsWith; static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package"); @@ -380,13 +416,13 @@ template moduleName(alias T) } /// -unittest +@safe unittest { import std.traits; static assert(moduleName!moduleName == "std.traits"); } -unittest +@safe unittest { import std.array; @@ -434,7 +470,7 @@ template fullyQualifiedName(T...) } /// -unittest +@safe unittest { static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName"); } @@ -508,13 +544,13 @@ private template fqnSym(alias T : X!A, alias X, A...) private template fqnSym(alias T) { static if (__traits(compiles, __traits(parent, T))) - enum parentPrefix = fqnSym!(__traits(parent, T)) ~ '.'; + enum parentPrefix = fqnSym!(__traits(parent, T)) ~ "."; else enum parentPrefix = null; static string adjustIdent(string s) { - import std.algorithm : skipOver, findSplit; + import std.algorithm.searching : findSplit, skipOver; if (s.skipOver("package ") || s.skipOver("module ")) return s; @@ -523,7 +559,7 @@ private template fqnSym(alias T) enum fqnSym = parentPrefix ~ adjustIdent(__traits(identifier, T)); } -unittest +@safe unittest { alias fqn = fullyQualifiedName; @@ -580,7 +616,7 @@ private template fqnType(T, string parametersTypeString(T)() @property { - alias parameters = ParameterTypeTuple!(T); + alias parameters = Parameters!(T); alias parameterStC = ParameterStorageClassTuple!(T); enum variadic = variadicFunctionStyle!T; @@ -597,8 +633,10 @@ private template fqnType(T, static if (parameters.length) { - import std.algorithm : map; - import std.range : join, zip; + import std.algorithm.iteration : map; + import std.array : join; + import std.meta : staticMap; + import std.range : zip; string result = join( map!(a => format("%s%s", a[0], a[1]))( @@ -695,8 +733,6 @@ private template fqnType(T, } else static if (isStaticArray!T) { - import std.conv; - enum fqnType = chain!( format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length) ); @@ -753,13 +789,13 @@ private template fqnType(T, enum fqnType = chain!( format("__vector(%s[%s])", fqnType!(V, qualifiers), N) ); - } + } else // In case something is forgotten static assert(0, "Unrecognized type " ~ T.stringof ~ ", can't convert to fully qualified string"); } -unittest +@safe unittest { import std.format : format; alias fqn = fullyQualifiedName; @@ -776,6 +812,15 @@ unittest static assert(fqn!(string) == "string"); static assert(fqn!(wstring) == "wstring"); static assert(fqn!(dstring) == "dstring"); + static assert(fqn!(void) == "void"); + static assert(fqn!(const(void)) == "const(void)"); + static assert(fqn!(shared(void)) == "shared(void)"); + static assert(fqn!(shared const(void)) == "const(shared(void))"); + static assert(fqn!(shared inout(void)) == "inout(shared(void))"); + static assert(fqn!(shared inout const(void)) == "const(shared(void))"); + static assert(fqn!(inout(void)) == "inout(void)"); + static assert(fqn!(inout const(void)) == "const(void)"); + static assert(fqn!(immutable(void)) == "immutable(void)"); // Basic qualified name static assert(fqn!(Inner) == inner_name); @@ -838,13 +883,13 @@ template ReturnType(func...) } /// -unittest +@safe unittest { int foo(); ReturnType!foo x; // x is declared as int } -unittest +@safe unittest { struct G { @@ -887,24 +932,29 @@ Get, as a tuple, the types of the parameters to a function, a pointer to function, a delegate, a struct with an $(D opCall), a pointer to a struct with an $(D opCall), or a class with an $(D opCall). */ -template ParameterTypeTuple(func...) +template Parameters(func...) if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!func P == function)) - alias ParameterTypeTuple = P; + alias Parameters = P; else static assert(0, "argument has no parameters"); } /// -unittest +@safe unittest { int foo(int, long); - void bar(ParameterTypeTuple!foo); // declares void bar(int, long); - void abc(ParameterTypeTuple!foo[1]); // declares void abc(long); + void bar(Parameters!foo); // declares void bar(int, long); + void abc(Parameters!foo[1]); // declares void abc(long); } -unittest +/** + * Alternate name for $(LREF Parameters), kept for legacy compatibility. + */ +alias ParameterTypeTuple = Parameters; + +@safe unittest { int foo(int i, bool b) { return 0; } static assert(is(ParameterTypeTuple!foo == TypeTuple!(int, bool))); @@ -935,17 +985,18 @@ arity is undefined for variadic functions. template arity(alias func) if ( isCallable!func && variadicFunctionStyle!func == Variadic.no ) { - enum size_t arity = ParameterTypeTuple!func.length; + enum size_t arity = Parameters!func.length; } /// -unittest { +@safe unittest +{ void foo(){} - static assert(arity!foo==0); + static assert(arity!foo == 0); void bar(uint){} - static assert(arity!bar==1); + static assert(arity!bar == 1); void variadicFoo(uint...){} - static assert(__traits(compiles,arity!variadicFoo)==false); + static assert(!__traits(compiles, arity!variadicFoo)); } /** @@ -959,11 +1010,11 @@ enum ParameterStorageClass : uint * class. */ none = 0, - scope_ = 0b000_1, /// ditto - out_ = 0b001_0, /// ditto - ref_ = 0b010_0, /// ditto - lazy_ = 0b100_0, /// ditto - return_ = 0b1000_0, /// ditto + scope_ = 1, /// ditto + out_ = 2, /// ditto + ref_ = 4, /// ditto + lazy_ = 8, /// ditto + return_ = 0x10, /// ditto } /// ditto @@ -976,7 +1027,7 @@ template ParameterStorageClassTuple(func...) * TypeFuncion: * CallConvention FuncAttrs Arguments ArgClose Type */ - alias Params = ParameterTypeTuple!Func; + alias Params = Parameters!Func; // chop off CallConvention and FuncAttrs enum margs = demangleFunctionAttributes(mangledName!Func[1 .. $]).rest; @@ -1006,7 +1057,7 @@ template ParameterStorageClassTuple(func...) } /// -unittest +@safe unittest { alias STC = ParameterStorageClass; // shorten the enum name @@ -1020,14 +1071,14 @@ unittest static assert(pstc[2] == STC.none); } -unittest +@safe unittest { alias STC = ParameterStorageClass; void noparam() {} static assert(ParameterStorageClassTuple!noparam.length == 0); - void test(scope int, ref int, out int, lazy int, int, return ref int) { } + ref int test(scope int*, ref int, out int, lazy int, int, return ref int i) { return i; } alias test_pstc = ParameterStorageClassTuple!test; static assert(test_pstc.length == 6); static assert(test_pstc[0] == STC.scope_); @@ -1061,7 +1112,7 @@ unittest static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none); } -unittest +@safe unittest { // Bugzilla 14253 static struct Foo { @@ -1084,9 +1135,9 @@ template ParameterIdentifierTuple(func...) { static if (!isFunctionPointer!func && !isDelegate!func // Unnamed parameters yield CT error. - && is(typeof(__traits(identifier, PT[i..i+1]))x)) + && is(typeof(__traits(identifier, PT[i .. i+1])))) { - enum Get = __traits(identifier, PT[i..i+1]); + enum Get = __traits(identifier, PT[i .. i+1]); } else { @@ -1115,13 +1166,13 @@ template ParameterIdentifierTuple(func...) } /// -unittest +@safe unittest { int foo(int num, string name, int); static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); } -unittest +@safe unittest { alias PIT = ParameterIdentifierTuple; @@ -1162,18 +1213,23 @@ unittest Get, as a tuple, the default value of the parameters to a function symbol. If a parameter doesn't have the default value, $(D void) is returned instead. */ -template ParameterDefaultValueTuple(func...) +template ParameterDefaults(func...) if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!(func[0]) PT == __parameters)) { template Get(size_t i) { - enum ParamName = ParameterIdentifierTuple!(func[0])[i]; - static if (ParamName.length) - enum get = (PT[i..i+1]) => mixin(ParamName); - else // Unnamed parameter - enum get = (PT[i..i+1] __args) => __args[0]; + // workaround scope escape check, see + // https://issues.dlang.org/show_bug.cgi?id=16582 + // should use return scope once available + enum get = (PT[i .. i+1] __args) @trusted + { + // If __args[0] is lazy, we force it to be evaluated like this. + PT[i] __pd_value = __args[0]; + PT[i]* __pd_val = &__pd_value; // workaround Bugzilla 16582 + return *__pd_val; + }; static if (is(typeof(get()))) enum Get = get(); else @@ -1181,13 +1237,6 @@ template ParameterDefaultValueTuple(func...) // If default arg doesn't exist, returns void instead. } } - else static if (is(FunctionTypeOf!func PT == __parameters)) - { - template Get(size_t i) - { - enum Get = ""; - } - } else { static assert(0, func[0].stringof ~ "is not a function"); @@ -1205,19 +1254,25 @@ template ParameterDefaultValueTuple(func...) alias Impl = TypeTuple!(Get!i, Impl!(i+1)); } - alias ParameterDefaultValueTuple = Impl!(); + alias ParameterDefaults = Impl!(); } /// -unittest +@safe unittest { - int foo(int num, string name = "hello", int[] = [1,2,3]); - static assert(is(ParameterDefaultValueTuple!foo[0] == void)); - static assert( ParameterDefaultValueTuple!foo[1] == "hello"); - static assert( ParameterDefaultValueTuple!foo[2] == [1,2,3]); + int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); + static assert(is(ParameterDefaults!foo[0] == void)); + static assert( ParameterDefaults!foo[1] == "hello"); + static assert( ParameterDefaults!foo[2] == [1,2,3]); + static assert( ParameterDefaults!foo[3] == 0); } -unittest +/** + * Alternate name for $(LREF ParameterDefaults), kept for legacy compatibility. + */ +alias ParameterDefaultValueTuple = ParameterDefaults; + +@safe unittest { alias PDVT = ParameterDefaultValueTuple; @@ -1246,14 +1301,19 @@ unittest static immutable Colour white = Colour(255,255,255,255); } - void bug8106(Colour c = Colour.white){} + void bug8106(Colour c = Colour.white) {} //pragma(msg, PDVT!bug8106); static assert(PDVT!bug8106[0] == Colour.white); + void bug16582(scope int* val = null) {} + static assert(PDVT!bug16582[0] is null); } /** -Returns the attributes attached to a function $(D func). +Returns the FunctionAttribute mask for function $(D func). + +See_Also: + $(LREF hasFunctionAttributes) */ enum FunctionAttribute : uint { @@ -1274,6 +1334,7 @@ enum FunctionAttribute : uint inout_ = 1 << 10, /// ditto shared_ = 1 << 11, /// ditto return_ = 1 << 12, /// ditto + scope_ = 1 << 13, /// ditto } /// ditto @@ -1289,7 +1350,7 @@ template functionAttributes(func...) } /// -unittest +@safe unittest { import std.traits : functionAttributes, FunctionAttribute; @@ -1304,7 +1365,7 @@ unittest static assert(!(functionAttributes!func & FA.trusted)); // not @trusted } -unittest +@system unittest { alias FA = FunctionAttribute; @@ -1344,8 +1405,8 @@ unittest static assert(functionAttributes!(S.sharedF) == (FA.shared_ | FA.system)); static assert(functionAttributes!(typeof(S.sharedF)) == (FA.shared_ | FA.system)); - static assert(functionAttributes!(S.refF) == (FA.ref_ | FA.system)); - static assert(functionAttributes!(typeof(S.refF)) == (FA.ref_ | FA.system)); + static assert(functionAttributes!(S.refF) == (FA.ref_ | FA.system | FA.return_)); + static assert(functionAttributes!(typeof(S.refF)) == (FA.ref_ | FA.system | FA.return_)); static assert(functionAttributes!(S.propertyF) == (FA.property | FA.system)); static assert(functionAttributes!(typeof(&S.propertyF)) == (FA.property | FA.system)); @@ -1368,10 +1429,10 @@ unittest static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system)); static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system)); - int pure_nothrow() nothrow pure { return 0; } - void safe_nothrow() @safe nothrow { } - static ref int static_ref_property() @property { return *(new int); } - ref int ref_property() @property { return *(new int); } + int pure_nothrow() nothrow pure; + void safe_nothrow() @safe nothrow; + static ref int static_ref_property() @property; + ref int ref_property() @property; static assert(functionAttributes!(pure_nothrow) == (FA.pure_ | FA.nothrow_ | FA.system)); static assert(functionAttributes!(typeof(pure_nothrow)) == (FA.pure_ | FA.nothrow_ | FA.system)); @@ -1434,6 +1495,7 @@ private FunctionAttribute extractAttribFlags(Attribs...)() case "inout": res |= inout_; break; case "shared": res |= shared_; break; case "return": res |= return_; break; + case "scope": res |= scope_; break; default: assert(0, attrib); } } @@ -1441,19 +1503,212 @@ private FunctionAttribute extractAttribFlags(Attribs...)() return res; } +/** +Checks whether a function has the given attributes attached. + +Params: + args = Function to check, followed by a + variadic number of function attributes as strings + +Returns: + `true`, if the function has the list of attributes attached and `false` otherwise. + +See_Also: + $(LREF functionAttributes) +*/ +template hasFunctionAttributes(args...) + if (args.length > 0 && isCallable!(args[0]) + && allSatisfy!(isSomeString, typeof(args[1 .. $]))) +{ + enum bool hasFunctionAttributes = { + import std.algorithm.searching : canFind; + import std.range : only; + enum funcAttribs = only(__traits(getFunctionAttributes, args[0])); + foreach (attribute; args[1 .. $]) + { + if (!funcAttribs.canFind(attribute)) + return false; + } + return true; + }(); +} + +/// +unittest +{ + real func(real x) pure nothrow @safe; + static assert(hasFunctionAttributes!(func, "@safe", "pure")); + static assert(!hasFunctionAttributes!(func, "@trusted")); + + // for templates attributes are automatically inferred + bool myFunc(T)(T b) + { + return !b; + } + static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow")); + static assert(!hasFunctionAttributes!(myFunc!bool, "shared")); +} + +unittest +{ + struct S + { + int noF(); + int constF() const; + int immutableF() immutable; + int inoutF() inout; + int sharedF() shared; + + ref int refF() return; + int propertyF() @property; + int nothrowF() nothrow; + int nogcF() @nogc; + + int systemF() @system; + int trustedF() @trusted; + int safeF() @safe; + + int pureF() pure; + } + + // true if no args passed + static assert(hasFunctionAttributes!(S.noF)); + + static assert(hasFunctionAttributes!(S.noF, "@system")); + static assert(hasFunctionAttributes!(typeof(S.noF), "@system")); + static assert(!hasFunctionAttributes!(S.noF, "@system", "pure")); + + static assert(hasFunctionAttributes!(S.constF, "const", "@system")); + static assert(hasFunctionAttributes!(typeof(S.constF), "const", "@system")); + static assert(!hasFunctionAttributes!(S.constF, "const", "@system", "@nogc")); + + static assert(hasFunctionAttributes!(S.immutableF, "immutable", "@system")); + static assert(hasFunctionAttributes!(typeof(S.immutableF), "immutable", "@system")); + static assert(!hasFunctionAttributes!(S.immutableF, "immutable", "@system", "pure")); + + static assert(hasFunctionAttributes!(S.inoutF, "inout", "@system")); + static assert(hasFunctionAttributes!(typeof(S.inoutF), "inout", "@system")); + static assert(!hasFunctionAttributes!(S.inoutF, "inout", "@system", "pure")); + + static assert(hasFunctionAttributes!(S.sharedF, "shared", "@system")); + static assert(hasFunctionAttributes!(typeof(S.sharedF), "shared", "@system")); + static assert(!hasFunctionAttributes!(S.sharedF, "shared", "@system", "@trusted")); + + static assert(hasFunctionAttributes!(S.refF, "ref", "@system", "return")); + static assert(hasFunctionAttributes!(typeof(S.refF), "ref", "@system", "return")); + static assert(!hasFunctionAttributes!(S.refF, "ref", "@system", "return", "pure")); + + static assert(hasFunctionAttributes!(S.propertyF, "@property", "@system")); + static assert(hasFunctionAttributes!(typeof(&S.propertyF), "@property", "@system")); + static assert(!hasFunctionAttributes!(S.propertyF, "@property", "@system", "ref")); + + static assert(hasFunctionAttributes!(S.nothrowF, "nothrow", "@system")); + static assert(hasFunctionAttributes!(typeof(S.nothrowF), "nothrow", "@system")); + static assert(!hasFunctionAttributes!(S.nothrowF, "nothrow", "@system", "@trusted")); + + static assert(hasFunctionAttributes!(S.nogcF, "@nogc", "@system")); + static assert(hasFunctionAttributes!(typeof(S.nogcF), "@nogc", "@system")); + static assert(!hasFunctionAttributes!(S.nogcF, "@nogc", "@system", "ref")); + + static assert(hasFunctionAttributes!(S.systemF, "@system")); + static assert(hasFunctionAttributes!(typeof(S.systemF), "@system")); + static assert(!hasFunctionAttributes!(S.systemF, "@system", "ref")); + + static assert(hasFunctionAttributes!(S.trustedF, "@trusted")); + static assert(hasFunctionAttributes!(typeof(S.trustedF), "@trusted")); + static assert(!hasFunctionAttributes!(S.trustedF, "@trusted", "@safe")); + + static assert(hasFunctionAttributes!(S.safeF, "@safe")); + static assert(hasFunctionAttributes!(typeof(S.safeF), "@safe")); + static assert(!hasFunctionAttributes!(S.safeF, "@safe", "nothrow")); + + static assert(hasFunctionAttributes!(S.pureF, "pure", "@system")); + static assert(hasFunctionAttributes!(typeof(S.pureF), "pure", "@system")); + static assert(!hasFunctionAttributes!(S.pureF, "pure", "@system", "ref")); + + int pure_nothrow() nothrow pure { return 0; } + void safe_nothrow() @safe nothrow { } + static ref int static_ref_property() @property { return *(new int); } + ref int ref_property() @property { return *(new int); } + + static assert(hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe")); + static assert(hasFunctionAttributes!(typeof(pure_nothrow), "pure", "nothrow", "@safe")); + static assert(!hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe", "@trusted")); + + static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow")); + static assert(hasFunctionAttributes!(typeof(safe_nothrow), "@safe", "nothrow")); + static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure")); + static assert(!hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure", "@trusted")); + + static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe")); + static assert(hasFunctionAttributes!(typeof(&static_ref_property), "@property", "ref", "@safe")); + static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow")); + static assert(!hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow", "@nogc")); + + static assert(hasFunctionAttributes!(ref_property, "@property", "ref", "@safe")); + static assert(hasFunctionAttributes!(typeof(&ref_property), "@property", "ref", "@safe")); + static assert(!hasFunctionAttributes!(ref_property, "@property", "ref", "@safe", "@nogc")); + + struct S2 + { + int pure_const() const pure { return 0; } + int pure_sharedconst() const shared pure { return 0; } + } + + static assert(hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system")); + static assert(hasFunctionAttributes!(typeof(S2.pure_const), "const", "pure", "@system")); + static assert(!hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system", "ref")); + + static assert(hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system")); + static assert(hasFunctionAttributes!(typeof(S2.pure_sharedconst), "const", "shared", "pure", "@system")); + static assert(!hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system", "@nogc")); + + static assert(hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe")); + static assert(hasFunctionAttributes!(typeof((int a) { }), "pure", "nothrow", "@nogc", "@safe")); + static assert(!hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe", "ref")); + + auto safeDel = delegate() @safe { }; + static assert(hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe")); + static assert(hasFunctionAttributes!(typeof(safeDel), "pure", "nothrow", "@nogc", "@safe")); + static assert(!hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe", "@system")); + + auto trustedDel = delegate() @trusted { }; + static assert(hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted")); + static assert(hasFunctionAttributes!(typeof(trustedDel), "pure", "nothrow", "@nogc", "@trusted")); + static assert(!hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted", "ref")); + + auto systemDel = delegate() @system { }; + static assert(hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system")); + static assert(hasFunctionAttributes!(typeof(systemDel), "pure", "nothrow", "@nogc", "@system")); + static assert(!hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system", "@property")); + + + // call functions to make CodeCov happy + { + assert(pure_nothrow == 0); + safe_nothrow; + assert(static_ref_property == 0); + assert(ref_property == 0); + assert(S2().pure_const == 0); + assert((shared S2()).pure_sharedconst == 0); + cast(void) safeDel; + cast(void) trustedDel; + cast(void) systemDel; + } +} /** $(D true) if $(D func) is $(D @safe) or $(D @trusted). */ template isSafe(alias func) - if(isCallable!func) + if (isCallable!func) { enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 || (functionAttributes!func & FunctionAttribute.trusted) != 0; } /// -unittest +@safe unittest { @safe int add(int a, int b) {return a+b;} @trusted int sub(int a, int b) {return a-b;} @@ -1465,7 +1720,7 @@ unittest } -unittest +@safe unittest { //Member functions interface Set @@ -1530,7 +1785,7 @@ template isUnsafe(alias func) } /// -unittest +@safe unittest { @safe int add(int a, int b) {return a+b;} @trusted int sub(int a, int b) {return a-b;} @@ -1541,7 +1796,7 @@ unittest static assert( isUnsafe!mul); } -unittest +@safe unittest { //Member functions interface Set @@ -1597,69 +1852,6 @@ unittest } -/** -$(RED Deprecated. It's badly named and provides redundant functionality. It was -also badly broken prior to 2.060 (bug# 8362), so any code which uses it -probably needs to be changed anyway. Please use $(D allSatisfy(isSafe, ...)) -instead. This will be removed in June 2015.) - -$(D true) all functions are $(D isSafe). - -Example -------------- -@safe int add(int a, int b) {return a+b;} -@trusted int sub(int a, int b) {return a-b;} -@system int mul(int a, int b) {return a*b;} - -static assert( areAllSafe!(add, add)); -static assert( areAllSafe!(add, sub)); -static assert(!areAllSafe!(sub, mul)); -------------- -*/ -deprecated("Please use allSatisfy(isSafe, ...) instead.") -template areAllSafe(funcs...) - if (funcs.length > 0) -{ - static if (funcs.length == 1) - { - enum areAllSafe = isSafe!(funcs[0]); - } - else static if (isSafe!(funcs[0])) - { - enum areAllSafe = areAllSafe!(funcs[1..$]); - } - else - { - enum areAllSafe = false; - } -} - -// Verify Example -deprecated unittest -{ - @safe int add(int a, int b) {return a+b;} - @trusted int sub(int a, int b) {return a-b;} - @system int mul(int a, int b) {return a*b;} - - static assert( areAllSafe!(add, add)); - static assert( areAllSafe!(add, sub)); - static assert(!areAllSafe!(sub, mul)); -} - -deprecated unittest -{ - interface Set - { - int systemF() @system; - int trustedF() @trusted; - int safeF() @safe; - } - static assert( areAllSafe!((int a){}, Set.safeF)); - static assert( areAllSafe!((int a){}, Set.safeF, Set.trustedF)); - static assert(!areAllSafe!(Set.trustedF, Set.systemF)); -} - - /** Returns the calling convention of function as a string. */ @@ -1679,25 +1871,23 @@ template functionLinkage(func...) } /// -unittest +@safe unittest { - import std.stdio : writeln, printf; + extern(D) void Dfunc() {} + extern(C) void Cfunc() {} + static assert(functionLinkage!Dfunc == "D"); + static assert(functionLinkage!Cfunc == "C"); - string a = functionLinkage!(writeln!(string, int)); - assert(a == "D"); // extern(D) + string a = functionLinkage!Dfunc; + assert(a == "D"); - auto fp = &printf; + auto fp = &Cfunc; string b = functionLinkage!fp; - assert(b == "C"); // extern(C) + assert(b == "C"); } -unittest +@safe unittest { - extern(D) void Dfunc() {} - extern(C) void Cfunc() {} - static assert(functionLinkage!Dfunc == "D"); - static assert(functionLinkage!Cfunc == "C"); - interface Test { void const_func() const; @@ -1744,7 +1934,7 @@ template variadicFunctionStyle(func...) } /// -unittest +@safe unittest { void func() {} static assert(variadicFunctionStyle!func == Variadic.no); @@ -1753,7 +1943,7 @@ unittest static assert(variadicFunctionStyle!printf == Variadic.c); } -unittest +@safe unittest { import core.vararg; @@ -1813,7 +2003,7 @@ template FunctionTypeOf(func...) } /// -unittest +@safe unittest { class C { @@ -1823,11 +2013,11 @@ unittest static assert(is( FunctionTypeOf!(C.value) == function )); } -unittest +@system unittest { - int test(int a) { return 0; } - int propGet() @property { return 0; } - int propSet(int a) @property { return 0; } + int test(int a); + int propGet() @property; + int propSet(int a) @property; int function(int) test_fp; int delegate(int) test_dg; static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) )); @@ -1891,13 +2081,13 @@ template SetFunctionAttributes(T, string linkage, uint attrs) if (isFunctionPointer!T || isDelegate!T) { mixin({ - import std.algorithm : canFind; + import std.algorithm.searching : canFind; static assert(!(attrs & FunctionAttribute.trusted) || !(attrs & FunctionAttribute.safe), "Cannot have a function/delegate that is both trusted and safe."); - enum linkages = ["D", "C", "Windows", "Pascal", "C++", "System"]; + static immutable linkages = ["D", "C", "Windows", "Pascal", "C++", "System"]; static assert(canFind(linkages, linkage), "Invalid linkage '" ~ linkage ~ "', must be one of " ~ linkages.stringof ~ "."); @@ -1918,8 +2108,8 @@ template SetFunctionAttributes(T, string linkage, uint attrs) result ~= "("; - static if (ParameterTypeTuple!T.length > 0) - result ~= "ParameterTypeTuple!T"; + static if (Parameters!T.length > 0) + result ~= "Parameters!T"; enum varStyle = variadicFunctionStyle!T; static if (varStyle == Variadic.c) @@ -1972,7 +2162,7 @@ template SetFunctionAttributes(T, string linkage, uint attrs) } /// -unittest +@safe unittest { alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T); @@ -1993,9 +2183,9 @@ version (unittest) extern(D) int dstyle(...); extern(D) int typesafe(int[]...); } -unittest +@safe unittest { - import std.algorithm : reduce; + import std.algorithm.iteration : reduce; alias FA = FunctionAttribute; foreach (BaseT; TypeTuple!(typeof(&sc), typeof(&novar), typeof(&cstyle), @@ -2027,7 +2217,8 @@ unittest // Add all known attributes, excluding conflicting ones. enum allAttrs = reduce!"a | b"([EnumMembers!FA]) - & ~FA.safe & ~FA.property & ~FA.const_ & ~FA.immutable_ & ~FA.inout_ & ~FA.shared_ & ~FA.system & ~FA.return_; + & ~FA.safe & ~FA.property & ~FA.const_ & ~FA.immutable_ & ~FA.inout_ + & ~FA.shared_ & ~FA.system & ~FA.return_ & ~FA.scope_; alias T2 = SetFunctionAttributes!(T1, functionLinkage!T, allAttrs); static assert(functionAttributes!T2 == allAttrs); @@ -2044,18 +2235,72 @@ unittest // Aggregate Types //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// +/** +Determines whether `T` is a class nested inside another class +and that `T.outer` is the implicit reference to the outer class +(i.e. `outer` has not been used as a field or method name) + +Params: + T = type to test + +Returns: +`true` if `T` is a class nested inside another, with the conditions described above; +`false` otherwise +*/ +template isInnerClass(T) + if (is(T == class)) +{ + import std.meta : staticIndexOf; + + static if (is(typeof(T.outer))) + enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) + && (staticIndexOf!(__traits(allMembers, T), "outer") == -1); + else + enum isInnerClass = false; +} + +/// +@safe unittest +{ + class C + { + int outer; + } + static assert(!isInnerClass!C); + + class Outer1 + { + class Inner1 { } + class Inner2 + { + int outer; + } + } + static assert(isInnerClass!(Outer1.Inner1)); + static assert(!isInnerClass!(Outer1.Inner2)); + + static class Outer2 + { + static class Inner + { + int outer; + } + } + static assert(!isInnerClass!(Outer2.Inner)); +} + /** Determines whether $(D T) has its own context pointer. $(D T) must be either $(D class), $(D struct), or $(D union). */ template isNested(T) - if(is(T == class) || is(T == struct) || is(T == union)) + if (is(T == class) || is(T == struct) || is(T == union)) { enum isNested = __traits(isNested, T); } /// -unittest +@safe unittest { static struct S { } static assert(!isNested!S); @@ -2071,17 +2316,18 @@ have a context pointer. */ template hasNested(T) { - static if(isStaticArray!T && T.length) + import std.meta : anySatisfy; + static if (isStaticArray!T && T.length) enum hasNested = hasNested!(typeof(T.init[0])); - else static if(is(T == class) || is(T == struct) || is(T == union)) + else static if (is(T == class) || is(T == struct) || is(T == union)) enum hasNested = isNested!T || - anySatisfy!(.hasNested, FieldTypeTuple!T); + anySatisfy!(.hasNested, Fields!T); else enum hasNested = false; } /// -unittest +@safe unittest { static struct S { } @@ -2092,7 +2338,7 @@ unittest static assert(hasNested!(NS[2])); } -unittest +@safe unittest { static assert(!__traits(compiles, isNested!int)); static assert(!hasNested!int); @@ -2145,31 +2391,36 @@ unittest /*** - * Get as a typetuple the types of the fields of a struct, class, or union. + * Get as a tuple the types of the fields of a struct, class, or union. * This consists of the fields that take up memory space, * excluding the hidden fields like the virtual function * table pointer or a context pointer for nested types. - * If $(D T) isn't a struct, class, or union returns typetuple + * If $(D T) isn't a struct, class, or union returns a tuple * with one element $(D T). */ -template FieldTypeTuple(T) +template Fields(T) { static if (is(T == struct) || is(T == union)) - alias FieldTypeTuple = typeof(T.tupleof[0 .. $ - isNested!T]); + alias Fields = typeof(T.tupleof[0 .. $ - isNested!T]); else static if (is(T == class)) - alias FieldTypeTuple = typeof(T.tupleof); + alias Fields = typeof(T.tupleof); else - alias FieldTypeTuple = TypeTuple!T; + alias Fields = TypeTuple!T; } /// -unittest +@safe unittest { struct S { int x; float y; } - static assert(is(FieldTypeTuple!S == TypeTuple!(int, float))); + static assert(is(Fields!S == TypeTuple!(int, float))); } -unittest +/** + * Alternate name for $(LREF Fields), kept for legacy compatibility. + */ +alias FieldTypeTuple = Fields; + +@safe unittest { static assert(is(FieldTypeTuple!int == TypeTuple!int)); @@ -2204,6 +2455,7 @@ private enum NameOf(alias T) = T.stringof; */ template FieldNameTuple(T) { + import std.meta : staticMap; static if (is(T == struct) || is(T == union)) alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]); else static if (is(T == class)) @@ -2213,14 +2465,14 @@ template FieldNameTuple(T) } /// -unittest +@safe unittest { struct S { int x; float y; } static assert(FieldNameTuple!S == TypeTuple!("x", "y")); static assert(FieldNameTuple!int == TypeTuple!""); } -unittest +@safe unittest { static assert(FieldNameTuple!int == TypeTuple!""); @@ -2288,7 +2540,7 @@ template RepresentationTypeTuple(T) } /// -unittest +@safe unittest { struct S1 { int a; float b; } struct S2 { char[] a; union { S1 b; S1 * c; } } @@ -2298,7 +2550,7 @@ unittest && is(R[2] == float) && is(R[3] == S1*)); } -unittest +@safe unittest { alias S1 = RepresentationTypeTuple!int; static assert(is(S1 == TypeTuple!int)); @@ -2362,7 +2614,7 @@ private template hasRawAliasing(T...) } /// -unittest +@safe unittest { // simple types static assert(!hasRawAliasing!int); @@ -2379,7 +2631,7 @@ unittest static assert(!hasRawAliasing!S2); } -unittest +@safe unittest { // struct with a pointer member struct S3 { int a; double * b; } @@ -2458,7 +2710,7 @@ private template hasRawUnsharedAliasing(T...) } /// -unittest +@safe unittest { // simple types static assert(!hasRawUnsharedAliasing!int); @@ -2482,7 +2734,7 @@ unittest static assert(!hasRawUnsharedAliasing!S4); } -unittest +@safe unittest { // struct with a pointer member struct S3 { int a; double * b; } @@ -2664,6 +2916,7 @@ $(LI a delegate.)) */ template hasAliasing(T...) { + import std.meta : anySatisfy; import std.typecons : Rebindable; static if (T.length && is(T[0] : Rebindable!R, R)) @@ -2684,7 +2937,7 @@ template hasAliasing(T...) } /// -unittest +@safe unittest { struct S1 { int a; Object b; } struct S2 { string a; } @@ -2696,7 +2949,7 @@ unittest static assert(!hasAliasing!S4); } -unittest +@safe unittest { static assert( hasAliasing!(uint[uint])); static assert(!hasAliasing!(immutable(uint[uint]))); @@ -2767,6 +3020,7 @@ $(LI an associative array.) $(LI a delegate.)) */ template hasIndirections(T) { + import std.meta : anySatisfy; static if (is(T == struct) || is(T == union)) enum hasIndirections = anySatisfy!(.hasIndirections, FieldTypeTuple!T); else static if (isStaticArray!T && is(T : E[N], E, size_t N)) @@ -2779,7 +3033,7 @@ template hasIndirections(T) } /// -unittest +@safe unittest { static assert( hasIndirections!(int[string])); static assert( hasIndirections!(void delegate())); @@ -2792,7 +3046,7 @@ unittest static assert(!hasIndirections!(byte[1])); } -unittest +@safe unittest { // void static array hides actual type of bits, so "may have indirections". static assert( hasIndirections!(void[1])); @@ -2851,7 +3105,7 @@ unittest static assert( hasIndirections!S26); } -unittest //12000 +@safe unittest //12000 { static struct S(T) { @@ -2877,6 +3131,7 @@ immutable or shared.) $(LI a delegate that is not shared.)) template hasUnsharedAliasing(T...) { + import std.meta : anySatisfy; import std.typecons : Rebindable; static if (!T.length) @@ -2908,7 +3163,7 @@ template hasUnsharedAliasing(T...) } /// -unittest +@safe unittest { struct S1 { int a; Object b; } struct S2 { string a; } @@ -2927,7 +3182,7 @@ unittest static assert(!hasUnsharedAliasing!S7); } -unittest +@safe unittest { /* Issue 6642 */ import std.typecons : Rebindable; @@ -2943,7 +3198,7 @@ unittest static assert(!hasUnsharedAliasing!(void delegate() shared const)); } -unittest +@safe unittest { import std.typecons : Rebindable; static assert( hasUnsharedAliasing!(const(void delegate()))); @@ -3052,11 +3307,12 @@ unittest */ template hasElaborateCopyConstructor(S) { - static if(isStaticArray!S && S.length) + import std.meta : anySatisfy; + static if (isStaticArray!S && S.length) { enum bool hasElaborateCopyConstructor = hasElaborateCopyConstructor!(typeof(S.init[0])); } - else static if(is(S == struct)) + else static if (is(S == struct)) { enum hasElaborateCopyConstructor = hasMember!(S, "__postblit") || anySatisfy!(.hasElaborateCopyConstructor, FieldTypeTuple!S); @@ -3068,7 +3324,7 @@ template hasElaborateCopyConstructor(S) } /// -unittest +@safe unittest { static assert(!hasElaborateCopyConstructor!int); @@ -3108,11 +3364,12 @@ unittest */ template hasElaborateAssign(S) { - static if(isStaticArray!S && S.length) + import std.meta : anySatisfy; + static if (isStaticArray!S && S.length) { enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0])); } - else static if(is(S == struct)) + else static if (is(S == struct)) { enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || is(typeof(S.init.opAssign(lvalueOf!S))) || @@ -3125,7 +3382,7 @@ template hasElaborateAssign(S) } /// -unittest +@safe unittest { static assert(!hasElaborateAssign!int); @@ -3143,7 +3400,7 @@ unittest static assert(!hasElaborateAssign!(S3[0])); } -unittest +@safe unittest { static struct S { void opAssign(S) {} } static struct S4 @@ -3178,8 +3435,8 @@ unittest static struct SS8 { S8 s; } static struct SS9 { S9 s; } static assert( hasElaborateAssign!SS6); - static assert( hasElaborateAssign!SS7); - static assert( hasElaborateAssign!SS8); + static assert(!hasElaborateAssign!SS7); + static assert(!hasElaborateAssign!SS8); static assert( hasElaborateAssign!SS9); } @@ -3194,11 +3451,12 @@ unittest */ template hasElaborateDestructor(S) { - static if(isStaticArray!S && S.length) + import std.meta : anySatisfy; + static if (isStaticArray!S && S.length) { enum bool hasElaborateDestructor = hasElaborateDestructor!(typeof(S.init[0])); } - else static if(is(S == struct)) + else static if (is(S == struct)) { enum hasElaborateDestructor = hasMember!(S, "__dtor") || anySatisfy!(.hasElaborateDestructor, FieldTypeTuple!S); @@ -3210,7 +3468,7 @@ template hasElaborateDestructor(S) } /// -unittest +@safe unittest { static assert(!hasElaborateDestructor!int); @@ -3233,28 +3491,16 @@ unittest static assert( hasElaborateDestructor!S7); } -alias Identity(alias A) = A; +package alias Identity(alias A) = A; /** Yields $(D true) if and only if $(D T) is an aggregate that defines a symbol called $(D name). */ -template hasMember(T, string name) -{ - static if (is(T == struct) || is(T == class) || is(T == union) || is(T == interface)) - { - enum bool hasMember = - staticIndexOf!(name, __traits(allMembers, T)) != -1 || - __traits(compiles, { mixin("alias Sym = Identity!(T."~name~");"); }); - } - else - { - enum bool hasMember = false; - } -} +enum hasMember(T, string name) = __traits(hasMember, T, name); /// -unittest +@safe unittest { static assert(!hasMember!(int, "blah")); struct S1 { int blah; } @@ -3267,7 +3513,7 @@ unittest static assert(hasMember!(C2, "blah")); } -unittest +@safe unittest { // 8321 struct S { @@ -3299,11 +3545,196 @@ unittest static assert(hasMember!(R2!S, "T")); } -/** -Retrieves the members of an enumerated type $(D enum E). - -Params: - E = An enumerated type. $(D E) may have duplicated values. +@safe unittest +{ + static struct S + { + void opDispatch(string n, A)(A dummy) {} + } + static assert(hasMember!(S, "foo")); +} + +/** + * Whether the symbol represented by the string, member, exists and is a static member of T. + * + * Params: + * T = Type containing symbol $(D member). + * member = Name of symbol to test that resides in $(D T). + * + * Returns: + * $(D true) iff $(D member) exists and is static. + */ +template hasStaticMember(T, string member) +{ + static if (__traits(hasMember, T, member)) + { + import std.meta : Alias; + alias sym = Alias!(__traits(getMember, T, member)); + + static if (__traits(getOverloads, T, member).length == 0) + enum bool hasStaticMember = __traits(compiles, &sym); + else + enum bool hasStaticMember = __traits(isStaticFunction, sym); + } + else + { + enum bool hasStaticMember = false; + } +} + +/// +@safe unittest +{ + static struct S + { + static void sf() {} + void f() {} + + static int si; + int i; + } + + static assert( hasStaticMember!(S, "sf")); + static assert(!hasStaticMember!(S, "f")); + + static assert( hasStaticMember!(S, "si")); + static assert(!hasStaticMember!(S, "i")); + + static assert(!hasStaticMember!(S, "hello")); +} + +@safe unittest +{ + static struct S + { + enum X = 10; + enum Y + { + i = 10 + } + struct S {} + class C {} + + static int sx = 0; + __gshared int gx = 0; + + Y y; + static Y sy; + + static void f(); + static void f2() pure nothrow @nogc @safe; + + shared void g(); + + static void function() fp; + __gshared void function() gfp; + void function() fpm; + + void delegate() dm; + static void delegate() sd; + + void m(); + final void m2() const pure nothrow @nogc @safe; + + inout(int) iom() inout; + static inout(int) iosf(inout int x); + + @property int p(); + static @property int sp(); + } + + static class C + { + enum X = 10; + enum Y + { + i = 10 + } + struct S {} + class C {} + + static int sx = 0; + __gshared int gx = 0; + + Y y; + static Y sy; + + static void f(); + static void f2() pure nothrow @nogc @safe; + + shared void g() { } + + static void function() fp; + __gshared void function() gfp; + void function() fpm; + + void delegate() dm; + static void delegate() sd; + + void m() {} + final void m2() const pure nothrow @nogc @safe; + + inout(int) iom() inout { return 10; } + static inout(int) iosf(inout int x); + + @property int p() { return 10; } + static @property int sp(); + } + + static assert(!hasStaticMember!(S, "X")); + static assert(!hasStaticMember!(S, "Y")); + static assert(!hasStaticMember!(S, "Y.i")); + static assert(!hasStaticMember!(S, "S")); + static assert(!hasStaticMember!(S, "C")); + static assert( hasStaticMember!(S, "sx")); + static assert( hasStaticMember!(S, "gx")); + static assert(!hasStaticMember!(S, "y")); + static assert( hasStaticMember!(S, "sy")); + static assert( hasStaticMember!(S, "f")); + static assert( hasStaticMember!(S, "f2")); + static assert(!hasStaticMember!(S, "dm")); + static assert( hasStaticMember!(S, "sd")); + static assert(!hasStaticMember!(S, "g")); + static assert( hasStaticMember!(S, "fp")); + static assert( hasStaticMember!(S, "gfp")); + static assert(!hasStaticMember!(S, "fpm")); + static assert(!hasStaticMember!(S, "m")); + static assert(!hasStaticMember!(S, "m2")); + static assert(!hasStaticMember!(S, "iom")); + static assert( hasStaticMember!(S, "iosf")); + static assert(!hasStaticMember!(S, "p")); + static assert( hasStaticMember!(S, "sp")); + + static assert(!hasStaticMember!(C, "X")); + static assert(!hasStaticMember!(C, "Y")); + static assert(!hasStaticMember!(C, "Y.i")); + static assert(!hasStaticMember!(C, "S")); + static assert(!hasStaticMember!(C, "C")); + static assert( hasStaticMember!(C, "sx")); + static assert( hasStaticMember!(C, "gx")); + static assert(!hasStaticMember!(C, "y")); + static assert( hasStaticMember!(C, "sy")); + static assert( hasStaticMember!(C, "f")); + static assert( hasStaticMember!(C, "f2")); + static assert(!hasStaticMember!(S, "dm")); + static assert( hasStaticMember!(S, "sd")); + static assert(!hasStaticMember!(C, "g")); + static assert( hasStaticMember!(C, "fp")); + static assert( hasStaticMember!(C, "gfp")); + static assert(!hasStaticMember!(C, "fpm")); + static assert(!hasStaticMember!(C, "m")); + static assert(!hasStaticMember!(C, "m2")); + static assert(!hasStaticMember!(C, "iom")); + static assert( hasStaticMember!(C, "iosf")); + static assert(!hasStaticMember!(C, "p")); + static assert( hasStaticMember!(C, "sp")); +} + +/** +Retrieves the members of an enumerated type $(D enum E). + +Params: + E = An enumerated type. $(D E) may have duplicated values. Returns: Static tuple composed of the members of the enumerated type $(D E). @@ -3312,7 +3743,7 @@ Returns: Note: An enum can have multiple members which have the same value. If you want to use EnumMembers to e.g. generate switch cases at compile-time, - you should use the $(XREF typetuple, NoDuplicates) template to avoid + you should use the $(REF NoDuplicates, std,typetuple) template to avoid generating duplicate switch cases. Note: @@ -3325,7 +3756,7 @@ int[] abc = cast(int[]) [ EnumMembers!E ]; Cast is not necessary if the type of the variable is inferred. See the example below. -Examples: +Example: Creating an array of enumerated values: -------------------- enum Sqrts : real @@ -3367,6 +3798,7 @@ assert(rank(Mode.map ) == 2); template EnumMembers(E) if (is(E == enum)) { + import std.meta : AliasSeq; // Supply the specified identifier to an constant value. template WithIdentifier(string ident) { @@ -3388,25 +3820,31 @@ template EnumMembers(E) template EnumSpecificMembers(names...) { - static if (names.length > 0) + static if (names.length == 1) + { + alias EnumSpecificMembers = AliasSeq!(WithIdentifier!(names[0]) + .Symbolize!(__traits(getMember, E, names[0]))); + } + else static if (names.length > 0) { alias EnumSpecificMembers = - TypeTuple!( + AliasSeq!( WithIdentifier!(names[0]) .Symbolize!(__traits(getMember, E, names[0])), - EnumSpecificMembers!(names[1 .. $]) + EnumSpecificMembers!(names[1 .. $/2]), + EnumSpecificMembers!(names[$/2..$]) ); } else { - alias EnumSpecificMembers = TypeTuple!(); + alias EnumSpecificMembers = AliasSeq!(); } } alias EnumMembers = EnumSpecificMembers!(__traits(allMembers, E)); } -unittest +@safe unittest { enum A { a } static assert([ EnumMembers!A ] == [ A.a ]); @@ -3414,7 +3852,7 @@ unittest static assert([ EnumMembers!B ] == [ B.a, B.b, B.c, B.d, B.e ]); } -unittest // typed enums +@safe unittest // typed enums { enum A : string { a = "alpha", b = "beta" } static assert([ EnumMembers!A ] == [ A.a, A.b ]); @@ -3428,7 +3866,7 @@ unittest // typed enums static assert([ EnumMembers!B ] == [ B.a, B.b, B.c ]); } -unittest // duplicated values +@safe unittest // duplicated values { enum A { @@ -3438,7 +3876,32 @@ unittest // duplicated values static assert([ EnumMembers!A ] == [ A.a, A.b, A.c, A.d, A.e ]); } -unittest +@safe unittest // Bugzilla 14561: huge enums +{ + string genEnum() + { + string result = "enum TLAs {"; + foreach (c0; '0'..'2'+1) + foreach (c1; '0'..'9'+1) + foreach (c2; '0'..'9'+1) + foreach (c3; '0'..'9'+1) + { + result ~= '_'; + result ~= c0; + result ~= c1; + result ~= c2; + result ~= c3; + result ~= ','; + } + result ~= '}'; + return result; + } + mixin(genEnum); + static assert(EnumMembers!TLAs[0] == TLAs._0000); + static assert(EnumMembers!TLAs[$-1] == TLAs._2999); +} + +@safe unittest { enum E { member, a = 0, b = 0 } static assert(__traits(identifier, EnumMembers!E[0]) == "member"); @@ -3465,7 +3928,7 @@ template BaseTypeTuple(A) } /// -unittest +@safe unittest { interface I1 { } interface I2 { } @@ -3477,7 +3940,7 @@ unittest static assert(is(BaseTypeTuple!I123 == TypeTuple!(I1, I2, I3))); } -unittest +@safe unittest { interface I1 { } interface I2 { } @@ -3518,7 +3981,7 @@ template BaseClassesTuple(T) } /// -unittest +@safe unittest { class C1 { } class C2 : C1 { } @@ -3527,10 +3990,9 @@ unittest static assert(is(BaseClassesTuple!C1 == TypeTuple!(Object))); static assert(is(BaseClassesTuple!C2 == TypeTuple!(C1, Object))); static assert(is(BaseClassesTuple!C3 == TypeTuple!(C2, C1, Object))); - static assert(!BaseClassesTuple!Object.length); } -unittest +@safe unittest { struct S { } static assert(!__traits(compiles, BaseClassesTuple!S)); @@ -3549,6 +4011,7 @@ unittest */ template InterfacesTuple(T) { + import std.meta : NoDuplicates; template Flatten(H, T...) { static if (T.length) @@ -3570,7 +4033,7 @@ template InterfacesTuple(T) alias InterfacesTuple = TypeTuple!(); } -unittest +@safe unittest { // doc example interface I1 {} @@ -3582,7 +4045,7 @@ unittest static assert(is(TL[0] == I1) && is(TL[1] == I2)); } -unittest +@safe unittest { interface Iaa {} interface Iab {} @@ -3617,7 +4080,7 @@ template TransitiveBaseTypeTuple(T) } /// -unittest +@safe unittest { interface J1 {} interface J2 {} @@ -3654,60 +4117,69 @@ template MemberFunctionsTuple(C, string name) static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name))) { // Get all overloads in sight (not hidden). - alias TypeTuple!(__traits(getVirtualFunctions, Node, name)) inSight; + alias inSight = TypeTuple!(__traits(getVirtualFunctions, Node, name)); // And collect all overloads in ancestor classes to reveal hidden // methods. The result may contain duplicates. template walkThru(Parents...) { static if (Parents.length > 0) - alias TypeTuple!( + alias walkThru = TypeTuple!( CollectOverloads!(Parents[0]), walkThru!(Parents[1 .. $]) - ) walkThru; + ); else - alias TypeTuple!() walkThru; + alias walkThru = TypeTuple!(); } static if (is(Node Parents == super)) - alias TypeTuple!(inSight, walkThru!Parents) CollectOverloads; + alias CollectOverloads = TypeTuple!(inSight, walkThru!Parents); else - alias TypeTuple!inSight CollectOverloads; + alias CollectOverloads = TypeTuple!inSight; } else - alias TypeTuple!() CollectOverloads; // no overloads in this hierarchy + alias CollectOverloads = TypeTuple!(); // no overloads in this hierarchy } // duplicates in this tuple will be removed by shrink() - alias CollectOverloads!C overloads; + alias overloads = CollectOverloads!C; // shrinkOne!args[0] = the most derived one in the covariant siblings of target // shrinkOne!args[1..$] = non-covariant others template shrinkOne(/+ alias target, rest... +/ args...) { - alias args[0 .. 1] target; // prevent property functions from being evaluated - alias args[1 .. $] rest; + import std.meta : AliasSeq; + alias target = args[0 .. 1]; // prevent property functions from being evaluated + alias rest = args[1 .. $]; static if (rest.length > 0) { - alias FunctionTypeOf!target Target; - alias FunctionTypeOf!(rest[0]) Rest0; + alias Target = FunctionTypeOf!target; + alias Rest0 = FunctionTypeOf!(rest[0]); - static if (isCovariantWith!(Target, Rest0)) + static if (isCovariantWith!(Target, Rest0) && isCovariantWith!(Rest0, Target)) + { + // One of these overrides the other. Choose the one from the most derived parent. + static if (is(AliasSeq!(__traits(parent, target))[0] : AliasSeq!(__traits(parent, rest[0]))[0])) + alias shrinkOne = shrinkOne!(target, rest[1 .. $]); + else + alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); + } + else static if (isCovariantWith!(Target, Rest0)) // target overrides rest[0] -- erase rest[0]. - alias shrinkOne!(target, rest[1 .. $]) shrinkOne; + alias shrinkOne = shrinkOne!(target, rest[1 .. $]); else static if (isCovariantWith!(Rest0, Target)) // rest[0] overrides target -- erase target. - alias shrinkOne!(rest[0], rest[1 .. $]) shrinkOne; + alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); else // target and rest[0] are distinct. - alias TypeTuple!( + alias shrinkOne = TypeTuple!( shrinkOne!(target, rest[1 .. $]), rest[0] // keep - ) shrinkOne; + ); } else - alias TypeTuple!target shrinkOne; // done + alias shrinkOne = TypeTuple!target; // done } /* @@ -3717,22 +4189,22 @@ template MemberFunctionsTuple(C, string name) { static if (overloads.length > 0) { - alias shrinkOne!overloads temp; - alias TypeTuple!(temp[0], shrink!(temp[1 .. $])) shrink; + alias temp = shrinkOne!overloads; + alias shrink = TypeTuple!(temp[0], shrink!(temp[1 .. $])); } else - alias TypeTuple!() shrink; // done + alias shrink = TypeTuple!(); // done } // done. - alias shrink!overloads MemberFunctionsTuple; + alias MemberFunctionsTuple = shrink!overloads; } else - alias TypeTuple!() MemberFunctionsTuple; + alias MemberFunctionsTuple = TypeTuple!(); } /// -unittest +@safe unittest { interface I { I foo(); } class B @@ -3743,13 +4215,32 @@ unittest { override C foo() { return this; } // covariant overriding of I.foo() } - alias MemberFunctionsTuple!(C, "foo") foos; + alias foos = MemberFunctionsTuple!(C, "foo"); static assert(foos.length == 2); static assert(__traits(isSame, foos[0], C.foo)); static assert(__traits(isSame, foos[1], B.foo)); } -unittest +@safe unittest // Issue 15920 +{ + import std.meta : AliasSeq; + class A + { + void f(){} + void f(int){} + } + class B : A + { + override void f(){} + override void f(int){} + } + alias fs = MemberFunctionsTuple!(B, "f"); + alias bfs = AliasSeq!(__traits(getOverloads, B, "f")); + assert(__traits(isSame, fs[0], bfs[0]) || __traits(isSame, fs[0], bfs[1])); + assert(__traits(isSame, fs[1], bfs[0]) || __traits(isSame, fs[1], bfs[1])); +} + +@safe unittest { interface I { I test(); } interface J : I { J test(); } @@ -3798,19 +4289,20 @@ template TemplateOf(alias T : Base!Args, alias Base, Args...) alias TemplateOf = Base; } +/// ditto template TemplateOf(T : Base!Args, alias Base, Args...) { alias TemplateOf = Base; } /// -unittest +@safe unittest { struct Foo(T, U) {} static assert(__traits(isSame, TemplateOf!(Foo!(int, real)), Foo)); } -unittest +@safe unittest { template Foo1(A) {} template Foo2(A, B) {} @@ -3849,13 +4341,13 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...) } /// -unittest +@safe unittest { struct Foo(T, U) {} static assert(is(TemplateArgsOf!(Foo!(int, real)) == TypeTuple!(int, real))); } -unittest +@safe unittest { template Foo1(A) {} template Foo2(A, B) {} @@ -3883,13 +4375,14 @@ unittest private template maxAlignment(U...) if (isTypeTuple!U) { + import std.meta : staticMap; static if (U.length == 0) static assert(0); else static if (U.length == 1) enum maxAlignment = U[0].alignof; else { - import std.algorithm : max; + import std.algorithm.comparison : max; enum maxAlignment = max(staticMap!(.maxAlignment, U)); } } @@ -3898,13 +4391,13 @@ private template maxAlignment(U...) if (isTypeTuple!U) /** Returns class instance alignment. */ -template classInstanceAlignment(T) if(is(T == class)) +template classInstanceAlignment(T) if (is(T == class)) { alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof)); } /// -unittest +@safe unittest { class A { byte b; } class B { long l; } @@ -3933,7 +4426,7 @@ template CommonType(T...) } else static if (T.length == 1) { - static if(is(typeof(T[0]))) + static if (is(typeof(T[0]))) { alias CommonType = typeof(T[0]); } @@ -3951,14 +4444,14 @@ template CommonType(T...) } /// -unittest +@safe unittest { alias X = CommonType!(int, long, short); assert(is(X == long)); alias Y = CommonType!(int, char[], short); assert(is(Y == void)); } -unittest +@safe unittest { static assert(is(CommonType!(3) == int)); static assert(is(CommonType!(double, 4, float) == double)); @@ -3982,32 +4475,36 @@ template ImplicitConversionTargets(T) { static if (is(T == bool)) alias ImplicitConversionTargets = - TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, + TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList, float, double, real, char, wchar, dchar); else static if (is(T == byte)) alias ImplicitConversionTargets = - TypeTuple!(short, ushort, int, uint, long, ulong, + TypeTuple!(short, ushort, int, uint, long, ulong, CentTypeList, float, double, real, char, wchar, dchar); else static if (is(T == ubyte)) alias ImplicitConversionTargets = - TypeTuple!(short, ushort, int, uint, long, ulong, + TypeTuple!(short, ushort, int, uint, long, ulong, CentTypeList, float, double, real, char, wchar, dchar); else static if (is(T == short)) alias ImplicitConversionTargets = - TypeTuple!(int, uint, long, ulong, float, double, real); + TypeTuple!(int, uint, long, ulong, CentTypeList, float, double, real); else static if (is(T == ushort)) alias ImplicitConversionTargets = - TypeTuple!(int, uint, long, ulong, float, double, real); + TypeTuple!(int, uint, long, ulong, CentTypeList, float, double, real); else static if (is(T == int)) alias ImplicitConversionTargets = - TypeTuple!(long, ulong, float, double, real); + TypeTuple!(long, ulong, CentTypeList, float, double, real); else static if (is(T == uint)) alias ImplicitConversionTargets = - TypeTuple!(long, ulong, float, double, real); + TypeTuple!(long, ulong, CentTypeList, float, double, real); else static if (is(T == long)) alias ImplicitConversionTargets = TypeTuple!(float, double, real); else static if (is(T == ulong)) alias ImplicitConversionTargets = TypeTuple!(float, double, real); + else static if (is(cent) && is(T == cent)) + alias ImplicitConversionTargets = TypeTuple!(float, double, real); + else static if (is(ucent) && is(T == ucent)) + alias ImplicitConversionTargets = TypeTuple!(float, double, real); else static if (is(T == float)) alias ImplicitConversionTargets = TypeTuple!(double, real); else static if (is(T == double)) @@ -4015,17 +4512,17 @@ template ImplicitConversionTargets(T) else static if (is(T == char)) alias ImplicitConversionTargets = TypeTuple!(wchar, dchar, byte, ubyte, short, ushort, - int, uint, long, ulong, float, double, real); + int, uint, long, ulong, CentTypeList, float, double, real); else static if (is(T == wchar)) alias ImplicitConversionTargets = - TypeTuple!(dchar, short, ushort, int, uint, long, ulong, + TypeTuple!(dchar, short, ushort, int, uint, long, ulong, CentTypeList, float, double, real); else static if (is(T == dchar)) alias ImplicitConversionTargets = - TypeTuple!(int, uint, long, ulong, float, double, real); + TypeTuple!(int, uint, long, ulong, CentTypeList, float, double, real); else static if (is(T : typeof(null))) alias ImplicitConversionTargets = TypeTuple!(typeof(null)); - else static if(is(T : Object)) + else static if (is(T : Object)) alias ImplicitConversionTargets = TransitiveBaseTypeTuple!(T); else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) alias ImplicitConversionTargets = @@ -4036,7 +4533,7 @@ template ImplicitConversionTargets(T) alias ImplicitConversionTargets = TypeTuple!(); } -unittest +@safe unittest { static assert(is(ImplicitConversionTargets!(double)[0] == real)); static assert(is(ImplicitConversionTargets!(string)[0] == const(char)[])); @@ -4056,22 +4553,20 @@ template isImplicitlyConvertible(From, To) })); } -unittest +/// +@safe unittest { static assert( isImplicitlyConvertible!(immutable(char), char)); static assert( isImplicitlyConvertible!(const(char), char)); static assert( isImplicitlyConvertible!(char, wchar)); - static assert(!isImplicitlyConvertible!(wchar, char)); - // bug6197 static assert(!isImplicitlyConvertible!(const(ushort), ubyte)); static assert(!isImplicitlyConvertible!(const(uint), ubyte)); static assert(!isImplicitlyConvertible!(const(ulong), ubyte)); - // from std.conv.implicitlyConverts - assert(!isImplicitlyConvertible!(const(char)[], string)); - assert( isImplicitlyConvertible!(string, const(char)[])); + static assert(!isImplicitlyConvertible!(const(char)[], string)); + static assert( isImplicitlyConvertible!(string, const(char)[])); } /** @@ -4085,7 +4580,7 @@ If you omit $(D Rhs), $(D isAssignable) will check identity assignable of $(D Lh enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs); /// -unittest +@safe unittest { static assert( isAssignable!(long, int)); static assert(!isAssignable!(int, long)); @@ -4105,7 +4600,7 @@ private enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lh // ditto private enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs); -unittest +@safe unittest { static assert(!isAssignable!(immutable int, int)); static assert( isAssignable!(int, immutable int)); @@ -4170,15 +4665,18 @@ package template isBlitAssignable(T) static if (i == 0) { } - else if (T.tupleof[i].offsetof == offset) - { - if (assignable) - continue; - } else { - if (!assignable) - return false; + if (T.tupleof[i].offsetof == offset) + { + if (assignable) + continue; + } + else + { + if (!assignable) + return false; + } } assignable = isBlitAssignable!(typeof(T.tupleof[i])); offset = T.tupleof[i].offsetof; @@ -4190,7 +4688,7 @@ package template isBlitAssignable(T) enum isBlitAssignable = isMutable!T; } -unittest +@safe unittest { static assert( isBlitAssignable!int); static assert(!isBlitAssignable!(const int)); @@ -4273,11 +4771,11 @@ private template isStorageClassImplicitlyConvertible(From, To) alias Pointify(T) = void*; enum isStorageClassImplicitlyConvertible = isImplicitlyConvertible!( - ModifyTypePreservingSTC!(Pointify, From), - ModifyTypePreservingSTC!(Pointify, To) ); + ModifyTypePreservingTQ!(Pointify, From), + ModifyTypePreservingTQ!(Pointify, To) ); } -unittest +@safe unittest { static assert( isStorageClassImplicitlyConvertible!( int, const int)); static assert( isStorageClassImplicitlyConvertible!(immutable int, const int)); @@ -4363,8 +4861,8 @@ template isCovariantWith(F, G) template checkParameters() { alias STC = ParameterStorageClass; - alias UprParams = ParameterTypeTuple!Upr; - alias LwrParams = ParameterTypeTuple!Lwr; + alias UprParams = Parameters!Upr; + alias LwrParams = Parameters!Lwr; alias UprPSTCs = ParameterStorageClassTuple!Upr; alias LwrPSTCs = ParameterStorageClassTuple!Lwr; // @@ -4402,7 +4900,7 @@ template isCovariantWith(F, G) } /// -unittest +@safe unittest { interface I { I clone(); } interface J { J clone(); } @@ -4422,7 +4920,7 @@ unittest static assert(!isCovariantWith!(typeof(C.clone), typeof(J.clone))); } -unittest +@safe unittest { enum bool isCovariantWith(alias f, alias g) = .isCovariantWith!(typeof(f), typeof(g)); @@ -4441,10 +4939,10 @@ unittest static assert( isCovariantWith!(DerivA_2.test, DerivA_2.test)); // scope parameter - interface BaseB { void test( int, int); } - interface DerivB_1 : BaseB { override void test(scope int, int); } - interface DerivB_2 : BaseB { override void test( int, scope int); } - interface DerivB_3 : BaseB { override void test(scope int, scope int); } + interface BaseB { void test( int*, int*); } + interface DerivB_1 : BaseB { override void test(scope int*, int*); } + interface DerivB_2 : BaseB { override void test( int*, scope int*); } + interface DerivB_3 : BaseB { override void test(scope int*, scope int*); } static assert( isCovariantWith!(DerivB_1.test, BaseB.test)); static assert( isCovariantWith!(DerivB_2.test, BaseB.test)); static assert( isCovariantWith!(DerivB_3.test, BaseB.test)); @@ -4494,7 +4992,7 @@ $(D __traits(compiles, ...)) purposes. No actual value is returned. Note: Trying to use returned value will result in a "Symbol Undefined" error at link time. -Examples: +Example: --- // Note that `f` doesn't have to be implemented // as is isn't called. @@ -4514,13 +5012,13 @@ int i = rvalueOf!int; // error, no actual value is returned // Note: unittest can't be used as an example here as function overloads // aren't allowed inside functions. -unittest +@system unittest { void needLvalue(T)(ref T); static struct S { } int i; struct Nested { void f() { ++i; } } - foreach(T; TypeTuple!(int, immutable int, inout int, string, S, Nested, Object)) + foreach (T; TypeTuple!(int, immutable int, inout int, string, S, Nested, Object)) { static assert(!__traits(compiles, needLvalue(rvalueOf!T))); static assert( __traits(compiles, needLvalue(lvalueOf!T))); @@ -4567,7 +5065,7 @@ template BooleanTypeOf(T) static assert(0, T.stringof~" is not boolean type"); } -unittest +@safe unittest { // unexpected failure, maybe dmd type-merging bug foreach (T; TypeTuple!bool) @@ -4585,7 +5083,7 @@ unittest } } -unittest +@safe unittest { struct B { @@ -4605,6 +5103,7 @@ unittest */ template IntegralTypeOf(T) { + import std.meta : staticIndexOf; static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = IntegralTypeOf!AT; else @@ -4618,7 +5117,7 @@ template IntegralTypeOf(T) static assert(0, T.stringof~" is not an integral type"); } -unittest +@safe unittest { foreach (T; IntegralTypeList) foreach (Q; TypeQualifierList) @@ -4639,6 +5138,7 @@ unittest */ template FloatingPointTypeOf(T) { + import std.meta : staticIndexOf; static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = FloatingPointTypeOf!AT; else @@ -4652,7 +5152,7 @@ template FloatingPointTypeOf(T) static assert(0, T.stringof~" is not a floating point type"); } -unittest +@safe unittest { foreach (T; FloatingPointTypeList) foreach (Q; TypeQualifierList) @@ -4681,7 +5181,7 @@ template NumericTypeOf(T) static assert(0, T.stringof~" is not a numeric type"); } -unittest +@safe unittest { foreach (T; NumericTypeList) foreach (Q; TypeQualifierList) @@ -4702,6 +5202,7 @@ unittest */ template UnsignedTypeOf(T) { + import std.meta : staticIndexOf; static if (is(IntegralTypeOf!T X) && staticIndexOf!(Unqual!X, UnsignedIntTypeList) >= 0) alias UnsignedTypeOf = X; @@ -4713,6 +5214,7 @@ template UnsignedTypeOf(T) */ template SignedTypeOf(T) { + import std.meta : staticIndexOf; static if (is(IntegralTypeOf!T X) && staticIndexOf!(Unqual!X, SignedIntTypeList) >= 0) alias SignedTypeOf = X; @@ -4726,6 +5228,7 @@ template SignedTypeOf(T) */ template CharTypeOf(T) { + import std.meta : staticIndexOf; static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = CharTypeOf!AT; else @@ -4739,7 +5242,7 @@ template CharTypeOf(T) static assert(0, T.stringof~" is not a character type"); } -unittest +@safe unittest { foreach (T; CharTypeList) foreach (Q; TypeQualifierList) @@ -4778,7 +5281,7 @@ template StaticArrayTypeOf(T) static assert(0, T.stringof~" is not a static array type"); } -unittest +@safe unittest { foreach (T; TypeTuple!(bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList)) foreach (Q; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf)) @@ -4815,7 +5318,7 @@ template DynamicArrayTypeOf(T) static assert(0, T.stringof~" is not a dynamic array"); } -unittest +@safe unittest { foreach (T; TypeTuple!(/*void, */bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList)) foreach (Q; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf)) @@ -4870,7 +5373,7 @@ template StringTypeOf(T) static assert(0, T.stringof~" is not a string type"); } -unittest +@safe unittest { foreach (T; CharTypeList) foreach (Q; TypeTuple!(MutableOf, ConstOf, ImmutableOf, InoutOf)) @@ -4894,7 +5397,7 @@ unittest } } -unittest +@safe unittest { static assert(is(StringTypeOf!(char[4]) == char[])); } @@ -4916,7 +5419,7 @@ template AssocArrayTypeOf(T) static assert(0, T.stringof~" is not an associative array type"); } -unittest +@safe unittest { foreach (T; TypeTuple!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) foreach (P; TypeTuple!(TypeQualifierList, InoutOf, SharedInoutOf)) @@ -4962,7 +5465,7 @@ template BuiltinTypeOf(T) enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T; /// -unittest +@safe unittest { static assert( isBoolean!bool); enum EB : bool { a = true } @@ -4970,13 +5473,51 @@ unittest static assert(!isBoolean!(SubTypeOf!bool)); } +@safe unittest +{ + static struct S(T) + { + T t; + alias t this; + } + static assert(!isIntegral!(S!bool)); +} + /** * Detect whether $(D T) is a built-in integral type. Types $(D bool), * $(D char), $(D wchar), and $(D dchar) are not considered integral. */ enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T; -unittest +/// +@safe unittest +{ + static assert( + isIntegral!byte && + isIntegral!short && + isIntegral!int && + isIntegral!long && + isIntegral!(const(long)) && + isIntegral!(immutable(long)) + ); + + static assert( + !isIntegral!bool && + !isIntegral!char && + !isIntegral!double + ); + + // types which act as integral values do not pass + struct S + { + int val; + alias val this; + } + + static assert(!isIntegral!S); +} + +@safe unittest { foreach (T; IntegralTypeList) { @@ -4998,9 +5539,43 @@ unittest /** * Detect whether $(D T) is a built-in floating point type. */ -enum bool isFloatingPoint(T) = is(FloatingPointTypeOf!T) && !isAggregateType!T; +enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloat) || + is(Unqual!T == cdouble) || + is(Unqual!T == creal) || + is(Unqual!T == ifloat) || + is(Unqual!T == idouble) || + is(Unqual!T == ireal)); -unittest +/// +@safe unittest +{ + static assert( + isFloatingPoint!float && + isFloatingPoint!double && + isFloatingPoint!real && + isFloatingPoint!(const(real)) && + isFloatingPoint!(immutable(real)) + ); + + static assert(!isFloatingPoint!int); + + // complex and imaginary numbers do not pass + static assert( + !isFloatingPoint!cfloat && + !isFloatingPoint!ifloat + ); + + // types which act as floating point values do not pass + struct S + { + float val; + alias val this; + } + + static assert(!isFloatingPoint!S); +} + +@safe unittest { enum EF : real { a = 1.414, b = 1.732, c = 2.236 } @@ -5021,13 +5596,60 @@ unittest } } +// https://issues.dlang.org/show_bug.cgi?id=17195 +@safe unittest +{ + static assert(!isFloatingPoint!cfloat); + static assert(!isFloatingPoint!cdouble); + static assert(!isFloatingPoint!creal); + + static assert(!isFloatingPoint!ifloat); + static assert(!isFloatingPoint!idouble); + static assert(!isFloatingPoint!ireal); +} + /** -Detect whether $(D T) is a built-in numeric type (integral or floating -point). + * Detect whether $(D T) is a built-in numeric type (integral or floating + * point). */ -enum bool isNumeric(T) = is(NumericTypeOf!T) && !isAggregateType!T; +enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == char) || + is(Unqual!T == wchar) || + is(Unqual!T == dchar)); -unittest +/// +@safe unittest +{ + static assert( + isNumeric!bool && + isNumeric!byte && + isNumeric!short && + isNumeric!int && + isNumeric!long && + isNumeric!float && + isNumeric!double && + isNumeric!real && + isNumeric!(const(real)) && + isNumeric!(immutable(real)) + ); + + static assert( + !isNumeric!void && + !isNumeric!char && + !isNumeric!wchar && + !isNumeric!dchar + ); + + // types which act as numeric values do not pass + struct S + { + int val; + alias val this; + } + + static assert(!isIntegral!S); +} + +@safe unittest { foreach (T; TypeTuple!(NumericTypeList)) { @@ -5037,32 +5659,65 @@ unittest static assert(!isNumeric!(SubTypeOf!(Q!T))); } } + + static struct S(T) + { + T t; + alias t this; + } + static assert(!isNumeric!(S!int)); } /** -Detect whether $(D T) is a scalar type (a built-in numeric, character or boolean type). + * Detect whether $(D T) is a scalar type (a built-in numeric, character or + * boolean type). */ -enum bool isScalarType(T) = isNumeric!T || isSomeChar!T || isBoolean!T; +enum bool isScalarType(T) = is(T : real) && !isAggregateType!T; /// -unittest +@safe unittest { static assert(!isScalarType!void); + static assert( isScalarType!(immutable(byte))); + static assert( isScalarType!(immutable(ushort))); static assert( isScalarType!(immutable(int))); + static assert( isScalarType!(ulong)); static assert( isScalarType!(shared(float))); static assert( isScalarType!(shared(const bool))); + static assert( isScalarType!(const(char))); + static assert( isScalarType!(wchar)); static assert( isScalarType!(const(dchar))); + static assert( isScalarType!(const(double))); + static assert( isScalarType!(const(real))); +} + +@safe unittest +{ + static struct S(T) + { + T t; + alias t this; + } + static assert(!isScalarType!(S!int)); } /** -Detect whether $(D T) is a basic type (scalar type or void). + * Detect whether $(D T) is a basic type (scalar type or void). */ -enum bool isBasicType(T) = isScalarType!T || is(T == void); +enum bool isBasicType(T) = isScalarType!T || is(Unqual!T == void); /// -unittest +@safe unittest { static assert(isBasicType!void); + static assert(isBasicType!(const(void))); + static assert(isBasicType!(shared(void))); + static assert(isBasicType!(immutable(void))); + static assert(isBasicType!(shared const(void))); + static assert(isBasicType!(shared inout(void))); + static assert(isBasicType!(shared inout const(void))); + static assert(isBasicType!(inout(void))); + static assert(isBasicType!(inout const(void))); static assert(isBasicType!(immutable(int))); static assert(isBasicType!(shared(float))); static assert(isBasicType!(shared(const bool))); @@ -5070,11 +5725,32 @@ unittest } /** -Detect whether $(D T) is a built-in unsigned numeric type. + * Detect whether $(D T) is a built-in unsigned numeric type. */ -enum bool isUnsigned(T) = is(UnsignedTypeOf!T) && !isAggregateType!T; +enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) || + is(Unqual!T == wchar) || + is(Unqual!T == dchar) || + is(Unqual!T == bool)); -unittest +/// +@safe unittest +{ + static assert( + isUnsigned!uint && + isUnsigned!ulong + ); + + static assert( + !isUnsigned!char && + !isUnsigned!int && + !isUnsigned!long && + !isUnsigned!char && + !isUnsigned!wchar && + !isUnsigned!dchar + ); +} + +@safe unittest { foreach (T; TypeTuple!(UnsignedIntTypeList)) { @@ -5084,15 +5760,42 @@ unittest static assert(!isUnsigned!(SubTypeOf!(Q!T))); } } + + static struct S(T) + { + T t; + alias t this; + } + static assert(!isUnsigned!(S!uint)); } /** -Detect whether $(D T) is a built-in signed numeric type. + * Detect whether $(D T) is a built-in signed numeric type. */ -enum bool isSigned(T) = is(SignedTypeOf!T) && !isAggregateType!T; +enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T); -unittest +/// +@safe unittest +{ + static assert( + isSigned!int && + isSigned!long + ); + + static assert( + !isSigned!uint && + !isSigned!ulong + ); +} + +@safe unittest { + enum E { e1 = 0 } + static assert(isSigned!E); + + enum Eubyte : ubyte { e1 = 0 } + static assert(!isSigned!Eubyte); + foreach (T; TypeTuple!(SignedIntTypeList)) { foreach (Q; TypeQualifierList) @@ -5101,16 +5804,42 @@ unittest static assert(!isSigned!(SubTypeOf!(Q!T))); } } + + static struct S(T) + { + T t; + alias t this; + } + static assert(!isSigned!(S!uint)); +} + +// https://issues.dlang.org/show_bug.cgi?id=17196 +@safe unittest +{ + static assert(isUnsigned!bool == false); + static assert(isSigned!bool == false); } /** -Detect whether $(D T) is one of the built-in character types. + * Detect whether $(D T) is one of the built-in character types. + * + * The built-in char types are any of $(D char), $(D wchar) or $(D dchar), with + * or without qualifiers. */ enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T; /// -unittest +@safe unittest { + //Char types + static assert( isSomeChar!char); + static assert( isSomeChar!wchar); + static assert( isSomeChar!dchar); + static assert( isSomeChar!(typeof('c'))); + static assert( isSomeChar!(immutable char)); + static assert( isSomeChar!(const dchar)); + + //Non char types static assert(!isSomeChar!int); static assert(!isSomeChar!byte); static assert(!isSomeChar!string); @@ -5119,7 +5848,7 @@ unittest static assert(!isSomeChar!(char[4])); } -unittest +@safe unittest { enum EC : char { a = 'x', b = 'y' } @@ -5131,6 +5860,14 @@ unittest static assert(!isSomeChar!( SubTypeOf!(Q!T) )); } } + + // alias-this types are not allowed + static struct S(T) + { + T t; + alias t this; + } + static assert(!isSomeChar!(S!char)); } /** @@ -5145,19 +5882,27 @@ built-in string types. enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStaticArray!T; /// -unittest +@safe unittest { + //String types + static assert( isSomeString!string); + static assert( isSomeString!(wchar[])); + static assert( isSomeString!(dchar[])); + static assert( isSomeString!(typeof("aaa"))); + static assert( isSomeString!(const(char)[])); + + enum ES : string { a = "aaa", b = "bbb" } + static assert( isSomeString!ES); + + //Non string types static assert(!isSomeString!int); static assert(!isSomeString!(int[])); static assert(!isSomeString!(byte[])); static assert(!isSomeString!(typeof(null))); static assert(!isSomeString!(char[4])); - - enum ES : string { a = "aaa", b = "bbb" } - static assert( isSomeString!ES); } -unittest +@safe unittest { foreach (T; TypeTuple!(char[], dchar[], string, wstring, dstring)) { @@ -5166,9 +5911,27 @@ unittest } } +/** + * Detect whether type $(D T) is a narrow string. + * + * All arrays that use char, wchar, and their qualified versions are narrow + * strings. (Those include string and wstring). + */ enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isAggregateType!T && !isStaticArray!T; -unittest +/// +@safe unittest +{ + static assert(isNarrowString!string); + static assert(isNarrowString!wstring); + static assert(isNarrowString!(char[])); + static assert(isNarrowString!(wchar[])); + + static assert(!isNarrowString!dstring); + static assert(!isNarrowString!(dchar[])); +} + +@safe unittest { foreach (T; TypeTuple!(char[], string, wstring)) { @@ -5189,15 +5952,92 @@ unittest } } + /** - * Detect whether type $(D T) is a static array. + * Detect whether $(D T) is a struct, static array, or enum that is implicitly + * convertible to a string. */ -enum bool isStaticArray(T) = is(StaticArrayTypeOf!T) && !isAggregateType!T; +template isConvertibleToString(T) +{ + enum isConvertibleToString = + (isAggregateType!T || isStaticArray!T || is(T == enum)) + && is(StringTypeOf!T); +} /// -unittest +@safe unittest { - static assert(!isStaticArray!(const(int)[])); + static struct AliasedString + { + string s; + alias s this; + } + + enum StringEnum { a = "foo" } + + assert(!isConvertibleToString!string); + assert(isConvertibleToString!AliasedString); + assert(isConvertibleToString!StringEnum); + assert(isConvertibleToString!(char[25])); + assert(!isConvertibleToString!(char[])); +} + +@safe unittest // Bugzilla 16573 +{ + enum I : int { foo = 1 } + enum S : string { foo = "foo" } + assert(!isConvertibleToString!I); + assert(isConvertibleToString!S); +} + +package template convertToString(T) +{ + static if (isConvertibleToString!T) + alias convertToString = StringTypeOf!T; + else + alias convertToString = T; +} + +/** + * Detect whether type $(D T) is a string that will be autodecoded. + * + * All arrays that use char, wchar, and their qualified versions are narrow + * strings. (Those include string and wstring). + * Aggregates that implicitly cast to narrow strings are included. + * + * Params: + * T = type to be tested + * + * Returns: + * true if T represents a string that is subject to autodecoding + * + * See Also: + * $(LREF isNarrowString) + */ +enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isStaticArray!T; + +/// +@safe unittest +{ + static struct Stringish + { + string s; + alias s this; + } + assert(isAutodecodableString!wstring); + assert(isAutodecodableString!Stringish); + assert(!isAutodecodableString!dstring); +} + +/** + * Detect whether type $(D T) is a static array. + */ +enum bool isStaticArray(T) = __traits(isStaticArray, T); + +/// +@safe unittest +{ + static assert(!isStaticArray!(const(int)[])); static assert(!isStaticArray!(immutable(int)[])); static assert(!isStaticArray!(const(int)[4][])); static assert(!isStaticArray!(int[])); @@ -5207,7 +6047,7 @@ unittest static assert(!isStaticArray!int); } -unittest +@safe unittest { foreach (T; TypeTuple!(int[51], int[][2], char[][int][11], immutable char[13u], @@ -5229,9 +6069,21 @@ unittest */ enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; -unittest +/// +@safe unittest { - foreach (T; TypeTuple!(int[], char[], string, long[3][], double[string][])) + static assert( isDynamicArray!(int[])); + static assert( isDynamicArray!(string)); + static assert( isDynamicArray!(long[3][])); + + static assert(!isDynamicArray!(int[5])); + static assert(!isDynamicArray!(typeof(null))); +} + +@safe unittest +{ + import std.meta : AliasSeq; + foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) { foreach (Q; TypeQualifierList) { @@ -5239,12 +6091,6 @@ unittest static assert(!isDynamicArray!( SubTypeOf!(Q!T) )); } } - - static assert(!isDynamicArray!(int[5])); - static assert(!isDynamicArray!(typeof(null))); - - //enum EDA : int[] { a = [1], b = [2] } - //static assert( isDynamicArray!EDA); } /** @@ -5253,9 +6099,22 @@ unittest */ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; -unittest +/// +@safe unittest { - foreach (T; TypeTuple!(int[], int[5], void[])) + static assert( isArray!(int[])); + static assert( isArray!(int[5])); + static assert( isArray!(string)); + + static assert(!isArray!uint); + static assert(!isArray!(uint[uint])); + static assert(!isArray!(typeof(null))); +} + +@safe unittest +{ + import std.meta : AliasSeq; + foreach (T; AliasSeq!(int[], int[5], void[])) { foreach (Q; TypeQualifierList) { @@ -5263,18 +6122,14 @@ unittest static assert(!isArray!(SubTypeOf!(Q!T))); } } - - static assert(!isArray!uint); - static assert(!isArray!(uint[uint])); - static assert(!isArray!(typeof(null))); } /** * Detect whether $(D T) is an associative array type */ -enum bool isAssociativeArray(T) = is(AssocArrayTypeOf!T) && !isAggregateType!T; +enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); -unittest +@safe unittest { struct Foo { @@ -5305,16 +6160,35 @@ unittest */ enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; +/// +@safe unittest +{ + class C; + union U; + struct S; + interface I; + + static assert( isBuiltinType!void); + static assert( isBuiltinType!string); + static assert( isBuiltinType!(int[])); + static assert( isBuiltinType!(C[string])); + static assert(!isBuiltinType!C); + static assert(!isBuiltinType!U); + static assert(!isBuiltinType!S); + static assert(!isBuiltinType!I); + static assert(!isBuiltinType!(void delegate(int))); +} + /** * Detect whether type $(D T) is a SIMD vector type. */ enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); -unittest +@safe unittest { static if (is(__vector(float[4]))) { - alias __vector(float[4]) SimdVec; + alias SimdVec = __vector(float[4]); static assert(isSIMDVector!(__vector(float[4]))); static assert(isSIMDVector!SimdVec); } @@ -5327,7 +6201,7 @@ unittest */ enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T; -unittest +@safe unittest { foreach (T; TypeTuple!(int*, void*, char[]*)) { @@ -5349,19 +6223,11 @@ Returns the target type of a pointer. */ alias PointerTarget(T : T*) = T; -/** - $(RED Deprecated. Please use $(LREF PointerTarget) instead. This will be - removed in June 2015.) - */ -deprecated("Please use PointerTarget instead.") -alias pointerTarget = PointerTarget; - -unittest +/// +@safe unittest { - static assert( is(PointerTarget!(int*) == int)); - static assert( is(PointerTarget!(long*) == long)); - - static assert(!is(PointerTarget!int)); + static assert(is(PointerTarget!(int*) == int)); + static assert(is(PointerTarget!(void*) == void)); } /** @@ -5370,6 +6236,25 @@ unittest enum bool isAggregateType(T) = is(T == struct) || is(T == union) || is(T == class) || is(T == interface); +/// +@safe unittest +{ + class C; + union U; + struct S; + interface I; + + static assert( isAggregateType!C); + static assert( isAggregateType!U); + static assert( isAggregateType!S); + static assert( isAggregateType!I); + static assert(!isAggregateType!void); + static assert(!isAggregateType!string); + static assert(!isAggregateType!(int[])); + static assert(!isAggregateType!(C[string])); + static assert(!isAggregateType!(void delegate(int))); +} + /** * Returns $(D true) if T can be iterated over using a $(D foreach) loop with * a single loop variable of automatically inferred type, regardless of how @@ -5377,14 +6262,14 @@ enum bool isAggregateType(T) = is(T == struct) || is(T == union) || * that define $(D opApply) with a single loop variable, and builtin dynamic, * static and associative arrays. */ -enum bool isIterable(T) = is(typeof({ foreach(elem; T.init) {} })); +enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} })); /// -unittest +@safe unittest { struct OpApply { - int opApply(int delegate(ref uint) dg) { assert(0); } + int opApply(scope int delegate(ref uint) dg) { assert(0); } } struct Range @@ -5409,7 +6294,7 @@ unittest enum bool isMutable(T) = !is(T == const) && !is(T == immutable) && !is(T == inout); /// -unittest +@safe unittest { static assert( isMutable!int); static assert( isMutable!string); @@ -5427,20 +6312,43 @@ unittest * Returns true if T is an instance of the template S. */ enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); +/// ditto +template isInstanceOf(alias S, alias T) +{ + enum impl(alias T : S!Args, Args...) = true; + enum impl(alias T) = false; + enum isInstanceOf = impl!T; +} /// -unittest +@safe unittest { static struct Foo(T...) { } static struct Bar(T...) { } static struct Doo(T) { } static struct ABC(int x) { } + static void fun(T)() { } + template templ(T) { } + static assert(isInstanceOf!(Foo, Foo!int)); static assert(!isInstanceOf!(Foo, Bar!int)); static assert(!isInstanceOf!(Foo, int)); static assert(isInstanceOf!(Doo, Doo!int)); static assert(isInstanceOf!(ABC, ABC!1)); - static assert(!__traits(compiles, isInstanceOf!(Foo, Foo))); + static assert(!isInstanceOf!(Foo, Foo)); + static assert(isInstanceOf!(fun, fun!int)); + static assert(isInstanceOf!(templ, templ!int)); +} + +@safe unittest +{ + static void fun1(T)() { } + static void fun2(T)() { } + template templ1(T) { } + template templ2(T) { } + + static assert(!isInstanceOf!(fun1, fun2!int)); + static assert(!isInstanceOf!(templ1, templ2!int)); } /** @@ -5449,32 +6357,38 @@ unittest * * See_Also: $(LREF isTypeTuple). */ -template isExpressionTuple(T ...) +template isExpressions(T ...) { static if (T.length >= 2) - enum bool isExpressionTuple = - isExpressionTuple!(T[0 .. $/2]) && - isExpressionTuple!(T[$/2 .. $]); + enum bool isExpressions = + isExpressions!(T[0 .. $/2]) && + isExpressions!(T[$/2 .. $]); else static if (T.length == 1) - enum bool isExpressionTuple = + enum bool isExpressions = !is(T[0]) && __traits(compiles, { auto ex = T[0]; }); else - enum bool isExpressionTuple = true; // default + enum bool isExpressions = true; // default } /// -unittest +@safe unittest { - static assert(isExpressionTuple!(1, 2.0, "a")); - static assert(!isExpressionTuple!(int, double, string)); - static assert(!isExpressionTuple!(int, 2.0, "a")); + static assert(isExpressions!(1, 2.0, "a")); + static assert(!isExpressions!(int, double, string)); + static assert(!isExpressions!(int, 2.0, "a")); } -unittest +/** + * Alternate name for $(LREF isExpressions), kept for legacy compatibility. + */ + +alias isExpressionTuple = isExpressions; + +@safe unittest { void foo(); static int bar() { return 42; } - enum aa = [ 1: -1 ]; + immutable aa = [ 1: -1 ]; alias myint = int; static assert( isExpressionTuple!(42)); @@ -5494,7 +6408,7 @@ unittest * Check whether the tuple $(D T) is a type tuple. * A type tuple only contains types. * - * See_Also: $(LREF isExpressionTuple). + * See_Also: $(LREF isExpressions). */ template isTypeTuple(T...) { @@ -5507,14 +6421,14 @@ template isTypeTuple(T...) } /// -unittest +@safe unittest { static assert(isTypeTuple!(int, float, string)); static assert(!isTypeTuple!(1, 2.0, "a")); static assert(!isTypeTuple!(1, double, string)); } -unittest +@safe unittest { class C {} void func(int) {} @@ -5551,7 +6465,7 @@ template isFunctionPointer(T...) } /// -unittest +@safe unittest { static void foo() {} void bar() {} @@ -5590,7 +6504,7 @@ template isDelegate(T...) } /// -unittest +@safe unittest { static void sfunc() { } int x; @@ -5630,7 +6544,7 @@ template isSomeFunction(T...) enum bool isSomeFunction = false; } -unittest +@safe unittest { static real func(ref int) { return 0; } static void prop() @property { } @@ -5685,7 +6599,7 @@ template isCallable(T...) } /// -unittest +@safe unittest { interface I { real value() @property; } struct S { static int opCall(int) { return 0; } } @@ -5703,7 +6617,7 @@ unittest /** - * Detect whether $(D T) is a an abstract function. + * Detect whether $(D T) is an abstract function. */ template isAbstractFunction(T...) if (T.length == 1) @@ -5711,18 +6625,19 @@ template isAbstractFunction(T...) enum bool isAbstractFunction = __traits(isAbstractFunction, T[0]); } -unittest +@safe unittest { struct S { void foo() { } } class C { void foo() { } } class AC { abstract void foo(); } + static assert(!isAbstractFunction!(int)); static assert(!isAbstractFunction!(S.foo)); static assert(!isAbstractFunction!(C.foo)); static assert( isAbstractFunction!(AC.foo)); } /** - * Detect whether $(D T) is a a final function. + * Detect whether $(D T) is a final function. */ template isFinalFunction(T...) if (T.length == 1) @@ -5731,7 +6646,7 @@ template isFinalFunction(T...) } /// -unittest +@safe unittest { struct S { void bar() { } } final class FC { void foo(); } @@ -5740,6 +6655,7 @@ unittest void bar() { } final void foo(); } + static assert(!isFinalFunction!(int)); static assert(!isFinalFunction!(S.bar)); static assert( isFinalFunction!(FC.foo)); static assert(!isFinalFunction!(C.bar)); @@ -5754,7 +6670,7 @@ template isNestedFunction(alias f) enum isNestedFunction = __traits(isNested, f); } -unittest +@safe unittest { static void f() { } void g() { } @@ -5763,7 +6679,7 @@ unittest } /** - * Detect whether $(D T) is a an abstract class. + * Detect whether $(D T) is an abstract class. */ template isAbstractClass(T...) if (T.length == 1) @@ -5772,7 +6688,7 @@ template isAbstractClass(T...) } /// -unittest +@safe unittest { struct S { } class C { } @@ -5780,10 +6696,14 @@ unittest static assert(!isAbstractClass!S); static assert(!isAbstractClass!C); static assert( isAbstractClass!AC); + C c; + static assert(!isAbstractClass!c); + AC ac; + static assert( isAbstractClass!ac); } /** - * Detect whether $(D T) is a a final class. + * Detect whether $(D T) is a final class. */ template isFinalClass(T...) if (T.length == 1) @@ -5792,7 +6712,7 @@ template isFinalClass(T...) } /// -unittest +@safe unittest { class C { } abstract class AC { } @@ -5802,6 +6722,10 @@ unittest static assert(!isFinalClass!AC); static assert( isFinalClass!FC1); static assert( isFinalClass!FC2); + C c; + static assert(!isFinalClass!c); + FC1 fc1; + static assert( isFinalClass!fc1); } //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// @@ -5836,7 +6760,7 @@ template Unqual(T) } /// -unittest +@safe unittest { static assert(is(Unqual!int == int)); static assert(is(Unqual!(const int) == int)); @@ -5845,7 +6769,7 @@ unittest static assert(is(Unqual!(shared(const int)) == int)); } -unittest +@safe unittest { static assert(is(Unqual!( int) == int)); static assert(is(Unqual!( const int) == int)); @@ -5862,31 +6786,140 @@ unittest } // [For internal use] -package template ModifyTypePreservingSTC(alias Modifier, T) +package template ModifyTypePreservingTQ(alias Modifier, T) { - static if (is(T U == immutable U)) alias ModifyTypePreservingSTC = immutable Modifier!U; - else static if (is(T U == shared inout const U)) alias ModifyTypePreservingSTC = shared inout const Modifier!U; - else static if (is(T U == shared inout U)) alias ModifyTypePreservingSTC = shared inout Modifier!U; - else static if (is(T U == shared const U)) alias ModifyTypePreservingSTC = shared const Modifier!U; - else static if (is(T U == shared U)) alias ModifyTypePreservingSTC = shared Modifier!U; - else static if (is(T U == inout const U)) alias ModifyTypePreservingSTC = inout const Modifier!U; - else static if (is(T U == inout U)) alias ModifyTypePreservingSTC = inout Modifier!U; - else static if (is(T U == const U)) alias ModifyTypePreservingSTC = const Modifier!U; - else alias ModifyTypePreservingSTC = Modifier!T; + static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; + else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; + else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; + else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; + else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; + else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; + else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; + else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; + else alias ModifyTypePreservingTQ = Modifier!T; } -unittest +@safe unittest { alias Intify(T) = int; - static assert(is(ModifyTypePreservingSTC!(Intify, real) == int)); - static assert(is(ModifyTypePreservingSTC!(Intify, const real) == const int)); - static assert(is(ModifyTypePreservingSTC!(Intify, inout real) == inout int)); - static assert(is(ModifyTypePreservingSTC!(Intify, inout const real) == inout const int)); - static assert(is(ModifyTypePreservingSTC!(Intify, shared real) == shared int)); - static assert(is(ModifyTypePreservingSTC!(Intify, shared const real) == shared const int)); - static assert(is(ModifyTypePreservingSTC!(Intify, shared inout real) == shared inout int)); - static assert(is(ModifyTypePreservingSTC!(Intify, shared inout const real) == shared inout const int)); - static assert(is(ModifyTypePreservingSTC!(Intify, immutable real) == immutable int)); + static assert(is(ModifyTypePreservingTQ!(Intify, real) == int)); + static assert(is(ModifyTypePreservingTQ!(Intify, const real) == const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, inout real) == inout int)); + static assert(is(ModifyTypePreservingTQ!(Intify, inout const real) == inout const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared real) == shared int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared const real) == shared const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared inout real) == shared inout int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared inout const real) == shared inout const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, immutable real) == immutable int)); +} + +/** + * Copies type qualifiers from $(D FromType) to $(D ToType). + * + * Supported type qualifiers: + * $(UL + * $(LI $(D const)) + * $(LI $(D inout)) + * $(LI $(D immutable)) + * $(LI $(D shared)) + * ) + */ +template CopyTypeQualifiers(FromType, ToType) +{ + alias T(U) = ToType; + alias CopyTypeQualifiers = ModifyTypePreservingTQ!(T, FromType); +} + +/// +@safe unittest +{ + static assert(is(CopyTypeQualifiers!(inout const real, int) == inout const int)); +} + +@safe unittest +{ + static assert(is(CopyTypeQualifiers!( real, int) == int)); + static assert(is(CopyTypeQualifiers!( const real, int) == const int)); + static assert(is(CopyTypeQualifiers!( inout real, int) == inout int)); + static assert(is(CopyTypeQualifiers!( inout const real, int) == inout const int)); + static assert(is(CopyTypeQualifiers!(shared real, int) == shared int)); + static assert(is(CopyTypeQualifiers!(shared const real, int) == shared const int)); + static assert(is(CopyTypeQualifiers!(shared inout real, int) == shared inout int)); + static assert(is(CopyTypeQualifiers!(shared inout const real, int) == shared inout const int)); + static assert(is(CopyTypeQualifiers!( immutable real, int) == immutable int)); +} + +/** +Returns the type of `Target` with the "constness" of `Source`. A type's $(BOLD constness) +refers to whether it is `const`, `immutable`, or `inout`. If `source` has no constness, the +returned type will be the same as `Target`. +*/ +template CopyConstness(FromType, ToType) +{ + alias Unshared(T) = T; + alias Unshared(T: shared U, U) = U; + + alias CopyConstness = Unshared!(CopyTypeQualifiers!(FromType, ToType)); +} + +/// +@safe unittest +{ + const(int) i; + CopyConstness!(typeof(i), float) f; + assert( is(typeof(f) == const float)); + + CopyConstness!(char, uint) u; + assert( is(typeof(u) == uint)); + + //The 'shared' qualifier will not be copied + assert(!is(CopyConstness!(shared bool, int) == shared int)); + + //But the constness will be + assert( is(CopyConstness!(shared const real, double) == const double)); + + //Careful, const(int)[] is a mutable array of const(int) + alias MutT = CopyConstness!(const(int)[], int); + assert(!is(MutT == const(int))); + + //Okay, const(int[]) applies to array and contained ints + alias CstT = CopyConstness!(const(int[]), int); + assert( is(CstT == const(int))); +} + +@safe unittest +{ + struct Test + { + void method1() {} + void method2() const {} + void method3() immutable {} + } + + assert(is(CopyConstness!(typeof(Test.method1), real) == real)); + + assert(is(CopyConstness!(typeof(Test.method2), byte) == const(byte))); + + assert(is(CopyConstness!(typeof(Test.method3), string) == immutable(string))); +} + +@safe unittest +{ + assert(is(CopyConstness!(inout(int)[], int[]) == int[])); + assert(is(CopyConstness!(inout(int[]), int[]) == inout(int[]))); +} + +@safe unittest +{ + static assert(is(CopyConstness!( int, real) == real)); + static assert(is(CopyConstness!(const int, real) == const real)); + static assert(is(CopyConstness!(inout int, real) == inout real)); + static assert(is(CopyConstness!(inout const int, real) == inout const real)); + static assert(is(CopyConstness!(shared int, real) == real)); + static assert(is(CopyConstness!(shared const int, real) == const real)); + static assert(is(CopyConstness!(shared inout int, real) == inout real)); + static assert(is(CopyConstness!(shared inout const int, real) == inout const real)); + static assert(is(CopyConstness!(immutable int, real) == immutable real)); } /** @@ -5901,7 +6934,7 @@ template ForeachType(T) alias ForeachType = ReturnType!(typeof( (inout int x = 0) { - foreach(elem; T.init) + foreach (elem; T.init) { return elem; } @@ -5910,7 +6943,7 @@ template ForeachType(T) } /// -unittest +@safe unittest { static assert(is(ForeachType!(uint[]) == uint)); static assert(is(ForeachType!string == immutable(char))); @@ -5930,11 +6963,11 @@ template OriginalType(T) else alias Impl = T; } - alias OriginalType = ModifyTypePreservingSTC!(Impl, T); + alias OriginalType = ModifyTypePreservingTQ!(Impl, T); } /// -unittest +@safe unittest { enum E : real { a } enum F : E { a = E.a } @@ -5950,7 +6983,7 @@ unittest alias KeyType(V : V[K], K) = K; /// -unittest +@safe unittest { import std.traits; alias Hash = int[string]; @@ -5966,7 +6999,7 @@ unittest alias ValueType(V : V[K], K) = V; /// -unittest +@safe unittest { import std.traits; alias Hash = int[string]; @@ -5994,16 +7027,17 @@ template Unsigned(T) static if (is(T == short)) alias Impl = ushort; static if (is(T == int )) alias Impl = uint; static if (is(T == long )) alias Impl = ulong; + static if (is(ucent) && is(T == cent )) alias Impl = ucent; } else static assert(false, "Type " ~ T.stringof ~ " does not have an Unsigned counterpart"); } - alias Unsigned = ModifyTypePreservingSTC!(Impl, OriginalType!T); + alias Unsigned = ModifyTypePreservingTQ!(Impl, OriginalType!T); } -unittest +@safe unittest { alias U1 = Unsigned!int; alias U2 = Unsigned!(const(int)); @@ -6013,14 +7047,23 @@ unittest static assert(is(U3 == immutable(uint))); static if (is(__vector(int[4])) && is(__vector(uint[4]))) { - alias Unsigned!(__vector(int[4])) UV1; - alias Unsigned!(const(__vector(int[4]))) UV2; + alias UV1 = Unsigned!(__vector(int[4])); + alias UV2 = Unsigned!(const(__vector(int[4]))); static assert(is(UV1 == __vector(uint[4]))); static assert(is(UV2 == const(__vector(uint[4])))); } //struct S {} //alias U2 = Unsigned!S; //alias U3 = Unsigned!double; + static if (is(ucent)) + { + alias U4 = Unsigned!cent; + alias U5 = Unsigned!(const(cent)); + alias U6 = Unsigned!(immutable(cent)); + static assert(is(U4 == ucent)); + static assert(is(U5 == const(ucent))); + static assert(is(U6 == immutable(ucent))); + } } /** @@ -6028,7 +7071,7 @@ Returns the largest type, i.e. T such that T.sizeof is the largest. If more than one type is of the same size, the leftmost argument of these in will be returned. */ -template Largest(T...) if(T.length >= 1) +template Largest(T...) if (T.length >= 1) { static if (T.length == 1) { @@ -6036,7 +7079,7 @@ template Largest(T...) if(T.length >= 1) } else static if (T.length == 2) { - static if(T[0].sizeof >= T[1].sizeof) + static if (T[0].sizeof >= T[1].sizeof) { alias Largest = T[0]; } @@ -6052,12 +7095,14 @@ template Largest(T...) if(T.length >= 1) } /// -unittest +@safe unittest { static assert(is(Largest!(uint, ubyte, ushort, real) == real)); static assert(is(Largest!(ulong, double) == ulong)); static assert(is(Largest!(double, ulong) == double)); static assert(is(Largest!(uint, byte, double, short) == double)); + static if (is(ucent)) + static assert(is(Largest!(uint, ubyte, ucent, ushort) == ucent)); } /** @@ -6078,17 +7123,18 @@ template Signed(T) static if (is(T == ushort)) alias Impl = short; static if (is(T == uint )) alias Impl = int; static if (is(T == ulong )) alias Impl = long; + static if (is(ucent) && is(T == ucent )) alias Impl = cent; } else static assert(false, "Type " ~ T.stringof ~ " does not have an Signed counterpart"); } - alias Signed = ModifyTypePreservingSTC!(Impl, OriginalType!T); + alias Signed = ModifyTypePreservingTQ!(Impl, OriginalType!T); } /// -unittest +@safe unittest { alias S1 = Signed!uint; static assert(is(S1 == int)); @@ -6096,15 +7142,20 @@ unittest static assert(is(S2 == const(int))); alias S3 = Signed!(immutable(uint)); static assert(is(S3 == immutable(int))); + static if (is(ucent)) + { + alias S4 = Signed!ucent; + static assert(is(S4 == cent)); + } } -unittest +@safe unittest { static assert(is(Signed!float == float)); static if (is(__vector(int[4])) && is(__vector(uint[4]))) { - alias Signed!(__vector(uint[4])) SV1; - alias Signed!(const(__vector(uint[4]))) SV2; + alias SV1 = Signed!(__vector(uint[4])); + alias SV2 = Signed!(const(__vector(uint[4]))); static assert(is(SV1 == __vector(int[4]))); static assert(is(SV2 == const(__vector(int[4])))); } @@ -6115,7 +7166,7 @@ unittest Returns the most negative value of the numeric type T. */ template mostNegative(T) - if(isNumeric!T || isSomeChar!T || isBoolean!T) + if (isNumeric!T || isSomeChar!T || isBoolean!T) { static if (is(typeof(T.min_normal))) enum mostNegative = -T.max; @@ -6126,7 +7177,7 @@ template mostNegative(T) } /// -unittest +@safe unittest { static assert(mostNegative!float == -float.max); static assert(mostNegative!double == -double.max); @@ -6135,15 +7186,53 @@ unittest } /// -unittest +@safe unittest { - foreach(T; TypeTuple!(bool, byte, short, int, long)) + foreach (T; TypeTuple!(bool, byte, short, int, long)) static assert(mostNegative!T == T.min); - foreach(T; TypeTuple!(ubyte, ushort, uint, ulong, char, wchar, dchar)) + foreach (T; TypeTuple!(ubyte, ushort, uint, ulong, char, wchar, dchar)) static assert(mostNegative!T == 0); } +/** +Get the type that a scalar type `T` will $(LINK2 $(ROOT_DIR)spec/type.html#integer-promotions, promote) +to in multi-term arithmetic expressions. +*/ +template Promoted(T) + if (isScalarType!T) +{ + alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init)); +} + +/// +unittest +{ + ubyte a = 3, b = 5; + static assert(is(typeof(a * b) == Promoted!ubyte)); + static assert(is(Promoted!ubyte == int)); + + static assert(is(Promoted!(shared(bool)) == shared(int))); + static assert(is(Promoted!(const(int)) == const(int))); + static assert(is(Promoted!double == double)); +} + +unittest +{ + // promote to int: + foreach (T; TypeTuple!(bool, byte, ubyte, short, ushort, char, wchar)) + { + static assert(is(Promoted!T == int)); + static assert(is(Promoted!(shared(const T)) == shared(const int))); + } + + // already promoted: + foreach (T; TypeTuple!(int, uint, long, ulong, float, double, real)) + { + static assert(is(Promoted!T == T)); + static assert(is(Promoted!(immutable(T)) == immutable(T))); + } +} //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// // Misc. @@ -6229,7 +7318,7 @@ private string removeDummyEnvelope(string s) return s; } -unittest +@safe unittest { class C { int value() @property { return 0; } } static assert(mangledName!int == int.mangleof); @@ -6245,15 +7334,17 @@ unittest static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc } -unittest +@system unittest { + // @system due to demangle // Test for bug 5718 - import std.demangle; + import std.demangle : demangle; int foo; auto foo_demangled = demangle(mangledName!foo); - assert(foo_demangled[0 .. 4] == "int " && foo_demangled[$-3 .. $] == "foo"); + assert(foo_demangled[0 .. 4] == "int " && foo_demangled[$-3 .. $] == "foo", + foo_demangled); - void bar(){} + void bar(); auto bar_demangled = demangle(mangledName!bar); assert(bar_demangled[0 .. 5] == "void " && bar_demangled[$-5 .. $] == "bar()"); } @@ -6268,15 +7359,18 @@ and to $(D T[1]) otherwise. */ template Select(bool condition, T...) if (T.length == 2) { - alias Select = T[!condition]; + import std.meta : Alias; + alias Select = Alias!(T[!condition]); } /// -unittest +@safe unittest { // can select types static assert(is(Select!(true, int, long) == int)); static assert(is(Select!(false, int, long) == long)); + static struct Foo {} + static assert(is(Select!(false, const(int), const(Foo)) == const(Foo))); // can select symbols int a = 1; @@ -6285,6 +7379,10 @@ unittest alias selB = Select!(false, a, b); assert(selA == 1); assert(selB == 2); + + // can select (compile-time) expressions + enum val = Select!(false, -4, 9 - 6); + static assert(val == 3); } /** @@ -6295,7 +7393,7 @@ A select(bool cond : true, A, B)(A a, lazy B b) { return a; } /// Ditto B select(bool cond : false, A, B)(lazy A a, B b) { return b; } -unittest +@safe unittest { real pleasecallme() { return 0; } int dontcallme() { assert(0); } @@ -6304,3 +7402,557 @@ unittest static assert(is(typeof(a) == real)); static assert(is(typeof(b) == real)); } + +/++ + Determine if a symbol has a given + $(DDSUBLINK spec/attribute, uda, user-defined attribute). + + See_Also: + $(LREF getUDAs) + +/ +template hasUDA(alias symbol, alias attribute) +{ + enum hasUDA = getUDAs!(symbol, attribute).length != 0; +} + +/// +@safe unittest +{ + enum E; + struct S {} + + @("alpha") int a; + static assert(hasUDA!(a, "alpha")); + static assert(!hasUDA!(a, S)); + static assert(!hasUDA!(a, E)); + + @(E) int b; + static assert(!hasUDA!(b, "alpha")); + static assert(!hasUDA!(b, S)); + static assert(hasUDA!(b, E)); + + @E int c; + static assert(!hasUDA!(c, "alpha")); + static assert(!hasUDA!(c, S)); + static assert(hasUDA!(c, E)); + + @(S, E) int d; + static assert(!hasUDA!(d, "alpha")); + static assert(hasUDA!(d, S)); + static assert(hasUDA!(d, E)); + + @S int e; + static assert(!hasUDA!(e, "alpha")); + static assert(hasUDA!(e, S)); + static assert(!hasUDA!(e, S())); + static assert(!hasUDA!(e, E)); + + @S() int f; + static assert(!hasUDA!(f, "alpha")); + static assert(hasUDA!(f, S)); + static assert(hasUDA!(f, S())); + static assert(!hasUDA!(f, E)); + + @(S, E, "alpha") int g; + static assert(hasUDA!(g, "alpha")); + static assert(hasUDA!(g, S)); + static assert(hasUDA!(g, E)); + + @(100) int h; + static assert(hasUDA!(h, 100)); + + struct Named { string name; } + + @Named("abc") int i; + static assert(hasUDA!(i, Named)); + static assert(hasUDA!(i, Named("abc"))); + static assert(!hasUDA!(i, Named("def"))); + + struct AttrT(T) + { + string name; + T value; + } + + @AttrT!int("answer", 42) int j; + static assert(hasUDA!(j, AttrT)); + static assert(hasUDA!(j, AttrT!int)); + static assert(!hasUDA!(j, AttrT!string)); + + @AttrT!string("hello", "world") int k; + static assert(hasUDA!(k, AttrT)); + static assert(!hasUDA!(k, AttrT!int)); + static assert(hasUDA!(k, AttrT!string)); + + struct FuncAttr(alias f) { alias func = f; } + static int fourtyTwo() { return 42; } + static size_t getLen(string s) { return s.length; } + + @FuncAttr!getLen int l; + static assert(hasUDA!(l, FuncAttr)); + static assert(!hasUDA!(l, FuncAttr!fourtyTwo)); + static assert(hasUDA!(l, FuncAttr!getLen)); + static assert(!hasUDA!(l, FuncAttr!fourtyTwo())); + static assert(!hasUDA!(l, FuncAttr!getLen())); + + @FuncAttr!getLen() int m; + static assert(hasUDA!(m, FuncAttr)); + static assert(!hasUDA!(m, FuncAttr!fourtyTwo)); + static assert(hasUDA!(m, FuncAttr!getLen)); + static assert(!hasUDA!(m, FuncAttr!fourtyTwo())); + static assert(hasUDA!(m, FuncAttr!getLen())); +} + +/++ + Gets the matching $(DDSUBLINK spec/attribute, uda, user-defined attributes) + from the given symbol. + + If the UDA is a type, then any UDAs of the same type on the symbol will + match. If the UDA is a template for a type, then any UDA which is an + instantiation of that template will match. And if the UDA is a value, + then any UDAs on the symbol which are equal to that value will match. + + See_Also: + $(LREF hasUDA) + +/ +template getUDAs(alias symbol, alias attribute) +{ + import std.meta : Filter; + + template isDesiredUDA(alias toCheck) + { + static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) + { + static if (__traits(compiles, toCheck == attribute)) + enum isDesiredUDA = toCheck == attribute; + else + enum isDesiredUDA = false; + } + else static if (is(typeof(toCheck))) + { + static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); + else + enum isDesiredUDA = is(typeof(toCheck) == attribute); + } + else static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, toCheck); + else + enum isDesiredUDA = is(toCheck == attribute); + } + alias getUDAs = Filter!(isDesiredUDA, __traits(getAttributes, symbol)); +} + +/// +@safe unittest +{ + struct Attr + { + string name; + int value; + } + + @Attr("Answer", 42) int a; + static assert(getUDAs!(a, Attr).length == 1); + static assert(getUDAs!(a, Attr)[0].name == "Answer"); + static assert(getUDAs!(a, Attr)[0].value == 42); + + @(Attr("Answer", 42), "string", 9999) int b; + static assert(getUDAs!(b, Attr).length == 1); + static assert(getUDAs!(b, Attr)[0].name == "Answer"); + static assert(getUDAs!(b, Attr)[0].value == 42); + + @Attr("Answer", 42) @Attr("Pi", 3) int c; + static assert(getUDAs!(c, Attr).length == 2); + static assert(getUDAs!(c, Attr)[0].name == "Answer"); + static assert(getUDAs!(c, Attr)[0].value == 42); + static assert(getUDAs!(c, Attr)[1].name == "Pi"); + static assert(getUDAs!(c, Attr)[1].value == 3); + + static assert(getUDAs!(c, Attr("Answer", 42)).length == 1); + static assert(getUDAs!(c, Attr("Answer", 42))[0].name == "Answer"); + static assert(getUDAs!(c, Attr("Answer", 42))[0].value == 42); + + static assert(getUDAs!(c, Attr("Answer", 99)).length == 0); + + struct AttrT(T) + { + string name; + T value; + } + + @AttrT!uint("Answer", 42) @AttrT!int("Pi", 3) @AttrT int d; + static assert(getUDAs!(d, AttrT).length == 2); + static assert(getUDAs!(d, AttrT)[0].name == "Answer"); + static assert(getUDAs!(d, AttrT)[0].value == 42); + static assert(getUDAs!(d, AttrT)[1].name == "Pi"); + static assert(getUDAs!(d, AttrT)[1].value == 3); + + static assert(getUDAs!(d, AttrT!uint).length == 1); + static assert(getUDAs!(d, AttrT!uint)[0].name == "Answer"); + static assert(getUDAs!(d, AttrT!uint)[0].value == 42); + + static assert(getUDAs!(d, AttrT!int).length == 1); + static assert(getUDAs!(d, AttrT!int)[0].name == "Pi"); + static assert(getUDAs!(d, AttrT!int)[0].value == 3); + + struct SimpleAttr {} + + @SimpleAttr int e; + static assert(getUDAs!(e, SimpleAttr).length == 1); + static assert(is(getUDAs!(e, SimpleAttr)[0] == SimpleAttr)); + + @SimpleAttr() int f; + static assert(getUDAs!(f, SimpleAttr).length == 1); + static assert(is(typeof(getUDAs!(f, SimpleAttr)[0]) == SimpleAttr)); + + struct FuncAttr(alias f) { alias func = f; } + static int add42(int v) { return v + 42; } + static string concat(string l, string r) { return l ~ r; } + + @FuncAttr!add42 int g; + static assert(getUDAs!(g, FuncAttr).length == 1); + static assert(getUDAs!(g, FuncAttr)[0].func(5) == 47); + + static assert(getUDAs!(g, FuncAttr!add42).length == 1); + static assert(getUDAs!(g, FuncAttr!add42)[0].func(5) == 47); + + static assert(getUDAs!(g, FuncAttr!add42()).length == 0); + + static assert(getUDAs!(g, FuncAttr!concat).length == 0); + static assert(getUDAs!(g, FuncAttr!concat()).length == 0); + + @FuncAttr!add42() int h; + static assert(getUDAs!(h, FuncAttr).length == 1); + static assert(getUDAs!(h, FuncAttr)[0].func(5) == 47); + + static assert(getUDAs!(h, FuncAttr!add42).length == 1); + static assert(getUDAs!(h, FuncAttr!add42)[0].func(5) == 47); + + static assert(getUDAs!(h, FuncAttr!add42()).length == 1); + static assert(getUDAs!(h, FuncAttr!add42())[0].func(5) == 47); + + static assert(getUDAs!(h, FuncAttr!concat).length == 0); + static assert(getUDAs!(h, FuncAttr!concat()).length == 0); + + @("alpha") @(42) int i; + static assert(getUDAs!(i, "alpha").length == 1); + static assert(getUDAs!(i, "alpha")[0] == "alpha"); + + static assert(getUDAs!(i, 42).length == 1); + static assert(getUDAs!(i, 42)[0] == 42); + + static assert(getUDAs!(i, 'c').length == 0); +} + +/** + * Gets all symbols within `symbol` that have the given user-defined attribute. + * This is not recursive; it will not search for symbols within symbols such as + * nested structs or unions. + */ +template getSymbolsByUDA(alias symbol, alias attribute) { + import std.format : format; + import std.meta : AliasSeq, Filter; + + // translate a list of strings into symbols. mixing in the entire alias + // avoids trying to access the symbol, which could cause a privacy violation + template toSymbols(names...) { + static if (names.length == 0) + alias toSymbols = AliasSeq!(); + else + mixin("alias toSymbols = AliasSeq!(symbol.%s, toSymbols!(names[1..$]));" + .format(names[0])); + } + + // filtering out nested class context + enum noThisMember(string name) = (name != "this"); + alias membersWithoutNestedCC = Filter!(noThisMember, __traits(allMembers, symbol)); + + enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol.%s, attribute)".format(name)); + alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, membersWithoutNestedCC)); + + // if the symbol itself has the UDA, tack it on to the front of the list + static if (hasUDA!(symbol, attribute)) + alias getSymbolsByUDA = AliasSeq!(symbol, membersWithUDA); + else + alias getSymbolsByUDA = membersWithUDA; +} + +/// +@safe unittest +{ + enum Attr; + + static struct A + { + @Attr int a; + int b; + @Attr void doStuff() {} + void doOtherStuff() {} + static struct Inner + { + // Not found by getSymbolsByUDA + @Attr int c; + } + } + + // Finds both variables and functions with the attribute, but + // doesn't include the variables and functions without it. + static assert(getSymbolsByUDA!(A, Attr).length == 2); + // Can access attributes on the symbols returned by getSymbolsByUDA. + static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); + static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr)); + + static struct UDA { string name; } + + static struct B + { + @UDA("X") + int x; + @UDA("Y") + int y; + @(100) + int z; + } + + // Finds both UDA attributes. + static assert(getSymbolsByUDA!(B, UDA).length == 2); + // Finds one `100` attribute. + static assert(getSymbolsByUDA!(B, 100).length == 1); + // Can get the value of the UDA from the return value + static assert(getUDAs!(getSymbolsByUDA!(B, UDA)[0], UDA)[0].name == "X"); + + @UDA("A") + static struct C + { + @UDA("B") + int d; + } + + // Also checks the symbol itself + static assert(getSymbolsByUDA!(C, UDA).length == 2); + static assert(getSymbolsByUDA!(C, UDA)[0].stringof == "C"); + static assert(getSymbolsByUDA!(C, UDA)[1].stringof == "d"); + + static struct D + { + int x; + } + + //Finds nothing if there is no member with specific UDA + static assert(getSymbolsByUDA!(D,UDA).length == 0); +} + +// #15335: getSymbolsByUDA fails if type has private members +@safe unittest +{ + // HasPrivateMembers has, well, private members, one of which has a UDA. + import std.internal.test.uda : Attr, HasPrivateMembers; + static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 2); + static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr)); + static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[1], Attr)); +} + +// #16387: getSymbolsByUDA works with structs but fails with classes +unittest +{ + enum Attr; + class A + { + @Attr uint a; + } + + alias res = getSymbolsByUDA!(A, Attr); + static assert(res.length == 1); + static assert(res[0].stringof == "a"); +} + +/** + Returns: $(D true) iff all types $(D T) are the same. +*/ +template allSameType(T...) +{ + static if (T.length <= 1) + { + enum bool allSameType = true; + } + else + { + enum bool allSameType = is(T[0] == T[1]) && allSameType!(T[1..$]); + } +} + +/// +@safe unittest +{ + static assert(allSameType!(int, int)); + static assert(allSameType!(int, int, int)); + static assert(allSameType!(float, float, float)); + static assert(!allSameType!(int, double)); + static assert(!allSameType!(int, float, double)); + static assert(!allSameType!(int, float, double, real)); + static assert(!allSameType!(short, int, float, double, real)); +} + +/** + Returns: $(D true) iff the type $(D T) can be tested in an $(D + if)-expression, that is if $(D if (pred(T.init)) {}) is compilable. +*/ +enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) {} }); + +@safe unittest +{ + import std.meta : AliasSeq, allSatisfy; + static assert(allSatisfy!(ifTestable, AliasSeq!(bool, int, float, double, string))); + struct BoolWrapper { bool value; } + static assert(!ifTestable!(bool, a => BoolWrapper(a))); +} + +/** + * Detect whether `X` is a type. Analogous to `is(X)`. This is useful when used + * in conjunction with other templates, e.g. `allSatisfy!(isType, X)`. + * + * Returns: + * `true` if `X` is a type, `false` otherwise + */ +template isType(X...) if (X.length == 1) +{ + enum isType = is(X[0]); +} + +/// +@safe unittest +{ + struct S { + template Test() {} + } + class C {} + interface I {} + union U {} + static assert(isType!int); + static assert(isType!string); + static assert(isType!(int[int])); + static assert(isType!S); + static assert(isType!C); + static assert(isType!I); + static assert(isType!U); + + int n; + void func(){} + static assert(!isType!n); + static assert(!isType!func); + static assert(!isType!(S.Test)); + static assert(!isType!(S.Test!())); +} + +/** + * Detect whether symbol or type `X` is a function. This is different that finding + * if a symbol is callable or satisfying `is(X == function)`, it finds + * specifically if the symbol represents a normal function declaration, i.e. + * not a delegate or a function pointer. + * + * Returns: + * `true` if `X` is a function, `false` otherwise + * + * See_Also: + * Use $(LREF isFunctionPointer) or $(LREF isDelegate) for detecting those types + * respectively. + */ +template isFunction(X...) if (X.length == 1) +{ + static if (is(typeof(&X[0]) U : U*) && is(U == function) || + is(typeof(&X[0]) U == delegate)) + { + // x is a (nested) function symbol. + enum isFunction = true; + } + else static if (is(X[0] T)) + { + // x is a type. Take the type of it and examine. + enum isFunction = is(T == function); + } + else + enum isFunction = false; +} + +/// +@safe unittest +{ + static void func(){} + static assert(isFunction!func); + + struct S + { + void func(){} + } + static assert(isFunction!(S.func)); +} + +/** + * Detect whether `X` is a final method or class. + * + * Returns: + * `true` if `X` is final, `false` otherwise + */ +template isFinal(X...) if (X.length == 1) +{ + static if (is(X[0] == class)) + enum isFinal = __traits(isFinalClass, X[0]); + else static if (isFunction!X) + enum isFinal = __traits(isFinalFunction, X[0]); + else + enum isFinal = false; +} + +/// +@safe unittest +{ + class C + { + void nf() {} + static void sf() {} + final void ff() {} + } + final class FC { } + + static assert(!isFinal!(C)); + static assert( isFinal!(FC)); + + static assert(!isFinal!(C.nf)); + static assert(!isFinal!(C.sf)); + static assert( isFinal!(C.ff)); +} + +/++ + + Determines whether the type `S` can be copied. + + If a type cannot be copied, then code such as `MyStruct x; auto y = x;` will fail to compile. + + Copying for structs can be disabled by using `@disable this(this)`. + + + + Params: + + S = The type to check. + + + + Returns: + + `true` if `S` can be copied. `false` otherwise. + + ++/ +enum isCopyable(S) = is(typeof( + { S foo = S.init; S copy = foo; } +)); + +/// +@safe unittest +{ + struct S1 {} // Fine. Can be copied + struct S2 { this(this) {}} // Fine. Can be copied + struct S3 {@disable this(this) {}} // Not fine. Copying is disabled. + struct S4 {S3 s;} // Not fine. A field has copying disabled. + + class C1 {} + + static assert( isCopyable!S1); + static assert( isCopyable!S2); + static assert(!isCopyable!S3); + static assert(!isCopyable!S4); + + static assert(isCopyable!C1); + static assert(isCopyable!int); +} diff --git a/std/typecons.d b/std/typecons.d index d7a490d527d..6671f5e9d46 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -6,10 +6,6 @@ that allow construction of new, useful general-purpose types. Source: $(PHOBOSSRC std/_typecons.d) -Macros: - -WIKI = Phobos/StdVariant - Synopsis: ---- @@ -35,26 +31,41 @@ void bar() ---- Copyright: Copyright the respective authors, 2008- -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: $(WEB erdani.org, Andrei Alexandrescu), - $(WEB bartoszmilewski.wordpress.com, Bartosz Milewski), +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu), + $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski), Don Clugston, Shin Fujishiro, Kenji Hara */ module std.typecons; + +import core.stdc.stdint : uintptr_t; +import std.meta; // : AliasSeq, allSatisfy; import std.traits; -// FIXME -import std.typetuple; // : TypeTuple, allSatisfy; debug(Unique) import std.stdio; /** -Encapsulates unique ownership of a resource. Resource of type $(D T) is -deleted at the end of the scope, unless it is transferred. The -transfer can be explicit, by calling $(D release), or implicit, when -returning Unique from a function. The resource can be a polymorphic -class object, in which case Unique behaves polymorphically too. +Encapsulates unique ownership of a resource. + +When a $(D Unique!T) goes out of scope it will call $(D destroy) +on the resource $(D T) that it manages, unless it is transferred. +One important consequence of $(D destroy) is that it will call the +destructor of the resource $(D T). GC-managed references are not +guaranteed to be valid during a destructor call, but other members of +$(D T), such as file handles or pointers to $(D malloc) memory, will +still be valid during the destructor call. This allows the resource +$(D T) to deallocate or clean up any non-GC resources. + +If it is desirable to persist a $(D Unique!T) outside of its original +scope, then it can be transferred. The transfer can be explicit, by +calling $(D release), or implicit, when returning Unique from a +function. The resource $(D T) can be a polymorphic class object, in +which case Unique behaves polymorphically too. + +If $(D T) is a value type, then $(D Unique!T) will be implemented +as a reference to a $(D T). */ struct Unique(T) { @@ -149,25 +160,30 @@ public: ~this() { debug(Unique) writeln("Unique destructor of ", (_p is null)? null: _p); - if (_p !is null) delete _p; - _p = null; + if (_p !is null) + { + destroy(_p); + _p = null; + } } + /** Returns whether the resource exists. */ @property bool isEmpty() const { return _p is null; } - /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. */ + /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. + Same as calling std.algorithm.move on it. + */ Unique release() { - debug(Unique) writeln("Release"); - auto u = Unique(_p); - assert(_p is null); - debug(Unique) writeln("return from Release"); - return u; + debug(Unique) writeln("Unique Release"); + import std.algorithm.mutation : move; + return this.move; } + /** Forwards member access to contents. */ - RefT opDot() { return _p; } + mixin Proxy!_p; /** Postblit operator is undefined to prevent the cloning of $(D Unique) objects. @@ -179,7 +195,7 @@ private: } /// -unittest +@system unittest { static struct S { @@ -214,7 +230,7 @@ unittest assert(u1.isEmpty); } -unittest +@system unittest { // test conversion to base ref int deleted = 0; @@ -239,7 +255,7 @@ unittest assert(deleted == 2); } -unittest +@system unittest { debug(Unique) writeln("Unique class"); class Bar @@ -264,13 +280,14 @@ unittest assert(!ub2.isEmpty); } -unittest +@system unittest { debug(Unique) writeln("Unique struct"); struct Foo { ~this() { debug(Unique) writeln(" Foo destructor"); } int val() const { return 3; } + @disable this(this); } alias UFoo = Unique!(Foo); @@ -291,9 +308,41 @@ unittest assert(!uf2.isEmpty); } +// ensure Unique behaves correctly through const access paths +@system unittest +{ + struct Bar {int val;} + struct Foo + { + Unique!Bar bar = new Bar; + } + + Foo foo; + foo.bar.val = 6; + const Foo* ptr = &foo; + static assert(is(typeof(ptr) == const(Foo*))); + static assert(is(typeof(ptr.bar) == const(Unique!Bar))); + static assert(is(typeof(ptr.bar.val) == const(int))); + assert(ptr.bar.val == 6); + foo.bar.val = 7; + assert(ptr.bar.val == 7); +} + +// Used in Tuple.toString +private template sharedToString(alias field) + if (is(typeof(field) == shared)) +{ + static immutable sharedToString = typeof(field).stringof; +} + +private template sharedToString(alias field) + if (!is(typeof(field) == shared)) +{ + alias sharedToString = field; +} /** -Tuple of values, for example $(D Tuple!(int, string)) is a record that +_Tuple of values, for example $(D Tuple!(int, string)) is a record that stores an $(D int) and a $(D string). $(D Tuple) can be used to bundle values together, notably when returning multiple values from a function. If $(D obj) is a `Tuple`, the individual members are @@ -301,16 +350,18 @@ accessible with the syntax $(D obj[0]) for the first field, $(D obj[1]) for the second, and so on. The choice of zero-based indexing instead of one-base indexing was -motivated by the ability to use value `Tuple`s with various compile-time -loop constructs (e.g. $(XREF typetuple, TypeTuple) iteration), all of which use +motivated by the ability to use value tuples with various compile-time +loop constructs (e.g. $(REF AliasSeq, std,meta) iteration), all of which use zero-based indexing. +See_Also: $(LREF tuple). + Params: Specs = A list of types (and optionally, member names) that the `Tuple` contains. */ template Tuple(Specs...) { - import std.typetuple : staticMap; + import std.meta : staticMap; // Parse (type,name) pairs (FieldSpecs) out of the specified // arguments. Some fields would have name, others not. @@ -318,21 +369,21 @@ template Tuple(Specs...) { static if (Specs.length == 0) { - alias parseSpecs = TypeTuple!(); + alias parseSpecs = AliasSeq!(); } else static if (is(Specs[0])) { static if (is(typeof(Specs[1]) : string)) { alias parseSpecs = - TypeTuple!(FieldSpec!(Specs[0 .. 2]), - parseSpecs!(Specs[2 .. $])); + AliasSeq!(FieldSpec!(Specs[0 .. 2]), + parseSpecs!(Specs[2 .. $])); } else { alias parseSpecs = - TypeTuple!(FieldSpec!(Specs[0]), - parseSpecs!(Specs[1 .. $])); + AliasSeq!(FieldSpec!(Specs[0]), + parseSpecs!(Specs[1 .. $])); } } else @@ -385,18 +436,17 @@ template Tuple(Specs...) { static if (spec.name.length == 0) { - alias expandSpec = TypeTuple!(spec.Type); + alias expandSpec = AliasSeq!(spec.Type); } else { - alias expandSpec = TypeTuple!(spec.Type, spec.name); + alias expandSpec = AliasSeq!(spec.Type, spec.name); } } enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof( + (ref Tup1 tup1, ref Tup2 tup2) { - Tup1 tup1 = void; - Tup2 tup2 = void; static assert(tup1.field.length == tup2.field.length); foreach (i, _; Tup1.Types) { @@ -434,24 +484,24 @@ template Tuple(Specs...) * The types of the `Tuple`'s components. */ alias Types = staticMap!(extractType, fieldSpecs); - + /// - unittest + static if (Specs.length == 0) @safe unittest { alias Fields = Tuple!(int, "id", string, float); - static assert(is(Fields.Types == TypeTuple!(int, string, float))); + static assert(is(Fields.Types == AliasSeq!(int, string, float))); } /** * The names of the `Tuple`'s components. Unnamed fields have empty names. */ alias fieldNames = staticMap!(extractName, fieldSpecs); - + /// - unittest + static if (Specs.length == 0) @safe unittest { alias Fields = Tuple!(int, "id", string, float); - static assert(Fields.fieldNames == TypeTuple!("id", "", "")); + static assert(Fields.fieldNames == AliasSeq!("id", "", "")); } /** @@ -462,18 +512,18 @@ template Tuple(Specs...) */ Types expand; mixin(injectNamedFields()); - + /// - unittest + static if (Specs.length == 0) @safe unittest { auto t1 = tuple(1, " hello ", 2.3); assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`); - - void takeSeveralTypes(int n, string s, bool b) + + void takeSeveralTypes(int n, string s, bool b) { assert(n == 4 && s == "test" && b == false); } - + auto t2 = tuple(4, "test", false); //t.expand acting as a list of values takeSeveralTypes(t2.expand); @@ -509,8 +559,8 @@ template Tuple(Specs...) * Params: * values = A list of values that are either the same * types as those given by the `Types` field - * of this `Tuple`, or can implicitly convert - * to those types. They must be in the same + * of this `Tuple`, or can implicitly convert + * to those types. They must be in the same * order as they appear in `Types`. */ static if (Types.length > 0) @@ -520,9 +570,9 @@ template Tuple(Specs...) field[] = values[]; } } - + /// - unittest + static if (Specs.length == 0) @safe unittest { alias ISD = Tuple!(int, string, double); auto tup = ISD(1, "test", 3.2); @@ -544,9 +594,9 @@ template Tuple(Specs...) field[i] = values[i]; } } - + /// - unittest + static if (Specs.length == 0) @safe unittest { int[2] ints; Tuple!(int, int) t = ints; @@ -567,15 +617,15 @@ template Tuple(Specs...) { field[] = another.field[]; } - + /// - unittest + static if (Specs.length == 0) @safe unittest { alias IntVec = Tuple!(int, int, int); alias DubVec = Tuple!(double, double, double); - + IntVec iv = tuple(1, 1, 1); - + //Ok, int can implicitly convert to double DubVec dv = iv; //Error: double cannot implicitly convert to int @@ -586,13 +636,13 @@ template Tuple(Specs...) * Comparison for equality. Two `Tuple`s are considered equal * $(B iff) they fulfill the following criteria: * - * $(UL + * $(UL * $(LI Each `Tuple` is the same length.) - * $(LI For each type `T` on the left-hand side and each type - * `U` on the right-hand side, values of type `T` can be + * $(LI For each type `T` on the left-hand side and each type + * `U` on the right-hand side, values of type `T` can be * compared with values of type `U`.) - * $(LI For each value `v1` on the left-hand side and each value - * `v2` on the right-hand side, the expression `v1 == v2` is + * $(LI For each value `v1` on the left-hand side and each value + * `v2` on the right-hand side, the expression `v1 == v2` is * true.)) * * Params: @@ -607,16 +657,16 @@ template Tuple(Specs...) { return field[] == rhs.field[]; } - + /// ditto bool opEquals(R)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "==")) { return field[] == rhs.field[]; } - + /// - unittest + static if (Specs.length == 0) @safe unittest { Tuple!(int, string) t1 = tuple(1, "test"); Tuple!(double, string) t2 = tuple(1.0, "test"); @@ -653,7 +703,7 @@ template Tuple(Specs...) } return 0; } - + /// ditto int opCmp(R)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "<")) @@ -667,17 +717,17 @@ template Tuple(Specs...) } return 0; } - - /** + + /** The first `v1` for which `v1 > v2` is true determines the result. This could lead to unexpected behaviour. */ - unittest + static if (Specs.length == 0) @safe unittest { auto tup1 = tuple(1, 1, 1); auto tup2 = tuple(1, 100, 100); assert(tup1 < tup2); - + //Only the first result matters for comparison tup1[0] = 2; assert(tup1 > tup2); @@ -687,14 +737,14 @@ template Tuple(Specs...) * Assignment from another `Tuple`. * * Params: - * rhs = The source `Tuple` to assign from. Each element of the + * rhs = The source `Tuple` to assign from. Each element of the * source `Tuple` must be implicitly assignable to each * respective element of the target `Tuple`. */ void opAssign(R)(auto ref R rhs) if (areCompatibleTuples!(typeof(this), R, "=")) { - import std.algorithm : swap; + import std.algorithm.mutation : swap; static if (is(R : Tuple!Types) && !__traits(isRef, rhs)) { @@ -717,26 +767,234 @@ template Tuple(Specs...) } /** - * Takes a slice of this `Tuple`. + * Renames the elements of a $(LREF Tuple). + * + * `rename` uses the passed `names` and returns a new + * $(LREF Tuple) using these names, with the content + * unchanged. + * If fewer names are passed than there are members + * of the $(LREF Tuple) then those trailing members are unchanged. + * An empty string will remove the name for that member. + * It is an compile-time error to pass more names than + * there are members of the $(LREF Tuple). + */ + ref rename(names...)() return + if (names.length == 0 || allSatisfy!(isSomeString, typeof(names))) + { + import std.algorithm.comparison : equal; + // to circumvent bug 16418 + static if (names.length == 0 || equal([names], [fieldNames])) + return this; + else + { + enum nT = Types.length; + enum nN = names.length; + static assert(nN <= nT, "Cannot have more names than tuple members"); + alias allNames = AliasSeq!(names, fieldNames[nN .. $]); + + template GetItem(size_t idx) + { + import std.array : empty; + static if (idx < nT) + alias GetItem = Alias!(Types[idx]); + else static if (allNames[idx - nT].empty) + alias GetItem = AliasSeq!(); + else + alias GetItem = Alias!(allNames[idx - nT]); + } + + import std.range : roundRobin, iota; + alias NewTupleT = Tuple!(staticMap!(GetItem, aliasSeqOf!( + roundRobin(iota(nT), iota(nT, 2*nT))))); + return *(() @trusted => cast(NewTupleT*)&this)(); + } + } + + /// + static if (Specs.length == 0) @safe unittest + { + auto t0 = tuple(4, "hello"); + + auto t0Named = t0.rename!("val", "tag"); + assert(t0Named.val == 4); + assert(t0Named.tag == "hello"); + + Tuple!(float, "dat", size_t[2], "pos") t1; + t1.pos = [2, 1]; + auto t1Named = t1.rename!"height"; + t1Named.height = 3.4f; + assert(t1Named.height == 3.4f); + assert(t1Named.pos == [2, 1]); + t1Named.rename!"altitude".altitude = 5; + assert(t1Named.height == 5); + + Tuple!(int, "a", int, int, "c") t2; + t2 = tuple(3,4,5); + auto t2Named = t2.rename!("", "b"); + // "a" no longer has a name + static assert(!hasMember!(typeof(t2Named), "a")); + assert(t2Named[0] == 3); + assert(t2Named.b == 4); + assert(t2Named.c == 5); + + // not allowed to specify more names than the tuple has members + static assert(!__traits(compiles, t2.rename!("a","b","c","d"))); + + // use it in a range pipeline + import std.range : iota, zip; + import std.algorithm.iteration : map, sum; + auto res = zip(iota(1, 4), iota(10, 13)) + .map!(t => t.rename!("a", "b")) + .map!(t => t.a * t.b) + .sum; + assert(res == 68); + } + + /** + * Overload of $(LREF _rename) that takes an associative array + * `translate` as a template parameter, where the keys are + * either the names or indices of the members to be changed + * and the new names are the corresponding values. + * Every key in `translate` must be the name of a member of the + * $(LREF tuple). + * The same rules for empty strings apply as for the variadic + * template overload of $(LREF _rename). + */ + ref rename(alias translate)() + if (is(typeof(translate) : V[K], V, K) && isSomeString!V && + (isSomeString!K || is(K : size_t))) + { + import std.range : ElementType; + static if (isSomeString!(ElementType!(typeof(translate.keys)))) + { + { + import std.conv : to; + import std.algorithm.iteration : filter; + import std.algorithm.searching : canFind; + enum notFound = translate.keys + .filter!(k => fieldNames.canFind(k) == -1); + static assert(notFound.empty, "Cannot find members " + ~ notFound.to!string ~ " in type " + ~ typeof(this).stringof); + } + return this.rename!(aliasSeqOf!( + { + import std.array : empty; + auto names = [fieldNames]; + foreach (ref n; names) + if (!n.empty) + if (auto p = n in translate) + n = *p; + return names; + }())); + } + else + { + { + import std.algorithm.iteration : filter; + import std.conv : to; + enum invalid = translate.keys. + filter!(k => k < 0 || k >= this.length); + static assert(invalid.empty, "Indices " ~ invalid.to!string + ~ " are out of bounds for tuple with length " + ~ this.length.to!string); + } + return this.rename!(aliasSeqOf!( + { + auto names = [fieldNames]; + foreach (k, v; translate) + names[k] = v; + return names; + }())); + } + } + + /// + static if (Specs.length == 0) @safe unittest + { + //replacing names by their current name + + Tuple!(float, "dat", size_t[2], "pos") t1; + t1.pos = [2, 1]; + auto t1Named = t1.rename!(["dat": "height"]); + t1Named.height = 3.4; + assert(t1Named.pos == [2, 1]); + t1Named.rename!(["height": "altitude"]).altitude = 5; + assert(t1Named.height == 5); + + Tuple!(int, "a", int, "b") t2; + t2 = tuple(3, 4); + auto t2Named = t2.rename!(["a": "b", "b": "c"]); + assert(t2Named.b == 3); + assert(t2Named.c == 4); + } + + /// + static if (Specs.length == 0) @safe unittest + { + //replace names by their position + + Tuple!(float, "dat", size_t[2], "pos") t1; + t1.pos = [2, 1]; + auto t1Named = t1.rename!([0: "height"]); + t1Named.height = 3.4; + assert(t1Named.pos == [2, 1]); + t1Named.rename!([0: "altitude"]).altitude = 5; + assert(t1Named.height == 5); + + Tuple!(int, "a", int, "b", int, "c") t2; + t2 = tuple(3, 4, 5); + auto t2Named = t2.rename!([0: "c", 2: "a"]); + assert(t2Named.a == 5); + assert(t2Named.b == 4); + assert(t2Named.c == 3); + } + + static if (Specs.length == 0) @safe unittest + { + //check that empty translations work fine + enum string[string] a0 = null; + enum string[int] a1 = null; + Tuple!(float, "a", float, "b") t0; + + auto t1 = t0.rename!a0; + + t1.a = 3; + t1.b = 4; + auto t2 = t0.rename!a1; + t2.a = 3; + t2.b = 4; + auto t3 = t0.rename; + t3.a = 3; + t3.b = 4; + } + + /** + * Takes a slice by-reference of this `Tuple`. * * Params: * from = A `size_t` designating the starting position of the slice. * to = A `size_t` designating the ending position (exclusive) of the slice. * * Returns: - * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original. - * It has the same types and values as the range `[from, to$(RPAREN)` in + * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original. + * It has the same types and values as the range `[from, to$(RPAREN)` in * the original. */ @property - ref Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)() @trusted + ref inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)() inout @trusted if (from <= to && to <= Types.length) { + static assert( + (typeof(this).alignof % typeof(return).alignof == 0) && + (expand[from].offsetof % typeof(return).alignof == 0), + "Slicing by reference is impossible because of an alignment mistmatch. (See Phobos issue #15645.)"); + return *cast(typeof(return)*) &(field[from]); } - + /// - unittest + static if (Specs.length == 0) @safe unittest { Tuple!(int, string, float, double) a; a[1] = "abc"; @@ -744,11 +1002,15 @@ template Tuple(Specs...) auto s = a.slice!(1, 3); static assert(is(typeof(s) == Tuple!(string, float))); assert(s[0] == "abc" && s[1] == 4.5); + + // Phobos issue #15645 + Tuple!(int, short, bool, double) b; + static assert(!__traits(compiles, b.slice!(2, 4))); } /** Creates a hash of this `Tuple`. - + Returns: A `size_t` representing the hash of this `Tuple`. */ @@ -759,50 +1021,143 @@ template Tuple(Specs...) h += typeid(T).getHash(cast(const void*)&field[i]); return h; } - - void toString(DG)(scope DG sink) - { - enum header = typeof(this).stringof ~ "(", - footer = ")", - separator = ", "; - sink(header); - foreach (i, Type; Types) + + /// + template toString() + { + /** + * Converts to string. + * + * Returns: + * The string representation of this `Tuple`. + */ + string toString()() const { - static if (i > 0) + import std.array : appender; + auto app = appender!string(); + this.toString((const(char)[] chunk) => app ~= chunk); + return app.data; + } + + import std.format : FormatSpec; + + /** + * Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`. + * + * $(TABLE2 Formats supported by Tuple, + * $(THEAD Format, Description) + * $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.)) + * $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`, so + * it may contain as many formats as the `Tuple` has fields.)) + * $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format, that is applied + * on all fields of the `Tuple`. The inner format must be compatible to all + * of them.))) + * --- + * Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ]; + * + * // Default format + * assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`); + * + * // One Format for each individual component + * assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`); + * assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`); + * + * // One Format for all components + * assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`); + * + * // Array of Tuples + * assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`); + * + * + * // Error: %( %) missing. + * assertThrown!FormatException( + * format("%d, %f", tuple(1, 2.0)) == `1, 2.0` + * ); + * + * // Error: %( %| %) missing. + * assertThrown!FormatException( + * format("%d", tuple(1, 2)) == `1, 2` + * ); + * + * // Error: %d inadequate for double. + * assertThrown!FormatException( + * format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0` + * ); + * --- + */ + void toString(DG)(scope DG sink) const + { + toString(sink, FormatSpec!char()); + } + + /// ditto + void toString(DG, Char)(scope DG sink, FormatSpec!Char fmt) const + { + import std.format : formatElement, formattedWrite, FormatException; + if (fmt.nested) { - sink(separator); + if (fmt.sep) + { + foreach (i, Type; Types) + { + static if (i > 0) + { + sink(fmt.sep); + } + // TODO: Change this once formattedWrite() works for shared objects. + static if (is(Type == class) && is(Type == shared)) + { + sink(Type.stringof); + } + else + { + formattedWrite(sink, fmt.nested, this.field[i]); + } + } + } + else + { + formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand)); + } } - // TODO: Change this once toString() works for shared objects. - static if (is(Type == class) && is(typeof(Type.init) == shared)) + else if (fmt.spec == 's') { - sink(Type.stringof); + enum header = Unqual!(typeof(this)).stringof ~ "(", + footer = ")", + separator = ", "; + sink(header); + foreach (i, Type; Types) + { + static if (i > 0) + { + sink(separator); + } + // TODO: Change this once formatElement() works for shared objects. + static if (is(Type == class) && is(Type == shared)) + { + sink(Type.stringof); + } + else + { + FormatSpec!Char f; + formatElement(sink, field[i], f); + } + } + sink(footer); } else { - import std.format : FormatSpec, formatElement; - FormatSpec!char f; - formatElement(sink, field[i], f); + throw new FormatException( + "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~ + Unqual!(typeof(this)).stringof ~ "', not '%" ~ fmt.spec ~ "'."); } } - sink(footer); - } - - /** - * Converts to string. - * - * Returns: - * The string representation of this `Tuple`. - */ - string toString()() - { - import std.conv : to; - return this.to!string; } } } /// -unittest +@safe unittest { Tuple!(int, int) point; // assign coordinates @@ -813,11 +1168,11 @@ unittest auto y = point[1]; } -/** +/** `Tuple` members can be named. It is legal to mix named and unnamed members. The method above is still applicable to all fields. */ -unittest +@safe unittest { alias Entry = Tuple!(int, "index", string, "value"); Entry e; @@ -833,7 +1188,7 @@ unittest `Tuple`s differing in naming only are still distinct, even though they might have the same structure. */ -unittest +@safe unittest { Tuple!(int, "x", int, "y") point1; Tuple!(int, int) point2; @@ -841,31 +1196,31 @@ unittest } /** - Create a copy of a `Tuple` with its fields in reverse order. - + Creates a copy of a $(LREF Tuple) with its fields in _reverse order. + Params: t = The `Tuple` to copy. - + Returns: - A copy of `t` with its fields in reverse order. + A new `Tuple`. */ -ReverseTupleType!T reverse(T)(T t) +auto reverse(T)(T t) if (isTuple!T) { - import std.typetuple : Reverse; + import std.meta : Reverse; // @@@BUG@@@ Cannot be an internal function due to forward reference issues. // @@@BUG@@@ 9929 Need 'this' when calling template with expanded tuple // return tuple(Reverse!(t.expand)); - typeof(return) result; + ReverseTupleType!T result; auto tup = t.expand; result.expand = Reverse!tup; return result; } /// -unittest +@safe unittest { auto tup = tuple(1, "2"); assert(tup.reverse == tuple("2", 1)); @@ -886,11 +1241,11 @@ private template ReverseTupleSpecs(T...) { static if (is(typeof(T[$-1]) : string)) { - alias ReverseTupleSpecs = TypeTuple!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2])); + alias ReverseTupleSpecs = AliasSeq!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2])); } else { - alias ReverseTupleSpecs = TypeTuple!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1])); + alias ReverseTupleSpecs = AliasSeq!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1])); } } else @@ -899,7 +1254,13 @@ private template ReverseTupleSpecs(T...) } } +// ensure that internal Tuple unittests are compiled unittest +{ + Tuple!() t; +} + +@safe unittest { import std.conv; { @@ -1072,13 +1433,13 @@ unittest } { Tuple!(wchar, dchar, int, "x", string, "y", char, byte, float) tup; - tup = tuple('a', 'b', 3, "4", 'c', cast(byte)0x0D, 0.00); + tup = tuple('a', 'b', 3, "4", 'c', cast(byte) 0x0D, 0.00); auto rev = tup.reverse; - assert(rev == tuple(0.00, cast(byte)0x0D, 'c', "4", 3, 'b', 'a')); + assert(rev == tuple(0.00, cast(byte) 0x0D, 'c', "4", 3, 'b', 'a')); assert(rev.x == 3 && rev.y == "4"); } } -unittest +@safe unittest { // opEquals { @@ -1148,6 +1509,23 @@ unittest static assert( is(typeof(tc4 < tm4))); static assert( is(typeof(tc4 < tc4))); } + // Bugzilla 14890 + static void test14890(inout int[] dummy) + { + alias V = Tuple!(int, int); + + V mv; + const V cv; + immutable V iv; + inout V wv; // OK <- NG + inout const V wcv; // OK <- NG + + foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv)) + foreach (v2; AliasSeq!(mv, cv, iv, wv, wcv)) + { + assert(!(v1 < v2)); + } + } { int[2] ints = [ 1, 2 ]; Tuple!(int, int) t = ints; @@ -1171,7 +1549,7 @@ unittest static assert(is(typeof(Tuple!(int, "x", string, "y").tupleof) == typeof(Tuple!(int, string ).tupleof))); } -unittest +@safe unittest { // Bugzilla 10686 immutable Tuple!(int) t1; @@ -1179,7 +1557,7 @@ unittest immutable Tuple!(int, "x") t2; auto r2 = t2[0]; // error } -unittest +@safe unittest { import std.exception : assertCTFEable; @@ -1190,13 +1568,13 @@ unittest t = tuple(2); // assignment }); } -unittest +@safe unittest { class Foo{} Tuple!(immutable(Foo)[]) a; } -unittest +@safe unittest { //Test non-assignable static struct S @@ -1219,18 +1597,18 @@ unittest } // Bugzilla #9819 -unittest +@safe unittest { alias T = Tuple!(int, "x", double, "foo"); static assert(T.fieldNames[0] == "x"); static assert(T.fieldNames[1] == "foo"); alias Fields = Tuple!(int, "id", string, float); - static assert(Fields.fieldNames == TypeTuple!("id", "", "")); + static assert(Fields.fieldNames == AliasSeq!("id", "", "")); } // Bugzilla 13837 -unittest +@safe unittest { // New behaviour, named arguments. static assert(is( @@ -1263,7 +1641,7 @@ unittest static assert(!__traits(compiles, tuple!("x", int)(2))); } -unittest +@safe unittest { class C {} Tuple!(Rebindable!(const C)) a; @@ -1271,32 +1649,74 @@ unittest a = b; } -@nogc unittest +@nogc @safe unittest { alias T = Tuple!(string, "s"); T x; x = T.init; } +@safe unittest +{ + import std.format : format, FormatException; + import std.exception : assertThrown; + + // enum tupStr = tuple(1, 1.0).toString; // toString is *impure*. + //static assert(tupStr == `Tuple!(int, double)(1, 1)`); + + Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ]; + + // Default format + assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`); + + // One Format for each individual component + assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`); + assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`); + + // One Format for all components + assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`); + + // Array of Tuples + assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`); + + + // Error: %( %) missing. + assertThrown!FormatException( + format("%d, %f", tuple(1, 2.0)) == `1, 2.0` + ); + + // Error: %( %| %) missing. + assertThrown!FormatException( + format("%d", tuple(1, 2)) == `1, 2` + ); + + // Error: %d inadequate for double + assertThrown!FormatException( + format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0` + ); +} + /** - Constructs a $(D Tuple) object instantiated and initialized according to + Constructs a $(LREF Tuple) object instantiated and initialized according to the given arguments. - + Params: - Names = A list of strings naming each successive field of the `Tuple`. + Names = An optional list of strings naming each successive field of the `Tuple`. Each name matches up with the corresponding field given by `Args`. A name does not have to be provided for every field, but as the names must proceed in order, it is not possible to skip one field and name the next after it. - +*/ +template tuple(Names...) +{ + /** + Params: args = Values to initialize the `Tuple` with. The `Tuple`'s type will be inferred from the types of the values given. - + Returns: A new `Tuple` with its type inferred from the arguments given. -*/ -template tuple(Names...) -{ + */ auto tuple(Args...)(Args args) { static if (Names.length == 0) @@ -1322,13 +1742,13 @@ template tuple(Names...) { template and(B...) if (B.length == 1) { - alias TypeTuple!(A[0], B[0]) and; + alias and = AliasSeq!(A[0], B[0]); } template and(B...) if (B.length != 1) { - alias TypeTuple!(A[0], B[0], - Interleave!(A[1..$]).and!(B[1..$])) and; + alias and = AliasSeq!(A[0], B[0], + Interleave!(A[1..$]).and!(B[1..$])); } } return Tuple!(Interleave!(Args).and!(Names))(args); @@ -1337,7 +1757,7 @@ template tuple(Names...) } /// -unittest +@safe unittest { auto value = tuple(5, 6.7, "hello"); assert(value[0] == 5); @@ -1352,27 +1772,21 @@ unittest /** Returns $(D true) if and only if $(D T) is an instance of $(D std.typecons.Tuple). - + Params: T = The type to check. - + Returns: true if `T` is a `Tuple` type, false otherwise. */ -template isTuple(T) -{ - static if (is(Unqual!T Unused : Tuple!Specs, Specs...)) - { - enum isTuple = true; - } - else - { - enum isTuple = false; - } -} +enum isTuple(T) = __traits(compiles, + { + void f(Specs...)(Tuple!Specs tup) {} + f(T.init); + } ); /// -unittest +@safe unittest { static assert(isTuple!(Tuple!())); static assert(isTuple!(Tuple!(int))); @@ -1381,7 +1795,7 @@ unittest static assert(isTuple!(Tuple!(int, Tuple!(real), string))); } -unittest +@safe unittest { static assert(isTuple!(const Tuple!(int))); static assert(isTuple!(immutable Tuple!(int))); @@ -1395,7 +1809,7 @@ unittest // used by both Rebindable and UnqualRef private mixin template RebindableCommon(T, U, alias This) - if (is(T == class) || is(T == interface)) + if (is(T == class) || is(T == interface) || isAssociativeArray!T) { private union { @@ -1429,7 +1843,7 @@ private mixin template RebindableCommon(T, U, alias This) opAssign(initializer); } - @property ref inout(T) get() inout + @property inout(T) get() inout { return original; } @@ -1442,8 +1856,7 @@ private mixin template RebindableCommon(T, U, alias This) $(D Rebindable!(T)) is a simple, efficient wrapper that behaves just like an object of type $(D T), except that you can reassign it to refer to another object. For completeness, $(D Rebindable!(T)) aliases -itself away to $(D T) if $(D T) is a non-const object type. However, -$(D Rebindable!(T)) does not compile if $(D T) is a non-class type. +itself away to $(D T) if $(D T) is a non-const object type. You may want to use $(D Rebindable) when you want to have mutable storage referring to $(D const) objects, for example an array of @@ -1452,9 +1865,10 @@ break the soundness of D's type system and does not incur any of the risks usually associated with $(D cast). Params: - T = An object, interface, or array slice type. + T = An object, interface, array slice type, or associative array type. */ -template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!T) +template Rebindable(T) + if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T) { static if (is(T == const U, U) || is(T == immutable U, U)) { @@ -1478,7 +1892,7 @@ template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArra } ///Regular $(D const) object references cannot be reassigned. -unittest +@system unittest { class Widget { int x; int y() const { return x; } } const a = new Widget; @@ -1491,10 +1905,10 @@ unittest } /** - However, $(D Rebindable!(Widget)) does allow reassignment, + However, $(D Rebindable!(Widget)) does allow reassignment, while otherwise behaving exactly like a $(D const Widget). */ -unittest +@system unittest { class Widget { int x; int y() const { return x; } } auto a = Rebindable!(const Widget)(new Widget); @@ -1506,19 +1920,26 @@ unittest a = new Widget; } +@safe unittest // issue 16054 +{ + Rebindable!(immutable Object) r; + static assert(__traits(compiles, r.get())); + static assert(!__traits(compiles, &r.get())); +} + /** Convenience function for creating a $(D Rebindable) using automatic type inference. Params: - obj = A reference to an object or interface, or an array slice + obj = A reference to an object, interface, associative array, or an array slice to initialize the `Rebindable` with. - + Returns: A newly constructed `Rebindable` initialized with the given reference. */ Rebindable!T rebindable(T)(T obj) -if (is(T == class) || is(T == interface) || isDynamicArray!T) + if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T) { typeof(return) ret; ret = obj; @@ -1541,7 +1962,7 @@ Rebindable!T rebindable(T)(Rebindable!T obj) return obj; } -unittest +@system unittest { interface CI { int foo() const; } class C : CI { @@ -1612,7 +2033,7 @@ unittest immutable(char[]) s7654; Rebindable!(typeof(s7654)) r7654 = s7654; - foreach (T; TypeTuple!(char, wchar, char, int)) + foreach (T; AliasSeq!(char, wchar, char, int)) { static assert(is(Rebindable!(immutable(T[])) == immutable(T)[])); static assert(is(Rebindable!(const(T[])) == const(T)[])); @@ -1622,13 +2043,21 @@ unittest // Issue 12046 static assert(!__traits(compiles, Rebindable!(int[1]))); static assert(!__traits(compiles, Rebindable!(const int[1]))); + + // Pull request 3341 + Rebindable!(immutable int[int]) pr3341 = [123:345]; + assert(pr3341[123] == 345); + immutable int[int] pr3341_aa = [321:543]; + pr3341 = pr3341_aa; + assert(pr3341[321] == 543); + assert(rebindable(pr3341_aa)[321] == 543); } /** Similar to $(D Rebindable!(T)) but strips all qualifiers from the reference as opposed to just constness / immutability. Primary intended use case is with shared (having thread-local reference to shared class data) - + Params: T = A class or interface type. */ @@ -1652,7 +2081,7 @@ template UnqualRef(T) } /// -unittest +@system unittest { class Data {} @@ -1673,11 +2102,11 @@ unittest assert(b is null); } -unittest +@safe unittest { class C { } alias T = UnqualRef!(const shared C); - static assert (is(typeof(T.stripped) == C)); + static assert(is(typeof(T.stripped) == C)); } @@ -1686,17 +2115,17 @@ unittest Order the provided members to minimize size while preserving alignment. Alignment is not always optimal for 80-bit reals, nor for structs declared as align(1). - + Params: - E = A list of the types to be aligned, representing fields + E = A list of the types to be aligned, representing fields of an aggregate such as a `struct` or `class`. - + names = The names of the fields that are to be aligned. - + Returns: A string to be mixed in to an aggregate, such as a `struct` or `class`. */ -string alignForSize(E...)(string[] names...) +string alignForSize(E...)(const char[][] names...) { // Sort all of the members by .alignof. // BUG: Alignment is not always optimal for align(1) structs @@ -1714,7 +2143,7 @@ string alignForSize(E...)(string[] names...) foreach (i, T; E) { auto a = T.alignof; - auto k = a>=64? 0 : a>=32? 1 : a>=16? 2 : a>=8? 3 : a>=4? 4 : a>=2? 5 : 6; + auto k = a >= 64? 0 : a >= 32? 1 : a >= 16? 2 : a >= 8? 3 : a >= 4? 4 : a >= 2? 5 : 6; declaration[k] ~= T.stringof ~ " " ~ names[i] ~ ";\n"; } @@ -1725,14 +2154,14 @@ string alignForSize(E...)(string[] names...) } /// -unittest +@safe unittest { struct Banner { mixin(alignForSize!(byte[6], double)(["name", "height"])); } } -unittest +@safe unittest { enum x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w"); struct Foo { int x; } @@ -1749,6 +2178,16 @@ unittest static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof); } +// Issue 12914 +@safe unittest +{ + immutable string[] fieldNames = ["x", "y"]; + struct S + { + mixin(alignForSize!(byte, int)(fieldNames)); + } +} + /** Defines a value paired with a distinctive "null" state that denotes the absence of a value. If default constructed, a $(D @@ -1764,6 +2203,9 @@ struct Nullable(T) /** Constructor initializing $(D this) with $(D value). + +Params: + value = The value to initialize this `Nullable` with. */ this(inout T value) inout { @@ -1771,6 +2213,69 @@ Constructor initializing $(D this) with $(D value). _isNull = false; } + /** + If they are both null, then they are equal. If one is null and the other + is not, then they are not equal. If they are both non-null, then they are + equal if their values are equal. + */ + bool opEquals()(auto ref const(typeof(this)) rhs) const + { + if (_isNull) + return rhs._isNull; + if (rhs._isNull) + return false; + return _value == rhs._value; + } + + /// Ditto + bool opEquals()(auto ref const(T) rhs) const + { + return _isNull ? false : rhs == _value; + } + + /// + @safe unittest + { + Nullable!int empty; + Nullable!int a = 42; + Nullable!int b = 42; + Nullable!int c = 27; + + assert(empty == empty); + assert(empty == Nullable!int.init); + assert(empty != a); + assert(empty != b); + assert(empty != c); + + assert(a == b); + assert(a != c); + + assert(empty != 42); + assert(a == 42); + assert(c != 42); + } + + @safe unittest + { + // Test constness + immutable Nullable!int a = 42; + Nullable!int b = 42; + immutable Nullable!int c = 29; + Nullable!int d = 29; + immutable e = 42; + int f = 29; + assert(a == a); + assert(a == b); + assert(a != c); + assert(a != d); + assert(a == e); + assert(a != f); + + // Test rvalue + assert(a == const Nullable!int(42)); + assert(a != Nullable!int(29)); + } + template toString() { import std.format : FormatSpec, formatValue; @@ -1786,16 +2291,54 @@ Constructor initializing $(D this) with $(D value). sink.formatValue(_value, fmt); } } + + // Issue 14940 + void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt) + { + if (isNull) + { + sink.formatValue("Nullable.null", fmt); + } + else + { + sink.formatValue(_value, fmt); + } + } } /** -Returns $(D true) if and only if $(D this) is in the null state. +Check if `this` is in the null state. + +Returns: + true $(B iff) `this` is in the null state, otherwise false. */ @property bool isNull() const @safe pure nothrow { return _isNull; } +/// +@system unittest +{ + Nullable!int ni; + assert(ni.isNull); + + ni = 0; + assert(!ni.isNull); +} + +// Issue 14940 +@safe unittest +{ + import std.array : appender; + import std.format : formattedWrite; + + auto app = appender!string(); + Nullable!int a = 1; + formattedWrite(app, "%s", a); + assert(app.data == "1"); +} + /** Forces $(D this) to the null state. */ @@ -1805,9 +2348,22 @@ Forces $(D this) to the null state. _isNull = true; } +/// +@safe unittest +{ + Nullable!int ni = 0; + assert(!ni.isNull); + + ni.nullify(); + assert(ni.isNull); +} + /** Assigns $(D value) to the internally-held state. If the assignment succeeds, $(D this) becomes non-null. + +Params: + value = A value of type `T` to assign to this `Nullable`. */ void opAssign()(T value) { @@ -1815,9 +2371,32 @@ succeeds, $(D this) becomes non-null. _isNull = false; } +/** + If this `Nullable` wraps a type that already has a null value + (such as a pointer), then assigning the null value to this + `Nullable` is no different than assigning any other value of + type `T`, and the resulting code will look very strange. It + is strongly recommended that this be avoided by instead using + the version of `Nullable` that takes an additional `nullValue` + template argument. + */ +@safe unittest +{ + //Passes + Nullable!(int*) npi; + assert(npi.isNull); + + //Passes?! + npi = null; + assert(!npi.isNull); +} + /** Gets the value. $(D this) must not be in the null state. This function is also called for the implicit conversion to $(D T). + +Returns: + The value held internally by this `Nullable`. */ @property ref inout(T) get() inout @safe pure nothrow { @@ -1825,25 +2404,85 @@ This function is also called for the implicit conversion to $(D T). assert(!isNull, message); return _value; } - -/** -Implicitly converts to $(D T). -$(D this) must not be in the null state. - */ - alias get this; + +/// +@system unittest +{ + import core.exception : AssertError; + import std.exception : assertThrown, assertNotThrown; + + Nullable!int ni; + int i = 42; + //`get` is implicitly called. Will throw + //an AssertError in non-release mode + assertThrown!AssertError(i = ni); + assert(i == 42); + + ni = 5; + assertNotThrown!AssertError(i = ni); + assert(i == 5); +} + +/** +Implicitly converts to $(D T). +$(D this) must not be in the null state. + */ + alias get this; +} + +/// ditto +auto nullable(T)(T t) +{ + return Nullable!T(t); +} + +/// +@safe unittest +{ + struct CustomerRecord + { + string name; + string address; + int customerNum; + } + + Nullable!CustomerRecord getByName(string name) + { + //A bunch of hairy stuff + + return Nullable!CustomerRecord.init; + } + + auto queryResult = getByName("Doe, John"); + if (!queryResult.isNull) + { + //Process Mr. Doe's customer record + auto address = queryResult.address; + auto customerNum = queryResult.customerNum; + + //Do some things with this customer's info + } + else + { + //Add the customer to the database + } } /// -unittest +@system unittest { - Nullable!int a; - assert(a.isNull); - a = 5; + import std.exception : assertThrown; + + auto a = 42.nullable; assert(!a.isNull); - assert(a == 5); + assert(a.get == 42); + + a.nullify(); + assert(a.isNull); + assertThrown!Throwable(a.get); } -unittest +@system unittest { import std.exception : assertThrown; @@ -1866,14 +2505,14 @@ unittest a.nullify(); assertThrown!Throwable(a += 2); } -unittest +@safe unittest { auto k = Nullable!int(74); assert(k == 74); k.nullify(); assert(k.isNull); } -unittest +@safe unittest { static int f(in Nullable!int x) { return x.isNull ? 42 : x.get; @@ -1885,7 +2524,7 @@ unittest a.nullify(); assert(f(a) == 42); } -unittest +@system unittest { import std.exception : assertThrown; @@ -1901,7 +2540,7 @@ unittest s.nullify(); assertThrown!Throwable(s.x = 9441); } -unittest +@safe unittest { // Ensure Nullable can be used in pure/nothrow/@safe environment. function() @safe pure nothrow @@ -1915,7 +2554,7 @@ unittest assert(n.isNull); }(); } -unittest +@system unittest { // Ensure Nullable can be used when the value is not pure/nothrow/@safe static struct S @@ -1932,7 +2571,7 @@ unittest s.nullify(); assert(s.isNull); } -unittest +@safe unittest { // Bugzilla 9404 alias N = Nullable!int; @@ -1945,7 +2584,7 @@ unittest N n; foo(n); } -unittest +@safe unittest { //Check nullable immutable is constructable { @@ -1960,7 +2599,7 @@ unittest auto i = a2.get; } } -unittest +@safe unittest { alias NInt = Nullable!int; @@ -2003,7 +2642,7 @@ unittest assert(b3.isNull); } } -unittest +@safe unittest { //Check nullable is nicelly embedable in a struct static struct S1 @@ -2022,7 +2661,7 @@ unittest ni = other.ni; } } - foreach (S; TypeTuple!(S1, S2)) + foreach (S; AliasSeq!(S1, S2)) { S a; S b = a; @@ -2030,7 +2669,7 @@ unittest c = a; } } -unittest +@system unittest { // Bugzilla 10268 import std.json; @@ -2044,10 +2683,14 @@ unittest { auto sm = S1(1); immutable si = immutable S1(1); - static assert( __traits(compiles, { auto x1 = Nullable!S1(sm); })); - static assert( __traits(compiles, { auto x2 = immutable Nullable!S1(sm); })); - static assert( __traits(compiles, { auto x3 = Nullable!S1(si); })); - static assert( __traits(compiles, { auto x4 = immutable Nullable!S1(si); })); + auto x1 = Nullable!S1(sm); + auto x2 = immutable Nullable!S1(sm); + auto x3 = Nullable!S1(si); + auto x4 = immutable Nullable!S1(si); + assert(x1.val == 1); + assert(x2.val == 1); + assert(x3.val == 1); + assert(x4.val == 1); } auto nm = 10; @@ -2056,30 +2699,36 @@ unittest { auto sm = S2(&nm); immutable si = immutable S2(&ni); - static assert( __traits(compiles, { auto x = Nullable!S2(sm); })); - static assert(!__traits(compiles, { auto x = immutable Nullable!S2(sm); })); - static assert(!__traits(compiles, { auto x = Nullable!S2(si); })); - static assert( __traits(compiles, { auto x = immutable Nullable!S2(si); })); + auto x1 = Nullable!S2(sm); + static assert(!__traits(compiles, { auto x2 = immutable Nullable!S2(sm); })); + static assert(!__traits(compiles, { auto x3 = Nullable!S2(si); })); + auto x4 = immutable Nullable!S2(si); + assert(*x1.val == 10); + assert(*x4.val == 10); } { auto sm = S3(&ni); immutable si = immutable S3(&ni); - static assert( __traits(compiles, { auto x = Nullable!S3(sm); })); - static assert( __traits(compiles, { auto x = immutable Nullable!S3(sm); })); - static assert( __traits(compiles, { auto x = Nullable!S3(si); })); - static assert( __traits(compiles, { auto x = immutable Nullable!S3(si); })); + auto x1 = Nullable!S3(sm); + auto x2 = immutable Nullable!S3(sm); + auto x3 = Nullable!S3(si); + auto x4 = immutable Nullable!S3(si); + assert(*x1.val == 10); + assert(*x2.val == 10); + assert(*x3.val == 10); + assert(*x4.val == 10); } } -unittest +@safe unittest { // Bugzila 10357 import std.datetime; Nullable!SysTime time = SysTime(0); } -unittest +@system unittest { - import std.conv: to; + import std.conv : to; import std.array; // Bugzilla 10915 @@ -2121,6 +2770,12 @@ particular value. For example, $(D Nullable!(uint, uint.max)) is an $(D uint) that sets aside the value $(D uint.max) to denote a null state. $(D Nullable!(T, nullValue)) is more storage-efficient than $(D Nullable!T) because it does not need to store an extra $(D bool). + +Params: + T = The wrapped type for which Nullable provides a null value. + + nullValue = The null value which denotes the null state of this + `Nullable`. Must be of type `T`. */ struct Nullable(T, T nullValue) { @@ -2128,6 +2783,9 @@ struct Nullable(T, T nullValue) /** Constructor initializing $(D this) with $(D value). + +Params: + value = The value to initialize this `Nullable` with. */ this(T value) { @@ -2152,7 +2810,10 @@ Constructor initializing $(D this) with $(D value). } /** -Returns $(D true) if and only if $(D this) is in the null state. +Check if `this` is in the null state. + +Returns: + true $(B iff) `this` is in the null state, otherwise false. */ @property bool isNull() const { @@ -2162,12 +2823,48 @@ Returns $(D true) if and only if $(D this) is in the null state. { return _value is nullValue; } + //Need to use 'is' if T is a float type + //because NaN != NaN + else static if (isFloatingPoint!T) + { + return _value is nullValue; + } else { return _value == nullValue; } } +/// +@system unittest +{ + Nullable!(int, -1) ni; + //Initialized to "null" state + assert(ni.isNull); + + ni = 0; + assert(!ni.isNull); +} + +// https://issues.dlang.org/show_bug.cgi?id=11135 +// disable test until https://issues.dlang.org/show_bug.cgi?id=15316 gets fixed +version (none) unittest +{ + foreach (T; AliasSeq!(float, double, real)) + { + Nullable!(T, T.init) nf; + //Initialized to "null" state + assert(nf.isNull); + assert(nf is typeof(nf).init); + + nf = 0; + assert(!nf.isNull); + + nf.nullify(); + assert(nf.isNull); + } +} + /** Forces $(D this) to the null state. */ @@ -2176,18 +2873,58 @@ Forces $(D this) to the null state. _value = nullValue; } +/// +@system unittest +{ + Nullable!(int, -1) ni = 0; + assert(!ni.isNull); + + ni = -1; + assert(ni.isNull); +} + /** -Assigns $(D value) to the internally-held state. No null checks are -made. Note that the assignment may leave $(D this) in the null state. +Assigns $(D value) to the internally-held state. If the assignment +succeeds, $(D this) becomes non-null. No null checks are made. Note +that the assignment may leave $(D this) in the null state. + +Params: + value = A value of type `T` to assign to this `Nullable`. + If it is `nullvalue`, then the internal state of + this `Nullable` will be set to null. */ void opAssign()(T value) { _value = value; } +/** + If this `Nullable` wraps a type that already has a null value + (such as a pointer), and that null value is not given for + `nullValue`, then assigning the null value to this `Nullable` + is no different than assigning any other value of type `T`, + and the resulting code will look very strange. It is strongly + recommended that this be avoided by using `T`'s "built in" + null value for `nullValue`. + */ +@system unittest +{ + //Passes + enum nullVal = cast(int*) 0xCAFEBABE; + Nullable!(int*, nullVal) npi; + assert(npi.isNull); + + //Passes?! + npi = null; + assert(!npi.isNull); +} + /** Gets the value. $(D this) must not be in the null state. This function is also called for the implicit conversion to $(D T). + +Returns: + The value held internally by this `Nullable`. */ @property ref inout(T) get() inout { @@ -2198,14 +2935,67 @@ This function is also called for the implicit conversion to $(D T). return _value; } +/// +@system unittest +{ + import std.exception : assertThrown, assertNotThrown; + + Nullable!(int, -1) ni; + //`get` is implicitly called. Will throw + //an error in non-release mode + assertThrown!Throwable(ni == 0); + + ni = 0; + assertNotThrown!Throwable(ni == 0); +} + /** Implicitly converts to $(D T). -Gets the value. $(D this) must not be in the null state. +$(D this) must not be in the null state. */ alias get this; } -unittest +/// ditto +auto nullable(alias nullValue, T)(T t) + if (is (typeof(nullValue) == T)) +{ + return Nullable!(T, nullValue)(t); +} + +/// +@safe unittest +{ + Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle) + { + //Find the needle, returning -1 if not found + + return Nullable!(size_t, size_t.max).init; + } + + void sendLunchInvite(string name) + { + } + + //It's safer than C... + auto coworkers = ["Jane", "Jim", "Marry", "Fred"]; + auto pos = indexOf(coworkers, "Bob"); + if (!pos.isNull) + { + //Send Bob an invitation to lunch + sendLunchInvite(coworkers[pos]); + } + else + { + //Bob not found; report the error + } + + //And there's no overhead + static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof); +} + +/// +@system unittest { import std.exception : assertThrown; @@ -2217,14 +3007,17 @@ unittest assert(a == 5); static assert(a.sizeof == int.sizeof); } -unittest + +/// +@safe unittest { - auto a = Nullable!(int, int.min)(8); + auto a = nullable!(int.min)(8); assert(a == 8); a.nullify(); assert(a.isNull); } -unittest + +@safe unittest { static int f(in Nullable!(int, int.min) x) { return x.isNull ? 42 : x.get; @@ -2236,7 +3029,7 @@ unittest a.nullify(); assert(f(a) == 42); } -unittest +@safe unittest { // Ensure Nullable can be used in pure/nothrow/@safe environment. function() @safe pure nothrow @@ -2250,9 +3043,9 @@ unittest assert(n.isNull); }(); } -unittest +@system unittest { - // Ensure Nullable can be used when the value is not pure/nothrow/@safe + // Ensure Nullable can be used when the value is not pure/nothrow/@system static struct S { int x; @@ -2267,7 +3060,7 @@ unittest s.nullify(); assert(s.isNull); } -unittest +@safe unittest { //Check nullable is nicelly embedable in a struct static struct S1 @@ -2286,7 +3079,7 @@ unittest ni = other.ni; } } - foreach (S; TypeTuple!(S1, S2)) + foreach (S; AliasSeq!(S1, S2)) { S a; S b = a; @@ -2294,9 +3087,9 @@ unittest c = a; } } -unittest +@system unittest { - import std.conv: to; + import std.conv : to; // Bugzilla 10915 Nullable!(int, 1) ni = 1; @@ -2343,7 +3136,10 @@ struct NullableRef(T) private T* _value; /** -Constructor binding $(D this) with $(D value). +Constructor binding $(D this) to $(D value). + +Params: + value = The value to bind to. */ this(T* value) @safe pure nothrow { @@ -2369,20 +3165,48 @@ Constructor binding $(D this) with $(D value). /** Binds the internal state to $(D value). + +Params: + value = A pointer to a value of type `T` to bind this `NullableRef` to. */ void bind(T* value) @safe pure nothrow { _value = value; } + /// + @safe unittest + { + NullableRef!int nr = new int(42); + assert(nr == 42); + + int* n = new int(1); + nr.bind(n); + assert(nr == 1); + } + /** Returns $(D true) if and only if $(D this) is in the null state. + +Returns: + true if `this` is in the null state, otherwise false. */ @property bool isNull() const @safe pure nothrow { return _value is null; } + /// + @safe unittest + { + NullableRef!int nr; + assert(nr.isNull); + + int* n = new int(42); + nr.bind(n); + assert(!nr.isNull && nr == 42); + } + /** Forces $(D this) to the null state. */ @@ -2391,8 +3215,24 @@ Forces $(D this) to the null state. _value = null; } + /// + @safe unittest + { + NullableRef!int nr = new int(42); + assert(!nr.isNull); + + nr.nullify(); + assert(nr.isNull); + } + /** Assigns $(D value) to the internally-held state. + +Params: + value = A value of type `T` to assign to this `NullableRef`. + If the internal state of this `NullableRef` has not + been initialized, an error will be thrown in + non-release mode. */ void opAssign()(T value) if (isAssignable!T) //@@@9416@@@ @@ -2402,6 +3242,21 @@ Assigns $(D value) to the internally-held state. *_value = value; } + /// + @system unittest + { + import std.exception : assertThrown, assertNotThrown; + + NullableRef!int nr; + assert(nr.isNull); + assertThrown!Throwable(nr = 42); + + nr.bind(new int(0)); + assert(!nr.isNull); + assertNotThrown!Throwable(nr = 42); + assert(nr == 42); + } + /** Gets the value. $(D this) must not be in the null state. This function is also called for the implicit conversion to $(D T). @@ -2413,6 +3268,20 @@ This function is also called for the implicit conversion to $(D T). return *_value; } + /// + @system unittest + { + import std.exception : assertThrown, assertNotThrown; + + NullableRef!int nr; + //`get` is implicitly called. Will throw + //an error in non-release mode + assertThrown!Throwable(nr == 0); + + nr.bind(new int(0)); + assertNotThrown!Throwable(nr == 0); + } + /** Implicitly converts to $(D T). $(D this) must not be in the null state. @@ -2420,12 +3289,19 @@ $(D this) must not be in the null state. alias get this; } -unittest +/// ditto +auto nullableRef(T)(T* t) +{ + return NullableRef!T(t); +} + +/// +@system unittest { import std.exception : assertThrown; int x = 5, y = 7; - auto a = NullableRef!(int)(&x); + auto a = nullableRef(&x); assert(!a.isNull); assert(a == 5); assert(x == 5); @@ -2443,18 +3319,18 @@ unittest y = 135; assert(a == 135); } -unittest +@system unittest { static int f(in NullableRef!int x) { return x.isNull ? 42 : x.get; } int x = 5; - auto a = NullableRef!int(&x); + auto a = nullableRef(&x); assert(f(a) == 5); a.nullify(); assert(f(a) == 42); } -unittest +@safe unittest { // Ensure NullableRef can be used in pure/nothrow/@safe environment. function() @safe pure nothrow @@ -2473,7 +3349,7 @@ unittest assert(n.isNull); }(); } -unittest +@system unittest { // Ensure NullableRef can be used when the value is not pure/nothrow/@safe static struct S @@ -2493,7 +3369,7 @@ unittest s.nullify(); assert(s.isNull); } -unittest +@safe unittest { //Check nullable is nicelly embedable in a struct static struct S1 @@ -2512,7 +3388,7 @@ unittest ni = other.ni; } } - foreach (S; TypeTuple!(S1, S2)) + foreach (S; AliasSeq!(S1, S2)) { S a; S b = a; @@ -2520,9 +3396,9 @@ unittest c = a; } } -unittest +@system unittest { - import std.conv: to; + import std.conv : to; // Bugzilla 10915 NullableRef!int nri; @@ -2562,38 +3438,42 @@ auto-implemented function just returns the default value of the return type without doing anything. The name came from -$(WEB search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole) +$(HTTP search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole) Perl module by Sean M. Burke. -Example: --------------------- -abstract class C -{ - int m_value; - this(int v) { m_value = v; } - int value() @property { return m_value; } +Params: + Base = A non-final class for `BlackHole` to inherit from. - abstract real realValue() @property; - abstract void doSomething(); -} +See_Also: + $(LREF AutoImplement), $(LREF generateEmptyFunction) + */ +alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction); -void main() +/// +@system unittest { + import std.math : isNaN; + + static abstract class C + { + int m_value; + this(int v) { m_value = v; } + int value() @property { return m_value; } + + abstract real realValue() @property; + abstract void doSomething(); + } + auto c = new BlackHole!C(42); - writeln(c.value); // prints "42" + assert(c.value == 42); - // Abstract functions are implemented as do-nothing: - writeln(c.realValue); // prints "NaN" - c.doSomething(); // does nothing + // Returns real.init which is NaN + assert(c.realValue.isNaN); + // Abstract functions are implemented as do-nothing + c.doSomething(); } --------------------- - -See_Also: - AutoImplement, generateEmptyFunction - */ -alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction); -unittest +@system unittest { import std.math : isNaN; @@ -2628,43 +3508,41 @@ unittest inout(Object) foo() inout; } BlackHole!Foo o; - - // Bugzilla 12464 - import std.stream; - import std.typecons; - BlackHole!OutputStream dout; } /** $(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements -all abstract member functions as throw-always functions. Each auto-implemented -function fails with throwing an $(D Error) and does never return. Useful for -trapping use of not-yet-implemented functions. +all abstract member functions as functions that always fail. These functions +simply throw an $(D Error) and never return. `Whitehole` is useful for +trapping the use of class member functions that haven't been implemented. The name came from -$(WEB search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole) +$(HTTP search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole) Perl module by Michael G Schwern. -Example: --------------------- -class C -{ - abstract void notYetImplemented(); -} - -void main() -{ - auto c = new WhiteHole!C; - c.notYetImplemented(); // throws an Error -} --------------------- +Params: + Base = A non-final class for `WhiteHole` to inherit from. See_Also: - AutoImplement, generateAssertTrap + $(LREF AutoImplement), $(LREF generateAssertTrap) */ alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunction); +/// +@system unittest +{ + import std.exception : assertThrown; + + static class C + { + abstract void notYetImplemented(); + } + + auto c = new WhiteHole!C; + assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error +} + // / ditto class NotImplementedError : Error { @@ -2674,7 +3552,7 @@ class NotImplementedError : Error } } -unittest +@system unittest { import std.exception : assertThrown; // nothrow @@ -2814,19 +3692,19 @@ private static: { template Impl(names...) { - import std.typetuple : Filter; + import std.meta : Filter; static if (names.length > 0) { alias methods = Filter!(pred, MemberFunctionsTuple!(C, names[0])); alias next = Impl!(names[1 .. $]); static if (methods.length > 0) - alias Impl = TypeTuple!(OverloadSet!(names[0], methods), next); + alias Impl = AliasSeq!(OverloadSet!(names[0], methods), next); else alias Impl = next; } else - alias Impl = TypeTuple!(); + alias Impl = AliasSeq!(); } alias enumerateOverloads = Impl!(__traits(allMembers, C)); @@ -2980,7 +3858,7 @@ private static: } //debug = SHOW_GENERATED_CODE; -unittest +@system unittest { import core.vararg; // no function to implement @@ -3059,10 +3937,52 @@ unittest }+/ } +// Issue 17177 - AutoImplement fails on function overload sets with "cannot infer type from overloaded function symbol" +@system unittest +{ + static class Issue17177 + { + private string n_; + + public { + Issue17177 overloaded(string n) + { + this.n_ = n; + + return this; + } + + string overloaded() + { + return this.n_; + } + } + } + + static string how(C, alias fun)() + { + static if (!is(ReturnType!fun == void)) + { + return q{ + return parent(args); + }; + } + else + { + return q{ + parent(args); + }; + } + } + + alias Implementation = AutoImplement!(Issue17177, how, templateNot!isFinalFunction); +} + version(unittest) { // Issue 10647 - private string generateDoNothing(C, alias fun)() @property + // Add prefix "issue10647_" as a workaround for issue 1238 + private string issue10647_generateDoNothing(C, alias fun)() @property { string stmt; @@ -3076,25 +3996,25 @@ version(unittest) return stmt; } - private template isAlwaysTrue(alias fun) + private template issue10647_isAlwaysTrue(alias fun) { - enum isAlwaysTrue = true; + enum issue10647_isAlwaysTrue = true; } // Do nothing template - private template DoNothing(Base) + private template issue10647_DoNothing(Base) { - alias DoNothing = AutoImplement!(Base, generateDoNothing, isAlwaysTrue); + alias issue10647_DoNothing = AutoImplement!(Base, issue10647_generateDoNothing, issue10647_isAlwaysTrue); } // A class to be overridden - private class Foo{ + private class issue10647_Foo{ void bar(int a) { } } } -unittest +@system unittest { - auto foo = new DoNothing!Foo(); + auto foo = new issue10647_DoNothing!issue10647_Foo(); foo.bar(13); } @@ -3112,13 +4032,13 @@ Used by MemberFunctionGenerator. */ package template FuncInfo(alias func, /+[BUG 4217 ?]+/ T = typeof(&func)) { - alias RT = ReturnType!T; - alias PT = ParameterTypeTuple!T; + alias RT = ReturnType!T; + alias PT = Parameters!T; } package template FuncInfo(Func) { - alias RT = ReturnType!Func; - alias PT = ParameterTypeTuple!Func; + alias RT = ReturnType!Func; + alias PT = Parameters!Func; } /* @@ -3178,9 +4098,9 @@ private static: template CountUp(size_t n) { static if (n > 0) - alias CountUp = TypeTuple!(CountUp!(n - 1), n - 1); + alias CountUp = AliasSeq!(CountUp!(n - 1), n - 1); else - alias CountUp = TypeTuple!(); + alias CountUp = AliasSeq!(); } @@ -3314,18 +4234,17 @@ private static: /*** Function Body ***/ code ~= "{\n"; { - enum nparams = ParameterTypeTuple!(func).length; + enum nparams = Parameters!(func).length; /* Declare keywords: args, self and parent. */ string preamble; - preamble ~= "alias args = TypeTuple!(" ~ enumerateParameters!(nparams) ~ ");\n"; + preamble ~= "alias args = AliasSeq!(" ~ enumerateParameters!(nparams) ~ ");\n"; if (!isCtor) { preamble ~= "alias self = " ~ name ~ ";\n"; if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func)) - //preamble ~= "alias super." ~ name ~ " parent;\n"; // [BUG 2540] - preamble ~= "auto parent = &super." ~ name ~ ";\n"; + preamble ~= "alias parent = AliasSeq!(__traits(getMember, super, \"" ~ name ~ "\"))[0];"; } // Function body @@ -3413,7 +4332,7 @@ private static: /** -Predefined how-policies for $(D AutoImplement). These templates are used by +Predefined how-policies for $(D AutoImplement). These templates are also used by $(D BlackHole) and $(D WhiteHole), respectively. */ template generateEmptyFunction(C, func.../+[BUG 4217]+/) @@ -3466,16 +4385,16 @@ if (is(T == class) || is(T == interface)) } else { - return cast(T)typecons_d_toObject(*cast(void**)(&source)); + return cast(T) typecons_d_toObject(*cast(void**)(&source)); } } } -unittest +@system unittest { class C { @disable opCast(T)() {} } auto c = new C; - static assert(!__traits(compiles, cast(Object)c)); + static assert(!__traits(compiles, cast(Object) c)); auto o = dynamicCast!Object(c); assert(c is o); @@ -3483,7 +4402,7 @@ unittest interface J { @disable opCast(T)() {} Object instance(); } class D : I, J { Object instance() { return this; } } I i = new D(); - static assert(!__traits(compiles, cast(J)i)); + static assert(!__traits(compiles, cast(J) i)); J j = dynamicCast!J(i); assert(i.instance() is j.instance()); } @@ -3498,7 +4417,7 @@ unittest template wrap(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) { - import std.typetuple : staticMap; + import std.meta : staticMap; // strict upcast auto wrap(Source)(inout Source src) @trusted pure nothrow @@ -3532,17 +4451,17 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) template Concat(size_t i = 0) { static if (i >= Targets.length) - alias Concat = TypeTuple!(); + alias Concat = AliasSeq!(); else { - alias Concat = TypeTuple!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1)); + alias Concat = AliasSeq!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1)); } } // Remove duplicated functions based on the identifier name and function type covariance template Uniq(members...) { static if (members.length == 0) - alias Uniq = TypeTuple!(); + alias Uniq = AliasSeq!(); else { alias func = members[0]; @@ -3570,14 +4489,14 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) !is(DerivedFunctionType!(typex, remain[0].type) == void)) { alias F = DerivedFunctionType!(typex, remain[0].type); - alias Uniq = TypeTuple!(FuncInfo!(name, F), remain[1 .. $]); + alias Uniq = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]); } else - alias Uniq = TypeTuple!(FuncInfo!(name, typex), remain); + alias Uniq = AliasSeq!(FuncInfo!(name, typex), remain); } else { - alias Uniq = TypeTuple!(FuncInfo!(name, type), Uniq!(members[1 .. $])); + alias Uniq = AliasSeq!(FuncInfo!(name, type), Uniq!(members[1 .. $])); } } } @@ -3631,7 +4550,7 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) } static @property mod() { - alias type = TypeTuple!(TargetMembers[i].type)[0]; + alias type = AliasSeq!(TargetMembers[i].type)[0]; string r; static if (is(type == immutable)) r ~= " immutable"; else @@ -3646,7 +4565,7 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) enum n = to!string(i); static if (fa & FunctionAttribute.property) { - static if (ParameterTypeTuple!(TargetMembers[i].type).length == 0) + static if (Parameters!(TargetMembers[i].type).length == 0) enum fbody = "_wrap_source."~name; else enum fbody = "_wrap_source."~name~" = forward!args"; @@ -3657,7 +4576,7 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) } enum generateFun = "override "~stc~"ReturnType!(TargetMembers["~n~"].type) " - ~ name~"(ParameterTypeTuple!(TargetMembers["~n~"].type) args) "~mod~ + ~ name~"(Parameters!(TargetMembers["~n~"].type) args) "~mod~ "{ return "~fbody~"; }"; } @@ -3671,7 +4590,7 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) template wrap(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets)) { - import std.typetuple : staticMap; + import std.meta : staticMap; alias wrap = .wrap!(staticMap!(Unqual, Targets)); } @@ -3724,7 +4643,7 @@ if (!isMutable!Target) } /// -unittest +@system unittest { interface Quack { @@ -3785,7 +4704,7 @@ unittest assert(hz is h1); } /// -unittest +@system unittest { interface A { int run(); } interface B { int stop(); @property int status(); } @@ -3805,7 +4724,7 @@ unittest assert(b.status == 3); static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property); } -unittest +@system unittest { class A { @@ -3851,7 +4770,7 @@ unittest assert(d.draw(10) == 10); } } -unittest +@system unittest { // Bugzilla 10377 import std.range, std.algorithm; @@ -3868,7 +4787,7 @@ unittest auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)(); assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); } -unittest +@system unittest { // Bugzilla 10536 interface Interface @@ -3884,7 +4803,7 @@ unittest Interface i = new Pluggable().wrap!Interface; assert(i.foo() == 1); } -unittest +@system unittest { // Enhancement 10538 interface Interface @@ -3903,16 +4822,16 @@ unittest } // Make a tuple of non-static function symbols -private template GetOverloadedMethods(T) +package template GetOverloadedMethods(T) { - import std.typetuple : Filter; + import std.meta : Filter; - alias allMembers = TypeTuple!(__traits(allMembers, T)); + alias allMembers = AliasSeq!(__traits(allMembers, T)); template follows(size_t i = 0) { static if (i >= allMembers.length) { - alias follows = TypeTuple!(); + alias follows = AliasSeq!(); } else static if (!__traits(compiles, mixin("T."~allMembers[i]))) { @@ -3929,8 +4848,8 @@ private template GetOverloadedMethods(T) else enum isMethod = false; } - alias follows = TypeTuple!( - std.typetuple.Filter!(isMethod, __traits(getOverloads, T, name)), + alias follows = AliasSeq!( + std.meta.Filter!(isMethod, __traits(getOverloads, T, name)), follows!(i + 1)); } } @@ -3954,7 +4873,7 @@ private template findCovariantFunction(alias finfo, Source, Fs...) enum x = check!(); static if (x == -1 && is(typeof(Source.opDispatch))) { - alias Params = ParameterTypeTuple!(finfo.type); + alias Params = Parameters!(finfo.type); enum ptrdiff_t findCovariantFunction = is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) || is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) || @@ -4003,7 +4922,7 @@ version(unittest) alias type = FunctionTypeOf!f; } } -unittest +@system unittest { class A { @@ -4024,10 +4943,10 @@ unittest static assert(__traits(identifier, methods[1]) == "value" && is(typeof(&methods[1]) == F2*)); static assert(__traits(identifier, methods[2]) == "run" && is(typeof(&methods[2]) == F1*)); - int draw() { return 0; } - @property int value() { return 0; } - void opEquals() {} - int nomatch() { return 0; } + int draw(); + @property int value(); + void opEquals(); + int nomatch(); static assert(findCovariantFunction!(UnittestFuncInfo!draw, A, methods) == 0); static assert(findCovariantFunction!(UnittestFuncInfo!value, A, methods) == 1); static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, A, methods) == -1); @@ -4045,7 +4964,7 @@ unittest static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, B, methodsB) == ptrdiff_t.max); } -private template DerivedFunctionType(T...) +package template DerivedFunctionType(T...) { static if (!T.length) { @@ -4099,7 +5018,7 @@ private template DerivedFunctionType(T...) else alias DerivedFunctionType = void; } -unittest +@safe unittest { // attribute covariance alias int F1(); @@ -4156,21 +5075,21 @@ package template staticIota(int beg, int end) { static if (beg >= end) { - alias staticIota = TypeTuple!(); + alias staticIota = AliasSeq!(); } else { - alias staticIota = TypeTuple!(+beg); + alias staticIota = AliasSeq!(+beg); } } else { enum mid = beg + (end - beg) / 2; - alias staticIota = TypeTuple!(staticIota!(beg, mid), staticIota!(mid, end)); + alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end)); } } -private template mixinAll(mixins...) +package template mixinAll(mixins...) { static if (mixins.length == 1) { @@ -4191,7 +5110,7 @@ private template mixinAll(mixins...) } } -private template Bind(alias Template, args1...) +package template Bind(alias Template, args1...) { alias Bind(args2...) = Template!(args1, args2); } @@ -4211,9 +5130,28 @@ enum RefCountedAutoInitialize /** Defines a reference-counted object containing a $(D T) value as -payload. $(D RefCounted) keeps track of all references of an object, -and when the reference count goes down to zero, frees the underlying -store. $(D RefCounted) uses $(D malloc) and $(D free) for operation. +payload. + +An instance of $(D RefCounted) is a reference to a structure, +which is referred to as the $(I store), or $(I storage implementation +struct) in this documentation. The store contains a reference count +and the $(D T) payload. $(D RefCounted) uses $(D malloc) to allocate +the store. As instances of $(D RefCounted) are copied or go out of +scope, they will automatically increment or decrement the reference +count. When the reference count goes down to zero, $(D RefCounted) +will call $(D destroy) against the payload and call $(D free) to +deallocate the store. If the $(D T) payload contains any references +to GC-allocated memory, then $(RefCounted) will add it to the GC memory +that is scanned for pointers, and remove it from GC scanning before +$(D free) is called on the store. + +One important consequence of $(D destroy) is that it will call the +destructor of the $(D T) payload. GC-managed references are not +guaranteed to be valid during a destructor call, but other members of +$(D T), such as file handles or pointers to $(D malloc) memory, will +still be valid during the destructor call. This allows the $(D T) to +deallocate or clean up any non-GC resources immediately after the +reference count has reached zero. $(D RefCounted) is unsafe and should be used with care. No references to the payload should be escaped outside the $(D RefCounted) object. @@ -4231,9 +5169,17 @@ struct RefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !(is(T == interface))) { + extern(C) private pure nothrow @nogc static // TODO remove pure when https://issues.dlang.org/show_bug.cgi?id=15862 has been fixed + { + pragma(mangle, "free") void pureFree( void *ptr ); + pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null ); + pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p ); + } + /// $(D RefCounted) storage implementation. struct RefCountedStore { + import core.memory : pureMalloc; private struct Impl { T _payload; @@ -4244,23 +5190,64 @@ if (!is(T == class) && !(is(T == interface))) private void initialize(A...)(auto ref A args) { - import core.memory : GC; - import core.stdc.stdlib : malloc; + import core.exception : onOutOfMemoryError; import std.conv : emplace; - import std.exception : enforce; - _store = cast(Impl*) enforce(malloc(Impl.sizeof)); + _store = cast(Impl*) pureMalloc(Impl.sizeof); + if (_store is null) + onOutOfMemoryError(); static if (hasIndirections!T) - GC.addRange(&_store._payload, T.sizeof); + pureGcAddRange(&_store._payload, T.sizeof); emplace(&_store._payload, args); _store._count = 1; } + private void move(ref T source) + { + import core.exception : onOutOfMemoryError; + import core.stdc.string : memcpy, memset; + + _store = cast(Impl*) pureMalloc(Impl.sizeof); + if (_store is null) + onOutOfMemoryError(); + static if (hasIndirections!T) + pureGcAddRange(&_store._payload, T.sizeof); + + // Can't use std.algorithm.move(source, _store._payload) + // here because it requires the target to be initialized. + // Might be worth to add this as `moveEmplace` + + // Can avoid destructing result. + static if (hasElaborateAssign!T || !isAssignable!T) + memcpy(&_store._payload, &source, T.sizeof); + else + _store._payload = source; + + // If the source defines a destructor or a postblit hook, we must obliterate the + // object in order to avoid double freeing and undue aliasing + static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) + { + // If T is nested struct, keep original context pointer + static if (__traits(isNested, T)) + enum sz = T.sizeof - (void*).sizeof; + else + enum sz = T.sizeof; + + auto init = typeid(T).initializer(); + if (init.ptr is null) // null ptr means initialize to 0s + memset(&source, 0, sz); + else + memcpy(&source, init.ptr, sz); + } + + _store._count = 1; + } + /** Returns $(D true) if and only if the underlying store has been allocated and initialized. */ - @property nothrow @safe + @property nothrow @safe pure @nogc bool isInitialized() const { return _store !is null; @@ -4270,7 +5257,7 @@ if (!is(T == class) && !(is(T == interface))) Returns underlying reference count if it is allocated and initialized (a positive integer), and $(D 0) otherwise. */ - @property nothrow @safe + @property nothrow @safe pure @nogc size_t refCount() const { return isInitialized ? _store._count : 0; @@ -4305,11 +5292,17 @@ Postcondition: $(D refCountedStore.isInitialized) _refCounted.initialize(args); } + /// Ditto + this(T val) + { + _refCounted.move(val); + } + /** Constructor that tracks the reference count appropriately. If $(D !refCountedStore.isInitialized), does nothing. */ - this(this) + this(this) @safe pure nothrow @nogc { if (!_refCounted.isInitialized) return; ++_refCounted._store._count; @@ -4331,11 +5324,10 @@ to deallocate the corresponding resource. .destroy(_refCounted._store._payload); static if (hasIndirections!T) { - import core.memory : GC; - GC.removeRange(&_refCounted._store._payload); + pureGcRemoveRange(&_refCounted._store._payload); } - import core.stdc.stdlib : free; - free(_refCounted._store); + + pureFree(_refCounted._store); _refCounted._store = null; } @@ -4344,7 +5336,7 @@ Assignment operators */ void opAssign(typeof(this) rhs) { - import std.algorithm : swap; + import std.algorithm.mutation : swap; swap(_refCounted._store, rhs._refCounted._store); } @@ -4352,7 +5344,7 @@ Assignment operators /// Ditto void opAssign(T rhs) { - import std.algorithm : move; + import std.algorithm.mutation : move; static if (autoInit == RefCountedAutoInitialize.yes) { @@ -4383,11 +5375,11 @@ Assignment operators (but will still assert if not initialized). */ @property - ref T refCountedPayload(); + ref T refCountedPayload() return; /// ditto - @property nothrow @safe - ref inout(T) refCountedPayload() inout; + @property nothrow @safe pure @nogc + ref inout(T) refCountedPayload() inout return; } else { @@ -4395,15 +5387,15 @@ Assignment operators { //Can't use inout here because of potential mutation @property - ref T refCountedPayload() + ref T refCountedPayload() return { _refCounted.ensureInitialized(); return _refCounted._store._payload; } } - @property nothrow @safe - ref inout(T) refCountedPayload() inout + @property nothrow @safe pure @nogc + ref inout(T) refCountedPayload() inout return { assert(_refCounted.isInitialized, "Attempted to access an uninitialized payload."); return _refCounted._store._payload; @@ -4420,7 +5412,7 @@ assert(refCountedStore.isInitialized)). } /// -unittest +pure @system nothrow @nogc unittest { // A pair of an $(D int) and a $(D size_t) - the latter being the // reference count - will be dynamically allocated @@ -4434,7 +5426,7 @@ unittest // the pair will be freed when rc1 and rc2 go out of scope } -unittest +pure @system unittest { RefCounted!int* p; { @@ -4473,16 +5465,16 @@ unittest assert(a.x._refCounted._store._count == 2, "BUG 4356 still unfixed"); } -unittest +pure @system nothrow @nogc unittest { - import std.algorithm : swap; + import std.algorithm.mutation : swap; RefCounted!int p1, p2; swap(p1, p2); } // 6606 -unittest +@safe pure nothrow @nogc unittest { union U { size_t i; @@ -4497,7 +5489,7 @@ unittest } // 6436 -unittest +@system pure unittest { struct S { this(ref int val) { assert(val == 3); ++val; } } @@ -4506,7 +5498,15 @@ unittest assert(val == 4); } -unittest +// gc_addRange coverage +@system pure unittest +{ + struct S { int* p; } + + auto s = RefCounted!S(null); +} + +@system pure nothrow @nogc unittest { RefCounted!int a; a = 5; //This should not assert @@ -4515,19 +5515,66 @@ unittest RefCounted!int b; b = a; //This should not assert either assert(b == 5); + + RefCounted!(int*) c; +} + +/** + * Initializes a `RefCounted` with `val`. The template parameter + * `T` of `RefCounted` is inferred from `val`. + * This function can be used to move non-copyable values to the heap. + * It also disables the `autoInit` option of `RefCounted`. + * + * Params: + * val = The value to be reference counted + * Returns: + * An initialized $(D RefCounted) containing $(D val). + * See_Also: + * $(HTTP en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared) + */ +RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val) +{ + typeof(return) res; + res._refCounted.move(val); + return res; +} + +/// +@system unittest +{ + static struct File + { + string name; + @disable this(this); // not copyable + ~this() { name = null; } + } + + auto file = File("name"); + assert(file.name == "name"); + // file cannot be copied and has unique ownership + static assert(!__traits(compiles, {auto file2 = file;})); + + // make the file refcounted to share ownership + import std.algorithm.mutation : move; + auto rcFile = refCounted(move(file)); + assert(rcFile.name == "name"); + assert(file.name == null); + auto rcFile2 = rcFile; + assert(rcFile.refCountedStore.refCount == 2); + // file gets properly closed when last reference is dropped } /** Creates a proxy for the value `a` that will forward all operations - while disabling implicit conversions. The aliased item `a` must be - an $(B lvalue). This is useful for creating a new type from the - "base" type (though this is $(B not) a subtype-supertype - relationship; the new type is not related to the old type in any way, + while disabling implicit conversions. The aliased item `a` must be + an $(B lvalue). This is useful for creating a new type from the + "base" type (though this is $(B not) a subtype-supertype + relationship; the new type is not related to the old type in any way, by design). - + The new type supports all operations that the underlying type does, including all operators such as `+`, `--`, `<`, `[]`, etc. - + Params: a = The value to act as a proxy for all operations. It must be an lvalue. @@ -4535,8 +5582,14 @@ unittest mixin template Proxy(alias a) { private alias ValueType = typeof({ return a; }()); + + /* Determine if 'T.a' can referenced via a const(T). + * Use T* as the parameter because 'scope' inference needs a fully + * analyzed T, which doesn't work when accessibleFrom() is used in a + * 'static if' in the definition of Proxy or T. + */ private enum bool accessibleFrom(T) = - is(typeof((ref T self){ cast(void)mixin("self." ~ a.stringof); })); + is(typeof((T* self){ cast(void) mixin("(*self)."~__traits(identifier, a)); })); static if (is(typeof(this) == class)) { @@ -4544,9 +5597,7 @@ mixin template Proxy(alias a) { if (auto b = cast(typeof(this))o) { - import std.algorithm : startsWith; - static assert(startsWith(a.stringof, "this.")); - return a == mixin("b."~a.stringof[5..$]); // remove "this." + return a == mixin("b."~__traits(identifier, a)); } return false; } @@ -4566,10 +5617,8 @@ mixin template Proxy(alias a) { if (auto b = cast(typeof(this))o) { - import std.algorithm : startsWith; - static assert(startsWith(a.stringof, "this.")); // remove "this." - return a < mixin("b."~a.stringof[5..$]) ? -1 - : a > mixin("b."~a.stringof[5..$]) ? +1 : 0; + return a < mixin("b."~__traits(identifier, a)) ? -1 + : a > mixin("b."~__traits(identifier, a)) ? +1 : 0; } static if (is(ValueType == class)) return a.opCmp(o); @@ -4606,9 +5655,7 @@ mixin template Proxy(alias a) { static if (is(immutable B == immutable typeof(this))) { - import std.algorithm : startsWith; - static assert(startsWith(a.stringof, "this.")); - return a == mixin("b."~a.stringof[5..$]); // remove "this." + return a == mixin("b."~__traits(identifier, a)); } else return a == b; @@ -4621,8 +5668,10 @@ mixin template Proxy(alias a) return a.opCmp(b); else static if (is(typeof(b.opCmp(a)))) return -b.opCmp(a); + else static if (isFloatingPoint!ValueType || isFloatingPoint!B) + return a < b ? -1 : a > b ? +1 : a == b ? 0 : float.nan; else - return a < b ? -1 : a > b ? +1 : 0; + return a < b ? -1 : (a > b); } static if (accessibleFrom!(const typeof(this))) @@ -4640,16 +5689,16 @@ mixin template Proxy(alias a) auto ref opCall(this X, Args...)(auto ref Args args) { return a(args); } - auto ref opCast(T, this X)() { return cast(T)a; } + auto ref opCast(T, this X)() { return cast(T) a; } auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; } auto ref opSlice(this X )() { return a[]; } - auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { return a[b..e]; } + auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { return a[b .. e]; } auto ref opUnary (string op, this X )() { return mixin(op~"a"); } auto ref opIndexUnary(string op, this X, D...)(auto ref D i) { return mixin(op~"a[i]"); } auto ref opSliceUnary(string op, this X )() { return mixin(op~"a[]"); } - auto ref opSliceUnary(string op, this X, B, E)(auto ref B b, auto ref E e) { return mixin(op~"a[b..e]"); } + auto ref opSliceUnary(string op, this X, B, E)(auto ref B b, auto ref E e) { return mixin(op~"a[b .. e]"); } auto ref opBinary(string op, this X, B)(auto ref B b) if (op == "in" && is(typeof(a in b)) || op != "in") @@ -4665,7 +5714,7 @@ mixin template Proxy(alias a) { auto ref opAssign(this X)(auto ref typeof(this) v) { - a = mixin("v."~a.stringof[5..$]); // remove "this." + a = mixin("v."~__traits(identifier, a)); return this; } } @@ -4678,12 +5727,12 @@ mixin template Proxy(alias a) auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; } auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; } auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; } - auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b..e] = v; } + auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; } auto ref opOpAssign (string op, this X, V )(auto ref V v) { return mixin("a " ~op~"= v"); } auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i) { return mixin("a[i] " ~op~"= v"); } auto ref opSliceOpAssign(string op, this X, V )(auto ref V v) { return mixin("a[] " ~op~"= v"); } - auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return mixin("a[b..e] "~op~"= v"); } + auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return mixin("a[b .. e] "~op~"= v"); } template opDispatch(string name) { @@ -4735,7 +5784,7 @@ mixin template Proxy(alias a) } /// -unittest +@safe unittest { struct MyInt { @@ -4760,65 +5809,65 @@ unittest } ///The proxied value must be an $(B lvalue). -unittest +@safe unittest { struct NewIntType { - //Won't work; the literal '1' is + //Won't work; the literal '1' //is an rvalue, not an lvalue - //mixin Proxy!1; - + //mixin Proxy!1; + //Okay, n is an lvalue int n; mixin Proxy!n; - + this(int n) { this.n = n; } } - + NewIntType nit = 0; nit++; assert(nit == 1); - - + + struct NewObjectType { Object obj; //Ok, obj is an lvalue mixin Proxy!obj; - + this (Object o) { obj = o; } } - + NewObjectType not = new Object(); assert(__traits(compiles, not.toHash())); } /** There is one exception to the fact that the new type is not related to the - old type. $(LINK2 http://dlang.org/function.html#pseudo-member, Pseudo-member) - functions are usable with the new type; they will be forwarded on to the + old type. $(DDSUBLINK spec/function,pseudo-member, Pseudo-member) + functions are usable with the new type; they will be forwarded on to the proxied value. */ -unittest +@safe unittest { import std.math; - + float f = 1.0; assert(!f.isInfinity); - + struct NewFloat { float _; mixin Proxy!_; - + this(float f) { _ = f; } } - + NewFloat nf = 1.0f; assert(!nf.isInfinity); } -unittest +@safe unittest { static struct MyInt { @@ -4830,7 +5879,7 @@ unittest static immutable arr = [1,2,3]; } - foreach (T; TypeTuple!(MyInt, const MyInt, immutable MyInt)) + foreach (T; AliasSeq!(MyInt, const MyInt, immutable MyInt)) { T m = 10; static assert(!__traits(compiles, { int x = m; })); @@ -4840,7 +5889,7 @@ unittest assert(m < 20); assert(+m == 10); assert(-m == -10); - assert(cast(double)m == 10.0); + assert(cast(double) m == 10.0); assert(m + 10 == 20); assert(m - 5 == 5); assert(m * 20 == 200); @@ -4865,7 +5914,7 @@ unittest static assert(T.arr == [1,2,3]); } } -unittest +@system unittest { static struct MyArray { @@ -4875,7 +5924,7 @@ unittest this(immutable int[] arr) immutable { value = arr; } } - foreach (T; TypeTuple!(MyArray, const MyArray, immutable MyArray)) + foreach (T; AliasSeq!(MyArray, const MyArray, immutable MyArray)) { static if (is(T == immutable) && !is(typeof({ T a = [1,2,3,4]; }))) T a = [1,2,3,4].idup; // workaround until qualified ctor is properly supported @@ -4885,27 +5934,27 @@ unittest assert(a != [5,6,7,8]); assert(+a[0] == 1); version (LittleEndian) - assert(cast(ulong[])a == [0x0000_0002_0000_0001, 0x0000_0004_0000_0003]); + assert(cast(ulong[]) a == [0x0000_0002_0000_0001, 0x0000_0004_0000_0003]); else - assert(cast(ulong[])a == [0x0000_0001_0000_0002, 0x0000_0003_0000_0004]); + assert(cast(ulong[]) a == [0x0000_0001_0000_0002, 0x0000_0003_0000_0004]); assert(a ~ [10,11] == [1,2,3,4,10,11]); assert(a[0] == 1); assert(a[] == [1,2,3,4]); - assert(a[2..4] == [3,4]); + assert(a[2 .. 4] == [3,4]); static if (is(T == MyArray)) // mutable { a = a; a = [5,6,7,8]; assert(a == [5,6,7,8]); a[0] = 0; assert(a == [0,6,7,8]); a[] = 1; assert(a == [1,1,1,1]); - a[0..3] = 2; assert(a == [2,2,2,1]); + a[0 .. 3] = 2; assert(a == [2,2,2,1]); a[0] += 2; assert(a == [4,2,2,1]); a[] *= 2; assert(a == [8,4,4,2]); - a[0..2] /= 2; assert(a == [4,2,4,2]); + a[0 .. 2] /= 2; assert(a == [4,2,4,2]); } } } -unittest +@system unittest { class Foo { @@ -4972,16 +6021,16 @@ unittest // bug5896 test assert(h.opCast!int() == 0); - assert(cast(int)h == 0); + assert(cast(int) h == 0); const ih = new const Hoge(new Foo()); static assert(!__traits(compiles, ih.opCast!int())); - static assert(!__traits(compiles, cast(int)ih)); + static assert(!__traits(compiles, cast(int) ih)); // template member function assert(h.tempfunc!int() == 0); } -unittest // about Proxy inside a class +@system unittest // about Proxy inside a class { class MyClass { @@ -5012,9 +6061,9 @@ unittest // about Proxy inside a class Object c = new MyClass2(5); Object d = new MyClass3(5); assert(a == b); - assert((cast(MyClass)a) == 5); - assert(5 == (cast(MyClass)b)); - assert(5 == cast(MyClass2)c); + assert((cast(MyClass) a) == 5); + assert(5 == (cast(MyClass) b)); + assert(5 == cast(MyClass2) c); assert(a != d); assert(c != a); @@ -5024,23 +6073,23 @@ unittest // about Proxy inside a class // MyClass.opEquals doesn't know MyClass2. // so, c.opEquals(a) is true, but a.opEquals(c) is false. // furthermore, opEquals(T) couldn't be invoked. - assert((cast(MyClass2)c) != (cast(MyClass)a)); + assert((cast(MyClass2) c) != (cast(MyClass) a)); // opCmp Object e = new MyClass2(7); - assert(a < cast(MyClass2)e); // OK. and + assert(a < cast(MyClass2) e); // OK. and assert(e > a); // OK, but... // assert(a < e); // RUNTIME ERROR! - // assert((cast(MyClass)a) < e); // RUNTIME ERROR! - assert(3 < cast(MyClass)a); - assert((cast(MyClass2)e) < 11); + // assert((cast(MyClass) a) < e); // RUNTIME ERROR! + assert(3 < cast(MyClass) a); + assert((cast(MyClass2) e) < 11); // opCall - assert((cast(MyClass2)e)("hello") == "hello"); + assert((cast(MyClass2) e)("hello") == "hello"); // opCast - assert((cast(MyClass)(cast(MyClass2)c)) == a); - assert((cast(int)(cast(MyClass2)c)) == 5); + assert((cast(MyClass)(cast(MyClass2) c)) == a); + assert((cast(int)(cast(MyClass2) c)) == 5); // opIndex class MyClass4 @@ -5061,27 +6110,27 @@ unittest // about Proxy inside a class assert(f[1] == 'e'); // opSlice - assert(f[2..4] == "ll"); + assert(f[2 .. 4] == "ll"); // opUnary - assert(-(cast(MyClass2)c) == -5); + assert(-(cast(MyClass2) c) == -5); // opBinary - assert((cast(MyClass)a) + (cast(MyClass2)c) == 10); - assert(5 + cast(MyClass)a == 10); + assert((cast(MyClass) a) + (cast(MyClass2) c) == 10); + assert(5 + cast(MyClass) a == 10); // opAssign - (cast(MyClass2)c) = 11; - assert((cast(MyClass2)c) == 11); - (cast(MyClass2)c) = new MyClass(13); - assert((cast(MyClass2)c) == 13); + (cast(MyClass2) c) = 11; + assert((cast(MyClass2) c) == 11); + (cast(MyClass2) c) = new MyClass(13); + assert((cast(MyClass2) c) == 13); // opOpAssign - assert((cast(MyClass2)c) += 4); - assert((cast(MyClass2)c) == 17); + assert((cast(MyClass2) c) += 4); + assert((cast(MyClass2) c) == 17); // opDispatch - assert((cast(MyClass2)c).pow(2) == 289); + assert((cast(MyClass2) c).pow(2) == 289); // opDollar assert(f[2..$-1] == "ll"); @@ -5094,7 +6143,7 @@ unittest // about Proxy inside a class assert(hash[c] == 21); } -unittest +@safe unittest { struct MyInt { @@ -5131,7 +6180,7 @@ unittest MyFoo2 f2; f2 = f2; } -unittest +@safe unittest { // bug8613 static struct Name @@ -5146,7 +6195,7 @@ unittest bool* b = Name("a") in names; } -unittest +@system unittest { // bug14213, using function for the payload static struct S @@ -5165,6 +6214,54 @@ unittest assert(s * 2 == 24); } +// Check all floating point comparisons for both Proxy and Typedef, +// also against int and a Typedef!int, to be as regression-proof +// as possible. bug 15561 +@safe unittest +{ + static struct MyFloatImpl + { + float value; + mixin Proxy!value; + } + static void allFail(T0, T1)(T0 a, T1 b) + { + assert(!(a == b)); + assert(!(a<b)); + assert(!(a <= b)); + assert(!(a>b)); + assert(!(a >= b)); + } + foreach (T1; AliasSeq!(MyFloatImpl, Typedef!float, Typedef!double, + float, real, Typedef!int, int)) + { + foreach (T2; AliasSeq!(MyFloatImpl, Typedef!float)) + { + T1 a; + T2 b; + + static if (isFloatingPoint!T1 || isFloatingPoint!(TypedefType!T1)) + allFail(a, b); + a = 3; + allFail(a, b); + + b = 4; + assert(a != b); + assert(a<b); + assert(a <= b); + assert(!(a>b)); + assert(!(a >= b)); + + a = 4; + assert(a == b); + assert(!(a<b)); + assert(a <= b); + assert(!(a>b)); + assert(a >= b); + } + } +} + /** $(B Typedef) allows the creation of a unique type which is based on an existing type. Unlike the $(D alias) feature, @@ -5230,19 +6327,45 @@ struct Typedef(T, T init = T.init, string cookie=null) this(tdef.Typedef_payload); } - // We need to add special overload for cast(Typedef!X)exp, + // We need to add special overload for cast(Typedef!X) exp, // thus we can't simply inherit Proxy!Typedef_payload T2 opCast(T2 : Typedef!(T, Unused), this X, T, Unused...)() { - return T2(cast(T)Typedef_payload); + return T2(cast(T) Typedef_payload); } auto ref opCast(T2, this X)() { - return cast(T2)Typedef_payload; + return cast(T2) Typedef_payload; } mixin Proxy!Typedef_payload; + + pure nothrow @nogc @safe @property + { + alias TD = typeof(this); + static if (isIntegral!T) + { + static TD min() {return TD(T.min);} + static TD max() {return TD(T.max);} + } + else static if (isFloatingPoint!T) + { + static TD infinity() {return TD(T.infinity);} + static TD nan() {return TD(T.nan);} + static TD dig() {return TD(T.dig);} + static TD epsilon() {return TD(T.epsilon);} + static TD mant_dig() {return TD(T.mant_dig);} + static TD max_10_exp() {return TD(T.max_10_exp);} + static TD max_exp() {return TD(T.max_exp);} + static TD min_10_exp() {return TD(T.min_10_exp);} + static TD min_exp() {return TD(T.min_exp);} + static TD max() {return TD(T.max);} + static TD min_normal() {return TD(T.min_normal);} + TD re() {return TD(Typedef_payload.re);} + TD im() {return TD(Typedef_payload.im);} + } + } } /** @@ -5258,10 +6381,10 @@ template TypedefType(T) } /// -unittest +@safe unittest { - import std.typecons: Typedef, TypedefType; - import std.conv: to; + import std.typecons : Typedef, TypedefType; + import std.conv : to; alias MyInt = Typedef!int; static assert(is(TypedefType!MyInt == int)); @@ -5276,14 +6399,14 @@ unittest assert(myInt == 5); // cast to the underlying type to get the value that's being wrapped - int x = cast(TypedefType!MyInt)myInt; + int x = cast(TypedefType!MyInt) myInt; alias MyIntInit = Typedef!(int, 42); static assert(is(TypedefType!MyIntInit == int)); static assert(MyIntInit() == 42); } -unittest +@safe unittest { Typedef!int x = 10; static assert(!__traits(compiles, { int y = x; })); @@ -5308,11 +6431,11 @@ unittest static assert(typeof(sa).length == 3); Typedef!(int[3]) dollar1; - assert(dollar1[0..$] is dollar1[0..3]); + assert(dollar1[0..$] is dollar1[0 .. 3]); Typedef!(int[]) dollar2; dollar2.length = 3; - assert(dollar2[0..$] is dollar2[0..3]); + assert(dollar2[0..$] is dollar2[0 .. 3]); static struct Dollar1 { @@ -5324,7 +6447,7 @@ unittest Typedef!Dollar1 drange1; assert(drange1[0..$] == 1); - assert(drange1[0..1] == 2); + assert(drange1[0 .. 1] == 2); static struct Dollar2 { @@ -5345,7 +6468,22 @@ unittest assert(drange3[$] == 123); } -unittest +@safe @nogc pure nothrow unittest // Bugzilla 11703 +{ + alias I = Typedef!int; + static assert(is(typeof(I.min) == I)); + static assert(is(typeof(I.max) == I)); + + alias F = Typedef!double; + static assert(is(typeof(F.infinity) == F)); + static assert(is(typeof(F.epsilon) == F)); + + F f; + assert(!is(typeof(F.re).stringof == double)); + assert(!is(typeof(F.im).stringof == double)); +} + +@safe unittest { // bug8655 import std.typecons; @@ -5363,7 +6501,7 @@ unittest } } -unittest // Issue 12596 +@safe unittest // Issue 12596 { import std.typecons; alias TD = Typedef!int; @@ -5372,7 +6510,7 @@ unittest // Issue 12596 assert(x == y); } -unittest // about toHash +@safe unittest // about toHash { import std.typecons; { @@ -5430,14 +6568,14 @@ unittest // about toHash } } -unittest +@system unittest { alias String = Typedef!(char[]); alias CString = Typedef!(const(char)[]); CString cs = "fubar"; - String s = cast(String)cs; + String s = cast(String) cs; assert(cs == s); - char[] s2 = cast(char[])cs; + char[] s2 = cast(char[]) cs; const(char)[] cs2 = cast(const(char)[])s; assert(s2 == cs2); } @@ -5448,7 +6586,16 @@ therefore avoiding the overhead of $(D new). This facility is unsafe; it is the responsibility of the user to not escape a reference to the object outside the scope. -Note: it's illegal to move a class reference even if you are sure there +The class destructor will be called when the result of `scoped()` is +itself destroyed. + +Scoped class instances can be embedded in a parent `class` or `struct`, +just like a child struct instance. Scoped member variables must have +type `typeof(scoped!Class(args))`, and be initialized with a call to +scoped. See below for an example. + +Note: +It's illegal to move a class instance even if you are sure there are no pointers to it. As such, it is illegal to move a scoped object. */ template scoped(T) @@ -5466,11 +6613,11 @@ template scoped(T) @property inout(T) Scoped_payload() inout { - void* alignedStore = cast(void*) aligned(cast(size_t) Scoped_store.ptr); + void* alignedStore = cast(void*) aligned(cast(uintptr_t) Scoped_store.ptr); // As `Scoped` can be unaligned moved in memory class instance should be moved accordingly. immutable size_t d = alignedStore - Scoped_store.ptr; size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof]; - if(d != *currD) + if (d != *currD) { import core.stdc.string; memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T)); @@ -5491,65 +6638,112 @@ template scoped(T) } } - /// Returns the scoped object + /** Returns the _scoped object. + Params: args = Arguments to pass to $(D T)'s constructor. + */ @system auto scoped(Args...)(auto ref Args args) { import std.conv : emplace; Scoped result = void; - void* alignedStore = cast(void*) aligned(cast(size_t) result.Scoped_store.ptr); + void* alignedStore = cast(void*) aligned(cast(uintptr_t) result.Scoped_store.ptr); immutable size_t d = alignedStore - result.Scoped_store.ptr; *cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d; emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args); return result; } } + /// -unittest +@system unittest { class A { int x; this() {x = 0;} this(int i){x = i;} + ~this() {} } - // Standard usage + // Standard usage, constructing A on the stack auto a1 = scoped!A(); - auto a2 = scoped!A(1); a1.x = 42; - assert(a1.x == 42); - assert(a2.x == 1); + + // Result of `scoped` call implicitly converts to a class reference + A aRef = a1; + assert(aRef.x == 42); + + // Scoped destruction + { + auto a2 = scoped!A(1); + assert(a2.x == 1); + aRef = a2; + // a2 is destroyed here, calling A's destructor + } + // aRef is now an invalid reference + + // Here the temporary scoped A is immediately destroyed. + // This means the reference is then invalid. + version(Bug) + { + // Wrong, should use `auto` + A invalid = scoped!A(); + } // Restrictions + version(Bug) + { + import std.algorithm.mutation : move; + auto invalid = a1.move; // illegal, scoped objects can't be moved + } static assert(!is(typeof({ auto e1 = a1; // illegal, scoped objects can't be copied - assert([a2][0].x == 42); // ditto + assert([a1][0].x == 42); // ditto + }))); + static assert(!is(typeof({ alias ScopedObject = typeof(a1); - auto e2 = ScopedObject(); //Illegal, must be built via scoped!A - auto e3 = ScopedObject(1); //Illegal, must be built via scoped!A + auto e2 = ScopedObject(); // illegal, must be built via scoped!A + auto e3 = ScopedObject(1); // ditto }))); + // Use with alias + alias makeScopedA = scoped!A; + auto a3 = makeScopedA(); + auto a4 = makeScopedA(1); + // Use as member variable struct B { typeof(scoped!A()) a; // note the trailing parentheses + + this(int i) + { + // construct member + a = scoped!A(i); + } } - // Use with alias - alias makeScopedA = scoped!A; - auto a6 = makeScopedA(); - auto a7 = makeScopedA(); + // Stack-allocate + auto b1 = B(5); + aRef = b1.a; + assert(aRef.x == 5); + destroy(b1); // calls A's destructor for b1.a + // aRef is now an invalid reference + + // Heap-allocate + auto b2 = new B(6); + assert(b2.a.x == 6); + destroy(*b2); // calls A's destructor for b2.a } -private size_t _alignUp(size_t alignment)(size_t n) - if(alignment > 0 && !((alignment - 1) & alignment)) +private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n) + if (alignment > 0 && !((alignment - 1) & alignment)) { enum badEnd = alignment - 1; // 0b11, 0b111, ... return (n + badEnd) & ~badEnd; } -unittest // Issue 6580 testcase +@system unittest // Issue 6580 testcase { enum alignment = (void*).alignof; @@ -5581,8 +6775,8 @@ unittest // Issue 6580 testcase auto c1long = scoped!C1long(3, var); assert(var == 6); auto c2long = scoped!C2long(); - assert(cast(size_t)&c1long.long_ % longAlignment == 0); - assert(cast(size_t)&c2long.long_ % longAlignment == 0); + assert(cast(uint)&c1long.long_ % longAlignment == 0); + assert(cast(uint)&c2long.long_ % longAlignment == 0); assert(c1long.long_ == 3 && c1long.byte_ == 4); assert(c2long.byte_ == [5, 6] && c2long.long_ == 7); } @@ -5597,7 +6791,7 @@ unittest // Issue 6580 testcase alloca(size); alignmentTest(); } - foreach(i; 0 .. 10) + foreach (i; 0 .. 10) test(i); } else @@ -5607,21 +6801,21 @@ unittest // Issue 6580 testcase byte[size] arr; alignmentTest(); } - foreach(i; TypeTuple!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + foreach (i; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) test!i(); } } -unittest // Original Issue 6580 testcase +@system unittest // Original Issue 6580 testcase { class C { int i; byte b; } auto sa = [scoped!C(), scoped!C()]; - assert(cast(size_t)&sa[0].i % int.alignof == 0); - assert(cast(size_t)&sa[1].i % int.alignof == 0); // fails + assert(cast(uint)&sa[0].i % int.alignof == 0); + assert(cast(uint)&sa[1].i % int.alignof == 0); // fails } -unittest +@system unittest { class A { int x = 1; } auto a1 = scoped!A(); @@ -5632,7 +6826,7 @@ unittest assert(a1.x == 42); } -unittest +@system unittest { class A { int x = 1; this() { x = 2; } } auto a1 = scoped!A(); @@ -5643,7 +6837,7 @@ unittest assert(a1.x == 42); } -unittest +@system unittest { class A { int x = 1; this(int y) { x = y; } ~this() {} } auto a1 = scoped!A(5); @@ -5654,7 +6848,7 @@ unittest assert(a1.x == 42); } -unittest +@system unittest { class A { static bool dead; ~this() { dead = true; } } class B : A { static bool dead; ~this() { dead = true; } } @@ -5665,7 +6859,7 @@ unittest assert(A.dead, "asdasd"); } -unittest // Issue 8039 testcase +@system unittest // Issue 8039 testcase { static int dels; static struct S { ~this(){ ++dels; } } @@ -5688,7 +6882,7 @@ unittest // Issue 8039 testcase assert(dels == 1+6); } -unittest +@system unittest { // bug4500 class A @@ -5709,7 +6903,7 @@ unittest assert(a1.check()); } -unittest +@system unittest { static class A { @@ -5732,13 +6926,13 @@ unittest auto abob = scoped!ABob(); } -unittest +@safe unittest { static class A { this(int) {} } static assert(!__traits(compiles, scoped!A())); } -unittest +@system unittest { static class A { @property inout(int) foo() inout { return 1; } } @@ -5767,7 +6961,7 @@ unittest static assert(is(typeof(c3.foo) == immutable(int))); } -unittest +@system unittest { class C { this(ref int val) { assert(val == 3); ++val; } } @@ -5776,7 +6970,7 @@ unittest assert(val == 4); } -unittest +@system unittest { class C { @@ -5833,29 +7027,21 @@ string getLine(Flag!"keepTerminator" keepTerminator) ... } ... -auto line = getLine(Flag!"keepTerminator".yes); +auto line = getLine(Yes.keepTerminator); ---- +The structs $(D Yes) and $(D No) are provided as shorthand for +$(D Flag!"Name".yes) and $(D Flag!"Name".no) and are preferred for brevity and +readability. These convenience structs mean it is usually unnecessary and +counterproductive to create an alias of a $(D Flag) as a way of avoiding typing +out the full type while specifying the affirmative or negative options. + Passing categorical data by means of unstructured $(D bool) parameters is classified under "simple-data coupling" by Steve McConnell in the $(LUCKY Code Complete) book, along with three other kinds of coupling. The author argues citing several studies that coupling has a negative effect on code quality. $(D Flag) offers a simple structuring method for passing yes/no flags to APIs. - -An alias can be used to reduce the verbosity of the flag's type: ----- -alias KeepTerminator = Flag!"keepTerminator"; -string getline(KeepTerminator keepTerminator) -{ - ... - if (keepTerminator) ... - ... -} -... -// Code calling getLine can now refer to flag values using the shorter name: -auto line = getLine(KeepTerminator.yes); ----- */ template Flag(string name) { /// @@ -5901,7 +7087,7 @@ struct No } /// -unittest +@safe unittest { Flag!"abc" flag1; assert(flag1 == Flag!"abc".no); @@ -5948,10 +7134,10 @@ template isBitFlagEnum(E) enum A { None, - A = 1<<0, - B = 1<<1, - C = 1<<2, - D = 1<<3, + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + D = 1 << 3, } static assert(isBitFlagEnum!A); @@ -5968,15 +7154,15 @@ template isBitFlagEnum(E) enum C: double { - A = 1<<0, - B = 1<<1 + A = 1 << 0, + B = 1 << 1 } static assert(!isBitFlagEnum!C); } /** -A typesafe structure for storing combination of enum values. +A typesafe structure for storing combinations of enum values. This template defines a simple struct to represent bitwise OR combinations of enum values. It can be used if all the enum values are integral constants with @@ -5987,12 +7173,12 @@ the OR combination, which can produce surprising effects like this: ---- enum E { - A = 1<<0, - B = 1<<1 + A = 1 << 0, + B = 1 << 1 } E e = E.A | E.B; // will throw SwitchError -final switch(e) +final switch (e) { case E.A: return; @@ -6135,11 +7321,12 @@ public: enum Enum { None, - A = 1<<0, - B = 1<<1, - C = 1<<2 + A = 1 << 0, + B = 1 << 1, + C = 1 << 2 } - static assert(__traits(compiles, BitFlags!Enum)); + BitFlags!Enum flags1; + assert(!(flags1 & (Enum.A | Enum.B | Enum.C))); // You need to specify the $(D unsafe) parameter for enum with custom values enum UnsafeEnum @@ -6149,8 +7336,8 @@ public: C, D = B|C } - static assert(!__traits(compiles, BitFlags!UnsafeEnum)); - static assert(__traits(compiles, BitFlags!(UnsafeEnum, Yes.unsafe))); + static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags2; })); + BitFlags!(UnsafeEnum, Yes.unsafe) flags3; immutable BitFlags!Enum flags_empty; // A default constructed BitFlags has no value set @@ -6178,7 +7365,7 @@ public: // use & between BitFlags for intersection immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C); - assert (flags_B == (flags_BC & flags_AB)); + assert(flags_B == (flags_BC & flags_AB)); // All the binary operators work in their assignment version BitFlags!Enum temp = flags_empty; @@ -6205,6 +7392,449 @@ public: assert(flags_AB & Enum.A); // Finally, you can of course get you raw value out of flags - auto value = cast(int)flags_A; + auto value = cast(int) flags_A; assert(value == Enum.A); } + +// ReplaceType +/** +Replaces all occurrences of `From` into `To`, in one or more types `T`. For +example, $(D ReplaceType!(int, uint, Tuple!(int, float)[string])) yields +$(D Tuple!(uint, float)[string]). The types in which replacement is performed +may be arbitrarily complex, including qualifiers, built-in type constructors +(pointers, arrays, associative arrays, functions, and delegates), and template +instantiations; replacement proceeds transitively through the type definition. +However, member types in `struct`s or `class`es are not replaced because there +are no ways to express the types resulting after replacement. + +This is an advanced type manipulation necessary e.g. for replacing the +placeholder type `This` in $(REF Algebraic, std,variant). + +Returns: `ReplaceType` aliases itself to the type(s) that result after +replacement. +*/ +template ReplaceType(From, To, T...) +{ + static if (T.length == 1) + { + static if (is(T[0] == From)) + alias ReplaceType = To; + else static if (is(T[0] == const(U), U)) + alias ReplaceType = const(ReplaceType!(From, To, U)); + else static if (is(T[0] == immutable(U), U)) + alias ReplaceType = immutable(ReplaceType!(From, To, U)); + else static if (is(T[0] == shared(U), U)) + alias ReplaceType = shared(ReplaceType!(From, To, U)); + else static if (is(T[0] == U*, U)) + { + static if (is(U == function)) + alias ReplaceType = replaceTypeInFunctionType!(From, To, T[0]); + else + alias ReplaceType = ReplaceType!(From, To, U)*; + } + else static if (is(T[0] == delegate)) + { + alias ReplaceType = replaceTypeInFunctionType!(From, To, T[0]); + } + else static if (is(T[0] == function)) + { + static assert(0, "Function types not supported," ~ + " use a function pointer type instead of " ~ T[0].stringof); + } + else static if (is(T[0] : U!V, alias U, V...)) + { + template replaceTemplateArgs(T...) + { + static if (is(typeof(T[0]))) // template argument is value or symbol + enum replaceTemplateArgs = T[0]; + else + alias replaceTemplateArgs = ReplaceType!(From, To, T[0]); + } + alias ReplaceType = U!(staticMap!(replaceTemplateArgs, V)); + } + else static if (is(T[0] == struct)) + // don't match with alias this struct below (Issue 15168) + alias ReplaceType = T[0]; + else static if (is(T[0] == U[], U)) + alias ReplaceType = ReplaceType!(From, To, U)[]; + else static if (is(T[0] == U[n], U, size_t n)) + alias ReplaceType = ReplaceType!(From, To, U)[n]; + else static if (is(T[0] == U[V], U, V)) + alias ReplaceType = + ReplaceType!(From, To, U)[ReplaceType!(From, To, V)]; + else + alias ReplaceType = T[0]; + } + else static if (T.length > 1) + { + alias ReplaceType = AliasSeq!(ReplaceType!(From, To, T[0]), + ReplaceType!(From, To, T[1 .. $])); + } + else + { + alias ReplaceType = AliasSeq!(); + } +} + +/// +@safe unittest +{ + static assert( + is(ReplaceType!(int, string, int[]) == string[]) && + is(ReplaceType!(int, string, int[int]) == string[string]) && + is(ReplaceType!(int, string, const(int)[]) == const(string)[]) && + is(ReplaceType!(int, string, Tuple!(int[], float)) + == Tuple!(string[], float)) + ); +} + +private template replaceTypeInFunctionType(From, To, fun) +{ + alias RX = ReplaceType!(From, To, ReturnType!fun); + alias PX = AliasSeq!(ReplaceType!(From, To, Parameters!fun)); + // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return + // tuple if Parameters!fun.length == 1 + + string gen() + { + enum linkage = functionLinkage!fun; + alias attributes = functionAttributes!fun; + enum variadicStyle = variadicFunctionStyle!fun; + alias storageClasses = ParameterStorageClassTuple!fun; + + string result; + + result ~= "extern(" ~ linkage ~ ") "; + static if (attributes & FunctionAttribute.ref_) + { + result ~= "ref "; + } + + result ~= "RX"; + static if (is(fun == delegate)) + result ~= " delegate"; + else + result ~= " function"; + + result ~= "("; + foreach (i, _; PX) + { + if (i) + result ~= ", "; + if (storageClasses[i] & ParameterStorageClass.scope_) + result ~= "scope "; + if (storageClasses[i] & ParameterStorageClass.out_) + result ~= "out "; + if (storageClasses[i] & ParameterStorageClass.ref_) + result ~= "ref "; + if (storageClasses[i] & ParameterStorageClass.lazy_) + result ~= "lazy "; + if (storageClasses[i] & ParameterStorageClass.return_) + result ~= "return "; + + result ~= "PX[" ~ i.stringof ~ "]"; + } + static if (variadicStyle == Variadic.typesafe) + result ~= " ..."; + else static if (variadicStyle != Variadic.no) + result ~= ", ..."; + result ~= ")"; + + static if (attributes & FunctionAttribute.pure_) + result ~= " pure"; + static if (attributes & FunctionAttribute.nothrow_) + result ~= " nothrow"; + static if (attributes & FunctionAttribute.property) + result ~= " @property"; + static if (attributes & FunctionAttribute.trusted) + result ~= " @trusted"; + static if (attributes & FunctionAttribute.safe) + result ~= " @safe"; + static if (attributes & FunctionAttribute.nogc) + result ~= " @nogc"; + static if (attributes & FunctionAttribute.system) + result ~= " @system"; + static if (attributes & FunctionAttribute.const_) + result ~= " const"; + static if (attributes & FunctionAttribute.immutable_) + result ~= " immutable"; + static if (attributes & FunctionAttribute.inout_) + result ~= " inout"; + static if (attributes & FunctionAttribute.shared_) + result ~= " shared"; + static if (attributes & FunctionAttribute.return_) + result ~= " return"; + + return result; + } + //pragma(msg, "gen ==> ", gen()); + + mixin("alias replaceTypeInFunctionType = " ~ gen() ~ ";"); +} + +@safe unittest +{ + template Test(Ts...) + { + static if (Ts.length) + { + //pragma(msg, "Testing: ReplaceType!("~Ts[0].stringof~", " + // ~Ts[1].stringof~", "~Ts[2].stringof~")"); + static assert(is(ReplaceType!(Ts[0], Ts[1], Ts[2]) == Ts[3]), + "ReplaceType!("~Ts[0].stringof~", "~Ts[1].stringof~", " + ~Ts[2].stringof~") == " + ~ReplaceType!(Ts[0], Ts[1], Ts[2]).stringof); + alias Test = Test!(Ts[4 .. $]); + } + else alias Test = void; + } + + //import core.stdc.stdio; + alias RefFun1 = ref int function(float, long); + alias RefFun2 = ref float function(float, long); + extern(C) int printf(const char*, ...) nothrow @nogc @system; + extern(C) float floatPrintf(const char*, ...) nothrow @nogc @system; + int func(float); + + int x; + struct S1 { void foo() { x = 1; } } + struct S2 { void bar() { x = 2; } } + + alias Pass = Test!( + int, float, typeof(&func), float delegate(float), + int, float, typeof(&printf), typeof(&floatPrintf), + int, float, int function(out long, ...), + float function(out long, ...), + int, float, int function(ref float, long), + float function(ref float, long), + int, float, int function(ref int, long), + float function(ref float, long), + int, float, int function(out int, long), + float function(out float, long), + int, float, int function(lazy int, long), + float function(lazy float, long), + int, float, int function(out long, ref const int), + float function(out long, ref const float), + int, int, int, int, + int, float, int, float, + int, float, const int, const float, + int, float, immutable int, immutable float, + int, float, shared int, shared float, + int, float, int*, float*, + int, float, const(int)*, const(float)*, + int, float, const(int*), const(float*), + const(int)*, float, const(int*), const(float), + int*, float, const(int)*, const(int)*, + int, float, int[], float[], + int, float, int[42], float[42], + int, float, const(int)[42], const(float)[42], + int, float, const(int[42]), const(float[42]), + int, float, int[int], float[float], + int, float, int[double], float[double], + int, float, double[int], double[float], + int, float, int function(float, long), float function(float, long), + int, float, int function(float), float function(float), + int, float, int function(float, int), float function(float, float), + int, float, int delegate(float, long), float delegate(float, long), + int, float, int delegate(float), float delegate(float), + int, float, int delegate(float, int), float delegate(float, float), + int, float, Unique!int, Unique!float, + int, float, Tuple!(float, int), Tuple!(float, float), + int, float, RefFun1, RefFun2, + S1, S2, + S1[1][][S1]* function(), + S2[1][][S2]* function(), + int, string, + int[3] function( int[] arr, int[2] ...) pure @trusted, + string[3] function(string[] arr, string[2] ...) pure @trusted, + ); + + // Bugzilla 15168 + static struct T1 { string s; alias s this; } + static struct T2 { char[10] s; alias s this; } + static struct T3 { string[string] s; alias s this; } + alias Pass2 = Test!( + ubyte, ubyte, T1, T1, + ubyte, ubyte, T2, T2, + ubyte, ubyte, T3, T3, + ); +} + +@safe unittest // Bugzilla 17116 +{ + alias ConstDg = void delegate(float) const; + alias B = void delegate(int) const; + alias A = ReplaceType!(float, int, ConstDg); + static assert(is(B == A)); +} + +/** +Ternary type with three truth values: + +$(UL + $(LI `Ternary.yes` for `true`) + $(LI `Ternary.no` for `false`) + $(LI `Ternary.unknown` as an unknown state) +) + +Also known as trinary, trivalent, or trilean. + +See_Also: + $(HTTP en.wikipedia.org/wiki/Three-valued_logic, + Three Valued Logic on Wikipedia) +*/ +struct Ternary +{ + @safe @nogc nothrow pure: + + private ubyte value = 6; + private static Ternary make(ubyte b) + { + Ternary r = void; + r.value = b; + return r; + } + + /** + The possible states of the `Ternary` + */ + enum no = make(0); + /// ditto + enum yes = make(2); + /// ditto + enum unknown = make(6); + + /** + Construct and assign from a `bool`, receiving `no` for `false` and `yes` + for `true`. + */ + this(bool b) { value = b << 1; } + + /// ditto + void opAssign(bool b) { value = b << 1; } + + /** + Construct a ternary value from another ternary value + */ + this(const Ternary b) { value = b.value; } + + /** + $(TABLE Truth table for logical operations, + $(TR $(TH `a`) $(TH `b`) $(TH `$(TILDE)a`) $(TH `a | b`) $(TH `a & b`) $(TH `a ^ b`)) + $(TR $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `no`)) + $(TR $(TD `no`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `no`) $(TD `yes`)) + $(TR $(TD `no`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `no`) $(TD `unknown`)) + $(TR $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `yes`)) + $(TR $(TD `yes`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `yes`) $(TD `no`)) + $(TR $(TD `yes`) $(TD `unknown`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`)) + $(TR $(TD `unknown`) $(TD `no`) $(TD `unknown`) $(TD `unknown`) $(TD `no`) $(TD `unknown`)) + $(TR $(TD `unknown`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`)) + $(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`)) + ) + */ + Ternary opUnary(string s)() if (s == "~") + { + return make((386 >> value) & 6); + } + + /// ditto + Ternary opBinary(string s)(Ternary rhs) if (s == "|") + { + return make((25_512 >> (value + rhs.value)) & 6); + } + + /// ditto + Ternary opBinary(string s)(Ternary rhs) if (s == "&") + { + return make((26_144 >> (value + rhs.value)) & 6); + } + + /// ditto + Ternary opBinary(string s)(Ternary rhs) if (s == "^") + { + return make((26_504 >> (value + rhs.value)) & 6); + } +} + +/// +@safe @nogc nothrow pure +unittest +{ + Ternary a; + assert(a == Ternary.unknown); + + assert(~Ternary.yes == Ternary.no); + assert(~Ternary.no == Ternary.yes); + assert(~Ternary.unknown == Ternary.unknown); +} + +@safe @nogc nothrow pure +unittest +{ + alias f = Ternary.no, t = Ternary.yes, u = Ternary.unknown; + Ternary[27] truthTableAnd = + [ + t, t, t, + t, u, u, + t, f, f, + u, t, u, + u, u, u, + u, f, f, + f, t, f, + f, u, f, + f, f, f, + ]; + + Ternary[27] truthTableOr = + [ + t, t, t, + t, u, t, + t, f, t, + u, t, t, + u, u, u, + u, f, u, + f, t, t, + f, u, u, + f, f, f, + ]; + + Ternary[27] truthTableXor = + [ + t, t, f, + t, u, u, + t, f, t, + u, t, u, + u, u, u, + u, f, u, + f, t, t, + f, u, u, + f, f, f, + ]; + + for (auto i = 0; i != truthTableAnd.length; i += 3) + { + assert((truthTableAnd[i] & truthTableAnd[i + 1]) + == truthTableAnd[i + 2]); + assert((truthTableOr[i] | truthTableOr[i + 1]) + == truthTableOr[i + 2]); + assert((truthTableXor[i] ^ truthTableXor[i + 1]) + == truthTableXor[i + 2]); + } + + Ternary a; + assert(a == Ternary.unknown); + static assert(!is(typeof({ if (a) {} }))); + assert(!is(typeof({ auto b = Ternary(3); }))); + a = true; + assert(a == Ternary.yes); + a = false; + assert(a == Ternary.no); + a = Ternary.unknown; + assert(a == Ternary.unknown); + Ternary b; + b = a; + assert(b == a); + assert(~Ternary.yes == Ternary.no); + assert(~Ternary.no == Ternary.yes); + assert(~Ternary.unknown == Ternary.unknown); +} diff --git a/std/typelist.d b/std/typelist.d deleted file mode 100644 index 9609b821bf3..00000000000 --- a/std/typelist.d +++ /dev/null @@ -1,457 +0,0 @@ -// Written in the D programming language. - -/** - * This module defines a list of types $(D_PARAM TypeList) - * and operations on $(D_PARAM TypeList)s. - * Together they define a compile-time functional programming framework, - * complete with lambdas, higher-order functions, and arbitrary data structures - * - * Macros: - * WIKI = Phobos/StdTypelist - * - * Synopsis: - * - * ---- - * // **** BUG **** problems with mutual recursion - * template Synopsis(T...) - * { - * alias TypeList!(T) list; - * - * template IsPtr(U) { - * static if (is(U foo: V*, V)) - * enum IsPtr = true; - * else - * enum IsPtr = false; - * } - * enum arePointers = All!(list, IsPtr); - * - * alias Map!(StripPtr, list) StripPointers; - * } - * static assert(is (Synopsis!(char**, void***).StripPointers.toTuple == TypeTuple!(char, void))); - * ---- - * - * Copyright: Copyright Bartosz Milewski 2008- 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB bartoszmilewski.wordpress.com, Bartosz Milewski) - * Source: $(PHOBOSSRC std/_typelist.d) - */ -/* Copyright Burton Radons 2008 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -deprecated("Please use std.typecons instead. This module will be removed in March 2015.") -module std.typelist; -version(unittest) { - import std.typetuple; -} - -/** - * Creates a compile-time list of types from a tuple. - * $(D TypeList)s are more general than tuples because - * you can pass more than one $(D TypeList) to a template. - * You may also combine them into higher-order structures. - * $(D TypeList)s are passed to other templates as alias parameters - * To create an empty list use $(D TypeList!()) - * - * $(D TypeList) efines several "methods": - * - * $(D_PARAM toTuple), $(D_PARAM head), $(D_PARAM tail), $(D_PARAM length), $(D_PARAM isEmpty) - * - * Example: - * --- - * template Filter(alias Pred, alias List) - * { - * static if (List.isEmpty) - * alias TypeList!() Filter; - * else static if (Pred!(List.head)) - * alias Cons!(List.head, Filter!(Pred, List.tail)) Filter; - * else - * alias Filter!(Pred, List.tail) Filter; - * } - * --- - */ - -template TypeList(T...) -{ - alias T toTuple; - - static if(T.length != 0) - { - alias T[0] head; - alias TypeList!(T[1..$]) tail; - enum length = T.length; - enum isEmpty = false; - } - else - { - enum length = 0; - enum isEmpty = true; - } -} - -unittest { - static assert (is (TypeList!(void*, int).toTuple == TypeTuple!(void*, int))); - static assert (is (TypeList!(void*, int).head == void*)); - static assert (is (TypeList!(void*, int).tail.toTuple == TypeTuple!(int))); - static assert (is (TypeList!(int).tail.toTuple == TypeTuple!())); - static assert (TypeList!(int).tail.isEmpty); - - static assert (TypeList!(void*, int).length == 2); - static assert (!TypeList!(void*, int).isEmpty); - static assert (TypeList!().length == 0); - static assert (TypeList!().isEmpty); -} - -/** - * Appends a type tuple to a $(D TypeList), returns a $(D TypeList) -*/ -template AppendTypes(alias List, T...) -{ - static if (List.isEmpty) - alias TypeList!(T) AppendTypes; - else - alias TypeList!(List.toTuple, T) AppendTypes; -} - -unittest { - static assert (is (AppendTypes!(TypeList!(void*, int), long, short).toTuple - == TypeTuple!(void*, int, long, short))); - static assert (is (AppendTypes!(TypeList!(void*, int)).toTuple - == TypeTuple!(void*, int))); - static assert (AppendTypes!(TypeList!()).isEmpty); -} - -/** - * Appends one $(D TypeList) to another, returns a $(D TypeList) -*/ -template Append(alias Left, alias Right) -{ - alias AppendTypes!(Left, Right.toTuple) Append; -} - -unittest { - static assert (is (Append!(TypeList!(void*, int), TypeList!(long, short)).toTuple - == TypeTuple!(void*, int, long, short))); - static assert (is (Append!(TypeList!(void*, int), TypeList!()).toTuple - == TypeTuple!(void*, int))); - static assert (Append!(TypeList!(), TypeList!()).isEmpty); -} - -/** - * Prepends a type to a $(D TypeList), returns a $(D TypeList) -*/ -template Cons(T, alias List) -{ - static if (List.isEmpty) - alias TypeList!(T) Cons; - else - alias TypeList!(T, List.toTuple) Cons; -} - -unittest { - static assert (is (Cons!(long, TypeList!(void*, int)).toTuple - == TypeTuple!(long, void*, int))); - static assert (is (Cons!(long, TypeList!(void*, int)).head - == long)); - static assert (is (Cons!(int, TypeList!()).toTuple == TypeTuple!(int))); - static assert (is (Cons!(char[], Cons!(int, TypeList!())).toTuple - == TypeTuple!(char[], int))); -} - -/** - * Tests if all emements of a $(D TypeList) against a predicate. - * Returns true if all all types satisfy the predicate, false otherwise. -*/ -template All(alias List, alias F) -{ - static if (List.isEmpty) - enum All = true; - else - enum All = F!(List.head) && All!(List.tail, F); -} - -version(unittest) { - template IsPointer(T) - { - static if (is (T foo: U*, U)) - enum IsPointer = true; - else - enum IsPointer = false; - } -} - -unittest { - static assert (All!(TypeList!(void*, char*, int**), IsPointer)); - static assert (!All!(TypeList!(void*, char*, int), IsPointer)); -} - -/** - * Tests if there is an emement in a $(D TypeList) that satisfies a predicate. -*/ -template Any(alias List, alias F) -{ - static if (List.isEmpty) - enum Any = false; - else - enum Any = F!(List.head) || Any!(List.tail, F); -} - -unittest { - static assert (Any!(TypeList!(int, char*, int**), IsPointer)); - static assert (!Any!(TypeList!(char[], char, int), IsPointer)); -} - -/** - * Applies a given "function" on types to a type tuple. Returns a tuple of results -*/ -template Map(alias F, T...) -{ - alias Map!(F, TypeList!(T)).toTuple Map; -} - -/** - * Applies a given "function" to a $(D TypeList). Returns a $(D TypeList) of results -*/ -private template Map(alias F, alias List) -{ - static if (List.isEmpty) - alias TypeList!() Map; - else - alias Cons!(F!(List.head), Map!(F, List.tail)) Map; -} - -version(unittest) { - template MakePtr(T) - { - alias T* MakePtr; - } -} - -unittest { - static assert (is (MakePtr!(int) == int*)); - static assert (is (Map!(MakePtr, void *, char) == TypeTuple!(void**, char*))); -} - -/** - * Filters a type tuple using a predicate. - * Takes a predicate and a tuple and returns another tuple -*/ -template Filter(alias Pred, T...) -{ - alias Filter!(Pred, TypeList!(T)).toTuple Filter; -} - -/** - * Filters a $(D TypeList) using a predicate. Returns a $(D TypeList) of elements that - * satisfy the predicate. -*/ -template Filter(alias Pred, alias List) -{ - static if (List.isEmpty) - alias TypeList!() Filter; - else static if (Pred!(List.head)) - alias Cons!(List.head, Filter!(Pred, List.tail)) Filter; - else - alias Filter!(Pred, List.tail) Filter; -} - -unittest { - static assert(is(Filter!(IsPointer, int, void*, char[], int*) == TypeTuple!(void*, int*))); - static assert(is(Filter!(IsPointer) == TypeTuple!())); -} - -template FoldRight(alias F, alias Init, alias List) -{ - static if (List.isEmpty) - alias Init FoldRight; - else - alias F!(List.head, FoldRight!(F, Init, List.tail)) FoldRight; -} - -template FoldRight(alias F, int Init, alias List) -{ - static if (List.isEmpty) - alias Init FoldRight; - else - alias F!(List.head, FoldRight!(F, Init, List.tail)) FoldRight; -} - -version(unittest) { - template snoC(T, alias List) - { - alias TypeList!(List.toTuple, T) snoC; - } - - template Inc(T, int i) - { - enum Inc = i + 1; - } -} - -unittest { - // *** Compiler bugs - //static assert (snoC!(int, TypeList!(long)).toTuple == TypeTuple!(long, int)); - //static assert (FoldRight!(snoC, TypeList!(), TypeList!(int, long)).toTuple == TypeTuple!(long, int)); - static assert (!FoldRight!(snoC, TypeList!(), TypeList!(int)).isEmpty); - static assert (FoldRight!(Inc, 0, TypeList!(int, long)) == 2); -} - -/** A list of functions operating on types. - * Used to pass multiple type functions to - * a template. - * - * Example: - * ---- - * template Or(alias FList) - * { - * template lambda(X) - * { - * static if (FList.isEmpty) - * enum lambda = true; - * else - * enum lambda = FList.head!(X) || Or!(FList.tail).apply!(X); - * } - * alias lambda apply; - * } - * ---- -*/ -template TypeFunList() -{ - enum length = 0; - enum isEmpty = true; -} - -template TypeFunList(alias F) -{ - alias F head; - alias TypeFunList!() tail; - enum length = 1; - enum isEmpty = false; -} - -template TypeFunList(alias F, alias Tail) -{ - alias F head; - alias Tail tail; - enum length = 1 + Tail.length; - enum isEmpty = false; -} - -unittest { - static assert (TypeFunList!().isEmpty); - static assert (!TypeFunList!(IsPointer).isEmpty); - static assert (TypeFunList!(IsPointer).tail.isEmpty); - static assert (TypeFunList!(IsPointer).head!(void*)); - static assert (TypeFunList!(IsPointer, TypeFunList!(IsPointer)).head!(void *)); - static assert (TypeFunList!(IsPointer, TypeFunList!(IsPointer)).tail.head!(void *)); -} - -/** Negates a type predicate. - * The negated predicate is a "member" $(D apply). - * - * Example: - * ---- - * static assert (Not!(IsPointer).apply!(int)); - * ---- -*/ -template Not(alias F) -{ - template lambda(X) - { - enum lambda = !F!(X); - } - alias lambda apply; -} - -unittest { - static assert (Not!(IsPointer).apply!(int)); -} - -/** Combines two type predicates using logical OR. - * The resulting predicate is callable through the field $(D apply) - * - * Example: - * ---- - * static assert(Or!(IsPointer, Not!(IsPointer).apply).apply!(int)); - * ---- -*/ -template Or(alias F1, alias F2) -{ - template lambda(X) - { - enum lambda = F1!(X) || F2!(X); - } - alias lambda apply; -} - -unittest { - static assert(Or!(IsPointer, IsPointer).apply!(int*)); - static assert(Or!(IsPointer, Not!(IsPointer).apply).apply!(int)); -} - -/** Combines a list of type predicates using logical OR. - * The resulting predicate is callable through the field $(D apply) -*/ -template Or(alias FList) -{ - template lambda(X) - { - static if (FList.isEmpty) - enum lambda = true; - else - enum lambda = FList.head!(X) || Or!(FList.tail).apply!(X); - } - alias lambda apply; -} - -unittest { - static assert (Or!( - TypeFunList!(IsPointer, - TypeFunList!(Not!(IsPointer).apply) - )).apply!(int*)); -} - -/** Combines two type predicates using logical AND. - * The resulting predicate is callable through the field $(D apply) - * - * Example: - * ---- - * static assert(!And!(IsPointer, Not!(IsPointer).apply).apply!(int)); - * ---- -*/ -template And(alias F1, alias F2) -{ - template lambda(X) - { - enum lambda = F1!(X) && F2!(X); - } - alias lambda apply; -} - -unittest { - static assert(And!(IsPointer, IsPointer).apply!(int*)); - static assert(!And!(IsPointer, Not!(IsPointer).apply).apply!(int)); -} - -/** Combines a list of type predicates using logical AND. - * The resulting predicate is callable through the field $(D apply) -*/ -template And(alias FList) -{ - template lambda(X) - { - static if (FList.isEmpty) - enum lambda = true; - else - enum lambda = FList.head!(X) && And!(FList.tail).apply!(X); - } - alias lambda apply; -} - -unittest { - static assert (!And!( - TypeFunList!(IsPointer, - TypeFunList!(Not!(IsPointer).apply) - )).apply!(int*)); -} diff --git a/std/typetuple.d b/std/typetuple.d index bba40bb81c2..dedbdc21580 100644 --- a/std/typetuple.d +++ b/std/typetuple.d @@ -1,1030 +1,40 @@ -// Written in the D programming language. - /** - * Templates with which to manipulate type tuples (also known as type lists). - * - * Some operations on type tuples are built in to the language, - * such as TL[$(I n)] which gets the $(I n)th type from the - * type tuple. TL[$(I lwr) .. $(I upr)] returns a new type - * list that is a slice of the old one. - * - * Several templates in this module use or operate on eponymous templates that - * take a single argument and evaluate to a boolean constant. Such templates - * are referred to as $(I template predicates). - * - * References: - * Based on ideas in Table 3.1 from - * $(LINK2 http://amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768, - * Modern C++ Design), - * Andrei Alexandrescu (Addison-Wesley Professional, 2001) - * Macros: - * WIKI = Phobos/StdTypeTuple + * This module was renamed to disambiguate the term tuple, use + * $(MREF std, meta) instead. * - * Copyright: Copyright Digital Mars 2005 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Copyright: Copyright Digital Mars 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: - * $(WEB digitalmars.com, Walter Bright), - * $(WEB klickverbot.at, David Nadlinger) * Source: $(PHOBOSSRC std/_typetuple.d) - */ -/* Copyright Digital Mars 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) + * + * $(SCRIPT inhibitQuickIndex = 1;) */ module std.typetuple; +public import std.meta; + /** - * Creates a typetuple out of a sequence of zero or more types. + * Alternate name for $(REF AliasSeq, std,meta) for legacy compatibility. */ -template TypeTuple(TList...) -{ - alias TypeTuple = TList; -} +alias TypeTuple = AliasSeq; /// -unittest +@safe unittest { import std.typetuple; alias TL = TypeTuple!(int, double); int foo(TL td) // same as int foo(int, double); { - return td[0] + cast(int)td[1]; + return td[0] + cast(int) td[1]; } } /// -unittest +@safe unittest { alias TL = TypeTuple!(int, double); alias Types = TypeTuple!(TL, char); static assert(is(Types == TypeTuple!(int, double, char))); } - -/** - * Returns the index of the first occurrence of type T in the - * sequence of zero or more types TList. - * If not found, -1 is returned. - */ -template staticIndexOf(T, TList...) -{ - enum staticIndexOf = genericIndexOf!(T, TList).index; -} - -/// Ditto -template staticIndexOf(alias T, TList...) -{ - enum staticIndexOf = genericIndexOf!(T, TList).index; -} - -/// -unittest -{ - import std.typetuple; - import std.stdio; - - void foo() - { - writefln("The index of long is %s", - staticIndexOf!(long, TypeTuple!(int, long, double))); - // prints: The index of long is 1 - } -} - -// [internal] -private template genericIndexOf(args...) - if (args.length >= 1) -{ - alias e = Alias!(args[0]); - alias tuple = args[1 .. $]; - - static if (tuple.length) - { - alias head = Alias!(tuple[0]); - alias tail = tuple[1 .. $]; - - static if (isSame!(e, head)) - { - enum index = 0; - } - else - { - enum next = genericIndexOf!(e, tail).index; - enum index = (next == -1) ? -1 : 1 + next; - } - } - else - { - enum index = -1; - } -} - -unittest -{ - static assert(staticIndexOf!( byte, byte, short, int, long) == 0); - static assert(staticIndexOf!(short, byte, short, int, long) == 1); - static assert(staticIndexOf!( int, byte, short, int, long) == 2); - static assert(staticIndexOf!( long, byte, short, int, long) == 3); - static assert(staticIndexOf!( char, byte, short, int, long) == -1); - static assert(staticIndexOf!( -1, byte, short, int, long) == -1); - static assert(staticIndexOf!(void) == -1); - - static assert(staticIndexOf!("abc", "abc", "def", "ghi", "jkl") == 0); - static assert(staticIndexOf!("def", "abc", "def", "ghi", "jkl") == 1); - static assert(staticIndexOf!("ghi", "abc", "def", "ghi", "jkl") == 2); - static assert(staticIndexOf!("jkl", "abc", "def", "ghi", "jkl") == 3); - static assert(staticIndexOf!("mno", "abc", "def", "ghi", "jkl") == -1); - static assert(staticIndexOf!( void, "abc", "def", "ghi", "jkl") == -1); - static assert(staticIndexOf!(42) == -1); - - static assert(staticIndexOf!(void, 0, "void", void) == 2); - static assert(staticIndexOf!("void", 0, void, "void") == 2); -} - -/// Kept for backwards compatibility -alias IndexOf = staticIndexOf; - -/** - * Returns a typetuple created from TList with the first occurrence, - * if any, of T removed. - */ -template Erase(T, TList...) -{ - alias Erase = GenericErase!(T, TList).result; -} - -/// Ditto -template Erase(alias T, TList...) -{ - alias Erase = GenericErase!(T, TList).result; -} - -/// -unittest -{ - alias Types = TypeTuple!(int, long, double, char); - alias TL = Erase!(long, Types); - static assert(is(TL == TypeTuple!(int, double, char))); -} - -// [internal] -private template GenericErase(args...) - if (args.length >= 1) -{ - alias e = Alias!(args[0]); - alias tuple = args[1 .. $] ; - - static if (tuple.length) - { - alias head = Alias!(tuple[0]); - alias tail = tuple[1 .. $]; - - static if (isSame!(e, head)) - alias result = tail; - else - alias result = TypeTuple!(head, GenericErase!(e, tail).result); - } - else - { - alias result = TypeTuple!(); - } -} - -unittest -{ - static assert(Pack!(Erase!(int, - short, int, int, 4)). - equals!(short, int, 4)); - - static assert(Pack!(Erase!(1, - real, 3, 1, 4, 1, 5, 9)). - equals!(real, 3, 4, 1, 5, 9)); -} - - -/** - * Returns a typetuple created from TList with the all occurrences, - * if any, of T removed. - */ -template EraseAll(T, TList...) -{ - alias EraseAll = GenericEraseAll!(T, TList).result; -} - -/// Ditto -template EraseAll(alias T, TList...) -{ - alias EraseAll = GenericEraseAll!(T, TList).result; -} - -/// -unittest -{ - alias Types = TypeTuple!(int, long, long, int); - - alias TL = EraseAll!(long, Types); - static assert(is(TL == TypeTuple!(int, int))); -} - -// [internal] -private template GenericEraseAll(args...) - if (args.length >= 1) -{ - alias e = Alias!(args[0]); - alias tuple = args[1 .. $]; - - static if (tuple.length) - { - alias head = Alias!(tuple[0]); - alias tail = tuple[1 .. $]; - alias next = GenericEraseAll!(e, tail).result; - - static if (isSame!(e, head)) - alias result = next; - else - alias result = TypeTuple!(head, next); - } - else - { - alias result = TypeTuple!(); - } -} - -unittest -{ - static assert(Pack!(EraseAll!(int, - short, int, int, 4)). - equals!(short, 4)); - - static assert(Pack!(EraseAll!(1, - real, 3, 1, 4, 1, 5, 9)). - equals!(real, 3, 4, 5, 9)); -} - - -/** - * Returns a typetuple created from TList with the all duplicate - * types removed. - */ -template NoDuplicates(TList...) -{ - static if (TList.length == 0) - alias NoDuplicates = TList; - else - alias NoDuplicates = - TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $]))); -} - -/// -unittest -{ - alias Types = TypeTuple!(int, long, long, int, float); - - alias TL = NoDuplicates!(Types); - static assert(is(TL == TypeTuple!(int, long, float))); -} - -unittest -{ - static assert( - Pack!( - NoDuplicates!(1, int, 1, NoDuplicates, int, NoDuplicates, real)) - .equals!(1, int, NoDuplicates, real)); -} - - -/** - * Returns a typetuple created from TList with the first occurrence - * of type T, if found, replaced with type U. - */ -template Replace(T, U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// Ditto -template Replace(alias T, U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// Ditto -template Replace(T, alias U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// Ditto -template Replace(alias T, alias U, TList...) -{ - alias Replace = GenericReplace!(T, U, TList).result; -} - -/// -unittest -{ - alias Types = TypeTuple!(int, long, long, int, float); - - alias TL = Replace!(long, char, Types); - static assert(is(TL == TypeTuple!(int, char, long, int, float))); -} - -// [internal] -private template GenericReplace(args...) - if (args.length >= 2) -{ - alias from = Alias!(args[0]); - alias to = Alias!(args[1]); - alias tuple = args[2 .. $]; - - static if (tuple.length) - { - alias head = Alias!(tuple[0]); - alias tail = tuple[1 .. $]; - - static if (isSame!(from, head)) - alias result = TypeTuple!(to, tail); - else - alias result = TypeTuple!(head, - GenericReplace!(from, to, tail).result); - } - else - { - alias result = TypeTuple!(); - } - } - -unittest -{ - static assert(Pack!(Replace!(byte, ubyte, - short, byte, byte, byte)). - equals!(short, ubyte, byte, byte)); - - static assert(Pack!(Replace!(1111, byte, - 2222, 1111, 1111, 1111)). - equals!(2222, byte, 1111, 1111)); - - static assert(Pack!(Replace!(byte, 1111, - short, byte, byte, byte)). - equals!(short, 1111, byte, byte)); - - static assert(Pack!(Replace!(1111, "11", - 2222, 1111, 1111, 1111)). - equals!(2222, "11", 1111, 1111)); -} - -/** - * Returns a typetuple created from TList with all occurrences - * of type T, if found, replaced with type U. - */ -template ReplaceAll(T, U, TList...) -{ - alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; -} - -/// Ditto -template ReplaceAll(alias T, U, TList...) -{ - alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; -} - -/// Ditto -template ReplaceAll(T, alias U, TList...) -{ - alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; -} - -/// Ditto -template ReplaceAll(alias T, alias U, TList...) -{ - alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; -} - -/// -unittest -{ - alias Types = TypeTuple!(int, long, long, int, float); - - alias TL = ReplaceAll!(long, char, Types); - static assert(is(TL == TypeTuple!(int, char, char, int, float))); -} - -// [internal] -private template GenericReplaceAll(args...) - if (args.length >= 2) -{ - alias from = Alias!(args[0]); - alias to = Alias!(args[1]); - alias tuple = args[2 .. $]; - - static if (tuple.length) - { - alias head = Alias!(tuple[0]); - alias tail = tuple[1 .. $]; - alias next = GenericReplaceAll!(from, to, tail).result; - - static if (isSame!(from, head)) - alias result = TypeTuple!(to, next); - else - alias result = TypeTuple!(head, next); - } - else - { - alias result = TypeTuple!(); - } -} - -unittest -{ - static assert(Pack!(ReplaceAll!(byte, ubyte, - byte, short, byte, byte)). - equals!(ubyte, short, ubyte, ubyte)); - - static assert(Pack!(ReplaceAll!(1111, byte, - 1111, 2222, 1111, 1111)). - equals!(byte, 2222, byte, byte)); - - static assert(Pack!(ReplaceAll!(byte, 1111, - byte, short, byte, byte)). - equals!(1111, short, 1111, 1111)); - - static assert(Pack!(ReplaceAll!(1111, "11", - 1111, 2222, 1111, 1111)). - equals!("11", 2222, "11", "11")); -} - -/** - * Returns a typetuple created from TList with the order reversed. - */ -template Reverse(TList...) -{ - static if (TList.length <= 1) - { - alias Reverse = TList; - } - else - { - alias Reverse = - TypeTuple!( - Reverse!(TList[$/2 .. $ ]), - Reverse!(TList[ 0 .. $/2])); - } -} - -/// -unittest -{ - alias Types = TypeTuple!(int, long, long, int, float); - - alias TL = Reverse!(Types); - static assert(is(TL == TypeTuple!(float, int, long, long, int))); -} - -/** - * Returns the type from TList that is the most derived from type T. - * If none are found, T is returned. - */ -template MostDerived(T, TList...) -{ - static if (TList.length == 0) - alias MostDerived = T; - else static if (is(TList[0] : T)) - alias MostDerived = MostDerived!(TList[0], TList[1 .. $]); - else - alias MostDerived = MostDerived!(T, TList[1 .. $]); -} - -/// -unittest -{ - class A { } - class B : A { } - class C : B { } - alias Types = TypeTuple!(A, C, B); - - MostDerived!(Object, Types) x; // x is declared as type C - static assert(is(typeof(x) == C)); -} - -/** - * Returns the typetuple TList with the types sorted so that the most - * derived types come first. - */ -template DerivedToFront(TList...) -{ - static if (TList.length == 0) - alias DerivedToFront = TList; - else - alias DerivedToFront = - TypeTuple!(MostDerived!(TList[0], TList[1 .. $]), - DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]), - TList[0], - TList[1 .. $]))); -} - -/// -unittest -{ - class A { } - class B : A { } - class C : B { } - alias Types = TypeTuple!(A, C, B); - - alias TL = DerivedToFront!(Types); - static assert(is(TL == TypeTuple!(C, B, A))); -} - -/** -Evaluates to $(D TypeTuple!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))). - */ -template staticMap(alias F, T...) -{ - static if (T.length == 0) - { - alias staticMap = TypeTuple!(); - } - else static if (T.length == 1) - { - alias staticMap = TypeTuple!(F!(T[0])); - } - else - { - alias staticMap = - TypeTuple!( - staticMap!(F, T[ 0 .. $/2]), - staticMap!(F, T[$/2 .. $ ])); - } -} - -/// -unittest -{ - import std.traits : Unqual; - alias TL = staticMap!(Unqual, int, const int, immutable int); - static assert(is(TL == TypeTuple!(int, int, int))); -} - -unittest -{ - import std.traits : Unqual; - - // empty - alias Empty = staticMap!(Unqual); - static assert(Empty.length == 0); - - // single - alias Single = staticMap!(Unqual, const int); - static assert(is(Single == TypeTuple!int)); - - alias T = staticMap!(Unqual, int, const int, immutable int); - static assert(is(T == TypeTuple!(int, int, int))); -} - -/** -Tests whether all given items satisfy a template predicate, i.e. evaluates to -$(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])). - -Evaluation is $(I not) short-circuited if a false result is encountered; the -template predicate must be instantiable with all the given items. - */ -template allSatisfy(alias F, T...) -{ - static if (T.length == 0) - { - enum allSatisfy = true; - } - else static if (T.length == 1) - { - enum allSatisfy = F!(T[0]); - } - else - { - enum allSatisfy = - allSatisfy!(F, T[ 0 .. $/2]) && - allSatisfy!(F, T[$/2 .. $ ]); - } -} - -/// -unittest -{ - import std.traits : isIntegral; - - static assert(!allSatisfy!(isIntegral, int, double)); - static assert( allSatisfy!(isIntegral, int, long)); -} - -/** -Tests whether any given items satisfy a template predicate, i.e. evaluates to -$(D F!(T[0]) || F!(T[1]) || ... || F!(T[$ - 1])). - -Evaluation is $(I not) short-circuited if a true result is encountered; the -template predicate must be instantiable with all the given items. - */ -template anySatisfy(alias F, T...) -{ - static if(T.length == 0) - { - enum anySatisfy = false; - } - else static if (T.length == 1) - { - enum anySatisfy = F!(T[0]); - } - else - { - enum anySatisfy = - anySatisfy!(F, T[ 0 .. $/2]) || - anySatisfy!(F, T[$/2 .. $ ]); - } -} - -/// -unittest -{ - import std.traits : isIntegral; - - static assert(!anySatisfy!(isIntegral, string, double)); - static assert( anySatisfy!(isIntegral, int, double)); -} - - -/** - * Filters a $(D TypeTuple) using a template predicate. Returns a - * $(D TypeTuple) of the elements which satisfy the predicate. - */ -template Filter(alias pred, TList...) -{ - static if (TList.length == 0) - { - alias Filter = TypeTuple!(); - } - else static if (TList.length == 1) - { - static if (pred!(TList[0])) - alias Filter = TypeTuple!(TList[0]); - else - alias Filter = TypeTuple!(); - } - else - { - alias Filter = - TypeTuple!( - Filter!(pred, TList[ 0 .. $/2]), - Filter!(pred, TList[$/2 .. $ ])); - } -} - -/// -unittest -{ - import std.traits : isNarrowString, isUnsigned; - - alias Types1 = TypeTuple!(string, wstring, dchar[], char[], dstring, int); - alias TL1 = Filter!(isNarrowString, Types1); - static assert(is(TL1 == TypeTuple!(string, wstring, char[]))); - - alias Types2 = TypeTuple!(int, byte, ubyte, dstring, dchar, uint, ulong); - alias TL2 = Filter!(isUnsigned, Types2); - static assert(is(TL2 == TypeTuple!(ubyte, uint, ulong))); -} - -unittest -{ - import std.traits : isPointer; - - static assert(is(Filter!(isPointer, int, void*, char[], int*) == TypeTuple!(void*, int*))); - static assert(is(Filter!isPointer == TypeTuple!())); -} - - -// Used in template predicate unit tests below. -private version (unittest) -{ - template testAlways(T...) - { - enum testAlways = true; - } - - template testNever(T...) - { - enum testNever = false; - } - - template testError(T...) - { - static assert(false, "Should never be instantiated."); - } -} - - -/** - * Negates the passed template predicate. - */ -template templateNot(alias pred) -{ - enum templateNot(T...) = !pred!T; -} - -/// -unittest -{ - import std.traits : isPointer; - - alias isNoPointer = templateNot!isPointer; - static assert(!isNoPointer!(int*)); - static assert(allSatisfy!(isNoPointer, string, char, float)); -} - -unittest -{ - foreach (T; TypeTuple!(int, staticMap, 42)) - { - static assert(!Instantiate!(templateNot!testAlways, T)); - static assert(Instantiate!(templateNot!testNever, T)); - } -} - - -/** - * Combines several template predicates using logical AND, i.e. constructs a new - * predicate which evaluates to true for a given input T if and only if all of - * the passed predicates are true for T. - * - * The predicates are evaluated from left to right, aborting evaluation in a - * short-cut manner if a false result is encountered, in which case the latter - * instantiations do not need to compile. - */ -template templateAnd(Preds...) -{ - template templateAnd(T...) - { - static if (Preds.length == 0) - { - enum templateAnd = true; - } - else - { - static if (Instantiate!(Preds[0], T)) - alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T); - else - enum templateAnd = false; - } - } -} - -/// -unittest -{ - import std.traits : isNumeric, isUnsigned; - - alias storesNegativeNumbers = templateAnd!(isNumeric, templateNot!isUnsigned); - static assert(storesNegativeNumbers!int); - static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint); - - // An empty list of predicates always yields true. - alias alwaysTrue = templateAnd!(); - static assert(alwaysTrue!int); -} - -unittest -{ - foreach (T; TypeTuple!(int, staticMap, 42)) - { - static assert( Instantiate!(templateAnd!(), T)); - static assert( Instantiate!(templateAnd!(testAlways), T)); - static assert( Instantiate!(templateAnd!(testAlways, testAlways), T)); - static assert(!Instantiate!(templateAnd!(testNever), T)); - static assert(!Instantiate!(templateAnd!(testAlways, testNever), T)); - static assert(!Instantiate!(templateAnd!(testNever, testAlways), T)); - - static assert(!Instantiate!(templateAnd!(testNever, testError), T)); - static assert(!is(typeof(Instantiate!(templateAnd!(testAlways, testError), T)))); - } -} - - -/** - * Combines several template predicates using logical OR, i.e. constructs a new - * predicate which evaluates to true for a given input T if and only at least - * one of the passed predicates is true for T. - * - * The predicates are evaluated from left to right, aborting evaluation in a - * short-cut manner if a true result is encountered, in which case the latter - * instantiations do not need to compile. - */ -template templateOr(Preds...) -{ - template templateOr(T...) - { - static if (Preds.length == 0) - { - enum templateOr = false; - } - else - { - static if (Instantiate!(Preds[0], T)) - enum templateOr = true; - else - alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T); - } - } -} - -/// -unittest -{ - import std.traits : isPointer, isUnsigned; - - alias isPtrOrUnsigned = templateOr!(isPointer, isUnsigned); - static assert( isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*)); - static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!(string)); - - // An empty list of predicates never yields true. - alias alwaysFalse = templateOr!(); - static assert(!alwaysFalse!int); -} - -unittest -{ - foreach (T; TypeTuple!(int, staticMap, 42)) - { - static assert( Instantiate!(templateOr!(testAlways), T)); - static assert( Instantiate!(templateOr!(testAlways, testAlways), T)); - static assert( Instantiate!(templateOr!(testAlways, testNever), T)); - static assert( Instantiate!(templateOr!(testNever, testAlways), T)); - static assert(!Instantiate!(templateOr!(), T)); - static assert(!Instantiate!(templateOr!(testNever), T)); - - static assert( Instantiate!(templateOr!(testAlways, testError), T)); - static assert( Instantiate!(templateOr!(testNever, testAlways, testError), T)); - // DMD @@BUG@@: Assertion fails for int, seems like a error gagging - // problem. The bug goes away when removing some of the other template - // instantiations in the module. - // static assert(!is(typeof(Instantiate!(templateOr!(testNever, testError), T)))); - } -} - - -// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : // -package: - -/* - * With the builtin alias declaration, you cannot declare - * aliases of, for example, literal values. You can alias anything - * including literal values via this template. - */ -// symbols and literal values -template Alias(alias a) -{ - static if (__traits(compiles, { alias x = a; })) - alias Alias = a; - else static if (__traits(compiles, { enum x = a; })) - enum Alias = a; - else - static assert(0, "Cannot alias " ~ a.stringof); -} -// types and tuples -template Alias(a...) -{ - alias Alias = a; -} - -unittest -{ - enum abc = 1; - static assert(__traits(compiles, { alias a = Alias!(123); })); - static assert(__traits(compiles, { alias a = Alias!(abc); })); - static assert(__traits(compiles, { alias a = Alias!(int); })); - static assert(__traits(compiles, { alias a = Alias!(1,abc,int); })); -} - - -// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : // -private: - -/* - * [internal] Returns true if a and b are the same thing, or false if - * not. Both a and b can be types, literals, or symbols. - * - * How: When: - * is(a == b) - both are types - * a == b - both are literals (true literals, enums) - * __traits(isSame, a, b) - other cases (variables, functions, - * templates, etc.) - */ -private template isSame(ab...) - if (ab.length == 2) -{ - static if (__traits(compiles, expectType!(ab[0]), - expectType!(ab[1]))) - { - enum isSame = is(ab[0] == ab[1]); - } - else static if (!__traits(compiles, expectType!(ab[0])) && - !__traits(compiles, expectType!(ab[1])) && - __traits(compiles, expectBool!(ab[0] == ab[1]))) - { - static if (!__traits(compiles, &ab[0]) || - !__traits(compiles, &ab[1])) - enum isSame = (ab[0] == ab[1]); - else - enum isSame = __traits(isSame, ab[0], ab[1]); - } - else - { - enum isSame = __traits(isSame, ab[0], ab[1]); - } -} -private template expectType(T) {} -private template expectBool(bool b) {} - -unittest -{ - static assert( isSame!(int, int)); - static assert(!isSame!(int, short)); - - enum a = 1, b = 1, c = 2, s = "a", t = "a"; - static assert( isSame!(1, 1)); - static assert( isSame!(a, 1)); - static assert( isSame!(a, b)); - static assert(!isSame!(b, c)); - static assert( isSame!("a", "a")); - static assert( isSame!(s, "a")); - static assert( isSame!(s, t)); - static assert(!isSame!(1, "1")); - static assert(!isSame!(a, "a")); - static assert( isSame!(isSame, isSame)); - static assert(!isSame!(isSame, a)); - - static assert(!isSame!(byte, a)); - static assert(!isSame!(short, isSame)); - static assert(!isSame!(a, int)); - static assert(!isSame!(long, isSame)); - - static immutable X = 1, Y = 1, Z = 2; - static assert( isSame!(X, X)); - static assert(!isSame!(X, Y)); - static assert(!isSame!(Y, Z)); - - int foo(); - int bar(); - real baz(int); - static assert( isSame!(foo, foo)); - static assert(!isSame!(foo, bar)); - static assert(!isSame!(bar, baz)); - static assert( isSame!(baz, baz)); - static assert(!isSame!(foo, 0)); - - int x, y; - real z; - static assert( isSame!(x, x)); - static assert(!isSame!(x, y)); - static assert(!isSame!(y, z)); - static assert( isSame!(z, z)); - static assert(!isSame!(x, 0)); -} - -/* - * [internal] Confines a tuple within a template. - */ -private template Pack(T...) -{ - alias tuple = T; - - // For convenience - template equals(U...) - { - static if (T.length == U.length) - { - static if (T.length == 0) - enum equals = true; - else - enum equals = isSame!(T[0], U[0]) && - Pack!(T[1 .. $]).equals!(U[1 .. $]); - } - else - { - enum equals = false; - } - } -} - -unittest -{ - static assert( Pack!(1, int, "abc").equals!(1, int, "abc")); - static assert(!Pack!(1, int, "abc").equals!(1, int, "cba")); -} - -/* - * Instantiates the given template with the given list of parameters. - * - * Used to work around syntactic limitations of D with regard to instantiating - * a template from a type tuple (e.g. T[0]!(...) is not valid) or a template - * returning another template (e.g. Foo!(Bar)!(Baz) is not allowed). - */ -// TODO: Consider publicly exposing this, maybe even if only for better -// understandability of error messages. -alias Instantiate(alias Template, Params...) = Template!Params; diff --git a/std/uni.d b/std/uni.d index ed3edabacd8..39b0474ad64 100644 --- a/std/uni.d +++ b/std/uni.d @@ -4,92 +4,182 @@ $(P The $(D std.uni) module provides an implementation of fundamental Unicode algorithms and data structures. This doesn't include UTF encoding and decoding primitives, - see $(XREF _utf, decode) and $(XREF _utf, encode) in std.utf + see $(REF decode, std,_utf) and $(REF encode, std,_utf) in $(MREF std, utf) for this functionality. ) +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Decode) $(TD + $(LREF byCodePoint) + $(LREF byGrapheme) + $(LREF decodeGrapheme) + $(LREF graphemeStride) +)) +$(TR $(TD Comparison) $(TD + $(LREF icmp) + $(LREF sicmp) +)) +$(TR $(TD Classification) $(TD + $(LREF isAlpha) + $(LREF isAlphaNum) + $(LREF isCodepointSet) + $(LREF isControl) + $(LREF isFormat) + $(LREF isGraphical) + $(LREF isIntegralPair) + $(LREF isMark) + $(LREF isNonCharacter) + $(LREF isNumber) + $(LREF isPrivateUse) + $(LREF isPunctuation) + $(LREF isSpace) + $(LREF isSurrogate) + $(LREF isSurrogateHi) + $(LREF isSurrogateLo) + $(LREF isSymbol) + $(LREF isWhite) +)) +$(TR $(TD Normalization) $(TD + $(LREF NFC) + $(LREF NFD) + $(LREF NFKD) + $(LREF NormalizationForm) + $(LREF normalize) +)) +$(TR $(TD Decompose) $(TD + $(LREF decompose) + $(LREF decomposeHangul) + $(LREF UnicodeDecomposition) +)) +$(TR $(TD Compose) $(TD + $(LREF compose) + $(LREF composeJamo) +)) +$(TR $(TD Sets) $(TD + $(LREF CodepointInterval) + $(LREF CodepointSet) + $(LREF InversionList) + $(LREF unicode) +)) +$(TR $(TD Trie) $(TD + $(LREF codepointSetTrie) + $(LREF CodepointSetTrie) + $(LREF codepointTrie) + $(LREF CodepointTrie) + $(LREF toTrie) + $(LREF toDelegate) +)) +$(TR $(TD Casing) $(TD + $(LREF asCapitalized) + $(LREF asLowerCase) + $(LREF asUpperCase) + $(LREF isLower) + $(LREF isUpper) + $(LREF toLower) + $(LREF toLowerInPlace) + $(LREF toUpper) + $(LREF toUpperInPlace) +)) +$(TR $(TD Utf8Matcher) $(TD + $(LREF isUtfMatcher) + $(LREF MatcherConcept) + $(LREF utfMatcher) +)) +$(TR $(TD Separators) $(TD + $(LREF lineSep) + $(LREF nelSep) + $(LREF paraSep) +)) +$(TR $(TD Building blocks) $(TD + $(LREF allowedIn) + $(LREF combiningClass) + $(LREF Grapheme) +)) +) + $(P All primitives listed operate on Unicode characters and - sets of characters. For functions which operate on ASCII characters - and ignore Unicode $(CHARACTERS), see $(LINK2 std_ascii.html, std.ascii). - For definitions of Unicode $(CHARACTER), $(CODEPOINT) and other terms - used throughout this module see the $(S_LINK Terminology, terminology) section - below. + sets of characters. For functions which operate on ASCII characters + and ignore Unicode $(CHARACTERS), see $(MREF std, ascii). + For definitions of Unicode $(CHARACTER), $(CODEPOINT) and other terms + used throughout this module see the $(S_LINK Terminology, terminology) section + below. ) - $(P The focus of this module is the core needs of developing Unicode-aware - applications. To that effect it provides the following optimized primitives: + applications. To that effect it provides the following optimized primitives: ) $(UL - $(LI Character classification by category and common properties: - $(LREF isAlpha), $(LREF isWhite) and others. - ) - $(LI - Case-insensitive string comparison ($(LREF sicmp), $(LREF icmp)). - ) - $(LI - Converting text to any of the four normalization forms via $(LREF normalize). - ) - $(LI - Decoding ($(LREF decodeGrapheme)) and iteration ($(LREF byGrapheme), $(LREF graphemeStride)) - by user-perceived characters, that is by $(LREF Grapheme) clusters. - ) - $(LI - Decomposing and composing of individual character(s) according to canonical - or compatibility rules, see $(LREF compose) and $(LREF decompose), - including the specific version for Hangul syllables $(LREF composeJamo) - and $(LREF decomposeHangul). - ) + $(LI Character classification by category and common properties: + $(LREF isAlpha), $(LREF isWhite) and others. + ) + $(LI + Case-insensitive string comparison ($(LREF sicmp), $(LREF icmp)). + ) + $(LI + Converting text to any of the four normalization forms via $(LREF normalize). + ) + $(LI + Decoding ($(LREF decodeGrapheme)) and iteration ($(LREF byGrapheme), $(LREF graphemeStride)) + by user-perceived characters, that is by $(LREF Grapheme) clusters. + ) + $(LI + Decomposing and composing of individual character(s) according to canonical + or compatibility rules, see $(LREF compose) and $(LREF decompose), + including the specific version for Hangul syllables $(LREF composeJamo) + and $(LREF decomposeHangul). + ) ) $(P It's recognized that an application may need further enhancements - and extensions, such as less commonly known algorithms, - or tailoring existing ones for region specific needs. To help users - with building any extra functionality beyond the core primitives, - the module provides: + and extensions, such as less commonly known algorithms, + or tailoring existing ones for region specific needs. To help users + with building any extra functionality beyond the core primitives, + the module provides: ) $(UL - $(LI - $(LREF CodepointSet), a type for easy manipulation of sets of characters. - Besides the typical set algebra it provides an unusual feature: - a D source code generator for detection of $(CODEPOINTS) in this set. - This is a boon for meta-programming parser frameworks, - and is used internally to power classification in small - sets like $(LREF isWhite). - ) - $(LI - A way to construct optimal packed multi-stage tables also known as a - special case of $(LUCKY Trie). - The functions $(LREF codepointTrie), $(LREF codepointSetTrie) - construct custom tries that map dchar to value. - The end result is a fast and predictable $(BIGOH 1) lookup that powers - functions like $(LREF isAlpha) and $(LREF combiningClass), - but for user-defined data sets. - ) - $(LI - A useful technique for Unicode-aware parsers that perform - character classification of encoded $(CODEPOINTS) - is to avoid unnecassary decoding at all costs. - $(LREF utfMatcher) provides an improvement over the usual workflow - of decode-classify-process, combining the decoding and classification - steps. By extracting necessary bits directly from encoded - $(S_LINK Code unit, code units) matchers achieve - significant performance improvements. See $(LREF MatcherConcept) for - the common interface of UTF matchers. - ) - $(LI - Generally useful building blocks for customized normalization: - $(LREF combiningClass) for querying combining class - and $(LREF allowedIn) for testing the Quick_Check - property of a given normalization form. - ) - $(LI - Access to a large selection of commonly used sets of $(CODEPOINTS). - $(S_LINK Unicode properties, Supported sets) include Script, - Block and General Category. The exact contents of a set can be - observed in the CLDR utility, on the - $(WEB www.unicode.org/cldr/utility/properties.jsp, property index) page - of the Unicode website. - See $(LREF unicode) for easy and (optionally) compile-time checked set - queries. - ) + $(LI + $(LREF CodepointSet), a type for easy manipulation of sets of characters. + Besides the typical set algebra it provides an unusual feature: + a D source code generator for detection of $(CODEPOINTS) in this set. + This is a boon for meta-programming parser frameworks, + and is used internally to power classification in small + sets like $(LREF isWhite). + ) + $(LI + A way to construct optimal packed multi-stage tables also known as a + special case of $(LINK2 https://en.wikipedia.org/wiki/Trie, Trie). + The functions $(LREF codepointTrie), $(LREF codepointSetTrie) + construct custom tries that map dchar to value. + The end result is a fast and predictable $(BIGOH 1) lookup that powers + functions like $(LREF isAlpha) and $(LREF combiningClass), + but for user-defined data sets. + ) + $(LI + A useful technique for Unicode-aware parsers that perform + character classification of encoded $(CODEPOINTS) + is to avoid unnecassary decoding at all costs. + $(LREF utfMatcher) provides an improvement over the usual workflow + of decode-classify-process, combining the decoding and classification + steps. By extracting necessary bits directly from encoded + $(S_LINK Code unit, code units) matchers achieve + significant performance improvements. See $(LREF MatcherConcept) for + the common interface of UTF matchers. + ) + $(LI + Generally useful building blocks for customized normalization: + $(LREF combiningClass) for querying combining class + and $(LREF allowedIn) for testing the Quick_Check + property of a given normalization form. + ) + $(LI + Access to a large selection of commonly used sets of $(CODEPOINTS). + $(S_LINK Unicode properties, Supported sets) include Script, + Block and General Category. The exact contents of a set can be + observed in the CLDR utility, on the + $(HTTP www.unicode.org/cldr/utility/properties.jsp, property index) page + of the Unicode website. + See $(LREF unicode) for easy and (optionally) compile-time checked set + queries. + ) ) $(SECTION Synopsis) --- @@ -151,84 +241,78 @@ assert(normalize!NFKD("2¹â°") == "210"); } --- - $(SECTION Terminology) + $(SECTION Terminology + ) $(P The following is a list of important Unicode notions and definitions. Any conventions used specifically in this module alone are marked as such. The descriptions are based on the formal - definition as found in $(WEB www.unicode.org/versions/Unicode6.2.0/ch03.pdf, + definition as found in $(HTTP www.unicode.org/versions/Unicode6.2.0/ch03.pdf, chapter three of The Unicode Standard Core Specification.) ) - $(P $(DEF Abstract character) A unit of information used for the organization, - control, or representation of textual data. - Note that: + control, or representation of textual data. + Note that: $(UL - $(LI When representing data, the nature of that data - is generally symbolic as opposed to some other - kind of data (for example, visual).) - - $(LI An abstract character has no concrete form - and should not be confused with a $(S_LINK Glyph, glyph).) - - $(LI An abstract character does not necessarily - correspond to what a user thinks of as a “character†- and should not be confused with a $(LREF Grapheme).) - - $(LI The abstract characters encoded (see Encoded character) - are known as Unicode abstract characters.) - - $(LI Abstract characters not directly - encoded by the Unicode Standard can often be - represented by the use of combining character sequences.) + $(LI When representing data, the nature of that data + is generally symbolic as opposed to some other + kind of data (for example, visual). + ) + $(LI An abstract character has no concrete form + and should not be confused with a $(S_LINK Glyph, glyph). + ) + $(LI An abstract character does not necessarily + correspond to what a user thinks of as a “character†+ and should not be confused with a $(LREF Grapheme). + ) + $(LI The abstract characters encoded (see Encoded character) + are known as Unicode abstract characters. + ) + $(LI Abstract characters not directly + encoded by the Unicode Standard can often be + represented by the use of combining character sequences. + ) ) ) - $(P $(DEF Canonical decomposition) - The decomposition of a character or character sequence - that results from recursively applying the canonical - mappings found in the Unicode Character Database - and these described in Conjoining Jamo Behavior - (section 12 of - $(WEB www.unicode.org/uni2book/ch03.pdf, Unicode Conformance)). + The decomposition of a character or character sequence + that results from recursively applying the canonical + mappings found in the Unicode Character Database + and these described in Conjoining Jamo Behavior + (section 12 of + $(HTTP www.unicode.org/uni2book/ch03.pdf, Unicode Conformance)). ) - $(P $(DEF Canonical composition) - The precise definition of the Canonical composition - is the algorithm as specified in $(WEB www.unicode.org/uni2book/ch03.pdf, - Unicode Conformance) section 11. - Informally it's the process that does the reverse of the canonical - decomposition with the addition of certain rules - that e.g. prevent legacy characters from appearing in the composed result. + The precise definition of the Canonical composition + is the algorithm as specified in $(HTTP www.unicode.org/uni2book/ch03.pdf, + Unicode Conformance) section 11. + Informally it's the process that does the reverse of the canonical + decomposition with the addition of certain rules + that e.g. prevent legacy characters from appearing in the composed result. ) - $(P $(DEF Canonical equivalent) - Two character sequences are said to be canonical equivalents if - their full canonical decompositions are identical. + Two character sequences are said to be canonical equivalents if + their full canonical decompositions are identical. ) - $(P $(DEF Character) Typically differs by context. - For the purpose of this documentation the term $(I character) - implies $(I encoded character), that is, a code point having - an assigned abstract character (a symbolic meaning). + For the purpose of this documentation the term $(I character) + implies $(I encoded character), that is, a code point having + an assigned abstract character (a symbolic meaning). ) - $(P $(DEF Code point) Any value in the Unicode codespace; - that is, the range of integers from 0 to 10FFFF (hex). - Not all code points are assigned to encoded characters. + that is, the range of integers from 0 to 10FFFF (hex). + Not all code points are assigned to encoded characters. ) - $(P $(DEF Code unit) The minimal bit combination that can represent - a unit of encoded text for processing or interchange. - Depending on the encoding this could be: - 8-bit code units in the UTF-8 ($(D char)), - 16-bit code units in the UTF-16 ($(D wchar)), - and 32-bit code units in the UTF-32 ($(D dchar)). - $(I Note that in UTF-32, a code unit is a code point - and is represented by the D $(D dchar) type.) + a unit of encoded text for processing or interchange. + Depending on the encoding this could be: + 8-bit code units in the UTF-8 ($(D char)), + 16-bit code units in the UTF-16 ($(D wchar)), + and 32-bit code units in the UTF-32 ($(D dchar)). + $(I Note that in UTF-32, a code unit is a code point + and is represented by the D $(D dchar) type.) ) - $(P $(DEF Combining character) A character with the General Category - of Combining Mark(M). + of Combining Mark(M). $(UL $(LI All characters with non-zero canonical combining class are combining characters, but the reverse is not the case: @@ -241,41 +325,34 @@ ) ) ) - $(P $(DEF Combining class) A numerical value used by the Unicode Canonical Ordering Algorithm to determine which sequences of combining marks are to be considered canonically equivalent and which are not. ) - $(P $(DEF Compatibility decomposition) - The decomposition of a character or character sequence that results - from recursively applying both the compatibility mappings and - the canonical mappings found in the Unicode Character Database, and those - described in Conjoining Jamo Behavior no characters - can be further decomposed. + The decomposition of a character or character sequence that results + from recursively applying both the compatibility mappings and + the canonical mappings found in the Unicode Character Database, and those + described in Conjoining Jamo Behavior no characters + can be further decomposed. ) - $(P $(DEF Compatibility equivalent) - Two character sequences are said to be compatibility - equivalents if their full compatibility decompositions are identical. + Two character sequences are said to be compatibility + equivalents if their full compatibility decompositions are identical. ) - $(P $(DEF Encoded character) An association (or mapping) - between an abstract character and a code point. + between an abstract character and a code point. ) - $(P $(DEF Glyph) The actual, concrete image of a glyph representation - having been rasterized or otherwise imaged onto some display surface. + having been rasterized or otherwise imaged onto some display surface. ) - $(P $(DEF Grapheme base) A character with the property - Grapheme_Base, or any standard Korean syllable block. + Grapheme_Base, or any standard Korean syllable block. ) - $(P $(DEF Grapheme cluster) Defined as the text between grapheme boundaries as specified by Unicode Standard Annex #29, - $(WEB www.unicode.org/reports/tr29/, Unicode text segmentation). + $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation). Important general properties of a grapheme: $(UL $(LI The grapheme cluster represents a horizontally segmentable @@ -301,83 +378,73 @@ as defined in the aforementioned standard annex. ) ) - - $(P $(DEF Nonspacing mark) A combining character with the General Category of Nonspacing Mark (Mn) or Enclosing Mark (Me). ) - - $(P $(DEF Spacing mark) A combining character that is not a nonspacing mark.) - - - $(SECTION Normalization) - + $(P $(DEF Spacing mark) A combining character that is not a nonspacing mark. + ) + $(SECTION Normalization + ) $(P The concepts of $(S_LINK Canonical equivalent, canonical equivalent) - or $(S_LINK Compatibility equivalent, compatibility equivalent) - characters in the Unicode Standard make it necessary to have a full, formal - definition of equivalence for Unicode strings. - String equivalence is determined by a process called normalization, - whereby strings are converted into forms which are compared - directly for identity. This is the primary goal of the normalization process, - see the function $(LREF normalize) to convert into any of - the four defined forms. + or $(S_LINK Compatibility equivalent, compatibility equivalent) + characters in the Unicode Standard make it necessary to have a full, formal + definition of equivalence for Unicode strings. + String equivalence is determined by a process called normalization, + whereby strings are converted into forms which are compared + directly for identity. This is the primary goal of the normalization process, + see the function $(LREF normalize) to convert into any of + the four defined forms. ) - $(P A very important attribute of the Unicode Normalization Forms - is that they must remain stable between versions of the Unicode Standard. - A Unicode string normalized to a particular Unicode Normalization Form - in one version of the standard is guaranteed to remain in that Normalization - Form for implementations of future versions of the standard. + is that they must remain stable between versions of the Unicode Standard. + A Unicode string normalized to a particular Unicode Normalization Form + in one version of the standard is guaranteed to remain in that Normalization + Form for implementations of future versions of the standard. ) - $(P The Unicode Standard specifies four normalization forms. - Informally, two of these forms are defined by maximal decomposition - of equivalent sequences, and two of these forms are defined - by maximal $(I composition) of equivalent sequences. - $(UL - $(LI Normalization Form D (NFD): The $(S_LINK Canonical decomposition, - canonical decomposition) of a character sequence.) - $(LI Normalization Form KD (NFKD): The $(S_LINK Compatibility decomposition, - compatibility decomposition) of a character sequence.) - $(LI Normalization Form C (NFC): The canonical composition of the - $(S_LINK Canonical decomposition, canonical decomposition) - of a coded character sequence.) - $(LI Normalization Form KC (NFKC): The canonical composition - of the $(S_LINK Compatibility decomposition, - compatibility decomposition) of a character sequence) - ) + Informally, two of these forms are defined by maximal decomposition + of equivalent sequences, and two of these forms are defined + by maximal $(I composition) of equivalent sequences. + $(UL + $(LI Normalization Form D (NFD): The $(S_LINK Canonical decomposition, + canonical decomposition) of a character sequence.) + $(LI Normalization Form KD (NFKD): The $(S_LINK Compatibility decomposition, + compatibility decomposition) of a character sequence.) + $(LI Normalization Form C (NFC): The canonical composition of the + $(S_LINK Canonical decomposition, canonical decomposition) + of a coded character sequence.) + $(LI Normalization Form KC (NFKC): The canonical composition + of the $(S_LINK Compatibility decomposition, + compatibility decomposition) of a character sequence) + ) ) - $(P The choice of the normalization form depends on the particular use case. - NFC is the best form for general text, since it's more compatible with - strings converted from legacy encodings. NFKC is the preferred form for - identifiers, especially where there are security concerns. NFD and NFKD - are the most useful for internal processing. + NFC is the best form for general text, since it's more compatible with + strings converted from legacy encodings. NFKC is the preferred form for + identifiers, especially where there are security concerns. NFD and NFKD + are the most useful for internal processing. + ) + $(SECTION Construction of lookup tables ) - - $(SECTION Construction of lookup tables) - $(P The Unicode standard describes a set of algorithms that - depend on having the ability to quickly look up various properties - of a code point. Given the the codespace of about 1 million $(CODEPOINTS), - it is not a trivial task to provide a space-efficient solution for - the multitude of properties.) - + depend on having the ability to quickly look up various properties + of a code point. Given the the codespace of about 1 million $(CODEPOINTS), + it is not a trivial task to provide a space-efficient solution for + the multitude of properties. + ) $(P Common approaches such as hash-tables or binary search over - sorted code point intervals (as in $(LREF InversionList)) are insufficient. - Hash-tables have enormous memory footprint and binary search - over intervals is not fast enough for some heavy-duty algorithms. - ) - + sorted code point intervals (as in $(LREF InversionList)) are insufficient. + Hash-tables have enormous memory footprint and binary search + over intervals is not fast enough for some heavy-duty algorithms. + ) $(P The recommended solution (see Unicode Implementation Guidelines) - is using multi-stage tables that are an implementation of the - $(WEB en.wikipedia.org/wiki/Trie, Trie) data structure with integer - keys and a fixed number of stages. For the remainder of the section - this will be called a fixed trie. The following describes a particular - implementation that is aimed for the speed of access at the expense - of ideal size savings. + is using multi-stage tables that are an implementation of the + $(HTTP en.wikipedia.org/wiki/Trie, Trie) data structure with integer + keys and a fixed number of stages. For the remainder of the section + this will be called a fixed trie. The following describes a particular + implementation that is aimed for the speed of access at the expense + of ideal size savings. ) - $(P Taking a 2-level Trie as an example the principle of operation is as follows. Split the number of bits in a key (code point, 21 bits) into 2 components (e.g. 15 and 8). The first is the number of bits in the index of the trie @@ -385,7 +452,6 @@ The layout of the trie is then an array of size 2^^bits-of-index followed an array of memory chunks of size 2^^bits-of-page/bits-per-element. ) - $(P The number of pages is variable (but not less then 1) unlike the number of entries in the index. The slots of the index all have to contain a number of a page that is present. The lookup is then @@ -401,93 +467,91 @@ pages[index[n >> bits_per_page]][n & (elemsPerPage - 1)]; --- $(P Where if $(D elemsPerPage) is a power of 2 the whole process is - a handful of simple instructions and 2 array reads. Subsequent levels - of the trie are introduced by recursing on this notion - the index array - is treated as values. The number of bits in index is then again - split into 2 parts, with pages over 'current-index' and the new 'upper-index'. + a handful of simple instructions and 2 array reads. Subsequent levels + of the trie are introduced by recursing on this notion - the index array + is treated as values. The number of bits in index is then again + split into 2 parts, with pages over 'current-index' and the new 'upper-index'. ) $(P For completeness a level 1 trie is simply an array. - The current implementation takes advantage of bit-packing values - when the range is known to be limited in advance (such as $(D bool)). - See also $(LREF BitPacked) for enforcing it manually. - The major size advantage however comes from the fact - that multiple $(B identical pages on every level are merged) by construction. + The current implementation takes advantage of bit-packing values + when the range is known to be limited in advance (such as $(D bool)). + See also $(LREF BitPacked) for enforcing it manually. + The major size advantage however comes from the fact + that multiple $(B identical pages on every level are merged) by construction. ) - $(P The process of constructing a trie is more involved and is hidden from - the user in a form of the convenience functions $(LREF codepointTrie), - $(LREF codepointSetTrie) and the even more convenient $(LREF toTrie). - In general a set or built-in AA with $(D dchar) type - can be turned into a trie. The trie object in this module - is read-only (immutable); it's effectively frozen after construction. + the user in a form of the convenience functions $(LREF codepointTrie), + $(LREF codepointSetTrie) and the even more convenient $(LREF toTrie). + In general a set or built-in AA with $(D dchar) type + can be turned into a trie. The trie object in this module + is read-only (immutable); it's effectively frozen after construction. + ) + $(SECTION Unicode properties ) - - $(SECTION Unicode properties) - $(P This is a full list of Unicode properties accessible through $(LREF unicode) - with specific helpers per category nested within. Consult the - $(WEB www.unicode.org/cldr/utility/properties.jsp, CLDR utility) - when in doubt about the contents of a particular set.) - + with specific helpers per category nested within. Consult the + $(HTTP www.unicode.org/cldr/utility/properties.jsp, CLDR utility) + when in doubt about the contents of a particular set. + ) $(P General category sets listed below are only accessible with the - $(LREF unicode) shorthand accessor.) - $(BOOKTABLE $(B General category ), - $(TR $(TH Abb.) $(TH Long form) - $(TH Abb.) $(TH Long form)$(TH Abb.) $(TH Long form)) - $(TR $(TD L) $(TD Letter) - $(TD Cn) $(TD Unassigned) $(TD Po) $(TD Other_Punctuation)) - $(TR $(TD Ll) $(TD Lowercase_Letter) - $(TD Co) $(TD Private_Use) $(TD Ps) $(TD Open_Punctuation)) - $(TR $(TD Lm) $(TD Modifier_Letter) - $(TD Cs) $(TD Surrogate) $(TD S) $(TD Symbol)) - $(TR $(TD Lo) $(TD Other_Letter) - $(TD N) $(TD Number) $(TD Sc) $(TD Currency_Symbol)) - $(TR $(TD Lt) $(TD Titlecase_Letter) - $(TD Nd) $(TD Decimal_Number) $(TD Sk) $(TD Modifier_Symbol)) - $(TR $(TD Lu) $(TD Uppercase_Letter) - $(TD Nl) $(TD Letter_Number) $(TD Sm) $(TD Math_Symbol)) - $(TR $(TD M) $(TD Mark) - $(TD No) $(TD Other_Number) $(TD So) $(TD Other_Symbol)) - $(TR $(TD Mc) $(TD Spacing_Mark) - $(TD P) $(TD Punctuation) $(TD Z) $(TD Separator)) - $(TR $(TD Me) $(TD Enclosing_Mark) - $(TD Pc) $(TD Connector_Punctuation) $(TD Zl) $(TD Line_Separator)) - $(TR $(TD Mn) $(TD Nonspacing_Mark) - $(TD Pd) $(TD Dash_Punctuation) $(TD Zp) $(TD Paragraph_Separator)) - $(TR $(TD C) $(TD Other) - $(TD Pe) $(TD Close_Punctuation) $(TD Zs) $(TD Space_Separator)) - $(TR $(TD Cc) $(TD Control) $(TD Pf) - $(TD Final_Punctuation) $(TD -) $(TD Any)) - $(TR $(TD Cf) $(TD Format) - $(TD Pi) $(TD Initial_Punctuation) $(TD -) $(TD ASCII)) + $(LREF unicode) shorthand accessor.) + $(BOOKTABLE $(B General category ), + $(TR $(TH Abb.) $(TH Long form) + $(TH Abb.) $(TH Long form)$(TH Abb.) $(TH Long form)) + $(TR $(TD L) $(TD Letter) + $(TD Cn) $(TD Unassigned) $(TD Po) $(TD Other_Punctuation)) + $(TR $(TD Ll) $(TD Lowercase_Letter) + $(TD Co) $(TD Private_Use) $(TD Ps) $(TD Open_Punctuation)) + $(TR $(TD Lm) $(TD Modifier_Letter) + $(TD Cs) $(TD Surrogate) $(TD S) $(TD Symbol)) + $(TR $(TD Lo) $(TD Other_Letter) + $(TD N) $(TD Number) $(TD Sc) $(TD Currency_Symbol)) + $(TR $(TD Lt) $(TD Titlecase_Letter) + $(TD Nd) $(TD Decimal_Number) $(TD Sk) $(TD Modifier_Symbol)) + $(TR $(TD Lu) $(TD Uppercase_Letter) + $(TD Nl) $(TD Letter_Number) $(TD Sm) $(TD Math_Symbol)) + $(TR $(TD M) $(TD Mark) + $(TD No) $(TD Other_Number) $(TD So) $(TD Other_Symbol)) + $(TR $(TD Mc) $(TD Spacing_Mark) + $(TD P) $(TD Punctuation) $(TD Z) $(TD Separator)) + $(TR $(TD Me) $(TD Enclosing_Mark) + $(TD Pc) $(TD Connector_Punctuation) $(TD Zl) $(TD Line_Separator)) + $(TR $(TD Mn) $(TD Nonspacing_Mark) + $(TD Pd) $(TD Dash_Punctuation) $(TD Zp) $(TD Paragraph_Separator)) + $(TR $(TD C) $(TD Other) + $(TD Pe) $(TD Close_Punctuation) $(TD Zs) $(TD Space_Separator)) + $(TR $(TD Cc) $(TD Control) $(TD Pf) + $(TD Final_Punctuation) $(TD -) $(TD Any)) + $(TR $(TD Cf) $(TD Format) + $(TD Pi) $(TD Initial_Punctuation) $(TD -) $(TD ASCII)) ) $(P Sets for other commonly useful properties that are - accessible with $(LREF unicode):) - $(BOOKTABLE $(B Common binary properties), - $(TR $(TH Name) $(TH Name) $(TH Name)) - $(TR $(TD Alphabetic) $(TD Ideographic) $(TD Other_Uppercase)) - $(TR $(TD ASCII_Hex_Digit) $(TD IDS_Binary_Operator) $(TD Pattern_Syntax)) - $(TR $(TD Bidi_Control) $(TD ID_Start) $(TD Pattern_White_Space)) - $(TR $(TD Cased) $(TD IDS_Trinary_Operator) $(TD Quotation_Mark)) - $(TR $(TD Case_Ignorable) $(TD Join_Control) $(TD Radical)) - $(TR $(TD Dash) $(TD Logical_Order_Exception) $(TD Soft_Dotted)) - $(TR $(TD Default_Ignorable_Code_Point) $(TD Lowercase) $(TD STerm)) - $(TR $(TD Deprecated) $(TD Math) $(TD Terminal_Punctuation)) - $(TR $(TD Diacritic) $(TD Noncharacter_Code_Point) $(TD Unified_Ideograph)) - $(TR $(TD Extender) $(TD Other_Alphabetic) $(TD Uppercase)) - $(TR $(TD Grapheme_Base) $(TD Other_Default_Ignorable_Code_Point) $(TD Variation_Selector)) - $(TR $(TD Grapheme_Extend) $(TD Other_Grapheme_Extend) $(TD White_Space)) - $(TR $(TD Grapheme_Link) $(TD Other_ID_Continue) $(TD XID_Continue)) - $(TR $(TD Hex_Digit) $(TD Other_ID_Start) $(TD XID_Start)) - $(TR $(TD Hyphen) $(TD Other_Lowercase) ) - $(TR $(TD ID_Continue) $(TD Other_Math) ) + accessible with $(LREF unicode):) + $(BOOKTABLE $(B Common binary properties), + $(TR $(TH Name) $(TH Name) $(TH Name)) + $(TR $(TD Alphabetic) $(TD Ideographic) $(TD Other_Uppercase)) + $(TR $(TD ASCII_Hex_Digit) $(TD IDS_Binary_Operator) $(TD Pattern_Syntax)) + $(TR $(TD Bidi_Control) $(TD ID_Start) $(TD Pattern_White_Space)) + $(TR $(TD Cased) $(TD IDS_Trinary_Operator) $(TD Quotation_Mark)) + $(TR $(TD Case_Ignorable) $(TD Join_Control) $(TD Radical)) + $(TR $(TD Dash) $(TD Logical_Order_Exception) $(TD Soft_Dotted)) + $(TR $(TD Default_Ignorable_Code_Point) $(TD Lowercase) $(TD STerm)) + $(TR $(TD Deprecated) $(TD Math) $(TD Terminal_Punctuation)) + $(TR $(TD Diacritic) $(TD Noncharacter_Code_Point) $(TD Unified_Ideograph)) + $(TR $(TD Extender) $(TD Other_Alphabetic) $(TD Uppercase)) + $(TR $(TD Grapheme_Base) $(TD Other_Default_Ignorable_Code_Point) $(TD Variation_Selector)) + $(TR $(TD Grapheme_Extend) $(TD Other_Grapheme_Extend) $(TD White_Space)) + $(TR $(TD Grapheme_Link) $(TD Other_ID_Continue) $(TD XID_Continue)) + $(TR $(TD Hex_Digit) $(TD Other_ID_Start) $(TD XID_Start)) + $(TR $(TD Hyphen) $(TD Other_Lowercase) ) + $(TR $(TD ID_Continue) $(TD Other_Math) ) + ) + $(P Below is the table with block names accepted by $(LREF unicode.block). + Note that the shorthand version $(LREF unicode) requires "In" + to be prepended to the names of blocks so as to disambiguate + scripts and blocks. ) - $(P Bellow is the table with block names accepted by $(LREF unicode.block). - Note that the shorthand version $(LREF unicode) requires "In" - to be prepended to the names of blocks so as to disambiguate - scripts and blocks.) - $(BOOKTABLE $(B Blocks), $(TR $(TD Aegean Numbers) $(TD Ethiopic Extended) $(TD Mongolian)) $(TR $(TD Alchemical Symbols) $(TD Ethiopic Extended-A) $(TD Musical Symbols)) @@ -564,76 +628,71 @@ $(TR $(TD Enclosed Ideographic Supplement) $(TD Miscellaneous Technical) ) $(TR $(TD Ethiopic) $(TD Modifier Tone Letters) ) ) - - $(P Bellow is the table with script names accepted by $(LREF unicode.script) - and by the shorthand version $(LREF unicode):) - $(BOOKTABLE $(B Scripts), - $(TR $(TD Arabic) $(TD Hanunoo) $(TD Old_Italic)) - $(TR $(TD Armenian) $(TD Hebrew) $(TD Old_Persian)) - $(TR $(TD Avestan) $(TD Hiragana) $(TD Old_South_Arabian)) - $(TR $(TD Balinese) $(TD Imperial_Aramaic) $(TD Old_Turkic)) - $(TR $(TD Bamum) $(TD Inherited) $(TD Oriya)) - $(TR $(TD Batak) $(TD Inscriptional_Pahlavi) $(TD Osmanya)) - $(TR $(TD Bengali) $(TD Inscriptional_Parthian) $(TD Phags_Pa)) - $(TR $(TD Bopomofo) $(TD Javanese) $(TD Phoenician)) - $(TR $(TD Brahmi) $(TD Kaithi) $(TD Rejang)) - $(TR $(TD Braille) $(TD Kannada) $(TD Runic)) - $(TR $(TD Buginese) $(TD Katakana) $(TD Samaritan)) - $(TR $(TD Buhid) $(TD Kayah_Li) $(TD Saurashtra)) - $(TR $(TD Canadian_Aboriginal) $(TD Kharoshthi) $(TD Sharada)) - $(TR $(TD Carian) $(TD Khmer) $(TD Shavian)) - $(TR $(TD Chakma) $(TD Lao) $(TD Sinhala)) - $(TR $(TD Cham) $(TD Latin) $(TD Sora_Sompeng)) - $(TR $(TD Cherokee) $(TD Lepcha) $(TD Sundanese)) - $(TR $(TD Common) $(TD Limbu) $(TD Syloti_Nagri)) - $(TR $(TD Coptic) $(TD Linear_B) $(TD Syriac)) - $(TR $(TD Cuneiform) $(TD Lisu) $(TD Tagalog)) - $(TR $(TD Cypriot) $(TD Lycian) $(TD Tagbanwa)) - $(TR $(TD Cyrillic) $(TD Lydian) $(TD Tai_Le)) - $(TR $(TD Deseret) $(TD Malayalam) $(TD Tai_Tham)) - $(TR $(TD Devanagari) $(TD Mandaic) $(TD Tai_Viet)) - $(TR $(TD Egyptian_Hieroglyphs) $(TD Meetei_Mayek) $(TD Takri)) - $(TR $(TD Ethiopic) $(TD Meroitic_Cursive) $(TD Tamil)) - $(TR $(TD Georgian) $(TD Meroitic_Hieroglyphs) $(TD Telugu)) - $(TR $(TD Glagolitic) $(TD Miao) $(TD Thaana)) - $(TR $(TD Gothic) $(TD Mongolian) $(TD Thai)) - $(TR $(TD Greek) $(TD Myanmar) $(TD Tibetan)) - $(TR $(TD Gujarati) $(TD New_Tai_Lue) $(TD Tifinagh)) - $(TR $(TD Gurmukhi) $(TD Nko) $(TD Ugaritic)) - $(TR $(TD Han) $(TD Ogham) $(TD Vai)) - $(TR $(TD Hangul) $(TD Ol_Chiki) $(TD Yi)) + $(P Below is the table with script names accepted by $(LREF unicode.script) + and by the shorthand version $(LREF unicode):) + $(BOOKTABLE $(B Scripts), + $(TR $(TD Arabic) $(TD Hanunoo) $(TD Old_Italic)) + $(TR $(TD Armenian) $(TD Hebrew) $(TD Old_Persian)) + $(TR $(TD Avestan) $(TD Hiragana) $(TD Old_South_Arabian)) + $(TR $(TD Balinese) $(TD Imperial_Aramaic) $(TD Old_Turkic)) + $(TR $(TD Bamum) $(TD Inherited) $(TD Oriya)) + $(TR $(TD Batak) $(TD Inscriptional_Pahlavi) $(TD Osmanya)) + $(TR $(TD Bengali) $(TD Inscriptional_Parthian) $(TD Phags_Pa)) + $(TR $(TD Bopomofo) $(TD Javanese) $(TD Phoenician)) + $(TR $(TD Brahmi) $(TD Kaithi) $(TD Rejang)) + $(TR $(TD Braille) $(TD Kannada) $(TD Runic)) + $(TR $(TD Buginese) $(TD Katakana) $(TD Samaritan)) + $(TR $(TD Buhid) $(TD Kayah_Li) $(TD Saurashtra)) + $(TR $(TD Canadian_Aboriginal) $(TD Kharoshthi) $(TD Sharada)) + $(TR $(TD Carian) $(TD Khmer) $(TD Shavian)) + $(TR $(TD Chakma) $(TD Lao) $(TD Sinhala)) + $(TR $(TD Cham) $(TD Latin) $(TD Sora_Sompeng)) + $(TR $(TD Cherokee) $(TD Lepcha) $(TD Sundanese)) + $(TR $(TD Common) $(TD Limbu) $(TD Syloti_Nagri)) + $(TR $(TD Coptic) $(TD Linear_B) $(TD Syriac)) + $(TR $(TD Cuneiform) $(TD Lisu) $(TD Tagalog)) + $(TR $(TD Cypriot) $(TD Lycian) $(TD Tagbanwa)) + $(TR $(TD Cyrillic) $(TD Lydian) $(TD Tai_Le)) + $(TR $(TD Deseret) $(TD Malayalam) $(TD Tai_Tham)) + $(TR $(TD Devanagari) $(TD Mandaic) $(TD Tai_Viet)) + $(TR $(TD Egyptian_Hieroglyphs) $(TD Meetei_Mayek) $(TD Takri)) + $(TR $(TD Ethiopic) $(TD Meroitic_Cursive) $(TD Tamil)) + $(TR $(TD Georgian) $(TD Meroitic_Hieroglyphs) $(TD Telugu)) + $(TR $(TD Glagolitic) $(TD Miao) $(TD Thaana)) + $(TR $(TD Gothic) $(TD Mongolian) $(TD Thai)) + $(TR $(TD Greek) $(TD Myanmar) $(TD Tibetan)) + $(TR $(TD Gujarati) $(TD New_Tai_Lue) $(TD Tifinagh)) + $(TR $(TD Gurmukhi) $(TD Nko) $(TD Ugaritic)) + $(TR $(TD Han) $(TD Ogham) $(TD Vai)) + $(TR $(TD Hangul) $(TD Ol_Chiki) $(TD Yi)) ) - - $(P Bellow is the table of names accepted by $(LREF unicode.hangulSyllableType).) - $(BOOKTABLE $(B Hangul syllable type), - $(TR $(TH Abb.) $(TH Long form)) - $(TR $(TD L) $(TD Leading_Jamo)) - $(TR $(TD LV) $(TD LV_Syllable)) - $(TR $(TD LVT) $(TD LVT_Syllable) ) - $(TR $(TD T) $(TD Trailing_Jamo)) - $(TR $(TD V) $(TD Vowel_Jamo)) + $(P Below is the table of names accepted by $(LREF unicode.hangulSyllableType).) + $(BOOKTABLE $(B Hangul syllable type), + $(TR $(TH Abb.) $(TH Long form)) + $(TR $(TD L) $(TD Leading_Jamo)) + $(TR $(TD LV) $(TD LV_Syllable)) + $(TR $(TD LVT) $(TD LVT_Syllable) ) + $(TR $(TD T) $(TD Trailing_Jamo)) + $(TR $(TD V) $(TD Vowel_Jamo)) ) References: - $(WEB www.digitalmars.com/d/ascii-table.html, ASCII Table), - $(WEB en.wikipedia.org/wiki/Unicode, Wikipedia), - $(WEB www.unicode.org, The Unicode Consortium), - $(WEB www.unicode.org/reports/tr15/, Unicode normalization forms), - $(WEB www.unicode.org/reports/tr29/, Unicode text segmentation) - $(WEB www.unicode.org/uni2book/ch05.pdf, + $(HTTP www.digitalmars.com/d/ascii-table.html, ASCII Table), + $(HTTP en.wikipedia.org/wiki/Unicode, Wikipedia), + $(HTTP www.unicode.org, The Unicode Consortium), + $(HTTP www.unicode.org/reports/tr15/, Unicode normalization forms), + $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation) + $(HTTP www.unicode.org/uni2book/ch05.pdf, Unicode Implementation Guidelines) - $(WEB www.unicode.org/uni2book/ch03.pdf, + $(HTTP www.unicode.org/uni2book/ch03.pdf, Unicode Conformance) Trademarks: Unicode(tm) is a trademark of Unicode, Inc. - Macros: - WIKI=Phobos/StdUni - Copyright: Copyright 2013 - - License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Dmitry Olshansky Source: $(PHOBOSSRC std/_uni.d) - Standards: $(WEB www.unicode.org/versions/Unicode6.2.0/, Unicode v6.2) + Standards: $(HTTP www.unicode.org/versions/Unicode6.2.0/, Unicode v6.2) Macros: @@ -648,34 +707,58 @@ CLUSTER = $(S_LINK Grapheme cluster, grapheme cluster) +/ module std.uni; -import core.stdc.stdlib; -import std.traits, std.typetuple; -import std.range.primitives; - +import std.meta; // AliasSeq +import std.range.primitives; // back, ElementEncodingType, ElementType, empty, + // front, isForwardRange, isInputRange, isRandomAccessRange, popFront, put, + // save +import std.traits; // isConvertibleToString, isIntegral, isSomeChar, + // isSomeString, Unqual // debug = std_uni; -debug(std_uni) import std.stdio; +debug(std_uni) import std.stdio; // writefln, writeln private: -version(std_uni_bootstrap){} -else +version (unittest) { - import std.internal.unicode_tables; // generated file +private: + struct TestAliasedString + { + string get() @safe @nogc pure nothrow { return _s; } + alias get this; + @disable this(this); + string _s; + } + + bool testAliasedString(alias func, Args...)(string s, Args args) + { + import std.algorithm.comparison : equal; + auto a = func(TestAliasedString(s), args); + auto b = func(s, args); + static if (is(typeof(equal(a, b)))) + { + // For ranges, compare contents instead of object identity. + return equal(a, b); + } + else + { + return a == b; + } + } } void copyBackwards(T,U)(T[] src, U[] dest) { assert(src.length == dest.length); - for(size_t i=src.length; i-- > 0; ) + for (size_t i=src.length; i-- > 0; ) dest[i] = src[i]; } void copyForward(T,U)(T[] src, U[] dest) { assert(src.length == dest.length); - for(size_t i=0; i<src.length; i++) + for (size_t i=0; i<src.length; i++) dest[i] = src[i]; } @@ -692,9 +775,9 @@ public enum dchar paraSep = '\u2029'; /// Constant $(CODEPOINT) (0x2029) - parag public enum dchar nelSep = '\u0085'; /// Constant $(CODEPOINT) (0x0085) - next line. // test the intro example -unittest +@safe unittest { - import std.algorithm : find; + import std.algorithm.searching : find; // initialize code point sets using script/block or property name // set contains code points from both scripts. auto set = unicode("Cyrillic") | unicode("Armenian"); @@ -753,21 +836,21 @@ unittest enum lastDchar = 0x10FFFF; auto force(T, F)(F from) - if(isIntegral!T && !is(T == F)) +if (isIntegral!T && !is(T == F)) { assert(from <= T.max && from >= T.min); - return cast(T)from; + return cast(T) from; } auto force(T, F)(F from) - if(isBitPacked!T && !is(T == F)) +if (isBitPacked!T && !is(T == F)) { assert(from <= 2^^bitSizeOf!T-1); - return T(cast(TypeOfBitPacked!T)from); + return T(cast(TypeOfBitPacked!T) from); } auto force(T, F)(F from) - if(is(T == F)) +if (is(T == F)) { return from; } @@ -775,28 +858,28 @@ auto force(T, F)(F from) // repeat X times the bit-pattern in val assuming it's length is 'bits' size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @nogc { - static if(times == 1) + static if (times == 1) return val; - else static if(bits == 1) + else static if (bits == 1) { - static if(times == size_t.sizeof*8) + static if (times == size_t.sizeof*8) return val ? size_t.max : 0; else - return val ? (1<<times)-1 : 0; + return val ? (1 << times)-1 : 0; } - else static if(times % 2) + else static if (times % 2) return (replicateBits!(times-1, bits)(val)<<bits) | val; else - return replicateBits!(times/2, bits*2)((val<<bits) | val); + return replicateBits!(times/2, bits*2)((val << bits) | val); } @safe pure nothrow @nogc unittest // for replicate { - import std.algorithm : sum, map; + import std.algorithm.iteration : sum, map; import std.range : iota; size_t m = 0b111; size_t m2 = 0b01; - foreach(i; TypeTuple!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + foreach (i; AliasSeq!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) { assert(replicateBits!(i, 3)(m)+1 == (1<<(3*i))); assert(replicateBits!(i, 2)(m2) == iota(0, i).map!"2^^(2*a)"().sum()); @@ -806,15 +889,16 @@ size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @ // multiple arrays squashed into one memory block struct MultiArray(Types...) { + import std.range.primitives : isOutputRange; this(size_t[] sizes...) @safe pure nothrow { assert(dim == sizes.length); size_t full_size; - foreach(i, v; Types) + foreach (i, v; Types) { full_size += spaceFor!(bitSizeOf!v)(sizes[i]); sz[i] = sizes[i]; - static if(i >= 1) + static if (i >= 1) offsets[i] = offsets[i-1] + spaceFor!(bitSizeOf!(Types[i-1]))(sizes[i-1]); } @@ -830,13 +914,13 @@ struct MultiArray(Types...) storage = data; } - @property auto slice(size_t n)()inout pure nothrow + @property auto slice(size_t n)()inout pure nothrow @nogc { auto ptr = raw_ptr!n; return packedArrayView!(Types[n])(ptr, sz[n]); } - @property auto ptr(size_t n)()inout pure nothrow + @property auto ptr(size_t n)()inout pure nothrow @nogc { auto ptr = raw_ptr!n; return inout(PackedPtr!(Types[n]))(ptr); @@ -844,11 +928,11 @@ struct MultiArray(Types...) template length(size_t n) { - @property size_t length()const{ return sz[n]; } + @property size_t length()const @safe pure nothrow @nogc{ return sz[n]; } @property void length(size_t new_size) { - if(new_size > sz[n]) + if (new_size > sz[n]) {// extend size_t delta = (new_size - sz[n]); sz[n] += delta; @@ -856,34 +940,34 @@ struct MultiArray(Types...) storage.length += delta;// extend space at end // raw_slice!x must follow resize as it could be moved! // next stmts move all data past this array, last-one-goes-first - static if(n != dim-1) + static if (n != dim-1) { auto start = raw_ptr!(n+1); // len includes delta size_t len = (storage.ptr+storage.length-start); - copyBackwards(start[0..len-delta], start[delta..len]); + copyBackwards(start[0 .. len-delta], start[delta .. len]); - start[0..delta] = 0; + start[0 .. delta] = 0; // offsets are used for raw_slice, ptr etc. - foreach(i; n+1..dim) + foreach (i; n+1 .. dim) offsets[i] += delta; } } - else if(new_size < sz[n]) + else if (new_size < sz[n]) {// shrink size_t delta = (sz[n] - new_size); sz[n] -= delta; delta = spaceFor!(bitSizeOf!(Types[n]))(delta); // move all data past this array, forward direction - static if(n != dim-1) + static if (n != dim-1) { auto start = raw_ptr!(n+1); size_t len = (storage.ptr+storage.length-start); - copyForward(start[0..len-delta], start[delta..len]); + copyForward(start[0 .. len-delta], start[delta .. len]); // adjust offsets last, they affect raw_slice - foreach(i; n+1..dim) + foreach (i; n+1 .. dim) offsets[i] -= delta; } storage.length -= delta; @@ -892,29 +976,30 @@ struct MultiArray(Types...) } } - @property size_t bytes(size_t n=size_t.max)() const + @property size_t bytes(size_t n=size_t.max)() const @safe { - static if(n == size_t.max) + static if (n == size_t.max) return storage.length*size_t.sizeof; - else static if(n != Types.length-1) + else static if (n != Types.length-1) return (raw_ptr!(n+1)-raw_ptr!n)*size_t.sizeof; else return (storage.ptr+storage.length - raw_ptr!n)*size_t.sizeof; } void store(OutRange)(scope OutRange sink) const - if(isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { - import std.format; + import std.format : formattedWrite; formattedWrite(sink, "[%( 0x%x, %)]", offsets[]); formattedWrite(sink, ", [%( 0x%x, %)]", sz[]); formattedWrite(sink, ", [%( 0x%x, %)]", storage); } private: - @property auto raw_ptr(size_t n)()inout + import std.meta : staticMap; + @property auto raw_ptr(size_t n)()inout pure nothrow @nogc { - static if(n == 0) + static if (n == 0) return storage.ptr; else { @@ -928,9 +1013,9 @@ private: size_t[] storage; } -unittest +@system unittest { - import std.conv; + import std.conv : text; enum dg = (){ // sizes are: // lvl0: 3, lvl1 : 2, lvl2: 1 @@ -938,25 +1023,25 @@ unittest static void check(size_t k, T)(ref T m, int n) { - foreach(i; 0..n) - assert(m.slice!(k)[i] == i+1, text("level:",i," : ",m.slice!(k)[0..n])); + foreach (i; 0 .. n) + assert(m.slice!(k)[i] == i+1, text("level:",i," : ",m.slice!(k)[0 .. n])); } static void checkB(size_t k, T)(ref T m, int n) { - foreach(i; 0..n) - assert(m.slice!(k)[i] == n-i, text("level:",i," : ",m.slice!(k)[0..n])); + foreach (i; 0 .. n) + assert(m.slice!(k)[i] == n-i, text("level:",i," : ",m.slice!(k)[0 .. n])); } static void fill(size_t k, T)(ref T m, int n) { - foreach(i; 0..n) + foreach (i; 0 .. n) m.slice!(k)[i] = force!ubyte(i+1); } static void fillB(size_t k, T)(ref T m, int n) { - foreach(i; 0..n) + foreach (i; 0 .. n) m.slice!(k)[i] = force!ubyte(n-i); } @@ -1001,9 +1086,9 @@ unittest auto rt = dg(); } -unittest +@system unittest {// more bitpacking tests - import std.conv; + import std.conv : text; alias Bitty = MultiArray!(BitPacked!(size_t, 3) @@ -1016,12 +1101,12 @@ unittest alias fn3 = sliceBits!( 6, 9); alias fn4 = sliceBits!( 0, 6); static void check(size_t lvl, MA)(ref MA arr){ - for(size_t i = 0; i< arr.length!lvl; i++) + for (size_t i = 0; i< arr.length!lvl; i++) assert(arr.slice!(lvl)[i] == i, text("Mismatch on lvl ", lvl, " idx ", i, " value: ", arr.slice!(lvl)[i])); } static void fillIdx(size_t lvl, MA)(ref MA arr){ - for(size_t i = 0; i< arr.length!lvl; i++) + for (size_t i = 0; i< arr.length!lvl; i++) arr.slice!(lvl)[i] = i; } Bitty m1; @@ -1034,7 +1119,7 @@ unittest m1.length!4 = 2^^16; - for(size_t i = 0; i< m1.length!4; i++) + for (size_t i = 0; i< m1.length!4; i++) m1.slice!(4)[i] = i % 2; fillIdx!1(m1); @@ -1048,7 +1133,7 @@ unittest check!3(m1); check!2(m1); check!1(m1); - for(size_t i=0; i < 2^^16; i++) + for (size_t i=0; i < 2^^16; i++) { m1.slice!(4)[i] = i % 2; m1.slice!(0)[fn1(i)] = fn1(i); @@ -1056,7 +1141,7 @@ unittest m1.slice!(2)[fn3(i)] = fn3(i); m1.slice!(3)[fn4(i)] = fn4(i); } - for(size_t i=0; i < 2^^16; i++) + for (size_t i=0; i < 2^^16; i++) { assert(m1.slice!(4)[i] == i % 2); assert(m1.slice!(0)[fn1(i)] == fn1(i)); @@ -1068,8 +1153,9 @@ unittest size_t spaceFor(size_t _bits)(size_t new_len) @safe pure nothrow @nogc { - enum bits = _bits == 1 ? 1 : ceilPowerOf2(_bits);// see PackedArrayView - static if(bits > 8*size_t.sizeof) + import std.math : nextPow2; + enum bits = _bits == 1 ? 1 : nextPow2(_bits - 1);// see PackedArrayView + static if (bits > 8*size_t.sizeof) { static assert(bits % (size_t.sizeof*8) == 0); return new_len * bits/(8*size_t.sizeof); @@ -1089,89 +1175,91 @@ template isBitPackableType(T) //============================================================================ template PackedArrayView(T) - if((is(T dummy == BitPacked!(U, sz), U, size_t sz) - && isBitPackableType!U) || isBitPackableType!T) +if ((is(T dummy == BitPacked!(U, sz), U, size_t sz) + && isBitPackableType!U) || isBitPackableType!T) { + import std.math : nextPow2; private enum bits = bitSizeOf!T; - alias PackedArrayView = PackedArrayViewImpl!(T, bits > 1 ? ceilPowerOf2(bits) : 1); + alias PackedArrayView = PackedArrayViewImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1); } //unsafe and fast access to a chunk of RAM as if it contains packed values template PackedPtr(T) - if((is(T dummy == BitPacked!(U, sz), U, size_t sz) - && isBitPackableType!U) || isBitPackableType!T) +if ((is(T dummy == BitPacked!(U, sz), U, size_t sz) + && isBitPackableType!U) || isBitPackableType!T) { + import std.math : nextPow2; private enum bits = bitSizeOf!T; - alias PackedPtr = PackedPtrImpl!(T, bits > 1 ? ceilPowerOf2(bits) : 1); + alias PackedPtr = PackedPtrImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1); } -@trusted struct PackedPtrImpl(T, size_t bits) +struct PackedPtrImpl(T, size_t bits) { pure nothrow: - static assert(isPowerOf2(bits)); + static assert(isPow2OrZero(bits)); - this(inout(size_t)* ptr)inout + this(inout(size_t)* ptr)inout @safe @nogc { origin = ptr; } private T simpleIndex(size_t n) inout { - auto q = n / factor; - auto r = n % factor; + immutable q = n / factor; + immutable r = n % factor; return cast(T)((origin[q] >> bits*r) & mask); } private void simpleWrite(TypeOfBitPacked!T val, size_t n) in { - static if(isIntegral!T) + static if (isIntegral!T) assert(val <= mask); } body { - auto q = n / factor; - auto r = n % factor; - size_t tgt_shift = bits*r; - size_t word = origin[q]; - origin[q] = (word & ~(mask<<tgt_shift)) - | (cast(size_t)val << tgt_shift); + immutable q = n / factor; + immutable r = n % factor; + immutable tgt_shift = bits*r; + immutable word = origin[q]; + origin[q] = (word & ~(mask << tgt_shift)) + | (cast(size_t) val << tgt_shift); } - static if(factor == bytesPerWord// can safely pack by byte + static if (factor == bytesPerWord// can safely pack by byte || factor == 1 // a whole word at a time || ((factor == bytesPerWord/2 || factor == bytesPerWord/4) && hasUnalignedReads)) // this needs unaligned reads { - static if(factor == bytesPerWord) + static if (factor == bytesPerWord) alias U = ubyte; - else static if(factor == bytesPerWord/2) + else static if (factor == bytesPerWord/2) alias U = ushort; - else static if(factor == bytesPerWord/4) + else static if (factor == bytesPerWord/4) alias U = uint; - else static if(size_t.sizeof == 8 && factor == bytesPerWord/8) + else static if (size_t.sizeof == 8 && factor == bytesPerWord/8) alias U = ulong; T opIndex(size_t idx) inout { return __ctfe ? simpleIndex(idx) : - cast(inout(T))(cast(U*)origin)[idx]; + cast(inout(T))(cast(U*) origin)[idx]; } - static if(isBitPacked!T) // lack of user-defined implicit conversion + static if (isBitPacked!T) // lack of user-defined implicit conversion { void opIndexAssign(T val, size_t idx) { - return opIndexAssign(cast(TypeOfBitPacked!T)val, idx); + return opIndexAssign(cast(TypeOfBitPacked!T) val, idx); } } void opIndexAssign(TypeOfBitPacked!T val, size_t idx) { - if(__ctfe) + if (__ctfe) simpleWrite(val, idx); else - (cast(U*)origin)[idx] = cast(U)val; + (cast(U*) origin)[idx] = cast(U) val; } } else @@ -1181,11 +1269,11 @@ pure nothrow: return simpleIndex(n); } - static if(isBitPacked!T) // lack of user-defined implicit conversion + static if (isBitPacked!T) // lack of user-defined implicit conversion { void opIndexAssign(T val, size_t idx) { - return opIndexAssign(cast(TypeOfBitPacked!T)val, idx); + return opIndexAssign(cast(TypeOfBitPacked!T) val, idx); } } @@ -1205,11 +1293,11 @@ private: // data is packed only by power of two sized packs per word, // thus avoiding mul/div overhead at the cost of ultimate packing // this construct doesn't own memory, only provides access, see MultiArray for usage -@trusted struct PackedArrayViewImpl(T, size_t bits) +struct PackedArrayViewImpl(T, size_t bits) { pure nothrow: - this(inout(size_t)* origin, size_t offset, size_t items) inout + this(inout(size_t)* origin, size_t offset, size_t items) inout @safe { ptr = inout(PackedPtr!(T))(origin); ofs = offset; @@ -1225,25 +1313,25 @@ pure nothrow: { s += ofs; e += ofs; - size_t pad_s = roundUp(s); + immutable pad_s = roundUp(s); if ( s >= e) { - foreach (i; s..e) - if(ptr[i]) + foreach (i; s .. e) + if (ptr[i]) return false; return true; } - size_t pad_e = roundDown(e); + immutable pad_e = roundDown(e); size_t i; - for(i=s; i<pad_s; i++) - if(ptr[i]) + for (i=s; i<pad_s; i++) + if (ptr[i]) return false; // all in between is x*factor elements - for(size_t j=i/factor; i<pad_e; i+=factor, j++) - if(ptr.origin[j]) + for (size_t j=i/factor; i<pad_e; i+=factor, j++) + if (ptr.origin[j]) return false; - for(; i<e; i++) - if(ptr[i]) + for (; i<e; i++) + if (ptr[i]) return false; return true; } @@ -1258,11 +1346,11 @@ pure nothrow: return ptr[ofs + idx]; } - static if(isBitPacked!T) // lack of user-defined implicit conversion + static if (isBitPacked!T) // lack of user-defined implicit conversion { void opIndexAssign(T val, size_t idx) { - return opIndexAssign(cast(TypeOfBitPacked!T)val, idx); + return opIndexAssign(cast(TypeOfBitPacked!T) val, idx); } } @@ -1276,11 +1364,11 @@ pure nothrow: ptr[ofs + idx] = val; } - static if(isBitPacked!T) // lack of user-defined implicit conversions + static if (isBitPacked!T) // lack of user-defined implicit conversions { void opSliceAssign(T val, size_t start, size_t end) { - opSliceAssign(cast(TypeOfBitPacked!T)val, start, end); + opSliceAssign(cast(TypeOfBitPacked!T) val, start, end); } } @@ -1296,26 +1384,26 @@ pure nothrow: start += ofs; end += ofs; // rounded to factor granularity - size_t pad_start = roundUp(start);// rounded up - if(pad_start >= end) //rounded up >= then end of slice + immutable pad_start = roundUp(start);// rounded up + if (pad_start >= end) //rounded up >= then end of slice { //nothing to gain, use per element assignment - foreach(i; start..end) + foreach (i; start .. end) ptr[i] = val; return; } - size_t pad_end = roundDown(end); // rounded down + immutable pad_end = roundDown(end); // rounded down size_t i; - for(i=start; i<pad_start; i++) + for (i=start; i<pad_start; i++) ptr[i] = val; // all in between is x*factor elements - if(pad_start != pad_end) + if (pad_start != pad_end) { - size_t repval = replicateBits!(factor, bits)(val); - for(size_t j=i/factor; i<pad_end; i+=factor, j++) + immutable repval = replicateBits!(factor, bits)(val); + for (size_t j=i/factor; i<pad_end; i+=factor, j++) ptr.origin[j] = repval;// so speed it up by factor } - for(; i<end; i++) + for (; i<end; i++) ptr[i] = val; } @@ -1334,17 +1422,17 @@ pure nothrow: bool opEquals(T)(auto ref T arr) const { - if(limit != arr.limit) + if (limit != arr.limit) return false; size_t s1 = ofs, s2 = arr.ofs; size_t e1 = s1 + limit, e2 = s2 + limit; - if(s1 % factor == 0 && s2 % factor == 0 && length % factor == 0) + if (s1 % factor == 0 && s2 % factor == 0 && length % factor == 0) { return ptr.origin[s1/factor .. e1/factor] == arr.ptr.origin[s2/factor .. e2/factor]; } - for(size_t i=0;i<limit; i++) - if(this[i] != arr[i]) + for (size_t i=0;i<limit; i++) + if (this[i] != arr[i]) return false; return true; } @@ -1364,7 +1452,7 @@ private: private struct SliceOverIndexed(T) { enum assignableIndex = is(typeof((){ T.init[0] = Item.init; })); - enum assignableSlice = is(typeof((){ T.init[0..0] = Item.init; })); + enum assignableSlice = is(typeof((){ T.init[0 .. 0] = Item.init; })); auto opIndex(size_t idx)const in { @@ -1375,7 +1463,7 @@ private struct SliceOverIndexed(T) return (*arr)[from+idx]; } - static if(assignableIndex) + static if (assignableIndex) void opIndexAssign(Item val, size_t idx) in { @@ -1391,7 +1479,7 @@ private struct SliceOverIndexed(T) return typeof(this)(from+a, from+b, arr); } - // static if(assignableSlice) + // static if (assignableSlice) void opSliceAssign(T)(T val, size_t start, size_t end) { (*arr)[start+from .. end+from] = val; @@ -1410,12 +1498,12 @@ private struct SliceOverIndexed(T) @property auto front()const { return (*arr)[from]; } - static if(assignableIndex) + static if (assignableIndex) @property void front(Item val) { (*arr)[from] = val; } @property auto back()const { return (*arr)[to-1]; } - static if(assignableIndex) + static if (assignableIndex) @property void back(Item val) { (*arr)[to-1] = val; } @property auto save() inout { return this; } @@ -1426,10 +1514,10 @@ private struct SliceOverIndexed(T) bool opEquals(T)(auto ref T arr) const { - if(arr.length != length) + if (arr.length != length) return false; - for(size_t i=0; i <length; i++) - if(this[i] != arr[i]) + for (size_t i=0; i <length; i++) + if (this[i] != arr[i]) return false; return true; } @@ -1441,9 +1529,8 @@ private: static assert(isRandomAccessRange!(SliceOverIndexed!(int[]))); -// BUG? forward reference to return type of sliceOverIndexed!Grapheme SliceOverIndexed!(const(T)) sliceOverIndexed(T)(size_t a, size_t b, const(T)* x) - if(is(Unqual!T == T)) +if (is(Unqual!T == T)) { return SliceOverIndexed!(const(T))(a, b, x); } @@ -1451,12 +1538,12 @@ SliceOverIndexed!(const(T)) sliceOverIndexed(T)(size_t a, size_t b, const(T)* x) // BUG? inout is out of reach //...SliceOverIndexed.arr only parameters or stack based variables can be inout SliceOverIndexed!T sliceOverIndexed(T)(size_t a, size_t b, T* x) - if(is(Unqual!T == T)) +if (is(Unqual!T == T)) { return SliceOverIndexed!T(a, b, x); } -unittest +@system unittest { int[] idxArray = [2, 3, 5, 8, 13]; auto sliced = sliceOverIndexed(0, idxArray.length, &idxArray); @@ -1485,8 +1572,8 @@ unittest int[] other = [2, 5]; assert(sliced[] == sliceOverIndexed(1, 2, &other)); - sliceOverIndexed(0, 2, &idxArray)[0..2] = -1; - assert(idxArray[0..2] == [-1, -1]); + sliceOverIndexed(0, 2, &idxArray)[0 .. 2] = -1; + assert(idxArray[0 .. 2] == [-1, -1]); uint[] nullArr = null; auto nullSlice = sliceOverIndexed(0, 0, &idxArray); assert(nullSlice.empty); @@ -1502,23 +1589,23 @@ private auto packedArrayView(T)(inout(size_t)* ptr, size_t items) @trusted pure // Partially unrolled binary search using Shar's method //============================================================================ -string genUnrolledSwitchSearch(size_t size) +string genUnrolledSwitchSearch(size_t size) @safe pure nothrow { - import std.conv : to; import core.bitop : bsr; import std.array : replace; - assert(isPowerOf2(size)); + import std.conv : to; + assert(isPow2OrZero(size)); string code = ` import core.bitop : bsr; auto power = bsr(m)+1; - switch(power){`; + switch (power){`; size_t i = bsr(size); - foreach_reverse(val; 0..bsr(size)) + foreach_reverse (val; 0 .. bsr(size)) { auto v = 2^^val; code ~= ` case pow: - if(pred(range[idx+m], needle)) + if (pred(range[idx+m], needle)) idx += m; goto case; `.replace("m", to!string(v)) @@ -1527,7 +1614,7 @@ string genUnrolledSwitchSearch(size_t size) } code ~= ` case 0: - if(pred(range[idx], needle)) + if (pred(range[idx], needle)) idx += 1; goto default; `; @@ -1537,36 +1624,37 @@ string genUnrolledSwitchSearch(size_t size) return code; } -bool isPowerOf2(size_t sz) @safe pure nothrow +bool isPow2OrZero(size_t sz) @safe pure nothrow @nogc { + // See also: std.math.isPowerOf2() return (sz & (sz-1)) == 0; } size_t uniformLowerBound(alias pred, Range, T)(Range range, T needle) - if(is(T : ElementType!Range)) +if (is(T : ElementType!Range)) { - assert(isPowerOf2(range.length)); + assert(isPow2OrZero(range.length)); size_t idx = 0, m = range.length/2; - while(m != 0) + while (m != 0) { - if(pred(range[idx+m], needle)) + if (pred(range[idx+m], needle)) idx += m; m /= 2; } - if(pred(range[idx], needle)) + if (pred(range[idx], needle)) idx += 1; return idx; } size_t switchUniformLowerBound(alias pred, Range, T)(Range range, T needle) - if(is(T : ElementType!Range)) +if (is(T : ElementType!Range)) { - assert(isPowerOf2(range.length)); + assert(isPow2OrZero(range.length)); size_t idx = 0, m = range.length/2; - enum max = 1<<10; - while(m >= max) + enum max = 1 << 10; + while (m >= max) { - if(pred(range[idx+m], needle)) + if (pred(range[idx+m], needle)) idx += m; m /= 2; } @@ -1574,49 +1662,37 @@ size_t switchUniformLowerBound(alias pred, Range, T)(Range range, T needle) return idx; } -// -size_t floorPowerOf2(size_t arg) @safe pure nothrow @nogc -{ - import core.bitop : bsr; - assert(arg > 1); // else bsr is undefined - return 1<<bsr(arg-1); -} - -size_t ceilPowerOf2(size_t arg) @safe pure nothrow @nogc -{ - import core.bitop : bsr; - assert(arg > 1); // else bsr is undefined - return 1<<bsr(arg-1)+1; -} - template sharMethod(alias uniLowerBound) { size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle) - if(is(T : ElementType!Range)) + if (is(T : ElementType!Range)) { - import std.functional; + import std.functional : binaryFun; + import std.math : nextPow2, truncPow2; alias pred = binaryFun!_pred; - if(range.length == 0) + if (range.length == 0) return 0; - if(isPowerOf2(range.length)) + if (isPow2OrZero(range.length)) return uniLowerBound!pred(range, needle); - size_t n = floorPowerOf2(range.length); - if(pred(range[n-1], needle)) + size_t n = truncPow2(range.length); + if (pred(range[n-1], needle)) {// search in another 2^^k area that fully covers the tail of range - size_t k = ceilPowerOf2(range.length - n + 1); + size_t k = nextPow2(range.length - n + 1); return range.length - k + uniLowerBound!pred(range[$-k..$], needle); } else - return uniLowerBound!pred(range[0..n], needle); + return uniLowerBound!pred(range[0 .. n], needle); } } alias sharLowerBound = sharMethod!uniformLowerBound; alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; -unittest +@safe unittest { - import std.range; + import std.array : array; + import std.range : assumeSorted, iota; + auto stdLowerBound(T)(T[] range, T needle) { return assumeSorted(range).lowerBound(needle).length; @@ -1624,7 +1700,7 @@ unittest immutable MAX = 5*1173; auto arr = array(iota(5, MAX, 5)); assert(arr.length == MAX/5-1); - foreach(i; 0..MAX+5) + foreach (i; 0 .. MAX+5) { auto st = stdLowerBound(arr, i); assert(st == sharLowerBound(arr, i)); @@ -1645,31 +1721,31 @@ unittest @trusted size_t genericReplace(Policy=void, T, Range) (ref T dest, size_t from, size_t to, Range stuff) { - import std.algorithm : copy; + import std.algorithm.mutation : copy; size_t delta = to - from; size_t stuff_end = from+stuff.length; - if(stuff.length > delta) + if (stuff.length > delta) {// replace increases length delta = stuff.length - delta;// now, new is > old by delta - static if(is(Policy == void)) + static if (is(Policy == void)) dest.length = dest.length+delta;//@@@BUG lame @property else dest = Policy.realloc(dest, dest.length+delta); - copyBackwards(dest[to..dest.length-delta], - dest[to+delta..dest.length]); - copyForward(stuff, dest[from..stuff_end]); + copyBackwards(dest[to .. dest.length-delta], + dest[to+delta .. dest.length]); + copyForward(stuff, dest[from .. stuff_end]); } - else if(stuff.length == delta) + else if (stuff.length == delta) { - copy(stuff, dest[from..to]); + copy(stuff, dest[from .. to]); } else {// replace decreases length by delta delta = delta - stuff.length; - copy(stuff, dest[from..stuff_end]); - copyForward(dest[to..dest.length], - dest[stuff_end..dest.length-delta]); - static if(is(Policy == void)) + copy(stuff, dest[from .. stuff_end]); + copyForward(dest[to .. dest.length], + dest[stuff_end .. dest.length-delta]); + static if (is(Policy == void)) dest.length = dest.length - delta;//@@@BUG lame @property else dest = Policy.realloc(dest, dest.length-delta); @@ -1679,8 +1755,10 @@ unittest // Simple storage manipulation policy -@trusted public struct GcPolicy +@trusted private struct GcPolicy { + import std.traits : isDynamicArray; + static T[] dup(T)(const T[] arr) { return arr.dup; @@ -1703,19 +1781,19 @@ unittest } static void append(T, V)(ref T[] arr, V value) - if(!isInputRange!V) + if (!isInputRange!V) { arr ~= force!T(value); } static void append(T, V)(ref T[] arr, V value) - if(isInputRange!V) + if (isInputRange!V) { insertInPlace(arr, arr.length, value); } static void destroy(T)(ref T arr) - if(isDynamicArray!T && is(Unqual!T == T)) + if (isDynamicArray!T && is(Unqual!T == T)) { debug { @@ -1725,7 +1803,7 @@ unittest } static void destroy(T)(ref T arr) - if(isDynamicArray!T && !is(Unqual!T == T)) + if (isDynamicArray!T && !is(Unqual!T == T)) { arr = null; } @@ -1734,6 +1812,8 @@ unittest // ditto @trusted struct ReallocPolicy { + import std.range.primitives : hasLength; + static T[] dup(T)(const T[] arr) { auto result = alloc!T(arr.length); @@ -1743,22 +1823,35 @@ unittest static T[] alloc(T)(size_t size) { + import core.stdc.stdlib : malloc; import std.exception : enforce; - auto ptr = cast(T*)enforce(malloc(T.sizeof*size), "out of memory on C heap"); - return ptr[0..size]; + + import core.checkedint : mulu; + bool overflow; + size_t nbytes = mulu(size, T.sizeof, overflow); + if (overflow) assert(0); + + auto ptr = cast(T*) enforce(malloc(nbytes), "out of memory on C heap"); + return ptr[0 .. size]; } static T[] realloc(T)(T[] arr, size_t size) { + import core.stdc.stdlib : realloc; import std.exception : enforce; - if(!size) + if (!size) { destroy(arr); return null; } - auto ptr = cast(T*)enforce(core.stdc.stdlib.realloc( - arr.ptr, T.sizeof*size), "out of memory on C heap"); - return ptr[0..size]; + + import core.checkedint : mulu; + bool overflow; + size_t nbytes = mulu(size, T.sizeof, overflow); + if (overflow) assert(0); + + auto ptr = cast(T*) enforce(realloc(arr.ptr, nbytes), "out of memory on C heap"); + return ptr[0 .. size]; } static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff) @@ -1767,22 +1860,49 @@ unittest } static void append(T, V)(ref T[] arr, V value) - if(!isInputRange!V) + if (!isInputRange!V) { + if (arr.length == size_t.max) assert(0); arr = realloc(arr, arr.length+1); arr[$-1] = force!T(value); } + @safe unittest + { + int[] arr; + ReallocPolicy.append(arr, 3); + + import std.algorithm.comparison : equal; + assert(equal(arr, [3])); + } + static void append(T, V)(ref T[] arr, V value) - if(isInputRange!V && hasLength!V) + if (isInputRange!V && hasLength!V) { - arr = realloc(arr, arr.length+value.length); + import core.checkedint : addu; + bool overflow; + size_t nelems = addu(arr.length, value.length, overflow); + if (overflow) assert(0); + + arr = realloc(arr, nelems); + + import std.algorithm.mutation : copy; copy(value, arr[$-value.length..$]); } + @safe unittest + { + int[] arr; + ReallocPolicy.append(arr, [1,2,3]); + + import std.algorithm.comparison : equal; + assert(equal(arr, [1,2,3])); + } + static void destroy(T)(ref T[] arr) { - if(arr.ptr) + import core.stdc.stdlib : free; + if (arr.ptr) free(arr.ptr); arr = null; } @@ -1791,8 +1911,10 @@ unittest //build hack alias _RealArray = CowArray!ReallocPolicy; -unittest +@safe unittest { + import std.algorithm.comparison : equal; + with(ReallocPolicy) { bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result, @@ -1801,7 +1923,7 @@ unittest { replaceImpl(orig, from, to, toReplace); scope(exit) destroy(orig); - if(!equalS(orig, result)) + if (!equal(orig, result)) return false; } return true; @@ -1824,7 +1946,7 @@ unittest */ public template isCodepointSet(T) { - static if(is(T dummy == InversionList!(Args), Args...)) + static if (is(T dummy == InversionList!(Args), Args...)) enum isCodepointSet = true; else enum isCodepointSet = false; @@ -1861,7 +1983,7 @@ public alias CodepointSet = InversionList!GcPolicy; // public alias CodepointInterval = Tuple!(uint, "a", uint, "b"); /** - The recommended type of $(XREF _typecons, Tuple) + The recommended type of $(REF Tuple, std,_typecons) to represent [a, b$(RPAREN) intervals of $(CODEPOINTS). As used in $(LREF InversionList). Any interval type should pass $(LREF isIntegralPair) trait. */ @@ -1886,23 +2008,6 @@ pure: @property ref inout(uint) b() inout { return _tuple[1]; } } -//@@@BUG another forward reference workaround -@trusted bool equalS(R1, R2)(R1 lhs, R2 rhs) -{ - for(;;){ - if(lhs.empty) - return rhs.empty; - if(rhs.empty) - return false; - if(lhs.front != rhs.front) - return false; - lhs.popFront(); - rhs.popFront(); - } -} - - - /** $(P $(D InversionList) is a set of $(CODEPOINTS) @@ -1926,7 +2031,7 @@ pure: As seen this provides a space-efficient storage of highly redundant data that comes in long runs. A description which Unicode $(CHARACTER) properties fit nicely. The technique itself could be seen as a variation - on $(LUCKY RLE encoding). + on $(LINK2 https://en.wikipedia.org/wiki/Run-length_encoding, RLE encoding). ) $(P Sets are value types (just like $(D int) is) thus they @@ -1947,7 +2052,7 @@ pure: $(P Memory usage is 8 bytes per each contiguous interval in a set. The value semantics are achieved by using the - $(WEB en.wikipedia.org/wiki/Copy-on-write, COW) technique + $(HTTP en.wikipedia.org/wiki/Copy-on-write, COW) technique and thus it's $(RED not) safe to cast this type to $(D_KEYWORD shared). ) @@ -1970,10 +2075,10 @@ public: Construct from another code point set of any type. */ this(Set)(Set set) pure - if(isCodepointSet!Set) + if (isCodepointSet!Set) { uint[] arr; - foreach(v; set.byInterval) + foreach (v; set.byInterval) { arr ~= v.a; arr ~= v.b; @@ -1985,10 +2090,10 @@ public: Construct a set from a forward range of code point intervals. */ this(Range)(Range intervals) pure - if(isForwardRange!Range && isIntegralPair!(ElementType!Range)) + if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) { uint[] arr; - foreach(v; intervals) + foreach (v; intervals) { SP.append(arr, v.a); SP.append(arr, v.b); @@ -2000,7 +2105,7 @@ public: //helper function that avoids sanity check to be CTFE-friendly private static fromIntervals(Range)(Range intervals) pure { - import std.algorithm : map; + import std.algorithm.iteration : map; import std.range : roundRobin; auto flattened = roundRobin(intervals.save.map!"a[0]"(), intervals.save.map!"a[1]"()); @@ -2029,20 +2134,6 @@ public: /** Construct a set from plain values of code point intervals. - Example: - --- - import std.algorithm; - auto set = CodepointSet('a', 'z'+1, 'а', 'Ñ'+1); - foreach(v; 'a'..'z'+1) - assert(set[v]); - // Cyrillic lowercase interval - foreach(v; 'а'..'Ñ'+1) - assert(set[v]); - //specific order is not required, intervals may interesect - auto set2 = CodepointSet('а', 'Ñ'+1, 'a', 'd', 'b', 'z'+1); - //the same end result - assert(set2.byInterval.equal(set.byInterval)); - --- */ this()(uint[] intervals...) in @@ -2061,15 +2152,35 @@ public: sanitize(); //enforce invariant: sort intervals etc. } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + + auto set = CodepointSet('a', 'z'+1, 'а', 'Ñ'+1); + foreach (v; 'a'..'z'+1) + assert(set[v]); + // Cyrillic lowercase interval + foreach (v; 'а'..'Ñ'+1) + assert(set[v]); + //specific order is not required, intervals may interesect + auto set2 = CodepointSet('а', 'Ñ'+1, 'a', 'd', 'b', 'z'+1); + //the same end result + assert(set2.byInterval.equal(set.byInterval)); + } + /** Get range that spans all of the $(CODEPOINT) intervals in this $(LREF InversionList). Example: - --- - import std.algorithm, std.typecons; + ----------- + import std.algorithm.comparison : equal; + import std.typecons : tuple; + auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1); - set.byInterval.equal([tuple('A', 'E'), tuple('a', 'e')]); - --- + + assert(set.byInterval.equal([tuple('A','E'), tuple('a','e')])); + ----------- */ @property auto byInterval() { @@ -2078,23 +2189,25 @@ public: /** Tests the presence of code point $(D val) in this set. + */ + bool opIndex(uint val) const + { + // the <= ensures that searching in interval of [a, b) for 'a' you get .length == 1 + // return assumeSorted!((a,b) => a <= b)(data[]).lowerBound(val).length & 1; + return sharSwitchLowerBound!"a <= b"(data[], val) & 1; + } - Example: - --- + /// + @safe unittest + { auto gothic = unicode.Gothic; // Gothic letter ahsa assert(gothic['\U00010330']); // no ascii in Gothic obviously assert(!gothic['$']); - --- - */ - bool opIndex(uint val) const - { - // the <= ensures that searching in interval of [a, b) for 'a' you get .length == 1 - // return assumeSorted!((a,b) => a<=b)(data[]).lowerBound(val).length & 1; - return sharSwitchLowerBound!"a<=b"(data[], val) & 1; } + // Linear scan for $(D ch). Useful only for small sets. // TODO: // used internally in std.regex @@ -2102,8 +2215,8 @@ public: package auto scanFor()(dchar ch) const { immutable len = data.length; - for(size_t i = 0; i < len; i++) - if(ch < data[i]) + for (size_t i = 0; i < len; i++) + if (ch < data[i]) return i & 1; return 0; } @@ -2112,7 +2225,7 @@ public: @property size_t length() { size_t sum = 0; - foreach(iv; byInterval) + foreach (iv; byInterval) { sum += iv.b - iv.a; } @@ -2132,36 +2245,13 @@ public: $(TR $(TD -) $(TD a ∖ b) $(TD subtraction) ) $(TR $(TD ~) $(TD a ~ b) $(TD symmetric set difference i.e. (a ∪ b) \ (a ∩ b)) ) ) - - Example: - --- - auto lower = unicode.LowerCase; - auto upper = unicode.UpperCase; - auto ascii = unicode.ASCII; - - assert((lower & upper).empty); // no intersection - auto lowerASCII = lower & ascii; - assert(lowerASCII.byCodepoint.equal(iota('a', 'z'+1))); - // throw away all of the lowercase ASCII - assert((ascii - lower).length == 128 - 26); - - auto onlyOneOf = lower ~ ascii; - assert(!onlyOneOf['Δ']); // not ASCII and not lowercase - assert(onlyOneOf['$']); // ASCII and not lowercase - assert(!onlyOneOf['a']); // ASCII and lowercase - assert(onlyOneOf['Ñ']); // not ASCII but lowercase - - // throw away all cased letters from ASCII - auto noLetters = ascii - (lower | upper); - assert(noLetters.length == 128 - 26*2); - --- */ This opBinary(string op, U)(U rhs) - if(isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { - static if(op == "&" || op == "|" || op == "~") + static if (op == "&" || op == "|" || op == "~") {// symmetric ops thus can swap arguments to reuse r-value - static if(is(U:dchar)) + static if (is(U:dchar)) { auto tmp = this; mixin("tmp "~op~"= rhs; "); @@ -2169,7 +2259,7 @@ public: } else { - static if(is(Unqual!U == U)) + static if (is(Unqual!U == U)) { // try hard to reuse r-value mixin("rhs "~op~"= this;"); @@ -2183,7 +2273,7 @@ public: } } } - else static if(op == "-") // anti-symmetric + else static if (op == "-") // anti-symmetric { auto tmp = this; tmp -= rhs; @@ -2193,13 +2283,40 @@ public: static assert(0, "no operator "~op~" defined for Set"); } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + import std.range : iota; + + auto lower = unicode.LowerCase; + auto upper = unicode.UpperCase; + auto ascii = unicode.ASCII; + + assert((lower & upper).empty); // no intersection + auto lowerASCII = lower & ascii; + assert(lowerASCII.byCodepoint.equal(iota('a', 'z'+1))); + // throw away all of the lowercase ASCII + assert((ascii - lower).length == 128 - 26); + + auto onlyOneOf = lower ~ ascii; + assert(!onlyOneOf['Δ']); // not ASCII and not lowercase + assert(onlyOneOf['$']); // ASCII and not lowercase + assert(!onlyOneOf['a']); // ASCII and lowercase + assert(onlyOneOf['Ñ']); // not ASCII but lowercase + + // throw away all cased letters from ASCII + auto noLetters = ascii - (lower | upper); + assert(noLetters.length == 128 - 26*2); + } + /// The 'op=' versions of the above overloaded operators. ref This opOpAssign(string op, U)(U rhs) - if(isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { - static if(op == "|") // union + static if (op == "|") // union { - static if(is(U:dchar)) + static if (is(U:dchar)) { this.addInterval(rhs, rhs+1); return this; @@ -2207,11 +2324,11 @@ public: else return this.add(rhs); } - else static if(op == "&") // intersection + else static if (op == "&") // intersection return this.intersect(rhs);// overloaded - else static if(op == "-") // set difference + else static if (op == "-") // set difference return this.sub(rhs);// overloaded - else static if(op == "~") // symmetric set difference + else static if (op == "~") // symmetric set difference { auto copy = this & rhs; this |= rhs; @@ -2227,13 +2344,13 @@ public: the same as $(LREF opIndex). */ bool opBinaryRight(string op: "in", U)(U ch) const - if(is(U : dchar)) + if (is(U : dchar)) { return this[ch]; } /// - unittest + @safe unittest { assert('Ñ' in unicode.Cyrillic); assert(!('z' in unicode.Cyrillic)); @@ -2253,13 +2370,6 @@ public: /** A range that spans each $(CODEPOINT) in this set. - - Example: - --- - import std.algorithm; - auto set = unicode.ASCII; - set.byCodepoint.equal(iota(0, 0x80)); - --- */ @property auto byCodepoint() { @@ -2268,13 +2378,13 @@ public: this(This set) { r = set.byInterval; - if(!r.empty) + if (!r.empty) cur = r.front.a; } @property dchar front() const { - return cast(dchar)cur; + return cast(dchar) cur; } @property bool empty() const @@ -2285,10 +2395,10 @@ public: void popFront() { cur++; - while(cur >= r.front.b) + while (cur >= r.front.b) { r.popFront(); - if(r.empty) + if (r.empty) break; cur = r.front.a; } @@ -2301,13 +2411,23 @@ public: return CodepointRange(this); } + /// + @safe unittest + { + import std.algorithm.comparison : equal; + import std.range : iota; + + auto set = unicode.ASCII; + set.byCodepoint.equal(iota(0, 0x80)); + } + /** $(P Obtain textual representation of this set in from of open-right intervals and feed it to $(D sink). ) $(P Used by various standard formatting facilities such as - $(XREF _format, formattedWrite), $(XREF _stdio, write), - $(XREF _stdio, writef), $(XREF _conv, to) and others. + $(REF formattedWrite, std,_format), $(REF write, std,_stdio), + $(REF writef, std,_stdio), $(REF to, std,_conv) and others. ) Example: --- @@ -2323,16 +2443,16 @@ public: * in form of open-right intervals. * * The formatting flag is applied individually to each value, for example: - * $(LI $(B %s) and $(B %d) format the intervals as a [low..high$(RPAREN) range of integrals) - * $(LI $(B %x) formats the intervals as a [low..high$(RPAREN) range of lowercase hex characters) - * $(LI $(B %X) formats the intervals as a [low..high$(RPAREN) range of uppercase hex characters) + * $(LI $(B %s) and $(B %d) format the intervals as a [low .. high$(RPAREN) range of integrals) + * $(LI $(B %x) formats the intervals as a [low .. high$(RPAREN) range of lowercase hex characters) + * $(LI $(B %X) formats the intervals as a [low .. high$(RPAREN) range of uppercase hex characters) */ - void toString(scope void delegate(const(char)[]) sink, + void toString(Writer)(scope Writer sink, FormatSpec!char fmt) /* const */ { - import std.format; + import std.format : formatValue; auto range = byInterval; - if(range.empty) + if (range.empty) return; while (1) @@ -2351,7 +2471,7 @@ public: } /// - unittest + @safe unittest { import std.conv : to; import std.format : format; @@ -2364,13 +2484,15 @@ public: assert(format("%d", unicode.Cyrillic) == unicode.Cyrillic.to!string); assert(format("%#x", unicode.Cyrillic) == - "[0x400..0x485) [0x487..0x528) [0x1d2b..0x1d2c) [0x1d78..0x1d79) [0x2de0..0x2e00) [0xa640..0xa698) [0xa69f..0xa6a0)"); + "[0x400..0x485) [0x487..0x528) [0x1d2b..0x1d2c) [0x1d78..0x1d79) [0x2de0..0x2e00) " + ~"[0xa640..0xa698) [0xa69f..0xa6a0)"); assert(format("%#X", unicode.Cyrillic) == - "[0X400..0X485) [0X487..0X528) [0X1D2B..0X1D2C) [0X1D78..0X1D79) [0X2DE0..0X2E00) [0XA640..0XA698) [0XA69F..0XA6A0)"); + "[0X400..0X485) [0X487..0X528) [0X1D2B..0X1D2C) [0X1D78..0X1D79) [0X2DE0..0X2E00) " + ~"[0XA640..0XA698) [0XA69F..0XA6A0)"); } - unittest + @safe unittest { import std.exception : assertThrown; import std.format : format; @@ -2380,9 +2502,16 @@ public: /** Add an interval [a, b$(RPAREN) to this set. - - Example: - --- + */ + ref add()(uint a, uint b) + { + addInterval(a, b); + return this; + } + + /// + @safe unittest + { CodepointSet someSet; someSet.add('0', '5').add('A','Z'+1); someSet.add('5', '9'+1); @@ -2390,21 +2519,16 @@ public: assert(someSet['5']); assert(someSet['9']); assert(someSet['Z']); - --- - */ - ref add()(uint a, uint b) - { - addInterval(a, b); - return this; } private: + package(std) // used from: std.regex.internal.parser ref intersect(U)(U rhs) - if(isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; - foreach( i; rhs.byInterval) + foreach ( i; rhs.byInterval) { mark = this.dropUpTo(i.a, mark); mark = this.skipUpTo(i.b, mark); @@ -2415,15 +2539,15 @@ private: ref intersect()(dchar ch) { - foreach(i; byInterval) - if(i.a <= ch && ch < i.b) + foreach (i; byInterval) + if (i.a <= ch && ch < i.b) return this = This.init.add(ch, ch+1); this = This.init; return this; } /// - unittest + @safe unittest { assert(unicode.Cyrillic.intersect('-').byInterval.empty); } @@ -2434,12 +2558,12 @@ private: } // same as the above except that skip & drop parts are swapped + package(std) // used from: std.regex.internal.parser ref sub(U)(U rhs) - if(isCodepointSet!U) + if (isCodepointSet!U) { - uint top; Marker mark; - foreach(i; rhs.byInterval) + foreach (i; rhs.byInterval) { mark = this.skipUpTo(i.a, mark); mark = this.dropUpTo(i.b, mark); @@ -2447,11 +2571,12 @@ private: return this; } + package(std) // used from: std.regex.internal.parse ref add(U)(U rhs) - if(isCodepointSet!U) + if (isCodepointSet!U) { Marker start; - foreach(i; rhs.byInterval) + foreach (i; rhs.byInterval) { start = addInterval(i.a, i.b, start); } @@ -2465,38 +2590,39 @@ public: Obtains a set that is the inversion of this set. See the '!' $(LREF opUnary) for the same but using operators. - - Example: - --- - set = unicode.ASCII; - // union with the inverse gets all of the code points in the Unicode - assert((set | set.inverted).length == 0x110000); - // no intersection with the inverse - assert((set & set.inverted).empty); - --- */ @property auto inverted() { InversionList inversion = this; - if(inversion.data.length == 0) + if (inversion.data.length == 0) { inversion.addInterval(0, lastDchar+1); return inversion; } - if(inversion.data[0] != 0) + if (inversion.data[0] != 0) genericReplace(inversion.data, 0, 0, [0]); else - genericReplace(inversion.data, 0, 1, cast(uint[])null); - if(data[data.length-1] != lastDchar+1) + genericReplace(inversion.data, 0, 1, cast(uint[]) null); + if (data[data.length-1] != lastDchar+1) genericReplace(inversion.data, inversion.data.length, inversion.data.length, [lastDchar+1]); else genericReplace(inversion.data, - inversion.data.length-1, inversion.data.length, cast(uint[])null); + inversion.data.length-1, inversion.data.length, cast(uint[]) null); return inversion; } + /// + @safe unittest + { + auto set = unicode.ASCII; + // union with the inverse gets all of the code points in the Unicode + assert((set | set.inverted).length == 0x110000); + // no intersection with the inverse + assert((set & set.inverted).empty); + } + /** Generates string with D source code of unary function with name of $(D funcName) taking a single $(D dchar) argument. If $(D funcName) is empty @@ -2524,16 +2650,16 @@ public: --- bool func(dchar ch) @safe pure nothrow @nogc { - if(ch < 45) + if (ch < 45) { - if(ch == 10 || ch == 11) return true; + if (ch == 10 || ch == 11) return true; return false; } else if (ch < 65) return true; else { - if(ch < 100) return false; - if(ch < 200) return true; + if (ch < 100) return false; + if (ch < 200) return true; return false; } } @@ -2541,32 +2667,32 @@ public: */ string toSourceCode(string funcName="") { + import std.algorithm.searching : countUntil; import std.array : array; import std.format : format; - import std.algorithm : countUntil; enum maxBinary = 3; static string linearScope(R)(R ivals, string indent) { string result = indent~"{\n"; string deeper = indent~" "; - foreach(ival; ivals) + foreach (ival; ivals) { - auto span = ival[1] - ival[0]; + immutable span = ival[1] - ival[0]; assert(span != 0); - if(span == 1) + if (span == 1) { - result ~= format("%sif(ch == %s) return true;\n", deeper, ival[0]); + result ~= format("%sif (ch == %s) return true;\n", deeper, ival[0]); } - else if(span == 2) + else if (span == 2) { - result ~= format("%sif(ch == %s || ch == %s) return true;\n", + result ~= format("%sif (ch == %s || ch == %s) return true;\n", deeper, ival[0], ival[0]+1); } else { - if(ival[0] != 0) // dchar is unsigned and < 0 is useless - result ~= format("%sif(ch < %s) return false;\n", deeper, ival[0]); - result ~= format("%sif(ch < %s) return true;\n", deeper, ival[1]); + if (ival[0] != 0) // dchar is unsigned and < 0 is useless + result ~= format("%sif (ch < %s) return false;\n", deeper, ival[0]); + result ~= format("%sif (ch < %s) return true;\n", deeper, ival[1]); } } result ~= format("%sreturn false;\n%s}\n", deeper, indent); // including empty range of intervals @@ -2576,7 +2702,7 @@ public: static string binaryScope(R)(R ivals, string indent) { // time to do unrolled comparisons? - if(ivals.length < maxBinary) + if (ivals.length < maxBinary) return linearScope(ivals, indent); else return bisect(ivals, ivals.length/2, indent); @@ -2586,11 +2712,11 @@ public: // and GDC is doing fine job either way static string switchScope(R)(R ivals, string indent) { - string result = indent~"switch(ch){\n"; + string result = indent~"switch (ch){\n"; string deeper = indent~" "; - foreach(ival; ivals) + foreach (ival; ivals) { - if(ival[0]+1 == ival[1]) + if (ival[0]+1 == ival[1]) { result ~= format("%scase %s: return true;\n", deeper, ival[0]); @@ -2611,8 +2737,8 @@ public: // bisect on one [a, b) interval at idx string result = indent~"{\n"; // less branch, < a - result ~= format("%sif(ch < %s)\n%s", - deeper, range[idx][0], binaryScope(range[0..idx], deeper)); + result ~= format("%sif (ch < %s)\n%s", + deeper, range[idx][0], binaryScope(range[0 .. idx], deeper)); // middle point, >= a && < b result ~= format("%selse if (ch < %s) return true;\n", deeper, range[idx][1]); @@ -2627,7 +2753,7 @@ public: auto range = byInterval.array(); // special case first bisection to be on ASCII vs beyond auto tillAscii = countUntil!"a[0] > 0x80"(range); - if(tillAscii <= 0) // everything is ASCII or nothing is ascii (-1 & 0) + if (tillAscii <= 0) // everything is ASCII or nothing is ascii (-1 & 0) code ~= binaryScope(range, ""); else code ~= bisect(range, tillAscii, ""); @@ -2636,18 +2762,20 @@ public: /** True if this set doesn't contain any $(CODEPOINTS). - Example: - --- - CodepointSet emptySet; - assert(emptySet.length == 0); - assert(emptySet.empty); - --- */ @property bool empty() const { return data.length == 0; } + /// + @safe unittest + { + CodepointSet emptySet; + assert(emptySet.length == 0); + assert(emptySet.empty); + } + private: alias This = typeof(this); alias Marker = size_t; @@ -2671,14 +2799,14 @@ private: @property auto front()const { - uint a = slice[start]; - uint b = slice[start+1]; + immutable a = slice[start]; + immutable b = slice[start+1]; return CodepointInterval(a, b); } //may break sorted property - but we need std.sort to access it //hence package protection attribute - package @property auto front(CodepointInterval val) + package @property void front(CodepointInterval val) { slice[start] = val.a; slice[start+1] = val.b; @@ -2686,13 +2814,13 @@ private: @property auto back()const { - uint a = slice[end-2]; - uint b = slice[end-1]; + immutable a = slice[end-2]; + immutable b = slice[end-1]; return CodepointInterval(a, b); } //ditto about package - package @property auto back(CodepointInterval val) + package @property void back(CodepointInterval val) { slice[end-2] = val.a; slice[end-1] = val.b; @@ -2710,13 +2838,13 @@ private: auto opIndex(size_t idx) const { - uint a = slice[start+idx*2]; - uint b = slice[start+idx*2+1]; + immutable a = slice[start+idx*2]; + immutable b = slice[start+idx*2+1]; return CodepointInterval(a, b); } //ditto about package - package auto opIndexAssign(CodepointInterval val, size_t idx) + package void opIndexAssign(CodepointInterval val, size_t idx) { slice[start+idx*2] = val.a; slice[start+idx*2+1] = val.b; @@ -2741,7 +2869,9 @@ private: // to make sure invariants hold void sanitize() { - import std.algorithm : sort, SwapStrategy, max; + import std.algorithm.comparison : max; + import std.algorithm.mutation : SwapStrategy; + import std.algorithm.sorting : sort; if (data.length == 0) return; alias Ival = CodepointInterval; @@ -2787,7 +2917,7 @@ private: ref subChar(dchar ch) { auto mark = skipUpTo(ch); - if(mark != data.length + if (mark != data.length && data[mark] == ch && data[mark-1] == ch) { // it has split, meaning that ch happens to be in one of intervals @@ -2808,25 +2938,25 @@ private: auto range = assumeSorted(data[]); size_t pos; size_t a_idx = hint + range[hint..$].lowerBound!(SearchPolicy.gallop)(a).length; - if(a_idx == range.length) + if (a_idx == range.length) { // [---+++----++++----++++++] // [ a b] data.append(a, b); return data.length-1; } - size_t b_idx = range[a_idx..range.length].lowerBound!(SearchPolicy.gallop)(b).length+a_idx; + size_t b_idx = range[a_idx .. range.length].lowerBound!(SearchPolicy.gallop)(b).length+a_idx; uint[3] buf = void; uint to_insert; debug(std_uni) { writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx); } - if(b_idx == range.length) + if (b_idx == range.length) { // [-------++++++++----++++++-] // [ s a b] - if(a_idx & 1)// a in positive + if (a_idx & 1)// a in positive { buf[0] = b; to_insert = 1; @@ -2837,7 +2967,7 @@ private: buf[1] = b; to_insert = 2; } - pos = genericReplace(data, a_idx, b_idx, buf[0..to_insert]); + pos = genericReplace(data, a_idx, b_idx, buf[0 .. to_insert]); return pos - 1; } @@ -2848,9 +2978,9 @@ private: writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx); writefln("a=%s; b=%s; top=%s;", a, b, top); } - if(a_idx & 1) + if (a_idx & 1) {// a in positive - if(b_idx & 1)// b in positive + if (b_idx & 1)// b in positive { // [-------++++++++----++++++-] // [ s a b ] @@ -2861,11 +2991,11 @@ private: { // [-------++++++++----++++++-] // [ s a b ] - if(top == b) + if (top == b) { assert(b_idx+1 < data.length); buf[0] = data[b_idx+1]; - pos = genericReplace(data, a_idx, b_idx+2, buf[0..1]); + pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]); return pos - 1; } buf[0] = b; @@ -2875,7 +3005,7 @@ private: } else { // a in negative - if(b_idx & 1) // b in positive + if (b_idx & 1) // b in positive { // [----------+++++----++++++-] // [ a b ] @@ -2887,12 +3017,12 @@ private: { // [----------+++++----++++++-] // [ a s b ] - if(top == b) + if (top == b) { assert(b_idx+1 < data.length); buf[0] = a; buf[1] = data[b_idx+1]; - pos = genericReplace(data, a_idx, b_idx+2, buf[0..2]); + pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 2]); return pos - 1; } buf[0] = a; @@ -2901,11 +3031,11 @@ private: to_insert = 3; } } - pos = genericReplace(data, a_idx, b_idx+1, buf[0..to_insert]); + pos = genericReplace(data, a_idx, b_idx+1, buf[0 .. to_insert]); debug(std_uni) { writefln("marker idx: %d; length=%d", pos, data[pos], data.length); - writeln("inserting ", buf[0..to_insert]); + writeln("inserting ", buf[0 .. to_insert]); } return pos - 1; } @@ -2918,8 +3048,8 @@ private: } body { - auto range = assumeSorted!"a<=b"(data[pos..data.length]); - if(range.empty) + auto range = assumeSorted!"a <= b"(data[pos .. data.length]); + if (range.empty) return pos; size_t idx = pos; idx += range.lowerBound(a).length; @@ -2929,9 +3059,9 @@ private: writeln("dropUpTo full length=", data.length); writeln(pos,"~~~", idx); } - if(idx == data.length) + if (idx == data.length) return genericReplace(data, pos, idx, cast(uint[])[]); - if(idx & 1) + if (idx & 1) { // a in positive //[--+++----++++++----+++++++------...] // |<---si s a t @@ -2956,20 +3086,20 @@ private: body { assert(data.length % 2 == 0); - auto range = assumeSorted!"a<=b"(data[pos..data.length]); + auto range = assumeSorted!"a <= b"(data[pos .. data.length]); size_t idx = pos+range.lowerBound(a).length; - if(idx >= data.length) // could have Marker point to recently removed stuff + if (idx >= data.length) // could have Marker point to recently removed stuff return data.length; - if(idx & 1)// inside of interval, check for split + if (idx & 1)// inside of interval, check for split { - uint top = data[idx]; - if(top == a)// no need to split, it's end + immutable top = data[idx]; + if (top == a)// no need to split, it's end return idx+1; - uint start = data[idx-1]; - if(a == start) + immutable start = data[idx-1]; + if (a == start) return idx-1; // split it up genericReplace(data, idx, idx+1, [a, a, top]); @@ -2979,103 +3109,46 @@ private: } CowArray!SP data; -}; +} @system unittest { - // test examples - import std.algorithm, std.typecons; - import std.range : iota; - auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1); - set.byInterval.equalS([tuple('A', 'E'), tuple('a', 'e')]); - set = unicode.ASCII; - assert(set.byCodepoint.equalS(iota(0, 0x80))); - set = CodepointSet('a', 'z'+1, 'а', 'Ñ'+1); - foreach(v; 'a'..'z'+1) - assert(set[v]); - // Cyrillic lowercase interval - foreach(v; 'а'..'Ñ'+1) - assert(set[v]); - //specific order is not required, intervals may interesect - auto set2 = CodepointSet('а', 'Ñ'+1, 'a', 'd', 'b', 'z'+1); - assert(set2.byInterval.equal(set.byInterval)); - - auto gothic = unicode.Gothic; - // Gothic letter ahsa - assert(gothic['\U00010330']); - // no ascii in Gothic obviously - assert(!gothic['$']); - - CodepointSet emptySet; - assert(emptySet.length == 0); - assert(emptySet.empty); - - set = unicode.ASCII; - // union with the inverse gets all of code points in the Unicode - assert((set | set.inverted).length == 0x110000); - // no intersection with inverse - assert((set & set.inverted).empty); - - CodepointSet someSet; - someSet.add('0', '5').add('A','Z'+1); - someSet.add('5', '9'+1); - assert(someSet['0']); - assert(someSet['5']); - assert(someSet['9']); - assert(someSet['Z']); - - auto lower = unicode.LowerCase; - auto upper = unicode.UpperCase; - auto ascii = unicode.ASCII; - assert((lower & upper).empty); // no intersection - auto lowerASCII = lower & ascii; - assert(lowerASCII.byCodepoint.equalS(iota('a', 'z'+1))); - // throw away all of the lowercase ASCII - assert((ascii - lower).length == 128 - 26); - auto onlyOneOf = lower ~ ascii; - assert(!onlyOneOf['Δ']); // not ASCII and not lowercase - assert(onlyOneOf['$']); // ASCII and not lowercase - assert(!onlyOneOf['a']); // ASCII and lowercase - assert(onlyOneOf['Ñ']); // not ASCII but lowercase - - auto noLetters = ascii - (lower | upper); - assert(noLetters.length == 128 - 26*2); - import std.conv; + import std.conv : to; assert(unicode.ASCII.to!string() == "[0..128)"); } // pedantic version for ctfe, and aligned-access only architectures -@trusted uint safeRead24(const ubyte* ptr, size_t idx) pure nothrow @nogc +@system private uint safeRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc { idx *= 3; version(LittleEndian) - return ptr[idx] + (cast(uint)ptr[idx+1]<<8) - + (cast(uint)ptr[idx+2]<<16); + return ptr[idx] + (cast(uint) ptr[idx+1]<<8) + + (cast(uint) ptr[idx+2]<<16); else - return (cast(uint)ptr[idx]<<16) + (cast(uint)ptr[idx+1]<<8) + return (cast(uint) ptr[idx]<<16) + (cast(uint) ptr[idx+1]<<8) + ptr[idx+2]; } // ditto -@trusted void safeWrite24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc +@system private void safeWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc { idx *= 3; version(LittleEndian) { ptr[idx] = val & 0xFF; - ptr[idx+1] = (val>>8) & 0xFF; - ptr[idx+2] = (val>>16) & 0xFF; + ptr[idx+1] = (val >> 8) & 0xFF; + ptr[idx+2] = (val >> 16) & 0xFF; } else { - ptr[idx] = (val>>16) & 0xFF; - ptr[idx+1] = (val>>8) & 0xFF; + ptr[idx] = (val >> 16) & 0xFF; + ptr[idx+1] = (val >> 8) & 0xFF; ptr[idx+2] = val & 0xFF; } } // unaligned x86-like read/write functions -@trusted uint unalignedRead24(const ubyte* ptr, size_t idx) pure nothrow @nogc +@system private uint unalignedRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc { uint* src = cast(uint*)(ptr+3*idx); version(LittleEndian) @@ -3085,32 +3158,36 @@ private: } // ditto -@trusted void unalignedWrite24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc +@system private void unalignedWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc { - uint* dest = cast(uint*)(cast(ubyte*)ptr + 3*idx); + uint* dest = cast(uint*)(cast(ubyte*) ptr + 3*idx); version(LittleEndian) *dest = val | (*dest & 0xFF00_0000); else - *dest = (val<<8) | (*dest & 0xFF); + *dest = (val << 8) | (*dest & 0xFF); } -uint read24(const ubyte* ptr, size_t idx) pure nothrow @nogc +@system private uint read24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc { - static if(hasUnalignedReads) + static if (hasUnalignedReads) return __ctfe ? safeRead24(ptr, idx) : unalignedRead24(ptr, idx); else return safeRead24(ptr, idx); } -void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc +@system private void write24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc { - static if(hasUnalignedReads) + static if (hasUnalignedReads) return __ctfe ? safeWrite24(ptr, val, idx) : unalignedWrite24(ptr, val, idx); else return safeWrite24(ptr, val, idx); } -@trusted struct CowArray(SP=GcPolicy) + +struct CowArray(SP=GcPolicy) { + import std.range.primitives : hasLength; + + @safe: static auto reuse(uint[] arr) { CowArray cow; @@ -3122,25 +3199,26 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc } this(Range)(Range range) - if(isInputRange!Range && hasLength!Range) + if (isInputRange!Range && hasLength!Range) { - import std.algorithm : copy; + import std.algorithm.mutation : copy; length = range.length; copy(range, data[0..$-1]); } this(Range)(Range range) - if(isForwardRange!Range && !hasLength!Range) + if (isForwardRange!Range && !hasLength!Range) { - import std.algorithm : copy; - auto len = walkLength(range.save); + import std.algorithm.mutation : copy; + import std.range.primitives : walkLength; + immutable len = walkLength(range.save); length = len; copy(range, data[0..$-1]); } this(this) { - if(!empty) + if (!empty) { refCount = refCount + 1; } @@ -3148,10 +3226,10 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc ~this() { - if(!empty) + if (!empty) { - auto cnt = refCount; - if(cnt == 1) + immutable cnt = refCount; + if (cnt == 1) SP.destroy(data); else refCount = cnt - 1; @@ -3170,28 +3248,29 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc //+ an extra slot for ref-count @property void length(size_t len) { - import std.algorithm : min, copy; - if(len == 0) + import std.algorithm.comparison : min; + import std.algorithm.mutation : copy; + if (len == 0) { - if(!empty) + if (!empty) freeThisReference(); return; } immutable total = len + 1; // including ref-count - if(empty) + if (empty) { data = SP.alloc!uint(total); refCount = 1; return; } - auto cur_cnt = refCount; - if(cur_cnt != 1) // have more references to this memory + immutable cur_cnt = refCount; + if (cur_cnt != 1) // have more references to this memory { refCount = cur_cnt - 1; auto new_data = SP.alloc!uint(total); // take shrinking into account auto to_copy = min(total, data.length) - 1; - copy(data[0..to_copy], new_data[0..to_copy]); + copy(data[0 .. to_copy], new_data[0 .. to_copy]); data = new_data; // before setting refCount! refCount = 1; } @@ -3213,7 +3292,7 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc void opIndexAssign(uint val, size_t idx) { auto cnt = refCount; - if(cnt != 1) + if (cnt != 1) dupThisReference(cnt); data[idx] = val; } @@ -3221,10 +3300,10 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc // auto opSlice(size_t from, size_t to) { - if(!empty) + if (!empty) { auto cnt = refCount; - if(cnt != 1) + if (cnt != 1) dupThisReference(cnt); } return data[from .. to]; @@ -3250,11 +3329,11 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc } void append(Range)(Range range) - if(isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) + if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) { size_t nl = length + range.length; length = nl; - copy(range, this[nl-range.length..nl]); + copy(range, this[nl-range.length .. nl]); } void append()(uint[] val...) @@ -3265,7 +3344,7 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow @nogc bool opEquals()(auto const ref CowArray rhs)const { - if(empty ^ rhs.empty) + if (empty ^ rhs.empty) return false; // one is empty and the other isn't return empty || data[0..$-1] == rhs.data[0..$-1]; } @@ -3284,8 +3363,8 @@ private: void freeThisReference() { - auto count = refCount; - if(count != 1) // have more references to this memory + immutable count = refCount; + if (count != 1) // have more references to this memory { // dec shared ref-count refCount = count - 1; @@ -3303,7 +3382,7 @@ private: } body { - import std.algorithm : copy; + import std.algorithm.mutation : copy; // dec shared ref-count refCount = count - 1; // copy to the new chunk of RAM @@ -3317,11 +3396,13 @@ private: uint[] data; } -@trusted unittest// Uint24 tests //@@@BUG@@ iota is system ?! +@safe unittest// Uint24 tests { - import std.algorithm; - import std.conv; - import std.range; + import std.algorithm.comparison : equal; + import std.algorithm.mutation : copy; + import std.conv : text; + import std.range : iota, chain; + import std.range.primitives : isBidirectionalRange, isOutputRange; void funcRef(T)(ref T u24) { u24.length = 2; @@ -3331,13 +3412,13 @@ private: u24.length = 0; assert(u24.empty); u24.append([1, 2]); - assert(equalS(u24[], [1, 2])); + assert(equal(u24[], [1, 2])); u24.append(111); - assert(equalS(u24[], [1, 2, 111])); + assert(equal(u24[], [1, 2, 111])); assert(!u24_c.empty && u24_c[1] == 1024); u24.length = 3; copy(iota(0, 3), u24[]); - assert(equalS(u24[], iota(0, 3))); + assert(equal(u24[], iota(0, 3))); assert(u24_c[1] == 1024); } @@ -3347,15 +3428,15 @@ private: T u24_3; u24_3 = u24_2; assert(u24_2 == u24_3); - assert(equalS(u24[], u24_2[])); - assert(equalS(u24_2[], u24_3[])); + assert(equal(u24[], u24_2[])); + assert(equal(u24_2[], u24_3[])); funcRef(u24_3); - assert(equalS(u24_3[], iota(0, 3))); - assert(!equalS(u24_2[], u24_3[])); - assert(equalS(u24_2[], u24[])); + assert(equal(u24_3[], iota(0, 3))); + assert(!equal(u24_2[], u24_3[])); + assert(equal(u24_2[], u24[])); u24_2 = u24_3; - assert(equalS(u24_2[], iota(0, 3))); + assert(equal(u24_2[], iota(0, 3))); // to test that passed arg is intact outside // plus try out opEquals u24 = u24_3; @@ -3366,7 +3447,7 @@ private: assert(u24 != u24_2); } - foreach(Policy; TypeTuple!(GcPolicy, ReallocPolicy)) + foreach (Policy; AliasSeq!(GcPolicy, ReallocPolicy)) { alias Range = typeof(CowArray!Policy.init[]); alias U24A = CowArray!Policy; @@ -3390,27 +3471,27 @@ private: assert(arr[0] == 72); assert(arr2[0] == 11); // set this to about 100M to stress-test COW memory management - foreach(v; 0..10_000) + foreach (v; 0 .. 10_000) func2(arr); - assert(equalS(arr[], [72, 0xFE_FEFE, 100])); + assert(equal(arr[], [72, 0xFE_FEFE, 100])); auto r2 = U24A(iota(0, 100)); - assert(equalS(r2[], iota(0, 100)), text(r2[])); - copy(iota(10, 170, 2), r2[10..90]); - assert(equalS(r2[], chain(iota(0, 10), iota(10, 170, 2), iota(90, 100))) + assert(equal(r2[], iota(0, 100)), text(r2[])); + copy(iota(10, 170, 2), r2[10 .. 90]); + assert(equal(r2[], chain(iota(0, 10), iota(10, 170, 2), iota(90, 100))) , text(r2[])); } } version(unittest) { - private alias AllSets = TypeTuple!(InversionList!GcPolicy, InversionList!ReallocPolicy); + private alias AllSets = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy); } -@trusted unittest// core set primitives test +@safe unittest// core set primitives test { - import std.conv; - foreach(CodeList; AllSets) + import std.conv : text; + foreach (CodeList; AllSets) { CodeList a; //"plug a hole" test @@ -3484,13 +3565,15 @@ version(unittest) //test constructor to work with any order of intervals -@system unittest //@@@BUG@@@ iota is @system +@safe unittest { - import std.conv, std.range, std.algorithm; - import std.typecons; + import std.algorithm.comparison : equal; + import std.conv : text, to; + import std.range : chain, iota; + import std.typecons : tuple; //ensure constructor handles bad ordering and overlap auto c1 = CodepointSet('а', 'Ñ'+1, 'Ð','Я'+1); - foreach(ch; chain(iota('а', 'Ñ'+1), iota('Ð','Я'+1))) + foreach (ch; chain(iota('а', 'Ñ'+1), iota('Ð','Я'+1))) assert(ch in c1, to!string(ch)); //contiguos @@ -3526,10 +3609,10 @@ version(unittest) } -@trusted unittest +@safe unittest { // full set operations - import std.conv; - foreach(CodeList; AllSets) + import std.conv : text; + foreach (CodeList; AllSets) { CodeList a, b, c, d; @@ -3631,48 +3714,51 @@ version(unittest) } -unittest// vs single dchar +@safe unittest// vs single dchar { - import std.conv; + import std.conv : text; CodepointSet a = CodepointSet(10, 100, 120, 200); assert(a - 'A' == CodepointSet(10, 65, 66, 100, 120, 200), text(a - 'A')); assert((a & 'B') == CodepointSet(66, 67)); } -unittest// iteration & opIndex +@safe unittest// iteration & opIndex { - import std.conv; - import std.typecons; - foreach(CodeList; TypeTuple!(InversionList!(ReallocPolicy))) + import std.algorithm.comparison : equal; + import std.conv : text; + import std.typecons : tuple, Tuple; + + foreach (CodeList; AliasSeq!(InversionList!(ReallocPolicy))) { auto arr = "ABCDEFGHIJKLMabcdefghijklm"d; auto a = CodeList('A','N','a', 'n'); - assert(equalS(a.byInterval, + assert(equal(a.byInterval, [tuple(cast(uint)'A', cast(uint)'N'), tuple(cast(uint)'a', cast(uint)'n')] ), text(a.byInterval)); // same @@@BUG as in issue 8949 ? version(bug8949) { - assert(equalS(retro(a.byInterval), + import std.range : retro; + assert(equal(retro(a.byInterval), [tuple(cast(uint)'a', cast(uint)'n'), tuple(cast(uint)'A', cast(uint)'N')] ), text(retro(a.byInterval))); } auto achr = a.byCodepoint; - assert(equalS(achr, arr), text(a.byCodepoint)); - foreach(ch; a.byCodepoint) + assert(equal(achr, arr), text(a.byCodepoint)); + foreach (ch; a.byCodepoint) assert(a[ch]); auto x = CodeList(100, 500, 600, 900, 1200, 1500); - assert(equalS(x.byInterval, [ tuple(100, 500), tuple(600, 900), tuple(1200, 1500)]), text(x.byInterval)); - foreach(ch; x.byCodepoint) + assert(equal(x.byInterval, [ tuple(100, 500), tuple(600, 900), tuple(1200, 1500)]), text(x.byInterval)); + foreach (ch; x.byCodepoint) assert(x[ch]); - static if(is(CodeList == CodepointSet)) + static if (is(CodeList == CodepointSet)) { auto y = CodeList(x.byInterval); - assert(equalS(x.byInterval, y.byInterval)); + assert(equal(x.byInterval, y.byInterval)); } - assert(equalS(CodepointSet.init.byInterval, cast(Tuple!(uint, uint)[])[])); - assert(equalS(CodepointSet.init.byCodepoint, cast(dchar[])[])); + assert(equal(CodepointSet.init.byInterval, cast(Tuple!(uint, uint)[])[])); + assert(equal(CodepointSet.init.byCodepoint, cast(dchar[])[])); } } @@ -3684,9 +3770,9 @@ unittest// iteration & opIndex auto arrayRepr(T)(T x) { import std.conv : text; - if(x.length > 32) + if (x.length > 32) { - return text(x[0..16],"~...~", x[x.length-16..x.length]); + return text(x[0 .. 16],"~...~", x[x.length-16 .. x.length]); } else return text(x); @@ -3703,11 +3789,11 @@ auto arrayRepr(T)(T x) template mapTrieIndex(Prefix...) { size_t mapTrieIndex(Key)(Key key) - if(isValidPrefixForTrie!(Key, Prefix)) + if (isValidPrefixForTrie!(Key, Prefix)) { alias p = Prefix; size_t idx; - foreach(i, v; p[0..$-1]) + foreach (i, v; p[0..$-1]) { idx |= p[i](key); idx <<= p[i+1].bitSize; @@ -3723,26 +3809,26 @@ template mapTrieIndex(Prefix...) See $(LREF buildTrie) for generic helpers built on top of it. */ -@trusted struct TrieBuilder(Value, Key, Args...) - if(isBitPackableType!Value && isValidArgsForTrie!(Key, Args)) +@trusted private struct TrieBuilder(Value, Key, Args...) +if (isBitPackableType!Value && isValidArgsForTrie!(Key, Args)) { import std.exception : enforce; private: // last index is not stored in table, it is used as an offset to values in a block. - static if(is(Value == bool))// always pack bool + static if (is(Value == bool))// always pack bool alias V = BitPacked!(Value, 1); else alias V = Value; static auto deduceMaxIndex(Preds...)() { size_t idx = 1; - foreach(v; Preds) + foreach (v; Preds) idx *= 2^^v.bitSize; return idx; } - static if(is(typeof(Args[0]) : Key)) // Args start with upper bound on Key + static if (is(typeof(Args[0]) : Key)) // Args start with upper bound on Key { alias Prefix = Args[1..$]; enum lastPageSize = 2^^Prefix[$-1].bitSize; @@ -3787,13 +3873,13 @@ private: void addValue(size_t level, T)(T val, size_t numVals) { alias j = idx!level; - enum pageSize = 1<<Prefix[level].bitSize; - if(numVals == 0) + enum pageSize = 1 << Prefix[level].bitSize; + if (numVals == 0) return; auto ptr = table.slice!(level); - if(numVals == 1) + if (numVals == 1) { - static if(level == Prefix.length-1) + static if (level == Prefix.length-1) ptr[j] = val; else {// can incur narrowing conversion @@ -3801,30 +3887,30 @@ private: ptr[j] = force!(typeof(ptr[j]))(val); } j++; - if(j % pageSize == 0) + if (j % pageSize == 0) spillToNextPage!level(ptr); return; } // longer row of values // get to the next page boundary - size_t nextPB = (j + pageSize) & ~(pageSize-1); - size_t n = nextPB - j;// can fill right in this page - if(numVals < n) //fits in current page + immutable nextPB = (j + pageSize) & ~(pageSize-1); + immutable n = nextPB - j;// can fill right in this page + if (numVals < n) //fits in current page { - ptr[j..j+numVals] = val; + ptr[j .. j+numVals] = val; j += numVals; return; } - static if(level != 0)//on the first level it always fits + static if (level != 0)//on the first level it always fits { numVals -= n; //write till the end of current page - ptr[j..j+n] = val; + ptr[j .. j+n] = val; j += n; //spill to the next page spillToNextPage!level(ptr); // page at once loop - if(state[level].idx_zeros != size_t.max && val == T.init) + if (state[level].idx_zeros != size_t.max && val == T.init) { alias NextIdx = typeof(table.slice!(level-1)[0]); addValue!(level-1)(force!NextIdx(state[level].idx_zeros), @@ -3834,18 +3920,18 @@ private: } else { - while(numVals >= pageSize) + while (numVals >= pageSize) { numVals -= pageSize; - ptr[j..j+pageSize] = val; + ptr[j .. j+pageSize] = val; j += pageSize; spillToNextPage!level(ptr); } } - if(numVals) + if (numVals) { // the leftovers, an incomplete page - ptr[j..j+numVals] = val; + ptr[j .. j+numVals] = val; j += numVals; } } @@ -3855,7 +3941,7 @@ private: { // last level (i.e. topmost) has 1 "page" // thus it need not to add a new page on upper level - static if(level != 0) + static if (level != 0) spillToNextPageImpl!(level)(ptr); } @@ -3865,44 +3951,46 @@ private: { alias NextIdx = typeof(table.slice!(level-1)[0]); NextIdx next_lvl_index; - enum pageSize = 1<<Prefix[level].bitSize; + enum pageSize = 1 << Prefix[level].bitSize; assert(idx!level % pageSize == 0); - auto last = idx!level-pageSize; - auto slice = ptr[idx!level - pageSize..idx!level]; + immutable last = idx!level-pageSize; + const slice = ptr[idx!level - pageSize .. idx!level]; size_t j; - for(j=0; j<last; j+=pageSize) + for (j=0; j<last; j+=pageSize) { - if(ptr[j..j+pageSize] == slice) + if (ptr[j .. j+pageSize] == slice) { // get index to it, reuse ptr space for the next block next_lvl_index = force!NextIdx(j/pageSize); version(none) { - writefln("LEVEL(%s) page maped idx: %s: 0..%s ---> [%s..%s]" + import std.stdio : writefln, writeln; + writefln("LEVEL(%s) page mapped idx: %s: 0..%s ---> [%s..%s]" ,level ,indices[level-1], pageSize, j, j+pageSize); writeln("LEVEL(", level - , ") mapped page is: ", slice, ": ", arrayRepr(ptr[j..j+pageSize])); + , ") mapped page is: ", slice, ": ", arrayRepr(ptr[j .. j+pageSize])); writeln("LEVEL(", level - , ") src page is :", ptr, ": ", arrayRepr(slice[0..pageSize])); + , ") src page is :", ptr, ": ", arrayRepr(slice[0 .. pageSize])); } idx!level -= pageSize; // reuse this page, it is duplicate break; } } - if(j == last) + if (j == last) { L_allocate_page: next_lvl_index = force!NextIdx(idx!level/pageSize - 1); - if(state[level].idx_zeros == size_t.max && ptr.zeros(j, j+pageSize)) + if (state[level].idx_zeros == size_t.max && ptr.zeros(j, j+pageSize)) { state[level].idx_zeros = next_lvl_index; } // allocate next page version(none) { + import std.stdio : writefln; writefln("LEVEL(%s) page allocated: %s" - , level, arrayRepr(slice[0..pageSize])); + , level, arrayRepr(slice[0 .. pageSize])); writefln("LEVEL(%s) index: %s ; page at this index %s" , level , next_lvl_index @@ -3924,7 +4012,7 @@ private: void putAt(size_t idx, Value v) { assert(idx >= curIndex); - size_t numFillers = idx - curIndex; + immutable numFillers = idx - curIndex; addValue!lastLevel(defValue, numFillers); addValue!lastLevel(v, 1); curIndex = idx + 1; @@ -3954,12 +4042,12 @@ public: curIndex = 0; defValue = filler; // zeros-page index, ones-page index - foreach(ref v; state) + foreach (ref v; state) v = ConstructState(size_t.max, size_t.max); table = typeof(table)(indices); // one page per level is a bootstrap minimum - foreach(i, Pred; Prefix) - table.length!i = (1<<Pred.bitSize); + foreach (i, Pred; Prefix) + table.length!i = (1 << Pred.bitSize); } /** @@ -3992,14 +4080,14 @@ public: /// Finishes construction of Trie, yielding an immutable Trie instance. auto build() { - static if(maxIndex != 0) // doesn't cover full range of size_t + static if (maxIndex != 0) // doesn't cover full range of size_t { assert(curIndex <= maxIndex); addValue!lastLevel(defValue, maxIndex - curIndex); } else { - if(curIndex != 0 // couldn't wrap around + if (curIndex != 0 // couldn't wrap around || (Prefix.length != 1 && indices[lastLevel] == 0)) // can be just empty { addValue!lastLevel(defValue, size_t.max - curIndex); @@ -4011,7 +4099,7 @@ public: } } -/* +/** $(P A generic Trie data-structure for a fixed number of stages. The design goal is optimal speed with smallest footprint size. ) @@ -4021,21 +4109,22 @@ public: ) */ -@trusted public struct Trie(Value, Key, Args...) - if(isValidPrefixForTrie!(Key, Args) - || (isValidPrefixForTrie!(Key, Args[1..$]) - && is(typeof(Args[0]) : size_t))) +@trusted private struct Trie(Value, Key, Args...) +if (isValidPrefixForTrie!(Key, Args) + || (isValidPrefixForTrie!(Key, Args[1..$]) + && is(typeof(Args[0]) : size_t))) { - static if(is(typeof(Args[0]) : size_t)) + import std.range.primitives : isOutputRange; + static if (is(typeof(Args[0]) : size_t)) { - enum maxIndex = Args[0]; - enum hasBoundsCheck = true; - alias Prefix = Args[1..$]; + private enum maxIndex = Args[0]; + private enum hasBoundsCheck = true; + private alias Prefix = Args[1..$]; } else { - enum hasBoundsCheck = false; - alias Prefix = Args; + private enum hasBoundsCheck = false; + private alias Prefix = Args; } private this()(typeof(_table) table) @@ -4050,7 +4139,7 @@ public: _table = typeof(_table)(offsets, sizes, data); } - /* + /** $(P Lookup the $(D key) in this $(D Trie). ) $(P The lookup always succeeds if key fits the domain @@ -4065,32 +4154,34 @@ public: Domain range-checking is only enabled in debug builds and results in assertion failure. */ - // templated to auto-detect pure, @safe and nothrow TypeOfBitPacked!Value opIndex()(Key key) const { - static if(hasBoundsCheck) + static if (hasBoundsCheck) assert(mapTrieIndex!Prefix(key) < maxIndex); size_t idx; alias p = Prefix; - idx = cast(size_t)p[0](key); - foreach(i, v; p[0..$-1]) + idx = cast(size_t) p[0](key); + foreach (i, v; p[0..$-1]) idx = cast(size_t)((_table.ptr!i[idx]<<p[i+1].bitSize) + p[i+1](key)); return _table.ptr!(p.length-1)[idx]; } + /// @property size_t bytes(size_t n=size_t.max)() const { return _table.bytes!n; } + /// @property size_t pages(size_t n)() const { return (bytes!n+2^^(Prefix[n].bitSize-1)) /2^^Prefix[n].bitSize; } + /// void store(OutRange)(scope OutRange sink) const - if(isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { _table.store(sink); } @@ -4103,19 +4194,19 @@ private: // left-to-right, the most significant bits first template GetBitSlicing(size_t top, sizes...) { - static if(sizes.length > 0) + static if (sizes.length > 0) alias GetBitSlicing = - TypeTuple!(sliceBits!(top - sizes[0], top), - GetBitSlicing!(top - sizes[0], sizes[1..$])); + AliasSeq!(sliceBits!(top - sizes[0], top), + GetBitSlicing!(top - sizes[0], sizes[1..$])); else - alias GetBitSlicing = TypeTuple!(); + alias GetBitSlicing = AliasSeq!(); } template callableWith(T) { template callableWith(alias Pred) { - static if(!is(typeof(Pred(T.init)))) + static if (!is(typeof(Pred(T.init)))) enum callableWith = false; else { @@ -4133,6 +4224,7 @@ template callableWith(T) */ template isValidPrefixForTrie(Key, Prefix...) { + import std.meta : allSatisfy; enum isValidPrefixForTrie = allSatisfy!(callableWith!Key, Prefix); // TODO: tighten the screws } @@ -4142,7 +4234,7 @@ template isValidPrefixForTrie(Key, Prefix...) */ template isValidArgsForTrie(Key, Args...) { - static if(Args.length > 1) + static if (Args.length > 1) { enum isValidArgsForTrie = isValidPrefixForTrie!(Key, Args) || (isValidPrefixForTrie!(Key, Args[1..$]) && is(typeof(Args[0]) : Key)); @@ -4154,7 +4246,7 @@ template isValidArgsForTrie(Key, Args...) @property size_t sumOfIntegerTuple(ints...)() { size_t count=0; - foreach(v; ints) + foreach (v; ints) count += v; return count; } @@ -4175,11 +4267,11 @@ template isValidArgsForTrie(Key, Args...) auto set = unicode("Number"); auto trie = codepointSetTrie!(8, 5, 8)(set); writeln("Input code points to test:"); - foreach(line; stdin.byLine) + foreach (line; stdin.byLine) { int count=0; - foreach(dchar ch; line) - if(trie[ch])// is number + foreach (dchar ch; line) + if (trie[ch])// is number count++; writefln("Contains %d number code points.", count); } @@ -4187,13 +4279,13 @@ template isValidArgsForTrie(Key, Args...) --- */ public template codepointSetTrie(sizes...) - if(sumOfIntegerTuple!sizes == 21) +if (sumOfIntegerTuple!sizes == 21) { auto codepointSetTrie(Set)(Set set) - if(isCodepointSet!Set) + if (isCodepointSet!Set) { auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false); - foreach(ival; set.byInterval) + foreach (ival; set.byInterval) builder.putRange(ival[0], ival[1], true); return builder.build(); } @@ -4201,7 +4293,7 @@ public template codepointSetTrie(sizes...) /// Type of Trie generated by codepointSetTrie function. public template CodepointSetTrie(sizes...) - if(sumOfIntegerTuple!sizes == 21) +if (sumOfIntegerTuple!sizes == 21) { alias Prefix = GetBitSlicing!(21, sizes); alias CodepointSetTrie = typeof(TrieBuilder!(bool, dchar, lastDchar+1, Prefix)(false).build()); @@ -4216,64 +4308,16 @@ public template CodepointSetTrie(sizes...) Note: Overload taking $(D CodepointSet)s will naturally convert only to bool mapping $(D Trie)s. - - Example: - --- - // pick characters from the Greek script - auto set = unicode.Greek; - - // a user-defined property (or an expensive function) - // that we want to look up - static uint luckFactor(dchar ch) - { - // here we consider a character lucky - // if its code point has a lot of identical hex-digits - // e.g. arabic letter DDAL (\u0688) has a "luck factor" of 2 - ubyte[6] nibbles; // 6 4-bit chunks of code point - uint value = ch; - foreach(i; 0..6) - { - nibbles[i] = value & 0xF; - value >>= 4; - } - uint luck; - foreach(n; nibbles) - luck = cast(uint)max(luck, count(nibbles[], n)); - return luck; - } - - // only unsigned built-ins are supported at the moment - alias LuckFactor = BitPacked!(uint, 3); - - // create a temporary associative array (AA) - LuckFactor[dchar] map; - foreach(ch; set.byCodepoint) - map[ch] = luckFactor(ch); - - // bits per stage are chosen randomly, fell free to optimize - auto trie = codepointTrie!(LuckFactor, 8, 5, 8)(map); - - // from now on the AA is not needed - foreach(ch; set.byCodepoint) - assert(trie[ch] == luckFactor(ch)); // verify - // CJK is not Greek, thus it has the default value - assert(trie['\u4444'] == 0); - // and here is a couple of quite lucky Greek characters: - // Greek small letter epsilon with dasia - assert(trie['\u1F11'] == 3); - // Ancient Greek metretes sign - assert(trie['\U00010181'] == 3); - --- */ public template codepointTrie(T, sizes...) - if(sumOfIntegerTuple!sizes == 21) +if (sumOfIntegerTuple!sizes == 21) { alias Prefix = GetBitSlicing!(21, sizes); - static if(is(TypeOfBitPacked!T == bool)) + static if (is(TypeOfBitPacked!T == bool)) { auto codepointTrie(Set)(in Set set) - if(isCodepointSet!Set) + if (isCodepointSet!Set) { return codepointSetTrie(set); } @@ -4286,7 +4330,7 @@ public template codepointTrie(T, sizes...) // unsorted range of pairs auto codepointTrie(R)(R range, T defValue=T.init) - if(isInputRange!R + if (isInputRange!R && is(typeof(ElementType!R.init[0]) : T) && is(typeof(ElementType!R.init[1]) : dchar)) { @@ -4296,9 +4340,12 @@ public template codepointTrie(T, sizes...) } } -unittest // codepointTrie example +/// +@system pure unittest { - import std.algorithm; + import std.algorithm.comparison : max; + import std.algorithm.searching : count; + // pick characters from the Greek script auto set = unicode.Greek; @@ -4311,14 +4358,14 @@ unittest // codepointTrie example // e.g. arabic letter DDAL (\u0688) has a "luck factor" of 2 ubyte[6] nibbles; // 6 4-bit chunks of code point uint value = ch; - foreach(i; 0..6) + foreach (i; 0 .. 6) { nibbles[i] = value & 0xF; value >>= 4; } uint luck; - foreach(n; nibbles) - luck = cast(uint)max(luck, count(nibbles[], n)); + foreach (n; nibbles) + luck = cast(uint) max(luck, count(nibbles[], n)); return luck; } @@ -4327,14 +4374,14 @@ unittest // codepointTrie example // create a temporary associative array (AA) LuckFactor[dchar] map; - foreach(ch; set.byCodepoint) + foreach (ch; set.byCodepoint) map[ch] = LuckFactor(luckFactor(ch)); // bits per stage are chosen randomly, fell free to optimize auto trie = codepointTrie!(LuckFactor, 8, 5, 8)(map); // from now on the AA is not needed - foreach(ch; set.byCodepoint) + foreach (ch; set.byCodepoint) assert(trie[ch] == luckFactor(ch)); // verify // CJK is not Greek, thus it has the default value assert(trie['\u4444'] == 0); @@ -4348,16 +4395,15 @@ unittest // codepointTrie example /// Type of Trie as generated by codepointTrie function. public template CodepointTrie(T, sizes...) - if(sumOfIntegerTuple!sizes == 21) +if (sumOfIntegerTuple!sizes == 21) { alias Prefix = GetBitSlicing!(21, sizes); alias CodepointTrie = typeof(TrieBuilder!(T, dchar, lastDchar+1, Prefix)(T.init).build()); } -// @@@BUG multiSort can's access private symbols from uni -public template cmpK0(alias Pred) +package template cmpK0(alias Pred) { - import std.typecons; + import std.typecons : Tuple; static bool cmpK0(Value, Key) (Tuple!(Value, Key) a, Tuple!(Value, Key) b) { @@ -4365,7 +4411,7 @@ public template cmpK0(alias Pred) } } -/* +/** The most general utility for construction of $(D Trie)s short of using $(D TrieBuilder) directly. @@ -4377,10 +4423,10 @@ public template cmpK0(alias Pred) then the whole tuple of $(D Args) is treated as predicates and the maximum Key is deduced from predicates. */ -public template buildTrie(Value, Key, Args...) - if(isValidArgsForTrie!(Key, Args)) +private template buildTrie(Value, Key, Args...) +if (isValidArgsForTrie!(Key, Args)) { - static if(is(typeof(Args[0]) : Key)) // prefix starts with upper bound on Key + static if (is(typeof(Args[0]) : Key)) // prefix starts with upper bound on Key { alias Prefix = Args[1..$]; } @@ -4392,11 +4438,11 @@ public template buildTrie(Value, Key, Args...) // for multi-sort template GetComparators(size_t n) { - static if(n > 0) + static if (n > 0) alias GetComparators = - TypeTuple!(GetComparators!(n-1), cmpK0!(Prefix[n-1])); + AliasSeq!(GetComparators!(n-1), cmpK0!(Prefix[n-1])); else - alias GetComparators = TypeTuple!(); + alias GetComparators = AliasSeq!(); } /* @@ -4410,16 +4456,16 @@ public template buildTrie(Value, Key, Args...) In other words $(LREF mapTrieIndex) should be a monotonically increasing function that maps $(D Key) to an integer. - See_Also: $(XREF _algorithm, sort), - $(XREF _range, SortedRange), - $(XREF _algorithm, setUnion). + See_Also: $(REF sort, std,_algorithm), + $(REF SortedRange, std,_range), + $(REF setUnion, std,_algorithm). */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if(isInputRange!Range && is(typeof(Range.init.front[0]) : Value) + if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach(v; range) + foreach (v; range) builder.putValue(v[1], v[0]); return builder.build(); } @@ -4435,24 +4481,24 @@ public template buildTrie(Value, Key, Args...) and $(D filler) is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if(is(TypeOfBitPacked!Value == bool) + if (is(TypeOfBitPacked!Value == bool) && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach(ival; range) + foreach (ival; range) builder.putRange(ival[0], ival[1], !filler); return builder.build(); } auto buildTrie(Range)(Range range, Value filler, bool unsorted) - if(isInputRange!Range + if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) && is(typeof(Range.init.front[1]) : Key)) { - import std.algorithm : multiSort; + import std.algorithm.sorting : multiSort; alias Comps = GetComparators!(Prefix.length); - if(unsorted) + if (unsorted) multiSort!(Comps)(range); return buildTrie(range, filler); } @@ -4467,11 +4513,11 @@ public template buildTrie(Value, Key, Args...) If no filler provided keys map to true, and $(D filler) is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if(is(TypeOfBitPacked!Value == bool) + if (is(TypeOfBitPacked!Value == bool) && isInputRange!Range && is(typeof(Range.init.front) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach(v; range) + foreach (v; range) builder.putValue(v, !filler); return builder.build(); } @@ -4481,10 +4527,10 @@ public template buildTrie(Value, Key, Args...) of values where array index serves as key. */ auto buildTrie()(Value[] array, Value filler=Value.init) - if(isUnsigned!Key) + if (isUnsigned!Key) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); - foreach(idx, v; array) + foreach (idx, v; array) builder.putValue(idx, v); return builder.build(); } @@ -4494,7 +4540,8 @@ public template buildTrie(Value, Key, Args...) */ auto buildTrie(Key, Value)(Value[Key] map, Value filler=Value.init) { - import std.range : zip, array; + import std.array : array; + import std.range : zip; auto range = array(zip(map.values, map.keys)); return buildTrie(range, filler, true); // sort it } @@ -4542,21 +4589,21 @@ public struct MatcherConcept of the result of test.) */ public bool match(Range)(ref Range inp) - if(isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool skip(Range)(ref Range inp) - if(isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool test(Range)(ref Range inp) - if(isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } @@ -4576,7 +4623,7 @@ public struct MatcherConcept assert(truth == "= 4"); // test never affects argument } - /* + /** Advanced feature - provide direct access to a subset of matcher based a set of known encoding lengths. Lengths are provided in $(S_LINK Code unit, code units). The sub-matcher then may do less @@ -4626,7 +4673,7 @@ public enum isUtfMatcher(M, C) = __traits(compiles, (){ M m; assert(is(typeof(m.match(d)) == bool)); assert(is(typeof(m.test(d)) == bool)); - static if(is(typeof(m.skip(d)))) + static if (is(typeof(m.skip(d)))) { assert(is(typeof(m.skip(d)) == bool)); assert(is(typeof(m.skip(s)) == bool)); @@ -4635,7 +4682,7 @@ public enum isUtfMatcher(M, C) = __traits(compiles, (){ assert(is(typeof(m.test(s)) == bool)); }); -unittest +@safe unittest { alias CharMatcher = typeof(utfMatcher!char(CodepointSet.init)); alias WcharMatcher = typeof(utfMatcher!wchar(CodepointSet.init)); @@ -4655,38 +4702,39 @@ mixin template ForwardStrings() { private bool fwdStr(string fn, C)(ref C[] str) const pure { - alias type = typeof(units(str)); + import std.utf : byCodeUnit; + alias type = typeof(byCodeUnit(str)); return mixin(fn~"(*cast(type*)&str)"); } } template Utf8Matcher() { - enum validSize(int sz) = sz >= 1 && sz <=4; + enum validSize(int sz) = sz >= 1 && sz <= 4; void badEncoding() pure @safe { - import std.utf; + import std.utf : UTFException; throw new UTFException("Invalid UTF-8 sequence"); } //for 1-stage ASCII - alias AsciiSpec = TypeTuple!(bool, char, clamp!7); + alias AsciiSpec = AliasSeq!(bool, char, clamp!7); //for 2-stage lookup of 2 byte UTF-8 sequences - alias Utf8Spec2 = TypeTuple!(bool, char[2], + alias Utf8Spec2 = AliasSeq!(bool, char[2], clampIdx!(0, 5), clampIdx!(1, 6)); //ditto for 3 byte - alias Utf8Spec3 = TypeTuple!(bool, char[3], + alias Utf8Spec3 = AliasSeq!(bool, char[3], clampIdx!(0, 4), clampIdx!(1, 6), clampIdx!(2, 6) ); //ditto for 4 byte - alias Utf8Spec4 = TypeTuple!(bool, char[4], + alias Utf8Spec4 = AliasSeq!(bool, char[4], clampIdx!(0, 3), clampIdx!(1, 6), clampIdx!(2, 6), clampIdx!(3, 6) ); - alias Tables = TypeTuple!( + alias Tables = AliasSeq!( typeof(TrieBuilder!(AsciiSpec)(false).build()), typeof(TrieBuilder!(Utf8Spec2)(false).build()), typeof(TrieBuilder!(Utf8Spec3)(false).build()), @@ -4694,8 +4742,8 @@ template Utf8Matcher() ); alias Table(int size) = Tables[size-1]; - enum leadMask(size_t size) = (cast(size_t)1<<(7 - size))-1; - enum encMask(size_t size) = ((1<<size)-1)<<(8-size); + enum leadMask(size_t size) = (cast(size_t) 1<<(7 - size))-1; + enum encMask(size_t size) = ((1 << size)-1)<<(8-size); char truncate()(char ch) pure @safe { @@ -4707,32 +4755,32 @@ template Utf8Matcher() else { badEncoding(); - return cast(char)0; + return cast(char) 0; } } static auto encode(size_t sz)(dchar ch) - if(sz > 1) + if (sz > 1) { import std.utf : encode; char[4] buf; std.utf.encode(buf, ch); char[sz] ret; buf[0] &= leadMask!sz; - foreach(n; 1..sz) + foreach (n; 1 .. sz) buf[n] = buf[n] & 0x3f; //keep 6 lower bits - ret[] = buf[0..sz]; + ret[] = buf[0 .. sz]; return ret; } auto build(Set)(Set set) { - import std.algorithm : map; + import std.algorithm.iteration : map; auto ascii = set & unicode.ASCII; auto utf8_2 = set & CodepointSet(0x80, 0x800); auto utf8_3 = set & CodepointSet(0x800, 0x1_0000); auto utf8_4 = set & CodepointSet(0x1_0000, lastDchar+1); - auto asciiT = ascii.byCodepoint.map!(x=>cast(char)x).buildTrie!(AsciiSpec); + auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec); auto utf8_2T = utf8_2.byCodepoint.map!(x=>encode!2(x)).buildTrie!(Utf8Spec2); auto utf8_3T = utf8_3.byCodepoint.map!(x=>encode!3(x)).buildTrie!(Utf8Spec3); auto utf8_4T = utf8_4.byCodepoint.map!(x=>encode!4(x)).buildTrie!(Utf8Spec4); @@ -4745,6 +4793,7 @@ template Utf8Matcher() mixin template DefMatcher() { import std.format : format; + import std.meta : Erase, staticIndexOf; enum hasASCII = staticIndexOf!(1, Sizes) >= 0; alias UniSizes = Erase!(1, Sizes); @@ -4752,7 +4801,7 @@ template Utf8Matcher() static auto genDispatch() { string code; - foreach(size; UniSizes) + foreach (size; UniSizes) code ~= format(q{ if ((ch & ~leadMask!%d) == encMask!(%d)) return lookup!(%d, mode)(inp); @@ -4766,18 +4815,18 @@ template Utf8Matcher() } enum dispatch = genDispatch(); - public bool match(Range)(ref Range inp) const pure @trusted - if(isRandomAccessRange!Range && is(ElementType!Range : char)) + public bool match(Range)(ref Range inp) const pure + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { enum mode = Mode.skipOnMatch; assert(!inp.empty); - auto ch = inp[0]; - static if(hasASCII) + immutable ch = inp[0]; + static if (hasASCII) { if (ch < 0x80) { - bool r = tab!1[ch]; - if(r) + immutable r = tab!1[ch]; + if (r) inp.popFront(); return r; } @@ -4788,15 +4837,15 @@ template Utf8Matcher() mixin(dispatch); } - static if(Sizes.length == 4) // can skip iff can detect all encodings + static if (Sizes.length == 4) // can skip iff can detect all encodings { public bool skip(Range)(ref Range inp) const pure @trusted - if(isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { enum mode = Mode.alwaysSkip; assert(!inp.empty); auto ch = inp[0]; - static if(hasASCII) + static if (hasASCII) { if (ch < 0x80) { @@ -4812,12 +4861,12 @@ template Utf8Matcher() } public bool test(Range)(ref Range inp) const pure @trusted - if(isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { enum mode = Mode.neverSkip; assert(!inp.empty); auto ch = inp[0]; - static if(hasASCII) + static if (hasASCII) { if (ch < 0x80) return tab!1[ch]; @@ -4829,19 +4878,19 @@ template Utf8Matcher() } bool match(C)(ref C[] str) const pure @trusted - if(isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const pure @trusted - if(isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const pure @trusted - if(isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -4851,6 +4900,7 @@ template Utf8Matcher() struct Impl(Sizes...) { + import std.meta : allSatisfy, staticMap; static assert(allSatisfy!(validSize, Sizes), "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8"); private: @@ -4868,46 +4918,46 @@ template Utf8Matcher() bool lookup(int size, Mode mode, Range)(ref Range inp) const pure @trusted { - import std.typecons; - if(inp.length < size) + import std.typecons : staticIota; + if (inp.length < size) { badEncoding(); return false; } char[size] needle = void; needle[0] = leadMask!size & inp[0]; - foreach(i; staticIota!(1, size)) + foreach (i; staticIota!(1, size)) { needle[i] = truncate(inp[i]); } //overlong encoding checks - static if(size == 2) + static if (size == 2) { //0x80-0x7FF //got 6 bits in needle[1], must use at least 8 bits //must use at least 2 bits in needle[1] - if(needle[0] < 2) badEncoding(); + if (needle[0] < 2) badEncoding(); } - else static if(size == 3) + else static if (size == 3) { //0x800-0xFFFF //got 6 bits in needle[2], must use at least 12bits //must use 6 bits in needle[1] or anything in needle[0] - if(needle[0] == 0 && needle[1] < 0x20) badEncoding(); + if (needle[0] == 0 && needle[1] < 0x20) badEncoding(); } - else static if(size == 4) + else static if (size == 4) { //0x800-0xFFFF - //got 2x6=12 bits in needle[2..3] must use at least 17bits + //got 2x6=12 bits in needle[2 .. 3] must use at least 17bits //must use 5 bits (or above) in needle[1] or anything in needle[0] - if(needle[0] == 0 && needle[1] < 0x10) badEncoding(); + if (needle[0] == 0 && needle[1] < 0x10) badEncoding(); } - static if(mode == Mode.alwaysSkip) + static if (mode == Mode.alwaysSkip) { inp.popFrontN(size); return tab!size[needle]; } - else static if(mode == Mode.neverSkip) + else static if (mode == Mode.neverSkip) { return tab!size[needle]; } @@ -4927,6 +4977,7 @@ template Utf8Matcher() struct CherryPick(I, Sizes...) { + import std.meta : allSatisfy; static assert(allSatisfy!(validSize, Sizes), "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8"); private: @@ -4942,23 +4993,22 @@ template Utf8Matcher() template Utf16Matcher() { - enum validSize(int sz) = sz >= 1 && sz <=2; + enum validSize(int sz) = sz >= 1 && sz <= 2; void badEncoding() pure { - import std.utf; + import std.utf : UTFException; throw new UTFException("Invalid UTF-16 sequence"); } - alias Seq = TypeTuple; // 1-stage ASCII - alias AsciiSpec = Seq!(bool, wchar, clamp!7); + alias AsciiSpec = AliasSeq!(bool, wchar, clamp!7); //2-stage BMP - alias BmpSpec = Seq!(bool, wchar, sliceBits!(7, 16), sliceBits!(0, 7)); + alias BmpSpec = AliasSeq!(bool, wchar, sliceBits!(7, 16), sliceBits!(0, 7)); //4-stage - full Unicode //assume that 0xD800 & 0xDC00 bits are cleared //thus leaving 10 bit per wchar to worry about - alias UniSpec = Seq!(bool, wchar[2], + alias UniSpec = AliasSeq!(bool, wchar[2], assumeSize!(x=>x[0]>>4, 6), assumeSize!(x=>x[0]&0xf, 4), assumeSize!(x=>x[1]>>6, 4), assumeSize!(x=>x[1]&0x3f, 6), ); @@ -4972,20 +5022,20 @@ template Utf16Matcher() assert(ch <= 0xF_FFFF); wchar[2] ret; //do not put surrogate bits, they are sliced off - ret[0] = (ch>>10); + ret[0] = cast(wchar)(ch >> 10); ret[1] = (ch & 0xFFF); return ret; } auto build(Set)(Set set) { - import std.algorithm : map; + import std.algorithm.iteration : map; auto ascii = set & unicode.ASCII; auto bmp = (set & CodepointSet.fromIntervals(0x80, 0xFFFF+1)) - CodepointSet.fromIntervals(0xD800, 0xDFFF+1); auto other = set - (bmp | ascii); - auto asciiT = ascii.byCodepoint.map!(x=>cast(char)x).buildTrie!(AsciiSpec); - auto bmpT = bmp.byCodepoint.map!(x=>cast(wchar)x).buildTrie!(BmpSpec); + auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec); + auto bmpT = bmp.byCodepoint.map!(x=>cast(wchar) x).buildTrie!(BmpSpec); auto otherT = other.byCodepoint.map!(x=>encode2(x)).buildTrie!(UniSpec); alias Ret = Impl!(1,2); return Ret(asciiT, bmpT, otherT); @@ -4996,12 +5046,12 @@ template Utf16Matcher() mixin template DefMatcher() { public bool match(Range)(ref Range inp) const pure @trusted - if(isRandomAccessRange!Range && is(ElementType!Range : wchar)) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar)) { enum mode = Mode.skipOnMatch; assert(!inp.empty); - auto ch = inp[0]; - static if(sizeFlags & 1) + immutable ch = inp[0]; + static if (sizeFlags & 1) { if (ch < 0x80) { @@ -5019,15 +5069,15 @@ template Utf16Matcher() return lookupUni!mode(inp); } - static if(Sizes.length == 2) + static if (Sizes.length == 2) { public bool skip(Range)(ref Range inp) const pure @trusted - if(isRandomAccessRange!Range && is(ElementType!Range : wchar)) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar)) { enum mode = Mode.alwaysSkip; assert(!inp.empty); - auto ch = inp[0]; - static if(sizeFlags & 1) + immutable ch = inp[0]; + static if (sizeFlags & 1) { if (ch < 0x80) { @@ -5043,31 +5093,31 @@ template Utf16Matcher() } public bool test(Range)(ref Range inp) const pure @trusted - if(isRandomAccessRange!Range && is(ElementType!Range : wchar)) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar)) { enum mode = Mode.neverSkip; assert(!inp.empty); auto ch = inp[0]; - static if(sizeFlags & 1) + static if (sizeFlags & 1) return ch < 0x80 ? ascii[ch] : lookupUni!mode(inp); else return lookupUni!mode(inp); } bool match(C)(ref C[] str) const pure @trusted - if(isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const pure @trusted - if(isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const pure @trusted - if(isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5076,22 +5126,23 @@ template Utf16Matcher() } struct Impl(Sizes...) - if(Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: + import std.meta : allSatisfy; static assert(allSatisfy!(validSize, Sizes), "Only lengths of 1 and 2 code units are possible in UTF-16"); - static if(Sizes.length > 1) + static if (Sizes.length > 1) enum sizeFlags = Sizes[0] | Sizes[1]; else enum sizeFlags = Sizes[0]; - static if(sizeFlags & 1) + static if (sizeFlags & 1) { Ascii ascii; Bmp bmp; } - static if(sizeFlags & 2) + static if (sizeFlags & 2) { Uni uni; } @@ -5106,16 +5157,16 @@ template Utf16Matcher() { wchar x = cast(wchar)(inp[0] - 0xD800); //not a high surrogate - if(x > 0x3FF) + if (x > 0x3FF) { //low surrogate - if(x <= 0x7FF) badEncoding(); - static if(sizeFlags & 1) + if (x <= 0x7FF) badEncoding(); + static if (sizeFlags & 1) { auto ch = inp[0]; - static if(mode == Mode.alwaysSkip) + static if (mode == Mode.alwaysSkip) inp.popFront(); - static if(mode == Mode.skipOnMatch) + static if (mode == Mode.skipOnMatch) { if (bmp[ch]) { @@ -5133,18 +5184,18 @@ template Utf16Matcher() } else { - static if(sizeFlags & 2) + static if (sizeFlags & 2) { - if(inp.length < 2) + if (inp.length < 2) badEncoding(); wchar y = cast(wchar)(inp[1] - 0xDC00); //not a low surrogate - if(y > 0x3FF) + if (y > 0x3FF) badEncoding(); wchar[2] needle = [inp[0] & 0x3ff, inp[1] & 0x3ff]; - static if(mode == Mode.alwaysSkip) + static if (mode == Mode.alwaysSkip) inp.popFrontN(2); - static if(mode == Mode.skipOnMatch) + static if (mode == Mode.skipOnMatch) { if (uni[needle]) { @@ -5164,13 +5215,14 @@ template Utf16Matcher() } struct CherryPick(I, Sizes...) - if(Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: + import std.meta : allSatisfy; I* m; enum sizeFlags = I.sizeFlags; - static if(sizeFlags & 1) + static if (sizeFlags & 1) { @property ref ascii()() const pure{ return m.ascii; } } @@ -5203,13 +5255,13 @@ private auto utf16Matcher(Set)(Set set) @trusted See $(LREF MatcherConcept) for API outline. */ public auto utfMatcher(Char, Set)(Set set) @trusted - if(isCodepointSet!Set) +if (isCodepointSet!Set) { - static if(is(Char : char)) + static if (is(Char : char)) return utf8Matcher(set); - else static if(is(Char : wchar)) + else static if (is(Char : wchar)) return utf16Matcher(set); - else static if(is(Char : dchar)) + else static if (is(Char : dchar)) static assert(false, "UTF-32 needs no decoding, and thus not supported by utfMatcher"); else @@ -5218,8 +5270,8 @@ public auto utfMatcher(Char, Set)(Set set) @trusted //a range of code units, packed with index to speed up forward iteration -package auto decoder(C)(C[] s, size_t offset=0) @trusted - if(is(C : wchar) || is(C : char)) +package auto decoder(C)(C[] s, size_t offset=0) @safe pure nothrow @nogc +if (is(C : wchar) || is(C : char)) { static struct Decoder { @@ -5236,44 +5288,15 @@ package auto decoder(C)(C[] s, size_t offset=0) @trusted auto opIndex(size_t i){ return str[idx+i]; } @property size_t length(){ return str.length - idx; } alias opDollar = length; - auto opSlice(size_t a, size_t b){ return Decoder(str[0..idx+b], idx+a); } + auto opSlice(size_t a, size_t b){ return Decoder(str[0 .. idx+b], idx+a); } } static assert(isRandomAccessRange!Decoder); static assert(is(ElementType!Decoder : C)); return Decoder(s, offset); } -/* - Expose UTF string $(D s) as a random-access - range of $(S_LINK Code unit, code units). -*/ -package auto units(C)(C[] s) - if(is(C : wchar) || is(C : char)) -{ - static struct Units - { - pure nothrow: - C[] str; - @property C front(){ return str[0]; } - @property C back(){ return str[$-1]; } - void popFront(){ str = str[1..$]; } - void popBack(){ str = str[0..$-1]; } - void popFrontN(size_t n){ str = str[n..$]; } - @property bool empty(){ return 0 == str.length; } - @property auto save(){ return this; } - auto opIndex(size_t i){ return str[i]; } - @property size_t length(){ return str.length; } - alias opDollar = length; - auto opSlice(size_t a, size_t b){ return Units(str[a..b]); } - } - static assert(isRandomAccessRange!Units); - static assert(is(ElementType!Units : C)); - return Units(s); -} - @safe unittest { - import std.range; string rs = "hi! ネемног砀 текÑта"; auto codec = rs.decoder; auto utf8 = utf8Matcher(unicode.Letter); @@ -5297,7 +5320,7 @@ package auto units(C)(C[] s) assert(!utf8.test(codec)); assert(!utf8.skip(codec)); assert(utf8.test(codec)); - foreach(i; 0..7) + foreach (i; 0 .. 7) { assert(!asc.test(codec)); assert(uni.test(codec)); @@ -5316,7 +5339,7 @@ package auto units(C)(C[] s) assert(!utf8.skip(codec)); assert(!utf8.skip(codec)); - foreach(i; 0..7) + foreach (i; 0 .. 7) { assert(!asc.test(codec)); assert(utf8.test(codec)); @@ -5329,7 +5352,7 @@ package auto units(C)(C[] s) @safe unittest { - import std.range; + import std.range : stride; static bool testAll(Matcher, Range)(ref Matcher m, ref Range r) { bool t = m.test(r); @@ -5337,7 +5360,7 @@ package auto units(C)(C[] s) assert(t == m.match(r)); assert(r.idx == save || t); //ether no change or was match r.idx = save; - static if(is(typeof(m.skip(r)))) + static if (is(typeof(m.skip(r)))) { assert(t == m.skip(r)); assert(r.idx != save); //always changed @@ -5353,15 +5376,15 @@ package auto units(C)(C[] s) auto uni2 = utf8.subMatcher!2; auto uni3 = utf8.subMatcher!3; auto uni24 = utf8.subMatcher!(2,4); - foreach(ch; unicode.L.byCodepoint.stride(3)) + foreach (ch; unicode.L.byCodepoint.stride(3)) { import std.utf : encode; char[4] buf; wchar[2] buf16; - auto len = std.utf.encode(buf, ch); - auto len16 = std.utf.encode(buf16, ch); - auto c8 = buf[0..len].decoder; - auto c16 = buf16[0..len16].decoder; + auto len = encode(buf, ch); + auto len16 = encode(buf16, ch); + auto c8 = buf[0 .. len].decoder; + auto c16 = buf16[0 .. len16].decoder; assert(testAll(utf16, c16)); assert(testAll(bmp, c16) || len16 != 1); assert(testAll(nonBmp, c16) || len16 != 2); @@ -5372,36 +5395,36 @@ package auto units(C)(C[] s) assert(testAll(ascii, c8) || len != 1); assert(testAll(uni2, c8) || len != 2); assert(testAll(uni3, c8) || len != 3); - assert(testAll(uni24, c8) || (len != 2 && len !=4)); + assert(testAll(uni24, c8) || (len != 2 && len != 4)); } } // cover decode fail cases of Matcher -unittest +@system unittest { + import std.algorithm.iteration : map; import std.exception : collectException; import std.format : format; - import std.algorithm; auto utf16 = utfMatcher!wchar(unicode.L); auto utf8 = utfMatcher!char(unicode.L); //decode failure cases UTF-8 - alias fails8 = TypeTuple!("\xC1", "\x80\x00","\xC0\x00", "\xCF\x79", + alias fails8 = AliasSeq!("\xC1", "\x80\x00","\xC0\x00", "\xCF\x79", "\xFF\x00\0x00\0x00\x00", "\xC0\0x80\0x80\x80", "\x80\0x00\0x00\x00", "\xCF\x00\0x00\0x00\x00"); - foreach(msg; fails8){ + foreach (msg; fails8) + { assert(collectException((){ auto s = msg; - import std.utf; size_t idx = 0; - //decode(s, idx); utf8.test(s); - }()), format("%( %2x %)", cast(ubyte[])msg)); + }()), format("%( %2x %)", cast(ubyte[]) msg)); } //decode failure cases UTF-16 - alias fails16 = TypeTuple!([0xD811], [0xDC02]); - foreach(msg; fails16){ + alias fails16 = AliasSeq!([0xD811], [0xDC02]); + foreach (msg; fails16) + { assert(collectException((){ - auto s = msg.map!(x => cast(wchar)x); + auto s = msg.map!(x => cast(wchar) x); utf16.test(s); }())); } @@ -5427,15 +5450,15 @@ unittest +/ public auto toTrie(size_t level, Set)(Set set) - if(isCodepointSet!Set) +if (isCodepointSet!Set) { - static if(level == 1) + static if (level == 1) return codepointSetTrie!(21)(set); - else static if(level == 2) + else static if (level == 2) return codepointSetTrie!(10, 11)(set); - else static if(level == 3) + else static if (level == 3) return codepointSetTrie!(8, 5, 8)(set); - else static if(level == 4) + else static if (level == 4) return codepointSetTrie!(6, 4, 4, 7)(set); else static assert(false, @@ -5453,7 +5476,7 @@ public auto toTrie(size_t level, Set)(Set set) See the $(S_LINK Synopsis, Synopsis) section for example. */ public auto toDelegate(Set)(Set set) - if(isCodepointSet!Set) +if (isCodepointSet!Set) { // 3 is very small and is almost as fast as 2-level (due to CPU caches?) auto t = toTrie!3(set); @@ -5475,7 +5498,7 @@ public auto toDelegate(Set)(Set set) operator to perform the conversion.) */ struct BitPacked(T, size_t sz) - if(isIntegral!T || is(T:dchar)) +if (isIntegral!T || is(T:dchar)) { enum bitSize = sz; T _value; @@ -5488,14 +5511,15 @@ struct BitPacked(T, size_t sz) or a return type of a given functor. */ template bitSizeOf(Args...) - if(Args.length == 1) +if (Args.length == 1) { + import std.traits : ReturnType; alias T = Args[0]; - static if(__traits(compiles, { size_t val = T.bitSize; })) //(is(typeof(T.bitSize) : size_t)) + static if (__traits(compiles, { size_t val = T.bitSize; })) //(is(typeof(T.bitSize) : size_t)) { enum bitSizeOf = T.bitSize; } - else static if(is(ReturnType!T dummy == BitPacked!(U, bits), U, size_t bits)) + else static if (is(ReturnType!T dummy == BitPacked!(U, bits), U, size_t bits)) { enum bitSizeOf = bitSizeOf!(ReturnType!T); } @@ -5511,7 +5535,7 @@ template bitSizeOf(Args...) */ template isBitPacked(T) { - static if(is(T dummy == BitPacked!(U, bits), U, size_t bits)) + static if (is(T dummy == BitPacked!(U, bits), U, size_t bits)) enum isBitPacked = true; else enum isBitPacked = false; @@ -5523,7 +5547,7 @@ template isBitPacked(T) */ template TypeOfBitPacked(T) { - static if(is(T dummy == BitPacked!(U, bits), U, size_t bits)) + static if (is(T dummy == BitPacked!(U, bits), U, size_t bits)) alias TypeOfBitPacked = U; else alias TypeOfBitPacked = T; @@ -5556,20 +5580,20 @@ struct sliceBits(size_t from, size_t to) static auto opCall(T)(T x) out(result) { - assert(result < (1<<to-from)); + assert(result < (1 << to-from)); } body { static assert(from < to); - static if(from == 0) - return x & ((1<<to)-1); + static if (from == 0) + return x & ((1 << to)-1); else return (x >> from) & ((1<<(to-from))-1); } } -uint low_8(uint x) { return x&0xFF; } -@safe pure nothrow uint midlow_8(uint x){ return (x&0xFF00)>>8; } +@safe pure nothrow @nogc uint low_8(uint x) { return x&0xFF; } +@safe pure nothrow @nogc uint midlow_8(uint x){ return (x&0xFF00)>>8; } alias lo8 = assumeSize!(low_8, 8); alias mlo8 = assumeSize!(midlow_8, 8); @@ -5579,25 +5603,27 @@ static assert(bitSizeOf!(BitPacked!(uint, 2)) == 2); template Sequence(size_t start, size_t end) { - static if(start < end) - alias Sequence = TypeTuple!(start, Sequence!(start+1, end)); + static if (start < end) + alias Sequence = AliasSeq!(start, Sequence!(start+1, end)); else - alias Sequence = TypeTuple!(); + alias Sequence = AliasSeq!(); } //---- TRIE TESTS ---- -unittest +@system unittest { - import std.conv; - import std.algorithm; - import std.range; + import std.algorithm.iteration : map; + import std.algorithm.sorting : sort; + import std.array : array; + import std.conv : text, to; + import std.range : iota; static trieStats(TRIE)(TRIE t) { version(std_uni_stats) { - import std.stdio; + import std.stdio : writefln, writeln; writeln("---TRIE FOOTPRINT STATS---"); - foreach(i; staticIota!(0, t.table.dim) ) + foreach (i; staticIota!(0, t.table.dim) ) { writefln("lvl%s = %s bytes; %s pages" , i, t.bytes!i, t.pages!i); @@ -5606,8 +5632,8 @@ unittest version(none) { writeln("INDEX (excluding value level):"); - foreach(i; staticIota!(0, t.table.dim-1) ) - writeln(t.table.slice!(i)[0..t.table.length!i]); + foreach (i; staticIota!(0, t.table.dim-1) ) + writeln(t.table.slice!(i)[0 .. t.table.length!i]); } writeln("---------------------------"); } @@ -5615,16 +5641,16 @@ unittest //@@@BUG link failure, lambdas not found by linker somehow (in case of trie2) // alias lo8 = assumeSize!(8, function (uint x) { return x&0xFF; }); // alias next8 = assumeSize!(7, function (uint x) { return (x&0x7F00)>>8; }); - alias CodepointSet Set; + alias Set = CodepointSet; auto set = Set('A','Z','a','z'); auto trie = buildTrie!(bool, uint, 256, lo8)(set.byInterval);// simple bool array - for(int a='a'; a<'z';a++) + for (int a='a'; a<'z';a++) assert(trie[a]); - for(int a='A'; a<'Z';a++) + for (int a='A'; a<'Z';a++) assert(trie[a]); - for(int a=0; a<'A'; a++) + for (int a=0; a<'A'; a++) assert(!trie[a]); - for(int a ='Z'; a<'a'; a++) + for (int a ='Z'; a<'a'; a++) assert(!trie[a]); trieStats(trie); @@ -5632,9 +5658,9 @@ unittest 1, 18, 256+2, 256+111, 512+1, 512+18, 768+2, 768+111); auto trie2 = buildTrie!(bool, uint, 1024, mlo8, lo8)(redundant2.byInterval); trieStats(trie2); - foreach(e; redundant2.byCodepoint) - assert(trie2[e], text(cast(uint)e, " - ", trie2[e])); - foreach(i; 0..1024) + foreach (e; redundant2.byCodepoint) + assert(trie2[e], text(cast(uint) e, " - ", trie2[e])); + foreach (i; 0 .. 1024) { assert(trie2[i] == (i in redundant2)); } @@ -5652,8 +5678,8 @@ unittest sliceBits!(6,8), sliceBits!(4,6), sliceBits!(0,4) )(redundant3.byInterval); trieStats(trie3); - foreach(i; 0..max3) - assert(trie3[i] == (i in redundant3), text(cast(uint)i)); + foreach (i; 0 .. max3) + assert(trie3[i] == (i in redundant3), text(cast(uint) i)); auto redundant4 = Set( 10, 64, 64+10, 128, 128+10, 256, 256+10, 512, @@ -5663,9 +5689,10 @@ unittest auto trie4 = buildTrie!(bool, size_t, max4, sliceBits!(13, 16), sliceBits!(9, 13), sliceBits!(6, 9) , sliceBits!(0, 6) )(redundant4.byInterval); - foreach(i; 0..max4){ - if(i in redundant4) - assert(trie4[i], text(cast(uint)i)); + foreach (i; 0 .. max4) + { + if (i in redundant4) + assert(trie4[i], text(cast(uint) i)); } trieStats(trie4); @@ -5683,12 +5710,12 @@ unittest auto a = array(map!(x => to!ubyte(x))(iota(0, 256))); auto bt = buildTrie!(bool, ubyte, sliceBits!(7, 8), sliceBits!(5, 7), sliceBits!(0, 5))(a); trieStats(bt); - foreach(i; 0..256) - assert(bt[cast(ubyte)i]); + foreach (i; 0 .. 256) + assert(bt[cast(ubyte) i]); } template useItemAt(size_t idx, T) - if(isIntegral!T || is(T: dchar)) +if (isIntegral!T || is(T: dchar)) { size_t impl(in T[] arr){ return arr[idx]; } alias useItemAt = assumeSize!(impl, 8*T.sizeof); @@ -5702,7 +5729,7 @@ template useLastItem(T) template fullBitSize(Prefix...) { - static if(Prefix.length > 0) + static if (Prefix.length > 0) enum fullBitSize = bitSizeOf!(Prefix[0])+fullBitSize!(Prefix[1..$]); else enum fullBitSize = 0; @@ -5710,9 +5737,9 @@ template fullBitSize(Prefix...) template idxTypes(Key, size_t fullBits, Prefix...) { - static if(Prefix.length == 1) + static if (Prefix.length == 1) {// the last level is value level, so no index once reduced to 1-level - alias idxTypes = TypeTuple!(); + alias idxTypes = AliasSeq!(); } else { @@ -5722,7 +5749,7 @@ template idxTypes(Key, size_t fullBits, Prefix...) // thus it's size in pages is full_bit_width - size_of_last_prefix // Recourse on this notion alias idxTypes = - TypeTuple!( + AliasSeq!( idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]), BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1])) ); @@ -5731,22 +5758,25 @@ template idxTypes(Key, size_t fullBits, Prefix...) //============================================================================ -@trusted int comparePropertyName(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) +@safe pure int comparePropertyName(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) +if (is(Char1 : dchar) && is(Char2 : dchar)) { + import std.algorithm.comparison : cmp; + import std.algorithm.iteration : map, filter; import std.ascii : toLower; - import std.algorithm : cmp, map, filter; static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';} return cmp( a.map!toLower.filter!pred, b.map!toLower.filter!pred); } -unittest +@safe pure unittest { assert(!comparePropertyName("foo-bar", "fooBar")); } -bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) +bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe pure +if (is(Char1 : dchar) && is(Char2 : dchar)) { return comparePropertyName(a, b) < 0; } @@ -5758,17 +5788,17 @@ bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe void compressTo(uint val, ref ubyte[] arr) pure nothrow { // not optimized as usually done 1 time (and not public interface) - if(val < 128) - arr ~= cast(ubyte)val; - else if(val < (1<<13)) + if (val < 128) + arr ~= cast(ubyte) val; + else if (val < (1 << 13)) { - arr ~= (0b1_00<<5) | cast(ubyte)(val>>8); + arr ~= (0b1_00 << 5) | cast(ubyte)(val >> 8); arr ~= val & 0xFF; } else { - assert(val < (1<<21)); - arr ~= (0b1_01<<5) | cast(ubyte)(val>>16); + assert(val < (1 << 21)); + arr ~= (0b1_01 << 5) | cast(ubyte)(val >> 16); arr ~= (val >> 8) & 0xFF; arr ~= val & 0xFF; } @@ -5777,30 +5807,30 @@ bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe uint decompressFrom(const(ubyte)[] arr, ref size_t idx) pure { import std.exception : enforce; - uint first = arr[idx++]; - if(!(first & 0x80)) // no top bit -> [0..127] + immutable first = arr[idx++]; + if (!(first & 0x80)) // no top bit -> [0 .. 127] return first; - uint extra = ((first>>5) & 1) + 1; // [1, 2] + immutable extra = ((first >> 5) & 1) + 1; // [1, 2] uint val = (first & 0x1F); enforce(idx + extra <= arr.length, "bad code point interval encoding"); - foreach(j; 0..extra) - val = (val<<8) | arr[idx+j]; + foreach (j; 0 .. extra) + val = (val << 8) | arr[idx+j]; idx += extra; return val; } package ubyte[] compressIntervals(Range)(Range intervals) - if(isInputRange!Range && isIntegralPair!(ElementType!Range)) +if (isInputRange!Range && isIntegralPair!(ElementType!Range)) { ubyte[] storage; uint base = 0; // RLE encode - foreach(val; intervals) + foreach (val; intervals) { compressTo(val[0]-base, storage); base = val[0]; - if(val[1] != lastDchar+1) // till the end of the domain so don't store it + if (val[1] != lastDchar+1) // till the end of the domain so don't store it { compressTo(val[1]-base, storage); base = val[1]; @@ -5809,25 +5839,27 @@ package ubyte[] compressIntervals(Range)(Range intervals) return storage; } -unittest +@safe pure unittest { - import std.typecons; - auto run = [tuple(80, 127), tuple(128, (1<<10)+128)]; - ubyte[] enc = [cast(ubyte)80, 47, 1, (0b1_00<<5) | (1<<2), 0]; + import std.algorithm.comparison : equal; + import std.typecons : tuple; + + auto run = [tuple(80, 127), tuple(128, (1 << 10)+128)]; + ubyte[] enc = [cast(ubyte) 80, 47, 1, (0b1_00 << 5) | (1 << 2), 0]; assert(compressIntervals(run) == enc); - auto run2 = [tuple(0, (1<<20)+512+1), tuple((1<<20)+512+4, lastDchar+1)]; - ubyte[] enc2 = [cast(ubyte)0, (0b1_01<<5) | (1<<4), 2, 1, 3]; // odd length-ed + auto run2 = [tuple(0, (1 << 20)+512+1), tuple((1 << 20)+512+4, lastDchar+1)]; + ubyte[] enc2 = [cast(ubyte) 0, (0b1_01 << 5) | (1 << 4), 2, 1, 3]; // odd length-ed assert(compressIntervals(run2) == enc2); size_t idx = 0; assert(decompressFrom(enc, idx) == 80); assert(decompressFrom(enc, idx) == 47); assert(decompressFrom(enc, idx) == 1); - assert(decompressFrom(enc, idx) == (1<<10)); + assert(decompressFrom(enc, idx) == (1 << 10)); idx = 0; assert(decompressFrom(enc2, idx) == 0); - assert(decompressFrom(enc2, idx) == (1<<20)+512+1); - assert(equalS(decompressIntervals(compressIntervals(run)), run)); - assert(equalS(decompressIntervals(compressIntervals(run2)), run2)); + assert(decompressFrom(enc2, idx) == (1 << 20)+512+1); + assert(equal(decompressIntervals(compressIntervals(run)), run)); + assert(equal(decompressIntervals(compressIntervals(run2)), run2)); } // Creates a range of $(D CodepointInterval) that lazily decodes compressed data. @@ -5836,7 +5868,7 @@ unittest return DecompressedIntervals(data); } -@trusted struct DecompressedIntervals +@safe struct DecompressedIntervals { pure: const(ubyte)[] _stream; @@ -5857,14 +5889,14 @@ pure: void popFront() { - if(_idx == _stream.length) + if (_idx == _stream.length) { _idx = size_t.max; return; } uint base = _front[1]; _front[0] = base + decompressFrom(_stream, _idx); - if(_idx == _stream.length)// odd length ---> till the end + if (_idx == _stream.length)// odd length ---> till the end _front[1] = lastDchar+1; else { @@ -5892,12 +5924,12 @@ else // helper for looking up code point sets @trusted ptrdiff_t findUnicodeSet(alias table, C)(in C[] name) pure { + import std.algorithm.iteration : map; import std.range : assumeSorted; - import std.algorithm : map; auto range = assumeSorted!((a,b) => propertyNameLess(a,b)) (table.map!"a.name"()); size_t idx = range.lowerBound(name).length; - if(idx < range.length && comparePropertyName(range[idx], name) == 0) + if (idx < range.length && comparePropertyName(range[idx], name) == 0) return idx; return -1; } @@ -5906,7 +5938,7 @@ else @trusted bool loadUnicodeSet(alias table, Set, C)(in C[] name, ref Set dest) pure { auto idx = findUnicodeSet!table(name); - if(idx >= 0) + if (idx >= 0) { dest = Set(asSet(table[idx].compressed)); return true; @@ -5917,9 +5949,10 @@ else @trusted bool loadProperty(Set=CodepointSet, C) (in C[] name, ref Set target) pure { + import std.internal.unicode_tables : uniProps; // generated file alias ucmp = comparePropertyName; // conjure cumulative properties by hand - if(ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0) + if (ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0) { target = asSet(uniProps.Lu); target |= asSet(uniProps.Ll); @@ -5927,25 +5960,25 @@ else target |= asSet(uniProps.Lo); target |= asSet(uniProps.Lm); } - else if(ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter")==0) + else if (ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter")==0) { target = asSet(uniProps.Ll); target |= asSet(uniProps.Lu); target |= asSet(uniProps.Lt);// Title case } - else if(ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0) + else if (ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0) { target = asSet(uniProps.Mn); target |= asSet(uniProps.Mc); target |= asSet(uniProps.Me); } - else if(ucmp(name, "N") == 0 || ucmp(name, "Number") == 0) + else if (ucmp(name, "N") == 0 || ucmp(name, "Number") == 0) { target = asSet(uniProps.Nd); target |= asSet(uniProps.Nl); target |= asSet(uniProps.No); } - else if(ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0) + else if (ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0) { target = asSet(uniProps.Pc); target |= asSet(uniProps.Pd); @@ -5955,20 +5988,20 @@ else target |= asSet(uniProps.Pf); target |= asSet(uniProps.Po); } - else if(ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0) + else if (ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0) { target = asSet(uniProps.Sm); target |= asSet(uniProps.Sc); target |= asSet(uniProps.Sk); target |= asSet(uniProps.So); } - else if(ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0) + else if (ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0) { target = asSet(uniProps.Zs); target |= asSet(uniProps.Zl); target |= asSet(uniProps.Zp); } - else if(ucmp(name, "C") == 0 || ucmp(name, "Other") == 0) + else if (ucmp(name, "C") == 0 || ucmp(name, "Other") == 0) { target = asSet(uniProps.Co); target |= asSet(uniProps.Lo); @@ -5976,7 +6009,8 @@ else target |= asSet(uniProps.So); target |= asSet(uniProps.Po); } - else if(ucmp(name, "graphical") == 0){ + else if (ucmp(name, "graphical") == 0) + { target = asSet(uniProps.Alphabetic); target |= asSet(uniProps.Mn); @@ -6002,9 +6036,9 @@ else target |= asSet(uniProps.Sk); target |= asSet(uniProps.So); } - else if(ucmp(name, "any") == 0) + else if (ucmp(name, "any") == 0) target = Set.fromIntervals(0, 0x110000); - else if(ucmp(name, "ascii") == 0) + else if (ucmp(name, "ascii") == 0) target = Set.fromIntervals(0, 0x80); else return loadUnicodeSet!(uniProps.tab)(name, target); @@ -6014,7 +6048,7 @@ else // CTFE-only helper for checking property names at compile-time @safe bool isPrettyPropertyName(C)(in C[] name) { - import std.algorithm : find; + import std.algorithm.searching : find; auto names = [ "L", "Letter", "LC", "Cased Letter", @@ -6041,11 +6075,11 @@ template SetSearcher(alias table, string kind) { /// Run-time checked search. static auto opCall(C)(in C[] name) - if(is(C : dchar)) + if (is(C : dchar)) { import std.conv : to; CodepointSet set; - if(loadUnicodeSet!table(name, set)) + if (loadUnicodeSet!table(name, set)) return set; throw new Exception("No unicode set for "~kind~" by name " ~name.to!string()~" was found."); @@ -6053,7 +6087,7 @@ template SetSearcher(alias table, string kind) /// Compile-time checked search. static @property auto opDispatch(string name)() { - static if(findSetName!table(name)) + static if (findSetName!table(name)) { CodepointSet set; loadUnicodeSet!table(name, set); @@ -6089,9 +6123,20 @@ template SetSearcher(alias table, string kind) See_Also: $(LREF block), $(LREF script) and (not included in this search) $(LREF hangulSyllableType). + */ - Example: - --- + static @property auto opDispatch(string name)() pure + { + static if (findAny(name)) + return loadAny(name); + else + static assert(false, "No unicode set by name "~name~" was found."); + } + + /// + @safe unittest + { + import std.exception : collectException; auto ascii = unicode.ASCII; assert(ascii['A']); assert(ascii['~']); @@ -6105,18 +6150,8 @@ template SetSearcher(alias table, string kind) assert(!latin['$']); // BTW Latin 1 Supplement is a block, hence "In" prefix assert(latin == unicode("In Latin 1 Supplement")); - import std.exception; // run-time look up throws if no such set is found assert(collectException(unicode("InCyrilliac"))); - --- - */ - - static @property auto opDispatch(string name)() pure - { - static if(findAny(name)) - return loadAny(name); - else - static assert(false, "No unicode set by name "~name~" was found."); } /** @@ -6130,7 +6165,7 @@ template SetSearcher(alias table, string kind) sets. */ static auto opCall(C)(in C[] name) - if(is(C : dchar)) + if (is(C : dchar)) { return loadAny(name); } @@ -6143,28 +6178,36 @@ template SetSearcher(alias table, string kind) and thus to search use simply $(D unicode.block.BlockName) notation. See $(S_LINK Unicode properties, table of properties) for available sets. - - Example: - --- - // use .block for explicitness - assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic); - --- - See_Also: $(S_LINK Unicode properties, table of properties). */ struct block { + import std.internal.unicode_tables : blocks; // generated file mixin SetSearcher!(blocks.tab, "block"); } + /// + @safe unittest + { + // use .block for explicitness + assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic); + } + /** Narrows down the search for sets of $(CODEPOINTS) to all Unicode scripts. See the $(S_LINK Unicode properties, table of properties) for available sets. + */ + struct script + { + import std.internal.unicode_tables : scripts; // generated file + mixin SetSearcher!(scripts.tab, "script"); + } - Example: - --- + /// + @safe unittest + { auto arabicScript = unicode.script.arabic; auto arabicBlock = unicode.block.arabic; // there is an intersection between script and block @@ -6174,11 +6217,6 @@ template SetSearcher(alias table, string kind) assert(arabicBlock != arabicScript); assert(arabicBlock == unicode.inArabic); assert(arabicScript == unicode.arabic); - --- - */ - struct script - { - mixin SetSearcher!(scripts.tab, "script"); } /** @@ -6191,20 +6229,22 @@ template SetSearcher(alias table, string kind) See the $(S_LINK Unicode properties, table of properties) for available sets. + */ + struct hangulSyllableType + { + import std.internal.unicode_tables : hangul; // generated file + mixin SetSearcher!(hangul.tab, "hangul syllable type"); + } - Example: - --- + /// + @safe unittest + { // L here is syllable type not Letter as in unicode.L short-cut auto leadingVowel = unicode.hangulSyllableType("L"); // check that some leading vowels are present - foreach(vowel; '\u1110'..'\u115F') + foreach (vowel; '\u1110'..'\u115F') assert(leadingVowel[vowel]); assert(leadingVowel == unicode.hangulSyllableType.L); - --- - */ - struct hangulSyllableType - { - mixin SetSearcher!(hangul.tab, "hangul syllable type"); } private: @@ -6212,19 +6252,21 @@ private: static bool findAny(string name) { + import std.internal.unicode_tables : blocks, scripts, uniProps; // generated file return isPrettyPropertyName(name) || findSetName!(uniProps.tab)(name) || findSetName!(scripts.tab)(name) - || (ucmp(name[0..2],"In") == 0 && findSetName!(blocks.tab)(name[2..$])); + || (ucmp(name[0 .. 2],"In") == 0 && findSetName!(blocks.tab)(name[2..$])); } static auto loadAny(Set=CodepointSet, C)(in C[] name) pure { import std.conv : to; + import std.internal.unicode_tables : blocks, scripts; // generated file Set set; - bool loaded = loadProperty(name, set) || loadUnicodeSet!(scripts.tab)(name, set) - || (name.length > 2 && ucmp(name[0..2],"In") == 0 + immutable loaded = loadProperty(name, set) || loadUnicodeSet!(scripts.tab)(name, set) + || (name.length > 2 && ucmp(name[0 .. 2],"In") == 0 && loadUnicodeSet!(blocks.tab)(name[2..$], set)); - if(loaded) + if (loaded) return set; throw new Exception("No unicode set by name "~name.to!string()~" was found."); } @@ -6234,49 +6276,9 @@ private: //@disable ~this(); } -unittest -{ - import std.exception : collectException; - auto ascii = unicode.ASCII; - assert(ascii['A']); - assert(ascii['~']); - assert(!ascii['\u00e0']); - // matching is case-insensitive - assert(ascii == unicode.ascII); - assert(!ascii['à']); - // underscores, '-' and whitespace in names are ignored too - auto latin = unicode.Inlatin1_Supplement; - assert(latin['à']); - assert(!latin['$']); - // BTW Latin 1 Supplement is a block, hence "In" prefix - assert(latin == unicode("In Latin 1 Supplement")); - import std.exception; - // R-T look up throws if no such set is found - assert(collectException(unicode("InCyrilliac"))); - assert(collectException(unicode("X"))); - - assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic); - - // L here is explicitly syllable type not "Letter" as in unicode.L - auto leadingVowel = unicode.hangulSyllableType("L"); - // check that some leading vowels are present - foreach(vowel; '\u1110'..'\u115F'+1) - assert(leadingVowel[vowel]); - assert(leadingVowel == unicode.hangulSyllableType.L); - - auto arabicScript = unicode.script.arabic; - auto arabicBlock = unicode.block.arabic; - // there is an intersection between script and block - assert(arabicBlock['Ø']); - assert(arabicScript['Ø']); - // but they are different - assert(arabicBlock != arabicScript); - assert(arabicBlock == unicode.inArabic); - assert(arabicScript == unicode.arabic); -} - -unittest +@safe unittest { + import std.internal.unicode_tables : blocks, uniProps; // generated file assert(unicode("InHebrew") == asSet(blocks.Hebrew)); assert(unicode("separator") == (asSet(uniProps.Zs) | asSet(uniProps.Zl) | asSet(uniProps.Zp))); assert(unicode("In-Kharoshthi") == asSet(blocks.Kharoshthi)); @@ -6300,13 +6302,14 @@ template genericDecodeGrapheme(bool getValue) { alias graphemeExtend = graphemeExtendTrie; alias spacingMark = mcTrie; - static if(getValue) + static if (getValue) alias Value = Grapheme; else alias Value = void; Value genericDecodeGrapheme(Input)(ref Input range) { + import std.internal.unicode_tables : isHangL, isHangT, isHangV; // generated file enum GraphemeState { Start, CR, @@ -6315,39 +6318,39 @@ template genericDecodeGrapheme(bool getValue) V, LVT } - static if(getValue) + static if (getValue) Grapheme grapheme; auto state = GraphemeState.Start; enum eat = q{ - static if(getValue) + static if (getValue) grapheme ~= ch; range.popFront(); }; dchar ch; assert(!range.empty, "Attempting to decode grapheme from an empty " ~ Input.stringof); - while(!range.empty) + while (!range.empty) { ch = range.front; - final switch(state) with(GraphemeState) + final switch (state) with(GraphemeState) { case Start: mixin(eat); - if(ch == '\r') + if (ch == '\r') state = CR; - else if(isRegionalIndicator(ch)) + else if (isRegionalIndicator(ch)) state = RI; - else if(isHangL(ch)) + else if (isHangL(ch)) state = L; - else if(hangLV[ch] || isHangV(ch)) + else if (hangLV[ch] || isHangV(ch)) state = V; - else if(hangLVT[ch]) + else if (hangLVT[ch]) state = LVT; - else if(isHangT(ch)) + else if (isHangT(ch)) state = LVT; else { - switch(ch) + switch (ch) { mixin(controlSwitch); goto L_End; @@ -6357,24 +6360,24 @@ template genericDecodeGrapheme(bool getValue) } break; case CR: - if(ch == '\n') + if (ch == '\n') mixin(eat); goto L_End_Extend; case RI: - if(isRegionalIndicator(ch)) + if (isRegionalIndicator(ch)) mixin(eat); else goto L_End_Extend; break; case L: - if(isHangL(ch)) + if (isHangL(ch)) mixin(eat); - else if(isHangV(ch) || hangLV[ch]) + else if (isHangV(ch) || hangLV[ch]) { state = V; mixin(eat); } - else if(hangLVT[ch]) + else if (hangLVT[ch]) { state = LVT; mixin(eat); @@ -6383,9 +6386,9 @@ template genericDecodeGrapheme(bool getValue) goto L_End_Extend; break; case V: - if(isHangV(ch)) + if (isHangV(ch)) mixin(eat); - else if(isHangT(ch)) + else if (isHangT(ch)) { state = LVT; mixin(eat); @@ -6394,7 +6397,7 @@ template genericDecodeGrapheme(bool getValue) goto L_End_Extend; break; case LVT: - if(isHangT(ch)) + if (isHangT(ch)) { mixin(eat); } @@ -6404,22 +6407,21 @@ template genericDecodeGrapheme(bool getValue) } } L_End_Extend: - while(!range.empty) + while (!range.empty) { ch = range.front; // extend & spacing marks - if(!graphemeExtend[ch] && !spacingMark[ch]) + if (!graphemeExtend[ch] && !spacingMark[ch]) break; mixin(eat); } L_End: - static if(getValue) + static if (getValue) return grapheme; } } -@trusted: public: // Public API continues /++ @@ -6434,21 +6436,9 @@ public: // Public API continues Returns: length of grapheme cluster - - Example: - --- - // ASCII as usual is 1 code unit, 1 code point etc. - assert(graphemeStride(" ", 1) == 1); - // A + combing ring above - string city = "A\u030Arhus"; - size_t first = graphemeStride(city, 0); - assert(first == 3); //\u030A has 2 UTF-8 code units - assert(city[0..first] == "A\u030A"); - assert(city[first..$] == "rhus"); - --- +/ size_t graphemeStride(C)(in C[] input, size_t index) - if(is(C : dchar)) +if (is(C : dchar)) { auto src = input[index..$]; auto n = src.length; @@ -6456,15 +6446,15 @@ size_t graphemeStride(C)(in C[] input, size_t index) return n - src.length; } -// for now tested separately see test_grapheme.d -unittest +/// +@safe unittest { assert(graphemeStride(" ", 1) == 1); // A + combing ring above string city = "A\u030Arhus"; size_t first = graphemeStride(city, 0); assert(first == 3); //\u030A has 2 UTF-8 code units - assert(city[0..first] == "A\u030A"); + assert(city[0 .. first] == "A\u030A"); assert(city[first..$] == "rhus"); } @@ -6478,25 +6468,27 @@ unittest must be an L-value. +/ Grapheme decodeGrapheme(Input)(ref Input inp) - if(isInputRange!Input && is(Unqual!(ElementType!Input) == dchar)) +if (isInputRange!Input && is(Unqual!(ElementType!Input) == dchar)) { return genericDecodeGrapheme!true(inp); } -unittest +@system unittest { + import std.algorithm.comparison : equal; + Grapheme gr; string s = " \u0020\u0308 "; gr = decodeGrapheme(s); assert(gr.length == 1 && gr[0] == ' '); gr = decodeGrapheme(s); - assert(gr.length == 2 && equalS(gr[0..2], " \u0308")); + assert(gr.length == 2 && equal(gr[0 .. 2], " \u0308")); s = "\u0300\u0308\u1100"; - assert(equalS(decodeGrapheme(s)[], "\u0300\u0308")); - assert(equalS(decodeGrapheme(s)[], "\u1100")); + assert(equal(decodeGrapheme(s)[], "\u0300\u0308")); + assert(equal(decodeGrapheme(s)[], "\u1100")); s = "\u11A8\u0308\uAC01"; - assert(equalS(decodeGrapheme(s)[], "\u11A8\u0308")); - assert(equalS(decodeGrapheme(s)[], "\uAC01")); + assert(equal(decodeGrapheme(s)[], "\u11A8\u0308")); + assert(equal(decodeGrapheme(s)[], "\uAC01")); } /++ @@ -6509,7 +6501,7 @@ unittest $(LREF byCodePoint) +/ auto byGrapheme(Range)(Range range) - if(isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) { // TODO: Bidirectional access static struct Result @@ -6532,7 +6524,7 @@ auto byGrapheme(Range)(Range range) _front = _range.empty ? Grapheme.init : _range.decodeGrapheme(); } - static if(isForwardRange!Range) + static if (isForwardRange!Range) { Result save() @property { @@ -6547,11 +6539,11 @@ auto byGrapheme(Range)(Range range) } /// -unittest +@safe unittest { - import std.conv; - import std.range; - import std.algorithm; + import std.algorithm.comparison : equal; + import std.range : take, drop; + import std.range.primitives : walkLength; auto text = "noe\u0308l"; // noël using e + combining diaeresis assert(text.walkLength == 5); // 5 code points @@ -6573,11 +6565,12 @@ private static struct InputRangeString void popFront() { s.popFront(); } } -unittest +@system unittest { - import std.conv; - import std.range; - import std.algorithm; + import std.algorithm.comparison : equal; + import std.array : array; + import std.range : retro; + import std.range.primitives : walkLength; assert("".byGrapheme.walkLength == 0); auto reverse = "le\u0308on"; @@ -6586,7 +6579,7 @@ unittest auto gReverse = reverse.byGrapheme; assert(gReverse.walkLength == 4); - foreach(text; TypeTuple!("noe\u0308l"c, "noe\u0308l"w, "noe\u0308l"d)) + foreach (text; AliasSeq!("noe\u0308l"c, "noe\u0308l"w, "noe\u0308l"d)) { assert(text.walkLength == 5); static assert(isForwardRange!(typeof(text))); @@ -6611,7 +6604,7 @@ unittest $(P Acts as the identity function when given a range of code points.) +/ auto byCodePoint(Range)(Range range) - if(isInputRange!Range && is(Unqual!(ElementType!Range) == Grapheme)) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == Grapheme)) { // TODO: Propagate bidirectional access static struct Result @@ -6633,14 +6626,14 @@ auto byCodePoint(Range)(Range range) { ++i; - if(i >= _range.front.length) + if (i >= _range.front.length) { _range.popFront(); i = 0; } } - static if(isForwardRange!Range) + static if (isForwardRange!Range) { Result save() @property { @@ -6654,16 +6647,17 @@ auto byCodePoint(Range)(Range range) /// Ditto Range byCodePoint(Range)(Range range) - if(isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) { return range; } /// -unittest +@safe unittest { + import std.array : array; import std.conv : text; - import std.range; + import std.range : retro; string s = "noe\u0308l"; // noël @@ -6677,10 +6671,10 @@ unittest assert(reverse == "le\u0308on"); // lëon } -unittest +@system unittest { - import std.conv; - import std.algorithm; + import std.algorithm.comparison : equal; + import std.range.primitives : walkLength; assert("".byGrapheme.byCodePoint.equal("")); string text = "noe\u0308l"; @@ -6695,6 +6689,8 @@ unittest assert(cpText.walkLength == text.walkLength); } +@trusted: + /++ $(P A structure designed to effectively pack $(CHARACTERS) of a $(CLUSTER). @@ -6706,39 +6702,24 @@ unittest long clusters. ) - Example: - --- - import std.algorithm; - string bold = "ku\u0308hn"; - - // note that decodeGrapheme takes parameter by ref - // slicing a grapheme yields a range of dchar - assert(decodeGrapheme(bold)[].equal("k")); - - // the next grapheme is 2 characters long - auto wideOne = decodeGrapheme(bold); - assert(wideOne.length == 2); - assert(wideOne[].equal("u\u0308")); - - // the usual range manipulation is possible - assert(wideOne[].filter!isMark.equal("\u0308")); - --- - - See_Also: $(LREF decodeGrapheme), $(LREF graphemeStride) -+/ -@trusted struct Grapheme -{ - import std.exception : enforce; + See_Also: $(LREF decodeGrapheme), $(LREF graphemeStride) ++/ +@trusted struct Grapheme +{ + import std.exception : enforce; + import std.traits : isDynamicArray; public: + /// Ctor this(C)(in C[] chars...) - if(is(C : dchar)) + if (is(C : dchar)) { this ~= chars; } + ///ditto this(Input)(Input seq) - if(!isDynamicArray!Input + if (!isDynamicArray!Input && isInputRange!Input && is(ElementType!Input : dchar)) { this ~= seq; @@ -6757,21 +6738,22 @@ public: Warning: Use of this facility may invalidate grapheme cluster, see also $(LREF Grapheme.valid). + +/ + void opIndexAssign(dchar ch, size_t index) pure nothrow @nogc + { + assert(index < length); + write24(isBig ? ptr_ : small_.ptr, ch, index); + } - Example: - --- + /// + @safe unittest + { auto g = Grapheme("A\u0302"); assert(g[0] == 'A'); assert(g.valid); g[1] = '~'; // ASCII tilda is not a combining mark assert(g[1] == '~'); assert(!g.valid); - --- - +/ - void opIndexAssign(dchar ch, size_t index) pure nothrow @nogc - { - assert(index < length); - write24(isBig ? ptr_ : small_.ptr, ch, index); } /++ @@ -6780,13 +6762,13 @@ public: Warning: Invalidates when this Grapheme leaves the scope, attempts to use it then would lead to memory corruption. +/ - @system auto opSlice(size_t a, size_t b) pure nothrow @nogc + @system SliceOverIndexed!Grapheme opSlice(size_t a, size_t b) pure nothrow @nogc { return sliceOverIndexed(a, b, &this); } /// ditto - @system auto opSlice() pure nothrow @nogc + @system SliceOverIndexed!Grapheme opSlice() pure nothrow @nogc { return sliceOverIndexed(0, length, &this); } @@ -6803,28 +6785,16 @@ public: Use of this facility may invalidate grapheme cluster, see also $(D valid). - Example: - --- - auto g = Grapheme("A"); - assert(g.valid); - g ~= '\u0301'; - assert(g[].equal("A\u0301")); - assert(g.valid); - g ~= "B"; - // not a valid grapheme cluster anymore - assert(!g.valid); - // still could be useful though - assert(g[].equal("A\u0301B")); - --- See_Also: $(LREF Grapheme.valid) +/ ref opOpAssign(string op)(dchar ch) { - static if(op == "~") + static if (op == "~") { - if(!isBig) + import core.stdc.stdlib : realloc; + if (!isBig) { - if(slen_ + 1 > small_cap) + if (slen_ == small_cap) convertToBig();// & fallthrough to "big" branch else { @@ -6835,10 +6805,16 @@ public: } assert(isBig); - if(len_ + 1 > cap_) + if (len_ == cap_) { - cap_ += grow; - ptr_ = cast(ubyte*)enforce(realloc(ptr_, 3*(cap_+1))); + import core.checkedint : addu, mulu; + bool overflow; + cap_ = addu(cap_, grow, overflow); + auto nelems = mulu(3, addu(cap_, 1, overflow), overflow); + if (overflow) assert(0); + + ptr_ = cast(ubyte*) enforce(realloc(ptr_, nelems), + "realloc failed"); } write24(ptr_, ch, len_++); return this; @@ -6847,13 +6823,29 @@ public: static assert(false, "No operation "~op~" defined for Grapheme"); } + /// + @system unittest + { + import std.algorithm.comparison : equal; + auto g = Grapheme("A"); + assert(g.valid); + g ~= '\u0301'; + assert(g[].equal("A\u0301")); + assert(g.valid); + g ~= "B"; + // not a valid grapheme cluster anymore + assert(!g.valid); + // still could be useful though + assert(g[].equal("A\u0301B")); + } + /// Append all $(CHARACTERS) from the input range $(D inp) to this Grapheme. ref opOpAssign(string op, Input)(Input inp) - if(isInputRange!Input && is(ElementType!Input : dchar)) + if (isInputRange!Input && is(ElementType!Input : dchar)) { - static if(op == "~") + static if (op == "~") { - foreach(dchar ch; inp) + foreach (dchar ch; inp) this ~= ch; return this; } @@ -6879,18 +6871,24 @@ public: this(this) { - if(isBig) + import core.stdc.stdlib : malloc; + if (isBig) {// dup it - auto raw_cap = 3*(cap_+1); - auto p = cast(ubyte*)enforce(malloc(raw_cap)); - p[0..raw_cap] = ptr_[0..raw_cap]; + import core.checkedint : addu, mulu; + bool overflow; + auto raw_cap = mulu(3, addu(cap_, 1, overflow), overflow); + if (overflow) assert(0); + + auto p = cast(ubyte*) enforce(malloc(raw_cap), "malloc failed"); + p[0 .. raw_cap] = ptr_[0 .. raw_cap]; ptr_ = p; } } ~this() { - if(isBig) + import core.stdc.stdlib : free; + if (isBig) { free(ptr_); } @@ -6923,9 +6921,13 @@ private: void convertToBig() { + import core.stdc.stdlib : malloc; + + static assert(grow.max / 3 - 1 >= grow); + enum nbytes = 3 * (grow + 1); size_t k = smallLength; - ubyte* p = cast(ubyte*)enforce(malloc(3*(grow+1))); - for(int i=0; i<k; i++) + ubyte* p = cast(ubyte*) enforce(malloc(nbytes), "malloc failed"); + for (int i=0; i<k; i++) write24(p, read24(small_.ptr, i), i); // now we can overwrite small array data ptr_ = p; @@ -6949,10 +6951,13 @@ private: static assert(Grapheme.sizeof == size_t.sizeof*4); -// verify the example -unittest +/// +@system unittest { - import std.algorithm; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; + import std.range : isRandomAccessRange; + string bold = "ku\u0308hn"; // note that decodeGrapheme takes parameter by ref @@ -6964,26 +6969,26 @@ unittest // the next grapheme is 2 characters long auto wideOne = decodeGrapheme(bold); // slicing a grapheme yields a random-access range of dchar - assert(wideOne[].equalS("u\u0308")); + assert(wideOne[].equal("u\u0308")); assert(wideOne.length == 2); static assert(isRandomAccessRange!(typeof(wideOne[]))); // all of the usual range manipulation is possible - assert(wideOne[].filter!isMark().equalS("\u0308")); + assert(wideOne[].filter!isMark().equal("\u0308")); auto g = Grapheme("A"); assert(g.valid); g ~= '\u0301'; - assert(g[].equalS("A\u0301")); + assert(g[].equal("A\u0301")); assert(g.valid); g ~= "B"; // not a valid grapheme cluster anymore assert(!g.valid); // still could be useful though - assert(g[].equalS("A\u0301B")); + assert(g[].equal("A\u0301B")); } -unittest +@safe unittest { auto g = Grapheme("A\u0302"); assert(g[0] == 'A'); @@ -6993,11 +6998,12 @@ unittest assert(!g.valid); } -unittest +@system unittest { - import std.conv; - import std.algorithm; - import std.range; + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.conv : text; + import std.range : iota; // not valid clusters (but it just a test) auto g = Grapheme('a', 'b', 'c', 'd', 'e'); @@ -7028,83 +7034,72 @@ unittest copy[1] = '-'; assert(g[0] == 'a' && copy[0] == 'X'); assert(g[1] == 'b' && copy[1] == '-'); - assert(equalS(g[2..g.length], copy[2..copy.length])); + assert(equal(g[2 .. g.length], copy[2 .. copy.length])); copy = Grapheme("ÐБВГДЕÐЖЗИКЛМ"); - assert(equalS(copy[0..8], "ÐБВГДЕÐЖ"), text(copy[0..8])); + assert(equal(copy[0 .. 8], "ÐБВГДЕÐЖ"), text(copy[0 .. 8])); copy ~= "xyz"; - assert(equalS(copy[13..15], "xy"), text(copy[13..15])); + assert(equal(copy[13 .. 15], "xy"), text(copy[13 .. 15])); assert(!copy.valid); Grapheme h; - foreach(dchar v; iota(cast(int)'A', cast(int)'Z'+1).map!"cast(dchar)a"()) + foreach (dchar v; iota(cast(int)'A', cast(int)'Z'+1).map!"cast(dchar)a"()) h ~= v; - assert(equalS(h[], iota(cast(int)'A', cast(int)'Z'+1))); + assert(equal(h[], iota(cast(int)'A', cast(int)'Z'+1))); } /++ - $(P Does basic case-insensitive comparison of strings $(D str1) and $(D str2). + $(P Does basic case-insensitive comparison of $(D r1) and $(D r2). This function uses simpler comparison rule thus achieving better performance than $(LREF icmp). However keep in mind the warning below.) Params: - str1 = a string or a $(D ForwardRange) of $(D dchar)s - str2 = a string or a $(D ForwardRange) of $(D dchar)s + r1 = an input range of characters + r2 = an input range of characters Returns: An $(D int) that is 0 if the strings match, - <0 if $(D str1) is lexicographically "less" than $(D str2), - >0 if $(D str1) is lexicographically "greater" than $(D str2) + <0 if $(D r1) is lexicographically "less" than $(D r2), + >0 if $(D r1) is lexicographically "greater" than $(D r2) Warning: This function only handles 1:1 $(CODEPOINT) mapping and thus is not sufficient for certain alphabets like German, Greek and few others. - Example: - --- - assert(sicmp("ÐвгуÑÑ‚", "авгуÑТ") == 0); - // Greek also works as long as there is no 1:M mapping in sight - assert(sicmp("ΌΎ", "ÏŒÏ") == 0); - // things like the following won't get matched as equal - // Greek small letter iota with dialytika and tonos - assert(sicmp("Î", "\u03B9\u0308\u0301") != 0); - - // while icmp has no problem with that - assert(icmp("Î", "\u03B9\u0308\u0301") == 0); - assert(icmp("ΌΎ", "ÏŒÏ") == 0); - --- - See_Also: $(LREF icmp) - $(XREF algorithm, cmp) + $(REF cmp, std,algorithm,comparison) +/ -int sicmp(S1, S2)(S1 str1, S2 str2) - if(isForwardRange!S1 && is(Unqual!(ElementType!S1) == dchar) - && isForwardRange!S2 && is(Unqual!(ElementType!S2) == dchar)) +int sicmp(S1, S2)(S1 r1, S2 r2) +if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1) + && isInputRange!S2 && isSomeChar!(ElementEncodingType!S2)) { - import std.utf : decode; + import std.internal.unicode_tables : sTable = simpleCaseTable; // generated file + import std.utf : byDchar; - alias sTable = simpleCaseTable; - size_t ridx=0; - foreach(dchar lhs; str1) + auto str1 = r1.byDchar; + auto str2 = r2.byDchar; + + foreach (immutable lhs; str1) { - if(ridx == str2.length) + if (str2.empty) return 1; - dchar rhs = std.utf.decode(str2, ridx); + immutable rhs = str2.front; + str2.popFront(); int diff = lhs - rhs; - if(!diff) + if (!diff) continue; size_t idx = simpleCaseTrie[lhs]; size_t idx2 = simpleCaseTrie[rhs]; // simpleCaseTrie is packed index table - if(idx != EMPTY_CASE_TRIE) + if (idx != EMPTY_CASE_TRIE) { - if(idx2 != EMPTY_CASE_TRIE) + if (idx2 != EMPTY_CASE_TRIE) {// both cased chars // adjust idx --> start of bucket idx = idx - sTable[idx].n; idx2 = idx2 - sTable[idx2].n; - if(idx == idx2)// one bucket, equivalent chars + if (idx == idx2)// one bucket, equivalent chars continue; else// not the same bucket diff = sTable[idx].ch - sTable[idx2].ch; @@ -7112,17 +7107,33 @@ int sicmp(S1, S2)(S1 str1, S2 str2) else diff = sTable[idx - sTable[idx].n].ch - rhs; } - else if(idx2 != EMPTY_CASE_TRIE) + else if (idx2 != EMPTY_CASE_TRIE) { diff = lhs - sTable[idx2 - sTable[idx2].n].ch; } // one of chars is not cased at all return diff; } - return ridx == str2.length ? 0 : -1; + return str2.empty ? 0 : -1; } + +/// +@safe @nogc pure nothrow unittest +{ + assert(sicmp("ÐвгуÑÑ‚", "авгуÑТ") == 0); + // Greek also works as long as there is no 1:M mapping in sight + assert(sicmp("ΌΎ", "ÏŒÏ") == 0); + // things like the following won't get matched as equal + // Greek small letter iota with dialytika and tonos + assert(sicmp("Î", "\u03B9\u0308\u0301") != 0); + + // while icmp has no problem with that + assert(icmp("Î", "\u03B9\u0308\u0301") == 0); + assert(icmp("ΌΎ", "ÏŒÏ") == 0); +} + // overloads for the most common cases to reduce compile time -@safe pure /*TODO nothrow*/ +@safe @nogc pure nothrow { int sicmp(const(char)[] str1, const(char)[] str2) { return sicmp!(const(char)[], const(char)[])(str1, str2); } @@ -7133,31 +7144,31 @@ int sicmp(S1, S2)(S1 str1, S2 str2) } private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail) - @trusted pure /*TODO nothrow*/ { - import std.algorithm : skipOver; + import std.algorithm.searching : skipOver; + import std.internal.unicode_tables : fullCaseTable; // generated file alias fTable = fullCaseTable; size_t idx = fullCaseTrie[lhs]; // fullCaseTrie is packed index table - if(idx == EMPTY_CASE_TRIE) + if (idx == EMPTY_CASE_TRIE) return lhs; - size_t start = idx - fTable[idx].n; - size_t end = fTable[idx].size + start; + immutable start = idx - fTable[idx].n; + immutable end = fTable[idx].size + start; assert(fTable[start].entry_len == 1); - for(idx=start; idx<end; idx++) + for (idx=start; idx<end; idx++) { auto entryLen = fTable[idx].entry_len; - if(entryLen == 1) + if (entryLen == 1) { - if(fTable[idx].seq[0] == rhs) + if (fTable[idx].seq[0] == rhs) { return 0; } } else {// OK it's a long chunk, like 'ss' for German - dstring seq = fTable[idx].seq[0..entryLen]; - if(rhs == seq[0] + dstring seq = fTable[idx].seq[0 .. entryLen]; + if (rhs == seq[0] && rtail.skipOver(seq[1..$])) { // note that this path modifies rtail @@ -7170,53 +7181,93 @@ private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail) } /++ - $(P Does case insensitive comparison of $(D str1) and $(D str2). + Does case insensitive comparison of `r1` and `r2`. Follows the rules of full case-folding mapping. This includes matching as equal german ß with "ss" and other 1:M $(CODEPOINT) mappings unlike $(LREF sicmp). - The cost of $(D icmp) being pedantically correct is + The cost of `icmp` being pedantically correct is slightly worse performance. - ) - Example: - --- - assert(icmp("Rußland", "Russland") == 0); - assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> á¾²") == 0); - --- + Params: + r1 = a forward range of characters + r2 = a forward range of characters + + Returns: + An $(D int) that is 0 if the strings match, + <0 if $(D str1) is lexicographically "less" than $(D str2), + >0 if $(D str1) is lexicographically "greater" than $(D str2) + + See_Also: + $(LREF sicmp) + $(REF cmp, std,algorithm,comparison) +/ -int icmp(S1, S2)(S1 str1, S2 str2) - if(isForwardRange!S1 && is(Unqual!(ElementType!S1) == dchar) - && isForwardRange!S2 && is(Unqual!(ElementType!S2) == dchar)) +int icmp(S1, S2)(S1 r1, S2 r2) +if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1) + && isForwardRange!S2 && isSomeChar!(ElementEncodingType!S2)) { - for(;;) + import std.utf : byDchar; + + auto str1 = r1.byDchar; + auto str2 = r2.byDchar; + + for (;;) { - if(str1.empty) + if (str1.empty) return str2.empty ? 0 : -1; - dchar lhs = str1.front; - if(str2.empty) + immutable lhs = str1.front; + if (str2.empty) return 1; - dchar rhs = str2.front; + immutable rhs = str2.front; str1.popFront(); str2.popFront(); - int diff = lhs - rhs; - if(!diff) + if (!(lhs - rhs)) continue; // first try to match lhs to <rhs,right-tail> sequence - int cmpLR = fullCasedCmp(lhs, rhs, str2); - if(!cmpLR) + immutable cmpLR = fullCasedCmp(lhs, rhs, str2); + if (!cmpLR) continue; // then rhs to <lhs,left-tail> sequence - int cmpRL = fullCasedCmp(rhs, lhs, str1); - if(!cmpRL) + immutable cmpRL = fullCasedCmp(rhs, lhs, str1); + if (!cmpRL) continue; // cmpXX contain remapped codepoints // to obtain stable ordering of icmp - diff = cmpLR - cmpRL; - return diff; + return cmpLR - cmpRL; } } + +/// +@safe @nogc pure nothrow unittest +{ + assert(icmp("Rußland", "Russland") == 0); + assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> á¾²") == 0); +} + +/** + * By using $(REF byUTF, std,utf) and its aliases, GC allocations via auto-decoding + * and thrown exceptions can be avoided, making `icmp` `@safe @nogc nothrow pure`. + */ +@safe @nogc nothrow pure unittest +{ + import std.utf : byDchar; + + assert(icmp("Rußland".byDchar, "Russland".byDchar) == 0); + assert(icmp("ᾩ -> \u1F70\u03B9".byDchar, "\u1F61\u03B9 -> á¾²".byDchar) == 0); +} + +// test different character types +@safe unittest +{ + assert(icmp("Rußland", "Russland") == 0); + assert(icmp("Rußland"w, "Russland") == 0); + assert(icmp("Rußland", "Russland"w) == 0); + assert(icmp("Rußland"w, "Russland"w) == 0); + assert(icmp("Rußland"d, "Russland"w) == 0); + assert(icmp("Rußland"w, "Russland"d) == 0); +} + // overloads for the most common cases to reduce compile time -@safe pure /*TODO nothrow*/ +@safe @nogc pure nothrow { int icmp(const(char)[] str1, const(char)[] str2) { return icmp!(const(char)[], const(char)[])(str1, str2); } @@ -7226,17 +7277,17 @@ int icmp(S1, S2)(S1 str1, S2 str2) { return icmp!(const(dchar)[], const(dchar)[])(str1, str2); } } -unittest +@safe unittest { - import std.conv; + import std.algorithm.sorting : sort; + import std.conv : to; import std.exception : assertCTFEable; - import std.algorithm; assertCTFEable!( { - foreach(cfunc; TypeTuple!(icmp, sicmp)) + foreach (cfunc; AliasSeq!(icmp, sicmp)) { - foreach(S1; TypeTuple!(string, wstring, dstring)) - foreach(S2; TypeTuple!(string, wstring, dstring)) + foreach (S1; AliasSeq!(string, wstring, dstring)) + foreach (S2; AliasSeq!(string, wstring, dstring)) (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 assert(cfunc("".to!S1(), "".to!S2()) == 0); assert(cfunc("A".to!S1(), "".to!S2()) > 0); @@ -7266,18 +7317,28 @@ unittest }); } +// issue 17372 +@safe pure unittest +{ + import std.algorithm.iteration : joiner, map; + import std.algorithm.sorting : sort; + import std.array : array; + auto a = [["foo", "bar"], ["baz"]].map!(line => line.joiner(" ")).array.sort!((a, b) => icmp(a, b) < 0); +} + // This is package for the moment to be used as a support tool for std.regex // It needs a better API /* Return a range of all $(CODEPOINTS) that casefold to and from this $(D ch). */ -package auto simpleCaseFoldings(dchar ch) +package auto simpleCaseFoldings(dchar ch) @safe { + import std.internal.unicode_tables : simpleCaseTable; // generated file alias sTable = simpleCaseTable; static struct Range { - pure nothrow: + @safe pure nothrow: uint idx; //if == uint.max, then read c. union { @@ -7301,7 +7362,7 @@ package auto simpleCaseFoldings(dchar ch) @property dchar front() const { assert(!empty); - if(isSmall) + if (isSmall) { return c; } @@ -7311,7 +7372,7 @@ package auto simpleCaseFoldings(dchar ch) @property bool empty() const { - if(isSmall) + if (isSmall) { return c == 0; } @@ -7320,7 +7381,7 @@ package auto simpleCaseFoldings(dchar ch) @property uint length() const { - if(isSmall) + if (isSmall) { return c == 0 ? 0 : 1; } @@ -7329,7 +7390,7 @@ package auto simpleCaseFoldings(dchar ch) void popFront() { - if(isSmall) + if (isSmall) c = 0; else { @@ -7346,17 +7407,18 @@ package auto simpleCaseFoldings(dchar ch) return Range(start, entry.size); } -unittest +@system unittest { + import std.algorithm.comparison : equal; + import std.algorithm.searching : canFind; + import std.array : array; import std.exception : assertCTFEable; - import std.algorithm : canFind; - import std.array; assertCTFEable!((){ auto r = simpleCaseFoldings('Э').array; assert(r.length == 2); assert(r.canFind('Ñ') && r.canFind('Э')); auto sr = simpleCaseFoldings('~'); - assert(sr.equalS("~")); + assert(sr.equal("~")); //A with ring above - casefolds to the same bucket as Angstrom sign sr = simpleCaseFoldings('Ã…'); assert(sr.length == 3); @@ -7366,9 +7428,15 @@ unittest /++ $(P Returns the $(S_LINK Combining class, combining class) of $(D ch).) ++/ +ubyte combiningClass(dchar ch) @safe pure nothrow @nogc +{ + return combiningClassTrie[ch]; +} - Example: - --- +/// +@safe unittest +{ // shorten the code alias CC = combiningClass; @@ -7378,16 +7446,11 @@ unittest assert(CC('\u0325') == 220); // the simple consequence is that "tilda" should be // placed after a "ring below" in a sequence - --- -+/ -ubyte combiningClass(dchar ch) -{ - return combiningClassTrie[ch]; } -unittest +@safe pure nothrow @nogc unittest { - foreach(ch; 0..0x80) + foreach (ch; 0 .. 0x80) assert(combiningClass(ch) == 0); assert(combiningClass('\u05BD') == 22); assert(combiningClass('\u0300') == 230); @@ -7405,7 +7468,7 @@ enum UnicodeDecomposition { typically suitable only for fuzzy matching and internal processing. */ Compatibility -}; +} /** Shorthand aliases for character decomposition type, passed as a @@ -7414,7 +7477,7 @@ enum UnicodeDecomposition { enum { Canonical = UnicodeDecomposition.Canonical, Compatibility = UnicodeDecomposition.Compatibility -}; +} /++ Try to canonically compose 2 $(CHARACTERS). @@ -7425,38 +7488,39 @@ enum { Note: Hangul syllables are not covered by this function. See $(D composeJamo) below. - - Example: - --- - assert(compose('A','\u0308') == '\u00C4'); - assert(compose('A', 'B') == dchar.init); - assert(compose('C', '\u0301') == '\u0106'); - // note that the starter is the first one - // thus the following doesn't compose - assert(compose('\u0308', 'A') == dchar.init); - --- +/ -public dchar compose(dchar first, dchar second) pure nothrow +public dchar compose(dchar first, dchar second) pure nothrow @safe { - import std.internal.unicode_comp; - import std.algorithm : map; + import std.algorithm.iteration : map; + import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask; import std.range : assumeSorted; - size_t packed = compositionJumpTrie[first]; - if(packed == ushort.max) + immutable packed = compositionJumpTrie[first]; + if (packed == ushort.max) return dchar.init; // unpack offset and length - size_t idx = packed & composeIdxMask, cnt = packed >> composeCntShift; + immutable idx = packed & composeIdxMask, cnt = packed >> composeCntShift; // TODO: optimize this micro binary search (no more then 4-5 steps) - auto r = compositionTable[idx..idx+cnt].map!"a.rhs"().assumeSorted(); - auto target = r.lowerBound(second).length; - if(target == cnt) + auto r = compositionTable[idx .. idx+cnt].map!"a.rhs"().assumeSorted(); + immutable target = r.lowerBound(second).length; + if (target == cnt) return dchar.init; - auto entry = compositionTable[idx+target]; - if(entry.rhs != second) + immutable entry = compositionTable[idx+target]; + if (entry.rhs != second) return dchar.init; return entry.composed; } +/// +@safe unittest +{ + assert(compose('A','\u0308') == '\u00C4'); + assert(compose('A', 'B') == dchar.init); + assert(compose('C', '\u0301') == '\u0106'); + // note that the starter is the first one + // thus the following doesn't compose + assert(compose('\u0308', 'A') == dchar.init); +} + /++ Returns a full $(S_LINK Canonical decomposition, Canonical) (by default) or $(S_LINK Compatibility decomposition, Compatibility) @@ -7471,40 +7535,33 @@ public dchar compose(dchar first, dchar second) pure nothrow See_Also: $(LREF decomposeHangul) for a restricted version that takes into account only hangul syllables but no other decompositions. - - Example: - --- - import std.algorithm; - assert(decompose('Ĉ')[].equal("C\u0302")); - assert(decompose('D')[].equal("D")); - assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7")); - assert(decompose!Compatibility('¹').equal("1")); - --- +/ -public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) +public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe { - import std.internal.unicode_decomp; - import std.algorithm : until; - static if(decompType == Canonical) + import std.algorithm.searching : until; + import std.internal.unicode_decomp : decompCompatTable, decompCanonTable; + static if (decompType == Canonical) { alias table = decompCanonTable; alias mapping = canonMappingTrie; } - else static if(decompType == Compatibility) + else static if (decompType == Compatibility) { alias table = decompCompatTable; alias mapping = compatMappingTrie; } - ushort idx = mapping[ch]; - if(!idx) // not found, check hangul arithmetic decomposition + immutable idx = mapping[ch]; + if (!idx) // not found, check hangul arithmetic decomposition return decomposeHangul(ch); auto decomp = table[idx..$].until(0); return Grapheme(decomp); } -unittest +/// +@system unittest { - // verify examples + import std.algorithm.comparison : equal; + assert(compose('A','\u0308') == '\u00C4'); assert(compose('A', 'B') == dchar.init); assert(compose('C', '\u0301') == '\u0106'); @@ -7512,11 +7569,10 @@ unittest // thus the following doesn't compose assert(compose('\u0308', 'A') == dchar.init); - import std.algorithm; - assert(decompose('Ĉ')[].equalS("C\u0302")); - assert(decompose('D')[].equalS("D")); - assert(decompose('\uD4DC')[].equalS("\u1111\u1171\u11B7")); - assert(decompose!Compatibility('¹')[].equalS("1")); + assert(decompose('Ĉ')[].equal("C\u0302")); + assert(decompose('D')[].equal("D")); + assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7")); + assert(decompose!Compatibility('¹')[].equal("1")); } //---------------------------------------------------------------------------- @@ -7530,14 +7586,14 @@ enum jamoNCount = jamoVCount * jamoTCount; enum jamoSCount = jamoLCount * jamoNCount; // Tests if $(D ch) is a Hangul leading consonant jamo. -bool isJamoL(dchar ch) pure nothrow @nogc +bool isJamoL(dchar ch) pure nothrow @nogc @safe { // first cmp rejects ~ 1M code points above leading jamo range return ch < jamoLBase+jamoLCount && ch >= jamoLBase; } // Tests if $(D ch) is a Hangul vowel jamo. -bool isJamoT(dchar ch) pure nothrow @nogc +bool isJamoT(dchar ch) pure nothrow @nogc @safe { // first cmp rejects ~ 1M code points above trailing jamo range // Note: ch == jamoTBase doesn't indicate trailing jamo (TIndex must be > 0) @@ -7545,29 +7601,29 @@ bool isJamoT(dchar ch) pure nothrow @nogc } // Tests if $(D ch) is a Hangul trailnig consonant jamo. -bool isJamoV(dchar ch) pure nothrow @nogc +bool isJamoV(dchar ch) pure nothrow @nogc @safe { // first cmp rejects ~ 1M code points above vowel range return ch < jamoVBase+jamoVCount && ch >= jamoVBase; } -int hangulSyllableIndex(dchar ch) pure nothrow @nogc +int hangulSyllableIndex(dchar ch) pure nothrow @nogc @safe { - int idxS = cast(int)ch - jamoSBase; + int idxS = cast(int) ch - jamoSBase; return idxS >= 0 && idxS < jamoSCount ? idxS : -1; } // internal helper: compose hangul syllables leaving dchar.init in holes -void hangulRecompose(dchar[] seq) pure nothrow @nogc +void hangulRecompose(dchar[] seq) pure nothrow @nogc @safe { - for(size_t idx = 0; idx + 1 < seq.length; ) + for (size_t idx = 0; idx + 1 < seq.length; ) { - if(isJamoL(seq[idx]) && isJamoV(seq[idx+1])) + if (isJamoL(seq[idx]) && isJamoV(seq[idx+1])) { - int indexL = seq[idx] - jamoLBase; - int indexV = seq[idx+1] - jamoVBase; - int indexLV = indexL * jamoNCount + indexV * jamoTCount; - if(idx + 2 < seq.length && isJamoT(seq[idx+2])) + immutable int indexL = seq[idx] - jamoLBase; + immutable int indexV = seq[idx+1] - jamoVBase; + immutable int indexLV = indexL * jamoNCount + indexV * jamoTCount; + if (idx + 2 < seq.length && isJamoT(seq[idx+2])) { seq[idx] = jamoSBase + indexLV + seq[idx+2] - jamoTBase; seq[idx+1] = dchar.init; @@ -7593,26 +7649,26 @@ public: Decomposes a Hangul syllable. If $(D ch) is not a composed syllable then this function returns $(LREF Grapheme) containing only $(D ch) as is. */ -Grapheme decomposeHangul(dchar ch) +Grapheme decomposeHangul(dchar ch) @safe { - int idxS = cast(int)ch - jamoSBase; - if(idxS < 0 || idxS >= jamoSCount) return Grapheme(ch); - int idxL = idxS / jamoNCount; - int idxV = (idxS % jamoNCount) / jamoTCount; - int idxT = idxS % jamoTCount; + immutable idxS = cast(int) ch - jamoSBase; + if (idxS < 0 || idxS >= jamoSCount) return Grapheme(ch); + immutable idxL = idxS / jamoNCount; + immutable idxV = (idxS % jamoNCount) / jamoTCount; + immutable idxT = idxS % jamoTCount; - int partL = jamoLBase + idxL; - int partV = jamoVBase + idxV; - if(idxT > 0) // there is a trailling consonant (T); <L,V,T> decomposition + immutable partL = jamoLBase + idxL; + immutable partV = jamoVBase + idxV; + if (idxT > 0) // there is a trailling consonant (T); <L,V,T> decomposition return Grapheme(partL, partV, jamoTBase + idxT); else // <L, V> decomposition return Grapheme(partL, partV); } /// -unittest +@system unittest { - import std.algorithm; + import std.algorithm.comparison : equal; assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6")); } @@ -7625,21 +7681,21 @@ unittest If any of $(D lead) and $(D vowel) are not a valid hangul jamo of the respective $(CHARACTER) class returns dchar.init. +/ -dchar composeJamo(dchar lead, dchar vowel, dchar trailing=dchar.init) pure nothrow @nogc +dchar composeJamo(dchar lead, dchar vowel, dchar trailing=dchar.init) pure nothrow @nogc @safe { - if(!isJamoL(lead)) + if (!isJamoL(lead)) return dchar.init; - int indexL = lead - jamoLBase; - if(!isJamoV(vowel)) + immutable indexL = lead - jamoLBase; + if (!isJamoV(vowel)) return dchar.init; - int indexV = vowel - jamoVBase; - int indexLV = indexL * jamoNCount + indexV * jamoTCount; - dchar syllable = jamoSBase + indexLV; + immutable indexV = vowel - jamoVBase; + immutable indexLV = indexL * jamoNCount + indexV * jamoTCount; + immutable dchar syllable = jamoSBase + indexLV; return isJamoT(trailing) ? syllable + (trailing - jamoTBase) : syllable; } /// -unittest +@safe unittest { assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB'); // leaving out T-vowel, or passing any codepoint @@ -7650,14 +7706,15 @@ unittest assert(composeJamo('A', '\u1171') == dchar.init); } -unittest +@system unittest { - import std.conv; + import std.algorithm.comparison : equal; + import std.conv : text; static void testDecomp(UnicodeDecomposition T)(dchar ch, string r) { Grapheme g = decompose!T(ch); - assert(equalS(g[], r), text(g[], " vs ", r)); + assert(equal(g[], r), text(g[], " vs ", r)); } testDecomp!Canonical('\u1FF4', "\u03C9\u0301\u0345"); testDecomp!Canonical('\uF907', "\u9F9C"); @@ -7665,7 +7722,7 @@ unittest testDecomp!Compatibility('\uA7F9', "\u0153"); // check examples - assert(decomposeHangul('\uD4DB')[].equalS("\u1111\u1171\u11B6")); + assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6")); assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB'); assert(composeJamo('\u1111', '\u1171') == '\uD4CC'); // leave out T-vowel assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC'); @@ -7696,7 +7753,7 @@ enum { NFKC = NormalizationForm.NFKC, ///ditto NFKD = NormalizationForm.NFKD -}; +} /++ Returns $(D input) string normalized to the chosen form. @@ -7711,12 +7768,13 @@ enum { +/ inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input) { - import std.algorithm : sort, SwapStrategy; - import std.range : zip; + import std.algorithm.mutation : SwapStrategy; + import std.algorithm.sorting : sort; import std.array : appender; + import std.range : zip; auto anchors = splitNormalized!norm(input); - if(anchors[0] == input.length && anchors[1] == input.length) + if (anchors[0] == input.length && anchors[1] == input.length) return input; dchar[] decomposed; decomposed.reserve(31); @@ -7725,34 +7783,34 @@ inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input) auto app = appender!(C[])(); do { - app.put(input[0..anchors[0]]); - foreach(dchar ch; input[anchors[0]..anchors[1]]) - static if(norm == NFD || norm == NFC) + app.put(input[0 .. anchors[0]]); + foreach (dchar ch; input[anchors[0]..anchors[1]]) + static if (norm == NFD || norm == NFC) { - foreach(dchar c; decompose!Canonical(ch)[]) + foreach (dchar c; decompose!Canonical(ch)[]) decomposed ~= c; } else // NFKD & NFKC { - foreach(dchar c; decompose!Compatibility(ch)[]) + foreach (dchar c; decompose!Compatibility(ch)[]) decomposed ~= c; } ccc.length = decomposed.length; size_t firstNonStable = 0; ubyte lastClazz = 0; - foreach(idx, dchar ch; decomposed) + foreach (idx, dchar ch; decomposed) { - auto clazz = combiningClass(ch); + immutable clazz = combiningClass(ch); ccc[idx] = clazz; - if(clazz == 0 && lastClazz != 0) + if (clazz == 0 && lastClazz != 0) { // found a stable code point after unstable ones sort!("a[0] < b[0]", SwapStrategy.stable) - (zip(ccc[firstNonStable..idx], decomposed[firstNonStable..idx])); + (zip(ccc[firstNonStable .. idx], decomposed[firstNonStable .. idx])); firstNonStable = decomposed.length; } - else if(clazz != 0 && lastClazz == 0) + else if (clazz != 0 && lastClazz == 0) { // found first unstable code point after stable ones firstNonStable = idx; @@ -7761,17 +7819,16 @@ inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input) } sort!("a[0] < b[0]", SwapStrategy.stable) (zip(ccc[firstNonStable..$], decomposed[firstNonStable..$])); - static if(norm == NFC || norm == NFKC) + static if (norm == NFC || norm == NFKC) { - import std.algorithm : countUntil; - size_t idx = 0; + import std.algorithm.searching : countUntil; auto first = countUntil(ccc, 0); - if(first >= 0) // no starters?? no recomposition + if (first >= 0) // no starters?? no recomposition { - for(;;) + for (;;) { - auto second = recompose(first, decomposed, ccc); - if(second == decomposed.length) + immutable second = recompose(first, decomposed, ccc); + if (second == decomposed.length) break; first = second; } @@ -7779,11 +7836,11 @@ inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input) hangulRecompose(decomposed); } } - static if(norm == NFD || norm == NFKD) + static if (norm == NFD || norm == NFKD) app.put(decomposed); else { - import std.algorithm : remove; + import std.algorithm.mutation : remove; auto clean = remove!("a == dchar.init", SwapStrategy.stable)(decomposed); app.put(decomposed[0 .. clean.length]); } @@ -7795,13 +7852,13 @@ inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input) input = input[anchors[1]..$]; // and move on anchors = splitNormalized!norm(input); - }while(anchors[0] != input.length); - app.put(input[0..anchors[0]]); + }while (anchors[0] != input.length); + app.put(input[0 .. anchors[0]]); return cast(inout(C)[])app.data; } /// -unittest +@safe unittest { // any encoding works wstring greet = "Hello world"; @@ -7815,9 +7872,9 @@ unittest assert(normalize!NFKD("Ï“") == "\u03A5\u0301"); } -unittest +@safe unittest { - import std.conv; + import std.conv : text; assert(normalize!NFD("abc\uF904def") == "abc\u6ED1def", text(normalize!NFD("abc\uF904def"))); assert(normalize!NFKD("2¹â°") == "210", normalize!NFKD("2¹â°")); @@ -7838,19 +7895,18 @@ unittest } // canonically recompose given slice of code points, works in-place and mutates data -private size_t recompose(size_t start, dchar[] input, ubyte[] ccc) pure nothrow +private size_t recompose(size_t start, dchar[] input, ubyte[] ccc) pure nothrow @safe { assert(input.length == ccc.length); - int accumCC = -1;// so that it's out of 0..255 range - bool foundSolidStarter = false; + int accumCC = -1;// so that it's out of 0 .. 255 range // writefln("recomposing %( %04x %)", input); // first one is always a starter thus we start at i == 1 size_t i = start+1; - for(; ; ) + for (; ; ) { - if(i == input.length) + if (i == input.length) break; - int curCC = ccc[i]; + immutable curCC = ccc[i]; // In any character sequence beginning with a starter S // a character C is blocked from S if and only if there // is some character B between S and C, and either B @@ -7862,27 +7918,29 @@ private size_t recompose(size_t start, dchar[] input, ubyte[] ccc) pure nothrow // as ccc are sorted // C is input[i] - if(curCC > accumCC) + if (curCC > accumCC) { - dchar comp = compose(input[start], input[i]); - if(comp != dchar.init) + immutable comp = compose(input[start], input[i]); + if (comp != dchar.init) { input[start] = comp; input[i] = dchar.init;// put a sentinel // current was merged so its CCC shouldn't affect // composing with the next one } - else { + else + { // if it was a starter then accumCC is now 0, end of loop accumCC = curCC; - if(accumCC == 0) + if (accumCC == 0) break; } } - else{ + else + { // ditto here accumCC = curCC; - if(accumCC == 0) + if (accumCC == 0) break; } i++; @@ -7896,24 +7954,23 @@ private size_t recompose(size_t start, dchar[] input, ubyte[] ccc) pure nothrow private auto splitNormalized(NormalizationForm norm, C)(const(C)[] input) { import std.typecons : tuple; - auto result = input; ubyte lastCC = 0; - foreach(idx, dchar ch; input) + foreach (idx, dchar ch; input) { - static if(norm == NFC) - if(ch < 0x0300) + static if (norm == NFC) + if (ch < 0x0300) { lastCC = 0; continue; } - ubyte CC = combiningClass(ch); - if(lastCC > CC && CC != 0) + immutable ubyte CC = combiningClass(ch); + if (lastCC > CC && CC != 0) { return seekStable!norm(idx, input); } - if(notAllowedIn!norm(ch)) + if (notAllowedIn!norm(ch)) { return seekStable!norm(idx, input); } @@ -7924,34 +7981,34 @@ private auto splitNormalized(NormalizationForm norm, C)(const(C)[] input) private auto seekStable(NormalizationForm norm, C)(size_t idx, in C[] input) { - import std.utf : codeLength; import std.typecons : tuple; + import std.utf : codeLength; - auto br = input[0..idx]; + auto br = input[0 .. idx]; size_t region_start = 0;// default - for(;;) + for (;;) { - if(br.empty)// start is 0 + if (br.empty)// start is 0 break; dchar ch = br.back; - if(combiningClass(ch) == 0 && allowedIn!norm(ch)) + if (combiningClass(ch) == 0 && allowedIn!norm(ch)) { - region_start = br.length - std.utf.codeLength!C(ch); + region_start = br.length - codeLength!C(ch); break; } br.popFront(); } ///@@@BUG@@@ can't use find: " find is a nested function and can't be used..." size_t region_end=input.length;// end is $ by default - foreach(i, dchar ch; input[idx..$]) + foreach (i, dchar ch; input[idx..$]) { - if(combiningClass(ch) == 0 && allowedIn!norm(ch)) + if (combiningClass(ch) == 0 && allowedIn!norm(ch)) { region_end = i+idx; break; } } - // writeln("Region to normalize: ", input[region_start..region_end]); + // writeln("Region to normalize: ", input[region_start .. region_end]); return tuple(region_start, region_end); } @@ -7965,7 +8022,7 @@ public bool allowedIn(NormalizationForm norm)(dchar ch) } /// -unittest +@safe unittest { // e.g. Cyrillic is always allowed, so is ASCII assert(allowedIn!NFC('Ñ')); @@ -7978,20 +8035,20 @@ unittest // not user friendly name but more direct private bool notAllowedIn(NormalizationForm norm)(dchar ch) { - static if(norm == NFC) + static if (norm == NFC) alias qcTrie = nfcQCTrie; - else static if(norm == NFD) + else static if (norm == NFD) alias qcTrie = nfdQCTrie; - else static if(norm == NFKC) + else static if (norm == NFKC) alias qcTrie = nfkcQCTrie; - else static if(norm == NFKD) + else static if (norm == NFKD) alias qcTrie = nfkdQCTrie; else static assert("Unknown normalization form "~norm); return qcTrie[ch]; } -unittest +@safe unittest { assert(allowedIn!NFC('Ñ')); assert(allowedIn!NFD('Ñ')); @@ -8020,8 +8077,10 @@ else { // trusted -> avoid bounds check -@trusted pure nothrow @nogc +@trusted pure nothrow @nogc private { + import std.internal.unicode_tables; // : toLowerTable, toTitleTable, toUpperTable; // generated file + // hide template instances behind functions (Bugzilla 13232) ushort toLowerIndex(dchar c) { return toLowerIndexTrie[c]; } ushort toLowerSimpleIndex(dchar c) { return toLowerSimpleIndexTrie[c]; } @@ -8046,6 +8105,7 @@ public: @safe pure nothrow @nogc public bool isWhite(dchar c) { + import std.internal.unicode_tables : isWhiteGen; // generated file return isWhiteGen(c); // call pregenerated binary search } @@ -8056,7 +8116,7 @@ public bool isWhite(dchar c) bool isLower(dchar c) { import std.ascii : isLower, isASCII; - if(isASCII(c)) + if (isASCII(c)) return isLower(c); return lowerCaseTrie[c]; } @@ -8064,7 +8124,7 @@ bool isLower(dchar c) @safe unittest { import std.ascii : isLower; - foreach(v; 0..0x80) + foreach (v; 0 .. 0x80) assert(isLower(v) == .isLower(v)); assert(.isLower('Ñ')); assert(.isLower('й')); @@ -8077,7 +8137,7 @@ bool isLower(dchar c) // from extended Greek assert(!.isLower('\u1F18')); assert(.isLower('\u1F00')); - foreach(v; unicode.lowerCase.byCodepoint) + foreach (v; unicode.lowerCase.byCodepoint) assert(.isLower(v) && !isUpper(v)); } @@ -8089,7 +8149,7 @@ bool isLower(dchar c) bool isUpper(dchar c) { import std.ascii : isUpper, isASCII; - if(isASCII(c)) + if (isASCII(c)) return isUpper(c); return upperCaseTrie[c]; } @@ -8097,7 +8157,7 @@ bool isUpper(dchar c) @safe unittest { import std.ascii : isLower; - foreach(v; 0..0x80) + foreach (v; 0 .. 0x80) assert(isLower(v) == .isLower(v)); assert(!isUpper('й')); assert(isUpper('Ж')); @@ -8109,94 +8169,75 @@ bool isUpper(dchar c) // from extended Greek assert(!isUpper('\u1F00')); assert(isUpper('\u1F18')); - foreach(v; unicode.upperCase.byCodepoint) + foreach (v; unicode.upperCase.byCodepoint) assert(isUpper(v) && !.isLower(v)); } -/++ - If $(D c) is a Unicode uppercase $(CHARACTER), then its lowercase equivalent - is returned. Otherwise $(D c) is returned. - - Warning: certain alphabets like German and Greek have no 1:1 - upper-lower mapping. Use overload of toLower which takes full string instead. -+/ -@safe pure nothrow @nogc -dchar toLower(dchar c) -{ - // optimize ASCII case - if(c < 0xAA) - { - if(c < 'A') - return c; - if(c <= 'Z') - return c + 32; - return c; - } - size_t idx = toLowerSimpleIndex(c); - if(idx != ushort.max) - { - return toLowerTab(idx); - } - return c; -} - //TODO: Hidden for now, needs better API. //Other transforms could use better API as well, but this one is a new primitive. @safe pure nothrow @nogc private dchar toTitlecase(dchar c) { // optimize ASCII case - if(c < 0xAA) + if (c < 0xAA) { - if(c < 'a') + if (c < 'a') return c; - if(c <= 'z') + if (c <= 'z') return c - 32; return c; } size_t idx = toTitleSimpleIndex(c); - if(idx != ushort.max) + if (idx != ushort.max) { return toTitleTab(idx); } return c; } -private alias UpperTriple = TypeTuple!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab); -private alias LowerTriple = TypeTuple!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab); +private alias UpperTriple = AliasSeq!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab); +private alias LowerTriple = AliasSeq!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab); // generic toUpper/toLower on whole string, creates new or returns as is -private S toCase(alias indexFn, uint maxIdx, alias tableFn, S)(S s) @trusted pure - if(isSomeString!S) +private S toCase(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, S)(S s) @trusted pure +if (isSomeString!S) { import std.array : appender; + import std.ascii : isASCII; - foreach(i, dchar cOuter; s) + foreach (i, dchar cOuter; s) { ushort idx = indexFn(cOuter); - if(idx == ushort.max) + if (idx == ushort.max) continue; - auto result = appender!S(s[0..i]); + auto result = appender!S(s[0 .. i]); result.reserve(s.length); - foreach(dchar c; s[i .. $]) + foreach (dchar c; s[i .. $]) { - idx = indexFn(c); - if(idx == ushort.max) - result.put(c); - else if(idx < maxIdx) + if (c.isASCII) { - c = tableFn(idx); - result.put(c); + result.put(asciiConvert(c)); } else { - auto val = tableFn(idx); - // unpack length + codepoint - uint len = val>>24; - result.put(cast(dchar)(val & 0xFF_FFFF)); - foreach(j; idx+1..idx+len) - result.put(tableFn(j)); + idx = indexFn(c); + if (idx == ushort.max) + result.put(c); + else if (idx < maxIdx) + { + c = tableFn(idx); + result.put(c); + } + else + { + auto val = tableFn(idx); + // unpack length + codepoint + immutable uint len = val >> 24; + result.put(cast(dchar)(val & 0xFF_FFFF)); + foreach (j; idx+1 .. idx+len) + result.put(tableFn(j)); + } } } return result.data; @@ -8204,11 +8245,11 @@ private S toCase(alias indexFn, uint maxIdx, alias tableFn, S)(S s) @trusted pur return s; } -unittest //12428 +@safe unittest //12428 { - import std.array; + import std.array : replicate; auto s = "abcdefghij".replicate(300); - s = s[0..10]; + s = s[0 .. 10]; toUpper(s); @@ -8217,11 +8258,11 @@ unittest //12428 // generic toUpper/toLower on whole range, returns range -private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, Range)(Range str) +private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, Range)(Range str) // Accept range of dchar's - if (isInputRange!Range && - isSomeChar!(ElementEncodingType!Range) && - ElementEncodingType!Range.sizeof == dchar.sizeof) +if (isInputRange!Range && + isSomeChar!(ElementEncodingType!Range) && + ElementEncodingType!Range.sizeof == dchar.sizeof) { static struct ToCaserImpl { @@ -8232,29 +8273,41 @@ private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, Range)(Range str @property auto front() { + import std.ascii : isASCII; + if (!nLeft) { dchar c = r.front; - const idx = indexFn(c); - if (idx == ushort.max) + if (c.isASCII) { - buf[0] = c; - nLeft = 1; - } - else if (idx < maxIdx) - { - buf[0] = tableFn(idx); + buf[0] = asciiConvert(c); nLeft = 1; } else { - auto val = tableFn(idx); - // unpack length + codepoint - nLeft = val >> 24; - assert(nLeft <= buf.length); - buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF); - foreach (j; 1 .. nLeft) - buf[nLeft - j - 1] = tableFn(idx + j); + const idx = indexFn(c); + if (idx == ushort.max) + { + buf[0] = c; + nLeft = 1; + } + else if (idx < maxIdx) + { + buf[0] = tableFn(idx); + nLeft = 1; + } + else + { + immutable val = tableFn(idx); + // unpack length + codepoint + nLeft = val >> 24; + if (nLeft == 0) + nLeft = 1; + assert(nLeft <= buf.length); + buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF); + foreach (j; 1 .. nLeft) + buf[nLeft - j - 1] = tableFn(idx + j); + } } } return buf[nLeft - 1]; @@ -8263,7 +8316,7 @@ private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, Range)(Range str void popFront() { if (!nLeft) - front(); + front; assert(nLeft); --nLeft; if (!nLeft) @@ -8294,7 +8347,7 @@ private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, Range)(Range str * * Does not allocate memory. * Characters in UTF-8 or UTF-16 format that cannot be decoded - * are treated as $(XREF utf, replacementDchar). + * are treated as $(REF replacementDchar, std,utf). * * Params: * str = string or range of characters @@ -8306,52 +8359,78 @@ private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, Range)(Range str * $(LREF toUpper), $(LREF toLower) */ -auto toLowerCase(Range)(Range str) - if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range)) +auto asLowerCase(Range)(Range str) +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) { static if (ElementEncodingType!Range.sizeof < dchar.sizeof) { import std.utf : byDchar; // Decode first - return toCaser!LowerTriple(str.byDchar); + return asLowerCase(str.byDchar); } else { - return toCaser!LowerTriple(str); + static import std.ascii; + return toCaser!(LowerTriple, std.ascii.toLower)(str); } } /// ditto -auto toUpperCase(Range)(Range str) - if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range)) +auto asUpperCase(Range)(Range str) +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) { static if (ElementEncodingType!Range.sizeof < dchar.sizeof) { import std.utf : byDchar; // Decode first - return toCaser!UpperTriple(str.byDchar); + return asUpperCase(str.byDchar); } else { - return toCaser!UpperTriple(str); + static import std.ascii; + return toCaser!(UpperTriple, std.ascii.toUpper)(str); } } /// @safe pure unittest { - import std.algorithm: equal; + import std.algorithm.comparison : equal; + + assert("hEllo".asUpperCase.equal("HELLO")); +} + +// explicitly undocumented +auto asLowerCase(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + import std.traits : StringTypeOf; + return asLowerCase!(StringTypeOf!Range)(str); +} + +// explicitly undocumented +auto asUpperCase(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + import std.traits : StringTypeOf; + return asUpperCase!(StringTypeOf!Range)(str); +} - assert("hEllo".toUpperCase.equal("HELLO")); +@safe unittest +{ + assert(testAliasedString!asLowerCase("hEllo")); + assert(testAliasedString!asUpperCase("hEllo")); } -unittest +@safe unittest { - import std.array; + import std.array : array; - auto a = "HELLo".toLowerCase; + auto a = "HELLo".asLowerCase; auto savea = a.save; auto s = a.array; assert(s == "hello"); @@ -8365,31 +8444,235 @@ unittest { import std.utf : byChar; - auto sx = slwr.toUpperCase.byChar.array; + auto sx = slwr.asUpperCase.byChar.array; assert(sx == toUpper(slwr)); - auto sy = upper[i].toLowerCase.byChar.array; + auto sy = upper[i].asLowerCase.byChar.array; assert(sy == toLower(upper[i])); } // Not necessary to call r.front - for (auto r = lower[3].toUpperCase; !r.empty; r.popFront()) + for (auto r = lower[3].asUpperCase; !r.empty; r.popFront()) + { + } + + import std.algorithm.comparison : equal; + + "HELLo"w.asLowerCase.equal("hello"d); + "HELLo"w.asUpperCase.equal("HELLO"d); + "HELLo"d.asLowerCase.equal("hello"d); + "HELLo"d.asUpperCase.equal("HELLO"d); + + import std.utf : byChar; + assert(toLower("\u1Fe2") == asLowerCase("\u1Fe2").byChar.array); +} + +// generic capitalizer on whole range, returns range +private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUpper, + Range)(Range str) + // Accept range of dchar's +if (isInputRange!Range && + isSomeChar!(ElementEncodingType!Range) && + ElementEncodingType!Range.sizeof == dchar.sizeof) +{ + static struct ToCapitalizerImpl + { + @property bool empty() + { + return lower ? lwr.empty : !nLeft && r.empty; + } + + @property auto front() + { + if (lower) + return lwr.front; + + if (!nLeft) + { + immutable dchar c = r.front; + const idx = indexFnUpper(c); + if (idx == ushort.max) + { + buf[0] = c; + nLeft = 1; + } + else if (idx < maxIdxUpper) + { + buf[0] = tableFnUpper(idx); + nLeft = 1; + } + else + { + immutable val = tableFnUpper(idx); + // unpack length + codepoint + nLeft = val >> 24; + if (nLeft == 0) + nLeft = 1; + assert(nLeft <= buf.length); + buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF); + foreach (j; 1 .. nLeft) + buf[nLeft - j - 1] = tableFnUpper(idx + j); + } + } + return buf[nLeft - 1]; + } + + void popFront() + { + if (lower) + lwr.popFront(); + else + { + if (!nLeft) + front; + assert(nLeft); + --nLeft; + if (!nLeft) + { + r.popFront(); + lwr = r.asLowerCase(); + lower = true; + } + } + } + + static if (isForwardRange!Range) + { + @property auto save() + { + auto ret = this; + ret.r = r.save; + ret.lwr = lwr.save; + return ret; + } + } + + private: + Range r; + typeof(r.asLowerCase) lwr; // range representing the lower case rest of string + bool lower = false; // false for first character, true for rest of string + dchar[3] buf = void; + uint nLeft = 0; + } + + return ToCapitalizerImpl(str); +} + +/********************* + * Capitalize input range or string, meaning convert the first + * character to upper case and subsequent characters to lower case. + * + * Does not allocate memory. + * Characters in UTF-8 or UTF-16 format that cannot be decoded + * are treated as $(REF replacementDchar, std,utf). + * + * Params: + * str = string or range of characters + * + * Returns: + * an InputRange of dchars + * + * See_Also: + * $(LREF toUpper), $(LREF toLower) + * $(LREF asUpperCase), $(LREF asLowerCase) + */ + +auto asCapitalized(Range)(Range str) +if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && + !isConvertibleToString!Range) +{ + static if (ElementEncodingType!Range.sizeof < dchar.sizeof) + { + import std.utf : byDchar; + + // Decode first + return toCapitalizer!UpperTriple(str.byDchar); + } + else + { + return toCapitalizer!UpperTriple(str); + } +} + +/// +@safe pure unittest +{ + import std.algorithm.comparison : equal; + + assert("hEllo".asCapitalized.equal("Hello")); +} + +auto asCapitalized(Range)(auto ref Range str) +if (isConvertibleToString!Range) +{ + import std.traits : StringTypeOf; + return asCapitalized!(StringTypeOf!Range)(str); +} + +@safe unittest +{ + assert(testAliasedString!asCapitalized("hEllo")); +} + +@safe pure nothrow @nogc unittest +{ + auto r = "hEllo".asCapitalized(); + assert(r.front == 'H'); +} + +@safe unittest +{ + import std.array : array; + + auto a = "hELLo".asCapitalized; + auto savea = a.save; + auto s = a.array; + assert(s == "Hello"); + s = savea.array; + assert(s == "Hello"); + + string[2][] cases = + [ + ["", ""], + ["h", "H"], + ["H", "H"], + ["3", "3"], + ["123", "123"], + ["h123A", "H123a"], + ["феж", "Феж"], + ["\u1Fe2", "\u03a5\u0308\u0300"], + ]; + + foreach (i; 0 .. cases.length) + { + import std.utf : byChar; + + auto r = cases[i][0].asCapitalized.byChar.array; + auto result = cases[i][1]; + assert(r == result); + } + + // Don't call r.front + for (auto r = "\u1Fe2".asCapitalized; !r.empty; r.popFront()) { } - import std.algorithm : equal; + import std.algorithm.comparison : equal; - "HELLo"w.toLowerCase.equal("hello"d); - "HELLo"w.toUpperCase.equal("HELLO"d); - "HELLo"d.toLowerCase.equal("hello"d); - "HELLo"d.toUpperCase.equal("HELLO"d); + "HELLo"w.asCapitalized.equal("Hello"d); + "hElLO"w.asCapitalized.equal("Hello"d); + "hello"d.asCapitalized.equal("Hello"d); + "HELLO"d.asCapitalized.equal("Hello"d); + + import std.utf : byChar; + assert(asCapitalized("\u0130").byChar.array == asUpperCase("\u0130").byChar.array); } // TODO: helper, I wish std.utf was more flexible (and stright) -private size_t encodeTo(char[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc +private size_t encodeTo(scope char[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc { if (c <= 0x7F) { - buf[idx] = cast(char)c; + buf[idx] = cast(char) c; idx++; } else if (c <= 0x7FF) @@ -8418,7 +8701,7 @@ private size_t encodeTo(char[] buf, size_t idx, dchar c) @trusted pure nothrow @ return idx; } -unittest +@safe unittest { char[] s = "abcd".dup; size_t i = 0; @@ -8430,14 +8713,14 @@ unittest } // TODO: helper, I wish std.utf was more flexible (and stright) -private size_t encodeTo(wchar[] buf, size_t idx, dchar c) @trusted pure +private size_t encodeTo(scope wchar[] buf, size_t idx, dchar c) @trusted pure { - import std.utf; + import std.utf : UTFException; if (c <= 0xFFFF) { if (0xD800 <= c && c <= 0xDFFF) throw (new UTFException("Encoding an isolated surrogate code point in UTF-16")).setSequence(c); - buf[idx] = cast(wchar)c; + buf[idx] = cast(wchar) c; idx++; } else if (c <= 0x10FFFF) @@ -8451,7 +8734,7 @@ private size_t encodeTo(wchar[] buf, size_t idx, dchar c) @trusted pure return idx; } -private size_t encodeTo(dchar[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc +private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc { buf[idx] = c; idx++; @@ -8459,9 +8742,9 @@ private size_t encodeTo(dchar[] buf, size_t idx, dchar c) @trusted pure nothrow } private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] s) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) +if (is(C == char) || is(C == wchar) || is(C == dchar)) { - import std.utf; + import std.utf : decode, codeLength; size_t curIdx = 0; size_t destIdx = 0; alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn); @@ -8474,32 +8757,32 @@ private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] // then have to copy if a re-cased char was smaller the original // later we may regain pace with char that got bigger // In the end it sometimes flip-flops between the 2 cases below - if(dest == from) + if (dest == from) return to; // got to copy - foreach(C c; str[from..to]) + foreach (C c; str[from .. to]) str[dest++] = c; return dest; } - while(curIdx != s.length) + while (curIdx != s.length) { size_t startIdx = curIdx; - dchar ch = decode(s, curIdx); + immutable ch = decode(s, curIdx); // TODO: special case for ASCII - auto caseIndex = indexFn(ch); - if(caseIndex == ushort.max) // unchanged, skip over + immutable caseIndex = indexFn(ch); + if (caseIndex == ushort.max) // unchanged, skip over { continue; } - else if(caseIndex < maxIdx) // 1:1 codepoint mapping + else if (caseIndex < maxIdx) // 1:1 codepoint mapping { // previous cased chars had the same length as uncased ones // thus can just adjust pointer destIdx = moveTo(s, destIdx, lastUnchanged, startIdx); lastUnchanged = curIdx; - dchar cased = tableFn(caseIndex); - auto casedLen = codeLength!C(cased); - if(casedLen + destIdx > curIdx) // no place to fit cased char + immutable cased = tableFn(caseIndex); + immutable casedLen = codeLength!C(cased); + if (casedLen + destIdx > curIdx) // no place to fit cased char { // switch to slow codepath, where we allocate return slowToCase(s, startIdx, destIdx); @@ -8517,11 +8800,11 @@ private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] } assert(destIdx <= curIdx); } - if(lastUnchanged != s.length) + if (lastUnchanged != s.length) { destIdx = moveTo(s, destIdx, lastUnchanged, s.length); } - s = s[0..destIdx]; + s = s[0 .. destIdx]; } // helper to precalculate size of case-converted string @@ -8529,45 +8812,44 @@ private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn) { size_t toCaseLength(C)(in C[] str) { - import std.utf; + import std.utf : decode, codeLength; size_t codeLen = 0; size_t lastNonTrivial = 0; size_t curIdx = 0; - while(curIdx != str.length) + while (curIdx != str.length) { - size_t startIdx = curIdx; - dchar ch = decode(str, curIdx); - ushort caseIndex = indexFn(ch); - if(caseIndex == ushort.max) + immutable startIdx = curIdx; + immutable ch = decode(str, curIdx); + immutable ushort caseIndex = indexFn(ch); + if (caseIndex == ushort.max) continue; - else if(caseIndex < maxIdx) + else if (caseIndex < maxIdx) { codeLen += startIdx - lastNonTrivial; lastNonTrivial = curIdx; - dchar cased = tableFn(caseIndex); + immutable cased = tableFn(caseIndex); codeLen += codeLength!C(cased); } else { codeLen += startIdx - lastNonTrivial; lastNonTrivial = curIdx; - auto val = tableFn(caseIndex); - auto len = val>>24; - dchar cased = val & 0xFF_FFFF; + immutable val = tableFn(caseIndex); + immutable len = val >> 24; + immutable dchar cased = val & 0xFF_FFFF; codeLen += codeLength!C(cased); - foreach(j; caseIndex+1..caseIndex+len) + foreach (j; caseIndex+1 .. caseIndex+len) codeLen += codeLength!C(tableFn(j)); } } - if(lastNonTrivial != str.length) + if (lastNonTrivial != str.length) codeLen += str.length - lastNonTrivial; return codeLen; } } -unittest +@safe unittest { - import std.conv; alias toLowerLength = toCaseLength!(LowerTriple); assert(toLowerLength("abcd") == 4); assert(toLowerLength("аБВгд456") == 10+3); @@ -8585,20 +8867,20 @@ private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn); auto trueLength = destIdx + caseLength(s[curIdx..$]); C[] ns = new C[trueLength]; - ns[0..destIdx] = s[0..destIdx]; + ns[0 .. destIdx] = s[0 .. destIdx]; size_t lastUnchanged = curIdx; - while(curIdx != s.length) + while (curIdx != s.length) { - size_t startIdx = curIdx; // start of current codepoint - dchar ch = decode(s, curIdx); - auto caseIndex = indexFn(ch); - if(caseIndex == ushort.max) // skip over + immutable startIdx = curIdx; // start of current codepoint + immutable ch = decode(s, curIdx); + immutable caseIndex = indexFn(ch); + if (caseIndex == ushort.max) // skip over { continue; } - else if(caseIndex < maxIdx) // 1:1 codepoint mapping + else if (caseIndex < maxIdx) // 1:1 codepoint mapping { - dchar cased = tableFn(caseIndex); + immutable cased = tableFn(caseIndex); auto toCopy = startIdx - lastUnchanged; ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx]; lastUnchanged = curIdx; @@ -8613,16 +8895,16 @@ private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) destIdx += toCopy; auto val = tableFn(caseIndex); // unpack length + codepoint - uint len = val>>24; + immutable uint len = val >> 24; destIdx = encodeTo(ns, destIdx, cast(dchar)(val & 0xFF_FFFF)); - foreach(j; caseIndex+1..caseIndex+len) + foreach (j; caseIndex+1 .. caseIndex+len) destIdx = encodeTo(ns, destIdx, tableFn(j)); } } - if(lastUnchanged != s.length) + if (lastUnchanged != s.length) { auto toCopy = s.length - lastUnchanged; - ns[destIdx..destIdx+toCopy] = s[lastUnchanged..$]; + ns[destIdx .. destIdx+toCopy] = s[lastUnchanged..$]; destIdx += toCopy; } assert(ns.length == destIdx); @@ -8637,7 +8919,7 @@ private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) If $(D s) does not have any uppercase characters, then $(D s) is unaltered. +/ void toLowerInPlace(C)(ref C[] s) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) +if (is(C == char) || is(C == wchar) || is(C == dchar)) { toCaseInPlace!(LowerTriple)(s); } @@ -8659,7 +8941,7 @@ void toLowerInPlace(C)(ref C[] s) @trusted pure If $(D s) does not have any lowercase characters, then $(D s) is unaltered. +/ void toUpperInPlace(C)(ref C[] s) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) +if (is(C == char) || is(C == wchar) || is(C == dchar)) { toCaseInPlace!(UpperTriple)(s); } @@ -8674,15 +8956,43 @@ void toUpperInPlace(C)(ref C[] s) @trusted pure { toUpperInPlace!dchar(s); } } +/++ + If $(D c) is a Unicode uppercase $(CHARACTER), then its lowercase equivalent + is returned. Otherwise $(D c) is returned. + + Warning: certain alphabets like German and Greek have no 1:1 + upper-lower mapping. Use overload of toLower which takes full string instead. ++/ +@safe pure nothrow @nogc +dchar toLower(dchar c) +{ + // optimize ASCII case + if (c < 0xAA) + { + if (c < 'A') + return c; + if (c <= 'Z') + return c + 32; + return c; + } + size_t idx = toLowerSimpleIndex(c); + if (idx != ushort.max) + { + return toLowerTab(idx); + } + return c; +} + /++ Returns a string which is identical to $(D s) except that all of its characters are converted to lowercase (by preforming Unicode lowercase mapping). If none of $(D s) characters were affected, then $(D s) itself is returned. +/ S toLower(S)(S s) @trusted pure - if(isSomeString!S) +if (isSomeString!S) { - return toCase!(LowerTriple)(s); + static import std.ascii; + return toCase!(LowerTriple, std.ascii.toLower)(s); } // overloads for the most common cases to reduce compile time @safe pure /*TODO nothrow*/ @@ -8693,17 +9003,34 @@ S toLower(S)(S s) @trusted pure { return toLower!wstring(s); } dstring toLower(dstring s) { return toLower!dstring(s); } + + @safe unittest + { + // https://issues.dlang.org/show_bug.cgi?id=16663 + + static struct String + { + string data; + alias data this; + } + + void foo() + { + auto u = toLower(String("")); + } + } } -@trusted unittest //@@@BUG std.format is not @safe +@system unittest //@@@BUG std.format is not @safe { import std.format : format; - foreach(ch; 0..0x80) + static import std.ascii; + foreach (ch; 0 .. 0x80) assert(std.ascii.toLower(ch) == toLower(ch)); assert(toLower('Я') == 'Ñ'); assert(toLower('Δ') == 'δ'); - foreach(ch; unicode.upperCase.byCodepoint) + foreach (ch; unicode.upperCase.byCodepoint) { dchar low = ch.toLower(); assert(low == ch || isLower(low), format("%s -> %s", ch, low)); @@ -8715,18 +9042,18 @@ S toLower(S)(S s) @trusted pure } //bugzilla 9629 -unittest +@safe unittest { wchar[] test = "hello þ world"w.dup; - auto piece = test[6..7]; + auto piece = test[6 .. 7]; toUpperInPlace(piece); assert(test == "hello Þ world"); } -unittest +@safe unittest { - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; string s1 = "FoL"; string s2 = toLower(s1); assert(cmp(s2, "fol") == 0, s2); @@ -8783,24 +9110,26 @@ unittest Certain alphabets like German and Greek have no 1:1 upper-lower mapping. Use overload of toUpper which takes full string instead. - toUpper can be used as an argument to $(XREF algorithm, map) to produce an algorithm that can - convert a range of characters to upper case without allocating memory. - A string can then be produced by using $(XREF algorithm, copy) to send it to an $(XREF array, appender). + toUpper can be used as an argument to $(REF map, std,algorithm,iteration) + to produce an algorithm that can convert a range of characters to upper case + without allocating memory. + A string can then be produced by using $(REF copy, std,algorithm,mutation) + to send it to an $(REF appender, std,array). +/ @safe pure nothrow @nogc dchar toUpper(dchar c) { // optimize ASCII case - if(c < 0xAA) + if (c < 0xAA) { - if(c < 'a') + if (c < 'a') return c; - if(c <= 'z') + if (c <= 'z') return c - 32; return c; } size_t idx = toUpperSimpleIndex(c); - if(idx != ushort.max) + if (idx != ushort.max) { return toUpperTab(idx); } @@ -8808,26 +9137,27 @@ dchar toUpper(dchar c) } /// -unittest +@system unittest { - import std.algorithm; - import std.uni; - import std.array; + import std.algorithm.iteration : map; + import std.algorithm.mutation : copy; + import std.array : appender; auto abuf = appender!(char[])(); "hello".map!toUpper.copy(&abuf); assert(abuf.data == "HELLO"); } -@trusted unittest +@safe unittest { import std.format : format; - foreach(ch; 0..0x80) + static import std.ascii; + foreach (ch; 0 .. 0x80) assert(std.ascii.toUpper(ch) == toUpper(ch)); assert(toUpper('Ñ') == 'Я'); assert(toUpper('δ') == 'Δ'); auto title = unicode.Titlecase_Letter; - foreach(ch; unicode.lowerCase.byCodepoint) + foreach (ch; unicode.lowerCase.byCodepoint) { dchar up = ch.toUpper(); assert(up == ch || isUpper(up) || title[up], @@ -8841,9 +9171,10 @@ unittest If none of $(D s) characters were affected, then $(D s) itself is returned. +/ S toUpper(S)(S s) @trusted pure - if(isSomeString!S) +if (isSomeString!S) { - return toCase!(UpperTriple)(s); + static import std.ascii; + return toCase!(UpperTriple, std.ascii.toUpper)(s); } // overloads for the most common cases to reduce compile time @safe pure /*TODO nothrow*/ @@ -8854,11 +9185,27 @@ S toUpper(S)(S s) @trusted pure { return toUpper!wstring(s); } dstring toUpper(dstring s) { return toUpper!dstring(s); } + + @safe unittest + { + // https://issues.dlang.org/show_bug.cgi?id=16663 + + static struct String + { + string data; + alias data this; + } + + void foo() + { + auto u = toUpper(String("")); + } + } } -unittest +@safe unittest { - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; string s1 = "FoL"; string s2; @@ -8885,7 +9232,7 @@ unittest assert(s2 !is s1); } -unittest +@system unittest { static void doTest(C)(const(C)[] s, const(C)[] trueUp, const(C)[] trueLow) { @@ -8898,11 +9245,11 @@ unittest assert(low == trueLow, format(diff, low, trueLow)); assert(up == trueUp, format(diff, up, trueUp)); assert(lowInp == trueLow, - format(diff, cast(ubyte[])s, cast(ubyte[])lowInp, cast(ubyte[])trueLow)); + format(diff, cast(ubyte[]) s, cast(ubyte[]) lowInp, cast(ubyte[]) trueLow)); assert(upInp == trueUp, - format(diff, cast(ubyte[])s, cast(ubyte[])upInp, cast(ubyte[])trueUp)); + format(diff, cast(ubyte[]) s, cast(ubyte[]) upInp, cast(ubyte[]) trueUp)); } - foreach(S; TypeTuple!(dstring, wstring, string)) + foreach (S; AliasSeq!(dstring, wstring, string)) { S easy = "123"; @@ -8913,7 +9260,7 @@ unittest S[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"]; S[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"]; - foreach(val; TypeTuple!(easy, good)) + foreach (val; AliasSeq!(easy, good)) { auto e = val.dup; auto g = e; @@ -8922,15 +9269,15 @@ unittest e.toLowerInPlace(); assert(e is g); } - foreach(i, v; options) + foreach (i, v; options) { doTest(v, upper[i], lower[i]); } // a few combinatorial runs - foreach(i; 0..options.length) - foreach(j; i..options.length) - foreach(k; j..options.length) + foreach (i; 0 .. options.length) + foreach (j; i .. options.length) + foreach (k; j .. options.length) { auto sample = options[i] ~ options[j] ~ options[k]; auto sample2 = options[k] ~ options[j] ~ options[i]; @@ -8951,15 +9298,15 @@ unittest bool isAlpha(dchar c) { // optimization - if(c < 0xAA) + if (c < 0xAA) { size_t x = c - 'A'; - if(x <= 'Z' - 'A') + if (x <= 'Z' - 'A') return true; else { x = c - 'a'; - if(x <= 'z'-'a') + if (x <= 'z'-'a') return true; } return false; @@ -8971,9 +9318,9 @@ bool isAlpha(dchar c) @safe unittest { auto alpha = unicode("Alphabetic"); - foreach(ch; alpha.byCodepoint) + foreach (ch; alpha.byCodepoint) assert(isAlpha(ch)); - foreach(ch; 0..0x4000) + foreach (ch; 0 .. 0x4000) assert((ch in alpha) == isAlpha(ch)); } @@ -8991,9 +9338,9 @@ bool isMark(dchar c) @safe unittest { auto mark = unicode("Mark"); - foreach(ch; mark.byCodepoint) + foreach (ch; mark.byCodepoint) assert(isMark(ch)); - foreach(ch; 0..0x4000) + foreach (ch; 0 .. 0x4000) assert((ch in mark) == isMark(ch)); } @@ -9004,18 +9351,68 @@ bool isMark(dchar c) @safe pure nothrow @nogc bool isNumber(dchar c) { - return numberTrie[c]; + // optimization for ascii case + if (c <= 0x7F) + { + return c >= '0' && c <= '9'; + } + else + { + return numberTrie[c]; + } } @safe unittest { auto n = unicode("N"); - foreach(ch; n.byCodepoint) + foreach (ch; n.byCodepoint) assert(isNumber(ch)); - foreach(ch; 0..0x4000) + foreach (ch; 0 .. 0x4000) assert((ch in n) == isNumber(ch)); } +/++ + Returns whether $(D c) is a Unicode alphabetic $(CHARACTER) or number. + (general Unicode category: Alphabetic, Nd, Nl, No). + + Params: + c = any Unicode character + Returns: + `true` if the character is in the Alphabetic, Nd, Nl, or No Unicode + categories ++/ +@safe pure nothrow @nogc +bool isAlphaNum(dchar c) +{ + static import std.ascii; + + // optimization for ascii case + if (std.ascii.isASCII(c)) + { + return std.ascii.isAlphaNum(c); + } + else + { + return isAlpha(c) || isNumber(c); + } +} + +@safe unittest +{ + auto n = unicode("N"); + auto alpha = unicode("Alphabetic"); + + foreach (ch; n.byCodepoint) + assert(isAlphaNum(ch)); + + foreach (ch; alpha.byCodepoint) + assert(isAlphaNum(ch)); + + foreach (ch; 0 .. 0x4000) + { + assert(((ch in n) || (ch in alpha)) == isAlphaNum(ch)); + } +} /++ Returns whether $(D c) is a Unicode punctuation $(CHARACTER) @@ -9024,10 +9421,20 @@ bool isNumber(dchar c) @safe pure nothrow @nogc bool isPunctuation(dchar c) { - return punctuationTrie[c]; + static import std.ascii; + + // optimization for ascii case + if (c <= 0x7F) + { + return std.ascii.isPunctuation(c); + } + else + { + return punctuationTrie[c]; + } } -unittest +@safe unittest { assert(isPunctuation('\u0021')); assert(isPunctuation('\u0028')); @@ -9036,7 +9443,7 @@ unittest assert(isPunctuation('\u005F')); assert(isPunctuation('\u00AB')); assert(isPunctuation('\u00BB')); - foreach(ch; unicode("P").byCodepoint) + foreach (ch; unicode("P").byCodepoint) assert(isPunctuation(ch)); } @@ -9050,14 +9457,14 @@ bool isSymbol(dchar c) return symbolTrie[c]; } -unittest +@safe unittest { import std.format : format; assert(isSymbol('\u0024')); assert(isSymbol('\u002B')); assert(isSymbol('\u005E')); assert(isSymbol('\u00A6')); - foreach(ch; unicode("S").byCodepoint) + foreach (ch; unicode("S").byCodepoint) assert(isSymbol(ch), format("%04x", ch)); } @@ -9070,16 +9477,17 @@ unittest @safe pure nothrow @nogc bool isSpace(dchar c) { + import std.internal.unicode_tables : isSpaceGen; // generated file return isSpaceGen(c); } -unittest +@safe unittest { assert(isSpace('\u0020')); auto space = unicode.Zs; - foreach(ch; space.byCodepoint) + foreach (ch; space.byCodepoint) assert(isSpace(ch)); - foreach(ch; 0..0x1000) + foreach (ch; 0 .. 0x1000) assert(isSpace(ch) == space[ch]); } @@ -9096,13 +9504,13 @@ bool isGraphical(dchar c) } -unittest +@safe unittest { auto set = unicode("Graphical"); import std.format : format; - foreach(ch; set.byCodepoint) + foreach (ch; set.byCodepoint) assert(isGraphical(ch), format("%4x", ch)); - foreach(ch; 0..0x4000) + foreach (ch; 0 .. 0x4000) assert((ch in set) == isGraphical(ch)); } @@ -9114,18 +9522,19 @@ unittest @safe pure nothrow @nogc bool isControl(dchar c) { + import std.internal.unicode_tables : isControlGen; // generated file return isControlGen(c); } -unittest +@safe unittest { assert(isControl('\u0000')); assert(isControl('\u0081')); assert(!isControl('\u0100')); auto cc = unicode.Cc; - foreach(ch; cc.byCodepoint) + foreach (ch; cc.byCodepoint) assert(isControl(ch)); - foreach(ch; 0..0x1000) + foreach (ch; 0 .. 0x1000) assert(isControl(ch) == cc[ch]); } @@ -9137,14 +9546,15 @@ unittest @safe pure nothrow @nogc bool isFormat(dchar c) { + import std.internal.unicode_tables : isFormatGen; // generated file return isFormatGen(c); } -unittest +@safe unittest { assert(isFormat('\u00AD')); - foreach(ch; unicode("Format").byCodepoint) + foreach (ch; unicode("Format").byCodepoint) assert(isFormat(ch)); } @@ -9202,10 +9612,10 @@ bool isNonCharacter(dchar c) return nonCharacterTrie[c]; } -unittest +@safe unittest { auto set = unicode("Cn"); - foreach(ch; set.byCodepoint) + foreach (ch; set.byCodepoint) assert(isNonCharacter(ch)); } @@ -9225,6 +9635,8 @@ private: @safe pure nothrow @nogc @property { + import std.internal.unicode_tables; // generated file + // It's important to use auto return here, so that the compiler // only runs semantic on the return type if the function gets // used. Also these are functions rather than templates to not @@ -9244,28 +9656,28 @@ private: //normalization quick-check tables auto nfcQCTrie() { - import std.internal.unicode_norm; + import std.internal.unicode_norm : nfcQCTrieEntries; static immutable res = asTrie(nfcQCTrieEntries); return res; } auto nfdQCTrie() { - import std.internal.unicode_norm; + import std.internal.unicode_norm : nfdQCTrieEntries; static immutable res = asTrie(nfdQCTrieEntries); return res; } auto nfkcQCTrie() { - import std.internal.unicode_norm; + import std.internal.unicode_norm : nfkcQCTrieEntries; static immutable res = asTrie(nfkcQCTrieEntries); return res; } auto nfkdQCTrie() { - import std.internal.unicode_norm; + import std.internal.unicode_norm : nfkdQCTrieEntries; static immutable res = asTrie(nfkdQCTrieEntries); return res; } @@ -9273,28 +9685,28 @@ private: //grapheme breaking algorithm tables auto mcTrie() { - import std.internal.unicode_grapheme; + import std.internal.unicode_grapheme : mcTrieEntries; static immutable res = asTrie(mcTrieEntries); return res; } auto graphemeExtendTrie() { - import std.internal.unicode_grapheme; + import std.internal.unicode_grapheme : graphemeExtendTrieEntries; static immutable res = asTrie(graphemeExtendTrieEntries); return res; } auto hangLV() { - import std.internal.unicode_grapheme; + import std.internal.unicode_grapheme : hangulLVTrieEntries; static immutable res = asTrie(hangulLVTrieEntries); return res; } auto hangLVT() { - import std.internal.unicode_grapheme; + import std.internal.unicode_grapheme : hangulLVTTrieEntries; static immutable res = asTrie(hangulLVTTrieEntries); return res; } @@ -9302,28 +9714,28 @@ private: // tables below are used for composition/decomposition auto combiningClassTrie() { - import std.internal.unicode_comp; + import std.internal.unicode_comp : combiningClassTrieEntries; static immutable res = asTrie(combiningClassTrieEntries); return res; } auto compatMappingTrie() { - import std.internal.unicode_decomp; + import std.internal.unicode_decomp : compatMappingTrieEntries; static immutable res = asTrie(compatMappingTrieEntries); return res; } auto canonMappingTrie() { - import std.internal.unicode_decomp; + import std.internal.unicode_decomp : canonMappingTrieEntries; static immutable res = asTrie(canonMappingTrieEntries); return res; } auto compositionJumpTrie() { - import std.internal.unicode_comp; + import std.internal.unicode_comp : compositionJumpTrieEntries; static immutable res = asTrie(compositionJumpTrieEntries); return res; } diff --git a/std/uri.d b/std/uri.d index ce79f36cab0..f05d7cc164a 100644 --- a/std/uri.d +++ b/std/uri.d @@ -11,12 +11,9 @@ * See_Also: * $(LINK2 http://www.ietf.org/rfc/rfc3986.txt, RFC 3986)<br> * $(LINK2 http://en.wikipedia.org/wiki/Uniform_resource_identifier, Wikipedia) - * Macros: - * WIKI = Phobos/StdUri - * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Source: $(PHOBOSSRC std/_uri.d) */ /* Copyright Digital Mars 2000 - 2009. @@ -28,28 +25,15 @@ module std.uri; //debug=uri; // uncomment to turn on debugging writefln's debug(uri) private import std.stdio; - -/* ====================== URI Functions ================ */ - -private import std.ascii; -private import core.stdc.stdlib; -private import std.utf; private import std.traits : isSomeChar; -import core.exception : OutOfMemoryError; -import std.exception : assumeUnique; /** This Exception is thrown if something goes wrong when encoding or decoding a URI. */ class URIException : Exception { - import std.array : empty; - @safe pure nothrow this(string msg, string file = __FILE__, - size_t line = __LINE__, Throwable next = null) - { - super("URI Exception" ~ (!msg.empty ? ": " ~ msg : ""), file, line, - next); - } + import std.exception : basicExceptionCtors; + mixin basicExceptionCtors; } private enum @@ -61,34 +45,31 @@ private enum URI_Hash = 0x10, // '#' } -immutable char[16] hex2ascii = "0123456789ABCDEF"; +private immutable char[16] hex2ascii = "0123456789ABCDEF"; -__gshared ubyte[128] uri_flags; // indexed by character +private immutable ubyte[128] uri_flags = // indexed by character + ({ + ubyte[128] uflags; -shared static this() -{ - // Initialize uri_flags[] - static void helper(immutable char[] p, uint flags) - { - for (int i = 0; i < p.length; i++) - uri_flags[p[i]] |= flags; - } - - uri_flags['#'] |= URI_Hash; - - for (int i = 'A'; i <= 'Z'; i++) - { - uri_flags[i] |= URI_Alpha; - uri_flags[i + 0x20] |= URI_Alpha; // lowercase letters - } - helper("0123456789", URI_Digit); - helper(";/?:@&=+$,", URI_Reserved); - helper("-_.!~*'()", URI_Mark); -} + // Compile time initialize + uflags['#'] |= URI_Hash; + foreach (c; 'A' .. 'Z' + 1) + { + uflags[c] |= URI_Alpha; + uflags[c + 0x20] |= URI_Alpha; // lowercase letters + } + foreach (c; '0' .. '9' + 1) uflags[c] |= URI_Digit; + foreach (c; ";/?:@&=+$,") uflags[c] |= URI_Reserved; + foreach (c; "-_.!~*'()") uflags[c] |= URI_Mark; + return uflags; + })(); -private string URI_Encode(dstring string, uint unescapedSet) +private string URI_Encode(dstring str, uint unescapedSet) { + import core.exception : OutOfMemoryError; + import core.stdc.stdlib : alloca; + uint j; uint k; dchar V; @@ -100,7 +81,7 @@ private string URI_Encode(dstring string, uint unescapedSet) uint Rlen; uint Rsize; // alloc'd size - auto len = string.length; + immutable len = str.length; R = buffer.ptr; Rsize = buffer.length; @@ -108,7 +89,7 @@ private string URI_Encode(dstring string, uint unescapedSet) for (k = 0; k != len; k++) { - C = string[k]; + C = str[k]; // if (C in unescapedSet) if (C < uri_flags.length && uri_flags[C] & unescapedSet) { @@ -117,19 +98,20 @@ private string URI_Encode(dstring string, uint unescapedSet) char* R2; Rsize *= 2; - if (Rsize > 1024) { + if (Rsize > 1024) + { R2 = (new char[Rsize]).ptr; } else { - R2 = cast(char *)alloca(Rsize * char.sizeof); + R2 = cast(char *) alloca(Rsize * char.sizeof); if (!R2) throw new OutOfMemoryError("Alloca failure"); } - R2[0..Rlen] = R[0..Rlen]; + R2[0 .. Rlen] = R[0 .. Rlen]; R = R2; } - R[Rlen] = cast(char)C; + R[Rlen] = cast(char) C; Rlen++; } else @@ -166,27 +148,6 @@ private string URI_Encode(dstring string, uint unescapedSet) Octet[3] = cast(char)(0x80 | (V & 0x3F)); L = 4; } - /+ - else if (V <= 0x3FFFFFF) - { - Octet[0] = cast(char)(0xF8 | (V >> 24)); - Octet[1] = cast(char)(0x80 | ((V >> 18) & 0x3F)); - Octet[2] = cast(char)(0x80 | ((V >> 12) & 0x3F)); - Octet[3] = cast(char)(0x80 | ((V >> 6) & 0x3F)); - Octet[4] = cast(char)(0x80 | (V & 0x3F)); - L = 5; - } - else if (V <= 0x7FFFFFFF) - { - Octet[0] = cast(char)(0xFC | (V >> 30)); - Octet[1] = cast(char)(0x80 | ((V >> 24) & 0x3F)); - Octet[2] = cast(char)(0x80 | ((V >> 18) & 0x3F)); - Octet[3] = cast(char)(0x80 | ((V >> 12) & 0x3F)); - Octet[4] = cast(char)(0x80 | ((V >> 6) & 0x3F)); - Octet[5] = cast(char)(0x80 | (V & 0x3F)); - L = 6; - } - +/ else { throw new URIException("Undefined UTF-32 code point"); @@ -197,16 +158,17 @@ private string URI_Encode(dstring string, uint unescapedSet) char *R2; Rsize = 2 * (Rlen + L * 3); - if (Rsize > 1024) { + if (Rsize > 1024) + { R2 = (new char[Rsize]).ptr; } else { - R2 = cast(char *)alloca(Rsize * char.sizeof); + R2 = cast(char *) alloca(Rsize * char.sizeof); if (!R2) throw new OutOfMemoryError("Alloca failure"); } - R2[0..Rlen] = R[0..Rlen]; + R2[0 .. Rlen] = R[0 .. Rlen]; R = R2; } @@ -221,18 +183,23 @@ private string URI_Encode(dstring string, uint unescapedSet) } } - return R[0..Rlen].idup; + return R[0 .. Rlen].idup; } -uint ascii2hex(dchar c) +private uint ascii2hex(dchar c) @nogc @safe pure nothrow { return (c <= '9') ? c - '0' : (c <= 'F') ? c - 'A' + 10 : c - 'a' + 10; } -private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) if (isSomeChar!Char) +private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) +if (isSomeChar!Char) { + import core.exception : OutOfMemoryError; + import core.stdc.stdlib : alloca; + import std.ascii : isHexDigit; + uint j; uint k; uint V; @@ -242,17 +209,18 @@ private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) if (isSomeChar dchar* R; uint Rlen; - auto len = uri.length; + immutable len = uri.length; auto s = uri.ptr; // Preallocate result buffer R guaranteed to be large enough for result auto Rsize = len; - if (Rsize > 1024 / dchar.sizeof) { + if (Rsize > 1024 / dchar.sizeof) + { R = (new dchar[Rsize]).ptr; } else { - R = cast(dchar *)alloca(Rsize * dchar.sizeof); + R = cast(dchar *) alloca(Rsize * dchar.sizeof); if (!R) throw new OutOfMemoryError("Alloca failure"); } @@ -322,7 +290,7 @@ private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) if (isSomeChar if (C < uri_flags.length && uri_flags[C] & reservedSet) { // R ~= s[start .. k + 1]; - int width = (k + 1) - start; + immutable width = (k + 1) - start; for (int ii = 0; ii < width; ii++) R[Rlen + ii] = s[start + ii]; Rlen += width; @@ -336,7 +304,7 @@ private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) if (isSomeChar assert(Rlen <= Rsize); // enforce our preallocation size guarantee // Copy array on stack to array in memory - return R[0..Rlen].idup; + return R[0 .. Rlen].idup; } /************************************* @@ -345,8 +313,12 @@ private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) if (isSomeChar * Escape sequences that resolve to the '#' character are not replaced. */ -string decode(Char)(in Char[] encodedURI) if (isSomeChar!Char) +string decode(Char)(in Char[] encodedURI) +if (isSomeChar!Char) { + // selective imports trigger wrong deprecation + // https://issues.dlang.org/show_bug.cgi?id=17193 + static import std.utf; auto s = URI_Decode(encodedURI, URI_Reserved | URI_Hash); return std.utf.toUTF8(s); } @@ -356,8 +328,12 @@ string decode(Char)(in Char[] encodedURI) if (isSomeChar!Char) * escape sequences are decoded. */ -string decodeComponent(Char)(in Char[] encodedURIComponent) if (isSomeChar!Char) +string decodeComponent(Char)(in Char[] encodedURIComponent) +if (isSomeChar!Char) { + // selective imports trigger wrong deprecation + // https://issues.dlang.org/show_bug.cgi?id=17193 + static import std.utf; auto s = URI_Decode(encodedURIComponent, 0); return std.utf.toUTF8(s); } @@ -367,9 +343,11 @@ string decodeComponent(Char)(in Char[] encodedURIComponent) if (isSomeChar!Char) * not a valid URI character is escaped. The '#' character is not escaped. */ -string encode(Char)(in Char[] uri) if (isSomeChar!Char) +string encode(Char)(in Char[] uri) +if (isSomeChar!Char) { - auto s = std.utf.toUTF32(uri); + import std.utf : toUTF32; + auto s = toUTF32(uri); return URI_Encode(s, URI_Reserved | URI_Hash | URI_Alpha | URI_Digit | URI_Mark); } @@ -378,34 +356,79 @@ string encode(Char)(in Char[] uri) if (isSomeChar!Char) * Any character not a letter, digit, or one of -_.!~*'() is escaped. */ -string encodeComponent(Char)(in Char[] uriComponent) if (isSomeChar!Char) +string encodeComponent(Char)(in Char[] uriComponent) +if (isSomeChar!Char) { - auto s = std.utf.toUTF32(uriComponent); + import std.utf : toUTF32; + auto s = toUTF32(uriComponent); return URI_Encode(s, URI_Alpha | URI_Digit | URI_Mark); } +/* Encode associative array using www-form-urlencoding + * + * Params: + * values = an associative array containing the values to be encoded. + * + * Returns: + * A string encoded using www-form-urlencoding. + */ +package string urlEncode(in string[string] values) +{ + if (values.length == 0) + return ""; + + import std.array : Appender; + import std.format : formattedWrite; + + Appender!string enc; + enc.reserve(values.length * 128); + + bool first = true; + foreach (k, v; values) + { + if (!first) + enc.put('&'); + formattedWrite(enc, "%s=%s", encodeComponent(k), encodeComponent(v)); + first = false; + } + return enc.data; +} + +@system unittest +{ + // @system because urlEncode -> encodeComponent -> URI_Encode + // URI_Encode uses alloca and pointer slicing + string[string] a; + assert(urlEncode(a) == ""); + assert(urlEncode(["name1" : "value1"]) == "name1=value1"); + assert(urlEncode(["name1" : "value1", "name2" : "value2"]) == "name1=value1&name2=value2"); +} + /*************************** * Does string s[] start with a URL? * Returns: * -1 it does not - * len it does, and s[0..len] is the slice of s[] that is that URL + * len it does, and s[0 .. len] is the slice of s[] that is that URL */ -size_t uriLength(Char)(in Char[] s) if (isSomeChar!Char) +ptrdiff_t uriLength(Char)(in Char[] s) +if (isSomeChar!Char) { /* Must start with one of: * http:// * https:// * www. */ + import std.ascii : isAlphaNum; import std.uni : icmp; - size_t i; + ptrdiff_t i; if (s.length <= 4) return -1; - if (s.length > 7 && icmp(s[0 .. 7], "http://") == 0) { + if (s.length > 7 && icmp(s[0 .. 7], "http://") == 0) + { i = 7; } else @@ -415,10 +438,8 @@ size_t uriLength(Char)(in Char[] s) if (isSomeChar!Char) else return -1; } - // if (icmp(s[0 .. 4], "www.") == 0) - // i = 4; - size_t lastdot; + ptrdiff_t lastdot; for (; i < s.length; i++) { auto c = s[i]; @@ -436,7 +457,6 @@ size_t uriLength(Char)(in Char[] s) if (isSomeChar!Char) } break; } - //if (!lastdot || (i - lastdot != 3 && i - lastdot != 4)) if (!lastdot) return -1; @@ -444,12 +464,13 @@ size_t uriLength(Char)(in Char[] s) if (isSomeChar!Char) } /// -unittest +@safe unittest { string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!"; - assert (uriLength(s1) == 49); + assert(uriLength(s1) == 49); string s2 = "no uri here"; - assert (uriLength(s2) == -1); + assert(uriLength(s2) == -1); + assert(uriLength("issue 14924") < 0); } @@ -457,13 +478,16 @@ unittest * Does string s[] start with an email address? * Returns: * -1 it does not - * len it does, and s[0..i] is the slice of s[] that is that email address + * len it does, and s[0 .. i] is the slice of s[] that is that email address * References: * RFC2822 */ -size_t emailLength(Char)(in Char[] s) if (isSomeChar!Char) +ptrdiff_t emailLength(Char)(in Char[] s) +if (isSomeChar!Char) { - size_t i; + import std.ascii : isAlpha, isAlphaNum; + + ptrdiff_t i; if (!isAlpha(s[0])) return -1; @@ -485,7 +509,7 @@ size_t emailLength(Char)(in Char[] s) if (isSomeChar!Char) /* Now do the part past the '@' */ - size_t lastdot; + ptrdiff_t lastdot; for (; i < s.length; i++) { auto c = s[i]; @@ -507,17 +531,19 @@ size_t emailLength(Char)(in Char[] s) if (isSomeChar!Char) } /// -unittest +@safe unittest { string s1 = "my.e-mail@www.example-domain.com with garbage added"; - assert (emailLength(s1) == 32); + assert(emailLength(s1) == 32); string s2 = "no email address here"; - assert (emailLength(s2) == -1); + assert(emailLength(s2) == -1); + assert(emailLength("issue 14924") < 0); } -unittest +@system unittest { + //@system because of encode -> URI_Encode debug(uri) writeln("uri.encodeURI.unittest"); string source = "http://www.digitalmars.com/~fred/fred's RX.html#foo"; @@ -545,8 +571,8 @@ unittest result = decode("%41%42%43"); debug(uri) writeln(result); - import std.typetuple : TypeTuple; - foreach (StringType; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + import std.meta : AliasSeq; + foreach (StringType; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { import std.conv : to; StringType decoded1 = source.to!StringType; diff --git a/std/utf.d b/std/utf.d index 7f3cb90df82..29609a42688 100644 --- a/std/utf.d +++ b/std/utf.d @@ -6,23 +6,64 @@ UTF character support is restricted to $(D '\u0000' <= character <= '\U0010FFFF'). +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Decode) $(TD + $(LREF decode) + $(LREF decodeFront) +)) +$(TR $(TD Lazy decode) $(TD + $(LREF byCodeUnit) + $(LREF byChar) + $(LREF byWchar) + $(LREF byDchar) + $(LREF byUTF) +)) +$(TR $(TD Encode) $(TD + $(LREF encode) + $(LREF toUTF8) + $(LREF toUTF16) + $(LREF toUTF32) + $(LREF toUTFz) + $(LREF toUTF16z) +)) +$(TR $(TD Length) $(TD + $(LREF codeLength) + $(LREF count) + $(LREF stride) + $(LREF strideBack) +)) +$(TR $(TD Index) $(TD + $(LREF toUCSindex) + $(LREF toUTFindex) +)) +$(TR $(TD Validation) $(TD + $(LREF isValidDchar) + $(LREF validate) +)) +$(TR $(TD Miscellaneous) $(TD + $(LREF replacementDchar) + $(LREF UseReplacementDchar) + $(LREF UTFException) +)) +) See_Also: $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br> $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br> $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335) - Macros: - WIKI = Phobos/StdUtf - Copyright: Copyright Digital Mars 2000 - 2012. - License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: $(WEB digitalmars.com, Walter Bright) and Jonathan M Davis + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: $(HTTP digitalmars.com, Walter Bright) and Jonathan M Davis Source: $(PHOBOSSRC std/_utf.d) +/ module std.utf; +import std.meta; // AliasSeq import std.range.primitives; import std.traits; // isSomeChar, isSomeString -import std.typetuple; // TypeTuple +import std.typecons; // Flag, Yes, No +import std.exception; // basicExceptionCtors //debug=utf; // uncomment to turn on debugging printf's @@ -34,13 +75,13 @@ debug (utf) import core.stdc.stdio : printf; +/ class UTFException : Exception { - import std.string : format; + import core.internal.string : unsignedToTempString, UnsignedStringBuf; uint[4] sequence; size_t len; @safe pure nothrow @nogc - UTFException setSequence(uint[] data...) + UTFException setSequence(scope uint[] data...) { assert(data.length <= 4); @@ -50,28 +91,47 @@ class UTFException : Exception return this; } - @safe pure nothrow - this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + // FIXME: Use std.exception.basicExceptionCtors here once bug #11500 is fixed + + this(string msg, string file = __FILE__, size_t line = __LINE__, + Throwable next = null) @nogc @safe pure nothrow { super(msg, file, line, next); } - @safe pure - this(string msg, size_t index, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + this(string msg, size_t index, string file = __FILE__, + size_t line = __LINE__, Throwable next = null) @safe pure nothrow { - super(msg ~ format(" (at index %s)", index), file, line, next); + UnsignedStringBuf buf = void; + msg ~= " (at index " ~ unsignedToTempString(index, buf, 10) ~ ")"; + super(msg, file, line, next); } - override string toString() + override string toString() const { if (len == 0) - return super.toString(); + { + /* Exception.toString() is not marked as const, although + * it is const-compatible. + */ + //return super.toString(); + auto e = () @trusted { return cast(Exception) super; } (); + return e.toString(); + } string result = "Invalid UTF sequence:"; foreach (i; sequence[0 .. len]) - result ~= format(" %02x", i); + { + UnsignedStringBuf buf = void; + result ~= ' '; + auto h = unsignedToTempString(i, buf, 16); + if (h.length == 1) + result ~= '0'; + result ~= h; + result ~= 'x'; + } if (super.msg.length > 0) { @@ -83,28 +143,120 @@ class UTFException : Exception } } +/* + Provide array of invalidly encoded UTF strings. Useful for testing. + + Params: + Char = char, wchar, or dchar + + Returns: + an array of invalidly encoded UTF strings + */ + +package auto invalidUTFstrings(Char)() @safe pure @nogc nothrow +if (isSomeChar!Char) +{ + static if (is(Char == char)) + { + enum x = 0xDC00; // invalid surrogate value + enum y = 0x110000; // out of range + + static immutable string[8] result = + [ + "\x80", // not a start byte + "\xC0", // truncated + "\xC0\xC0", // invalid continuation + "\xF0\x82\x82\xAC", // overlong + [ + 0xE0 | (x >> 12), + 0x80 | ((x >> 6) & 0x3F), + 0x80 | (x & 0x3F) + ], + [ + cast(char)(0xF0 | (y >> 18)), + cast(char)(0x80 | ((y >> 12) & 0x3F)), + cast(char)(0x80 | ((y >> 6) & 0x3F)), + cast(char)(0x80 | (y & 0x3F)) + ], + [ + cast(char)(0xF8 | 3), // 5 byte encoding + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + ], + [ + cast(char)(0xFC | 3), // 6 byte encoding + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + cast(char)(0x80 | 3), + ], + ]; + + return result[]; + } + else static if (is(Char == wchar)) + { + static immutable wstring[5] result = + [ + [ + cast(wchar) 0xDC00, + ], + [ + cast(wchar) 0xDFFF, + ], + [ + cast(wchar) 0xDBFF, + cast(wchar) 0xDBFF, + ], + [ + cast(wchar) 0xDBFF, + cast(wchar) 0xE000, + ], + [ + cast(wchar) 0xD800, + ], + ]; + + return result[]; + } + else static if (is(Char == dchar)) + { + static immutable dstring[3] result = + [ + [ cast(dchar) 0x110000 ], + [ cast(dchar) 0x00D800 ], + [ cast(dchar) 0x00DFFF ], + ]; + + return result; + } + else + static assert(0); +} /++ - Returns whether $(D c) is a valid UTF-32 character. + Check whether the given Unicode code point is valid. + + Params: + c = code point to check + Returns: + $(D true) iff $(D c) is a valid Unicode code point + + Note: $(D '\uFFFE') and $(D '\uFFFF') are considered valid by $(D isValidDchar), as they are permitted for internal use by an application, but they are not allowed for interchange by the Unicode standard. +/ -@safe -pure nothrow bool isValidDchar(dchar c) @nogc +bool isValidDchar(dchar c) pure nothrow @safe @nogc { - /* Note: FFFE and FFFF are specifically permitted by the - * Unicode standard for application internal use, but are not - * allowed for interchange. - * (thanks to Arcane Jill) - */ - - return c < 0xD800 || - (c > 0xDFFF && c <= 0x10FFFF /*&& c != 0xFFFE && c != 0xFFFF*/); + return c < 0xD800 || (c > 0xDFFF && c <= 0x10FFFF); } -unittest +pure nothrow @safe @nogc unittest { import std.exception; debug(utf) printf("utf.isValidDchar.unittest\n"); @@ -112,48 +264,48 @@ unittest assertCTFEable!( { assert( isValidDchar(cast(dchar)'a') == true); - assert( isValidDchar(cast(dchar)0x1FFFFF) == false); - - assert(!isValidDchar(cast(dchar)0x00D800)); - assert(!isValidDchar(cast(dchar)0x00DBFF)); - assert(!isValidDchar(cast(dchar)0x00DC00)); - assert(!isValidDchar(cast(dchar)0x00DFFF)); - assert( isValidDchar(cast(dchar)0x00FFFE)); - assert( isValidDchar(cast(dchar)0x00FFFF)); - assert( isValidDchar(cast(dchar)0x01FFFF)); - assert( isValidDchar(cast(dchar)0x10FFFF)); - assert(!isValidDchar(cast(dchar)0x110000)); + assert( isValidDchar(cast(dchar) 0x1FFFFF) == false); + + assert(!isValidDchar(cast(dchar) 0x00D800)); + assert(!isValidDchar(cast(dchar) 0x00DBFF)); + assert(!isValidDchar(cast(dchar) 0x00DC00)); + assert(!isValidDchar(cast(dchar) 0x00DFFF)); + assert( isValidDchar(cast(dchar) 0x00FFFE)); + assert( isValidDchar(cast(dchar) 0x00FFFF)); + assert( isValidDchar(cast(dchar) 0x01FFFF)); + assert( isValidDchar(cast(dchar) 0x10FFFF)); + assert(!isValidDchar(cast(dchar) 0x110000)); }); } /++ - $(D stride) returns the length of the UTF-8 sequence starting at $(D index) + Calculate the length of the UTF sequence starting at $(D index) in $(D str). - $(D stride) works with both UTF-8 strings and ranges of $(D char). If no - index is passed, then an input range will work, but if an index is passed, - then a random-access range is required. - - $(D index) defaults to $(D 0) if none is passed. + Params: + str = input range of UTF code units. Must be random access if + $(D index) is passed + index = starting index of UTF sequence (default: $(D 0)) Returns: - The number of bytes in the UTF-8 sequence, a value between 1 and 4 - (as per $(WEB tools.ietf.org/html/rfc3629#section-3, RFC 3629$(COMMA) section 3)). + The number of code units in the UTF sequence. For UTF-8, this is a + value between 1 and 4 (as per $(HTTP tools.ietf.org/html/rfc3629#section-3, RFC 3629$(COMMA) section 3)). + For UTF-16, it is either 1 or 2. For UTF-32, it is always 1. Throws: May throw a $(D UTFException) if $(D str[index]) is not the start of a - valid UTF-8 sequence. + valid UTF sequence. - Notes: + Note: $(D stride) will only analyze the first $(D str[index]) element. It - will not fully verify the validity of UTF-8 sequence, nor even verify + will not fully verify the validity of the UTF sequence, nor even verify the presence of the sequence: it will not actually guarantee that $(D index + stride(str, index) <= str.length). +/ uint stride(S)(auto ref S str, size_t index) - if (is(S : const char[]) || - (isRandomAccessRange!S && is(Unqual!(ElementType!S) == char))) +if (is(S : const char[]) || + (isRandomAccessRange!S && is(Unqual!(ElementType!S) == char))) { static if (is(typeof(str.length) : ulong)) assert(index < str.length, "Past the end of the UTF-8 sequence"); @@ -167,8 +319,8 @@ uint stride(S)(auto ref S str, size_t index) /// Ditto uint stride(S)(auto ref S str) - if (is(S : const char[]) || - (isInputRange!S && is(Unqual!(ElementType!S) == char))) +if (is(S : const char[]) || + (isInputRange!S && is(Unqual!(ElementType!S) == char))) { static if (is(S : const char[])) immutable c = str[0]; @@ -192,11 +344,11 @@ body return msbs; } -unittest +@system unittest { import std.conv : to; import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; static void test(string s, dchar c, size_t i = 0, size_t line = __LINE__) { @@ -248,7 +400,7 @@ unittest test("hello\U00010143\u0100\U00010143", '\u0100', 9); test("hello\U00010143\u0100\U00010143", '\U00010143', 11); - foreach (S; TypeTuple!(char[], const char[], string)) + foreach (S; AliasSeq!(char[], const char[], string)) { enum str = to!S("hello world"); static assert(isSafe!({ stride(str, 0); })); @@ -259,9 +411,9 @@ unittest }); } -unittest // invalid start bytes +@safe unittest // invalid start bytes { - import std.exception: assertThrown; + import std.exception : assertThrown; immutable char[] invalidStartBytes = [ 0b1111_1000, // indicating a sequence length of 5 0b1111_1100, // 6 @@ -269,36 +421,210 @@ unittest // invalid start bytes 0b1111_1111, // 8 0b1000_0000, // continuation byte ]; - foreach(c; invalidStartBytes) + foreach (c; invalidStartBytes) assertThrown!UTFException(stride([c])); } +/// Ditto +uint stride(S)(auto ref S str, size_t index) +if (is(S : const wchar[]) || + (isRandomAccessRange!S && is(Unqual!(ElementType!S) == wchar))) +{ + static if (is(typeof(str.length) : ulong)) + assert(index < str.length, "Past the end of the UTF-16 sequence"); + immutable uint u = str[index]; + return 1 + (u >= 0xD800 && u <= 0xDBFF); +} + +/// Ditto +uint stride(S)(auto ref S str) @safe pure +if (is(S : const wchar[])) +{ + return stride(str, 0); +} -/++ - $(D strideBack) returns the length of the UTF-8 sequence ending one code - unit before $(D index) in $(D str). +/// Ditto +uint stride(S)(auto ref S str) +if (isInputRange!S && is(Unqual!(ElementType!S) == wchar)) +{ + assert(!str.empty, "UTF-16 sequence is empty"); + immutable uint u = str.front; + return 1 + (u >= 0xD800 && u <= 0xDBFF); +} + +@system unittest +{ + import std.conv : to; + import std.exception; + import std.string : format; + import core.exception : AssertError; + static void test(wstring s, dchar c, size_t i = 0, size_t line = __LINE__) + { + enforce(stride(s, i) == codeLength!wchar(c), + new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); + + enforce(stride(RandomCU!wchar(s), i) == codeLength!wchar(c), + new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); + + auto refRandom = new RefRandomCU!wchar(s); + immutable randLen = refRandom.length; + enforce(stride(refRandom, i) == codeLength!wchar(c), + new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); + enforce(refRandom.length == randLen, + new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); + + if (i == 0) + { + enforce(stride(s) == codeLength!wchar(c), + new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); + + enforce(stride(InputCU!wchar(s)) == codeLength!wchar(c), + new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); + + auto refBidir = new RefBidirCU!wchar(s); + immutable bidirLen = refBidir.length; + enforce(stride(refBidir) == codeLength!wchar(c), + new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); + enforce(refBidir.length == bidirLen, + new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); + } + } + + assertCTFEable!( + { + test("a", 'a'); + test(" ", ' '); + test("\u2029", '\u2029'); //paraSep + test("\u0100", '\u0100'); + test("\u0430", '\u0430'); + test("\U00010143", '\U00010143'); + test("abcdefcdef", 'a'); + test("hello\U00010143\u0100\U00010143", 'h', 0); + test("hello\U00010143\u0100\U00010143", 'e', 1); + test("hello\U00010143\u0100\U00010143", 'l', 2); + test("hello\U00010143\u0100\U00010143", 'l', 3); + test("hello\U00010143\u0100\U00010143", 'o', 4); + test("hello\U00010143\u0100\U00010143", '\U00010143', 5); + test("hello\U00010143\u0100\U00010143", '\u0100', 7); + test("hello\U00010143\u0100\U00010143", '\U00010143', 8); + + foreach (S; AliasSeq!(wchar[], const wchar[], wstring)) + { + enum str = to!S("hello world"); + static assert(isSafe!(() => stride(str, 0))); + static assert(isSafe!(() => stride(str) )); + static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); + } + }); +} + +/// Ditto +uint stride(S)(auto ref S str, size_t index = 0) +if (is(S : const dchar[]) || + (isInputRange!S && is(Unqual!(ElementEncodingType!S) == dchar))) +{ + static if (is(typeof(str.length) : ulong)) + assert(index < str.length, "Past the end of the UTF-32 sequence"); + else + assert(!str.empty, "UTF-32 sequence is empty."); + return 1; +} + +@system unittest +{ + import std.conv : to; + import std.exception; + import std.string : format; + import core.exception : AssertError; + static void test(dstring s, dchar c, size_t i = 0, size_t line = __LINE__) + { + enforce(stride(s, i) == codeLength!dchar(c), + new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); + + enforce(stride(RandomCU!dchar(s), i) == codeLength!dchar(c), + new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); + + auto refRandom = new RefRandomCU!dchar(s); + immutable randLen = refRandom.length; + enforce(stride(refRandom, i) == codeLength!dchar(c), + new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); + enforce(refRandom.length == randLen, + new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); + + if (i == 0) + { + enforce(stride(s) == codeLength!dchar(c), + new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); + + enforce(stride(InputCU!dchar(s)) == codeLength!dchar(c), + new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); + + auto refBidir = new RefBidirCU!dchar(s); + immutable bidirLen = refBidir.length; + enforce(stride(refBidir) == codeLength!dchar(c), + new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); + enforce(refBidir.length == bidirLen, + new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); + } + } + + assertCTFEable!( + { + test("a", 'a'); + test(" ", ' '); + test("\u2029", '\u2029'); //paraSep + test("\u0100", '\u0100'); + test("\u0430", '\u0430'); + test("\U00010143", '\U00010143'); + test("abcdefcdef", 'a'); + test("hello\U00010143\u0100\U00010143", 'h', 0); + test("hello\U00010143\u0100\U00010143", 'e', 1); + test("hello\U00010143\u0100\U00010143", 'l', 2); + test("hello\U00010143\u0100\U00010143", 'l', 3); + test("hello\U00010143\u0100\U00010143", 'o', 4); + test("hello\U00010143\u0100\U00010143", '\U00010143', 5); + test("hello\U00010143\u0100\U00010143", '\u0100', 6); + test("hello\U00010143\u0100\U00010143", '\U00010143', 7); + + foreach (S; AliasSeq!(dchar[], const dchar[], dstring)) + { + enum str = to!S("hello world"); + static assert(isSafe!(() => stride(str, 0))); + static assert(isSafe!(() => stride(str) )); + static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); + } + }); +} - $(D strideBack) works with both UTF-8 strings and bidirectional ranges of - $(D char). If no index is passed, then a bidirectional range will work, but - if an index is passed, then a random-access range is required. +/++ + Calculate the length of the UTF sequence ending one code unit before + $(D index) in $(D str). - $(D index) defaults to $(D str.length) if none is passed. + Params: + str = bidirectional range of UTF code units. Must be random access if + $(D index) is passed + index = index one past end of UTF sequence (default: $(D str.length)) Returns: - The number of bytes in the UTF-8 sequence. + The number of code units in the UTF sequence. For UTF-8, this is a + value between 1 and 4 (as per $(HTTP tools.ietf.org/html/rfc3629#section-3, RFC 3629$(COMMA) section 3)). + For UTF-16, it is either 1 or 2. For UTF-32, it is always 1. Throws: May throw a $(D UTFException) if $(D str[index]) is not one past the - end of a valid UTF-8 sequence. + end of a valid UTF sequence. - Notes: - $(D strideBack) will not fully verify the validity of the UTF-8 - sequence. It will, however, guarantee that - $(D index - stride(str, index)) is a valid index. + Note: + $(D strideBack) will only analyze the element at $(D str[index - 1]) + element. It will not fully verify the validity of the UTF sequence, nor + even verify the presence of the sequence: it will not actually + guarantee that $(D strideBack(str, index) <= index). +/ uint strideBack(S)(auto ref S str, size_t index) - if (is(S : const char[]) || - (isRandomAccessRange!S && is(Unqual!(ElementType!S) == char))) +if (is(S : const char[]) || + (isRandomAccessRange!S && is(Unqual!(ElementType!S) == char))) { static if (is(typeof(str.length) : ulong)) assert(index <= str.length, "Past the end of the UTF-8 sequence"); @@ -309,7 +635,7 @@ uint strideBack(S)(auto ref S str, size_t index) if (index >= 4) //single verification for most common case { - foreach (i; TypeTuple!(2, 3, 4)) + foreach (i; AliasSeq!(2, 3, 4)) { if ((str[index-i] & 0b1100_0000) != 0b1000_0000) return i; @@ -317,7 +643,7 @@ uint strideBack(S)(auto ref S str, size_t index) } else { - foreach (i; TypeTuple!(2, 3)) + foreach (i; AliasSeq!(2, 3)) { if (index >= i && (str[index-i] & 0b1100_0000) != 0b1000_0000) return i; @@ -328,18 +654,19 @@ uint strideBack(S)(auto ref S str, size_t index) /// Ditto uint strideBack(S)(auto ref S str) - if (is(S : const char[]) || - (isRandomAccessRange!S && hasLength!S && is(Unqual!(ElementType!S) == char))) +if (is(S : const char[]) || + (isRandomAccessRange!S && hasLength!S && is(Unqual!(ElementType!S) == char))) { return strideBack(str, str.length); } +/// Ditto uint strideBack(S)(auto ref S str) - if (isBidirectionalRange!S && is(Unqual!(ElementType!S) == char) && !isRandomAccessRange!S) +if (isBidirectionalRange!S && is(Unqual!(ElementType!S) == char) && !isRandomAccessRange!S) { assert(!str.empty, "Past the end of the UTF-8 sequence"); auto temp = str.save; - foreach (i; TypeTuple!(1, 2, 3, 4)) + foreach (i; AliasSeq!(1, 2, 3, 4)) { if ((temp.back & 0b1100_0000) != 0b1000_0000) return i; @@ -350,11 +677,11 @@ uint strideBack(S)(auto ref S str) throw new UTFException("The last code unit is not the end of the UTF-8 sequence"); } -unittest +@system unittest { import std.conv : to; import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; static void test(string s, dchar c, size_t i = size_t.max, size_t line = __LINE__) { @@ -406,7 +733,7 @@ unittest test("\U00010143\u0100\U00010143hello", '\u0100', 6); test("\U00010143\u0100\U00010143hello", '\U00010143', 4); - foreach (S; TypeTuple!(char[], const char[], string)) + foreach (S; AliasSeq!(char[], const char[], string)) { enum str = to!S("hello world"); static assert(isSafe!({ strideBack(str, 0); })); @@ -417,187 +744,48 @@ unittest }); } - -/++ - $(D stride) returns the length of the UTF-16 sequence starting at $(D index) - in $(D str). - - $(D stride) works with both UTF-16 strings and ranges of $(D wchar). If no - index is passed, then an input range will work, but if an index is passed, - then a random-access range is required. - - $(D index) defaults to $(D 0) if none is passed. - - Returns: - The number of bytes in the UTF-16 sequence. - - Throws: - May throw a $(D UTFException) if $(D str[index]) is not the start of a - valid UTF-16 sequence. - - Notes: - $(D stride) will only analyze the first $(D str[index]) element. It - will not fully verify the validity of UTF-16 sequence, nor even verify - the presence of the sequence: it will not actually guarantee that - $(D index + stride(str, index) <= str.length). - +/ -uint stride(S)(auto ref S str, size_t index) - if (is(S : const wchar[]) || - (isRandomAccessRange!S && is(Unqual!(ElementType!S) == wchar))) +//UTF-16 is self synchronizing: The length of strideBack can be found from +//the value of a single wchar +/// Ditto +uint strideBack(S)(auto ref S str, size_t index) +if (is(S : const wchar[]) || + (isRandomAccessRange!S && is(Unqual!(ElementType!S) == wchar))) { static if (is(typeof(str.length) : ulong)) - assert(index < str.length, "Past the end of the UTF-16 sequence"); - immutable uint u = str[index]; - return 1 + (u >= 0xD800 && u <= 0xDBFF); -} + assert(index <= str.length, "Past the end of the UTF-16 sequence"); + assert(index > 0, "Not the end of a UTF-16 sequence"); -/// Ditto -uint stride(S)(auto ref S str) @safe pure - if (is(S : const wchar[])) -{ - return stride(str, 0); + immutable c2 = str[index-1]; + return 1 + (0xDC00 <= c2 && c2 < 0xE000); } -uint stride(S)(auto ref S str) - if (isInputRange!S && is(Unqual!(ElementType!S) == wchar)) +/// Ditto +uint strideBack(S)(auto ref S str) +if (is(S : const wchar[]) || + (isBidirectionalRange!S && is(Unqual!(ElementType!S) == wchar))) { assert(!str.empty, "UTF-16 sequence is empty"); - immutable uint u = str.front; - return 1 + (u >= 0xD800 && u <= 0xDBFF); + + static if (is(S : const(wchar)[])) + immutable c2 = str[$ - 1]; + else + immutable c2 = str.back; + + return 1 + (0xDC00 <= c2 && c2 <= 0xE000); } -@trusted unittest +@system unittest { import std.conv : to; import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; - static void test(wstring s, dchar c, size_t i = 0, size_t line = __LINE__) + static void test(wstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) { - enforce(stride(s, i) == codeLength!wchar(c), + enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!wchar(c), new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - enforce(stride(RandomCU!wchar(s), i) == codeLength!wchar(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!wchar(s); - immutable randLen = refRandom.length; - enforce(stride(refRandom, i) == codeLength!wchar(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == 0) - { - enforce(stride(s) == codeLength!wchar(c), - new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); - - enforce(stride(InputCU!wchar(s)) == codeLength!wchar(c), - new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!wchar(s); - immutable bidirLen = refBidir.length; - enforce(stride(refBidir) == codeLength!wchar(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'a'); - test("hello\U00010143\u0100\U00010143", 'h', 0); - test("hello\U00010143\u0100\U00010143", 'e', 1); - test("hello\U00010143\u0100\U00010143", 'l', 2); - test("hello\U00010143\u0100\U00010143", 'l', 3); - test("hello\U00010143\u0100\U00010143", 'o', 4); - test("hello\U00010143\u0100\U00010143", '\U00010143', 5); - test("hello\U00010143\u0100\U00010143", '\u0100', 7); - test("hello\U00010143\u0100\U00010143", '\U00010143', 8); - - foreach (S; TypeTuple!(wchar[], const wchar[], wstring)) - { - enum str = to!S("hello world"); - static assert(isSafe!(() => stride(str, 0))); - static assert(isSafe!(() => stride(str) )); - static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); - } - }); -} - - -/++ - $(D strideBack) returns the length of the UTF-16 sequence ending one code - unit before $(D index) in $(D str). - - $(D strideBack) works with both UTF-16 strings and ranges of $(D wchar). If - no index is passed, then a bidirectional range will work, but if an index is - passed, then a random-access range is required. - - $(D index) defaults to $(D str.length) if none is passed. - - Returns: - The number of bytes in the UTF-16 sequence. - - Throws: - May throw a $(D UTFException) if $(D str[index]) is not one past the - end of a valid UTF-16 sequence. - - Notes: - $(D stride) will only analyze the element at $(D str[index - 1]) - element. It will not fully verify the validity of UTF-16 sequence, nor - even verify the presence of the sequence: it will not actually - guarantee that $(D stride(str, index) <= index). - +/ -//UTF-16 is self synchronizing: The length of strideBack can be found from -//the value of a single wchar -uint strideBack(S)(auto ref S str, size_t index) - if (is(S : const wchar[]) || - (isRandomAccessRange!S && is(Unqual!(ElementType!S) == wchar))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index <= str.length, "Past the end of the UTF-16 sequence"); - assert(index > 0, "Not the end of a UTF-16 sequence"); - - immutable c2 = str[index-1]; - return 1 + (0xDC00 <= c2 && c2 < 0xE000); -} - -/// Ditto -uint strideBack(S)(auto ref S str) - if (is(S : const wchar[]) || - (isBidirectionalRange!S && is(Unqual!(ElementType!S) == wchar))) -{ - assert(!str.empty, "UTF-16 sequence is empty"); - - static if (is(S : const(wchar)[])) - immutable c2 = str[$ - 1]; - else - immutable c2 = str.back; - - return 1 + (0xDC00 <= c2 && c2 <= 0xE000); -} - -unittest -{ - import std.conv : to; - import std.exception; - import std. string : format; - import core.exception : AssertError; - static void test(wstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) - { - enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!wchar(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(strideBack(RandomCU!wchar(s), i == size_t.max ? s.length : i) == codeLength!wchar(c), + enforce(strideBack(RandomCU!wchar(s), i == size_t.max ? s.length : i) == codeLength!wchar(c), new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); auto refRandom = new RefRandomCU!wchar(s); @@ -642,7 +830,7 @@ unittest test("\U00010143\u0100\U00010143hello", '\u0100', 3); test("\U00010143\u0100\U00010143hello", '\U00010143', 2); - foreach (S; TypeTuple!(wchar[], const wchar[], wstring)) + foreach (S; AliasSeq!(wchar[], const wchar[], wstring)) { enum str = to!S("hello world"); static assert(isSafe!(() => strideBack(str, 0))); @@ -653,116 +841,9 @@ unittest }); } - -/++ - $(D stride) returns the length of the UTF-32 sequence starting at $(D index) - in $(D str). - - $(D stride) works with both UTF-32 strings and ranges of $(D dchar). - - Returns: - The number of bytes in the UTF-32 sequence (always $(D 1)). - - Throws: - Never. - +/ -uint stride(S)(auto ref S str, size_t index = 0) - if (is(S : const dchar[]) || - (isInputRange!S && is(Unqual!(ElementEncodingType!S) == dchar))) -{ - static if (is(typeof(str.length) : ulong)) - assert(index < str.length, "Past the end of the UTF-32 sequence"); - else - assert(!str.empty, "UTF-32 sequence is empty."); - return 1; -} - -unittest -{ - import std.conv : to; - import std.exception; - import std. string : format; - import core.exception : AssertError; - static void test(dstring s, dchar c, size_t i = 0, size_t line = __LINE__) - { - enforce(stride(s, i) == codeLength!dchar(c), - new AssertError(format("Unit test failure string: %s", s), __FILE__, line)); - - enforce(stride(RandomCU!dchar(s), i) == codeLength!dchar(c), - new AssertError(format("Unit test failure range: %s", s), __FILE__, line)); - - auto refRandom = new RefRandomCU!dchar(s); - immutable randLen = refRandom.length; - enforce(stride(refRandom, i) == codeLength!dchar(c), - new AssertError(format("Unit test failure rand ref range: %s", s), __FILE__, line)); - enforce(refRandom.length == randLen, - new AssertError(format("Unit test failure rand ref range length: %s", s), __FILE__, line)); - - if (i == 0) - { - enforce(stride(s) == codeLength!dchar(c), - new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line)); - - enforce(stride(InputCU!dchar(s)) == codeLength!dchar(c), - new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line)); - - auto refBidir = new RefBidirCU!dchar(s); - immutable bidirLen = refBidir.length; - enforce(stride(refBidir) == codeLength!dchar(c), - new AssertError(format("Unit test failure bidir ref range code length: %s", s), __FILE__, line)); - enforce(refBidir.length == bidirLen, - new AssertError(format("Unit test failure bidir ref range length: %s", s), __FILE__, line)); - } - } - - assertCTFEable!( - { - test("a", 'a'); - test(" ", ' '); - test("\u2029", '\u2029'); //paraSep - test("\u0100", '\u0100'); - test("\u0430", '\u0430'); - test("\U00010143", '\U00010143'); - test("abcdefcdef", 'a'); - test("hello\U00010143\u0100\U00010143", 'h', 0); - test("hello\U00010143\u0100\U00010143", 'e', 1); - test("hello\U00010143\u0100\U00010143", 'l', 2); - test("hello\U00010143\u0100\U00010143", 'l', 3); - test("hello\U00010143\u0100\U00010143", 'o', 4); - test("hello\U00010143\u0100\U00010143", '\U00010143', 5); - test("hello\U00010143\u0100\U00010143", '\u0100', 6); - test("hello\U00010143\u0100\U00010143", '\U00010143', 7); - - foreach (S; TypeTuple!(dchar[], const dchar[], dstring)) - { - enum str = to!S("hello world"); - static assert(isSafe!(() => stride(str, 0))); - static assert(isSafe!(() => stride(str) )); - static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); - } - }); -} - - -/++ - $(D strideBack) returns the length of the UTF-32 sequence ending one code - unit before $(D index) in $(D str). - - $(D strideBack) works with both UTF-32 strings and ranges of $(D dchar). If - no index is passed, then a bidirectional range will work, but if an index is - passed, then a random-access range is required. - - $(D index) defaults to $(D str.length) if none is passed. - - Returns: - The number of bytes in the UTF-32 sequence (always $(D 1)). - - Throws: - Never. - +/ +/// Ditto uint strideBack(S)(auto ref S str, size_t index) - if (isRandomAccessRange!S && is(Unqual!(ElementEncodingType!S) == dchar)) +if (isRandomAccessRange!S && is(Unqual!(ElementEncodingType!S) == dchar)) { static if (is(typeof(str.length) : ulong)) assert(index <= str.length, "Past the end of the UTF-32 sequence"); @@ -772,17 +853,17 @@ uint strideBack(S)(auto ref S str, size_t index) /// Ditto uint strideBack(S)(auto ref S str) - if (isBidirectionalRange!S && is(Unqual!(ElementEncodingType!S) == dchar)) +if (isBidirectionalRange!S && is(Unqual!(ElementEncodingType!S) == dchar)) { assert(!str.empty, "Empty UTF-32 sequence"); return 1; } -unittest +@system unittest { import std.conv : to; import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; static void test(dstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) { @@ -834,7 +915,7 @@ unittest test("\U00010143\u0100\U00010143hello", '\u0100', 2); test("\U00010143\u0100\U00010143hello", '\U00010143', 1); - foreach (S; TypeTuple!(dchar[], const dchar[], dstring)) + foreach (S; AliasSeq!(dchar[], const dchar[], dstring)) { enum str = to!S("hello world"); static assert(isSafe!(() => strideBack(str, 0))); @@ -854,7 +935,7 @@ unittest the string that that code point is. +/ size_t toUCSindex(C)(const(C)[] str, size_t index) @safe pure - if (isSomeChar!C) +if (isSomeChar!C) { static if (is(Unqual!C == dchar)) return index; @@ -879,7 +960,7 @@ size_t toUCSindex(C)(const(C)[] str, size_t index) @safe pure } /// -unittest +@safe unittest { assert(toUCSindex(`hello world`, 7) == 7); assert(toUCSindex(`hello world`w, 7) == 7); @@ -901,7 +982,7 @@ unittest the array index of the code unit is returned. +/ size_t toUTFindex(C)(const(C)[] str, size_t n) @safe pure - if (isSomeChar!C) +if (isSomeChar!C) { static if (is(Unqual!C == dchar)) { @@ -919,7 +1000,7 @@ size_t toUTFindex(C)(const(C)[] str, size_t n) @safe pure } /// -unittest +@safe unittest { assert(toUTFindex(`hello world`, 7) == 7); assert(toUTFindex(`hello world`w, 7) == 7); @@ -937,6 +1018,9 @@ unittest /* =================== Decode ======================= */ +/// Whether or not to replace invalid UTF with $(LREF replacementDchar) +alias UseReplacementDchar = Flag!"useReplacementDchar"; + /++ Decodes and returns the code point starting at $(D str[index]). $(D index) is advanced to one past the decoded code point. If the code point is not @@ -947,13 +1031,21 @@ unittest with length and slicing, whereas $(LREF decodeFront) will work with any input range of code units. + Params: + useReplacementDchar = if invalid UTF, return replacementDchar rather than throwing + str = input string or indexable Range + index = starting index into s[]; incremented by number of code units processed + + Returns: + decoded character + Throws: $(LREF UTFException) if $(D str[index]) is not the start of a valid UTF - sequence. + sequence and useReplacementDchar is $(D No.useReplacementDchar) +/ -dchar decode(S)(auto ref S str, ref size_t index) - if (!isSomeString!S && - isRandomAccessRange!S && hasSlicing!S && hasLength!S && isSomeChar!(ElementType!S)) +dchar decode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(auto ref S str, ref size_t index) +if (!isSomeString!S && + isRandomAccessRange!S && hasSlicing!S && hasLength!S && isSomeChar!(ElementType!S)) in { assert(index < str.length, "Attempted to decode past the end of a string"); @@ -967,11 +1059,12 @@ body if (str[index] < codeUnitLimit!S) return str[index++]; else - return decodeImpl!true(str, index); + return decodeImpl!(true, useReplacementDchar)(str, index); } -dchar decode(S)(auto ref S str, ref size_t index) @trusted pure - if (isSomeString!S) +dchar decode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( +auto ref S str, ref size_t index) @trusted pure +if (isSomeString!S) in { assert(index < str.length, "Attempted to decode past the end of a string"); @@ -985,7 +1078,7 @@ body if (str[index] < codeUnitLimit!S) return str[index++]; else - return decodeImpl!true(str, index); + return decodeImpl!(true, useReplacementDchar)(str, index); } /++ @@ -996,6 +1089,14 @@ body decodes them. If $(D numCodeUnits) is passed in, it gets set to the number of code units which were in the code point which was decoded. + Params: + useReplacementDchar = if invalid UTF, return replacementDchar rather than throwing + str = input string or indexable Range + numCodeUnits = set to number of code units processed + + Returns: + decoded character + Throws: $(LREF UTFException) if $(D str.front) is not the start of a valid UTF sequence. If an exception is thrown, then there is no guarantee as to @@ -1003,8 +1104,9 @@ body type of range being used and how many code units had to be popped off before the code point was determined to be invalid. +/ -dchar decodeFront(S)(ref S str, out size_t numCodeUnits) - if (!isSomeString!S && isInputRange!S && isSomeChar!(ElementType!S)) +dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( +ref S str, out size_t numCodeUnits) +if (!isSomeString!S && isInputRange!S && isSomeChar!(ElementType!S)) in { assert(!str.empty); @@ -1025,11 +1127,11 @@ body } else { - //@@@BUG@@@ 8521 forces canIndex to be done outside of decodeImpl, which + //@@@BUG@@@ 14447 forces canIndex to be done outside of decodeImpl, which //is undesirable, since not all overloads of decodeImpl need it. So, it //should be moved back into decodeImpl once bug# 8521 has been fixed. enum canIndex = isRandomAccessRange!S && hasSlicing!S && hasLength!S; - immutable retval = decodeImpl!canIndex(str, numCodeUnits); + immutable retval = decodeImpl!(canIndex, useReplacementDchar)(str, numCodeUnits); // The other range types were already popped by decodeImpl. static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S) @@ -1039,8 +1141,9 @@ body } } -dchar decodeFront(S)(ref S str, out size_t numCodeUnits) @trusted pure - if (isSomeString!S) +dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( +ref S str, out size_t numCodeUnits) @trusted pure +if (isSomeString!S) in { assert(!str.empty); @@ -1060,63 +1163,198 @@ body } else { - immutable retval = decodeImpl!true(str, numCodeUnits); + immutable retval = decodeImpl!(true, useReplacementDchar)(str, numCodeUnits); str = str[numCodeUnits .. $]; return retval; } } /++ Ditto +/ -dchar decodeFront(S)(ref S str) - if (isInputRange!S && isSomeChar!(ElementType!S)) +dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(ref S str) +if (isInputRange!S && isSomeChar!(ElementType!S)) { size_t numCodeUnits; - return decodeFront(str, numCodeUnits); + return decodeFront!useReplacementDchar(str, numCodeUnits); } -// Gives the maximum value that a code unit for the given range type can hold. -private template codeUnitLimit(S) - if (isSomeChar!(ElementEncodingType!S)) +/++ + $(D decodeBack) is a variant of $(LREF decode) which specifically decodes + the last code point. Unlike $(LREF decode), $(D decodeBack) accepts any + bidirectional range of code units (rather than just a string or random access + range). It also takes the range by $(D ref) and pops off the elements as it + decodes them. If $(D numCodeUnits) is passed in, it gets set to the number + of code units which were in the code point which was decoded. + + Params: + useReplacementDchar = if invalid UTF, return `replacementDchar` rather than throwing + str = input string or bidirectional Range + numCodeUnits = gives the number of code units processed + + Returns: + A decoded UTF character. + + Throws: + $(LREF UTFException) if $(D str.back) is not the end of a valid UTF + sequence. If an exception is thrown, the $(D str) itself remains unchanged, + but there is no guarantee as to the value of $(D numCodeUnits) (when passed). + +/ +dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( + ref S str, out size_t numCodeUnits) +if (isSomeString!S) +in { - static if (is(Unqual!(ElementEncodingType!S) == char)) - enum char codeUnitLimit = 0x80; - else static if (is(Unqual!(ElementEncodingType!S) == wchar)) - enum wchar codeUnitLimit = 0xD800; + assert(!str.empty); +} +out (result) +{ + assert(isValidDchar(result)); +} +body +{ + if (str[$ - 1] < codeUnitLimit!S) + { + numCodeUnits = 1; + immutable retval = str[$ - 1]; + str = str[0 .. $ - 1]; + return retval; + } else - enum dchar codeUnitLimit = 0xD800; + { + numCodeUnits = strideBack(str); + immutable newLength = str.length - numCodeUnits; + size_t index = newLength; + immutable retval = decodeImpl!(true, useReplacementDchar)(str, index); + str = str[0 .. newLength]; + return retval; + } } -/* - * For strings, this function does its own bounds checking to give a - * more useful error message when attempting to decode past the end of a string. - * Subsequently it uses a pointer instead of an array to avoid - * redundant bounds checking. - */ -private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) - if (is(S : const char[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == char))) +/++ Ditto +/ +dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( + ref S str, out size_t numCodeUnits) +if (!isSomeString!S && isSomeChar!(ElementType!S) && isBidirectionalRange!S + && ((isRandomAccessRange!S && hasLength!S) || !isRandomAccessRange!S)) +in { - /* The following encodings are valid, except for the 5 and 6 byte - * combinations: - * 0xxxxxxx - * 110xxxxx 10xxxxxx - * 1110xxxx 10xxxxxx 10xxxxxx - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + assert(!str.empty); +} +out (result) +{ + assert(isValidDchar(result)); +} +body +{ + if (str.back < codeUnitLimit!S) + { + numCodeUnits = 1; + immutable retval = str.back; + str.popBack(); + return retval; + } + else + { + numCodeUnits = strideBack(str); + static if (isRandomAccessRange!S) + { + size_t index = str.length - numCodeUnits; + immutable retval = decodeImpl!(true, useReplacementDchar)(str, index); + str.popBackExactly(numCodeUnits); + return retval; + } + else + { + alias Char = Unqual!(ElementType!S); + Char[4] codeUnits; + S tmp = str.save; + for (size_t i = numCodeUnits; i > 0; ) + { + codeUnits[--i] = tmp.back; + tmp.popBack(); + } + const Char[] codePoint = codeUnits[0 .. numCodeUnits]; + size_t index = 0; + immutable retval = decodeImpl!(true, useReplacementDchar)(codePoint, index); + str = tmp; + return retval; + } + } +} + +/++ Ditto +/ +dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(ref S str) +if (isSomeString!S + || (isRandomAccessRange!S && hasLength!S && isSomeChar!(ElementType!S)) + || (!isRandomAccessRange!S && isBidirectionalRange!S && isSomeChar!(ElementType!S))) +in +{ + assert(!str.empty); +} +out (result) +{ + assert(isValidDchar(result)); +} +body +{ + size_t numCodeUnits; + return decodeBack!useReplacementDchar(str, numCodeUnits); +} + +// Gives the maximum value that a code unit for the given range type can hold. +package template codeUnitLimit(S) +if (isSomeChar!(ElementEncodingType!S)) +{ + static if (is(Unqual!(ElementEncodingType!S) == char)) + enum char codeUnitLimit = 0x80; + else static if (is(Unqual!(ElementEncodingType!S) == wchar)) + enum wchar codeUnitLimit = 0xD800; + else + enum dchar codeUnitLimit = 0xD800; +} + +/* + * For strings, this function does its own bounds checking to give a + * more useful error message when attempting to decode past the end of a string. + * Subsequently it uses a pointer instead of an array to avoid + * redundant bounds checking. + * + * The three overloads of this operate on chars, wchars, and dchars. + * + * Params: + * canIndex = if S is indexable + * useReplacementDchar = if invalid UTF, return replacementDchar rather than throwing + * str = input string or Range + * index = starting index into s[]; incremented by number of code units processed + * + * Returns: + * decoded character + */ +private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( + auto ref S str, ref size_t index) +if ( + is(S : const char[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == char))) +{ + /* The following encodings are valid, except for the 5 and 6 byte + * combinations: + * 0xxxxxxx + * 110xxxxx 10xxxxxx + * 1110xxxx 10xxxxxx 10xxxxxx + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ /* Dchar bitmask for different numbers of UTF-8 code units. */ - alias bitMask = TypeTuple!((1 << 7) - 1, (1 << 11) - 1, (1 << 16) - 1, (1 << 21) - 1); + alias bitMask = AliasSeq!((1 << 7) - 1, (1 << 11) - 1, (1 << 16) - 1, (1 << 21) - 1); static if (is(S : const char[])) - auto pstr = str.ptr + index; + auto pstr = str.ptr + index; // this is what makes decodeImpl() @system code else static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S) auto pstr = str[index .. str.length]; else alias pstr = str; - //@@@BUG@@@ 8521 forces this to be done outside of decodeImpl + //@@@BUG@@@ 14447 forces this to be done outside of decodeImpl //enum canIndex = is(S : const char[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S); static if (canIndex) @@ -1130,64 +1368,92 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) pstr.popFront(); } - static if (canIndex) + static if (!useReplacementDchar) { - static UTFException exception(S)(S str, string msg) + static if (canIndex) { - uint[4] sequence = void; - size_t i; - - do + static UTFException exception(S)(S str, string msg) { - sequence[i] = str[i]; - } while (++i < str.length && i < 4 && (str[i] & 0xC0) == 0x80); + uint[4] sequence = void; + size_t i; - return new UTFException(msg, i).setSequence(sequence[0 .. i]); + do + { + sequence[i] = str[i]; + } while (++i < str.length && i < 4 && (str[i] & 0xC0) == 0x80); + + return new UTFException(msg, i).setSequence(sequence[0 .. i]); + } } - } - UTFException invalidUTF() - { - static if (canIndex) - return exception(pstr[0 .. length], "Invalid UTF-8 sequence"); - else + UTFException invalidUTF() + { + static if (canIndex) + return exception(pstr[0 .. length], "Invalid UTF-8 sequence"); + else + { + //We can't include the invalid sequence with input strings without + //saving each of the code units along the way, and we can't do it with + //forward ranges without saving the entire range. Both would incur a + //cost for the decoding of every character just to provide a better + //error message for the (hopefully) rare case when an invalid UTF-8 + //sequence is encountered, so we don't bother trying to include the + //invalid sequence here, unlike with strings and sliceable ranges. + return new UTFException("Invalid UTF-8 sequence"); + } + } + + UTFException outOfBounds() { - //We can't include the invalid sequence with input strings without - //saving each of the code units along the way, and we can't do it with - //forward ranges without saving the entire range. Both would incur a - //cost for the decoding of every character just to provide a better - //error message for the (hopefully) rare case when an invalid UTF-8 - //sequence is encountered, so we don't bother trying to include the - //invalid sequence here, unlike with strings and sliceable ranges. - return new UTFException("Invalid UTF-8 sequence"); + static if (canIndex) + return exception(pstr[0 .. length], "Attempted to decode past the end of a string"); + else + return new UTFException("Attempted to decode past the end of a string"); } } - UTFException outOfBounds() + if ((fst & 0b1100_0000) != 0b1100_0000) { - static if (canIndex) - return exception(pstr[0 .. length], "Attempted to decode past the end of a string"); + static if (useReplacementDchar) + { + ++index; // always consume bad input to avoid infinite loops + return replacementDchar; + } else - return new UTFException("Attempted to decode past the end of a string"); + throw invalidUTF(); // starter must have at least 2 first bits set } - if((fst & 0b1100_0000) != 0b1100_0000) - throw invalidUTF(); // starter must have at least 2 first bits set ubyte tmp = void; dchar d = fst; // upper control bits are masked out later fst <<= 1; - foreach (i; TypeTuple!(1, 2, 3)) + foreach (i; AliasSeq!(1, 2, 3)) { static if (canIndex) { if (i == length) - throw outOfBounds(); + { + static if (useReplacementDchar) + { + index += i; + return replacementDchar; + } + else + throw outOfBounds(); + } } else { if (pstr.empty) - throw outOfBounds(); + { + static if (useReplacementDchar) + { + index += i; + return replacementDchar; + } + else + throw outOfBounds(); + } } static if (canIndex) @@ -1199,7 +1465,15 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) } if ((tmp & 0xC0) != 0x80) - throw invalidUTF(); + { + static if (useReplacementDchar) + { + index += i + 1; + return replacementDchar; + } + else + throw invalidUTF(); + } d = (d << 6) | (tmp & 0x3F); fst <<= 1; @@ -1210,28 +1484,84 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) // overlong, could have been encoded with i bytes if ((d & ~bitMask[i - 1]) == 0) - throw invalidUTF(); + { + static if (useReplacementDchar) + { + index += i + 1; + return replacementDchar; + } + else + throw invalidUTF(); + } // check for surrogates only needed for 3 bytes static if (i == 2) { if (!isValidDchar(d)) - throw invalidUTF(); + { + static if (useReplacementDchar) + { + index += i + 1; + return replacementDchar; + } + else + throw invalidUTF(); + } } index += i + 1; static if (i == 3) + { if (d > dchar.max) - throw invalidUTF(); + { + static if (useReplacementDchar) + d = replacementDchar; + else + throw invalidUTF(); + } + } return d; } } - throw invalidUTF(); + static if (useReplacementDchar) + { + index += 4; // read 4 chars by now + return replacementDchar; + } + else + throw invalidUTF(); +} + +@safe pure @nogc nothrow +unittest +{ + // Add tests for useReplacemendDchar == yes path + + static struct R + { + @safe pure @nogc nothrow: + this(string s) { this.s = s; } + @property bool empty() { return idx == s.length; } + @property char front() { return s[idx]; } + void popFront() { ++idx; } + size_t idx; + string s; + } + + foreach (s; invalidUTFstrings!char()) + { + auto r = R(s); + size_t index; + dchar dc = decodeImpl!(false, Yes.useReplacementDchar)(r, index); + assert(dc == replacementDchar); + assert(1 <= index && index <= s.length); + } } -private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) - if (is(S : const wchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == wchar))) +private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S) +(auto ref S str, ref size_t index) +if (is(S : const wchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == wchar))) { static if (is(S : const wchar[])) auto pstr = str.ptr + index; @@ -1240,7 +1570,7 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) else alias pstr = str; - //@@@BUG@@@ 8521 forces this to be done outside of decodeImpl + //@@@BUG@@@ 14447 forces this to be done outside of decodeImpl //enum canIndex = is(S : const wchar[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S); static if (canIndex) @@ -1254,15 +1584,18 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) pstr.popFront(); } - UTFException exception(string msg) + static if (!useReplacementDchar) { - static if (canIndex) - return new UTFException(msg).setSequence(pstr[0]); - else - return new UTFException(msg); + UTFException exception(string msg) + { + static if (canIndex) + return new UTFException(msg).setSequence(pstr[0]); + else + return new UTFException(msg); + } } - string msg; + // The < case must be taken care of before decodeImpl is called. assert(u >= 0xD800); if (u <= 0xDBFF) @@ -1273,7 +1606,15 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) immutable onlyOneCodeUnit = pstr.empty; if (onlyOneCodeUnit) - throw exception("surrogate UTF-16 high value past end of string"); + { + static if (useReplacementDchar) + { + ++index; + return replacementDchar; + } + else + throw exception("surrogate UTF-16 high value past end of string"); + } static if (canIndex) immutable uint u2 = pstr[1]; @@ -1284,55 +1625,129 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) } if (u2 < 0xDC00 || u2 > 0xDFFF) - throw exception("surrogate UTF-16 low value out of range"); - - u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00); - index += 2; + { + static if (useReplacementDchar) + u = replacementDchar; + else + throw exception("surrogate UTF-16 low value out of range"); + } + else + u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00); + ++index; } else if (u >= 0xDC00 && u <= 0xDFFF) - throw exception("unpaired surrogate UTF-16 value"); - else - ++index; + { + static if (useReplacementDchar) + u = replacementDchar; + else + throw exception("unpaired surrogate UTF-16 value"); + } + ++index; // Note: u+FFFE and u+FFFF are specifically permitted by the // Unicode standard for application internal use (see isValidDchar) - return cast(dchar)u; + return cast(dchar) u; +} + +@safe pure @nogc nothrow +unittest +{ + // Add tests for useReplacemendDchar == true path + + static struct R + { + @safe pure @nogc nothrow: + this(wstring s) { this.s = s; } + @property bool empty() { return idx == s.length; } + @property wchar front() { return s[idx]; } + void popFront() { ++idx; } + size_t idx; + wstring s; + } + + foreach (s; invalidUTFstrings!wchar()) + { + auto r = R(s); + size_t index; + dchar dc = decodeImpl!(false, Yes.useReplacementDchar)(r, index); + assert(dc == replacementDchar); + assert(1 <= index && index <= s.length); + } } -private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) - if (is(S : const dchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == dchar))) +private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)( + auto ref S str, ref size_t index) +if (is(S : const dchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == dchar))) { static if (is(S : const dchar[])) auto pstr = str.ptr; else alias pstr = str; - static if (is(S : const dchar[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S)) + static if (is(S : const dchar[]) || isRandomAccessRange!S) { - if (!isValidDchar(pstr[index])) - throw new UTFException("Invalid UTF-32 value").setSequence(pstr[index]); - return pstr[index++]; + dchar dc = pstr[index]; + if (!isValidDchar(dc)) + { + static if (useReplacementDchar) + dc = replacementDchar; + else + throw new UTFException("Invalid UTF-32 value").setSequence(dc); + } + ++index; + return dc; } else { - if (!isValidDchar(pstr.front)) - throw new UTFException("Invalid UTF-32 value").setSequence(pstr.front); + dchar dc = pstr.front; + if (!isValidDchar(dc)) + { + static if (useReplacementDchar) + dc = replacementDchar; + else + throw new UTFException("Invalid UTF-32 value").setSequence(dc); + } ++index; - immutable retval = pstr.front; pstr.popFront(); - return retval; + return dc; } } +@safe pure @nogc nothrow +unittest +{ + // Add tests for useReplacemendDchar == true path + + static struct R + { + @safe pure @nogc nothrow: + this(dstring s) { this.s = s; } + @property bool empty() { return idx == s.length; } + @property dchar front() { return s[idx]; } + void popFront() { ++idx; } + size_t idx; + dstring s; + } + + foreach (s; invalidUTFstrings!dchar()) + { + auto r = R(s); + size_t index; + dchar dc = decodeImpl!(false, Yes.useReplacementDchar)(r, index); + assert(dc == replacementDchar); + assert(1 <= index && index <= s.length); + } +} + + version(unittest) private void testDecode(R)(R range, size_t index, dchar expectedChar, size_t expectedIndex, size_t line = __LINE__) { - import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; static if (hasLength!R) @@ -1360,8 +1775,7 @@ version(unittest) private void testDecodeFront(R)(ref R range, size_t expectedNumCodeUnits, size_t line = __LINE__) { - import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; static if (hasLength!R) @@ -1381,19 +1795,54 @@ version(unittest) private void testDecodeFront(R)(ref R range, } } -version(unittest) private void testBothDecode(R)(R range, +version(unittest) private void testDecodeBack(R)(ref R range, dchar expectedChar, - size_t expectedIndex, + size_t expectedNumCodeUnits, size_t line = __LINE__) +{ + // This condition is to allow unit testing all `decode` functions together + static if (!isBidirectionalRange!R) + return; + else + { + import std.string : format; + import core.exception : AssertError; + + static if (hasLength!R) + immutable lenBefore = range.length; + + size_t numCodeUnits; + immutable result = decodeBack(range, numCodeUnits); + enforce(result == expectedChar, + new AssertError(format("decodeBack: Wrong character: %s", result), __FILE__, line)); + enforce(numCodeUnits == expectedNumCodeUnits, + new AssertError(format("decodeBack: Wrong numCodeUnits: %s", numCodeUnits), __FILE__, line)); + + static if (hasLength!R) + { + enforce(range.length == lenBefore - numCodeUnits, + new AssertError(format("decodeBack: wrong length: %s", range.length), __FILE__, line)); + } + } +} + +version(unittest) private void testAllDecode(R)(R range, + dchar expectedChar, + size_t expectedIndex, + size_t line = __LINE__) { testDecode(range, 0, expectedChar, expectedIndex, line); + static if (isBidirectionalRange!R) + { + auto rangeCopy = range.save; + testDecodeBack(rangeCopy, expectedChar, expectedIndex, line); + } testDecodeFront(range, expectedChar, expectedIndex, line); } version(unittest) private void testBadDecode(R)(R range, size_t index, size_t line = __LINE__) { - import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; immutable initialIndex = index; @@ -1417,7 +1866,32 @@ version(unittest) private void testBadDecode(R)(R range, size_t index, size_t li assertThrown!UTFException(decodeFront(range, index), null, __FILE__, line); } -unittest +version(unittest) private void testBadDecodeBack(R)(R range, size_t line = __LINE__) +{ + // This condition is to allow unit testing all `decode` functions together + static if (!isBidirectionalRange!R) + return; + else + { + import std.string : format; + import core.exception : AssertError; + + static if (hasLength!R) + immutable lenBefore = range.length; + + static if (isRandomAccessRange!R) + { + assertThrown!UTFException(decodeBack(range), null, __FILE__, line); + static if (hasLength!R) + { + enforce(range.length == lenBefore, + new AssertError(format("decodeBack: length changed:", range.length), __FILE__, line)); + } + } + } +} + +@system unittest { import std.conv : to; import std.exception; @@ -1425,9 +1899,9 @@ unittest assertCTFEable!( { - foreach (S; TypeTuple!(to!string, InputCU!char, RandomCU!char, - (string s) => new RefBidirCU!char(s), - (string s) => new RefRandomCU!char(s))) + foreach (S; AliasSeq!(to!string, InputCU!char, RandomCU!char, + (string s) => new RefBidirCU!char(s), + (string s) => new RefRandomCU!char(s))) { enum sHasLength = hasLength!(typeof(S("abcd"))); @@ -1451,8 +1925,24 @@ unittest assert(decodeFront(range) == 'サ'); } - testBothDecode(S("\xC2\xA9"), '\u00A9', 2); - testBothDecode(S("\xE2\x89\xA0"), '\u2260', 3); + { + auto range = S("abcd"); + testDecodeBack(range, 'd', 1); + testDecodeBack(range, 'c', 1); + testDecodeBack(range, 'b', 1); + testDecodeBack(range, 'a', 1); + } + + { + auto range = S("ウェブサイト"); + testDecodeBack(range, 'ト', 3); + testDecodeBack(range, 'イ', 3); + testDecodeBack(range, 'サ', 3); + testDecodeBack(range, 'ブ', 3); + } + + testAllDecode(S("\xC2\xA9"), '\u00A9', 2); + testAllDecode(S("\xE2\x89\xA0"), '\u2260', 3); foreach (str; ["\xE2\x89", // too short "\xC0\x8A", @@ -1463,42 +1953,50 @@ unittest { testBadDecode(S(str), 0); testBadDecode(S(str), 1); + testBadDecodeBack(S(str)); } //Invalid UTF-8 sequence where the first code unit is valid. - testBothDecode(S("\xEF\xBF\xBE"), cast(dchar)0xFFFE, 3); - testBothDecode(S("\xEF\xBF\xBF"), cast(dchar)0xFFFF, 3); + testAllDecode(S("\xEF\xBF\xBE"), cast(dchar) 0xFFFE, 3); + testAllDecode(S("\xEF\xBF\xBF"), cast(dchar) 0xFFFF, 3); //Invalid UTF-8 sequence where the first code unit isn't valid. - testBadDecode(S("\xED\xA0\x80"), 0); - testBadDecode(S("\xED\xAD\xBF"), 0); - testBadDecode(S("\xED\xAE\x80"), 0); - testBadDecode(S("\xED\xAF\xBF"), 0); - testBadDecode(S("\xED\xB0\x80"), 0); - testBadDecode(S("\xED\xBE\x80"), 0); - testBadDecode(S("\xED\xBF\xBF"), 0); + foreach (str; ["\xED\xA0\x80", + "\xED\xAD\xBF", + "\xED\xAE\x80", + "\xED\xAF\xBF", + "\xED\xB0\x80", + "\xED\xBE\x80", + "\xED\xBF\xBF"]) + { + testBadDecode(S(str), 0); + testBadDecodeBack(S(str)); + } } }); } -unittest +@system unittest { import std.conv : to; import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(to!wstring, InputCU!wchar, RandomCU!wchar, - (wstring s) => new RefBidirCU!wchar(s), - (wstring s) => new RefRandomCU!wchar(s))) + foreach (S; AliasSeq!(to!wstring, InputCU!wchar, RandomCU!wchar, + (wstring s) => new RefBidirCU!wchar(s), + (wstring s) => new RefRandomCU!wchar(s))) { - testBothDecode(S([cast(wchar)0x1111]), cast(dchar)0x1111, 1); - testBothDecode(S([cast(wchar)0xD800, cast(wchar)0xDC00]), cast(dchar)0x10000, 2); - testBothDecode(S([cast(wchar)0xDBFF, cast(wchar)0xDFFF]), cast(dchar)0x10FFFF, 2); - testBothDecode(S([cast(wchar)0xFFFE]), cast(dchar)0xFFFE, 1); - testBothDecode(S([cast(wchar)0xFFFF]), cast(dchar)0xFFFF, 1); + testAllDecode(S([cast(wchar) 0x1111]), cast(dchar) 0x1111, 1); + testAllDecode(S([cast(wchar) 0xD800, cast(wchar) 0xDC00]), cast(dchar) 0x10000, 2); + testAllDecode(S([cast(wchar) 0xDBFF, cast(wchar) 0xDFFF]), cast(dchar) 0x10FFFF, 2); + testAllDecode(S([cast(wchar) 0xFFFE]), cast(dchar) 0xFFFE, 1); + testAllDecode(S([cast(wchar) 0xFFFF]), cast(dchar) 0xFFFF, 1); - testBadDecode(S([ cast(wchar)0xD801 ]), 0); - testBadDecode(S([ cast(wchar)0xD800, cast(wchar)0x1200 ]), 0); + testBadDecode(S([ cast(wchar) 0xD801 ]), 0); + testBadDecode(S([ cast(wchar) 0xD800, cast(wchar) 0x1200 ]), 0); + + testBadDecodeBack(S([ cast(wchar) 0xD801 ])); + testBadDecodeBack(S([ cast(wchar) 0x0010, cast(wchar) 0xD800 ])); { auto range = S("ウェブサイト"); @@ -1509,39 +2007,54 @@ unittest assert(decodeFront(range) == 'ブ'); assert(decodeFront(range) == 'サ'); } + + { + auto range = S("ウェブサイト"); + testDecodeBack(range, 'ト', 1); + testDecodeBack(range, 'イ', 1); + testDecodeBack(range, 'サ', 1); + testDecodeBack(range, 'ブ', 1); + } } - foreach (S; TypeTuple!(to!wstring, RandomCU!wchar, (wstring s) => new RefRandomCU!wchar(s))) - { - auto str = S([cast(wchar)0xD800, cast(wchar)0xDC00, - cast(wchar)0x1400, - cast(wchar)0xDAA7, cast(wchar)0xDDDE]); - testDecode(str, 0, cast(dchar)0x10000, 2); - testDecode(str, 2, cast(dchar)0x1400, 3); - testDecode(str, 3, cast(dchar)0xB9DDE, 5); + foreach (S; AliasSeq!(to!wstring, RandomCU!wchar, (wstring s) => new RefRandomCU!wchar(s))) + { + auto str = S([cast(wchar) 0xD800, cast(wchar) 0xDC00, + cast(wchar) 0x1400, + cast(wchar) 0xDAA7, cast(wchar) 0xDDDE]); + testDecode(str, 0, cast(dchar) 0x10000, 2); + testDecode(str, 2, cast(dchar) 0x1400, 3); + testDecode(str, 3, cast(dchar) 0xB9DDE, 5); + testDecodeBack(str, cast(dchar) 0xB9DDE, 2); + testDecodeBack(str, cast(dchar) 0x1400, 1); + testDecodeBack(str, cast(dchar) 0x10000, 2); } }); } -unittest +@system unittest { import std.conv : to; import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!(to!dstring, RandomCU!dchar, InputCU!dchar, - (dstring s) => new RefBidirCU!dchar(s), - (dstring s) => new RefRandomCU!dchar(s))) + foreach (S; AliasSeq!(to!dstring, RandomCU!dchar, InputCU!dchar, + (dstring s) => new RefBidirCU!dchar(s), + (dstring s) => new RefRandomCU!dchar(s))) { - testBothDecode(S([cast(dchar)0x1111]), cast(dchar)0x1111, 1); - testBothDecode(S([cast(dchar)0x10000]), cast(dchar)0x10000, 1); - testBothDecode(S([cast(dchar)0x10FFFF]), cast(dchar)0x10FFFF, 1); - testBothDecode(S([cast(dchar)0xFFFE]), cast(dchar)0xFFFE, 1); - testBothDecode(S([cast(dchar)0xFFFF]), cast(dchar)0xFFFF, 1); + testAllDecode(S([cast(dchar) 0x1111]), cast(dchar) 0x1111, 1); + testAllDecode(S([cast(dchar) 0x10000]), cast(dchar) 0x10000, 1); + testAllDecode(S([cast(dchar) 0x10FFFF]), cast(dchar) 0x10FFFF, 1); + testAllDecode(S([cast(dchar) 0xFFFE]), cast(dchar) 0xFFFE, 1); + testAllDecode(S([cast(dchar) 0xFFFF]), cast(dchar) 0xFFFF, 1); - testBadDecode(S([cast(dchar)0xD800]), 0); - testBadDecode(S([cast(dchar)0xDFFE]), 0); - testBadDecode(S([cast(dchar)0x110000]), 0); + testBadDecode(S([cast(dchar) 0xD800]), 0); + testBadDecode(S([cast(dchar) 0xDFFE]), 0); + testBadDecode(S([cast(dchar) 0x110000]), 0); + + testBadDecodeBack(S([cast(dchar) 0xD800])); + testBadDecodeBack(S([cast(dchar) 0xDFFE])); + testBadDecodeBack(S([cast(dchar) 0x110000])); { auto range = S("ウェブサイト"); @@ -1552,38 +2065,55 @@ unittest assert(decodeFront(range) == 'ブ'); assert(decodeFront(range) == 'サ'); } + + { + auto range = S("ウェブサイト"); + testDecodeBack(range, 'ト', 1); + testDecodeBack(range, 'イ', 1); + testDecodeBack(range, 'サ', 1); + testDecodeBack(range, 'ブ', 1); + } } - foreach (S; TypeTuple!(to!dstring, RandomCU!dchar, (dstring s) => new RefRandomCU!dchar(s))) + foreach (S; AliasSeq!(to!dstring, RandomCU!dchar, (dstring s) => new RefRandomCU!dchar(s))) { - auto str = S([cast(dchar)0x10000, cast(dchar)0x1400, cast(dchar)0xB9DDE]); + auto str = S([cast(dchar) 0x10000, cast(dchar) 0x1400, cast(dchar) 0xB9DDE]); testDecode(str, 0, 0x10000, 1); testDecode(str, 1, 0x1400, 2); testDecode(str, 2, 0xB9DDE, 3); + testDecodeBack(str, cast(dchar) 0xB9DDE, 1); + testDecodeBack(str, cast(dchar) 0x1400, 1); + testDecodeBack(str, cast(dchar) 0x10000, 1); } }); } -unittest +@safe unittest { import std.exception; assertCTFEable!( { - foreach (S; TypeTuple!( char[], const( char)[], string, - wchar[], const(wchar)[], wstring, - dchar[], const(dchar)[], dstring)) + foreach (S; AliasSeq!( char[], const( char)[], string, + wchar[], const(wchar)[], wstring, + dchar[], const(dchar)[], dstring)) { static assert(isSafe!({ S str; size_t i = 0; decode(str, i); })); static assert(isSafe!({ S str; size_t i = 0; decodeFront(str, i); })); static assert(isSafe!({ S str; decodeFront(str); })); - static assert((functionAttributes!({ S str; size_t i = 0; decode(str, i); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ S str; size_t i = 0; decodeFront(str, i); }) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!({ S str; size_t i = 0; decode(str, i); }) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!({ + S str; size_t i = 0; decodeFront(str, i); + }) & FunctionAttribute.pure_) != 0); static assert((functionAttributes!({ S str; decodeFront(str); }) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!({ + S str; size_t i = 0; decodeBack(str, i); + }) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!({ S str; decodeBack(str); }) & FunctionAttribute.pure_) != 0); } }); } -unittest +@safe unittest { import std.exception; char[4] val; @@ -1596,21 +2126,30 @@ unittest } /* =================== Encode ======================= */ -/++ - Encodes $(D c) into the static array, $(D buf), and returns the actual - length of the encoded character (a number between $(D 1) and $(D 4) for +private dchar _utfException(UseReplacementDchar useReplacementDchar)(string msg, dchar c) +{ + static if (useReplacementDchar) + return replacementDchar; + else + throw new UTFException(msg).setSequence(c); +} + +/++ + Encodes $(D c) into the static array, $(D buf), and returns the actual + length of the encoded character (a number between $(D 1) and $(D 4) for $(D char[4]) buffers and a number between $(D 1) and $(D 2) for $(D wchar[2]) buffers). Throws: $(D UTFException) if $(D c) is not a valid UTF code point. +/ -size_t encode(ref char[4] buf, dchar c) @safe pure +size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( + ref char[4] buf, dchar c) @safe pure { if (c <= 0x7F) { assert(isValidDchar(c)); - buf[0] = cast(char)c; + buf[0] = cast(char) c; return 1; } if (c <= 0x7FF) @@ -1623,9 +2162,10 @@ size_t encode(ref char[4] buf, dchar c) @safe pure if (c <= 0xFFFF) { if (0xD800 <= c && c <= 0xDFFF) - throw new UTFException("Encoding a surrogate code point in UTF-8").setSequence(c); + c = _utfException!useReplacementDchar("Encoding a surrogate code point in UTF-8", c); assert(isValidDchar(c)); + L3: buf[0] = cast(char)(0xE0 | (c >> 12)); buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf[2] = cast(char)(0x80 | (c & 0x3F)); @@ -1642,10 +2182,11 @@ size_t encode(ref char[4] buf, dchar c) @safe pure } assert(!isValidDchar(c)); - throw new UTFException("Encoding an invalid code point in UTF-8").setSequence(c); + c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-8", c); + goto L3; } -unittest +@safe unittest { import std.exception; assertCTFEable!( @@ -1664,25 +2205,30 @@ unittest assert(encode(buf, '\U00010000') == 4 && buf[0 .. 4] == "\U00010000"); assert(encode(buf, '\U0010FFFF') == 4 && buf[0 .. 4] == "\U0010FFFF"); - assertThrown!UTFException(encode(buf, cast(dchar)0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0x110000)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); + + assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride); + assert(buf.front == replacementDchar); }); } /// Ditto -size_t encode(ref wchar[2] buf, dchar c) @safe pure +size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( + ref wchar[2] buf, dchar c) @safe pure { if (c <= 0xFFFF) { if (0xD800 <= c && c <= 0xDFFF) - throw new UTFException("Encoding an isolated surrogate code point in UTF-16").setSequence(c); + c = _utfException!useReplacementDchar("Encoding an isolated surrogate code point in UTF-16", c); assert(isValidDchar(c)); - buf[0] = cast(wchar)c; + L1: + buf[0] = cast(wchar) c; return 1; } if (c <= 0x10FFFF) @@ -1693,11 +2239,11 @@ size_t encode(ref wchar[2] buf, dchar c) @safe pure return 2; } - assert(!isValidDchar(c)); - throw new UTFException("Encoding an invalid code point in UTF-16").setSequence(c); + c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-16", c); + goto L1; } -unittest +@safe unittest { import std.exception; assertCTFEable!( @@ -1712,11 +2258,52 @@ unittest assert(encode(buf, '\U00010000') == 2 && buf[0 .. 2] == "\U00010000"); assert(encode(buf, '\U0010FFFF') == 2 && buf[0 .. 2] == "\U0010FFFF"); - assertThrown!UTFException(encode(buf, cast(dchar)0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0x110000)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); + + assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride); + assert(buf.front == replacementDchar); + }); +} + + +/// Ditto +size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( + ref dchar[1] buf, dchar c) @safe pure +{ + if ((0xD800 <= c && c <= 0xDFFF) || 0x10FFFF < c) + c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-32", c); + else + assert(isValidDchar(c)); + buf[0] = c; + return 1; +} + +@safe unittest +{ + import std.exception; + assertCTFEable!( + { + dchar[1] buf; + + encode(buf, '\u0000'); assert(buf[0] == '\u0000'); + encode(buf, '\uD7FF'); assert(buf[0] == '\uD7FF'); + encode(buf, '\uE000'); assert(buf[0] == '\uE000'); + encode(buf, 0xFFFE ); assert(buf[0] == 0xFFFE); + encode(buf, 0xFFFF ); assert(buf[0] == 0xFFFF); + encode(buf, '\U0010FFFF'); assert(buf[0] == '\U0010FFFF'); + + assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); + + assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride); + assert(buf.front == replacementDchar); }); } @@ -1727,14 +2314,15 @@ unittest Throws: $(D UTFException) if $(D c) is not a valid UTF code point. +/ -void encode(ref char[] str, dchar c) @safe pure +void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( + ref char[] str, dchar c) @safe pure { char[] r = str; if (c <= 0x7F) { assert(isValidDchar(c)); - r ~= cast(char)c; + r ~= cast(char) c; } else { @@ -1751,9 +2339,10 @@ void encode(ref char[] str, dchar c) @safe pure else if (c <= 0xFFFF) { if (0xD800 <= c && c <= 0xDFFF) - throw new UTFException("Encoding a surrogate code point in UTF-8").setSequence(c); + c = _utfException!useReplacementDchar("Encoding a surrogate code point in UTF-8", c); assert(isValidDchar(c)); + L3: buf[0] = cast(char)(0xE0 | (c >> 12)); buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf[2] = cast(char)(0x80 | (c & 0x3F)); @@ -1771,14 +2360,15 @@ void encode(ref char[] str, dchar c) @safe pure else { assert(!isValidDchar(c)); - throw new UTFException("Encoding an invalid code point in UTF-8").setSequence(c); + c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-8", c); + goto L3; } r ~= buf[0 .. L]; } str = r; } -unittest +@safe unittest { import std.exception; debug(utf) printf("utf.encode.unittest\n"); @@ -1801,7 +2391,7 @@ unittest }); } -unittest +@safe unittest { import std.exception; assertCTFEable!( @@ -1820,26 +2410,32 @@ unittest encode(buf, '\U00010000'); assert(buf[21 .. $] == "\U00010000"); encode(buf, '\U0010FFFF'); assert(buf[25 .. $] == "\U0010FFFF"); - assertThrown!UTFException(encode(buf, cast(dchar)0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0x110000)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); + + assert(buf.back != replacementDchar); + encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); + assert(buf.back == replacementDchar); }); } /// ditto -void encode(ref wchar[] str, dchar c) @safe pure +void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( + ref wchar[] str, dchar c) @safe pure { wchar[] r = str; if (c <= 0xFFFF) { if (0xD800 <= c && c <= 0xDFFF) - throw new UTFException("Encoding an isolated surrogate code point in UTF-16").setSequence(c); + c = _utfException!useReplacementDchar("Encoding an isolated surrogate code point in UTF-16", c); assert(isValidDchar(c)); - r ~= cast(wchar)c; + L1: + r ~= cast(wchar) c; } else if (c <= 0x10FFFF) { @@ -1853,13 +2449,14 @@ void encode(ref wchar[] str, dchar c) @safe pure else { assert(!isValidDchar(c)); - throw new UTFException("Encoding an invalid code point in UTF-16").setSequence(c); + c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-16", c); + goto L1; } str = r; } -unittest +@safe unittest { import std.exception; assertCTFEable!( @@ -1874,25 +2471,30 @@ unittest encode(buf, '\U00010000'); assert(buf[5 .. $] == "\U00010000"); encode(buf, '\U0010FFFF'); assert(buf[7 .. $] == "\U0010FFFF"); - assertThrown!UTFException(encode(buf, cast(dchar)0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0x110000)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); + + assert(buf.back != replacementDchar); + encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); + assert(buf.back == replacementDchar); }); } /// ditto -void encode(ref dchar[] str, dchar c) @safe pure +void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( + ref dchar[] str, dchar c) @safe pure { if ((0xD800 <= c && c <= 0xDFFF) || 0x10FFFF < c) - throw new UTFException("Encoding an invalid code point in UTF-32").setSequence(c); - - assert(isValidDchar(c)); + c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-32", c); + else + assert(isValidDchar(c)); str ~= c; } -unittest +@safe unittest { import std.exception; assertCTFEable!( @@ -1906,11 +2508,15 @@ unittest encode(buf, 0xFFFF ); assert(buf[4] == 0xFFFF); encode(buf, '\U0010FFFF'); assert(buf[5] == '\U0010FFFF'); - assertThrown!UTFException(encode(buf, cast(dchar)0xD800)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDBFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDC00)); - assertThrown!UTFException(encode(buf, cast(dchar)0xDFFF)); - assertThrown!UTFException(encode(buf, cast(dchar)0x110000)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xD800)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00)); + assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF)); + assertThrown!UTFException(encode(buf, cast(dchar) 0x110000)); + + assert(buf.back != replacementDchar); + encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000); + assert(buf.back == replacementDchar); }); } @@ -1920,7 +2526,7 @@ unittest $(D c) when $(D C) is the character type used to encode it. +/ ubyte codeLength(C)(dchar c) @safe pure nothrow @nogc - if (isSomeChar!C) +if (isSomeChar!C) { static if (C.sizeof == 1) { @@ -1942,7 +2548,7 @@ ubyte codeLength(C)(dchar c) @safe pure nothrow @nogc } /// -pure nothrow @nogc unittest +@safe pure nothrow @nogc unittest { assert(codeLength!char('a') == 1); assert(codeLength!wchar('a') == 1); @@ -1959,9 +2565,15 @@ pure nothrow @nogc unittest in a string whose character type is $(D C). This is particularly useful when slicing one string with the length of another and the two string types use different character types. + + Params: + C = the character type to get the encoding length for + input = the input range to calculate the encoding length from + Returns: + The number of code units in `input` when encoded to `C` +/ size_t codeLength(C, InputRange)(InputRange input) - if (isInputRange!InputRange && is(ElementType!InputRange : dchar)) +if (isInputRange!InputRange && !isInfinite!InputRange && is(ElementType!InputRange : dchar)) { alias EncType = Unqual!(ElementEncodingType!InputRange); static if (isSomeString!InputRange && is(EncType == C) && is(typeof(input.length))) @@ -1978,7 +2590,7 @@ size_t codeLength(C, InputRange)(InputRange input) } /// -unittest +@safe unittest { import std.conv : to; assert(codeLength!char("hello world") == @@ -2001,19 +2613,19 @@ unittest `, ça, ce ne serait pas bien.`); } -unittest +@safe unittest { import std.conv : to; import std.exception; - import std.algorithm : filter; + import std.algorithm.iteration : filter; assertCTFEable!( { - foreach (S; TypeTuple!( char[], const char[], string, - wchar[], const wchar[], wstring, - dchar[], const dchar[], dstring)) + foreach (S; AliasSeq!( char[], const char[], string, + wchar[], const wchar[], wstring, + dchar[], const dchar[], dstring)) { - foreach (C; TypeTuple!(char, wchar, dchar)) + foreach (C; AliasSeq!(char, wchar, dchar)) { assert(codeLength!C(to!S("Walter Bright")) == to!(C[])("Walter Bright").length); assert(codeLength!C(to!S(`言語`)) == to!(C[])(`言語`).length); @@ -2047,7 +2659,7 @@ if (isSomeChar!C) else static assert(0); } -unittest +@safe unittest { assert( canSearchInCodeUnits! char('a')); assert( canSearchInCodeUnits!wchar('a')); @@ -2059,8 +2671,8 @@ unittest assert(!canSearchInCodeUnits! char('æ—¥')); assert( canSearchInCodeUnits!wchar('æ—¥')); assert( canSearchInCodeUnits!dchar('æ—¥')); - assert(!canSearchInCodeUnits!wchar(cast(wchar)0xDA00)); - assert( canSearchInCodeUnits!dchar(cast(dchar)0xDA00)); + assert(!canSearchInCodeUnits!wchar(cast(wchar) 0xDA00)); + assert( canSearchInCodeUnits!dchar(cast(dchar) 0xDA00)); assert(!canSearchInCodeUnits! char('\U00010001')); assert(!canSearchInCodeUnits!wchar('\U00010001')); assert( canSearchInCodeUnits!dchar('\U00010001')); @@ -2075,7 +2687,7 @@ unittest $(D UTFException) if $(D str) is not well-formed. +/ void validate(S)(in S str) @safe pure - if (isSomeString!S) +if (isSomeString!S) { immutable len = str.length; for (size_t i = 0; i < len; ) @@ -2085,7 +2697,7 @@ void validate(S)(in S str) @safe pure } -unittest // bugzilla 12923 +@safe unittest // bugzilla 12923 { import std.exception; assertThrown((){ @@ -2094,21 +2706,13 @@ unittest // bugzilla 12923 }()); } -/* =================== Conversion to UTF8 ======================= */ - -pure -{ - -char[] toUTF8(return out char[4] buf, dchar c) nothrow @nogc @safe -in -{ - assert(isValidDchar(c)); -} -body +//@@@DEPRECATED_2017-10@@@ +deprecated("To be removed November 2017. Please use std.utf.encode instead.") +char[] toUTF8(return out char[4] buf, dchar c) nothrow @nogc @safe pure { if (c <= 0x7F) { - buf[0] = cast(char)c; + buf[0] = cast(char) c; return buf[0 .. 1]; } else if (c <= 0x7FF) @@ -2119,95 +2723,75 @@ body } else if (c <= 0xFFFF) { + if (c >= 0xD800 && c <= 0xDFFF) + c = replacementDchar; + + L3: buf[0] = cast(char)(0xE0 | (c >> 12)); buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf[2] = cast(char)(0x80 | (c & 0x3F)); return buf[0 .. 3]; } - else if (c <= 0x10FFFF) + else { + if (c > 0x10FFFF) + { + c = replacementDchar; + goto L3; + } + buf[0] = cast(char)(0xF0 | (c >> 18)); buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf[3] = cast(char)(0x80 | (c & 0x3F)); return buf[0 .. 4]; } - - assert(0); } - -/******************* - * Encodes string $(D_PARAM s) into UTF-8 and returns the encoded string. +/** + * Encodes the elements of `s` to UTF-8 and returns a newly allocated + * string of the elements. + * + * Params: + * s = the string to encode + * Returns: + * A UTF-8 string + * See_Also: + * For a lazy, non-allocating version of these functions, see $(LREF byUTF). */ -string toUTF8(in char[] s) @safe +string toUTF8(S)(S s) +if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) { - validate(s); - return s.idup; + return toUTFImpl!string(s); } -/// ditto -string toUTF8(in wchar[] s) @trusted +/// +@safe pure unittest { - import std.exception : assumeUnique; - - char[] r; - size_t i; - size_t slen = s.length; - - r.length = slen; - for (i = 0; i < slen; i++) - { - wchar c = s[i]; + import std.algorithm.comparison : equal; - if (c <= 0x7F) - r[i] = cast(char)c; // fast path for ascii - else - { - r.length = i; - while (i < slen) - encode(r, decode(s, i)); - break; - } - } + // The ö is represented by two UTF-8 code units + assert("Hellø"w.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8])); - return r.assumeUnique(); + // ð· is four code units in UTF-8 + assert("ð·"d.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7])); } -/// ditto -string toUTF8(in dchar[] s) @trusted +@system pure unittest { - import std.exception : assumeUnique; - - char[] r; - size_t i; - size_t slen = s.length; + import std.internal.test.dummyrange : ReferenceInputRange; + import std.algorithm.comparison : equal; - r.length = slen; - for (i = 0; i < slen; i++) - { - dchar c = s[i]; - - if (c <= 0x7F) - r[i] = cast(char)c; // fast path for ascii - else - { - r.length = i; - foreach (dchar d; s[i .. slen]) - { - encode(r, d); - } - break; - } - } + auto r1 = new ReferenceInputRange!dchar("Hellø"); + auto r2 = new ReferenceInputRange!dchar("ð·"); - return r.assumeUnique(); + assert(r1.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8])); + assert(r2.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7])); } - -/* =================== Conversion to UTF16 ======================= */ - -wchar[] toUTF16(return ref wchar[2] buf, dchar c) nothrow @nogc @safe +//@@@DEPRECATED_2017-10@@@ +deprecated("To be removed November 2017. Please use std.utf.encode instead.") +wchar[] toUTF16(return ref wchar[2] buf, dchar c) nothrow @nogc @safe pure in { assert(isValidDchar(c)); @@ -2216,7 +2800,7 @@ body { if (c <= 0xFFFF) { - buf[0] = cast(wchar)c; + buf[0] = cast(wchar) c; return buf[0 .. 1]; } else @@ -2227,121 +2811,86 @@ body } } -/**************** - * Encodes string $(D s) into UTF-16 and returns the encoded string. +/** + * Encodes the elements of `s` to UTF-16 and returns a newly GC allocated + * `wstring` of the elements. + * + * Params: + * s = the range to encode + * Returns: + * A UTF-16 string + * See_Also: + * For a lazy, non-allocating version of these functions, see $(LREF byUTF). */ -wstring toUTF16(in char[] s) @trusted +wstring toUTF16(S)(S s) +if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) { - import std.exception : assumeUnique; - - wchar[] r; - size_t slen = s.length; - - r.length = slen; - r.length = 0; - for (size_t i = 0; i < slen; ) - { - dchar c = s[i]; - if (c <= 0x7F) - { - i++; - r ~= cast(wchar)c; - } - else - { - c = decode(s, i); - encode(r, c); - } - } - - return r.assumeUnique(); // ok because r is unique + return toUTFImpl!wstring(s); } -/// ditto -wstring toUTF16(in wchar[] s) @safe +/// +@safe pure unittest { - validate(s); - return s.idup; + import std.algorithm.comparison : equal; + + // these graphemes are two code units in UTF-16 and one in UTF-32 + assert("𤭢"d.length == 1); + assert("ð·"d.length == 1); + + assert("𤭢"d.toUTF16.equal([0xD852, 0xDF62])); + assert("ð·"d.toUTF16.equal([0xD801, 0xDC37])); } -/// ditto -pure wstring toUTF16(in dchar[] s) @trusted +@system pure unittest { - import std.exception : assumeUnique; - - wchar[] r; - size_t slen = s.length; + import std.internal.test.dummyrange : ReferenceInputRange; + import std.algorithm.comparison : equal; - r.length = slen; - r.length = 0; - for (size_t i = 0; i < slen; i++) - { - encode(r, s[i]); - } + auto r1 = new ReferenceInputRange!dchar("𤭢"); + auto r2 = new ReferenceInputRange!dchar("ð·"); - return r.assumeUnique(); // ok because r is unique + assert(r1.toUTF16.equal([0xD852, 0xDF62])); + assert(r2.toUTF16.equal([0xD801, 0xDC37])); } -/* =================== Conversion to UTF32 ======================= */ - -/***** - * Encodes string $(D_PARAM s) into UTF-32 and returns the encoded string. +/** + * Encodes the elements of `s` to UTF-32 and returns a newly GC allocated + * `dstring` of the elements. + * + * Params: + * s = the range to encode + * Returns: + * A UTF-32 string + * See_Also: + * For a lazy, non-allocating version of these functions, see $(LREF byUTF). */ -dstring toUTF32(in char[] s) @trusted +dstring toUTF32(S)(S s) +if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) { - import std.exception : assumeUnique; - - dchar[] r; - size_t slen = s.length; - size_t j = 0; - - r.length = slen; // r[] will never be longer than s[] - for (size_t i = 0; i < slen; ) - { - dchar c = s[i]; - if (c >= 0x80) - c = decode(s, i); - else - i++; // c is ascii, no need for decode - r[j++] = c; - } - - return r[0 .. j].assumeUnique(); // legit because it's unique + return toUTFImpl!dstring(s); } -/// ditto -dstring toUTF32(in wchar[] s) @trusted +private T toUTFImpl(T, S)(S s) { - import std.exception : assumeUnique; - - dchar[] r; - size_t slen = s.length; - size_t j = 0; - - r.length = slen; // r[] will never be longer than s[] - for (size_t i = 0; i < slen; ) + static if (is(S : T)) { - dchar c = s[i]; - if (c >= 0x80) - c = decode(s, i); - else - i++; // c is ascii, no need for decode - r[j++] = c; + return s.idup; } + else + { + import std.array : appender; + auto app = appender!T(); - return r[0 .. j].assumeUnique(); // legit because it's unique -} - -/// ditto -dstring toUTF32(in dchar[] s) @safe -{ - validate(s); - return s.idup; -} + static if (hasLength!S || isSomeString!S) + app.reserve(s.length); -} // Convert functions are @safe + foreach (c; s.byUTF!(Unqual!(ElementEncodingType!T))) + app.put(c); + return app.data; + } +} /* =================== toUTFz ======================= */ @@ -2402,16 +2951,17 @@ template toUTFz(P) } private P toUTFzImpl(P, S)(S str) @safe pure - if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && - is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) && - is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S)) +if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && + is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) && + is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S)) //immutable(C)[] -> C*, const(C)*, or immutable(C)* { if (str.empty) { typeof(*P.init)[] retval = ['\0']; - return retval.ptr; + auto trustedPtr() @trusted { return retval.ptr; } + return trustedPtr(); } alias C = Unqual!(ElementEncodingType!S); @@ -2437,8 +2987,8 @@ private P toUTFzImpl(P, S)(S str) @safe pure // might be pointing to a new block of memory, which might be // unreadable. Otherwise, it's definitely pointing to valid // memory. - if ((cast(size_t)p & 3) && *p == '\0') - return str.ptr; + if ((cast(size_t) p & 3) && *p == '\0') + return &str[0]; } return toUTFzImpl!(P, const(C)[])(cast(const(C)[])str); @@ -2446,9 +2996,9 @@ private P toUTFzImpl(P, S)(S str) @safe pure } private P toUTFzImpl(P, S)(S str) @safe pure - if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && - is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) && - !is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S)) +if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && + is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) && + !is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S)) //C[] or const(C)[] -> C*, const(C)*, or immutable(C)* { alias InChar = ElementEncodingType!S; @@ -2464,12 +3014,12 @@ private P toUTFzImpl(P, S)(S str) @safe pure auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; } auto p = trustedPtrAdd(str); - if ((cast(size_t)p & 3) && *p == '\0') - return str.ptr; + if ((cast(size_t) p & 3) && *p == '\0') + return &str[0]; } str ~= '\0'; - return str.ptr; + return &str[0]; } //const(C)[] -> C* or immutable(C)* or //C[] -> immutable(C)* @@ -2480,14 +3030,14 @@ private P toUTFzImpl(P, S)(S str) @safe pure copy[0 .. $ - 1] = str[]; copy[$ - 1] = '\0'; - auto trustedCast(typeof(copy) c) @trusted { return cast(P)c.ptr; } + auto trustedCast(typeof(copy) c) @trusted { return cast(P) c.ptr; } return trustedCast(copy); } } private P toUTFzImpl(P, S)(S str) @safe pure - if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && - !is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S))) +if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && + !is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S))) //C1[], const(C1)[], or immutable(C1)[] -> C2*, const(C2)*, or immutable(C2)* { import std.array : appender; @@ -2497,20 +3047,20 @@ private P toUTFzImpl(P, S)(S str) @safe pure retval.put(c); retval.put('\0'); - return cast(P)retval.data.ptr; + return () @trusted { return cast(P) retval.data.ptr; } (); } @safe pure unittest { import std.conv : to; import std.exception; - import std. string : format; + import std.string : format; import core.exception : AssertError; import std.algorithm; assertCTFEable!( { - foreach (S; TypeTuple!(string, wstring, dstring)) + foreach (S; AliasSeq!(string, wstring, dstring)) { alias C = Unqual!(ElementEncodingType!S); @@ -2530,7 +3080,7 @@ private P toUTFzImpl(P, S)(S str) @safe pure assert(p[s.length] == '\0'); } - foreach (P; TypeTuple!(C*, const(C)*, immutable(C)*)) + foreach (P; AliasSeq!(C*, const(C)*, immutable(C)*)) { trustedCStringAssert!P(s1); trustedCStringAssert!P(s2); @@ -2556,30 +3106,30 @@ private P toUTFzImpl(P, S)(S str) @safe pure assertCTFEable!( { - foreach (P; TypeTuple!(wchar*, const(wchar)*, immutable(wchar)*, - dchar*, const(dchar)*, immutable(dchar)*)) + foreach (P; AliasSeq!(wchar*, const(wchar)*, immutable(wchar)*, + dchar*, const(dchar)*, immutable(dchar)*)) { test!P("hello\U00010143\u0100\U00010143"); } - foreach (P; TypeTuple!( char*, const( char)*, immutable( char)*, - dchar*, const(dchar)*, immutable(dchar)*)) + foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*, + dchar*, const(dchar)*, immutable(dchar)*)) { test!P("hello\U00010143\u0100\U00010143"w); } - foreach (P; TypeTuple!( char*, const( char)*, immutable( char)*, - wchar*, const(wchar)*, immutable(wchar)*)) + foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*, + wchar*, const(wchar)*, immutable(wchar)*)) { test!P("hello\U00010143\u0100\U00010143"d); } - foreach (S; TypeTuple!( char[], const( char)[], - wchar[], const(wchar)[], - dchar[], const(dchar)[])) + foreach (S; AliasSeq!( char[], const( char)[], + wchar[], const(wchar)[], + dchar[], const(dchar)[])) { auto s = to!S("hello\U00010143\u0100\U00010143"); - foreach (P; TypeTuple!( char*, const( char)*, immutable( char)*, - wchar*, const(wchar)*, immutable(wchar)*, - dchar*, const(dchar)*, immutable(dchar)*)) + foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*, + wchar*, const(wchar)*, immutable(wchar)*, + dchar*, const(dchar)*, immutable(dchar)*)) { test!P(s); } @@ -2596,7 +3146,7 @@ private P toUTFzImpl(P, S)(S str) @safe pure that take an $(D LPWSTR) or $(D LPCWSTR) argument. +/ const(wchar)* toUTF16z(C)(const(C)[] str) @safe pure - if (isSomeChar!C) +if (isSomeChar!C) { return toUTFz!(const(wchar)*)(str); } @@ -2606,14 +3156,14 @@ const(wchar)* toUTF16z(C)(const(C)[] str) @safe pure import std.conv : to; //toUTFz is already thoroughly tested, so this will just verify that //toUTF16z compiles properly for the various string types. - foreach (S; TypeTuple!(string, wstring, dstring)) - static assert(__traits(compiles, toUTF16z(to!S("hello world")))); + foreach (S; AliasSeq!(string, wstring, dstring)) + assert(toUTF16z(to!S("hello world")) !is null); } /* ================================ tests ================================== */ -pure unittest +@safe pure unittest { import std.exception; debug(utf) printf("utf.toUTF.unittest\n"); @@ -2655,12 +3205,12 @@ pure unittest $(D UTFException) if $(D str) is not well-formed. +/ size_t count(C)(const(C)[] str) @trusted pure nothrow @nogc - if (isSomeChar!C) +if (isSomeChar!C) { return walkLength(str); } -pure nothrow @nogc unittest +@safe pure nothrow @nogc unittest { import std.exception; assertCTFEable!( @@ -2785,312 +3335,433 @@ enum dchar replacementDchar = '\uFFFD'; * Iterate a range of char, wchar, or dchars by code unit. * * The purpose is to bypass the special case decoding that - * $(XREF array,front) does to character arrays. + * $(REF front, std,range,primitives) does to character arrays. As a result, + * using ranges with `byCodeUnit` can be `nothrow` while + * $(REF front, std,range,primitives) throws when it encounters invalid Unicode + * sequences. + * + * A code unit is a building block of the UTF encodings. Generally, an + * individual code unit does not represent what's perceived as a full + * character (a.k.a. a grapheme cluster in Unicode terminology). Many characters + * are encoded with multiple code units. For example, the UTF-8 code units for + * `ø` are `0xC3 0xB8`. That means, an individual element of `byCodeUnit` + * often does not form a character on its own. Attempting to treat it as + * one while iterating over the resulting range will give nonsensical results. + * * Params: - * r = input range of characters, or array of characters + * r = an input range of characters (including strings) or a type that + * implicitly converts to a string type. * Returns: - * input range + * If `r` is not an auto-decodable string (i.e. a narrow string or a + * user-defined type that implicits converts to a string type), then `r` + * is returned. + * + * Otherwise, `r` is converted to its corresponding string type (if it's + * not already a string) and wrapped in a random-access range where the + * element encoding type of the string (its code unit) is the element type + * of the range, and that range returned. The range has slicing. + * + * If `r` is quirky enough to be a struct or class which is an input range + * of characters on its own (i.e. it has the input range API as member + * functions), $(I and) it's implicitly convertible to a string type, then + * `r` is returned, and no implicit conversion takes place. + * See_Also: + * Refer to the $(MREF std, uni) docs for a reference on Unicode + * terminology. + * + * For a range that iterates by grapheme cluster (written character) see + * $(REF byGrapheme, std,uni). */ - -auto byCodeUnit(R)(R r) if (isNarrowString!R) +auto byCodeUnit(R)(R r) +if (isAutodecodableString!R || + isInputRange!R && isSomeChar!(ElementEncodingType!R) || + (is(R : const dchar[]) && !isStaticArray!R)) { - /* Turn an array into an InputRange. - */ - static struct ByCodeUnitImpl + static if (isNarrowString!R || + // This would be cleaner if we had a way to check whether a type + // was a range without any implicit conversions. + (isAutodecodableString!R && !__traits(hasMember, R, "empty") && + !__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront"))) { - pure nothrow @nogc: + static struct ByCodeUnitImpl + { + @safe pure nothrow @nogc: - @property bool empty() const { return r.length == 0; } - @property auto ref front() inout { return r[0]; } - void popFront() { r = r[1 .. $]; } - auto ref opIndex(size_t index) inout { return r[index]; } + @property bool empty() const { return str.length == 0; } + @property auto ref front() inout { return str[0]; } + void popFront() { str = str[1 .. $]; } - @property auto ref back() inout - { - return r[$ - 1]; - } + @property auto save() { return ByCodeUnitImpl(str.save); } - void popBack() - { - r = r[0 .. $-1]; - } + @property auto ref back() inout { return str[$ - 1]; } + void popBack() { str = str[0 .. $-1]; } - auto opSlice(size_t lower, size_t upper) - { - return ByCodeUnitImpl(r[lower..upper]); - } + auto ref opIndex(size_t index) inout { return str[index]; } + auto opSlice(size_t lower, size_t upper) { return ByCodeUnitImpl(str[lower .. upper]); } - @property size_t length() const - { - return r.length; - } - alias opDollar = length; + @property size_t length() const { return str.length; } + alias opDollar = length; - @property auto save() - { - return ByCodeUnitImpl(r.save); + private: + StringTypeOf!R str; } - private: - R r; - } + static assert(isRandomAccessRange!ByCodeUnitImpl); - static assert(isRandomAccessRange!ByCodeUnitImpl); + return ByCodeUnitImpl(r); + } + else static if (is(R : const dchar[]) && !__traits(hasMember, R, "empty") && + !__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront")) + { + return cast(StringTypeOf!R) r; + } + else + { + // byCodeUnit for ranges and dchar[] is a no-op + return r; + } +} - return ByCodeUnitImpl(r); +/// +@safe unittest +{ + import std.range.primitives; + + auto r = "Hello, World!".byCodeUnit(); + static assert(hasLength!(typeof(r))); + static assert(hasSlicing!(typeof(r))); + static assert(isRandomAccessRange!(typeof(r))); + static assert(is(ElementType!(typeof(r)) == immutable char)); + + // contrast with the range capabilities of standard strings + auto s = "Hello, World!"; + static assert(isBidirectionalRange!(typeof(r))); + static assert(is(ElementType!(typeof(s)) == dchar)); + + static assert(!isRandomAccessRange!(typeof(s))); + static assert(!hasSlicing!(typeof(s))); + static assert(!hasLength!(typeof(s))); } -/// Ditto -auto ref byCodeUnit(R)(R r) - if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) +/// `byCodeUnit` does no Unicode decoding +@safe unittest { - // byCodeUnit for ranges and dchar[] is a no-op - return r; + string noel1 = "noe\u0308l"; // noël using e + combining diaeresis + assert(noel1.byCodeUnit[2] != 'ë'); + assert(noel1.byCodeUnit[2] == 'e'); + + string noel2 = "no\u00EBl"; // noël using a precomposed ë character + // Because string is UTF-8, the code unit at index 2 is just + // the first of a sequence that encodes 'ë' + assert(noel2.byCodeUnit[2] != 'ë'); } -pure nothrow @nogc unittest +@safe pure nothrow @nogc unittest { import std.range; { - char[5] s; + enum testStr = "ð„ð‚Œðƒ¯ hello ディラン"; + char[testStr.length] s; int i; - foreach (c; "hello".byCodeUnit().byCodeUnit()) + foreach (c; testStr.byCodeUnit().byCodeUnit()) { s[i++] = c; } - assert(s == "hello"); + assert(s == testStr); } { - wchar[5] s; + enum testStr = "ð„ð‚Œðƒ¯ hello ディラン"w; + wchar[testStr.length] s; int i; - foreach (c; "hello"w.byCodeUnit().byCodeUnit()) + foreach (c; testStr.byCodeUnit().byCodeUnit()) { s[i++] = c; } - assert(s == "hello"w); + assert(s == testStr); } { - dchar[5] s; + enum testStr = "ð„ð‚Œðƒ¯ hello ディラン"d; + dchar[testStr.length] s; int i; - foreach (c; "hello"d.byCodeUnit().byCodeUnit()) + foreach (c; testStr.byCodeUnit().byCodeUnit()) { s[i++] = c; } - assert(s == "hello"d); + assert(s == testStr); } { - auto r = "hello".byCodeUnit(); - assert(r.length == 5); - assert(r[3] == 'l'); - assert(r[2..4][1] == 'l'); + auto bcu = "hello".byCodeUnit(); + assert(bcu.length == 5); + assert(bcu[3] == 'l'); + assert(bcu[2 .. 4][1] == 'l'); } { - char[5] buff = "hello"; - auto s = buff[].byCodeUnit(); - s.front = 'H'; - assert(s.front == 'H'); - s[1] = 'E'; - assert(s[1] == 'E'); + char[5] orig = "hello"; + auto bcu = orig[].byCodeUnit(); + bcu.front = 'H'; + assert(bcu.front == 'H'); + bcu[1] = 'E'; + assert(bcu[1] == 'E'); } { - auto r = "hello".byCodeUnit().byCodeUnit(); - static assert(isForwardRange!(typeof(r))); - auto s = r.save; - r.popFront(); + auto bcu = "hello".byCodeUnit().byCodeUnit(); + static assert(isForwardRange!(typeof(bcu))); + static assert(is(typeof(bcu) == struct)); + auto s = bcu.save; + bcu.popFront(); assert(s.front == 'h'); } { - auto r = "hello".byCodeUnit(); - static assert(hasSlicing!(typeof(r))); - static assert(isBidirectionalRange!(typeof(r))); - auto ret = r.retro; + auto bcu = "hello".byCodeUnit(); + static assert(hasSlicing!(typeof(bcu))); + static assert(isBidirectionalRange!(typeof(bcu))); + static assert(is(typeof(bcu) == struct)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + auto ret = bcu.retro; assert(ret.front == 'o'); ret.popFront(); assert(ret.front == 'l'); } { - auto r = "κόσμε"w.byCodeUnit(); - static assert(hasSlicing!(typeof(r))); - static assert(isBidirectionalRange!(typeof(r))); - auto ret = r.retro; + auto bcu = "κόσμε"w.byCodeUnit(); + static assert(hasSlicing!(typeof(bcu))); + static assert(isBidirectionalRange!(typeof(bcu))); + static assert(is(typeof(bcu) == struct)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + auto ret = bcu.retro; assert(ret.front == 'ε'); ret.popFront(); assert(ret.front == 'μ'); } -} - -/**************************** - * Iterate an input range of characters by char, wchar, or dchar. - * - * UTF sequences that cannot be converted to UTF-8 are replaced by U+FFFD - * per "5.22 Best Practice for U+FFFD Substitution" of the Unicode Standard 6.2. - * Hence, byChar, byWchar, and byDchar are not symmetric. - * This algorithm is lazy, and does not allocate memory. - * Purity, nothrow, and safety are inferred from the r parameter. - * Params: - * r = input range of characters, or array of characters - * Returns: - * input range - */ - -auto byChar(R)(R r) if (isNarrowString!R) -{ - /* This and the following two serve as adapters to convert arrays to ranges, - * so the following three - * won't get auto-decoded by std.array.front(). - */ - alias tchar = Unqual!(ElementEncodingType!R); - - static if (is(tchar == char)) { - return r.byCodeUnit(); + static struct Stringish + { + string s; + alias s this; + } + + auto orig = Stringish("\U0010fff8 ðŠ foo ð‚“"); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == struct)); + static assert(!is(typeof(bcu) == Stringish)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == immutable char)); + assert(bcu.front == cast(char) 244); } - else { - return r.byCodeUnit().byChar(); - } -} - -/// Ditto -auto byWchar(R)(R r) if (isNarrowString!R) -{ - alias tchar = Unqual!(ElementEncodingType!R); + static struct WStringish + { + wstring s; + alias s this; + } - static if (is(tchar == wchar)) + auto orig = WStringish("\U0010fff8 ðŠ foo ð‚“"w); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == struct)); + static assert(!is(typeof(bcu) == WStringish)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == immutable wchar)); + assert(bcu.front == cast(wchar) 56319); + } { - return r.byCodeUnit(); + static struct DStringish + { + dstring s; + alias s this; + } + + auto orig = DStringish("\U0010fff8 ðŠ foo ð‚“"d); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == dstring)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == immutable dchar)); + assert(bcu.front == cast(dchar) 1114104); } - else { - return r.byCodeUnit().byWchar(); + static struct FuncStringish + { + string str; + string s() pure nothrow @nogc { return str; } + alias s this; + } + + auto orig = FuncStringish("\U0010fff8 ðŠ foo ð‚“"); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == struct)); + static assert(!is(typeof(bcu) == FuncStringish)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == immutable char)); + assert(bcu.front == cast(char) 244); } -} + { + static struct Range + { + string data; + bool empty() pure nothrow @nogc { return data.empty; } + char front() pure nothrow @nogc { return data[0]; } + void popFront() pure nothrow @nogc { data = data[1 .. $]; } + } -/// Ditto -auto byDchar(R)(R r) if (isNarrowString!R) -{ - alias tchar = Unqual!(ElementEncodingType!R); + auto orig = Range("\U0010fff8 ðŠ foo ð‚“"); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == Range)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == char)); + assert(bcu.front == cast(char) 244); + } + { + static struct WRange + { + wstring data; + bool empty() pure nothrow @nogc { return data.empty; } + wchar front() pure nothrow @nogc { return data[0]; } + void popFront() pure nothrow @nogc { data = data[1 .. $]; } + } - return r.byCodeUnit().byDchar(); -} + auto orig = WRange("\U0010fff8 ðŠ foo ð‚“"w); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == WRange)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == wchar)); + assert(bcu.front == 56319); + } + { + static struct DRange + { + dstring data; + bool empty() pure nothrow @nogc { return data.empty; } + dchar front() pure nothrow @nogc { return data[0]; } + void popFront() pure nothrow @nogc { data = data[1 .. $]; } + } + auto orig = DRange("\U0010fff8 ðŠ foo ð‚“"d); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == DRange)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == dchar)); + assert(bcu.front == 1114104); + } + { + static struct RangeAndStringish + { + bool empty() pure nothrow @nogc { return data.empty; } + char front() pure nothrow @nogc { return data[0]; } + void popFront() pure nothrow @nogc { data = data[1 .. $]; } -/// Ditto -auto ref byChar(R)(R r) - if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) -{ - alias tchar = Unqual!(ElementEncodingType!R); + string data; + string s; + alias s this; + } - /* Defeat the auto-decoding of std.array.put() by handling arrays of chars - * explicitly. - */ - static if (is(tchar == char)) - { - return r; + auto orig = RangeAndStringish("test.d", "other"); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == RangeAndStringish)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == char)); + assert(bcu.front == 't'); } - else { - static if (is(tchar == wchar)) + static struct WRangeAndStringish { - // Convert wchar => dchar => char - auto r2 = r.byDchar(); + bool empty() pure nothrow @nogc { return data.empty; } + wchar front() pure nothrow @nogc { return data[0]; } + void popFront() pure nothrow @nogc { data = data[1 .. $]; } + + wstring data; + wstring s; + alias s this; } - else static if (is(tchar == dchar)) + + auto orig = WRangeAndStringish("test.d"w, "other"w); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == WRangeAndStringish)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == wchar)); + assert(bcu.front == 't'); + } + { + static struct DRangeAndStringish { - alias r2 = r; + bool empty() pure nothrow @nogc { return data.empty; } + dchar front() pure nothrow @nogc { return data[0]; } + void popFront() pure nothrow @nogc { data = data[1 .. $]; } + + dstring data; + dstring s; + alias s this; } - else - static assert(0); - static struct byCharImpl - { - this(ref typeof(r2) r) - { - this.r = r; - } + auto orig = DRangeAndStringish("test.d"d, "other"d); + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == DRangeAndStringish)); + static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit()))); + static assert(is(ElementType!(typeof(bcu)) == dchar)); + assert(bcu.front == 't'); + } + { + enum Enum : string { a = "test.d" } - @property bool empty() - { - return !nLeft && r.empty; - } + auto orig = Enum.a; + auto bcu = orig.byCodeUnit(); + static assert(!is(typeof(bcu) == Enum)); + static assert(is(typeof(bcu) == struct)); + static assert(is(ElementType!(typeof(bcu)) == immutable char)); + assert(bcu.front == 't'); + } + { + enum WEnum : wstring { a = "test.d"w } - @property auto front() - { - static assert(replacementDchar > 0x7FF && replacementDchar <= 0xFFFF); - if (!nLeft) - { - dchar c = r.front; + auto orig = WEnum.a; + auto bcu = orig.byCodeUnit(); + static assert(!is(typeof(bcu) == WEnum)); + static assert(is(typeof(bcu) == struct)); + static assert(is(ElementType!(typeof(bcu)) == immutable wchar)); + assert(bcu.front == 't'); + } + { + enum DEnum : dstring { a = "test.d"d } - if (c <= 0x7F) - { - buf[0] = cast(char)c; - nLeft = 1; - } - else if (c <= 0x7FF) - { - buf[1] = cast(char)(0xC0 | (c >> 6)); - buf[0] = cast(char)(0x80 | (c & 0x3F)); - nLeft = 2; - } - else if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - c = replacementDchar; + auto orig = DEnum.a; + auto bcu = orig.byCodeUnit(); + static assert(is(typeof(bcu) == dstring)); + static assert(is(ElementType!(typeof(bcu)) == immutable dchar)); + assert(bcu.front == 't'); + } - buf[2] = cast(char)(0xE0 | (c >> 12)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[0] = cast(char)(0x80 | (c & 0x3F)); - nLeft = 3; - } - else if (c <= 0x10FFFF) - { - buf[3] = cast(char)(0xF0 | (c >> 18)); - buf[2] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[0] = cast(char)(0x80 | (c & 0x3F)); - nLeft = 4; - } - else - { - buf[2] = cast(char)(0xE0 | (replacementDchar >> 12)); - buf[1] = cast(char)(0x80 | ((replacementDchar >> 6) & 0x3F)); - buf[0] = cast(char)(0x80 | (replacementDchar & 0x3F)); - nLeft = 3; - } - } - return buf[nLeft - 1]; - } + static assert(!is(typeof(byCodeUnit("hello")) == string)); + static assert(!is(typeof(byCodeUnit("hello"w)) == wstring)); + static assert(is(typeof(byCodeUnit("hello"d)) == dstring)); - void popFront() - { - if (!nLeft) - front; - --nLeft; - if (!nLeft) - r.popFront(); - } - - static if (isForwardRange!(typeof(r2))) - { - @property auto save() - { - auto ret = this; - ret.r = r.save; - return ret; - } - } + static assert(!__traits(compiles, byCodeUnit((char[5]).init))); + static assert(!__traits(compiles, byCodeUnit((wchar[5]).init))); + static assert(!__traits(compiles, byCodeUnit((dchar[5]).init))); - private: - typeof(r2) r; - char[4] buf = void; - uint nLeft; - } + enum SEnum : char[5] { a = "hello" } + enum WSEnum : wchar[5] { a = "hello"w } + enum DSEnum : dchar[5] { a = "hello"d } - return byCharImpl(r2); - } + static assert(!__traits(compiles, byCodeUnit(SEnum.a))); + static assert(!__traits(compiles, byCodeUnit(WSEnum.a))); + static assert(!__traits(compiles, byCodeUnit(DSEnum.a))); } -pure nothrow @nogc unittest +/**************************** + * Iterate an input range of characters by char, wchar, or dchar. + * These aliases simply forward to $(LREF byUTF) with the + * corresponding C argument. + * + * Params: + * r = input range of characters, or array of characters + */ +alias byChar = byUTF!char; + +/// Ditto +alias byWchar = byUTF!wchar; + +/// Ditto +alias byDchar = byUTF!dchar; + +@safe pure nothrow @nogc unittest { { char[5] s; @@ -3106,9 +3777,9 @@ pure nothrow @nogc unittest char[5+2+3+4+3+3] s; int i; dchar[10] a; - a[0..8] = "hello\u07FF\uD7FF\U0010FFFF"d; + a[0 .. 8] = "hello\u07FF\uD7FF\U0010FFFF"d; a[8] = 0xD800; // invalid - a[9] = cast(dchar)0x110000; // invalid + a[9] = cast(dchar) 0x110000; // invalid foreach (c; a[].byChar()) { //writefln("[%d] '%c'", i, c); @@ -3137,110 +3808,15 @@ pure nothrow @nogc unittest } } - -/// Ditto -auto ref byWchar(R)(R r) - if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) -{ - alias tchar = Unqual!(ElementEncodingType!R); - - static if (is(tchar == wchar)) - { - return r; - } - else - { - static if (is(tchar == char)) - { - auto r2 = r.byDchar(); - } - else static if (is(tchar == dchar)) - { - alias r2 = r; - } - else - static assert(0); - - static struct byWcharImpl - { - this(ref typeof(r2) r) - { - this.r = r; - } - - @property bool empty() - { - return !nLeft && r.empty; - } - - @property auto front() - { - static assert(replacementDchar > 0x7FF && replacementDchar <= 0xFFFF); - if (!nLeft) - { - dchar c = r.front; - - if (c <= 0xFFFF) - { - if (0xD800 <= c && c <= 0xDFFF) - c = replacementDchar; - - buf[0] = cast(wchar)c; - nLeft = 1; - } - else if (c <= 0x10FFFF) - { - buf[1] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[0] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); - nLeft = 2; - } - else - { - buf[0] = replacementDchar; - nLeft = 1; - } - } - return buf[nLeft - 1]; - } - - void popFront() - { - if (!nLeft) - front; - --nLeft; - if (!nLeft) - r.popFront(); - } - - static if (isForwardRange!(typeof(r2))) - { - @property auto save() - { - auto ret = this; - ret.r = r.save; - return ret; - } - } - - private: - typeof(r2) r; - wchar[2] buf = void; - uint nLeft; - } - - return byWcharImpl(r2); - } -} - -pure nothrow @nogc unittest +@safe pure nothrow @nogc unittest { { wchar[11] s; int i; dchar[10] a; - a[0..8] = "hello\u07FF\uD7FF\U0010FFFF"d; + a[0 .. 8] = "hello\u07FF\uD7FF\U0010FFFF"d; a[8] = 0xD800; // invalid - a[9] = cast(dchar)0x110000; // invalid + a[9] = cast(dchar) 0x110000; // invalid foreach (c; a[].byWchar()) { //writefln("[%d] '%c' x%x", i, c, c); @@ -3274,193 +3850,7 @@ pure nothrow @nogc unittest } } - -/// Ditto -auto ref byDchar(R)(R r) - if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) -{ - alias tchar = Unqual!(ElementEncodingType!R); - - static if (is(tchar == char)) - { - static struct byDcharImpl - { - this(ref R r) - { - this.r = r; - } - - @property bool empty() - { - return !haveData && r.empty; - } - - @property dchar front() - { - if (haveData) - return frontChar; - dchar c = r.front; - if (c < 0x80) - { - } - else - { - uint fst = c; // upper control bits are masked out later - - /* Dchar bitmask for different numbers of UTF-8 code units. - */ - alias bitMask = TypeTuple!((1 << 7) - 1, (1 << 11) - 1, (1 << 16) - 1, (1 << 21) - 1); - - foreach (i; TypeTuple!(1, 2, 3)) - { - - r.popFront(); - if (r.empty) - break; - - ubyte tmp = r.front; - - if ((tmp & 0xC0) != 0x80) - break; - - c = (c << 6) | (tmp & 0x3F); - - if (!(fst & (0x40 >> i))) // if no more bytes - { - c &= bitMask[i]; // mask out control bits - - // overlong, could have been encoded with i bytes - if ((c & ~bitMask[i - 1]) == 0) - break; - - // check for surrogates only needed for 3 bytes - static if (i == 2) - { - if (c >= 0xD800 && c < 0xE000) - break; - } - - // check for out of range only needed for 4 bytes - static if (i == 3) - { - if (c > 0x10FFFF) - break; - } - - frontChar = c; - haveData = true; - return c; - } - } - c = replacementDchar; - } - frontChar = c; - haveData = true; - return c; - } - - void popFront() - { - if (!haveData) - front; - r.popFront(); - haveData = false; - } - - static if (isForwardRange!R) - { - @property auto save() - { - auto ret = this; - ret.r = r.save; - return ret; - } - } - - private: - R r; - dchar frontChar; - bool haveData; - } - return byDcharImpl(r); - } - else static if (is(tchar == wchar)) - { - static struct byDcharImpl - { - this(ref R r) - { - this.r = r; - } - - @property bool empty() - { - return !haveData && r.empty; - } - - @property dchar front() - { - if (haveData) - return frontChar; - dchar c = r.front; - if (c < 0xD800) - { - } - else if (c <= 0xDBFF) - { - r.popFront(); - if (r.empty) - c = replacementDchar; - else - { - dchar c2 = r.front; - if (c2 < 0xDC00 || c2 > 0xDFFF) - c = replacementDchar; - else - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); - } - } - else if (c <= 0xDFFF) - c = replacementDchar; - frontChar = c; - haveData = true; - return c; - } - - void popFront() - { - if (!haveData) - front; - r.popFront(); - haveData = false; - } - - static if (isForwardRange!R) - { - @property auto save() - { - auto ret = this; - ret.r = r.save; - return ret; - } - } - - private: - R r; - dchar frontChar; - bool haveData; - } - return byDcharImpl(r); - } - else static if (is(tchar == dchar)) - { - return r; - } - else - static assert(0); -} - -pure nothrow @nogc unittest +@safe pure nothrow @nogc unittest { { dchar[9] s; @@ -3468,63 +3858,19 @@ pure nothrow @nogc unittest string a = "hello\u07FF\uD7FF\U00010000\U0010FFFF"; // 1,2,3,4 byte sequences foreach (c; a.byDchar()) { - //writefln("[%d] '%c' x%x", i, c, c); s[i++] = c; } assert(s == "hello\u07FF\uD7FF\U00010000\U0010FFFF"d); } { - char[1] cs; - cs[0] = 0xC0; // truncated - auto r = cs[].byDchar(); - assert(!r.empty); - dchar c = r.front; - assert(c == replacementDchar); - } - { - char[2] cs; - cs[0] = 0xC0; - cs[1] = 0xC0; // invalid continuation - auto r = cs[].byDchar(); - assert(!r.empty); - assert(r.front == r.front); - dchar c = r.front; - assert(c == replacementDchar); - } - { - char[3] cs; - enum x = 0xDC00; // invalid surrogate value - cs[0] = 0xE0 | (x >> 12); - cs[1] = 0x80 | ((x >> 6) & 0x3F); - cs[2] = 0x80 | (x & 0x3F); - auto r = cs[].byDchar(); - assert(!r.empty); - dchar c = r.front; - assert(c == replacementDchar); - } - { - char[4] cs; - cs[0] = 0xF0; - cs[1] = 0x82; - cs[2] = 0x82; - cs[3] = 0xAC; // overlong - auto r = cs[].byDchar(); - assert(!r.empty); - assert(r.front == r.front); - dchar c = r.front; - assert(c == replacementDchar); - } - { - char[4] cs; - enum x = 0x110000; // out of range - cs[0] = cast(char)(0xF0 | (x >> 18)); - cs[1] = cast(char)(0x80 | ((x >> 12) & 0x3F)); - cs[2] = cast(char)(0x80 | ((x >> 6) & 0x3F)); - cs[3] = cast(char)(0x80 | (x & 0x3F)); - auto r = cs[].byDchar(); - assert(!r.empty); - dchar c = r.front; - assert(c == replacementDchar); + foreach (s; invalidUTFstrings!char()) + { + auto r = s.byDchar(); + assert(!r.empty); + assert(r.front == r.front); + dchar c = r.front; + assert(c == replacementDchar); + } } { auto r = "hello".byDchar(); @@ -3545,20 +3891,14 @@ pure nothrow @nogc unittest assert(s == "hello\u07FF\uD7FF\U0010FFFF"d); } { - wchar[1] ws; - ws[0] = 0xD801; // truncated surrogate pair - auto r = ws[].byDchar(); - assert(!r.empty); - dchar c = r.front; - assert(c == replacementDchar); - } - { - wchar[1] ws; - ws[0] = 0xDC00; // unpaired 2nd surrogate - auto r = ws[].byDchar(); - assert(!r.empty); - dchar c = r.front; - assert(c == replacementDchar); + foreach (s; invalidUTFstrings!wchar()) + { + auto r = s.byDchar(); + assert(!r.empty); + assert(r.front == r.front); + dchar c = r.front; + assert(c == replacementDchar); + } } { wchar[2] ws; @@ -3570,16 +3910,6 @@ pure nothrow @nogc unittest dchar c = r.front; assert(c == '\U00010100'); } - { - wchar[2] ws; - ws[0] = 0xD800; - ws[1] = 0xDBFF; // second surrogate out of range - auto r = ws[].byDchar(); - assert(!r.empty); - assert(r.front == r.front); - dchar c = r.front; - assert(c == replacementDchar); - } { auto r = "hello"w.byDchar(); r.popFront(); @@ -3641,7 +3971,7 @@ int impureVariable; } } - foreach (Char; TypeTuple!(char, wchar, dchar)) + foreach (Char; AliasSeq!(char, wchar, dchar)) { ImpureThrowingSystemRange!Char range; foreach (c; range.byChar()) { } @@ -3649,3 +3979,136 @@ int impureVariable; foreach (c; range.byDchar()) { } } } + +/**************************** + * Iterate an input range of characters by char type `C` by + * encoding the elements of the range. + * + * UTF sequences that cannot be converted to the specified encoding are + * replaced by U+FFFD per "5.22 Best Practice for U+FFFD Substitution" + * of the Unicode Standard 6.2. Hence byUTF is not symmetric. + * This algorithm is lazy, and does not allocate memory. + * `@nogc`, `pure`-ity, `nothrow`, and `@safe`-ty are inferred from the + * `r` parameter. + * + * Params: + * C = `char`, `wchar`, or `dchar` + * + * Returns: + * A forward range if `R` is a range and not auto-decodable, as defined by + * $(REF isAutodecodableString, std, traits), and if the base range is + * also a forward range. + * + * Or, if `R` is a range and it is auto-decodable and + * `is(ElementEncodingType!typeof(r) == C)`, then the range is passed + * to $(LREF byCodeUnit). + * + * Otherwise, an input range of characters. + */ +template byUTF(C) +if (isSomeChar!C) +{ + static if (!is(Unqual!C == C)) + alias byUTF = byUTF!(Unqual!C); + else: + + auto ref byUTF(R)(R r) + if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) + { + return byUTF(r.byCodeUnit()); + } + + auto ref byUTF(R)(R r) + if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) + { + alias RC = Unqual!(ElementEncodingType!R); + + static if (is(RC == C)) + { + return r.byCodeUnit(); + } + else + { + static struct Result + { + @property bool empty() + { + return pos == fill && r.empty; + } + + @property auto front() scope // 'scope' required by call to decodeFront() below + { + if (pos == fill) + { + pos = 0; + auto c = r.front; + + if (c <= 0x7F) + { + fill = 1; + r.popFront; + buf[pos] = cast(C) c; + } + else + { + static if (is(RC == dchar)) + { + r.popFront; + dchar dc = c; + } + else + dchar dc = () @trusted { return decodeFront!(Yes.useReplacementDchar)(r); }(); + fill = cast(ushort) encode!(Yes.useReplacementDchar)(buf, dc); + } + } + return buf[pos]; + } + + void popFront() + { + if (pos == fill) + front; + ++pos; + } + + static if (isForwardRange!R) + { + @property auto save() return scope + /* `return scope` cannot be inferred because compiler does not + * track it backwards from assignment to local `ret` + */ + { + auto ret = this; + ret.r = r.save; + return ret; + } + } + + private: + + R r; + C[4 / C.sizeof] buf = void; + ushort pos, fill; + } + + return Result(r); + } + } +} + +/// +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + // hellö as a range of `char`s, which are UTF-8 + "hell\u00F6".byUTF!char().equal(['h', 'e', 'l', 'l', 0xC3, 0xB6]); + + // `wchar`s are able to hold the ö in a single element (UTF-16 code unit) + "hell\u00F6".byUTF!wchar().equal(['h', 'e', 'l', 'l', 'ö']); + + // ð· is four code units in UTF-8, two in UTF-16, and one in UTF-32 + "ð·".byUTF!char().equal([0xF0, 0x90, 0x90, 0xB7]); + "ð·".byUTF!wchar().equal([0xD801, 0xDC37]); + "ð·".byUTF!dchar().equal([0x00010437]); +} diff --git a/std/uuid.d b/std/uuid.d index f53c0c3482c..879a99215e4 100644 --- a/std/uuid.d +++ b/std/uuid.d @@ -75,27 +75,6 @@ $(TR $(TDNW UUID namespaces) * boost._uuid) from the Boost project with some minor additions and API * changes for a more D-like API. * - * Examples: - * ------------------------ - * UUID[] ids; - * ids ~= randomUUID(); - * ids ~= md5UUID("test.name.123"); - * ids ~= sha1UUID("test.name.123"); - * - * foreach(entry; ids) - * { - * assert(entry.variant == UUID.Variant.rfc4122); - * } - * - * assert(ids[0].uuidVersion == UUID.Version.randomNumberBased); - * assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd"); - * assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207, - * 234, 161, 157, 12, 205]); - * - * UUID id; - * assert(id.empty); - * - * ------------------------ * Standards: * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122) * @@ -103,7 +82,7 @@ $(TR $(TDNW UUID namespaces) * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier) * * Copyright: Copyright Johannes Pfau 2011 - . - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Johannes Pfau * Source: $(PHOBOSSRC std/_uuid.d) * @@ -118,6 +97,28 @@ $(TR $(TDNW UUID namespaces) */ module std.uuid; +/// +@safe unittest +{ + import std.uuid; + + UUID[] ids; + ids ~= randomUUID(); + ids ~= md5UUID("test.name.123"); + ids ~= sha1UUID("test.name.123"); + + foreach (entry; ids) + { + assert(entry.variant == UUID.Variant.rfc4122); + } + assert(ids[0].uuidVersion == UUID.Version.randomNumberBased); + assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd"); + assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207, + 234, 161, 157, 12, 205]); + UUID id; + assert(id.empty); +} + import std.range.primitives; import std.traits; @@ -126,44 +127,24 @@ import std.traits; */ public struct UUID { - import std.typetuple : allSatisfy; - import std.traits : isIntegral; + import std.meta : AliasSeq, allSatisfy; private: - @safe pure nothrow char toChar(size_t i) const - { - if(i <= 9) - return cast(char)('0' + i); - else - return cast(char)('a' + (i-10)); - } + alias skipSeq = AliasSeq!(8, 13, 18, 23); + alias byteSeq = AliasSeq!(0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34); - @safe pure nothrow char[36] _toString() const + @safe pure nothrow @nogc Char toChar(Char)(size_t i) const { - char[36] result; - - size_t i=0; - foreach(entry; this.data) - { - const size_t hi = (entry >> 4) & 0x0F; - result[i++] = toChar(hi); - - const size_t lo = (entry) & 0x0F; - result[i++] = toChar(lo); - - if (i == 8 || i == 13 || i == 18 || i == 23) - { - result[i++] = '-'; - } - } - - return result; + if (i <= 9) + return cast(Char)('0' + i); + else + return cast(Char)('a' + (i-10)); } - @safe pure unittest + @safe pure nothrow unittest { assert(UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, - 179, 189, 251, 70])._toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); + 179, 189, 251, 70]).toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); } // Reinterpret the UUID as an array of some other primitive. @@ -180,7 +161,7 @@ public struct UUID * possible to read, compare and use all these Variants, but * UUIDs generated by this module will always be in rfc4122 format. * - * Note: Do not confuse this with $(XREF _variant, _Variant). + * Note: Do not confuse this with $(REF _Variant, std,_variant). */ enum Variant { @@ -215,25 +196,31 @@ public struct UUID nameBasedSHA1 = 5 } - /** - * It is sometimes useful to get or set the 16 bytes of a UUID - * directly. - * - * Note: - * UUID uses a 16-ubyte representation for the UUID data. - * RFC 4122 defines a UUID as a special structure in big-endian - * format. These 16-ubytes always equal the big-endian structure - * defined in RFC 4122. - * - * Examples: - * ----------------------------------------------- - * auto rawData = uuid.data; //get data - * rawData[0] = 1; //modify - * uuid.data = rawData; //set data - * uuid.data[1] = 2; //modify directly - * ----------------------------------------------- - */ - ubyte[16] data; + union + { + /** + * It is sometimes useful to get or set the 16 bytes of a UUID + * directly. + * + * Note: + * UUID uses a 16-ubyte representation for the UUID data. + * RFC 4122 defines a UUID as a special structure in big-endian + * format. These 16-ubytes always equal the big-endian structure + * defined in RFC 4122. + * + * Example: + * ----------------------------------------------- + * auto rawData = uuid.data; //get data + * rawData[0] = 1; //modify + * uuid.data = rawData; //set data + * uuid.data[1] = 2; //modify directly + * ----------------------------------------------- + */ + ubyte[16] data; + private ulong[2] ulongs; + static if (size_t.sizeof == 4) + private uint[4] uints; + } /* * We could use a union here to also provide access to the @@ -253,7 +240,7 @@ public struct UUID assert(tmp.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, 12,13,14,15]); - auto tmp2 = cast(immutable UUID)tmp; + auto tmp2 = cast(immutable UUID) tmp; assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11, 12,13,14,15]); } @@ -288,25 +275,25 @@ public struct UUID * You need to pass exactly 16 ubytes. */ @safe pure this(T...)(T uuidData) - if(uuidData.length == 16 && allSatisfy!(isIntegral, T)) + if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) { import std.conv : to; - foreach(idx, it; uuidData) + foreach (idx, it; uuidData) { this.data[idx] = to!ubyte(it); } } /// - unittest + @safe unittest { auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, 12,13,14,15]); } - unittest + @safe unittest { UUID tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11, @@ -343,85 +330,83 @@ public struct UUID * hyphens exactly like above. * * For a less strict parser, see $(LREF parseUUID) - * - * Examples: - * ------------------------- - * id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"); - * assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, - * 181, 45, 179, 189, 251, 70]); - * assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - * - * //Can also be used in CTFE, for example as UUID literals: - * enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); - * //here parsing is done at compile time, no runtime overhead! - * ------------------------- */ - this(T)(in T[] uuid) if(isSomeChar!(Unqual!T)) + this(T)(in T[] uuid) if (isSomeChar!(Unqual!T)) { import std.conv : to, parse; - if(uuid.length < 36) + if (uuid.length < 36) { throw new UUIDParsingException(to!string(uuid), 0, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); } - if(uuid.length > 36) + if (uuid.length > 36) { throw new UUIDParsingException(to!string(uuid), 35, UUIDParsingException.Reason.tooMuch, "Input is too long, need exactly 36 characters"); } + static immutable skipInd = [skipSeq]; + foreach (pos; skipInd) + if (uuid[pos] != '-') + throw new UUIDParsingException(to!string(uuid), pos, + UUIDParsingException.Reason.invalidChar, "Expected '-'"); ubyte[16] data2; //ctfe bug - size_t element = 0, pairStart = -1; + uint pos = void; - foreach(pos, dchar character; uuid) + foreach (i, p; byteSeq) { - if(pos == 8 || pos == 13 || pos == 18 || pos == 23) + enum uint s = 'a'-10-'0'; + uint h = uuid[p]; + uint l = uuid[p+1]; + pos = p; + if (h < '0') goto Lerr; + if (l < '0') goto Lerr; + if (h > '9') { - if(character != '-') - { - throw new UUIDParsingException(to!string(uuid), pos, - UUIDParsingException.Reason.invalidChar, "Expected '-'"); - } + h |= 0x20; //poorman's tolower + if (h < 'a') goto Lerr; + if (h > 'f') goto Lerr; + h -= s; } - else + if (l > '9') { - if(pairStart == -1) - pairStart = pos; - else - { - try - { - auto part = uuid[pairStart .. pos+1]; - data2[element++] = parse!ubyte(part, 16); - pairStart = -1; - } - catch(Exception e) - { - throw new UUIDParsingException(to!string(uuid), pos, - UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte", e); - } - } + l |= 0x20; //poorman's tolower + if (l < 'a') goto Lerr; + if (l > 'f') goto Lerr; + l -= s; } - } + h -= '0'; + l -= '0'; - assert(element <= 16); - - if(element < 16) - { - throw new UUIDParsingException(to!string(uuid), 0, - UUIDParsingException.Reason.tooLittle, "Insufficient Input"); + data2[i] = cast(ubyte)((h << 4) ^ l); } - this.data = data2; + return; + + Lerr: throw new UUIDParsingException(to!string(uuid), pos, + UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte"); + } + + /// + @safe pure unittest + { + auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46"); + assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, + 181, 45, 179, 189, 251, 70]); + assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); + + //Can also be used in CTFE, for example as UUID literals: + enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"); + //here parsing is done at compile time, no runtime overhead! } @safe pure unittest { import std.exception; - import std.typetuple; + import std.meta; import std.conv : to; - foreach(S; TypeTuple!(char[], const(char)[], immutable(char)[], + foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], wchar[], const(wchar)[], immutable(wchar)[], dchar[], const(dchar)[], immutable(dchar)[], immutable(char[]), immutable(wchar[]), immutable(dchar[]))) @@ -479,13 +464,13 @@ public struct UUID */ @trusted pure nothrow @nogc @property bool empty() const { - if(__ctfe) + if (__ctfe) return data == (ubyte[16]).init; auto p = cast(const(size_t*))data.ptr; - static if(size_t.sizeof == 4) + static if (size_t.sizeof == 4) return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0; - else static if(size_t.sizeof == 8) + else static if (size_t.sizeof == 8) return p[0] == 0 && p[1] == 0; else static assert(false, "nonsense, it's not 32 or 64 bit"); @@ -509,7 +494,7 @@ public struct UUID return data; } - for(size_t i = 0; i < 16; i++) + for (size_t i = 0; i < 16; i++) { assert(!UUID(getData(i)).empty); } @@ -519,7 +504,7 @@ public struct UUID bool ctfeTest() { - for(size_t i = 0; i < 16; i++) + for (size_t i = 0; i < 16; i++) { auto ctfeEmpty2 = UUID(getData(i)).empty; assert(!ctfeEmpty2); @@ -533,7 +518,7 @@ public struct UUID * RFC 4122 defines different internal data layouts for UUIDs. * Returns the format used by this UUID. * - * Note: Do not confuse this with $(XREF _variant, _Variant). + * Note: Do not confuse this with $(REF _Variant, std,_variant). * The type of this property is $(MYREF3 std.uuid.UUID.Variant, _Variant). * * See_Also: @@ -543,13 +528,13 @@ public struct UUID { //variant is stored in octet 7 //which is index 8, since indexes count backwards - auto octet7 = data[8]; //octet 7 is array index 8 + immutable octet7 = data[8]; //octet 7 is array index 8 - if((octet7 & 0x80) == 0x00) //0b0xxxxxxx + if ((octet7 & 0x80) == 0x00) //0b0xxxxxxx return Variant.ncs; - else if((octet7 & 0xC0) == 0x80) //0b10xxxxxx + else if ((octet7 & 0xC0) == 0x80) //0b10xxxxxx return Variant.rfc4122; - else if((octet7 & 0xE0) == 0xC0) //0b110xxxxx + else if ((octet7 & 0xE0) == 0xC0) //0b110xxxxx return Variant.microsoft; else { @@ -564,8 +549,9 @@ public struct UUID assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant == UUID.Variant.rfc4122); } - pure unittest + @system pure unittest { + // @system due to Variant Variant[ubyte] tests = cast(Variant[ubyte])[0x00 : Variant.ncs, 0x10 : Variant.ncs, 0x20 : Variant.ncs, @@ -582,7 +568,7 @@ public struct UUID 0xd0 : Variant.microsoft, 0xe0 : Variant.future, 0xf0 : Variant.future]; - foreach(key, value; tests) + foreach (key, value; tests) { UUID u; u.data[8] = key; @@ -603,7 +589,7 @@ public struct UUID { //version is stored in octet 9 //which is index 6, since indexes count backwards - auto octet9 = data[6]; + immutable octet9 = data[6]; if ((octet9 & 0xF0) == 0x10) return Version.timeBased; else if ((octet9 & 0xF0) == 0x20) @@ -619,13 +605,14 @@ public struct UUID } /// - unittest + @safe unittest { assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion == UUID.Version.randomNumberBased); } - unittest + @system unittest { + // @system due to cast Version[ubyte] tests = cast(Version[ubyte]) [ 0x00 : UUID.Version.unknown, 0x10 : UUID.Version.timeBased, @@ -643,7 +630,7 @@ public struct UUID 0xd0 : UUID.Version.unknown, 0xe0 : UUID.Version.unknown, 0xf0 : UUID.Version.unknown]; - foreach(key, value; tests) + foreach (key, value; tests) { UUID u; u.data[6] = key; @@ -656,13 +643,13 @@ public struct UUID */ @safe pure nothrow @nogc void swap(ref UUID rhs) { - auto bck = data; + immutable bck = data; data = rhs.data; rhs.data = bck; } /// - unittest + @safe unittest { immutable ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; UUID u1; @@ -679,11 +666,11 @@ public struct UUID */ @safe pure nothrow @nogc bool opEquals(in UUID s) const { - return s.data == this.data; + return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1]; } /// - pure unittest + @safe pure unittest { //compare UUIDs assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init); @@ -708,7 +695,7 @@ public struct UUID */ @safe pure nothrow @nogc bool opEquals(ref in UUID s) const { - return s.data == this.data; + return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1]; } /** @@ -716,7 +703,7 @@ public struct UUID */ @safe pure nothrow @nogc int opCmp(in UUID s) const { - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; return cmp(this.data[], s.data[]); } @@ -725,23 +712,103 @@ public struct UUID */ @safe pure nothrow @nogc int opCmp(ref in UUID s) const { - import std.algorithm : cmp; + import std.algorithm.comparison : cmp; return cmp(this.data[], s.data[]); } /** * ditto */ - @safe pure nothrow @nogc size_t toHash() const + @safe pure nothrow @nogc UUID opAssign(in UUID s) { - size_t seed = 0; - foreach(entry; this.data) - seed ^= cast(size_t)entry + 0x9e3779b9 + (seed << 6) + (seed >> 2); + ulongs[0] = s.ulongs[0]; + ulongs[1] = s.ulongs[1]; + return this; + } - return seed; + /** + * ditto + */ + @safe pure nothrow @nogc UUID opAssign(ref in UUID s) + { + ulongs[0] = s.ulongs[0]; + ulongs[1] = s.ulongs[1]; + return this; } - unittest + /** + * ditto + */ + //MurmurHash2 + @safe pure nothrow @nogc size_t toHash() const + { + static if (size_t.sizeof == 4) + { + enum uint m = 0x5bd1e995; + enum uint n = 16; + enum uint r = 24; + + uint h = n; + + uint k = uints[0]; + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + + k = uints[1]; + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + + k = uints[2]; + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + + k = uints[3]; + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + else + { + enum ulong m = 0xc6a4a7935bd1e995UL; + enum ulong n = m * 16; + enum uint r = 47; + + ulong h = n; + + ulong k = ulongs[0]; + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + + k = ulongs[1]; + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + return h; + } + @safe unittest { assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init); int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1, @@ -793,18 +860,44 @@ public struct UUID assert(u3.toHash() != u1.toHash()); } + /** - * Return the UUID as a string in the canonical form. + * Write the UUID into `sink` as an ASCII string in the canonical form, + * which is 36 characters in the form "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + * Params: + * sink = OutputRange or writeable array at least 36 entries long */ - void toString(scope void delegate(const(char)[]) sink) const + void toString(Writer)(scope Writer sink) const { - sink(_toString()); + char[36] result = void; + foreach (pos; skipSeq) + result[pos] = '-'; + foreach (i, pos; byteSeq) + { + const uint entry = this.data[i]; + const uint hi = entry >> 4; + result[pos ] = toChar!char(hi); + const uint lo = (entry) & 0x0F; + result[pos+1] = toChar!char(lo); + } + foreach (i, c; result) + { + static if (__traits(compiles, put(sink, c))) + put(sink, c); + else + sink[i] = cast(typeof(sink[i]))c; + } } - ///ditto - @safe pure nothrow string toString() const + /** + * Return the UUID as a string in the canonical form. + */ + @trusted pure nothrow string toString() const { - return _toString().idup; + import std.exception : assumeUnique; + auto result = new char[36]; + toString(result); + return result.assumeUnique; } /// @@ -815,7 +908,42 @@ public struct UUID assert(id.toString() == str); } - unittest + @safe pure nothrow @nogc unittest + { + import std.meta : AliasSeq; + foreach (Char; AliasSeq!(char, wchar, dchar)) + { + alias String = immutable(Char)[]; + //CTFE + enum String s = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; + enum id = UUID(s); + static if (is(Char == char)) + { + enum p = id.toString(); + static assert(s == p); + } + //nogc + Char[36] str; + id.toString(str[]); + assert(str == s); + } + } + + @system pure nothrow @nogc unittest + { + // @system due to cast + import std.encoding : Char = AsciiChar; + enum utfstr = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46"; + alias String = immutable(Char)[]; + enum String s = cast(String) utfstr; + enum id = UUID(utfstr); + //nogc + Char[36] str; + id.toString(str[]); + assert(str == s); + } + + @safe unittest { auto u1 = UUID(cast(ubyte[16])[138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]); @@ -870,9 +998,7 @@ public struct UUID return md5UUID(cast(const(ubyte[]))name, namespace); } -/** - * ditto - */ +/// ditto @safe pure nothrow @nogc UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init) { import std.digest.md : MD5; @@ -905,7 +1031,7 @@ public struct UUID } /// -unittest +@safe unittest { //Use default UUID.init namespace auto simpleID = md5UUID("test.uuid.any.string"); @@ -983,9 +1109,7 @@ unittest return sha1UUID(cast(const(ubyte[]))name, namespace); } -/** - * ditto - */ +/// ditto @safe pure nothrow @nogc UUID sha1UUID(in ubyte[] data, const UUID namespace = UUID.init) { import std.digest.sha : SHA1; @@ -1019,7 +1143,7 @@ unittest } /// -unittest +@safe unittest { //Use default UUID.init namespace auto simpleID = sha1UUID("test.uuid.any.string"); @@ -1062,9 +1186,11 @@ unittest * This function generates a random number based UUID from a random * number generator. * - * CTFE: * This function is not supported at compile time. * + * Params: + * randomGen = uniform RNG + * See_Also: $(REF isUniformRNG, std,random) */ @safe UUID randomUUID() { @@ -1072,24 +1198,17 @@ unittest return randomUUID(rndGen); } -/** - * ditto - */ -/** - * Params: - * randomGen = uniform RNG - * See_Also: $(XREF random, isUniformRNG) - */ +/// ditto UUID randomUUID(RNG)(ref RNG randomGen) if (isInputRange!RNG && isIntegral!(ElementType!RNG)) { import std.random : isUniformRNG; - static assert (isUniformRNG!RNG, "randomGen must be a uniform RNG"); + static assert(isUniformRNG!RNG, "randomGen must be a uniform RNG"); alias E = ElementEncodingType!RNG; enum size_t elemSize = E.sizeof; - static assert (elemSize <= 16); - static assert (16 % elemSize == 0); + static assert(elemSize <= 16); + static assert(16 % elemSize == 0); UUID u; foreach (ref E e ; u.asArrayOf!E()) @@ -1184,28 +1303,30 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG)) * caused by a malformed UUID parsed at compile time can be cryptic, * but errors are detected and reported at compile time. */ -UUID parseUUID(T)(T uuidString) if(isSomeString!T) +UUID parseUUID(T)(T uuidString) +if (isSomeString!T) { return parseUUID(uuidString); } ///ditto -UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range +UUID parseUUID(Range)(ref Range uuidRange) +if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) { import std.conv : ConvException, parse; import std.ascii : isHexDigit; - static if(isForwardRange!Range) + static if (isForwardRange!Range) auto errorCopy = uuidRange.save; void parserError()(size_t pos, UUIDParsingException.Reason reason, string message, Throwable next = null, string file = __FILE__, size_t line = __LINE__) { - static if(isForwardRange!Range) + static if (isForwardRange!Range) { import std.conv : to; - static if(isInfinite!Range) + static if (isInfinite!Range) { throw new UUIDParsingException(to!string(take(errorCopy, pos)), pos, reason, message, next, file, line); @@ -1222,10 +1343,10 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range } } - static if(hasLength!Range) + static if (hasLength!Range) { import std.conv : to; - if(uuidRange.length < 32) + if (uuidRange.length < 32) { throw new UUIDParsingException(to!string(uuidRange), 0, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); @@ -1240,7 +1361,7 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range size_t skip()() { size_t skipped; - while(!uuidRange.empty && !isHexDigit(uuidRange.front)) + while (!uuidRange.empty && !isHexDigit(uuidRange.front)) { skipped++; uuidRange.popFront(); @@ -1250,25 +1371,25 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range consumed += skip(); - if(uuidRange.empty) + if (uuidRange.empty) parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); bool dashAllowed = false; - parseLoop: while(!uuidRange.empty) + parseLoop: while (!uuidRange.empty) { - dchar character = uuidRange.front; + immutable character = uuidRange.front; - if(character == '-') + if (character == '-') { - if(!dashAllowed) + if (!dashAllowed) parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected '-'"); else dashAllowed = false; consumed++; } - else if(!isHexDigit(character)) + else if (!isHexDigit(character)) { parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character (wanted a hexDigit)"); @@ -1278,9 +1399,9 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range try { consumed += 2; - static if(isSomeString!Range) + static if (isSomeString!Range) { - if(uuidRange.length < 2) + if (uuidRange.length < 2) { parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); @@ -1294,7 +1415,7 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range dchar[2] copyBuf; copyBuf[0] = character; uuidRange.popFront(); - if(uuidRange.empty) + if (uuidRange.empty) { parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); @@ -1304,7 +1425,7 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range result.data[element++] = parse!ubyte(part, 16); } - if(element == 16) + if (element == 16) { uuidRange.popFront(); break parseLoop; @@ -1312,7 +1433,7 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range dashAllowed = true; } - catch(ConvException e) + catch (ConvException e) { parserError(consumed, UUIDParsingException.Reason.invalidChar, "Couldn't parse ubyte", e); @@ -1322,18 +1443,18 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range } assert(element <= 16); - if(element < 16) + if (element < 16) parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input"); consumed += skip(); - if(!uuidRange.empty) + if (!uuidRange.empty) parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character"); return result; } /// -unittest +@safe unittest { auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"); //no dashes @@ -1355,7 +1476,7 @@ unittest @safe pure unittest { import std.exception; - import std.typetuple; + import std.meta; import std.conv : to; struct TestRange(bool forward) @@ -1377,7 +1498,7 @@ unittest return input.empty; } - static if(forward) + static if (forward) { @property TestRange!true save() { @@ -1397,7 +1518,7 @@ unittest //Helper function for unittests - Need to pass ranges by ref UUID parseHelper(T)(string input) { - static if(is(T == TestInputRange) || is(T == TestForwardRange)) + static if (is(T == TestInputRange) || is(T == TestForwardRange)) { T range = T(to!dstring(input)); return parseUUID(range); @@ -1406,7 +1527,7 @@ unittest return parseUUID(to!T(input)); } - foreach(S; TypeTuple!(char[], const(char)[], immutable(char)[], + foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], wchar[], const(wchar)[], immutable(wchar)[], dchar[], const(dchar)[], immutable(dchar)[], immutable(char[]), immutable(wchar[]), immutable(dchar[]), @@ -1525,7 +1646,7 @@ enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~ "-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"; /// -unittest +@safe unittest { import std.algorithm; import std.regex; @@ -1539,7 +1660,7 @@ unittest auto r = regex(uuidRegex, "g"); UUID[] found; - foreach(c; match(test, r)) + foreach (c; match(test, r)) { found ~= UUID(c.hit); } @@ -1589,7 +1710,7 @@ public class UUIDParsingException : Exception } /// -unittest +@safe unittest { auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch); assert(ex.input == "foo"); diff --git a/std/variant.d b/std/variant.d index 20b6dc6bd2d..eb15fd6f26e 100644 --- a/std/variant.d +++ b/std/variant.d @@ -1,74 +1,71 @@ // Written in the D programming language. /** - * This module implements a - * $(LINK2 http://erdani.org/publications/cuj-04-2002.html,discriminated union) - * type (a.k.a. - * $(LINK2 http://en.wikipedia.org/wiki/Tagged_union,tagged union), - * $(LINK2 http://en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)). - * Such types are useful - * for type-uniform binary interfaces, interfacing with scripting - * languages, and comfortable exploratory programming. - * - * Macros: - * WIKI = Phobos/StdVariant - * - * Synopsis: - * - * ---- - * Variant a; // Must assign before use, otherwise exception ensues - * // Initialize with an integer; make the type int - * Variant b = 42; - * assert(b.type == typeid(int)); - * // Peek at the value - * assert(b.peek!(int) !is null && *b.peek!(int) == 42); - * // Automatically convert per language rules - * auto x = b.get!(real); - * // Assign any other type, including other variants - * a = b; - * a = 3.14; - * assert(a.type == typeid(double)); - * // Implicit conversions work just as with built-in types - * assert(a < b); - * // Check for convertibility - * assert(!a.convertsTo!(int)); // double not convertible to int - * // Strings and all other arrays are supported - * a = "now I'm a string"; - * assert(a == "now I'm a string"); - * a = new int[42]; // can also assign arrays - * assert(a.length == 42); - * a[5] = 7; - * assert(a[5] == 7); - * // Can also assign class values - * class Foo {} - * auto foo = new Foo; - * a = foo; - * assert(*a.peek!(Foo) == foo); // and full type information is preserved - * ---- - * - * Credits: - * - * Reviewed by Brad Roberts. Daniel Keep provided a detailed code - * review prompting the following improvements: (1) better support for - * arrays; (2) support for associative arrays; (3) friendlier behavior - * towards the garbage collector. - * - * Copyright: Copyright Andrei Alexandrescu 2007 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB erdani.org, Andrei Alexandrescu) - * Source: $(PHOBOSSRC std/_variant.d) - */ -/* Copyright Andrei Alexandrescu 2007 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ +This module implements a +$(HTTP erdani.org/publications/cuj-04-2002.html,discriminated union) +type (a.k.a. +$(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union), +$(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)). +Such types are useful +for type-uniform binary interfaces, interfacing with scripting +languages, and comfortable exploratory programming. + +Synopsis: +---- +Variant a; // Must assign before use, otherwise exception ensues +// Initialize with an integer; make the type int +Variant b = 42; +assert(b.type == typeid(int)); +// Peek at the value +assert(b.peek!(int) !is null && *b.peek!(int) == 42); +// Automatically convert per language rules +auto x = b.get!(real); +// Assign any other type, including other variants +a = b; +a = 3.14; +assert(a.type == typeid(double)); +// Implicit conversions work just as with built-in types +assert(a < b); +// Check for convertibility +assert(!a.convertsTo!(int)); // double not convertible to int +// Strings and all other arrays are supported +a = "now I'm a string"; +assert(a == "now I'm a string"); +a = new int[42]; // can also assign arrays +assert(a.length == 42); +a[5] = 7; +assert(a[5] == 7); +// Can also assign class values +class Foo {} +auto foo = new Foo; +a = foo; +assert(*a.peek!(Foo) == foo); // and full type information is preserved +---- + +A $(LREF Variant) object can hold a value of any type, with very few +restrictions (such as `shared` types and noncopyable types). Setting the value +is as immediate as assigning to the `Variant` object. To read back the value of +the appropriate type `T`, use the $(LREF get!T) call. To query whether a +`Variant` currently holds a value of type `T`, use $(LREF peek!T). To fetch the +exact type currently held, call $(LREF type), which returns the `TypeInfo` of +the current value. + +In addition to $(LREF Variant), this module also defines the $(LREF Algebraic) +type constructor. Unlike `Variant`, `Algebraic` only allows a finite set of +types, which are specified in the instantiation (e.g. $(D Algebraic!(int, +string)) may only hold an `int` or a `string`). + +Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code review +prompting the following improvements: (1) better support for arrays; (2) support +for associative arrays; (3) friendlier behavior towards the garbage collector. +Copyright: Copyright Andrei Alexandrescu 2007 - 2015. +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +Authors: $(HTTP erdani.org, Andrei Alexandrescu) +Source: $(PHOBOSSRC std/_variant.d) +*/ module std.variant; -import core.stdc.string, std.conv, std.exception, std.traits, std.typecons, - std.typetuple; - -@trusted: +import std.meta, std.traits, std.typecons; /++ Gives the $(D sizeof) the largest type given. @@ -81,83 +78,58 @@ template maxSize(T...) } else { - enum size_t maxSize = T[0].sizeof >= maxSize!(T[1 .. $]) - ? T[0].sizeof : maxSize!(T[1 .. $]); + import std.algorithm.comparison : max; + enum size_t maxSize = max(T[0].sizeof, maxSize!(T[1 .. $])); } } -struct This; - -template AssociativeArray(T) +/// +@safe unittest { - enum bool valid = false; - alias Key = void; - alias Value = void; -} + static assert(maxSize!(int, long) == 8); + static assert(maxSize!(bool, byte) == 1); -template AssociativeArray(T : V[K], K, V) -{ - enum bool valid = true; - alias Key = K; - alias Value = V; + struct Cat { int a, b, c; } + static assert(maxSize!(bool, Cat) == 12); } -template This2Variant(V, T...) -{ - static if (T.length == 0) - alias This2Variant = TypeTuple!(); - else static if (is(AssociativeArray!(T[0]).Key == This)) - { - static if (is(AssociativeArray!(T[0]).Value == This)) - alias This2Variant = - TypeTuple!(V[V], - This2Variant!(V, T[1 .. $])); - else - alias This2Variant = - TypeTuple!(AssociativeArray!(T[0]).Value[V], - This2Variant!(V, T[1 .. $])); - } - else static if (is(AssociativeArray!(T[0]).Value == This)) - alias This2Variant = - TypeTuple!(V[AssociativeArray!(T[0]).Key], - This2Variant!(V, T[1 .. $])); - else static if (is(T[0] == This[])) - alias This2Variant = TypeTuple!(V[], This2Variant!(V, T[1 .. $])); - else static if (is(T[0] == This*)) - alias This2Variant = TypeTuple!(V*, This2Variant!(V, T[1 .. $])); - else - alias This2Variant = TypeTuple!(T[0], This2Variant!(V, T[1 .. $])); -} +struct This; + +private alias This2Variant(V, T...) = AliasSeq!(ReplaceType!(This, V, T)); /** - * $(D_PARAM VariantN) is a back-end type seldom used directly by user - * code. Two commonly-used types using $(D_PARAM VariantN) as - * back-end are: + * Back-end type seldom used directly by user + * code. Two commonly-used types using $(D VariantN) are: * - * $(OL $(LI $(B Algebraic): A closed discriminated union with a - * limited type universe (e.g., $(D_PARAM Algebraic!(int, double, + * $(OL $(LI $(LREF Algebraic): A closed discriminated union with a + * limited type universe (e.g., $(D Algebraic!(int, double, * string)) only accepts these three types and rejects anything - * else).) $(LI $(B Variant): An open discriminated union allowing an - * unbounded set of types. The restriction is that the size of the - * stored type cannot be larger than the largest built-in type. This - * means that $(D_PARAM Variant) can accommodate all primitive types - * and all user-defined types except for large $(D_PARAM struct)s.) ) + * else).) $(LI $(LREF Variant): An open discriminated union allowing an + * unbounded set of types. If any of the types in the $(D Variant) + * are larger than the largest built-in type, they will automatically + * be boxed. This means that even large types will only be the size + * of a pointer within the $(D Variant), but this also implies some + * overhead. $(D Variant) can accommodate all primitive types and + * all user-defined types.)) * - * Both $(D_PARAM Algebraic) and $(D_PARAM Variant) share $(D_PARAM + * Both $(D Algebraic) and $(D Variant) share $(D * VariantN)'s interface. (See their respective documentations below.) * - * $(D_PARAM VariantN) is a discriminated union type parameterized - * with the largest size of the types stored ($(D_PARAM maxDataSize)) - * and with the list of allowed types ($(D_PARAM AllowedTypes)). If - * the list is empty, then any type up of size up to $(D_PARAM + * $(D VariantN) is a discriminated union type parameterized + * with the largest size of the types stored ($(D maxDataSize)) + * and with the list of allowed types ($(D AllowedTypes)). If + * the list is empty, then any type up of size up to $(D * maxDataSize) (rounded up for alignment) can be stored in a - * $(D_PARAM VariantN) object. + * $(D VariantN) object without being boxed (types larger + * than this will be boxed). * */ - -struct VariantN(size_t maxDataSize, AllowedTypesX...) +struct VariantN(size_t maxDataSize, AllowedTypesParam...) { - alias AllowedTypes = This2Variant!(VariantN, AllowedTypesX); + /** + The list of allowed types. If empty, any type is allowed. + */ + alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam); private: // Compute the largest practical size from maxDataSize @@ -168,11 +140,9 @@ private: } enum size = SizeChecker.sizeof - (int function()).sizeof; - /** Tells whether a type $(D_PARAM T) is statically allowed for - * storage inside a $(D_PARAM VariantN) object by looking - * $(D_PARAM T) up in $(D_PARAM AllowedTypes). If $(D_PARAM - * AllowedTypes) is empty, all types of size up to $(D_PARAM - * maxSize) are allowed. + /** Tells whether a type $(D T) is statically _allowed for + * storage inside a $(D VariantN) object by looking + * $(D T) up in $(D AllowedTypes). */ public template allowed(T) { @@ -243,6 +213,7 @@ private: // Handler for all of a type's operations static ptrdiff_t handler(A)(OpID selector, ubyte[size]* pStore, void* parm) { + import std.conv : to; static A* getPtr(void* untyped) { if (untyped) @@ -295,61 +266,56 @@ private: static bool tryPutting(A* src, TypeInfo targetType, void* target) { alias UA = Unqual!A; - alias MutaTypes = TypeTuple!(UA, ImplicitConversionTargets!UA); + alias MutaTypes = AliasSeq!(UA, ImplicitConversionTargets!UA); alias ConstTypes = staticMap!(ConstOf, MutaTypes); alias SharedTypes = staticMap!(SharedOf, MutaTypes); alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes); alias ImmuTypes = staticMap!(ImmutableOf, MutaTypes); static if (is(A == immutable)) - alias AllTypes = TypeTuple!(ImmuTypes, ConstTypes, SharedConstTypes); + alias AllTypes = AliasSeq!(ImmuTypes, ConstTypes, SharedConstTypes); else static if (is(A == shared)) { static if (is(A == const)) alias AllTypes = SharedConstTypes; else - alias AllTypes = TypeTuple!(SharedTypes, SharedConstTypes); + alias AllTypes = AliasSeq!(SharedTypes, SharedConstTypes); } else { static if (is(A == const)) alias AllTypes = ConstTypes; else - alias AllTypes = TypeTuple!(MutaTypes, ConstTypes); + alias AllTypes = AliasSeq!(MutaTypes, ConstTypes); } foreach (T ; AllTypes) { if (targetType != typeid(T)) - { continue; - } - static if (is(typeof(*cast(T*) target = *src))) + static if (is(typeof(*cast(T*) target = *src)) || + is(T == const(U), U) || + is(T == shared(U), U) || + is(T == shared const(U), U) || + is(T == immutable(U), U)) { + import std.conv : emplaceRef; + auto zat = cast(T*) target; if (src) { - assert(target, "target must be non-null"); - *zat = *src; - } - } - else static if (is(T == const(U), U) || - is(T == shared(U), U) || - is(T == shared const(U), U) || - is(T == immutable(U), U)) - { - auto zat = cast(U*) target; - if (src) - { - assert(target, "target must be non-null"); - *zat = *(cast(UA*) (src)); + static if (T.sizeof > 0) + assert(target, "target must be non-null"); + + emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src); } } else { - // type is not assignable - if (src) assert(false, A.stringof); + // type T is not constructible from A + if (src) + assert(false, A.stringof); } return true; } @@ -375,7 +341,8 @@ private: target.fptr = &handler!(A); break; case OpID.get: - return !tryPutting(zis, *cast(TypeInfo*) parm, parm); + auto t = * cast(Tuple!(TypeInfo, void*)*) parm; + return !tryPutting(zis, t[0], t[1]); case OpID.testConversion: return !tryPutting(null, *cast(TypeInfo*) parm, null); case OpID.compare: @@ -388,7 +355,8 @@ private: // cool! Same type! auto rhsPA = getPtr(&rhsP.store); return compare(rhsPA, zis, selector); - } else if (rhsType == typeid(void)) + } + else if (rhsType == typeid(void)) { // No support for ordering comparisons with // uninitialized vars @@ -408,8 +376,8 @@ private: return temp.opEquals(*rhsP) ? 0 : 1; } // Does rhs convert to zis? - *cast(TypeInfo*) &temp.store = typeid(A); - if (rhsP.fptr(OpID.get, &rhsP.store, &temp.store) == 0) + auto t = tuple(typeid(A), &temp.store); + if (rhsP.fptr(OpID.get, &rhsP.store, &t) == 0) { // cool! Now temp has rhs in my type! auto rhsPA = getPtr(&temp.store); @@ -513,12 +481,16 @@ private: case OpID.apply: static if (!isFunctionPointer!A && !isDelegate!A) { + import std.conv : text; + import std.exception : enforce; enforce(0, text("Cannot apply `()' to a value of type `", A.stringof, "'.")); } else { - alias ParamTypes = ParameterTypeTuple!A; + import std.conv : text; + import std.exception : enforce; + alias ParamTypes = Parameters!A; auto p = cast(Variant*) parm; auto argCount = p.get!size_t; // To assign the tuple we need to use the unqualified version, @@ -533,11 +505,11 @@ private: auto variantArgs = p[1 .. argCount + 1]; foreach (i, T; ParamTypes) { - t[i] = cast()variantArgs[i].get!T; + t[i] = cast() variantArgs[i].get!T; } auto args = cast(Tuple!(ParamTypes))t; - static if(is(ReturnType!A == void)) + static if (is(ReturnType!A == void)) { (*zis)(args.expand); *p = Variant.init; // void returns uninitialized Variant. @@ -568,8 +540,10 @@ private: return 0; } + enum doUnittest = is(VariantN == Variant); + public: - /** Constructs a $(D_PARAM VariantN) value given an argument of a + /** Constructs a $(D VariantN) value given an argument of a * generic type. Statically rejects disallowed types. */ @@ -580,6 +554,13 @@ public: opAssign(value); } + /// Allows assignment from a subset algebraic type + this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value) + if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) + { + opAssign(value); + } + static if (!AllowedTypes.length || anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) { this(this) @@ -596,7 +577,7 @@ public: } } - /** Assigns a $(D_PARAM VariantN) from a generic + /** Assigns a $(D VariantN) from a generic * argument. Statically rejects disallowed types. */ VariantN opAssign(T)(T rhs) @@ -626,6 +607,7 @@ public: static if (T.sizeof <= size) { + import core.stdc.string : memcpy; // If T is a class we're only copying the reference, so it // should be safe to cast away shared so the memcpy will work. // @@ -643,7 +625,8 @@ public: } else { - static if (__traits(compiles, {new T(rhs);})) + import core.stdc.string : memcpy; + static if (__traits(compiles, {new T(T.init);})) { auto p = new T(rhs); } @@ -659,6 +642,18 @@ public: return this; } + // Allow assignment from another variant which is a subset of this one + VariantN opAssign(T : VariantN!(tsize, Types), size_t tsize, Types...)(T rhs) + if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) + { + // discover which type rhs is actually storing + foreach (V; T.AllowedTypes) + if (rhs.type == typeid(V)) + return this = rhs.get!V; + assert(0, T.AllowedTypes.stringof); + } + + Variant opCall(P...)(auto ref P params) { Variant[P.length + 1] pack; @@ -671,7 +666,7 @@ public: return pack[0]; } - /** Returns true if and only if the $(D_PARAM VariantN) object + /** Returns true if and only if the $(D VariantN) object * holds a valid value (has been initialized with, or assigned * from, a valid value). */ @@ -682,7 +677,8 @@ public: } /// - unittest + static if (doUnittest) + @system unittest { Variant a; assert(!a.hasValue); @@ -694,10 +690,10 @@ public: } /** - * If the $(D_PARAM VariantN) object holds a value of the - * $(I exact) type $(D_PARAM T), returns a pointer to that - * value. Otherwise, returns $(D_PARAM null). In cases - * where $(D_PARAM T) is statically disallowed, $(D_PARAM + * If the $(D VariantN) object holds a value of the + * $(I exact) type $(D T), returns a pointer to that + * value. Otherwise, returns $(D null). In cases + * where $(D T) is statically disallowed, $(D * peek) will not compile. */ @property inout(T)* peek(T)() inout @@ -714,7 +710,8 @@ public: } /// - unittest + static if (doUnittest) + @system unittest { Variant a = 5; auto b = a.peek!(int); @@ -724,7 +721,7 @@ public: } /** - * Returns the $(D_PARAM typeid) of the currently held value. + * Returns the $(D typeid) of the currently held value. */ @property TypeInfo type() const nothrow @trusted @@ -737,10 +734,10 @@ public: } /** - * Returns $(D_PARAM true) if and only if the $(D_PARAM VariantN) - * object holds an object implicitly convertible to type $(D_PARAM - * U). Implicit convertibility is defined as per - * $(LINK2 std_traits.html#ImplicitConversionTargets,ImplicitConversionTargets). + * Returns $(D true) if and only if the $(D VariantN) + * object holds an object implicitly convertible to type `T`. + * Implicit convertibility is defined as per + * $(REF_ALTTEXT ImplicitConversionTargets, ImplicitConversionTargets, std,traits). */ @property bool convertsTo(T)() const @@ -750,79 +747,56 @@ public: } /** - * Returns the value stored in the $(D_PARAM VariantN) object, - * implicitly converted to the requested type $(D_PARAM T), in - * fact $(D_PARAM DecayStaticToDynamicArray!(T)). If an implicit - * conversion is not possible, throws a $(D_PARAM - * VariantException). + Returns the value stored in the `VariantN` object, either by specifying the + needed type or the index in the list of allowed types. The latter overload + only applies to bounded variants (e.g. $(LREF Algebraic)). + + Params: + T = The requested type. The currently stored value must implicitly convert + to the requested type, in fact `DecayStaticToDynamicArray!T`. If an + implicit conversion is not possible, throws a `VariantException`. + index = The index of the type among `AllowedTypesParam`, zero-based. */ - - @property T get(T)() if (!is(T == const)) + @property inout(T) get(T)() inout { - auto p = *cast(T**) &store; - - /* handler(OpID.get, ) expects the TypeInfo for T in the same buffer it - * writes the result to afterwards. Because T might have a non-trivial - * destructor, postblit or invariant, we cannot use a union. - */ - struct Buf - { - T result; - // Make sure Buf.sizeof is big enough to store a TypeInfo in - void[T.sizeof < TypeInfo.sizeof ? TypeInfo.sizeof - T.sizeof : 0] init = void; - } - TypeInfo info = typeid(T); - Buf buf; - memcpy(&buf, &info, info.sizeof); + inout(T) result = void; + static if (is(T == shared)) + alias R = shared Unqual!T; + else + alias R = Unqual!T; + auto buf = tuple(typeid(T), cast(R*)&result); - if (fptr(OpID.get, &store, &buf)) + if (fptr(OpID.get, cast(ubyte[size]*) &store, &buf)) { throw new VariantException(type, typeid(T)); } - return buf.result; + return result; } - @property T get(T)() const if (is(T == const)) + /// Ditto + @property auto get(uint index)() inout + if (index < AllowedTypes.length) { - auto p = *cast(T**) &store; - - /* handler(OpID.get, ) expects the TypeInfo for T in the same buffer it - * writes the result to afterwards. Because T might have a non-trivial - * destructor, postblit or invariant, we cannot use a union. - */ - struct Buf + foreach (i, T; AllowedTypes) { - static if (is(T == shared)) - shared(Unqual!T) result; - else - Unqual!T result; - - // Make sure Buf.sizeof is big enough to store a TypeInfo in - void[T.sizeof < TypeInfo.sizeof ? TypeInfo.sizeof - T.sizeof : 0] init = void; + static if (index == i) return get!T; } - TypeInfo info = typeid(T); - Buf buf; - memcpy(&buf, &info, info.sizeof); - - if (fptr(OpID.get, cast(ubyte[size]*) &store, &buf)) - { - throw new VariantException(type, typeid(T)); - } - return buf.result; + assert(0); } /** - * Returns the value stored in the $(D_PARAM VariantN) object, - * explicitly converted (coerced) to the requested type $(D_PARAM - * T). If $(D_PARAM T) is a string type, the value is formatted as - * a string. If the $(D_PARAM VariantN) object is a string, a - * parse of the string to type $(D_PARAM T) is attempted. If a - * conversion is not possible, throws a $(D_PARAM + * Returns the value stored in the $(D VariantN) object, + * explicitly converted (coerced) to the requested type $(D + * T). If $(D T) is a string type, the value is formatted as + * a string. If the $(D VariantN) object is a string, a + * parse of the string to type $(D T) is attempted. If a + * conversion is not possible, throws a $(D * VariantException). */ @property T coerce(T)() { + import std.conv : to, text; static if (isNumeric!T || isBoolean!T) { if (convertsTo!real) @@ -845,6 +819,7 @@ public: } else { + import std.exception : enforce; enforce(false, text("Type ", type, " does not convert to ", typeid(T))); assert(0); @@ -882,6 +857,7 @@ public: // returns 1 if the two are equal bool opEquals(T)(auto ref T rhs) const + if (allowed!T || is(Unqual!T == VariantN)) { static if (is(Unqual!T == VariantN)) alias temp = rhs; @@ -894,16 +870,17 @@ public: // workaround for bug 10567 fix int opCmp(ref const VariantN rhs) const { - return (cast()this).opCmp!(VariantN)(cast()rhs); + return (cast() this).opCmp!(VariantN)(cast() rhs); } /** * Ordering comparison used by the "<", "<=", ">", and ">=" * operators. In case comparison is not sensible between the held - * value and $(D_PARAM rhs), an exception is thrown. + * value and $(D rhs), an exception is thrown. */ int opCmp(T)(T rhs) + if (allowed!T) // includes T == VariantN { static if (is(T == VariantN)) alias temp = rhs; @@ -1003,8 +980,8 @@ public: } /** - * Arithmetic between $(D_PARAM VariantN) objects and numeric - * values. All arithmetic operations return a $(D_PARAM VariantN) + * Arithmetic between $(D VariantN) objects and numeric + * values. All arithmetic operations return a $(D VariantN) * object typed depending on the types of both values * involved. The conversion rules mimic D's built-in rules for * arithmetic conversions. @@ -1113,59 +1090,57 @@ public: } /** - * Array and associative array operations. If a $(D_PARAM + * Array and associative array operations. If a $(D * VariantN) contains an (associative) array, it can be indexed * into. Otherwise, an exception is thrown. */ - Variant opIndex(K)(K i) + inout(Variant) opIndex(K)(K i) inout { auto result = Variant(i); - fptr(OpID.index, &store, &result) == 0 || assert(false); + fptr(OpID.index, cast(ubyte[size]*) &store, &result) == 0 || assert(false); return result; } /// - unittest - { - auto a = Variant(new int[10]); - a[5] = 42; - assert(a[5] == 42); - int[int] hash = [ 42:24 ]; - a = hash; - assert(a[42] == 24); - } - - /** Caveat: - Due to limitations in current language, read-modify-write - operations $(D_PARAM op=) will not work properly: - */ - unittest + static if (doUnittest) + @system unittest { Variant a = new int[10]; a[5] = 42; + assert(a[5] == 42); a[5] += 8; - //assert(a[5] == 50); // will fail, a[5] is still 42 - } + assert(a[5] == 50); - unittest - { int[int] hash = [ 42:24 ]; - Variant v = hash; - assert(v[42] == 24); - v[42] = 5; - assert(v[42] == 5); + a = hash; + assert(a[42] == 24); + a[42] /= 2; + assert(a[42] == 12); } /// ditto Variant opIndexAssign(T, N)(T value, N i) { + static if (AllowedTypes.length && !isInstanceOf!(.VariantN, T)) + { + enum canAssign(U) = __traits(compiles, (U u){ u[i] = value; }); + static assert(anySatisfy!(canAssign, AllowedTypes), + "Cannot assign " ~ T.stringof ~ " to " ~ VariantN.stringof ~ + " indexed with " ~ N.stringof); + } Variant[2] args = [ Variant(value), Variant(i) ]; fptr(OpID.indexAssign, &store, &args) == 0 || assert(false); return args[0]; } - /** If the $(D_PARAM VariantN) contains an (associative) array, - * returns the length of that array. Otherwise, throws an + /// ditto + Variant opIndexOpAssign(string op, T, N)(T value, N i) + { + return opIndexAssign(mixin(`opIndex(i)` ~ op ~ `value`), i); + } + + /** If the $(D VariantN) contains an (associative) array, + * returns the _length of that array. Otherwise, throws an * exception. */ @property size_t length() @@ -1179,7 +1154,7 @@ public: */ int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) { - alias A = ParameterTypeTuple!(Delegate)[0]; + alias A = Parameters!(Delegate)[0]; if (type == typeid(A[])) { auto arr = get!(A[]); @@ -1202,6 +1177,8 @@ public: } else { + import std.conv : text; + import std.exception : enforce; enforce(false, text("Variant type ", type, " not iterable with values of type ", A.stringof)); @@ -1210,8 +1187,9 @@ public: } } -unittest +@system unittest { + import std.conv : to; Variant v; int foo() { return 42; } v = &foo; @@ -1222,8 +1200,17 @@ unittest assert(v("43") == 43); } +@system unittest +{ + int[int] hash = [ 42:24 ]; + Variant v = hash; + assert(v[42] == 24); + v[42] = 5; + assert(v[42] == 5); +} + // opIndex with static arrays, issue 12771 -unittest +@system unittest { int[4] elements = [0, 1, 2, 3]; Variant v = elements; @@ -1235,8 +1222,26 @@ unittest assert(v != elements); } +@system unittest +{ + import std.exception : assertThrown; + Algebraic!(int[]) v = [2, 2]; + + assert(v == [2, 2]); + v[0] = 1; + assert(v[0] == 1); + assert(v != [2, 2]); + + // opIndexAssign from Variant + v[1] = v[0]; + assert(v[1] == 1); + + static assert(!__traits(compiles, (v[1] = null))); + assertThrown!VariantException(v[1] = Variant(null)); +} + //Issue# 8195 -unittest +@system unittest { struct S { @@ -1248,7 +1253,7 @@ unittest } static assert(S.sizeof >= Variant.sizeof); - alias Types = TypeTuple!(string, int, S); + alias Types = AliasSeq!(string, int, S); alias MyVariant = VariantN!(maxSize!Types, Types); auto v = MyVariant(S.init); @@ -1256,7 +1261,7 @@ unittest } // Issue #10961 -unittest +@system unittest { // Primarily test that we can assign a void[] to a Variant. void[] elements = cast(void[])[1, 2, 3]; @@ -1266,7 +1271,7 @@ unittest } // Issue #13352 -unittest +@system unittest { alias TP = Algebraic!(long); auto a = TP(1L); @@ -1282,7 +1287,7 @@ unittest } // Issue #13354 -unittest +@system unittest { alias A = Algebraic!(string[]); A a = ["a", "b"]; @@ -1300,13 +1305,14 @@ unittest } // Issue #14198 -unittest +@system unittest { Variant a = true; + assert(a.type == typeid(bool)); } // Issue #14233 -unittest +@system unittest { alias Atom = Algebraic!(string, This[]); @@ -1315,40 +1321,72 @@ unittest } pure nothrow @nogc -unittest +@system unittest { Algebraic!(int, double) a; a = 100; a = 1.0; } +// Issue 14457 +@system unittest +{ + alias A = Algebraic!(int, float, double); + alias B = Algebraic!(int, float); + + A a = 1; + B b = 6f; + a = b; + + assert(a.type == typeid(float)); + assert(a.get!float == 6f); +} + +// Issue 14585 +@system unittest +{ + static struct S + { + int x = 42; + ~this() {assert(x == 42);} + } + Variant(S()).get!S; +} + +// Issue 14586 +@system unittest +{ + const Variant v = new immutable Object; + v.get!(immutable Object); +} + +@system unittest +{ + static struct S + { + T opCast(T)() {assert(false);} + } + Variant v = S(); + v.get!S; +} + /** - * Algebraic data type restricted to a closed set of possible - * types. It's an alias for a $(D_PARAM VariantN) with an - * appropriately-constructed maximum size. $(D_PARAM Algebraic) is - * useful when it is desirable to restrict what a discriminated type - * could hold to the end of defining simpler and more efficient - * manipulation. - * - * Future additions to $(D_PARAM Algebraic) will allow compile-time - * checking that all possible types are handled by user code, - * eliminating a large class of errors. - * - * Bugs: - * - * Currently, $(D_PARAM Algebraic) does not allow recursive data - * types. They will be allowed in a future iteration of the - * implementation. - */ +_Algebraic data type restricted to a closed set of possible +types. It's an alias for $(LREF VariantN) with an +appropriately-constructed maximum size. `Algebraic` is +useful when it is desirable to restrict what a discriminated type +could hold to the end of defining simpler and more efficient +manipulation. +*/ template Algebraic(T...) { - alias Algebraic = VariantN!(maxSize!(T), T); + alias Algebraic = VariantN!(maxSize!T, T); } /// -unittest +@system unittest { auto v = Algebraic!(int, double, string)(5); assert(v.peek!(int)); @@ -1359,21 +1397,53 @@ unittest } /** -$(D_PARAM Variant) is an alias for $(D_PARAM VariantN) instantiated -with the largest of $(D_PARAM creal), $(D_PARAM char[]), and $(D_PARAM -void delegate()). This ensures that $(D_PARAM Variant) is large enough -to hold all of D's predefined types, including all numeric types, +$(H4 Self-Referential Types) + +A useful and popular use of algebraic data structures is for defining $(LUCKY +self-referential data structures), i.e. structures that embed references to +values of their own type within. + +This is achieved with `Algebraic` by using `This` as a placeholder whenever a +reference to the type being defined is needed. The `Algebraic` instantiation +will perform $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial, +alpha renaming) on its constituent types, replacing `This` +with the self-referenced type. The structure of the type involving `This` may +be arbitrarily complex. +*/ +@system unittest +{ + import std.typecons : Tuple, tuple; + + // A tree is either a leaf or a branch of two other trees + alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*)); + Tree!int tree = tuple(new Tree!int(42), new Tree!int(43)); + Tree!int* right = tree.get!1[1]; + assert(*right == 43); + + // An object is a double, a string, or a hash of objects + alias Obj = Algebraic!(double, string, This[string]); + Obj obj = "hello"; + assert(obj.get!1 == "hello"); + obj = 42.0; + assert(obj.get!0 == 42); + obj = ["customer": Obj("John"), "paid": Obj(23.95)]; + assert(obj.get!2["customer"] == "John"); +} + +/** +Alias for $(LREF VariantN) instantiated with the largest size of `creal`, +`char[]`, and `void delegate()`. This ensures that `Variant` is large enough +to hold all of D's predefined types unboxed, including all numeric types, pointers, delegates, and class references. You may want to use -$(D_PARAM VariantN) directly with a different maximum size either for -storing larger types, or for saving memory. +$(D VariantN) directly with a different maximum size either for +storing larger types unboxed, or for saving memory. */ - alias Variant = VariantN!(maxSize!(creal, char[], void delegate())); /** - * Returns an array of variants constructed from $(D_PARAM args). + * Returns an array of variants constructed from $(D args). * - * This is by design. During construction the $(D_PARAM Variant) needs + * This is by design. During construction the $(D Variant) needs * static type information about the type being held, so as to store a * pointer to function for fast retrieval. */ @@ -1388,7 +1458,7 @@ Variant[] variantArray(T...)(T args) } /// -unittest +@system unittest { auto a = variantArray(1, 3.14, "Hi!"); assert(a[1] == 3.14); @@ -1396,35 +1466,13 @@ unittest assert(b[1] == 3.14); } -/** Code that needs functionality similar to the $(D_PARAM boxArray) -function in the $(D_PARAM std.boxer) module can achieve it like this: -*/ -unittest -{ - /* old - Box[] fun(...) - { - // ... - return boxArray(_arguments, _argptr); - } - */ - // new - Variant[] fun(T...)(T args) - { - // ... - return variantArray(args); - } -} - -/** - /** * Thrown in three cases: * - * $(OL $(LI An uninitialized Variant is used in any way except - * assignment and $(D_PARAM hasValue);) $(LI A $(D_PARAM get) or - * $(D_PARAM coerce) is attempted with an incompatible target type;) - * $(LI A comparison between $(D_PARAM Variant) objects of + * $(OL $(LI An uninitialized `Variant` is used in any way except + * assignment and $(D hasValue);) $(LI A $(D get) or + * $(D coerce) is attempted with an incompatible target type;) + * $(LI A comparison between $(D Variant) objects of * incompatible types is attempted.)) * */ @@ -1450,34 +1498,35 @@ static class VariantException : Exception } } -unittest +@system unittest { alias W1 = This2Variant!(char, int, This[int]); - alias W2 = TypeTuple!(int, char[int]); + alias W2 = AliasSeq!(int, char[int]); static assert(is(W1 == W2)); alias var_t = Algebraic!(void, string); var_t foo = "quux"; } -unittest +@system unittest { - // @@@BUG@@@ - // alias A = Algebraic!(real, This[], This[int], This[This]); - // A v1, v2, v3; - // v2 = 5.0L; - // v3 = 42.0L; - // //v1 = [ v2 ][]; - // auto v = v1.peek!(A[]); - // //writeln(v[0]); - // v1 = [ 9 : v3 ]; - // //writeln(v1); - // v1 = [ v3 : v3 ]; - // //writeln(v1); + alias A = Algebraic!(real, This[], This[int], This[This]); + A v1, v2, v3; + v2 = 5.0L; + v3 = 42.0L; + //v1 = [ v2 ][]; + auto v = v1.peek!(A[]); + //writeln(v[0]); + v1 = [ 9 : v3 ]; + //writeln(v1); + v1 = [ v3 : v3 ]; + //writeln(v1); } -unittest +@system unittest { + import std.conv : ConvException; + import std.exception : assertThrown, collectException; // try it with an oddly small size VariantN!(1) test; assert(test.size > 1); @@ -1571,7 +1620,7 @@ unittest // tests adapted from // http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601 -unittest +@system unittest { Variant v; @@ -1583,8 +1632,7 @@ unittest assert( v.get!(long) == 42L ); assert( v.get!(ulong) == 42uL ); - // should be string... @@@BUG IN COMPILER - v = "Hello, World!"c; + v = "Hello, World!"; assert( v.peek!(string) ); assert( v.get!(string) == "Hello, World!" ); @@ -1598,10 +1646,9 @@ unittest assert( v.get!(int[4]) == [1,2,3,4] ); { - // @@@BUG@@@: array literals should have type T[], not T[5] (I guess) - // v = [1,2,3,4,5]; - // assert( v.peek!(int[]) ); - // assert( v.get!(int[]) == [1,2,3,4,5] ); + v = [1,2,3,4,5]; + assert( v.peek!(int[]) ); + assert( v.get!(int[]) == [1,2,3,4,5] ); } v = 3.1413; @@ -1680,9 +1727,7 @@ unittest assert( hash[v2] == 1 ); assert( hash[v3] == 2 ); } - /+ - // @@@BUG@@@ - // dmd: mtype.c:3886: StructDeclaration* TypeAArray::getImpl(): Assertion `impl' failed. + { int[char[]] hash; hash["a"] = 1; @@ -1694,10 +1739,22 @@ unittest assert( vhash.get!(int[char[]])["b"] == 2 ); assert( vhash.get!(int[char[]])["c"] == 3 ); } - +/ } -unittest +@system unittest +{ + // check comparisons incompatible with AllowedTypes + Algebraic!int v = 2; + + assert(v == 2); + assert(v < 3); + static assert(!__traits(compiles, {v == long.max;})); + static assert(!__traits(compiles, {v == null;})); + static assert(!__traits(compiles, {v < long.max;})); + static assert(!__traits(compiles, {v > null;})); +} + +@system unittest { // bug 1558 Variant va=1; @@ -1706,7 +1763,7 @@ unittest assert((va-vb).get!(int) == 3); } -unittest +@system unittest { Variant a; a=5; @@ -1717,14 +1774,14 @@ unittest assert(c[3] == "hello"); } -unittest +@system unittest { Variant v = 5; - assert (!__traits(compiles, v.coerce!(bool delegate()))); + assert(!__traits(compiles, v.coerce!(bool delegate()))); } -unittest +@system unittest { struct Huge { real a, b, c, d, e, f, g; @@ -1737,7 +1794,7 @@ unittest assert(v.get!(Huge).e == 42); } -unittest +@system unittest { const x = Variant(42); auto y1 = x.get!(const int); @@ -1746,7 +1803,7 @@ unittest } // test iteration -unittest +@system unittest { auto v = Variant([ 1, 2, 3, 4 ][]); auto j = 0; @@ -1758,14 +1815,14 @@ unittest } // test convertibility -unittest +@system unittest { auto v = Variant("abc".dup); assert(v.convertsTo!(char[])); } // http://d.puremagic.com/issues/show_bug.cgi?id=5424 -unittest +@system unittest { interface A { void func1(); @@ -1780,7 +1837,7 @@ unittest Variant b = Variant(a); } -unittest +@system unittest { // bug 7070 Variant v; @@ -1788,7 +1845,7 @@ unittest } // Class and interface opEquals, issue 12157 -unittest +@system unittest { class Foo { } @@ -1806,7 +1863,7 @@ unittest } // Const parameters with opCall, issue 11361. -unittest +@system unittest { static string t1(string c) { return c ~ "a"; @@ -1817,6 +1874,7 @@ unittest } static char[] t3(int p) { + import std.conv : text; return p.text.dup; } @@ -1836,13 +1894,13 @@ unittest } // issue 12071 -unittest +@system unittest { static struct Structure { int data; } - alias VariantTest = Algebraic!(Structure delegate()); + alias VariantTest = Algebraic!(Structure delegate() pure nothrow @nogc @safe); bool called = false; - Structure example() + Structure example() pure nothrow @nogc @safe { called = true; return Structure.init; @@ -1853,8 +1911,9 @@ unittest } // Ordering comparisons of incompatible types, e.g. issue 7990. -unittest +@system unittest { + import std.exception : assertThrown; assertThrown!VariantException(Variant(3) < "a"); assertThrown!VariantException("a" < Variant(3)); assertThrown!VariantException(Variant(3) < Variant("a")); @@ -1864,8 +1923,9 @@ unittest } // Handling of unordered types, e.g. issue 9043. -unittest +@system unittest { + import std.exception : assertThrown; static struct A { int a; } assert(Variant(A(3)) != A(4)); @@ -1876,7 +1936,7 @@ unittest } // Handling of empty types and arrays, e.g. issue 10958 -unittest +@system unittest { class EmptyClass { } struct EmptyStruct { } @@ -1907,7 +1967,7 @@ unittest } // Handling of void function pointers / delegates, e.g. issue 11360 -unittest +@system unittest { static void t1() { } Variant v = &t1; @@ -1919,7 +1979,7 @@ unittest } // Using peek for large structs, issue 8580 -unittest +@system unittest { struct TestStruct(bool pad) { @@ -1950,68 +2010,88 @@ unittest } /** - * Applies a delegate or function to the given Algebraic depending on the held type, + * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type, * ensuring that all types are handled by the visiting functions. * * The delegate or function having the currently held value as parameter is called - * with $(D_PARAM variant)'s current value. Visiting handlers are passed + * with $(D variant)'s current value. Visiting handlers are passed * in the template parameter list. - * It is statically ensured that all types of - * $(D_PARAM variant) are handled across all handlers. - * $(D_PARAM visit) allows delegates and static functions to be passed + * It is statically ensured that all held types of + * $(D variant) are handled across all handlers. + * $(D visit) allows delegates and static functions to be passed * as parameters. * + * If a function with an untyped parameter is specified, this function is called + * when the variant contains a type that does not match any other function. + * This can be used to apply the same function across multiple possible types. + * Exactly one generic function is allowed. + * * If a function without parameters is specified, this function is called - * when variant doesn't hold a value. Exactly one parameter-less function + * when `variant` doesn't hold a value. Exactly one parameter-less function * is allowed. * * Duplicate overloads matching the same type in one of the visitors are disallowed. * * Returns: The return type of visit is deduced from the visiting functions and must be * the same across all overloads. - * Throws: If no parameter-less, error function is specified: - * $(D_PARAM VariantException) if $(D_PARAM variant) doesn't hold a value. + * Throws: $(LREF VariantException) if `variant` doesn't hold a value and no + * parameter-less fallback function is specified. */ -template visit(Handler ...) - if (Handler.length > 0) +template visit(Handlers...) +if (Handlers.length > 0) { + /// auto visit(VariantType)(VariantType variant) if (isAlgebraic!VariantType) { - return visitImpl!(true, VariantType, Handler)(variant); + return visitImpl!(true, VariantType, Handlers)(variant); } } /// -unittest +@system unittest { Algebraic!(int, string) variant; variant = 10; - assert(variant.visit!((string s) => cast(int)s.length, + assert(variant.visit!((string s) => cast(int) s.length, (int i) => i)() == 10); variant = "string"; assert(variant.visit!((int i) => i, - (string s) => cast(int)s.length)() + (string s) => cast(int) s.length)() == 6); // Error function usage Algebraic!(int, string) emptyVar; - auto rslt = emptyVar.visit!((string s) => cast(int)s.length, + auto rslt = emptyVar.visit!((string s) => cast(int) s.length, (int i) => i, () => -1)(); assert(rslt == -1); + + // Generic function usage + Algebraic!(int, float, real) number = 2; + assert(number.visit!(x => x += 1) == 3); + + // Generic function for int/float with separate behavior for string + Algebraic!(int, float, string) something = 2; + assert(something.visit!((string s) => s.length, x => x) == 2); // generic + something = "asdf"; + assert(something.visit!((string s) => s.length, x => x) == 4); // string + + // Generic handler and empty handler + Algebraic!(int, float, real) empty2; + assert(empty2.visit!(x => x + 1, () => -1) == -1); } -unittest +@system unittest { Algebraic!(size_t, string) variant; // not all handled check static assert(!__traits(compiles, variant.visit!((size_t i){ })() )); - variant = cast(size_t)10; + variant = cast(size_t) 10; auto which = 0; variant.visit!( (string s) => which = 1, (size_t i) => which = 0 @@ -2039,15 +2119,15 @@ unittest Algebraic!(int, float, string) variant2 = 5.0f; // Shouldn' t compile as float not handled by visitor. static assert(!__traits(compiles, variant2.visit!( - (int) {}, - (string) {})())); + (int _) {}, + (string _) {})())); Algebraic!(size_t, string, float) variant3; variant3 = 10.0f; auto floatVisited = false; assert(variant3.visit!( - (float f) { floatVisited = true; return cast(size_t)f; }, + (float f) { floatVisited = true; return cast(size_t) f; }, func, (size_t i) { return i; } )() == 10); @@ -2055,41 +2135,64 @@ unittest Algebraic!(float, string) variant4; - assert(variant4.visit!(func, (float f) => cast(size_t)f, () => size_t.max)() == size_t.max); + assert(variant4.visit!(func, (float f) => cast(size_t) f, () => size_t.max)() == size_t.max); // double error func check static assert(!__traits(compiles, - visit!(() => size_t.max, func, (float f) => cast(size_t)f, () => size_t.max)(variant4)) + visit!(() => size_t.max, func, (float f) => cast(size_t) f, () => size_t.max)(variant4)) ); } +// disallow providing multiple generic handlers to visit +// disallow a generic handler that does not apply to all types +@system unittest +{ + Algebraic!(int, float) number = 2; + // ok, x + 1 valid for int and float + static assert( __traits(compiles, number.visit!(x => x + 1))); + // bad, two generic handlers + static assert(!__traits(compiles, number.visit!(x => x + 1, x => x + 2))); + // bad, x ~ "a" does not apply to int or float + static assert(!__traits(compiles, number.visit!(x => x ~ "a"))); + // bad, x ~ "a" does not apply to int or float + static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a"))); + + Algebraic!(int, string) maybenumber = 2; + // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic + static assert( __traits(compiles, number.visit!((string x) => x ~ "a", x => x + 1))); + // bad, x ~ "a" valid for string but not int + static assert(!__traits(compiles, number.visit!(x => x ~ "a"))); + // bad, two generics, each only applies in one case + static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a"))); +} + /** - * Behaves as $(D_PARAM visit) but doesn't enforce that all types are handled + * Behaves as $(LREF visit) but doesn't enforce that all types are handled * by the visiting functions. * * If a parameter-less function is specified it is called when - * either $(D_PARAM variant) doesn't hold a value or holds a type + * either $(D variant) doesn't hold a value or holds a type * which isn't handled by the visiting functions. * * Returns: The return type of tryVisit is deduced from the visiting functions and must be * the same across all overloads. - * Throws: If no parameter-less, error function is specified: $(D_PARAM VariantException) if - * $(D_PARAM variant) doesn't hold a value or - * if $(D_PARAM variant) holds a value which isn't handled by the visiting - * functions. + * Throws: $(LREF VariantException) if `variant` doesn't hold a value or + * `variant` holds a value which isn't handled by the visiting functions, + * when no parameter-less fallback function is specified. */ -template tryVisit(Handler ...) - if (Handler.length > 0) +template tryVisit(Handlers...) +if (Handlers.length > 0) { + /// auto tryVisit(VariantType)(VariantType variant) if (isAlgebraic!VariantType) { - return visitImpl!(false, VariantType, Handler)(variant); + return visitImpl!(false, VariantType, Handlers)(variant); } } /// -unittest +@system unittest { Algebraic!(int, string) variant; @@ -2105,8 +2208,9 @@ unittest assert(which == -100); } -unittest +@system unittest { + import std.exception : assertThrown; Algebraic!(int, string) variant; variant = 10; @@ -2132,12 +2236,12 @@ unittest private template isAlgebraic(Type) { static if (is(Type _ == VariantN!T, T...)) - enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesX + enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam else enum isAlgebraic = false; } -unittest +@system unittest { static assert(!isAlgebraic!(Variant)); static assert( isAlgebraic!(Algebraic!(string))); @@ -2145,17 +2249,17 @@ unittest } private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant) - if (isAlgebraic!VariantType && Handler.length > 0) +if (isAlgebraic!VariantType && Handler.length > 0) { alias AllowedTypes = VariantType.AllowedTypes; /** - * Returns: Struct where $(D_PARAM indices) is an array which + * Returns: Struct where $(D indices) is an array which * contains at the n-th position the index in Handler which takes the * n-th type of AllowedTypes. If an Handler doesn't match an * AllowedType, -1 is set. If a function in the delegates doesn't - * have parameters, the field $(D_PARAM exceptionFuncIdx) is set; + * have parameters, the field $(D exceptionFuncIdx) is set; * otherwise it's -1. */ auto visitGetOverloadMap() @@ -2163,19 +2267,20 @@ private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant struct Result { int[AllowedTypes.length] indices; int exceptionFuncIdx = -1; + int generalFuncIdx = -1; } Result result; - foreach(tidx, T; AllowedTypes) + foreach (tidx, T; AllowedTypes) { bool added = false; - foreach(dgidx, dg; Handler) + foreach (dgidx, dg; Handler) { // Handle normal function objects static if (isSomeFunction!dg) { - alias Params = ParameterTypeTuple!dg; + alias Params = Parameters!dg; static if (Params.length == 0) { // Just check exception functions in the first @@ -2189,7 +2294,7 @@ private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant result.exceptionFuncIdx = dgidx; } } - else if (is(Unqual!(Params[0]) == T)) + else static if (is(Params[0] == T) || is(Unqual!(Params[0]) == T)) { if (added) assert(false, "duplicate overload specified for type '" ~ T.stringof ~ "'"); @@ -2198,6 +2303,13 @@ private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant result.indices[tidx] = dgidx; } } + else static if (isSomeFunction!(dg!T)) + { + assert(result.generalFuncIdx == -1 || + result.generalFuncIdx == dgidx, + "Only one generic visitor function is allowed"); + result.generalFuncIdx = dgidx; + } // Handle composite visitors with opCall overloads else { @@ -2225,23 +2337,26 @@ private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant throw new VariantException("variant must hold a value before being visited."); } - foreach(idx, T; AllowedTypes) + foreach (idx, T; AllowedTypes) { - if (T* ptr = variant.peek!T) + if (auto ptr = variant.peek!T) { enum dgIdx = HandlerOverloadMap.indices[idx]; static if (dgIdx == -1) { - static if (Strict) + static if (HandlerOverloadMap.generalFuncIdx >= 0) + return Handler[HandlerOverloadMap.generalFuncIdx](*ptr); + else static if (Strict) static assert(false, "overload for type '" ~ T.stringof ~ "' hasn't been specified"); + else static if (HandlerOverloadMap.exceptionFuncIdx != -1) + return Handler[HandlerOverloadMap.exceptionFuncIdx](); else - { - static if (HandlerOverloadMap.exceptionFuncIdx != -1) - return Handler[ HandlerOverloadMap.exceptionFuncIdx ](); - else - throw new VariantException("variant holds value of type '" ~ T.stringof ~ "' but no visitor has been provided"); - } + throw new VariantException( + "variant holds value of type '" + ~ T.stringof ~ + "' but no visitor has been provided" + ); } else { @@ -2253,7 +2368,35 @@ private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant assert(false); } -unittest +@system unittest +{ + // validate that visit can be called with a const type + struct Foo { int depth; } + struct Bar { int depth; } + alias FooBar = Algebraic!(Foo, Bar); + + int depth(in FooBar fb) { + return fb.visit!((Foo foo) => foo.depth, + (Bar bar) => bar.depth); + } + + FooBar fb = Foo(3); + assert(depth(fb) == 3); +} + +@system unittest +{ + // https://issues.dlang.org/show_bug.cgi?id=16383 + class Foo {this() immutable {}} + alias V = Algebraic!(immutable Foo); + + auto x = V(new immutable Foo).visit!( + (immutable(Foo) _) => 3 + ); + assert(x == 3); +} + +@system unittest { // http://d.puremagic.com/issues/show_bug.cgi?id=5310 const Variant a; @@ -2263,7 +2406,13 @@ unittest assert(b == a); } -unittest +@system unittest +{ + const Variant a = [2]; + assert(a[0] == 2); +} + +@system unittest { // http://d.puremagic.com/issues/show_bug.cgi?id=10017 static struct S @@ -2276,31 +2425,32 @@ unittest v2 = v1; // AssertError: target must be non-null assert(v1 == v2); } -unittest +@system unittest { + import std.exception : assertThrown; // http://d.puremagic.com/issues/show_bug.cgi?id=7069 Variant v; int i = 10; v = i; - foreach (qual; TypeTuple!(MutableOf, ConstOf)) + foreach (qual; AliasSeq!(MutableOf, ConstOf)) { assert(v.get!(qual!int) == 10); assert(v.get!(qual!float) == 10.0f); } - foreach (qual; TypeTuple!(ImmutableOf, SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!int)); } const(int) ci = 20; v = ci; - foreach (qual; TypeTuple!(ConstOf)) + foreach (qual; AliasSeq!(ConstOf)) { assert(v.get!(qual!int) == 20); assert(v.get!(qual!float) == 20.0f); } - foreach (qual; TypeTuple!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!int)); assertThrown!VariantException(v.get!(qual!float)); @@ -2308,12 +2458,12 @@ unittest immutable(int) ii = ci; v = ii; - foreach (qual; TypeTuple!(ImmutableOf, ConstOf, SharedConstOf)) + foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) { assert(v.get!(qual!int) == 20); assert(v.get!(qual!float) == 20.0f); } - foreach (qual; TypeTuple!(MutableOf, SharedOf)) + foreach (qual; AliasSeq!(MutableOf, SharedOf)) { assertThrown!VariantException(v.get!(qual!int)); assertThrown!VariantException(v.get!(qual!float)); @@ -2321,12 +2471,12 @@ unittest int[] ai = [1,2,3]; v = ai; - foreach (qual; TypeTuple!(MutableOf, ConstOf)) + foreach (qual; AliasSeq!(MutableOf, ConstOf)) { assert(v.get!(qual!(int[])) == [1,2,3]); assert(v.get!(qual!(int)[]) == [1,2,3]); } - foreach (qual; TypeTuple!(ImmutableOf, SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!(int[]))); assertThrown!VariantException(v.get!(qual!(int)[])); @@ -2334,12 +2484,12 @@ unittest const(int[]) cai = [4,5,6]; v = cai; - foreach (qual; TypeTuple!(ConstOf)) + foreach (qual; AliasSeq!(ConstOf)) { assert(v.get!(qual!(int[])) == [4,5,6]); assert(v.get!(qual!(int)[]) == [4,5,6]); } - foreach (qual; TypeTuple!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!(int[]))); assertThrown!VariantException(v.get!(qual!(int)[])); @@ -2353,7 +2503,7 @@ unittest assert(v.get!(const(int)[]) == [7,8,9]); //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error - foreach (qual; TypeTuple!(MutableOf)) + foreach (qual; AliasSeq!(MutableOf)) { assertThrown!VariantException(v.get!(qual!(int[]))); assertThrown!VariantException(v.get!(qual!(int)[])); @@ -2363,13 +2513,13 @@ unittest class B : A {} B b = new B(); v = b; - foreach (qual; TypeTuple!(MutableOf, ConstOf)) + foreach (qual; AliasSeq!(MutableOf, ConstOf)) { assert(v.get!(qual!B) is b); assert(v.get!(qual!A) is b); assert(v.get!(qual!Object) is b); } - foreach (qual; TypeTuple!(ImmutableOf, SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2378,13 +2528,13 @@ unittest const(B) cb = new B(); v = cb; - foreach (qual; TypeTuple!(ConstOf)) + foreach (qual; AliasSeq!(ConstOf)) { assert(v.get!(qual!B) is cb); assert(v.get!(qual!A) is cb); assert(v.get!(qual!Object) is cb); } - foreach (qual; TypeTuple!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2393,13 +2543,13 @@ unittest immutable(B) ib = new immutable(B)(); v = ib; - foreach (qual; TypeTuple!(ImmutableOf, ConstOf, SharedConstOf)) + foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) { assert(v.get!(qual!B) is ib); assert(v.get!(qual!A) is ib); assert(v.get!(qual!Object) is ib); } - foreach (qual; TypeTuple!(MutableOf, SharedOf)) + foreach (qual; AliasSeq!(MutableOf, SharedOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2408,13 +2558,13 @@ unittest shared(B) sb = new shared B(); v = sb; - foreach (qual; TypeTuple!(SharedOf, SharedConstOf)) + foreach (qual; AliasSeq!(SharedOf, SharedConstOf)) { assert(v.get!(qual!B) is sb); assert(v.get!(qual!A) is sb); assert(v.get!(qual!Object) is sb); } - foreach (qual; TypeTuple!(MutableOf, ImmutableOf, ConstOf)) + foreach (qual; AliasSeq!(MutableOf, ImmutableOf, ConstOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2423,13 +2573,13 @@ unittest shared(const(B)) scb = new shared const B(); v = scb; - foreach (qual; TypeTuple!(SharedConstOf)) + foreach (qual; AliasSeq!(SharedConstOf)) { assert(v.get!(qual!B) is scb); assert(v.get!(qual!A) is scb); assert(v.get!(qual!Object) is scb); } - foreach (qual; TypeTuple!(MutableOf, ConstOf, ImmutableOf, SharedOf)) + foreach (qual; AliasSeq!(MutableOf, ConstOf, ImmutableOf, SharedOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2437,7 +2587,7 @@ unittest } } -unittest +@system unittest { static struct DummyScope { @@ -2451,7 +2601,7 @@ unittest } } -unittest +@system unittest { // https://issues.dlang.org/show_bug.cgi?id=10194 // Also test for elaborate copying @@ -2497,7 +2647,7 @@ unittest assert(S.cnt == 0); } -unittest +@system unittest { // Bugzilla 13300 static struct S @@ -2517,7 +2667,7 @@ unittest static assert( hasElaborateDestructor!(Algebraic!(bool, S))); import std.array; - alias Algebraic!bool Value; + alias Value = Algebraic!bool; static struct T { @@ -2527,7 +2677,7 @@ unittest auto a = appender!(T[]); } -unittest +@system unittest { // Bugzilla 13871 alias A = Algebraic!(int, typeof(null)); @@ -2538,8 +2688,9 @@ unittest var = C(B()); } -unittest +@system unittest { + import std.exception : assertThrown, assertNotThrown; // Make sure Variant can handle types with opDispatch but no length field. struct SWithNoLength { @@ -2565,3 +2716,51 @@ unittest assertThrown!VariantException(v.length); } +@system unittest +{ + // Bugzilla 13534 + static assert(!__traits(compiles, () @safe { + auto foo() @system { return 3; } + auto v = Variant(&foo); + v(); // foo is called in safe code!? + })); +} + +@system unittest +{ + // Bugzilla 15039 + import std.variant; + import std.typecons; + + alias IntTypedef = Typedef!int; + alias Obj = Algebraic!(int, IntTypedef, This[]); + + Obj obj = 1; + + obj.visit!( + (int x) {}, + (IntTypedef x) {}, + (Obj[] x) {}, + ); +} + +@system unittest +{ + // Bugzilla 15791 + int n = 3; + struct NS1 { int foo() { return n + 10; } } + struct NS2 { int foo() { return n * 10; } } + + Variant v; + v = NS1(); + assert(v.get!NS1.foo() == 13); + v = NS2(); + assert(v.get!NS2.foo() == 30); +} + +@system unittest +{ + // Bugzilla 15827 + static struct Foo15827 { Variant v; this(Foo15827 v) {} } + Variant v = Foo15827.init; +} diff --git a/std/windows/charset.d b/std/windows/charset.d index 9d2cf755f5b..5c40189f07e 100644 --- a/std/windows/charset.d +++ b/std/windows/charset.d @@ -3,12 +3,9 @@ /** * Support UTF-8 on Windows 95, 98 and ME systems. * - * Macros: - * WIKI = Phobos/StdWindowsCharset - * * Copyright: Copyright Digital Mars 2005 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) */ /* Copyright Digital Mars 2005 - 2009. * Distributed under the Boost Software License, Version 1.0. @@ -16,6 +13,41 @@ * http://www.boost.org/LICENSE_1_0.txt) */ module std.windows.charset; + +version (StdDdoc) +{ + /****************************************** + * Converts the UTF-8 string s into a null-terminated string in a Windows + * 8-bit character set. + * + * Params: + * s = UTF-8 string to convert. + * codePage = is the number of the target codepage, or + * 0 - ANSI, + * 1 - OEM, + * 2 - Mac + * + * Authors: + * yaneurao, Walter Bright, Stewart Gordon + */ + const(char)* toMBSz(in char[] s, uint codePage = 0); + + /********************************************** + * Converts the null-terminated string s from a Windows 8-bit character set + * into a UTF-8 char array. + * + * Params: + * s = UTF-8 string to convert. + * codePage = is the number of the source codepage, or + * 0 - ANSI, + * 1 - OEM, + * 2 - Mac + * Authors: Stewart Gordon, Walter Bright + */ + string fromMBSz(immutable(char)* s, int codePage = 0); +} +else: + version (Windows): private import std.conv; @@ -26,21 +58,6 @@ private import std.string; import std.internal.cstring; -/****************************************** - * Converts the UTF-8 string s into a null-terminated string in a Windows - * 8-bit character set. - * - * Params: - * s = UTF-8 string to convert. - * codePage = is the number of the target codepage, or - * 0 - ANSI, - * 1 - OEM, - * 2 - Mac - * - * Authors: - * yaneurao, Walter Bright, Stewart Gordon - */ - const(char)* toMBSz(in char[] s, uint codePage = 0) { // Only need to do this if any chars have the high bit set @@ -72,20 +89,6 @@ const(char)* toMBSz(in char[] s, uint codePage = 0) return std.string.toStringz(s); } - -/********************************************** - * Converts the null-terminated string s from a Windows 8-bit character set - * into a UTF-8 char array. - * - * Params: - * s = UTF-8 string to convert. - * codePage = is the number of the source codepage, or - * 0 - ANSI, - * 1 - OEM, - * 2 - Mac - * Authors: Stewart Gordon, Walter Bright - */ - string fromMBSz(immutable(char)* s, int codePage = 0) { const(char)* c; diff --git a/std/windows/iunknown.d b/std/windows/iunknown.d index d206d7052f6..78099196a89 100644 --- a/std/windows/iunknown.d +++ b/std/windows/iunknown.d @@ -1,6 +1,12 @@ // Written in the D programming language. -/// Please import core.sys.windows.com instead. This module will be deprecated in DMD 2.068. +// @@@DEPRECATED_2017-06@@@ + +/++ + $(RED Deprecated. Use $(D core.sys.windows.com) instead. This module + will be removed in June 2017.) + +/ +deprecated("Import core.sys.windows.com instead") module std.windows.iunknown; // Replaced by: diff --git a/std/windows/registry.d b/std/windows/registry.d index 4823f3a7561..b1197b4b1e5 100644 --- a/std/windows/registry.d +++ b/std/windows/registry.d @@ -4,7 +4,7 @@ Copyright: Copyright 2003-2004 by Matthew Wilson and Synesis Software Written by Matthew Wilson - License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Author: Matthew Wilson, Kenji Hara @@ -53,7 +53,7 @@ debug(winreg) import std.stdio; private { - extern (Windows) int lstrlenW(LPCWSTR lpString); + import core.sys.windows.winbase : lstrlenW; void enforceSucc(LONG res, lazy string message, string fn = __FILE__, size_t ln = __LINE__) { @@ -64,36 +64,38 @@ private /* ************* Exceptions *************** */ -/** - */ -class Win32Exception : Exception +// Do not use. Left for compatibility. +class Win32Exception : WindowsException { - int error; - - @safe pure nothrow + @safe this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) { - super(message, fn, ln, next); + super(0, message, fn, ln); } - @safe pure + @safe this(string message, int errnum, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) { - super(text(message, " (", errnum, ")"), fn, ln, next); - error = errnum; + super(errnum, message, fn, ln); } + + @property int error() { return super.code; } } -unittest { +version(unittest) import std.string : startsWith, endsWith; + +@safe unittest +{ // Test that we can throw and catch one by its own type string message = "Test W1"; auto e = collectException!Win32Exception( enforce(false, new Win32Exception(message))); - assert(e.msg == message); + assert(e.msg.startsWith(message)); } -unittest { +@system unittest +{ // ditto string message = "Test W2"; int code = 5; @@ -101,10 +103,7 @@ unittest { auto e = collectException!Win32Exception( enforce(false, new Win32Exception(message, code))); assert(e.error == code); - - // CAUTION: this test is to be removed in D1 - // because e.msg does not contains the (code) section. - assert(e.msg == text(message, " (", code, ")")); + assert(e.msg.startsWith(message)); } /** @@ -120,7 +119,7 @@ public: Params: message = The message associated with the exception. */ - @safe pure + @safe this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) { super(message, fn, ln, next); @@ -133,14 +132,14 @@ public: message = The message associated with the exception. error = The Win32 error number associated with the exception. */ - @safe pure + @safe this(string message, int error, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) { super(message, error, fn, ln, next); } } -unittest +@system unittest { // (i) Test that we can throw and catch one by its own type string message = "Test 1"; @@ -149,20 +148,17 @@ unittest auto e = collectException!RegistryException( enforce(false, new RegistryException(message, code))); assert(e.error == code); - - // CAUTION: this test is to be removed in D1 - // because e.msg does not contains the (code) section. - assert(e.msg == text(message, " (", code, ")")); + assert(e.msg.startsWith(message)); } -unittest +@safe unittest { // ditto string message = "Test 2"; auto e = collectException!RegistryException( enforce(false, new RegistryException(message))); - assert(e.msg == message); + assert(e.msg.startsWith(message)); } /* ************* public enumerations *************** */ @@ -228,27 +224,26 @@ enum REG_VALUE_TYPE : DWORD /* ************* private *************** */ -private -{ - enum DWORD DELETE = 0x00010000L; - enum DWORD READ_CONTROL = 0x00020000L; - enum DWORD WRITE_DAC = 0x00040000L; - enum DWORD WRITE_OWNER = 0x00080000L; - enum DWORD SYNCHRONIZE = 0x00100000L; +private import core.sys.windows.winnt : + DELETE , + READ_CONTROL , + WRITE_DAC , + WRITE_OWNER , + SYNCHRONIZE , - enum DWORD STANDARD_RIGHTS_REQUIRED = 0x000F0000L; + STANDARD_RIGHTS_REQUIRED, - enum DWORD STANDARD_RIGHTS_READ = 0x00020000L/* READ_CONTROL */; - enum DWORD STANDARD_RIGHTS_WRITE = 0x00020000L/* READ_CONTROL */; - enum DWORD STANDARD_RIGHTS_EXECUTE = 0x00020000L/* READ_CONTROL */; + STANDARD_RIGHTS_READ , + STANDARD_RIGHTS_WRITE , + STANDARD_RIGHTS_EXECUTE , - enum DWORD STANDARD_RIGHTS_ALL = 0x001F0000L; + STANDARD_RIGHTS_ALL , - enum DWORD SPECIFIC_RIGHTS_ALL = 0x0000FFFFL; + SPECIFIC_RIGHTS_ALL ; - enum DWORD REG_CREATED_NEW_KEY = 0x00000001; - enum DWORD REG_OPENED_EXISTING_KEY = 0x00000002; -} +private import core.sys.windows.winreg : + REG_CREATED_NEW_KEY , + REG_OPENED_EXISTING_KEY ; // Returns samDesired but without WoW64 flags if not in WoW64 mode // for compatibility with Windows 2000 @@ -287,9 +282,9 @@ body * these hive keys is ignored, we'd rather not trust the Win32 * API. */ - if (cast(uint)hkey & 0x80000000) + if (cast(uint) hkey & 0x80000000) { - switch (cast(uint)hkey) + switch (cast(uint) hkey) { case HKEY_CLASSES_ROOT: case HKEY_CURRENT_USER: @@ -381,9 +376,9 @@ in body { /* Can't duplicate standard keys, but don't need to, so can just return */ - if (cast(uint)hkey & 0x80000000) + if (cast(uint) hkey & 0x80000000) { - switch (cast(uint)hkey) + switch (cast(uint) hkey) { case HKEY_CLASSES_ROOT: case HKEY_CURRENT_USER: @@ -569,12 +564,12 @@ body version(LittleEndian) value = to!string(u.dw); else - value = to!string(core.bitop.bswap(u.dw)); + value = to!string(bswap(u.dw)); break; case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN: version(LittleEndian) - value = to!string(core.bitop.bswap(u.dw)); + value = to!string(bswap(u.dw)); else value = to!string(u.dw); break; @@ -655,12 +650,12 @@ body version(LittleEndian) static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN); else - value = core.bitop.bswap(value); + value = bswap(value); break; case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN: version(LittleEndian) - value = core.bitop.bswap(value); + value = bswap(value); else static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN); break; @@ -971,7 +966,7 @@ public: Params: name = The name of the key to delete. May not be $(D null). */ - void deleteKey(string name, REGSAM access = cast(REGSAM)0) + void deleteKey(string name, REGSAM access = cast(REGSAM) 0) { enforce(!name.empty, new RegistryException("Key name is invalid")); @@ -1297,12 +1292,6 @@ public: return value; } - deprecated("Please use value_QWORD instead.") - ulong value_QWORD_LITTLEENDIAN() - { - return value_QWORD; - } - /** Obtains the value as a binary blob. @@ -1749,7 +1738,7 @@ private: } -unittest +@system unittest { debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); debug(winreg) writefln("std.windows.registry.unittest read"); @@ -1782,7 +1771,7 @@ unittest } } -unittest +@system unittest { debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); debug(winreg) writefln("std.windows.registry.unittest write"); @@ -1797,7 +1786,10 @@ unittest string unittestKeyName = "Temporary key for a D UnitTest which can be deleted afterwards"; Key unittestKey = HKCU.createKey(unittestKeyName); assert(unittestKey); - Key cityKey = unittestKey.createKey("CityCollection using foreign names with umlauts and accents: \u00f6\u00e4\u00fc\u00d6\u00c4\u00dc\u00e0\u00e1\u00e2\u00df"); + Key cityKey = unittestKey.createKey( + "CityCollection using foreign names with umlauts and accents: " + ~"\u00f6\u00e4\u00fc\u00d6\u00c4\u00dc\u00e0\u00e1\u00e2\u00df" + ); cityKey.setValue("K\u00f6ln", "Germany"); // Cologne cityKey.setValue("\u041c\u0438\u043d\u0441\u043a", "Belarus"); // Minsk cityKey.setValue("\u5317\u4eac", "China"); // Bejing @@ -1845,4 +1837,7 @@ unittest unittestKey.deleteKey(stateKey.name); unittestKey.deleteKey(cityKey.name); HKCU.deleteKey(unittestKeyName); + + auto e = collectException!RegistryException(HKCU.getKey("cDhmxsX9K23a8Uf869uB")); + assert(e.msg.endsWith(" (error 2)")); } diff --git a/std/windows/syserror.d b/std/windows/syserror.d index 60c610ddff3..83de681662a 100644 --- a/std/windows/syserror.d +++ b/std/windows/syserror.d @@ -4,11 +4,11 @@ * Convert Win32 error code to string. * * Copyright: Copyright Digital Mars 2006 - 2013. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Credits: Based on code written by Regan Heath - * - * Copyright Digital Mars 2006 - 2013. + */ +/* Copyright Digital Mars 2006 - 2013. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -24,9 +24,10 @@ version (StdDdoc) enum LANG_NEUTRAL = 0, SUBLANG_DEFAULT = 1; } - /// Query the text for a Windows error code (as returned by $(LINK2 - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, - /// $(D GetLastError))) as a D string. + /** Query the text for a Windows error code, as returned by + $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, + $(D GetLastError)), as a D string. + */ string sysErrorString( DWORD errCode, // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language @@ -34,15 +35,15 @@ version (StdDdoc) int subLangId = SUBLANG_DEFAULT) @trusted; /********************* - * Thrown if errors that set $(LINK2 - * http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, - * $(D GetLastError)) occur. + Thrown if errors that set + $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx, + $(D GetLastError)) occur. */ class WindowsException : Exception { private alias DWORD = int; final @property DWORD code(); /// $(D GetLastError)'s return value. - @disable this(int dummy); + this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted; } /++ @@ -58,7 +59,7 @@ version (StdDdoc) +/ T wenforce(T, S)(T value, lazy S msg = null, string file = __FILE__, size_t line = __LINE__) @safe - if (isSomeString!S); + if (isSomeString!S); } else: @@ -106,7 +107,7 @@ bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) if (lpMsgBuf) { import std.string : strip; - w.put(lpMsgBuf[0..res].strip()); + w.put(lpMsgBuf[0 .. res].strip()); return true; } else @@ -116,7 +117,7 @@ bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) class WindowsException : Exception { - import core.sys.windows.windows; + import core.sys.windows.windows : DWORD; final @property DWORD code() { return _code; } /// $(D GetLastError)'s return value. private DWORD _code; @@ -130,11 +131,15 @@ class WindowsException : Exception if (str != null) { buf.put(str); - buf.put(": "); + if (code) + buf.put(": "); } - auto success = putSysError(code, buf); - formattedWrite(buf, success ? " (error %d)" : "Error %d", code); + if (code) + { + auto success = putSysError(code, buf); + formattedWrite(buf, success ? " (error %d)" : "Error %d", code); + } super(buf.data, file, line); } @@ -142,19 +147,42 @@ class WindowsException : Exception T wenforce(T, S)(T value, lazy S msg = null, - string file = __FILE__, size_t line = __LINE__) if (isSomeString!S) +string file = __FILE__, size_t line = __LINE__) +if (isSomeString!S) { if (!value) throw new WindowsException(GetLastError(), to!string(msg), file, line); return value; } +T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file = __FILE__, size_t line = __LINE__) +{ + if (condition) + return condition; + string names; + if (!name) + { + static string trustedToString(const(wchar)* stringz) @trusted + { + import std.conv : to; + import core.stdc.wchar_ : wcslen; + auto len = wcslen(stringz); + return to!string(stringz[0 .. len]); + } + + names = trustedToString(namez); + } + else + names = to!string(name); + throw new WindowsException(GetLastError(), names, file, line); +} + version(Windows) -unittest +@system unittest { import std.exception; import std.string; - import std.algorithm : startsWith, endsWith; + import std.algorithm.searching : startsWith, endsWith; auto e = collectException!WindowsException( DeleteFileA("unexisting.txt").wenforce("DeleteFile") @@ -163,4 +191,11 @@ unittest assert(e.msg.startsWith("DeleteFile: ")); // can't test the entire message, as it depends on Windows locale assert(e.msg.endsWith(" (error 2)")); + + // Test code zero + e = new WindowsException(0); + assert(e.msg == ""); + + e = new WindowsException(0, "Test"); + assert(e.msg == "Test"); } diff --git a/std/xml.d b/std/xml.d index 474e81b82c9..d2a28d87c8c 100644 --- a/std/xml.d +++ b/std/xml.d @@ -30,7 +30,7 @@ import std.file; void main() { - string s = cast(string)std.file.read("books.xml"); + string s = cast(string) std.file.read("books.xml"); // Check for well-formedness check(s); @@ -64,7 +64,7 @@ struct Book void main() { - string s = cast(string)std.file.read("books.xml"); + string s = cast(string) std.file.read("books.xml"); // Check for well-formedness check(s); @@ -93,7 +93,7 @@ void main() // Put it back together again; auto doc = new Document(new Tag("catalog")); - foreach(book;books) + foreach (book;books) { auto element = new Element("book"); element.tag.attr["id"] = book.id; @@ -112,11 +112,8 @@ void main() writefln(join(doc.pretty(3),"\n")); } ------------------------------------------------------------------------------- -Macros: - WIKI=Phobos/StdXml - Copyright: Copyright Janice Caron 2008 - 2009. -License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Janice Caron Source: $(PHOBOSSRC std/_xml.d) */ @@ -128,12 +125,6 @@ Distributed under the Boost Software License, Version 1.0. */ module std.xml; -import std.algorithm : count, startsWith; -import std.array; -import std.ascii; -import std.string; -import std.encoding; - enum cdata = "<![CDATA["; /** @@ -144,13 +135,13 @@ enum cdata = "<![CDATA["; * Params: * c = the character to be tested */ -bool isChar(dchar c) // rule 2 +bool isChar(dchar c) @safe @nogc pure nothrow // rule 2 { if (c <= 0xD7FF) { if (c >= 0x20) return true; - switch(c) + switch (c) { case 0xA: case 0x9: @@ -168,30 +159,28 @@ bool isChar(dchar c) // rule 2 return false; } -unittest +@safe @nogc nothrow pure unittest { -// const CharTable=[0x9,0x9,0xA,0xA,0xD,0xD,0x20,0xD7FF,0xE000,0xFFFD, -// 0x10000,0x10FFFF]; - assert(!isChar(cast(dchar)0x8)); - assert( isChar(cast(dchar)0x9)); - assert( isChar(cast(dchar)0xA)); - assert(!isChar(cast(dchar)0xB)); - assert(!isChar(cast(dchar)0xC)); - assert( isChar(cast(dchar)0xD)); - assert(!isChar(cast(dchar)0xE)); - assert(!isChar(cast(dchar)0x1F)); - assert( isChar(cast(dchar)0x20)); + assert(!isChar(cast(dchar) 0x8)); + assert( isChar(cast(dchar) 0x9)); + assert( isChar(cast(dchar) 0xA)); + assert(!isChar(cast(dchar) 0xB)); + assert(!isChar(cast(dchar) 0xC)); + assert( isChar(cast(dchar) 0xD)); + assert(!isChar(cast(dchar) 0xE)); + assert(!isChar(cast(dchar) 0x1F)); + assert( isChar(cast(dchar) 0x20)); assert( isChar('J')); - assert( isChar(cast(dchar)0xD7FF)); - assert(!isChar(cast(dchar)0xD800)); - assert(!isChar(cast(dchar)0xDFFF)); - assert( isChar(cast(dchar)0xE000)); - assert( isChar(cast(dchar)0xFFFD)); - assert(!isChar(cast(dchar)0xFFFE)); - assert(!isChar(cast(dchar)0xFFFF)); - assert( isChar(cast(dchar)0x10000)); - assert( isChar(cast(dchar)0x10FFFF)); - assert(!isChar(cast(dchar)0x110000)); + assert( isChar(cast(dchar) 0xD7FF)); + assert(!isChar(cast(dchar) 0xD800)); + assert(!isChar(cast(dchar) 0xDFFF)); + assert( isChar(cast(dchar) 0xE000)); + assert( isChar(cast(dchar) 0xFFFD)); + assert(!isChar(cast(dchar) 0xFFFE)); + assert(!isChar(cast(dchar) 0xFFFF)); + assert( isChar(cast(dchar) 0x10000)); + assert( isChar(cast(dchar) 0x10FFFF)); + assert(!isChar(cast(dchar) 0x110000)); debug (stdxml_TestHardcodedChecks) { @@ -211,7 +200,7 @@ unittest * Params: * c = the character to be tested */ -bool isSpace(dchar c) +bool isSpace(dchar c) @safe @nogc pure nothrow { return c == '\u0020' || c == '\u0009' || c == '\u000A' || c == '\u000D'; } @@ -224,7 +213,7 @@ bool isSpace(dchar c) * Params: * c = the character to be tested */ -bool isDigit(dchar c) +bool isDigit(dchar c) @safe @nogc pure nothrow { if (c <= 0x0039 && c >= 0x0030) return true; @@ -232,7 +221,7 @@ bool isDigit(dchar c) return lookup(DigitTable,c); } -unittest +@safe @nogc nothrow pure unittest { debug (stdxml_TestHardcodedChecks) { @@ -249,7 +238,7 @@ unittest * Params: * c = the character to be tested */ -bool isLetter(dchar c) // rule 84 +bool isLetter(dchar c) @safe @nogc nothrow pure // rule 84 { return isIdeographic(c) || isBaseChar(c); } @@ -263,7 +252,7 @@ bool isLetter(dchar c) // rule 84 * Params: * c = the character to be tested */ -bool isIdeographic(dchar c) +bool isIdeographic(dchar c) @safe @nogc nothrow pure { if (c == 0x3007) return true; @@ -274,7 +263,7 @@ bool isIdeographic(dchar c) return false; } -unittest +@safe @nogc nothrow pure unittest { assert(isIdeographic('\u4E00')); assert(isIdeographic('\u9FA5')); @@ -298,7 +287,7 @@ unittest * Params: * c = the character to be tested */ -bool isBaseChar(dchar c) +bool isBaseChar(dchar c) @safe @nogc nothrow pure { return lookup(BaseCharTable,c); } @@ -312,7 +301,7 @@ bool isBaseChar(dchar c) * Params: * c = the character to be tested */ -bool isCombiningChar(dchar c) +bool isCombiningChar(dchar c) @safe @nogc nothrow pure { return lookup(CombiningCharTable,c); } @@ -325,7 +314,7 @@ bool isCombiningChar(dchar c) * Params: * c = the character to be tested */ -bool isExtender(dchar c) +bool isExtender(dchar c) @safe @nogc nothrow pure { return lookup(ExtenderTable,c); } @@ -349,13 +338,15 @@ bool isExtender(dchar c) * * Returns: The encoded string * - * Examples: + * Example: * -------------- * writefln(encode("a > b")); // writes "a > b" * -------------- */ S encode(S)(S s) { + import std.array : appender; + string r; size_t lastI; auto result = appender!S(); @@ -382,7 +373,7 @@ S encode(S)(S s) return result.data; } -unittest +@safe unittest { auto s = "hello"; assert(encode(s) is s); @@ -434,18 +425,18 @@ enum DecodeMode * * Returns: The decoded string * - * Examples: + * Example: * -------------- * writefln(decode("a > b")); // writes "a > b" * -------------- */ -string decode(string s, DecodeMode mode=DecodeMode.LOOSE) +string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure { - import std.utf : encode; + import std.algorithm.searching : startsWith; if (mode == DecodeMode.NONE) return s; - char[] buffer; + string buffer; foreach (ref i; 0 .. s.length) { char c = s[i]; @@ -467,10 +458,11 @@ string decode(string s, DecodeMode mode=DecodeMode.LOOSE) string t = s[i..$]; checkCharRef(t, d); char[4] temp; - buffer ~= temp[0 .. std.utf.encode(temp, d)]; + import std.utf : encode; + buffer ~= temp[0 .. encode(temp, d)]; i = s.length - t.length - 1; } - catch(Err e) + catch (Err e) { if (mode == DecodeMode.STRICT) throw new DecodeException("Unescaped &"); @@ -490,12 +482,12 @@ string decode(string s, DecodeMode mode=DecodeMode.LOOSE) } } } - return (buffer.length == 0) ? s : cast(string)buffer; + return (buffer.length == 0) ? s : buffer; } -unittest +@safe pure unittest { - void assertNot(string s) + void assertNot(string s) pure { bool b = false; try { decode(s,DecodeMode.STRICT); } @@ -572,7 +564,7 @@ class Document : Element this(xml.tag); prolog = s[0 .. tagString.ptr - s.ptr]; parse(xml); - epilog = *xml.s; + epilog = xml.s; } /** @@ -591,7 +583,7 @@ class Document : Element /** * Compares two Documents for equality * - * Examples: + * Example: * -------------- * Document d1,d2; * if (d1 == d2) { } @@ -600,11 +592,9 @@ class Document : Element override bool opEquals(Object o) { const doc = toType!(const Document)(o); - return - (prolog != doc.prolog ) ? false : ( - (super != cast(const Element)doc) ? false : ( - (epilog != doc.epilog ) ? false : ( - true ))); + return prolog == doc.prolog + && (cast() this).Element.opEquals(cast() doc) + && epilog == doc.epilog; } /** @@ -613,7 +603,7 @@ class Document : Element * You should rarely need to call this function. It exists so that * Documents can be used as associative array keys. * - * Examples: + * Example: * -------------- * Document d1,d2; * if (d1 < d2) { } @@ -622,14 +612,13 @@ class Document : Element override int opCmp(Object o) { const doc = toType!(const Document)(o); - return - ((prolog != doc.prolog ) - ? ( prolog < doc.prolog ? -1 : 1 ) : - ((super != cast(const Element)doc) - ? ( cast()super < cast()cast(const Element)doc ? -1 : 1 ) : - ((epilog != doc.epilog ) - ? ( epilog < doc.epilog ? -1 : 1 ) : - 0 ))); + if (prolog != doc.prolog) + return prolog < doc.prolog ? -1 : 1; + if (int cmp = (cast() this).Element.opCmp(cast() doc)) + return cmp; + if (epilog != doc.epilog) + return epilog < doc.epilog ? -1 : 1; + return 0; } /** @@ -640,20 +629,38 @@ class Document : Element */ override size_t toHash() @trusted { - return hash(prolog, hash(epilog, (cast()super).toHash())); + return hash(prolog, hash(epilog, (cast() this).Element.toHash())); } /** * Returns the string representation of a Document. (That is, the * complete XML of a document). */ - override string toString() + override string toString() @safe { return prolog ~ super.toString() ~ epilog; } } } +@system unittest +{ + // https://issues.dlang.org/show_bug.cgi?id=14966 + auto xml = `<?xml version="1.0" encoding="UTF-8"?><foo></foo>`; + + auto a = new Document(xml); + auto b = new Document(xml); + assert(a == b); + assert(!(a < b)); + int[Document] aa; + aa[a] = 1; + assert(aa[b] == 1); + + b ~= new Element("b"); + assert(a < b); + assert(b > a); +} + /** * Class representing an XML element. * @@ -677,13 +684,13 @@ class Element : Item * name = the name of the element. * interior = (optional) the string interior. * - * Examples: + * Example: * ------------------------------------------------------- * auto element = new Element("title","Serenity") * // constructs the element <title>Serenity * ------------------------------------------------------- */ - this(string name, string interior=null) + this(string name, string interior=null) @safe pure { this(new Tag(name)); if (interior.length != 0) opCatAssign(new Text(interior)); @@ -695,11 +702,11 @@ class Element : Item * Params: * tag_ = the start or empty tag of the element. */ - this(const(Tag) tag_) + this(const(Tag) tag_) @safe pure { this.tag = new Tag(tag_.name); tag.type = TagType.EMPTY; - foreach(k,v;tag_.attr) tag.attr[k] = v; + foreach (k,v;tag_.attr) tag.attr[k] = v; tag.tagString = tag_.tagString; } @@ -709,13 +716,13 @@ class Element : Item * Params: * item = the item you wish to append. * - * Examples: + * Example: * -------------- * Element element; * element ~= new Text("hello"); * -------------- */ - void opCatAssign(Text item) + void opCatAssign(Text item) @safe pure { texts ~= item; appendItem(item); @@ -727,13 +734,13 @@ class Element : Item * Params: * item = the item you wish to append. * - * Examples: + * Example: * -------------- * Element element; * element ~= new CData("hello"); * -------------- */ - void opCatAssign(CData item) + void opCatAssign(CData item) @safe pure { cdatas ~= item; appendItem(item); @@ -745,13 +752,13 @@ class Element : Item * Params: * item = the item you wish to append. * - * Examples: + * Example: * -------------- * Element element; * element ~= new Comment("hello"); * -------------- */ - void opCatAssign(Comment item) + void opCatAssign(Comment item) @safe pure { comments ~= item; appendItem(item); @@ -763,13 +770,13 @@ class Element : Item * Params: * item = the item you wish to append. * - * Examples: + * Example: * -------------- * Element element; * element ~= new ProcessingInstruction("hello"); * -------------- */ - void opCatAssign(ProcessingInstruction item) + void opCatAssign(ProcessingInstruction item) @safe pure { pis ~= item; appendItem(item); @@ -781,7 +788,7 @@ class Element : Item * Params: * item = the item you wish to append. * - * Examples: + * Example: * -------------- * Element element; * Element other = new Element("br"); @@ -789,13 +796,13 @@ class Element : Item * // appends element representing
* -------------- */ - void opCatAssign(Element item) + void opCatAssign(Element item) @safe pure { elements ~= item; appendItem(item); } - private void appendItem(Item item) + private void appendItem(Item item) @safe pure { items ~= item; if (tag.type == TagType.EMPTY && !item.isEmptyXML) @@ -822,7 +829,7 @@ class Element : Item /** * Compares two Elements for equality * - * Examples: + * Example: * -------------- * Element e1,e2; * if (e1 == e2) { } @@ -831,11 +838,11 @@ class Element : Item override bool opEquals(Object o) { const element = toType!(const Element)(o); - auto len = items.length; + immutable len = items.length; if (len != element.items.length) return false; foreach (i; 0 .. len) { - if (!items[i].opEquals(cast()element.items[i])) return false; + if (!items[i].opEquals(cast() element.items[i])) return false; } return true; } @@ -846,7 +853,7 @@ class Element : Item * You should rarely need to call this function. It exists so that Elements * can be used as associative array keys. * - * Examples: + * Example: * -------------- * Element e1,e2; * if (e1 < e2) { } @@ -861,7 +868,7 @@ class Element : Item if (i == items.length) return -1; if (i == element.items.length) return 1; if (items[i] != element.items[i]) - return items[i].opCmp(cast()element.items[i]); + return items[i].opCmp(cast() element.items[i]); } } @@ -871,10 +878,10 @@ class Element : Item * You should rarely need to call this function. It exists so that Elements * can be used as associative array keys. */ - override size_t toHash() const + override size_t toHash() const @safe { size_t hash = tag.toHash(); - foreach(item;items) hash += item.toHash(); + foreach (item;items) hash += item.toHash(); return hash; } @@ -895,9 +902,9 @@ class Element : Item string text(DecodeMode mode=DecodeMode.LOOSE) { string buffer; - foreach(item;items) + foreach (item;items) { - Text t = cast(Text)item; + Text t = cast(Text) item; if (t is null) throw new DecodeException(item.toString()); buffer ~= decode(t.toString(),mode); } @@ -913,6 +920,8 @@ class Element : Item */ override string[] pretty(uint indent=2) { + import std.algorithm.searching : count; + import std.string : rightJustify; if (isEmptyXML) return [ tag.toEmptyString() ]; @@ -926,10 +935,10 @@ class Element : Item } string[] a = [ tag.toStartString() ]; - foreach(item;items) + foreach (item;items) { string[] b = item.pretty(indent); - foreach(s;b) + foreach (s;b) { a ~= rightJustify(s,count(s) + indent); } @@ -941,13 +950,13 @@ class Element : Item /** * Returns the string representation of an Element * - * Examples: + * Example: * -------------- * auto element = new Element("br"); * writefln(element.toString()); // writes "
" * -------------- */ - override string toString() + override string toString() @safe { if (isEmptyXML) return tag.toEmptyString(); @@ -957,7 +966,7 @@ class Element : Item return buffer; } - override @property bool isEmptyXML() { return items.length == 0; } + override @property @safe pure @nogc nothrow bool isEmptyXML() { return items.length == 0; } } } @@ -1001,13 +1010,13 @@ class Tag s = name; try { checkName(s,t); } - catch(Err e) { assert(false,"Invalid tag name:" ~ e.toString()); } + catch (Err e) { assert(false,"Invalid tag name:" ~ e.toString()); } - foreach(k,v;attr) + foreach (k,v;attr) { s = k; try { checkName(s,t); } - catch(Err e) + catch (Err e) { assert(false,"Invalid atrribute name:" ~ e.toString()); } } } @@ -1023,13 +1032,13 @@ class Tag * type = (optional) the Tag's type. If omitted, defaults to * TagType.START. * - * Examples: + * Example: * -------------- * auto tag = new Tag("img",Tag.EMPTY); * tag.attr["src"] = "http://example.com/example.jpg"; * -------------- */ - this(string name, TagType type=TagType.START) + this(string name, TagType type=TagType.START) @safe pure { this.name = name; this.type = type; @@ -1045,25 +1054,44 @@ class Tag * The second parameter is a dummy parameter only, required solely to * distinguish this constructor from the public one. */ - private this(ref string s, bool dummy) + private this(ref string s, bool dummy) @safe pure { + import std.algorithm.searching : countUntil; + import std.ascii : isWhite; + import std.utf : byCodeUnit; + tagString = s; try { reqc(s,'<'); if (optc(s,'/')) type = TagType.END; - name = munch(s,"^/>"~whitespace); - munch(s,whitespace); - while(s.length > 0 && s[0] != '>' && s[0] != '/') + ptrdiff_t i = s.byCodeUnit.countUntil(">", "/>", " ", "\t", "\v", "\r", "\n", "\f"); + name = s[0 .. i]; + s = s[i .. $]; + + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; + + while (s.length > 0 && s[0] != '>' && s[0] != '/') { - string key = munch(s,"^="~whitespace); - munch(s,whitespace); + i = s.byCodeUnit.countUntil("=", " ", "\t", "\v", "\r", "\n", "\f"); + string key = s[0 .. i]; + s = s[i .. $]; + + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; reqc(s,'='); - munch(s,whitespace); - reqc(s,'"'); - string val = decode(munch(s,"^\""), DecodeMode.LOOSE); - reqc(s,'"'); - munch(s,whitespace); + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; + + immutable char quote = requireOneOf(s,"'\""); + i = s.byCodeUnit.countUntil(quote); + string val = decode(s[0 .. i], DecodeMode.LOOSE); + s = s[i .. $]; + reqc(s,quote); + + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; attr[key] = val; } if (optc(s,'/')) @@ -1072,11 +1100,11 @@ class Tag type = TagType.EMPTY; } reqc(s,'>'); - tagString.length = (s.ptr - tagString.ptr); + tagString.length = tagString.length - s.length; } - catch(XMLException e) + catch (XMLException e) { - tagString.length = (s.ptr - tagString.ptr); + tagString.length = tagString.length - s.length; throw new TagException(tagString); } } @@ -1089,7 +1117,7 @@ class Tag * You should rarely need to call this function. It exists so that Tags * can be used as associative array keys. * - * Examples: + * Example: * -------------- * Tag tag1,tag2 * if (tag1 == tag2) { } @@ -1108,7 +1136,7 @@ class Tag /** * Compares two Tags * - * Examples: + * Example: * -------------- * Tag tag1,tag2 * if (tag1 < tag2) { } @@ -1120,7 +1148,7 @@ class Tag // Note that attr is an AA, so the comparison is nonsensical (bug 10381) return ((name != tag.name) ? ( name < tag.name ? -1 : 1 ) : - ((attr != tag.attr) ? ( cast(void *)attr < cast(void*)tag.attr ? -1 : 1 ) : + ((attr != tag.attr) ? ( cast(void *) attr < cast(void*) tag.attr ? -1 : 1 ) : ((type != tag.type) ? ( type < tag.type ? -1 : 1 ) : 0 ))); } @@ -1139,13 +1167,13 @@ class Tag /** * Returns the string representation of a Tag * - * Examples: + * Example: * -------------- * auto tag = new Tag("book",TagType.START); * writefln(tag.toString()); // writes "" * -------------- */ - override string toString() + override string toString() @safe { if (isEmpty) return toEmptyString(); return (isEnd) ? toEndString() : toStartString(); @@ -1153,50 +1181,52 @@ class Tag private { - string toNonEndString() + string toNonEndString() @safe { + import std.format : format; + string s = "<" ~ name; - foreach(key,val;attr) + foreach (key,val;attr) s ~= format(" %s=\"%s\"",key,encode(val)); return s; } - string toStartString() { return toNonEndString() ~ ">"; } + string toStartString() @safe { return toNonEndString() ~ ">"; } - string toEndString() { return ""; } + string toEndString() @safe { return ""; } - string toEmptyString() { return toNonEndString() ~ " />"; } + string toEmptyString() @safe { return toNonEndString() ~ " />"; } } /** * Returns true if the Tag is a start tag * - * Examples: + * Example: * -------------- * if (tag.isStart) { } * -------------- */ - @property bool isStart() { return type == TagType.START; } + @property bool isStart() @safe @nogc pure nothrow { return type == TagType.START; } /** * Returns true if the Tag is an end tag * - * Examples: + * Example: * -------------- * if (tag.isEnd) { } * -------------- */ - @property bool isEnd() { return type == TagType.END; } + @property bool isEnd() @safe @nogc pure nothrow { return type == TagType.END; } /** * Returns true if the Tag is an empty tag * - * Examples: + * Example: * -------------- * if (tag.isEmpty) { } * -------------- */ - @property bool isEmpty() { return type == TagType.EMPTY; } + @property bool isEmpty() @safe @nogc pure nothrow { return type == TagType.EMPTY; } } } @@ -1216,15 +1246,17 @@ class Comment : Item * Throws: CommentException if the comment body is illegal (contains "--" * or exactly equals "-") * - * Examples: + * Example: * -------------- * auto item = new Comment("This is a comment"); * // constructs * -------------- */ - this(string content) + this(string content) @safe pure { - if (content == "-" || content.indexOf("==") != -1) + import std.string : indexOf; + + if (content == "-" || content.indexOf("--") != -1) throw new CommentException(content); this.content = content; } @@ -1232,7 +1264,7 @@ class Comment : Item /** * Compares two comments for equality * - * Examples: + * Example: * -------------- * Comment item1,item2; * if (item1 == item2) { } @@ -1241,7 +1273,7 @@ class Comment : Item override bool opEquals(Object o) { const item = toType!(const Item)(o); - const t = cast(Comment)item; + const t = cast(Comment) item; return t !is null && content == t.content; } @@ -1251,7 +1283,7 @@ class Comment : Item * You should rarely need to call this function. It exists so that Comments * can be used as associative array keys. * - * Examples: + * Example: * -------------- * Comment item1,item2; * if (item1 < item2) { } @@ -1260,7 +1292,7 @@ class Comment : Item override int opCmp(Object o) { const item = toType!(const Item)(o); - const t = cast(Comment)item; + const t = cast(Comment) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1271,14 +1303,22 @@ class Comment : Item * You should rarely need to call this function. It exists so that Comments * can be used as associative array keys. */ - override size_t toHash() const { return hash(content); } + override size_t toHash() const nothrow { return hash(content); } /** * Returns a string representation of this comment */ - override string toString() const { return ""; } + override string toString() const @safe pure nothrow { return ""; } - override @property bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always +} + +@safe unittest // issue 16241 +{ + import std.exception : assertThrown; + auto c = new Comment("=="); + assert(c.content == "=="); + assertThrown!CommentException(new Comment("--")); } /** @@ -1296,14 +1336,15 @@ class CData : Item * * Throws: CDataException if the segment body is illegal (contains "]]>") * - * Examples: + * Example: * -------------- * auto item = new CData("hello"); * // constructs hello]]> * -------------- */ - this(string content) + this(string content) @safe pure { + import std.string : indexOf; if (content.indexOf("]]>") != -1) throw new CDataException(content); this.content = content; } @@ -1311,7 +1352,7 @@ class CData : Item /** * Compares two CDatas for equality * - * Examples: + * Example: * -------------- * CData item1,item2; * if (item1 == item2) { } @@ -1320,7 +1361,7 @@ class CData : Item override bool opEquals(Object o) { const item = toType!(const Item)(o); - const t = cast(CData)item; + const t = cast(CData) item; return t !is null && content == t.content; } @@ -1330,7 +1371,7 @@ class CData : Item * You should rarely need to call this function. It exists so that CDatas * can be used as associative array keys. * - * Examples: + * Example: * -------------- * CData item1,item2; * if (item1 < item2) { } @@ -1339,7 +1380,7 @@ class CData : Item override int opCmp(Object o) { const item = toType!(const Item)(o); - const t = cast(CData)item; + const t = cast(CData) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1350,14 +1391,14 @@ class CData : Item * You should rarely need to call this function. It exists so that CDatas * can be used as associative array keys. */ - override size_t toHash() const { return hash(content); } + override size_t toHash() const nothrow { return hash(content); } /** * Returns a string representation of this CData section */ - override string toString() const { return cdata ~ content ~ "]]>"; } + override string toString() const @safe pure nothrow { return cdata ~ content ~ "]]>"; } - override @property bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always } /** @@ -1374,13 +1415,13 @@ class Text : Item * content = the text. This function encodes the text before * insertion, so it is safe to insert any text * - * Examples: + * Example: * -------------- * auto Text = new CData("a < b"); * // constructs a < b * -------------- */ - this(string content) + this(string content) @safe pure { this.content = encode(content); } @@ -1388,7 +1429,7 @@ class Text : Item /** * Compares two text sections for equality * - * Examples: + * Example: * -------------- * Text item1,item2; * if (item1 == item2) { } @@ -1397,7 +1438,7 @@ class Text : Item override bool opEquals(Object o) { const item = toType!(const Item)(o); - const t = cast(Text)item; + const t = cast(Text) item; return t !is null && content == t.content; } @@ -1407,7 +1448,7 @@ class Text : Item * You should rarely need to call this function. It exists so that Texts * can be used as associative array keys. * - * Examples: + * Example: * -------------- * Text item1,item2; * if (item1 < item2) { } @@ -1416,7 +1457,7 @@ class Text : Item override int opCmp(Object o) { const item = toType!(const Item)(o); - const t = cast(Text)item; + const t = cast(Text) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1427,17 +1468,17 @@ class Text : Item * You should rarely need to call this function. It exists so that Texts * can be used as associative array keys. */ - override size_t toHash() const { return hash(content); } + override size_t toHash() const nothrow { return hash(content); } /** * Returns a string representation of this Text section */ - override string toString() const { return content; } + override string toString() const @safe @nogc pure nothrow { return content; } /** * Returns true if the content is the empty string */ - override @property bool isEmptyXML() const { return content.length == 0; } + override @property @safe @nogc pure nothrow bool isEmptyXML() const { return content.length == 0; } } /** @@ -1455,14 +1496,15 @@ class XMLInstruction : Item * * Throws: XIException if the segment body is illegal (contains ">") * - * Examples: + * Example: * -------------- * auto item = new XMLInstruction("ATTLIST"); * // constructs * -------------- */ - this(string content) + this(string content) @safe pure { + import std.string : indexOf; if (content.indexOf(">") != -1) throw new XIException(content); this.content = content; } @@ -1470,7 +1512,7 @@ class XMLInstruction : Item /** * Compares two XML instructions for equality * - * Examples: + * Example: * -------------- * XMLInstruction item1,item2; * if (item1 == item2) { } @@ -1479,7 +1521,7 @@ class XMLInstruction : Item override bool opEquals(Object o) { const item = toType!(const Item)(o); - const t = cast(XMLInstruction)item; + const t = cast(XMLInstruction) item; return t !is null && content == t.content; } @@ -1489,7 +1531,7 @@ class XMLInstruction : Item * You should rarely need to call this function. It exists so that * XmlInstructions can be used as associative array keys. * - * Examples: + * Example: * -------------- * XMLInstruction item1,item2; * if (item1 < item2) { } @@ -1498,7 +1540,7 @@ class XMLInstruction : Item override int opCmp(Object o) { const item = toType!(const Item)(o); - const t = cast(XMLInstruction)item; + const t = cast(XMLInstruction) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1509,14 +1551,14 @@ class XMLInstruction : Item * You should rarely need to call this function. It exists so that * XmlInstructions can be used as associative array keys. */ - override size_t toHash() const { return hash(content); } + override size_t toHash() const nothrow { return hash(content); } /** * Returns a string representation of this XmlInstruction */ - override string toString() const { return ""; } + override string toString() const @safe pure nothrow { return ""; } - override @property bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always } /** @@ -1534,14 +1576,15 @@ class ProcessingInstruction : Item * * Throws: PIException if the segment body is illegal (contains "?>") * - * Examples: + * Example: * -------------- * auto item = new ProcessingInstruction("php"); * // constructs * -------------- */ - this(string content) + this(string content) @safe pure { + import std.string : indexOf; if (content.indexOf("?>") != -1) throw new PIException(content); this.content = content; } @@ -1549,7 +1592,7 @@ class ProcessingInstruction : Item /** * Compares two processing instructions for equality * - * Examples: + * Example: * -------------- * ProcessingInstruction item1,item2; * if (item1 == item2) { } @@ -1558,7 +1601,7 @@ class ProcessingInstruction : Item override bool opEquals(Object o) { const item = toType!(const Item)(o); - const t = cast(ProcessingInstruction)item; + const t = cast(ProcessingInstruction) item; return t !is null && content == t.content; } @@ -1568,7 +1611,7 @@ class ProcessingInstruction : Item * You should rarely need to call this function. It exists so that * ProcessingInstructions can be used as associative array keys. * - * Examples: + * Example: * -------------- * ProcessingInstruction item1,item2; * if (item1 < item2) { } @@ -1577,7 +1620,7 @@ class ProcessingInstruction : Item override int opCmp(Object o) { const item = toType!(const Item)(o); - const t = cast(ProcessingInstruction)item; + const t = cast(ProcessingInstruction) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1588,14 +1631,14 @@ class ProcessingInstruction : Item * You should rarely need to call this function. It exists so that * ProcessingInstructions can be used as associative array keys. */ - override size_t toHash() const { return hash(content); } + override size_t toHash() const nothrow { return hash(content); } /** * Returns a string representation of this ProcessingInstruction */ - override string toString() const { return ""; } + override string toString() const @safe pure nothrow { return ""; } - override @property bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always } /** @@ -1613,7 +1656,7 @@ abstract class Item abstract override size_t toHash() const; /// Returns a string representation of this item - abstract override string toString() const; + abstract override string toString() @safe const; /** * Returns an indented string representation of this item @@ -1623,12 +1666,13 @@ abstract class Item */ string[] pretty(uint indent) const { + import std.string : strip; string s = strip(toString()); return s.length == 0 ? [] : [ s ]; } /// Returns true if the item represents empty XML text - abstract @property bool isEmptyXML() const; + abstract @property @safe @nogc pure nothrow bool isEmptyXML() const; } /** @@ -1677,7 +1721,7 @@ class DocumentParser : ElementParser body { xmlText = xmlText_; - s = &xmlText; + s = xmlText; super(); // Initialize everything parse(); // Parse through the root tag (but not beyond) } @@ -1704,7 +1748,7 @@ class ElementParser { Tag tag_; string elementStart; - string* s; + string s; Handler commentHandler = null; Handler cdataHandler = null; @@ -1714,7 +1758,7 @@ class ElementParser Handler textHandler = null; // Private constructor for start tags - this(ElementParser parent) + this(ElementParser parent) @safe @nogc pure nothrow { s = parent.s; this(); @@ -1722,7 +1766,7 @@ class ElementParser } // Private constructor for empty tags - this(Tag tag, string* t) + this(Tag tag, string t) @safe @nogc pure nothrow { s = t; this(); @@ -1734,7 +1778,7 @@ class ElementParser * The Tag at the start of the element being parsed. You can read this to * determine the tag's name and attributes. */ - @property const(Tag) tag() const { return tag_; } + @property @safe @nogc pure nothrow const(Tag) tag() const { return tag_; } /** * Register a handler which will be called whenever a start tag is @@ -1742,7 +1786,7 @@ class ElementParser * the name, in which case the handler will be called for any unmatched * start tag. * - * Examples: + * Example: * -------------- * // Call this function whenever a start tag is encountered * onStartTag["podcast"] = (ElementParser xml) @@ -1778,7 +1822,7 @@ class ElementParser * the name, in which case the handler will be called for any unmatched * end tag. * - * Examples: + * Example: * -------------- * // Call this function whenever a end tag is encountered * onEndTag["podcast"] = (in Element e) @@ -1803,15 +1847,15 @@ class ElementParser */ ElementHandler[string] onEndTag; - protected this() + protected this() @safe @nogc pure nothrow { - elementStart = *s; + elementStart = s; } /** * Register a handler which will be called whenever text is encountered. * - * Examples: + * Example: * -------------- * // Call this function whenever text is encountered * onText = (string s) @@ -1826,7 +1870,7 @@ class ElementParser * }; * -------------- */ - @property void onText(Handler handler) { textHandler = handler; } + @property @safe @nogc pure nothrow void onText(Handler handler) { textHandler = handler; } /** * Register an alternative handler which will be called whenever text @@ -1838,7 +1882,7 @@ class ElementParser * probably want to use onTextRaw only in circumstances where you * know that decoding is unnecessary. * - * Examples: + * Example: * -------------- * // Call this function whenever text is encountered * onText = (string s) @@ -1852,13 +1896,13 @@ class ElementParser * }; * -------------- */ - void onTextRaw(Handler handler) { rawTextHandler = handler; } + @safe @nogc pure nothrow void onTextRaw(Handler handler) { rawTextHandler = handler; } /** * Register a handler which will be called whenever a character data * segment is encountered. * - * Examples: + * Example: * -------------- * // Call this function whenever a CData section is encountered * onCData = (string s) @@ -1873,13 +1917,13 @@ class ElementParser * }; * -------------- */ - @property void onCData(Handler handler) { cdataHandler = handler; } + @property @safe @nogc pure nothrow void onCData(Handler handler) { cdataHandler = handler; } /** * Register a handler which will be called whenever a comment is * encountered. * - * Examples: + * Example: * -------------- * // Call this function whenever a comment is encountered * onComment = (string s) @@ -1894,13 +1938,13 @@ class ElementParser * }; * -------------- */ - @property void onComment(Handler handler) { commentHandler = handler; } + @property @safe @nogc pure nothrow void onComment(Handler handler) { commentHandler = handler; } /** * Register a handler which will be called whenever a processing * instruction is encountered. * - * Examples: + * Example: * -------------- * // Call this function whenever a processing instruction is encountered * onPI = (string s) @@ -1915,13 +1959,13 @@ class ElementParser * }; * -------------- */ - @property void onPI(Handler handler) { piHandler = handler; } + @property @safe @nogc pure nothrow void onPI(Handler handler) { piHandler = handler; } /** * Register a handler which will be called whenever an XML instruction is * encountered. * - * Examples: + * Example: * -------------- * // Call this function whenever an XML instruction is encountered * // (Note: XML instructions may only occur preceding the root tag of a @@ -1938,7 +1982,7 @@ class ElementParser * }; * -------------- */ - @property void onXI(Handler handler) { xiHandler = handler; } + @property @safe @nogc pure nothrow void onXI(Handler handler) { xiHandler = handler; } /** * Parse an XML element. @@ -1951,44 +1995,47 @@ class ElementParser */ void parse() { + import std.algorithm.searching : startsWith; + import std.string : indexOf; + string t; - Tag root = tag_; + const Tag root = tag_; Tag[string] startTags; if (tag_ !is null) startTags[tag_.name] = tag_; - while(s.length != 0) + while (s.length != 0) { - if (startsWith(*s,"")); + chop(s,4); + t = chop(s,indexOf(s,"-->")); if (commentHandler.funcptr !is null) commentHandler(t); - chop(*s,3); + chop(s,3); } - else if (startsWith(*s,"")); + chop(s,9); + t = chop(s,indexOf(s,"]]>")); if (cdataHandler.funcptr !is null) cdataHandler(t); - chop(*s,3); + chop(s,3); } - else if (startsWith(*s,"")); + chop(s,2); + t = chop(s,indexOf(s,">")); if (xiHandler.funcptr !is null) xiHandler(t); - chop(*s,1); + chop(s,1); } - else if (startsWith(*s,"")); + chop(s,2); + t = chop(s,indexOf(s,"?>")); if (piHandler.funcptr !is null) piHandler(t); - chop(*s,2); + chop(s,2); } - else if (startsWith(*s,"<")) + else if (startsWith(s,"<")) { - tag_ = new Tag(*s,true); + tag_ = new Tag(s,true); if (root is null) return; // Return to constructor of derived class @@ -2008,7 +2055,7 @@ class ElementParser } else if (tag_.isEnd) { - auto startTag = startTags[tag_.name]; + const startTag = startTags[tag_.name]; string text; immutable(char)* p = startTag.tagString.ptr @@ -2036,12 +2083,12 @@ class ElementParser // FIX by hed010gy, for bug 2979 // http://d.puremagic.com/issues/show_bug.cgi?id=2979 if (tag_.attr.length > 0) - foreach(tn,tv; tag_.attr) startTag.attr[tn]=tv; + foreach (tn,tv; tag_.attr) startTag.attr[tn]=tv; // END FIX // Handle the pretend start tag string s2; - auto parser = new ElementParser(startTag,&s2); + auto parser = new ElementParser(startTag,s2); auto handler1 = startTag.name in onStartTag; if (handler1 !is null) (*handler1)(parser); else @@ -2063,7 +2110,7 @@ class ElementParser } else { - t = chop(*s,indexOf(*s,"<")); + t = chop(s,indexOf(s,"<")); if (rawTextHandler.funcptr !is null) rawTextHandler(t); else if (textHandler.funcptr !is null) @@ -2075,7 +2122,7 @@ class ElementParser /** * Returns that part of the element which has already been parsed */ - override string toString() const + override string toString() const @nogc @safe pure nothrow { assert(elementStart.length >= s.length); return elementStart[0 .. elementStart.length - s.length]; @@ -2089,26 +2136,28 @@ private { string old = s; - void fail() + void fail() @safe pure { s = old; throw new Err(s,msg); } - void fail(Err e) + void fail(Err e) @safe pure { s = old; throw new Err(s,msg,e); } - void fail(string msg2) + void fail(string msg2) @safe pure { fail(new Err(s,msg2)); } } - void checkMisc(ref string s) // rule 27 + void checkMisc(ref string s) @safe pure // rule 27 { + import std.algorithm.searching : startsWith; + mixin Check!("Misc"); try @@ -2117,10 +2166,10 @@ private else if (s.startsWith(" !isWhite(a)); + if (i == -1 && s.length > 0 && isWhite(s[0])) + s = s[$ .. $]; + else if (i > -1) + s = s[i .. $]; if (s is old) fail(); } - void checkName(ref string s, out string name) // rule 5 + void checkName(ref string s, out string name) @safe pure // rule 5 { mixin Check!("Name"); if (s.length == 0) fail(); int n; - foreach(int i,dchar c;s) + foreach (int i,dchar c;s) { if (c == '_' || c == ':' || isLetter(c)) continue; if (i == 0) fail(); @@ -2179,12 +2237,15 @@ private n = i; break; } - name = s[0..n]; + name = s[0 .. n]; s = s[n..$]; } - void checkAttValue(ref string s) // rule 10 + void checkAttValue(ref string s) @safe pure // rule 10 { + import std.algorithm.searching : countUntil; + import std.utf : byCodeUnit; + mixin Check!("AttValue"); if (s.length == 0) fail(); @@ -2192,19 +2253,21 @@ private if (c != '\u0022' && c != '\u0027') fail("attribute value requires quotes"); s = s[1..$]; - for(;;) + for (;;) { - munch(s,"^<&"~c); + s = s[s.byCodeUnit.countUntil(c) .. $]; if (s.length == 0) fail("unterminated attribute value"); if (s[0] == '<') fail("< found in attribute value"); if (s[0] == c) break; - try { checkReference(s); } catch(Err e) { fail(e); } + try { checkReference(s); } catch (Err e) { fail(e); } } s = s[1..$]; } - void checkCharData(ref string s) // rule 14 + void checkCharData(ref string s) @safe pure // rule 14 { + import std.algorithm.searching : startsWith; + mixin Check!("CharData"); while (s.length != 0) @@ -2216,18 +2279,20 @@ private } } - void checkComment(ref string s) // rule 15 + void checkComment(ref string s) @safe pure // rule 15 { + import std.string : indexOf; + mixin Check!("Comment"); - try { checkLiteral("",s); } catch(Err e) { fail(e); } + try { checkLiteral("-->",s); } catch (Err e) { fail(e); } } - void checkPI(ref string s) // rule 16 + void checkPI(ref string s) @safe pure // rule 16 { mixin Check!("PI"); @@ -2236,10 +2301,10 @@ private checkLiteral("",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkCDSect(ref string s) // rule 18 + void checkCDSect(ref string s) @safe pure // rule 18 { mixin Check!("CDSect"); @@ -2248,10 +2313,10 @@ private checkLiteral(cdata,s); checkEnd("]]>",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkProlog(ref string s) // rule 22 + void checkProlog(ref string s) @safe pure // rule 22 { mixin Check!("Prolog"); @@ -2265,10 +2330,10 @@ private star!(checkMisc)(s); opt!(seq!(checkDocTypeDecl,star!(checkMisc)))(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkXMLDecl(ref string s) // rule 23 + void checkXMLDecl(ref string s) @safe pure // rule 23 { mixin Check!("XMLDecl"); @@ -2281,10 +2346,10 @@ private opt!(checkSpace)(s); checkLiteral("?>",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkVersionInfo(ref string s) // rule 24 + void checkVersionInfo(ref string s) @safe pure // rule 24 { mixin Check!("VersionInfo"); @@ -2295,10 +2360,10 @@ private checkEq(s); quoted!(checkVersionNum)(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkEq(ref string s) // rule 25 + void checkEq(ref string s) @safe pure // rule 25 { mixin Check!("Eq"); @@ -2308,18 +2373,21 @@ private checkLiteral("=",s); opt!(checkSpace)(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkVersionNum(ref string s) // rule 26 + void checkVersionNum(ref string s) @safe pure // rule 26 { + import std.algorithm.searching : countUntil; + import std.utf : byCodeUnit; + mixin Check!("VersionNum"); - munch(s,"a-zA-Z0-9_.:-"); + s = s[s.byCodeUnit.countUntil('\"') .. $]; if (s is old) fail(); } - void checkDocTypeDecl(ref string s) // rule 28 + void checkDocTypeDecl(ref string s) @safe pure // rule 28 { mixin Check!("DocTypeDecl"); @@ -2332,11 +2400,13 @@ private // checkEnd(">",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkSDDecl(ref string s) // rule 32 + void checkSDDecl(ref string s) @safe pure // rule 32 { + import std.algorithm.searching : startsWith; + mixin Check!("SDDecl"); try @@ -2345,7 +2415,7 @@ private checkLiteral("standalone",s); checkEq(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } int n = 0; if (s.startsWith("'yes'") || s.startsWith("\"yes\"")) n = 5; @@ -2355,12 +2425,12 @@ private s = s[n..$]; } - void checkElement(ref string s) // rule 39 + void checkElement(ref string s) @safe pure // rule 39 { mixin Check!("Element"); string sname,ename,t; - try { checkTag(s,t,sname); } catch(Err e) { fail(e); } + try { checkTag(s,t,sname); } catch (Err e) { fail(e); } if (t == "STag") { @@ -2370,7 +2440,7 @@ private t = s; checkETag(s,ename); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } if (sname != ename) { @@ -2382,7 +2452,7 @@ private } // rules 40 and 44 - void checkTag(ref string s, out string type, out string name) + void checkTag(ref string s, out string type, out string name) @safe pure { mixin Check!("Tag"); @@ -2400,10 +2470,10 @@ private } checkLiteral(">",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkAttribute(ref string s) // rule 41 + void checkAttribute(ref string s) @safe pure // rule 41 { mixin Check!("Attribute"); @@ -2414,10 +2484,10 @@ private checkEq(s); checkAttValue(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkETag(ref string s, out string name) // rule 42 + void checkETag(ref string s, out string name) @safe pure // rule 42 { mixin Check!("ETag"); @@ -2428,11 +2498,13 @@ private opt!(checkSpace)(s); checkLiteral(">",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkContent(ref string s) // rule 43 + void checkContent(ref string s) @safe pure // rule 43 { + import std.algorithm.searching : startsWith; + mixin Check!("Content"); try @@ -2449,15 +2521,17 @@ private else { checkCharData(s); } } } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkCharRef(ref string s, out dchar c) // rule 66 + void checkCharRef(ref string s, out dchar c) @safe pure // rule 66 { + import std.format : format; + mixin Check!("CharRef"); c = 0; - try { checkLiteral("&#",s); } catch(Err e) { fail(e); } + try { checkLiteral("&#",s); } catch (Err e) { fail(e); } int radix = 10; if (s.length != 0 && s[0] == 'x') { @@ -2469,9 +2543,9 @@ private fail("character reference must have at least one digit"); while (s.length != 0) { - char d = s[0]; + immutable char d = s[0]; int n = 0; - switch(d) + switch (d) { case 'F','f': ++n; goto case; case 'E','e': ++n; goto case; @@ -2501,8 +2575,10 @@ private else s = s[1..$]; } - void checkReference(ref string s) // rule 67 + void checkReference(ref string s) @safe pure // rule 67 { + import std.algorithm.searching : startsWith; + mixin Check!("Reference"); try @@ -2511,10 +2587,10 @@ private if (s.startsWith("&#")) checkCharRef(s,c); else checkEntityRef(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkEntityRef(ref string s) // rule 68 + void checkEntityRef(ref string s) @safe pure // rule 68 { mixin Check!("EntityRef"); @@ -2525,19 +2601,23 @@ private checkName(s,name); checkLiteral(";",s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } - void checkEncName(ref string s) // rule 81 + void checkEncName(ref string s) @safe pure // rule 81 { + import std.algorithm.searching : countUntil; + import std.ascii : isAlpha; + import std.utf : byCodeUnit; + mixin Check!("EncName"); - munch(s,"a-zA-Z"); + s = s[s.byCodeUnit.countUntil!(a => !isAlpha(a)) .. $]; if (s is old) fail(); - munch(s,"a-zA-Z0-9_.-"); + s = s[s.byCodeUnit.countUntil('\"', '\'') .. $]; } - void checkEncodingDecl(ref string s) // rule 80 + void checkEncodingDecl(ref string s) @safe pure // rule 80 { mixin Check!("EncodingDecl"); @@ -2548,21 +2628,24 @@ private checkEq(s); quoted!(checkEncName)(s); } - catch(Err e) { fail(e); } + catch (Err e) { fail(e); } } // Helper functions - void checkLiteral(string literal,ref string s) + void checkLiteral(string literal,ref string s) @safe pure { + import std.string : startsWith; + mixin Check!("Literal"); if (!s.startsWith(literal)) fail("Expected literal \""~literal~"\""); s = s[literal.length..$]; } - void checkEnd(string end,ref string s) + void checkEnd(string end,ref string s) @safe pure { + import std.string : indexOf; // Deliberately no mixin Check here. auto n = s.indexOf(end); @@ -2575,7 +2658,7 @@ private void opt(alias f)(ref string s) { - try { f(s); } catch(Err e) {} + try { f(s); } catch (Err e) {} } void plus(alias f)(ref string s) @@ -2589,12 +2672,14 @@ private while (s.length != 0) { try { f(s); } - catch(Err e) { return; } + catch (Err e) { return; } } } void quoted(alias f)(ref string s) { + import std.string : startsWith; + if (s.startsWith("'")) { checkLiteral("'",s); @@ -2628,7 +2713,7 @@ private * parse failure (the XML equivalent of a stack trace), giving the line and * column number of every failure at every level. */ -void check(string s) +void check(string s) pure { try { @@ -2636,17 +2721,17 @@ void check(string s) checkDocument(s); if (s.length != 0) throw new Err(s,"Junk found after document"); } - catch(Err e) + catch (Err e) { e.complete(s); throw e; } } -unittest +@system pure unittest { - version (none) // WHY ARE WE NOT RUNNING THIS UNIT TEST? - { + import std.string : indexOf; + try { check(q"[ @@ -2682,18 +2767,17 @@ unittest ]"); - assert(false); + assert(false); } - catch(CheckException e) + catch (CheckException e) { - int n = e.toString().indexOf("end tag name \"genres\" differs"~ - " from start tag name \"genre\""); + auto n = e.toString().indexOf("end tag name \"genres\" differs"~ + " from start tag name \"genre\""); assert(n != -1); } - } } -unittest +@system unittest { string s = q"EOS @@ -2713,7 +2797,27 @@ EOS"; } } -unittest +@system unittest +{ + string test_xml = ` + `; + + DocumentParser parser = new DocumentParser(test_xml); + bool tested = false; + parser.onStartTag["stream:stream"] = (ElementParser p) { + assert(p.tag.attr["xmlns"] == "jabber:'client'"); + assert(p.tag.attr["from"] == "jid.pl"); + assert(p.tag.attr["attr"] == "a\"b\"c"); + tested = true; + }; + parser.parse(); + assert(tested); +} + +@system unittest { string s = q"EOS @@ -2732,7 +2836,7 @@ EOS"; xml.parse(); } -unittest +@system unittest { string s = ``; auto doc = new Document(s); @@ -2740,41 +2844,41 @@ unittest } /** The base class for exceptions thrown by this module */ -class XMLException : Exception { this(string msg) { super(msg); } } +class XMLException : Exception { this(string msg) @safe pure { super(msg); } } // Other exceptions /// Thrown during Comment constructor class CommentException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown during CData constructor class CDataException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown during XMLInstruction constructor class XIException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown during ProcessingInstruction constructor class PIException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown during Text constructor class TextException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown during decode() class DecodeException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown if comparing with wrong type class InvalidTypeException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /// Thrown when parsing for Tags class TagException : XMLException -{ private this(string msg) { super(msg); } } +{ private this(string msg) @safe pure { super(msg); } } /** * Thrown during check() @@ -2791,7 +2895,7 @@ class CheckException : XMLException size_t line = 0; /// Line number at which parse failure occurred size_t column = 0; /// Column number at which parse failure occurred - private this(string tail,string msg,Err err=null) + private this(string tail,string msg,Err err=null) @safe pure { super(null); this.tail = tail; @@ -2799,8 +2903,11 @@ class CheckException : XMLException this.err = err; } - private void complete(string entire) + private void complete(string entire) pure { + import std.encoding : transcode; + import std.string : count, lastIndexOf; + string head = entire[0..$-tail.length]; ptrdiff_t n = head.lastIndexOf('\n') + 1; line = head.count("\n") + 1; @@ -2810,8 +2917,10 @@ class CheckException : XMLException if (err !is null) err.complete(entire); } - override string toString() const + override string toString() const @safe pure { + import std.format : format; + string s; if (line != 0) s = format("Line %d, column %d: ",line,column); s ~= msg; @@ -2838,27 +2947,38 @@ private return t; } - string chop(ref string s, size_t n) + string chop(ref string s, size_t n) @safe pure nothrow { if (n == -1) n = s.length; - string t = s[0..n]; + string t = s[0 .. n]; s = s[n..$]; return t; } - bool optc(ref string s, char c) + bool optc(ref string s, char c) @safe pure nothrow { - bool b = s.length != 0 && s[0] == c; + immutable bool b = s.length != 0 && s[0] == c; if (b) s = s[1..$]; return b; } - void reqc(ref string s, char c) + void reqc(ref string s, char c) @safe pure { if (s.length == 0 || s[0] != c) throw new TagException(""); s = s[1..$]; } + char requireOneOf(ref string s, string chars) @safe pure + { + import std.string : indexOf; + + if (s.length == 0 || indexOf(chars,s[0]) == -1) + throw new TagException(""); + immutable char ch = s[0]; + s = s[1..$]; + return ch; + } + size_t hash(string s,size_t h=0) @trusted nothrow { return typeid(s).getHash(&s) + h; @@ -2937,14 +3057,14 @@ private 0x0387,0x0640,0x0640,0x0E46,0x0E46,0x0EC6,0x0EC6,0x3005,0x3005,0x3031, 0x3035,0x309D,0x309E,0x30FC,0x30FE]; - bool lookup(const(int)[] table, int c) + bool lookup(const(int)[] table, int c) @safe @nogc nothrow pure { while (table.length != 0) { auto m = (table.length >> 1) & ~1; if (c < table[m]) { - table = table[0..m]; + table = table[0 .. m]; } else if (c > table[m+1]) { @@ -2955,10 +3075,10 @@ private return false; } - string startOf(string s) + string startOf(string s) @safe nothrow pure { string r; - foreach(char c;s) + foreach (char c;s) { r ~= (c < 0x20 || c > 0x7F) ? '.' : c; if (r.length >= 40) { r ~= "___"; break; } @@ -2971,4 +3091,3 @@ private throw new XMLException(s); } } - diff --git a/std/zip.d b/std/zip.d index 412d5810aff..961eaa4f974 100644 --- a/std/zip.d +++ b/std/zip.d @@ -1,7 +1,7 @@ // Written in the D programming language. /** - * Read/write data in the $(LINK2 http://www.info-_zip.org, zip archive) format. + * Read/write data in the $(LINK2 http://www.info-zip.org, _zip archive) format. * Makes use of the etc.c.zlib compression library. * * Bugs: @@ -14,11 +14,9 @@ * $(LI $(BUGZILLA 2137)) * ) * - * Macros: - * WIKI = Phobos/StdZip - * - * Examples: + * Example: * --- +// Read existing zip file. import std.digest.crc, std.file, std.stdio, std.zip; void main(string[] args) @@ -38,11 +36,31 @@ void main(string[] args) assert(am.expandedData.length == am.expandedSize); } } + +// Create and write new zip file. +import std.file : write; +import std.string : representation; + +void main() +{ + char[] data = "Test data.\n".dup; + // Create an ArchiveMember for the test file. + ArchiveMember am = new ArchiveMember(); + am.name = "test.txt"; + am.expandedData(data.representation); + // Create an archive and add the member. + ZipArchive zip = new ZipArchive(); + zip.addMember(am); + // Build the archive + void[] compressed_data = zip.build(); + // Write to a file + write("test.zip", compressed_data); +} * --- * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Source: $(PHOBOSSRC std/_zip.d) */ @@ -59,7 +77,7 @@ module std.zip; */ class ZipException : Exception { - this(string msg) + this(string msg) @safe { super("ZipException: " ~ msg); } @@ -104,6 +122,8 @@ final class ArchiveMember private ushort _diskNumber; private uint _externalAttributes; private DosFileTime _time; + // by default, no explicit order goes after explicit order + private uint _index = uint.max; ushort flags; /// Read/Write: normally set to 0 ushort internalAttributes; /// Read/Write @@ -111,16 +131,6 @@ final class ArchiveMember @property ushort extractVersion() { return _extractVersion; } /// Read Only @property uint crc32() { return _crc32; } /// Read Only: cyclic redundancy check (CRC) value - // Explicitly undocumented. It will be removed in January 2015. - deprecated("Please use fileAttributes instead.") - @property ref inout(ushort) madeVersion() inout @safe pure nothrow - { return _madeVersion; } - - // Explicitly undocumented. It will be removed in January 2015. - deprecated("Please use fileAttributes instead.") - @property ref inout(uint) externalAttributes() inout @safe pure nothrow - { return _externalAttributes; } - /// Read Only: size of data of member in compressed form. @property uint compressedSize() { return _compressedSize; } @@ -135,7 +145,7 @@ final class ArchiveMember @property ubyte[] expandedData() { return _expandedData; } /// Write data of member in uncompressed form. - @property void expandedData(ubyte[] ed) + @property @safe void expandedData(ubyte[] ed) { _expandedData = ed; _expandedSize = to!uint(_expandedData.length); @@ -147,9 +157,9 @@ final class ArchiveMember /** * Set the OS specific file attributes, as obtained by - * $(XREF file,getAttributes) or $(XREF file,DirEntry.attributes), for this archive member. + * $(REF getAttributes, std,file) or $(REF DirEntry.attributes, std,file), for this archive member. */ - @property void fileAttributes(uint attr) + @property @safe void fileAttributes(uint attr) { version (Posix) { @@ -168,7 +178,7 @@ final class ArchiveMember } } - version (Posix) unittest + version (Posix) @safe unittest { auto am = new ArchiveMember(); am.fileAttributes = octal!100644; @@ -226,14 +236,7 @@ final class ArchiveMember * See_Also: * CompressionMethod **/ - @property CompressionMethod compressionMethod() { return _compressionMethod; } - - // Explicitly undocumented. It will be removed in January 2015. - deprecated("Please use the enum CompressionMethod to set this property instead.") - @property void compressionMethod(ushort cm) - { - compressionMethod = cast(CompressionMethod)(cm); - } + @property @safe CompressionMethod compressionMethod() { return _compressionMethod; } /** * Write compression method used for this member @@ -250,6 +253,12 @@ final class ArchiveMember _compressionMethod = cm; } + /** + * The index of this archive member within the archive. + */ + @property uint index() const pure nothrow @nogc { return _index; } + @property uint index(uint value) pure nothrow @nogc { return _index = value; } + debug(print) { void print() @@ -266,6 +275,7 @@ final class ArchiveMember printf("\tcompressedSize = %d\n", compressedSize); printf("\tinternalAttributes = x%04x\n", internalAttributes); printf("\texternalAttributes = x%08x\n", externalAttributes); + printf("\tindex = x%08x\n", index); } } } @@ -277,9 +287,8 @@ final class ArchiveMember final class ZipArchive { import std.bitmanip : littleEndianToNative, nativeToLittleEndian; - import std.algorithm : max; + import std.algorithm.comparison : max; import std.conv : to; - import std.zlib : compress; import std.datetime : DosFileTime; string comment; /// Read/Write: the archive comment. Must be less than 65536 bytes in length. @@ -298,23 +307,23 @@ final class ZipArchive static const int eocd64Length = 56; /// Read Only: array representing the entire contents of the archive. - @property ubyte[] data() { return _data; } + @property @safe ubyte[] data() { return _data; } /// Read Only: 0 since multi-disk zip archives are not supported. - @property uint diskNumber() { return _diskNumber; } + @property @safe uint diskNumber() { return _diskNumber; } /// Read Only: 0 since multi-disk zip archives are not supported - @property uint diskStartDir() { return _diskStartDir; } + @property @safe uint diskStartDir() { return _diskStartDir; } /// Read Only: number of ArchiveMembers in the directory. - @property uint numEntries() { return _numEntries; } - @property uint totalEntries() { return _totalEntries; } /// ditto + @property @safe uint numEntries() { return _numEntries; } + @property @safe uint totalEntries() { return _totalEntries; } /// ditto /// True when the archive is in Zip64 format. - @property bool isZip64() { return _isZip64; } + @property @safe bool isZip64() { return _isZip64; } /// Set this to true to force building a Zip64 archive. - @property void isZip64(bool value) { _isZip64 = value; } + @property @safe void isZip64(bool value) { _isZip64 = value; } /** * Read Only: array indexed by the name of each member of the archive. * All the members of the archive can be accessed with a foreach loop: @@ -327,13 +336,13 @@ final class ZipArchive * } * -------------------- */ - @property ArchiveMember[string] directory() { return _directory; } + @property @safe ArchiveMember[string] directory() { return _directory; } private ArchiveMember[string] _directory; debug (print) { - void print() + @safe void print() { printf("\tdiskNumber = %u\n", diskNumber); printf("\tdiskStartDir = %u\n", diskStartDir); @@ -347,20 +356,20 @@ final class ZipArchive /** Constructor to use when creating a new archive. */ - this() + this() @safe { } /** Add de to the archive. */ - void addMember(ArchiveMember de) + @safe void addMember(ArchiveMember de) { _directory[de.name] = de; } /** Delete de from the archive. */ - void deleteMember(ArchiveMember de) + @safe void deleteMember(ArchiveMember de) { _directory.remove(de.name); } @@ -376,7 +385,9 @@ final class ZipArchive * Returns: array representing the entire archive. */ void[] build() - { uint i; + { + import std.algorithm.sorting : sort; + uint i; uint directoryOffset; if (comment.length > 0xFFFF) @@ -385,7 +396,8 @@ final class ZipArchive // Compress each member; compute size uint archiveSize = 0; uint directorySize = 0; - foreach (ArchiveMember de; _directory) + auto directory = _directory.values().sort!((x, y) => x.index < y.index).release; + foreach (ArchiveMember de; directory) { if (!de._compressedData.length) { @@ -396,7 +408,8 @@ final class ZipArchive break; case CompressionMethod.deflate: - de._compressedData = cast(ubyte[])std.zlib.compress(cast(void[])de._expandedData); + import std.zlib : compress; + de._compressedData = cast(ubyte[]) compress(cast(void[]) de._expandedData); de._compressedData = de._compressedData[2 .. de._compressedData.length - 4]; break; @@ -405,7 +418,8 @@ final class ZipArchive } de._compressedSize = to!uint(de._compressedData.length); - de._crc32 = std.zlib.crc32(0, cast(void[])de._expandedData); + import std.zlib : crc32; + de._crc32 = crc32(0, cast(void[]) de._expandedData); } assert(de._compressedData.length == de._compressedSize); @@ -424,7 +438,7 @@ final class ZipArchive if (!isZip64 && _directory.length > ushort.max) _isZip64 = true; - uint dataSize = archiveSize + directorySize + 22 + cast(uint)comment.length; + uint dataSize = archiveSize + directorySize + 22 + cast(uint) comment.length; if (isZip64) dataSize += eocd64LocLength + eocd64Length; @@ -434,24 +448,24 @@ final class ZipArchive // Store each archive member i = 0; - foreach (ArchiveMember de; _directory) + foreach (ArchiveMember de; directory) { de.offset = i; _data[i .. i + 4] = cast(ubyte[])"PK\x03\x04"; putUshort(i + 4, de.extractVersion); putUshort(i + 6, de.flags); putUshort(i + 8, de._compressionMethod); - putUint (i + 10, cast(uint)de.time); + putUint (i + 10, cast(uint) de.time); putUint (i + 14, de.crc32); putUint (i + 18, de.compressedSize); putUint (i + 22, to!uint(de.expandedSize)); - putUshort(i + 26, cast(ushort)de.name.length); - putUshort(i + 28, cast(ushort)de.extra.length); + putUshort(i + 26, cast(ushort) de.name.length); + putUshort(i + 28, cast(ushort) de.extra.length); i += 30; - _data[i .. i + de.name.length] = (cast(ubyte[])de.name)[]; + _data[i .. i + de.name.length] = (cast(ubyte[]) de.name)[]; i += de.name.length; - _data[i .. i + de.extra.length] = (cast(ubyte[])de.extra)[]; + _data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[]; i += de.extra.length; _data[i .. i + de.compressedSize] = de.compressedData[]; i += de.compressedSize; @@ -460,31 +474,31 @@ final class ZipArchive // Write directory directoryOffset = i; _numEntries = 0; - foreach (ArchiveMember de; _directory) + foreach (ArchiveMember de; directory) { _data[i .. i + 4] = cast(ubyte[])"PK\x01\x02"; putUshort(i + 4, de._madeVersion); putUshort(i + 6, de.extractVersion); putUshort(i + 8, de.flags); putUshort(i + 10, de._compressionMethod); - putUint (i + 12, cast(uint)de.time); + putUint (i + 12, cast(uint) de.time); putUint (i + 16, de.crc32); putUint (i + 20, de.compressedSize); putUint (i + 24, de.expandedSize); - putUshort(i + 28, cast(ushort)de.name.length); - putUshort(i + 30, cast(ushort)de.extra.length); - putUshort(i + 32, cast(ushort)de.comment.length); + putUshort(i + 28, cast(ushort) de.name.length); + putUshort(i + 30, cast(ushort) de.extra.length); + putUshort(i + 32, cast(ushort) de.comment.length); putUshort(i + 34, de.diskNumber); putUshort(i + 36, de.internalAttributes); putUint (i + 38, de._externalAttributes); putUint (i + 42, de.offset); i += 46; - _data[i .. i + de.name.length] = (cast(ubyte[])de.name)[]; + _data[i .. i + de.name.length] = (cast(ubyte[]) de.name)[]; i += de.name.length; - _data[i .. i + de.extra.length] = (cast(ubyte[])de.extra)[]; + _data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[]; i += de.extra.length; - _data[i .. i + de.comment.length] = (cast(ubyte[])de.comment)[]; + _data[i .. i + de.comment.length] = (cast(ubyte[]) de.comment)[]; i += de.comment.length; _numEntries++; } @@ -517,20 +531,20 @@ final class ZipArchive // Write end record endrecOffset = i; _data[i .. i + 4] = cast(ubyte[])"PK\x05\x06"; - putUshort(i + 4, cast(ushort)diskNumber); - putUshort(i + 6, cast(ushort)diskStartDir); - putUshort(i + 8, (numEntries > ushort.max ? ushort.max : cast(ushort)numEntries)); - putUshort(i + 10, (totalEntries > ushort.max ? ushort.max : cast(ushort)totalEntries)); + putUshort(i + 4, cast(ushort) diskNumber); + putUshort(i + 6, cast(ushort) diskStartDir); + putUshort(i + 8, (numEntries > ushort.max ? ushort.max : cast(ushort) numEntries)); + putUshort(i + 10, (totalEntries > ushort.max ? ushort.max : cast(ushort) totalEntries)); putUint (i + 12, directorySize); putUint (i + 16, directoryOffset); - putUshort(i + 20, cast(ushort)comment.length); + putUshort(i + 20, cast(ushort) comment.length); i += 22; // Write archive comment assert(i + comment.length == data.length); - _data[i .. data.length] = (cast(ubyte[])comment)[]; + _data[i .. data.length] = (cast(ubyte[]) comment)[]; - return cast(void[])data; + return cast(void[]) data; } /* ============ Reading an existing archive =================== */ @@ -563,7 +577,7 @@ final class ZipArchive throw new ZipException("zip files bigger than 4 GB are unsupported"); // Find 'end record index' by searching backwards for signature - iend = (data.length > 66000 ? to!uint(data.length - 66000) : 0); + iend = (data.length > 66_000 ? to!uint(data.length - 66_000) : 0); for (i = to!uint(data.length) - 22; 1; i--) { if (i < iend || i >= data.length) @@ -592,7 +606,6 @@ final class ZipArchive if (isZip64) { // Read Zip64 record data - uint eocd64LocStart = i; ulong eocdOffset = getUlong(i + 8); if (eocdOffset + eocd64Length > _data.length) throw new ZipException("corrupted directory"); @@ -658,7 +671,6 @@ final class ZipArchive * comment */ - uint offset; uint namelen; uint extralen; uint commentlen; @@ -666,11 +678,12 @@ final class ZipArchive if (_data[i .. i + 4] != cast(ubyte[])"PK\x01\x02") throw new ZipException("invalid directory entry 1"); ArchiveMember de = new ArchiveMember(); + de._index = n; de._madeVersion = getUshort(i + 4); de._extractVersion = getUshort(i + 6); de.flags = getUshort(i + 8); - de._compressionMethod = cast(CompressionMethod)getUshort(i + 10); - de.time = cast(DosFileTime)getUint(i + 12); + de._compressionMethod = cast(CompressionMethod) getUshort(i + 10); + de.time = cast(DosFileTime) getUint(i + 12); de._crc32 = getUint(i + 16); de._compressedSize = getUint(i + 20); de._expandedSize = getUint(i + 24); @@ -722,8 +735,8 @@ final class ZipArchive // These values should match what is in the main zip archive directory de._extractVersion = getUshort(de.offset + 4); de.flags = getUshort(de.offset + 6); - de._compressionMethod = cast(CompressionMethod)getUshort(de.offset + 8); - de.time = cast(DosFileTime)getUint(de.offset + 10); + de._compressionMethod = cast(CompressionMethod) getUshort(de.offset + 8); + de.time = cast(DosFileTime) getUint(de.offset + 10); de._crc32 = getUint(de.offset + 14); de._compressedSize = max(getUint(de.offset + 18), de.compressedSize); de._expandedSize = max(getUint(de.offset + 22), de.expandedSize); @@ -759,7 +772,8 @@ final class ZipArchive // -15 is a magic value used to decompress zip files. // It has the effect of not requiring the 2 byte header // and 4 byte trailer. - de._expandedData = cast(ubyte[])std.zlib.uncompress(cast(void[])de.compressedData, de.expandedSize, -15); + import std.zlib : uncompress; + de._expandedData = cast(ubyte[]) uncompress(cast(void[]) de.compressedData, de.expandedSize, -15); return de.expandedData; default: @@ -769,35 +783,35 @@ final class ZipArchive /* ============ Utility =================== */ - ushort getUshort(int i) + @safe ushort getUshort(int i) { ubyte[2] result = data[i .. i + 2]; return littleEndianToNative!ushort(result); } - uint getUint(int i) + @safe uint getUint(int i) { ubyte[4] result = data[i .. i + 4]; return littleEndianToNative!uint(result); } - ulong getUlong(int i) + @safe ulong getUlong(int i) { ubyte[8] result = data[i .. i + 8]; return littleEndianToNative!ulong(result); } - void putUshort(int i, ushort us) + @safe void putUshort(int i, ushort us) { data[i .. i + 2] = nativeToLittleEndian(us); } - void putUint(int i, uint ui) + @safe void putUint(int i, uint ui) { data[i .. i + 4] = nativeToLittleEndian(ui); } - void putUlong(int i, ulong ul) + @safe void putUlong(int i, ulong ul) { data[i .. i + 8] = nativeToLittleEndian(ul); } @@ -805,9 +819,9 @@ final class ZipArchive debug(print) { - void arrayPrint(ubyte[] array) + @safe void arrayPrint(ubyte[] array) { - printf("array %p,%d\n", cast(void*)array, array.length); + printf("array %p,%d\n", cast(void*) array, array.length); for (int i = 0; i < array.length; i++) { printf("%02x ", array[i]); @@ -818,8 +832,9 @@ debug(print) } } -unittest +@system unittest { + // @system due to (at least) ZipArchive.build auto zip1 = new ZipArchive(); auto zip2 = new ZipArchive(); auto am1 = new ArchiveMember(); @@ -841,13 +856,13 @@ unittest import std.stdio, std.conv; MinstdRand0 gen; const uint itemCount = 20, minSize = 10, maxSize = 500; - foreach (variant; 0..2) + foreach (variant; 0 .. 2) { bool useZip64 = !!variant; zip1 = new ZipArchive(); zip1.isZip64 = useZip64; ArchiveMember[itemCount] ams; - foreach (i; 0..itemCount) + foreach (i; 0 .. itemCount) { ams[i] = new ArchiveMember(); ams[i].name = to!string(i); @@ -869,3 +884,105 @@ unittest } } } + +@system unittest +{ + import std.random : Mt19937, randomShuffle; + import std.conv : to; + // Test if packing and unpacking preserves order. + auto rand = Mt19937(15966); + string[] names; + int value = 0; + // Generate a series of unique numbers as filenames. + foreach (i; 0 .. 20) + { + value += 1 + rand.front & 0xFFFF; + rand.popFront; + names ~= value.to!string; + } + // Insert them in a random order. + names.randomShuffle(rand); + auto zip1 = new ZipArchive(); + foreach (i, name; names) + { + auto member = new ArchiveMember(); + member.name = name; + member.expandedData = cast(ubyte[]) name; + member.index = cast(int) i; + zip1.addMember(member); + } + auto data = zip1.build(); + + // Ensure that they appear in the same order. + auto zip2 = new ZipArchive(data); + foreach (i, name; names) + { + const member = zip2.directory[name]; + assert(member.index == i, "member " ~ name ~ " had index " ~ + member.index.to!string ~ " but we expected index " ~ i.to!string ~ + ". The input array was " ~ names.to!string); + } +} + +@system unittest +{ + import std.zlib; + + ubyte[] src = cast(ubyte[]) +"the quick brown fox jumps over the lazy dog\r +the quick brown fox jumps over the lazy dog\r +"; + auto dst = cast(ubyte[]) compress(cast(void[]) src); + auto after = cast(ubyte[]) uncompress(cast(void[]) dst); + assert(src == after); +} + +@system unittest +{ + // @system due to ZipArchive.build + import std.datetime; + ubyte[] buf = [1, 2, 3, 4, 5, 0, 7, 8, 9]; + + auto ar = new ZipArchive; + auto am = new ArchiveMember; // 10 + am.name = "buf"; + am.expandedData = buf; + am.compressionMethod = CompressionMethod.deflate; + am.time = SysTimeToDosFileTime(Clock.currTime()); + ar.addMember(am); // 15 + + auto zip1 = ar.build(); + auto arAfter = new ZipArchive(zip1); + assert(arAfter.directory.length == 1); + auto amAfter = arAfter.directory["buf"]; + arAfter.expand(amAfter); + assert(amAfter.name == am.name); + assert(amAfter.expandedData == am.expandedData); + assert(amAfter.time == am.time); +} + +// Non-Android Posix-only, because we can't rely on the unzip command being +// available on Android or Windows +version(Android) {} else +version(Posix) @system unittest +{ + import std.datetime, std.file, std.format, std.path, std.process, std.stdio; + + auto zr = new ZipArchive(); + auto am = new ArchiveMember(); + am.compressionMethod = CompressionMethod.deflate; + am.name = "foo.bar"; + am.time = SysTimeToDosFileTime(Clock.currTime()); + am.expandedData = cast(ubyte[])"We all live in a yellow submarine, a yellow submarine"; + zr.addMember(am); + auto data2 = zr.build(); + + mkdirRecurse(deleteme); + scope(exit) rmdirRecurse(deleteme); + string zipFile = buildPath(deleteme, "foo.zip"); + std.file.write(zipFile, cast(byte[]) data2); + + auto result = executeShell(format("unzip -l %s", zipFile)); + scope(failure) writeln(result.output); + assert(result.status == 0); +} diff --git a/std/zlib.d b/std/zlib.d index cf1e7668a2d..68954719077 100644 --- a/std/zlib.d +++ b/std/zlib.d @@ -1,17 +1,51 @@ // Written in the D programming language. /** - * Compress/decompress data using the $(WEB www.zlib.net, zlib library). + * Compress/decompress data using the $(HTTP www._zlib.net, _zlib library). * - * References: - * $(WEB en.wikipedia.org/wiki/Zlib, Wikipedia) + * Examples: + * + * If you have a small buffer you can use $(LREF compress) and + * $(LREF uncompress) directly. + * + * ------- + * import std.zlib; + * + * auto src = + * "the quick brown fox jumps over the lazy dog\r + * the quick brown fox jumps over the lazy dog\r"; + * + * ubyte[] dst; + * ubyte[] result; * - * Macros: - * WIKI = Phobos/StdZlib + * dst = compress(src); + * result = cast(ubyte[]) uncompress(dst); + * assert(result == src); + * ------- + * + * When the data to be compressed doesn't fit in one buffer, use + * $(LREF Compress) and $(LREF UnCompress). + * + * ------- + * import std.zlib; + * import std.stdio; + * import std.conv : to; + * import std.algorithm.iteration : map; + * + * UnCompress decmp = new UnCompress; + * foreach (chunk; stdin.byChunk(4096).map!(x => decmp.uncompress(x))) + * { + * chunk.to!string.write; + * } + + * ------- + * + * References: + * $(HTTP en.wikipedia.org/wiki/Zlib, Wikipedia) * * Copyright: Copyright Digital Mars 2000 - 2011. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * Source: $(PHOBOSSRC std/_zlib.d) */ /* Copyright Digital Mars 2000 - 2011. @@ -64,7 +98,7 @@ class ZlibException : Exception * $(P Compute the Adler-32 checksum of a buffer's worth of data.) * * Params: - * adler = the starting checksum for the computation. Use 0 + * adler = the starting checksum for the computation. Use 1 * for a new checksum. Use the output of this function * for a cumulative checksum. * buf = buffer containing input data @@ -79,25 +113,30 @@ class ZlibException : Exception uint adler32(uint adler, const(void)[] buf) { import std.range : chunks; - foreach(chunk; (cast(ubyte[])buf).chunks(0xFFFF0000)) + foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000)) { - adler = etc.c.zlib.adler32(adler, chunk.ptr, cast(uint)chunk.length); + adler = etc.c.zlib.adler32(adler, chunk.ptr, cast(uint) chunk.length); } return adler; } -unittest +/// +@system unittest { static ubyte[] data = [1,2,3,4,5,6,7,8,9,10]; - uint adler; - - debug(zlib) printf("D.zlib.adler32.unittest\n"); - adler = adler32(0u, cast(void[])data); - debug(zlib) printf("adler = %x\n", adler); + uint adler = adler32(0u, data); assert(adler == 0xdc0037); } +@system unittest +{ + static string data = "test"; + + uint adler = adler32(1, data); + assert(adler == 0x045d01c1); +} + /** * $(P Compute the CRC32 checksum of a buffer's worth of data.) * @@ -117,21 +156,21 @@ unittest uint crc32(uint crc, const(void)[] buf) { import std.range : chunks; - foreach(chunk; (cast(ubyte[])buf).chunks(0xFFFF0000)) + foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000)) { - crc = etc.c.zlib.crc32(crc, chunk.ptr, cast(uint)chunk.length); + crc = etc.c.zlib.crc32(crc, chunk.ptr, cast(uint) chunk.length); } return crc; } -unittest +@system unittest { static ubyte[] data = [1,2,3,4,5,6,7,8,9,10]; uint crc; debug(zlib) printf("D.zlib.crc32.unittest\n"); - crc = crc32(0u, cast(void[])data); + crc = crc32(0u, cast(void[]) data); debug(zlib) printf("crc = %x\n", crc); assert(crc == 0x2520577b); } @@ -141,26 +180,28 @@ unittest * * Params: * srcbuf = buffer containing the data to compress - * level = compression level. Legal values are 1..9, with 1 being the - * least compression and 9 being the most. The default value - * is 6. + * level = compression level. Legal values are -1 .. 9, with -1 indicating + * the default level (6), 0 indicating no compression, 1 being the + * least compression and 9 being the most. * * Returns: * the compressed data */ -const(void)[] compress(const(void)[] srcbuf, int level) +ubyte[] compress(const(void)[] srcbuf, int level) in { assert(-1 <= level && level <= 9); } body { + import core.memory : GC; auto destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12; auto destbuf = new ubyte[destlen]; - auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *)srcbuf.ptr, srcbuf.length, level); + auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *) srcbuf.ptr, srcbuf.length, level); if (err) - { delete destbuf; + { + GC.free(destbuf.ptr); throw new ZlibException(err); } @@ -172,7 +213,7 @@ body * ditto */ -const(void)[] compress(const(void)[] srcbuf) +ubyte[] compress(const(void)[] srcbuf) { return compress(srcbuf, Z_DEFAULT_COMPRESSION); } @@ -188,7 +229,7 @@ const(void)[] compress(const(void)[] srcbuf) * Returns: the decompressed data. */ -void[] uncompress(void[] srcbuf, size_t destlen = 0u, int winbits = 15) +void[] uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15) { import std.conv : to; int err; @@ -198,7 +239,7 @@ void[] uncompress(void[] srcbuf, size_t destlen = 0u, int winbits = 15) destlen = srcbuf.length * 2 + 1; etc.c.zlib.z_stream zs; - zs.next_in = cast(typeof(zs.next_in)) srcbuf; + zs.next_in = cast(typeof(zs.next_in)) srcbuf.ptr; zs.avail_in = to!uint(srcbuf.length); err = etc.c.zlib.inflateInit2(&zs, winbits); if (err) @@ -238,9 +279,9 @@ void[] uncompress(void[] srcbuf, size_t destlen = 0u, int winbits = 15) assert(0); } -unittest +@system unittest { - ubyte[] src = cast(ubyte[]) + auto src = "the quick brown fox jumps over the lazy dog\r the quick brown fox jumps over the lazy dog\r "; @@ -248,30 +289,30 @@ the quick brown fox jumps over the lazy dog\r ubyte[] result; //arrayPrint(src); - dst = cast(ubyte[])compress(cast(void[])src); + dst = compress(src); //arrayPrint(dst); - result = cast(ubyte[])uncompress(cast(void[])dst); + result = cast(ubyte[]) uncompress(dst); //arrayPrint(result); assert(result == src); } -unittest +@system unittest { ubyte[] src = new ubyte[1000000]; ubyte[] dst; ubyte[] result; src[] = 0x80; - dst = cast(ubyte[])compress(cast(void[])src); + dst = compress(src); assert(dst.length*2 + 1 < src.length); - result = cast(ubyte[])uncompress(cast(void[])dst); + result = cast(ubyte[]) uncompress(dst); assert(result == src); } /+ void arrayPrint(ubyte[] array) { - //printf("array %p,%d\n", cast(void*)array, array.length); + //printf("array %p,%d\n", cast(void*) array, array.length); for (size_t i = 0; i < array.length; i++) { printf("%02x ", array[i]); @@ -295,7 +336,7 @@ enum HeaderFormat { class Compress { - import std.conv: to; + import std.conv : to; private: z_stream zs; @@ -318,7 +359,7 @@ class Compress * Constructor. * * Params: - * level = compression level. Legal values are 1..9, with 1 being the least + * level = compression level. Legal values are 1 .. 9, with 1 being the least * compression and 9 being the most. The default value is 6. * header = sets the compression type to one of the options available * in $(LREF HeaderFormat). Defaults to HeaderFormat.deflate. @@ -363,7 +404,9 @@ class Compress * */ const(void)[] compress(const(void)[] buf) - { int err; + { + import core.memory : GC; + int err; ubyte[] destbuf; if (buf.length == 0) @@ -389,7 +432,8 @@ class Compress err = deflate(&zs, Z_NO_FLUSH); if (err != Z_STREAM_END && err != Z_OK) - { delete destbuf; + { + GC.free(destbuf.ptr); error(err); } destbuf.length = destbuf.length - zs.avail_out; @@ -421,6 +465,7 @@ class Compress } body { + import core.memory : GC; ubyte[] destbuf; ubyte[512] tmpbuf = void; int err; @@ -435,13 +480,13 @@ class Compress zs.next_out = tmpbuf.ptr; zs.avail_out = tmpbuf.length; - while( (err = deflate(&zs, mode)) != Z_STREAM_END) + while ( (err = deflate(&zs, mode)) != Z_STREAM_END) { if (err == Z_OK) { if (zs.avail_out != 0 && mode != Z_FINISH) break; - else if(zs.avail_out == 0) + else if (zs.avail_out == 0) { destbuf ~= tmpbuf; zs.next_out = tmpbuf.ptr; @@ -450,7 +495,7 @@ class Compress } err = Z_BUF_ERROR; } - delete destbuf; + GC.free(destbuf.ptr); error(err); } destbuf ~= tmpbuf[0 .. (tmpbuf.length - zs.avail_out)]; @@ -472,7 +517,7 @@ class Compress class UnCompress { - import std.conv: to; + import std.conv : to; private: z_stream zs; @@ -529,7 +574,9 @@ class UnCompress assert(!done); } body - { int err; + { + import core.memory : GC; + int err; ubyte[] destbuf; if (buf.length == 0) @@ -538,9 +585,9 @@ class UnCompress if (!inited) { int windowBits = 15; - if(format == HeaderFormat.gzip) + if (format == HeaderFormat.gzip) windowBits += 16; - else if(format == HeaderFormat.determineFromData) + else if (format == HeaderFormat.determineFromData) windowBits += 32; err = inflateInit2(&zs, windowBits); @@ -558,12 +605,13 @@ class UnCompress if (zs.avail_in) buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf; - zs.next_in = cast(ubyte*) buf; + zs.next_in = cast(ubyte*) buf.ptr; zs.avail_in = to!uint(buf.length); err = inflate(&zs, Z_NO_FLUSH); if (err != Z_STREAM_END && err != Z_OK) - { delete destbuf; + { + GC.free(destbuf.ptr); error(err); } destbuf.length = destbuf.length - zs.avail_out; @@ -586,6 +634,7 @@ class UnCompress } body { + import core.memory : GC; ubyte[] extra; ubyte[] destbuf; int err; @@ -607,7 +656,7 @@ class UnCompress } if (err != Z_STREAM_END) { - delete destbuf; + GC.free(destbuf.ptr); if (err == Z_OK) err = Z_BUF_ERROR; error(err); @@ -628,17 +677,17 @@ class UnCompress private import std.stdio; private import std.random; -unittest // by Dave +@system unittest // by Dave { debug(zlib) writeln("std.zlib.unittest"); - bool CompressThenUncompress (ubyte[] src) + bool CompressThenUncompress (void[] src) { - ubyte[] dst = cast(ubyte[])std.zlib.compress(cast(void[])src); - double ratio = (dst.length / cast(double)src.length); + ubyte[] dst = std.zlib.compress(src); + double ratio = (dst.length / cast(double) src.length); debug(zlib) writef("src.length: %1$d, dst: %2$d, Ratio = %3$f", src.length, dst.length, ratio); ubyte[] uncompressedBuf; - uncompressedBuf = cast(ubyte[])std.zlib.uncompress(cast(void[])dst); + uncompressedBuf = cast(ubyte[]) std.zlib.uncompress(dst); assert(src.length == uncompressedBuf.length); assert(src == uncompressedBuf); @@ -647,31 +696,39 @@ unittest // by Dave // smallish buffers - for(int idx = 0; idx < 25; idx++) { + for (int idx = 0; idx < 25; idx++) + { char[] buf = new char[uniform(0, 100)]; // Alternate between more & less compressible - foreach(ref char c; buf) + foreach (ref char c; buf) c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 2))); - if(CompressThenUncompress(cast(ubyte[])buf)) { + if (CompressThenUncompress(buf)) + { debug(zlib) writeln("; Success."); - } else { + } + else + { return; } } // larger buffers - for(int idx = 0; idx < 25; idx++) { + for (int idx = 0; idx < 25; idx++) + { char[] buf = new char[uniform(0, 1000/*0000*/)]; // Alternate between more & less compressible - foreach(ref char c; buf) + foreach (ref char c; buf) c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 10))); - if(CompressThenUncompress(cast(ubyte[])buf)) { + if (CompressThenUncompress(buf)) + { debug(zlib) writefln("; Success."); - } else { + } + else + { return; } } @@ -680,7 +737,7 @@ unittest // by Dave } -unittest // by Artem Rebrov +@system unittest // by Artem Rebrov { Compress cmp = new Compress; UnCompress decmp = new UnCompress; @@ -692,8 +749,12 @@ unittest // by Artem Rebrov buf ~= cmp.flush(); const(void)[] output = decmp.uncompress(buf); - //writefln("input = '%s'", cast(char[])input); - //writefln("output = '%s'", cast(char[])output); + //writefln("input = '%s'", cast(char[]) input); + //writefln("output = '%s'", cast(char[]) output); assert( output[] == input[] ); } +@system unittest +{ + static assert(__traits(compiles, etc.c.zlib.gzclose(null))); // bugzilla 15457 +} diff --git a/unittest.d b/unittest.d index 7b7f548d42a..ac8e07c8483 100644 --- a/unittest.d +++ b/unittest.d @@ -5,8 +5,8 @@ * tests on them. Then, it prints out the arguments passed to main(). * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(WEB digitalmars.com, Walter Bright) + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: $(HTTP digitalmars.com, Walter Bright) * * Copyright Digital Mars 2000 - 2009. * Distributed under the Boost Software License, Version 1.0. @@ -19,7 +19,6 @@ public import std.compiler; public import std.concurrency; public import std.conv; public import std.container; -public import std.cstream; public import std.datetime; public import std.demangle; public import std.file; @@ -37,12 +36,9 @@ public import std.regex; public import std.signals; //public import std.slist; public import std.socket; -public import std.socketstream; public import std.stdint; public import std.stdio; -public import std.stream; public import std.string; -public import std.syserror; public import std.system; public import std.traits; public import std.typetuple; @@ -59,28 +55,29 @@ public import std.digest.digest; public import std.digest.crc; public import std.digest.sha; public import std.digest.md; +public import std.digest.hmac; int main(string[] args) { // Bring in unit test for module by referencing function in it - cast(void)cmp("foo", "bar"); // string - cast(void)filenameCharCmp('a', 'b'); // path - cast(void)isNaN(1.0); // math + cast(void) cmp("foo", "bar"); // string + cast(void) filenameCharCmp('a', 'b'); // path + cast(void) isNaN(1.0); // math std.conv.to!double("1.0"); // std.conv OutBuffer b = new OutBuffer(); // outbuffer auto r = regex(""); // regex uint ranseed = std.random.unpredictableSeed; thisTid; int[] a; - import std.algorithm : sort, reverse; + import std.algorithm.sorting : sort; + import std.algorithm.mutation : reverse; reverse(a); // adi sort(a); // qsort Clock.currTime(); // datetime - Exception e = new ReadException(""); // stream - din.eof(); // cstream - cast(void)isValidDchar(cast(dchar)0); // utf - std.uri.ascii2hex(0); // uri + cast(void) isValidDchar(cast(dchar) 0); // utf + string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!"; + assert(uriLength(s1) == 49); std.zlib.adler32(0,null); // D.zlib auto t = task!cmp("foo", "bar"); // parallelism @@ -102,12 +99,12 @@ int main(string[] args) assert(x[1] == 3); assert(x[2] == 45); - cast(void)std.math.sin(3.0); - cast(void)std.mathspecial.gamma(6.2); + cast(void) std.math.sin(3.0); + cast(void) std.mathspecial.gamma(6.2); std.demangle.demangle("hello"); - cast(void)std.uni.isAlpha('A'); + cast(void) std.uni.isAlpha('A'); std.file.exists("foo"); diff --git a/win32.mak b/win32.mak index 3baf0b883f4..3a9f3b48d37 100644 --- a/win32.mak +++ b/win32.mak @@ -101,134 +101,256 @@ test.exe : test.obj $(LIB) # ti_bit.obj ti_Abit.obj +SRC= \ + unittest.d \ + index.d + # The separation is a workaround for bug 4904 (optlink bug 3372). -# SRCS_1 is the heavyweight modules which are most likely to trigger the bug. -# Do not add any more modules to SRCS_1. -SRC_STD_1_HEAVY= std\stdio.d std\stdiobase.d \ - std\string.d std\format.d \ +SRC_STD_1= \ + std\stdio.d \ + std\stdiobase.d \ + std\string.d \ + std\format.d \ std\file.d -SRC_STD_2a_HEAVY= std\array.d std\functional.d std\path.d std\outbuffer.d std\utf.d - -SRC_STD_3= std\csv.d std\math.d std\complex.d std\numeric.d std\bigint.d \ - std\bitmanip.d std\typecons.d \ - std\uni.d std\base64.d std\ascii.d \ - std\demangle.d std\uri.d std\metastrings.d std\mmfile.d std\getopt.d - -SRC_STD_3a= std\signals.d std\typetuple.d std\traits.d \ - std\encoding.d std\xml.d \ +SRC_STD_2a= \ + std\array.d \ + std\functional.d \ + std\path.d \ + std\outbuffer.d \ + std\utf.d + +SRC_STD_3= \ + std\csv.d \ + std\math.d \ + std\complex.d \ + std\numeric.d \ + std\bigint.d \ + std\bitmanip.d \ + std\typecons.d \ + std\uni.d \ + std\base64.d \ + std\ascii.d \ + std\demangle.d \ + std\uri.d \ + std\mmfile.d \ + std\getopt.d + +SRC_STD_3a= \ + std\signals.d \ + std\meta.d \ + std\typetuple.d \ + std\traits.d \ + std\encoding.d \ + std\xml.d \ std\random.d \ std\exception.d \ std\compiler.d \ - std\system.d std\concurrency.d - -SRC_STD_3b= std\datetime.d - -#can't place SRC_STD_DIGEST in SRC_STD_REST because of out-of-memory issues -SRC_STD_DIGEST= std\digest\crc.d std\digest\sha.d std\digest\md.d \ - std\digest\ripemd.d std\digest\digest.d - -SRC_STD_CONTAINER= std\container\array.d std\container\binaryheap.d \ - std\container\dlist.d std\container\rbtree.d std\container\slist.d \ - std\container\util.d std\container\package.d - -SRC_STD_4= std\uuid.d $(SRC_STD_DIGEST) - -SRC_STD_ALGO= std\algorithm\package.d std\algorithm\comparison.d \ - std\algorithm\iteration.d std\algorithm\mutation.d \ - std\algorithm\searching.d std\algorithm\setops.d \ - std\algorithm\sorting.d std\algorithm\internal.d - -SRC_STD_5_HEAVY= $(SRC_STD_ALGO) - -SRC_STD_LOGGER= std\experimental\logger\core.d std\experimental\logger\filelogger.d \ - std\experimental\logger\multilogger.d std\experimental\logger\nulllogger.d \ - std\experimental\logger\package.d - -SRC_STD_6= std\variant.d \ - std\syserror.d std\zlib.d \ - std\stream.d std\socket.d std\socketstream.d \ - std\conv.d std\zip.d std\cstream.d \ - $(SRC_STD_CONTAINER) $(SRC_STD_LOGGER) - -SRC_STD_REST= std\stdint.d \ + std\system.d \ + std\concurrency.d \ + std\concurrencybase.d + +SRC_STD_4= \ + std\uuid.d + +SRC_STD_6= \ + std\variant.d \ + std\zlib.d \ + std\socket.d \ + std\conv.d \ + std\zip.d + +SRC_STD_7= \ + std\stdint.d \ std\json.d \ std\parallelism.d \ std\mathspecial.d \ std\process.d -SRC_STD_ALL= $(SRC_STD_1_HEAVY) $(SRC_STD_2a_HEAVY) \ - $(SRC_STD_3) $(SRC_STD_3a) $(SRC_STD_3b) $(SRC_STD_4) \ - $(SRC_STD_6) $(SRC_STD_REST) - -SRC= unittest.d index.d - -SRC_STD= std\zlib.d std\zip.d std\stdint.d std\conv.d std\utf.d std\uri.d \ - std\math.d std\string.d std\path.d std\datetime.d \ - std\csv.d std\file.d std\compiler.d std\system.d \ - std\outbuffer.d std\base64.d \ - std\metastrings.d std\mmfile.d \ - std\syserror.d \ - std\random.d std\stream.d std\process.d \ - std\socket.d std\socketstream.d std\format.d \ - std\stdio.d std\uni.d std\uuid.d \ - std\cstream.d std\demangle.d \ - std\signals.d std\typetuple.d std\traits.d \ - std\getopt.d \ - std\variant.d std\numeric.d std\bitmanip.d std\complex.d std\mathspecial.d \ - std\functional.d std\array.d std\typecons.d \ - std\json.d std\xml.d std\encoding.d std\bigint.d std\concurrency.d \ - std\stdiobase.d std\parallelism.d \ - std\exception.d std\ascii.d - -SRC_STD_REGEX= std\regex\internal\ir.d std\regex\package.d std\regex\internal\parser.d \ - std\regex\internal\tests.d std\regex\internal\backtracking.d \ - std\regex\internal\thompson.d std\regex\internal\kickstart.d \ - std\regex\internal\generator.d - -SRC_STD_RANGE= std\range\package.d std\range\primitives.d \ +SRC_STD= \ + $(SRC_STD_1) \ + $(SRC_STD_2a) \ + $(SRC_STD_3) \ + $(SRC_STD_3a) \ + $(SRC_STD_4) \ + $(SRC_STD_6) \ + $(SRC_STD_7) + +SRC_STD_ALGO= \ + std\algorithm\package.d \ + std\algorithm\comparison.d \ + std\algorithm\iteration.d \ + std\algorithm\mutation.d \ + std\algorithm\searching.d \ + std\algorithm\setops.d \ + std\algorithm\sorting.d \ + std\algorithm\internal.d + +SRC_STD_CONTAINER= \ + std\container\array.d \ + std\container\binaryheap.d \ + std\container\dlist.d \ + std\container\rbtree.d \ + std\container\slist.d \ + std\container\util.d \ + std\container\package.d + +SRC_STD_DATETIME= \ + std\datetime\date.d \ + std\datetime\interval.d \ + std\datetime\package.d \ + std\datetime\stopwatch.d \ + std\datetime\systime.d \ + std\datetime\timezone.d + +SRC_STD_DIGEST= \ + std\digest\crc.d \ + std\digest\sha.d \ + std\digest\md.d \ + std\digest\ripemd.d \ + std\digest\digest.d \ + std\digest\hmac.d \ + std\digest\murmurhash.d + +SRC_STD_NET= \ + std\net\isemail.d \ + std\net\curl.d + +SRC_STD_RANGE= \ + std\range\package.d \ + std\range\primitives.d \ std\range\interfaces.d -SRC_STD_NET= std\net\isemail.d std\net\curl.d - -SRC_STD_C= std\c\process.d std\c\stdlib.d std\c\time.d std\c\stdio.d \ - std\c\math.d std\c\stdarg.d std\c\stddef.d std\c\fenv.d std\c\string.d \ - std\c\locale.d std\c\wcharh.d - -SRC_STD_WIN= std\windows\registry.d \ - std\windows\iunknown.d std\windows\syserror.d std\windows\charset.d - -SRC_STD_C_WIN= std\c\windows\windows.d std\c\windows\com.d \ - std\c\windows\winsock.d std\c\windows\stat.d +SRC_STD_REGEX= \ + std\regex\internal\ir.d \ + std\regex\package.d \ + std\regex\internal\parser.d \ + std\regex\internal\tests.d \ + std\regex\internal\backtracking.d \ + std\regex\internal\thompson.d \ + std\regex\internal\kickstart.d \ + std\regex\internal\generator.d -SRC_STD_C_LINUX= std\c\linux\linux.d \ - std\c\linux\socket.d std\c\linux\pthread.d std\c\linux\termios.d \ +SRC_STD_C= \ + std\c\process.d \ + std\c\stdlib.d \ + std\c\time.d \ + std\c\stdio.d \ + std\c\math.d \ + std\c\stdarg.d \ + std\c\stddef.d \ + std\c\fenv.d \ + std\c\string.d \ + std\c\locale.d \ + std\c\wcharh.d + +SRC_STD_WIN= \ + std\windows\registry.d \ + std\windows\iunknown.d \ + std\windows\syserror.d \ + std\windows\charset.d + +SRC_STD_C_WIN= \ + std\c\windows\windows.d \ + std\c\windows\com.d \ + std\c\windows\winsock.d \ + std\c\windows\stat.d + +SRC_STD_C_LINUX= \ + std\c\linux\linux.d \ + std\c\linux\socket.d \ + std\c\linux\pthread.d \ + std\c\linux\termios.d \ std\c\linux\tipc.d -SRC_STD_C_OSX= std\c\osx\socket.d - -SRC_STD_C_FREEBSD= std\c\freebsd\socket.d - -SRC_STD_INTERNAL= std\internal\cstring.d std\internal\processinit.d \ - std\internal\unicode_tables.d std\internal\unicode_comp.d std\internal\unicode_decomp.d \ - std\internal\unicode_grapheme.d std\internal\unicode_norm.d std\internal\scopebuffer.d \ +SRC_STD_C_OSX= \ + std\c\osx\socket.d + +SRC_STD_C_FREEBSD= \ + std\c\freebsd\socket.d + +SRC_STD_INTERNAL= \ + std\internal\cstring.d \ + std\internal\encodinginit.d \ + std\internal\processinit.d \ + std\internal\unicode_tables.d \ + std\internal\unicode_comp.d \ + std\internal\unicode_decomp.d \ + std\internal\unicode_grapheme.d \ + std\internal\unicode_norm.d \ + std\internal\scopebuffer.d \ std\internal\test\dummyrange.d -SRC_STD_INTERNAL_DIGEST= std\internal\digest\sha_SSSE3.d - -SRC_STD_INTERNAL_MATH= std\internal\math\biguintcore.d \ - std\internal\math\biguintnoasm.d std\internal\math\biguintx86.d \ - std\internal\math\gammafunction.d std\internal\math\errorfunction.d - -SRC_STD_INTERNAL_WINDOWS= std\internal\windows\advapi32.d +SRC_STD_INTERNAL_DIGEST= \ + std\internal\digest\sha_SSSE3.d + +SRC_STD_INTERNAL_MATH= \ + std\internal\math\biguintcore.d \ + std\internal\math\biguintnoasm.d \ + std\internal\math\biguintx86.d \ + std\internal\math\gammafunction.d \ + std\internal\math\errorfunction.d + +SRC_STD_INTERNAL_WINDOWS= \ + std\internal\windows\advapi32.d + +SRC_STD_EXP= \ + std\experimental\checkedint.d std\experimental\typecons.d + +SRC_STD_EXP_ALLOC_BB= \ + std\experimental\allocator\building_blocks\affix_allocator.d \ + std\experimental\allocator\building_blocks\allocator_list.d \ + std\experimental\allocator\building_blocks\bitmapped_block.d \ + std\experimental\allocator\building_blocks\bucketizer.d \ + std\experimental\allocator\building_blocks\fallback_allocator.d \ + std\experimental\allocator\building_blocks\free_list.d \ + std\experimental\allocator\building_blocks\free_tree.d \ + std\experimental\allocator\building_blocks\kernighan_ritchie.d \ + std\experimental\allocator\building_blocks\null_allocator.d \ + std\experimental\allocator\building_blocks\quantizer.d \ + std\experimental\allocator\building_blocks\region.d \ + std\experimental\allocator\building_blocks\scoped_allocator.d \ + std\experimental\allocator\building_blocks\segregator.d \ + std\experimental\allocator\building_blocks\stats_collector.d \ + std\experimental\allocator\building_blocks\package.d + +SRC_STD_EXP_ALLOC= \ + std\experimental\allocator\common.d \ + std\experimental\allocator\gc_allocator.d \ + std\experimental\allocator\mallocator.d \ + std\experimental\allocator\mmap_allocator.d \ + std\experimental\allocator\showcase.d \ + std\experimental\allocator\typed.d \ + std\experimental\allocator\package.d \ + $(SRC_STD_EXP_ALLOC_BB) + +SRC_STD_EXP_LOGGER= \ + std\experimental\logger\core.d \ + std\experimental\logger\filelogger.d \ + std\experimental\logger\multilogger.d \ + std\experimental\logger\nulllogger.d \ + std\experimental\logger\package.d SRC_ETC= -SRC_ETC_C= etc\c\zlib.d etc\c\curl.d etc\c\sqlite3.d - -SRC_TO_COMPILE_NOT_STD= \ - $(SRC_STD_REGEX) \ +SRC_ETC_C= \ + etc\c\zlib.d \ + etc\c\curl.d \ + etc\c\sqlite3.d \ + etc\c\odbc\sql.d \ + etc\c\odbc\sqlext.d \ + etc\c\odbc\sqltypes.d \ + etc\c\odbc\sqlucode.d + +SRC_TO_COMPILE= \ + $(SRC_STD) \ + $(SRC_STD_ALGO) \ + $(SRC_STD_CONTAINER) \ + $(SRC_STD_DATETIME) \ + $(SRC_STD_DIGEST) \ $(SRC_STD_NET) \ + $(SRC_STD_RANGE) \ + $(SRC_STD_REGEX) \ $(SRC_STD_C) \ $(SRC_STD_WIN) \ $(SRC_STD_C_WIN) \ @@ -236,14 +358,12 @@ SRC_TO_COMPILE_NOT_STD= \ $(SRC_STD_INTERNAL_DIGEST) \ $(SRC_STD_INTERNAL_MATH) \ $(SRC_STD_INTERNAL_WINDOWS) \ + $(SRC_STD_EXP) \ + $(SRC_STD_EXP_ALLOC) \ + $(SRC_STD_EXP_LOGGER) \ $(SRC_ETC) \ $(SRC_ETC_C) -SRC_TO_COMPILE= $(SRC_STD_ALL) \ - $(SRC_STD_ALGO) \ - $(SRC_STD_RANGE) \ - $(SRC_TO_COMPILE_NOT_STD) - SRC_ZLIB= \ etc\c\zlib\crc32.h \ etc\c\zlib\deflate.h \ @@ -283,7 +403,8 @@ SRC_ZLIB= \ etc\c\zlib\osx.mak -DOCS= $(DOC)\object.html \ +DOCS= \ + $(DOC)\object.html \ $(DOC)\core_atomic.html \ $(DOC)\core_bitop.html \ $(DOC)\core_exception.html \ @@ -327,10 +448,16 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_digest_sha.html \ $(DOC)\std_digest_md.html \ $(DOC)\std_digest_ripemd.html \ + $(DOC)\std_digest_hmac.html \ $(DOC)\std_digest_digest.html \ - $(DOC)\std_cstream.html \ + $(DOC)\std_digest_hmac.html \ $(DOC)\std_csv.html \ $(DOC)\std_datetime.html \ + $(DOC)\std_datetime_date.html \ + $(DOC)\std_datetime_interval.html \ + $(DOC)\std_datetime_stopwatch.html \ + $(DOC)\std_datetime_systime.html \ + $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ $(DOC)\std_encoding.html \ $(DOC)\std_exception.html \ @@ -341,6 +468,7 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_json.html \ $(DOC)\std_math.html \ $(DOC)\std_mathspecial.html \ + $(DOC)\std_meta.html \ $(DOC)\std_mmfile.html \ $(DOC)\std_numeric.html \ $(DOC)\std_outbuffer.html \ @@ -354,10 +482,8 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_regex.html \ $(DOC)\std_signals.html \ $(DOC)\std_socket.html \ - $(DOC)\std_socketstream.html \ $(DOC)\std_stdint.html \ $(DOC)\std_stdio.html \ - $(DOC)\std_stream.html \ $(DOC)\std_string.html \ $(DOC)\std_system.html \ $(DOC)\std_traits.html \ @@ -378,6 +504,28 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_experimental_logger_multilogger.html \ $(DOC)\std_experimental_logger_nulllogger.html \ $(DOC)\std_experimental_logger.html \ + $(DOC)\std_experimental_allocator_building_blocks_affix_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_allocator_list.html \ + $(DOC)\std_experimental_allocator_building_blocks_bitmapped_block.html \ + $(DOC)\std_experimental_allocator_building_blocks_bucketizer.html \ + $(DOC)\std_experimental_allocator_building_blocks_fallback_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_free_list.html \ + $(DOC)\std_experimental_allocator_building_blocks_free_tree.html \ + $(DOC)\std_experimental_allocator_building_blocks_kernighan_ritchie.html \ + $(DOC)\std_experimental_allocator_building_blocks_null_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_quantizer.html \ + $(DOC)\std_experimental_allocator_building_blocks_region.html \ + $(DOC)\std_experimental_allocator_building_blocks_scoped_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_segregator.html \ + $(DOC)\std_experimental_allocator_building_blocks_stats_collector.html \ + $(DOC)\std_experimental_allocator_building_blocks.html \ + $(DOC)\std_experimental_allocator_common.html \ + $(DOC)\std_experimental_allocator_gc_allocator.html \ + $(DOC)\std_experimental_allocator_mmap_allocator.html \ + $(DOC)\std_experimental_allocator_showcase.html \ + $(DOC)\std_experimental_allocator_typed.html \ + $(DOC)\std_experimental_allocator.html \ + $(DOC)\std_experimental_typecons.html \ $(DOC)\std_windows_charset.html \ $(DOC)\std_windows_registry.html \ $(DOC)\std_c_fenv.html \ @@ -394,6 +542,10 @@ DOCS= $(DOC)\object.html \ $(DOC)\etc_c_curl.html \ $(DOC)\etc_c_sqlite3.html \ $(DOC)\etc_c_zlib.html \ + $(DOC)\etc_c_odbc_sql.html \ + $(DOC)\etc_c_odbc_sqlext.html \ + $(DOC)\etc_c_odbc_sqltypes.html \ + $(DOC)\etc_c_odbc_sqlucode.html \ $(DOC)\index.html $(LIB) : $(SRC_TO_COMPILE) \ @@ -412,20 +564,30 @@ UNITTEST_OBJS= \ unittest5.obj \ unittest6.obj \ unittest7.obj \ - unittest8.obj + unittest8a.obj \ + unittest8b.obj \ + unittest8c.obj \ + unittest8d.obj \ + unittest8e.obj \ + unittest8f.obj unittest : $(LIB) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest1.obj $(SRC_STD_1_HEAVY) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest1.obj $(SRC_STD_1) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest2.obj $(SRC_STD_RANGE) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest2a.obj $(SRC_STD_2a_HEAVY) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest2a.obj $(SRC_STD_2a) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3.obj $(SRC_STD_3) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3a.obj $(SRC_STD_3a) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3b.obj $(SRC_STD_3b) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest4.obj $(SRC_STD_4) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest5.obj $(SRC_STD_5_HEAVY) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest6.obj $(SRC_STD_6) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest7.obj $(SRC_STD_REST) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8.obj $(SRC_TO_COMPILE_NOT_STD) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3b.obj $(SRC_STD_DATETIME) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest4.obj $(SRC_STD_4) $(SRC_STD_DIGEST) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest5.obj $(SRC_STD_ALGO) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest6.obj $(SRC_STD_6) $(SRC_STD_CONTAINER) $(SRC_STD_EXP_ALLOC) $(SRC_STD_EXP_LOGGER) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest7.obj $(SRC_STD_7) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8a.obj $(SRC_STD_REGEX) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8b.obj $(SRC_STD_NET) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8c.obj $(SRC_STD_C) $(SRC_STD_WIN) $(SRC_STD_C_WIN) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8d.obj $(SRC_STD_INTERNAL) $(SRC_STD_INTERNAL_DIGEST) $(SRC_STD_INTERNAL_MATH) $(SRC_STD_INTERNAL_WINDOWS) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8e.obj $(SRC_ETC) $(SRC_ETC_C) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8f.obj $(SRC_STD_EXP) $(DMD) $(UDFLAGS) -L/co -unittest unittest.d $(UNITTEST_OBJS) \ $(ZLIB) $(DRUNTIMELIB) .\unittest.exe @@ -467,6 +629,7 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=51 -unittest -main -run std\mmfile.d $(DMD) -conf= -cov=95 -unittest -main -run std\getopt.d $(DMD) -conf= -cov=92 -unittest -main -run std\signals.d + $(DMD) -conf= -cov=100 -unittest -main -run std\meta.d $(DMD) -conf= -cov=100 -unittest -main -run std\typetuple.d $(DMD) -conf= -cov=85 -unittest -main -run std\traits.d $(DMD) -conf= -cov=62 -unittest -main -run std\encoding.d @@ -474,13 +637,20 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=79 -unittest -main -run std\random.d $(DMD) -conf= -cov=92 -unittest -main -run std\exception.d $(DMD) -conf= -cov=73 -unittest -main -run std\concurrency.d - $(DMD) -conf= -cov=95 -unittest -main -run std\datetime.d + $(DMD) -conf= -cov=100 -unittest -main -run std\concurrencybase.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\date.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\interval.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\package.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\stopwatch.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\systime.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\timezone.d $(DMD) -conf= -cov=96 -unittest -main -run std\uuid.d $(DMD) -conf= -cov=100 -unittest -main -run std\digest\crc.d $(DMD) -conf= -cov=55 -unittest -main -run std\digest\sha.d $(DMD) -conf= -cov=100 -unittest -main -run std\digest\md.d $(DMD) -conf= -cov=100 -unittest -main -run std\digest\ripemd.d $(DMD) -conf= -cov=75 -unittest -main -run std\digest\digest.d + $(DMD) -conf= -cov=100 -unittest -main -run std\digest\hmac.d $(DMD) -conf= -cov=95 -unittest -main -run std\algorithm\package.d $(DMD) -conf= -cov=95 -unittest -main -run std\algorithm\comparison.d $(DMD) -conf= -cov=95 -unittest -main -run std\algorithm\iteration.d @@ -489,11 +659,8 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=95 -unittest -main -run std\algorithm\setops.d $(DMD) -conf= -cov=95 -unittest -main -run std\algorithm\sorting.d $(DMD) -conf= -cov=83 -unittest -main -run std\variant.d - $(DMD) -conf= -cov=0 -unittest -main -run std\syserror.d $(DMD) -conf= -cov=58 -unittest -main -run std\zlib.d - $(DMD) -conf= -cov=54 -unittest -main -run std\stream.d $(DMD) -conf= -cov=53 -unittest -main -run std\socket.d - $(DMD) -conf= -cov=0 -unittest -main -run std\socketstream.d $(DMD) -conf= -cov=95 -unittest -main -run std\container\array.d $(DMD) -conf= -cov=68 -unittest -main -run std\container\binaryheap.d $(DMD) -conf= -cov=91 -unittest -main -run std\container\dlist.d @@ -503,7 +670,6 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=100 -unittest -main -run std\container\package.d $(DMD) -conf= -cov=90 -unittest -main -run std\conv.d $(DMD) -conf= -cov=0 -unittest -main -run std\zip.d - $(DMD) -conf= -cov=92 -unittest -main -run std\cstream.d $(DMD) -conf= -cov=77 -unittest -main -run std\regex\tests.d $(DMD) -conf= -cov=92 -unittest -main -run std\json.d $(DMD) -conf= -cov=87 -unittest -main -run std\parallelism.d @@ -523,6 +689,9 @@ cov : $(SRC_TO_COMPILE) $(LIB) html : $(DOCS) +changelog.html: changelog.dd + $(DMD) -Dfchangelog.html changelog.dd + ###################################################### $(ZLIB): $(SRC_ZLIB) @@ -534,8 +703,8 @@ $(ZLIB): $(SRC_ZLIB) DDOCFLAGS=$(DFLAGS) -version=StdDdoc -$(DOC)\object.html : $(STDDOC) $(DRUNTIME)\src\object_.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\object.html $(STDDOC) $(DRUNTIME)\src\object_.d -I$(DRUNTIME)\src\ +$(DOC)\object.html : $(STDDOC) $(DRUNTIME)\src\object.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\object.html $(STDDOC) $(DRUNTIME)\src\object.d -I$(DRUNTIME)\src\ $(DOC)\index.html : $(STDDOC) index.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\index.html $(STDDOC) index.d @@ -666,14 +835,26 @@ $(DOC)\std_range_primitives.html : $(STDDOC) std\range\primitives.d $(DOC)\std_range_interfaces.html : $(STDDOC) std\range\interfaces.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_range_interfaces.html $(STDDOC) std\range\interfaces.d -$(DOC)\std_cstream.html : $(STDDOC) std\cstream.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_cstream.html $(STDDOC) std\cstream.d - $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DMD) -c -o- $(DFLAGS) -Df$(DOC)\std_csv.html $(STDDOC) std\csv.d -$(DOC)\std_datetime.html : $(STDDOC) std\datetime.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime.d +$(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d + +$(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_date.html $(STDDOC) std\datetime\date.d + +$(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d + +$(DOC)\std_datetime_stopwatch.html : $(STDDOC) std\datetime\stopwatch.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_stopwatch.html $(STDDOC) std\datetime\stopwatch.d + +$(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d + +$(DOC)\std_datetime_timezone.html : $(STDDOC) std\datetime\timezone.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timezone.html $(STDDOC) std\datetime\timezone.d $(DOC)\std_demangle.html : $(STDDOC) std\demangle.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_demangle.html $(STDDOC) std\demangle.d @@ -699,6 +880,9 @@ $(DOC)\std_json.html : $(STDDOC) std\json.d $(DOC)\std_math.html : $(STDDOC) std\math.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_math.html $(STDDOC) std\math.d +$(DOC)\std_meta.html : $(STDDOC) std\meta.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_meta.html $(STDDOC) std\meta.d + $(DOC)\std_mathspecial.html : $(STDDOC) std\mathspecial.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_mathspecial.html $(STDDOC) std\mathspecial.d @@ -735,18 +919,12 @@ $(DOC)\std_signals.html : $(STDDOC) std\signals.d $(DOC)\std_socket.html : $(STDDOC) std\socket.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_socket.html $(STDDOC) std\socket.d -$(DOC)\std_socketstream.html : $(STDDOC) std\socketstream.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_socketstream.html $(STDDOC) std\socketstream.d - $(DOC)\std_stdint.html : $(STDDOC) std\stdint.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_stdint.html $(STDDOC) std\stdint.d $(DOC)\std_stdio.html : $(STDDOC) std\stdio.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_stdio.html $(STDDOC) std\stdio.d -$(DOC)\std_stream.html : $(STDDOC) std\stream.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_stream.html $(STDDOC) std\stream.d - $(DOC)\std_string.html : $(STDDOC) std\string.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_string.html $(STDDOC) std\string.d @@ -810,6 +988,90 @@ $(DOC)\std_experimental_logger_nulllogger.html : $(STDDOC) std\experimental\logg $(DOC)\std_experimental_logger.html : $(STDDOC) std\experimental\logger\package.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_logger.html $(STDDOC) std\experimental\logger\package.d +$(DOC)\std_experimental_allocator_building_blocks_affix_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\affix_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_affix_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\affix_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_allocator_list.html : $(STDDOC) std\experimental\allocator\building_blocks\allocator_list.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_allocator_list.html \ + $(STDDOC) std\experimental\allocator\building_blocks\allocator_list.d + +$(DOC)\std_experimental_allocator_building_blocks_bitmapped_block.html : $(STDDOC) std\experimental\allocator\building_blocks\bitmapped_block.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_bitmapped_block.html \ + $(STDDOC) std\experimental\allocator\building_blocks\bitmapped_block.d + +$(DOC)\std_experimental_allocator_building_blocks_bucketizer.html : $(STDDOC) std\experimental\allocator\building_blocks\bucketizer.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_bucketizer.html \ + $(STDDOC) std\experimental\allocator\building_blocks\bucketizer.d + +$(DOC)\std_experimental_allocator_building_blocks_fallback_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\fallback_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_fallback_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\fallback_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_free_list.html : $(STDDOC) std\experimental\allocator\building_blocks\free_list.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_free_list.html \ + $(STDDOC) std\experimental\allocator\building_blocks\free_list.d + +$(DOC)\std_experimental_allocator_building_blocks_free_tree.html : $(STDDOC) std\experimental\allocator\building_blocks\free_tree.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_free_tree.html \ + $(STDDOC) std\experimental\allocator\building_blocks\free_tree.d + +$(DOC)\std_experimental_allocator_building_blocks_kernighan_ritchie.html : $(STDDOC) std\experimental\allocator\building_blocks\kernighan_ritchie.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_kernighan_ritchie.html \ + $(STDDOC) std\experimental\allocator\building_blocks\kernighan_ritchie.d + +$(DOC)\std_experimental_allocator_building_blocks_null_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\null_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_null_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\null_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_quantizer.html : $(STDDOC) std\experimental\allocator\building_blocks\quantizer.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_quantizer.html \ + $(STDDOC) std\experimental\allocator\building_blocks\quantizer.d + +$(DOC)\std_experimental_allocator_building_blocks_region.html : $(STDDOC) std\experimental\allocator\building_blocks\region.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_region.html \ + $(STDDOC) std\experimental\allocator\building_blocks\region.d + +$(DOC)\std_experimental_allocator_building_blocks_scoped_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\scoped_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_scoped_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\scoped_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_segregator.html : $(STDDOC) std\experimental\allocator\building_blocks\segregator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_segregator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\segregator.d + +$(DOC)\std_experimental_allocator_building_blocks_stats_collector.html : $(STDDOC) std\experimental\allocator\building_blocks\stats_collector.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_stats_collector.html \ + $(STDDOC) std\experimental\allocator\building_blocks\stats_collector.d + +$(DOC)\std_experimental_allocator_building_blocks.html : $(STDDOC) std\experimental\allocator\building_blocks\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks.html \ + $(STDDOC) std\experimental\allocator\building_blocks\package.d + +$(DOC)\std_experimental_allocator_common.html : $(STDDOC) std\experimental\allocator\common.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_common.html $(STDDOC) std\experimental\allocator\common.d + +$(DOC)\std_experimental_allocator_gc_allocator.html : $(STDDOC) std\experimental\allocator\gc_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_gc_allocator.html $(STDDOC) std\experimental\allocator\gc_allocator.d + +$(DOC)\std_experimental_allocator_mallocator.html : $(STDDOC) std\experimental\allocator\mallocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_mallocator.html $(STDDOC) std\experimental\allocator\mallocator.d + +$(DOC)\std_experimental_allocator_mmap_allocator.html : $(STDDOC) std\experimental\allocator\mmap_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_mmap_allocator.html $(STDDOC) std\experimental\allocator\mmap_allocator.d + +$(DOC)\std_experimental_allocator_showcase.html : $(STDDOC) std\experimental\allocator\showcase.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_showcase.html $(STDDOC) std\experimental\allocator\showcase.d + +$(DOC)\std_experimental_allocator_typed.html : $(STDDOC) std\experimental\allocator\typed.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_typed.html $(STDDOC) std\experimental\allocator\typed.d + +$(DOC)\std_experimental_allocator.html : $(STDDOC) std\experimental\allocator\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator.html $(STDDOC) std\experimental\allocator\package.d + +$(DOC)\std_experimental_typecons.html : $(STDDOC) std\experimental\typecons.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_typecons.html $(STDDOC) std\experimental\typecons.d + $(DOC)\std_digest_crc.html : $(STDDOC) std\digest\crc.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_crc.html $(STDDOC) std\digest\crc.d @@ -825,6 +1087,9 @@ $(DOC)\std_digest_ripemd.html : $(STDDOC) std\digest\ripemd.d $(DOC)\std_digest_digest.html : $(STDDOC) std\digest\digest.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_digest.html $(STDDOC) std\digest\digest.d +$(DOC)\std_digest_hmac.html : $(STDDOC) std\digest\hmac.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_hmac.html $(STDDOC) std\digest\hmac.d + $(DOC)\std_windows_charset.html : $(STDDOC) std\windows\charset.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_windows_charset.html $(STDDOC) std\windows\charset.d @@ -873,39 +1138,26 @@ $(DOC)\etc_c_sqlite3.html : $(STDDOC) etc\c\sqlite3.d $(DOC)\etc_c_zlib.html : $(STDDOC) etc\c\zlib.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_zlib.html $(STDDOC) etc\c\zlib.d +$(DOC)\etc_c_odbc_sql.html : $(STDDOC) etc\c\odbc\sql.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sql.html $(STDDOC) etc\c\odbc\sql.d + +$(DOC)\etc_c_odbc_sqlext.html : $(STDDOC) etc\c\odbc\sqlext.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sqlext.html $(STDDOC) etc\c\odbc\sqlext.d + +$(DOC)\etc_c_odbc_sqltypes.html : $(STDDOC) etc\c\odbc\sqltypes.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sqltypes.html $(STDDOC) etc\c\odbc\sqltypes.d + +$(DOC)\etc_c_odbc_sqlucode.html : $(STDDOC) etc\c\odbc\sqlucode.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sqlucode.html $(STDDOC) etc\c\odbc\sqlucode.d + +$(DOC)\etc_c_odbc_sql.html : $(STDDOC) etc\c\odbc\sql.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sql.html $(STDDOC) etc\c\odbc\sql.d ###################################################### -zip : win32.mak win64.mak posix.mak $(STDDOC) $(SRC) \ - $(SRC_STD) $(SRC_STD_C) $(SRC_STD_WIN) \ - $(SRC_STD_C_WIN) $(SRC_STD_C_LINUX) $(SRC_STD_C_OSX) $(SRC_STD_C_FREEBSD) \ - $(SRC_ETC) $(SRC_ETC_C) $(SRC_ZLIB) $(SRC_STD_NET) $(SRC_STD_DIGEST) $(SRC_STD_CONTAINER) \ - $(SRC_STD_INTERNAL) $(SRC_STD_INTERNAL_DIGEST) $(SRC_STD_INTERNAL_MATH) \ - $(SRC_STD_INTERNAL_WINDOWS) $(SRC_STD_REGEX) $(SRC_STD_RANGE) $(SRC_STD_ALGO) \ - $(SRC_STD_LOGGER) +zip: del phobos.zip - zip32 -u phobos win32.mak win64.mak posix.mak $(STDDOC) - zip32 -u phobos $(SRC) - zip32 -u phobos $(SRC_STD) - zip32 -u phobos $(SRC_STD_C) - zip32 -u phobos $(SRC_STD_WIN) - zip32 -u phobos $(SRC_STD_C_WIN) - zip32 -u phobos $(SRC_STD_C_LINUX) - zip32 -u phobos $(SRC_STD_C_OSX) - zip32 -u phobos $(SRC_STD_C_FREEBSD) - zip32 -u phobos $(SRC_STD_INTERNAL) - zip32 -u phobos $(SRC_STD_INTERNAL_DIGEST) - zip32 -u phobos $(SRC_STD_INTERNAL_MATH) - zip32 -u phobos $(SRC_STD_INTERNAL_WINDOWS) - zip32 -u phobos $(SRC_ETC) $(SRC_ETC_C) - zip32 -u phobos $(SRC_ZLIB) - zip32 -u phobos $(SRC_STD_NET) - zip32 -u phobos $(SRC_STD_LOGGER) - zip32 -u phobos $(SRC_STD_DIGEST) - zip32 -u phobos $(SRC_STD_CONTAINER) - zip32 -u phobos $(SRC_STD_REGEX) - zip32 -u phobos $(SRC_STD_RANGE) - zip32 -u phobos $(SRC_STD_ALGO) + zip32 -r phobos.zip . -x .git\* -x \*.lib -x \*.obj phobos.zip : zip @@ -930,3 +1182,7 @@ install: phobos.zip $(CP) $(DOC)\index.html $(DIR)\html\d\phobos\index.html +rd/s/q $(DIR)\src\phobos unzip -o phobos.zip -d $(DIR)\src\phobos + +auto-tester-build: targets + +auto-tester-test: unittest diff --git a/win64.mak b/win64.mak index 7d775b518f8..80bc8467ea0 100644 --- a/win64.mak +++ b/win64.mak @@ -10,6 +10,10 @@ # Delete unneeded files created by build process # make unittest # Build phobos64.lib, build and run unit tests +# make phobos32mscoff +# Build phobos32mscoff.lib +# make unittest32mscoff +# Build phobos32mscoff.lib, build and run unit tests # make cov # Build for coverage tests, run coverage tests # make html @@ -101,59 +105,69 @@ test.exe : test.obj $(LIB) # ti_bit.obj ti_Abit.obj +SRC= \ + unittest.d \ + index.d + # The separation is a workaround for bug 4904 (optlink bug 3372). -# SRCS_1 is the heavyweight modules which are most likely to trigger the bug. -# Do not add any more modules to SRCS_1. -SRC_STD_1_HEAVY= std\stdio.d std\stdiobase.d \ - std\string.d std\format.d \ +SRC_STD_1= \ + std\stdio.d \ + std\stdiobase.d \ + std\string.d \ + std\format.d \ std\file.d -SRC_STD_2a_HEAVY= std\array.d std\functional.d std\path.d std\outbuffer.d std\utf.d - -SRC_STD_math=std\math.d -SRC_STD_3= std\csv.d std\complex.d std\numeric.d std\bigint.d -SRC_STD_3c= std\datetime.d std\bitmanip.d std\typecons.d - -SRC_STD_3a= std\uni.d std\base64.d std\ascii.d \ - std\demangle.d std\uri.d std\metastrings.d std\mmfile.d std\getopt.d - -SRC_STD_3b= std\signals.d std\typetuple.d std\traits.d \ - std\encoding.d std\xml.d \ +SRC_STD_2a= \ + std\array.d \ + std\functional.d \ + std\path.d \ + std\outbuffer.d \ + std\utf.d + +SRC_STD_3= \ + std\csv.d \ + std\complex.d \ + std\numeric.d \ + std\bigint.d + +SRC_STD_3a= \ + std\math.d + +SRC_STD_3b= \ + std\uni.d \ + std\base64.d \ + std\ascii.d \ + std\demangle.d \ + std\uri.d \ + std\mmfile.d \ + std\getopt.d + +SRC_STD_3c= \ + std\signals.d \ + std\meta.d \ + std\typetuple.d \ + std\traits.d \ + std\encoding.d \ + std\xml.d \ std\random.d \ std\exception.d \ std\compiler.d \ - std\system.d std\concurrency.d - -#can't place SRC_STD_DIGEST in SRC_STD_REST because of out-of-memory issues -SRC_STD_DIGEST= std\digest\crc.d std\digest\sha.d std\digest\md.d \ - std\digest\ripemd.d std\digest\digest.d + std\system.d \ + std\concurrency.d \ + std\concurrencybase.d -SRC_STD_CONTAINER= std\container\array.d std\container\binaryheap.d \ - std\container\dlist.d std\container\rbtree.d std\container\slist.d \ - std\container\util.d std\container\package.d +SRC_STD_3d= \ + std\bitmanip.d \ + std\typecons.d -SRC_STD_4= std\uuid.d $(SRC_STD_DIGEST) - -SRC_STD_ALGO= std\algorithm\package.d std\algorithm\comparison.d \ - std\algorithm\iteration.d std\algorithm\mutation.d \ - std\algorithm\searching.d std\algorithm\setops.d \ - std\algorithm\sorting.d std\algorithm\internal.d - -SRC_STD_LOGGER= std\experimental\logger\core.d std\experimental\logger\filelogger.d \ - std\experimental\logger\multilogger.d std\experimental\logger\nulllogger.d \ - std\experimental\logger\package.d - -SRC_STD_5_HEAVY= $(SRC_STD_ALGO) +SRC_STD_4= \ + std\uuid.d SRC_STD_6a=std\variant.d -SRC_STD_6b=std\syserror.d SRC_STD_6c=std\zlib.d -SRC_STD_6d=std\stream.d SRC_STD_6e=std\socket.d -SRC_STD_6f=std\socketstream.d SRC_STD_6h=std\conv.d SRC_STD_6i=std\zip.d -SRC_STD_6j=std\cstream.d SRC_STD_7= \ std\stdint.d \ @@ -162,90 +176,206 @@ SRC_STD_7= \ std\mathspecial.d \ std\process.d -SRC_STD_ALL= $(SRC_STD_1_HEAVY) $(SRC_STD_2a_HEAVY) \ - $(SRC_STD_math) \ - $(SRC_STD_3) $(SRC_STD_3a) $(SRC_STD_3b) $(SRC_STD_3c) $(SRC_STD_4) \ +SRC_STD= \ + $(SRC_STD_1) \ + $(SRC_STD_2a) \ + $(SRC_STD_3) \ + $(SRC_STD_3a) \ + $(SRC_STD_3b) \ + $(SRC_STD_3c) \ + $(SRC_STD_3d) \ + $(SRC_STD_4) \ $(SRC_STD_6a) \ - $(SRC_STD_6b) \ $(SRC_STD_6c) \ - $(SRC_STD_6d) \ $(SRC_STD_6e) \ - $(SRC_STD_6f) \ - $(SRC_STD_CONTAINER) \ $(SRC_STD_6h) \ $(SRC_STD_6i) \ - $(SRC_STD_6j) \ - $(SRC_STD_7) \ - $(SRC_STD_LOGGER) - -SRC= unittest.d index.d - -SRC_STD= std\zlib.d std\zip.d std\stdint.d std\conv.d std\utf.d std\uri.d \ - std\math.d std\string.d std\path.d std\datetime.d \ - std\csv.d std\file.d std\compiler.d std\system.d \ - std\outbuffer.d std\base64.d \ - std\metastrings.d std\mmfile.d \ - std\syserror.d \ - std\random.d std\stream.d std\process.d \ - std\socket.d std\socketstream.d std\format.d \ - std\stdio.d std\uni.d std\uuid.d \ - std\cstream.d std\demangle.d \ - std\signals.d std\typetuple.d std\traits.d \ - std\getopt.d \ - std\variant.d std\numeric.d std\bitmanip.d std\complex.d std\mathspecial.d \ - std\functional.d std\array.d std\typecons.d \ - std\json.d std\xml.d std\encoding.d std\bigint.d std\concurrency.d \ - std\stdiobase.d std\parallelism.d \ - std\exception.d std\ascii.d - -SRC_STD_REGEX= std\regex\internal\ir.d std\regex\package.d std\regex\internal\parser.d \ - std\regex\internal\tests.d std\regex\internal\backtracking.d \ - std\regex\internal\thompson.d std\regex\internal\kickstart.d \ - std\regex\internal\generator.d - -SRC_STD_RANGE= std\range\package.d std\range\primitives.d \ + $(SRC_STD_7) + +SRC_STD_ALGO_1= \ + std\algorithm\package.d \ + std\algorithm\comparison.d \ + std\algorithm\iteration.d \ + std\algorithm\mutation.d + +SRC_STD_ALGO_2= \ + std\algorithm\searching.d \ + std\algorithm\setops.d + +SRC_STD_ALGO_3= \ + std\algorithm\sorting.d \ + std\algorithm\internal.d + +SRC_STD_ALGO= \ + $(SRC_STD_ALGO_1) \ + $(SRC_STD_ALGO_2) \ + $(SRC_STD_ALGO_3) + +SRC_STD_CONTAINER= \ + std\container\array.d \ + std\container\binaryheap.d \ + std\container\dlist.d \ + std\container\rbtree.d \ + std\container\slist.d \ + std\container\util.d \ + std\container\package.d + +SRC_STD_DATETIME= \ + std\datetime\date.d \ + std\datetime\interval.d \ + std\datetime\package.d \ + std\datetime\stopwatch.d \ + std\datetime\systime.d \ + std\datetime\timezone.d + +SRC_STD_DIGEST= \ + std\digest\crc.d \ + std\digest\sha.d \ + std\digest\md.d \ + std\digest\ripemd.d \ + std\digest\digest.d \ + std\digest\hmac.d \ + std\digest\murmurhash.d + +SRC_STD_NET= \ + std\net\isemail.d \ + std\net\curl.d + +SRC_STD_RANGE= \ + std\range\package.d \ + std\range\primitives.d \ std\range\interfaces.d -SRC_STD_NET= std\net\isemail.d std\net\curl.d - -SRC_STD_C= std\c\process.d std\c\stdlib.d std\c\time.d std\c\stdio.d \ - std\c\math.d std\c\stdarg.d std\c\stddef.d std\c\fenv.d std\c\string.d \ - std\c\locale.d std\c\wcharh.d - -SRC_STD_WIN= std\windows\registry.d \ - std\windows\iunknown.d std\windows\syserror.d std\windows\charset.d - -SRC_STD_C_WIN= std\c\windows\windows.d std\c\windows\com.d \ - std\c\windows\winsock.d std\c\windows\stat.d +SRC_STD_REGEX= \ + std\regex\internal\ir.d \ + std\regex\package.d \ + std\regex\internal\parser.d \ + std\regex\internal\tests.d \ + std\regex\internal\backtracking.d \ + std\regex\internal\thompson.d \ + std\regex\internal\kickstart.d \ + std\regex\internal\generator.d -SRC_STD_C_LINUX= std\c\linux\linux.d \ - std\c\linux\socket.d std\c\linux\pthread.d std\c\linux\termios.d \ +SRC_STD_C= \ + std\c\process.d \ + std\c\stdlib.d \ + std\c\time.d \ + std\c\stdio.d \ + std\c\math.d \ + std\c\stdarg.d \ + std\c\stddef.d \ + std\c\fenv.d \ + std\c\string.d \ + std\c\locale.d \ + std\c\wcharh.d + +SRC_STD_WIN= \ + std\windows\registry.d \ + std\windows\iunknown.d \ + std\windows\syserror.d \ + std\windows\charset.d + +SRC_STD_C_WIN= \ + std\c\windows\windows.d \ + std\c\windows\com.d \ + std\c\windows\winsock.d \ + std\c\windows\stat.d + +SRC_STD_C_LINUX= \ + std\c\linux\linux.d \ + std\c\linux\socket.d \ + std\c\linux\pthread.d \ + std\c\linux\termios.d \ std\c\linux\tipc.d -SRC_STD_C_OSX= std\c\osx\socket.d - -SRC_STD_C_FREEBSD= std\c\freebsd\socket.d - -SRC_STD_INTERNAL= std\internal\cstring.d std\internal\processinit.d \ - std\internal\unicode_tables.d std\internal\unicode_comp.d std\internal\unicode_decomp.d \ - std\internal\unicode_grapheme.d std\internal\unicode_norm.d std\internal\scopebuffer.d \ +SRC_STD_C_OSX= \ + std\c\osx\socket.d + +SRC_STD_C_FREEBSD= \ + std\c\freebsd\socket.d + +SRC_STD_INTERNAL= \ + std\internal\cstring.d \ + std\internal\encodinginit.d \ + std\internal\processinit.d \ + std\internal\unicode_tables.d \ + std\internal\unicode_comp.d \ + std\internal\unicode_decomp.d \ + std\internal\unicode_grapheme.d \ + std\internal\unicode_norm.d \ + std\internal\scopebuffer.d \ std\internal\test\dummyrange.d -SRC_STD_INTERNAL_DIGEST= std\internal\digest\sha_SSSE3.d - -SRC_STD_INTERNAL_MATH= std\internal\math\biguintcore.d \ - std\internal\math\biguintnoasm.d std\internal\math\biguintx86.d \ - std\internal\math\gammafunction.d std\internal\math\errorfunction.d - -SRC_STD_INTERNAL_WINDOWS= std\internal\windows\advapi32.d +SRC_STD_INTERNAL_DIGEST= \ + std\internal\digest\sha_SSSE3.d + +SRC_STD_INTERNAL_MATH= \ + std\internal\math\biguintcore.d \ + std\internal\math\biguintnoasm.d \ + std\internal\math\biguintx86.d \ + std\internal\math\gammafunction.d \ + std\internal\math\errorfunction.d + +SRC_STD_INTERNAL_WINDOWS= \ + std\internal\windows\advapi32.d + +SRC_STD_EXP= \ + std\experimental\checkedint.d std\experimental\typecons.d + +SRC_STD_EXP_ALLOC_BB= \ + std\experimental\allocator\building_blocks\affix_allocator.d \ + std\experimental\allocator\building_blocks\allocator_list.d \ + std\experimental\allocator\building_blocks\bitmapped_block.d \ + std\experimental\allocator\building_blocks\bucketizer.d \ + std\experimental\allocator\building_blocks\fallback_allocator.d \ + std\experimental\allocator\building_blocks\free_list.d \ + std\experimental\allocator\building_blocks\free_tree.d \ + std\experimental\allocator\building_blocks\kernighan_ritchie.d \ + std\experimental\allocator\building_blocks\null_allocator.d \ + std\experimental\allocator\building_blocks\quantizer.d \ + std\experimental\allocator\building_blocks\region.d \ + std\experimental\allocator\building_blocks\scoped_allocator.d \ + std\experimental\allocator\building_blocks\segregator.d \ + std\experimental\allocator\building_blocks\stats_collector.d \ + std\experimental\allocator\building_blocks\package.d + +SRC_STD_EXP_ALLOC= \ + std\experimental\allocator\common.d \ + std\experimental\allocator\gc_allocator.d \ + std\experimental\allocator\mallocator.d \ + std\experimental\allocator\mmap_allocator.d \ + std\experimental\allocator\showcase.d \ + std\experimental\allocator\typed.d \ + std\experimental\allocator\package.d \ + $(SRC_STD_EXP_ALLOC_BB) + +SRC_STD_EXP_LOGGER= \ + std\experimental\logger\core.d \ + std\experimental\logger\filelogger.d \ + std\experimental\logger\multilogger.d \ + std\experimental\logger\nulllogger.d \ + std\experimental\logger\package.d SRC_ETC= -SRC_ETC_C= etc\c\zlib.d etc\c\curl.d etc\c\sqlite3.d - -SRC_TO_COMPILE_NOT_STD= \ - $(SRC_STD_REGEX) \ +SRC_ETC_C= \ + etc\c\zlib.d \ + etc\c\curl.d \ + etc\c\sqlite3.d \ + etc\c\odbc\sql.d \ + etc\c\odbc\sqlext.d \ + etc\c\odbc\sqltypes.d \ + etc\c\odbc\sqlucode.d + +SRC_TO_COMPILE= \ + $(SRC_STD) \ + $(SRC_STD_ALGO) \ + $(SRC_STD_CONTAINER) \ + $(SRC_STD_DATETIME) \ + $(SRC_STD_DIGEST) \ $(SRC_STD_NET) \ + $(SRC_STD_RANGE) \ + $(SRC_STD_REGEX) \ $(SRC_STD_C) \ $(SRC_STD_WIN) \ $(SRC_STD_C_WIN) \ @@ -253,14 +383,12 @@ SRC_TO_COMPILE_NOT_STD= \ $(SRC_STD_INTERNAL_DIGEST) \ $(SRC_STD_INTERNAL_MATH) \ $(SRC_STD_INTERNAL_WINDOWS) \ + $(SRC_STD_EXP) \ + $(SRC_STD_EXP_ALLOC) \ + $(SRC_STD_EXP_LOGGER) \ $(SRC_ETC) \ $(SRC_ETC_C) -SRC_TO_COMPILE= $(SRC_STD_ALL) \ - $(SRC_STD_ALGO) \ - $(SRC_STD_RANGE) \ - $(SRC_TO_COMPILE_NOT_STD) - SRC_ZLIB= \ etc\c\zlib\crc32.h \ etc\c\zlib\deflate.h \ @@ -300,7 +428,8 @@ SRC_ZLIB= \ etc\c\zlib\osx.mak -DOCS= $(DOC)\object.html \ +DOCS= \ + $(DOC)\object.html \ $(DOC)\core_atomic.html \ $(DOC)\core_bitop.html \ $(DOC)\core_exception.html \ @@ -344,10 +473,16 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_digest_sha.html \ $(DOC)\std_digest_md.html \ $(DOC)\std_digest_ripemd.html \ + $(DOC)\std_digest_hmac.html \ $(DOC)\std_digest_digest.html \ - $(DOC)\std_cstream.html \ + $(DOC)\std_digest_hmac.html \ $(DOC)\std_csv.html \ $(DOC)\std_datetime.html \ + $(DOC)\std_datetime_date.html \ + $(DOC)\std_datetime_interval.html \ + $(DOC)\std_datetime_stopwatch.html \ + $(DOC)\std_datetime_systime.html \ + $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ $(DOC)\std_encoding.html \ $(DOC)\std_exception.html \ @@ -358,6 +493,7 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_json.html \ $(DOC)\std_math.html \ $(DOC)\std_mathspecial.html \ + $(DOC)\std_meta.html \ $(DOC)\std_mmfile.html \ $(DOC)\std_numeric.html \ $(DOC)\std_outbuffer.html \ @@ -371,10 +507,8 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_regex.html \ $(DOC)\std_signals.html \ $(DOC)\std_socket.html \ - $(DOC)\std_socketstream.html \ $(DOC)\std_stdint.html \ $(DOC)\std_stdio.html \ - $(DOC)\std_stream.html \ $(DOC)\std_string.html \ $(DOC)\std_system.html \ $(DOC)\std_traits.html \ @@ -395,6 +529,28 @@ DOCS= $(DOC)\object.html \ $(DOC)\std_experimental_logger_multilogger.html \ $(DOC)\std_experimental_logger_nulllogger.html \ $(DOC)\std_experimental_logger.html \ + $(DOC)\std_experimental_allocator_building_blocks_affix_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_allocator_list.html \ + $(DOC)\std_experimental_allocator_building_blocks_bitmapped_block.html \ + $(DOC)\std_experimental_allocator_building_blocks_bucketizer.html \ + $(DOC)\std_experimental_allocator_building_blocks_fallback_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_free_list.html \ + $(DOC)\std_experimental_allocator_building_blocks_free_tree.html \ + $(DOC)\std_experimental_allocator_building_blocks_kernighan_ritchie.html \ + $(DOC)\std_experimental_allocator_building_blocks_null_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_quantizer.html \ + $(DOC)\std_experimental_allocator_building_blocks_region.html \ + $(DOC)\std_experimental_allocator_building_blocks_scoped_allocator.html \ + $(DOC)\std_experimental_allocator_building_blocks_segregator.html \ + $(DOC)\std_experimental_allocator_building_blocks_stats_collector.html \ + $(DOC)\std_experimental_allocator_building_blocks.html \ + $(DOC)\std_experimental_allocator_common.html \ + $(DOC)\std_experimental_allocator_gc_allocator.html \ + $(DOC)\std_experimental_allocator_mmap_allocator.html \ + $(DOC)\std_experimental_allocator_showcase.html \ + $(DOC)\std_experimental_allocator_typed.html \ + $(DOC)\std_experimental_allocator.html \ + $(DOC)\std_experimental_typecons.html \ $(DOC)\std_windows_charset.html \ $(DOC)\std_windows_registry.html \ $(DOC)\std_c_fenv.html \ @@ -411,6 +567,10 @@ DOCS= $(DOC)\object.html \ $(DOC)\etc_c_curl.html \ $(DOC)\etc_c_sqlite3.html \ $(DOC)\etc_c_zlib.html \ + $(DOC)\etc_c_odbc_sql.html \ + $(DOC)\etc_c_odbc_sqlext.html \ + $(DOC)\etc_c_odbc_sqltypes.html \ + $(DOC)\etc_c_odbc_sqlucode.html \ $(DOC)\index.html $(LIB) : $(SRC_TO_COMPILE) \ @@ -422,48 +582,57 @@ UNITTEST_OBJS= \ unittest1.obj \ unittest2.obj \ unittest2a.obj \ - unittestM.obj \ unittest3.obj \ unittest3a.obj \ unittest3b.obj \ unittest3c.obj \ + unittest3d.obj \ unittest4.obj \ - unittest5.obj \ + unittest5a.obj \ + unittest5b.obj \ + unittest5c.obj \ unittest6a.obj \ - unittest6b.obj \ unittest6c.obj \ - unittest6d.obj \ unittest6e.obj \ - unittest6f.obj \ unittest6g.obj \ unittest6h.obj \ unittest6i.obj \ - unittest6j.obj \ - unittest7.obj + unittest7.obj \ + unittest8a.obj \ + unittest8b.obj \ + unittest8c.obj \ + unittest8d.obj \ + unittest8e.obj \ + unittest8f.obj \ + unittest9.obj unittest : $(LIB) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest1.obj $(SRC_STD_1_HEAVY) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest1.obj $(SRC_STD_1) $(DMD) $(UDFLAGS) -c -unittest -ofunittest2.obj $(SRC_STD_RANGE) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest2a.obj $(SRC_STD_2a_HEAVY) - $(DMD) $(UDFLAGS) -c -unittest -ofunittestM.obj $(SRC_STD_math) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest2a.obj $(SRC_STD_2a) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3.obj $(SRC_STD_3) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3a.obj $(SRC_STD_3a) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3b.obj $(SRC_STD_3b) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3c.obj $(SRC_STD_3c) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest4.obj $(SRC_STD_4) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest5.obj $(SRC_STD_5_HEAVY) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest3d.obj $(SRC_STD_3d) $(SRC_STD_DATETIME) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest4.obj $(SRC_STD_4) $(SRC_STD_DIGEST) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest5a.obj $(SRC_STD_ALGO_1) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest5b.obj $(SRC_STD_ALGO_2) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest5c.obj $(SRC_STD_ALGO_3) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6a.obj $(SRC_STD_6a) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest6b.obj $(SRC_STD_6b) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6c.obj $(SRC_STD_6c) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest6d.obj $(SRC_STD_6d) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6e.obj $(SRC_STD_6e) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest6g.obj $(SRC_STD_CONTAINER) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6h.obj $(SRC_STD_6h) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6i.obj $(SRC_STD_6i) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest6f.obj $(SRC_STD_6f) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest6g.obj $(SRC_STD_CONTAINER) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest6j.obj $(SRC_STD_6j) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest7.obj $(SRC_STD_7) $(SRC_STD_LOGGER) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest8.obj $(SRC_TO_COMPILE_NOT_STD) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest7.obj $(SRC_STD_7) $(SRC_STD_EXP_LOGGER) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest8a.obj $(SRC_STD_REGEX) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest8b.obj $(SRC_STD_NET) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest8c.obj $(SRC_STD_C) $(SRC_STD_WIN) $(SRC_STD_C_WIN) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest8d.obj $(SRC_STD_INTERNAL) $(SRC_STD_INTERNAL_DIGEST) $(SRC_STD_INTERNAL_MATH) $(SRC_STD_INTERNAL_WINDOWS) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest8e.obj $(SRC_ETC) $(SRC_ETC_C) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest8f.obj $(SRC_STD_EXP) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest9.obj $(SRC_STD_EXP_ALLOC) $(DMD) $(UDFLAGS) -L/OPT:NOICF -unittest unittest.d $(UNITTEST_OBJS) \ $(ZLIB) $(DRUNTIMELIB) .\unittest.exe @@ -481,6 +650,23 @@ cov : $(SRC_TO_COMPILE) $(LIB) html : $(DOCS) +changelog.html: changelog.dd + $(DMD) -Dfchangelog.html changelog.dd + +################### Win32 COFF support ######################### + +# default to 32-bit compiler relative to the location of the 64-bit compiler, +# link and lib are architecture agnostic +CC32=$(CC)\..\..\cl + +# build phobos32mscoff.lib +phobos32mscoff: + $(MAKE) -f win64.mak "DMD=$(DMD)" "MAKE=$(MAKE)" MODEL=32mscoff "CC=\$(CC32)"\"" "AR=\$(AR)"\"" "VCDIR=$(VCDIR)" "SDKDIR=$(SDKDIR)" + +# run unittests for 32-bit COFF version +unittest32mscoff: + $(MAKE) -f win64.mak "DMD=$(DMD)" "MAKE=$(MAKE)" MODEL=32mscoff "CC=\$(CC32)"\"" "AR=\$(AR)"\"" "VCDIR=$(VCDIR)" "SDKDIR=$(SDKDIR)" unittest + ###################################################### $(ZLIB): $(SRC_ZLIB) @@ -492,8 +678,8 @@ $(ZLIB): $(SRC_ZLIB) DDOCFLAGS=$(DFLAGS) -version=StdDdoc -$(DOC)\object.html : $(STDDOC) $(DRUNTIME)\src\object_.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\object.html $(STDDOC) $(DRUNTIME)\src\object_.d -I$(DRUNTIME)\src\ +$(DOC)\object.html : $(STDDOC) $(DRUNTIME)\src\object.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\object.html $(STDDOC) $(DRUNTIME)\src\object.d -I$(DRUNTIME)\src\ $(DOC)\index.html : $(STDDOC) index.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\index.html $(STDDOC) index.d @@ -624,14 +810,26 @@ $(DOC)\std_range_primitives.html : $(STDDOC) std\range\primitives.d $(DOC)\std_range_interfaces.html : $(STDDOC) std\range\interfaces.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_range_interfaces.html $(STDDOC) std\range\interfaces.d -$(DOC)\std_cstream.html : $(STDDOC) std\cstream.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_cstream.html $(STDDOC) std\cstream.d - $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DMD) -c -o- $(DFLAGS) -Df$(DOC)\std_csv.html $(STDDOC) std\csv.d -$(DOC)\std_datetime.html : $(STDDOC) std\datetime.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime.d +$(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d + +$(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_date.html $(STDDOC) std\datetime\date.d + +$(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d + +$(DOC)\std_datetime_stopwatch.html : $(STDDOC) std\datetime\stopwatch.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_stopwatch.html $(STDDOC) std\datetime\stopwatch.d + +$(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d + +$(DOC)\std_datetime_timezone.html : $(STDDOC) std\datetime\timezone.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timezone.html $(STDDOC) std\datetime\timezone.d $(DOC)\std_demangle.html : $(STDDOC) std\demangle.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_demangle.html $(STDDOC) std\demangle.d @@ -657,6 +855,9 @@ $(DOC)\std_json.html : $(STDDOC) std\json.d $(DOC)\std_math.html : $(STDDOC) std\math.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_math.html $(STDDOC) std\math.d +$(DOC)\std_meta.html : $(STDDOC) std\meta.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_meta.html $(STDDOC) std\meta.d + $(DOC)\std_mathspecial.html : $(STDDOC) std\mathspecial.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_mathspecial.html $(STDDOC) std\mathspecial.d @@ -693,18 +894,12 @@ $(DOC)\std_signals.html : $(STDDOC) std\signals.d $(DOC)\std_socket.html : $(STDDOC) std\socket.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_socket.html $(STDDOC) std\socket.d -$(DOC)\std_socketstream.html : $(STDDOC) std\socketstream.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_socketstream.html $(STDDOC) std\socketstream.d - $(DOC)\std_stdint.html : $(STDDOC) std\stdint.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_stdint.html $(STDDOC) std\stdint.d $(DOC)\std_stdio.html : $(STDDOC) std\stdio.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_stdio.html $(STDDOC) std\stdio.d -$(DOC)\std_stream.html : $(STDDOC) std\stream.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_stream.html $(STDDOC) std\stream.d - $(DOC)\std_string.html : $(STDDOC) std\string.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_string.html $(STDDOC) std\string.d @@ -768,6 +963,90 @@ $(DOC)\std_experimental_logger_nulllogger.html : $(STDDOC) std\experimental\logg $(DOC)\std_experimental_logger.html : $(STDDOC) std\experimental\logger\package.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_logger.html $(STDDOC) std\experimental\logger\package.d +$(DOC)\std_experimental_allocator_building_blocks_affix_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\affix_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_affix_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\affix_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_allocator_list.html : $(STDDOC) std\experimental\allocator\building_blocks\allocator_list.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_allocator_list.html \ + $(STDDOC) std\experimental\allocator\building_blocks\allocator_list.d + +$(DOC)\std_experimental_allocator_building_blocks_bitmapped_block.html : $(STDDOC) std\experimental\allocator\building_blocks\bitmapped_block.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_bitmapped_block.html \ + $(STDDOC) std\experimental\allocator\building_blocks\bitmapped_block.d + +$(DOC)\std_experimental_allocator_building_blocks_bucketizer.html : $(STDDOC) std\experimental\allocator\building_blocks\bucketizer.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_bucketizer.html \ + $(STDDOC) std\experimental\allocator\building_blocks\bucketizer.d + +$(DOC)\std_experimental_allocator_building_blocks_fallback_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\fallback_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_fallback_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\fallback_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_free_list.html : $(STDDOC) std\experimental\allocator\building_blocks\free_list.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_free_list.html \ + $(STDDOC) std\experimental\allocator\building_blocks\free_list.d + +$(DOC)\std_experimental_allocator_building_blocks_free_tree.html : $(STDDOC) std\experimental\allocator\building_blocks\free_tree.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_free_tree.html \ + $(STDDOC) std\experimental\allocator\building_blocks\free_tree.d + +$(DOC)\std_experimental_allocator_building_blocks_kernighan_ritchie.html : $(STDDOC) std\experimental\allocator\building_blocks\kernighan_ritchie.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_kernighan_ritchie.html \ + $(STDDOC) std\experimental\allocator\building_blocks\kernighan_ritchie.d + +$(DOC)\std_experimental_allocator_building_blocks_null_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\null_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_null_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\null_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_quantizer.html : $(STDDOC) std\experimental\allocator\building_blocks\quantizer.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_quantizer.html \ + $(STDDOC) std\experimental\allocator\building_blocks\quantizer.d + +$(DOC)\std_experimental_allocator_building_blocks_region.html : $(STDDOC) std\experimental\allocator\building_blocks\region.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_region.html \ + $(STDDOC) std\experimental\allocator\building_blocks\region.d + +$(DOC)\std_experimental_allocator_building_blocks_scoped_allocator.html : $(STDDOC) std\experimental\allocator\building_blocks\scoped_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_scoped_allocator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\scoped_allocator.d + +$(DOC)\std_experimental_allocator_building_blocks_segregator.html : $(STDDOC) std\experimental\allocator\building_blocks\segregator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_segregator.html \ + $(STDDOC) std\experimental\allocator\building_blocks\segregator.d + +$(DOC)\std_experimental_allocator_building_blocks_stats_collector.html : $(STDDOC) std\experimental\allocator\building_blocks\stats_collector.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks_stats_collector.html \ + $(STDDOC) std\experimental\allocator\building_blocks\stats_collector.d + +$(DOC)\std_experimental_allocator_building_blocks.html : $(STDDOC) std\experimental\allocator\building_blocks\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_building_blocks.html \ + $(STDDOC) std\experimental\allocator\building_blocks\package.d + +$(DOC)\std_experimental_allocator_common.html : $(STDDOC) std\experimental\allocator\common.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_common.html $(STDDOC) std\experimental\allocator\common.d + +$(DOC)\std_experimental_allocator_gc_allocator.html : $(STDDOC) std\experimental\allocator\gc_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_gc_allocator.html $(STDDOC) std\experimental\allocator\gc_allocator.d + +$(DOC)\std_experimental_allocator_mallocator.html : $(STDDOC) std\experimental\allocator\mallocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_mallocator.html $(STDDOC) std\experimental\allocator\mallocator.d + +$(DOC)\std_experimental_allocator_mmap_allocator.html : $(STDDOC) std\experimental\allocator\mmap_allocator.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_mmap_allocator.html $(STDDOC) std\experimental\allocator\mmap_allocator.d + +$(DOC)\std_experimental_allocator_showcase.html : $(STDDOC) std\experimental\allocator\showcase.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_showcase.html $(STDDOC) std\experimental\allocator\showcase.d + +$(DOC)\std_experimental_allocator_typed.html : $(STDDOC) std\experimental\allocator\typed.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator_typed.html $(STDDOC) std\experimental\allocator\typed.d + +$(DOC)\std_experimental_allocator.html : $(STDDOC) std\experimental\allocator\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_allocator.html $(STDDOC) std\experimental\allocator\package.d + +$(DOC)\std_experimental_typecons.html : $(STDDOC) std\experimental\typecons.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_typecons.html $(STDDOC) std\experimental\typecons.d + $(DOC)\std_digest_crc.html : $(STDDOC) std\digest\crc.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_crc.html $(STDDOC) std\digest\crc.d @@ -783,6 +1062,9 @@ $(DOC)\std_digest_ripemd.html : $(STDDOC) std\digest\ripemd.d $(DOC)\std_digest_digest.html : $(STDDOC) std\digest\digest.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_digest.html $(STDDOC) std\digest\digest.d +$(DOC)\std_digest_hmac.html : $(STDDOC) std\digest\hmac.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_hmac.html $(STDDOC) std\digest\hmac.d + $(DOC)\std_windows_charset.html : $(STDDOC) std\windows\charset.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_windows_charset.html $(STDDOC) std\windows\charset.d @@ -831,45 +1113,32 @@ $(DOC)\etc_c_sqlite3.html : $(STDDOC) etc\c\sqlite3.d $(DOC)\etc_c_zlib.html : $(STDDOC) etc\c\zlib.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_zlib.html $(STDDOC) etc\c\zlib.d +$(DOC)\etc_c_odbc_sql.html : $(STDDOC) etc\c\odbc\sql.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sql.html $(STDDOC) etc\c\odbc\sql.d + +$(DOC)\etc_c_odbc_sqlext.html : $(STDDOC) etc\c\odbc\sqlext.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sqlext.html $(STDDOC) etc\c\odbc\sqlext.d + +$(DOC)\etc_c_odbc_sqltypes.html : $(STDDOC) etc\c\odbc\sqltypes.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sqltypes.html $(STDDOC) etc\c\odbc\sqltypes.d + +$(DOC)\etc_c_odbc_sqlucode.html : $(STDDOC) etc\c\odbc\sqlucode.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sqlucode.html $(STDDOC) etc\c\odbc\sqlucode.d + +$(DOC)\etc_c_odbc_sql.html : $(STDDOC) etc\c\odbc\sql.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\etc_c_odbc_sql.html $(STDDOC) etc\c\odbc\sql.d ###################################################### -zip : win32.mak win64.mak posix.mak $(STDDOC) $(SRC) \ - $(SRC_STD) $(SRC_STD_C) $(SRC_STD_WIN) \ - $(SRC_STD_C_WIN) $(SRC_STD_C_LINUX) $(SRC_STD_C_OSX) $(SRC_STD_C_FREEBSD) \ - $(SRC_ETC) $(SRC_ETC_C) $(SRC_ZLIB) $(SRC_STD_NET) $(SRC_STD_DIGEST) $(SRC_STD_CONTAINER) \ - $(SRC_STD_INTERNAL) $(SRC_STD_INTERNAL_DIGEST) $(SRC_STD_INTERNAL_MATH) \ - $(SRC_STD_INTERNAL_WINDOWS) $(SRC_STD_REGEX) $(SRC_STD_RANGE) $(SRC_STD_ALGO) \ - $(SRC_STD_LOGGER) +zip: del phobos.zip - zip32 -u phobos win32.mak win64.mak posix.mak $(STDDOC) - zip32 -u phobos $(SRC) - zip32 -u phobos $(SRC_STD) - zip32 -u phobos $(SRC_STD_C) - zip32 -u phobos $(SRC_STD_WIN) - zip32 -u phobos $(SRC_STD_C_WIN) - zip32 -u phobos $(SRC_STD_C_LINUX) - zip32 -u phobos $(SRC_STD_C_OSX) - zip32 -u phobos $(SRC_STD_C_FREEBSD) - zip32 -u phobos $(SRC_STD_INTERNAL) - zip32 -u phobos $(SRC_STD_INTERNAL_DIGEST) - zip32 -u phobos $(SRC_STD_INTERNAL_MATH) - zip32 -u phobos $(SRC_STD_INTERNAL_WINDOWS) - zip32 -u phobos $(SRC_ETC) $(SRC_ETC_C) - zip32 -u phobos $(SRC_ZLIB) - zip32 -u phobos $(SRC_STD_NET) - zip32 -u phobos $(SRC_STD_LOGGER) - zip32 -u phobos $(SRC_STD_DIGEST) - zip32 -u phobos $(SRC_STD_CONTAINER) - zip32 -u phobos $(SRC_STD_REGEX) - zip32 -u phobos $(SRC_STD_RANGE) - zip32 -u phobos $(SRC_STD_ALGO) + zip32 -r phobos.zip . -x .git\* -x \*.lib -x \*.obj phobos.zip : zip clean: cd etc\c\zlib - $(MAKE) -f win$(MODEL).mak clean + $(MAKE) -f win64.mak MODEL=$(MODEL) clean cd ..\..\.. del $(DOCS) del $(UNITTEST_OBJS) unittest.obj unittest.exe @@ -884,3 +1153,7 @@ install: phobos.zip $(CP) $(DRUNTIME)\lib\gcstub.obj $(DRUNTIME)\lib\gcstub64.obj $(DIR)\windows\lib +rd/s/q $(DIR)\src\phobos unzip -o phobos.zip -d $(DIR)\src\phobos + +auto-tester-build: targets + +auto-tester-test: unittest